2 * DNS-SD discovery backend for CUPS.
4 * Copyright © 2008-2018 by Apple Inc.
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
11 * Include necessary headers.
14 #include "backend-private.h"
15 #include <cups/array.h>
18 #endif /* HAVE_DNSSD */
20 # include <avahi-client/client.h>
21 # include <avahi-client/lookup.h>
22 # include <avahi-common/simple-watch.h>
23 # include <avahi-common/domain.h>
24 # include <avahi-common/error.h>
25 # include <avahi-common/malloc.h>
26 #define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
27 #endif /* HAVE_AVAHI */
36 CUPS_DEVICE_PRINTER
= 0, /* lpd://... */
37 CUPS_DEVICE_IPPS
, /* ipps://... */
38 CUPS_DEVICE_IPP
, /* ipp://... */
39 CUPS_DEVICE_FAX_IPP
, /* ipp://... */
40 CUPS_DEVICE_PDL_DATASTREAM
, /* socket://... */
41 CUPS_DEVICE_RIOUSBPRINT
/* riousbprint://... */
48 DNSServiceRef ref
; /* Service reference for query */
49 #endif /* HAVE_DNSSD */
51 AvahiRecordBrowser
*ref
; /* Browser for query */
52 #endif /* HAVE_AVAHI */
53 char *name
, /* Service name */
54 *domain
, /* Domain name */
55 *fullName
, /* Full name */
56 *make_and_model
, /* Make and model from TXT record */
57 *device_id
, /* 1284 device ID from TXT record */
58 *uuid
; /* UUID from TXT record */
59 cups_devtype_t type
; /* Device registration type */
60 int priority
, /* Priority associated with type */
61 cups_shared
, /* CUPS shared printer? */
62 sent
; /* Did we list the device? */
70 static int job_canceled
= 0;
71 /* Set to 1 on SIGTERM */
73 static AvahiSimplePoll
*simple_poll
= NULL
;
74 /* Poll information */
75 static int got_data
= 0; /* Got data from poll? */
76 static int browsers
= 0; /* Number of running browsers */
77 #endif /* HAVE_AVAHI */
85 static void browse_callback(DNSServiceRef sdRef
, DNSServiceFlags flags
, uint32_t interfaceIndex
, DNSServiceErrorType errorCode
, const char *serviceName
, const char *regtype
, const char *replyDomain
, void *context
) _CUPS_NONNULL(1,5,6,7,8);
86 static void browse_local_callback(DNSServiceRef sdRef
, DNSServiceFlags flags
, uint32_t interfaceIndex
, DNSServiceErrorType errorCode
, const char *serviceName
, const char *regtype
, const char *replyDomain
, void *context
) _CUPS_NONNULL(1,5,6,7,8);
87 #endif /* HAVE_DNSSD */
89 static void browse_callback(AvahiServiceBrowser
*browser
,
90 AvahiIfIndex interface
,
91 AvahiProtocol protocol
,
92 AvahiBrowserEvent event
,
93 const char *serviceName
,
95 const char *replyDomain
,
96 AvahiLookupResultFlags flags
,
98 static void client_callback(AvahiClient
*client
,
99 AvahiClientState state
,
101 #endif /* HAVE_AVAHI */
103 static int compare_devices(cups_device_t
*a
, cups_device_t
*b
);
104 static void exec_backend(char **argv
) _CUPS_NORETURN
;
105 static cups_device_t
*get_device(cups_array_t
*devices
, const char *serviceName
, const char *regtype
, const char *replyDomain
) _CUPS_NONNULL(1,2,3,4);
107 static void query_callback(DNSServiceRef sdRef
, DNSServiceFlags flags
, uint32_t interfaceIndex
, DNSServiceErrorType errorCode
, const char *fullName
, uint16_t rrtype
, uint16_t rrclass
, uint16_t rdlen
, const void *rdata
, uint32_t ttl
, void *context
) _CUPS_NONNULL(1,5,9,11);
108 #elif defined(HAVE_AVAHI)
109 static int poll_callback(struct pollfd
*pollfds
,
110 unsigned int num_pollfds
, int timeout
,
112 static void query_callback(AvahiRecordBrowser
*browser
,
113 AvahiIfIndex interface
,
114 AvahiProtocol protocol
,
115 AvahiBrowserEvent event
,
116 const char *name
, uint16_t rrclass
,
117 uint16_t rrtype
, const void *rdata
,
119 AvahiLookupResultFlags flags
,
121 #endif /* HAVE_DNSSD */
122 static void sigterm_handler(int sig
);
123 static void unquote(char *dst
, const char *src
, size_t dstsize
) _CUPS_NONNULL(1,2);
127 * 'main()' - Browse for printers.
130 int /* O - Exit status */
131 main(int argc
, /* I - Number of command-line args */
132 char *argv
[]) /* I - Command-line arguments */
134 const char *name
; /* Backend name */
135 cups_array_t
*devices
; /* Device array */
136 cups_device_t
*device
; /* Current device */
137 char uriName
[1024]; /* Unquoted fullName for URI */
139 int fd
; /* Main file descriptor */
140 fd_set input
; /* Input set for select() */
141 struct timeval timeout
; /* Timeout for select() */
142 DNSServiceRef main_ref
, /* Main service reference */
143 fax_ipp_ref
, /* IPP fax service reference */
144 ipp_ref
, /* IPP service reference */
145 ipp_tls_ref
, /* IPP w/TLS service reference */
146 ipps_ref
, /* IPP service reference */
147 local_fax_ipp_ref
, /* Local IPP fax service reference */
148 local_ipp_ref
, /* Local IPP service reference */
149 local_ipp_tls_ref
, /* Local IPP w/TLS service reference */
150 local_ipps_ref
, /* Local IPP service reference */
151 local_printer_ref
, /* Local LPD service reference */
152 pdl_datastream_ref
, /* AppSocket service reference */
153 printer_ref
, /* LPD service reference */
154 riousbprint_ref
; /* Remote IO service reference */
155 #endif /* HAVE_DNSSD */
157 AvahiClient
*client
; /* Client information */
158 int error
; /* Error code, if any */
159 #endif /* HAVE_AVAHI */
160 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
161 struct sigaction action
; /* Actions for POSIX signals */
162 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
166 * Don't buffer stderr, and catch SIGTERM...
169 setbuf(stderr
, NULL
);
171 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
172 sigset(SIGTERM
, sigterm_handler
);
173 #elif defined(HAVE_SIGACTION)
174 memset(&action
, 0, sizeof(action
));
176 sigemptyset(&action
.sa_mask
);
177 action
.sa_handler
= sigterm_handler
;
178 sigaction(SIGTERM
, &action
, NULL
);
180 signal(SIGTERM
, sigterm_handler
);
181 #endif /* HAVE_SIGSET */
184 * Check command-line...
191 _cupsLangPrintf(stderr
,
192 _("Usage: %s job-id user title copies options [file]"),
198 * Only do discovery when run as "dnssd"...
201 if ((name
= strrchr(argv
[0], '/')) != NULL
)
206 if (strcmp(name
, "dnssd"))
210 * Create an array to track devices...
213 devices
= cupsArrayNew((cups_array_func_t
)compare_devices
, NULL
);
216 * Browse for different kinds of printers...
220 if (DNSServiceCreateConnection(&main_ref
) != kDNSServiceErr_NoError
)
222 perror("ERROR: Unable to create service connection");
226 fd
= DNSServiceRefSockFD(main_ref
);
228 fax_ipp_ref
= main_ref
;
229 DNSServiceBrowse(&fax_ipp_ref
, kDNSServiceFlagsShareConnection
, 0,
230 "_fax-ipp._tcp", NULL
, browse_callback
, devices
);
233 DNSServiceBrowse(&ipp_ref
, kDNSServiceFlagsShareConnection
, 0,
234 "_ipp._tcp", NULL
, browse_callback
, devices
);
236 ipp_tls_ref
= main_ref
;
237 DNSServiceBrowse(&ipp_tls_ref
, kDNSServiceFlagsShareConnection
, 0,
238 "_ipp-tls._tcp", NULL
, browse_callback
, devices
);
241 DNSServiceBrowse(&ipps_ref
, kDNSServiceFlagsShareConnection
, 0,
242 "_ipps._tcp", NULL
, browse_callback
, devices
);
244 local_fax_ipp_ref
= main_ref
;
245 DNSServiceBrowse(&local_fax_ipp_ref
, kDNSServiceFlagsShareConnection
,
246 kDNSServiceInterfaceIndexLocalOnly
,
247 "_fax-ipp._tcp", NULL
, browse_local_callback
, devices
);
249 local_ipp_ref
= main_ref
;
250 DNSServiceBrowse(&local_ipp_ref
, kDNSServiceFlagsShareConnection
,
251 kDNSServiceInterfaceIndexLocalOnly
,
252 "_ipp._tcp", NULL
, browse_local_callback
, devices
);
254 local_ipp_tls_ref
= main_ref
;
255 DNSServiceBrowse(&local_ipp_tls_ref
, kDNSServiceFlagsShareConnection
,
256 kDNSServiceInterfaceIndexLocalOnly
,
257 "_ipp-tls._tcp", NULL
, browse_local_callback
, devices
);
259 local_ipps_ref
= main_ref
;
260 DNSServiceBrowse(&local_ipps_ref
, kDNSServiceFlagsShareConnection
,
261 kDNSServiceInterfaceIndexLocalOnly
,
262 "_ipps._tcp", NULL
, browse_local_callback
, devices
);
264 local_printer_ref
= main_ref
;
265 DNSServiceBrowse(&local_printer_ref
, kDNSServiceFlagsShareConnection
,
266 kDNSServiceInterfaceIndexLocalOnly
,
267 "_printer._tcp", NULL
, browse_local_callback
, devices
);
269 pdl_datastream_ref
= main_ref
;
270 DNSServiceBrowse(&pdl_datastream_ref
, kDNSServiceFlagsShareConnection
, 0,
271 "_pdl-datastream._tcp", NULL
, browse_callback
, devices
);
273 printer_ref
= main_ref
;
274 DNSServiceBrowse(&printer_ref
, kDNSServiceFlagsShareConnection
, 0,
275 "_printer._tcp", NULL
, browse_callback
, devices
);
277 riousbprint_ref
= main_ref
;
278 DNSServiceBrowse(&riousbprint_ref
, kDNSServiceFlagsShareConnection
, 0,
279 "_riousbprint._tcp", NULL
, browse_callback
, devices
);
280 #endif /* HAVE_DNSSD */
283 if ((simple_poll
= avahi_simple_poll_new()) == NULL
)
285 fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr
);
289 avahi_simple_poll_set_func(simple_poll
, poll_callback
, NULL
);
291 client
= avahi_client_new(avahi_simple_poll_get(simple_poll
),
292 0, client_callback
, simple_poll
, &error
);
295 fputs("DEBUG: Unable to create Avahi client.\n", stderr
);
300 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
302 "_fax-ipp._tcp", NULL
, 0,
303 browse_callback
, devices
);
304 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
306 "_ipp._tcp", NULL
, 0,
307 browse_callback
, devices
);
308 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
310 "_ipp-tls._tcp", NULL
, 0,
311 browse_callback
, devices
);
312 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
314 "_ipps._tcp", NULL
, 0,
315 browse_callback
, devices
);
316 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
318 "_pdl-datastream._tcp",
322 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
324 "_printer._tcp", NULL
, 0,
325 browse_callback
, devices
);
326 #endif /* HAVE_AVAHI */
329 * Loop until we are killed...
332 while (!job_canceled
)
334 int announce
= 0; /* Announce printers? */
341 timeout
.tv_usec
= 500000;
343 if (select(fd
+ 1, &input
, NULL
, NULL
, &timeout
) < 0)
346 if (FD_ISSET(fd
, &input
))
349 * Process results of our browsing...
352 DNSServiceProcessResult(main_ref
);
357 #elif defined(HAVE_AVAHI)
360 if ((error
= avahi_simple_poll_iterate(simple_poll
, 500)) > 0)
363 * We've been told to exit the loop. Perhaps the connection to
372 #endif /* HAVE_DNSSD */
374 /* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
379 * Announce any devices we've found...
383 DNSServiceErrorType status
; /* DNS query status */
384 #endif /* HAVE_DNSSD */
385 cups_device_t
*best
; /* Best matching device */
386 char device_uri
[1024]; /* Device URI */
387 int count
; /* Number of queries */
388 int sent
; /* Number of sent */
390 for (device
= (cups_device_t
*)cupsArrayFirst(devices
),
391 best
= NULL
, count
= 0, sent
= 0;
393 device
= (cups_device_t
*)cupsArrayNext(devices
))
401 if (!device
->ref
&& !device
->sent
)
404 * Found the device, now get the TXT record(s) for it...
409 fprintf(stderr
, "DEBUG: Querying \"%s\"...\n", device
->fullName
);
412 device
->ref
= main_ref
;
414 status
= DNSServiceQueryRecord(&(device
->ref
),
415 kDNSServiceFlagsShareConnection
,
418 kDNSServiceClass_IN
, query_callback
,
420 if (status
!= kDNSServiceErr_NoError
)
422 "ERROR: Unable to query \"%s\" for TXT records: %d\n",
423 device
->fullName
, status
);
424 /* Users never see this */
429 if ((device
->ref
= avahi_record_browser_new(client
, AVAHI_IF_UNSPEC
,
438 "ERROR: Unable to query \"%s\" for TXT records: %s\n",
440 avahi_strerror(avahi_client_errno(client
)));
441 /* Users never see this */
444 #endif /* HAVE_AVAHI */
447 else if (!device
->sent
)
451 * Got the TXT records, now report the device...
454 DNSServiceRefDeallocate(device
->ref
);
456 avahi_record_browser_free(device
->ref
);
457 #endif /* HAVE_DNSSD */
463 else if (_cups_strcasecmp(best
->name
, device
->name
) ||
464 _cups_strcasecmp(best
->domain
, device
->domain
))
466 unquote(uriName
, best
->fullName
, sizeof(uriName
));
469 httpAssembleURIf(HTTP_URI_CODING_ALL
, device_uri
,
470 sizeof(device_uri
), "dnssd", NULL
, uriName
, 0,
471 best
->cups_shared
? "/cups?uuid=%s" : "/?uuid=%s",
474 httpAssembleURI(HTTP_URI_CODING_ALL
, device_uri
,
475 sizeof(device_uri
), "dnssd", NULL
, uriName
, 0,
476 best
->cups_shared
? "/cups" : "/");
478 cupsBackendReport("network", device_uri
, best
->make_and_model
,
479 best
->name
, best
->device_id
, NULL
);
485 else if (best
->priority
> device
->priority
||
486 (best
->priority
== device
->priority
&&
487 best
->type
< device
->type
))
505 unquote(uriName
, best
->fullName
, sizeof(uriName
));
508 httpAssembleURIf(HTTP_URI_CODING_ALL
, device_uri
,
509 sizeof(device_uri
), "dnssd", NULL
, uriName
, 0,
510 best
->cups_shared
? "/cups?uuid=%s" : "/?uuid=%s",
513 httpAssembleURI(HTTP_URI_CODING_ALL
, device_uri
,
514 sizeof(device_uri
), "dnssd", NULL
, uriName
, 0,
515 best
->cups_shared
? "/cups" : "/");
517 cupsBackendReport("network", device_uri
, best
->make_and_model
,
518 best
->name
, best
->device_id
, NULL
);
523 fprintf(stderr
, "DEBUG: sent=%d, count=%d\n", sent
, count
);
526 if (sent
== cupsArrayCount(devices
) && browsers
== 0)
528 if (sent
== cupsArrayCount(devices
))
529 #endif /* HAVE_AVAHI */
534 return (CUPS_BACKEND_OK
);
540 * 'browse_callback()' - Browse devices.
545 DNSServiceRef sdRef
, /* I - Service reference */
546 DNSServiceFlags flags
, /* I - Option flags */
547 uint32_t interfaceIndex
, /* I - Interface number */
548 DNSServiceErrorType errorCode
, /* I - Error, if any */
549 const char *serviceName
, /* I - Name of service/device */
550 const char *regtype
, /* I - Type of service */
551 const char *replyDomain
, /* I - Service domain */
552 void *context
) /* I - Devices array */
554 fprintf(stderr
, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
555 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
556 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
557 sdRef
, flags
, interfaceIndex
, errorCode
,
558 serviceName
, regtype
, replyDomain
, context
);
561 * Only process "add" data...
564 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
571 get_device((cups_array_t
*)context
, serviceName
, regtype
, replyDomain
);
576 * 'browse_local_callback()' - Browse local devices.
580 browse_local_callback(
581 DNSServiceRef sdRef
, /* I - Service reference */
582 DNSServiceFlags flags
, /* I - Option flags */
583 uint32_t interfaceIndex
, /* I - Interface number */
584 DNSServiceErrorType errorCode
, /* I - Error, if any */
585 const char *serviceName
, /* I - Name of service/device */
586 const char *regtype
, /* I - Type of service */
587 const char *replyDomain
, /* I - Service domain */
588 void *context
) /* I - Devices array */
590 cups_device_t
*device
; /* Device */
593 fprintf(stderr
, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
594 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
595 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
596 sdRef
, flags
, interfaceIndex
, errorCode
,
597 serviceName
, regtype
, replyDomain
, context
);
600 * Only process "add" data...
603 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
610 device
= get_device((cups_array_t
*)context
, serviceName
, regtype
,
614 * Hide locally-registered devices...
617 fprintf(stderr
, "DEBUG: Hiding local printer \"%s\"...\n",
621 #endif /* HAVE_DNSSD */
626 * 'browse_callback()' - Browse devices.
631 AvahiServiceBrowser
*browser
, /* I - Browser */
632 AvahiIfIndex interface
, /* I - Interface index (unused) */
633 AvahiProtocol protocol
, /* I - Network protocol (unused) */
634 AvahiBrowserEvent event
, /* I - What happened */
635 const char *name
, /* I - Service name */
636 const char *type
, /* I - Registration type */
637 const char *domain
, /* I - Domain */
638 AvahiLookupResultFlags flags
, /* I - Flags */
639 void *context
) /* I - Devices array */
641 AvahiClient
*client
= avahi_service_browser_get_client(browser
);
642 /* Client information */
651 case AVAHI_BROWSER_FAILURE
:
652 fprintf(stderr
, "DEBUG: browse_callback: %s\n",
653 avahi_strerror(avahi_client_errno(client
)));
654 avahi_simple_poll_quit(simple_poll
);
657 case AVAHI_BROWSER_NEW
:
659 * This object is new on the network.
662 if (flags
& AVAHI_LOOKUP_RESULT_LOCAL
)
665 * This comes from the local machine so ignore it.
668 fprintf(stderr
, "DEBUG: Ignoring local service %s.\n", name
);
673 * Create a device entry for it if it doesn't yet exist.
676 get_device((cups_array_t
*)context
, name
, type
, domain
);
680 case AVAHI_BROWSER_REMOVE
:
681 case AVAHI_BROWSER_CACHE_EXHAUSTED
:
684 case AVAHI_BROWSER_ALL_FOR_NOW
:
692 * 'client_callback()' - Avahi client callback function.
697 AvahiClient
*client
, /* I - Client information (unused) */
698 AvahiClientState state
, /* I - Current state */
699 void *context
) /* I - User data (unused) */
705 * If the connection drops, quit.
708 if (state
== AVAHI_CLIENT_FAILURE
)
710 fputs("DEBUG: Avahi connection failed.\n", stderr
);
711 avahi_simple_poll_quit(simple_poll
);
714 #endif /* HAVE_AVAHI */
718 * 'compare_devices()' - Compare two devices.
721 static int /* O - Result of comparison */
722 compare_devices(cups_device_t
*a
, /* I - First device */
723 cups_device_t
*b
) /* I - Second device */
725 return (strcmp(a
->name
, b
->name
));
730 * 'exec_backend()' - Execute the backend that corresponds to the
731 * resolved service name.
735 exec_backend(char **argv
) /* I - Command-line arguments */
737 const char *resolved_uri
, /* Resolved device URI */
738 *cups_serverbin
; /* Location of programs */
739 char scheme
[1024], /* Scheme from URI */
740 *ptr
, /* Pointer into scheme */
741 filename
[1024]; /* Backend filename */
745 * Resolve the device URI...
750 while ((resolved_uri
= cupsBackendDeviceURI(argv
)) == NULL
)
752 _cupsLangPrintFilter(stderr
, "INFO", _("Unable to locate printer."));
755 if (getenv("CLASS") != NULL
)
756 exit(CUPS_BACKEND_FAILED
);
760 * Extract the scheme from the URI...
763 strlcpy(scheme
, resolved_uri
, sizeof(scheme
));
764 if ((ptr
= strchr(scheme
, ':')) != NULL
)
768 * Get the filename of the backend...
771 if ((cups_serverbin
= getenv("CUPS_SERVERBIN")) == NULL
)
772 cups_serverbin
= CUPS_SERVERBIN
;
774 snprintf(filename
, sizeof(filename
), "%s/backend/%s", cups_serverbin
, scheme
);
777 * Overwrite the device URI and run the new backend...
780 setenv("DEVICE_URI", resolved_uri
, 1);
782 argv
[0] = (char *)resolved_uri
;
784 fprintf(stderr
, "DEBUG: Executing backend \"%s\"...\n", filename
);
786 execv(filename
, argv
);
788 fprintf(stderr
, "ERROR: Unable to execute backend \"%s\": %s\n", filename
,
790 exit(CUPS_BACKEND_STOP
);
795 * 'device_type()' - Get DNS-SD type enumeration from string.
798 static cups_devtype_t
/* O - Device type */
799 device_type(const char *regtype
) /* I - Service registration type */
802 if (!strcmp(regtype
, "_ipp._tcp"))
803 return (CUPS_DEVICE_IPP
);
804 else if (!strcmp(regtype
, "_ipps._tcp") ||
805 !strcmp(regtype
, "_ipp-tls._tcp"))
806 return (CUPS_DEVICE_IPPS
);
807 else if (!strcmp(regtype
, "_fax-ipp._tcp"))
808 return (CUPS_DEVICE_FAX_IPP
);
809 else if (!strcmp(regtype
, "_printer._tcp"))
810 return (CUPS_DEVICE_PDL_DATASTREAM
);
812 if (!strcmp(regtype
, "_ipp._tcp."))
813 return (CUPS_DEVICE_IPP
);
814 else if (!strcmp(regtype
, "_ipps._tcp.") ||
815 !strcmp(regtype
, "_ipp-tls._tcp."))
816 return (CUPS_DEVICE_IPPS
);
817 else if (!strcmp(regtype
, "_fax-ipp._tcp."))
818 return (CUPS_DEVICE_FAX_IPP
);
819 else if (!strcmp(regtype
, "_printer._tcp."))
820 return (CUPS_DEVICE_PRINTER
);
821 else if (!strcmp(regtype
, "_pdl-datastream._tcp."))
822 return (CUPS_DEVICE_PDL_DATASTREAM
);
823 #endif /* HAVE_AVAHI */
825 return (CUPS_DEVICE_RIOUSBPRINT
);
830 * 'get_device()' - Create or update a device.
833 static cups_device_t
* /* O - Device */
834 get_device(cups_array_t
*devices
, /* I - Device array */
835 const char *serviceName
, /* I - Name of service/device */
836 const char *regtype
, /* I - Type of service */
837 const char *replyDomain
) /* I - Service domain */
839 cups_device_t key
, /* Search key */
840 *device
; /* Device */
841 char fullName
[kDNSServiceMaxDomainName
];
842 /* Full name for query */
846 * See if this is a new device...
849 key
.name
= (char *)serviceName
;
850 key
.type
= device_type(regtype
);
852 for (device
= cupsArrayFind(devices
, &key
);
854 device
= cupsArrayNext(devices
))
855 if (_cups_strcasecmp(device
->name
, key
.name
))
857 else if (device
->type
== key
.type
)
859 if (!_cups_strcasecmp(device
->domain
, "local.") &&
860 _cups_strcasecmp(device
->domain
, replyDomain
))
863 * Update the .local listing to use the "global" domain name instead.
864 * The backend will try local lookups first, then the global domain name.
867 free(device
->domain
);
868 device
->domain
= strdup(replyDomain
);
871 DNSServiceConstructFullName(fullName
, device
->name
, regtype
,
873 #else /* HAVE_AVAHI */
874 avahi_service_name_join(fullName
, kDNSServiceMaxDomainName
,
875 serviceName
, regtype
, replyDomain
);
876 #endif /* HAVE_DNSSD */
878 free(device
->fullName
);
879 device
->fullName
= strdup(fullName
);
886 * Yes, add the device...
889 device
= calloc(sizeof(cups_device_t
), 1);
890 device
->name
= strdup(serviceName
);
891 device
->domain
= strdup(replyDomain
);
892 device
->type
= key
.type
;
893 device
->priority
= 50;
895 cupsArrayAdd(devices
, device
);
898 * Set the "full name" of this service, which is used for queries...
902 DNSServiceConstructFullName(fullName
, serviceName
, regtype
, replyDomain
);
903 #else /* HAVE_AVAHI */
904 avahi_service_name_join(fullName
, kDNSServiceMaxDomainName
, serviceName
, regtype
, replyDomain
);
905 #endif /* HAVE_DNSSD */
907 device
->fullName
= strdup(fullName
);
915 * 'poll_callback()' - Wait for input on the specified file descriptors.
917 * Note: This function is needed because avahi_simple_poll_iterate is broken
918 * and always uses a timeout of 0 (!) milliseconds.
919 * (https://github.com/lathiat/avahi/issues/127)
922 static int /* O - Number of file descriptors matching */
924 struct pollfd
*pollfds
, /* I - File descriptors */
925 unsigned int num_pollfds
, /* I - Number of file descriptors */
926 int timeout
, /* I - Timeout in milliseconds (unused) */
927 void *context
) /* I - User data (unused) */
929 int val
; /* Return value */
935 val
= poll(pollfds
, num_pollfds
, 500);
938 fprintf(stderr
, "DEBUG: poll_callback: %s\n", strerror(errno
));
944 #endif /* HAVE_AVAHI */
947 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
950 * 'query_callback()' - Process query data.
955 DNSServiceRef sdRef
, /* I - Service reference */
956 DNSServiceFlags flags
, /* I - Data flags */
957 uint32_t interfaceIndex
, /* I - Interface */
958 DNSServiceErrorType errorCode
, /* I - Error, if any */
959 const char *fullName
, /* I - Full service name */
960 uint16_t rrtype
, /* I - Record type */
961 uint16_t rrclass
, /* I - Record class */
962 uint16_t rdlen
, /* I - Length of record data */
963 const void *rdata
, /* I - Record data */
964 uint32_t ttl
, /* I - Time-to-live */
965 void *context
) /* I - Device */
969 * 'query_callback()' - Process query data.
974 AvahiRecordBrowser
*browser
, /* I - Record browser */
975 AvahiIfIndex interfaceIndex
,
976 /* I - Interface index (unused) */
977 AvahiProtocol protocol
, /* I - Network protocol (unused) */
978 AvahiBrowserEvent event
, /* I - What happened? */
979 const char *fullName
, /* I - Service name */
980 uint16_t rrclass
, /* I - Record class */
981 uint16_t rrtype
, /* I - Record type */
982 const void *rdata
, /* I - TXT record */
983 size_t rdlen
, /* I - Length of TXT record */
984 AvahiLookupResultFlags flags
, /* I - Flags */
985 void *context
) /* I - Device */
987 AvahiClient
*client
= avahi_record_browser_get_client(browser
);
988 /* Client information */
989 # endif /* HAVE_DNSSD */
990 char *ptr
; /* Pointer into string */
991 cups_device_t
*device
= (cups_device_t
*)context
;
993 const uint8_t *data
, /* Pointer into data */
994 *datanext
, /* Next key/value pair */
995 *dataend
; /* End of entire TXT record */
996 uint8_t datalen
; /* Length of current key/value pair */
997 char key
[256], /* Key string */
998 value
[256], /* Value string */
999 make_and_model
[512], /* Manufacturer and model */
1000 model
[256], /* Model */
1002 device_id
[2048]; /* 1284 device ID */
1006 fprintf(stderr
, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
1007 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
1008 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
1010 sdRef
, flags
, interfaceIndex
, errorCode
, fullName
, rrtype
, rrclass
, rdlen
, rdata
, ttl
, context
);
1013 * Only process "add" data...
1016 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
1020 fprintf(stderr
, "DEBUG2: query_callback(browser=%p, interfaceIndex=%d, "
1021 "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
1022 "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
1023 browser
, interfaceIndex
, protocol
, event
, fullName
, rrclass
, rrtype
, rdata
, (unsigned)rdlen
, flags
, context
);
1026 * Only process "add" data...
1029 if (event
!= AVAHI_BROWSER_NEW
)
1031 if (event
== AVAHI_BROWSER_FAILURE
)
1032 fprintf(stderr
, "ERROR: %s\n",
1033 avahi_strerror(avahi_client_errno(client
)));
1037 # endif /* HAVE_DNSSD */
1040 * Pull out the priority and make and model from the TXT
1041 * record and save it...
1044 device_id
[0] = '\0';
1045 make_and_model
[0] = '\0';
1048 strlcpy(model
, "Unknown", sizeof(model
));
1050 for (data
= rdata
, dataend
= data
+ rdlen
;
1055 * Read a key/value pair starting with an 8-bit length. Since the
1056 * length is 8 bits and the size of the key/value buffers is 256, we
1057 * don't need to check for overflow...
1062 if (!datalen
|| (data
+ datalen
) > dataend
)
1065 datanext
= data
+ datalen
;
1067 for (ptr
= key
; data
< datanext
&& *data
!= '='; data
++)
1068 *ptr
++ = (char)*data
;
1071 if (data
< datanext
&& *data
== '=')
1075 if (data
< datanext
)
1076 memcpy(value
, data
, (size_t)(datanext
- data
));
1077 value
[datanext
- data
] = '\0';
1079 fprintf(stderr
, "DEBUG2: query_callback: \"%s=%s\".\n",
1084 fprintf(stderr
, "DEBUG2: query_callback: \"%s\" with no value.\n",
1089 if (!_cups_strncasecmp(key
, "usb_", 4))
1092 * Add USB device ID information...
1095 ptr
= device_id
+ strlen(device_id
);
1096 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "%s:%s;", key
+ 4, value
);
1099 if (!_cups_strcasecmp(key
, "usb_MFG") || !_cups_strcasecmp(key
, "usb_MANU") ||
1100 !_cups_strcasecmp(key
, "usb_MANUFACTURER"))
1101 strlcpy(make_and_model
, value
, sizeof(make_and_model
));
1102 else if (!_cups_strcasecmp(key
, "usb_MDL") || !_cups_strcasecmp(key
, "usb_MODEL"))
1103 strlcpy(model
, value
, sizeof(model
));
1104 else if (!_cups_strcasecmp(key
, "product") && !strstr(value
, "Ghostscript"))
1106 if (value
[0] == '(')
1109 * Strip parenthesis...
1112 if ((ptr
= value
+ strlen(value
) - 1) > value
&& *ptr
== ')')
1115 strlcpy(model
, value
+ 1, sizeof(model
));
1118 strlcpy(model
, value
, sizeof(model
));
1120 else if (!_cups_strcasecmp(key
, "ty"))
1122 strlcpy(model
, value
, sizeof(model
));
1124 if ((ptr
= strchr(model
, ',')) != NULL
)
1127 else if (!_cups_strcasecmp(key
, "pdl"))
1128 strlcpy(pdl
, value
, sizeof(pdl
));
1129 else if (!_cups_strcasecmp(key
, "priority"))
1130 device
->priority
= atoi(value
);
1131 else if ((device
->type
== CUPS_DEVICE_IPP
||
1132 device
->type
== CUPS_DEVICE_IPPS
||
1133 device
->type
== CUPS_DEVICE_PRINTER
) &&
1134 !_cups_strcasecmp(key
, "printer-type"))
1137 * This is a CUPS printer!
1140 device
->cups_shared
= 1;
1142 if (device
->type
== CUPS_DEVICE_PRINTER
)
1145 else if (!_cups_strcasecmp(key
, "UUID"))
1146 device
->uuid
= strdup(value
);
1149 if (device
->device_id
)
1150 free(device
->device_id
);
1152 if (!device_id
[0] && strcmp(model
, "Unknown"))
1154 if (make_and_model
[0])
1155 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;",
1156 make_and_model
, model
);
1157 else if (!_cups_strncasecmp(model
, "designjet ", 10))
1158 snprintf(device_id
, sizeof(device_id
), "MFG:HP;MDL:%s;", model
+ 10);
1159 else if (!_cups_strncasecmp(model
, "stylus ", 7))
1160 snprintf(device_id
, sizeof(device_id
), "MFG:EPSON;MDL:%s;", model
+ 7);
1161 else if ((ptr
= strchr(model
, ' ')) != NULL
)
1164 * Assume the first word is the make...
1167 memcpy(make_and_model
, model
, (size_t)(ptr
- model
));
1168 make_and_model
[ptr
- model
] = '\0';
1170 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;",
1171 make_and_model
, ptr
+ 1);
1176 !strstr(device_id
, "CMD:") &&
1177 !strstr(device_id
, "COMMAND SET:") &&
1178 (strstr(pdl
, "application/pdf") ||
1179 strstr(pdl
, "application/postscript") ||
1180 strstr(pdl
, "application/vnd.hp-PCL") ||
1181 strstr(pdl
, "image/")))
1184 if (strstr(pdl
, "application/pdf"))
1185 strlcat(value
, ",PDF", sizeof(value
));
1186 if (strstr(pdl
, "application/postscript"))
1187 strlcat(value
, ",PS", sizeof(value
));
1188 if (strstr(pdl
, "application/vnd.hp-PCL"))
1189 strlcat(value
, ",PCL", sizeof(value
));
1190 for (ptr
= strstr(pdl
, "image/"); ptr
; ptr
= strstr(ptr
, "image/"))
1192 char *valptr
= value
+ strlen(value
);
1193 /* Pointer into value */
1195 if (valptr
< (value
+ sizeof(value
) - 1))
1199 while (isalnum(*ptr
& 255) || *ptr
== '-' || *ptr
== '.')
1201 if (isalnum(*ptr
& 255) && valptr
< (value
+ sizeof(value
) - 1))
1202 *valptr
++ = (char)toupper(*ptr
++ & 255);
1210 ptr
= device_id
+ strlen(device_id
);
1211 snprintf(ptr
, sizeof(device_id
) - (size_t)(ptr
- device_id
), "CMD:%s;", value
+ 1);
1215 device
->device_id
= strdup(device_id
);
1217 device
->device_id
= NULL
;
1219 if (device
->make_and_model
)
1220 free(device
->make_and_model
);
1222 if (make_and_model
[0])
1224 strlcat(make_and_model
, " ", sizeof(make_and_model
));
1225 strlcat(make_and_model
, model
, sizeof(make_and_model
));
1227 if (!_cups_strncasecmp(make_and_model
, "EPSON EPSON ", 12))
1228 _cups_strcpy(make_and_model
, make_and_model
+ 6);
1229 else if (!_cups_strncasecmp(make_and_model
, "HP HP ", 6))
1230 _cups_strcpy(make_and_model
, make_and_model
+ 3);
1231 else if (!_cups_strncasecmp(make_and_model
, "Lexmark International Lexmark ", 30))
1232 _cups_strcpy(make_and_model
, make_and_model
+ 22);
1234 device
->make_and_model
= strdup(make_and_model
);
1237 device
->make_and_model
= strdup(model
);
1239 #endif /* HAVE_DNSSD || HAVE_AVAHI */
1243 * 'sigterm_handler()' - Handle termination signals.
1247 sigterm_handler(int sig
) /* I - Signal number (unused) */
1252 _exit(CUPS_BACKEND_OK
);
1259 * 'unquote()' - Unquote a name string.
1263 unquote(char *dst
, /* I - Destination buffer */
1264 const char *src
, /* I - Source string */
1265 size_t dstsize
) /* I - Size of destination buffer */
1267 char *dstend
= dst
+ dstsize
- 1; /* End of destination buffer */
1270 while (*src
&& dst
< dstend
)
1275 if (isdigit(src
[0] & 255) && isdigit(src
[1] & 255) &&
1276 isdigit(src
[2] & 255))
1278 *dst
++ = ((((src
[0] - '0') * 10) + src
[1] - '0') * 10) + src
[2] - '0';