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