Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / cups-deviced.c
1 /*
2  * "$Id: cups-deviced.c 4881 2005-12-15 22:03:40Z mike $"
3  *
4  *   Device scanning mini-daemon for the Common UNIX Printing System (CUPS).
5  *
6  *   Copyright 1997-2005 by Easy Software Products.
7  *
8  *   These coded instructions, statements, and computer programs are the
9  *   property of Easy Software Products and are protected by Federal
10  *   copyright law.  Distribution and use rights are outlined in the file
11  *   "LICENSE.txt" which should have been included with this file.  If this
12  *   file is missing or damaged please contact Easy Software Products
13  *   at:
14  *
15  *       Attn: CUPS Licensing Information
16  *       Easy Software Products
17  *       44141 Airport View Drive, Suite 204
18  *       Hollywood, Maryland 20636 USA
19  *
20  *       Voice: (301) 373-9600
21  *       EMail: cups-info@cups.org
22  *         WWW: http://www.cups.org
23  *
24  * Contents:
25  *
26  *   main()            - Scan for devices and return an IPP response.
27  *   add_dev()         - Add a new device to the list.
28  *   compare_devs()    - Compare device names for sorting.
29  *   sigalrm_handler() - Handle alarm signals for backends that get hung
30  */
31
32 /*
33  * Include necessary headers...
34  */
35
36 #include "util.h"
37 #include <cups/array.h>
38 #include <cups/dir.h>
39
40
41 /*
42  * Device information structure...
43  */
44
45 typedef struct
46 {
47   char  device_class[128],              /* Device class */
48         device_make_and_model[128],     /* Make and model, if known */
49         device_info[128],               /* Device info/description */
50         device_uri[1024],               /* Device URI */
51         device_id[1024];                /* 1284 Device ID */
52 } dev_info_t;
53
54
55 /*
56  * Local globals...
57  */
58
59 static int              alarm_tripped;  /* Non-zero if alarm was tripped */
60 static cups_array_t     *devs;          /* Device info */
61
62
63 /*
64  * Local functions...
65  */
66
67 static dev_info_t       *add_dev(const char *device_class,
68                                  const char *device_make_and_model,
69                                  const char *device_info,
70                                  const char *device_uri,
71                                  const char *device_id);
72 static int              compare_devs(dev_info_t *p0, dev_info_t *p1);
73 static void             sigalrm_handler(int sig);
74
75
76 /*
77  * 'main()' - Scan for devices and return an IPP response.
78  *
79  * Usage:
80  *
81  *    cups-deviced request_id limit options
82  */
83
84 int                                     /* O - Exit code */
85 main(int  argc,                         /* I - Number of command-line args */
86      char *argv[])                      /* I - Command-line arguments */
87 {
88   const char    *server_bin;            /* CUPS_SERVERBIN environment variable */
89   char          backends[1024];         /* Location of backends */
90   int           count;                  /* Number of devices from backend */
91   int           compat;                 /* Compatibility device? */
92   FILE          *fp;                    /* Pipe to device backend */
93   cups_dir_t    *dir;                   /* Directory pointer */
94   cups_dentry_t *dent;                  /* Directory entry */
95   char          filename[1024],         /* Name of backend */
96                 line[2048],             /* Line from backend */
97                 dclass[64],             /* Device class */
98                 uri[1024],              /* Device URI */
99                 info[128],              /* Device info */
100                 make_model[256],        /* Make and model */
101                 device_id[1024];        /* 1284 device ID */
102   int           num_options;            /* Number of options */
103   cups_option_t *options;               /* Options */
104   const char    *requested;             /* requested-attributes option */
105   int           send_class,             /* Send device-class attribute? */
106                 send_info,              /* Send device-info attribute? */
107                 send_make_and_model,    /* Send device-make-and-model attribute? */
108                 send_uri,               /* Send device-uri attribute? */
109                 send_id;                /* Send device-id attribute? */
110   dev_info_t    *dev;                   /* Current device */
111 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
112   struct sigaction action;              /* Actions for POSIX signals */
113 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
114
115
116  /*
117   * Check the command-line...
118   */
119
120   if (argc != 4)
121   {
122     fputs("Usage: cups-deviced request_id limit options\n", stderr);
123     return (1);
124   }
125
126   num_options = cupsParseOptions(argv[3], 0, &options);
127   requested   = cupsGetOption("requested-attributes", num_options, options);
128
129   if (!requested || strstr(requested, "all"))
130   {
131     send_class          = 1;
132     send_info           = 1;
133     send_make_and_model = 1;
134     send_uri            = 1;
135     send_id             = 1;
136   }
137   else
138   {
139     send_class          = strstr(requested, "device-class") != NULL;
140     send_info           = strstr(requested, "device-info") != NULL;
141     send_make_and_model = strstr(requested, "device-make-and-model") != NULL;
142     send_uri            = strstr(requested, "device-uri") != NULL;
143     send_id             = strstr(requested, "device-id") != NULL;
144   }
145
146  /*
147   * Try opening the backend directory...
148   */
149
150   if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
151     server_bin = CUPS_SERVERBIN;
152
153   snprintf(backends, sizeof(backends), "%s/backend", server_bin);
154
155   if ((dir = cupsDirOpen(backends)) == NULL)
156   {
157     fprintf(stderr, "ERROR: [cups-deviced] Unable to open backend directory \"%s\": %s",
158             backends, strerror(errno));
159     return (1);
160   }
161
162  /*
163   * Setup the devices array...
164   */
165
166   devs = cupsArrayNew((cups_array_func_t)compare_devs, NULL);
167
168  /*
169   * Loop through all of the device backends...
170   */
171
172   while ((dent = cupsDirRead(dir)) != NULL)
173   {
174    /*
175     * Run the backend with no arguments and collect the output...
176     */
177
178     snprintf(filename, sizeof(filename), "%s/%s", backends, dent->filename);
179     if ((fp = popen(filename, "r")) != NULL)
180     {
181      /*
182       * Set an alarm for the first read from the backend; this avoids
183       * problems when a backend is hung getting device information.
184       */
185
186 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
187       sigset(SIGALRM, sigalrm_handler);
188 #elif defined(HAVE_SIGACTION)
189       memset(&action, 0, sizeof(action));
190
191       sigemptyset(&action.sa_mask);
192       sigaddset(&action.sa_mask, SIGALRM);
193       action.sa_handler = sigalrm_handler;
194       sigaction(SIGALRM, &action, NULL);
195 #else
196       signal(SIGALRM, sigalrm_handler);
197 #endif /* HAVE_SIGSET */
198
199       alarm_tripped = 0;
200       count         = 0;
201       compat        = !strcmp(dent->filename, "smb");
202
203       alarm(30);
204
205       while (fgets(line, sizeof(line), fp) != NULL)
206       {
207        /*
208         * Reset the alarm clock...
209         */
210
211         alarm(30);
212
213        /*
214         * Each line is of the form:
215         *
216         *   class URI "make model" "name" ["1284 device ID"]
217         */
218
219         device_id[0] = '\0';
220
221         if (!strncasecmp(line, "Usage", 5))
222           compat = 1;
223         else if (sscanf(line,
224                         "%63s%1023s%*[ \t]\"%255[^\"]\"%*[ \t]\"%127[^\"]"
225                         "%*[ \t]\"%1023[^\"]",
226                         dclass, uri, make_model, info, device_id) < 4)
227         {
228          /*
229           * Bad format; strip trailing newline and write an error message.
230           */
231
232           if (line[strlen(line) - 1] == '\n')
233             line[strlen(line) - 1] = '\0';
234
235           fprintf(stderr, "ERROR: [cups-deviced] Bad line from \"%s\": %s\n",
236                   dent->filename, line);
237           compat = 1;
238           break;
239         }
240         else
241         {
242          /*
243           * Add the device to the array of available devices...
244           */
245
246           dev = add_dev(dclass, make_model, info, uri, device_id);
247           if (!dev)
248           {
249             cupsDirClose(dir);
250             return (1);
251           }
252
253           fprintf(stderr, "DEBUG: [cups-deviced] Added device \"%s\"...\n", uri);
254           count ++;
255         }
256       }
257
258      /*
259       * Turn the alarm clock off and close the pipe to the command...
260       */
261
262       alarm(0);
263
264       if (alarm_tripped)
265         fprintf(stderr, "WARNING: [cups-deviced] Backend \"%s\" did not respond within 30 seconds!\n",
266                 dent->filename);
267
268       pclose(fp);
269
270      /*
271       * Hack for backends that don't support the CUPS 1.1 calling convention:
272       * add a network device with the method == backend name.
273       */
274
275       if (count == 0 && compat)
276       {
277         snprintf(line, sizeof(line), "Unknown Network Device (%s)",
278                  dent->filename);
279
280         dev = add_dev("network", line, "Unknown", dent->filename, "");
281         if (!dev)
282         {
283           cupsDirClose(dir);
284           return (1);
285         }
286
287         fprintf(stderr, "DEBUG: [cups-deviced] Compatibility device \"%s\"...\n",
288                 dent->filename);
289       }
290     }
291     else
292       fprintf(stderr, "WARNING: [cups-deviced] Unable to execute \"%s\" backend: %s\n",
293               dent->filename, strerror(errno));
294   }
295
296   cupsDirClose(dir);
297
298  /*
299   * Output the list of devices...
300   */
301
302   puts("Content-Type: application/ipp\n");
303
304   cupsdSendIPPHeader(IPP_OK, atoi(argv[1]));
305   cupsdSendIPPGroup(IPP_TAG_OPERATION);
306   cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
307   cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
308
309   if ((count = atoi(argv[2])) <= 0)
310     count = cupsArrayCount(devs);
311
312   if (count > cupsArrayCount(devs))
313     count = cupsArrayCount(devs);
314
315   for (dev = (dev_info_t *)cupsArrayFirst(devs);
316        count > 0;
317        count --, dev = (dev_info_t *)cupsArrayNext(devs))
318   {
319    /*
320     * Add strings to attributes...
321     */
322
323     cupsdSendIPPGroup(IPP_TAG_PRINTER);
324     if (send_class)
325       cupsdSendIPPString(IPP_TAG_KEYWORD, "device-class", dev->device_class);
326     if (send_info)
327       cupsdSendIPPString(IPP_TAG_TEXT, "device-info", dev->device_info);
328     if (send_make_and_model)
329       cupsdSendIPPString(IPP_TAG_TEXT, "device-make-and-model",
330                          dev->device_make_and_model);
331     if (send_uri)
332       cupsdSendIPPString(IPP_TAG_URI, "device-uri", dev->device_uri);
333     if (send_id)
334       cupsdSendIPPString(IPP_TAG_TEXT, "device-id", dev->device_id);
335   }
336
337   cupsdSendIPPTrailer();
338
339  /*
340   * Free the devices array and return...
341   */
342
343   for (dev = (dev_info_t *)cupsArrayFirst(devs);
344        dev;
345        dev = (dev_info_t *)cupsArrayNext(devs))
346     free(dev);
347
348   cupsArrayDelete(devs);
349
350   return (0);
351 }
352
353
354 /*
355  * 'add_dev()' - Add a new device to the list.
356  */
357
358 static dev_info_t *                     /* O - New device or NULL on error */
359 add_dev(
360     const char *device_class,           /* I - Device class */
361     const char *device_make_and_model,  /* I - Device make and model */
362     const char *device_info,            /* I - Device information */
363     const char *device_uri,             /* I - Device URI */
364     const char *device_id)              /* I - 1284 device ID */
365 {
366   dev_info_t    *dev;                   /* New device */
367
368
369  /*
370   * Allocate memory for the device record...
371   */
372
373   if ((dev = calloc(1, sizeof(dev_info_t))) == NULL)
374   {
375     fputs("ERROR: [cups-deviced] Ran out of memory allocating a device!\n",
376           stderr);
377     return (NULL);
378   }
379
380  /*
381   * Copy the strings over...
382   */
383
384   strlcpy(dev->device_class, device_class, sizeof(dev->device_class));
385   strlcpy(dev->device_make_and_model, device_make_and_model,
386           sizeof(dev->device_make_and_model));
387   strlcpy(dev->device_info, device_info, sizeof(dev->device_info));
388   strlcpy(dev->device_uri, device_uri, sizeof(dev->device_uri));
389   strlcpy(dev->device_id, device_id, sizeof(dev->device_id));
390
391  /*
392   * Add the device to the array and return...
393   */
394
395   cupsArrayAdd(devs, dev);
396
397   return (dev);
398 }
399
400
401 /*
402  * 'compare_devs()' - Compare device names for sorting.
403  */
404
405 static int                              /* O - Result of comparison */
406 compare_devs(dev_info_t *d0,            /* I - First device */
407              dev_info_t *d1)            /* I - Second device */
408 {
409   int           diff;                   /* Difference between strings */
410
411
412  /*
413   * Sort devices by device-info, device-class, and device-uri...
414   */
415
416   if ((diff = cupsdCompareNames(d0->device_info, d1->device_info)) != 0)
417     return (diff);
418   else if ((diff = strcasecmp(d0->device_class, d1->device_class)) != 0)
419     return (diff);
420   else
421     return (strcasecmp(d0->device_uri, d1->device_uri));
422 }
423
424
425 /*
426  * 'sigalrm_handler()' - Handle alarm signals for backends that get hung
427  *                       trying to list the available devices...
428  */
429
430 static void
431 sigalrm_handler(int sig)                /* I - Signal number */
432 {
433   (void)sig; /* remove compiler warnings... */
434
435   alarm_tripped = 1;
436 }
437
438
439 /*
440  * End of "$Id: cups-deviced.c 4881 2005-12-15 22:03:40Z mike $".
441  */