]> 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 2fc7a27d38ebf93838ad0a144a869705efed8484..4b7bb5c38856c75bbf308db5634866523761fde5 100644 (file)
@@ -1,7 +1,7 @@
 /*
 * "$Id: usb-darwin.c 7953 2008-09-17 01:43:19Z mike $"
 *
-* Copyright 2005-2009 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 */
@@ -282,7 +292,6 @@ static void status_timer_cb(CFRunLoopTimerRef timer, void *info);
 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 */
 #endif /* __i386__ || __x86_64__ */
-static int     job_canceled = 0;       /* Was the job canceled? */
 static void sigterm_handler(int sig);  /* SIGTERM handler */
 
 #ifdef PARSE_PS_ERRORS
@@ -318,8 +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 */
-                 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? */
@@ -364,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);
   }
@@ -422,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)
@@ -440,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 */
       }
@@ -451,9 +462,9 @@ print_device(const char *uri,               /* I - Device URI */
   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...)
    */
 
@@ -465,7 +476,7 @@ print_device(const char *uri,               /* I - Device URI */
     memset(&action, 0, sizeof(action));
 
     sigemptyset(&action.sa_mask);
-    action.sa_handler = sigterm_handler;
+    action.sa_handler = SIG_IGN;
     sigaction(SIGTERM, &action, NULL);
   }
 
@@ -487,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);
     }
   }
@@ -505,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);
   }
 
@@ -521,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)
     {
@@ -584,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);
+               "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);
        }
       }
 
@@ -612,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)
        {
@@ -622,8 +651,10 @@ print_device(const char *uri,              /* I - Device URI */
 
          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);
          }
 
@@ -646,8 +677,7 @@ 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);
 
        /*
@@ -656,49 +686,68 @@ print_device(const char *uri,             /* I - Device URI */
        */
 
        if (iostatus == kIOUSBTransactionTimeout)
+       {
+         fputs("DEBUG: Got USB transaction timeout during write\n", stderr);
          iostatus = 0;
+       }
 
        /*
-        * Ignore stall errors, since we clear any stalls in the class driver...
+        * If we've stalled, retry the write...
        */
 
-       if (iostatus == kIOUSBPipeStalled)
-         iostatus = 0;
+       else if (iostatus == kIOUSBPipeStalled)
+       {
+         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);
+       }
 
        /*
-       * 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 (<rdar://problem/6860126>)...
        */
 
-       if (iostatus == kIOReturnAborted && prev_iostatus != kIOReturnAborted)
+       else if (iostatus == kIOReturnAborted)
        {
-         prev_iostatus = iostatus;
-         iostatus      = 0;
-       }
-       else
-         prev_iostatus = iostatus;
+         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 (iostatus || bytes < 0)
+#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...
          */
-
-         IOReturn err = (*g.classdriver)->Abort(g.classdriver);
-         _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);
+
+         IOReturn err = (*g.classdriver)->Abort(g.classdriver);
          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: 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)
@@ -709,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...
   */
@@ -739,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);
       }
     }
@@ -779,6 +845,7 @@ print_device(const char *uri,               /* I - Device URI */
       */
 
       g.wait_eof = 0;
+      fputs("DEBUG: Force the read thread to exit...\n", stderr);
       pthread_kill(read_thread_id, SIGTERM);
     }
   }
@@ -844,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.
@@ -858,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...
@@ -899,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;
     }
   }
@@ -1078,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);
@@ -1164,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;
@@ -1182,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)
   {
@@ -1281,15 +1403,32 @@ static kern_return_t load_classdriver(CFStringRef           driverPath,
 
   if (stat(bundlestr, &bundleinfo))
   {
-    fprintf(stderr, "Unable to load class driver \"%s\": %s", bundlestr,
-           strerror(errno));
-    return (kr);
+    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_mode & S_IWOTH)
+  else if (bundleinfo.st_uid ||
+           (bundleinfo.st_gid && (bundleinfo.st_mode & S_IWGRP)) ||
+          (bundleinfo.st_mode & S_IWOTH))
   {
-    fprintf(stderr, "Unable to load class driver \"%s\": insecure file "
-                   "permissions (0%o)", bundlestr, bundleinfo.st_mode);
-    return (kr);
+    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);
+    }
   }
 
  /*
@@ -1725,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);
@@ -1815,6 +1954,7 @@ 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 */
@@ -1863,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);
       }
     }
@@ -1881,11 +2022,13 @@ static void run_legacy_backend(int argc,
 
     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);
     }
 
@@ -1911,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",
@@ -1944,6 +2087,10 @@ static void
 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)
   {
    /*
@@ -1961,17 +2108,11 @@ sigterm_handler(int sig)                /* I - Signal */
       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;
 }
 
 
@@ -2123,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);