]>
Commit | Line | Data |
---|---|---|
6217d6ec | 1 | /* |
17438bf4 | 2 | * "$Id: devices.c,v 1.17 2002/05/16 13:45:00 mike Exp $" |
6217d6ec | 3 | * |
4 | * Device scanning routines for the Common UNIX Printing System (CUPS). | |
5 | * | |
efb2f309 | 6 | * Copyright 1997-2002 by Easy Software Products. |
6217d6ec | 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 | ||
6217d6ec | 35 | |
a2c6b8b1 | 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); | |
e8ab2e97 | 63 | static void sigalrm_handler(int sig); |
a2c6b8b1 | 64 | |
65 | ||
6217d6ec | 66 | /* |
67 | * 'LoadDevices()' - Load all available devices. | |
68 | */ | |
69 | ||
70 | void | |
71 | LoadDevices(const char *d) /* I - Directory to scan */ | |
72 | { | |
a2c6b8b1 | 73 | int i; /* Looping var */ |
e8ab2e97 | 74 | int count; /* Number of devices from backend */ |
679b5f2d | 75 | int compat; /* Compatibility device? */ |
6217d6ec | 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 */ | |
a2c6b8b1 | 85 | dev_info_t *dev; /* Current device */ |
e8ab2e97 | 86 | #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) |
87 | struct sigaction action; /* Actions for POSIX signals */ | |
88 | #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ | |
a2c6b8b1 | 89 | |
6217d6ec | 90 | |
a2c6b8b1 | 91 | /* |
31d9b588 | 92 | * Initialize the device list. |
a2c6b8b1 | 93 | */ |
6217d6ec | 94 | |
95 | Devices = ippNew(); | |
96 | ||
a2c6b8b1 | 97 | /* |
98 | * Try opening the backend directory... | |
99 | */ | |
100 | ||
6217d6ec | 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 | ||
a2c6b8b1 | 108 | /* |
109 | * Setup the devices array... | |
110 | */ | |
111 | ||
112 | alloc_devs = 0; | |
113 | num_devs = 0; | |
114 | devs = (dev_info_t *)0; | |
115 | ||
d7845573 | 116 | /* |
117 | * Ignore child signals... | |
118 | */ | |
119 | ||
120 | IgnoreChildSignals(); | |
121 | ||
a2c6b8b1 | 122 | /* |
123 | * Loop through all of the device backends... | |
124 | */ | |
125 | ||
6217d6ec | 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 | ||
04de52f8 | 139 | snprintf(filename, sizeof(filename), "%s/%s", d, dent->d_name); |
6217d6ec | 140 | if ((fp = popen(filename, "r")) != NULL) |
141 | { | |
e8ab2e97 | 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); | |
679b5f2d | 161 | count = 0; |
f937463b | 162 | compat = strcmp(dent->d_name, "smb") == 0; |
e8ab2e97 | 163 | |
6217d6ec | 164 | while (fgets(line, sizeof(line), fp) != NULL) |
165 | { | |
e8ab2e97 | 166 | /* |
167 | * Reset the alarm clock... | |
168 | */ | |
169 | ||
170 | alarm(30); | |
171 | ||
6217d6ec | 172 | /* |
173 | * Each line is of the form: | |
174 | * | |
175 | * class URI "make model" "name" | |
176 | */ | |
177 | ||
f937463b | 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) | |
6217d6ec | 182 | { |
183 | /* | |
184 | * Bad format; strip trailing newline and write an error message. | |
185 | */ | |
186 | ||
58781c85 | 187 | if (line[strlen(line) - 1] == '\n') |
188 | line[strlen(line) - 1] = '\0'; | |
189 | ||
6217d6ec | 190 | LogMessage(L_ERROR, "LoadDevices: Bad line from \"%s\": %s", |
191 | dent->d_name, line); | |
679b5f2d | 192 | compat = 1; |
58781c85 | 193 | break; |
6217d6ec | 194 | } |
195 | else | |
196 | { | |
197 | /* | |
a2c6b8b1 | 198 | * Add the device to the array of available devices... |
6217d6ec | 199 | */ |
200 | ||
a2c6b8b1 | 201 | if (num_devs >= alloc_devs) |
202 | { | |
203 | /* | |
e8ab2e97 | 204 | * Allocate (more) memory for the devices... |
a2c6b8b1 | 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 | { | |
e8ab2e97 | 214 | LogMessage(L_ERROR, "LoadDevices: Ran out of memory for %d devices!", |
a2c6b8b1 | 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)); | |
17438bf4 | 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)); | |
a2c6b8b1 | 233 | |
234 | LogMessage(L_DEBUG, "LoadDevices: Added device \"%s\"...", uri); | |
e8ab2e97 | 235 | count ++; |
6217d6ec | 236 | } |
237 | } | |
238 | ||
5b81f4f5 | 239 | /* |
240 | * Turn the alarm clock off and close the pipe to the command... | |
241 | */ | |
242 | ||
243 | alarm(0); | |
244 | ||
6217d6ec | 245 | pclose(fp); |
e8ab2e97 | 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 | ||
679b5f2d | 252 | if (count == 0 && compat) |
e8ab2e97 | 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"); | |
04de52f8 | 282 | snprintf(dev->device_info, sizeof(dev->device_info), |
e8ab2e97 | 283 | "Unknown Network Device (%s)", dent->d_name); |
284 | strcpy(dev->device_make_and_model, "Unknown"); | |
17438bf4 | 285 | strlcpy(dev->device_uri, dent->d_name, sizeof(dev->device_uri)); |
e8ab2e97 | 286 | |
287 | LogMessage(L_DEBUG, "LoadDevices: Compatibility device \"%s\"...", | |
288 | dent->d_name); | |
289 | } | |
6217d6ec | 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); | |
a2c6b8b1 | 297 | |
d7845573 | 298 | /* |
299 | * Catch child signals... | |
300 | */ | |
301 | ||
302 | CatchChildSignals(); | |
303 | ||
a2c6b8b1 | 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 | ||
31d9b588 | 322 | if (i < num_devs) |
323 | ippAddSeparator(Devices); | |
324 | ||
a2c6b8b1 | 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)); | |
6217d6ec | 463 | } |
464 | ||
465 | ||
466 | /* | |
e8ab2e97 | 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 | { | |
d74efa25 | 474 | (void)sig; /* remove compiler warnings... */ |
475 | ||
e8ab2e97 | 476 | LogMessage(L_WARN, "LoadDevices: Backend did not respond within 30 seconds!"); |
477 | } | |
478 | ||
479 | ||
480 | /* | |
17438bf4 | 481 | * End of "$Id: devices.c,v 1.17 2002/05/16 13:45:00 mike Exp $". |
6217d6ec | 482 | */ |