1 /*
2  *   Serial port backend for CUPS.
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 printer or server.
15  *   list_devices() - List all serial devices.
16  *   side_cb()      - Handle side-channel requests...
17  */
18 
19 /*
20  * Include necessary headers.
21  */
22 
23 #include "backend-private.h"
24 #include <stdio.h>
25 
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <termios.h>
29 #include <sys/select.h>
30 #ifdef HAVE_SYS_IOCTL_H
31 #  include <sys/ioctl.h>
32 #endif /* HAVE_SYS_IOCTL_H */
33 
34 #ifndef CRTSCTS
35 #  ifdef CNEW_RTSCTS
36 #    define CRTSCTS CNEW_RTSCTS
37 #  else
38 #    define CRTSCTS 0
39 #  endif /* CNEW_RTSCTS */
40 #endif /* !CRTSCTS */
41 
42 #if defined(__APPLE__)
43 #  include <CoreFoundation/CoreFoundation.h>
44 #  include <IOKit/IOKitLib.h>
45 #  include <IOKit/serial/IOSerialKeys.h>
46 #  include <IOKit/IOBSD.h>
47 #endif /* __APPLE__ */
48 
49 #if defined(__linux) && defined(TIOCGSERIAL)
50 #  include <linux/serial.h>
51 #  include <linux/ioctl.h>
52 #endif /* __linux && TIOCGSERIAL */
53 
54 
55 /*
56  * Local functions...
57  */
58 
59 static int	drain_output(int print_fd, int device_fd);
60 static void	list_devices(void);
61 static int	side_cb(int print_fd, int device_fd, int use_bc);
62 
63 
64 /*
65  * 'main()' - Send a file to the printer or server.
66  *
67  * Usage:
68  *
69  *    printer-uri job-id user title copies options [file]
70  */
71 
72 int					/* O - Exit status */
main(int argc,char * argv[])73 main(int  argc,				/* I - Number of command-line arguments (6 or 7) */
74      char *argv[])			/* I - Command-line arguments */
75 {
76   char		method[255],		/* Method in URI */
77 		hostname[1024],		/* Hostname */
78 		username[255],		/* Username info (not used) */
79 		resource[1024],		/* Resource info (device and options) */
80 		*options,		/* Pointer to options */
81 		*name,			/* Name of option */
82 		*value,			/* Value of option */
83 		sep;			/* Option separator */
84   int		port;			/* Port number (not used) */
85   int		copies;			/* Number of copies to print */
86   int		side_eof = 0,		/* Saw EOF on side-channel? */
87 		print_fd,		/* Print file */
88 		device_fd;		/* Serial device */
89   int		nfds;			/* Maximum file descriptor value + 1 */
90   fd_set	input,			/* Input set for reading */
91 		output;			/* Output set for writing */
92   ssize_t	print_bytes,		/* Print bytes read */
93 		bc_bytes,		/* Backchannel bytes read */
94 		total_bytes,		/* Total bytes written */
95 		bytes;			/* Bytes written */
96   int		dtrdsr;			/* Do dtr/dsr flow control? */
97   int		print_size;		/* Size of output buffer for writes */
98   char		print_buffer[8192],	/* Print data buffer */
99 		*print_ptr,		/* Pointer into print data buffer */
100 		bc_buffer[1024];	/* Back-channel data buffer */
101   struct termios opts;			/* Serial port options */
102   struct termios origopts;		/* Original port options */
103 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
104   struct sigaction action;		/* Actions for POSIX signals */
105 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
106 
107 
108  /*
109   * Make sure status messages are not buffered...
110   */
111 
112   setbuf(stderr, NULL);
113 
114  /*
115   * Ignore SIGPIPE signals...
116   */
117 
118 #ifdef HAVE_SIGSET
119   sigset(SIGPIPE, SIG_IGN);
120 #elif defined(HAVE_SIGACTION)
121   memset(&action, 0, sizeof(action));
122   action.sa_handler = SIG_IGN;
123   sigaction(SIGPIPE, &action, NULL);
124 #else
125   signal(SIGPIPE, SIG_IGN);
126 #endif /* HAVE_SIGSET */
127 
128  /*
129   * Check command-line...
130   */
131 
132   if (argc == 1)
133   {
134     list_devices();
135     return (CUPS_BACKEND_OK);
136   }
137   else if (argc < 6 || argc > 7)
138   {
139     fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
140 	    argv[0]);
141     return (CUPS_BACKEND_FAILED);
142   }
143 
144  /*
145   * If we have 7 arguments, print the file named on the command-line.
146   * Otherwise, send stdin instead...
147   */
148 
149   if (argc == 6)
150   {
151     print_fd = 0;
152     copies   = 1;
153   }
154   else
155   {
156    /*
157     * Try to open the print file...
158     */
159 
160     if ((print_fd = open(argv[6], O_RDONLY)) < 0)
161     {
162       perror("ERROR: Unable to open print file");
163       return (CUPS_BACKEND_FAILED);
164     }
165 
166     copies = atoi(argv[4]);
167   }
168 
169  /*
170   * Extract the device name and options from the URI...
171   */
172 
173   httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
174                   method, sizeof(method), username, sizeof(username),
175 		  hostname, sizeof(hostname), &port,
176 		  resource, sizeof(resource));
177 
178  /*
179   * See if there are any options...
180   */
181 
182   if ((options = strchr(resource, '?')) != NULL)
183   {
184    /*
185     * Yup, terminate the device name string and move to the first
186     * character of the options...
187     */
188 
189     *options++ = '\0';
190   }
191 
192  /*
193   * Open the serial port device...
194   */
195 
196   fputs("STATE: +connecting-to-device\n", stderr);
197 
198   do
199   {
200     if ((device_fd = open(resource, O_RDWR | O_NOCTTY | O_EXCL |
201                                     O_NDELAY)) == -1)
202     {
203       if (getenv("CLASS") != NULL)
204       {
205        /*
206         * If the CLASS environment variable is set, the job was submitted
207 	* to a class and not to a specific queue.  In this case, we want
208 	* to abort immediately so that the job can be requeued on the next
209 	* available printer in the class.
210 	*/
211 
212         fputs("INFO: Unable to contact printer, queuing on next printer in "
213               "class.\n", stderr);
214 
215        /*
216         * Sleep 5 seconds to keep the job from requeuing too rapidly...
217 	*/
218 
219 	sleep(5);
220 
221         return (CUPS_BACKEND_FAILED);
222       }
223 
224       if (errno == EBUSY)
225       {
226         fputs("INFO: Printer busy; will retry in 30 seconds.\n", stderr);
227 	sleep(30);
228       }
229       else
230       {
231 	perror("ERROR: Unable to open serial port");
232 	return (CUPS_BACKEND_FAILED);
233       }
234     }
235   }
236   while (device_fd < 0);
237 
238   fputs("STATE: -connecting-to-device\n", stderr);
239 
240  /*
241   * Set any options provided...
242   */
243 
244   tcgetattr(device_fd, &origopts);
245   tcgetattr(device_fd, &opts);
246 
247   opts.c_lflag &= ~(ICANON | ECHO | ISIG);
248 					/* Raw mode */
249   opts.c_oflag &= ~OPOST;		/* Don't post-process */
250 
251   print_size = 96;			/* 9600 baud / 10 bits/char / 10Hz */
252   dtrdsr     = 0;			/* No dtr/dsr flow control */
253 
254   if (options)
255   {
256     while (*options)
257     {
258      /*
259       * Get the name...
260       */
261 
262       name = options;
263 
264       while (*options && *options != '=' && *options != '+' && *options != '&')
265         options ++;
266 
267       if ((sep = *options) != '\0')
268         *options++ = '\0';
269 
270       if (sep == '=')
271       {
272        /*
273         * Get the value...
274 	*/
275 
276         value = options;
277 
278 	while (*options && *options != '+' && *options != '&')
279 	  options ++;
280 
281         if (*options)
282 	  *options++ = '\0';
283       }
284       else
285         value = (char *)"";
286 
287      /*
288       * Process the option...
289       */
290 
291       if (!strcasecmp(name, "baud"))
292       {
293        /*
294         * Set the baud rate...
295 	*/
296 
297         print_size = atoi(value) / 100;
298 
299 #if B19200 == 19200
300         cfsetispeed(&opts, atoi(value));
301 	cfsetospeed(&opts, atoi(value));
302 #else
303         switch (atoi(value))
304 	{
305 	  case 1200 :
306 	      cfsetispeed(&opts, B1200);
307 	      cfsetospeed(&opts, B1200);
308 	      break;
309 	  case 2400 :
310 	      cfsetispeed(&opts, B2400);
311 	      cfsetospeed(&opts, B2400);
312 	      break;
313 	  case 4800 :
314 	      cfsetispeed(&opts, B4800);
315 	      cfsetospeed(&opts, B4800);
316 	      break;
317 	  case 9600 :
318 	      cfsetispeed(&opts, B9600);
319 	      cfsetospeed(&opts, B9600);
320 	      break;
321 	  case 19200 :
322 	      cfsetispeed(&opts, B19200);
323 	      cfsetospeed(&opts, B19200);
324 	      break;
325 	  case 38400 :
326 	      cfsetispeed(&opts, B38400);
327 	      cfsetospeed(&opts, B38400);
328 	      break;
329 #  ifdef B57600
330 	  case 57600 :
331 	      cfsetispeed(&opts, B57600);
332 	      cfsetospeed(&opts, B57600);
333 	      break;
334 #  endif /* B57600 */
335 #  ifdef B115200
336 	  case 115200 :
337 	      cfsetispeed(&opts, B115200);
338 	      cfsetospeed(&opts, B115200);
339 	      break;
340 #  endif /* B115200 */
341 #  ifdef B230400
342 	  case 230400 :
343 	      cfsetispeed(&opts, B230400);
344 	      cfsetospeed(&opts, B230400);
345 	      break;
346 #  endif /* B230400 */
347           default :
348 	      fprintf(stderr, "WARNING: Unsupported baud rate: %s\n", value);
349 	      break;
350 	}
351 #endif /* B19200 == 19200 */
352       }
353       else if (!strcasecmp(name, "bits"))
354       {
355        /*
356         * Set number of data bits...
357 	*/
358 
359         switch (atoi(value))
360 	{
361 	  case 7 :
362 	      opts.c_cflag &= ~CSIZE;
363               opts.c_cflag |= CS7;
364 	      opts.c_cflag |= PARENB;
365               opts.c_cflag &= ~PARODD;
366               break;
367 	  case 8 :
368 	      opts.c_cflag &= ~CSIZE;
369               opts.c_cflag |= CS8;
370 	      opts.c_cflag &= ~PARENB;
371 	      break;
372 	}
373       }
374       else if (!strcasecmp(name, "parity"))
375       {
376        /*
377 	* Set parity checking...
378 	*/
379 
380 	if (!strcasecmp(value, "even"))
381 	{
382 	  opts.c_cflag |= PARENB;
383           opts.c_cflag &= ~PARODD;
384 	}
385 	else if (!strcasecmp(value, "odd"))
386 	{
387 	  opts.c_cflag |= PARENB;
388           opts.c_cflag |= PARODD;
389 	}
390 	else if (!strcasecmp(value, "none"))
391 	  opts.c_cflag &= ~PARENB;
392 	else if (!strcasecmp(value, "space"))
393 	{
394 	 /*
395 	  * Note: we only support space parity with 7 bits per character...
396 	  */
397 
398 	  opts.c_cflag &= ~CSIZE;
399           opts.c_cflag |= CS8;
400 	  opts.c_cflag &= ~PARENB;
401         }
402 	else if (!strcasecmp(value, "mark"))
403 	{
404 	 /*
405 	  * Note: we only support mark parity with 7 bits per character
406 	  * and 1 stop bit...
407 	  */
408 
409 	  opts.c_cflag &= ~CSIZE;
410           opts.c_cflag |= CS7;
411 	  opts.c_cflag &= ~PARENB;
412           opts.c_cflag |= CSTOPB;
413         }
414       }
415       else if (!strcasecmp(name, "flow"))
416       {
417        /*
418 	* Set flow control...
419 	*/
420 
421 	if (!strcasecmp(value, "none"))
422 	{
423 	  opts.c_iflag &= ~(IXON | IXOFF);
424           opts.c_cflag &= ~CRTSCTS;
425 	}
426 	else if (!strcasecmp(value, "soft"))
427 	{
428 	  opts.c_iflag |= IXON | IXOFF;
429           opts.c_cflag &= ~CRTSCTS;
430 	}
431 	else if (!strcasecmp(value, "hard") ||
432 	         !strcasecmp(value, "rtscts"))
433         {
434 	  opts.c_iflag &= ~(IXON | IXOFF);
435           opts.c_cflag |= CRTSCTS;
436 	}
437 	else if (!strcasecmp(value, "dtrdsr"))
438 	{
439 	  opts.c_iflag &= ~(IXON | IXOFF);
440           opts.c_cflag &= ~CRTSCTS;
441 
442 	  dtrdsr = 1;
443 	}
444       }
445       else if (!strcasecmp(name, "stop"))
446       {
447         switch (atoi(value))
448 	{
449 	  case 1 :
450 	      opts.c_cflag &= ~CSTOPB;
451 	      break;
452 
453 	  case 2 :
454 	      opts.c_cflag |= CSTOPB;
455 	      break;
456 	}
457       }
458     }
459   }
460 
461   tcsetattr(device_fd, TCSANOW, &opts);
462   fcntl(device_fd, F_SETFL, 0);
463 
464  /*
465   * Now that we are "connected" to the port, ignore SIGTERM so that we
466   * can finish out any page data the driver sends (e.g. to eject the
467   * current page...  Only ignore SIGTERM if we are printing data from
468   * stdin (otherwise you can't cancel raw jobs...)
469   */
470 
471   if (!print_fd)
472   {
473 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
474     sigset(SIGTERM, SIG_IGN);
475 #elif defined(HAVE_SIGACTION)
476     memset(&action, 0, sizeof(action));
477 
478     sigemptyset(&action.sa_mask);
479     action.sa_handler = SIG_IGN;
480     sigaction(SIGTERM, &action, NULL);
481 #else
482     signal(SIGTERM, SIG_IGN);
483 #endif /* HAVE_SIGSET */
484   }
485 
486  /*
487   * Figure out the maximum file descriptor value to use with select()...
488   */
489 
490   nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
491 
492  /*
493   * Finally, send the print file.  Ordinarily we would just use the
494   * backendRunLoop() function, however since we need to use smaller
495   * writes and may need to do DSR/DTR flow control, we duplicate much
496   * of the code here instead...
497   */
498 
499   if (print_size > sizeof(print_buffer))
500     print_size = sizeof(print_buffer);
501 
502   total_bytes = 0;
503 
504   while (copies > 0)
505   {
506     copies --;
507 
508     if (print_fd != 0)
509     {
510       fputs("PAGE: 1 1\n", stderr);
511       lseek(print_fd, 0, SEEK_SET);
512     }
513 
514    /*
515     * Now loop until we are out of data from print_fd...
516     */
517 
518     for (print_bytes = 0, print_ptr = print_buffer;;)
519     {
520      /*
521       * Use select() to determine whether we have data to copy around...
522       */
523 
524       FD_ZERO(&input);
525       if (!print_bytes)
526 	FD_SET(print_fd, &input);
527       FD_SET(device_fd, &input);
528       if (!print_bytes && !side_eof)
529         FD_SET(CUPS_SC_FD, &input);
530 
531       FD_ZERO(&output);
532       if (print_bytes)
533 	FD_SET(device_fd, &output);
534 
535       if (select(nfds, &input, &output, NULL, NULL) < 0)
536 	continue;			/* Ignore errors here */
537 
538      /*
539       * Check if we have a side-channel request ready...
540       */
541 
542       if (FD_ISSET(CUPS_SC_FD, &input))
543       {
544        /*
545 	* Do the side-channel request, then start back over in the select
546 	* loop since it may have read from print_fd...
547 	*/
548 
549         if (side_cb(print_fd, device_fd, 1))
550 	  side_eof = 1;
551 	continue;
552       }
553 
554      /*
555       * Check if we have back-channel data ready...
556       */
557 
558       if (FD_ISSET(device_fd, &input))
559       {
560 	if ((bc_bytes = read(device_fd, bc_buffer, sizeof(bc_buffer))) > 0)
561 	{
562 	  fprintf(stderr, "DEBUG: Received %d bytes of back-channel data.\n",
563 	          (int)bc_bytes);
564           cupsBackChannelWrite(bc_buffer, bc_bytes, 1.0);
565 	}
566       }
567 
568      /*
569       * Check if we have print data ready...
570       */
571 
572       if (FD_ISSET(print_fd, &input))
573       {
574 	if ((print_bytes = read(print_fd, print_buffer, print_size)) < 0)
575 	{
576 	 /*
577           * Read error - bail if we don't see EAGAIN or EINTR...
578 	  */
579 
580 	  if (errno != EAGAIN && errno != EINTR)
581 	  {
582 	    perror("DEBUG: Unable to read print data");
583 
584             tcsetattr(device_fd, TCSADRAIN, &origopts);
585 
586 	    close(device_fd);
587 
588 	    if (print_fd != 0)
589 	      close(print_fd);
590 
591 	    return (CUPS_BACKEND_FAILED);
592 	  }
593 
594           print_bytes = 0;
595 	}
596 	else if (print_bytes == 0)
597 	{
598 	 /*
599           * End of file, break out of the loop...
600 	  */
601 
602           break;
603 	}
604 
605 	print_ptr = print_buffer;
606       }
607 
608      /*
609       * Check if the device is ready to receive data and we have data to
610       * send...
611       */
612 
613       if (print_bytes && FD_ISSET(device_fd, &output))
614       {
615 	if (dtrdsr)
616 	{
617 	 /*
618 	  * Check the port and sleep until DSR is set...
619 	  */
620 
621 	  int status;
622 
623 
624 	  if (!ioctl(device_fd, TIOCMGET, &status))
625             if (!(status & TIOCM_DSR))
626 	    {
627 	     /*
628 	      * Wait for DSR to go high...
629 	      */
630 
631 	      fputs("DEBUG: DSR is low; waiting for device.\n", stderr);
632 
633               do
634 	      {
635 	       /*
636 	        * Poll every 100ms...
637 		*/
638 
639 		usleep(100000);
640 
641 		if (ioctl(device_fd, TIOCMGET, &status))
642 		  break;
643 	      }
644 	      while (!(status & TIOCM_DSR));
645 
646 	      fputs("DEBUG: DSR is high; writing to device.\n", stderr);
647             }
648 	}
649 
650 	if ((bytes = write(device_fd, print_ptr, print_bytes)) < 0)
651 	{
652 	 /*
653           * Write error - bail if we don't see an error we can retry...
654 	  */
655 
656 	  if (errno != EAGAIN && errno != EINTR && errno != ENOTTY)
657 	  {
658 	    perror("DEBUG: Unable to write print data");
659 
660             tcsetattr(device_fd, TCSADRAIN, &origopts);
661 
662 	    close(device_fd);
663 
664 	    if (print_fd != 0)
665 	      close(print_fd);
666 
667 	    return (CUPS_BACKEND_FAILED);
668 	  }
669 	}
670 	else
671 	{
672           fprintf(stderr, "DEBUG: Wrote %d bytes.\n", (int)bytes);
673 
674           print_bytes -= bytes;
675 	  print_ptr   += bytes;
676 	  total_bytes += bytes;
677 	}
678       }
679     }
680   }
681 
682  /*
683   * Close the serial port and input file and return...
684   */
685 
686   tcsetattr(device_fd, TCSADRAIN, &origopts);
687 
688   close(device_fd);
689 
690   if (print_fd != 0)
691     close(print_fd);
692 
693   return (CUPS_BACKEND_OK);
694 }
695 
696 
697 /*
698  * 'drain_output()' - Drain pending print data to the device.
699  */
700 
701 static int				/* O - 0 on success, -1 on error */
drain_output(int print_fd,int device_fd)702 drain_output(int print_fd,		/* I - Print file descriptor */
703              int device_fd)		/* I - Device file descriptor */
704 {
705   int		nfds;			/* Maximum file descriptor value + 1 */
706   fd_set	input;			/* Input set for reading */
707   ssize_t	print_bytes,		/* Print bytes read */
708 		bytes;			/* Bytes written */
709   char		print_buffer[8192],	/* Print data buffer */
710 		*print_ptr;		/* Pointer into print data buffer */
711   struct timeval timeout;		/* Timeout for read... */
712 
713 
714  /*
715   * Figure out the maximum file descriptor value to use with select()...
716   */
717 
718   nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
719 
720  /*
721   * Now loop until we are out of data from print_fd...
722   */
723 
724   for (;;)
725   {
726    /*
727     * Use select() to determine whether we have data to copy around...
728     */
729 
730     FD_ZERO(&input);
731     FD_SET(print_fd, &input);
732 
733     timeout.tv_sec  = 0;
734     timeout.tv_usec = 0;
735 
736     if (select(nfds, &input, NULL, NULL, &timeout) < 0)
737       return (-1);
738 
739     if (!FD_ISSET(print_fd, &input))
740       return (0);
741 
742     if ((print_bytes = read(print_fd, print_buffer,
743 			    sizeof(print_buffer))) < 0)
744     {
745      /*
746       * Read error - bail if we don't see EAGAIN or EINTR...
747       */
748 
749       if (errno != EAGAIN && errno != EINTR)
750       {
751         perror("ERROR: Unable to read print data");
752 	return (-1);
753       }
754 
755       print_bytes = 0;
756     }
757     else if (print_bytes == 0)
758     {
759      /*
760       * End of file, return...
761       */
762 
763       return (0);
764     }
765 
766     fprintf(stderr, "DEBUG: Read %d bytes of print data.\n",
767 	    (int)print_bytes);
768 
769     for (print_ptr = print_buffer; print_bytes > 0;)
770     {
771       if ((bytes = write(device_fd, print_ptr, print_bytes)) < 0)
772       {
773        /*
774         * Write error - bail if we don't see an error we can retry...
775 	*/
776 
777         if (errno != ENOSPC && errno != ENXIO && errno != EAGAIN &&
778 	    errno != EINTR && errno != ENOTTY)
779 	{
780 	  perror("ERROR: Unable to write print data");
781 	  return (-1);
782 	}
783       }
784       else
785       {
786         fprintf(stderr, "DEBUG: Wrote %d bytes of print data.\n", (int)bytes);
787 
788         print_bytes -= bytes;
789 	print_ptr   += bytes;
790       }
791     }
792   }
793 }
794 
795 
796 /*
797  * 'list_devices()' - List all serial devices.
798  */
799 
800 static void
list_devices(void)801 list_devices(void)
802 {
803 #if defined(__sun) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
804   static char	*funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
805 					/* Funky hex numbering used for some *
806 					 * devices                           */
807 #endif /* __sun || __FreeBSD__ || __OpenBSD__ || __FreeBSD_kernel__ */
808 
809 
810 #ifdef __linux
811   int			i, j;		/* Looping vars */
812   int			fd;		/* File descriptor */
813   char			device[255];	/* Device filename */
814   char			info[255];	/* Device info/description */
815 #  ifdef TIOCGSERIAL
816   struct serial_struct	serinfo;	/* serial port info */
817 #  endif /* TIOCGSERIAL */
818 
819 
820   for (i = 0; i < 100; i ++)
821   {
822     sprintf(device, "/dev/ttyS%d", i);
823 
824     if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
825     {
826 #  ifdef TIOCGSERIAL
827      /*
828       * See if this port exists...
829       */
830 
831       serinfo.reserved_char[0] = 0;
832 
833       if (!ioctl(fd, TIOCGSERIAL, &serinfo))
834       {
835 	if (serinfo.type == PORT_UNKNOWN)
836 	{
837 	 /*
838 	  * Nope...
839 	  */
840 
841 	  close(fd);
842 	  continue;
843 	}
844       }
845 #  endif /* TIOCGSERIAL */
846 
847       close(fd);
848 
849       snprintf(info, sizeof(info), "Serial Port #%d", i + 1);
850 
851 #  if defined(_ARCH_PPC) || defined(powerpc) || defined(__powerpc)
852       printf("serial serial:%s?baud=230400 \"Unknown\" \"%s\"\n", device, info);
853 #  else
854       printf("serial serial:%s?baud=115200 \"Unknown\" \"%s\"\n", device, info);
855 #  endif /* _ARCH_PPC || powerpc || __powerpc */
856     }
857   }
858 
859   for (i = 0; i < 16; i ++)
860   {
861     snprintf(info, sizeof(info), "USB Serial Port #%d", i + 1);
862 
863     sprintf(device, "/dev/usb/ttyUSB%d", i);
864     if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
865     {
866       close(fd);
867       printf("serial serial:%s?baud=230400 \"Unknown\" \"%s\"\n", device, info);
868     }
869 
870     sprintf(device, "/dev/ttyUSB%d", i);
871     if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
872     {
873       close(fd);
874       printf("serial serial:%s?baud=230400 \"Unknown\" \"%s\"\n", device, info);
875     }
876   }
877 
878   for (i = 0; i < 64; i ++)
879   {
880     for (j = 0; j < 8; j ++)
881     {
882       sprintf(device, "/dev/ttyQ%02de%d", i, j);
883       if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
884       {
885         close(fd);
886 
887         printf("serial serial:%s?baud=115200 \"Unknown\" "
888 	       "\"Equinox ESP %d Port #%d\"\n", device, i, j + 1);
889       }
890     }
891   }
892 #elif defined(__sun)
893   int		i, j, n;		/* Looping vars */
894   char		device[255];		/* Device filename */
895   char		info[255];		/* Device info/description */
896 
897 
898  /*
899   * Standard serial ports...
900   */
901 
902   for (i = 0; i < 26; i ++)
903   {
904     sprintf(device, "/dev/cua/%c", 'a' + i);
905     if (!access(device, 0))
906     {
907       snprintf(info, sizeof(info), "Serial Port #%d", i + 1);
908 
909 #  ifdef B115200
910       printf("serial serial:%s?baud=115200 \"Unknown\" \"%s\"\n", device, info);
911 #  else
912       printf("serial serial:%s?baud=38400 \"Unknown\" \"%s\"\n", device, info);
913 #  endif /* B115200 */
914     }
915   }
916 
917  /*
918   * MAGMA serial ports...
919   */
920 
921   for (i = 0; i < 40; i ++)
922   {
923     sprintf(device, "/dev/term/%02d", i);
924     if (access(device, 0) == 0)
925       printf("serial serial:%s?baud=38400 \"Unknown\" \"MAGMA Serial Board #%d Port #%d\"\n",
926              device, (i / 10) + 1, (i % 10) + 1);
927   }
928 
929  /*
930   * Central Data serial ports...
931   */
932 
933   for (i = 0; i < 9; i ++)
934     for (j = 0; j < 8; j ++)
935       for (n = 0; n < 32; n ++)
936       {
937         if (i == 8)	/* EtherLite */
938           sprintf(device, "/dev/sts/ttyN%d%c", j, funky_hex[n]);
939         else
940           sprintf(device, "/dev/sts/tty%c%d%c", i + 'C', j,
941                   funky_hex[n]);
942 
943 	if (access(device, 0) == 0)
944 	{
945 	  if (i == 8)
946 	    printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data EtherLite Serial Port, ID %d, port %d\"\n",
947   	           device, j, n);
948   	  else
949 	    printf("serial serial:%s?baud=38400 \"Unknown\" \"Central Data SCSI Serial Port, logical bus %d, ID %d, port %d\"\n",
950   	           device, i, j, n);
951 	}
952       }
953 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
954   int	i, j;				/* Looping vars */
955   int	fd;				/* File descriptor */
956   char	device[255];			/* Device filename */
957   char	info[255];			/* Device info/description */
958 
959 
960  /*
961   * SIO ports...
962   */
963 
964   for (i = 0; i < 32; i ++)
965   {
966     sprintf(device, "/dev/ttyd%c", funky_hex[i]);
967     if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
968     {
969       close(fd);
970 
971       snprintf(info, sizeof(info), "Serial Port #%d", i + 1);
972 
973       printf("serial serial:%s?baud=115200 \"Unknown\" \"%s\"\n", device, info);
974     }
975   }
976 
977  /*
978   * Cyclades ports...
979   */
980 
981   for (i = 0; i < 16; i ++) /* Should be up to 65536 boards... */
982     for (j = 0; j < 32; j ++)
983     {
984       sprintf(device, "/dev/ttyc%d%c", i, funky_hex[j]);
985       if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
986       {
987 	close(fd);
988 	printf("serial serial:%s?baud=115200 \"Unknown\" \"Cyclades #%d Serial Port #%d\"\n",
989                device, i, j + 1);
990       }
991 
992       sprintf(device, "/dev/ttyC%d%c", i, funky_hex[j]);
993       if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
994       {
995 	close(fd);
996 	printf("serial serial:%s?baud=115200 \"Unknown\" \"Cyclades #%d Serial Port #%d\"\n",
997                device, i, j + 1);
998       }
999     }
1000 
1001  /*
1002   * Digiboard ports...
1003   */
1004 
1005   for (i = 0; i < 16; i ++) /* Should be up to 65536 boards... */
1006     for (j = 0; j < 32; j ++)
1007     {
1008       sprintf(device, "/dev/ttyD%d%c", i, funky_hex[j]);
1009       if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
1010       {
1011 	close(fd);
1012 	printf("serial serial:%s?baud=115200 \"Unknown\" \"Digiboard #%d Serial Port #%d\"\n",
1013                device, i, j + 1);
1014       }
1015     }
1016 
1017  /*
1018   * Stallion ports...
1019   */
1020 
1021   for (i = 0; i < 32; i ++)
1022   {
1023     sprintf(device, "/dev/ttyE%c", funky_hex[i]);
1024     if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
1025     {
1026       close(fd);
1027       printf("serial serial:%s?baud=115200 \"Unknown\" \"Stallion Serial Port #%d\"\n",
1028              device, i + 1);
1029     }
1030   }
1031 
1032  /*
1033   * SX ports...
1034   */
1035 
1036   for (i = 0; i < 128; i ++)
1037   {
1038     sprintf(device, "/dev/ttyA%d", i + 1);
1039     if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
1040     {
1041       close(fd);
1042       printf("serial serial:%s?baud=115200 \"Unknown\" \"SX Serial Port #%d\"\n",
1043              device, i + 1);
1044     }
1045   }
1046 #elif defined(__NetBSD__)
1047   int	i, j;				/* Looping vars */
1048   int	fd;				/* File descriptor */
1049   char	device[255];			/* Device filename */
1050   char	info[255];			/* Device info/description */
1051 
1052 
1053  /*
1054   * Standard serial ports...
1055   */
1056 
1057   for (i = 0; i < 4; i ++)
1058   {
1059     sprintf(device, "/dev/tty%02d", i);
1060     if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
1061     {
1062       close(fd);
1063 
1064       snprintf(info, sizeof(info), "Serial Port #%d", i + 1);
1065 
1066       printf("serial serial:%s?baud=115200 \"Unknown\" \"%s\"\n", device, info);
1067     }
1068   }
1069 
1070  /*
1071   * Cyclades-Z ports...
1072   */
1073 
1074   for (i = 0; i < 16; i ++) /* Should be up to 65536 boards... */
1075     for (j = 0; j < 64; j ++)
1076     {
1077       sprintf(device, "/dev/ttyCZ%02d%02d", i, j);
1078       if ((fd = open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0)
1079       {
1080 	close(fd);
1081 	printf("serial serial:%s?baud=115200 \"Unknown\" \"Cyclades #%d Serial Prt #%d\"\n",
1082 	       device, i, j + 1);
1083       }
1084     }
1085 #elif defined(__APPLE__)
1086  /*
1087   * Standard serial ports on MacOS X...
1088   */
1089 
1090   kern_return_t			kernResult;
1091   mach_port_t			masterPort;
1092   io_iterator_t			serialPortIterator;
1093   CFMutableDictionaryRef	classesToMatch;
1094   io_object_t			serialService;
1095 
1096 
1097   kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
1098   if (KERN_SUCCESS != kernResult)
1099     return;
1100 
1101  /*
1102   * Serial devices are instances of class IOSerialBSDClient.
1103   */
1104 
1105   classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
1106   if (classesToMatch != NULL)
1107   {
1108     CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey),
1109                          CFSTR(kIOSerialBSDRS232Type));
1110 
1111     kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch,
1112                                               &serialPortIterator);
1113     if (kernResult == KERN_SUCCESS)
1114     {
1115       while ((serialService = IOIteratorNext(serialPortIterator)))
1116       {
1117 	CFTypeRef	serialNameAsCFString;
1118 	CFTypeRef	bsdPathAsCFString;
1119 	CFTypeRef	hiddenVal;
1120 	char		serialName[128];
1121 	char		bsdPath[1024];
1122 	Boolean		result;
1123 
1124 
1125 	/* Check if hidden... */
1126 	hiddenVal = IORegistryEntrySearchCFProperty(serialService,
1127 						    kIOServicePlane,
1128 						    CFSTR("HiddenPort"),
1129 						    kCFAllocatorDefault,
1130 						    kIORegistryIterateRecursively |
1131 						    kIORegistryIterateParents);
1132 	if (hiddenVal)
1133 	  CFRelease(hiddenVal);	/* This interface should not be used */
1134 	else
1135 	{
1136 	  serialNameAsCFString =
1137 	      IORegistryEntryCreateCFProperty(serialService,
1138 					      CFSTR(kIOTTYDeviceKey),
1139 					      kCFAllocatorDefault, 0);
1140 	  if (serialNameAsCFString)
1141 	  {
1142 	    result = CFStringGetCString(serialNameAsCFString, serialName,
1143 					sizeof(serialName),
1144 					kCFStringEncodingASCII);
1145 	    CFRelease(serialNameAsCFString);
1146 
1147 	    if (result)
1148 	    {
1149 	      bsdPathAsCFString =
1150 		  IORegistryEntryCreateCFProperty(serialService,
1151 						  CFSTR(kIOCalloutDeviceKey),
1152 						  kCFAllocatorDefault, 0);
1153 	      if (bsdPathAsCFString)
1154 	      {
1155 		result = CFStringGetCString(bsdPathAsCFString, bsdPath,
1156 					    sizeof(bsdPath),
1157 					    kCFStringEncodingASCII);
1158 		CFRelease(bsdPathAsCFString);
1159 
1160 		if (result)
1161 		  printf("serial serial:%s?baud=115200 \"Unknown\" \"%s\"\n",
1162 			 bsdPath, serialName);
1163 	      }
1164 	    }
1165 	  }
1166 	}
1167 
1168 	IOObjectRelease(serialService);
1169       }
1170 
1171      /*
1172       * Release the iterator.
1173       */
1174 
1175       IOObjectRelease(serialPortIterator);
1176     }
1177   }
1178 #endif
1179 }
1180 
1181 
1182 /*
1183  * 'side_cb()' - Handle side-channel requests...
1184  */
1185 
1186 static int				/* O - 0 on success, -1 on error */
side_cb(int print_fd,int device_fd,int use_bc)1187 side_cb(int print_fd,			/* I - Print file */
1188         int device_fd,			/* I - Device file */
1189 	int use_bc)			/* I - Using back-channel? */
1190 {
1191   cups_sc_command_t	command;	/* Request command */
1192   cups_sc_status_t	status;		/* Request/response status */
1193   char			data[2048];	/* Request/response data */
1194   int			datalen;	/* Request/response data size */
1195 
1196 
1197   datalen = sizeof(data);
1198 
1199   if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
1200     return (-1);
1201 
1202   switch (command)
1203   {
1204     case CUPS_SC_CMD_DRAIN_OUTPUT :
1205         if (drain_output(print_fd, device_fd))
1206 	  status = CUPS_SC_STATUS_IO_ERROR;
1207 	else if (tcdrain(device_fd))
1208 	  status = CUPS_SC_STATUS_IO_ERROR;
1209 	else
1210 	  status = CUPS_SC_STATUS_OK;
1211 
1212 	datalen = 0;
1213         break;
1214 
1215     case CUPS_SC_CMD_GET_BIDI :
1216 	status  = CUPS_SC_STATUS_OK;
1217         data[0] = use_bc;
1218         datalen = 1;
1219         break;
1220 
1221     default :
1222         status  = CUPS_SC_STATUS_NOT_IMPLEMENTED;
1223 	datalen = 0;
1224 	break;
1225   }
1226 
1227   return (cupsSideChannelWrite(command, status, data, datalen, 1.0));
1228 }
1229