2 * SNMP discovery backend for CUPS.
4 * Copyright © 2007-2014 by Apple Inc.
5 * Copyright © 2006-2007 by Easy Software Products, all rights reserved.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 * Include necessary headers.
15 #include "backend-private.h"
16 #include <cups/array.h>
17 #include <cups/file.h>
18 #include <cups/http-private.h>
23 * This backend implements SNMP printer discovery. It uses a broadcast-
24 * based approach to get SNMP response packets from potential printers,
25 * requesting OIDs from the Host and Port Monitor MIBs, does a URI
26 * lookup based on the device description string, and finally a probe of
27 * port 9100 (AppSocket) and 515 (LPD).
29 * The current focus is on printers with internal network cards, although
30 * the code also works with many external print servers as well.
32 * The backend reads the snmp.conf file from the CUPS_SERVERROOT directory
33 * which can contain comments, blank lines, or any number of the following
41 * DeviceURI "regex pattern" uri
46 * The default is to use:
54 * This backend is known to work with the following network printers and
57 * Axis OfficeBasic, 5400, 5600
67 * It does not currently work with:
74 * (for all of these, they do not support the Host MIB)
81 enum /**** Request IDs for each field ****/
91 typedef struct device_uri_s
/**** DeviceURI values ****/
93 regex_t re
; /* Regular expression to match */
94 cups_array_t
*uris
; /* URIs */
97 typedef struct snmp_cache_s
/**** SNMP scan cache ****/
99 http_addr_t address
; /* Address of device */
100 char *addrname
, /* Name of device */
101 *uri
, /* device-uri */
103 *info
, /* device-info */
104 *location
, /* device-location */
105 *make_and_model
; /* device-make-and-model */
106 int sent
; /* Has this device been listed? */
114 static char *add_array(cups_array_t
*a
, const char *s
);
115 static void add_cache(http_addr_t
*addr
, const char *addrname
,
116 const char *uri
, const char *id
,
117 const char *make_and_model
);
118 static device_uri_t
*add_device_uri(char *value
);
119 static void alarm_handler(int sig
);
120 static int compare_cache(snmp_cache_t
*a
, snmp_cache_t
*b
);
121 static void debug_printf(const char *format
, ...);
122 static void fix_make_model(char *make_model
,
123 const char *old_make_model
,
124 int make_model_size
);
125 static void free_array(cups_array_t
*a
);
126 static void free_cache(void);
127 static http_addrlist_t
*get_interface_addresses(const char *ifname
);
128 static void list_device(snmp_cache_t
*cache
);
129 static const char *password_cb(const char *prompt
);
130 static void probe_device(snmp_cache_t
*device
);
131 static void read_snmp_conf(const char *address
);
132 static void read_snmp_response(int fd
);
133 static double run_time(void);
134 static void scan_devices(int ipv4
, int ipv6
);
135 static int try_connect(http_addr_t
*addr
, const char *addrname
,
137 static void update_cache(snmp_cache_t
*device
, const char *uri
,
138 const char *id
, const char *make_model
);
145 static cups_array_t
*Addresses
= NULL
;
146 static cups_array_t
*Communities
= NULL
;
147 static cups_array_t
*Devices
= NULL
;
148 static int DebugLevel
= 0;
149 static const int DescriptionOID
[] = { CUPS_OID_hrDeviceDescr
, 1, -1 };
150 static const int LocationOID
[] = { CUPS_OID_sysLocation
, 0, -1 };
151 static const int DeviceTypeOID
[] = { CUPS_OID_hrDeviceType
, 1, -1 };
152 static const int DeviceIdOID
[] = { CUPS_OID_ppmPrinterIEEE1284DeviceId
, 1, -1 };
153 static const int UriOID
[] = { CUPS_OID_ppmPortServiceNameOrURI
, 1, 1, -1 };
154 static const int LexmarkProductOID
[] = { 1,3,6,1,4,1,641,2,1,2,1,2,1,-1 };
155 static const int LexmarkProductOID2
[] = { 1,3,6,1,4,1,674,10898,100,2,1,2,1,2,1,-1 };
156 static const int LexmarkDeviceIdOID
[] = { 1,3,6,1,4,1,641,2,1,2,1,3,1,-1 };
157 static const int HPDeviceIdOID
[] = { 1,3,6,1,4,1,11,2,3,9,1,1,7,0,-1 };
158 static const int RicohDeviceIdOID
[] = { 1,3,6,1,4,1,367,3,2,1,1,1,11,0,-1 };
159 static const int XeroxProductOID
[] = { 1,3,6,1,4,1,128,2,1,3,1,2,0,-1 };
160 static cups_array_t
*DeviceURIs
= NULL
;
161 static int HostNameLookups
= 0;
162 static int MaxRunTime
= 120;
163 static struct timeval StartTime
;
167 * 'main()' - Discover printers via SNMP.
170 int /* O - Exit status */
171 main(int argc
, /* I - Number of command-line arguments (6 or 7) */
172 char *argv
[]) /* I - Command-line arguments */
174 int ipv4
, /* SNMP IPv4 socket */
175 ipv6
; /* SNMP IPv6 socket */
176 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
177 struct sigaction action
; /* Actions for POSIX signals */
178 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
182 * Check command-line options...
187 _cupsLangPuts(stderr
, _("Usage: snmp [host-or-ip-address]"));
192 * Set the password callback for IPP operations...
195 cupsSetPasswordCB(password_cb
);
198 * Catch SIGALRM signals...
202 sigset(SIGALRM
, alarm_handler
);
203 #elif defined(HAVE_SIGACTION)
204 memset(&action
, 0, sizeof(action
));
206 sigemptyset(&action
.sa_mask
);
207 sigaddset(&action
.sa_mask
, SIGALRM
);
208 action
.sa_handler
= alarm_handler
;
209 sigaction(SIGALRM
, &action
, NULL
);
211 signal(SIGALRM
, alarm_handler
);
212 #endif /* HAVE_SIGSET */
215 * Open the SNMP socket...
218 if ((ipv4
= _cupsSNMPOpen(AF_INET
)) < 0)
222 if ((ipv6
= _cupsSNMPOpen(AF_INET6
)) < 0)
223 perror("DEBUG: Unable to create IPv6 socket");
226 #endif /* AF_INET6 */
229 * Read the configuration file and any cache data...
232 read_snmp_conf(argv
[1]);
234 _cupsSNMPSetDebug(DebugLevel
);
236 Devices
= cupsArrayNew((cups_array_func_t
)compare_cache
, NULL
);
239 * Scan for devices...
242 scan_devices(ipv4
, ipv6
);
245 * Close, free, and return with no errors...
248 _cupsSNMPClose(ipv4
);
250 _cupsSNMPClose(ipv6
);
252 free_array(Addresses
);
253 free_array(Communities
);
261 * 'add_array()' - Add a string to an array.
264 static char * /* O - New string */
265 add_array(cups_array_t
*a
, /* I - Array */
266 const char *s
) /* I - String to add */
268 char *dups
; /* New string */
273 cupsArrayAdd(a
, dups
);
280 * 'add_cache()' - Add a cached device...
284 add_cache(http_addr_t
*addr
, /* I - Device IP address */
285 const char *addrname
, /* I - IP address or name string */
286 const char *uri
, /* I - Device URI */
287 const char *id
, /* I - 1284 device ID */
288 const char *make_and_model
) /* I - Make and model */
290 snmp_cache_t
*temp
; /* New device entry */
293 debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
294 "id=\"%s\", make_and_model=\"%s\")\n",
295 addr
, addrname
, uri
? uri
: "(null)", id
? id
: "(null)",
296 make_and_model
? make_and_model
: "(null)");
298 temp
= calloc(1, sizeof(snmp_cache_t
));
299 memcpy(&(temp
->address
), addr
, sizeof(temp
->address
));
301 temp
->addrname
= strdup(addrname
);
304 temp
->uri
= strdup(uri
);
307 temp
->id
= strdup(id
);
310 temp
->make_and_model
= strdup(make_and_model
);
312 cupsArrayAdd(Devices
, temp
);
320 * 'add_device_uri()' - Add a device URI to the cache.
322 * The value string is modified (chopped up) as needed.
325 static device_uri_t
* /* O - Device URI */
326 add_device_uri(char *value
) /* I - Value from snmp.conf */
328 device_uri_t
*device_uri
; /* Device URI */
329 char *start
; /* Start of value */
333 * Allocate memory as needed...
337 DeviceURIs
= cupsArrayNew(NULL
, NULL
);
342 if ((device_uri
= calloc(1, sizeof(device_uri_t
))) == NULL
)
345 if ((device_uri
->uris
= cupsArrayNew(NULL
, NULL
)) == NULL
)
352 * Scan the value string for the regular expression and URI(s)...
355 value
++; /* Skip leading " */
357 for (start
= value
; *value
&& *value
!= '\"'; value
++)
358 if (*value
== '\\' && value
[1])
359 _cups_strcpy(value
, value
+ 1);
363 fputs("ERROR: Missing end quote for DeviceURI!\n", stderr
);
365 cupsArrayDelete(device_uri
->uris
);
373 if (regcomp(&(device_uri
->re
), start
, REG_EXTENDED
| REG_ICASE
))
375 fputs("ERROR: Bad regular expression for DeviceURI!\n", stderr
);
377 cupsArrayDelete(device_uri
->uris
);
385 while (isspace(*value
& 255))
391 for (start
= value
; *value
&& !isspace(*value
& 255); value
++);
396 cupsArrayAdd(device_uri
->uris
, strdup(start
));
400 * Add the device URI to the list and return it...
403 cupsArrayAdd(DeviceURIs
, device_uri
);
410 * 'alarm_handler()' - Handle alarm signals...
414 alarm_handler(int sig
) /* I - Signal number */
422 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
423 signal(SIGALRM
, alarm_handler
);
424 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
427 write(2, "DEBUG: ALARM!\n", 14);
432 * 'compare_cache()' - Compare two cache entries.
435 static int /* O - Result of comparison */
436 compare_cache(snmp_cache_t
*a
, /* I - First cache entry */
437 snmp_cache_t
*b
) /* I - Second cache entry */
439 return (_cups_strcasecmp(a
->addrname
, b
->addrname
));
444 * 'debug_printf()' - Display some debugging information.
448 debug_printf(const char *format
, /* I - Printf-style format string */
449 ...) /* I - Additional arguments as needed */
451 va_list ap
; /* Pointer to arguments */
457 va_start(ap
, format
);
458 vfprintf(stderr
, format
, ap
);
464 * 'fix_make_model()' - Fix common problems in the make-and-model string.
469 char *make_model
, /* I - New make-and-model string */
470 const char *old_make_model
, /* I - Old make-and-model string */
471 int make_model_size
) /* I - Size of new string buffer */
473 char *mmptr
; /* Pointer into make-and-model string */
477 * Fix some common problems with the make-and-model string so
478 * that printer driver detection works better...
481 if (!_cups_strncasecmp(old_make_model
, "Hewlett-Packard", 15))
484 * Strip leading Hewlett-Packard and hp prefixes and replace
485 * with a single HP manufacturer prefix...
488 mmptr
= (char *)old_make_model
+ 15;
490 while (isspace(*mmptr
& 255))
493 if (!_cups_strncasecmp(mmptr
, "hp", 2))
497 while (isspace(*mmptr
& 255))
504 strlcpy(make_model
+ 3, mmptr
, (size_t)make_model_size
- 3);
506 else if (!_cups_strncasecmp(old_make_model
, "deskjet", 7))
507 snprintf(make_model
, (size_t)make_model_size
, "HP DeskJet%s", old_make_model
+ 7);
508 else if (!_cups_strncasecmp(old_make_model
, "officejet", 9))
509 snprintf(make_model
, (size_t)make_model_size
, "HP OfficeJet%s", old_make_model
+ 9);
510 else if (!_cups_strncasecmp(old_make_model
, "stylus_pro_", 11))
511 snprintf(make_model
, (size_t)make_model_size
, "EPSON Stylus Pro %s", old_make_model
+ 11);
513 strlcpy(make_model
, old_make_model
, (size_t)make_model_size
);
515 if ((mmptr
= strstr(make_model
, ", Inc.,")) != NULL
)
518 * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560"
519 * becomes "Tektronix Phaser 560"...
522 _cups_strcpy(mmptr
, mmptr
+ 7);
525 if ((mmptr
= strstr(make_model
, " Network")) != NULL
)
528 * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025
529 * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"...
535 if ((mmptr
= strchr(make_model
, ',')) != NULL
)
538 * Drop anything after a trailing comma...
547 * 'free_array()' - Free an array of strings.
551 free_array(cups_array_t
*a
) /* I - Array */
553 char *s
; /* Current string */
556 for (s
= (char *)cupsArrayFirst(a
); s
; s
= (char *)cupsArrayNext(a
))
564 * 'free_cache()' - Free the array of cached devices.
570 snmp_cache_t
*cache
; /* Cached device */
573 for (cache
= (snmp_cache_t
*)cupsArrayFirst(Devices
);
575 cache
= (snmp_cache_t
*)cupsArrayNext(Devices
))
577 free(cache
->addrname
);
585 if (cache
->make_and_model
)
586 free(cache
->make_and_model
);
591 cupsArrayDelete(Devices
);
597 * 'get_interface_addresses()' - Get the broadcast address(es) associated
601 static http_addrlist_t
* /* O - List of addresses */
602 get_interface_addresses(
603 const char *ifname
) /* I - Interface name */
605 struct ifaddrs
*addrs
, /* Interface address list */
606 *addr
; /* Current interface address */
607 http_addrlist_t
*first
, /* First address in list */
608 *last
, /* Last address in list */
609 *current
; /* Current address */
612 if (getifaddrs(&addrs
) < 0)
615 for (addr
= addrs
, first
= NULL
, last
= NULL
; addr
; addr
= addr
->ifa_next
)
616 if ((addr
->ifa_flags
& IFF_BROADCAST
) && addr
->ifa_broadaddr
&&
617 addr
->ifa_broadaddr
->sa_family
== AF_INET
&&
618 (!ifname
|| !strcmp(ifname
, addr
->ifa_name
)))
620 current
= calloc(1, sizeof(http_addrlist_t
));
622 memcpy(&(current
->addr
), addr
->ifa_broadaddr
,
623 sizeof(struct sockaddr_in
));
628 last
->next
= current
;
640 * 'list_device()' - List a device we found...
644 list_device(snmp_cache_t
*cache
) /* I - Cached device */
647 cupsBackendReport("network", cache
->uri
, cache
->make_and_model
,
648 cache
->info
, cache
->id
, cache
->location
);
653 * 'password_cb()' - Handle authentication requests.
655 * All we do right now is return NULL, indicating that no authentication
659 static const char * /* O - Password (NULL) */
660 password_cb(const char *prompt
) /* I - Prompt message */
662 (void)prompt
; /* Anti-compiler-warning-code */
669 * 'probe_device()' - Probe a device to discover whether it is a printer.
671 * TODO: Try using the Port Monitor MIB to discover the correct protocol
672 * to use - first need a commercially-available printer that supports
677 probe_device(snmp_cache_t
*device
) /* I - Device */
679 char uri
[1024], /* Full device URI */
680 *uriptr
, /* Pointer into URI */
681 *format
; /* Format string for device */
682 device_uri_t
*device_uri
; /* Current DeviceURI match */
685 debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device
->addrname
);
689 * If the printer supports Bonjour/mDNS, don't report it from the SNMP backend.
692 if (!try_connect(&(device
->address
), device
->addrname
, 5353))
694 debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device
->addrname
);
697 #endif /* __APPLE__ */
700 * Lookup the device in the match table...
703 for (device_uri
= (device_uri_t
*)cupsArrayFirst(DeviceURIs
);
705 device_uri
= (device_uri_t
*)cupsArrayNext(DeviceURIs
))
706 if (device
->make_and_model
&&
707 !regexec(&(device_uri
->re
), device
->make_and_model
, 0, NULL
, 0))
710 * Found a match, add the URIs...
713 for (format
= (char *)cupsArrayFirst(device_uri
->uris
);
715 format
= (char *)cupsArrayNext(device_uri
->uris
))
717 for (uriptr
= uri
; *format
&& uriptr
< (uri
+ sizeof(uri
) - 1);)
718 if (*format
== '%' && format
[1] == 's')
721 * Insert hostname/address...
724 strlcpy(uriptr
, device
->addrname
, sizeof(uri
) - (size_t)(uriptr
- uri
));
725 uriptr
+= strlen(uriptr
);
729 *uriptr
++ = *format
++;
733 update_cache(device
, uri
, NULL
, NULL
);
740 * Then try the standard ports...
743 if (!try_connect(&(device
->address
), device
->addrname
, 9100))
745 debug_printf("DEBUG: %s supports AppSocket!\n", device
->addrname
);
747 snprintf(uri
, sizeof(uri
), "socket://%s", device
->addrname
);
748 update_cache(device
, uri
, NULL
, NULL
);
750 else if (!try_connect(&(device
->address
), device
->addrname
, 515))
752 debug_printf("DEBUG: %s supports LPD!\n", device
->addrname
);
754 snprintf(uri
, sizeof(uri
), "lpd://%s/", device
->addrname
);
755 update_cache(device
, uri
, NULL
, NULL
);
761 * 'read_snmp_conf()' - Read the snmp.conf file.
765 read_snmp_conf(const char *address
) /* I - Single address to probe */
767 cups_file_t
*fp
; /* File pointer */
768 char filename
[1024], /* Filename */
769 line
[1024], /* Line from file */
770 *value
; /* Value on line */
771 int linenum
; /* Line number */
772 const char *cups_serverroot
; /* CUPS_SERVERROOT env var */
773 const char *debug
; /* CUPS_DEBUG_LEVEL env var */
774 const char *runtime
; /* CUPS_MAX_RUN_TIME env var */
778 * Initialize the global address and community lists...
781 Addresses
= cupsArrayNew(NULL
, NULL
);
782 Communities
= cupsArrayNew(NULL
, NULL
);
785 add_array(Addresses
, address
);
787 if ((debug
= getenv("CUPS_DEBUG_LEVEL")) != NULL
)
788 DebugLevel
= atoi(debug
);
790 if ((runtime
= getenv("CUPS_MAX_RUN_TIME")) != NULL
)
791 MaxRunTime
= atoi(runtime
);
794 * Find the snmp.conf file...
797 if ((cups_serverroot
= getenv("CUPS_SERVERROOT")) == NULL
)
798 cups_serverroot
= CUPS_SERVERROOT
;
800 snprintf(filename
, sizeof(filename
), "%s/snmp.conf", cups_serverroot
);
802 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
805 * Read the snmp.conf file...
810 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
813 fprintf(stderr
, "ERROR: Missing value on line %d of %s!\n", linenum
,
815 else if (!_cups_strcasecmp(line
, "Address"))
818 add_array(Addresses
, value
);
820 else if (!_cups_strcasecmp(line
, "Community"))
821 add_array(Communities
, value
);
822 else if (!_cups_strcasecmp(line
, "DebugLevel"))
823 DebugLevel
= atoi(value
);
824 else if (!_cups_strcasecmp(line
, "DeviceURI"))
828 "ERROR: Missing double quote for regular expression on "
829 "line %d of %s!\n", linenum
, filename
);
831 add_device_uri(value
);
833 else if (!_cups_strcasecmp(line
, "HostNameLookups"))
834 HostNameLookups
= !_cups_strcasecmp(value
, "on") ||
835 !_cups_strcasecmp(value
, "yes") ||
836 !_cups_strcasecmp(value
, "true") ||
837 !_cups_strcasecmp(value
, "double");
838 else if (!_cups_strcasecmp(line
, "MaxRunTime"))
839 MaxRunTime
= atoi(value
);
841 fprintf(stderr
, "ERROR: Unknown directive %s on line %d of %s!\n",
842 line
, linenum
, filename
);
849 * Use defaults if parameters are undefined...
852 if (cupsArrayCount(Addresses
) == 0)
855 * If we have no addresses, exit immediately...
859 "DEBUG: No address specified and no Address line in %s...\n",
864 if (cupsArrayCount(Communities
) == 0)
866 fputs("INFO: Using default SNMP Community public\n", stderr
);
867 add_array(Communities
, "public");
873 * 'read_snmp_response()' - Read and parse a SNMP response...
877 read_snmp_response(int fd
) /* I - SNMP socket file descriptor */
879 char addrname
[256]; /* Source address name */
880 cups_snmp_t packet
; /* Decoded packet */
881 snmp_cache_t key
, /* Search key */
882 *device
; /* Matching device */
886 * Read the response data...
889 if (!_cupsSNMPRead(fd
, &packet
, -1.0))
891 fprintf(stderr
, "ERROR: Unable to read data from socket: %s\n",
897 httpAddrLookup(&(packet
.address
), addrname
, sizeof(addrname
));
899 httpAddrString(&(packet
.address
), addrname
, sizeof(addrname
));
901 debug_printf("DEBUG: %.3f Received data from %s...\n", run_time(), addrname
);
904 * Look for the response status code in the SNMP message header...
909 fprintf(stderr
, "ERROR: Bad SNMP packet from %s: %s\n", addrname
,
915 debug_printf("DEBUG: community=\"%s\"\n", packet
.community
);
916 debug_printf("DEBUG: request-id=%d\n", packet
.request_id
);
917 debug_printf("DEBUG: error-status=%d\n", packet
.error_status
);
919 if (packet
.error_status
&& packet
.request_id
!= DEVICE_TYPE
)
923 * Find a matching device in the cache...
926 key
.addrname
= addrname
;
927 device
= (snmp_cache_t
*)cupsArrayFind(Devices
, &key
);
930 * Process the message...
933 switch (packet
.request_id
)
937 * Got the device type response...
942 debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
948 * Add the device and request the device data...
951 add_cache(&(packet
.address
), addrname
, NULL
, NULL
, NULL
);
953 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
954 packet
.community
, CUPS_ASN1_GET_REQUEST
,
955 DEVICE_DESCRIPTION
, DescriptionOID
);
956 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
957 packet
.community
, CUPS_ASN1_GET_REQUEST
,
958 DEVICE_ID
, DeviceIdOID
);
959 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
960 packet
.community
, CUPS_ASN1_GET_REQUEST
,
962 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
963 packet
.community
, CUPS_ASN1_GET_REQUEST
,
964 DEVICE_LOCATION
, LocationOID
);
965 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
966 packet
.community
, CUPS_ASN1_GET_REQUEST
,
967 DEVICE_PRODUCT
, LexmarkProductOID
);
968 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
969 packet
.community
, CUPS_ASN1_GET_REQUEST
,
970 DEVICE_PRODUCT
, LexmarkProductOID2
);
971 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
972 packet
.community
, CUPS_ASN1_GET_REQUEST
,
973 DEVICE_ID
, LexmarkDeviceIdOID
);
974 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
975 packet
.community
, CUPS_ASN1_GET_REQUEST
,
976 DEVICE_ID
, RicohDeviceIdOID
);
977 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
978 packet
.community
, CUPS_ASN1_GET_REQUEST
,
979 DEVICE_PRODUCT
, XeroxProductOID
);
980 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
981 packet
.community
, CUPS_ASN1_GET_REQUEST
,
982 DEVICE_ID
, HPDeviceIdOID
);
985 case DEVICE_DESCRIPTION
:
986 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
)
989 * Update an existing cache entry...
992 char make_model
[256]; /* Make and model */
995 if (strchr((char *)packet
.object_value
.string
.bytes
, ':') &&
996 strchr((char *)packet
.object_value
.string
.bytes
, ';'))
999 * Description is the IEEE-1284 device ID...
1002 char *ptr
; /* Pointer into device ID */
1004 for (ptr
= (char *)packet
.object_value
.string
.bytes
; *ptr
; ptr
++)
1006 *ptr
= ';'; /* A lot of bad printers put a newline */
1008 device
->id
= strdup((char *)packet
.object_value
.string
.bytes
);
1010 backendGetMakeModel((char *)packet
.object_value
.string
.bytes
,
1011 make_model
, sizeof(make_model
));
1016 device
->info
= strdup(make_model
);
1021 * Description is plain text...
1024 fix_make_model(make_model
, (char *)packet
.object_value
.string
.bytes
,
1025 sizeof(make_model
));
1030 device
->info
= strdup((char *)packet
.object_value
.string
.bytes
);
1033 if (!device
->make_and_model
)
1034 device
->make_and_model
= strdup(make_model
);
1039 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
&&
1041 strlen(device
->id
) < packet
.object_value
.string
.num_bytes
))
1044 * Update an existing cache entry...
1047 char make_model
[256]; /* Make and model */
1048 char *ptr
; /* Pointer into device ID */
1050 for (ptr
= (char *)packet
.object_value
.string
.bytes
; *ptr
; ptr
++)
1052 *ptr
= ';'; /* A lot of bad printers put a newline */
1056 device
->id
= strdup((char *)packet
.object_value
.string
.bytes
);
1059 * Convert the ID to a make and model string...
1062 backendGetMakeModel((char *)packet
.object_value
.string
.bytes
,
1063 make_model
, sizeof(make_model
));
1064 if (device
->make_and_model
)
1065 free(device
->make_and_model
);
1067 device
->make_and_model
= strdup(make_model
);
1071 case DEVICE_LOCATION
:
1072 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
&&
1074 device
->location
= strdup((char *)packet
.object_value
.string
.bytes
);
1077 case DEVICE_PRODUCT
:
1078 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
&&
1082 * Update an existing cache entry...
1086 device
->info
= strdup((char *)packet
.object_value
.string
.bytes
);
1088 if (device
->make_and_model
)
1089 free(device
->make_and_model
);
1091 device
->make_and_model
= strdup((char *)packet
.object_value
.string
.bytes
);
1096 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
&&
1097 !device
->uri
&& packet
.object_value
.string
.num_bytes
> 3)
1100 * Update an existing cache entry...
1103 char scheme
[32], /* URI scheme */
1104 userpass
[256], /* Username:password in URI */
1105 hostname
[256], /* Hostname in URI */
1106 resource
[1024]; /* Resource path in URI */
1107 int port
; /* Port number in URI */
1109 if (!strncmp((char *)packet
.object_value
.string
.bytes
, "lpr:", 4))
1112 * We want "lpd://..." for the URI...
1115 packet
.object_value
.string
.bytes
[2] = 'd';
1118 if (httpSeparateURI(HTTP_URI_CODING_ALL
,
1119 (char *)packet
.object_value
.string
.bytes
,
1120 scheme
, sizeof(scheme
),
1121 userpass
, sizeof(userpass
),
1122 hostname
, sizeof(hostname
), &port
,
1123 resource
, sizeof(resource
)) >= HTTP_URI_OK
)
1124 device
->uri
= strdup((char *)packet
.object_value
.string
.bytes
);
1132 * 'run_time()' - Return the total running time...
1135 static double /* O - Number of seconds */
1138 struct timeval curtime
; /* Current time */
1141 gettimeofday(&curtime
, NULL
);
1143 return (curtime
.tv_sec
- StartTime
.tv_sec
+
1144 0.000001 * (curtime
.tv_usec
- StartTime
.tv_usec
));
1149 * 'scan_devices()' - Scan for devices using SNMP.
1153 scan_devices(int ipv4
, /* I - SNMP IPv4 socket */
1154 int ipv6
) /* I - SNMP IPv6 socket */
1156 int fd
, /* File descriptor for this address */
1157 busy
; /* Are we busy processing something? */
1158 char *address
, /* Current address */
1159 *community
; /* Current community */
1160 fd_set input
; /* Input set for select() */
1161 struct timeval timeout
; /* Timeout for select() */
1162 time_t endtime
; /* End time for scan */
1163 http_addrlist_t
*addrs
, /* List of addresses */
1164 *addr
; /* Current address */
1165 snmp_cache_t
*device
; /* Current device */
1166 char temp
[1024]; /* Temporary address string */
1169 gettimeofday(&StartTime
, NULL
);
1172 * First send all of the broadcast queries...
1175 for (address
= (char *)cupsArrayFirst(Addresses
);
1177 address
= (char *)cupsArrayNext(Addresses
))
1179 if (!strcmp(address
, "@LOCAL"))
1180 addrs
= get_interface_addresses(NULL
);
1181 else if (!strncmp(address
, "@IF(", 4))
1183 char ifname
[255]; /* Interface name */
1185 strlcpy(ifname
, address
+ 4, sizeof(ifname
));
1187 ifname
[strlen(ifname
) - 1] = '\0';
1189 addrs
= get_interface_addresses(ifname
);
1192 addrs
= httpAddrGetList(address
, AF_UNSPEC
, NULL
);
1196 fprintf(stderr
, "ERROR: Unable to scan \"%s\"!\n", address
);
1200 for (community
= (char *)cupsArrayFirst(Communities
);
1202 community
= (char *)cupsArrayNext(Communities
))
1204 debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
1205 community
, address
);
1207 for (addr
= addrs
; addr
; addr
= addr
->next
)
1210 if (httpAddrFamily(&(addr
->addr
)) == AF_INET6
)
1213 #endif /* AF_INET6 */
1216 debug_printf("DEBUG: Sending get request to %s...\n",
1217 httpAddrString(&(addr
->addr
), temp
, sizeof(temp
)));
1219 _cupsSNMPWrite(fd
, &(addr
->addr
), CUPS_SNMP_VERSION_1
, community
,
1220 CUPS_ASN1_GET_REQUEST
, DEVICE_TYPE
, DeviceTypeOID
);
1224 httpAddrFreeList(addrs
);
1228 * Then read any responses that come in over the next 3 seconds...
1231 endtime
= time(NULL
) + MaxRunTime
;
1235 while (time(NULL
) < endtime
)
1238 timeout
.tv_usec
= 0;
1240 FD_SET(ipv4
, &input
);
1242 FD_SET(ipv6
, &input
);
1244 fd
= ipv4
> ipv6
? ipv4
: ipv6
;
1245 if (select(fd
+ 1, &input
, NULL
, NULL
, &timeout
) < 0)
1247 fprintf(stderr
, "ERROR: %.3f select() for %d/%d failed: %s\n", run_time(),
1248 ipv4
, ipv6
, strerror(errno
));
1254 if (FD_ISSET(ipv4
, &input
))
1256 read_snmp_response(ipv4
);
1260 if (ipv6
>= 0 && FD_ISSET(ipv6
, &input
))
1262 read_snmp_response(ipv6
);
1269 * List devices with complete information...
1272 int sent_something
= 0;
1274 for (device
= (snmp_cache_t
*)cupsArrayFirst(Devices
);
1276 device
= (snmp_cache_t
*)cupsArrayNext(Devices
))
1277 if (!device
->sent
&& device
->info
&& device
->make_and_model
)
1280 list_device(device
);
1282 probe_device(device
);
1284 device
->sent
= sent_something
= 1;
1287 if (!sent_something
)
1292 debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
1297 * 'try_connect()' - Try connecting on a port...
1300 static int /* O - 0 on success or -1 on error */
1301 try_connect(http_addr_t
*addr
, /* I - Socket address */
1302 const char *addrname
, /* I - Hostname or IP address */
1303 int port
) /* I - Port number */
1305 int fd
; /* Socket */
1306 int status
; /* Connection status */
1309 debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
1310 port
== 515 ? "lpd" : "socket", addrname
, port
);
1312 if ((fd
= socket(httpAddrFamily(addr
), SOCK_STREAM
, 0)) < 0)
1314 fprintf(stderr
, "ERROR: Unable to create socket: %s\n",
1319 _httpAddrSetPort(addr
, port
);
1323 status
= connect(fd
, (void *)addr
, (socklen_t
)httpAddrLength(addr
));
1333 * 'update_cache()' - Update a cached device...
1337 update_cache(snmp_cache_t
*device
, /* I - Device */
1338 const char *uri
, /* I - Device URI */
1339 const char *id
, /* I - Device ID */
1340 const char *make_model
) /* I - Device make and model */
1345 device
->uri
= strdup(uri
);
1352 device
->id
= strdup(id
);
1357 if (device
->make_and_model
)
1358 free(device
->make_and_model
);
1360 device
->make_and_model
= strdup(make_model
);
1363 list_device(device
);