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