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 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * "LICENSE" which should have been included with this file. If this
11 * file is missing or damaged, see the license at "http://www.cups.org/".
13 * This file is subject to the Apple OS-Developed Software exception.
17 * Include necessary headers.
20 #include "backend-private.h"
21 #include <cups/array.h>
22 #include <cups/file.h>
23 #include <cups/http-private.h>
28 * This backend implements SNMP printer discovery. It uses a broadcast-
29 * based approach to get SNMP response packets from potential printers,
30 * requesting OIDs from the Host and Port Monitor MIBs, does a URI
31 * lookup based on the device description string, and finally a probe of
32 * port 9100 (AppSocket) and 515 (LPD).
34 * The current focus is on printers with internal network cards, although
35 * the code also works with many external print servers as well.
37 * The backend reads the snmp.conf file from the CUPS_SERVERROOT directory
38 * which can contain comments, blank lines, or any number of the following
46 * DeviceURI "regex pattern" uri
51 * The default is to use:
59 * This backend is known to work with the following network printers and
62 * Axis OfficeBasic, 5400, 5600
72 * It does not currently work with:
79 * (for all of these, they do not support the Host MIB)
86 enum /**** Request IDs for each field ****/
96 typedef struct device_uri_s
/**** DeviceURI values ****/
98 regex_t re
; /* Regular expression to match */
99 cups_array_t
*uris
; /* URIs */
102 typedef struct snmp_cache_s
/**** SNMP scan cache ****/
104 http_addr_t address
; /* Address of device */
105 char *addrname
, /* Name of device */
106 *uri
, /* device-uri */
108 *info
, /* device-info */
109 *location
, /* device-location */
110 *make_and_model
; /* device-make-and-model */
111 int sent
; /* Has this device been listed? */
119 static char *add_array(cups_array_t
*a
, const char *s
);
120 static void add_cache(http_addr_t
*addr
, const char *addrname
,
121 const char *uri
, const char *id
,
122 const char *make_and_model
);
123 static device_uri_t
*add_device_uri(char *value
);
124 static void alarm_handler(int sig
);
125 static int compare_cache(snmp_cache_t
*a
, snmp_cache_t
*b
);
126 static void debug_printf(const char *format
, ...);
127 static void fix_make_model(char *make_model
,
128 const char *old_make_model
,
129 int make_model_size
);
130 static void free_array(cups_array_t
*a
);
131 static void free_cache(void);
132 static http_addrlist_t
*get_interface_addresses(const char *ifname
);
133 static void list_device(snmp_cache_t
*cache
);
134 static const char *password_cb(const char *prompt
);
135 static void probe_device(snmp_cache_t
*device
);
136 static void read_snmp_conf(const char *address
);
137 static void read_snmp_response(int fd
);
138 static double run_time(void);
139 static void scan_devices(int ipv4
, int ipv6
);
140 static int try_connect(http_addr_t
*addr
, const char *addrname
,
142 static void update_cache(snmp_cache_t
*device
, const char *uri
,
143 const char *id
, const char *make_model
);
150 static cups_array_t
*Addresses
= NULL
;
151 static cups_array_t
*Communities
= NULL
;
152 static cups_array_t
*Devices
= NULL
;
153 static int DebugLevel
= 0;
154 static const int DescriptionOID
[] = { CUPS_OID_hrDeviceDescr
, 1, -1 };
155 static const int LocationOID
[] = { CUPS_OID_sysLocation
, 0, -1 };
156 static const int DeviceTypeOID
[] = { CUPS_OID_hrDeviceType
, 1, -1 };
157 static const int DeviceIdOID
[] = { CUPS_OID_ppmPrinterIEEE1284DeviceId
, 1, -1 };
158 static const int UriOID
[] = { CUPS_OID_ppmPortServiceNameOrURI
, 1, 1, -1 };
159 static const int LexmarkProductOID
[] = { 1,3,6,1,4,1,641,2,1,2,1,2,1,-1 };
160 static const int LexmarkProductOID2
[] = { 1,3,6,1,4,1,674,10898,100,2,1,2,1,2,1,-1 };
161 static const int LexmarkDeviceIdOID
[] = { 1,3,6,1,4,1,641,2,1,2,1,3,1,-1 };
162 static const int XeroxProductOID
[] = { 1,3,6,1,4,1,128,2,1,3,1,2,0,-1 };
163 static cups_array_t
*DeviceURIs
= NULL
;
164 static int HostNameLookups
= 0;
165 static int MaxRunTime
= 120;
166 static struct timeval StartTime
;
170 * 'main()' - Discover printers via SNMP.
173 int /* O - Exit status */
174 main(int argc
, /* I - Number of command-line arguments (6 or 7) */
175 char *argv
[]) /* I - Command-line arguments */
177 int ipv4
, /* SNMP IPv4 socket */
178 ipv6
; /* SNMP IPv6 socket */
179 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
180 struct sigaction action
; /* Actions for POSIX signals */
181 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
185 * Check command-line options...
190 _cupsLangPuts(stderr
, _("Usage: snmp [host-or-ip-address]"));
195 * Set the password callback for IPP operations...
198 cupsSetPasswordCB(password_cb
);
201 * Catch SIGALRM signals...
205 sigset(SIGALRM
, alarm_handler
);
206 #elif defined(HAVE_SIGACTION)
207 memset(&action
, 0, sizeof(action
));
209 sigemptyset(&action
.sa_mask
);
210 sigaddset(&action
.sa_mask
, SIGALRM
);
211 action
.sa_handler
= alarm_handler
;
212 sigaction(SIGALRM
, &action
, NULL
);
214 signal(SIGALRM
, alarm_handler
);
215 #endif /* HAVE_SIGSET */
218 * Open the SNMP socket...
221 if ((ipv4
= _cupsSNMPOpen(AF_INET
)) < 0)
225 if ((ipv6
= _cupsSNMPOpen(AF_INET6
)) < 0)
226 perror("DEBUG: Unable to create IPv6 socket");
229 #endif /* AF_INET6 */
232 * Read the configuration file and any cache data...
235 read_snmp_conf(argv
[1]);
237 _cupsSNMPSetDebug(DebugLevel
);
239 Devices
= cupsArrayNew((cups_array_func_t
)compare_cache
, NULL
);
242 * Scan for devices...
245 scan_devices(ipv4
, ipv6
);
248 * Close, free, and return with no errors...
251 _cupsSNMPClose(ipv4
);
253 _cupsSNMPClose(ipv6
);
255 free_array(Addresses
);
256 free_array(Communities
);
264 * 'add_array()' - Add a string to an array.
267 static char * /* O - New string */
268 add_array(cups_array_t
*a
, /* I - Array */
269 const char *s
) /* I - String to add */
271 char *dups
; /* New string */
276 cupsArrayAdd(a
, dups
);
283 * 'add_cache()' - Add a cached device...
287 add_cache(http_addr_t
*addr
, /* I - Device IP address */
288 const char *addrname
, /* I - IP address or name string */
289 const char *uri
, /* I - Device URI */
290 const char *id
, /* I - 1284 device ID */
291 const char *make_and_model
) /* I - Make and model */
293 snmp_cache_t
*temp
; /* New device entry */
296 debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
297 "id=\"%s\", make_and_model=\"%s\")\n",
298 addr
, addrname
, uri
? uri
: "(null)", id
? id
: "(null)",
299 make_and_model
? make_and_model
: "(null)");
301 temp
= calloc(1, sizeof(snmp_cache_t
));
302 memcpy(&(temp
->address
), addr
, sizeof(temp
->address
));
304 temp
->addrname
= strdup(addrname
);
307 temp
->uri
= strdup(uri
);
310 temp
->id
= strdup(id
);
313 temp
->make_and_model
= strdup(make_and_model
);
315 cupsArrayAdd(Devices
, temp
);
323 * 'add_device_uri()' - Add a device URI to the cache.
325 * The value string is modified (chopped up) as needed.
328 static device_uri_t
* /* O - Device URI */
329 add_device_uri(char *value
) /* I - Value from snmp.conf */
331 device_uri_t
*device_uri
; /* Device URI */
332 char *start
; /* Start of value */
336 * Allocate memory as needed...
340 DeviceURIs
= cupsArrayNew(NULL
, NULL
);
345 if ((device_uri
= calloc(1, sizeof(device_uri_t
))) == NULL
)
348 if ((device_uri
->uris
= cupsArrayNew(NULL
, NULL
)) == NULL
)
355 * Scan the value string for the regular expression and URI(s)...
358 value
++; /* Skip leading " */
360 for (start
= value
; *value
&& *value
!= '\"'; value
++)
361 if (*value
== '\\' && value
[1])
362 _cups_strcpy(value
, value
+ 1);
366 fputs("ERROR: Missing end quote for DeviceURI!\n", stderr
);
368 cupsArrayDelete(device_uri
->uris
);
376 if (regcomp(&(device_uri
->re
), start
, REG_EXTENDED
| REG_ICASE
))
378 fputs("ERROR: Bad regular expression for DeviceURI!\n", stderr
);
380 cupsArrayDelete(device_uri
->uris
);
388 while (isspace(*value
& 255))
394 for (start
= value
; *value
&& !isspace(*value
& 255); value
++);
399 cupsArrayAdd(device_uri
->uris
, strdup(start
));
403 * Add the device URI to the list and return it...
406 cupsArrayAdd(DeviceURIs
, device_uri
);
413 * 'alarm_handler()' - Handle alarm signals...
417 alarm_handler(int sig
) /* I - Signal number */
425 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
426 signal(SIGALRM
, alarm_handler
);
427 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
430 write(2, "DEBUG: ALARM!\n", 14);
435 * 'compare_cache()' - Compare two cache entries.
438 static int /* O - Result of comparison */
439 compare_cache(snmp_cache_t
*a
, /* I - First cache entry */
440 snmp_cache_t
*b
) /* I - Second cache entry */
442 return (_cups_strcasecmp(a
->addrname
, b
->addrname
));
447 * 'debug_printf()' - Display some debugging information.
451 debug_printf(const char *format
, /* I - Printf-style format string */
452 ...) /* I - Additional arguments as needed */
454 va_list ap
; /* Pointer to arguments */
460 va_start(ap
, format
);
461 vfprintf(stderr
, format
, ap
);
467 * 'fix_make_model()' - Fix common problems in the make-and-model string.
472 char *make_model
, /* I - New make-and-model string */
473 const char *old_make_model
, /* I - Old make-and-model string */
474 int make_model_size
) /* I - Size of new string buffer */
476 char *mmptr
; /* Pointer into make-and-model string */
480 * Fix some common problems with the make-and-model string so
481 * that printer driver detection works better...
484 if (!_cups_strncasecmp(old_make_model
, "Hewlett-Packard", 15))
487 * Strip leading Hewlett-Packard and hp prefixes and replace
488 * with a single HP manufacturer prefix...
491 mmptr
= (char *)old_make_model
+ 15;
493 while (isspace(*mmptr
& 255))
496 if (!_cups_strncasecmp(mmptr
, "hp", 2))
500 while (isspace(*mmptr
& 255))
507 strlcpy(make_model
+ 3, mmptr
, (size_t)make_model_size
- 3);
509 else if (!_cups_strncasecmp(old_make_model
, "deskjet", 7))
510 snprintf(make_model
, (size_t)make_model_size
, "HP DeskJet%s", old_make_model
+ 7);
511 else if (!_cups_strncasecmp(old_make_model
, "officejet", 9))
512 snprintf(make_model
, (size_t)make_model_size
, "HP OfficeJet%s", old_make_model
+ 9);
513 else if (!_cups_strncasecmp(old_make_model
, "stylus_pro_", 11))
514 snprintf(make_model
, (size_t)make_model_size
, "EPSON Stylus Pro %s", old_make_model
+ 11);
516 strlcpy(make_model
, old_make_model
, (size_t)make_model_size
);
518 if ((mmptr
= strstr(make_model
, ", Inc.,")) != NULL
)
521 * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560"
522 * becomes "Tektronix Phaser 560"...
525 _cups_strcpy(mmptr
, mmptr
+ 7);
528 if ((mmptr
= strstr(make_model
, " Network")) != NULL
)
531 * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025
532 * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"...
538 if ((mmptr
= strchr(make_model
, ',')) != NULL
)
541 * Drop anything after a trailing comma...
550 * 'free_array()' - Free an array of strings.
554 free_array(cups_array_t
*a
) /* I - Array */
556 char *s
; /* Current string */
559 for (s
= (char *)cupsArrayFirst(a
); s
; s
= (char *)cupsArrayNext(a
))
567 * 'free_cache()' - Free the array of cached devices.
573 snmp_cache_t
*cache
; /* Cached device */
576 for (cache
= (snmp_cache_t
*)cupsArrayFirst(Devices
);
578 cache
= (snmp_cache_t
*)cupsArrayNext(Devices
))
580 free(cache
->addrname
);
588 if (cache
->make_and_model
)
589 free(cache
->make_and_model
);
594 cupsArrayDelete(Devices
);
600 * 'get_interface_addresses()' - Get the broadcast address(es) associated
604 static http_addrlist_t
* /* O - List of addresses */
605 get_interface_addresses(
606 const char *ifname
) /* I - Interface name */
608 struct ifaddrs
*addrs
, /* Interface address list */
609 *addr
; /* Current interface address */
610 http_addrlist_t
*first
, /* First address in list */
611 *last
, /* Last address in list */
612 *current
; /* Current address */
615 if (getifaddrs(&addrs
) < 0)
618 for (addr
= addrs
, first
= NULL
, last
= NULL
; addr
; addr
= addr
->ifa_next
)
619 if ((addr
->ifa_flags
& IFF_BROADCAST
) && addr
->ifa_broadaddr
&&
620 addr
->ifa_broadaddr
->sa_family
== AF_INET
&&
621 (!ifname
|| !strcmp(ifname
, addr
->ifa_name
)))
623 current
= calloc(1, sizeof(http_addrlist_t
));
625 memcpy(&(current
->addr
), addr
->ifa_broadaddr
,
626 sizeof(struct sockaddr_in
));
631 last
->next
= current
;
643 * 'list_device()' - List a device we found...
647 list_device(snmp_cache_t
*cache
) /* I - Cached device */
650 cupsBackendReport("network", cache
->uri
, cache
->make_and_model
,
651 cache
->info
, cache
->id
, cache
->location
);
656 * 'password_cb()' - Handle authentication requests.
658 * All we do right now is return NULL, indicating that no authentication
662 static const char * /* O - Password (NULL) */
663 password_cb(const char *prompt
) /* I - Prompt message */
665 (void)prompt
; /* Anti-compiler-warning-code */
672 * 'probe_device()' - Probe a device to discover whether it is a printer.
674 * TODO: Try using the Port Monitor MIB to discover the correct protocol
675 * to use - first need a commercially-available printer that supports
680 probe_device(snmp_cache_t
*device
) /* I - Device */
682 char uri
[1024], /* Full device URI */
683 *uriptr
, /* Pointer into URI */
684 *format
; /* Format string for device */
685 device_uri_t
*device_uri
; /* Current DeviceURI match */
688 debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device
->addrname
);
692 * If the printer supports Bonjour/mDNS, don't report it from the SNMP backend.
695 if (!try_connect(&(device
->address
), device
->addrname
, 5353))
697 debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device
->addrname
);
700 #endif /* __APPLE__ */
703 * Lookup the device in the match table...
706 for (device_uri
= (device_uri_t
*)cupsArrayFirst(DeviceURIs
);
708 device_uri
= (device_uri_t
*)cupsArrayNext(DeviceURIs
))
709 if (device
->make_and_model
&&
710 !regexec(&(device_uri
->re
), device
->make_and_model
, 0, NULL
, 0))
713 * Found a match, add the URIs...
716 for (format
= (char *)cupsArrayFirst(device_uri
->uris
);
718 format
= (char *)cupsArrayNext(device_uri
->uris
))
720 for (uriptr
= uri
; *format
&& uriptr
< (uri
+ sizeof(uri
) - 1);)
721 if (*format
== '%' && format
[1] == 's')
724 * Insert hostname/address...
727 strlcpy(uriptr
, device
->addrname
, sizeof(uri
) - (size_t)(uriptr
- uri
));
728 uriptr
+= strlen(uriptr
);
732 *uriptr
++ = *format
++;
736 update_cache(device
, uri
, NULL
, NULL
);
743 * Then try the standard ports...
746 if (!try_connect(&(device
->address
), device
->addrname
, 9100))
748 debug_printf("DEBUG: %s supports AppSocket!\n", device
->addrname
);
750 snprintf(uri
, sizeof(uri
), "socket://%s", device
->addrname
);
751 update_cache(device
, uri
, NULL
, NULL
);
753 else if (!try_connect(&(device
->address
), device
->addrname
, 515))
755 debug_printf("DEBUG: %s supports LPD!\n", device
->addrname
);
757 snprintf(uri
, sizeof(uri
), "lpd://%s/", device
->addrname
);
758 update_cache(device
, uri
, NULL
, NULL
);
764 * 'read_snmp_conf()' - Read the snmp.conf file.
768 read_snmp_conf(const char *address
) /* I - Single address to probe */
770 cups_file_t
*fp
; /* File pointer */
771 char filename
[1024], /* Filename */
772 line
[1024], /* Line from file */
773 *value
; /* Value on line */
774 int linenum
; /* Line number */
775 const char *cups_serverroot
; /* CUPS_SERVERROOT env var */
776 const char *debug
; /* CUPS_DEBUG_LEVEL env var */
777 const char *runtime
; /* CUPS_MAX_RUN_TIME env var */
781 * Initialize the global address and community lists...
784 Addresses
= cupsArrayNew(NULL
, NULL
);
785 Communities
= cupsArrayNew(NULL
, NULL
);
788 add_array(Addresses
, address
);
790 if ((debug
= getenv("CUPS_DEBUG_LEVEL")) != NULL
)
791 DebugLevel
= atoi(debug
);
793 if ((runtime
= getenv("CUPS_MAX_RUN_TIME")) != NULL
)
794 MaxRunTime
= atoi(runtime
);
797 * Find the snmp.conf file...
800 if ((cups_serverroot
= getenv("CUPS_SERVERROOT")) == NULL
)
801 cups_serverroot
= CUPS_SERVERROOT
;
803 snprintf(filename
, sizeof(filename
), "%s/snmp.conf", cups_serverroot
);
805 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
808 * Read the snmp.conf file...
813 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
816 fprintf(stderr
, "ERROR: Missing value on line %d of %s!\n", linenum
,
818 else if (!_cups_strcasecmp(line
, "Address"))
821 add_array(Addresses
, value
);
823 else if (!_cups_strcasecmp(line
, "Community"))
824 add_array(Communities
, value
);
825 else if (!_cups_strcasecmp(line
, "DebugLevel"))
826 DebugLevel
= atoi(value
);
827 else if (!_cups_strcasecmp(line
, "DeviceURI"))
831 "ERROR: Missing double quote for regular expression on "
832 "line %d of %s!\n", linenum
, filename
);
834 add_device_uri(value
);
836 else if (!_cups_strcasecmp(line
, "HostNameLookups"))
837 HostNameLookups
= !_cups_strcasecmp(value
, "on") ||
838 !_cups_strcasecmp(value
, "yes") ||
839 !_cups_strcasecmp(value
, "true") ||
840 !_cups_strcasecmp(value
, "double");
841 else if (!_cups_strcasecmp(line
, "MaxRunTime"))
842 MaxRunTime
= atoi(value
);
844 fprintf(stderr
, "ERROR: Unknown directive %s on line %d of %s!\n",
845 line
, linenum
, filename
);
852 * Use defaults if parameters are undefined...
855 if (cupsArrayCount(Addresses
) == 0)
858 * If we have no addresses, exit immediately...
862 "DEBUG: No address specified and no Address line in %s...\n",
867 if (cupsArrayCount(Communities
) == 0)
869 fputs("INFO: Using default SNMP Community public\n", stderr
);
870 add_array(Communities
, "public");
876 * 'read_snmp_response()' - Read and parse a SNMP response...
880 read_snmp_response(int fd
) /* I - SNMP socket file descriptor */
882 char addrname
[256]; /* Source address name */
883 cups_snmp_t packet
; /* Decoded packet */
884 snmp_cache_t key
, /* Search key */
885 *device
; /* Matching device */
889 * Read the response data...
892 if (!_cupsSNMPRead(fd
, &packet
, -1.0))
894 fprintf(stderr
, "ERROR: Unable to read data from socket: %s\n",
900 httpAddrLookup(&(packet
.address
), addrname
, sizeof(addrname
));
902 httpAddrString(&(packet
.address
), addrname
, sizeof(addrname
));
904 debug_printf("DEBUG: %.3f Received data from %s...\n", run_time(), addrname
);
907 * Look for the response status code in the SNMP message header...
912 fprintf(stderr
, "ERROR: Bad SNMP packet from %s: %s\n", addrname
,
918 debug_printf("DEBUG: community=\"%s\"\n", packet
.community
);
919 debug_printf("DEBUG: request-id=%d\n", packet
.request_id
);
920 debug_printf("DEBUG: error-status=%d\n", packet
.error_status
);
922 if (packet
.error_status
&& packet
.request_id
!= DEVICE_TYPE
)
926 * Find a matching device in the cache...
929 key
.addrname
= addrname
;
930 device
= (snmp_cache_t
*)cupsArrayFind(Devices
, &key
);
933 * Process the message...
936 switch (packet
.request_id
)
940 * Got the device type response...
945 debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
951 * Add the device and request the device data...
954 add_cache(&(packet
.address
), addrname
, NULL
, NULL
, NULL
);
956 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
957 packet
.community
, CUPS_ASN1_GET_REQUEST
,
958 DEVICE_DESCRIPTION
, DescriptionOID
);
959 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
960 packet
.community
, CUPS_ASN1_GET_REQUEST
,
961 DEVICE_ID
, DeviceIdOID
);
962 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
963 packet
.community
, CUPS_ASN1_GET_REQUEST
,
965 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
966 packet
.community
, CUPS_ASN1_GET_REQUEST
,
967 DEVICE_LOCATION
, LocationOID
);
968 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
969 packet
.community
, CUPS_ASN1_GET_REQUEST
,
970 DEVICE_PRODUCT
, LexmarkProductOID
);
971 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
972 packet
.community
, CUPS_ASN1_GET_REQUEST
,
973 DEVICE_PRODUCT
, LexmarkProductOID2
);
974 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
975 packet
.community
, CUPS_ASN1_GET_REQUEST
,
976 DEVICE_ID
, LexmarkDeviceIdOID
);
977 _cupsSNMPWrite(fd
, &(packet
.address
), CUPS_SNMP_VERSION_1
,
978 packet
.community
, CUPS_ASN1_GET_REQUEST
,
979 DEVICE_PRODUCT
, XeroxProductOID
);
982 case DEVICE_DESCRIPTION
:
983 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
)
986 * Update an existing cache entry...
989 char make_model
[256]; /* Make and model */
992 if (strchr((char *)packet
.object_value
.string
.bytes
, ':') &&
993 strchr((char *)packet
.object_value
.string
.bytes
, ';'))
996 * Description is the IEEE-1284 device ID...
999 char *ptr
; /* Pointer into device ID */
1001 for (ptr
= (char *)packet
.object_value
.string
.bytes
; *ptr
; ptr
++)
1003 *ptr
= ';'; /* A lot of bad printers put a newline */
1005 device
->id
= strdup((char *)packet
.object_value
.string
.bytes
);
1007 backendGetMakeModel((char *)packet
.object_value
.string
.bytes
,
1008 make_model
, sizeof(make_model
));
1013 device
->info
= strdup(make_model
);
1018 * Description is plain text...
1021 fix_make_model(make_model
, (char *)packet
.object_value
.string
.bytes
,
1022 sizeof(make_model
));
1027 device
->info
= strdup((char *)packet
.object_value
.string
.bytes
);
1030 if (!device
->make_and_model
)
1031 device
->make_and_model
= strdup(make_model
);
1036 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
&&
1038 strlen(device
->id
) < packet
.object_value
.string
.num_bytes
))
1041 * Update an existing cache entry...
1044 char make_model
[256]; /* Make and model */
1045 char *ptr
; /* Pointer into device ID */
1047 for (ptr
= (char *)packet
.object_value
.string
.bytes
; *ptr
; ptr
++)
1049 *ptr
= ';'; /* A lot of bad printers put a newline */
1053 device
->id
= strdup((char *)packet
.object_value
.string
.bytes
);
1056 * Convert the ID to a make and model string...
1059 backendGetMakeModel((char *)packet
.object_value
.string
.bytes
,
1060 make_model
, sizeof(make_model
));
1061 if (device
->make_and_model
)
1062 free(device
->make_and_model
);
1064 device
->make_and_model
= strdup(make_model
);
1068 case DEVICE_LOCATION
:
1069 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
&&
1071 device
->location
= strdup((char *)packet
.object_value
.string
.bytes
);
1074 case DEVICE_PRODUCT
:
1075 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
&&
1079 * Update an existing cache entry...
1083 device
->info
= strdup((char *)packet
.object_value
.string
.bytes
);
1085 if (device
->make_and_model
)
1086 free(device
->make_and_model
);
1088 device
->make_and_model
= strdup((char *)packet
.object_value
.string
.bytes
);
1093 if (device
&& packet
.object_type
== CUPS_ASN1_OCTET_STRING
&&
1094 !device
->uri
&& packet
.object_value
.string
.num_bytes
> 3)
1097 * Update an existing cache entry...
1100 char scheme
[32], /* URI scheme */
1101 userpass
[256], /* Username:password in URI */
1102 hostname
[256], /* Hostname in URI */
1103 resource
[1024]; /* Resource path in URI */
1104 int port
; /* Port number in URI */
1106 if (!strncmp((char *)packet
.object_value
.string
.bytes
, "lpr:", 4))
1109 * We want "lpd://..." for the URI...
1112 packet
.object_value
.string
.bytes
[2] = 'd';
1115 if (httpSeparateURI(HTTP_URI_CODING_ALL
,
1116 (char *)packet
.object_value
.string
.bytes
,
1117 scheme
, sizeof(scheme
),
1118 userpass
, sizeof(userpass
),
1119 hostname
, sizeof(hostname
), &port
,
1120 resource
, sizeof(resource
)) >= HTTP_URI_OK
)
1121 device
->uri
= strdup((char *)packet
.object_value
.string
.bytes
);
1129 * 'run_time()' - Return the total running time...
1132 static double /* O - Number of seconds */
1135 struct timeval curtime
; /* Current time */
1138 gettimeofday(&curtime
, NULL
);
1140 return (curtime
.tv_sec
- StartTime
.tv_sec
+
1141 0.000001 * (curtime
.tv_usec
- StartTime
.tv_usec
));
1146 * 'scan_devices()' - Scan for devices using SNMP.
1150 scan_devices(int ipv4
, /* I - SNMP IPv4 socket */
1151 int ipv6
) /* I - SNMP IPv6 socket */
1153 int fd
, /* File descriptor for this address */
1154 busy
; /* Are we busy processing something? */
1155 char *address
, /* Current address */
1156 *community
; /* Current community */
1157 fd_set input
; /* Input set for select() */
1158 struct timeval timeout
; /* Timeout for select() */
1159 time_t endtime
; /* End time for scan */
1160 http_addrlist_t
*addrs
, /* List of addresses */
1161 *addr
; /* Current address */
1162 snmp_cache_t
*device
; /* Current device */
1163 char temp
[1024]; /* Temporary address string */
1166 gettimeofday(&StartTime
, NULL
);
1169 * First send all of the broadcast queries...
1172 for (address
= (char *)cupsArrayFirst(Addresses
);
1174 address
= (char *)cupsArrayNext(Addresses
))
1176 if (!strcmp(address
, "@LOCAL"))
1177 addrs
= get_interface_addresses(NULL
);
1178 else if (!strncmp(address
, "@IF(", 4))
1180 char ifname
[255]; /* Interface name */
1182 strlcpy(ifname
, address
+ 4, sizeof(ifname
));
1184 ifname
[strlen(ifname
) - 1] = '\0';
1186 addrs
= get_interface_addresses(ifname
);
1189 addrs
= httpAddrGetList(address
, AF_UNSPEC
, NULL
);
1193 fprintf(stderr
, "ERROR: Unable to scan \"%s\"!\n", address
);
1197 for (community
= (char *)cupsArrayFirst(Communities
);
1199 community
= (char *)cupsArrayNext(Communities
))
1201 debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
1202 community
, address
);
1204 for (addr
= addrs
; addr
; addr
= addr
->next
)
1207 if (httpAddrFamily(&(addr
->addr
)) == AF_INET6
)
1210 #endif /* AF_INET6 */
1213 debug_printf("DEBUG: Sending get request to %s...\n",
1214 httpAddrString(&(addr
->addr
), temp
, sizeof(temp
)));
1216 _cupsSNMPWrite(fd
, &(addr
->addr
), CUPS_SNMP_VERSION_1
, community
,
1217 CUPS_ASN1_GET_REQUEST
, DEVICE_TYPE
, DeviceTypeOID
);
1221 httpAddrFreeList(addrs
);
1225 * Then read any responses that come in over the next 3 seconds...
1228 endtime
= time(NULL
) + MaxRunTime
;
1232 while (time(NULL
) < endtime
)
1235 timeout
.tv_usec
= 0;
1237 FD_SET(ipv4
, &input
);
1239 FD_SET(ipv6
, &input
);
1241 fd
= ipv4
> ipv6
? ipv4
: ipv6
;
1242 if (select(fd
+ 1, &input
, NULL
, NULL
, &timeout
) < 0)
1244 fprintf(stderr
, "ERROR: %.3f select() for %d/%d failed: %s\n", run_time(),
1245 ipv4
, ipv6
, strerror(errno
));
1251 if (FD_ISSET(ipv4
, &input
))
1253 read_snmp_response(ipv4
);
1257 if (ipv6
>= 0 && FD_ISSET(ipv6
, &input
))
1259 read_snmp_response(ipv6
);
1266 * List devices with complete information...
1269 int sent_something
= 0;
1271 for (device
= (snmp_cache_t
*)cupsArrayFirst(Devices
);
1273 device
= (snmp_cache_t
*)cupsArrayNext(Devices
))
1274 if (!device
->sent
&& device
->info
&& device
->make_and_model
)
1277 list_device(device
);
1279 probe_device(device
);
1281 device
->sent
= sent_something
= 1;
1284 if (!sent_something
)
1289 debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
1294 * 'try_connect()' - Try connecting on a port...
1297 static int /* O - 0 on success or -1 on error */
1298 try_connect(http_addr_t
*addr
, /* I - Socket address */
1299 const char *addrname
, /* I - Hostname or IP address */
1300 int port
) /* I - Port number */
1302 int fd
; /* Socket */
1303 int status
; /* Connection status */
1306 debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
1307 port
== 515 ? "lpd" : "socket", addrname
, port
);
1309 if ((fd
= socket(httpAddrFamily(addr
), SOCK_STREAM
, 0)) < 0)
1311 fprintf(stderr
, "ERROR: Unable to create socket: %s\n",
1316 _httpAddrSetPort(addr
, port
);
1320 status
= connect(fd
, (void *)addr
, (socklen_t
)httpAddrLength(addr
));
1330 * 'update_cache()' - Update a cached device...
1334 update_cache(snmp_cache_t
*device
, /* I - Device */
1335 const char *uri
, /* I - Device URI */
1336 const char *id
, /* I - Device ID */
1337 const char *make_model
) /* I - Device make and model */
1342 device
->uri
= strdup(uri
);
1349 device
->id
= strdup(id
);
1354 if (device
->make_and_model
)
1355 free(device
->make_and_model
);
1357 device
->make_and_model
= strdup(make_model
);
1360 list_device(device
);