Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/backend/testbackend.c
1090 views
1
/*
2
* Backend test program for CUPS.
3
*
4
* Copyright © 2007-2014 by Apple Inc.
5
* Copyright © 1997-2005 by Easy Software Products, all rights reserved.
6
*
7
* Licensed under Apache License v2.0. See the file "LICENSE" for more
8
* information.
9
*/
10
11
/*
12
* Include necessary headers.
13
*/
14
15
#include <cups/string-private.h>
16
#include <cups/cups.h>
17
#include <cups/sidechannel.h>
18
#include <unistd.h>
19
#include <fcntl.h>
20
#include <sys/wait.h>
21
#include <signal.h>
22
#include "backend-private.h"
23
24
25
/*
26
* Local globals...
27
*/
28
29
static int job_canceled = 0;
30
31
32
/*
33
* Local functions...
34
*/
35
36
static void sigterm_handler(int sig);
37
static void usage(void) _CUPS_NORETURN;
38
static void walk_cb(const char *oid, const char *data, int datalen,
39
void *context);
40
41
42
/*
43
* 'main()' - Run the named backend.
44
*
45
* Usage:
46
*
47
* testbackend [-s] [-t] device-uri job-id user title copies options [file]
48
*/
49
50
int /* O - Exit status */
51
main(int argc, /* I - Number of command-line args */
52
char *argv[]) /* I - Command-line arguments */
53
{
54
int first_arg, /* First argument for backend */
55
do_cancel = 0, /* Simulate a cancel-job via SIGTERM */
56
do_ps = 0, /* Do PostScript query+test? */
57
do_pcl = 0, /* Do PCL query+test? */
58
do_side_tests = 0, /* Test side-channel ops? */
59
do_trickle = 0, /* Trickle data to backend */
60
do_walk = 0, /* Do OID lookup (0) or walking (1) */
61
show_log = 0; /* Show log messages from backends? */
62
const char *oid = ".1.3.6.1.2.1.43.10.2.1.4.1.1";
63
/* OID to lookup or walk */
64
char scheme[255], /* Scheme in URI == backend */
65
backend[1024], /* Backend path */
66
libpath[1024], /* Path for libcups */
67
*ptr; /* Pointer into path */
68
const char *serverbin; /* CUPS_SERVERBIN environment variable */
69
int fd, /* Temporary file descriptor */
70
back_fds[2], /* Back-channel pipe */
71
side_fds[2], /* Side-channel socket */
72
data_fds[2], /* Data pipe */
73
back_pid = -1, /* Backend process ID */
74
data_pid = -1, /* Trickle process ID */
75
pid, /* Process ID */
76
status; /* Exit status */
77
78
79
/*
80
* Get the current directory and point the run-time linker at the "cups"
81
* subdirectory...
82
*/
83
84
if (getcwd(libpath, sizeof(libpath)) &&
85
(ptr = strrchr(libpath, '/')) != NULL && !strcmp(ptr, "/backend"))
86
{
87
strlcpy(ptr, "/cups", sizeof(libpath) - (size_t)(ptr - libpath));
88
if (!access(libpath, 0))
89
{
90
#ifdef __APPLE__
91
fprintf(stderr, "Setting DYLD_LIBRARY_PATH to \"%s\".\n", libpath);
92
setenv("DYLD_LIBRARY_PATH", libpath, 1);
93
#else
94
fprintf(stderr, "Setting LD_LIBRARY_PATH to \"%s\".\n", libpath);
95
setenv("LD_LIBRARY_PATH", libpath, 1);
96
#endif /* __APPLE__ */
97
}
98
else
99
perror(libpath);
100
}
101
102
/*
103
* See if we have side-channel tests to do...
104
*/
105
106
for (first_arg = 1;
107
argv[first_arg] && argv[first_arg][0] == '-';
108
first_arg ++)
109
if (!strcmp(argv[first_arg], "-d"))
110
show_log = 1;
111
else if (!strcmp(argv[first_arg], "-cancel"))
112
do_cancel = 1;
113
else if (!strcmp(argv[first_arg], "-pcl"))
114
do_pcl = 1;
115
else if (!strcmp(argv[first_arg], "-ps"))
116
do_ps = 1;
117
else if (!strcmp(argv[first_arg], "-s"))
118
do_side_tests = 1;
119
else if (!strcmp(argv[first_arg], "-t"))
120
do_trickle = 1;
121
else if (!strcmp(argv[first_arg], "-get") && (first_arg + 1) < argc)
122
{
123
first_arg ++;
124
125
do_side_tests = 1;
126
oid = argv[first_arg];
127
}
128
else if (!strcmp(argv[first_arg], "-walk") && (first_arg + 1) < argc)
129
{
130
first_arg ++;
131
132
do_side_tests = 1;
133
do_walk = 1;
134
oid = argv[first_arg];
135
}
136
else
137
usage();
138
139
argc -= first_arg;
140
if (argc < 6 || argc > 7 || (argc == 7 && do_trickle))
141
usage();
142
143
/*
144
* Extract the scheme from the device-uri - that's the program we want to
145
* execute.
146
*/
147
148
if (sscanf(argv[first_arg], "%254[^:]", scheme) != 1)
149
{
150
fputs("testbackend: Bad device-uri - no colon!\n", stderr);
151
return (1);
152
}
153
154
if (!access(scheme, X_OK))
155
strlcpy(backend, scheme, sizeof(backend));
156
else
157
{
158
if ((serverbin = getenv("CUPS_SERVERBIN")) == NULL)
159
serverbin = CUPS_SERVERBIN;
160
161
snprintf(backend, sizeof(backend), "%s/backend/%s", serverbin, scheme);
162
if (access(backend, X_OK))
163
{
164
fprintf(stderr, "testbackend: Unknown device scheme \"%s\"!\n", scheme);
165
return (1);
166
}
167
}
168
169
/*
170
* Create the back-channel pipe and side-channel socket...
171
*/
172
173
open("/dev/null", O_WRONLY); /* Make sure fd 3 and 4 are used */
174
open("/dev/null", O_WRONLY);
175
176
pipe(back_fds);
177
fcntl(back_fds[0], F_SETFL, fcntl(back_fds[0], F_GETFL) | O_NONBLOCK);
178
fcntl(back_fds[1], F_SETFL, fcntl(back_fds[1], F_GETFL) | O_NONBLOCK);
179
180
socketpair(AF_LOCAL, SOCK_STREAM, 0, side_fds);
181
fcntl(side_fds[0], F_SETFL, fcntl(side_fds[0], F_GETFL) | O_NONBLOCK);
182
fcntl(side_fds[1], F_SETFL, fcntl(side_fds[1], F_GETFL) | O_NONBLOCK);
183
184
/*
185
* Execute the trickle process as needed...
186
*/
187
188
if (do_trickle || do_pcl || do_ps || do_cancel)
189
{
190
pipe(data_fds);
191
192
signal(SIGTERM, sigterm_handler);
193
194
if ((data_pid = fork()) == 0)
195
{
196
/*
197
* Trickle/query child comes here. Rearrange file descriptors so that
198
* FD 1, 3, and 4 point to the backend...
199
*/
200
201
if ((fd = open("/dev/null", O_RDONLY)) != 0)
202
{
203
dup2(fd, 0);
204
close(fd);
205
}
206
207
if (data_fds[1] != 1)
208
{
209
dup2(data_fds[1], 1);
210
close(data_fds[1]);
211
}
212
close(data_fds[0]);
213
214
if (back_fds[0] != 3)
215
{
216
dup2(back_fds[0], 3);
217
close(back_fds[0]);
218
}
219
close(back_fds[1]);
220
221
if (side_fds[0] != 4)
222
{
223
dup2(side_fds[0], 4);
224
close(side_fds[0]);
225
}
226
close(side_fds[1]);
227
228
if (do_trickle)
229
{
230
/*
231
* Write 10 spaces, 1 per second...
232
*/
233
234
int i; /* Looping var */
235
236
for (i = 0; i < 10; i ++)
237
{
238
write(1, " ", 1);
239
sleep(1);
240
}
241
}
242
else if (do_cancel)
243
{
244
/*
245
* Write PS or PCL lines until we see SIGTERM...
246
*/
247
248
int line = 0, page = 0; /* Current line and page */
249
ssize_t bytes; /* Number of bytes of response data */
250
char buffer[1024]; /* Output buffer */
251
252
253
if (do_pcl)
254
write(1, "\033E", 2);
255
else
256
write(1, "%!\n/Courier findfont 12 scalefont setfont 0 setgray\n", 52);
257
258
while (!job_canceled)
259
{
260
if (line == 0)
261
{
262
page ++;
263
264
if (do_pcl)
265
snprintf(buffer, sizeof(buffer), "PCL Page %d\r\n\r\n", page);
266
else
267
snprintf(buffer, sizeof(buffer),
268
"18 732 moveto (PS Page %d) show\n", page);
269
270
write(1, buffer, strlen(buffer));
271
}
272
273
line ++;
274
275
if (do_pcl)
276
snprintf(buffer, sizeof(buffer), "Line %d\r\n", line);
277
else
278
snprintf(buffer, sizeof(buffer), "18 %d moveto (Line %d) show\n",
279
720 - line * 12, line);
280
281
write(1, buffer, strlen(buffer));
282
283
if (line >= 55)
284
{
285
/*
286
* Eject after 55 lines...
287
*/
288
289
line = 0;
290
if (do_pcl)
291
write(1, "\014", 1);
292
else
293
write(1, "showpage\n", 9);
294
}
295
296
/*
297
* Check for back-channel data...
298
*/
299
300
if ((bytes = cupsBackChannelRead(buffer, sizeof(buffer), 0)) > 0)
301
write(2, buffer, (size_t)bytes);
302
303
/*
304
* Throttle output to ~100hz...
305
*/
306
307
usleep(10000);
308
}
309
310
/*
311
* Eject current page with info...
312
*/
313
314
if (do_pcl)
315
snprintf(buffer, sizeof(buffer),
316
"Canceled on line %d of page %d\r\n\014\033E", line, page);
317
else
318
snprintf(buffer, sizeof(buffer),
319
"\n18 %d moveto (Canceled on line %d of page %d)\nshowpage\n",
320
720 - line * 12, line, page);
321
322
write(1, buffer, strlen(buffer));
323
324
/*
325
* See if we get any back-channel data...
326
*/
327
328
while ((bytes = cupsBackChannelRead(buffer, sizeof(buffer), 5.0)) > 0)
329
write(2, buffer, (size_t)bytes);
330
331
exit(0);
332
}
333
else
334
{
335
/*
336
* Do PS or PCL query + test pages.
337
*/
338
339
char buffer[1024]; /* Buffer for response data */
340
ssize_t bytes; /* Number of bytes of response data */
341
double timeout; /* Timeout */
342
const char *data; /* Data to send */
343
static const char *pcl_data = /* PCL data */
344
"\033%-12345X@PJL\r\n"
345
"@PJL JOB NAME = \"Hello, World!\"\r\n"
346
"@PJL INFO USTATUS\r\n"
347
"@PJL ENTER LANGUAGE = PCL\r\n"
348
"\033E"
349
"Hello, World!\n"
350
"\014"
351
"\033%-12345X@PJL\r\n"
352
"@PJL EOJ NAME=\"Hello, World!\"\r\n"
353
"\033%-12345X";
354
static const char *ps_data = /* PostScript data */
355
"%!\n"
356
"save\n"
357
"product = flush\n"
358
"currentpagedevice /PageSize get aload pop\n"
359
"2 copy gt {exch} if\n"
360
"(Unknown)\n"
361
"19 dict\n"
362
"dup [612 792] (Letter) put\n"
363
"dup [612 1008] (Legal) put\n"
364
"dup [612 935] (w612h935) put\n"
365
"dup [522 756] (Executive) put\n"
366
"dup [595 842] (A4) put\n"
367
"dup [420 595] (A5) put\n"
368
"dup [499 709] (ISOB5) put\n"
369
"dup [516 728] (B5) put\n"
370
"dup [612 936] (w612h936) put\n"
371
"dup [284 419] (Postcard) put\n"
372
"dup [419.5 567] (DoublePostcard) put\n"
373
"dup [558 774] (w558h774) put\n"
374
"dup [553 765] (w553h765) put\n"
375
"dup [522 737] (w522h737) put\n"
376
"dup [499 709] (EnvISOB5) put\n"
377
"dup [297 684] (Env10) put\n"
378
"dup [459 649] (EnvC5) put\n"
379
"dup [312 624] (EnvDL) put\n"
380
"dup [279 540] (EnvMonarch) put\n"
381
"{ exch aload pop 4 index sub abs 5 le exch\n"
382
" 5 index sub abs 5 le and\n"
383
" {exch pop exit} {pop} ifelse\n"
384
"} bind forall\n"
385
"= flush pop pop\n"
386
"/Courier findfont 12 scalefont setfont\n"
387
"0 setgray 36 720 moveto (Hello, ) show product show (!) show\n"
388
"showpage\n"
389
"restore\n"
390
"\004";
391
392
393
if (do_pcl)
394
data = pcl_data;
395
else
396
data = ps_data;
397
398
write(1, data, strlen(data));
399
backendMessage("DEBUG: START\n");
400
timeout = 60.0;
401
while ((bytes = cupsBackChannelRead(buffer, sizeof(buffer),
402
timeout)) > 0)
403
{
404
write(2, buffer, (size_t)bytes);
405
timeout = 5.0;
406
}
407
backendMessage("\nDEBUG: END\n");
408
}
409
410
exit(0);
411
}
412
else if (data_pid < 0)
413
{
414
perror("testbackend: Unable to fork");
415
return (1);
416
}
417
}
418
else
419
data_fds[0] = data_fds[1] = -1;
420
421
/*
422
* Execute the backend...
423
*/
424
425
if ((back_pid = fork()) == 0)
426
{
427
/*
428
* Child comes here...
429
*/
430
431
if (do_trickle || do_ps || do_pcl || do_cancel)
432
{
433
if (data_fds[0] != 0)
434
{
435
dup2(data_fds[0], 0);
436
close(data_fds[0]);
437
}
438
close(data_fds[1]);
439
}
440
441
if (!show_log)
442
{
443
if ((fd = open("/dev/null", O_WRONLY)) != 2)
444
{
445
dup2(fd, 2);
446
close(fd);
447
}
448
}
449
450
if (back_fds[1] != 3)
451
{
452
dup2(back_fds[1], 3);
453
close(back_fds[0]);
454
}
455
close(back_fds[1]);
456
457
if (side_fds[1] != 4)
458
{
459
dup2(side_fds[1], 4);
460
close(side_fds[0]);
461
}
462
close(side_fds[1]);
463
464
execv(backend, argv + first_arg);
465
fprintf(stderr, "testbackend: Unable to execute \"%s\": %s\n", backend,
466
strerror(errno));
467
return (errno);
468
}
469
else if (back_pid < 0)
470
{
471
perror("testbackend: Unable to fork");
472
return (1);
473
}
474
475
/*
476
* Parent comes here, setup back and side channel file descriptors...
477
*/
478
479
if (do_trickle || do_ps || do_pcl || do_cancel)
480
{
481
close(data_fds[0]);
482
close(data_fds[1]);
483
}
484
485
if (back_fds[0] != 3)
486
{
487
dup2(back_fds[0], 3);
488
close(back_fds[0]);
489
}
490
close(back_fds[1]);
491
492
if (side_fds[0] != 4)
493
{
494
dup2(side_fds[0], 4);
495
close(side_fds[0]);
496
}
497
close(side_fds[1]);
498
499
/*
500
* Do side-channel tests as needed, then wait for the backend...
501
*/
502
503
if (do_side_tests)
504
{
505
int length; /* Length of buffer */
506
char buffer[2049]; /* Buffer for response */
507
cups_sc_status_t scstatus; /* Status of side-channel command */
508
static const char * const statuses[] =
509
{
510
"CUPS_SC_STATUS_NONE", /* No status */
511
"CUPS_SC_STATUS_OK", /* Operation succeeded */
512
"CUPS_SC_STATUS_IO_ERROR", /* An I/O error occurred */
513
"CUPS_SC_STATUS_TIMEOUT", /* The backend did not respond */
514
"CUPS_SC_STATUS_NO_RESPONSE", /* The device did not respond */
515
"CUPS_SC_STATUS_BAD_MESSAGE", /* The command/response message was invalid */
516
"CUPS_SC_STATUS_TOO_BIG", /* Response too big */
517
"CUPS_SC_STATUS_NOT_IMPLEMENTED" /* Command not implemented */
518
};
519
520
521
sleep(2);
522
523
length = 0;
524
scstatus = cupsSideChannelDoRequest(CUPS_SC_CMD_DRAIN_OUTPUT, buffer,
525
&length, 60.0);
526
printf("CUPS_SC_CMD_DRAIN_OUTPUT returned %s\n", statuses[scstatus]);
527
528
length = 1;
529
scstatus = cupsSideChannelDoRequest(CUPS_SC_CMD_GET_BIDI, buffer,
530
&length, 5.0);
531
printf("CUPS_SC_CMD_GET_BIDI returned %s, %d\n", statuses[scstatus], buffer[0]);
532
533
length = sizeof(buffer) - 1;
534
scstatus = cupsSideChannelDoRequest(CUPS_SC_CMD_GET_DEVICE_ID, buffer,
535
&length, 5.0);
536
buffer[length] = '\0';
537
printf("CUPS_SC_CMD_GET_DEVICE_ID returned %s, \"%s\"\n",
538
statuses[scstatus], buffer);
539
540
length = 1;
541
scstatus = cupsSideChannelDoRequest(CUPS_SC_CMD_GET_STATE, buffer,
542
&length, 5.0);
543
printf("CUPS_SC_CMD_GET_STATE returned %s, %02X\n", statuses[scstatus],
544
buffer[0] & 255);
545
546
if (do_walk)
547
{
548
/*
549
* Walk the OID tree...
550
*/
551
552
scstatus = cupsSideChannelSNMPWalk(oid, 5.0, walk_cb, NULL);
553
printf("CUPS_SC_CMD_SNMP_WALK returned %s\n", statuses[scstatus]);
554
}
555
else
556
{
557
/*
558
* Lookup the same OID twice...
559
*/
560
561
length = sizeof(buffer);
562
scstatus = cupsSideChannelSNMPGet(oid, buffer, &length, 5.0);
563
printf("CUPS_SC_CMD_SNMP_GET %s returned %s, %d bytes (%s)\n", oid,
564
statuses[scstatus], (int)length, buffer);
565
566
length = sizeof(buffer);
567
scstatus = cupsSideChannelSNMPGet(oid, buffer, &length, 5.0);
568
printf("CUPS_SC_CMD_SNMP_GET %s returned %s, %d bytes (%s)\n", oid,
569
statuses[scstatus], (int)length, buffer);
570
}
571
572
length = 0;
573
scstatus = cupsSideChannelDoRequest(CUPS_SC_CMD_SOFT_RESET, buffer,
574
&length, 5.0);
575
printf("CUPS_SC_CMD_SOFT_RESET returned %s\n", statuses[scstatus]);
576
}
577
578
if (do_cancel)
579
{
580
sleep(1);
581
kill(data_pid, SIGTERM);
582
kill(back_pid, SIGTERM);
583
}
584
585
while ((pid = wait(&status)) > 0)
586
{
587
if (status)
588
{
589
if (WIFEXITED(status))
590
printf("%s exited with status %d!\n",
591
pid == back_pid ? backend : "test",
592
WEXITSTATUS(status));
593
else
594
printf("%s crashed with signal %d!\n",
595
pid == back_pid ? backend : "test",
596
WTERMSIG(status));
597
}
598
}
599
600
/*
601
* Exit accordingly...
602
*/
603
604
return (status != 0);
605
}
606
607
608
/*
609
* 'sigterm_handler()' - Flag when we get SIGTERM.
610
*/
611
612
static void
613
sigterm_handler(int sig) /* I - Signal */
614
{
615
(void)sig;
616
617
job_canceled = 1;
618
}
619
620
621
/*
622
* 'usage()' - Show usage information.
623
*/
624
625
static void
626
usage(void)
627
{
628
puts("Usage: testbackend [-cancel] [-d] [-ps | -pcl] [-s [-get OID] "
629
"[-walk OID]] [-t] device-uri job-id user title copies options [file]");
630
puts("");
631
puts("Options:");
632
puts(" -cancel Simulate a canceled print job after 2 seconds.");
633
puts(" -d Show log messages from backend.");
634
puts(" -get OID Lookup the specified SNMP OID.");
635
puts(" (.1.3.6.1.2.1.43.10.2.1.4.1.1 is a good one for printers)");
636
puts(" -pcl Send PCL+PJL query and test page to backend.");
637
puts(" -ps Send PostScript query and test page to backend.");
638
puts(" -s Do side-channel + SNMP tests.");
639
puts(" -t Send spaces slowly to backend ('trickle').");
640
puts(" -walk OID Walk the specified SNMP OID.");
641
puts(" (.1.3.6.1.2.1.43 is a good one for printers)");
642
643
exit(1);
644
}
645
646
647
/*
648
* 'walk_cb()' - Show results of cupsSideChannelSNMPWalk...
649
*/
650
651
static void
652
walk_cb(const char *oid, /* I - OID */
653
const char *data, /* I - Data */
654
int datalen, /* I - Length of data */
655
void *context) /* I - Context (unused) */
656
{
657
char temp[80];
658
659
(void)context;
660
661
if ((size_t)datalen > (sizeof(temp) - 1))
662
{
663
memcpy(temp, data, sizeof(temp) - 1);
664
temp[sizeof(temp) - 1] = '\0';
665
}
666
else
667
{
668
memcpy(temp, data, (size_t)datalen);
669
temp[datalen] = '\0';
670
}
671
672
printf("CUPS_SC_CMD_SNMP_WALK %s, %d bytes (%s)\n", oid, datalen, temp);
673
}
674
675