]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/usb-darwin.c
Merge changes from CUPS 1.5svn-r9631.
[thirdparty/cups.git] / backend / usb-darwin.c
index 73c3e4c57d33fd1034d4208d21d25c4964d9ea74..4b7bb5c38856c75bbf308db5634866523761fde5 100644 (file)
@@ -1,7 +1,7 @@
 /*
 * "$Id: usb-darwin.c 7953 2008-09-17 01:43:19Z mike $"
 *
-* Copyright 2005-2008 Apple Inc. All rights reserved.
+* Copyright 2005-2010 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
 *
 * 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.
+ *   list_devices()       - List all USB devices.
+ *   print_device()       - Print a file to a USB device.
+ *   read_thread()        - Thread to read the backchannel data on.
+ *   sidechannel_thread() - Handle side-channel requests.
+ *   iterate_printers()   - Iterate over all the printers.
+ *   device_added()       - Device added notifier.
+ *   list_device_cb()     - list_device iterator callback.
+ *   find_device_cb()     - print_device iterator callback.
+ *   status_timer_cb()    - Status timer callback.
+ *   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.
+ *   sigterm_handler()    - SIGTERM handler.
+ *   next_line()          - Find the next line in a buffer.
+ *   parse_pserror()      - Scan the backchannel data for postscript errors.
+ *   soft_reset()         - Send a soft reset to the device.
+ *   get_device_id()      - Return IEEE-1284 device ID.
 */
 
 /*
@@ -89,9 +88,9 @@
 #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/sidechannel.h>
-#include <cups/i18n.h>
+#include <cups/language-private.h>
 #include "backend-private.h"
 
 #include <CoreFoundation/CoreFoundation.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
@@ -232,6 +239,9 @@ typedef struct globals_s
 
   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 */
@@ -279,10 +289,10 @@ static void soft_reset();
 static void status_timer_cb(CFRunLoopTimerRef timer, void *info);
 
 #if defined(__i386__) || defined(__x86_64__)
-static pid_t   child_pid;                                      /* Child PID */
+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 sigterm_handler(int sig);                          /* SIGTERM handler */
 #endif /* __i386__ || __x86_64__ */
+static void sigterm_handler(int sig);  /* SIGTERM handler */
 
 #ifdef PARSE_PS_ERRORS
 static const char *next_line (const char *buffer);
@@ -317,6 +327,7 @@ print_device(const char *uri,               /* I - Device URI */
 {
   char           serial[1024];         /* Serial number buffer */
   OSStatus       status;               /* Function results */
+  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? */
@@ -361,12 +372,14 @@ print_device(const char *uri,             /* I - Device URI */
 
   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);
   }
@@ -419,7 +432,8 @@ print_device(const char *uri,               /* I - Device URI */
         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)
@@ -437,8 +451,8 @@ print_device(const char *uri,               /* I - Device URI */
       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 */
       }
@@ -484,8 +498,11 @@ print_device(const char *uri,              /* I - Device URI */
 
     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);
     }
   }
@@ -502,8 +519,11 @@ print_device(const char *uri,              /* I - Device URI */
 
   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);
   }
 
@@ -518,7 +538,7 @@ print_device(const char *uri,               /* I - Device URI */
 
   while (status == noErr && copies-- > 0)
   {
-    _cupsLangPuts(stderr, _("INFO: Sending print data...\n"));
+    _cupsLangPrintFilter(stderr, "INFO", _("Sending data to printer."));
 
     if (print_fd != STDIN_FILENO)
     {
@@ -581,14 +601,17 @@ print_device(const char *uri,             /* I - Device URI */
        if (errno == EINTR && total_bytes == 0)
        {
          fputs("DEBUG: Received an interrupt before any bytes were "
-               "written, aborting!\n", stderr);
-          return (0);
+               "written, aborting\n", stderr);
+          registry_close();
+          return (CUPS_BACKEND_OK);
        }
-       else if (errno != EAGAIN)
+       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_STOP);
+         registry_close();
+          return (CUPS_BACKEND_FAILED);
        }
       }
 
@@ -609,7 +632,16 @@ print_device(const char *uri,              /* I - Device URI */
 
       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)
        {
@@ -617,11 +649,13 @@ print_device(const char *uri,             /* I - Device URI */
          * Read error - bail if we don't see EAGAIN or EINTR...
          */
 
-         if (errno != EAGAIN || errno != EINTR)
+         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");
-           return CUPS_BACKEND_STOP;
+           registry_close();
+           return (CUPS_BACKEND_FAILED);
          }
 
          g.print_bytes = 0;
@@ -643,41 +677,77 @@ print_device(const char *uri,             /* I - Device URI */
 
       if (g.print_bytes)
       {
-       bytes = g.print_bytes;
+       bytes    = 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>)...
+       */
 
-       status = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
+       if (iostatus == kIOUSBTransactionTimeout)
+       {
+         fputs("DEBUG: Got USB transaction timeout during write\n", stderr);
+         iostatus = 0;
+       }
 
        /*
-       * Ignore timeout errors...
+        * If we've stalled, retry the write...
        */
 
-       if (status == kIOUSBTransactionTimeout)
+       else if (iostatus == kIOUSBPipeStalled)
        {
-         status = 0;
-         bytes = 0;
+         fputs("DEBUG: Got USB pipe stalled during write\n", stderr);
+
+         bytes    = g.print_bytes;
+         iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
        }
 
-       if (status || bytes < 0)
+       /*
+       * Retry a write after an aborted write since we probably just got
+       * SIGTERM (<rdar://problem/6860126>)...
+       */
+
+       else if (iostatus == kIOReturnAborted)
+       {
+         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);
+
+#if DEBUG_WRITES
+          sleep(5);
+#endif /* DEBUG_WRITES */
+
+         bytes    = g.print_bytes;
+         iostatus = (*g.classdriver)->WritePipe(g.classdriver, (UInt8*)print_ptr, &bytes, 0);
+        }
+
+       if (iostatus)
        {
         /*
          * Write error - bail if we don't see an error we can retry...
          */
+         _cupsLangPrintFilter(stderr, "ERROR",
+                              _("Unable to send data to printer."));
+         fprintf(stderr, "DEBUG: USB class driver WritePipe returned %x\n",
+                 iostatus);
+
+         IOReturn err = (*g.classdriver)->Abort(g.classdriver);
+         fprintf(stderr, "DEBUG: USB class driver Abort returned %x\n",
+                 err);
 
-         OSStatus err = (*g.classdriver)->Abort(g.classdriver);
-         _cupsLangPuts(stderr, _("ERROR: Unable to send print data!\n"));
-         fprintf(stderr, "DEBUG: USB class driver WritePipe returned %ld\n",
-                 (long)status);
-         fprintf(stderr, "DEBUG: USB class driver Abort returned %ld\n",
-                 (long)err);
-         status = CUPS_BACKEND_STOP;
+         status = CUPS_BACKEND_FAILED;
          break;
        }
+       else if (bytes > 0)
+       {
+         fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
 
-        fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
-
-        g.print_bytes -= bytes;
-       print_ptr   += bytes;
-       total_bytes += bytes;
+         g.print_bytes -= bytes;
+         print_ptr   += bytes;
+         total_bytes += bytes;
+       }
       }
 
       if (print_fd != 0 && status == noErr)
@@ -688,6 +758,22 @@ print_device(const char *uri,              /* I - Device URI */
 
   fprintf(stderr, "DEBUG: Sent %lld bytes...\n", (off_t)total_bytes);
 
+  if (!print_fd)
+  {
+   /*
+    * Re-enable the SIGTERM handler so pthread_kill() will work...
+    */
+  
+    struct sigaction   action;         /* POSIX signal action */
+
+    memset(&action, 0, sizeof(action));
+
+    sigemptyset(&action.sa_mask);
+    sigaddset(&action.sa_mask, SIGTERM);
+    action.sa_handler = sigterm_handler;
+    sigaction(SIGTERM, &action, NULL);
+  }
+
  /*
   * Wait for the side channel thread to exit...
   */
@@ -718,6 +804,7 @@ print_device(const char *uri,               /* I - Device URI */
        * Force the side-channel thread to exit...
        */
 
+        fputs("DEBUG: Force the side-channel thread to exit...\n", stderr);
        pthread_kill(sidechannel_thread_id, SIGTERM);
       }
     }
@@ -757,6 +844,8 @@ print_device(const char *uri,               /* I - Device URI */
       * Force the read thread to exit...
       */
 
+      g.wait_eof = 0;
+      fputs("DEBUG: Force the read thread to exit...\n", stderr);
       pthread_kill(read_thread_id, SIGTERM);
     }
   }
@@ -822,6 +911,8 @@ static void *read_thread(void *reference)
     readstatus = (*g.classdriver)->ReadPipe(g.classdriver, readbuffer, &rbytes);
     if (readstatus == kIOReturnSuccess && rbytes > 0)
     {
+      fprintf(stderr, "DEBUG: Read %d bytes of back-channel data...\n",
+              (int)rbytes);
       cupsBackChannelWrite((char*)readbuffer, rbytes, 1.0);
 
       /* cntrl-d is echoed by the printer.
@@ -836,6 +927,12 @@ static void *read_thread(void *reference)
       parse_pserror(readbuffer, rbytes);
 #endif
     }
+    else if (readstatus == kIOUSBTransactionTimeout)
+      fputs("DEBUG: Got USB transaction timeout during read\n", stderr);
+    else if (readstatus == kIOUSBPipeStalled)
+      fputs("DEBUG: Got USB pipe stalled during read\n", stderr);
+    else if (readstatus == kIOReturnAborted)
+      fputs("DEBUG: Got USB return aborted during read\n", stderr);
 
    /*
     * Make sure this loop executes no more than once every 250 miliseconds...
@@ -877,46 +974,93 @@ sidechannel_thread(void *reference)
     datalen = sizeof(data);
 
     if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
-      continue;
+    {
+      if (status == CUPS_SC_STATUS_TIMEOUT)
+       continue;
+      else
+       break;
+    }
 
     switch (command)
     {
       case CUPS_SC_CMD_SOFT_RESET:     /* Do a soft reset */
+         fputs("DEBUG: CUPS_SC_CMD_SOFT_RESET received from driver...\n",
+               stderr);
+
           if ((*g.classdriver)->SoftReset != NULL)
          {
            soft_reset();
            cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, NULL, 0, 1.0);
+           fputs("DEBUG: Returning status CUPS_STATUS_OK with no bytes...\n",
+                 stderr);
          }
          else
          {
            cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED,
                                 NULL, 0, 1.0);
+           fputs("DEBUG: Returning status CUPS_STATUS_NOT_IMPLEMENTED with "
+                 "no bytes...\n", stderr);
          }
          break;
 
       case CUPS_SC_CMD_DRAIN_OUTPUT:   /* Drain all pending output */
+         fputs("DEBUG: CUPS_SC_CMD_DRAIN_OUTPUT received from driver...\n",
+               stderr);
+
          g.drain_output = 1;
          break;
 
       case CUPS_SC_CMD_GET_BIDI:               /* Is the connection bidirectional? */
+         fputs("DEBUG: CUPS_SC_CMD_GET_BIDI received from driver...\n",
+               stderr);
+
          data[0] = g.bidi_flag;
          cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
+
+         fprintf(stderr,
+                 "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n",
+                 data[0]);
          break;
 
       case CUPS_SC_CMD_GET_DEVICE_ID:  /* Return IEEE-1284 device ID */
+         fputs("DEBUG: CUPS_SC_CMD_GET_DEVICE_ID received from driver...\n",
+               stderr);
+
          datalen = sizeof(data);
          get_device_id(&status, data, &datalen);
          cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, datalen, 1.0);
+
+          if (datalen < sizeof(data))
+           data[datalen] = '\0';
+         else
+           data[sizeof(data) - 1] = '\0';
+
+         fprintf(stderr,
+                 "DEBUG: Returning CUPS_SC_STATUS_OK with %d bytes (%s)...\n",
+                 datalen, data);
          break;
 
       case CUPS_SC_CMD_GET_STATE:              /* Return device state */
+         fputs("DEBUG: CUPS_SC_CMD_GET_STATE received from driver...\n",
+               stderr);
+
          data[0] = CUPS_SC_STATE_ONLINE;
          cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
+
+         fprintf(stderr,
+                 "DEBUG: Returned CUPS_SC_STATUS_OK with 1 byte (%02X)...\n",
+                 data[0]);
          break;
 
       default:
+         fprintf(stderr, "DEBUG: Unknown side-channel command (%d) received "
+                         "from driver...\n", command);
+
          cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED,
                               NULL, 0, 1.0);
+
+         fputs("DEBUG: Returned CUPS_SC_STATUS_NOT_IMPLEMENTED with no bytes...\n",
+               stderr);
          break;
     }
   }
@@ -1036,11 +1180,13 @@ static Boolean list_device_cb(void *refcon,
 
       modelstr[0] = '/';
 
-      if (!CFStringGetCString(make, makestr, sizeof(makestr),
+      if (!make ||
+          !CFStringGetCString(make, makestr, sizeof(makestr),
                              kCFStringEncodingUTF8))
         strcpy(makestr, "Unknown");
 
-      if (!CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1,
+      if (!model ||
+          !CFStringGetCString(model, &modelstr[1], sizeof(modelstr)-1,
                              kCFStringEncodingUTF8))
         strcpy(modelstr + 1, "Printer");
 
@@ -1054,7 +1200,7 @@ static Boolean list_device_cb(void *refcon,
        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);
@@ -1140,7 +1286,7 @@ static Boolean find_device_cb(void *refcon,
   if (!keepLooking && g.status_timer != NULL)
   {
     fputs("STATE: -offline-report\n", stderr);
-    _cupsLangPuts(stderr, _("INFO: Printer is now online.\n"));
+    _cupsLangPrintFilter(stderr, "INFO", _("Printer is now online."));
     CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), g.status_timer, kCFRunLoopDefaultMode);
     CFRelease(g.status_timer);
     g.status_timer = NULL;
@@ -1158,7 +1304,7 @@ static void status_timer_cb(CFRunLoopTimerRef timer,
                            void *info)
 {
   fputs("STATE: +offline-report\n", stderr);
-  _cupsLangPuts(stderr, _("INFO: Printer is offline.\n"));
+  _cupsLangPrintFilter(stderr, "INFO", _("Printer is offline."));
 
   if (getenv("CLASS") != NULL)
   {
@@ -1193,18 +1339,10 @@ static void copy_deviceinfo(CFStringRef deviceIDString,
   CFStringRef serialKeys[] = { CFSTR("SN:"),  CFSTR("SERN:"), NULL };
 
   if (make != NULL)
-  {
-    if ((*make = copy_value_for_key(deviceIDString, makeKeys)) == NULL)
-      *make = CFStringCreateWithCString(kCFAllocatorDefault, "Unknown",
-                                        kCFStringEncodingUTF8);
-  }
+    *make = copy_value_for_key(deviceIDString, makeKeys);
 
   if (model != NULL)
-  {
-    if ((*model = copy_value_for_key(deviceIDString, modelKeys)) == NULL)
-      *model = CFStringCreateWithCString(kCFAllocatorDefault, "Printer",
-                                         kCFStringEncodingUTF8);
-  }
+    *model = copy_value_for_key(deviceIDString, modelKeys);
 
   if (serial != NULL)
     *serial = copy_value_for_key(deviceIDString, serialKeys);
@@ -1248,58 +1386,100 @@ static kern_return_t load_classdriver(CFStringRef          driverPath,
                                      printer_interface_t   intf,
                                      classdriver_t         ***printerDriver)
 {
-  kern_return_t kr = kUSBPrinterClassDeviceNotOpen;
-  classdriver_t **driver = NULL;
-  CFStringRef bundle = (driverPath == NULL ? kUSBGenericTOPrinterClassDriver : driverPath);
+  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 */
+
+
+  CFStringGetCString(bundle, bundlestr, sizeof(bundlestr), kCFStringEncodingUTF8);
+
+ /*
+  * Validate permissions for the class driver...
+  */
 
-  if (bundle != NULL)
+  if (stat(bundlestr, &bundleinfo))
   {
-    CFURLRef url = CFURLCreateWithFileSystemPath(NULL, bundle, kCFURLPOSIXPathStyle, true);
-    CFPlugInRef plugin = (url != NULL ? CFPlugInCreate(NULL, url) : NULL);
+    fprintf(stderr, "DEBUG: Class driver \"%s\" not available: %s\n",
+           bundlestr, strerror(errno));
+    fputs("STATE: +cups-missing-filter-warning\n", stderr);
+
+    if (errno == ENOENT && driverPath)
+      return (load_classdriver(NULL, intf, printerDriver));
+    else
+      return (kr);
+  }
+  else if (bundleinfo.st_uid ||
+           (bundleinfo.st_gid && (bundleinfo.st_mode & S_IWGRP)) ||
+          (bundleinfo.st_mode & S_IWOTH))
+  {
+    fprintf(stderr, "DEBUG: Class driver \"%s\" has insecure file "
+                   "permissions (0%o/uid=%d/gid=%d).\n", bundlestr,
+                   bundleinfo.st_mode, (int)bundleinfo.st_uid,
+                   (int)bundleinfo.st_gid);
+    fputs("STATE: +cups-insecure-filter-warning\n", stderr);
+
+    if (bundleinfo.st_uid || (bundleinfo.st_mode & S_IWOTH))
+    {
+      if (driverPath)
+        return (load_classdriver(NULL, intf, printerDriver));
+      else
+        return (kr);
+    }
+  }
+
+ /*
+  * Try loading the class driver...
+  */
 
-    if (url != NULL)
-      CFRelease(url);
+  url = CFURLCreateWithFileSystemPath(NULL, bundle, kCFURLPOSIXPathStyle, true);
 
-    if (plugin != NULL)
+  if (url)
+  {
+    plugin = CFPlugInCreate(NULL, url);
+    CFRelease(url);
+  }
+  else
+    plugin = NULL;
+
+  if (plugin)
+  {
+    CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID, plugin);
+    if (factories != NULL && CFArrayGetCount(factories) > 0)
     {
-      CFArrayRef factories = CFPlugInFindFactoriesForPlugInTypeInPlugIn(kUSBPrinterClassTypeID, plugin);
-      if (factories != NULL && CFArrayGetCount(factories) > 0)
+      CFUUIDRef factoryID = CFArrayGetValueAtIndex(factories, 0);
+      IUnknownVTbl **iunknown = CFPlugInInstanceCreate(NULL, factoryID, kUSBPrinterClassTypeID);
+      if (iunknown != NULL)
       {
-       CFUUIDRef factoryID = CFArrayGetValueAtIndex(factories, 0);
-       IUnknownVTbl **iunknown = CFPlugInInstanceCreate(NULL, factoryID, kUSBPrinterClassTypeID);
-       if (iunknown != NULL)
+       kr = (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID), (LPVOID *)&driver);
+       if (kr == kIOReturnSuccess && driver != NULL)
        {
-         kr = (*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(kUSBPrinterClassInterfaceID), (LPVOID *)&driver);
-         if (kr == kIOReturnSuccess && driver != NULL)
-         {
-           classdriver_t **genericDriver = NULL;
-           if (driverPath != NULL && CFStringCompare(driverPath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo)
-             kr = load_classdriver(NULL, intf, &genericDriver);
+         classdriver_t **genericDriver = NULL;
+         if (driverPath != NULL && CFStringCompare(driverPath, kUSBGenericTOPrinterClassDriver, 0) != kCFCompareEqualTo)
+           kr = load_classdriver(NULL, intf, &genericDriver);
 
-           if (kr == kIOReturnSuccess)
-           {
-             (*driver)->interface = intf;
-             (*driver)->Initialize(driver, genericDriver);
+         if (kr == kIOReturnSuccess)
+         {
+           (*driver)->interface = intf;
+           (*driver)->Initialize(driver, genericDriver);
 
-             (*driver)->plugin = plugin;
-             (*driver)->interface = intf;
-             *printerDriver = driver;
-           }
+           (*driver)->plugin = plugin;
+           (*driver)->interface = intf;
+           *printerDriver = driver;
          }
-         (*iunknown)->Release(iunknown);
        }
-       CFRelease(factories);
+       (*iunknown)->Release(iunknown);
       }
+      CFRelease(factories);
     }
   }
 
-#ifdef DEBUG
-  char bundlestr[1024];
-  CFStringGetCString(bundle, bundlestr, sizeof(bundlestr), kCFStringEncodingUTF8);
   fprintf(stderr, "DEBUG: load_classdriver(%s) (kr:0x%08x)\n", bundlestr, (int)kr);
-#endif /* DEBUG */
 
-  return kr;
+  return (kr);
 }
 
 
@@ -1684,9 +1864,9 @@ static void parse_options(char *options,
               !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"))
       strlcpy(serial, value, serial_size);
@@ -1722,7 +1902,7 @@ static void setup_cfLanguage(void)
     lang[0] = CFStringCreateWithCString(kCFAllocatorDefault, requestedLang, kCFStringEncodingUTF8);
     langArray = CFArrayCreate(kCFAllocatorDefault, (const void **)lang, sizeof(lang) / sizeof(lang[0]), &kCFTypeArrayCallBacks);
 
-    CFPreferencesSetAppValue(CFSTR("AppleLanguages"), langArray, kCFPreferencesCurrentApplication);
+    CFPreferencesSetValue(CFSTR("AppleLanguages"), langArray, kCFPreferencesCurrentApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
     fprintf(stderr, "DEBUG: usb: AppleLanguages=\"%s\"\n", requestedLang);
 
     CFRelease(lang[0]);
@@ -1774,9 +1954,13 @@ static void run_legacy_backend(int argc,
     * 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 */
+    char               usbpath[1024];  /* Path to USB backend */
+    const char         *cups_serverbin;/* Path to CUPS binaries */
+
 
     memset(&action, 0, sizeof(action));
     sigaddset(&action.sa_mask, SIGTERM);
@@ -1819,7 +2003,8 @@ static void run_legacy_backend(int argc,
 #  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);
       }
     }
@@ -1828,16 +2013,22 @@ static void run_legacy_backend(int argc,
     * Set up the arguments and call posix_spawn...
     */
 
+    if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
+      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 ++)
       my_argv[i] = argv[i];
 
     my_argv[i] = NULL;
 
-    if (posix_spawn(&child_pid, "/usr/libexec/cups/backend/usb", NULL, &attrs,
-                    my_argv, environ))
+    if ((err = posix_spawn(&child_pid, usbpath, NULL, &attrs, my_argv,
+                           environ)) != 0)
     {
-      perror("DEBUG: Unable to exec /usr/libexec/cups/backend/usb");
-      _cupsLangPrintf(stderr, _("Unable to use legacy USB class driver!\n"));
+      fprintf(stderr, "DEBUG: Unable to exec %s: %s\n", usbpath,
+              strerror(err));
+      _cupsLangPrintFilter(stderr, "ERROR",
+                           _("Unable to use legacy USB class driver."));
       exit(CUPS_BACKEND_STOP);
     }
 
@@ -1863,14 +2054,14 @@ static void run_legacy_backend(int argc,
     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",
@@ -1885,22 +2076,44 @@ static void run_legacy_backend(int argc,
 
   exit(exitstatus);
 }
+#endif /* __i386__ || __x86_64__ */
+
 
 /*
  * 'sigterm_handler()' - SIGTERM handler.
  */
 
-static void sigterm_handler(int sig)
+static void
+sigterm_handler(int sig)               /* I - Signal */
 {
-  /* If we started a child process pass the signal on to it...
-   */
+#if defined(__i386__) || defined(__x86_64__)
+ /*
+  * If we started a child process pass the signal on to it...
+  */
+
   if (child_pid)
-    kill(child_pid, sig);
+  {
+   /*
+    * If we started a child process pass the signal on to it...
+    */
 
-  exit(1);
-}
+    int        status;
 
+    kill(child_pid, sig);
+    while (waitpid(child_pid, &status, 0) < 0 && errno == EINTR);
+
+    if (WIFEXITED(status))
+      exit(WEXITSTATUS(status));
+    else if (status == SIGTERM || status == SIGKILL)
+      exit(0);
+    else
+    {
+      fprintf(stderr, "DEBUG: Child crashed on signal %d\n", status);
+      exit(CUPS_BACKEND_STOP);
+    }
+  }
 #endif /* __i386__ || __x86_64__ */
+}
 
 
 #ifdef PARSE_PS_ERRORS
@@ -2051,12 +2264,11 @@ static void get_device_id(cups_sc_status_t *status,
                          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);