/*
-* "$Id: usb-darwin.c 7953 2008-09-17 01:43:19Z mike $"
-*
-* Copyright 2005-2009 Apple Inc. All rights reserved.
-*
-* IMPORTANT: This Apple software is supplied to you by Apple Computer,
-* Inc. ("Apple") in consideration of your agreement to the following
-* terms, and your use, installation, modification or redistribution of
-* this Apple software constitutes acceptance of these terms. If you do
-* not agree with these terms, please do not use, install, modify or
-* redistribute this Apple software.
-*
-* In consideration of your agreement to abide by the following terms, and
-* subject to these terms, Apple grants you a personal, non-exclusive
-* license, under Apple's copyrights in this original Apple software (the
-* "Apple Software"), to use, reproduce, modify and redistribute the Apple
-* Software, with or without modifications, in source and/or binary forms;
-* provided that if you redistribute the Apple Software in its entirety and
-* without modifications, you must retain this notice and the following
-* text and disclaimers in all such redistributions of the Apple Software.
-* Neither the name, trademarks, service marks or logos of Apple Computer,
-* Inc. may be used to endorse or promote products derived from the Apple
-* Software without specific prior written permission from Apple. Except
-* as expressly stated in this notice, no other rights or licenses, express
-* or implied, are granted by Apple herein, including but not limited to
-* any patent rights that may be infringed by your derivative works or by
-* other works in which the Apple Software may be incorporated.
-*
-* The Apple Software is provided by Apple on an "AS IS" basis. APPLE
-* MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
-* THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
-* FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
-* OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
-*
-* IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
-* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-* INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
-* MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
-* AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
-* STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
-* POSSIBILITY OF SUCH DAMAGE.
-*
-* Contents:
-*
-* list_devices() - List all USB devices.
-* print_device() - Print a file to a USB device.
-* sidechannel_thread() - Thread to handle side-channel requests.
-* read_thread() - Thread to read the backchannel data on.
-* list_device_cb() - list_device iterator callback.
-* find_device_cb() - print_device iterator callback.
-* status_timer_cb() - Status timer callback.
-* iterate_printers() - Iterate over all the printers.
-* device_added() - Device added notifier.
-* copy_deviceinfo() - Copy strings from the 1284 device ID.
-* release_deviceinfo() - Release deviceinfo strings.
-* load_classdriver() - Load a classdriver.
-* unload_classdriver() - Unload a classdriver.
-* load_printerdriver() - Load vendor's classdriver.
-* registry_open() - Open a connection to the printer.
-* registry_close() - Close the connection to the printer.
-* copy_deviceid() - Copy the 1284 device id string.
-* copy_devicestring() - Copy the 1284 device id string.
-* copy_value_for_key() - Copy value string associated with a key.
-* cfstr_create_trim() - Create CFString and trim whitespace characters.
-* parse_options() - Parse uri options.
-* setup_cfLanguage() - Create AppleLanguages array from LANG environment var.
-* run_legacy_backend() - Re-exec backend as ppc or i386.
-* sigterm_handler() - SIGTERM handler.
-* next_line() - Find the next line in a buffer.
-* parse_pserror() - Scan the backchannel data for postscript errors.
-* get_device_id() - Return IEEE-1284 device ID.
-*/
+ * "$Id$"
+ *
+ * Copyright 2005-2014 Apple Inc. All rights reserved.
+ *
+ * IMPORTANT: This Apple software is supplied to you by Apple Computer,
+ * Inc. ("Apple") in consideration of your agreement to the following
+ * terms, and your use, installation, modification or redistribution of
+ * this Apple software constitutes acceptance of these terms. If you do
+ * not agree with these terms, please do not use, install, modify or
+ * redistribute this Apple software.
+ *
+ * In consideration of your agreement to abide by the following terms, and
+ * subject to these terms, Apple grants you a personal, non-exclusive
+ * license, under Apple's copyrights in this original Apple software (the
+ * "Apple Software"), to use, reproduce, modify and redistribute the Apple
+ * Software, with or without modifications, in source and/or binary forms;
+ * provided that if you redistribute the Apple Software in its entirety and
+ * without modifications, you must retain this notice and the following
+ * text and disclaimers in all such redistributions of the Apple Software.
+ * Neither the name, trademarks, service marks or logos of Apple Computer,
+ * Inc. may be used to endorse or promote products derived from the Apple
+ * Software without specific prior written permission from Apple. Except
+ * as expressly stated in this notice, no other rights or licenses, express
+ * or implied, are granted by Apple herein, including but not limited to
+ * any patent rights that may be infringed by your derivative works or by
+ * other works in which the Apple Software may be incorporated.
+ *
+ * The Apple Software is provided by Apple on an "AS IS" basis. APPLE
+ * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
+ * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
+ * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+ *
+ * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
+ * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
+ * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
+ * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
/*
* Include necessary headers.
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_time.h>
-#include <cups/debug.h>
+#include <cups/debug-private.h>
+#include <cups/file-private.h>
#include <cups/sidechannel.h>
-#include <cups/i18n.h>
+#include <cups/language-private.h>
#include "backend-private.h"
-
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/usb/IOUSBLib.h>
#include <IOKit/IOCFPlugIn.h>
+#include <libproc.h>
#include <spawn.h>
#include <pthread.h>
extern char **environ;
+/*
+ * DEBUG_WRITES, if defined, causes the backend to write data to the printer in
+ * 512 byte increments, up to 8192 bytes, to make debugging with a USB bus
+ * analyzer easier.
+ */
+
+#define DEBUG_WRITES 0
+
/*
* WAIT_EOF_DELAY is number of seconds we'll wait for responses from
* the printer after we've finished sending all the data
#define kUSBClassDriverProperty CFSTR("USB Printing Class")
-#define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericTOPrintingClass.plugin")
+#define kUSBGenericTOPrinterClassDriver CFSTR("/System/Library/Printers/Libraries/USBGenericPrintingClass.plugin")
#define kUSBPrinterClassDeviceNotOpen -9664 /*kPMInvalidIOMContext*/
+#define CRSetCrashLogMessage(m) _crc_make_setter(message, m)
+#define _crc_make_setter(attr, arg) (gCRAnnotations.attr = (uint64_t)(unsigned long)(arg))
+#define CRASH_REPORTER_CLIENT_HIDDEN __attribute__((visibility("hidden")))
+#define CRASHREPORTER_ANNOTATIONS_VERSION 4
+#define CRASHREPORTER_ANNOTATIONS_SECTION "__crash_info"
+
+struct crashreporter_annotations_t {
+ uint64_t version; // unsigned long
+ uint64_t message; // char *
+ uint64_t signature_string; // char *
+ uint64_t backtrace; // char *
+ uint64_t message2; // char *
+ uint64_t thread; // uint64_t
+ uint64_t dialog_mode; // unsigned int
+};
+
+CRASH_REPORTER_CLIENT_HIDDEN
+struct crashreporter_annotations_t gCRAnnotations
+ __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION)))
+ = { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 };
/*
* Section 5.3 USB Printing Class spec
UInt16 vendorID; /* Vendor id */
UInt16 productID; /* Product id */
printer_interface_t interface; /* identify the device to IOKit */
- UInt8 outpipe; /* mandatory bulkOut pipe */
+ UInt8 outpipe; /* mandatory bulkOut pipe */
UInt8 inpipe; /* optional bulkIn pipe */
/* general class requests */
int print_fd; /* File descriptor to print */
ssize_t print_bytes; /* Print bytes read */
+#if DEBUG_WRITES
+ ssize_t debug_bytes; /* Current bytes to read */
+#endif /* DEBUG_WRITES */
Boolean wait_eof;
int drain_output; /* Drain all pending output */
*/
globals_t g = { 0 }; /* Globals */
+int Iterating = 0; /* Are we iterating the bus? */
/*
static Boolean list_device_cb(void *refcon, io_service_t obj);
static CFStringRef cfstr_create_trim(const char *cstr);
static CFStringRef copy_value_for_key(CFStringRef deviceID, CFStringRef *keys);
-static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_t intf, classdriver_t ***printerDriver);
+static kern_return_t load_classdriver(CFStringRef driverPath, printer_interface_t interface, classdriver_t ***printerDriver);
static kern_return_t load_printerdriver(CFStringRef *driverBundlePath);
-static kern_return_t registry_close();
+static kern_return_t registry_close(void);
static kern_return_t registry_open(CFStringRef *driverBundlePath);
-static kern_return_t unload_classdriver();
+static kern_return_t unload_classdriver(classdriver_t ***classdriver);
static OSStatus copy_deviceid(classdriver_t **printer, CFStringRef *deviceID);
static void *read_thread(void *reference);
static void *sidechannel_thread(void *reference);
static void parse_options(char *options, char *serial, int serial_size, UInt32 *location, Boolean *wait_eof);
static void release_deviceinfo(CFStringRef *make, CFStringRef *model, CFStringRef *serial);
static void setup_cfLanguage(void);
-static void soft_reset();
+static void soft_reset(void);
static void status_timer_cb(CFRunLoopTimerRef timer, void *info);
#if defined(__i386__) || defined(__x86_64__)
static pid_t child_pid; /* Child PID */
-static void run_legacy_backend(int argc, char *argv[], int fd); /* Starts child backend process running as a ppc executable */
+static void run_legacy_backend(int argc, char *argv[], int fd) __attribute__((noreturn)); /* Starts child backend process running as a ppc executable */
#endif /* __i386__ || __x86_64__ */
-static int job_canceled = 0; /* Was the job canceled? */
static void sigterm_handler(int sig); /* SIGTERM handler */
+static void sigquit_handler(int sig, siginfo_t *si, void *unused) __attribute__((noreturn));
#ifdef PARSE_PS_ERRORS
static const char *next_line (const char *buffer);
{
char serial[1024]; /* Serial number buffer */
OSStatus status; /* Function results */
- IOReturn iostatus, /* Current IO status */
- prev_iostatus = 0; /* Previous IO status */
+ IOReturn iostatus; /* Current IO status */
pthread_t read_thread_id, /* Read thread */
sidechannel_thread_id;/* Side-channel thread */
int have_sidechannel = 0; /* Was the side-channel thread started? */
ssize_t total_bytes; /* Total bytes written */
UInt32 bytes; /* Bytes written */
struct timeval *timeout, /* Timeout pointer */
- stimeout; /* Timeout for select() */
+ tv; /* Time value */
struct timespec cond_timeout; /* pthread condition timeout */
+ struct sigaction action; /* Actions for POSIX signals */
+
+
+ (void)uri;
+
+ /*
+ * Catch SIGQUIT to determine who is sending it...
+ */
+ memset(&action, 0, sizeof(action));
+ action.sa_sigaction = sigquit_handler;
+ action.sa_flags = SA_SIGINFO;
+ sigaction(SIGQUIT, &action, NULL);
/*
* See if the side-channel descriptor is valid...
if (!g.make || !g.model)
{
- _cupsLangPuts(stderr, _("ERROR: Fatal USB error!\n"));
+ fprintf(stderr, "DEBUG: Fatal USB error.\n");
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("There was an unrecoverable USB error."));
if (!g.make)
- fputs("DEBUG: USB make string is NULL!\n", stderr);
+ fputs("DEBUG: USB make string is NULL\n", stderr);
if (!g.model)
- fputs("DEBUG: USB model string is NULL!\n", stderr);
+ fputs("DEBUG: USB model string is NULL\n", stderr);
return (CUPS_BACKEND_STOP);
}
strlcpy(print_buffer, "USB class driver", sizeof(print_buffer));
fputs("STATE: +apple-missing-usbclassdriver-error\n", stderr);
- _cupsLangPuts(stderr, _("ERROR: Fatal USB error!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("There was an unrecoverable USB error."));
fprintf(stderr, "DEBUG: Could not load %s\n", print_buffer);
if (driverBundlePath)
countdown -= PRINTER_POLLING_INTERVAL;
if (countdown <= 0)
{
- _cupsLangPuts(stderr,
- _("INFO: Waiting for printer to become available...\n"));
+ _cupsLangPrintFilter(stderr, "INFO",
+ _("Waiting for printer to become available."));
fprintf(stderr, "DEBUG: USB printer status: 0x%08x\n", (int)status);
countdown = SUBSEQUENT_LOG_INTERVAL; /* subsequent log entries, every 15 seconds */
}
fputs("STATE: -connecting-to-device\n", stderr);
/*
- * Now that we are "connected" to the port, catch SIGTERM so that we
+ * Now that we are "connected" to the port, ignore SIGTERM so that we
* can finish out any page data the driver sends (e.g. to eject the
- * current page... Only catch SIGTERM if we are printing data from
+ * current page... Only ignore SIGTERM if we are printing data from
* stdin (otherwise you can't cancel raw jobs...)
*/
if (!print_fd)
{
- struct sigaction action; /* POSIX signal action */
-
-
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
- action.sa_handler = sigterm_handler;
+ action.sa_handler = SIG_IGN;
sigaction(SIGTERM, &action, NULL);
}
if (pthread_create(&sidechannel_thread_id, NULL, sidechannel_thread, NULL))
{
- _cupsLangPuts(stderr, _("ERROR: Fatal USB error!\n"));
- fputs("DEBUG: Couldn't create side-channel thread!\n", stderr);
+ fprintf(stderr, "DEBUG: Fatal USB error.\n");
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("There was an unrecoverable USB error."));
+ fputs("DEBUG: Couldn't create side-channel thread\n", stderr);
+ registry_close();
return (CUPS_BACKEND_STOP);
}
}
if (pthread_create(&read_thread_id, NULL, read_thread, NULL))
{
- _cupsLangPuts(stderr, _("ERROR: Fatal USB error!\n"));
- fputs("DEBUG: Couldn't create read thread!\n", stderr);
+ fprintf(stderr, "DEBUG: Fatal USB error.\n");
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("There was an unrecoverable USB error."));
+ fputs("DEBUG: Couldn't create read thread\n", stderr);
+ registry_close();
return (CUPS_BACKEND_STOP);
}
while (status == noErr && copies-- > 0)
{
- _cupsLangPuts(stderr, _("INFO: Sending print data...\n"));
+ _cupsLangPrintFilter(stderr, "INFO", _("Sending data to printer."));
if (print_fd != STDIN_FILENO)
{
if (g.print_bytes)
{
- stimeout.tv_sec = 0;
- stimeout.tv_usec = 100000; /* 100ms */
- timeout = &stimeout;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000; /* 100ms */
+ timeout = &tv;
}
else if (g.drain_output)
{
- stimeout.tv_sec = 0;
- stimeout.tv_usec = 0;
- timeout = &stimeout;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ timeout = &tv;
}
else
timeout = NULL;
if (errno == EINTR && total_bytes == 0)
{
fputs("DEBUG: Received an interrupt before any bytes were "
- "written, aborting!\n", stderr);
+ "written, aborting\n", stderr);
+ registry_close();
return (CUPS_BACKEND_OK);
}
else if (errno != EAGAIN && errno != EINTR)
{
- _cupsLangPuts(stderr, _("ERROR: Unable to read print data!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to read print data."));
perror("DEBUG: select");
- return (CUPS_BACKEND_FAILED);
+ registry_close();
+ return (CUPS_BACKEND_FAILED);
}
}
if (FD_ISSET(print_fd, &input_set))
{
+#if DEBUG_WRITES
+ g.debug_bytes += 512;
+ if (g.debug_bytes > sizeof(print_buffer))
+ g.debug_bytes = 512;
+
+ g.print_bytes = read(print_fd, print_buffer, g.debug_bytes);
+
+#else
g.print_bytes = read(print_fd, print_buffer, sizeof(print_buffer));
+#endif /* DEBUG_WRITES */
if (g.print_bytes < 0)
{
if (errno != EAGAIN && errno != EINTR)
{
- _cupsLangPuts(stderr, _("ERROR: Unable to read print data!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to read print data."));
perror("DEBUG: read");
+ registry_close();
return (CUPS_BACKEND_FAILED);
}
if (g.print_bytes)
{
- bytes = g.print_bytes;
-
+ bytes = (UInt32)g.print_bytes;
iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
/*
* Ignore timeout errors, but retain the number of bytes written to
- * avoid sending duplicate data (<rdar://problem/6254911>)...
+ * avoid sending duplicate data...
*/
if (iostatus == kIOUSBTransactionTimeout)
{
- fputs("DEBUG: Got USB transaction timeout during write!\n", stderr);
+ fputs("DEBUG: Got USB transaction timeout during write\n", stderr);
iostatus = 0;
}
/*
- * Ignore the first stall error we get since we try to clear any stalls
- * in the class driver...
+ * If we've stalled, retry the write...
*/
else if (iostatus == kIOUSBPipeStalled)
{
- fputs("DEBUG: Got USB pipe stalled during write!\n", stderr);
+ fputs("DEBUG: Got USB pipe stalled during write\n", stderr);
- bytes = 0;
-
- if (prev_iostatus != kIOUSBPipeStalled)
- {
- prev_iostatus = iostatus;
- iostatus = 0;
- }
+ bytes = (UInt32)g.print_bytes;
+ iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
}
/*
- * Ignore the first "aborted" status we get, since we might have
- * received a signal (<rdar://problem/6860126>)...
+ * Retry a write after an aborted write since we probably just got
+ * SIGTERM...
*/
else if (iostatus == kIOReturnAborted)
{
- fputs("DEBUG: Got return aborted during write!\n", stderr);
+ fputs("DEBUG: Got USB return aborted during write\n", stderr);
IOReturn err = (*g.classdriver)->Abort(g.classdriver);
fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n", err);
- bytes = 0;
+#if DEBUG_WRITES
+ sleep(5);
+#endif /* DEBUG_WRITES */
- if (prev_iostatus != kIOReturnAborted)
- {
- prev_iostatus = iostatus;
- iostatus = 0;
- }
+ bytes = (UInt32)g.print_bytes;
+ iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
}
- else
- prev_iostatus = iostatus;
- if (iostatus || bytes < 0)
+ if (iostatus)
{
/*
* Write error - bail if we don't see an error we can retry...
*/
- _cupsLangPuts(stderr, _("ERROR: Unable to send print data!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to send data to printer."));
fprintf(stderr, "DEBUG: USB class driver WritePipe returned %x\n",
iostatus);
fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n",
err);
- status = job_canceled ? CUPS_BACKEND_FAILED : CUPS_BACKEND_STOP;
+ status = CUPS_BACKEND_FAILED;
break;
}
else if (bytes > 0)
}
fprintf(stderr, "DEBUG: Sent %lld bytes...\n", (off_t)total_bytes);
+ fputs("STATE: +cups-waiting-for-job-completed\n", stderr);
/*
- * Wait for the side channel thread to exit...
+ * Signal the side channel thread to exit...
*/
if (have_sidechannel)
g.sidechannel_thread_stop = 1;
pthread_mutex_lock(&g.sidechannel_thread_mutex);
+
if (!g.sidechannel_thread_done)
{
- /*
- * Wait for the side-channel thread to exit...
- */
+ gettimeofday(&tv, NULL);
+ cond_timeout.tv_sec = tv.tv_sec + WAIT_SIDE_DELAY;
+ cond_timeout.tv_nsec = tv.tv_usec * 1000;
- cond_timeout.tv_sec = time(NULL) + WAIT_SIDE_DELAY;
- cond_timeout.tv_nsec = 0;
- if (pthread_cond_timedwait(&g.sidechannel_thread_cond,
- &g.sidechannel_thread_mutex,
- &cond_timeout) != 0)
+ while (!g.sidechannel_thread_done)
{
- /*
- * Force the side-channel thread to exit...
- */
-
- pthread_kill(sidechannel_thread_id, SIGTERM);
+ if (pthread_cond_timedwait(&g.sidechannel_thread_cond,
+ &g.sidechannel_thread_mutex,
+ &cond_timeout) != 0)
+ break;
}
}
- pthread_mutex_unlock(&g.sidechannel_thread_mutex);
-
- pthread_join(sidechannel_thread_id, NULL);
- pthread_cond_destroy(&g.sidechannel_thread_cond);
- pthread_mutex_destroy(&g.sidechannel_thread_mutex);
+ pthread_mutex_unlock(&g.sidechannel_thread_mutex);
}
- pthread_cond_destroy(&g.readwrite_lock_cond);
- pthread_mutex_destroy(&g.readwrite_lock_mutex);
-
/*
- * Signal the read thread to stop...
+ * Signal the read thread to exit then wait 7 seconds for it to complete...
*/
g.read_thread_stop = 1;
- /*
- * Give the read thread WAIT_EOF_DELAY seconds to complete all the data. If
- * we are not signaled in that time then force the thread to exit.
- */
-
pthread_mutex_lock(&g.read_thread_mutex);
if (!g.read_thread_done)
{
- cond_timeout.tv_sec = time(NULL) + WAIT_EOF_DELAY;
- cond_timeout.tv_nsec = 0;
+ fputs("DEBUG: Waiting for read thread to exit...\n", stderr);
- if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
- &cond_timeout) != 0)
+ gettimeofday(&tv, NULL);
+ cond_timeout.tv_sec = tv.tv_sec + WAIT_EOF_DELAY;
+ cond_timeout.tv_nsec = tv.tv_usec * 1000;
+
+ while (!g.read_thread_done)
{
- /*
- * Force the read thread to exit...
- */
+ if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
+ &cond_timeout) != 0)
+ break;
+ }
+
+ /*
+ * If it didn't exit abort the pending read and wait an additional second...
+ */
+
+ if (!g.read_thread_done)
+ {
+ fputs("DEBUG: Read thread still active, aborting the pending read...\n",
+ stderr);
g.wait_eof = 0;
- pthread_kill(read_thread_id, SIGTERM);
+
+ (*g.classdriver)->Abort(g.classdriver);
+
+ gettimeofday(&tv, NULL);
+ cond_timeout.tv_sec = tv.tv_sec + 1;
+ cond_timeout.tv_nsec = tv.tv_usec * 1000;
+
+ while (!g.read_thread_done)
+ {
+ if (pthread_cond_timedwait(&g.read_thread_cond, &g.read_thread_mutex,
+ &cond_timeout) != 0)
+ break;
+ }
}
}
- pthread_mutex_unlock(&g.read_thread_mutex);
-
- pthread_join(read_thread_id, NULL); /* wait for the read thread to return */
- pthread_cond_destroy(&g.read_thread_cond);
- pthread_mutex_destroy(&g.read_thread_mutex);
+ pthread_mutex_unlock(&g.read_thread_mutex);
/*
* Close the connection and input file and general clean up...
uint64_t start,
delay;
+
+ (void)reference;
+
/* Calculate what 250 milliSeconds are in mach absolute time...
*/
mach_timebase_info(&timeBaseInfo);
#endif
}
else if (readstatus == kIOUSBTransactionTimeout)
- fputs("DEBUG: Got USB transaction timeout during write!\n", stderr);
+ fputs("DEBUG: Got USB transaction timeout during read\n", stderr);
else if (readstatus == kIOUSBPipeStalled)
- fputs("DEBUG: Got USB pipe stalled during read!\n", stderr);
+ fputs("DEBUG: Got USB pipe stalled during read\n", stderr);
else if (readstatus == kIOReturnAborted)
- fputs("DEBUG: Got return aborted during read!\n", stderr);
+ fputs("DEBUG: Got USB return aborted during read\n", stderr);
/*
* Make sure this loop executes no more than once every 250 miliseconds...
int datalen; /* Request/response data size */
+ (void)reference;
+
do
{
datalen = sizeof(data);
if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
- continue;
+ {
+ if (status == CUPS_SC_STATUS_TIMEOUT)
+ continue;
+ else
+ break;
+ }
switch (command)
{
fputs("DEBUG: CUPS_SC_CMD_GET_BIDI received from driver...\n",
stderr);
- data[0] = g.bidi_flag;
+ data[0] = (char)g.bidi_flag;
cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
fprintf(stderr,
get_device_id(&status, data, &datalen);
cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, datalen, 1.0);
- if (datalen < sizeof(data))
+ if ((size_t)datalen < sizeof(data))
data[datalen] = '\0';
else
data[sizeof(data) - 1] = '\0';
static void iterate_printers(iterator_callback_t callBack,
void *userdata)
{
+ Iterating = 1;
+
mach_port_t masterPort = 0x0;
kern_return_t kr = IOMasterPort (bootstrap_port, &masterPort);
CFRelease(usb_klass);
CFRelease(usb_subklass);
- kr = IOServiceAddMatchingNotification(addNotification, kIOMatchedNotification, usbPrinterMatchDictionary, &device_added, &reference, &addIterator);
+ IOServiceAddMatchingNotification(addNotification, kIOMatchedNotification, usbPrinterMatchDictionary, &device_added, &reference, &addIterator);
if (addIterator != 0x0)
{
device_added (&reference, addIterator);
}
mach_port_deallocate(mach_task_self(), masterPort);
}
+
+ Iterating = 0;
}
/* One last call to the call back now that we are not longer have printers left to iterate...
*/
- if (reference->keepRunning)
+ if (reference->keepRunning && reference->callback)
reference->keepRunning = reference->callback(reference->userdata, 0x0);
if (!reference->keepRunning)
{
Boolean keepRunning = (obj != 0x0);
+
+ (void)refcon;
+
if (keepRunning)
{
CFStringRef deviceIDString = NULL;
if (!make ||
!CFStringGetCString(make, makestr, sizeof(makestr),
kCFStringEncodingUTF8))
- strcpy(makestr, "Unknown");
+ strlcpy(makestr, "Unknown", sizeof(makestr));
if (!model ||
!CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1,
kCFStringEncodingUTF8))
- strcpy(modelstr + 1, "Printer");
+ strlcpy(modelstr + 1, "Printer", sizeof(modelstr) - 1);
optionsstr[0] = '\0';
if (serial != NULL)
snprintf(optionsstr, sizeof(optionsstr), "?location=%x", (unsigned)deviceLocation);
httpAssembleURI(HTTP_URI_CODING_ALL, uristr, sizeof(uristr), "usb", NULL, makestr, 0, modelstr);
- strncat(uristr, optionsstr, sizeof(uristr));
+ strlcat(uristr, optionsstr, sizeof(uristr));
cupsBackendReport("direct", uristr, make_modelstr, make_modelstr, idstr,
NULL);
if (obj != 0x0)
{
CFStringRef idString = NULL;
- UInt32 location = -1;
+ UInt32 location = ~0U;
UInt8 interfaceNum = 0;
copy_devicestring(obj, &idString, &location, &interfaceNum);
if (!keepLooking && g.status_timer != NULL)
{
fputs("STATE: -offline-report\n", stderr);
- _cupsLangPuts(stderr, _("INFO: Printer is now online.\n"));
+ _cupsLangPrintFilter(stderr, "INFO", _("The printer is now online."));
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), g.status_timer, kCFRunLoopDefaultMode);
CFRelease(g.status_timer);
g.status_timer = NULL;
static void status_timer_cb(CFRunLoopTimerRef timer,
void *info)
{
+ (void)timer;
+ (void)info;
+
fputs("STATE: +offline-report\n", stderr);
- _cupsLangPuts(stderr, _("INFO: Printer is offline.\n"));
+ _cupsLangPrintFilter(stderr, "INFO", _("The printer is offline."));
if (getenv("CLASS") != NULL)
{
*/
static kern_return_t load_classdriver(CFStringRef driverPath,
- printer_interface_t intf,
+ printer_interface_t interface,
classdriver_t ***printerDriver)
{
kern_return_t kr = kUSBPrinterClassDeviceNotOpen;
classdriver_t **driver = NULL;
CFStringRef bundle = driverPath ? driverPath : kUSBGenericTOPrinterClassDriver;
char bundlestr[1024]; /* Bundle path */
- struct stat bundleinfo; /* File information for bundle */
CFURLRef url; /* URL for driver */
CFPlugInRef plugin = NULL; /* Plug-in address */
* Validate permissions for the class driver...
*/
- if (stat(bundlestr, &bundleinfo))
- {
- fprintf(stderr, "DEBUG: Unable to load class driver \"%s\": %s\n",
- bundlestr, strerror(errno));
- return (kr);
- }
- else if (bundleinfo.st_mode & S_IWOTH)
- {
- fprintf(stderr, "DEBUG: Unable to load class driver \"%s\": insecure file "
- "permissions (0%o)\n", bundlestr, bundleinfo.st_mode);
+ _cups_fc_result_t result = _cupsFileCheck(bundlestr,
+ _CUPS_FILE_CHECK_DIRECTORY, 1,
+ Iterating ? NULL : _cupsFileCheckFilter, NULL);
+
+ if (result && driverPath)
+ return (load_classdriver(NULL, interface, printerDriver));
+ else if (result)
return (kr);
- }
/*
* Try loading the class driver...
{
classdriver_t **genericDriver = NULL;
if (driverPath != NULL && CFStringCompare(driverPath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo)
- kr = load_classdriver(NULL, intf, &genericDriver);
+ kr = load_classdriver(NULL, interface, &genericDriver);
if (kr == kIOReturnSuccess)
{
- (*driver)->interface = intf;
+ (*driver)->interface = interface;
(*driver)->Initialize(driver, genericDriver);
(*driver)->plugin = plugin;
- (*driver)->interface = intf;
+ (*driver)->interface = interface;
*printerDriver = driver;
}
}
IOCFPlugInInterface **iodev = NULL;
SInt32 score;
kern_return_t kr;
- printer_interface_t intf;
+ printer_interface_t interface;
HRESULT res;
kr = IOCreatePlugInInterfaceForService(g.printer_obj, kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score);
if (kr == kIOReturnSuccess)
{
- if ((res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &intf)) == noErr)
+ if ((res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *) &interface)) == noErr)
{
*driverBundlePath = IORegistryEntryCreateCFProperty(g.printer_obj, kUSBClassDriverProperty, NULL, kNilOptions);
- kr = load_classdriver(*driverBundlePath, intf, &g.classdriver);
+ kr = load_classdriver(*driverBundlePath, interface, &g.classdriver);
if (kr != kIOReturnSuccess)
- (*intf)->Release(intf);
+ (*interface)->Release(interface);
}
IODestroyPlugInInterface(iodev);
}
* 'registry_close()' - Close the connection to the printer.
*/
-static kern_return_t registry_close()
+static kern_return_t registry_close(void)
{
if (g.classdriver != NULL)
(*g.classdriver)->Close(g.classdriver);
static OSStatus copy_deviceid(classdriver_t **classdriver,
CFStringRef *deviceID)
{
- CFStringRef devID = NULL,
+ CFStringRef devID = NULL;
+ CFStringRef deviceMake = NULL;
+ CFStringRef deviceModel = NULL;
+ CFStringRef deviceSerial = NULL;
- deviceMake = NULL,
- deviceModel = NULL,
- deviceSerial = NULL;
+ *deviceID = NULL;
OSStatus err = (*classdriver)->GetDeviceID(classdriver, &devID, DEFAULT_TIMEOUT);
if (deviceSerial == NULL && desc.iSerialNumber != 0)
{
- CFStringRef data = NULL;
- err = (*classdriver)->GetString(classdriver, desc.iSerialNumber, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &data);
- if (data != NULL)
+ err = (*classdriver)->GetString(classdriver, desc.iSerialNumber, kUSBLanguageEnglish, DEFAULT_TIMEOUT, &deviceSerial);
+ if (deviceSerial != NULL)
{
- CFStringAppendFormat(newDevID, NULL, CFSTR("SERN:%@;"), data);
- CFRelease(data);
+ CFStringAppendFormat(newDevID, NULL, CFSTR("SERN:%@;"), deviceSerial);
}
}
{
*deviceID = devID;
}
+
+ if (*deviceID == NULL)
+ return err;
+
+ /* Remove special characters from the serial number */
+ CFRange range = (deviceSerial != NULL ? CFStringFind(deviceSerial, CFSTR("+"), 0) : CFRangeMake(0, 0));
+ if (range.length == 1) {
+ range = CFStringFind(*deviceID, deviceSerial, 0);
+
+ CFMutableStringRef deviceIDString = CFStringCreateMutableCopy(NULL, 0, *deviceID);
+ CFStringFindAndReplace(deviceIDString, CFSTR("+"), CFSTR(""), range, 0);
+ CFRelease(*deviceID);
+ *deviceID = deviceIDString;
+ }
+
release_deviceinfo(&deviceMake, &deviceModel, &deviceSerial);
return err;
IOCFPlugInInterface **iodev = NULL;
SInt32 score;
kern_return_t kr;
- printer_interface_t intf;
+ printer_interface_t interface;
HRESULT res;
classdriver_t **klassDriver = NULL;
CFStringRef driverBundlePath;
&iodev, &score)) == kIOReturnSuccess)
{
if ((res = (*iodev)->QueryInterface(iodev, USB_INTERFACE_KIND, (LPVOID *)
- &intf)) == noErr)
+ &interface)) == noErr)
{
- (*intf)->GetLocationID(intf, deviceLocation);
- (*intf)->GetInterfaceNumber(intf, interfaceNumber);
+ (*interface)->GetLocationID(interface, deviceLocation);
+ (*interface)->GetInterfaceNumber(interface, interfaceNumber);
driverBundlePath = IORegistryEntryCreateCFProperty(usbInterface,
kUSBClassDriverProperty,
NULL, kNilOptions);
- kr = load_classdriver(driverBundlePath, intf, &klassDriver);
+ kr = load_classdriver(driverBundlePath, interface, &klassDriver);
if (kr != kIOReturnSuccess && driverBundlePath != NULL)
- kr = load_classdriver(NULL, intf, &klassDriver);
+ kr = load_classdriver(NULL, interface, &klassDriver);
if (kr == kIOReturnSuccess && klassDriver != NULL)
- kr = copy_deviceid(klassDriver, deviceID);
+ copy_deviceid(klassDriver, deviceID);
unload_classdriver(&klassDriver);
if (driverBundlePath != NULL)
CFRelease(driverBundlePath);
- /* (*intf)->Release(intf); */
+ /* (*interface)->Release(interface); */
}
IODestroyPlugInInterface(iodev);
}
* Process the option...
*/
- if (!strcasecmp(name, "waiteof"))
+ if (!_cups_strcasecmp(name, "waiteof"))
{
- if (!strcasecmp(value, "on") ||
- !strcasecmp(value, "yes") ||
- !strcasecmp(value, "true"))
+ if (!_cups_strcasecmp(value, "on") ||
+ !_cups_strcasecmp(value, "yes") ||
+ !_cups_strcasecmp(value, "true"))
*wait_eof = true;
- else if (!strcasecmp(value, "off") ||
- !strcasecmp(value, "no") ||
- !strcasecmp(value, "false"))
+ else if (!_cups_strcasecmp(value, "off") ||
+ !_cups_strcasecmp(value, "no") ||
+ !_cups_strcasecmp(value, "false"))
*wait_eof = false;
else
- _cupsLangPrintf(stderr,
- _("WARNING: Boolean expected for waiteof option "
- "\"%s\"\n"), value);
+ _cupsLangPrintFilter(stderr, "WARNING",
+ _("Boolean expected for waiteof option \"%s\"."),
+ value);
}
- else if (!strcasecmp(name, "serial"))
+ else if (!_cups_strcasecmp(name, "serial"))
strlcpy(serial, value, serial_size);
- else if (!strcasecmp(name, "location") && location)
- *location = strtol(value, NULL, 16);
+ else if (!_cups_strcasecmp(name, "location") && location)
+ *location = (UInt32)strtoul(value, NULL, 16);
}
}
char *my_argv[32];
char *usb_legacy_status;
+
/*
* If we're running as x86_64 or i386 and couldn't load the class driver
* (because it's ppc or i386), then try to re-exec ourselves in ppc or i386
* Setup a SIGTERM handler then block it before forking...
*/
+ int err; /* posix_spawn result */
struct sigaction action; /* POSIX signal action */
sigset_t newmask, /* New signal mask */
oldmask; /* Old signal mask */
# else
perror("DEBUG: Unable to set binary preference to ppc");
# endif /* __x86_64__ */
- _cupsLangPrintf(stderr, _("Unable to use legacy USB class driver!\n"));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to use legacy USB class driver."));
exit(CUPS_BACKEND_STOP);
}
}
cups_serverbin = CUPS_SERVERBIN;
snprintf(usbpath, sizeof(usbpath), "%s/backend/usb", cups_serverbin);
- for (i = 0; i < argc && i < (sizeof(my_argv) / sizeof(my_argv[0])) - 1; i ++)
+ for (i = 0; i < argc && i < (int)(sizeof(my_argv) / sizeof(my_argv[0])) - 1; i ++)
my_argv[i] = argv[i];
my_argv[i] = NULL;
- if (posix_spawn(&child_pid, usbpath, NULL, &attrs, my_argv, environ))
+ if ((err = posix_spawn(&child_pid, usbpath, NULL, &attrs, my_argv,
+ environ)) != 0)
{
fprintf(stderr, "DEBUG: Unable to exec %s: %s\n", usbpath,
- strerror(errno));
- _cupsLangPrintf(stderr, _("Unable to use legacy USB class driver!\n"));
+ strerror(err));
+ _cupsLangPrintFilter(stderr, "ERROR",
+ _("Unable to use legacy USB class driver."));
exit(CUPS_BACKEND_STOP);
}
if (WIFSIGNALED(childstatus))
{
exitstatus = CUPS_BACKEND_STOP;
- fprintf(stderr, "DEBUG: usb(legacy) backend %d crashed on signal %d!\n",
+ fprintf(stderr, "DEBUG: usb(legacy) backend %d crashed on signal %d\n",
child_pid, WTERMSIG(childstatus));
}
else
{
if ((exitstatus = WEXITSTATUS(childstatus)) != 0)
fprintf(stderr,
- "DEBUG: usb(legacy) backend %d stopped with status %d!\n",
+ "DEBUG: usb(legacy) backend %d stopped with status %d\n",
child_pid, exitstatus);
else
fprintf(stderr, "DEBUG: usb(legacy) backend %d exited with no errors\n",
sigterm_handler(int sig) /* I - Signal */
{
#if defined(__i386__) || defined(__x86_64__)
+ /*
+ * If we started a child process pass the signal on to it...
+ */
+
if (child_pid)
{
/*
exit(0);
else
{
- fprintf(stderr, "DEBUG: Child crashed on signal %d!\n", status);
+ fprintf(stderr, "DEBUG: Child crashed on signal %d\n", status);
exit(CUPS_BACKEND_STOP);
}
}
#endif /* __i386__ || __x86_64__ */
+}
- /*
- * Otherwise just flag that the job has been canceled...
- */
- job_canceled = 1;
+/*
+ * 'sigquit_handler()' - SIGQUIT handler.
+ */
+
+static void sigquit_handler(int sig, siginfo_t *si, void *unused)
+{
+ char *path;
+ char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
+ static char msgbuf[256] = "";
+
+
+ (void)sig;
+ (void)unused;
+
+ if (proc_pidpath(si->si_pid, pathbuf, sizeof(pathbuf)) > 0 &&
+ (path = basename(pathbuf)) != NULL)
+ snprintf(msgbuf, sizeof(msgbuf), "SIGQUIT sent by %s(%d)", path, (int)si->si_pid);
+ else
+ snprintf(msgbuf, sizeof(msgbuf), "SIGQUIT sent by PID %d", (int)si->si_pid);
+
+ CRSetCrashLogMessage(msgbuf);
+
+ abort();
}
pCommentEnd += 3; /* Skip past "]%%" */
*pCommentEnd = '\0'; /* There's always room for the nul */
- if (strncasecmp(pCommentBegin, "%%[ Error:", 10) == 0)
+ if (_cups_strncasecmp(pCommentBegin, "%%[ Error:", 10) == 0)
logLevel = "DEBUG";
- else if (strncasecmp(pCommentBegin, "%%[ Flushing", 12) == 0)
+ else if (_cups_strncasecmp(pCommentBegin, "%%[ Flushing", 12) == 0)
logLevel = "DEBUG";
else
logLevel = "INFO";
}
/* move everything over... */
- strcpy(gErrorBuffer, pLineEnd);
+ strlcpy(gErrorBuffer, pLineEnd, sizeof(gErrorBuffer));
gErrorBufferPtr = gErrorBuffer;
pLineEnd = (char *)next_line((const char *)gErrorBuffer);
}
* 'soft_reset()' - Send a soft reset to the device.
*/
-static void soft_reset()
+static void soft_reset(void)
{
fd_set input_set; /* Input set for select() */
- struct timeval stimeout; /* Timeout for select() */
+ struct timeval tv; /* Time value */
char buffer[2048]; /* Buffer */
struct timespec cond_timeout; /* pthread condition timeout */
{
(*g.classdriver)->Abort(g.classdriver);
- cond_timeout.tv_sec = time(NULL) + 1;
- cond_timeout.tv_nsec = 0;
+ gettimeofday(&tv, NULL);
+ cond_timeout.tv_sec = tv.tv_sec + 1;
+ cond_timeout.tv_nsec = tv.tv_usec * 1000;
- pthread_cond_timedwait(&g.readwrite_lock_cond, &g.readwrite_lock_mutex, &cond_timeout);
+ while (g.readwrite_lock)
+ {
+ if (pthread_cond_timedwait(&g.readwrite_lock_cond,
+ &g.readwrite_lock_mutex,
+ &cond_timeout) != 0)
+ break;
+ }
}
g.readwrite_lock = 1;
FD_ZERO(&input_set);
FD_SET(g.print_fd, &input_set);
- stimeout.tv_sec = 0;
- stimeout.tv_usec = 0;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
- while (select(g.print_fd+1, &input_set, NULL, NULL, &stimeout) > 0)
+ while (select(g.print_fd+1, &input_set, NULL, NULL, &tv) > 0)
if (read(g.print_fd, buffer, sizeof(buffer)) <= 0)
break;
char *data,
int *datalen)
{
- UInt32 deviceLocation = 0;
- UInt8 interfaceNum = 0;
CFStringRef deviceIDString = NULL;
/* GetDeviceID */
- copy_devicestring(g.printer_obj, &deviceIDString, &deviceLocation, &interfaceNum);
+ copy_deviceid(g.classdriver, &deviceIDString);
+
if (deviceIDString)
{
CFStringGetCString(deviceIDString, data, *datalen, kCFStringEncodingUTF8);
- *datalen = strlen(data);
+ *datalen = (int)strlen(data);
CFRelease(deviceIDString);
}
*status = CUPS_SC_STATUS_OK;
/*
- * End of "$Id: usb-darwin.c 7953 2008-09-17 01:43:19Z mike $".
+ * End of "$Id$".
*/