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