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