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