]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/devices.c
Add strlcat() and strlcpy() checks and emulation functions.
[thirdparty/cups.git] / scheduler / devices.c
CommitLineData
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
40typedef 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
53static int num_devs, /* Number of devices */
54 alloc_devs; /* Number of allocated entries */
55static dev_info_t *devs; /* Device info */
56
57
58/*
59 * Local functions...
60 */
61
62static int compare_devs(const dev_info_t *p0, const dev_info_t *p1);
e8ab2e97 63static void sigalrm_handler(int sig);
a2c6b8b1 64
65
6217d6ec 66/*
67 * 'LoadDevices()' - Load all available devices.
68 */
69
70void
71LoadDevices(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
348static int /* O - Result of comparison */
349compare_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
471static void
472sigalrm_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 */