]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/cups-deviced.c
Import CUPS 1.4svn-r7464.
[thirdparty/cups.git] / scheduler / cups-deviced.c
index 385037ea244d35287dbbe5a08e1a8e9c13b2634d..9c5b6991f5d236916fa61d7356fe75ecfb9d3976 100644 (file)
  *
  * Contents:
  *
- *   main()            - Scan for devices and return an IPP response.
- *   add_dev()         - Add a new device to the list.
- *   compare_devs()    - Compare device names for sorting.
- *   run_backend()     - Run a backend to gather the available devices.
- *   sigalrm_handler() - Handle alarm signals for backends that get hung
+ *   main()                 - Scan for devices and return an IPP response.
+ *   add_device()           - Add a new device to the list.
+ *   compare_devices()      - Compare device names to eliminate duplicates.
+ *   create_strings_array() - Create a CUPS array of strings.
+ *   get_current_time()     - Get the current time as a double value in seconds.
+ *   get_device()           - Get a device from a backend.
+ *   process_children()     - Process all dead children...
+ *   sigchld_handler()      - Handle 'child' signals from old processes.
+ *   start_backend()        - Run a backend to gather the available devices.
  */
 
 /*
 #include <cups/array.h>
 #include <cups/dir.h>
 #include <fcntl.h>
+#include <sys/wait.h>
+#include <poll.h>
+
+
+/*
+ * Constants...
+ */
+
+#define MAX_BACKENDS   200             /* Maximum number of backends we'll run */
+
+
+/*
+ * Backend information...
+ */
+
+typedef struct
+{
+  char         *name;                  /* Name of backend */
+  int          pid,                    /* Process ID */
+               status;                 /* Exit status */
+  cups_file_t  *pipe;                  /* Pipe from backend stdout */
+  int          count;                  /* Number of devices found */
+} cupsd_backend_t;
 
 
 /*
@@ -42,30 +69,51 @@ typedef struct
        device_info[128],               /* Device info/description */
        device_uri[1024],               /* Device URI */
        device_id[1024];                /* 1284 Device ID */
-} dev_info_t;
+} cupsd_device_t;
 
 
 /*
  * Local globals...
  */
 
-static int             alarm_tripped;  /* Non-zero if alarm was tripped */
-static cups_array_t    *devs;          /* Device info */
+static int             num_backends = 0,
+                                       /* Total backends */
+                       active_backends = 0;
+                                       /* Active backends */
+static cupsd_backend_t backends[MAX_BACKENDS];
+                                       /* Array of backends */
+static struct pollfd   backend_fds[MAX_BACKENDS];
+                                       /* Array for poll() */
+static cups_array_t    *devices;       /* Array of devices */
 static int             normal_user;    /* Normal user ID */
+static int             device_limit;   /* Maximum number of devices */
+static int             send_class,     /* Send device-class attribute? */
+                       send_info,      /* Send device-info attribute? */
+                       send_make_and_model,
+                                       /* Send device-make-and-model attribute? */
+                       send_uri,       /* Send device-uri attribute? */
+                       send_id;        /* Send device-id attribute? */
+static int             dead_children = 0;
+                                       /* Dead children? */
 
 
 /*
  * Local functions...
  */
 
-static dev_info_t      *add_dev(const char *device_class,
-                                const char *device_make_and_model,
-                                const char *device_info,
-                                const char *device_uri,
-                                const char *device_id);
-static int             compare_devs(dev_info_t *p0, dev_info_t *p1);
-static FILE            *run_backend(const char *backend, int uid, int *pid);
-static void            sigalrm_handler(int sig);
+static int             add_device(const char *device_class,
+                                  const char *device_make_and_model,
+                                  const char *device_info,
+                                  const char *device_uri,
+                                  const char *device_id);
+static int             compare_devices(cupsd_device_t *p0,
+                                       cupsd_device_t *p1);
+static cups_array_t    *create_strings_array(const char *s);
+static double          get_current_time(void);
+static int             get_device(cupsd_backend_t *backend);
+static void            process_children(void);
+static void            sigchld_handler(int sig);
+static int             start_backend(const char *backend, int root);
 
 
 /*
@@ -80,31 +128,19 @@ int                                        /* O - Exit code */
 main(int  argc,                                /* I - Number of command-line args */
      char *argv[])                     /* I - Command-line arguments */
 {
-  const char   *server_bin;            /* CUPS_SERVERBIN environment variable */
-  char         backends[1024];         /* Location of backends */
+  int          i;                      /* Looping var */
   int          request_id;             /* Request ID */
-  int          count;                  /* Number of devices from backend */
-  int          compat;                 /* Compatibility device? */
-  FILE         *fp;                    /* Pipe to device backend */
-  int          pid;                    /* Process ID of backend */
+  int          timeout;                /* Timeout in seconds */
+  const char   *server_bin;            /* CUPS_SERVERBIN environment variable */
+  char         filename[1024];         /* Backend directory filename */
   cups_dir_t   *dir;                   /* Directory pointer */
   cups_dentry_t *dent;                 /* Directory entry */
-  char         filename[1024],         /* Name of backend */
-               line[2048],             /* Line from backend */
-               dclass[64],             /* Device class */
-               uri[1024],              /* Device URI */
-               info[128],              /* Device info */
-               make_model[256],        /* Make and model */
-               device_id[1024];        /* 1284 device ID */
+  double       current_time,           /* Current time */
+               end_time;               /* Ending time */
   int          num_options;            /* Number of options */
   cups_option_t        *options;               /* Options */
-  const char   *requested;             /* requested-attributes option */
-  int          send_class,             /* Send device-class attribute? */
-               send_info,              /* Send device-info attribute? */
-               send_make_and_model,    /* Send device-make-and-model attribute? */
-               send_uri,               /* Send device-uri attribute? */
-               send_id;                /* Send device-id attribute? */
-  dev_info_t   *dev;                   /* Current device */
+  cups_array_t *requested,             /* requested-attributes values */
+               *exclude;               /* exclude-schemes values */
 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
   struct sigaction action;             /* Actions for POSIX signals */
 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
@@ -116,53 +152,79 @@ main(int  argc,                           /* I - Number of command-line args */
   * Check the command-line...
   */
 
-  if (argc > 1)
-    request_id = atoi(argv[1]);
-  else
-    request_id = 1;
-
-  if (argc != 5)
+  if (argc != 6)
   {
-    fputs("Usage: cups-deviced request-id limit user-id options\n", stderr);
+    fputs("Usage: cups-deviced request-id limit timeout user-id options\n", stderr);
 
     return (1);
   }
 
+  request_id = atoi(argv[1]);
   if (request_id < 1)
   {
-    fprintf(stderr, "cups-deviced: Bad request ID %d!\n", request_id);
+    fprintf(stderr, "ERROR: [cups-deviced] Bad request ID %d!\n", request_id);
 
     return (1);
   }
 
-  normal_user = atoi(argv[3]);
-  if (normal_user <= 0)
+  device_limit = atoi(argv[2]);
+  if (device_limit < 0)
   {
-    fprintf(stderr, "cups-deviced: Bad user %d!\n", normal_user);
+    fprintf(stderr, "ERROR: [cups-deviced] Bad limit %d!\n", device_limit);
 
     return (1);
   }
 
-  num_options = cupsParseOptions(argv[4], 0, &options);
-  requested   = cupsGetOption("requested-attributes", num_options, options);
+  timeout = atoi(argv[3]);
+  if (timeout < 1)
+  {
+    fprintf(stderr, "ERROR: [cups-deviced] Bad timeout %d!\n", timeout);
+
+    return (1);
+  }
 
-  if (!requested || strstr(requested, "all"))
+  normal_user = atoi(argv[4]);
+  if (normal_user <= 0)
   {
-    send_class          = 1;
-    send_info           = 1;
-    send_make_and_model = 1;
-    send_uri            = 1;
-    send_id             = 1;
+    fprintf(stderr, "ERROR: [cups-deviced] Bad user %d!\n", normal_user);
+
+    return (1);
   }
+
+  num_options = cupsParseOptions(argv[5], 0, &options);
+  requested   = create_strings_array(cupsGetOption("requested-attributes",
+                                                   num_options, options));
+  exclude     = create_strings_array(cupsGetOption("exclude-schemes",
+                                                   num_options, options));
+
+  if (!requested || cupsArrayFind(requested, "all") != NULL)
+    send_class = send_info = send_make_and_model = send_uri = send_id = 1;
   else
   {
-    send_class          = strstr(requested, "device-class") != NULL;
-    send_info           = strstr(requested, "device-info") != NULL;
-    send_make_and_model = strstr(requested, "device-make-and-model") != NULL;
-    send_uri            = strstr(requested, "device-uri") != NULL;
-    send_id             = strstr(requested, "device-id") != NULL;
+    send_class          = cupsArrayFind(requested, "device-class") != NULL;
+    send_info           = cupsArrayFind(requested, "device-info") != NULL;
+    send_make_and_model = cupsArrayFind(requested, "device-make-and-model") != NULL;
+    send_uri            = cupsArrayFind(requested, "device-uri") != NULL;
+    send_id             = cupsArrayFind(requested, "device-id") != NULL;
   }
 
+ /*
+  * Listen to child signals...
+  */
+
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+  sigset(SIGCHLD, sigchld_handler);
+#elif defined(HAVE_SIGACTION)
+  memset(&action, 0, sizeof(action));
+
+  sigemptyset(&action.sa_mask);
+  sigaddset(&action.sa_mask, SIGCHLD);
+  action.sa_handler = sigchld_handler;
+  sigaction(SIGCHLD, &action, NULL);
+#else
+  signal(SIGCLD, sigchld_handler);     /* No, SIGCLD isn't a typo... */
+#endif /* HAVE_SIGSET */
+
  /*
   * Try opening the backend directory...
   */
@@ -170,12 +232,12 @@ main(int  argc,                           /* I - Number of command-line args */
   if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
     server_bin = CUPS_SERVERBIN;
 
-  snprintf(backends, sizeof(backends), "%s/backend", server_bin);
+  snprintf(filename, sizeof(filename), "%s/backend", server_bin);
 
-  if ((dir = cupsDirOpen(backends)) == NULL)
+  if ((dir = cupsDirOpen(filename)) == NULL)
   {
     fprintf(stderr, "ERROR: [cups-deviced] Unable to open backend directory "
-                    "\"%s\": %s", backends, strerror(errno));
+                    "\"%s\": %s", filename, strerror(errno));
 
     return (1);
   }
@@ -184,7 +246,7 @@ main(int  argc,                             /* I - Number of command-line args */
   * Setup the devices array...
   */
 
-  devs = cupsArrayNew((cups_array_func_t)compare_devs, NULL);
+  devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
 
  /*
   * Loop through all of the device backends...
@@ -197,273 +259,172 @@ main(int  argc,                         /* I - Number of command-line args */
     */
 
     if (!S_ISREG(dent->fileinfo.st_mode) ||
+        !isalnum(dent->filename[0] & 255) ||
         (dent->fileinfo.st_mode & (S_IRUSR | S_IXUSR)) != (S_IRUSR | S_IXUSR))
       continue;
 
-   /*
-    * Change effective users depending on the backend permissions...
-    */
-
-    snprintf(filename, sizeof(filename), "%s/%s", backends, dent->filename);
+    if (cupsArrayFind(exclude, dent->filename))
+      continue;
 
    /*
     * Backends without permissions for normal users run as root,
     * all others run as the unprivileged user...
     */
 
-    fp = run_backend(filename,
-                     (dent->fileinfo.st_mode & (S_IRWXG | S_IRWXO))
-                        ? normal_user : 0,
-                    &pid);
-
-   /*
-    * Collect the output from the backend...
-    */
-
-    if (fp)
-    {
-     /*
-      * Set an alarm for the first read from the backend; this avoids
-      * problems when a backend is hung getting device information.
-      */
-
-#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
-      sigset(SIGALRM, sigalrm_handler);
-#elif defined(HAVE_SIGACTION)
-      memset(&action, 0, sizeof(action));
-
-      sigemptyset(&action.sa_mask);
-      sigaddset(&action.sa_mask, SIGALRM);
-      action.sa_handler = sigalrm_handler;
-      sigaction(SIGALRM, &action, NULL);
-#else
-      signal(SIGALRM, sigalrm_handler);
-#endif /* HAVE_SIGSET */
-
-      alarm_tripped = 0;
-      count         = 0;
-      compat        = !strcmp(dent->filename, "smb");
-
-      alarm(30);
-
-      while (fgets(line, sizeof(line), fp) != NULL)
-      {
-       /*
-        * Reset the alarm clock...
-       */
-
-        alarm(30);
-
-       /*
-        * Each line is of the form:
-       *
-       *   class URI "make model" "name" ["1284 device ID"]
-       */
-
-        device_id[0] = '\0';
-
-        if (!strncasecmp(line, "Usage", 5))
-         compat = 1;
-        else if (sscanf(line,
-                       "%63s%1023s%*[ \t]\"%255[^\"]\"%*[ \t]\"%127[^\"]\""
-                       "%*[ \t]\"%1023[^\"]",
-                       dclass, uri, make_model, info, device_id) < 4)
-        {
-        /*
-         * Bad format; strip trailing newline and write an error message.
-         */
-
-          if (line[strlen(line) - 1] == '\n')
-           line[strlen(line) - 1] = '\0';
-
-         fprintf(stderr, "ERROR: [cups-deviced] Bad line from \"%s\": %s\n",
-                 dent->filename, line);
-          compat = 1;
-         break;
-        }
-       else
-       {
-        /*
-         * Add the device to the array of available devices...
-         */
-
-          dev = add_dev(dclass, make_model, info, uri, device_id);
-         if (!dev)
-         {
-            cupsDirClose(dir);
-           fclose(fp);
-            kill(pid, SIGTERM);
-           return (1);
-         }
-
-          fprintf(stderr, "DEBUG: [cups-deviced] Added device \"%s\"...\n",
-                 uri);
-         count ++;
-       }
-      }
-
-     /*
-      * Turn the alarm clock off and close the pipe to the command...
-      */
-
-      alarm(0);
-
-      if (alarm_tripped)
-        fprintf(stderr, "WARNING: [cups-deviced] Backend \"%s\" did not "
-                       "respond within 30 seconds!\n", dent->filename);
-
-      fclose(fp);
-      kill(pid, SIGTERM);
-
-     /*
-      * Hack for backends that don't support the CUPS 1.1 calling convention:
-      * add a network device with the method == backend name.
-      */
-
-      if (count == 0 && compat)
-      {
-       snprintf(line, sizeof(line), "Unknown Network Device (%s)",
-                dent->filename);
-
-        dev = add_dev("network", line, "Unknown", dent->filename, "");
-       if (!dev)
-       {
-          cupsDirClose(dir);
-         return (1);
-       }
-
-        fprintf(stderr, "DEBUG: [cups-deviced] Compatibility device "
-                       "\"%s\"...\n", dent->filename);
-      }
-    }
-    else
-      fprintf(stderr, "WARNING: [cups-deviced] Unable to execute \"%s\" "
-                      "backend: %s\n", dent->filename, strerror(errno));
+    start_backend(dent->filename,
+                  !(dent->fileinfo.st_mode & (S_IRWXG | S_IRWXO)));
   }
 
   cupsDirClose(dir);
 
  /*
-  * Output the list of devices...
+  * Collect devices...
   */
 
-  puts("Content-Type: application/ipp\n");
+  if (getenv("SOFTWARE"))
+    puts("Content-Type: application/ipp\n");
 
   cupsdSendIPPHeader(IPP_OK, request_id);
   cupsdSendIPPGroup(IPP_TAG_OPERATION);
   cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
   cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
 
-  if ((count = atoi(argv[2])) <= 0)
-    count = cupsArrayCount(devs);
-
-  if (count > cupsArrayCount(devs))
-    count = cupsArrayCount(devs);
+  end_time = get_current_time() + timeout;
 
-  for (dev = (dev_info_t *)cupsArrayFirst(devs);
-       count > 0;
-       count --, dev = (dev_info_t *)cupsArrayNext(devs))
+  while (active_backends > 0 && (current_time = get_current_time()) < end_time)
   {
    /*
-    * Add strings to attributes...
+    * Collect the output from the backends...
+    */
+
+    timeout = (int)(1000 * (end_time - current_time));
+
+    if (poll(backend_fds, num_backends, timeout) > 0)
+    {
+      for (i = 0; i < num_backends; i ++)
+        if (backend_fds[i].revents && backends[i].pipe)
+         if (get_device(backends + i))
+         {
+           backend_fds[i].fd     = 0;
+           backend_fds[i].events = 0;
+         }
+    }
+
+   /*
+    * Get exit status from children...
     */
 
-    cupsdSendIPPGroup(IPP_TAG_PRINTER);
-    if (send_class)
-      cupsdSendIPPString(IPP_TAG_KEYWORD, "device-class", dev->device_class);
-    if (send_info)
-      cupsdSendIPPString(IPP_TAG_TEXT, "device-info", dev->device_info);
-    if (send_make_and_model)
-      cupsdSendIPPString(IPP_TAG_TEXT, "device-make-and-model",
-                        dev->device_make_and_model);
-    if (send_uri)
-      cupsdSendIPPString(IPP_TAG_URI, "device-uri", dev->device_uri);
-    if (send_id)
-      cupsdSendIPPString(IPP_TAG_TEXT, "device-id", dev->device_id);
+    if (dead_children)
+      process_children();
   }
 
   cupsdSendIPPTrailer();
 
  /*
-  * Free the devices array and return...
+  * Terminate any remaining backends and exit...
   */
 
-  for (dev = (dev_info_t *)cupsArrayFirst(devs);
-       dev;
-       dev = (dev_info_t *)cupsArrayNext(devs))
-    free(dev);
-
-  cupsArrayDelete(devs);
+  if (active_backends > 0)
+  {
+    for (i = 0; i < num_backends; i ++)
+      if (backends[i].pid)
+       kill(backends[i].pid, SIGTERM);
+  }
 
   return (0);
 }
 
 
 /*
- * 'add_dev()' - Add a new device to the list.
+ * 'add_device()' - Add a new device to the list.
  */
 
-static dev_info_t *                    /* O - New device or NULL on error */
-add_dev(
+static int                             /* O - 0 on success, -1 on error */
+add_device(
     const char *device_class,          /* I - Device class */
     const char *device_make_and_model, /* I - Device make and model */
     const char *device_info,           /* I - Device information */
     const char *device_uri,            /* I - Device URI */
     const char *device_id)             /* I - 1284 device ID */
 {
-  dev_info_t   *dev,                   /* New device */
-               *temp;                  /* Found device */
+  cupsd_device_t       *device,        /* New device */
+                       *temp;          /* Found device */
 
 
  /*
   * Allocate memory for the device record...
   */
 
-  if ((dev = calloc(1, sizeof(dev_info_t))) == NULL)
+  if ((device = calloc(1, sizeof(cupsd_device_t))) == NULL)
   {
     fputs("ERROR: [cups-deviced] Ran out of memory allocating a device!\n",
           stderr);
-    return (NULL);
+    return (-1);
   }
 
  /*
   * Copy the strings over...
   */
 
-  strlcpy(dev->device_class, device_class, sizeof(dev->device_class));
-  strlcpy(dev->device_make_and_model, device_make_and_model,
-          sizeof(dev->device_make_and_model));
-  strlcpy(dev->device_info, device_info, sizeof(dev->device_info));
-  strlcpy(dev->device_uri, device_uri, sizeof(dev->device_uri));
-  strlcpy(dev->device_id, device_id, sizeof(dev->device_id));
+  strlcpy(device->device_class, device_class, sizeof(device->device_class));
+  strlcpy(device->device_make_and_model, device_make_and_model,
+          sizeof(device->device_make_and_model));
+  strlcpy(device->device_info, device_info, sizeof(device->device_info));
+  strlcpy(device->device_uri, device_uri, sizeof(device->device_uri));
+  strlcpy(device->device_id, device_id, sizeof(device->device_id));
 
  /*
   * Add the device to the array and return...
   */
 
-  if ((temp = cupsArrayFind(devs, dev)) != NULL)
+  if ((temp = cupsArrayFind(devices, device)) != NULL)
   {
    /*
     * Avoid duplicates!
     */
 
-    free(dev);
-    dev = temp;
+    free(device);
   }
   else
-    cupsArrayAdd(devs, dev);
-    
-  return (dev);
+  {
+    cupsArrayAdd(devices, device);
+
+    if (device_limit <= 0 || cupsArrayCount(devices) < device_limit)
+    {
+     /*
+      * Send device info...
+      */
+
+      cupsdSendIPPGroup(IPP_TAG_PRINTER);
+      if (send_class)
+       cupsdSendIPPString(IPP_TAG_KEYWORD, "device-class",
+                          device->device_class);
+      if (send_info)
+       cupsdSendIPPString(IPP_TAG_TEXT, "device-info", device->device_info);
+      if (send_make_and_model)
+       cupsdSendIPPString(IPP_TAG_TEXT, "device-make-and-model",
+                          device->device_make_and_model);
+      if (send_uri)
+       cupsdSendIPPString(IPP_TAG_URI, "device-uri", device->device_uri);
+      if (send_id)
+       cupsdSendIPPString(IPP_TAG_TEXT, "device-id", device->device_id);
+
+      fflush(stdout);
+      fputs("DEBUG: Flushed attributes...\n", stderr);
+    }
+  }
+
+  return (0);
 }
 
 
 /*
- * 'compare_devs()' - Compare device names for sorting.
+ * 'compare_devices()' - Compare device names to eliminate duplicates.
  */
 
 static int                             /* O - Result of comparison */
-compare_devs(dev_info_t *d0,           /* I - First device */
-             dev_info_t *d1)           /* I - Second device */
+compare_devices(cupsd_device_t *d0,    /* I - First device */
+                cupsd_device_t *d1)    /* I - Second device */
 {
   int          diff;                   /* Difference between strings */
 
@@ -482,44 +443,276 @@ compare_devs(dev_info_t *d0,             /* I - First device */
 
 
 /*
- * 'run_backend()' - Run a backend to gather the available devices.
+ * 'create_strings_array()' - Create a CUPS array of strings.
  */
 
-static FILE *                          /* O - stdout of backend */
-run_backend(const char *backend,       /* I - Backend to run */
-            int        uid,            /* I - User ID to run as */
-           int        *pid)            /* O - Process ID of backend */
+static cups_array_t *                  /* O - CUPS array */
+create_strings_array(const char *s)    /* I - Comma-delimited strings */
 {
-  int  fds[2];                         /* Pipe file descriptors */
+  cups_array_t *a;                     /* CUPS array */
+  const char   *start,                 /* Start of string */
+               *end;                   /* End of string */
+  char         *ptr;                   /* New string */
+
+
+  if (!s)
+    return (NULL);
+
+  if ((a = cupsArrayNew((cups_array_func_t)strcmp, NULL)) != NULL)
+  {
+    for (start = end = s; *end; start = end + 1)
+    {
+     /*
+      * Find the end of the current delimited string...
+      */
+
+      if ((end = strchr(start, ',')) == NULL)
+        end = start + strlen(start);
+
+     /*
+      * Duplicate the string and add it to the array...
+      */
+
+      if ((ptr = calloc(1, end - start + 1)) == NULL)
+        break;
 
+      memcpy(ptr, start, end - start);
+      cupsArrayAdd(a, ptr);
+    }
+  }
+
+  return (a);
+}
+
+
+/*
+ * 'get_current_time()' - Get the current time as a double value in seconds.
+ */
+
+static double                          /* O - Time in seconds */
+get_current_time(void)
+{
+  struct timeval       curtime;        /* Current time */
+
+
+  gettimeofday(&curtime, NULL);
+
+  return (curtime.tv_sec + 0.000001 * curtime.tv_usec);
+}
+
+
+/*
+ * 'get_device()' - Get a device from a backend.
+ */
+
+static int                             /* O - 0 on success, -1 on error */
+get_device(cupsd_backend_t *backend)   /* I - Backend to read from */
+{
+  char line[2048],                     /* Line from backend */
+       dclass[64],                     /* Device class */
+       uri[1024],                      /* Device URI */
+       info[128],                      /* Device info */
+       make_model[256],                /* Make and model */
+       device_id[1024];                /* 1284 device ID */
+
+
+  if (cupsFileGets(backend->pipe, line, sizeof(line)))
+  {
+   /*
+    * Each line is of the form:
+    *
+    *   class URI "make model" "name" ["1284 device ID"]
+    */
+
+    device_id[0] = '\0';
+
+    if (sscanf(line,
+              "%63s%1023s%*[ \t]\"%255[^\"]\"%*[ \t]\"%127[^\"]\""
+              "%*[ \t]\"%1023[^\"]",
+              dclass, uri, make_model, info, device_id) < 4)
+    {
+     /*
+      * Bad format; strip trailing newline and write an error message.
+      */
+
+      if (line[strlen(line) - 1] == '\n')
+       line[strlen(line) - 1] = '\0';
+
+      fprintf(stderr, "ERROR: [cups-deviced] Bad line from \"%s\": %s\n",
+             backend->name, line);
+    }
+    else
+    {
+     /*
+      * Add the device to the array of available devices...
+      */
+
+      if (!add_device(dclass, make_model, info, uri, device_id))
+        fprintf(stderr, "DEBUG: [cups-deviced] Found device \"%s\"...\n", uri);
+    }
+
+    return (0);
+  }
+
+ /*
+  * End of file...
+  */
+
+  cupsFileClose(backend->pipe);
+  backend->pipe = NULL;
+
+  return (-1);
+}
+
+
+/*
+ * 'process_children()' - Process all dead children...
+ */
+
+static void
+process_children(void)
+{
+  int                  i;              /* Looping var */
+  int                  status;         /* Exit status of child */
+  int                  pid;            /* Process ID of child */
+  cupsd_backend_t      *backend;       /* Current backend */
+  const char           *name;          /* Name of process */
+
+
+ /*
+  * Reset the dead_children flag...
+  */
+
+  dead_children = 0;
+
+ /*
+  * Collect the exit status of some children...
+  */
+
+#ifdef HAVE_WAITPID
+  while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+#elif defined(HAVE_WAIT3)
+  while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
+#else
+  if ((pid = wait(&status)) > 0)
+#endif /* HAVE_WAITPID */
+  {
+    if (status == SIGTERM)
+      status = 0;
+
+    for (i = num_backends, backend = backends; i > 0; i --, backend ++)
+      if (backend->pid == pid)
+        break;
+
+    if (i > 0)
+    {
+      name            = backend->name;
+      backend->pid    = 0;
+      backend->status = status;
+
+      active_backends --;
+    }
+    else
+      name = "Unknown";
+
+    if (status)
+    {
+      if (WIFEXITED(status))
+       fprintf(stderr,
+               "ERROR: [cups-deviced] PID %d (%s) stopped with status %d!\n",
+               pid, name, WEXITSTATUS(status));
+      else
+       fprintf(stderr,
+               "ERROR: [cups-deviced] PID %d (%s) crashed on signal %d!\n",
+               pid, name, WTERMSIG(status));
+    }
+    else
+      fprintf(stderr,
+              "DEBUG: [cups-deviced] PID %d (%s) exited with no errors.\n",
+             pid, name);
+  }
+}
+
+
+/*
+ * 'sigchld_handler()' - Handle 'child' signals from old processes.
+ */
+
+static void
+sigchld_handler(int sig)               /* I - Signal number */
+{
+  (void)sig;
+
+ /*
+  * Flag that we have dead children...
+  */
+
+  dead_children = 1;
+
+ /*
+  * Reset the signal handler as needed...
+  */
+
+#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
+  signal(SIGCLD, sigchld_handler);
+#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
+}
+
+
+/*
+ * 'start_backend()' - Run a backend to gather the available devices.
+ */
+
+static int                             /* O - 0 on success, -1 on error */
+start_backend(const char *name,                /* I - Backend to run */
+              int        root)         /* I - Run as root? */
+{
+  const char           *server_bin;    /* CUPS_SERVERBIN environment variable */
+  char                 program[1024];  /* Full path to backend */
+  int                  fds[2];         /* Pipe file descriptors */
+  cupsd_backend_t      *backend;       /* Current backend */
+
+
+  if (num_backends >= MAX_BACKENDS)
+  {
+    fprintf(stderr, "ERROR: Too many backends (%d)!\n", num_backends);
+    return (-1);
+  }
 
   if (pipe(fds))
   {
     fprintf(stderr, "ERROR: Unable to create a pipe for \"%s\" - %s\n",
-            backend, strerror(errno));
-    return (NULL);
+            name, strerror(errno));
+    return (-1);
   }
 
-  if ((*pid = fork()) < 0)
+  if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
+    server_bin = CUPS_SERVERBIN;
+
+  snprintf(program, sizeof(program), "%s/backend/%s", server_bin, name);
+
+  backend = backends + num_backends;
+
+  if ((backend->pid = fork()) < 0)
   {
    /*
     * Error!
     */
 
-    fprintf(stderr, "ERROR: Unable to fork for \"%s\" - %s\n", backend,
-            strerror(errno));
+    fprintf(stderr, "ERROR: [cups-deviced] Unable to fork for \"%s\" - %s\n",
+            program, strerror(errno));
     close(fds[0]);
     close(fds[1]);
-    return (NULL);
+    return (-1);
   }
-  else if (!*pid)
+  else if (!backend->pid)
   {
    /*
     * Child comes here...
     */
 
-    if (!getuid() && uid)
-      setuid(uid);                     /* Run as restricted user */
+    if (!getuid() && !root)
+      setuid(normal_user);             /* Run as restricted user */
 
     close(0);                          /* </dev/null */
     open("/dev/null", O_RDONLY);
@@ -530,33 +723,34 @@ run_backend(const char *backend,  /* I - Backend to run */
     close(fds[0]);                     /* Close copies of pipes */
     close(fds[1]);
 
-    execl(backend, backend, (char *)0);        /* Run it! */
-    fprintf(stderr, "ERROR: Unable to execute \"%s\" - %s\n", backend,
-            strerror(errno));
+    execl(program, name, (char *)0);   /* Run it! */
+    fprintf(stderr, "ERROR: [cups-deviced] Unable to execute \"%s\" - %s\n",
+            program, strerror(errno));
     exit(1);
   }
 
  /*
-  * Parent comes here, make a FILE * from the input side of the pipe...
+  * Parent comes here, allocate a backend and open the input side of the
+  * pipe...
   */
 
-  close(fds[1]);
+  fprintf(stderr, "DEBUG: [cups-deviced] Started backend %s (PID %d)\n",
+          program, backend->pid);
 
-  return (fdopen(fds[0], "r"));
-}
+  close(fds[1]);
 
+  backend_fds[num_backends].fd     = fds[0];
+  backend_fds[num_backends].events = POLLIN;
 
-/*
- * 'sigalrm_handler()' - Handle alarm signals for backends that get hung
- *                       trying to list the available devices...
- */
+  backend->name   = strdup(name);
+  backend->status = 0;
+  backend->pipe   = cupsFileOpenFd(fds[0], "r");
+  backend->count  = 0;
 
-static void
-sigalrm_handler(int sig)               /* I - Signal number */
-{
-  (void)sig; /* remove compiler warnings... */
+  active_backends ++;
+  num_backends ++;
 
-  alarm_tripped = 1;
+  return (0);
 }