]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-deviced.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / cups-deviced.c
1 /*
2 * "$Id: cups-deviced.c 5044 2006-02-01 21:35:30Z 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 static int normal_user; /* Normal user ID */
62
63
64 /*
65 * Local functions...
66 */
67
68 static dev_info_t *add_dev(const char *device_class,
69 const char *device_make_and_model,
70 const char *device_info,
71 const char *device_uri,
72 const char *device_id);
73 static int compare_devs(dev_info_t *p0, dev_info_t *p1);
74 static void sigalrm_handler(int sig);
75
76
77 /*
78 * 'main()' - Scan for devices and return an IPP response.
79 *
80 * Usage:
81 *
82 * cups-deviced request_id limit options
83 */
84
85 int /* O - Exit code */
86 main(int argc, /* I - Number of command-line args */
87 char *argv[]) /* I - Command-line arguments */
88 {
89 const char *server_bin; /* CUPS_SERVERBIN environment variable */
90 char backends[1024]; /* Location of backends */
91 int request_id; /* Request ID */
92 int count; /* Number of devices from backend */
93 int compat; /* Compatibility device? */
94 FILE *fp; /* Pipe to device backend */
95 cups_dir_t *dir; /* Directory pointer */
96 cups_dentry_t *dent; /* Directory entry */
97 char filename[1024], /* Name of backend */
98 line[2048], /* Line from backend */
99 dclass[64], /* Device class */
100 uri[1024], /* Device URI */
101 info[128], /* Device info */
102 make_model[256], /* Make and model */
103 device_id[1024]; /* 1284 device ID */
104 int num_options; /* Number of options */
105 cups_option_t *options; /* Options */
106 const char *requested; /* requested-attributes option */
107 int send_class, /* Send device-class attribute? */
108 send_info, /* Send device-info attribute? */
109 send_make_and_model, /* Send device-make-and-model attribute? */
110 send_uri, /* Send device-uri attribute? */
111 send_id; /* Send device-id attribute? */
112 dev_info_t *dev; /* Current device */
113 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
114 struct sigaction action; /* Actions for POSIX signals */
115 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
116
117
118 setbuf(stderr, NULL);
119
120 /*
121 * Check the command-line...
122 */
123
124 if (argc > 1)
125 request_id = atoi(argv[1]);
126 else
127 request_id = 1;
128
129 if (argc != 5)
130 {
131 fputs("Usage: cups-deviced request-id limit user-id options\n", stderr);
132
133 cupsdSendIPPHeader(IPP_BAD_REQUEST, request_id);
134 cupsdSendIPPGroup(IPP_TAG_OPERATION);
135 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
136 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
137 "en-US");
138 cupsdSendIPPString(IPP_TAG_TEXT, "status-message",
139 "Bad command-line arguments passed to cups-deviced!");
140 cupsdSendIPPTrailer();
141
142 return (1);
143 }
144
145 if (request_id < 1)
146 {
147 fprintf(stderr, "cups-deviced: Bad request ID %d!\n", request_id);
148
149 cupsdSendIPPHeader(IPP_BAD_REQUEST, request_id);
150 cupsdSendIPPGroup(IPP_TAG_OPERATION);
151 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
152 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
153 "en-US");
154 cupsdSendIPPString(IPP_TAG_TEXT, "status-message",
155 "Bad request ID argument passed to cups-deviced!");
156 cupsdSendIPPTrailer();
157
158 return (1);
159 }
160
161 normal_user = atoi(argv[3]);
162 if (normal_user <= 0)
163 {
164 fprintf(stderr, "cups-deviced: Bad user %d!\n", normal_user);
165
166 cupsdSendIPPHeader(IPP_BAD_REQUEST, request_id);
167 cupsdSendIPPGroup(IPP_TAG_OPERATION);
168 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
169 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
170 "en-US");
171 cupsdSendIPPString(IPP_TAG_TEXT, "status-message",
172 "Bad user ID argument passed to cups-deviced!");
173 cupsdSendIPPTrailer();
174
175 return (1);
176 }
177
178 num_options = cupsParseOptions(argv[4], 0, &options);
179 requested = cupsGetOption("requested-attributes", num_options, options);
180
181 if (!requested || strstr(requested, "all"))
182 {
183 send_class = 1;
184 send_info = 1;
185 send_make_and_model = 1;
186 send_uri = 1;
187 send_id = 1;
188 }
189 else
190 {
191 send_class = strstr(requested, "device-class") != NULL;
192 send_info = strstr(requested, "device-info") != NULL;
193 send_make_and_model = strstr(requested, "device-make-and-model") != NULL;
194 send_uri = strstr(requested, "device-uri") != NULL;
195 send_id = strstr(requested, "device-id") != NULL;
196 }
197
198 /*
199 * Try opening the backend directory...
200 */
201
202 if ((server_bin = getenv("CUPS_SERVERBIN")) == NULL)
203 server_bin = CUPS_SERVERBIN;
204
205 snprintf(backends, sizeof(backends), "%s/backend", server_bin);
206
207 if ((dir = cupsDirOpen(backends)) == NULL)
208 {
209 fprintf(stderr, "ERROR: [cups-deviced] Unable to open backend directory "
210 "\"%s\": %s", backends, strerror(errno));
211
212 snprintf(line, sizeof(line), "Unable to open backend directory \"%s\": %s",
213 backends, strerror(errno));
214 cupsdSendIPPHeader(IPP_BAD_REQUEST, request_id);
215 cupsdSendIPPGroup(IPP_TAG_OPERATION);
216 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
217 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language",
218 "en-US");
219 cupsdSendIPPString(IPP_TAG_TEXT, "status-message", line);
220 cupsdSendIPPTrailer();
221
222 return (1);
223 }
224
225 /*
226 * Setup the devices array...
227 */
228
229 devs = cupsArrayNew((cups_array_func_t)compare_devs, NULL);
230
231 /*
232 * Loop through all of the device backends...
233 */
234
235 while ((dent = cupsDirRead(dir)) != NULL)
236 {
237 /*
238 * Skip entries that are not executable files...
239 */
240
241 if (!S_ISREG(dent->fileinfo.st_mode) ||
242 (dent->fileinfo.st_mode & (S_IRUSR | S_IXUSR)) != (S_IRUSR | S_IXUSR))
243 continue;
244
245 /*
246 * Change effective users depending on the backend permissions...
247 */
248
249 if (!getuid())
250 {
251 /*
252 * Backends without permissions for normal users run as root,
253 * all others run as the unprivileged user...
254 */
255
256 if (!(dent->fileinfo.st_mode & (S_IRWXG | S_IRWXO)))
257 seteuid(0);
258 else
259 seteuid(normal_user);
260 }
261
262 /*
263 * Run the backend with no arguments and collect the output...
264 */
265
266 snprintf(filename, sizeof(filename), "%s/%s", backends, dent->filename);
267 if ((fp = popen(filename, "r")) != NULL)
268 {
269 /*
270 * Set an alarm for the first read from the backend; this avoids
271 * problems when a backend is hung getting device information.
272 */
273
274 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
275 sigset(SIGALRM, sigalrm_handler);
276 #elif defined(HAVE_SIGACTION)
277 memset(&action, 0, sizeof(action));
278
279 sigemptyset(&action.sa_mask);
280 sigaddset(&action.sa_mask, SIGALRM);
281 action.sa_handler = sigalrm_handler;
282 sigaction(SIGALRM, &action, NULL);
283 #else
284 signal(SIGALRM, sigalrm_handler);
285 #endif /* HAVE_SIGSET */
286
287 alarm_tripped = 0;
288 count = 0;
289 compat = !strcmp(dent->filename, "smb");
290
291 alarm(30);
292
293 while (fgets(line, sizeof(line), fp) != NULL)
294 {
295 /*
296 * Reset the alarm clock...
297 */
298
299 alarm(30);
300
301 /*
302 * Each line is of the form:
303 *
304 * class URI "make model" "name" ["1284 device ID"]
305 */
306
307 device_id[0] = '\0';
308
309 if (!strncasecmp(line, "Usage", 5))
310 compat = 1;
311 else if (sscanf(line,
312 "%63s%1023s%*[ \t]\"%255[^\"]\"%*[ \t]\"%127[^\"]"
313 "%*[ \t]\"%1023[^\"]",
314 dclass, uri, make_model, info, device_id) < 4)
315 {
316 /*
317 * Bad format; strip trailing newline and write an error message.
318 */
319
320 if (line[strlen(line) - 1] == '\n')
321 line[strlen(line) - 1] = '\0';
322
323 fprintf(stderr, "ERROR: [cups-deviced] Bad line from \"%s\": %s\n",
324 dent->filename, line);
325 compat = 1;
326 break;
327 }
328 else
329 {
330 /*
331 * Add the device to the array of available devices...
332 */
333
334 dev = add_dev(dclass, make_model, info, uri, device_id);
335 if (!dev)
336 {
337 cupsDirClose(dir);
338 return (1);
339 }
340
341 fprintf(stderr, "DEBUG: [cups-deviced] Added device \"%s\"...\n",
342 uri);
343 count ++;
344 }
345 }
346
347 /*
348 * Turn the alarm clock off and close the pipe to the command...
349 */
350
351 alarm(0);
352
353 if (alarm_tripped)
354 fprintf(stderr, "WARNING: [cups-deviced] Backend \"%s\" did not "
355 "respond within 30 seconds!\n", dent->filename);
356
357 pclose(fp);
358
359 /*
360 * Hack for backends that don't support the CUPS 1.1 calling convention:
361 * add a network device with the method == backend name.
362 */
363
364 if (count == 0 && compat)
365 {
366 snprintf(line, sizeof(line), "Unknown Network Device (%s)",
367 dent->filename);
368
369 dev = add_dev("network", line, "Unknown", dent->filename, "");
370 if (!dev)
371 {
372 cupsDirClose(dir);
373 return (1);
374 }
375
376 fprintf(stderr, "DEBUG: [cups-deviced] Compatibility device "
377 "\"%s\"...\n", dent->filename);
378 }
379 }
380 else
381 fprintf(stderr, "WARNING: [cups-deviced] Unable to execute \"%s\" "
382 "backend: %s\n", dent->filename, strerror(errno));
383 }
384
385 cupsDirClose(dir);
386
387 /*
388 * Switch back to root as needed...
389 */
390
391 if (!getuid() && geteuid())
392 seteuid(0);
393
394 /*
395 * Output the list of devices...
396 */
397
398 puts("Content-Type: application/ipp\n");
399
400 cupsdSendIPPHeader(IPP_OK, request_id);
401 cupsdSendIPPGroup(IPP_TAG_OPERATION);
402 cupsdSendIPPString(IPP_TAG_CHARSET, "attributes-charset", "utf-8");
403 cupsdSendIPPString(IPP_TAG_LANGUAGE, "attributes-natural-language", "en-US");
404
405 if ((count = atoi(argv[2])) <= 0)
406 count = cupsArrayCount(devs);
407
408 if (count > cupsArrayCount(devs))
409 count = cupsArrayCount(devs);
410
411 for (dev = (dev_info_t *)cupsArrayFirst(devs);
412 count > 0;
413 count --, dev = (dev_info_t *)cupsArrayNext(devs))
414 {
415 /*
416 * Add strings to attributes...
417 */
418
419 cupsdSendIPPGroup(IPP_TAG_PRINTER);
420 if (send_class)
421 cupsdSendIPPString(IPP_TAG_KEYWORD, "device-class", dev->device_class);
422 if (send_info)
423 cupsdSendIPPString(IPP_TAG_TEXT, "device-info", dev->device_info);
424 if (send_make_and_model)
425 cupsdSendIPPString(IPP_TAG_TEXT, "device-make-and-model",
426 dev->device_make_and_model);
427 if (send_uri)
428 cupsdSendIPPString(IPP_TAG_URI, "device-uri", dev->device_uri);
429 if (send_id)
430 cupsdSendIPPString(IPP_TAG_TEXT, "device-id", dev->device_id);
431 }
432
433 cupsdSendIPPTrailer();
434
435 /*
436 * Free the devices array and return...
437 */
438
439 for (dev = (dev_info_t *)cupsArrayFirst(devs);
440 dev;
441 dev = (dev_info_t *)cupsArrayNext(devs))
442 free(dev);
443
444 cupsArrayDelete(devs);
445
446 return (0);
447 }
448
449
450 /*
451 * 'add_dev()' - Add a new device to the list.
452 */
453
454 static dev_info_t * /* O - New device or NULL on error */
455 add_dev(
456 const char *device_class, /* I - Device class */
457 const char *device_make_and_model, /* I - Device make and model */
458 const char *device_info, /* I - Device information */
459 const char *device_uri, /* I - Device URI */
460 const char *device_id) /* I - 1284 device ID */
461 {
462 dev_info_t *dev; /* New device */
463
464
465 /*
466 * Allocate memory for the device record...
467 */
468
469 if ((dev = calloc(1, sizeof(dev_info_t))) == NULL)
470 {
471 fputs("ERROR: [cups-deviced] Ran out of memory allocating a device!\n",
472 stderr);
473 return (NULL);
474 }
475
476 /*
477 * Copy the strings over...
478 */
479
480 strlcpy(dev->device_class, device_class, sizeof(dev->device_class));
481 strlcpy(dev->device_make_and_model, device_make_and_model,
482 sizeof(dev->device_make_and_model));
483 strlcpy(dev->device_info, device_info, sizeof(dev->device_info));
484 strlcpy(dev->device_uri, device_uri, sizeof(dev->device_uri));
485 strlcpy(dev->device_id, device_id, sizeof(dev->device_id));
486
487 /*
488 * Add the device to the array and return...
489 */
490
491 cupsArrayAdd(devs, dev);
492
493 return (dev);
494 }
495
496
497 /*
498 * 'compare_devs()' - Compare device names for sorting.
499 */
500
501 static int /* O - Result of comparison */
502 compare_devs(dev_info_t *d0, /* I - First device */
503 dev_info_t *d1) /* I - Second device */
504 {
505 int diff; /* Difference between strings */
506
507
508 /*
509 * Sort devices by device-info, device-class, and device-uri...
510 */
511
512 if ((diff = cupsdCompareNames(d0->device_info, d1->device_info)) != 0)
513 return (diff);
514 else if ((diff = strcasecmp(d0->device_class, d1->device_class)) != 0)
515 return (diff);
516 else
517 return (strcasecmp(d0->device_uri, d1->device_uri));
518 }
519
520
521 /*
522 * 'sigalrm_handler()' - Handle alarm signals for backends that get hung
523 * trying to list the available devices...
524 */
525
526 static void
527 sigalrm_handler(int sig) /* I - Signal number */
528 {
529 (void)sig; /* remove compiler warnings... */
530
531 alarm_tripped = 1;
532 }
533
534
535 /*
536 * End of "$Id: cups-deviced.c 5044 2006-02-01 21:35:30Z mike $".
537 */