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