]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/snmp.c
Remove old files.
[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 120
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 = 120;
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 {
661 printf("network %s \"%s\" \"%s %s\" \"%s\"\n",
662 cache->uri,
663 cache->make_and_model ? cache->make_and_model : "Unknown",
664 cache->make_and_model ? cache->make_and_model : "Unknown",
665 cache->addrname, cache->id ? cache->id : "");
666 fflush(stdout);
667 }
668 }
669
670
671 /*
672 * 'password_cb()' - Handle authentication requests.
673 *
674 * All we do right now is return NULL, indicating that no authentication
675 * is possible.
676 */
677
678 static const char * /* O - Password (NULL) */
679 password_cb(const char *prompt) /* I - Prompt message */
680 {
681 (void)prompt; /* Anti-compiler-warning-code */
682
683 return (NULL);
684 }
685
686
687 /*
688 * 'probe_device()' - Probe a device to discover whether it is a printer.
689 *
690 * TODO: Try using the Port Monitor MIB to discover the correct protocol
691 * to use - first need a commercially-available printer that supports
692 * it, though...
693 */
694
695 static void
696 probe_device(snmp_cache_t *device) /* I - Device */
697 {
698 char uri[1024], /* Full device URI */
699 *uriptr, /* Pointer into URI */
700 *format; /* Format string for device */
701 device_uri_t *device_uri; /* Current DeviceURI match */
702
703
704 debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device->addrname);
705
706 #ifdef __APPLE__
707 /*
708 * TODO: Try an mDNS query first, and then fallback on direct probes...
709 */
710
711 if (!try_connect(&(device->address), device->addrname, 5353))
712 {
713 debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device->addrname);
714 return;
715 }
716 #endif /* __APPLE__ */
717
718 /*
719 * Lookup the device in the match table...
720 */
721
722 for (device_uri = (device_uri_t *)cupsArrayFirst(DeviceURIs);
723 device_uri;
724 device_uri = (device_uri_t *)cupsArrayNext(DeviceURIs))
725 if (device->make_and_model &&
726 !regexec(&(device_uri->re), device->make_and_model, 0, NULL, 0))
727 {
728 /*
729 * Found a match, add the URIs...
730 */
731
732 for (format = (char *)cupsArrayFirst(device_uri->uris);
733 format;
734 format = (char *)cupsArrayNext(device_uri->uris))
735 {
736 for (uriptr = uri; *format && uriptr < (uri + sizeof(uri) - 1);)
737 if (*format == '%' && format[1] == 's')
738 {
739 /*
740 * Insert hostname/address...
741 */
742
743 strlcpy(uriptr, device->addrname, sizeof(uri) - (uriptr - uri));
744 uriptr += strlen(uriptr);
745 format += 2;
746 }
747 else
748 *uriptr++ = *format++;
749
750 *uriptr = '\0';
751
752 update_cache(device, uri, NULL, NULL);
753 }
754
755 return;
756 }
757
758 /*
759 * Then try the standard ports...
760 */
761
762 if (!try_connect(&(device->address), device->addrname, 9100))
763 {
764 debug_printf("DEBUG: %s supports AppSocket!\n", device->addrname);
765
766 snprintf(uri, sizeof(uri), "socket://%s", device->addrname);
767 update_cache(device, uri, NULL, NULL);
768 }
769 else if (!try_connect(&(device->address), device->addrname, 515))
770 {
771 debug_printf("DEBUG: %s supports LPD!\n", device->addrname);
772
773 snprintf(uri, sizeof(uri), "lpd://%s/", device->addrname);
774 update_cache(device, uri, NULL, NULL);
775 }
776 }
777
778
779 /*
780 * 'read_snmp_conf()' - Read the snmp.conf file.
781 */
782
783 static void
784 read_snmp_conf(const char *address) /* I - Single address to probe */
785 {
786 cups_file_t *fp; /* File pointer */
787 char filename[1024], /* Filename */
788 line[1024], /* Line from file */
789 *value; /* Value on line */
790 int linenum; /* Line number */
791 const char *cups_serverroot; /* CUPS_SERVERROOT env var */
792 const char *debug; /* CUPS_DEBUG_LEVEL env var */
793 const char *runtime; /* CUPS_MAX_RUN_TIME env var */
794
795
796 /*
797 * Initialize the global address and community lists...
798 */
799
800 Addresses = cupsArrayNew(NULL, NULL);
801 Communities = cupsArrayNew(NULL, NULL);
802
803 if (address)
804 add_array(Addresses, address);
805
806 if ((debug = getenv("CUPS_DEBUG_LEVEL")) != NULL)
807 DebugLevel = atoi(debug);
808
809 if ((runtime = getenv("CUPS_MAX_RUN_TIME")) != NULL)
810 MaxRunTime = atoi(runtime);
811
812 /*
813 * Find the snmp.conf file...
814 */
815
816 if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
817 cups_serverroot = CUPS_SERVERROOT;
818
819 snprintf(filename, sizeof(filename), "%s/snmp.conf", cups_serverroot);
820
821 if ((fp = cupsFileOpen(filename, "r")) != NULL)
822 {
823 /*
824 * Read the snmp.conf file...
825 */
826
827 linenum = 0;
828
829 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
830 {
831 if (!value)
832 fprintf(stderr, "ERROR: Missing value on line %d of %s!\n", linenum,
833 filename);
834 else if (!strcasecmp(line, "Address"))
835 {
836 if (!address)
837 add_array(Addresses, value);
838 }
839 else if (!strcasecmp(line, "Community"))
840 add_array(Communities, value);
841 else if (!strcasecmp(line, "DebugLevel"))
842 DebugLevel = atoi(value);
843 else if (!strcasecmp(line, "DeviceURI"))
844 {
845 if (*value != '\"')
846 fprintf(stderr,
847 "ERROR: Missing double quote for regular expression on "
848 "line %d of %s!\n", linenum, filename);
849 else
850 add_device_uri(value);
851 }
852 else if (!strcasecmp(line, "HostNameLookups"))
853 HostNameLookups = !strcasecmp(value, "on") ||
854 !strcasecmp(value, "yes") ||
855 !strcasecmp(value, "true") ||
856 !strcasecmp(value, "double");
857 else if (!strcasecmp(line, "MaxRunTime"))
858 MaxRunTime = atoi(value);
859 else
860 fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n",
861 line, linenum, filename);
862 }
863
864 cupsFileClose(fp);
865 }
866
867 /*
868 * Use defaults if parameters are undefined...
869 */
870
871 if (cupsArrayCount(Addresses) == 0)
872 {
873 /*
874 * If we have no addresses, exit immediately...
875 */
876
877 fprintf(stderr,
878 "DEBUG: No address specified and no Address line in %s...\n",
879 filename);
880 exit(0);
881 }
882
883 if (cupsArrayCount(Communities) == 0)
884 {
885 fputs("INFO: Using default SNMP Community public\n", stderr);
886 add_array(Communities, "public");
887 }
888 }
889
890
891 /*
892 * 'read_snmp_response()' - Read and parse a SNMP response...
893 */
894
895 static void
896 read_snmp_response(int fd) /* I - SNMP socket file descriptor */
897 {
898 char addrname[256]; /* Source address name */
899 cups_snmp_t packet; /* Decoded packet */
900 snmp_cache_t key, /* Search key */
901 *device; /* Matching device */
902
903
904 /*
905 * Read the response data...
906 */
907
908 if (!cupsSNMPRead(fd, &packet, -1.0))
909 {
910 fprintf(stderr, "ERROR: Unable to read data from socket: %s\n",
911 strerror(errno));
912 return;
913 }
914
915 if (HostNameLookups)
916 httpAddrLookup(&(packet.address), addrname, sizeof(addrname));
917 else
918 httpAddrString(&(packet.address), addrname, sizeof(addrname));
919
920 debug_printf("DEBUG: %.3f Received data from %s...\n", run_time(), addrname);
921
922 /*
923 * Look for the response status code in the SNMP message header...
924 */
925
926 if (packet.error)
927 {
928 fprintf(stderr, "ERROR: Bad SNMP packet from %s: %s\n", addrname,
929 packet.error);
930
931 return;
932 }
933
934 debug_printf("DEBUG: community=\"%s\"\n", packet.community);
935 debug_printf("DEBUG: request-id=%d\n", packet.request_id);
936 debug_printf("DEBUG: error-status=%d\n", packet.error_status);
937
938 if (packet.error_status)
939 return;
940
941 /*
942 * Find a matching device in the cache...
943 */
944
945 key.addrname = addrname;
946 device = (snmp_cache_t *)cupsArrayFind(Devices, &key);
947
948 /*
949 * Process the message...
950 */
951
952 if (packet.request_id == DeviceTypeRequest)
953 {
954 /*
955 * Got the device type response...
956 */
957
958 if (device)
959 {
960 debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
961 addrname);
962 return;
963 }
964
965 /*
966 * Add the device and request the device description...
967 */
968
969 add_cache(&(packet.address), addrname, NULL, NULL, NULL);
970
971 cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, packet.community,
972 CUPS_ASN1_GET_REQUEST, DeviceDescRequest, DeviceDescOID);
973 }
974 else if (packet.request_id == DeviceDescRequest &&
975 packet.object_type == CUPS_ASN1_OCTET_STRING)
976 {
977 /*
978 * Update an existing cache entry...
979 */
980
981 char make_model[256]; /* Make and model */
982
983
984 if (!device)
985 {
986 debug_printf("DEBUG: Discarding device description for \"%s\"...\n",
987 addrname);
988 return;
989 }
990
991 /*
992 * Convert the description to a make and model string...
993 */
994
995 if (strchr(packet.object_value.string, ':') &&
996 strchr(packet.object_value.string, ';'))
997 {
998 /*
999 * Description is the IEEE-1284 device ID...
1000 */
1001
1002 backendGetMakeModel(packet.object_value.string, make_model,
1003 sizeof(make_model));
1004 }
1005 else
1006 {
1007 /*
1008 * Description is plain text...
1009 */
1010
1011 fix_make_model(make_model, packet.object_value.string,
1012 sizeof(make_model));
1013 }
1014
1015 if (device->make_and_model)
1016 free(device->make_and_model);
1017
1018 device->make_and_model = strdup(make_model);
1019
1020 probe_device(device);
1021 }
1022 }
1023
1024
1025 /*
1026 * 'run_time()' - Return the total running time...
1027 */
1028
1029 static double /* O - Number of seconds */
1030 run_time(void)
1031 {
1032 struct timeval curtime; /* Current time */
1033
1034
1035 gettimeofday(&curtime, NULL);
1036
1037 return (curtime.tv_sec - StartTime.tv_sec +
1038 0.000001 * (curtime.tv_usec - StartTime.tv_usec));
1039 }
1040
1041
1042 /*
1043 * 'scan_devices()' - Scan for devices using SNMP.
1044 */
1045
1046 static void
1047 scan_devices(int fd) /* I - SNMP socket */
1048 {
1049 char *address, /* Current address */
1050 *community; /* Current community */
1051 fd_set input; /* Input set for select() */
1052 struct timeval timeout; /* Timeout for select() */
1053 time_t endtime; /* End time for scan */
1054 http_addrlist_t *addrs, /* List of addresses */
1055 *addr; /* Current address */
1056 snmp_cache_t *device; /* Current device */
1057
1058
1059 /*
1060 * Setup the request IDs...
1061 */
1062
1063 gettimeofday(&StartTime, NULL);
1064
1065 DeviceTypeRequest = StartTime.tv_sec;
1066 DeviceDescRequest = StartTime.tv_sec + 1;
1067
1068 /*
1069 * First send all of the broadcast queries...
1070 */
1071
1072 for (address = (char *)cupsArrayFirst(Addresses);
1073 address;
1074 address = (char *)cupsArrayNext(Addresses))
1075 {
1076 if (!strcmp(address, "@LOCAL"))
1077 addrs = get_interface_addresses(NULL);
1078 else if (!strncmp(address, "@IF(", 4))
1079 {
1080 char ifname[255]; /* Interface name */
1081
1082
1083 strlcpy(ifname, address + 4, sizeof(ifname));
1084 if (ifname[0])
1085 ifname[strlen(ifname) - 1] = '\0';
1086
1087 addrs = get_interface_addresses(ifname);
1088 }
1089 else
1090 addrs = httpAddrGetList(address, AF_INET, NULL);
1091
1092 if (!addrs)
1093 {
1094 fprintf(stderr, "ERROR: Unable to scan \"%s\"!\n", address);
1095 continue;
1096 }
1097
1098 for (community = (char *)cupsArrayFirst(Communities);
1099 community;
1100 community = (char *)cupsArrayNext(Communities))
1101 {
1102 debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
1103 community, address);
1104
1105 for (addr = addrs; addr; addr = addr->next)
1106 cupsSNMPWrite(fd, &(addr->addr), CUPS_SNMP_VERSION_1, community,
1107 CUPS_ASN1_GET_REQUEST, DeviceTypeRequest, DeviceTypeOID);
1108 }
1109
1110 httpAddrFreeList(addrs);
1111 }
1112
1113 /*
1114 * Then read any responses that come in over the next 3 seconds...
1115 */
1116
1117 endtime = time(NULL) + MaxRunTime;
1118
1119 FD_ZERO(&input);
1120
1121 while (time(NULL) < endtime)
1122 {
1123 timeout.tv_sec = 2;
1124 timeout.tv_usec = 0;
1125
1126 FD_SET(fd, &input);
1127 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
1128 {
1129 fprintf(stderr, "ERROR: %.3f select() for %d failed: %s\n", run_time(),
1130 fd, strerror(errno));
1131 break;
1132 }
1133
1134 if (FD_ISSET(fd, &input))
1135 read_snmp_response(fd);
1136 else
1137 break;
1138 }
1139
1140 /*
1141 * Finally, probe all of the printers we discovered to see how they are
1142 * connected...
1143 */
1144
1145 for (device = (snmp_cache_t *)cupsArrayFirst(Devices);
1146 device;
1147 device = (snmp_cache_t *)cupsArrayNext(Devices))
1148 if (MaxRunTime > 0 && run_time() >= MaxRunTime)
1149 break;
1150 else if (!device->uri)
1151 probe_device(device);
1152
1153 debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
1154 }
1155
1156
1157 /*
1158 * 'try_connect()' - Try connecting on a port...
1159 */
1160
1161 static int /* O - 0 on success or -1 on error */
1162 try_connect(http_addr_t *addr, /* I - Socket address */
1163 const char *addrname, /* I - Hostname or IP address */
1164 int port) /* I - Port number */
1165 {
1166 int fd; /* Socket */
1167 int status; /* Connection status */
1168
1169
1170 debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
1171 port == 515 ? "lpd" : "socket", addrname, port);
1172
1173 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
1174 {
1175 fprintf(stderr, "ERROR: Unable to create socket: %s\n",
1176 strerror(errno));
1177 return (-1);
1178 }
1179
1180 addr->ipv4.sin_port = htons(port);
1181
1182 alarm(1);
1183
1184 status = connect(fd, (void *)addr, httpAddrLength(addr));
1185
1186 close(fd);
1187 alarm(0);
1188
1189 return (status);
1190 }
1191
1192
1193 /*
1194 * 'update_cache()' - Update a cached device...
1195 */
1196
1197 static void
1198 update_cache(snmp_cache_t *device, /* I - Device */
1199 const char *uri, /* I - Device URI */
1200 const char *id, /* I - Device ID */
1201 const char *make_model) /* I - Device make and model */
1202 {
1203 if (device->uri)
1204 free(device->uri);
1205
1206 device->uri = strdup(uri);
1207
1208 if (id)
1209 {
1210 if (device->id)
1211 free(device->id);
1212
1213 device->id = strdup(id);
1214 }
1215
1216 if (make_model)
1217 {
1218 if (device->make_and_model)
1219 free(device->make_and_model);
1220
1221 device->make_and_model = strdup(make_model);
1222 }
1223
1224 list_device(device);
1225 }
1226
1227
1228 /*
1229 * End of "$Id: snmp.c 6649 2007-07-11 21:46:42Z mike $".
1230 */