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