]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/cups-deviced.c
Update svn:keyword properties.
[thirdparty/cups.git] / scheduler / cups-deviced.c
index 6617d911c7ba8a1ef9ef2a2530a5f8d7b071e1f9..0cd5b8ea8c4042065ae87a2c5c315a40cffb6871 100644 (file)
@@ -1,32 +1,27 @@
 /*
- * "$Id: cups-deviced.c 4881 2005-12-15 22:03:40Z mike $"
+ * "$Id$"
  *
- *   Device scanning mini-daemon for the Common UNIX Printing System (CUPS).
+ *   Device scanning mini-daemon for CUPS.
  *
- *   Copyright 1997-2005 by Easy Software Products.
+ *   Copyright 2007-2011 by Apple Inc.
+ *   Copyright 1997-2006 by Easy Software Products.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  * 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.
- *   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.
+ *   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 "util.h"
 #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;
 
 
 /*
 typedef struct
 {
   char device_class[128],              /* Device class */
-       device_make_and_model[128],     /* Make and model, if known */
        device_info[128],               /* Device info/description */
-       device_uri[1024],               /* Device URI */
-       device_id[1024];                /* 1284 Device ID */
-} dev_info_t;
+       device_uri[1024];               /* Device URI */
+} 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? */
+                       send_location;  /* Send device-location 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 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,
+                                  const char *device_location);
+static int             compare_devices(cupsd_device_t *p0,
+                                       cupsd_device_t *p1);
+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);
 
 
 /*
@@ -85,64 +126,110 @@ int                                       /* O - Exit code */
 main(int  argc,                                /* I - Number of command-line args */
      char *argv[])                     /* I - Command-line arguments */
 {
+  int          i;                      /* Looping var */
+  int          request_id;             /* Request ID */
+  int          timeout;                /* Timeout in seconds */
   const char   *server_bin;            /* CUPS_SERVERBIN environment variable */
-  char         backends[1024];         /* Location of backends */
-  int          count;                  /* Number of devices from backend */
-  int          compat;                 /* Compatibility device? */
-  FILE         *fp;                    /* Pipe to device backend */
+  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 */
+               *include;               /* include-schemes values */
 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
   struct sigaction action;             /* Actions for POSIX signals */
 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
 
 
+  setbuf(stderr, NULL);
+
  /*
   * Check the command-line...
   */
 
-  if (argc != 4)
+  if (argc != 6)
   {
-    fputs("Usage: cups-deviced request_id limit options\n", stderr);
+    fputs("Usage: cups-deviced request-id limit timeout user-id options\n", stderr);
+
     return (1);
   }
 
-  num_options = cupsParseOptions(argv[3], 0, &options);
-  requested   = cupsGetOption("requested-attributes", num_options, options);
+  request_id = atoi(argv[1]);
+  if (request_id < 1)
+  {
+    fprintf(stderr, "ERROR: [cups-deviced] Bad request ID %d!\n", request_id);
 
-  if (!requested || strstr(requested, "all"))
+    return (1);
+  }
+
+  device_limit = atoi(argv[2]);
+  if (device_limit < 0)
   {
-    send_class          = 1;
-    send_info           = 1;
-    send_make_and_model = 1;
-    send_uri            = 1;
-    send_id             = 1;
+    fprintf(stderr, "ERROR: [cups-deviced] Bad limit %d!\n", device_limit);
+
+    return (1);
+  }
+
+  timeout = atoi(argv[3]);
+  if (timeout < 1)
+  {
+    fprintf(stderr, "ERROR: [cups-deviced] Bad timeout %d!\n", timeout);
+
+    return (1);
+  }
+
+  normal_user = atoi(argv[4]);
+  if (normal_user <= 0)
+  {
+    fprintf(stderr, "ERROR: [cups-deviced] Bad user %d!\n", normal_user);
+
+    return (1);
+  }
+
+  num_options = cupsParseOptions(argv[5], 0, &options);
+  requested   = cupsdCreateStringsArray(cupsGetOption("requested-attributes",
+                                                      num_options, options));
+  exclude     = cupsdCreateStringsArray(cupsGetOption("exclude-schemes",
+                                                      num_options, options));
+  include     = cupsdCreateStringsArray(cupsGetOption("include-schemes",
+                                                      num_options, options));
+
+  if (!requested || cupsArrayFind(requested, "all") != NULL)
+  {
+    send_class = send_info = send_make_and_model = send_uri = send_id =
+        send_location = 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;
+    send_location       = cupsArrayFind(requested, "device-location") != 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...
   */
@@ -150,12 +237,13 @@ 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));
+    fprintf(stderr, "ERROR: [cups-deviced] Unable to open backend directory "
+                    "\"%s\": %s", filename, strerror(errno));
+
     return (1);
   }
 
@@ -163,7 +251,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...
@@ -172,239 +260,193 @@ main(int  argc,                         /* I - Number of command-line args */
   while ((dent = cupsDirRead(dir)) != NULL)
   {
    /*
-    * Run the backend with no arguments and collect the output...
+    * Skip entries that are not executable files...
     */
 
-    snprintf(filename, sizeof(filename), "%s/%s", backends, dent->filename);
-    if ((fp = popen(filename, "r")) != NULL)
-    {
-     /*
-      * 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);
-           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);
+    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;
 
-      pclose(fp);
-
-     /*
-      * Hack for backends that don't support the CUPS 1.1 calling convention:
-      * add a network device with the method == backend name.
-      */
+   /*
+    * Skip excluded or not included backends...
+    */
 
-      if (count == 0 && compat)
-      {
-       snprintf(line, sizeof(line), "Unknown Network Device (%s)",
-                dent->filename);
+    if (cupsArrayFind(exclude, dent->filename) ||
+        (include && !cupsArrayFind(include, dent->filename)))
+      continue;
 
-        dev = add_dev("network", line, "Unknown", dent->filename, "");
-       if (!dev)
-       {
-          cupsDirClose(dir);
-         return (1);
-       }
+   /*
+    * Backends without permissions for normal users run as root,
+    * all others run as the unprivileged user...
+    */
 
-        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, atoi(argv[1]));
+  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);
+  end_time = get_current_time() + timeout;
 
-  if (count > cupsArrayCount(devs))
-    count = cupsArrayCount(devs);
-
-  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...
     */
 
-    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);
+    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)
+       {
+         cups_file_t *bpipe = backends[i].pipe;
+                                       /* Copy of pipe for backend... */
+
+         do
+         {
+           if (get_device(backends + i))
+           {
+             backend_fds[i].fd     = 0;
+             backend_fds[i].events = 0;
+             break;
+           }
+         }
+         while (bpipe->ptr &&
+                memchr(bpipe->ptr, '\n', bpipe->end - bpipe->ptr));
+        }
+    }
+
+   /*
+    * Get exit status from children...
+    */
+
+    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 */
+    const char *device_id,             /* I - 1284 device ID */
+    const char *device_location)       /* I - Physical location */
 {
-  dev_info_t   *dev;                   /* New device */
+  cupsd_device_t       *device;        /* New 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_info, device_info, sizeof(device->device_info));
+  strlcpy(device->device_uri, device_uri, sizeof(device->device_uri));
 
  /*
   * Add the device to the array and return...
   */
 
-  cupsArrayAdd(devs, dev);
+  if (cupsArrayFind(devices, device))
+  {
+   /*
+    * Avoid duplicates!
+    */
 
-  return (dev);
+    free(device);
+  }
+  else
+  {
+    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_class);
+      if (send_info)
+       cupsdSendIPPString(IPP_TAG_TEXT, "device-info", device_info);
+      if (send_make_and_model)
+       cupsdSendIPPString(IPP_TAG_TEXT, "device-make-and-model",
+                          device_make_and_model);
+      if (send_uri)
+       cupsdSendIPPString(IPP_TAG_URI, "device-uri", device_uri);
+      if (send_id)
+       cupsdSendIPPString(IPP_TAG_TEXT, "device-id",
+                          device_id ? device_id : "");
+      if (send_location)
+       cupsdSendIPPString(IPP_TAG_TEXT, "device-location",
+                          device_location ? device_location : "");
+
+      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 */
 
@@ -415,27 +457,354 @@ compare_devs(dev_info_t *d0,             /* I - First device */
 
   if ((diff = cupsdCompareNames(d0->device_info, d1->device_info)) != 0)
     return (diff);
-  else if ((diff = strcasecmp(d0->device_class, d1->device_class)) != 0)
+  else if ((diff = _cups_strcasecmp(d0->device_class, d1->device_class)) != 0)
     return (diff);
   else
-    return (strcasecmp(d0->device_uri, d1->device_uri));
+    return (_cups_strcasecmp(d0->device_uri, d1->device_uri));
+}
+
+
+/*
+ * '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);
 }
 
 
 /*
- * 'sigalrm_handler()' - Handle alarm signals for backends that get hung
- *                       trying to list the available devices...
+ * '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 */
+       temp[2048],                     /* Copy of line */
+       *ptr,                           /* Pointer into line */
+       *dclass,                        /* Device class */
+       *uri,                           /* Device URI */
+       *make_model,                    /* Make and model */
+       *info,                          /* Device info */
+       *device_id,                     /* 1284 device ID */
+       *location;                      /* Physical location */
+
+
+  if (cupsFileGets(backend->pipe, line, sizeof(line)))
+  {
+   /*
+    * Each line is of the form:
+    *
+    *   class URI "make model" "name" ["1284 device ID"] ["location"]
+    */
+
+    strlcpy(temp, line, sizeof(temp));
+
+   /*
+    * device-class
+    */
+
+    dclass = temp;
+
+    for (ptr = temp; *ptr; ptr ++)
+      if (isspace(*ptr & 255))
+        break;
+
+    while (isspace(*ptr & 255))
+      *ptr++ = '\0';
+
+   /*
+    * device-uri
+    */
+
+    if (!*ptr)
+      goto error;
+
+    for (uri = ptr; *ptr; ptr ++)
+      if (isspace(*ptr & 255))
+        break;
+
+    while (isspace(*ptr & 255))
+      *ptr++ = '\0';
+
+   /*
+    * device-make-and-model
+    */
+
+    if (*ptr != '\"')
+      goto error;
+
+    for (ptr ++, make_model = ptr; *ptr && *ptr != '\"'; ptr ++)
+    {
+      if (*ptr == '\\' && ptr[1])
+        _cups_strcpy(ptr, ptr + 1);
+    }
+
+    if (*ptr != '\"')
+      goto error;
+
+    for (*ptr++ = '\0'; isspace(*ptr & 255); *ptr++ = '\0');
+
+   /*
+    * device-info
+    */
+
+    if (*ptr != '\"')
+      goto error;
+
+    for (ptr ++, info = ptr; *ptr && *ptr != '\"'; ptr ++)
+    {
+      if (*ptr == '\\' && ptr[1])
+        _cups_strcpy(ptr, ptr + 1);
+    }
+
+    if (*ptr != '\"')
+      goto error;
+
+    for (*ptr++ = '\0'; isspace(*ptr & 255); *ptr++ = '\0');
+
+   /*
+    * device-id
+    */
+
+    if (*ptr == '\"')
+    {
+      for (ptr ++, device_id = ptr; *ptr && *ptr != '\"'; ptr ++)
+      {
+       if (*ptr == '\\' && ptr[1])
+         _cups_strcpy(ptr, ptr + 1);
+      }
+
+      if (*ptr != '\"')
+       goto error;
+
+      for (*ptr++ = '\0'; isspace(*ptr & 255); *ptr++ = '\0');
+
+     /*
+      * device-location
+      */
+
+      if (*ptr == '\"')
+      {
+       for (ptr ++, location = ptr; *ptr && *ptr != '\"'; ptr ++)
+       {
+         if (*ptr == '\\' && ptr[1])
+           _cups_strcpy(ptr, ptr + 1);
+       }
+
+       if (*ptr != '\"')
+         goto error;
+
+       *ptr = '\0';
+      }
+      else
+        location = NULL;
+    }
+    else
+    {
+      device_id = NULL;
+      location  = NULL;
+    }
+
+   /*
+    * Add the device to the array of available devices...
+    */
+
+    if (!add_device(dclass, make_model, info, uri, device_id, location))
+      fprintf(stderr, "DEBUG: [cups-deviced] Found device \"%s\"...\n", uri);
+
+    return (0);
+  }
+
+ /*
+  * End of file...
+  */
+
+  cupsFileClose(backend->pipe);
+  backend->pipe = NULL;
+
+  return (-1);
+
+ /*
+  * Bad format; strip trailing newline and write an error message.
+  */
+
+  error:
+
+  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);
+  return (0);
+}
+
+
+/*
+ * '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
-sigalrm_handler(int sig)               /* I - Signal number */
+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? */
 {
-  (void)sig; /* remove compiler warnings... */
+  const char           *server_bin;    /* CUPS_SERVERBIN environment variable */
+  char                 program[1024];  /* Full path to backend */
+  cupsd_backend_t      *backend;       /* Current backend */
+  char                 *argv[2];       /* Command-line arguments */
+
+
+  if (num_backends >= MAX_BACKENDS)
+  {
+    fprintf(stderr, "ERROR: Too many backends (%d)!\n", num_backends);
+    return (-1);
+  }
+
+  if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
+    server_bin = CUPS_SERVERBIN;
+
+  snprintf(program, sizeof(program), "%s/backend/%s", server_bin, name);
+
+  if (_cupsFileCheck(program, _CUPS_FILE_CHECK_PROGRAM, !geteuid(),
+                     _cupsFileCheckFilter, NULL))
+    return (-1);
+
+  backend = backends + num_backends;
+
+  argv[0] = (char *)name;
+  argv[1] = NULL;
+
+  if ((backend->pipe = cupsdPipeCommand(&(backend->pid), program, argv,
+                                        root ? 0 : normal_user)) == NULL)
+  {
+    fprintf(stderr, "ERROR: [cups-deviced] Unable to execute \"%s\" - %s\n",
+            program, strerror(errno));
+    return (-1);
+  }
 
-  alarm_tripped = 1;
+ /*
+  * Fill in the rest of the backend information...
+  */
+
+  fprintf(stderr, "DEBUG: [cups-deviced] Started backend %s (PID %d)\n",
+          program, backend->pid);
+
+  backend_fds[num_backends].fd     = cupsFileNumber(backend->pipe);
+  backend_fds[num_backends].events = POLLIN;
+
+  backend->name   = strdup(name);
+  backend->status = 0;
+  backend->count  = 0;
+
+  active_backends ++;
+  num_backends ++;
+
+  return (0);
 }
 
 
 /*
- * End of "$Id: cups-deviced.c 4881 2005-12-15 22:03:40Z mike $".
+ * End of "$Id$".
  */