1 /*
2  *   Parallel port backend for OpenPrinting CUPS Filters.
3  *
4  *   Copyright 2007-2011 by Apple Inc.
5  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
6  *
7  *   These coded instructions, statements, and computer programs are the
8  *   property of Apple Inc. and are protected by Federal copyright
9  *   law.  Distribution and use rights are outlined in the file "COPYING"
10  *   which should have been included with this file.
11  *
12  * Contents:
13  *
14  *   main()         - Send a file to the specified parallel port.
15  *   drain_output() - Drain pending print data to the device.
16  *   list_devices() - List all parallel devices.
17  *   run_loop()     - Read and write print and back-channel data.
18  *   side_cb()      - Handle side-channel requests...
19  */
20 
21 /*
22  * Include necessary headers.
23  */
24 
25 #include "backend-private.h"
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <termios.h>
29 #include <sys/socket.h>
30 
31 
32 /*
33  * Local functions...
34  */
35 
36 static int	drain_output(int print_fd, int device_fd);
37 static void	list_devices(void);
38 static ssize_t	run_loop(int print_fd, int device_fd, int use_bc,
39 		         int update_state);
40 static int	side_cb(int print_fd, int device_fd, int use_bc);
41 
42 
43 /*
44  * 'main()' - Send a file to the specified parallel port.
45  *
46  * Usage:
47  *
48  *    printer-uri job-id user title copies options [file]
49  */
50 
51 int					/* O - Exit status */
main(int argc,char * argv[])52 main(int  argc,				/* I - Number of command-line arguments (6 or 7) */
53      char *argv[])			/* I - Command-line arguments */
54 {
55   char		method[255],		/* Method in URI */
56 		hostname[1024],		/* Hostname */
57 		username[255],		/* Username info (not used) */
58 		resource[1024],		/* Resource info (device and options) */
59 		*options;		/* Pointer to options */
60   int		port;			/* Port number (not used) */
61   int		print_fd,		/* Print file */
62 		device_fd,		/* Parallel device */
63 		use_bc;			/* Read back-channel data? */
64   int		copies;			/* Number of copies to print */
65   ssize_t	tbytes;			/* Total number of bytes written */
66   struct termios opts;			/* Parallel port options */
67 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
68   struct sigaction action;		/* Actions for POSIX signals */
69 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
70 
71 
72  /*
73   * Make sure status messages are not buffered...
74   */
75 
76   setbuf(stderr, NULL);
77 
78  /*
79   * Ignore SIGPIPE signals...
80   */
81 
82 #ifdef HAVE_SIGSET
83   sigset(SIGPIPE, SIG_IGN);
84 #elif defined(HAVE_SIGACTION)
85   memset(&action, 0, sizeof(action));
86   action.sa_handler = SIG_IGN;
87   sigaction(SIGPIPE, &action, NULL);
88 #else
89   signal(SIGPIPE, SIG_IGN);
90 #endif /* HAVE_SIGSET */
91 
92  /*
93   * Check command-line...
94   */
95 
96   if (argc == 1)
97   {
98     list_devices();
99     return (CUPS_BACKEND_OK);
100   }
101   else if (argc < 6 || argc > 7)
102   {
103     fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
104             argv[0]);
105     return (CUPS_BACKEND_FAILED);
106   }
107 
108  /*
109   * If we have 7 arguments, print the file named on the command-line.
110   * Otherwise, send stdin instead...
111   */
112 
113   if (argc == 6)
114   {
115     print_fd = 0;
116     copies   = 1;
117   }
118   else
119   {
120    /*
121     * Try to open the print file...
122     */
123 
124     if ((print_fd = open(argv[6], O_RDONLY)) < 0)
125     {
126       perror("ERROR: Unable to open print file");
127       return (CUPS_BACKEND_FAILED);
128     }
129 
130     copies = atoi(argv[4]);
131   }
132 
133  /*
134   * Extract the device name and options from the URI...
135   */
136 
137   httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
138                   method, sizeof(method), username, sizeof(username),
139 		  hostname, sizeof(hostname), &port,
140 		  resource, sizeof(resource));
141 
142  /*
143   * See if there are any options...
144   */
145 
146   if ((options = strchr(resource, '?')) != NULL)
147   {
148    /*
149     * Yup, terminate the device name string and move to the first
150     * character of the options...
151     */
152 
153     *options++ = '\0';
154   }
155 
156  /*
157   * Open the parallel port device...
158   */
159 
160   fputs("STATE: +connecting-to-device\n", stderr);
161 
162   do
163   {
164 #if defined(__linux) || defined(__FreeBSD__)
165    /*
166     * The Linux and FreeBSD parallel port drivers currently are broken WRT
167     * select() and bidirection I/O...
168     */
169 
170     device_fd = open(resource, O_WRONLY | O_EXCL);
171     use_bc    = 0;
172 
173 #else
174     if ((device_fd = open(resource, O_RDWR | O_EXCL)) < 0)
175     {
176       device_fd = open(resource, O_WRONLY | O_EXCL);
177       use_bc    = 0;
178     }
179     else
180       use_bc = 1;
181 #endif /* __linux || __FreeBSD__ */
182 
183     if (device_fd == -1)
184     {
185       if (getenv("CLASS") != NULL)
186       {
187        /*
188         * If the CLASS environment variable is set, the job was submitted
189 	* to a class and not to a specific queue.  In this case, we want
190 	* to abort immediately so that the job can be requeued on the next
191 	* available printer in the class.
192 	*/
193 
194         fputs("INFO: Unable to contact printer, queuing on next printer in "
195               "class.\n", stderr);
196 
197        /*
198         * Sleep 5 seconds to keep the job from requeuing too rapidly...
199 	*/
200 
201 	sleep(5);
202 
203         return (CUPS_BACKEND_FAILED);
204       }
205 
206       if (errno == EBUSY)
207       {
208         fputs("INFO: Printer busy; will retry in 30 seconds.\n", stderr);
209 	sleep(30);
210       }
211       else if (errno == ENXIO || errno == EIO || errno == ENOENT)
212       {
213         fputs("INFO: Printer not connected; will retry in 30 seconds.\n",
214               stderr);
215 	sleep(30);
216       }
217       else
218       {
219 	perror("ERROR: Unable to open parallel port");
220 	return (CUPS_BACKEND_FAILED);
221       }
222     }
223   }
224   while (device_fd < 0);
225 
226   fputs("STATE: -connecting-to-device\n", stderr);
227 
228  /*
229   * Set any options provided...
230   */
231 
232   tcgetattr(device_fd, &opts);
233 
234   opts.c_lflag &= ~(ICANON | ECHO | ISIG);	/* Raw mode */
235 
236   /**** No options supported yet ****/
237 
238   tcsetattr(device_fd, TCSANOW, &opts);
239 
240  /*
241   * Finally, send the print file...
242   */
243 
244   tbytes = 0;
245 
246   while (copies > 0 && tbytes >= 0)
247   {
248     copies --;
249 
250     if (print_fd != 0)
251     {
252       fputs("PAGE: 1 1\n", stderr);
253       lseek(print_fd, 0, SEEK_SET);
254     }
255 
256     tbytes = run_loop(print_fd, device_fd, use_bc, 1);
257 
258     if (print_fd != 0 && tbytes >= 0)
259       fputs("INFO: Print file sent.\n", stderr);
260   }
261 
262  /*
263   * Close the socket connection and input file and return...
264   */
265 
266   close(device_fd);
267 
268   if (print_fd != 0)
269     close(print_fd);
270 
271   return (CUPS_BACKEND_OK);
272 }
273 
274 
275 /*
276  * 'drain_output()' - Drain pending print data to the device.
277  */
278 
279 static int				/* O - 0 on success, -1 on error */
drain_output(int print_fd,int device_fd)280 drain_output(int print_fd,		/* I - Print file descriptor */
281              int device_fd)		/* I - Device file descriptor */
282 {
283   int		nfds;			/* Maximum file descriptor value + 1 */
284   fd_set	input;			/* Input set for reading */
285   ssize_t	print_bytes,		/* Print bytes read */
286 		bytes;			/* Bytes written */
287   char		print_buffer[8192],	/* Print data buffer */
288 		*print_ptr;		/* Pointer into print data buffer */
289   struct timeval timeout;		/* Timeout for read... */
290 
291 
292  /*
293   * Figure out the maximum file descriptor value to use with select()...
294   */
295 
296   nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
297 
298  /*
299   * Now loop until we are out of data from print_fd...
300   */
301 
302   for (;;)
303   {
304    /*
305     * Use select() to determine whether we have data to copy around...
306     */
307 
308     FD_ZERO(&input);
309     FD_SET(print_fd, &input);
310 
311     timeout.tv_sec  = 0;
312     timeout.tv_usec = 0;
313 
314     if (select(nfds, &input, NULL, NULL, &timeout) < 0)
315       return (-1);
316 
317     if (!FD_ISSET(print_fd, &input))
318       return (0);
319 
320     if ((print_bytes = read(print_fd, print_buffer,
321 			    sizeof(print_buffer))) < 0)
322     {
323      /*
324       * Read error - bail if we don't see EAGAIN or EINTR...
325       */
326 
327       if (errno != EAGAIN && errno != EINTR)
328       {
329         perror("ERROR: Unable to read print data");
330 	return (-1);
331       }
332 
333       print_bytes = 0;
334     }
335     else if (print_bytes == 0)
336     {
337      /*
338       * End of file, return...
339       */
340 
341       return (0);
342     }
343 
344     fprintf(stderr, "DEBUG: Read %d bytes of print data.\n",
345 	    (int)print_bytes);
346 
347     for (print_ptr = print_buffer; print_bytes > 0;)
348     {
349       if ((bytes = write(device_fd, print_ptr, print_bytes)) < 0)
350       {
351        /*
352         * Write error - bail if we don't see an error we can retry...
353 	*/
354 
355         if (errno != ENOSPC && errno != ENXIO && errno != EAGAIN &&
356 	    errno != EINTR && errno != ENOTTY)
357 	{
358 	  perror("ERROR: Unable to write print data");
359 	  return (-1);
360 	}
361       }
362       else
363       {
364         fprintf(stderr, "DEBUG: Wrote %d bytes of print data.\n", (int)bytes);
365 
366         print_bytes -= bytes;
367 	print_ptr   += bytes;
368       }
369     }
370   }
371 }
372 
373 
374 /*
375  * 'list_devices()' - List all parallel devices.
376  */
377 
378 static void
list_devices(void)379 list_devices(void)
380 {
381 #ifdef __sun
382   static char	*funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
383 				/* Funky hex numbering used for some devices */
384 #endif /* __sun */
385 
386 #ifdef __linux
387   int	i;			/* Looping var */
388   int	fd;			/* File descriptor */
389   char	device[512],		/* Device filename */
390 	basedevice[255],	/* Base device filename for ports */
391 	device_id[1024],	/* Device ID string */
392 	make_model[1024],	/* Make and model */
393 	info[2048],		/* Info string */
394 	uri[1024];		/* Device URI */
395 
396 
397   if (!access("/dev/parallel/", 0))
398     strcpy(basedevice, "/dev/parallel/");
399   else if (!access("/dev/printers/", 0))
400     strcpy(basedevice, "/dev/printers/");
401   else
402     strcpy(basedevice, "/dev/lp");
403 
404   for (i = 0; i < 4; i ++)
405   {
406    /*
407     * Open the port, if available...
408     */
409 
410     sprintf(device, "%s%d", basedevice, i);
411     if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
412       fd = open(device, O_WRONLY);
413 
414     if (fd >= 0)
415     {
416      /*
417       * Now grab the IEEE 1284 device ID string...
418       */
419 
420       snprintf(uri, sizeof(uri), "parallel:%s", device);
421 
422       if (!backendGetDeviceID(fd, device_id, sizeof(device_id),
423                               make_model, sizeof(make_model),
424 			      NULL, uri, sizeof(uri)))
425       {
426         snprintf(info, sizeof(info), "%s LPT #%d", make_model, i + 1);
427 	cupsBackendReport("direct", uri, make_model, info, device_id, NULL);
428       }
429       else
430       {
431         snprintf(info, sizeof(info), "LPT #%d", i + 1);
432 	cupsBackendReport("direct", uri, NULL, info, NULL, NULL);
433       }
434 
435       close(fd);
436     }
437   }
438 #elif defined(__sun)
439   int		i, j, n;	/* Looping vars */
440   char		device[255];	/* Device filename */
441 
442 
443  /*
444   * Standard parallel ports...
445   */
446 
447   for (i = 0; i < 10; i ++)
448   {
449     sprintf(device, "/dev/ecpp%d", i);
450     if (access(device, 0) == 0)
451       printf("direct parallel:%s \"Unknown\" \"Sun IEEE-1284 Parallel Port #%d\"\n",
452              device, i + 1);
453   }
454 
455   for (i = 0; i < 10; i ++)
456   {
457     sprintf(device, "/dev/bpp%d", i);
458     if (access(device, 0) == 0)
459       printf("direct parallel:%s \"Unknown\" \"Sun Standard Parallel Port #%d\"\n",
460              device, i + 1);
461   }
462 
463   for (i = 0; i < 3; i ++)
464   {
465     sprintf(device, "/dev/lp%d", i);
466 
467     if (access(device, 0) == 0)
468       printf("direct parallel:%s \"Unknown\" \"PC Parallel Port #%d\"\n",
469              device, i + 1);
470   }
471 
472  /*
473   * MAGMA parallel ports...
474   */
475 
476   for (i = 0; i < 40; i ++)
477   {
478     sprintf(device, "/dev/pm%02d", i);
479     if (access(device, 0) == 0)
480       printf("direct parallel:%s \"Unknown\" \"MAGMA Parallel Board #%d Port #%d\"\n",
481              device, (i / 10) + 1, (i % 10) + 1);
482   }
483 
484  /*
485   * Central Data parallel ports...
486   */
487 
488   for (i = 0; i < 9; i ++)
489     for (j = 0; j < 8; j ++)
490       for (n = 0; n < 32; n ++)
491       {
492         if (i == 8)	/* EtherLite */
493           sprintf(device, "/dev/sts/lpN%d%c", j, funky_hex[n]);
494         else
495           sprintf(device, "/dev/sts/lp%c%d%c", i + 'C', j,
496                   funky_hex[n]);
497 
498 	if (access(device, 0) == 0)
499 	{
500 	  if (i == 8)
501 	    printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
502 	           device, j, n);
503   	  else
504 	    printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
505 	           device, i, j, n);
506 	}
507       }
508 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
509   int	i;			/* Looping var */
510   int	fd;			/* File descriptor */
511   char	device[255];		/* Device filename */
512 
513 
514   for (i = 0; i < 3; i ++)
515   {
516     sprintf(device, "/dev/lpt%d", i);
517     if ((fd = open(device, O_WRONLY)) >= 0)
518     {
519       close(fd);
520       printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (interrupt-driven)\"\n", device, i + 1);
521     }
522 
523     sprintf(device, "/dev/lpa%d", i);
524     if ((fd = open(device, O_WRONLY)) >= 0)
525     {
526       close(fd);
527       printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (polled)\"\n", device, i + 1);
528     }
529   }
530 #endif
531 }
532 
533 
534 /*
535  * 'run_loop()' - Read and write print and back-channel data.
536  */
537 
538 static ssize_t				/* O - Total bytes on success, -1 on error */
run_loop(int print_fd,int device_fd,int use_bc,int update_state)539 run_loop(int print_fd,			/* I - Print file descriptor */
540 	int device_fd,			/* I - Device file descriptor */
541 	int use_bc,			/* I - Use back-channel? */
542 	int update_state)		/* I - Update printer-state-reasons? */
543 {
544   int		nfds;			/* Maximum file descriptor value + 1 */
545   fd_set	input,			/* Input set for reading */
546 		output;			/* Output set for writing */
547   ssize_t	print_bytes,		/* Print bytes read */
548 		bc_bytes,		/* Backchannel bytes read */
549 		total_bytes,		/* Total bytes written */
550 		bytes;			/* Bytes written */
551   int		paperout;		/* "Paper out" status */
552   int		offline;		/* "Off-line" status */
553   char		print_buffer[8192],	/* Print data buffer */
554 		*print_ptr,		/* Pointer into print data buffer */
555 		bc_buffer[1024];	/* Back-channel data buffer */
556   struct timeval timeout;		/* Timeout for select() */
557   int           sc_ok;                  /* Flag a side channel error and
558 					   stop using the side channel
559 					   in such a case. */
560 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
561   struct sigaction action;		/* Actions for POSIX signals */
562 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
563 
564 
565  /*
566   * If we are printing data from a print driver on stdin, ignore SIGTERM
567   * so that the driver can finish out any page data, e.g. to eject the
568   * current page.  We only do this for stdin printing as otherwise there
569   * is no way to cancel a raw print job...
570   */
571 
572   if (!print_fd)
573   {
574 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
575     sigset(SIGTERM, SIG_IGN);
576 #elif defined(HAVE_SIGACTION)
577     memset(&action, 0, sizeof(action));
578 
579     sigemptyset(&action.sa_mask);
580     action.sa_handler = SIG_IGN;
581     sigaction(SIGTERM, &action, NULL);
582 #else
583     signal(SIGTERM, SIG_IGN);
584 #endif /* HAVE_SIGSET */
585   }
586   else if (print_fd < 0)
587   {
588    /*
589     * Copy print data from stdin, but don't mess with the signal handlers...
590     */
591 
592     print_fd = 0;
593   }
594 
595  /*
596   * Figure out the maximum file descriptor value to use with select()...
597   */
598 
599   nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
600 
601 
602  /*
603   * Side channel is OK...
604   */
605 
606   sc_ok = 1;
607 
608  /*
609   * Now loop until we are out of data from print_fd...
610   */
611 
612   for (print_bytes = 0, print_ptr = print_buffer, offline = -1,
613            paperout = -1, total_bytes = 0;;)
614   {
615    /*
616     * Use select() to determine whether we have data to copy around...
617     */
618 
619     FD_ZERO(&input);
620     if (!print_bytes)
621       FD_SET(print_fd, &input);
622     if (use_bc)
623       FD_SET(device_fd, &input);
624     if (!print_bytes && sc_ok)
625       FD_SET(CUPS_SC_FD, &input);
626 
627     FD_ZERO(&output);
628     if (print_bytes)
629       FD_SET(device_fd, &output);
630 
631     timeout.tv_sec  = 5;
632     timeout.tv_usec = 0;
633 
634     if (select(nfds, &input, &output, NULL, &timeout) < 0)
635     {
636      /*
637       * Pause printing to clear any pending errors...
638       */
639 
640       if (errno == ENXIO && offline != 1 && update_state)
641       {
642 	fputs("STATE: +offline-report\n", stderr);
643 	offline = 1;
644       }
645       else if (errno == EINTR && total_bytes == 0)
646       {
647 	fputs("DEBUG: Received an interrupt before any bytes were "
648 	      "written, aborting.\n", stderr);
649 	return (0);
650       }
651 
652       sleep(1);
653       continue;
654     }
655 
656    /*
657     * Check if we have a side-channel request ready...
658     */
659 
660     if (sc_ok && FD_ISSET(CUPS_SC_FD, &input))
661     {
662      /*
663       * Do the side-channel request, then start back over in the select
664       * loop since it may have read from print_fd...
665       *
666       * If the side channel processing errors, go straight on to avoid
667       * blocking of the backend by side channel problems, deactivate the side
668       * channel.
669       */
670 
671       if (side_cb(print_fd, device_fd, use_bc))
672 	sc_ok = 0;
673       continue;
674     }
675 
676    /*
677     * Check if we have back-channel data ready...
678     */
679 
680     if (FD_ISSET(device_fd, &input))
681     {
682       if ((bc_bytes = read(device_fd, bc_buffer, sizeof(bc_buffer))) > 0)
683       {
684 	fprintf(stderr, "DEBUG: Received %d bytes of back-channel data.\n",
685 	        (int)bc_bytes);
686         cupsBackChannelWrite(bc_buffer, bc_bytes, 1.0);
687       }
688       else if (bc_bytes < 0 && errno != EAGAIN && errno != EINTR)
689       {
690         perror("DEBUG: Error reading back-channel data");
691 	use_bc = 0;
692       }
693       else if (bc_bytes == 0)
694         use_bc = 0;
695     }
696 
697    /*
698     * Check if we have print data ready...
699     */
700 
701     if (FD_ISSET(print_fd, &input))
702     {
703       if ((print_bytes = read(print_fd, print_buffer,
704                               sizeof(print_buffer))) < 0)
705       {
706        /*
707         * Read error - bail if we don't see EAGAIN or EINTR...
708 	*/
709 
710 	if (errno != EAGAIN && errno != EINTR)
711 	{
712 	  perror("ERROR: Unable to read print data");
713 	  return (-1);
714 	}
715 
716         print_bytes = 0;
717       }
718       else if (print_bytes == 0)
719       {
720        /*
721         * End of file, break out of the loop...
722 	*/
723 
724         break;
725       }
726 
727       print_ptr = print_buffer;
728 
729       fprintf(stderr, "DEBUG: Read %d bytes of print data.\n",
730               (int)print_bytes);
731     }
732 
733    /*
734     * Check if the device is ready to receive data and we have data to
735     * send...
736     */
737 
738     if (print_bytes && FD_ISSET(device_fd, &output))
739     {
740       if ((bytes = write(device_fd, print_ptr, print_bytes)) < 0)
741       {
742        /*
743         * Write error - bail if we don't see an error we can retry...
744 	*/
745 
746         if (errno == ENOSPC)
747 	{
748 	  if (paperout != 1 && update_state)
749 	  {
750 	    fputs("STATE: +media-empty-warning\n", stderr);
751 	    paperout = 1;
752 	  }
753         }
754 	else if (errno == ENXIO)
755 	{
756 	  if (offline != 1 && update_state)
757 	  {
758 	    fputs("STATE: +offline-report\n", stderr);
759 	    offline = 1;
760 	  }
761 	}
762 	else if (errno != EAGAIN && errno != EINTR && errno != ENOTTY)
763 	{
764 	  perror("ERROR: Unable to write print data");
765 	  return (-1);
766 	}
767       }
768       else
769       {
770         if (paperout && update_state)
771 	{
772 	  fputs("STATE: -media-empty-warning\n", stderr);
773 	  paperout = 0;
774 	}
775 
776 	if (offline && update_state)
777 	{
778 	  fputs("STATE: -offline-report\n", stderr);
779 	  offline = 0;
780 	}
781 
782         fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
783 
784         print_bytes -= bytes;
785 	print_ptr   += bytes;
786 	total_bytes += bytes;
787       }
788     }
789   }
790 
791  /*
792   * Return with success...
793   */
794 
795   return (total_bytes);
796 }
797 
798 
799 /*
800  * 'side_cb()' - Handle side-channel requests...
801  */
802 
803 static int				/* O - 0 on success, -1 on error */
side_cb(int print_fd,int device_fd,int use_bc)804 side_cb(int         print_fd,		/* I - Print file */
805         int         device_fd,		/* I - Device file */
806 	int         use_bc)		/* I - Using back-channel? */
807 {
808   cups_sc_command_t	command;	/* Request command */
809   cups_sc_status_t	status;		/* Request/response status */
810   char			data[2048];	/* Request/response data */
811   int			datalen;	/* Request/response data size */
812 
813 
814   datalen = sizeof(data);
815 
816   if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
817     return (-1);
818 
819   switch (command)
820   {
821     case CUPS_SC_CMD_DRAIN_OUTPUT :
822         if (drain_output(print_fd, device_fd))
823 	  status = CUPS_SC_STATUS_IO_ERROR;
824 	else if (tcdrain(device_fd))
825 	  status = CUPS_SC_STATUS_IO_ERROR;
826 	else
827 	  status = CUPS_SC_STATUS_OK;
828 
829 	datalen = 0;
830         break;
831 
832     case CUPS_SC_CMD_GET_BIDI :
833 	status  = CUPS_SC_STATUS_OK;
834         data[0] = use_bc;
835         datalen = 1;
836         break;
837 
838     case CUPS_SC_CMD_GET_DEVICE_ID :
839         memset(data, 0, sizeof(data));
840 
841         if (backendGetDeviceID(device_fd, data, sizeof(data) - 1,
842 	                       NULL, 0, NULL, NULL, 0))
843         {
844 	  status  = CUPS_SC_STATUS_NOT_IMPLEMENTED;
845 	  datalen = 0;
846 	}
847 	else
848         {
849 	  status  = CUPS_SC_STATUS_OK;
850 	  datalen = strlen(data);
851 	}
852         break;
853 
854     default :
855         status  = CUPS_SC_STATUS_NOT_IMPLEMENTED;
856 	datalen = 0;
857 	break;
858   }
859 
860   return (cupsSideChannelWrite(command, status, data, datalen, 1.0));
861 }
862