• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
main(int argc,char * argv[])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
sigterm_handler(int sig)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
usage(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
walk_cb(const char * oid,const char * data,int datalen,void * context)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 }
673