• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * USB backend for macOS.
3  *
4  * Copyright © 2022-2024 by OpenPrinting.
5  * Copyright © 2005-2021 Apple Inc. All rights reserved.
6  *
7  * IMPORTANT:  This Apple software is supplied to you by Apple Computer,
8  * Inc. ("Apple") in consideration of your agreement to the following
9  * terms, and your use, installation, modification or redistribution of
10  * this Apple software constitutes acceptance of these terms.  If you do
11  * not agree with these terms, please do not use, install, modify or
12  * redistribute this Apple software.
13  *
14  * In consideration of your agreement to abide by the following terms, and
15  * subject to these terms, Apple grants you a personal, non-exclusive
16  * license, under Apple's copyrights in this original Apple software (the
17  * "Apple Software"), to use, reproduce, modify and redistribute the Apple
18  * Software, with or without modifications, in source and/or binary forms;
19  * provided that if you redistribute the Apple Software in its entirety and
20  * without modifications, you must retain this notice and the following
21  * text and disclaimers in all such redistributions of the Apple Software.
22  * Neither the name, trademarks, service marks or logos of Apple Computer,
23  * Inc. may be used to endorse or promote products derived from the Apple
24  * Software without specific prior written permission from Apple.  Except
25  * as expressly stated in this notice, no other rights or licenses, express
26  * or implied, are granted by Apple herein, including but not limited to
27  * any patent rights that may be infringed by your derivative works or by
28  * other works in which the Apple Software may be incorporated.
29  *
30  * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
31  * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
32  * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
33  * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
34  * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
35  *
36  * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
37  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39  * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
40  * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
41  * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
42  * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
43  * POSSIBILITY OF SUCH DAMAGE.
44  */
45 
46 /*
47  * Include necessary headers.
48  */
49 
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <errno.h>
53 #include <signal.h>
54 #include <fcntl.h>
55 #include <termios.h>
56 #include <unistd.h>
57 #include <sys/stat.h>
58 #include <sys/sysctl.h>
59 #include <libgen.h>
60 #include <mach/mach.h>
61 #include <mach/mach_error.h>
62 #include <mach/mach_time.h>
63 #include <cups/debug-private.h>
64 #include <cups/file-private.h>
65 #include <cups/sidechannel.h>
66 #include <cups/language-private.h>
67 #include <cups/ppd-private.h>
68 #include "backend-private.h"
69 #include <CoreFoundation/CoreFoundation.h>
70 #include <IOKit/usb/IOUSBLib.h>
71 #include <IOKit/IOCFPlugIn.h>
72 #include <libproc.h>
73 #include <asl.h>
74 #include <spawn.h>
75 #include <pthread.h>
76 
77 /*
78  * Include necessary headers.
79  */
80 
81 extern char **environ;
82 
83 
84 /*
85  * DEBUG_WRITES, if defined, causes the backend to write data to the printer in
86  * 512 byte increments, up to 8192 bytes, to make debugging with a USB bus
87  * analyzer easier.
88  */
89 
90 #define DEBUG_WRITES 0
91 
92 /*
93  * WAIT_EOF_DELAY is number of seconds we'll wait for responses from
94  * the printer after we've finished sending all the data
95  */
96 #define WAIT_EOF_DELAY			7
97 #define WAIT_SIDE_DELAY			3
98 #define DEFAULT_TIMEOUT			5000L
99 
100 #define	USB_INTERFACE_KIND		CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID245)
101 #define kUSBLanguageEnglish		0x409
102 
103 #define PRINTER_POLLING_INTERVAL	5			/* seconds */
104 #define INITIAL_LOG_INTERVAL		PRINTER_POLLING_INTERVAL
105 #define SUBSEQUENT_LOG_INTERVAL		3 * INITIAL_LOG_INTERVAL
106 
107 #define kUSBPrinterClassTypeID		CFUUIDGetConstantUUIDWithBytes(NULL, 0x06, 0x04, 0x7D, 0x16, 0x53, 0xA2, 0x11, 0xD6, 0x92, 0x06, 0x00, 0x30, 0x65, 0x52, 0x45, 0x92)
108 #define	kUSBPrinterClassInterfaceID	CFUUIDGetConstantUUIDWithBytes(NULL, 0x03, 0x34, 0x6D, 0x74, 0x53, 0xA3, 0x11, 0xD6, 0x9E, 0xA1, 0x76, 0x30, 0x65, 0x52, 0x45, 0x92)
109 
110 #define kUSBClassDriverProperty		CFSTR("USB Printing Class")
111 
112 #define kUSBGenericTOPrinterClassDriver	CFSTR("/System/Library/Printers/Libraries/USBGenericPrintingClass.plugin")
113 #define kUSBPrinterClassDeviceNotOpen	-9664	/*kPMInvalidIOMContext*/
114 
115 #define CRSetCrashLogMessage(m) _crc_make_setter(message, m)
116 #define _crc_make_setter(attr, arg) (gCRAnnotations.attr = (uint64_t)(unsigned long)(arg))
117 #define CRASH_REPORTER_CLIENT_HIDDEN __attribute__((visibility("hidden")))
118 #define CRASHREPORTER_ANNOTATIONS_VERSION 4
119 #define CRASHREPORTER_ANNOTATIONS_SECTION "__crash_info"
120 
121 struct crashreporter_annotations_t {
122 	uint64_t version;		// unsigned long
123 	uint64_t message;		// char *
124 	uint64_t signature_string;	// char *
125 	uint64_t backtrace;		// char *
126 	uint64_t message2;		// char *
127 	uint64_t thread;		// uint64_t
128 	uint64_t dialog_mode;		// unsigned int
129 };
130 
131 CRASH_REPORTER_CLIENT_HIDDEN
132 struct crashreporter_annotations_t gCRAnnotations
133 	__attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION)))
134 	= { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 };
135 
136 /*
137  * Section 5.3 USB Printing Class spec
138  */
139 #define kUSBPrintingSubclass			1
140 #define kUSBPrintingProtocolNoOpen		0
141 #define kUSBPrintingProtocolUnidirectional	1
142 #define kUSBPrintingProtocolBidirectional	2
143 #define kUSBPrintingProtocolIPP			4
144 
145 typedef IOUSBInterfaceInterface245	**printer_interface_t;
146 
147 typedef struct iodevice_request_s	/**** Device request ****/
148 {
149   UInt8		requestType;
150   UInt8		request;
151   UInt16	value;
152   UInt16	index;
153   UInt16	length;
154   void		*buffer;
155 } iodevice_request_t;
156 
157 typedef union				/**** Centronics status byte ****/
158 {
159   char		b;
160   struct
161   {
162     unsigned	reserved0:2;
163     unsigned	paperError:1;
164     unsigned	select:1;
165     unsigned	notError:1;
166     unsigned	reserved1:3;
167   } status;
168 } centronics_status_t;
169 
170 typedef struct classdriver_s		/**** g.classdriver context ****/
171 {
172   IUNKNOWN_C_GUTS;
173   CFPlugInRef		plugin;			/* release plugin */
174   IUnknownVTbl		**factory;		/* Factory */
175   void			*vendorReference;	/* vendor class specific usage */
176   UInt32		location;		/* unique location in bus topology */
177   UInt8			interfaceNumber;	/* Interface number */
178   UInt16		vendorID;		/* Vendor id */
179   UInt16		productID;		/* Product id */
180   printer_interface_t	interface;		/* identify the device to IOKit */
181   UInt8			outpipe;		/* mandatory bulkOut pipe */
182   UInt8			inpipe;			/* optional bulkIn pipe */
183 
184   /* general class requests */
185   kern_return_t (*DeviceRequest)(struct classdriver_s **printer, iodevice_request_t *iorequest, UInt16 timeout);
186   kern_return_t	(*GetString)(struct classdriver_s **printer, UInt8 whichString, UInt16 language, UInt16 timeout, CFStringRef *result);
187 
188   /* standard printer class requests */
189   kern_return_t	(*SoftReset)(struct classdriver_s **printer, UInt16 timeout);
190   kern_return_t	(*GetCentronicsStatus)(struct classdriver_s **printer, centronics_status_t *result, UInt16 timeout);
191   kern_return_t	(*GetDeviceID)(struct classdriver_s **printer, CFStringRef *devid, UInt16 timeout);
192 
193   /* standard bulk device requests */
194   kern_return_t (*ReadPipe)(struct classdriver_s **printer, UInt8 *buffer, UInt32 *count);
195   kern_return_t (*WritePipe)(struct classdriver_s **printer, UInt8 *buffer, UInt32 *count, Boolean eoj);
196 
197   /* interface requests */
198   kern_return_t (*Open)(struct classdriver_s **printer, UInt32 location, UInt8 protocol);
199   kern_return_t (*Abort)(struct classdriver_s **printer);
200   kern_return_t (*Close)(struct classdriver_s **printer);
201 
202   /* initialize and terminate */
203   kern_return_t (*Initialize)(struct classdriver_s **printer, struct classdriver_s **baseclass);
204   kern_return_t (*Terminate)(struct classdriver_s **printer);
205 
206 } classdriver_t;
207 
208 typedef Boolean (*iterator_callback_t)(io_service_t obj, printer_interface_t printerIntf, void *refcon);
209 
210 typedef struct iterator_reference_s	/**** Iterator reference data */
211 {
212   iterator_callback_t callback;
213   void		*userdata;
214   Boolean	keepRunning;
215 } iterator_reference_t;
216 
217 typedef struct globals_s
218 {
219   io_service_t		printer_obj;
220   classdriver_t		**classdriver;
221 
222   pthread_mutex_t	read_thread_mutex;
223   pthread_cond_t	read_thread_cond;
224   int			read_thread_stop;
225   int			read_thread_done;
226 
227   pthread_mutex_t	readwrite_lock_mutex;
228   pthread_cond_t	readwrite_lock_cond;
229   int			readwrite_lock;
230 
231   CFStringRef		make;
232   CFStringRef		model;
233   CFStringRef		serial;
234   UInt32		location;
235   UInt8			interfaceNum;
236   UInt8			alternateSetting;
237   UInt8                 interfaceProtocol;
238 
239   CFRunLoopTimerRef 	status_timer;
240 
241   int			print_fd;	/* File descriptor to print */
242   ssize_t		print_bytes;	/* Print bytes read */
243 #if DEBUG_WRITES
244   ssize_t		debug_bytes;	/* Current bytes to read */
245 #endif /* DEBUG_WRITES */
246 
247   Boolean		use_generic_class_driver;
248   Boolean		wait_eof;
249   int			drain_output;	/* Drain all pending output */
250   int			bidi_flag;	/* 0=unidirectional, 1=bidirectional */
251 
252   pthread_mutex_t	sidechannel_thread_mutex;
253   pthread_cond_t	sidechannel_thread_cond;
254   int			sidechannel_thread_stop;
255   int			sidechannel_thread_done;
256 } globals_t;
257 
258 
259 /*
260  * Globals...
261  */
262 
263 globals_t g = { 0 };			/* Globals */
264 int Iterating = 0;			/* Are we iterating the bus? */
265 
266 
267 /*
268  * Local functions...
269  */
270 
271 static Boolean list_device_cb(io_service_t obj, printer_interface_t printerIntf, void *refcon);
272 static Boolean find_device_cb(io_service_t obj, printer_interface_t printerIntf, void *refcon);
273 
274 static CFStringRef cfstr_create_trim(const char *cstr);
275 static CFStringRef copy_value_for_key(CFStringRef deviceID, CFStringRef *keys);
276 static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_t interface, classdriver_t ***printerDriver);
277 static kern_return_t load_printerdriver(CFStringRef *driverBundlePath);
278 static kern_return_t registry_close(void);
279 static kern_return_t registry_open(CFStringRef *driverBundlePath);
280 static kern_return_t unload_classdriver(classdriver_t ***classdriver);
281 
282 static void *read_thread(void *reference);
283 static void *sidechannel_thread(void *reference);
284 static void device_added(void *userdata, io_iterator_t iterator);
285 static void get_device_id(cups_sc_status_t *status, char *data, int *datalen);
286 static void iterate_printers(iterator_callback_t callBack, void *userdata);
287 static void parse_options(char *options, char *serial, int serial_size, UInt32 *location, Boolean *wait_eof);
288 static void setup_cfLanguage(void);
289 static void soft_reset(void);
290 static void status_timer_cb(CFRunLoopTimerRef timer, void *info);
291 #define IS_64BIT 1
292 #define IS_NOT_64BIT 0
293 
294 #if defined(__arm64e__)
295 static pid_t	child_pid;		/* Child PID */
296 static void run_legacy_backend(int argc, char *argv[], int fd) _CUPS_NORETURN;	/* Starts child backend process running as a x86_64 executable */
297 static void sigterm_handler(int sig);    /* SIGTERM handler */
298 #endif /* __arm64e__ */
299 static void sigquit_handler(int sig, siginfo_t *si, void *unused) _CUPS_NORETURN;
300 
301 #ifdef PARSE_PS_ERRORS
302 static const char *next_line (const char *buffer);
303 static void parse_pserror (char *sockBuffer, int len);
304 #endif /* PARSE_PS_ERRORS */
305 
306 static printer_interface_t usb_printer_interface_interface(io_service_t usbClass);
307 
308 static CFStringRef copy_printer_interface_deviceid(printer_interface_t printer, UInt8 alternateSetting);
309 static CFStringRef copy_printer_interface_indexed_description(printer_interface_t  printer, UInt8 index, UInt16 language);
310 static CFStringRef deviceIDCopyManufacturer(CFStringRef deviceID);
311 static CFStringRef deviceIDCopyModel(CFStringRef deviceID);
312 static CFStringRef deviceIDCopySerialNumber(CFStringRef deviceID);
313 
314 #pragma mark -
315 
316 /*
317  * 'list_devices()' - List all USB devices.
318  */
319 
list_devices()320 void list_devices()
321 {
322   iterate_printers(list_device_cb, NULL);
323 }
324 
325 
326 /*
327  * 'print_device()' - Print a file to a USB device.
328  */
329 
330 int					/* O - Exit status */
print_device(const char * uri,const char * hostname,const char * resource,char * options,int print_fd,int copies,int argc,char * argv[])331 print_device(const char *uri,		/* I - Device URI */
332              const char *hostname,	/* I - Hostname/manufacturer */
333              const char *resource,	/* I - Resource/modelname */
334 	     char       *options,	/* I - Device options/serial number */
335 	     int        print_fd,	/* I - File descriptor to print */
336 	     int        copies,		/* I - Copies to print */
337 	     int	argc,		/* I - Number of command-line arguments (6 or 7) */
338 	     char	*argv[])	/* I - Command-line arguments */
339 {
340   char		  serial[1024];		/* Serial number buffer */
341   OSStatus	  status;		/* Function results */
342   IOReturn	  iostatus;		/* Current IO status */
343   pthread_t	  read_thread_id,	/* Read thread */
344 		  sidechannel_thread_id;/* Side-channel thread */
345   int		  have_sidechannel = 0;	/* Was the side-channel thread started? */
346   struct stat     sidechannel_info;	/* Side-channel file descriptor info */
347   char		  print_buffer[8192],	/* Print data buffer */
348 		  *print_ptr;		/* Pointer into print data buffer */
349   UInt32	  location;		/* Unique location in bus topology */
350   fd_set	  input_set;		/* Input set for select() */
351   CFStringRef	  driverBundlePath;	/* Class driver path */
352   int		  countdown,		/* Logging interval */
353 		  nfds;			/* Number of file descriptors */
354   ssize_t	  total_bytes;		/* Total bytes written */
355   UInt32	  bytes;		/* Bytes written */
356   struct timeval  *timeout,		/* Timeout pointer */
357 		  tv;			/* Time value */
358   struct timespec cond_timeout;		/* pthread condition timeout */
359   struct sigaction action;		/* Actions for POSIX signals */
360 
361 
362   (void)uri;
363   (void)argc;
364   (void)argv;
365 
366  /*
367   * Catch SIGQUIT to determine who is sending it...
368   */
369 
370   memset(&action, 0, sizeof(action));
371   action.sa_sigaction = sigquit_handler;
372   action.sa_flags = SA_SIGINFO;
373   sigaction(SIGQUIT, &action, NULL);
374 
375  /*
376   * See if the side-channel descriptor is valid...
377   */
378 
379   have_sidechannel = !fstat(CUPS_SC_FD, &sidechannel_info) &&
380                      S_ISSOCK(sidechannel_info.st_mode);
381 
382  /*
383   * Localize using CoreFoundation...
384   */
385 
386   setup_cfLanguage();
387 
388   parse_options(options, serial, sizeof(serial), &location, &g.wait_eof);
389 
390   if (resource[0] == '/')
391     resource++;
392 
393   g.print_fd	= print_fd;
394   g.make	= cfstr_create_trim(hostname);
395   g.model	= cfstr_create_trim(resource);
396   g.serial	= cfstr_create_trim(serial);
397   g.location	= location;
398 
399   if (!g.make || !g.model)
400   {
401     fprintf(stderr, "DEBUG: Fatal USB error.\n");
402     _cupsLangPrintFilter(stderr, "ERROR",
403                          _("There was an unrecoverable USB error."));
404 
405     if (!g.make)
406       fputs("DEBUG: USB make string is NULL\n", stderr);
407     if (!g.model)
408       fputs("DEBUG: USB model string is NULL\n", stderr);
409 
410     return (CUPS_BACKEND_STOP);
411   }
412 
413   fputs("STATE: +connecting-to-device\n", stderr);
414 
415   countdown = INITIAL_LOG_INTERVAL;
416 
417   do
418   {
419     if (g.printer_obj)
420     {
421       IOObjectRelease(g.printer_obj);
422       unload_classdriver(&g.classdriver);
423       g.printer_obj = 0x0;
424       g.classdriver = 0x0;
425     }
426     fprintf(stderr, "DEBUG: Looking for '%s %s'\n", hostname, resource);
427 
428     do
429     {
430       iterate_printers(find_device_cb, NULL);
431       if (g.printer_obj != 0x0)
432         break;
433 
434       _cupsLangPrintFilter(stderr, "INFO", _("Waiting for printer to become available."));
435       sleep(5);
436     } while (true);
437 
438     fputs("DEBUG: Opening connection\n", stderr);
439 
440     driverBundlePath = NULL;
441 
442     status = registry_open(&driverBundlePath);
443 
444 #if defined(__arm64e__)
445     /*
446      * If we were unable to load the class drivers for this printer it's
447      * probably because they're x86_64 (or older). In this case try to run this
448      * backend as x86_64 so we can use them...
449      */
450     if (status == -2)
451     {
452       run_legacy_backend(argc, argv, print_fd);
453       /* Never returns here */
454     }
455 #endif /* __arm64e__ */
456 
457     if (status ==  -2)
458     {
459      /*
460       * If we still were unable to load the class drivers for this printer log
461       * the error and stop the queue...
462       */
463 
464       if (driverBundlePath == NULL || !CFStringGetCString(driverBundlePath, print_buffer, sizeof(print_buffer), kCFStringEncodingUTF8))
465         strlcpy(print_buffer, "USB class driver", sizeof(print_buffer));
466 
467       fputs("STATE: +apple-missing-usbclassdriver-error\n", stderr);
468       _cupsLangPrintFilter(stderr, "ERROR",
469 			   _("There was an unrecoverable USB error."));
470       fprintf(stderr, "DEBUG: Could not load %s\n", print_buffer);
471 
472       if (driverBundlePath)
473 	CFRelease(driverBundlePath);
474 
475       return (CUPS_BACKEND_STOP);
476     }
477 
478     if (driverBundlePath)
479       CFRelease(driverBundlePath);
480 
481     if (status != noErr)
482     {
483       sleep(PRINTER_POLLING_INTERVAL);
484       countdown -= PRINTER_POLLING_INTERVAL;
485       if (countdown <= 0)
486       {
487 	_cupsLangPrintFilter(stderr, "INFO",
488 		             _("Waiting for printer to become available."));
489 	fprintf(stderr, "DEBUG: USB printer status: 0x%08x\n", (int)status);
490 	countdown = SUBSEQUENT_LOG_INTERVAL;	/* subsequent log entries, every 15 seconds */
491       }
492     }
493   } while (status != noErr);
494 
495   fputs("STATE: -connecting-to-device\n", stderr);
496 
497   /*
498    * Now that we are "connected" to the port, ignore SIGTERM so that we
499    * can finish out any page data the driver sends (e.g. to eject the
500    * current page...  Only ignore SIGTERM if we are printing data from
501    * stdin (otherwise you can't cancel raw jobs...)
502    */
503 
504   if (!print_fd)
505   {
506     memset(&action, 0, sizeof(action));
507 
508     sigemptyset(&action.sa_mask);
509     action.sa_handler = SIG_IGN;
510     sigaction(SIGTERM, &action, NULL);
511   }
512 
513  /*
514   * Start the side channel thread if the descriptor is valid...
515   */
516 
517   pthread_mutex_init(&g.readwrite_lock_mutex, NULL);
518   pthread_cond_init(&g.readwrite_lock_cond, NULL);
519   g.readwrite_lock = 1;
520 
521   if (have_sidechannel)
522   {
523     g.sidechannel_thread_stop = 0;
524     g.sidechannel_thread_done = 0;
525 
526     pthread_cond_init(&g.sidechannel_thread_cond, NULL);
527     pthread_mutex_init(&g.sidechannel_thread_mutex, NULL);
528 
529     if (pthread_create(&sidechannel_thread_id, NULL, sidechannel_thread, NULL))
530     {
531       fprintf(stderr, "DEBUG: Fatal USB error.\n");
532       _cupsLangPrintFilter(stderr, "ERROR",
533 			   _("There was an unrecoverable USB error."));
534       fputs("DEBUG: Couldn't create side-channel thread\n", stderr);
535       registry_close();
536       return (CUPS_BACKEND_STOP);
537     }
538   }
539 
540  /*
541   * Get the read thread going...
542   */
543 
544   g.read_thread_stop = 0;
545   g.read_thread_done = 0;
546 
547   pthread_cond_init(&g.read_thread_cond, NULL);
548   pthread_mutex_init(&g.read_thread_mutex, NULL);
549 
550   if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
551   {
552     fprintf(stderr, "DEBUG: Fatal USB error.\n");
553     _cupsLangPrintFilter(stderr, "ERROR",
554                          _("There was an unrecoverable USB error."));
555     fputs("DEBUG: Couldn't create read thread\n", stderr);
556     registry_close();
557     return (CUPS_BACKEND_STOP);
558   }
559 
560  /*
561   * The main thread sends the print file...
562   */
563 
564   g.drain_output = 0;
565   g.print_bytes	 = 0;
566   total_bytes	 = 0;
567   print_ptr	 = print_buffer;
568 
569   while (status == noErr && copies-- > 0)
570   {
571     _cupsLangPrintFilter(stderr, "INFO", _("Sending data to printer."));
572 
573     if (print_fd != STDIN_FILENO)
574     {
575       fputs("PAGE: 1 1\n", stderr);
576       lseek(print_fd, 0, SEEK_SET);
577     }
578 
579     while (status == noErr)
580     {
581       FD_ZERO(&input_set);
582 
583       if (!g.print_bytes)
584 	FD_SET(print_fd, &input_set);
585 
586      /*
587       * Calculate select timeout...
588       *   If we have data waiting to send timeout is 100ms.
589       *   else if we're draining print_fd timeout is 0.
590       *   else we're waiting forever...
591       */
592 
593       if (g.print_bytes)
594       {
595 	tv.tv_sec  = 0;
596 	tv.tv_usec = 100000;		/* 100ms */
597 	timeout = &tv;
598       }
599       else if (g.drain_output)
600       {
601 	tv.tv_sec  = 0;
602 	tv.tv_usec = 0;
603 	timeout = &tv;
604       }
605       else
606 	timeout = NULL;
607 
608      /*
609       * I/O is unlocked around select...
610       */
611 
612       pthread_mutex_lock(&g.readwrite_lock_mutex);
613       g.readwrite_lock = 0;
614       pthread_cond_signal(&g.readwrite_lock_cond);
615       pthread_mutex_unlock(&g.readwrite_lock_mutex);
616 
617       nfds = select(print_fd + 1, &input_set, NULL, NULL, timeout);
618 
619      /*
620       * Reacquire the lock...
621       */
622 
623       pthread_mutex_lock(&g.readwrite_lock_mutex);
624       while (g.readwrite_lock)
625 	pthread_cond_wait(&g.readwrite_lock_cond, &g.readwrite_lock_mutex);
626       g.readwrite_lock = 1;
627       pthread_mutex_unlock(&g.readwrite_lock_mutex);
628 
629       if (nfds < 0)
630       {
631 	if (errno == EINTR && total_bytes == 0)
632 	{
633 	  fputs("DEBUG: Received an interrupt before any bytes were "
634 	        "written, aborting\n", stderr);
635           registry_close();
636           return (CUPS_BACKEND_OK);
637 	}
638 	else if (errno != EAGAIN && errno != EINTR)
639 	{
640 	  _cupsLangPrintFilter(stderr, "ERROR",
641 	                       _("Unable to read print data."));
642 	  perror("DEBUG: select");
643 	  registry_close();
644           return (CUPS_BACKEND_FAILED);
645 	}
646       }
647 
648      /*
649       * If drain output has finished send a response...
650       */
651 
652       if (g.drain_output && !nfds && !g.print_bytes)
653       {
654 	/* Send a response... */
655 	cupsSideChannelWrite(CUPS_SC_CMD_DRAIN_OUTPUT, CUPS_SC_STATUS_OK, NULL, 0, 1.0);
656 	g.drain_output = 0;
657       }
658 
659      /*
660       * Check if we have print data ready...
661       */
662 
663       if (FD_ISSET(print_fd, &input_set))
664       {
665 #if DEBUG_WRITES
666 	g.debug_bytes += 512;
667         if (g.debug_bytes > sizeof(print_buffer))
668 	  g.debug_bytes = 512;
669 
670 	g.print_bytes = read(print_fd, print_buffer, g.debug_bytes);
671 
672 #else
673 	g.print_bytes = read(print_fd, print_buffer, sizeof(print_buffer));
674 #endif /* DEBUG_WRITES */
675 
676 	if (g.print_bytes < 0)
677 	{
678 	 /*
679 	  * Read error - bail if we don't see EAGAIN or EINTR...
680 	  */
681 
682 	  if (errno != EAGAIN && errno != EINTR)
683 	  {
684 	    _cupsLangPrintFilter(stderr, "ERROR",
685 				 _("Unable to read print data."));
686 	    perror("DEBUG: read");
687 	    registry_close();
688 	    return (CUPS_BACKEND_FAILED);
689 	  }
690 
691 	  g.print_bytes = 0;
692 	}
693 	else if (g.print_bytes == 0)
694 	{
695 	 /*
696 	  * End of file, break out of the loop...
697 	  */
698 
699 	  break;
700 	}
701 
702 	print_ptr = print_buffer;
703 
704 	fprintf(stderr, "DEBUG: Read %d bytes of print data...\n",
705 		(int)g.print_bytes);
706       }
707 
708       if (g.print_bytes)
709       {
710 	bytes    = (UInt32)g.print_bytes;
711 	iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
712 
713        /*
714 	* Ignore timeout errors, but retain the number of bytes written to
715 	* avoid sending duplicate data...
716 	*/
717 
718 	if (iostatus == kIOUSBTransactionTimeout)
719 	{
720 	  fputs("DEBUG: Got USB transaction timeout during write\n", stderr);
721 	  iostatus = 0;
722 	}
723 
724        /*
725         * If we've stalled, retry the write...
726 	*/
727 
728 	else if (iostatus == kIOUSBPipeStalled)
729 	{
730 	  fputs("DEBUG: Got USB pipe stalled during write\n", stderr);
731 
732 	  bytes    = (UInt32)g.print_bytes;
733 	  iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
734 	}
735 
736        /*
737 	* Retry a write after an aborted write since we probably just got
738 	* SIGTERM...
739 	*/
740 
741 	else if (iostatus == kIOReturnAborted)
742 	{
743 	  fputs("DEBUG: Got USB return aborted during write\n", stderr);
744 
745 	  IOReturn err = (*g.classdriver)->Abort(g.classdriver);
746 	  fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n", err);
747 
748 #if DEBUG_WRITES
749           sleep(5);
750 #endif /* DEBUG_WRITES */
751 
752 	  bytes    = (UInt32)g.print_bytes;
753 	  iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
754         }
755 
756 	if (iostatus)
757 	{
758 	 /*
759 	  * Write error - bail if we don't see an error we can retry...
760 	  */
761 
762 	  _cupsLangPrintFilter(stderr, "ERROR",
763 	                       _("Unable to send data to printer."));
764 	  fprintf(stderr, "DEBUG: USB class driver WritePipe returned %x\n",
765 	          iostatus);
766 
767 	  IOReturn err = (*g.classdriver)->Abort(g.classdriver);
768 	  fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n",
769 	          err);
770 
771 	  status = CUPS_BACKEND_FAILED;
772 	  break;
773 	}
774 	else if (bytes > 0)
775 	{
776 	  fprintf(stderr, "DEBUG: Wrote %u bytes of print data...\n", (unsigned)bytes);
777 
778 	  g.print_bytes -= bytes;
779 	  print_ptr   += bytes;
780 	  total_bytes += bytes;
781 	}
782       }
783 
784       if (print_fd != 0 && status == noErr)
785 	fprintf(stderr, "DEBUG: Sending print file, %lld bytes...\n",
786 		(off_t)total_bytes);
787     }
788   }
789 
790   fprintf(stderr, "DEBUG: Sent %lld bytes...\n", (off_t)total_bytes);
791   fputs("STATE: +cups-waiting-for-job-completed\n", stderr);
792 
793  /*
794   * Signal the side channel thread to exit...
795   */
796 
797   if (have_sidechannel)
798   {
799     close(CUPS_SC_FD);
800     pthread_mutex_lock(&g.readwrite_lock_mutex);
801     g.readwrite_lock = 0;
802     pthread_cond_signal(&g.readwrite_lock_cond);
803     pthread_mutex_unlock(&g.readwrite_lock_mutex);
804 
805     g.sidechannel_thread_stop = 1;
806     pthread_mutex_lock(&g.sidechannel_thread_mutex);
807 
808     if (!g.sidechannel_thread_done)
809     {
810       gettimeofday(&tv, NULL);
811       cond_timeout.tv_sec  = tv.tv_sec + WAIT_SIDE_DELAY;
812       cond_timeout.tv_nsec = tv.tv_usec * 1000;
813 
814       while (!g.sidechannel_thread_done)
815       {
816 	if (pthread_cond_timedwait(&g.sidechannel_thread_cond,
817 				   &g.sidechannel_thread_mutex,
818 				   &cond_timeout) != 0)
819 	  break;
820       }
821     }
822 
823     pthread_mutex_unlock(&g.sidechannel_thread_mutex);
824   }
825 
826  /*
827   * Signal the read thread to exit then wait 7 seconds for it to complete...
828   */
829 
830   g.read_thread_stop = 1;
831 
832   pthread_mutex_lock(&g.read_thread_mutex);
833 
834   if (!g.read_thread_done)
835   {
836     fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
837 
838     gettimeofday(&tv, NULL);
839     cond_timeout.tv_sec  = tv.tv_sec + WAIT_EOF_DELAY;
840     cond_timeout.tv_nsec = tv.tv_usec * 1000;
841 
842     while (!g.read_thread_done)
843     {
844       if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
845 				 &cond_timeout) != 0)
846 	break;
847     }
848 
849    /*
850     * If it didn't exit abort the pending read and wait an additional second...
851     */
852 
853     if (!g.read_thread_done)
854     {
855       fputs("DEBUG: Read thread still active, aborting the pending read...\n",
856 	    stderr);
857 
858       g.wait_eof = 0;
859 
860       (*g.classdriver)->Abort(g.classdriver);
861 
862       gettimeofday(&tv, NULL);
863       cond_timeout.tv_sec  = tv.tv_sec + 1;
864       cond_timeout.tv_nsec = tv.tv_usec * 1000;
865 
866       while (!g.read_thread_done)
867       {
868 	if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
869 				   &cond_timeout) != 0)
870 	  break;
871       }
872     }
873   }
874 
875   pthread_mutex_unlock(&g.read_thread_mutex);
876 
877  /*
878   * Close the connection and input file and general clean up...
879   */
880 
881   registry_close();
882 
883   if (print_fd != STDIN_FILENO)
884     close(print_fd);
885 
886   if (g.make != NULL)
887     CFRelease(g.make);
888 
889   if (g.model != NULL)
890     CFRelease(g.model);
891 
892   if (g.serial != NULL)
893     CFRelease(g.serial);
894 
895   if (g.printer_obj != 0x0)
896     IOObjectRelease(g.printer_obj);
897 
898   return status;
899 }
900 
901 
902 /*
903  * 'read_thread()' - Thread to read the backchannel data on.
904  */
905 
read_thread(void * reference)906 static void *read_thread(void *reference)
907 {
908   UInt8				readbuffer[512];
909   UInt32			rbytes;
910   kern_return_t			readstatus;
911   struct mach_timebase_info	timeBaseInfo;
912   uint64_t			start,
913 				delay;
914 
915 
916   (void)reference;
917 
918   /* Calculate what 250 milliSeconds are in mach absolute time...
919    */
920   mach_timebase_info(&timeBaseInfo);
921   delay = ((uint64_t)250000000 * (uint64_t)timeBaseInfo.denom) / (uint64_t)timeBaseInfo.numer;
922 
923   do
924   {
925    /*
926     * Remember when we started so we can throttle the loop after the read call...
927     */
928 
929     start = mach_absolute_time();
930 
931     rbytes = sizeof(readbuffer);
932     readstatus = (*g.classdriver)->ReadPipe(g.classdriver, readbuffer, &rbytes);
933     if (readstatus == kIOReturnSuccess && rbytes > 0)
934     {
935       fprintf(stderr, "DEBUG: Read %d bytes of back-channel data...\n",
936               (int)rbytes);
937       cupsBackChannelWrite((char*)readbuffer, rbytes, 1.0);
938 
939       /* cntrl-d is echoed by the printer.
940        * NOTES:
941        *   Xerox Phaser 6250D doesn't echo the cntrl-d.
942        *   Xerox Phaser 6250D doesn't always send the product query.
943        */
944       if (g.wait_eof && readbuffer[rbytes-1] == 0x4)
945 	break;
946 
947 #ifdef PARSE_PS_ERRORS
948       parse_pserror(readbuffer, rbytes);
949 #endif
950     }
951     else if (readstatus == kIOUSBTransactionTimeout)
952       fputs("DEBUG: Got USB transaction timeout during read\n", stderr);
953     else if (readstatus == kIOUSBPipeStalled)
954       fputs("DEBUG: Got USB pipe stalled during read\n", stderr);
955     else if (readstatus == kIOReturnAborted)
956       fputs("DEBUG: Got USB return aborted during read\n", stderr);
957 
958    /*
959     * Make sure this loop executes no more than once every 250 milliseconds...
960     */
961 
962     if ((readstatus != kIOReturnSuccess || rbytes == 0) && (g.wait_eof || !g.read_thread_stop))
963       mach_wait_until(start + delay);
964 
965   } while (g.wait_eof || !g.read_thread_stop);	/* Abort from main thread tests error here */
966 
967   /* Workaround for usb race condition. <rdar://problem/21882551> */
968   if (!g.wait_eof && g.use_generic_class_driver)
969   {
970      const char *pdl = getenv("FINAL_CONTENT_TYPE");
971      if (pdl && strcmp(pdl, "application/vnd.cups-postscript") == 0)
972      {
973        while (readstatus == kIOReturnSuccess && ((rbytes > 0 && readbuffer[rbytes-1] != 0x4) || rbytes == 0))
974        {
975          start = mach_absolute_time();
976 
977          rbytes = sizeof(readbuffer);
978          readstatus = (*g.classdriver)->ReadPipe(g.classdriver, readbuffer, &rbytes);
979          if (readstatus == kIOReturnSuccess && rbytes > 0 && readbuffer[rbytes-1] == 0x4)
980            break;
981 
982          /* Make sure this loop executes no more than once every 250 milliseconds... */
983          mach_wait_until(start + delay);
984        }
985      }
986   }
987 
988  /*
989   * Let the main thread know that we have completed the read thread...
990   */
991 
992   pthread_mutex_lock(&g.read_thread_mutex);
993   g.read_thread_done = 1;
994   pthread_cond_signal(&g.read_thread_cond);
995   pthread_mutex_unlock(&g.read_thread_mutex);
996 
997   return NULL;
998 }
999 
1000 
1001 /*
1002  * 'sidechannel_thread()' - Handle side-channel requests.
1003  */
1004 
1005 static void*
sidechannel_thread(void * reference)1006 sidechannel_thread(void *reference)
1007 {
1008   cups_sc_command_t	command;	/* Request command */
1009   cups_sc_status_t	status;		/* Request/response status */
1010   char			data[2048];	/* Request/response data */
1011   int			datalen;	/* Request/response data size */
1012 
1013 
1014   (void)reference;
1015 
1016   do
1017   {
1018     datalen = sizeof(data);
1019 
1020     if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
1021     {
1022       if (status == CUPS_SC_STATUS_TIMEOUT)
1023 	continue;
1024       else
1025 	break;
1026     }
1027 
1028     switch (command)
1029     {
1030       case CUPS_SC_CMD_SOFT_RESET:	/* Do a soft reset */
1031 	  fputs("DEBUG: CUPS_SC_CMD_SOFT_RESET received from driver...\n",
1032 		stderr);
1033 
1034           if ((*g.classdriver)->SoftReset != NULL)
1035 	  {
1036 	    soft_reset();
1037 	    cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, NULL, 0, 1.0);
1038 	    fputs("DEBUG: Returning status CUPS_STATUS_OK with no bytes...\n",
1039 	          stderr);
1040 	  }
1041 	  else
1042 	  {
1043 	    cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED,
1044 	                         NULL, 0, 1.0);
1045 	    fputs("DEBUG: Returning status CUPS_STATUS_NOT_IMPLEMENTED with "
1046 	          "no bytes...\n", stderr);
1047 	  }
1048 	  break;
1049 
1050       case CUPS_SC_CMD_DRAIN_OUTPUT:	/* Drain all pending output */
1051 	  fputs("DEBUG: CUPS_SC_CMD_DRAIN_OUTPUT received from driver...\n",
1052 		stderr);
1053 
1054 	  g.drain_output = 1;
1055 	  break;
1056 
1057       case CUPS_SC_CMD_GET_BIDI:		/* Is the connection bidirectional? */
1058 	  fputs("DEBUG: CUPS_SC_CMD_GET_BIDI received from driver...\n",
1059 		stderr);
1060 
1061 	  data[0] = (char)g.bidi_flag;
1062 	  cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
1063 
1064 	  fprintf(stderr,
1065 	          "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n",
1066 		  data[0]);
1067 	  break;
1068 
1069       case CUPS_SC_CMD_GET_DEVICE_ID:	/* Return IEEE-1284 device ID */
1070 	  fputs("DEBUG: CUPS_SC_CMD_GET_DEVICE_ID received from driver...\n",
1071 		stderr);
1072 
1073 	  datalen = sizeof(data);
1074 	  get_device_id(&status, data, &datalen);
1075 	  cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, datalen, 1.0);
1076 
1077           if ((size_t)datalen < sizeof(data))
1078 	    data[datalen] = '\0';
1079 	  else
1080 	    data[sizeof(data) - 1] = '\0';
1081 
1082 	  fprintf(stderr,
1083 	          "DEBUG: Returning CUPS_SC_STATUS_OK with %d bytes (%s)...\n",
1084 		  datalen, data);
1085 	  break;
1086 
1087       case CUPS_SC_CMD_GET_STATE:		/* Return device state */
1088 	  fputs("DEBUG: CUPS_SC_CMD_GET_STATE received from driver...\n",
1089 		stderr);
1090 
1091 	  data[0] = CUPS_SC_STATE_ONLINE;
1092 	  cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
1093 
1094 	  fprintf(stderr,
1095 	          "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n",
1096 		  data[0]);
1097 	  break;
1098 
1099       default:
1100 	  fprintf(stderr, "DEBUG: Unknown side-channel command (%d) received "
1101 			  "from driver...\n", command);
1102 
1103 	  cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED,
1104 			       NULL, 0, 1.0);
1105 
1106 	  fputs("DEBUG: Returned CUPS_SC_STATUS_NOT_IMPLEMENTED with no bytes...\n",
1107 		stderr);
1108 	  break;
1109     }
1110   }
1111   while (!g.sidechannel_thread_stop);
1112 
1113   pthread_mutex_lock(&g.sidechannel_thread_mutex);
1114   g.sidechannel_thread_done = 1;
1115   pthread_cond_signal(&g.sidechannel_thread_cond);
1116   pthread_mutex_unlock(&g.sidechannel_thread_mutex);
1117 
1118   return NULL;
1119 }
1120 
1121 
1122 #pragma mark -
1123 /*
1124  * 'iterate_printers()' - Iterate over all the printers.
1125  */
iterate_printers(iterator_callback_t callBack,void * userdata)1126 static void iterate_printers(iterator_callback_t callBack, void *userdata)
1127 {
1128   Iterating = 1;
1129 
1130   iterator_reference_t reference = { callBack, userdata, true };
1131 
1132   IONotificationPortRef addNotification = IONotificationPortCreate(kIOMainPortDefault);
1133 
1134   int printingClass = kUSBPrintingClass;
1135   int printingSubclass = kUSBPrintingSubclass;
1136 
1137   CFNumberRef interfaceClass = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &printingClass);
1138   CFNumberRef interfaceSubClass = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &printingSubclass);
1139 
1140   CFMutableDictionaryRef usbPrinterMatchDictionary = IOServiceMatching(kIOUSBInterfaceClassName);
1141   CFDictionaryAddValue(usbPrinterMatchDictionary, CFSTR("bInterfaceClass"), interfaceClass);
1142   CFDictionaryAddValue(usbPrinterMatchDictionary, CFSTR("bInterfaceSubClass"), interfaceSubClass);
1143 
1144   CFRelease(interfaceClass);
1145   CFRelease(interfaceSubClass);
1146 
1147   io_iterator_t add_iterator = IO_OBJECT_NULL;
1148   IOServiceAddMatchingNotification(addNotification, kIOMatchedNotification,
1149                 usbPrinterMatchDictionary, &device_added, &reference, &add_iterator);
1150   if (add_iterator != IO_OBJECT_NULL)
1151   {
1152     device_added (&reference, add_iterator);
1153     if (reference.keepRunning)
1154     {
1155       CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(addNotification), kCFRunLoopDefaultMode);
1156       CFRunLoopRun();
1157     }
1158     IOObjectRelease(add_iterator);
1159   }
1160   Iterating = 0;
1161 }
1162 
1163 
1164 /*
1165  * 'device_added()' - Device added notifier.
1166  */
device_added(void * userdata,io_iterator_t iterator)1167 static void device_added(void *userdata, io_iterator_t iterator)
1168 {
1169   iterator_reference_t *reference = userdata;
1170   io_service_t intf;
1171 
1172   while (reference->keepRunning && (intf = IOIteratorNext(iterator)) != 0x0)
1173   {
1174     printer_interface_t printerIntf = usb_printer_interface_interface(intf);
1175     if (printerIntf != NULL)
1176     {
1177       UInt8 intfClass = 0, intfSubClass = 0;
1178 
1179       (*printerIntf)->GetInterfaceClass(printerIntf, &intfClass);
1180       (*printerIntf)->GetInterfaceSubClass(printerIntf, &intfSubClass);
1181       if (intfClass == kUSBPrintingInterfaceClass && intfSubClass == kUSBPrintingSubclass)
1182         reference->keepRunning = reference->callback(intf, printerIntf, userdata);
1183         (*printerIntf)->Release(printerIntf);
1184       }
1185       IOObjectRelease(intf);
1186     }
1187 
1188     if (reference->keepRunning && reference->callback)
1189       reference->keepRunning = reference->callback(IO_OBJECT_NULL, NULL, reference->userdata);
1190 
1191     if (!reference->keepRunning)
1192       CFRunLoopStop(CFRunLoopGetCurrent());
1193 }
1194 
1195 /*
1196  * 'list_device_cb()' - list_device iterator callback.
1197  */
list_device_cb(io_service_t obj,printer_interface_t printerIntf,void * refcon)1198 static Boolean list_device_cb(io_service_t obj, printer_interface_t printerIntf, void *refcon)
1199 {
1200   (void)refcon;
1201 
1202   if (obj != IO_OBJECT_NULL)
1203   {
1204     CFStringRef deviceIDString = NULL;
1205     CFStringRef make = NULL;
1206     CFStringRef model = NULL;
1207     CFStringRef serial = NULL;
1208     UInt32 intfLocation;
1209 
1210     deviceIDString = copy_printer_interface_deviceid(printerIntf, 0);
1211     if (deviceIDString == NULL)
1212       goto list_device_done;
1213 
1214     make = deviceIDCopyManufacturer(deviceIDString);
1215     model = deviceIDCopyModel(deviceIDString);
1216     serial = deviceIDCopySerialNumber(deviceIDString);
1217 
1218     char uristr[1024], makestr[1024], modelstr[1024], serialstr[1024];
1219     char optionsstr[1024], idstr[1024], make_modelstr[1024];
1220 
1221     CFStringGetCString(deviceIDString, idstr, sizeof(idstr), kCFStringEncodingUTF8);
1222     backendGetMakeModel(idstr, make_modelstr, sizeof(make_modelstr));
1223 
1224     modelstr[0] = '/';
1225 
1226     if (make  == NULL || !CFStringGetCString(make, makestr, sizeof(makestr), kCFStringEncodingUTF8))
1227       strlcpy(makestr, "Unknown", sizeof(makestr));
1228 
1229     if (model == NULL || !CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1, kCFStringEncodingUTF8))
1230       strlcpy(modelstr + 1, "Printer", sizeof(modelstr) - 1);
1231 
1232     optionsstr[0] = '\0';
1233     if (serial != NULL && CFStringGetCString(serial, serialstr, sizeof(serialstr), kCFStringEncodingUTF8))
1234       snprintf(optionsstr, sizeof(optionsstr), "?serial=%s", serialstr);
1235     else if ((*printerIntf)->GetLocationID(printerIntf, &intfLocation) == kIOReturnSuccess)
1236       snprintf(optionsstr, sizeof(optionsstr), "?location=%x", (unsigned)intfLocation);
1237 
1238     httpAssembleURI(HTTP_URI_CODING_ALL, uristr, sizeof(uristr), "usb", NULL, makestr, 0, modelstr);
1239     strlcat(uristr, optionsstr, sizeof(uristr));
1240 
1241     cupsBackendReport("direct", uristr, make_modelstr, make_modelstr, idstr,
1242                           NULL);
1243   list_device_done:
1244 
1245     if (make != NULL) CFRelease(make);
1246     if (model != NULL) CFRelease(model);
1247     if (serial != NULL) CFRelease(serial);
1248   }
1249   return obj != IO_OBJECT_NULL;
1250 }
1251 
1252 /*
1253  * 'find_device_cb()' - print_device iterator callback.
1254  */
find_device_cb(io_service_t obj,printer_interface_t printerIntf,void * refcon)1255 static Boolean find_device_cb(io_service_t obj, printer_interface_t printerIntf, void *refcon)
1256 {
1257   (void)refcon;
1258 
1259   Boolean keepLooking = true;
1260 
1261   if (obj != IO_OBJECT_NULL)
1262   {
1263     CFStringRef deviceIDString = NULL;
1264     CFStringRef make = NULL;
1265     CFStringRef model = NULL;
1266     CFStringRef serial = NULL;
1267 
1268     deviceIDString = copy_printer_interface_deviceid(printerIntf, 0);
1269     if (deviceIDString == NULL)
1270       goto find_device_done;
1271 
1272     make = deviceIDCopyManufacturer(deviceIDString);
1273     model = deviceIDCopyModel(deviceIDString);
1274     serial = deviceIDCopySerialNumber(deviceIDString);
1275 
1276     if (make && CFStringCompare(make, g.make, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
1277     {
1278       if (model && CFStringCompare(model, g.model, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
1279       {
1280         UInt8 intfAltSetting = 0, intfNumber = 0, intfProtocol = 0;
1281         UInt32 intfLocation = 0;
1282 
1283         (*printerIntf)->GetInterfaceProtocol(printerIntf, &intfProtocol);
1284         (*printerIntf)->GetAlternateSetting(printerIntf, &intfAltSetting);
1285         (*printerIntf)->GetInterfaceNumber(printerIntf, &intfNumber);
1286         (*printerIntf)->GetLocationID(printerIntf, &intfLocation);
1287 
1288         if (intfProtocol == kUSBPrintingProtocolIPP)
1289             return keepLooking;
1290 
1291         if (g.serial != NULL && CFStringGetLength(g.serial) > 0)
1292         {
1293           if (serial != NULL && CFStringCompare(serial, g.serial, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
1294           {
1295             g.interfaceProtocol = intfProtocol;
1296             g.location = intfLocation;
1297             g.alternateSetting = intfAltSetting;
1298             g.printer_obj = obj;
1299             IOObjectRetain(obj);
1300             keepLooking = false;
1301           }
1302         }
1303         else
1304         {
1305           if (g.printer_obj != 0)
1306             IOObjectRelease(g.printer_obj);
1307 
1308             if (g.location == 0 || g.location == intfLocation)
1309                 keepLooking = false;
1310 
1311             g.location = intfLocation;
1312             g.alternateSetting = intfAltSetting;
1313             g.interfaceProtocol = intfProtocol;
1314             g.printer_obj = obj;
1315             IOObjectRetain(obj);
1316         }
1317 
1318         if (!keepLooking)
1319           g.interfaceNum = intfNumber;
1320       }
1321     }
1322 
1323   find_device_done:
1324     if (deviceIDString != NULL) CFRelease(deviceIDString);
1325     if (make != NULL) CFRelease(make);
1326     if (model != NULL) CFRelease(model);
1327     if (serial != NULL) CFRelease(serial);
1328   }
1329   else
1330   {
1331     keepLooking = (g.printer_obj == 0 && g.interfaceProtocol != kUSBPrintingProtocolIPP);
1332     if (obj == IO_OBJECT_NULL && keepLooking)
1333     {
1334       CFRunLoopTimerContext context = { 0, refcon, NULL, NULL, NULL };
1335       CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 1.0, 10, 0x0, 0x0, status_timer_cb, &context);
1336       if (timer != NULL)
1337       {
1338         CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
1339         g.status_timer = timer;
1340       }
1341     }
1342   }
1343 
1344   if (!keepLooking && g.status_timer != NULL)
1345   {
1346     fputs("STATE: -offline-report\n", stderr);
1347     _cupsLangPrintFilter(stderr, "INFO", _("The printer is now online."));
1348     CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), g.status_timer, kCFRunLoopDefaultMode);
1349     CFRelease(g.status_timer);
1350     g.status_timer = NULL;
1351   }
1352 
1353   return keepLooking;
1354 }
1355 
deviceIDCopySerialNumber(CFStringRef deviceID)1356 static CFStringRef deviceIDCopySerialNumber(CFStringRef deviceID)
1357 {
1358     CFStringRef serialKeys[] = { CFSTR("SN:"),  CFSTR("SERN:"), NULL };
1359 
1360     return copy_value_for_key(deviceID, serialKeys);
1361 }
1362 
deviceIDCopyModel(CFStringRef deviceID)1363 static CFStringRef deviceIDCopyModel(CFStringRef deviceID)
1364 {
1365     CFStringRef modelKeys[] = { CFSTR("MDL:"), CFSTR("MODEL:"), NULL };
1366     return copy_value_for_key(deviceID, modelKeys);
1367 }
1368 
deviceIDCopyManufacturer(CFStringRef deviceID)1369 static CFStringRef deviceIDCopyManufacturer(CFStringRef deviceID)
1370 {
1371     CFStringRef makeKeys[]   = { CFSTR("MFG:"), CFSTR("MANUFACTURER:"), NULL };
1372     return copy_value_for_key(deviceID, makeKeys);
1373 }
1374 
1375 /*
1376  * 'status_timer_cb()' - Status timer callback.
1377  */
1378 
status_timer_cb(CFRunLoopTimerRef timer,void * info)1379 static void status_timer_cb(CFRunLoopTimerRef timer,
1380 			    void *info)
1381 {
1382   (void)timer;
1383   (void)info;
1384 
1385   fputs("STATE: +offline-report\n", stderr);
1386   _cupsLangPrintFilter(stderr, "INFO", _("The printer is offline."));
1387 
1388   if (getenv("CLASS") != NULL)
1389   {
1390    /*
1391     * If the CLASS environment variable is set, the job was submitted
1392     * to a class and not to a specific queue.  In this case, we want
1393     * to abort immediately so that the job can be requeued on the next
1394     * available printer in the class.
1395     *
1396     * Sleep 5 seconds to keep the job from requeuing too rapidly...
1397     */
1398 
1399     sleep(5);
1400 
1401     exit(CUPS_BACKEND_FAILED);
1402   }
1403 }
1404 
1405 
1406 #pragma mark -
1407 /*
1408  * 'load_classdriver()' - Load a classdriver.
1409  */
1410 
load_classdriver(CFStringRef driverPath,printer_interface_t interface,classdriver_t *** printerDriver)1411 static kern_return_t load_classdriver(CFStringRef	    driverPath,
1412 				      printer_interface_t   interface,
1413 				      classdriver_t	    ***printerDriver)
1414 {
1415   kern_return_t	kr = kUSBPrinterClassDeviceNotOpen;
1416   classdriver_t	**driver = NULL;
1417   CFStringRef	bundle = driverPath ? driverPath : kUSBGenericTOPrinterClassDriver;
1418   char 		bundlestr[1024];	/* Bundle path */
1419   CFURLRef	url;			/* URL for driver */
1420   CFPlugInRef	plugin = NULL;		/* Plug-in address */
1421 
1422 
1423   CFStringGetCString(bundle, bundlestr, sizeof(bundlestr), kCFStringEncodingUTF8);
1424 
1425  /*
1426   * Validate permissions for the class driver...
1427   */
1428 
1429   _cups_fc_result_t result = _cupsFileCheck(bundlestr,
1430                                             _CUPS_FILE_CHECK_DIRECTORY, 1,
1431                                             Iterating ? NULL : _cupsFileCheckFilter, NULL);
1432 
1433   if (result && driverPath)
1434     return (load_classdriver(NULL, interface, printerDriver));
1435   else if (result)
1436     return (kr);
1437 
1438  /*
1439   * Try loading the class driver...
1440   */
1441 
1442   url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, bundle, kCFURLPOSIXPathStyle, true);
1443 
1444   if (url == NULL)
1445     return (kr);
1446 
1447   plugin = CFPlugInCreate(kCFAllocatorDefault, url);
1448   CFRelease(url);
1449 
1450   if (plugin)
1451   {
1452     CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID, plugin);
1453     if (factories == NULL)
1454       return (kr);
1455     if (CFArrayGetCount(factories) > 0)
1456     {
1457       CFUUIDRef factoryID = CFArrayGetValueAtIndex(factories, 0);
1458       IUnknownVTbl **iunknown = CFPlugInInstanceCreate(kCFAllocatorDefault, factoryID, kUSBPrinterClassTypeID);
1459       if (iunknown != NULL)
1460       {
1461 	kr = (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID), (LPVOID *)&driver);
1462 	if (kr == kIOReturnSuccess && driver != NULL)
1463 	{
1464 	  classdriver_t **genericDriver = NULL;
1465 	  if (driverPath != NULL && CFStringCompare(driverPath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo)
1466 	    kr = load_classdriver(NULL, interface, &genericDriver);
1467 
1468 	  if (kr == kIOReturnSuccess)
1469 	  {
1470 	    (*driver)->interface = interface;
1471 	    (*driver)->Initialize(driver, genericDriver);
1472 
1473 	    (*driver)->plugin = plugin;
1474 	    (*driver)->interface = interface;
1475 	    *printerDriver = driver;
1476 	  }
1477 	}
1478 	(*iunknown)->Release(iunknown);
1479       }
1480     }
1481     CFRelease(factories);
1482   }
1483 
1484   fprintf(stderr, "DEBUG: load_classdriver(%s) (kr:0x%08x)\n", bundlestr, (int)kr);
1485 
1486   return (kr);
1487 }
1488 
1489 
1490 /*
1491  * 'unload_classdriver()' - Unload a classdriver.
1492  */
1493 
unload_classdriver(classdriver_t *** classdriver)1494 static kern_return_t unload_classdriver(classdriver_t ***classdriver)
1495 {
1496   if (*classdriver != NULL)
1497   {
1498     (**classdriver)->Release(*classdriver);
1499     *classdriver = NULL;
1500   }
1501 
1502   return kIOReturnSuccess;
1503 }
1504 
1505 
1506 /*
1507  * 'load_printerdriver()' - Load vendor's classdriver.
1508  *
1509  * If driverBundlePath is not NULL on return it is the callers responsbility to release it!
1510  */
1511 
load_printerdriver(CFStringRef * driverBundlePath)1512 static kern_return_t load_printerdriver(CFStringRef *driverBundlePath)
1513 {
1514   IOCFPlugInInterface	**iodev = NULL;
1515   SInt32		score;
1516   kern_return_t		kr;
1517   printer_interface_t	interface;
1518 
1519   kr = IOCreatePlugInInterfaceForService(g.printer_obj, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
1520   if (kr == kIOReturnSuccess)
1521   {
1522     if ((*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &interface) == noErr)
1523     {
1524       *driverBundlePath = IORegistryEntryCreateCFProperty(g.printer_obj, kUSBClassDriverProperty, NULL, kNilOptions);
1525 
1526       g.use_generic_class_driver = (*driverBundlePath == NULL || (CFStringCompare(*driverBundlePath, kUSBGenericTOPrinterClassDriver, 0x0) == kCFCompareEqualTo));
1527       kr = load_classdriver(*driverBundlePath, interface, &g.classdriver);
1528 
1529       if (kr != kIOReturnSuccess)
1530 	(*interface)->Release(interface);
1531     }
1532     IODestroyPlugInInterface(iodev);
1533   }
1534   return kr;
1535 }
1536 
usb_printer_interface_interface(io_service_t usbClass)1537 static printer_interface_t usb_printer_interface_interface(io_service_t usbClass)
1538 {
1539 	printer_interface_t  intf = NULL;
1540 	IOCFPlugInInterface **plugin = NULL;
1541 	SInt32	score;
1542 	int kr = IOCreatePlugInInterfaceForService(usbClass, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score);
1543 	if (kr == kIOReturnSuccess)
1544 	{
1545 		(*plugin)->QueryInterface(plugin, USB_INTERFACE_KIND, (LPVOID *)&intf);
1546 		IODestroyPlugInInterface(plugin);
1547 	}
1548 
1549 	return intf;
1550 }
1551 
copy_printer_interface_deviceid(printer_interface_t printer,UInt8 alternateSetting)1552 static CFStringRef copy_printer_interface_deviceid(printer_interface_t printer, UInt8 alternateSetting)
1553 {
1554 	// I have tried to make this function as neat as I can, but the possibility of needing to resend
1555 	// a request to get the entire string makes it hideous...
1556 	//
1557 	// We package the job of sending a request up into the block (^sendRequest), which takes the size
1558 	// it should allocate for the message buffer. It frees the current buffer if one is set and
1559 	// allocates one of the specified size, then performs the request. We can then easily retry by
1560 	// calling the block again if we fail to get the whole string the first time around.
1561 
1562 	#define kUSBPrintClassGetDeviceID           0
1563 	#define kDefaultNoDataTimeout               5000L
1564 	#define pack_device_id_wIndex(intf, alt)  ((UInt16)((((UInt16)(intf)) << 8) | ((UInt8)(alt))))
1565 
1566 	if (printer == NULL)
1567 			return NULL;
1568 
1569 
1570 	IOReturn err        = kIOReturnError;
1571 	UInt8    configurationIndex	= 0;
1572 	UInt8    interfaceNumber	= 0;
1573 	size_t   bufferLength		= 256;
1574 	CFStringRef ret             = NULL;
1575 
1576 	if ((*printer)->GetConfigurationValue( printer, &configurationIndex) == kIOReturnSuccess &&
1577 			(*printer)->GetInterfaceNumber( printer, &interfaceNumber) == kIOReturnSuccess)
1578 	{
1579 		__block IOUSBDevRequestTO	request;
1580 		IOReturn (^sendRequest)(size_t) = ^ (size_t size)
1581 		{
1582 			if (request.pData)
1583 			{
1584 				free(request.pData);
1585 				request.wLength = 0;
1586 				request.pData = NULL;
1587 			}
1588 
1589 			IOReturn berr = kIOReturnError;
1590 			char *buffer = malloc(size);
1591 			if (buffer == NULL)
1592 				return kIOReturnNoMemory;
1593 
1594 			request.wLength = HostToUSBWord(size);
1595 			request.pData = buffer;
1596 			berr = (*printer)->ControlRequestTO(printer, 0, &request);
1597 			return berr;
1598 		};
1599 
1600 		/* This request takes the 0 based configuration index. IOKit returns a 1 based configuration index */
1601 		configurationIndex -= 1;
1602 
1603 		memset(&request, 0, sizeof(request));
1604 
1605 		request.bmRequestType		= USBmakebmRequestType(kUSBIn, kUSBClass, kUSBInterface);
1606 		request.bRequest			= kUSBPrintClassGetDeviceID;
1607 		request.wValue				= HostToUSBWord(configurationIndex);
1608 		request.wIndex				= HostToUSBWord(pack_device_id_wIndex(interfaceNumber, alternateSetting));
1609 		request.noDataTimeout		= kDefaultNoDataTimeout;
1610 		request.completionTimeout	= 0; // Copying behavior from Generic Class Driver
1611 
1612 		err = sendRequest(bufferLength);
1613 
1614 		if (err == kIOReturnSuccess && request.wLenDone > 1)
1615 		{
1616 			UInt16 actualLength = OSSwapBigToHostInt16(*((UInt16 *)request.pData));
1617 
1618 			if (actualLength > 2 && actualLength <= bufferLength - 2)
1619 			{
1620 				ret = CFStringCreateWithBytes(NULL, (const UInt8 *)request.pData + 2, actualLength - 2, kCFStringEncodingUTF8, false);
1621 			}
1622 			else if (actualLength > 2) {
1623 				err = sendRequest(actualLength);
1624 				if (err == kIOReturnSuccess && request.wLenDone > 0)
1625 				{
1626 					actualLength = OSSwapBigToHostInt16(*((UInt16 *)request.pData));
1627 					ret = CFStringCreateWithBytes(NULL, (const UInt8 *)request.pData + 2, actualLength - 2, kCFStringEncodingUTF8, false);
1628 				}
1629 			}
1630 		}
1631 
1632 		if (request.pData)
1633 			free(request.pData);
1634 	}
1635 
1636 	CFStringRef manufacturer = deviceIDCopyManufacturer(ret);
1637 	CFStringRef model = deviceIDCopyModel(ret);
1638 	CFStringRef serial = deviceIDCopySerialNumber(ret);
1639 
1640 	if (manufacturer == NULL || serial == NULL || model == NULL)
1641 	{
1642 		IOUSBDevRequestTO		request;
1643 		IOUSBDeviceDescriptor	desc;
1644 
1645 		memset(&request, 0, sizeof(request));
1646 
1647 		request.bmRequestType = USBmakebmRequestType( kUSBIn,  kUSBStandard, kUSBDevice );
1648 		request.bRequest = kUSBRqGetDescriptor;
1649 		request.wValue = kUSBDeviceDesc << 8;
1650 		request.wIndex = 0;
1651 		request.wLength = sizeof(desc);
1652 		request.pData = &desc;
1653 		request.completionTimeout = 0;
1654 		request.noDataTimeout = 60L;
1655 
1656 		err = (*printer)->ControlRequestTO(printer, 0, &request);
1657 		if (err == kIOReturnSuccess)
1658 		{
1659 			CFMutableStringRef extras = CFStringCreateMutable(kCFAllocatorDefault, 0);
1660 			if (manufacturer == NULL)
1661 			{
1662 				manufacturer = copy_printer_interface_indexed_description(printer, desc.iManufacturer, kUSBLanguageEnglish);
1663 				if (manufacturer && CFStringGetLength(manufacturer) > 0)
1664 					CFStringAppendFormat(extras, NULL, CFSTR("MFG:%@;"), manufacturer);
1665 			}
1666 
1667 			if (model == NULL)
1668 			{
1669 				model = copy_printer_interface_indexed_description(printer, desc.iProduct, kUSBLanguageEnglish);
1670 				if (model && CFStringGetLength(model) > 0)
1671 					CFStringAppendFormat(extras, NULL, CFSTR("MDL:%@;"), model);
1672 			}
1673 
1674 			if (desc.iSerialNumber != 0)
1675 			{
1676 				// Always look at the USB serial number since some printers
1677 				// incorrectly include a bogus static serial number in their
1678 				// IEEE-1284 device ID string...
1679 				CFStringRef userial = copy_printer_interface_indexed_description(printer, desc.iSerialNumber, kUSBLanguageEnglish);
1680 				if (userial && CFStringGetLength(userial) > 0 && (serial == NULL || CFStringCompare(serial, userial, kCFCompareCaseInsensitive) != kCFCompareEqualTo))
1681 				{
1682 					if (serial != NULL)
1683 					{
1684 						// 1284 serial number doesn't match USB serial number, so  replace the existing SERN: in device ID
1685 						CFRange range = CFStringFind(ret, serial, 0);
1686 						CFMutableStringRef deviceIDString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, ret);
1687 						CFStringReplace(deviceIDString, range, userial);
1688 						CFRelease(ret);
1689 						ret = deviceIDString;
1690 
1691 						CFRelease(serial);
1692 					}
1693 					else
1694 					{
1695 						// No 1284 serial number so add SERN: with USB serial number to device ID
1696 						CFStringAppendFormat(extras, NULL, CFSTR("SERN:%@;"), userial);
1697 					}
1698 					serial = userial;
1699 				}
1700 				else if (userial != NULL)
1701 					CFRelease(userial);
1702 			}
1703 
1704 			if (ret != NULL)
1705 			{
1706 				CFStringAppend(extras, ret);
1707 				CFRelease(ret);
1708 			}
1709       ret = extras;
1710 		}
1711 	}
1712 
1713 	if (ret != NULL)
1714 	{
1715 		/* Remove special characters from the serial number */
1716 		CFRange range = (serial != NULL ? CFStringFind(serial, CFSTR("+"), 0) : CFRangeMake(0, 0));
1717 		if (range.length == 1)
1718 		{
1719 			range = CFStringFind(ret, serial, 0);
1720 
1721 			CFMutableStringRef deviceIDString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, ret);
1722 			CFRelease(ret);
1723 
1724 			ret = deviceIDString;
1725 			CFStringFindAndReplace(deviceIDString, CFSTR("+"), CFSTR(""), range, 0);
1726 		}
1727 	}
1728 
1729 	if (manufacturer != NULL)
1730 		CFRelease(manufacturer);
1731 
1732 	if (model != NULL)
1733 		CFRelease(model);
1734 
1735 	if (serial != NULL)
1736 		CFRelease(serial);
1737 
1738 	if (ret != NULL && CFStringGetLength(ret) == 0)
1739 	{
1740 		CFRelease(ret);
1741 		return NULL;
1742 	}
1743 
1744 	return ret;
1745 }
1746 
copy_printer_interface_indexed_description(printer_interface_t printer,UInt8 index,UInt16 language)1747 static CFStringRef copy_printer_interface_indexed_description(printer_interface_t  printer, UInt8 index, UInt16 language)
1748 {
1749 	IOReturn err;
1750 	UInt8 description[256]; // Max possible descriptor length
1751 	IOUSBDevRequestTO	request;
1752 
1753 	description[0] = description[1] = 0;
1754 
1755 	request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
1756 	request.bRequest = kUSBRqGetDescriptor;
1757 	request.wValue = (kUSBStringDesc << 8) | index;
1758 	request.wIndex = language;
1759 	request.wLength = 2;
1760 	request.pData = &description;
1761 	request.completionTimeout = 0;
1762 	request.noDataTimeout = 60U;
1763 
1764 	err = (*printer)->ControlRequestTO(printer, 0, &request);
1765 	if (err != kIOReturnSuccess && err != kIOReturnOverrun)
1766 	{
1767 		memset(description, 0, request.wLength);
1768 
1769 		// Let's try again full length. Here's why:
1770 		//      On USB 2.0 controllers, we will not get an overrun error.  We just get a "babble" error
1771 		//      and no valid data.  So, if we ask for the max size, we will either get it, or we'll get an underrun.
1772 		//      It looks like we get it w/out an underrun
1773 
1774 		request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
1775 		request.bRequest = kUSBRqGetDescriptor;
1776 		request.wValue = (kUSBStringDesc << 8) | index;
1777 		request.wIndex = language;
1778 		request.wLength = sizeof description;
1779 		request.pData = &description;
1780 		request.completionTimeout = 0;
1781 		request.noDataTimeout = 60U;
1782 
1783 		err = (*printer)->ControlRequestTO(printer, 0, &request);
1784 		if (err != kIOReturnSuccess && err != kIOReturnUnderrun)
1785 			return NULL;
1786 	}
1787 
1788 	UInt8 length = description[0];
1789 	if (length == 0)
1790 		return CFSTR("");
1791 
1792 	if (description[1] != kUSBStringDesc)
1793 		return NULL;
1794 
1795 	request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
1796 	request.bRequest = kUSBRqGetDescriptor;
1797 	request.wValue = (kUSBStringDesc << 8) | index;
1798 	request.wIndex = language;
1799 
1800 	memset(description, 0, length);
1801 	request.wLength = (UInt16)length;
1802 	request.pData = &description;
1803 	request.completionTimeout = 0;
1804 	request.noDataTimeout = 60U;
1805 
1806 	err = (*printer)->ControlRequestTO(printer, 0, &request);
1807 	if (err != kIOReturnSuccess)
1808 		return NULL;
1809 
1810 	if (description[1] != kUSBStringDesc)
1811 		return NULL;
1812 
1813 	description[0] &= ~1;
1814 
1815 	if (description[0] < 2)
1816 		return CFSTR("");
1817 
1818 	char buffer[(sizeof(description) - 2) / 2];
1819 	length = (description[0] - 2) / 2;
1820 
1821 	for (UInt8 i = 0; i < length; i++)
1822 		buffer[i] = (char) description[2 * i + 2];
1823 
1824 	buffer[length] = 0;
1825 
1826 	return CFStringCreateWithCString(kCFAllocatorDefault, buffer, kCFStringEncodingUTF8);
1827 }
1828 
1829 /*
1830  * 'registry_open()' - Open a connection to the printer.
1831  */
1832 
registry_open(CFStringRef * driverBundlePath)1833 static kern_return_t registry_open(CFStringRef *driverBundlePath)
1834 {
1835   g.bidi_flag = 0;	/* 0=unidirectional */
1836 
1837   kern_return_t kr = load_printerdriver(driverBundlePath);
1838   if (kr != kIOReturnSuccess)
1839     kr = -2;
1840 
1841   if (g.classdriver != NULL)
1842   {
1843   	(*g.classdriver)->interfaceNumber = g.interfaceNum;
1844     kr = (*g.classdriver)->Open(g.classdriver, g.location, kUSBPrintingProtocolBidirectional);
1845     if (kr != kIOReturnSuccess || (*g.classdriver)->interface == NULL)
1846     {
1847       kr = (*g.classdriver)->Open(g.classdriver, g.location, kUSBPrintingProtocolUnidirectional);
1848       if (kr == kIOReturnSuccess)
1849       {
1850 	if ((*g.classdriver)->interface == NULL)
1851 	{
1852 	  (*g.classdriver)->Close(g.classdriver);
1853 	  kr = -1;
1854 	}
1855       }
1856     }
1857     else
1858       g.bidi_flag = 1;	/* 1=bidirectional */
1859   }
1860 
1861   if (kr != kIOReturnSuccess)
1862     unload_classdriver(&g.classdriver);
1863 
1864   return kr;
1865 }
1866 
1867 
1868 /*
1869  * 'registry_close()' - Close the connection to the printer.
1870  */
1871 
registry_close(void)1872 static kern_return_t registry_close(void)
1873 {
1874   if (g.classdriver != NULL)
1875     (*g.classdriver)->Close(g.classdriver);
1876 
1877   unload_classdriver(&g.classdriver);
1878   return kIOReturnSuccess;
1879 }
1880 
1881 #pragma mark -
1882 /*
1883  * 'copy_value_for_key()' - Copy value string associated with a key.
1884  */
1885 
copy_value_for_key(CFStringRef deviceID,CFStringRef * keys)1886 static CFStringRef copy_value_for_key(CFStringRef deviceID,
1887                                       CFStringRef *keys)
1888 {
1889   CFStringRef value = NULL; /* Value to return */
1890   CFArrayRef kvPairs;       /* pairs derived from separating the device ID*/
1891   CFIndex max;              /* The size of the array*/
1892 
1893   if (deviceID == NULL)
1894     return NULL;
1895 
1896   kvPairs = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, deviceID, CFSTR(";"));
1897   max = CFArrayGetCount(kvPairs);
1898 
1899   for (CFIndex idx = 0; idx < max; idx++)
1900   {
1901     CFStringRef kvpair = CFArrayGetValueAtIndex(kvPairs, idx);
1902     CFIndex idxx = 0;
1903     for (idxx = 0; keys[idxx] != NULL; idxx++)
1904     {
1905       CFRange range = CFStringFind(kvpair, keys[idxx], kCFCompareCaseInsensitive);
1906       if (range.length != kCFNotFound)
1907       {
1908 	if (range.location != 0)
1909 	{
1910 	  CFMutableStringRef theString = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, kvpair);
1911 	  CFStringTrimWhitespace(theString);
1912 	  range = CFStringFind(theString, keys[idxx], kCFCompareCaseInsensitive);
1913 	  if (range.location == 0)
1914 	    value = CFStringCreateWithSubstring(kCFAllocatorDefault, theString, CFRangeMake(range.length, CFStringGetLength(theString) - range.length));
1915 
1916 	  CFRelease(theString);
1917 	}
1918 	else
1919 	{
1920 	  CFStringRef theString = CFStringCreateWithSubstring(kCFAllocatorDefault, kvpair, CFRangeMake(range.length, CFStringGetLength(kvpair) - range.length));
1921 	  CFMutableStringRef theString2 = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, theString);
1922 	  CFRelease(theString);
1923 
1924 	  CFStringTrimWhitespace(theString2);
1925 	  value = theString2;
1926 	}
1927       }
1928 
1929       if (value != NULL)
1930       {
1931         CFRelease(kvPairs);
1932         return value;
1933       }
1934     }
1935   }
1936 
1937   CFRelease(kvPairs);
1938   return NULL;
1939 }
1940 
1941 
1942 /*
1943  * 'cfstr_create_trim()' - Create CFString and trim whitespace characters.
1944  */
1945 
cfstr_create_trim(const char * cstr)1946 CFStringRef cfstr_create_trim(const char *cstr)
1947 {
1948   CFStringRef		cfstr;
1949   CFMutableStringRef	cfmutablestr = NULL;
1950 
1951   if ((cfstr = CFStringCreateWithCString(kCFAllocatorDefault, cstr, kCFStringEncodingUTF8)) != NULL)
1952   {
1953     if ((cfmutablestr = CFStringCreateMutableCopy(kCFAllocatorDefault, 1024, cfstr)) != NULL)
1954       CFStringTrimWhitespace(cfmutablestr);
1955 
1956     CFRelease(cfstr);
1957   }
1958   return (CFStringRef) cfmutablestr;
1959 }
1960 
1961 
1962 #pragma mark -
1963 /*
1964  * 'parse_options()' - Parse URI options.
1965  */
1966 
parse_options(char * options,char * serial,int serial_size,UInt32 * location,Boolean * wait_eof)1967 static void parse_options(char *options,
1968 			  char *serial,
1969 			  int serial_size,
1970 			  UInt32 *location,
1971 			  Boolean *wait_eof)
1972 {
1973   char	sep,				/* Separator character */
1974 	*name,				/* Name of option */
1975 	*value;				/* Value of option */
1976 
1977 
1978   if (serial)
1979     *serial = '\0';
1980   if (location)
1981     *location = 0;
1982 
1983   if (!options)
1984     return;
1985 
1986   while (*options)
1987   {
1988    /*
1989     * Get the name...
1990     */
1991 
1992     name = options;
1993 
1994     while (*options && *options != '=' && *options != '+' && *options != '&')
1995       options ++;
1996 
1997     if ((sep = *options) != '\0')
1998       *options++ = '\0';
1999 
2000     if (sep == '=')
2001     {
2002      /*
2003       * Get the value...
2004       */
2005 
2006       value = options;
2007 
2008       while (*options && *options != '+' && *options != '&')
2009 	options ++;
2010 
2011       if (*options)
2012 	*options++ = '\0';
2013     }
2014     else
2015       value = (char *)"";
2016 
2017    /*
2018     * Process the option...
2019     */
2020 
2021     if (!_cups_strcasecmp(name, "waiteof"))
2022     {
2023       if (!_cups_strcasecmp(value, "on") ||
2024 	  !_cups_strcasecmp(value, "yes") ||
2025 	  !_cups_strcasecmp(value, "true"))
2026 	*wait_eof = true;
2027       else if (!_cups_strcasecmp(value, "off") ||
2028 	       !_cups_strcasecmp(value, "no") ||
2029 	       !_cups_strcasecmp(value, "false"))
2030 	*wait_eof = false;
2031       else
2032 	_cupsLangPrintFilter(stderr, "WARNING",
2033 			     _("Boolean expected for waiteof option \"%s\"."),
2034 			     value);
2035     }
2036     else if (!_cups_strcasecmp(name, "serial"))
2037       strlcpy(serial, value, (size_t)serial_size);
2038     else if (!_cups_strcasecmp(name, "location") && location)
2039       *location = (UInt32)strtoul(value, NULL, 16);
2040   }
2041 }
2042 
2043 
2044 /*!
2045  * @function	setup_cfLanguage
2046  * @abstract	Convert the contents of the CUPS 'APPLE_LANGUAGE' environment
2047  *		variable into a one element CF array of languages.
2048  *
2049  * @discussion	Each submitted job comes with a natural language. CUPS passes
2050  * 		that language in an environment variable. We take that language
2051  * 		and jam it into the AppleLanguages array so that CF will use
2052  * 		it when reading localized resources. We need to do this before
2053  * 		any CF code reads and caches the languages array, so this function
2054  *		should be called early in main()
2055  */
setup_cfLanguage(void)2056 static void setup_cfLanguage(void)
2057 {
2058   CFStringRef lang[1] = {NULL}; /* StringRef used to create the array */
2059   CFArrayRef langArray;      /* The array used to set the language perference */
2060   const char *requestedLang; /* The language as retrived from the language
2061                                 environment variable */
2062 
2063   if ((requestedLang = getenv("APPLE_LANGUAGE")) == NULL)
2064     requestedLang = getenv("LANG");
2065 
2066   if (requestedLang == NULL)
2067   {
2068     fputs("DEBUG: usb: LANG and APPLE_LANGUAGE environment variables missing.\n", stderr);
2069     return;
2070   }
2071 
2072   lang[0] = CFStringCreateWithCString(kCFAllocatorDefault, requestedLang, kCFStringEncodingUTF8);
2073   langArray = CFArrayCreate(kCFAllocatorDefault, (const void **)lang, sizeof(lang) / sizeof(lang[0]), &kCFTypeArrayCallBacks);
2074 
2075   CFPreferencesSetValue(CFSTR("AppleLanguages"), langArray, kCFPreferencesCurrentApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
2076   fprintf(stderr, "DEBUG: usb: AppleLanguages=\"%s\"\n", requestedLang);
2077 
2078   CFRelease(lang[0]);
2079   CFRelease(langArray);
2080 }
2081 
2082 #pragma mark -
2083 #if defined(__arm64e__)
2084 /*!
2085  * @function	run_legacy_backend
2086  *
2087  * @abstract	Starts child backend process running as a x86_64 executable.
2088  *
2089  * @result	Never returns; always calls exit().
2090  *
2091  * @discussion
2092  */
run_legacy_backend(int argc,char * argv[],int fd)2093 static void run_legacy_backend(int argc,
2094 			       char *argv[],
2095 			       int fd)
2096 {
2097   size_t i;
2098   int	exitstatus = 0;
2099   int	childstatus;
2100   pid_t	waitpid_status;
2101   char	*my_argv[32];
2102   char	*usb_legacy_status;
2103 
2104 
2105  /*
2106   * If we're running as ARM and couldn't load the class driver
2107   * (because it's x86_64, i386 or ppc), then try to re-exec ourselves in x86_64
2108   * mode to try again. If we don't have that architecture we may be
2109   * running with the same architecture again so guard against this by setting
2110   * and testing an environment variable...
2111   */
2112 
2113   usb_legacy_status = getenv("USB_LEGACY_STATUS");
2114 
2115   if (!usb_legacy_status)
2116   {
2117    /*
2118     * Setup a SIGTERM handler then block it before forking...
2119     */
2120 
2121     int			err;		/* posix_spawn result */
2122     struct sigaction	action;		/* POSIX signal action */
2123     sigset_t		newmask,	/* New signal mask */
2124 			oldmask;	/* Old signal mask */
2125     char		usbpath[1024];	/* Path to USB backend */
2126     const char		*cups_serverbin;/* Path to CUPS binaries */
2127 
2128 
2129     memset(&action, 0, sizeof(action));
2130     sigaddset(&action.sa_mask, SIGTERM);
2131     action.sa_handler = sigterm_handler;
2132     sigaction(SIGTERM, &action, NULL);
2133 
2134     sigemptyset(&newmask);
2135     sigaddset(&newmask, SIGTERM);
2136     sigprocmask(SIG_BLOCK, &newmask, &oldmask);
2137 
2138    /*
2139     * Set the environment variable...
2140     */
2141 
2142     setenv("USB_LEGACY_STATUS", "1", false);
2143 
2144    /*
2145     * Tell the kernel to use the specified CPU architecture...
2146     */
2147 
2148     cpu_type_t cpu = CPU_TYPE_X86_64;
2149     size_t ocount = 1;
2150     posix_spawnattr_t attrs;
2151 
2152     if (!posix_spawnattr_init(&attrs))
2153     {
2154       posix_spawnattr_setsigdefault(&attrs, &oldmask);
2155       if (posix_spawnattr_setbinpref_np(&attrs, 1, &cpu, &ocount) || ocount != 1)
2156       {
2157 	perror("DEBUG: Unable to set binary preference to X86_64");
2158 	_cupsLangPrintFilter(stderr, "ERROR",
2159 	                     _("Unable to use legacy USB class driver."));
2160 	exit(CUPS_BACKEND_STOP);
2161       }
2162     }
2163 
2164    /*
2165     * Set up the arguments and call posix_spawn...
2166     */
2167 
2168     if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
2169       cups_serverbin = CUPS_SERVERBIN;
2170     snprintf(usbpath, sizeof(usbpath), "%s/backend/usb", cups_serverbin);
2171 
2172     for (i = 0; i < argc && i < (sizeof(my_argv) / sizeof(my_argv[0])) - 1; i++)
2173       my_argv[i] = argv[i];
2174 
2175     my_argv[i] = NULL;
2176 
2177     if ((err = posix_spawn(&child_pid, usbpath, NULL, &attrs, my_argv,
2178                            environ)) != 0)
2179     {
2180       fprintf(stderr, "DEBUG: Unable to exec %s: %s\n", usbpath,
2181               strerror(err));
2182       _cupsLangPrintFilter(stderr, "ERROR",
2183                            _("Unable to use legacy USB class driver."));
2184       exit(CUPS_BACKEND_STOP);
2185     }
2186 
2187    /*
2188     * Unblock signals...
2189     */
2190 
2191     sigprocmask(SIG_SETMASK, &oldmask, NULL);
2192 
2193    /*
2194     * Close the fds we won't be using then wait for the child backend to exit.
2195     */
2196 
2197     close(fd);
2198     close(1);
2199 
2200     fprintf(stderr, "DEBUG: Started usb(legacy) backend (PID %d)\n",
2201             (int)child_pid);
2202 
2203     while ((waitpid_status = waitpid(child_pid, &childstatus, 0)) == (pid_t)-1 && errno == EINTR)
2204       usleep(1000);
2205 
2206     if (WIFSIGNALED(childstatus))
2207     {
2208       exitstatus = CUPS_BACKEND_STOP;
2209       fprintf(stderr, "DEBUG: usb(legacy) backend %d crashed on signal %d\n",
2210               child_pid, WTERMSIG(childstatus));
2211     }
2212     else
2213     {
2214       if ((exitstatus = WEXITSTATUS(childstatus)) != 0)
2215 	fprintf(stderr,
2216 	        "DEBUG: usb(legacy) backend %d stopped with status %d\n",
2217 		child_pid, exitstatus);
2218       else
2219 	fprintf(stderr, "DEBUG: usb(legacy) backend %d exited with no errors\n",
2220 	        child_pid);
2221     }
2222   }
2223   else
2224   {
2225     fputs("DEBUG: usb(legacy) backend running native again\n", stderr);
2226     exitstatus = CUPS_BACKEND_STOP;
2227   }
2228 
2229   exit(exitstatus);
2230 }
2231 
2232 /*
2233  * 'sigterm_handler()' - SIGTERM handler.
2234  */
2235 
2236 static void
sigterm_handler(int sig)2237 sigterm_handler(int sig)		/* I - Signal */
2238 {
2239  /*
2240   * If we started a child process pass the signal on to it...
2241   */
2242 
2243   if (child_pid)
2244   {
2245    /*
2246     * If we started a child process pass the signal on to it...
2247     */
2248 
2249     int	status;
2250 
2251     kill(child_pid, sig);
2252     while (waitpid(child_pid, &status, 0) < 0 && errno == EINTR);
2253 
2254     if (WIFEXITED(status))
2255       _exit(WEXITSTATUS(status));
2256     else if (status == SIGTERM || status == SIGKILL)
2257       _exit(0);
2258     else
2259     {
2260       backendMessage("DEBUG: Child crashed.\n");
2261       _exit(CUPS_BACKEND_STOP);
2262     }
2263   }
2264 }
2265 #endif /* __arm64e__ */
2266 
2267 
2268 /*
2269  * 'sigquit_handler()' - SIGQUIT handler.
2270  */
2271 
sigquit_handler(int sig,siginfo_t * si,void * unused)2272 static void sigquit_handler(int sig, siginfo_t *si, void *unused)
2273 {
2274   char  *path;
2275   char	pathbuf[PROC_PIDPATHINFO_MAXSIZE];
2276   static char msgbuf[256] = "";
2277 
2278 
2279   (void)sig;
2280   (void)unused;
2281 
2282   if (proc_pidpath(si->si_pid, pathbuf, sizeof(pathbuf)) > 0 &&
2283       (path = basename(pathbuf)) != NULL)
2284     snprintf(msgbuf, sizeof(msgbuf), "SIGQUIT sent by %s(%d)", path, (int)si->si_pid);
2285   else
2286     snprintf(msgbuf, sizeof(msgbuf), "SIGQUIT sent by PID %d", (int)si->si_pid);
2287 
2288   CRSetCrashLogMessage(msgbuf);
2289 
2290   abort();
2291 }
2292 
2293 
2294 #ifdef PARSE_PS_ERRORS
2295 /*
2296  * 'next_line()' - Find the next line in a buffer.
2297  */
2298 
next_line(const char * buffer)2299 static const char *next_line (const char *buffer)
2300 {
2301   const char *cptr, *lptr = NULL;
2302 
2303   for (cptr = buffer; *cptr && lptr == NULL; cptr++)
2304     if (*cptr == '\n' || *cptr == '\r')
2305       lptr = cptr;
2306   return lptr;
2307 }
2308 
2309 
2310 /*
2311  * 'parse_pserror()' - Scan the backchannel data for postscript errors.
2312  */
2313 
parse_pserror(char * sockBuffer,int len)2314 static void parse_pserror(char *sockBuffer,
2315 			  int len)
2316 {
2317   static char  gErrorBuffer[1024] = "";
2318   static char *gErrorBufferPtr = gErrorBuffer;
2319   static char *gErrorBufferEndPtr = gErrorBuffer + sizeof(gErrorBuffer);
2320 
2321   char *pCommentBegin, *pCommentEnd, *pLineEnd;
2322   char *logLevel;
2323   char logstr[1024];
2324   int  logstrlen;
2325 
2326   if (gErrorBufferPtr + len > gErrorBufferEndPtr - 1)
2327     gErrorBufferPtr = gErrorBuffer;
2328   if (len > sizeof(gErrorBuffer) - 1)
2329     len = sizeof(gErrorBuffer) - 1;
2330 
2331   memcpy(gErrorBufferPtr, (const void *)sockBuffer, len);
2332   gErrorBufferPtr += len;
2333   *(gErrorBufferPtr + 1) = '\0';
2334 
2335   pLineEnd = (char *)next_line((const char *)gErrorBuffer);
2336   while (pLineEnd != NULL)
2337   {
2338     *pLineEnd++ = '\0';
2339 
2340     pCommentBegin = strstr(gErrorBuffer,"%%[");
2341     pCommentEnd = strstr(gErrorBuffer, "]%%");
2342     if (pCommentBegin != gErrorBuffer && pCommentEnd != NULL)
2343     {
2344       pCommentEnd += 3;            /* Skip past "]%%" */
2345       *pCommentEnd = '\0';         /* There's always room for the nul */
2346 
2347       if (_cups_strncasecmp(pCommentBegin, "%%[ Error:", 10) == 0)
2348 	logLevel = "DEBUG";
2349       else if (_cups_strncasecmp(pCommentBegin, "%%[ Flushing", 12) == 0)
2350 	logLevel = "DEBUG";
2351       else
2352 	logLevel = "INFO";
2353 
2354       if ((logstrlen = snprintf(logstr, sizeof(logstr), "%s: %s\n", logLevel, pCommentBegin)) >= sizeof(logstr))
2355       {
2356 	/* If the string was truncated make sure it has a linefeed before the nul */
2357 	logstrlen = sizeof(logstr) - 1;
2358 	logstr[logstrlen - 1] = '\n';
2359       }
2360       write(STDERR_FILENO, logstr, logstrlen);
2361     }
2362 
2363     /* move everything over... */
2364     strlcpy(gErrorBuffer, pLineEnd, sizeof(gErrorBuffer));
2365     gErrorBufferPtr = gErrorBuffer;
2366     pLineEnd = (char *)next_line((const char *)gErrorBuffer);
2367   }
2368 }
2369 #endif /* PARSE_PS_ERRORS */
2370 
2371 
2372 /*
2373  * 'soft_reset()' - Send a soft reset to the device.
2374  */
2375 
soft_reset(void)2376 static void soft_reset(void)
2377 {
2378   fd_set	  input_set;		/* Input set for select() */
2379   struct timeval  tv;			/* Time value */
2380   char		  buffer[2048];		/* Buffer */
2381   struct timespec cond_timeout;		/* pthread condition timeout */
2382 
2383  /*
2384   * Send an abort once a second until the I/O lock is released by the main thread...
2385   */
2386 
2387   pthread_mutex_lock(&g.readwrite_lock_mutex);
2388   while (g.readwrite_lock)
2389   {
2390     (*g.classdriver)->Abort(g.classdriver);
2391 
2392     gettimeofday(&tv, NULL);
2393     cond_timeout.tv_sec  = tv.tv_sec + 1;
2394     cond_timeout.tv_nsec = tv.tv_usec * 1000;
2395 
2396     while (g.readwrite_lock)
2397     {
2398       if (pthread_cond_timedwait(&g.readwrite_lock_cond,
2399 				 &g.readwrite_lock_mutex,
2400 				 &cond_timeout) != 0)
2401 	break;
2402     }
2403   }
2404 
2405   g.readwrite_lock = 1;
2406   pthread_mutex_unlock(&g.readwrite_lock_mutex);
2407 
2408  /*
2409   * Flush bytes waiting on print_fd...
2410   */
2411 
2412   g.print_bytes = 0;
2413 
2414   FD_ZERO(&input_set);
2415   FD_SET(g.print_fd, &input_set);
2416 
2417   tv.tv_sec  = 0;
2418   tv.tv_usec = 0;
2419 
2420   while (select(g.print_fd+1, &input_set, NULL, NULL, &tv) > 0)
2421     if (read(g.print_fd, buffer, sizeof(buffer)) <= 0)
2422       break;
2423 
2424  /*
2425   * Send the reset...
2426   */
2427 
2428   (*g.classdriver)->SoftReset(g.classdriver, DEFAULT_TIMEOUT);
2429 
2430  /*
2431   * Release the I/O lock...
2432   */
2433 
2434   pthread_mutex_lock(&g.readwrite_lock_mutex);
2435   g.readwrite_lock = 0;
2436   pthread_cond_signal(&g.readwrite_lock_cond);
2437   pthread_mutex_unlock(&g.readwrite_lock_mutex);
2438 }
2439 
2440 
2441 /*
2442  * 'get_device_id()' - Return IEEE-1284 device ID.
2443  */
2444 
get_device_id(cups_sc_status_t * status,char * data,int * datalen)2445 static void get_device_id(cups_sc_status_t *status,
2446 			  char *data,
2447 			  int *datalen)
2448 {
2449   CFStringRef deviceIDString = NULL;
2450 
2451   if (g.printer_obj != IO_OBJECT_NULL)
2452   {
2453     printer_interface_t printerIntf = usb_printer_interface_interface(g.printer_obj);
2454     if (printerIntf)
2455     {
2456       deviceIDString = copy_printer_interface_deviceid(printerIntf, g.alternateSetting);
2457       (*printerIntf)->Release(printerIntf);
2458     }
2459   }
2460 
2461 
2462   if (deviceIDString)
2463   {
2464     if (CFStringGetCString(deviceIDString, data, *datalen, kCFStringEncodingUTF8))
2465       *datalen = (int)strlen(data);
2466     else
2467       *datalen = 0;
2468 
2469     CFRelease(deviceIDString);
2470   }
2471   else
2472   {
2473     *datalen = 0;
2474   }
2475 
2476   *status  = CUPS_SC_STATUS_OK;
2477 }
2478