]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/devices.c
9c72a2fceb36775be93f0c3636f605fd6f5f00e2
[thirdparty/cups.git] / scheduler / devices.c
1 /*
2 * "$Id: devices.c,v 1.14.2.4 2003/01/07 18:27:19 mike Exp $"
3 *
4 * Device scanning routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2003 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-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * LoadDevices() - Load all available devices.
27 */
28
29 /*
30 * Include necessary headers...
31 */
32
33 #include "cupsd.h"
34
35
36 /*
37 * Device information structure...
38 */
39
40 typedef struct
41 {
42 char device_class[128], /* Device class */
43 device_make_and_model[128], /* Make and model, if known */
44 device_info[128], /* Device info/description */
45 device_uri[1024]; /* Device URI */
46 } dev_info_t;
47
48
49 /*
50 * Local globals...
51 */
52
53 static int num_devs, /* Number of devices */
54 alloc_devs; /* Number of allocated entries */
55 static dev_info_t *devs; /* Device info */
56
57
58 /*
59 * Local functions...
60 */
61
62 static int compare_devs(const dev_info_t *p0, const dev_info_t *p1);
63 static void sigalrm_handler(int sig);
64
65
66 /*
67 * 'LoadDevices()' - Load all available devices.
68 */
69
70 void
71 LoadDevices(const char *d) /* I - Directory to scan */
72 {
73 int i; /* Looping var */
74 int count; /* Number of devices from backend */
75 int compat; /* Compatibility device? */
76 FILE *fp; /* Pipe to device backend */
77 DIR *dir; /* Directory pointer */
78 DIRENT *dent; /* Directory entry */
79 char filename[1024], /* Name of backend */
80 line[2048], /* Line from backend */
81 dclass[64], /* Device class */
82 uri[1024], /* Device URI */
83 info[128], /* Device info */
84 make_model[256];/* Make and model */
85 dev_info_t *dev; /* Current device */
86 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
87 struct sigaction action; /* Actions for POSIX signals */
88 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
89
90
91 /*
92 * Initialize the device list.
93 */
94
95 Devices = ippNew();
96
97 /*
98 * Try opening the backend directory...
99 */
100
101 if ((dir = opendir(d)) == NULL)
102 {
103 LogMessage(L_ERROR, "LoadDevices: Unable to open backend directory \"%s\": %s",
104 d, strerror(errno));
105 return;
106 }
107
108 /*
109 * Setup the devices array...
110 */
111
112 alloc_devs = 0;
113 num_devs = 0;
114 devs = (dev_info_t *)0;
115
116 /*
117 * Ignore child signals...
118 */
119
120 IgnoreChildSignals();
121
122 /*
123 * Loop through all of the device backends...
124 */
125
126 while ((dent = readdir(dir)) != NULL)
127 {
128 /*
129 * Skip "." and ".."...
130 */
131
132 if (dent->d_name[0] == '.')
133 continue;
134
135 /*
136 * Run the backend with no arguments and collect the output...
137 */
138
139 snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name);
140 if ((fp = popen(filename, "r")) != NULL)
141 {
142 /*
143 * Set an alarm for the first read from the backend; this avoids
144 * problems when a backend is hung getting device information.
145 */
146
147 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
148 sigset(SIGALRM, sigalrm_handler);
149 #elif defined(HAVE_SIGACTION)
150 memset(&action, 0, sizeof(action));
151
152 sigemptyset(&action.sa_mask);
153 sigaddset(&action.sa_mask, SIGALRM);
154 action.sa_handler = sigalrm_handler;
155 sigaction(SIGALRM, &action, NULL);
156 #else
157 signal(SIGALRM, sigalrm_handler);
158 #endif /* HAVE_SIGSET */
159
160 alarm(30);
161 count = 0;
162 compat = strcmp(dent->d_name, "smb") == 0;
163
164 while (fgets(line, sizeof(line), fp) != NULL)
165 {
166 /*
167 * Reset the alarm clock...
168 */
169
170 alarm(30);
171
172 /*
173 * Each line is of the form:
174 *
175 * class URI "make model" "name"
176 */
177
178 if (strncasecmp(line, "Usage", 5) == 0)
179 compat = 1;
180 else if (sscanf(line, "%63s%1023s%*[ \t]\"%127[^\"]\"%*[ \t]\"%255[^\"]",
181 dclass, uri, make_model, info) != 4)
182 {
183 /*
184 * Bad format; strip trailing newline and write an error message.
185 */
186
187 if (line[strlen(line) - 1] == '\n')
188 line[strlen(line) - 1] = '\0';
189
190 LogMessage(L_ERROR, "LoadDevices: Bad line from \"%s\": %s",
191 dent->d_name, line);
192 compat = 1;
193 break;
194 }
195 else
196 {
197 /*
198 * Add the device to the array of available devices...
199 */
200
201 if (num_devs >= alloc_devs)
202 {
203 /*
204 * Allocate (more) memory for the devices...
205 */
206
207 if (alloc_devs == 0)
208 dev = malloc(sizeof(dev_info_t) * 16);
209 else
210 dev = realloc(devs, sizeof(dev_info_t) * (alloc_devs + 16));
211
212 if (dev == NULL)
213 {
214 LogMessage(L_ERROR, "LoadDevices: Ran out of memory for %d devices!",
215 alloc_devs + 16);
216 closedir(dir);
217 return;
218 }
219
220 devs = dev;
221 alloc_devs += 16;
222 }
223
224 dev = devs + num_devs;
225 num_devs ++;
226
227 memset(dev, 0, sizeof(dev_info_t));
228 strlcpy(dev->device_class, dclass, sizeof(dev->device_class));
229 strlcpy(dev->device_info, info, sizeof(dev->device_info));
230 strlcpy(dev->device_make_and_model, make_model,
231 sizeof(dev->device_make_and_model));
232 strlcpy(dev->device_uri, uri, sizeof(dev->device_uri));
233
234 LogMessage(L_DEBUG, "LoadDevices: Added device \"%s\"...", uri);
235 count ++;
236 }
237 }
238
239 /*
240 * Turn the alarm clock off and close the pipe to the command...
241 */
242
243 alarm(0);
244
245 pclose(fp);
246
247 /*
248 * Hack for backends that don't support the CUPS 1.1 calling convention:
249 * add a network device with the method == backend name.
250 */
251
252 if (count == 0 && compat)
253 {
254 if (num_devs >= alloc_devs)
255 {
256 /*
257 * Allocate (more) memory for the devices...
258 */
259
260 if (alloc_devs == 0)
261 dev = malloc(sizeof(dev_info_t) * 16);
262 else
263 dev = realloc(devs, sizeof(dev_info_t) * (alloc_devs + 16));
264
265 if (dev == NULL)
266 {
267 LogMessage(L_ERROR, "LoadDevices: Ran out of memory for %d devices!",
268 alloc_devs + 16);
269 closedir(dir);
270 return;
271 }
272
273 devs = dev;
274 alloc_devs += 16;
275 }
276
277 dev = devs + num_devs;
278 num_devs ++;
279
280 memset(dev, 0, sizeof(dev_info_t));
281 strcpy(dev->device_class, "network");
282 snprintf(dev->device_info, sizeof(dev->device_info),
283 "Unknown Network Device (%s)", dent->d_name);
284 strcpy(dev->device_make_and_model, "Unknown");
285 strlcpy(dev->device_uri, dent->d_name, sizeof(dev->device_uri));
286
287 LogMessage(L_DEBUG, "LoadDevices: Compatibility device \"%s\"...",
288 dent->d_name);
289 }
290 }
291 else
292 LogMessage(L_WARN, "LoadDevices: Unable to execute \"%s\" backend: %s",
293 dent->d_name, strerror(errno));
294 }
295
296 closedir(dir);
297
298 /*
299 * Catch child signals...
300 */
301
302 CatchChildSignals();
303
304 /*
305 * Sort the available devices...
306 */
307
308 if (num_devs > 1)
309 qsort(devs, num_devs, sizeof(dev_info_t),
310 (int (*)(const void *, const void *))compare_devs);
311
312 /*
313 * Create the list of devices...
314 */
315
316 for (i = num_devs, dev = devs; i > 0; i --, dev ++)
317 {
318 /*
319 * Add strings to attributes...
320 */
321
322 if (i < num_devs)
323 ippAddSeparator(Devices);
324
325 ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
326 "device-class", NULL, dev->device_class);
327 ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
328 "device-info", NULL, dev->device_info);
329 ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
330 "device-make-and-model", NULL, dev->device_make_and_model);
331 ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_URI,
332 "device-uri", NULL, dev->device_uri);
333 }
334
335 /*
336 * Free the devices array...
337 */
338
339 if (alloc_devs)
340 free(devs);
341 }
342
343
344 /*
345 * 'compare_devs()' - Compare PPD file make and model names for sorting.
346 */
347
348 static int /* O - Result of comparison */
349 compare_devs(const dev_info_t *d0, /* I - First PPD file */
350 const dev_info_t *d1) /* I - Second PPD file */
351 {
352 const char *s, /* First name */
353 *t; /* Second name */
354 int diff, /* Difference between digits */
355 digits; /* Number of digits */
356
357
358 /*
359 * First compare names...
360 */
361
362 s = d0->device_info;
363 t = d1->device_info;
364
365 /*
366 * Loop through both nicknames, returning only when a difference is
367 * seen. Also, compare whole numbers rather than just characters, too!
368 */
369
370 while (*s && *t)
371 {
372 if (isdigit(*s) && isdigit(*t))
373 {
374 /*
375 * Got a number; start by skipping leading 0's...
376 */
377
378 while (*s == '0')
379 s ++;
380 while (*t == '0')
381 t ++;
382
383 /*
384 * Skip equal digits...
385 */
386
387 while (isdigit(*s) && *s == *t)
388 {
389 s ++;
390 t ++;
391 }
392
393 /*
394 * Bounce out if *s and *t aren't both digits...
395 */
396
397 if (isdigit(*s) && !isdigit(*t))
398 return (1);
399 else if (!isdigit(*s) && isdigit(*t))
400 return (-1);
401 else if (!isdigit(*s) || !isdigit(*t))
402 continue;
403
404 if (*s < *t)
405 diff = -1;
406 else
407 diff = 1;
408
409 /*
410 * Figure out how many more digits there are...
411 */
412
413 digits = 0;
414 s ++;
415 t ++;
416
417 while (isdigit(*s))
418 {
419 digits ++;
420 s ++;
421 }
422
423 while (isdigit(*t))
424 {
425 digits --;
426 t ++;
427 }
428
429 /*
430 * Return if the number or value of the digits is different...
431 */
432
433 if (digits < 0)
434 return (-1);
435 else if (digits > 0)
436 return (1);
437 else if (diff)
438 return (diff);
439 }
440 else if (tolower(*s) < tolower(*t))
441 return (-1);
442 else if (tolower(*s) > tolower(*t))
443 return (1);
444 else
445 {
446 s ++;
447 t ++;
448 }
449 }
450
451 /*
452 * Return the results of the final comparison...
453 */
454
455 if (*s)
456 return (1);
457 else if (*t)
458 return (-1);
459 else if ((diff = strcasecmp(d0->device_class, d1->device_class)) != 0)
460 return (diff);
461 else
462 return (strcasecmp(d0->device_uri, d1->device_uri));
463 }
464
465
466 /*
467 * 'sigalrm_handler()' - Handle alarm signals for backends that get hung
468 * trying to list the available devices...
469 */
470
471 static void
472 sigalrm_handler(int sig) /* I - Signal number */
473 {
474 (void)sig; /* remove compiler warnings... */
475
476 LogMessage(L_WARN, "LoadDevices: Backend did not respond within 30 seconds!");
477 }
478
479
480 /*
481 * End of "$Id: devices.c,v 1.14.2.4 2003/01/07 18:27:19 mike Exp $".
482 */