]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/snmp.c
62fc54327e827af578b78d65b8a6f6fd51859019
[thirdparty/cups.git] / backend / snmp.c
1 /*
2 * "$Id$"
3 *
4 * SNMP discovery backend for CUPS.
5 *
6 * Copyright 2007-2012 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 ipv4, int ipv6);
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 ipv4, /* SNMP IPv4 socket */
206 ipv6; /* SNMP IPv6 socket */
207 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
208 struct sigaction action; /* Actions for POSIX signals */
209 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
210
211
212 /*
213 * Check command-line options...
214 */
215
216 if (argc > 2)
217 {
218 _cupsLangPuts(stderr, _("Usage: snmp [host-or-ip-address]"));
219 return (1);
220 }
221
222 /*
223 * Set the password callback for IPP operations...
224 */
225
226 cupsSetPasswordCB(password_cb);
227
228 /*
229 * Catch SIGALRM signals...
230 */
231
232 #ifdef HAVE_SIGSET
233 sigset(SIGALRM, alarm_handler);
234 #elif defined(HAVE_SIGACTION)
235 memset(&action, 0, sizeof(action));
236
237 sigemptyset(&action.sa_mask);
238 sigaddset(&action.sa_mask, SIGALRM);
239 action.sa_handler = alarm_handler;
240 sigaction(SIGALRM, &action, NULL);
241 #else
242 signal(SIGALRM, alarm_handler);
243 #endif /* HAVE_SIGSET */
244
245 /*
246 * Open the SNMP socket...
247 */
248
249 if ((ipv4 = _cupsSNMPOpen(AF_INET)) < 0)
250 return (1);
251
252 #ifdef AF_INET6
253 if ((ipv6 = _cupsSNMPOpen(AF_INET6)) < 0)
254 perror("DEBUG: Unable to create IPv6 socket");
255 #else
256 ipv6 = -1;
257 #endif /* AF_INET6 */
258
259 /*
260 * Read the configuration file and any cache data...
261 */
262
263 read_snmp_conf(argv[1]);
264
265 _cupsSNMPSetDebug(DebugLevel);
266
267 Devices = cupsArrayNew((cups_array_func_t)compare_cache, NULL);
268
269 /*
270 * Scan for devices...
271 */
272
273 scan_devices(ipv4, ipv6);
274
275 /*
276 * Close, free, and return with no errors...
277 */
278
279 _cupsSNMPClose(ipv4);
280 if (ipv6 >= 0)
281 _cupsSNMPClose(ipv6);
282
283 free_array(Addresses);
284 free_array(Communities);
285 free_cache();
286
287 return (0);
288 }
289
290
291 /*
292 * 'add_array()' - Add a string to an array.
293 */
294
295 static char * /* O - New string */
296 add_array(cups_array_t *a, /* I - Array */
297 const char *s) /* I - String to add */
298 {
299 char *dups; /* New string */
300
301
302 dups = strdup(s);
303
304 cupsArrayAdd(a, dups);
305
306 return (dups);
307 }
308
309
310 /*
311 * 'add_cache()' - Add a cached device...
312 */
313
314 static void
315 add_cache(http_addr_t *addr, /* I - Device IP address */
316 const char *addrname, /* I - IP address or name string */
317 const char *uri, /* I - Device URI */
318 const char *id, /* I - 1284 device ID */
319 const char *make_and_model) /* I - Make and model */
320 {
321 snmp_cache_t *temp; /* New device entry */
322
323
324 debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
325 "id=\"%s\", make_and_model=\"%s\")\n",
326 addr, addrname, uri ? uri : "(null)", id ? id : "(null)",
327 make_and_model ? make_and_model : "(null)");
328
329 temp = calloc(1, sizeof(snmp_cache_t));
330 memcpy(&(temp->address), addr, sizeof(temp->address));
331
332 temp->addrname = strdup(addrname);
333
334 if (uri)
335 temp->uri = strdup(uri);
336
337 if (id)
338 temp->id = strdup(id);
339
340 if (make_and_model)
341 temp->make_and_model = strdup(make_and_model);
342
343 cupsArrayAdd(Devices, temp);
344
345 if (uri)
346 list_device(temp);
347 }
348
349
350 /*
351 * 'add_device_uri()' - Add a device URI to the cache.
352 *
353 * The value string is modified (chopped up) as needed.
354 */
355
356 static device_uri_t * /* O - Device URI */
357 add_device_uri(char *value) /* I - Value from snmp.conf */
358 {
359 device_uri_t *device_uri; /* Device URI */
360 char *start; /* Start of value */
361
362
363 /*
364 * Allocate memory as needed...
365 */
366
367 if (!DeviceURIs)
368 DeviceURIs = cupsArrayNew(NULL, NULL);
369
370 if (!DeviceURIs)
371 return (NULL);
372
373 if ((device_uri = calloc(1, sizeof(device_uri_t))) == NULL)
374 return (NULL);
375
376 if ((device_uri->uris = cupsArrayNew(NULL, NULL)) == NULL)
377 {
378 free(device_uri);
379 return (NULL);
380 }
381
382 /*
383 * Scan the value string for the regular expression and URI(s)...
384 */
385
386 value ++; /* Skip leading " */
387
388 for (start = value; *value && *value != '\"'; value ++)
389 if (*value == '\\' && value[1])
390 _cups_strcpy(value, value + 1);
391
392 if (!*value)
393 {
394 fputs("ERROR: Missing end quote for DeviceURI!\n", stderr);
395
396 cupsArrayDelete(device_uri->uris);
397 free(device_uri);
398
399 return (NULL);
400 }
401
402 *value++ = '\0';
403
404 if (regcomp(&(device_uri->re), start, REG_EXTENDED | REG_ICASE))
405 {
406 fputs("ERROR: Bad regular expression for DeviceURI!\n", stderr);
407
408 cupsArrayDelete(device_uri->uris);
409 free(device_uri);
410
411 return (NULL);
412 }
413
414 while (*value)
415 {
416 while (isspace(*value & 255))
417 value ++;
418
419 if (!*value)
420 break;
421
422 for (start = value; *value && !isspace(*value & 255); value ++);
423
424 if (*value)
425 *value++ = '\0';
426
427 cupsArrayAdd(device_uri->uris, strdup(start));
428 }
429
430 /*
431 * Add the device URI to the list and return it...
432 */
433
434 cupsArrayAdd(DeviceURIs, device_uri);
435
436 return (device_uri);
437 }
438
439
440 /*
441 * 'alarm_handler()' - Handle alarm signals...
442 */
443
444 static void
445 alarm_handler(int sig) /* I - Signal number */
446 {
447 /*
448 * Do nothing...
449 */
450
451 (void)sig;
452
453 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
454 signal(SIGALRM, alarm_handler);
455 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
456
457 if (DebugLevel)
458 write(2, "DEBUG: ALARM!\n", 14);
459 }
460
461
462 /*
463 * 'compare_cache()' - Compare two cache entries.
464 */
465
466 static int /* O - Result of comparison */
467 compare_cache(snmp_cache_t *a, /* I - First cache entry */
468 snmp_cache_t *b) /* I - Second cache entry */
469 {
470 return (_cups_strcasecmp(a->addrname, b->addrname));
471 }
472
473
474 /*
475 * 'debug_printf()' - Display some debugging information.
476 */
477
478 static void
479 debug_printf(const char *format, /* I - Printf-style format string */
480 ...) /* I - Additional arguments as needed */
481 {
482 va_list ap; /* Pointer to arguments */
483
484
485 if (!DebugLevel)
486 return;
487
488 va_start(ap, format);
489 vfprintf(stderr, format, ap);
490 va_end(ap);
491 }
492
493
494 /*
495 * 'fix_make_model()' - Fix common problems in the make-and-model string.
496 */
497
498 static void
499 fix_make_model(
500 char *make_model, /* I - New make-and-model string */
501 const char *old_make_model, /* I - Old make-and-model string */
502 int make_model_size) /* I - Size of new string buffer */
503 {
504 char *mmptr; /* Pointer into make-and-model string */
505
506
507 /*
508 * Fix some common problems with the make-and-model string so
509 * that printer driver detection works better...
510 */
511
512 if (!_cups_strncasecmp(old_make_model, "Hewlett-Packard", 15))
513 {
514 /*
515 * Strip leading Hewlett-Packard and hp prefixes and replace
516 * with a single HP manufacturer prefix...
517 */
518
519 mmptr = (char *)old_make_model + 15;
520
521 while (isspace(*mmptr & 255))
522 mmptr ++;
523
524 if (!_cups_strncasecmp(mmptr, "hp", 2))
525 {
526 mmptr += 2;
527
528 while (isspace(*mmptr & 255))
529 mmptr ++;
530 }
531
532 make_model[0] = 'H';
533 make_model[1] = 'P';
534 make_model[2] = ' ';
535 strlcpy(make_model + 3, mmptr, make_model_size - 3);
536 }
537 else if (!_cups_strncasecmp(old_make_model, "deskjet", 7))
538 snprintf(make_model, make_model_size, "HP DeskJet%s", old_make_model + 7);
539 else if (!_cups_strncasecmp(old_make_model, "officejet", 9))
540 snprintf(make_model, make_model_size, "HP OfficeJet%s", old_make_model + 9);
541 else if (!_cups_strncasecmp(old_make_model, "stylus_pro_", 11))
542 snprintf(make_model, make_model_size, "EPSON Stylus Pro %s",
543 old_make_model + 11);
544 else
545 strlcpy(make_model, old_make_model, make_model_size);
546
547 if ((mmptr = strstr(make_model, ", Inc.,")) != NULL)
548 {
549 /*
550 * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560"
551 * becomes "Tektronix Phaser 560"...
552 */
553
554 _cups_strcpy(mmptr, mmptr + 7);
555 }
556
557 if ((mmptr = strstr(make_model, " Network")) != NULL)
558 {
559 /*
560 * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025
561 * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"...
562 */
563
564 *mmptr = '\0';
565 }
566
567 if ((mmptr = strchr(make_model, ',')) != NULL)
568 {
569 /*
570 * Drop anything after a trailing comma...
571 */
572
573 *mmptr = '\0';
574 }
575 }
576
577
578 /*
579 * 'free_array()' - Free an array of strings.
580 */
581
582 static void
583 free_array(cups_array_t *a) /* I - Array */
584 {
585 char *s; /* Current string */
586
587
588 for (s = (char *)cupsArrayFirst(a); s; s = (char *)cupsArrayNext(a))
589 free(s);
590
591 cupsArrayDelete(a);
592 }
593
594
595 /*
596 * 'free_cache()' - Free the array of cached devices.
597 */
598
599 static void
600 free_cache(void)
601 {
602 snmp_cache_t *cache; /* Cached device */
603
604
605 for (cache = (snmp_cache_t *)cupsArrayFirst(Devices);
606 cache;
607 cache = (snmp_cache_t *)cupsArrayNext(Devices))
608 {
609 free(cache->addrname);
610
611 if (cache->uri)
612 free(cache->uri);
613
614 if (cache->id)
615 free(cache->id);
616
617 if (cache->make_and_model)
618 free(cache->make_and_model);
619
620 free(cache);
621 }
622
623 cupsArrayDelete(Devices);
624 Devices = NULL;
625 }
626
627
628 /*
629 * 'get_interface_addresses()' - Get the broadcast address(es) associated
630 * with an interface.
631 */
632
633 static http_addrlist_t * /* O - List of addresses */
634 get_interface_addresses(
635 const char *ifname) /* I - Interface name */
636 {
637 struct ifaddrs *addrs, /* Interface address list */
638 *addr; /* Current interface address */
639 http_addrlist_t *first, /* First address in list */
640 *last, /* Last address in list */
641 *current; /* Current address */
642
643
644 if (getifaddrs(&addrs) < 0)
645 return (NULL);
646
647 for (addr = addrs, first = NULL, last = NULL; addr; addr = addr->ifa_next)
648 if ((addr->ifa_flags & IFF_BROADCAST) && addr->ifa_broadaddr &&
649 addr->ifa_broadaddr->sa_family == AF_INET &&
650 (!ifname || !strcmp(ifname, addr->ifa_name)))
651 {
652 current = calloc(1, sizeof(http_addrlist_t));
653
654 memcpy(&(current->addr), addr->ifa_broadaddr,
655 sizeof(struct sockaddr_in));
656
657 if (!last)
658 first = current;
659 else
660 last->next = current;
661
662 last = current;
663 }
664
665 freeifaddrs(addrs);
666
667 return (first);
668 }
669
670
671 /*
672 * 'list_device()' - List a device we found...
673 */
674
675 static void
676 list_device(snmp_cache_t *cache) /* I - Cached device */
677 {
678 if (cache->uri)
679 cupsBackendReport("network", cache->uri, cache->make_and_model,
680 cache->info, cache->id, cache->location);
681 }
682
683
684 /*
685 * 'password_cb()' - Handle authentication requests.
686 *
687 * All we do right now is return NULL, indicating that no authentication
688 * is possible.
689 */
690
691 static const char * /* O - Password (NULL) */
692 password_cb(const char *prompt) /* I - Prompt message */
693 {
694 (void)prompt; /* Anti-compiler-warning-code */
695
696 return (NULL);
697 }
698
699
700 /*
701 * 'probe_device()' - Probe a device to discover whether it is a printer.
702 *
703 * TODO: Try using the Port Monitor MIB to discover the correct protocol
704 * to use - first need a commercially-available printer that supports
705 * it, though...
706 */
707
708 static void
709 probe_device(snmp_cache_t *device) /* I - Device */
710 {
711 char uri[1024], /* Full device URI */
712 *uriptr, /* Pointer into URI */
713 *format; /* Format string for device */
714 device_uri_t *device_uri; /* Current DeviceURI match */
715
716
717 debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device->addrname);
718
719 #ifdef __APPLE__
720 /*
721 * If the printer supports Bonjour/mDNS, don't report it from the SNMP backend.
722 */
723
724 if (!try_connect(&(device->address), device->addrname, 5353))
725 {
726 debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device->addrname);
727 return;
728 }
729 #endif /* __APPLE__ */
730
731 /*
732 * Lookup the device in the match table...
733 */
734
735 for (device_uri = (device_uri_t *)cupsArrayFirst(DeviceURIs);
736 device_uri;
737 device_uri = (device_uri_t *)cupsArrayNext(DeviceURIs))
738 if (device->make_and_model &&
739 !regexec(&(device_uri->re), device->make_and_model, 0, NULL, 0))
740 {
741 /*
742 * Found a match, add the URIs...
743 */
744
745 for (format = (char *)cupsArrayFirst(device_uri->uris);
746 format;
747 format = (char *)cupsArrayNext(device_uri->uris))
748 {
749 for (uriptr = uri; *format && uriptr < (uri + sizeof(uri) - 1);)
750 if (*format == '%' && format[1] == 's')
751 {
752 /*
753 * Insert hostname/address...
754 */
755
756 strlcpy(uriptr, device->addrname, sizeof(uri) - (uriptr - uri));
757 uriptr += strlen(uriptr);
758 format += 2;
759 }
760 else
761 *uriptr++ = *format++;
762
763 *uriptr = '\0';
764
765 update_cache(device, uri, NULL, NULL);
766 }
767
768 return;
769 }
770
771 /*
772 * Then try the standard ports...
773 */
774
775 if (!try_connect(&(device->address), device->addrname, 9100))
776 {
777 debug_printf("DEBUG: %s supports AppSocket!\n", device->addrname);
778
779 snprintf(uri, sizeof(uri), "socket://%s", device->addrname);
780 update_cache(device, uri, NULL, NULL);
781 }
782 else if (!try_connect(&(device->address), device->addrname, 515))
783 {
784 debug_printf("DEBUG: %s supports LPD!\n", device->addrname);
785
786 snprintf(uri, sizeof(uri), "lpd://%s/", device->addrname);
787 update_cache(device, uri, NULL, NULL);
788 }
789 }
790
791
792 /*
793 * 'read_snmp_conf()' - Read the snmp.conf file.
794 */
795
796 static void
797 read_snmp_conf(const char *address) /* I - Single address to probe */
798 {
799 cups_file_t *fp; /* File pointer */
800 char filename[1024], /* Filename */
801 line[1024], /* Line from file */
802 *value; /* Value on line */
803 int linenum; /* Line number */
804 const char *cups_serverroot; /* CUPS_SERVERROOT env var */
805 const char *debug; /* CUPS_DEBUG_LEVEL env var */
806 const char *runtime; /* CUPS_MAX_RUN_TIME env var */
807
808
809 /*
810 * Initialize the global address and community lists...
811 */
812
813 Addresses = cupsArrayNew(NULL, NULL);
814 Communities = cupsArrayNew(NULL, NULL);
815
816 if (address)
817 add_array(Addresses, address);
818
819 if ((debug = getenv("CUPS_DEBUG_LEVEL")) != NULL)
820 DebugLevel = atoi(debug);
821
822 if ((runtime = getenv("CUPS_MAX_RUN_TIME")) != NULL)
823 MaxRunTime = atoi(runtime);
824
825 /*
826 * Find the snmp.conf file...
827 */
828
829 if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
830 cups_serverroot = CUPS_SERVERROOT;
831
832 snprintf(filename, sizeof(filename), "%s/snmp.conf", cups_serverroot);
833
834 if ((fp = cupsFileOpen(filename, "r")) != NULL)
835 {
836 /*
837 * Read the snmp.conf file...
838 */
839
840 linenum = 0;
841
842 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
843 {
844 if (!value)
845 fprintf(stderr, "ERROR: Missing value on line %d of %s!\n", linenum,
846 filename);
847 else if (!_cups_strcasecmp(line, "Address"))
848 {
849 if (!address)
850 add_array(Addresses, value);
851 }
852 else if (!_cups_strcasecmp(line, "Community"))
853 add_array(Communities, value);
854 else if (!_cups_strcasecmp(line, "DebugLevel"))
855 DebugLevel = atoi(value);
856 else if (!_cups_strcasecmp(line, "DeviceURI"))
857 {
858 if (*value != '\"')
859 fprintf(stderr,
860 "ERROR: Missing double quote for regular expression on "
861 "line %d of %s!\n", linenum, filename);
862 else
863 add_device_uri(value);
864 }
865 else if (!_cups_strcasecmp(line, "HostNameLookups"))
866 HostNameLookups = !_cups_strcasecmp(value, "on") ||
867 !_cups_strcasecmp(value, "yes") ||
868 !_cups_strcasecmp(value, "true") ||
869 !_cups_strcasecmp(value, "double");
870 else if (!_cups_strcasecmp(line, "MaxRunTime"))
871 MaxRunTime = atoi(value);
872 else
873 fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n",
874 line, linenum, filename);
875 }
876
877 cupsFileClose(fp);
878 }
879
880 /*
881 * Use defaults if parameters are undefined...
882 */
883
884 if (cupsArrayCount(Addresses) == 0)
885 {
886 /*
887 * If we have no addresses, exit immediately...
888 */
889
890 fprintf(stderr,
891 "DEBUG: No address specified and no Address line in %s...\n",
892 filename);
893 exit(0);
894 }
895
896 if (cupsArrayCount(Communities) == 0)
897 {
898 fputs("INFO: Using default SNMP Community public\n", stderr);
899 add_array(Communities, "public");
900 }
901 }
902
903
904 /*
905 * 'read_snmp_response()' - Read and parse a SNMP response...
906 */
907
908 static void
909 read_snmp_response(int fd) /* I - SNMP socket file descriptor */
910 {
911 char addrname[256]; /* Source address name */
912 cups_snmp_t packet; /* Decoded packet */
913 snmp_cache_t key, /* Search key */
914 *device; /* Matching device */
915
916
917 /*
918 * Read the response data...
919 */
920
921 if (!_cupsSNMPRead(fd, &packet, -1.0))
922 {
923 fprintf(stderr, "ERROR: Unable to read data from socket: %s\n",
924 strerror(errno));
925 return;
926 }
927
928 if (HostNameLookups)
929 httpAddrLookup(&(packet.address), addrname, sizeof(addrname));
930 else
931 httpAddrString(&(packet.address), addrname, sizeof(addrname));
932
933 debug_printf("DEBUG: %.3f Received data from %s...\n", run_time(), addrname);
934
935 /*
936 * Look for the response status code in the SNMP message header...
937 */
938
939 if (packet.error)
940 {
941 fprintf(stderr, "ERROR: Bad SNMP packet from %s: %s\n", addrname,
942 packet.error);
943
944 return;
945 }
946
947 debug_printf("DEBUG: community=\"%s\"\n", packet.community);
948 debug_printf("DEBUG: request-id=%d\n", packet.request_id);
949 debug_printf("DEBUG: error-status=%d\n", packet.error_status);
950
951 if (packet.error_status && packet.request_id != DEVICE_TYPE)
952 return;
953
954 /*
955 * Find a matching device in the cache...
956 */
957
958 key.addrname = addrname;
959 device = (snmp_cache_t *)cupsArrayFind(Devices, &key);
960
961 /*
962 * Process the message...
963 */
964
965 switch (packet.request_id)
966 {
967 case DEVICE_TYPE :
968 /*
969 * Got the device type response...
970 */
971
972 if (device)
973 {
974 debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
975 addrname);
976 return;
977 }
978
979 /*
980 * Add the device and request the device data...
981 */
982
983 add_cache(&(packet.address), addrname, NULL, NULL, NULL);
984
985 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
986 packet.community, CUPS_ASN1_GET_REQUEST,
987 DEVICE_DESCRIPTION, DescriptionOID);
988 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
989 packet.community, CUPS_ASN1_GET_REQUEST,
990 DEVICE_ID, DeviceIdOID);
991 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
992 packet.community, CUPS_ASN1_GET_REQUEST,
993 DEVICE_URI, UriOID);
994 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
995 packet.community, CUPS_ASN1_GET_REQUEST,
996 DEVICE_LOCATION, LocationOID);
997 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
998 packet.community, CUPS_ASN1_GET_REQUEST,
999 DEVICE_PRODUCT, LexmarkProductOID);
1000 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
1001 packet.community, CUPS_ASN1_GET_REQUEST,
1002 DEVICE_PRODUCT, LexmarkProductOID2);
1003 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
1004 packet.community, CUPS_ASN1_GET_REQUEST,
1005 DEVICE_ID, LexmarkDeviceIdOID);
1006 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
1007 packet.community, CUPS_ASN1_GET_REQUEST,
1008 DEVICE_PRODUCT, XeroxProductOID);
1009 break;
1010
1011 case DEVICE_DESCRIPTION :
1012 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING)
1013 {
1014 /*
1015 * Update an existing cache entry...
1016 */
1017
1018 char make_model[256]; /* Make and model */
1019
1020
1021 if (strchr((char *)packet.object_value.string.bytes, ':') &&
1022 strchr((char *)packet.object_value.string.bytes, ';'))
1023 {
1024 /*
1025 * Description is the IEEE-1284 device ID...
1026 */
1027
1028 char *ptr; /* Pointer into device ID */
1029
1030 for (ptr = (char *)packet.object_value.string.bytes; *ptr; ptr ++)
1031 if (*ptr == '\n')
1032 *ptr = ';'; /* A lot of bad printers put a newline */
1033 if (!device->id)
1034 device->id = strdup((char *)packet.object_value.string.bytes);
1035
1036 backendGetMakeModel((char *)packet.object_value.string.bytes,
1037 make_model, sizeof(make_model));
1038
1039 if (device->info)
1040 free(device->info);
1041
1042 device->info = strdup(make_model);
1043 }
1044 else
1045 {
1046 /*
1047 * Description is plain text...
1048 */
1049
1050 fix_make_model(make_model, (char *)packet.object_value.string.bytes,
1051 sizeof(make_model));
1052
1053 if (device->info)
1054 free(device->info);
1055
1056 device->info = strdup((char *)packet.object_value.string.bytes);
1057 }
1058
1059 if (!device->make_and_model)
1060 device->make_and_model = strdup(make_model);
1061 }
1062 break;
1063
1064 case DEVICE_ID :
1065 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
1066 (!device->id ||
1067 strlen(device->id) < packet.object_value.string.num_bytes))
1068 {
1069 /*
1070 * Update an existing cache entry...
1071 */
1072
1073 char make_model[256]; /* Make and model */
1074 char *ptr; /* Pointer into device ID */
1075
1076 for (ptr = (char *)packet.object_value.string.bytes; *ptr; ptr ++)
1077 if (*ptr == '\n')
1078 *ptr = ';'; /* A lot of bad printers put a newline */
1079 if (device->id)
1080 free(device->id);
1081
1082 device->id = strdup((char *)packet.object_value.string.bytes);
1083
1084 /*
1085 * Convert the ID to a make and model string...
1086 */
1087
1088 backendGetMakeModel((char *)packet.object_value.string.bytes,
1089 make_model, sizeof(make_model));
1090 if (device->make_and_model)
1091 free(device->make_and_model);
1092
1093 device->make_and_model = strdup(make_model);
1094 }
1095 break;
1096
1097 case DEVICE_LOCATION :
1098 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
1099 !device->location)
1100 device->location = strdup((char *)packet.object_value.string.bytes);
1101 break;
1102
1103 case DEVICE_PRODUCT :
1104 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
1105 !device->id)
1106 {
1107 /*
1108 * Update an existing cache entry...
1109 */
1110
1111 if (!device->info)
1112 device->info = strdup((char *)packet.object_value.string.bytes);
1113
1114 if (device->make_and_model)
1115 free(device->make_and_model);
1116
1117 device->make_and_model = strdup((char *)packet.object_value.string.bytes);
1118 }
1119 break;
1120
1121 case DEVICE_URI :
1122 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
1123 !device->uri && packet.object_value.string.num_bytes > 3)
1124 {
1125 /*
1126 * Update an existing cache entry...
1127 */
1128
1129 char scheme[32], /* URI scheme */
1130 userpass[256], /* Username:password in URI */
1131 hostname[256], /* Hostname in URI */
1132 resource[1024]; /* Resource path in URI */
1133 int port; /* Port number in URI */
1134
1135 if (!strncmp((char *)packet.object_value.string.bytes, "lpr:", 4))
1136 {
1137 /*
1138 * We want "lpd://..." for the URI...
1139 */
1140
1141 packet.object_value.string.bytes[2] = 'd';
1142 }
1143
1144 if (httpSeparateURI(HTTP_URI_CODING_ALL,
1145 (char *)packet.object_value.string.bytes,
1146 scheme, sizeof(scheme),
1147 userpass, sizeof(userpass),
1148 hostname, sizeof(hostname), &port,
1149 resource, sizeof(resource)) >= HTTP_URI_OK)
1150 device->uri = strdup((char *)packet.object_value.string.bytes);
1151 }
1152 break;
1153 }
1154 }
1155
1156
1157 /*
1158 * 'run_time()' - Return the total running time...
1159 */
1160
1161 static double /* O - Number of seconds */
1162 run_time(void)
1163 {
1164 struct timeval curtime; /* Current time */
1165
1166
1167 gettimeofday(&curtime, NULL);
1168
1169 return (curtime.tv_sec - StartTime.tv_sec +
1170 0.000001 * (curtime.tv_usec - StartTime.tv_usec));
1171 }
1172
1173
1174 /*
1175 * 'scan_devices()' - Scan for devices using SNMP.
1176 */
1177
1178 static void
1179 scan_devices(int ipv4, /* I - SNMP IPv4 socket */
1180 int ipv6) /* I - SNMP IPv6 socket */
1181 {
1182 int fd, /* File descriptor for this address */
1183 busy; /* Are we busy processing something? */
1184 char *address, /* Current address */
1185 *community; /* Current community */
1186 fd_set input; /* Input set for select() */
1187 struct timeval timeout; /* Timeout for select() */
1188 time_t endtime; /* End time for scan */
1189 http_addrlist_t *addrs, /* List of addresses */
1190 *addr; /* Current address */
1191 snmp_cache_t *device; /* Current device */
1192 char temp[1024]; /* Temporary address string */
1193
1194
1195 gettimeofday(&StartTime, NULL);
1196
1197 /*
1198 * First send all of the broadcast queries...
1199 */
1200
1201 for (address = (char *)cupsArrayFirst(Addresses);
1202 address;
1203 address = (char *)cupsArrayNext(Addresses))
1204 {
1205 if (!strcmp(address, "@LOCAL"))
1206 addrs = get_interface_addresses(NULL);
1207 else if (!strncmp(address, "@IF(", 4))
1208 {
1209 char ifname[255]; /* Interface name */
1210
1211 strlcpy(ifname, address + 4, sizeof(ifname));
1212 if (ifname[0])
1213 ifname[strlen(ifname) - 1] = '\0';
1214
1215 addrs = get_interface_addresses(ifname);
1216 }
1217 else
1218 addrs = httpAddrGetList(address, AF_UNSPEC, NULL);
1219
1220 if (!addrs)
1221 {
1222 fprintf(stderr, "ERROR: Unable to scan \"%s\"!\n", address);
1223 continue;
1224 }
1225
1226 for (community = (char *)cupsArrayFirst(Communities);
1227 community;
1228 community = (char *)cupsArrayNext(Communities))
1229 {
1230 debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
1231 community, address);
1232
1233 for (addr = addrs; addr; addr = addr->next)
1234 {
1235 #ifdef AF_INET6
1236 if (httpAddrFamily(&(addr->addr)) == AF_INET6)
1237 fd = ipv6;
1238 else
1239 #endif /* AF_INET6 */
1240 fd = ipv4;
1241
1242 debug_printf("DEBUG: Sending get request to %s...\n",
1243 httpAddrString(&(addr->addr), temp, sizeof(temp)));
1244
1245 _cupsSNMPWrite(fd, &(addr->addr), CUPS_SNMP_VERSION_1, community,
1246 CUPS_ASN1_GET_REQUEST, DEVICE_TYPE, DeviceTypeOID);
1247 }
1248 }
1249
1250 httpAddrFreeList(addrs);
1251 }
1252
1253 /*
1254 * Then read any responses that come in over the next 3 seconds...
1255 */
1256
1257 endtime = time(NULL) + MaxRunTime;
1258
1259 FD_ZERO(&input);
1260
1261 while (time(NULL) < endtime)
1262 {
1263 timeout.tv_sec = 2;
1264 timeout.tv_usec = 0;
1265
1266 FD_SET(ipv4, &input);
1267 if (ipv6 >= 0)
1268 FD_SET(ipv6, &input);
1269
1270 fd = ipv4 > ipv6 ? ipv4 : ipv6;
1271 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
1272 {
1273 fprintf(stderr, "ERROR: %.3f select() for %d/%d failed: %s\n", run_time(),
1274 ipv4, ipv6, strerror(errno));
1275 break;
1276 }
1277
1278 busy = 0;
1279
1280 if (FD_ISSET(ipv4, &input))
1281 {
1282 read_snmp_response(ipv4);
1283 busy = 1;
1284 }
1285
1286 if (ipv6 >= 0 && FD_ISSET(ipv6, &input))
1287 {
1288 read_snmp_response(ipv6);
1289 busy = 1;
1290 }
1291
1292 if (!busy)
1293 {
1294 /*
1295 * List devices with complete information...
1296 */
1297
1298 int sent_something = 0;
1299
1300 for (device = (snmp_cache_t *)cupsArrayFirst(Devices);
1301 device;
1302 device = (snmp_cache_t *)cupsArrayNext(Devices))
1303 if (!device->sent && device->info && device->make_and_model)
1304 {
1305 if (device->uri)
1306 list_device(device);
1307 else
1308 probe_device(device);
1309
1310 device->sent = sent_something = 1;
1311 }
1312
1313 if (!sent_something)
1314 break;
1315 }
1316 }
1317
1318 debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
1319 }
1320
1321
1322 /*
1323 * 'try_connect()' - Try connecting on a port...
1324 */
1325
1326 static int /* O - 0 on success or -1 on error */
1327 try_connect(http_addr_t *addr, /* I - Socket address */
1328 const char *addrname, /* I - Hostname or IP address */
1329 int port) /* I - Port number */
1330 {
1331 int fd; /* Socket */
1332 int status; /* Connection status */
1333
1334
1335 debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
1336 port == 515 ? "lpd" : "socket", addrname, port);
1337
1338 if ((fd = socket(httpAddrFamily(addr), SOCK_STREAM, 0)) < 0)
1339 {
1340 fprintf(stderr, "ERROR: Unable to create socket: %s\n",
1341 strerror(errno));
1342 return (-1);
1343 }
1344
1345 _httpAddrSetPort(addr, port);
1346
1347 alarm(1);
1348
1349 status = connect(fd, (void *)addr, httpAddrLength(addr));
1350
1351 close(fd);
1352 alarm(0);
1353
1354 return (status);
1355 }
1356
1357
1358 /*
1359 * 'update_cache()' - Update a cached device...
1360 */
1361
1362 static void
1363 update_cache(snmp_cache_t *device, /* I - Device */
1364 const char *uri, /* I - Device URI */
1365 const char *id, /* I - Device ID */
1366 const char *make_model) /* I - Device make and model */
1367 {
1368 if (device->uri)
1369 free(device->uri);
1370
1371 device->uri = strdup(uri);
1372
1373 if (id)
1374 {
1375 if (device->id)
1376 free(device->id);
1377
1378 device->id = strdup(id);
1379 }
1380
1381 if (make_model)
1382 {
1383 if (device->make_and_model)
1384 free(device->make_and_model);
1385
1386 device->make_and_model = strdup(make_model);
1387 }
1388
1389 list_device(device);
1390 }
1391
1392
1393 /*
1394 * End of "$Id$".
1395 */