]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/snmp.c
Merge changes from CUPS 1.5svn-r9136.
[thirdparty/cups.git] / backend / snmp.c
1 /*
2 * "$Id: snmp.c 7810 2008-07-29 01:11:15Z mike $"
3 *
4 * SNMP discovery backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2009 by Apple Inc.
7 * Copyright 2006-2007 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * "LICENSE" which should have been included with this file. If this
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * main() - Discover printers via SNMP.
20 * add_array() - Add a string to an array.
21 * add_cache() - Add a cached device...
22 * add_device_uri() - Add a device URI to the cache.
23 * alarm_handler() - Handle alarm signals...
24 * compare_cache() - Compare two cache entries.
25 * debug_printf() - Display some debugging information.
26 * fix_make_model() - Fix common problems in the make-and-model
27 * string.
28 * free_array() - Free an array of strings.
29 * free_cache() - Free the array of cached devices.
30 * get_interface_addresses() - Get the broadcast address(es) associated with
31 * an interface.
32 * list_device() - List a device we found...
33 * password_cb() - Handle authentication requests.
34 * probe_device() - Probe a device to discover whether it is a
35 * printer.
36 * read_snmp_conf() - Read the snmp.conf file.
37 * read_snmp_response() - Read and parse a SNMP response...
38 * run_time() - Return the total running time...
39 * scan_devices() - Scan for devices using SNMP.
40 * try_connect() - Try connecting on a port...
41 * update_cache() - Update a cached device...
42 */
43
44 /*
45 * Include necessary headers.
46 */
47
48 #include "backend-private.h"
49 #include <cups/array.h>
50 #include <cups/file.h>
51 #include <cups/http-private.h>
52 #include <regex.h>
53
54
55 /*
56 * This backend implements SNMP printer discovery. It uses a broadcast-
57 * based approach to get SNMP response packets from potential printers,
58 * requesting OIDs from the Host and Port Monitor MIBs, does a URI
59 * lookup based on the device description string, and finally a probe of
60 * port 9100 (AppSocket) and 515 (LPD).
61 *
62 * The current focus is on printers with internal network cards, although
63 * the code also works with many external print servers as well.
64 *
65 * The backend reads the snmp.conf file from the CUPS_SERVERROOT directory
66 * which can contain comments, blank lines, or any number of the following
67 * directives:
68 *
69 * Address ip-address
70 * Address @LOCAL
71 * Address @IF(name)
72 * Community name
73 * DebugLevel N
74 * DeviceURI "regex pattern" uri
75 * HostNameLookups on
76 * HostNameLookups off
77 * MaxRunTime N
78 *
79 * The default is to use:
80 *
81 * Address @LOCAL
82 * Community public
83 * DebugLevel 0
84 * HostNameLookups off
85 * MaxRunTime 120
86 *
87 * This backend is known to work with the following network printers and
88 * print servers:
89 *
90 * Axis OfficeBasic, 5400, 5600
91 * Brother
92 * EPSON
93 * Genicom
94 * HP JetDirect
95 * Lexmark
96 * Sharp
97 * Tektronix
98 * Xerox
99 *
100 * It does not currently work with:
101 *
102 * DLink
103 * Linksys
104 * Netgear
105 * Okidata
106 *
107 * (for all of these, they do not support the Host MIB)
108 */
109
110 /*
111 * Types...
112 */
113
114 enum /**** Request IDs for each field ****/
115 {
116 DEVICE_TYPE = 1,
117 DEVICE_DESCRIPTION,
118 DEVICE_LOCATION,
119 DEVICE_ID,
120 DEVICE_URI,
121 DEVICE_PRODUCT
122 };
123
124 typedef struct device_uri_s /**** DeviceURI values ****/
125 {
126 regex_t re; /* Regular expression to match */
127 cups_array_t *uris; /* URIs */
128 } device_uri_t;
129
130 typedef struct snmp_cache_s /**** SNMP scan cache ****/
131 {
132 http_addr_t address; /* Address of device */
133 char *addrname, /* Name of device */
134 *uri, /* device-uri */
135 *id, /* device-id */
136 *info, /* device-info */
137 *location, /* device-location */
138 *make_and_model; /* device-make-and-model */
139 int sent; /* Has this device been listed? */
140 } snmp_cache_t;
141
142
143 /*
144 * Local functions...
145 */
146
147 static char *add_array(cups_array_t *a, const char *s);
148 static void add_cache(http_addr_t *addr, const char *addrname,
149 const char *uri, const char *id,
150 const char *make_and_model);
151 static device_uri_t *add_device_uri(char *value);
152 static void alarm_handler(int sig);
153 static int compare_cache(snmp_cache_t *a, snmp_cache_t *b);
154 static void debug_printf(const char *format, ...);
155 static void fix_make_model(char *make_model,
156 const char *old_make_model,
157 int make_model_size);
158 static void free_array(cups_array_t *a);
159 static void free_cache(void);
160 static http_addrlist_t *get_interface_addresses(const char *ifname);
161 static void list_device(snmp_cache_t *cache);
162 static const char *password_cb(const char *prompt);
163 static void probe_device(snmp_cache_t *device);
164 static void read_snmp_conf(const char *address);
165 static void read_snmp_response(int fd);
166 static double run_time(void);
167 static void scan_devices(int fd);
168 static int try_connect(http_addr_t *addr, const char *addrname,
169 int port);
170 static void update_cache(snmp_cache_t *device, const char *uri,
171 const char *id, const char *make_model);
172
173
174 /*
175 * Local globals...
176 */
177
178 static cups_array_t *Addresses = NULL;
179 static cups_array_t *Communities = NULL;
180 static cups_array_t *Devices = NULL;
181 static int DebugLevel = 0;
182 static const int DescriptionOID[] = { CUPS_OID_hrDeviceDescr, 1, -1 };
183 static const int LocationOID[] = { CUPS_OID_sysLocation, 0, -1 };
184 static const int DeviceTypeOID[] = { CUPS_OID_hrDeviceType, 1, -1 };
185 static const int DeviceIdOID[] = { CUPS_OID_ppmPrinterIEEE1284DeviceId, 1, -1 };
186 static const int UriOID[] = { CUPS_OID_ppmPortServiceNameOrURI, 1, 1, -1 };
187 static const int LexmarkProductOID[] = { 1,3,6,1,4,1,641,2,1,2,1,2,1,-1 };
188 static const int LexmarkProductOID2[] = { 1,3,6,1,4,1,674,10898,100,2,1,2,1,2,1,-1 };
189 static const int LexmarkDeviceIdOID[] = { 1,3,6,1,4,1,641,2,1,2,1,3,1,-1 };
190 static const int XeroxProductOID[] = { 1,3,6,1,4,1,128,2,1,3,1,2,0,-1 };
191 static cups_array_t *DeviceURIs = NULL;
192 static int HostNameLookups = 0;
193 static int MaxRunTime = 120;
194 static struct timeval StartTime;
195
196
197 /*
198 * 'main()' - Discover printers via SNMP.
199 */
200
201 int /* O - Exit status */
202 main(int argc, /* I - Number of command-line arguments (6 or 7) */
203 char *argv[]) /* I - Command-line arguments */
204 {
205 int fd; /* SNMP socket */
206 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
207 struct sigaction action; /* Actions for POSIX signals */
208 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
209
210
211 /*
212 * Check command-line options...
213 */
214
215 if (argc > 2)
216 {
217 fputs(_("Usage: snmp [host-or-ip-address]\n"), stderr);
218 return (1);
219 }
220
221 /*
222 * Set the password callback for IPP operations...
223 */
224
225 cupsSetPasswordCB(password_cb);
226
227 /*
228 * Catch SIGALRM signals...
229 */
230
231 #ifdef HAVE_SIGSET
232 sigset(SIGALRM, alarm_handler);
233 #elif defined(HAVE_SIGACTION)
234 memset(&action, 0, sizeof(action));
235
236 sigemptyset(&action.sa_mask);
237 sigaddset(&action.sa_mask, SIGALRM);
238 action.sa_handler = alarm_handler;
239 sigaction(SIGALRM, &action, NULL);
240 #else
241 signal(SIGALRM, alarm_handler);
242 #endif /* HAVE_SIGSET */
243
244 /*
245 * Open the SNMP socket...
246 */
247
248 if ((fd = _cupsSNMPOpen(AF_INET)) < 0)
249 return (1);
250
251 /*
252 * Read the configuration file and any cache data...
253 */
254
255 read_snmp_conf(argv[1]);
256
257 _cupsSNMPSetDebug(DebugLevel);
258
259 Devices = cupsArrayNew((cups_array_func_t)compare_cache, NULL);
260
261 /*
262 * Scan for devices...
263 */
264
265 scan_devices(fd);
266
267 /*
268 * Close, free, and return with no errors...
269 */
270
271 _cupsSNMPClose(fd);
272
273 free_array(Addresses);
274 free_array(Communities);
275 free_cache();
276
277 return (0);
278 }
279
280
281 /*
282 * 'add_array()' - Add a string to an array.
283 */
284
285 static char * /* O - New string */
286 add_array(cups_array_t *a, /* I - Array */
287 const char *s) /* I - String to add */
288 {
289 char *dups; /* New string */
290
291
292 dups = strdup(s);
293
294 cupsArrayAdd(a, dups);
295
296 return (dups);
297 }
298
299
300 /*
301 * 'add_cache()' - Add a cached device...
302 */
303
304 static void
305 add_cache(http_addr_t *addr, /* I - Device IP address */
306 const char *addrname, /* I - IP address or name string */
307 const char *uri, /* I - Device URI */
308 const char *id, /* I - 1284 device ID */
309 const char *make_and_model) /* I - Make and model */
310 {
311 snmp_cache_t *temp; /* New device entry */
312
313
314 debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
315 "id=\"%s\", make_and_model=\"%s\")\n",
316 addr, addrname, uri ? uri : "(null)", id ? id : "(null)",
317 make_and_model ? make_and_model : "(null)");
318
319 temp = calloc(1, sizeof(snmp_cache_t));
320 memcpy(&(temp->address), addr, sizeof(temp->address));
321
322 temp->addrname = strdup(addrname);
323
324 if (uri)
325 temp->uri = strdup(uri);
326
327 if (id)
328 temp->id = strdup(id);
329
330 if (make_and_model)
331 temp->make_and_model = strdup(make_and_model);
332
333 cupsArrayAdd(Devices, temp);
334
335 if (uri)
336 list_device(temp);
337 }
338
339
340 /*
341 * 'add_device_uri()' - Add a device URI to the cache.
342 *
343 * The value string is modified (chopped up) as needed.
344 */
345
346 static device_uri_t * /* O - Device URI */
347 add_device_uri(char *value) /* I - Value from snmp.conf */
348 {
349 device_uri_t *device_uri; /* Device URI */
350 char *start; /* Start of value */
351
352
353 /*
354 * Allocate memory as needed...
355 */
356
357 if (!DeviceURIs)
358 DeviceURIs = cupsArrayNew(NULL, NULL);
359
360 if (!DeviceURIs)
361 return (NULL);
362
363 if ((device_uri = calloc(1, sizeof(device_uri_t))) == NULL)
364 return (NULL);
365
366 if ((device_uri->uris = cupsArrayNew(NULL, NULL)) == NULL)
367 {
368 free(device_uri);
369 return (NULL);
370 }
371
372 /*
373 * Scan the value string for the regular expression and URI(s)...
374 */
375
376 value ++; /* Skip leading " */
377
378 for (start = value; *value && *value != '\"'; value ++)
379 if (*value == '\\' && value[1])
380 _cups_strcpy(value, value + 1);
381
382 if (!*value)
383 {
384 fputs("ERROR: Missing end quote for DeviceURI!\n", stderr);
385
386 cupsArrayDelete(device_uri->uris);
387 free(device_uri);
388
389 return (NULL);
390 }
391
392 *value++ = '\0';
393
394 if (regcomp(&(device_uri->re), start, REG_EXTENDED | REG_ICASE))
395 {
396 fputs("ERROR: Bad regular expression for DeviceURI!\n", stderr);
397
398 cupsArrayDelete(device_uri->uris);
399 free(device_uri);
400
401 return (NULL);
402 }
403
404 while (*value)
405 {
406 while (isspace(*value & 255))
407 value ++;
408
409 if (!*value)
410 break;
411
412 for (start = value; *value && !isspace(*value & 255); value ++);
413
414 if (*value)
415 *value++ = '\0';
416
417 cupsArrayAdd(device_uri->uris, strdup(start));
418 }
419
420 /*
421 * Add the device URI to the list and return it...
422 */
423
424 cupsArrayAdd(DeviceURIs, device_uri);
425
426 return (device_uri);
427 }
428
429
430 /*
431 * 'alarm_handler()' - Handle alarm signals...
432 */
433
434 static void
435 alarm_handler(int sig) /* I - Signal number */
436 {
437 /*
438 * Do nothing...
439 */
440
441 (void)sig;
442
443 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
444 signal(SIGALRM, alarm_handler);
445 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
446
447 if (DebugLevel)
448 write(2, "DEBUG: ALARM!\n", 14);
449 }
450
451
452 /*
453 * 'compare_cache()' - Compare two cache entries.
454 */
455
456 static int /* O - Result of comparison */
457 compare_cache(snmp_cache_t *a, /* I - First cache entry */
458 snmp_cache_t *b) /* I - Second cache entry */
459 {
460 return (strcasecmp(a->addrname, b->addrname));
461 }
462
463
464 /*
465 * 'debug_printf()' - Display some debugging information.
466 */
467
468 static void
469 debug_printf(const char *format, /* I - Printf-style format string */
470 ...) /* I - Additional arguments as needed */
471 {
472 va_list ap; /* Pointer to arguments */
473
474
475 if (!DebugLevel)
476 return;
477
478 va_start(ap, format);
479 vfprintf(stderr, format, ap);
480 va_end(ap);
481 }
482
483
484 /*
485 * 'fix_make_model()' - Fix common problems in the make-and-model string.
486 */
487
488 static void
489 fix_make_model(
490 char *make_model, /* I - New make-and-model string */
491 const char *old_make_model, /* I - Old make-and-model string */
492 int make_model_size) /* I - Size of new string buffer */
493 {
494 char *mmptr; /* Pointer into make-and-model string */
495
496
497 /*
498 * Fix some common problems with the make-and-model string so
499 * that printer driver detection works better...
500 */
501
502 if (!strncasecmp(old_make_model, "Hewlett-Packard", 15))
503 {
504 /*
505 * Strip leading Hewlett-Packard and hp prefixes and replace
506 * with a single HP manufacturer prefix...
507 */
508
509 mmptr = (char *)old_make_model + 15;
510
511 while (isspace(*mmptr & 255))
512 mmptr ++;
513
514 if (!strncasecmp(mmptr, "hp", 2))
515 {
516 mmptr += 2;
517
518 while (isspace(*mmptr & 255))
519 mmptr ++;
520 }
521
522 make_model[0] = 'H';
523 make_model[1] = 'P';
524 make_model[2] = ' ';
525 strlcpy(make_model + 3, mmptr, make_model_size - 3);
526 }
527 else if (!strncasecmp(old_make_model, "deskjet", 7))
528 snprintf(make_model, make_model_size, "HP DeskJet%s", old_make_model + 7);
529 else if (!strncasecmp(old_make_model, "officejet", 9))
530 snprintf(make_model, make_model_size, "HP OfficeJet%s", old_make_model + 9);
531 else if (!strncasecmp(old_make_model, "stylus_pro_", 11))
532 snprintf(make_model, make_model_size, "EPSON Stylus Pro %s",
533 old_make_model + 11);
534 else
535 strlcpy(make_model, old_make_model, make_model_size);
536
537 if ((mmptr = strstr(make_model, ", Inc.,")) != NULL)
538 {
539 /*
540 * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560"
541 * becomes "Tektronix Phaser 560"...
542 */
543
544 _cups_strcpy(mmptr, mmptr + 7);
545 }
546
547 if ((mmptr = strstr(make_model, " Network")) != NULL)
548 {
549 /*
550 * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025
551 * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"...
552 */
553
554 *mmptr = '\0';
555 }
556
557 if ((mmptr = strchr(make_model, ',')) != NULL)
558 {
559 /*
560 * Drop anything after a trailing comma...
561 */
562
563 *mmptr = '\0';
564 }
565 }
566
567
568 /*
569 * 'free_array()' - Free an array of strings.
570 */
571
572 static void
573 free_array(cups_array_t *a) /* I - Array */
574 {
575 char *s; /* Current string */
576
577
578 for (s = (char *)cupsArrayFirst(a); s; s = (char *)cupsArrayNext(a))
579 free(s);
580
581 cupsArrayDelete(a);
582 }
583
584
585 /*
586 * 'free_cache()' - Free the array of cached devices.
587 */
588
589 static void
590 free_cache(void)
591 {
592 snmp_cache_t *cache; /* Cached device */
593
594
595 for (cache = (snmp_cache_t *)cupsArrayFirst(Devices);
596 cache;
597 cache = (snmp_cache_t *)cupsArrayNext(Devices))
598 {
599 free(cache->addrname);
600
601 if (cache->uri)
602 free(cache->uri);
603
604 if (cache->id)
605 free(cache->id);
606
607 if (cache->make_and_model)
608 free(cache->make_and_model);
609
610 free(cache);
611 }
612
613 cupsArrayDelete(Devices);
614 Devices = NULL;
615 }
616
617
618 /*
619 * 'get_interface_addresses()' - Get the broadcast address(es) associated
620 * with an interface.
621 */
622
623 static http_addrlist_t * /* O - List of addresses */
624 get_interface_addresses(
625 const char *ifname) /* I - Interface name */
626 {
627 struct ifaddrs *addrs, /* Interface address list */
628 *addr; /* Current interface address */
629 http_addrlist_t *first, /* First address in list */
630 *last, /* Last address in list */
631 *current; /* Current address */
632
633
634 if (getifaddrs(&addrs) < 0)
635 return (NULL);
636
637 for (addr = addrs, first = NULL, last = NULL; addr; addr = addr->ifa_next)
638 if ((addr->ifa_flags & IFF_BROADCAST) && addr->ifa_broadaddr &&
639 addr->ifa_broadaddr->sa_family == AF_INET &&
640 (!ifname || !strcmp(ifname, addr->ifa_name)))
641 {
642 current = calloc(1, sizeof(http_addrlist_t));
643
644 memcpy(&(current->addr), addr->ifa_broadaddr,
645 sizeof(struct sockaddr_in));
646
647 if (!last)
648 first = current;
649 else
650 last->next = current;
651
652 last = current;
653 }
654
655 freeifaddrs(addrs);
656
657 return (first);
658 }
659
660
661 /*
662 * 'list_device()' - List a device we found...
663 */
664
665 static void
666 list_device(snmp_cache_t *cache) /* I - Cached device */
667 {
668 if (cache->uri)
669 cupsBackendReport("network", cache->uri, cache->make_and_model,
670 cache->info, cache->id, cache->location);
671 }
672
673
674 /*
675 * 'password_cb()' - Handle authentication requests.
676 *
677 * All we do right now is return NULL, indicating that no authentication
678 * is possible.
679 */
680
681 static const char * /* O - Password (NULL) */
682 password_cb(const char *prompt) /* I - Prompt message */
683 {
684 (void)prompt; /* Anti-compiler-warning-code */
685
686 return (NULL);
687 }
688
689
690 /*
691 * 'probe_device()' - Probe a device to discover whether it is a printer.
692 *
693 * TODO: Try using the Port Monitor MIB to discover the correct protocol
694 * to use - first need a commercially-available printer that supports
695 * it, though...
696 */
697
698 static void
699 probe_device(snmp_cache_t *device) /* I - Device */
700 {
701 char uri[1024], /* Full device URI */
702 *uriptr, /* Pointer into URI */
703 *format; /* Format string for device */
704 device_uri_t *device_uri; /* Current DeviceURI match */
705
706
707 debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device->addrname);
708
709 #ifdef __APPLE__
710 /*
711 * TODO: Try an mDNS query first, and then fallback on direct probes...
712 */
713
714 if (!try_connect(&(device->address), device->addrname, 5353))
715 {
716 debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device->addrname);
717 return;
718 }
719 #endif /* __APPLE__ */
720
721 /*
722 * Lookup the device in the match table...
723 */
724
725 for (device_uri = (device_uri_t *)cupsArrayFirst(DeviceURIs);
726 device_uri;
727 device_uri = (device_uri_t *)cupsArrayNext(DeviceURIs))
728 if (device->make_and_model &&
729 !regexec(&(device_uri->re), device->make_and_model, 0, NULL, 0))
730 {
731 /*
732 * Found a match, add the URIs...
733 */
734
735 for (format = (char *)cupsArrayFirst(device_uri->uris);
736 format;
737 format = (char *)cupsArrayNext(device_uri->uris))
738 {
739 for (uriptr = uri; *format && uriptr < (uri + sizeof(uri) - 1);)
740 if (*format == '%' && format[1] == 's')
741 {
742 /*
743 * Insert hostname/address...
744 */
745
746 strlcpy(uriptr, device->addrname, sizeof(uri) - (uriptr - uri));
747 uriptr += strlen(uriptr);
748 format += 2;
749 }
750 else
751 *uriptr++ = *format++;
752
753 *uriptr = '\0';
754
755 update_cache(device, uri, NULL, NULL);
756 }
757
758 return;
759 }
760
761 /*
762 * Then try the standard ports...
763 */
764
765 if (!try_connect(&(device->address), device->addrname, 9100))
766 {
767 debug_printf("DEBUG: %s supports AppSocket!\n", device->addrname);
768
769 snprintf(uri, sizeof(uri), "socket://%s", device->addrname);
770 update_cache(device, uri, NULL, NULL);
771 }
772 else if (!try_connect(&(device->address), device->addrname, 515))
773 {
774 debug_printf("DEBUG: %s supports LPD!\n", device->addrname);
775
776 snprintf(uri, sizeof(uri), "lpd://%s/", device->addrname);
777 update_cache(device, uri, NULL, NULL);
778 }
779 }
780
781
782 /*
783 * 'read_snmp_conf()' - Read the snmp.conf file.
784 */
785
786 static void
787 read_snmp_conf(const char *address) /* I - Single address to probe */
788 {
789 cups_file_t *fp; /* File pointer */
790 char filename[1024], /* Filename */
791 line[1024], /* Line from file */
792 *value; /* Value on line */
793 int linenum; /* Line number */
794 const char *cups_serverroot; /* CUPS_SERVERROOT env var */
795 const char *debug; /* CUPS_DEBUG_LEVEL env var */
796 const char *runtime; /* CUPS_MAX_RUN_TIME env var */
797
798
799 /*
800 * Initialize the global address and community lists...
801 */
802
803 Addresses = cupsArrayNew(NULL, NULL);
804 Communities = cupsArrayNew(NULL, NULL);
805
806 if (address)
807 add_array(Addresses, address);
808
809 if ((debug = getenv("CUPS_DEBUG_LEVEL")) != NULL)
810 DebugLevel = atoi(debug);
811
812 if ((runtime = getenv("CUPS_MAX_RUN_TIME")) != NULL)
813 MaxRunTime = atoi(runtime);
814
815 /*
816 * Find the snmp.conf file...
817 */
818
819 if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
820 cups_serverroot = CUPS_SERVERROOT;
821
822 snprintf(filename, sizeof(filename), "%s/snmp.conf", cups_serverroot);
823
824 if ((fp = cupsFileOpen(filename, "r")) != NULL)
825 {
826 /*
827 * Read the snmp.conf file...
828 */
829
830 linenum = 0;
831
832 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
833 {
834 if (!value)
835 fprintf(stderr, "ERROR: Missing value on line %d of %s!\n", linenum,
836 filename);
837 else if (!strcasecmp(line, "Address"))
838 {
839 if (!address)
840 add_array(Addresses, value);
841 }
842 else if (!strcasecmp(line, "Community"))
843 add_array(Communities, value);
844 else if (!strcasecmp(line, "DebugLevel"))
845 DebugLevel = atoi(value);
846 else if (!strcasecmp(line, "DeviceURI"))
847 {
848 if (*value != '\"')
849 fprintf(stderr,
850 "ERROR: Missing double quote for regular expression on "
851 "line %d of %s!\n", linenum, filename);
852 else
853 add_device_uri(value);
854 }
855 else if (!strcasecmp(line, "HostNameLookups"))
856 HostNameLookups = !strcasecmp(value, "on") ||
857 !strcasecmp(value, "yes") ||
858 !strcasecmp(value, "true") ||
859 !strcasecmp(value, "double");
860 else if (!strcasecmp(line, "MaxRunTime"))
861 MaxRunTime = atoi(value);
862 else
863 fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n",
864 line, linenum, filename);
865 }
866
867 cupsFileClose(fp);
868 }
869
870 /*
871 * Use defaults if parameters are undefined...
872 */
873
874 if (cupsArrayCount(Addresses) == 0)
875 {
876 /*
877 * If we have no addresses, exit immediately...
878 */
879
880 fprintf(stderr,
881 "DEBUG: No address specified and no Address line in %s...\n",
882 filename);
883 exit(0);
884 }
885
886 if (cupsArrayCount(Communities) == 0)
887 {
888 fputs("INFO: Using default SNMP Community public\n", stderr);
889 add_array(Communities, "public");
890 }
891 }
892
893
894 /*
895 * 'read_snmp_response()' - Read and parse a SNMP response...
896 */
897
898 static void
899 read_snmp_response(int fd) /* I - SNMP socket file descriptor */
900 {
901 char addrname[256]; /* Source address name */
902 cups_snmp_t packet; /* Decoded packet */
903 snmp_cache_t key, /* Search key */
904 *device; /* Matching device */
905
906
907 /*
908 * Read the response data...
909 */
910
911 if (!_cupsSNMPRead(fd, &packet, -1.0))
912 {
913 fprintf(stderr, "ERROR: Unable to read data from socket: %s\n",
914 strerror(errno));
915 return;
916 }
917
918 if (HostNameLookups)
919 httpAddrLookup(&(packet.address), addrname, sizeof(addrname));
920 else
921 httpAddrString(&(packet.address), addrname, sizeof(addrname));
922
923 debug_printf("DEBUG: %.3f Received data from %s...\n", run_time(), addrname);
924
925 /*
926 * Look for the response status code in the SNMP message header...
927 */
928
929 if (packet.error)
930 {
931 fprintf(stderr, "ERROR: Bad SNMP packet from %s: %s\n", addrname,
932 packet.error);
933
934 return;
935 }
936
937 debug_printf("DEBUG: community=\"%s\"\n", packet.community);
938 debug_printf("DEBUG: request-id=%d\n", packet.request_id);
939 debug_printf("DEBUG: error-status=%d\n", packet.error_status);
940
941 if (packet.error_status)
942 return;
943
944 /*
945 * Find a matching device in the cache...
946 */
947
948 key.addrname = addrname;
949 device = (snmp_cache_t *)cupsArrayFind(Devices, &key);
950
951 /*
952 * Process the message...
953 */
954
955 switch (packet.request_id)
956 {
957 case DEVICE_TYPE :
958 /*
959 * Got the device type response...
960 */
961
962 if (device)
963 {
964 debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
965 addrname);
966 return;
967 }
968
969 /*
970 * Add the device and request the device data...
971 */
972
973 add_cache(&(packet.address), addrname, NULL, NULL, NULL);
974
975 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
976 packet.community, CUPS_ASN1_GET_REQUEST,
977 DEVICE_DESCRIPTION, DescriptionOID);
978 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
979 packet.community, CUPS_ASN1_GET_REQUEST,
980 DEVICE_ID, DeviceIdOID);
981 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
982 packet.community, CUPS_ASN1_GET_REQUEST,
983 DEVICE_URI, UriOID);
984 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
985 packet.community, CUPS_ASN1_GET_REQUEST,
986 DEVICE_LOCATION, LocationOID);
987 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
988 packet.community, CUPS_ASN1_GET_REQUEST,
989 DEVICE_PRODUCT, LexmarkProductOID);
990 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
991 packet.community, CUPS_ASN1_GET_REQUEST,
992 DEVICE_PRODUCT, LexmarkProductOID2);
993 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
994 packet.community, CUPS_ASN1_GET_REQUEST,
995 DEVICE_ID, LexmarkDeviceIdOID);
996 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
997 packet.community, CUPS_ASN1_GET_REQUEST,
998 DEVICE_PRODUCT, XeroxProductOID);
999 break;
1000
1001 case DEVICE_DESCRIPTION :
1002 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING)
1003 {
1004 /*
1005 * Update an existing cache entry...
1006 */
1007
1008 char make_model[256]; /* Make and model */
1009
1010
1011 if (strchr((char *)packet.object_value.string.bytes, ':') &&
1012 strchr((char *)packet.object_value.string.bytes, ';'))
1013 {
1014 /*
1015 * Description is the IEEE-1284 device ID...
1016 */
1017
1018 if (!device->id)
1019 device->id = strdup((char *)packet.object_value.string.bytes);
1020
1021 backendGetMakeModel((char *)packet.object_value.string.bytes,
1022 make_model, sizeof(make_model));
1023
1024 if (device->info)
1025 free(device->info);
1026
1027 device->info = strdup(make_model);
1028 }
1029 else
1030 {
1031 /*
1032 * Description is plain text...
1033 */
1034
1035 fix_make_model(make_model, (char *)packet.object_value.string.bytes,
1036 sizeof(make_model));
1037
1038 if (device->info)
1039 free(device->info);
1040
1041 device->info = strdup((char *)packet.object_value.string.bytes);
1042 }
1043
1044 if (!device->make_and_model)
1045 device->make_and_model = strdup(make_model);
1046 }
1047 break;
1048
1049 case DEVICE_ID :
1050 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
1051 (!device->id ||
1052 strlen(device->id) < packet.object_value.string.num_bytes))
1053 {
1054 /*
1055 * Update an existing cache entry...
1056 */
1057
1058 char make_model[256]; /* Make and model */
1059
1060
1061 if (device->id)
1062 free(device->id);
1063
1064 device->id = strdup((char *)packet.object_value.string.bytes);
1065
1066 /*
1067 * Convert the ID to a make and model string...
1068 */
1069
1070 backendGetMakeModel((char *)packet.object_value.string.bytes,
1071 make_model, sizeof(make_model));
1072 if (device->make_and_model)
1073 free(device->make_and_model);
1074
1075 device->make_and_model = strdup(make_model);
1076 }
1077 break;
1078
1079 case DEVICE_LOCATION :
1080 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
1081 !device->location)
1082 device->location = strdup((char *)packet.object_value.string.bytes);
1083 break;
1084
1085 case DEVICE_PRODUCT :
1086 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
1087 !device->id)
1088 {
1089 /*
1090 * Update an existing cache entry...
1091 */
1092
1093 if (!device->info)
1094 device->info = strdup((char *)packet.object_value.string.bytes);
1095
1096 if (device->make_and_model)
1097 free(device->make_and_model);
1098
1099 device->make_and_model = strdup((char *)packet.object_value.string.bytes);
1100 }
1101 break;
1102
1103 case DEVICE_URI :
1104 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
1105 !device->uri && packet.object_value.string.num_bytes > 0)
1106 {
1107 /*
1108 * Update an existing cache entry...
1109 */
1110
1111 if (!strncmp((char *)packet.object_value.string.bytes, "lpr:", 4))
1112 {
1113 /*
1114 * We want "lpd://..." for the URI...
1115 */
1116
1117 packet.object_value.string.bytes[2] = 'd';
1118 }
1119
1120 device->uri = strdup((char *)packet.object_value.string.bytes);
1121 }
1122 break;
1123 }
1124 }
1125
1126
1127 /*
1128 * 'run_time()' - Return the total running time...
1129 */
1130
1131 static double /* O - Number of seconds */
1132 run_time(void)
1133 {
1134 struct timeval curtime; /* Current time */
1135
1136
1137 gettimeofday(&curtime, NULL);
1138
1139 return (curtime.tv_sec - StartTime.tv_sec +
1140 0.000001 * (curtime.tv_usec - StartTime.tv_usec));
1141 }
1142
1143
1144 /*
1145 * 'scan_devices()' - Scan for devices using SNMP.
1146 */
1147
1148 static void
1149 scan_devices(int fd) /* I - SNMP socket */
1150 {
1151 char *address, /* Current address */
1152 *community; /* Current community */
1153 fd_set input; /* Input set for select() */
1154 struct timeval timeout; /* Timeout for select() */
1155 time_t endtime; /* End time for scan */
1156 http_addrlist_t *addrs, /* List of addresses */
1157 *addr; /* Current address */
1158 snmp_cache_t *device; /* Current device */
1159
1160
1161 gettimeofday(&StartTime, NULL);
1162
1163 /*
1164 * First send all of the broadcast queries...
1165 */
1166
1167 for (address = (char *)cupsArrayFirst(Addresses);
1168 address;
1169 address = (char *)cupsArrayNext(Addresses))
1170 {
1171 if (!strcmp(address, "@LOCAL"))
1172 addrs = get_interface_addresses(NULL);
1173 else if (!strncmp(address, "@IF(", 4))
1174 {
1175 char ifname[255]; /* Interface name */
1176
1177
1178 strlcpy(ifname, address + 4, sizeof(ifname));
1179 if (ifname[0])
1180 ifname[strlen(ifname) - 1] = '\0';
1181
1182 addrs = get_interface_addresses(ifname);
1183 }
1184 else
1185 addrs = httpAddrGetList(address, AF_INET, NULL);
1186
1187 if (!addrs)
1188 {
1189 fprintf(stderr, "ERROR: Unable to scan \"%s\"!\n", address);
1190 continue;
1191 }
1192
1193 for (community = (char *)cupsArrayFirst(Communities);
1194 community;
1195 community = (char *)cupsArrayNext(Communities))
1196 {
1197 debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
1198 community, address);
1199
1200 for (addr = addrs; addr; addr = addr->next)
1201 _cupsSNMPWrite(fd, &(addr->addr), CUPS_SNMP_VERSION_1, community,
1202 CUPS_ASN1_GET_REQUEST, DEVICE_TYPE, DeviceTypeOID);
1203 }
1204
1205 httpAddrFreeList(addrs);
1206 }
1207
1208 /*
1209 * Then read any responses that come in over the next 3 seconds...
1210 */
1211
1212 endtime = time(NULL) + MaxRunTime;
1213
1214 FD_ZERO(&input);
1215
1216 while (time(NULL) < endtime)
1217 {
1218 timeout.tv_sec = 2;
1219 timeout.tv_usec = 0;
1220
1221 FD_SET(fd, &input);
1222 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
1223 {
1224 fprintf(stderr, "ERROR: %.3f select() for %d failed: %s\n", run_time(),
1225 fd, strerror(errno));
1226 break;
1227 }
1228
1229 if (FD_ISSET(fd, &input))
1230 read_snmp_response(fd);
1231 else
1232 {
1233 /*
1234 * List devices with complete information...
1235 */
1236
1237 int sent_something = 0;
1238
1239 for (device = (snmp_cache_t *)cupsArrayFirst(Devices);
1240 device;
1241 device = (snmp_cache_t *)cupsArrayNext(Devices))
1242 if (!device->sent && device->info && device->make_and_model)
1243 {
1244 if (device->uri)
1245 list_device(device);
1246 else
1247 probe_device(device);
1248
1249 device->sent = sent_something = 1;
1250 }
1251
1252 if (!sent_something)
1253 break;
1254 }
1255 }
1256
1257 debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
1258 }
1259
1260
1261 /*
1262 * 'try_connect()' - Try connecting on a port...
1263 */
1264
1265 static int /* O - 0 on success or -1 on error */
1266 try_connect(http_addr_t *addr, /* I - Socket address */
1267 const char *addrname, /* I - Hostname or IP address */
1268 int port) /* I - Port number */
1269 {
1270 int fd; /* Socket */
1271 int status; /* Connection status */
1272
1273
1274 debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
1275 port == 515 ? "lpd" : "socket", addrname, port);
1276
1277 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
1278 {
1279 fprintf(stderr, "ERROR: Unable to create socket: %s\n",
1280 strerror(errno));
1281 return (-1);
1282 }
1283
1284 addr->ipv4.sin_port = htons(port);
1285
1286 alarm(1);
1287
1288 status = connect(fd, (void *)addr, httpAddrLength(addr));
1289
1290 close(fd);
1291 alarm(0);
1292
1293 return (status);
1294 }
1295
1296
1297 /*
1298 * 'update_cache()' - Update a cached device...
1299 */
1300
1301 static void
1302 update_cache(snmp_cache_t *device, /* I - Device */
1303 const char *uri, /* I - Device URI */
1304 const char *id, /* I - Device ID */
1305 const char *make_model) /* I - Device make and model */
1306 {
1307 if (device->uri)
1308 free(device->uri);
1309
1310 device->uri = strdup(uri);
1311
1312 if (id)
1313 {
1314 if (device->id)
1315 free(device->id);
1316
1317 device->id = strdup(id);
1318 }
1319
1320 if (make_model)
1321 {
1322 if (device->make_and_model)
1323 free(device->make_and_model);
1324
1325 device->make_and_model = strdup(make_model);
1326 }
1327
1328 list_device(device);
1329 }
1330
1331
1332 /*
1333 * End of "$Id: snmp.c 7810 2008-07-29 01:11:15Z mike $".
1334 */