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