]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/devices.c
f6ac4556179e951d24f84ab39d67be8d1d8e5b77
[thirdparty/cups.git] / scheduler / devices.c
1 /*
2 * "$Id: devices.c,v 1.14 2001/03/23 13:58:17 mike Exp $"
3 *
4 * Device scanning routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2001 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 line[strlen(line) - 1] = '\0';
188 LogMessage(L_ERROR, "LoadDevices: Bad line from \"%s\": %s",
189 dent->d_name, line);
190 compat = 1;
191 }
192 else
193 {
194 /*
195 * Add the device to the array of available devices...
196 */
197
198 if (num_devs >= alloc_devs)
199 {
200 /*
201 * Allocate (more) memory for the devices...
202 */
203
204 if (alloc_devs == 0)
205 dev = malloc(sizeof(dev_info_t) * 16);
206 else
207 dev = realloc(devs, sizeof(dev_info_t) * (alloc_devs + 16));
208
209 if (dev == NULL)
210 {
211 LogMessage(L_ERROR, "LoadDevices: Ran out of memory for %d devices!",
212 alloc_devs + 16);
213 closedir(dir);
214 return;
215 }
216
217 devs = dev;
218 alloc_devs += 16;
219 }
220
221 dev = devs + num_devs;
222 num_devs ++;
223
224 memset(dev, 0, sizeof(dev_info_t));
225 strncpy(dev->device_class, dclass, sizeof(dev->device_class) - 1);
226 strncpy(dev->device_info, info, sizeof(dev->device_info) - 1);
227 strncpy(dev->device_make_and_model, make_model,
228 sizeof(dev->device_make_and_model) - 1);
229 strncpy(dev->device_uri, uri, sizeof(dev->device_uri) - 1);
230
231 LogMessage(L_DEBUG, "LoadDevices: Added device \"%s\"...", uri);
232 count ++;
233 }
234 }
235
236 /*
237 * Turn the alarm clock off and close the pipe to the command...
238 */
239
240 alarm(0);
241
242 pclose(fp);
243
244 /*
245 * Hack for backends that don't support the CUPS 1.1 calling convention:
246 * add a network device with the method == backend name.
247 */
248
249 if (count == 0 && compat)
250 {
251 if (num_devs >= alloc_devs)
252 {
253 /*
254 * Allocate (more) memory for the devices...
255 */
256
257 if (alloc_devs == 0)
258 dev = malloc(sizeof(dev_info_t) * 16);
259 else
260 dev = realloc(devs, sizeof(dev_info_t) * (alloc_devs + 16));
261
262 if (dev == NULL)
263 {
264 LogMessage(L_ERROR, "LoadDevices: Ran out of memory for %d devices!",
265 alloc_devs + 16);
266 closedir(dir);
267 return;
268 }
269
270 devs = dev;
271 alloc_devs += 16;
272 }
273
274 dev = devs + num_devs;
275 num_devs ++;
276
277 memset(dev, 0, sizeof(dev_info_t));
278 strcpy(dev->device_class, "network");
279 snprintf(dev->device_info, sizeof(dev->device_info),
280 "Unknown Network Device (%s)", dent->d_name);
281 strcpy(dev->device_make_and_model, "Unknown");
282 strncpy(dev->device_uri, dent->d_name, sizeof(dev->device_uri) - 1);
283
284 LogMessage(L_DEBUG, "LoadDevices: Compatibility device \"%s\"...",
285 dent->d_name);
286 }
287 }
288 else
289 LogMessage(L_WARN, "LoadDevices: Unable to execute \"%s\" backend: %s",
290 dent->d_name, strerror(errno));
291 }
292
293 closedir(dir);
294
295 /*
296 * Catch child signals...
297 */
298
299 CatchChildSignals();
300
301 /*
302 * Sort the available devices...
303 */
304
305 if (num_devs > 1)
306 qsort(devs, num_devs, sizeof(dev_info_t),
307 (int (*)(const void *, const void *))compare_devs);
308
309 /*
310 * Create the list of devices...
311 */
312
313 for (i = num_devs, dev = devs; i > 0; i --, dev ++)
314 {
315 /*
316 * Add strings to attributes...
317 */
318
319 if (i < num_devs)
320 ippAddSeparator(Devices);
321
322 ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
323 "device-class", NULL, dev->device_class);
324 ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
325 "device-info", NULL, dev->device_info);
326 ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_TEXT,
327 "device-make-and-model", NULL, dev->device_make_and_model);
328 ippAddString(Devices, IPP_TAG_PRINTER, IPP_TAG_URI,
329 "device-uri", NULL, dev->device_uri);
330 }
331
332 /*
333 * Free the devices array...
334 */
335
336 if (alloc_devs)
337 free(devs);
338 }
339
340
341 /*
342 * 'compare_devs()' - Compare PPD file make and model names for sorting.
343 */
344
345 static int /* O - Result of comparison */
346 compare_devs(const dev_info_t *d0, /* I - First PPD file */
347 const dev_info_t *d1) /* I - Second PPD file */
348 {
349 const char *s, /* First name */
350 *t; /* Second name */
351 int diff, /* Difference between digits */
352 digits; /* Number of digits */
353
354
355 /*
356 * First compare names...
357 */
358
359 s = d0->device_info;
360 t = d1->device_info;
361
362 /*
363 * Loop through both nicknames, returning only when a difference is
364 * seen. Also, compare whole numbers rather than just characters, too!
365 */
366
367 while (*s && *t)
368 {
369 if (isdigit(*s) && isdigit(*t))
370 {
371 /*
372 * Got a number; start by skipping leading 0's...
373 */
374
375 while (*s == '0')
376 s ++;
377 while (*t == '0')
378 t ++;
379
380 /*
381 * Skip equal digits...
382 */
383
384 while (isdigit(*s) && *s == *t)
385 {
386 s ++;
387 t ++;
388 }
389
390 /*
391 * Bounce out if *s and *t aren't both digits...
392 */
393
394 if (isdigit(*s) && !isdigit(*t))
395 return (1);
396 else if (!isdigit(*s) && isdigit(*t))
397 return (-1);
398 else if (!isdigit(*s) || !isdigit(*t))
399 continue;
400
401 if (*s < *t)
402 diff = -1;
403 else
404 diff = 1;
405
406 /*
407 * Figure out how many more digits there are...
408 */
409
410 digits = 0;
411 s ++;
412 t ++;
413
414 while (isdigit(*s))
415 {
416 digits ++;
417 s ++;
418 }
419
420 while (isdigit(*t))
421 {
422 digits --;
423 t ++;
424 }
425
426 /*
427 * Return if the number or value of the digits is different...
428 */
429
430 if (digits < 0)
431 return (-1);
432 else if (digits > 0)
433 return (1);
434 else if (diff)
435 return (diff);
436 }
437 else if (tolower(*s) < tolower(*t))
438 return (-1);
439 else if (tolower(*s) > tolower(*t))
440 return (1);
441 else
442 {
443 s ++;
444 t ++;
445 }
446 }
447
448 /*
449 * Return the results of the final comparison...
450 */
451
452 if (*s)
453 return (1);
454 else if (*t)
455 return (-1);
456 else if ((diff = strcasecmp(d0->device_class, d1->device_class)) != 0)
457 return (diff);
458 else
459 return (strcasecmp(d0->device_uri, d1->device_uri));
460 }
461
462
463 /*
464 * 'sigalrm_handler()' - Handle alarm signals for backends that get hung
465 * trying to list the available devices...
466 */
467
468 static void
469 sigalrm_handler(int sig) /* I - Signal number */
470 {
471 (void)sig; /* remove compiler warnings... */
472
473 LogMessage(L_WARN, "LoadDevices: Backend did not respond within 30 seconds!");
474 }
475
476
477 /*
478 * End of "$Id: devices.c,v 1.14 2001/03/23 13:58:17 mike Exp $".
479 */