2 * "$Id: snmp.c 6649 2007-07-11 21:46:42Z mike $"
4 * SNMP discovery backend for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 2006-2007 by Easy Software Products, all rights reserved.
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/".
15 * This file is subject to the Apple OS-Developed Software exception.
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
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
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
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...
45 * Include necessary headers.
48 #include "backend-private.h"
49 #include <cups/array.h>
50 #include <cups/file.h>
51 #include <cups/http-private.h>
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).
62 * The current focus is on printers with internal network cards, although
63 * the code also works with many external print servers as well.
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
74 * DeviceURI "regex pattern" uri
79 * The default is to use:
87 * This backend is known to work with the following network printers and
90 * Axis OfficeBasic, 5400, 5600
100 * It does not currently work with:
107 * (for all of these, they do not support the Host MIB)
114 typedef struct device_uri_s
/**** DeviceURI values ****/
116 regex_t re
; /* Regular expression to match */
117 cups_array_t
*uris
; /* URIs */
120 typedef struct snmp_cache_s
/**** SNMP scan cache ****/
122 http_addr_t address
; /* Address of device */
123 char *addrname
, /* Name of device */
124 *uri
, /* device-uri */
126 *info
, /* device-info */
127 *make_and_model
; /* device-make-and-model */
132 * Private CUPS API to set the last error...
135 extern void _cupsSetError(ipp_status_t status
, const char *message
);
142 static char *add_array(cups_array_t
*a
, const char *s
);
143 static void add_cache(http_addr_t
*addr
, const char *addrname
,
144 const char *uri
, const char *id
,
145 const char *make_and_model
);
146 static device_uri_t
*add_device_uri(char *value
);
147 static void alarm_handler(int sig
);
148 static int compare_cache(snmp_cache_t
*a
, snmp_cache_t
*b
);
149 static void debug_printf(const char *format
, ...);
150 static void fix_make_model(char *make_model
,
151 const char *old_make_model
,
152 int make_model_size
);
153 static void free_array(cups_array_t
*a
);
154 static void free_cache(void);
155 static http_addrlist_t
*get_interface_addresses(const char *ifname
);
156 static void list_device(snmp_cache_t
*cache
);
157 static const char *password_cb(const char *prompt
);
158 static void probe_device(snmp_cache_t
*device
);
159 static void read_snmp_conf(const char *address
);
160 static void read_snmp_response(int fd
);
161 static double run_time(void);
162 static void scan_devices(int fd
);
163 static int try_connect(http_addr_t
*addr
, const char *addrname
,
165 static void update_cache(snmp_cache_t
*device
, const char *uri
,
166 const char *id
, const char *make_model
);
173 static cups_array_t
*Addresses
= NULL
;
174 static cups_array_t
*Communities
= NULL
;
175 static cups_array_t
*Devices
= NULL
;
176 static int DebugLevel
= 0;
177 static const int DeviceDescOID
[] = { CUPS_OID_hrDeviceDescr
, 1, -1 };
178 static unsigned DeviceDescRequest
;
179 static const int DeviceTypeOID
[] = { CUPS_OID_hrDeviceType
, 1, -1 };
180 static unsigned DeviceTypeRequest
;
181 static const int DeviceIdOID
[] = { CUPS_OID_ppmPrinterIEEE1284DeviceId
, 1, -1 };
182 static unsigned DeviceIdRequest
;
183 static const int DeviceUriOID
[] = { CUPS_OID_ppmPortServiceNameOrURI
, 1, 1, -1 };
184 static unsigned DeviceUriRequest
;
185 static cups_array_t
*DeviceURIs
= NULL
;
186 static int HostNameLookups
= 0;
187 static int MaxRunTime
= 120;
188 static struct timeval StartTime
;
192 * 'main()' - Discover printers via SNMP.
195 int /* O - Exit status */
196 main(int argc
, /* I - Number of command-line arguments (6 or 7) */
197 char *argv
[]) /* I - Command-line arguments */
199 int fd
; /* SNMP socket */
200 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
201 struct sigaction action
; /* Actions for POSIX signals */
202 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
206 * Check command-line options...
211 fputs(_("Usage: snmp [host-or-ip-address]\n"), stderr
);
216 * Set the password callback for IPP operations...
219 cupsSetPasswordCB(password_cb
);
222 * Catch SIGALRM signals...
226 sigset(SIGALRM
, alarm_handler
);
227 #elif defined(HAVE_SIGACTION)
228 memset(&action
, 0, sizeof(action
));
230 sigemptyset(&action
.sa_mask
);
231 sigaddset(&action
.sa_mask
, SIGALRM
);
232 action
.sa_handler
= alarm_handler
;
233 sigaction(SIGALRM
, &action
, NULL
);
235 signal(SIGALRM
, alarm_handler
);
236 #endif /* HAVE_SIGSET */
239 * Open the SNMP socket...
242 if ((fd
= _cupsSNMPOpen(AF_INET
)) < 0)
246 * Read the configuration file and any cache data...
249 read_snmp_conf(argv
[1]);
251 _cupsSNMPSetDebug(DebugLevel
);
253 Devices
= cupsArrayNew((cups_array_func_t
)compare_cache
, NULL
);
256 * Scan for devices...
262 * Close, free, and return with no errors...
267 free_array(Addresses
);
268 free_array(Communities
);
276 * 'add_array()' - Add a string to an array.
279 static char * /* O - New string */
280 add_array(cups_array_t
*a
, /* I - Array */
281 const char *s
) /* I - String to add */
283 char *dups
; /* New string */
288 cupsArrayAdd(a
, dups
);
295 * 'add_cache()' - Add a cached device...
299 add_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 */
305 snmp_cache_t
*temp
; /* New device entry */
308 debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
309 "id=\"%s\", make_and_model=\"%s\")\n",
310 addr
, addrname
, uri
? uri
: "(null)", id
? id
: "(null)",
311 make_and_model
? make_and_model
: "(null)");
313 temp
= calloc(1, sizeof(snmp_cache_t
));
314 memcpy(&(temp
->address
), addr
, sizeof(temp
->address
));
316 temp
->addrname
= strdup(addrname
);
319 temp
->uri
= strdup(uri
);
322 temp
->id
= strdup(id
);
325 temp
->make_and_model
= strdup(make_and_model
);
327 cupsArrayAdd(Devices
, temp
);
335 * 'add_device_uri()' - Add a device URI to the cache.
337 * The value string is modified (chopped up) as needed.
340 static device_uri_t
* /* O - Device URI */
341 add_device_uri(char *value
) /* I - Value from snmp.conf */
343 device_uri_t
*device_uri
; /* Device URI */
344 char *start
; /* Start of value */
348 * Allocate memory as needed...
352 DeviceURIs
= cupsArrayNew(NULL
, NULL
);
357 if ((device_uri
= calloc(1, sizeof(device_uri_t
))) == NULL
)
360 if ((device_uri
->uris
= cupsArrayNew(NULL
, NULL
)) == NULL
)
367 * Scan the value string for the regular expression and URI(s)...
370 value
++; /* Skip leading " */
372 for (start
= value
; *value
&& *value
!= '\"'; value
++)
373 if (*value
== '\\' && value
[1])
374 _cups_strcpy(value
, value
+ 1);
378 fputs("ERROR: Missing end quote for DeviceURI!\n", stderr
);
380 cupsArrayDelete(device_uri
->uris
);
388 if (regcomp(&(device_uri
->re
), start
, REG_EXTENDED
| REG_ICASE
))
390 fputs("ERROR: Bad regular expression for DeviceURI!\n", stderr
);
392 cupsArrayDelete(device_uri
->uris
);
400 while (isspace(*value
& 255))
406 for (start
= value
; *value
&& !isspace(*value
& 255); value
++);
411 cupsArrayAdd(device_uri
->uris
, strdup(start
));
415 * Add the device URI to the list and return it...
418 cupsArrayAdd(DeviceURIs
, device_uri
);
425 * 'alarm_handler()' - Handle alarm signals...
429 alarm_handler(int sig
) /* I - Signal number */
437 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
438 signal(SIGALRM
, alarm_handler
);
439 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
442 write(2, "DEBUG: ALARM!\n", 14);
447 * 'compare_cache()' - Compare two cache entries.
450 static int /* O - Result of comparison */
451 compare_cache(snmp_cache_t
*a
, /* I - First cache entry */
452 snmp_cache_t
*b
) /* I - Second cache entry */
454 return (strcasecmp(a
->addrname
, b
->addrname
));
459 * 'debug_printf()' - Display some debugging information.
463 debug_printf(const char *format
, /* I - Printf-style format string */
464 ...) /* I - Additional arguments as needed */
466 va_list ap
; /* Pointer to arguments */
472 va_start(ap
, format
);
473 vfprintf(stderr
, format
, ap
);
479 * 'fix_make_model()' - Fix common problems in the make-and-model string.
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 */
488 char *mmptr
; /* Pointer into make-and-model string */
492 * Fix some common problems with the make-and-model string so
493 * that printer driver detection works better...
496 if (!strncasecmp(old_make_model
, "Hewlett-Packard", 15))
499 * Strip leading Hewlett-Packard and hp prefixes and replace
500 * with a single HP manufacturer prefix...
503 mmptr
= (char *)old_make_model
+ 15;
505 while (isspace(*mmptr
& 255))
508 if (!strncasecmp(mmptr
, "hp", 2))
512 while (isspace(*mmptr
& 255))
519 strlcpy(make_model
+ 3, mmptr
, make_model_size
- 3);
521 else if (!strncasecmp(old_make_model
, "deskjet", 7))
522 snprintf(make_model
, make_model_size
, "HP DeskJet%s", old_make_model
+ 7);
523 else if (!strncasecmp(old_make_model
, "officejet", 9))
524 snprintf(make_model
, make_model_size
, "HP OfficeJet%s", old_make_model
+ 9);
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);
529 strlcpy(make_model
, old_make_model
, make_model_size
);
531 if ((mmptr
= strstr(make_model
, ", Inc.,")) != NULL
)
534 * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560"
535 * becomes "Tektronix Phaser 560"...
538 _cups_strcpy(mmptr
, mmptr
+ 7);
541 if ((mmptr
= strstr(make_model
, " Network")) != NULL
)
544 * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025
545 * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"...
551 if ((mmptr
= strchr(make_model
, ',')) != NULL
)
554 * Drop anything after a trailing comma...
563 * 'free_array()' - Free an array of strings.
567 free_array(cups_array_t
*a
) /* I - Array */
569 char *s
; /* Current string */
572 for (s
= (char *)cupsArrayFirst(a
); s
; s
= (char *)cupsArrayNext(a
))
580 * 'free_cache()' - Free the array of cached devices.
586 snmp_cache_t
*cache
; /* Cached device */
589 for (cache
= (snmp_cache_t
*)cupsArrayFirst(Devices
);
591 cache
= (snmp_cache_t
*)cupsArrayNext(Devices
))
593 free(cache
->addrname
);
601 if (cache
->make_and_model
)
602 free(cache
->make_and_model
);
607 cupsArrayDelete(Devices
);
613 * 'get_interface_addresses()' - Get the broadcast address(es) associated
617 static http_addrlist_t
* /* O - List of addresses */
618 get_interface_addresses(
619 const char *ifname
) /* I - Interface name */
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 */
628 if (getifaddrs(&addrs
) < 0)
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
)))
636 current
= calloc(1, sizeof(http_addrlist_t
));
638 memcpy(&(current
->addr
), addr
->ifa_broadaddr
,
639 sizeof(struct sockaddr_in
));
644 last
->next
= current
;
656 * 'list_device()' - List a device we found...
660 list_device(snmp_cache_t
*cache
) /* I - Cached device */
664 printf("network %s \"%s\" \"%s %s\" \"%s\"\n",
666 cache
->make_and_model
? cache
->make_and_model
: "Unknown",
667 cache
->info
? cache
->info
: "Unknown",
669 cache
->id
? cache
->id
: "");
676 * 'password_cb()' - Handle authentication requests.
678 * All we do right now is return NULL, indicating that no authentication
682 static const char * /* O - Password (NULL) */
683 password_cb(const char *prompt
) /* I - Prompt message */
685 (void)prompt
; /* Anti-compiler-warning-code */
692 * 'probe_device()' - Probe a device to discover whether it is a printer.
694 * TODO: Try using the Port Monitor MIB to discover the correct protocol
695 * to use - first need a commercially-available printer that supports
700 probe_device(snmp_cache_t
*device
) /* I - Device */
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 */
708 debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device
->addrname
);
712 * TODO: Try an mDNS query first, and then fallback on direct probes...
715 if (!try_connect(&(device
->address
), device
->addrname
, 5353))
717 debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device
->addrname
);
720 #endif /* __APPLE__ */
723 * Lookup the device in the match table...
726 for (device_uri
= (device_uri_t
*)cupsArrayFirst(DeviceURIs
);
728 device_uri
= (device_uri_t
*)cupsArrayNext(DeviceURIs
))
729 if (device
->make_and_model
&&
730 !regexec(&(device_uri
->re
), device
->make_and_model
, 0, NULL
, 0))
733 * Found a match, add the URIs...
736 for (format
= (char *)cupsArrayFirst(device_uri
->uris
);
738 format
= (char *)cupsArrayNext(device_uri
->uris
))
740 for (uriptr
= uri
; *format
&& uriptr
< (uri
+ sizeof(uri
) - 1);)
741 if (*format
== '%' && format
[1] == 's')
744 * Insert hostname/address...
747 strlcpy(uriptr
, device
->addrname
, sizeof(uri
) - (uriptr
- uri
));
748 uriptr
+= strlen(uriptr
);
752 *uriptr
++ = *format
++;
756 update_cache(device
, uri
, NULL
, NULL
);
763 * Then try the standard ports...
766 if (!try_connect(&(device
->address
), device
->addrname
, 9100))
768 debug_printf("DEBUG: %s supports AppSocket!\n", device
->addrname
);
770 snprintf(uri
, sizeof(uri
), "socket://%s", device
->addrname
);
771 update_cache(device
, uri
, NULL
, NULL
);
773 else if (!try_connect(&(device
->address
), device
->addrname
, 515))
775 debug_printf("DEBUG: %s supports LPD!\n", device
->addrname
);
777 snprintf(uri
, sizeof(uri
), "lpd://%s/", device
->addrname
);
778 update_cache(device
, uri
, NULL
, NULL
);
784 * 'read_snmp_conf()' - Read the snmp.conf file.
788 read_snmp_conf(const char *address
) /* I - Single address to probe */
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 */
797 const char *runtime
; /* CUPS_MAX_RUN_TIME env var */
801 * Initialize the global address and community lists...
804 Addresses
= cupsArrayNew(NULL
, NULL
);
805 Communities
= cupsArrayNew(NULL
, NULL
);
808 add_array(Addresses
, address
);
810 if ((debug
= getenv("CUPS_DEBUG_LEVEL")) != NULL
)
811 DebugLevel
= atoi(debug
);
813 if ((runtime
= getenv("CUPS_MAX_RUN_TIME")) != NULL
)
814 MaxRunTime
= atoi(runtime
);
817 * Find the snmp.conf file...
820 if ((cups_serverroot
= getenv("CUPS_SERVERROOT")) == NULL
)
821 cups_serverroot
= CUPS_SERVERROOT
;
823 snprintf(filename
, sizeof(filename
), "%s/snmp.conf", cups_serverroot
);
825 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
828 * Read the snmp.conf file...
833 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
836 fprintf(stderr
, "ERROR: Missing value on line %d of %s!\n", linenum
,
838 else if (!strcasecmp(line
, "Address"))
841 add_array(Addresses
, value
);
843 else if (!strcasecmp(line
, "Community"))
844 add_array(Communities
, value
);
845 else if (!strcasecmp(line
, "DebugLevel"))
846 DebugLevel
= atoi(value
);
847 else if (!strcasecmp(line
, "DeviceURI"))
851 "ERROR: Missing double quote for regular expression on "
852 "line %d of %s!\n", linenum
, filename
);
854 add_device_uri(value
);
856 else if (!strcasecmp(line
, "HostNameLookups"))
857 HostNameLookups
= !strcasecmp(value
, "on") ||
858 !strcasecmp(value
, "yes") ||
859 !strcasecmp(value
, "true") ||
860 !strcasecmp(value
, "double");
861 else if (!strcasecmp(line
, "MaxRunTime"))
862 MaxRunTime
= atoi(value
);
864 fprintf(stderr
, "ERROR: Unknown directive %s on line %d of %s!\n",
865 line
, linenum
, filename
);
872 * Use defaults if parameters are undefined...
875 if (cupsArrayCount(Addresses
) == 0)
878 * If we have no addresses, exit immediately...
882 "DEBUG: No address specified and no Address line in %s...\n",
887 if (cupsArrayCount(Communities
) == 0)
889 fputs("INFO: Using default SNMP Community public\n", stderr
);
890 add_array(Communities
, "public");
896 * 'read_snmp_response()' - Read and parse a SNMP response...
900 read_snmp_response(int fd
) /* I - SNMP socket file descriptor */
902 char addrname
[256]; /* Source address name */
903 cups_snmp_t packet
; /* Decoded packet */
904 snmp_cache_t key
, /* Search key */
905 *device
; /* Matching device */
909 * Read the response data...
912 if (!_cupsSNMPRead(fd
, &packet
, -1.0))
914 fprintf(stderr
, "ERROR: Unable to read data from socket: %s\n",
920 httpAddrLookup(&(packet
.address
), addrname
, sizeof(addrname
));
922 httpAddrString(&(packet
.address
), addrname
, sizeof(addrname
));
924 debug_printf("DEBUG: %.3f Received data from %s...\n", run_time(), addrname
);
927 * Look for the response status code in the SNMP message header...
932 fprintf(stderr
, "ERROR: Bad SNMP packet from %s: %s\n", addrname
,
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
);
942 if (packet
.error_status
)
946 * Find a matching device in the cache...
949 key
.addrname
= addrname
;
950 device
= (snmp_cache_t
*)cupsArrayFind(Devices
, &key
);
953 * Process the message...
956 if (packet
.request_id
== DeviceTypeRequest
)
959 * Got the device type response...
964 debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
970 * Add the device and request the device description...
973 add_cache(&(packet
.address
), addrname
, NULL
, NULL
, NULL
);
975 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
, packet
.community
,
976 CUPS_ASN1_GET_REQUEST
, DeviceDescRequest
, DeviceDescOID
);
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
);
982 else if (packet
.request_id
== DeviceDescRequest
&&
983 packet
.object_type
== CUPS_ASN1_OCTET_STRING
)
986 * Update an existing cache entry...
989 char make_model
[256]; /* Make and model */
994 debug_printf("DEBUG: Discarding device description for \"%s\"...\n",
999 if (strchr(packet
.object_value
.string
, ':') &&
1000 strchr(packet
.object_value
.string
, ';'))
1003 * Description is the IEEE-1284 device ID...
1007 device
->id
= strdup(packet
.object_value
.string
);
1009 backendGetMakeModel(packet
.object_value
.string
, make_model
,
1010 sizeof(make_model
));
1011 device
->info
= strdup(make_model
);
1016 * Description is plain text...
1019 fix_make_model(make_model
, packet
.object_value
.string
,
1020 sizeof(make_model
));
1022 device
->info
= strdup(packet
.object_value
.string
);
1025 if (!device
->make_and_model
)
1026 device
->make_and_model
= strdup(make_model
);
1029 * List the device now if we have all the info...
1032 if (device
->id
&& device
->info
&& device
->make_and_model
&& device
->uri
)
1033 list_device(device
);
1035 else if (packet
.request_id
== DeviceIdRequest
&&
1036 packet
.object_type
== CUPS_ASN1_OCTET_STRING
)
1039 * Update an existing cache entry...
1042 char make_model
[256]; /* Make and model */
1047 debug_printf("DEBUG: Discarding device ID for \"%s\"...\n",
1055 device
->id
= strdup(packet
.object_value
.string
);
1058 * Convert the ID to a make and model string...
1061 backendGetMakeModel(packet
.object_value
.string
, make_model
,
1062 sizeof(make_model
));
1063 if (device
->make_and_model
)
1064 free(device
->make_and_model
);
1066 device
->make_and_model
= strdup(make_model
);
1069 * List the device now if we have all the info...
1072 if (device
->id
&& device
->info
&& device
->make_and_model
&& device
->uri
)
1073 list_device(device
);
1075 else if (packet
.request_id
== DeviceUriRequest
&&
1076 packet
.object_type
== CUPS_ASN1_OCTET_STRING
)
1079 * Update an existing cache entry...
1084 debug_printf("DEBUG: Discarding device URI for \"%s\"...\n",
1089 if (!strncmp(packet
.object_value
.string
, "lpr:", 4))
1092 * We want "lpd://..." for the URI...
1095 packet
.object_value
.string
[2] = 'd';
1098 device
->uri
= strdup(packet
.object_value
.string
);
1101 * List the device now if we have all the info...
1104 if (device
->id
&& device
->info
&& device
->make_and_model
&& device
->uri
)
1105 list_device(device
);
1111 * 'run_time()' - Return the total running time...
1114 static double /* O - Number of seconds */
1117 struct timeval curtime
; /* Current time */
1120 gettimeofday(&curtime
, NULL
);
1122 return (curtime
.tv_sec
- StartTime
.tv_sec
+
1123 0.000001 * (curtime
.tv_usec
- StartTime
.tv_usec
));
1128 * 'scan_devices()' - Scan for devices using SNMP.
1132 scan_devices(int fd
) /* I - SNMP socket */
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 */
1145 * Setup the request IDs...
1148 gettimeofday(&StartTime
, NULL
);
1150 DeviceTypeRequest
= StartTime
.tv_sec
;
1151 DeviceDescRequest
= StartTime
.tv_sec
+ 1;
1154 * First send all of the broadcast queries...
1157 for (address
= (char *)cupsArrayFirst(Addresses
);
1159 address
= (char *)cupsArrayNext(Addresses
))
1161 if (!strcmp(address
, "@LOCAL"))
1162 addrs
= get_interface_addresses(NULL
);
1163 else if (!strncmp(address
, "@IF(", 4))
1165 char ifname
[255]; /* Interface name */
1168 strlcpy(ifname
, address
+ 4, sizeof(ifname
));
1170 ifname
[strlen(ifname
) - 1] = '\0';
1172 addrs
= get_interface_addresses(ifname
);
1175 addrs
= httpAddrGetList(address
, AF_INET
, NULL
);
1179 fprintf(stderr
, "ERROR: Unable to scan \"%s\"!\n", address
);
1183 for (community
= (char *)cupsArrayFirst(Communities
);
1185 community
= (char *)cupsArrayNext(Communities
))
1187 debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
1188 community
, address
);
1190 for (addr
= addrs
; addr
; addr
= addr
->next
)
1191 _cupsSNMPWrite(fd
, &(addr
->addr
), CUPS_SNMP_VERSION_1
, community
,
1192 CUPS_ASN1_GET_REQUEST
, DeviceTypeRequest
, DeviceTypeOID
);
1195 httpAddrFreeList(addrs
);
1199 * Then read any responses that come in over the next 3 seconds...
1202 endtime
= time(NULL
) + MaxRunTime
;
1206 while (time(NULL
) < endtime
)
1209 timeout
.tv_usec
= 0;
1212 if (select(fd
+ 1, &input
, NULL
, NULL
, &timeout
) < 0)
1214 fprintf(stderr
, "ERROR: %.3f select() for %d failed: %s\n", run_time(),
1215 fd
, strerror(errno
));
1219 if (FD_ISSET(fd
, &input
))
1220 read_snmp_response(fd
);
1226 * Finally, probe all of the printers we discovered to see how they are
1230 for (device
= (snmp_cache_t
*)cupsArrayFirst(Devices
);
1232 device
= (snmp_cache_t
*)cupsArrayNext(Devices
))
1233 if (MaxRunTime
> 0 && run_time() >= MaxRunTime
)
1235 else if (!device
->uri
)
1236 probe_device(device
);
1238 debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
1243 * 'try_connect()' - Try connecting on a port...
1246 static int /* O - 0 on success or -1 on error */
1247 try_connect(http_addr_t
*addr
, /* I - Socket address */
1248 const char *addrname
, /* I - Hostname or IP address */
1249 int port
) /* I - Port number */
1251 int fd
; /* Socket */
1252 int status
; /* Connection status */
1255 debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
1256 port
== 515 ? "lpd" : "socket", addrname
, port
);
1258 if ((fd
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0)
1260 fprintf(stderr
, "ERROR: Unable to create socket: %s\n",
1265 addr
->ipv4
.sin_port
= htons(port
);
1269 status
= connect(fd
, (void *)addr
, httpAddrLength(addr
));
1279 * 'update_cache()' - Update a cached device...
1283 update_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 */
1291 device
->uri
= strdup(uri
);
1298 device
->id
= strdup(id
);
1303 if (device
->make_and_model
)
1304 free(device
->make_and_model
);
1306 device
->make_and_model
= strdup(make_model
);
1309 list_device(device
);
1314 * End of "$Id: snmp.c 6649 2007-07-11 21:46:42Z mike $".