2 * "$Id: dnssd.c 11623 2014-02-19 20:18:10Z msweet $"
4 * DNS-SD discovery backend for CUPS.
6 * Copyright 2008-2012 by Apple Inc.
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged, see the license at "http://www.cups.org/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * main() - Browse for printers.
19 * browse_callback() - Browse devices.
20 * browse_local_callback() - Browse local devices.
21 * client_callback() - Avahi client callback function.
22 * compare_devices() - Compare two devices.
23 * exec_backend() - Execute the backend that corresponds to the
24 * resolved service name.
25 * device_type() - Get DNS-SD type enumeration from string.
26 * get_device() - Create or update a device.
27 * query_callback() - Process query data.
28 * find_device() - Find a device from its name and domain.
29 * sigterm_handler() - Handle termination signals.
30 * unquote() - Unquote a name string.
34 * Include necessary headers.
37 #include "backend-private.h"
38 #include <cups/array.h>
41 #endif /* HAVE_DNSSD */
43 # include <avahi-client/client.h>
44 # include <avahi-client/lookup.h>
45 # include <avahi-common/simple-watch.h>
46 # include <avahi-common/domain.h>
47 # include <avahi-common/error.h>
48 # include <avahi-common/malloc.h>
49 #define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
50 #endif /* HAVE_AVAHI */
59 CUPS_DEVICE_PRINTER
= 0, /* lpd://... */
60 CUPS_DEVICE_IPPS
, /* ipps://... */
61 CUPS_DEVICE_IPP
, /* ipp://... */
62 CUPS_DEVICE_FAX_IPP
, /* ipp://... */
63 CUPS_DEVICE_PDL_DATASTREAM
, /* socket://... */
64 CUPS_DEVICE_RIOUSBPRINT
/* riousbprint://... */
71 DNSServiceRef ref
; /* Service reference for query */
72 #endif /* HAVE_DNSSD */
74 AvahiRecordBrowser
*ref
; /* Browser for query */
75 #endif /* HAVE_AVAHI */
76 char *name
, /* Service name */
77 *domain
, /* Domain name */
78 *fullName
, /* Full name */
79 *make_and_model
, /* Make and model from TXT record */
80 *device_id
; /* 1284 device ID from TXT record */
81 cups_devtype_t type
; /* Device registration type */
82 int priority
, /* Priority associated with type */
83 cups_shared
, /* CUPS shared printer? */
84 sent
; /* Did we list the device? */
92 static int job_canceled
= 0;
93 /* Set to 1 on SIGTERM */
95 static AvahiSimplePoll
*simple_poll
= NULL
;
96 /* Poll information */
97 static int got_data
= 0; /* Got data from poll? */
98 static int browsers
= 0; /* Number of running browsers */
99 #endif /* HAVE_AVAHI */
107 static void browse_callback(DNSServiceRef sdRef
,
108 DNSServiceFlags flags
,
109 uint32_t interfaceIndex
,
110 DNSServiceErrorType errorCode
,
111 const char *serviceName
,
113 const char *replyDomain
, void *context
)
114 __attribute__((nonnull(1,5,6,7,8)));
115 static void browse_local_callback(DNSServiceRef sdRef
,
116 DNSServiceFlags flags
,
117 uint32_t interfaceIndex
,
118 DNSServiceErrorType errorCode
,
119 const char *serviceName
,
121 const char *replyDomain
,
123 __attribute__((nonnull(1,5,6,7,8)));
124 #endif /* HAVE_DNSSD */
126 static void browse_callback(AvahiServiceBrowser
*browser
,
127 AvahiIfIndex interface
,
128 AvahiProtocol protocol
,
129 AvahiBrowserEvent event
,
130 const char *serviceName
,
132 const char *replyDomain
,
133 AvahiLookupResultFlags flags
,
135 static void client_callback(AvahiClient
*client
,
136 AvahiClientState state
,
138 #endif /* HAVE_AVAHI */
140 static int compare_devices(cups_device_t
*a
, cups_device_t
*b
);
141 static void exec_backend(char **argv
);
142 static cups_device_t
*get_device(cups_array_t
*devices
,
143 const char *serviceName
,
145 const char *replyDomain
)
146 __attribute__((nonnull(1,2,3,4)));
148 static void query_callback(DNSServiceRef sdRef
,
149 DNSServiceFlags flags
,
150 uint32_t interfaceIndex
,
151 DNSServiceErrorType errorCode
,
152 const char *fullName
, uint16_t rrtype
,
153 uint16_t rrclass
, uint16_t rdlen
,
154 const void *rdata
, uint32_t ttl
,
156 __attribute__((nonnull(1,5,9,11)));
157 #elif defined(HAVE_AVAHI)
158 static int poll_callback(struct pollfd
*pollfds
,
159 unsigned int num_pollfds
, int timeout
,
161 static void query_callback(AvahiRecordBrowser
*browser
,
162 AvahiIfIndex interface
,
163 AvahiProtocol protocol
,
164 AvahiBrowserEvent event
,
165 const char *name
, uint16_t rrclass
,
166 uint16_t rrtype
, const void *rdata
,
168 AvahiLookupResultFlags flags
,
170 #endif /* HAVE_DNSSD */
171 static void sigterm_handler(int sig
);
172 static void unquote(char *dst
, const char *src
, size_t dstsize
)
173 __attribute__((nonnull(1,2)));
177 * 'main()' - Browse for printers.
180 int /* O - Exit status */
181 main(int argc
, /* I - Number of command-line args */
182 char *argv
[]) /* I - Command-line arguments */
184 const char *name
; /* Backend name */
185 cups_array_t
*devices
; /* Device array */
186 cups_device_t
*device
; /* Current device */
187 char uriName
[1024]; /* Unquoted fullName for URI */
189 int fd
; /* Main file descriptor */
190 fd_set input
; /* Input set for select() */
191 struct timeval timeout
; /* Timeout for select() */
192 DNSServiceRef main_ref
, /* Main service reference */
193 fax_ipp_ref
, /* IPP fax service reference */
194 ipp_ref
, /* IPP service reference */
195 ipp_tls_ref
, /* IPP w/TLS service reference */
196 ipps_ref
, /* IPP service reference */
197 local_fax_ipp_ref
, /* Local IPP fax service reference */
198 local_ipp_ref
, /* Local IPP service reference */
199 local_ipp_tls_ref
, /* Local IPP w/TLS service reference */
200 local_ipps_ref
, /* Local IPP service reference */
201 local_printer_ref
, /* Local LPD service reference */
202 pdl_datastream_ref
, /* AppSocket service reference */
203 printer_ref
, /* LPD service reference */
204 riousbprint_ref
; /* Remote IO service reference */
205 #endif /* HAVE_DNSSD */
207 AvahiClient
*client
; /* Client information */
208 int error
; /* Error code, if any */
209 #endif /* HAVE_AVAHI */
210 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
211 struct sigaction action
; /* Actions for POSIX signals */
212 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
216 * Don't buffer stderr, and catch SIGTERM...
219 setbuf(stderr
, NULL
);
221 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
222 sigset(SIGTERM
, sigterm_handler
);
223 #elif defined(HAVE_SIGACTION)
224 memset(&action
, 0, sizeof(action
));
226 sigemptyset(&action
.sa_mask
);
227 action
.sa_handler
= sigterm_handler
;
228 sigaction(SIGTERM
, &action
, NULL
);
230 signal(SIGTERM
, sigterm_handler
);
231 #endif /* HAVE_SIGSET */
234 * Check command-line...
241 _cupsLangPrintf(stderr
,
242 _("Usage: %s job-id user title copies options [file]"),
248 * Only do discovery when run as "dnssd"...
251 if ((name
= strrchr(argv
[0], '/')) != NULL
)
256 if (strcmp(name
, "dnssd"))
260 * Create an array to track devices...
263 devices
= cupsArrayNew((cups_array_func_t
)compare_devices
, NULL
);
266 * Browse for different kinds of printers...
270 if (DNSServiceCreateConnection(&main_ref
) != kDNSServiceErr_NoError
)
272 perror("ERROR: Unable to create service connection");
276 fd
= DNSServiceRefSockFD(main_ref
);
278 fax_ipp_ref
= main_ref
;
279 DNSServiceBrowse(&fax_ipp_ref
, kDNSServiceFlagsShareConnection
, 0,
280 "_fax-ipp._tcp", NULL
, browse_callback
, devices
);
283 DNSServiceBrowse(&ipp_ref
, kDNSServiceFlagsShareConnection
, 0,
284 "_ipp._tcp", NULL
, browse_callback
, devices
);
286 ipp_tls_ref
= main_ref
;
287 DNSServiceBrowse(&ipp_tls_ref
, kDNSServiceFlagsShareConnection
, 0,
288 "_ipp-tls._tcp", NULL
, browse_callback
, devices
);
291 DNSServiceBrowse(&ipps_ref
, kDNSServiceFlagsShareConnection
, 0,
292 "_ipps._tcp", NULL
, browse_callback
, devices
);
294 local_fax_ipp_ref
= main_ref
;
295 DNSServiceBrowse(&local_fax_ipp_ref
, kDNSServiceFlagsShareConnection
,
296 kDNSServiceInterfaceIndexLocalOnly
,
297 "_fax-ipp._tcp", NULL
, browse_local_callback
, devices
);
299 local_ipp_ref
= main_ref
;
300 DNSServiceBrowse(&local_ipp_ref
, kDNSServiceFlagsShareConnection
,
301 kDNSServiceInterfaceIndexLocalOnly
,
302 "_ipp._tcp", NULL
, browse_local_callback
, devices
);
304 local_ipp_tls_ref
= main_ref
;
305 DNSServiceBrowse(&local_ipp_tls_ref
, kDNSServiceFlagsShareConnection
,
306 kDNSServiceInterfaceIndexLocalOnly
,
307 "_ipp-tls._tcp", NULL
, browse_local_callback
, devices
);
309 local_ipps_ref
= main_ref
;
310 DNSServiceBrowse(&local_ipps_ref
, kDNSServiceFlagsShareConnection
,
311 kDNSServiceInterfaceIndexLocalOnly
,
312 "_ipps._tcp", NULL
, browse_local_callback
, devices
);
314 local_printer_ref
= main_ref
;
315 DNSServiceBrowse(&local_printer_ref
, kDNSServiceFlagsShareConnection
,
316 kDNSServiceInterfaceIndexLocalOnly
,
317 "_printer._tcp", NULL
, browse_local_callback
, devices
);
319 pdl_datastream_ref
= main_ref
;
320 DNSServiceBrowse(&pdl_datastream_ref
, kDNSServiceFlagsShareConnection
, 0,
321 "_pdl-datastream._tcp", NULL
, browse_callback
, devices
);
323 printer_ref
= main_ref
;
324 DNSServiceBrowse(&printer_ref
, kDNSServiceFlagsShareConnection
, 0,
325 "_printer._tcp", NULL
, browse_callback
, devices
);
327 riousbprint_ref
= main_ref
;
328 DNSServiceBrowse(&riousbprint_ref
, kDNSServiceFlagsShareConnection
, 0,
329 "_riousbprint._tcp", NULL
, browse_callback
, devices
);
330 #endif /* HAVE_DNSSD */
333 if ((simple_poll
= avahi_simple_poll_new()) == NULL
)
335 fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr
);
339 avahi_simple_poll_set_func(simple_poll
, poll_callback
, NULL
);
341 client
= avahi_client_new(avahi_simple_poll_get(simple_poll
),
342 0, client_callback
, simple_poll
, &error
);
345 fputs("DEBUG: Unable to create Avahi client.\n", stderr
);
350 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
352 "_fax-ipp._tcp", NULL
, 0,
353 browse_callback
, devices
);
354 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
356 "_ipp._tcp", NULL
, 0,
357 browse_callback
, devices
);
358 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
360 "_ipp-tls._tcp", NULL
, 0,
361 browse_callback
, devices
);
362 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
364 "_ipps._tcp", NULL
, 0,
365 browse_callback
, devices
);
366 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
368 "_pdl-datastream._tcp",
372 avahi_service_browser_new(client
, AVAHI_IF_UNSPEC
,
374 "_printer._tcp", NULL
, 0,
375 browse_callback
, devices
);
376 #endif /* HAVE_AVAHI */
379 * Loop until we are killed...
382 while (!job_canceled
)
384 int announce
= 0; /* Announce printers? */
391 timeout
.tv_usec
= 500000;
393 if (select(fd
+ 1, &input
, NULL
, NULL
, &timeout
) < 0)
396 if (FD_ISSET(fd
, &input
))
399 * Process results of our browsing...
402 DNSServiceProcessResult(main_ref
);
407 #elif defined(HAVE_AVAHI)
410 if ((error
= avahi_simple_poll_iterate(simple_poll
, 500)) > 0)
413 * We've been told to exit the loop. Perhaps the connection to
422 #endif /* HAVE_DNSSD */
424 /* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
429 * Announce any devices we've found...
433 DNSServiceErrorType status
; /* DNS query status */
434 #endif /* HAVE_DNSSD */
435 cups_device_t
*best
; /* Best matching device */
436 char device_uri
[1024]; /* Device URI */
437 int count
; /* Number of queries */
438 int sent
; /* Number of sent */
440 for (device
= (cups_device_t
*)cupsArrayFirst(devices
),
441 best
= NULL
, count
= 0, sent
= 0;
443 device
= (cups_device_t
*)cupsArrayNext(devices
))
451 if (!device
->ref
&& !device
->sent
)
454 * Found the device, now get the TXT record(s) for it...
459 fprintf(stderr
, "DEBUG: Querying \"%s\"...\n", device
->fullName
);
462 device
->ref
= main_ref
;
464 status
= DNSServiceQueryRecord(&(device
->ref
),
465 kDNSServiceFlagsShareConnection
,
468 kDNSServiceClass_IN
, query_callback
,
470 if (status
!= kDNSServiceErr_NoError
)
472 "ERROR: Unable to query \"%s\" for TXT records: %d\n",
473 device
->fullName
, status
);
474 /* Users never see this */
479 if ((device
->ref
= avahi_record_browser_new(client
, AVAHI_IF_UNSPEC
,
488 "ERROR: Unable to query \"%s\" for TXT records: %s\n",
490 avahi_strerror(avahi_client_errno(client
)));
491 /* Users never see this */
494 #endif /* HAVE_AVAHI */
497 else if (!device
->sent
)
501 * Got the TXT records, now report the device...
504 DNSServiceRefDeallocate(device
->ref
);
506 avahi_record_browser_free(device
->ref
);
507 #endif /* HAVE_DNSSD */
513 else if (_cups_strcasecmp(best
->name
, device
->name
) ||
514 _cups_strcasecmp(best
->domain
, device
->domain
))
516 unquote(uriName
, best
->fullName
, sizeof(uriName
));
518 httpAssembleURI(HTTP_URI_CODING_ALL
, device_uri
, sizeof(device_uri
),
519 "dnssd", NULL
, uriName
, 0,
520 best
->cups_shared
? "/cups" : "/");
522 cupsBackendReport("network", device_uri
, best
->make_and_model
,
523 best
->name
, best
->device_id
, NULL
);
529 else if (best
->priority
> device
->priority
||
530 (best
->priority
== device
->priority
&&
531 best
->type
< device
->type
))
549 unquote(uriName
, best
->fullName
, sizeof(uriName
));
551 httpAssembleURI(HTTP_URI_CODING_ALL
, device_uri
, sizeof(device_uri
),
552 "dnssd", NULL
, uriName
, 0,
553 best
->cups_shared
? "/cups" : "/");
555 cupsBackendReport("network", device_uri
, best
->make_and_model
,
556 best
->name
, best
->device_id
, NULL
);
561 fprintf(stderr
, "DEBUG: sent=%d, count=%d\n", sent
, count
);
564 if (sent
== cupsArrayCount(devices
) && browsers
== 0)
566 if (sent
== cupsArrayCount(devices
))
567 #endif /* HAVE_AVAHI */
572 return (CUPS_BACKEND_OK
);
578 * 'browse_callback()' - Browse devices.
583 DNSServiceRef sdRef
, /* I - Service reference */
584 DNSServiceFlags flags
, /* I - Option flags */
585 uint32_t interfaceIndex
, /* I - Interface number */
586 DNSServiceErrorType errorCode
, /* I - Error, if any */
587 const char *serviceName
, /* I - Name of service/device */
588 const char *regtype
, /* I - Type of service */
589 const char *replyDomain
, /* I - Service domain */
590 void *context
) /* I - Devices array */
592 fprintf(stderr
, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
593 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
594 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
595 sdRef
, flags
, interfaceIndex
, errorCode
,
596 serviceName
, regtype
, replyDomain
, context
);
599 * Only process "add" data...
602 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
609 get_device((cups_array_t
*)context
, serviceName
, regtype
, replyDomain
);
614 * 'browse_local_callback()' - Browse local devices.
618 browse_local_callback(
619 DNSServiceRef sdRef
, /* I - Service reference */
620 DNSServiceFlags flags
, /* I - Option flags */
621 uint32_t interfaceIndex
, /* I - Interface number */
622 DNSServiceErrorType errorCode
, /* I - Error, if any */
623 const char *serviceName
, /* I - Name of service/device */
624 const char *regtype
, /* I - Type of service */
625 const char *replyDomain
, /* I - Service domain */
626 void *context
) /* I - Devices array */
628 cups_device_t
*device
; /* Device */
631 fprintf(stderr
, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
632 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
633 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
634 sdRef
, flags
, interfaceIndex
, errorCode
,
635 serviceName
, regtype
, replyDomain
, context
);
638 * Only process "add" data...
641 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
648 device
= get_device((cups_array_t
*)context
, serviceName
, regtype
,
652 * Hide locally-registered devices...
655 fprintf(stderr
, "DEBUG: Hiding local printer \"%s\"...\n",
659 #endif /* HAVE_DNSSD */
664 * 'browse_callback()' - Browse devices.
669 AvahiServiceBrowser
*browser
, /* I - Browser */
670 AvahiIfIndex interface
, /* I - Interface index (unused) */
671 AvahiProtocol protocol
, /* I - Network protocol (unused) */
672 AvahiBrowserEvent event
, /* I - What happened */
673 const char *name
, /* I - Service name */
674 const char *type
, /* I - Registration type */
675 const char *domain
, /* I - Domain */
676 AvahiLookupResultFlags flags
, /* I - Flags */
677 void *context
) /* I - Devices array */
679 AvahiClient
*client
= avahi_service_browser_get_client(browser
);
680 /* Client information */
689 case AVAHI_BROWSER_FAILURE
:
690 fprintf(stderr
, "DEBUG: browse_callback: %s\n",
691 avahi_strerror(avahi_client_errno(client
)));
692 avahi_simple_poll_quit(simple_poll
);
695 case AVAHI_BROWSER_NEW
:
697 * This object is new on the network.
700 if (flags
& AVAHI_LOOKUP_RESULT_LOCAL
)
703 * This comes from the local machine so ignore it.
706 fprintf(stderr
, "DEBUG: Ignoring local service %s.\n", name
);
711 * Create a device entry for it if it doesn't yet exist.
714 get_device((cups_array_t
*)context
, name
, type
, domain
);
718 case AVAHI_BROWSER_REMOVE
:
719 case AVAHI_BROWSER_CACHE_EXHAUSTED
:
722 case AVAHI_BROWSER_ALL_FOR_NOW
:
730 * 'client_callback()' - Avahi client callback function.
735 AvahiClient
*client
, /* I - Client information (unused) */
736 AvahiClientState state
, /* I - Current state */
737 void *context
) /* I - User data (unused) */
743 * If the connection drops, quit.
746 if (state
== AVAHI_CLIENT_FAILURE
)
748 fputs("DEBUG: Avahi connection failed.\n", stderr
);
749 avahi_simple_poll_quit(simple_poll
);
752 #endif /* HAVE_AVAHI */
756 * 'compare_devices()' - Compare two devices.
759 static int /* O - Result of comparison */
760 compare_devices(cups_device_t
*a
, /* I - First device */
761 cups_device_t
*b
) /* I - Second device */
763 return (strcmp(a
->name
, b
->name
));
768 * 'exec_backend()' - Execute the backend that corresponds to the
769 * resolved service name.
773 exec_backend(char **argv
) /* I - Command-line arguments */
775 const char *resolved_uri
, /* Resolved device URI */
776 *cups_serverbin
; /* Location of programs */
777 char scheme
[1024], /* Scheme from URI */
778 *ptr
, /* Pointer into scheme */
779 filename
[1024]; /* Backend filename */
783 * Resolve the device URI...
788 while ((resolved_uri
= cupsBackendDeviceURI(argv
)) == NULL
)
790 _cupsLangPrintFilter(stderr
, "INFO", _("Unable to locate printer."));
793 if (getenv("CLASS") != NULL
)
794 exit(CUPS_BACKEND_FAILED
);
798 * Extract the scheme from the URI...
801 strlcpy(scheme
, resolved_uri
, sizeof(scheme
));
802 if ((ptr
= strchr(scheme
, ':')) != NULL
)
806 * Get the filename of the backend...
809 if ((cups_serverbin
= getenv("CUPS_SERVERBIN")) == NULL
)
810 cups_serverbin
= CUPS_SERVERBIN
;
812 snprintf(filename
, sizeof(filename
), "%s/backend/%s", cups_serverbin
, scheme
);
815 * Overwrite the device URI and run the new backend...
818 setenv("DEVICE_URI", resolved_uri
, 1);
820 argv
[0] = (char *)resolved_uri
;
822 fprintf(stderr
, "DEBUG: Executing backend \"%s\"...\n", filename
);
824 execv(filename
, argv
);
826 fprintf(stderr
, "ERROR: Unable to execute backend \"%s\": %s\n", filename
,
828 exit(CUPS_BACKEND_STOP
);
833 * 'device_type()' - Get DNS-SD type enumeration from string.
837 device_type(const char *regtype
)
840 if (!strcmp(regtype
, "_ipp._tcp"))
841 return (CUPS_DEVICE_IPP
);
842 else if (!strcmp(regtype
, "_ipps._tcp") ||
843 !strcmp(regtype
, "_ipp-tls._tcp"))
844 return (CUPS_DEVICE_IPPS
);
845 else if (!strcmp(regtype
, "_fax-ipp._tcp"))
846 return (CUPS_DEVICE_FAX_IPP
);
847 else if (!strcmp(regtype
, "_printer._tcp"))
848 return (CUPS_DEVICE_PDL_DATASTREAM
);
850 if (!strcmp(regtype
, "_ipp._tcp."))
851 return (CUPS_DEVICE_IPP
);
852 else if (!strcmp(regtype
, "_ipps._tcp.") ||
853 !strcmp(regtype
, "_ipp-tls._tcp."))
854 return (CUPS_DEVICE_IPPS
);
855 else if (!strcmp(regtype
, "_fax-ipp._tcp."))
856 return (CUPS_DEVICE_FAX_IPP
);
857 else if (!strcmp(regtype
, "_printer._tcp."))
858 return (CUPS_DEVICE_PRINTER
);
859 else if (!strcmp(regtype
, "_pdl-datastream._tcp."))
860 return (CUPS_DEVICE_PDL_DATASTREAM
);
861 #endif /* HAVE_AVAHI */
863 return (CUPS_DEVICE_RIOUSBPRINT
);
868 * 'get_device()' - Create or update a device.
871 static cups_device_t
* /* O - Device */
872 get_device(cups_array_t
*devices
, /* I - Device array */
873 const char *serviceName
, /* I - Name of service/device */
874 const char *regtype
, /* I - Type of service */
875 const char *replyDomain
) /* I - Service domain */
877 cups_device_t key
, /* Search key */
878 *device
; /* Device */
879 char fullName
[kDNSServiceMaxDomainName
];
880 /* Full name for query */
884 * See if this is a new device...
887 key
.name
= (char *)serviceName
;
888 key
.type
= device_type(regtype
);
890 for (device
= cupsArrayFind(devices
, &key
);
892 device
= cupsArrayNext(devices
))
893 if (_cups_strcasecmp(device
->name
, key
.name
))
895 else if (device
->type
== key
.type
)
897 if (!_cups_strcasecmp(device
->domain
, "local.") &&
898 _cups_strcasecmp(device
->domain
, replyDomain
))
901 * Update the .local listing to use the "global" domain name instead.
902 * The backend will try local lookups first, then the global domain name.
905 free(device
->domain
);
906 device
->domain
= strdup(replyDomain
);
909 DNSServiceConstructFullName(fullName
, device
->name
, regtype
,
911 #else /* HAVE_AVAHI */
912 avahi_service_name_join(fullName
, kDNSServiceMaxDomainName
,
913 serviceName
, regtype
, replyDomain
);
914 #endif /* HAVE_DNSSD */
916 free(device
->fullName
);
917 device
->fullName
= strdup(fullName
);
924 * Yes, add the device...
927 fprintf(stderr
, "DEBUG: Found \"%s.%s%s\"...\n", serviceName
, regtype
,
930 device
= calloc(sizeof(cups_device_t
), 1);
931 device
->name
= strdup(serviceName
);
932 device
->domain
= strdup(replyDomain
);
933 device
->type
= key
.type
;
934 device
->priority
= 50;
936 cupsArrayAdd(devices
, device
);
939 * Set the "full name" of this service, which is used for queries...
943 DNSServiceConstructFullName(fullName
, serviceName
, regtype
, replyDomain
);
944 #else /* HAVE_AVAHI */
945 avahi_service_name_join(fullName
, kDNSServiceMaxDomainName
,
946 serviceName
, regtype
, replyDomain
);
947 #endif /* HAVE_DNSSD */
949 device
->fullName
= strdup(fullName
);
957 * 'poll_callback()' - Wait for input on the specified file descriptors.
959 * Note: This function is needed because avahi_simple_poll_iterate is broken
960 * and always uses a timeout of 0 (!) milliseconds.
961 * (Avahi Ticket #364)
964 static int /* O - Number of file descriptors matching */
966 struct pollfd
*pollfds
, /* I - File descriptors */
967 unsigned int num_pollfds
, /* I - Number of file descriptors */
968 int timeout
, /* I - Timeout in milliseconds (unused) */
969 void *context
) /* I - User data (unused) */
971 int val
; /* Return value */
977 val
= poll(pollfds
, num_pollfds
, 500);
980 fprintf(stderr
, "DEBUG: poll_callback: %s\n", strerror(errno
));
986 #endif /* HAVE_AVAHI */
989 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
992 * 'query_callback()' - Process query data.
997 DNSServiceRef sdRef
, /* I - Service reference */
998 DNSServiceFlags flags
, /* I - Data flags */
999 uint32_t interfaceIndex
, /* I - Interface */
1000 DNSServiceErrorType errorCode
, /* I - Error, if any */
1001 const char *fullName
, /* I - Full service name */
1002 uint16_t rrtype
, /* I - Record type */
1003 uint16_t rrclass
, /* I - Record class */
1004 uint16_t rdlen
, /* I - Length of record data */
1005 const void *rdata
, /* I - Record data */
1006 uint32_t ttl
, /* I - Time-to-live */
1007 void *context
) /* I - Device */
1011 * 'query_callback()' - Process query data.
1016 AvahiRecordBrowser
*browser
, /* I - Record browser */
1017 AvahiIfIndex interfaceIndex
,
1018 /* I - Interface index (unused) */
1019 AvahiProtocol protocol
, /* I - Network protocol (unused) */
1020 AvahiBrowserEvent event
, /* I - What happened? */
1021 const char *fullName
, /* I - Service name */
1022 uint16_t rrclass
, /* I - Record class */
1023 uint16_t rrtype
, /* I - Record type */
1024 const void *rdata
, /* I - TXT record */
1025 size_t rdlen
, /* I - Length of TXT record */
1026 AvahiLookupResultFlags flags
, /* I - Flags */
1027 void *context
) /* I - Device */
1029 AvahiClient
*client
= avahi_record_browser_get_client(browser
);
1030 /* Client information */
1031 # endif /* HAVE_DNSSD */
1032 char *ptr
; /* Pointer into string */
1033 cups_device_t
*device
= (cups_device_t
*)context
;
1035 const uint8_t *data
, /* Pointer into data */
1036 *datanext
, /* Next key/value pair */
1037 *dataend
; /* End of entire TXT record */
1038 uint8_t datalen
; /* Length of current key/value pair */
1039 char key
[256], /* Key string */
1040 value
[256], /* Value string */
1041 make_and_model
[512], /* Manufacturer and model */
1042 model
[256], /* Model */
1044 device_id
[2048]; /* 1284 device ID */
1048 fprintf(stderr
, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
1049 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
1050 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
1052 sdRef
, flags
, interfaceIndex
, errorCode
,
1053 fullName
? fullName
: "(null)", rrtype
, rrclass
, rdlen
, rdata
, ttl
,
1057 * Only process "add" data...
1060 if (errorCode
!= kDNSServiceErr_NoError
|| !(flags
& kDNSServiceFlagsAdd
))
1064 fprintf(stderr
, "DEBUG2: query_callback(browser=%p, interfaceIndex=%d, "
1065 "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
1066 "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
1067 browser
, interfaceIndex
, protocol
, event
,
1068 fullName
? fullName
: "(null)", rrclass
, rrtype
, rdata
,
1069 (unsigned)rdlen
, flags
, context
);
1072 * Only process "add" data...
1075 if (event
!= AVAHI_BROWSER_NEW
)
1077 if (event
== AVAHI_BROWSER_FAILURE
)
1078 fprintf(stderr
, "ERROR: %s\n",
1079 avahi_strerror(avahi_client_errno(client
)));
1083 # endif /* HAVE_DNSSD */
1086 * Pull out the priority and make and model from the TXT
1087 * record and save it...
1090 device_id
[0] = '\0';
1091 make_and_model
[0] = '\0';
1094 strlcpy(model
, "Unknown", sizeof(model
));
1096 for (data
= rdata
, dataend
= data
+ rdlen
;
1101 * Read a key/value pair starting with an 8-bit length. Since the
1102 * length is 8 bits and the size of the key/value buffers is 256, we
1103 * don't need to check for overflow...
1108 if (!datalen
|| (data
+ datalen
) > dataend
)
1111 datanext
= data
+ datalen
;
1113 for (ptr
= key
; data
< datanext
&& *data
!= '='; data
++)
1117 if (data
< datanext
&& *data
== '=')
1121 if (data
< datanext
)
1122 memcpy(value
, data
, datanext
- data
);
1123 value
[datanext
- data
] = '\0';
1125 fprintf(stderr
, "DEBUG2: query_callback: \"%s=%s\".\n",
1130 fprintf(stderr
, "DEBUG2: query_callback: \"%s\" with no value.\n",
1135 if (!_cups_strncasecmp(key
, "usb_", 4))
1138 * Add USB device ID information...
1141 ptr
= device_id
+ strlen(device_id
);
1142 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "%s:%s;",
1146 if (!_cups_strcasecmp(key
, "usb_MFG") || !_cups_strcasecmp(key
, "usb_MANU") ||
1147 !_cups_strcasecmp(key
, "usb_MANUFACTURER"))
1148 strlcpy(make_and_model
, value
, sizeof(make_and_model
));
1149 else if (!_cups_strcasecmp(key
, "usb_MDL") || !_cups_strcasecmp(key
, "usb_MODEL"))
1150 strlcpy(model
, value
, sizeof(model
));
1151 else if (!_cups_strcasecmp(key
, "product") && !strstr(value
, "Ghostscript"))
1153 if (value
[0] == '(')
1156 * Strip parenthesis...
1159 if ((ptr
= value
+ strlen(value
) - 1) > value
&& *ptr
== ')')
1162 strlcpy(model
, value
+ 1, sizeof(model
));
1165 strlcpy(model
, value
, sizeof(model
));
1167 else if (!_cups_strcasecmp(key
, "ty"))
1169 strlcpy(model
, value
, sizeof(model
));
1171 if ((ptr
= strchr(model
, ',')) != NULL
)
1174 else if (!_cups_strcasecmp(key
, "pdl"))
1175 strlcpy(pdl
, value
, sizeof(pdl
));
1176 else if (!_cups_strcasecmp(key
, "priority"))
1177 device
->priority
= atoi(value
);
1178 else if ((device
->type
== CUPS_DEVICE_IPP
||
1179 device
->type
== CUPS_DEVICE_IPPS
||
1180 device
->type
== CUPS_DEVICE_PRINTER
) &&
1181 !_cups_strcasecmp(key
, "printer-type"))
1184 * This is a CUPS printer!
1187 device
->cups_shared
= 1;
1189 if (device
->type
== CUPS_DEVICE_PRINTER
)
1194 if (device
->device_id
)
1195 free(device
->device_id
);
1197 if (!device_id
[0] && strcmp(model
, "Unknown"))
1199 if (make_and_model
[0])
1200 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s;",
1201 make_and_model
, model
);
1202 else if (!_cups_strncasecmp(model
, "designjet ", 10))
1203 snprintf(device_id
, sizeof(device_id
), "MFG:HP;MDL:%s", model
+ 10);
1204 else if (!_cups_strncasecmp(model
, "stylus ", 7))
1205 snprintf(device_id
, sizeof(device_id
), "MFG:EPSON;MDL:%s", model
+ 7);
1206 else if ((ptr
= strchr(model
, ' ')) != NULL
)
1209 * Assume the first word is the make...
1212 memcpy(make_and_model
, model
, ptr
- model
);
1213 make_and_model
[ptr
- model
] = '\0';
1215 snprintf(device_id
, sizeof(device_id
), "MFG:%s;MDL:%s",
1216 make_and_model
, ptr
+ 1);
1221 !strstr(device_id
, "CMD:") &&
1222 !strstr(device_id
, "COMMAND SET:") &&
1223 (strstr(pdl
, "application/pdf") ||
1224 strstr(pdl
, "application/postscript") ||
1225 strstr(pdl
, "application/vnd.hp-PCL") ||
1226 strstr(pdl
, "image/")))
1229 if (strstr(pdl
, "application/pdf"))
1230 strlcat(value
, ",PDF", sizeof(value
));
1231 if (strstr(pdl
, "application/postscript"))
1232 strlcat(value
, ",PS", sizeof(value
));
1233 if (strstr(pdl
, "application/vnd.hp-PCL"))
1234 strlcat(value
, ",PCL", sizeof(value
));
1235 for (ptr
= strstr(pdl
, "image/"); ptr
; ptr
= strstr(ptr
, "image/"))
1237 char *valptr
= value
+ strlen(value
);
1238 /* Pointer into value */
1240 if (valptr
< (value
+ sizeof(value
) - 1))
1244 while (isalnum(*ptr
& 255) || *ptr
== '-' || *ptr
== '.')
1246 if (isalnum(*ptr
& 255) && valptr
< (value
+ sizeof(value
) - 1))
1247 *valptr
++ = toupper(*ptr
++ & 255);
1255 ptr
= device_id
+ strlen(device_id
);
1256 snprintf(ptr
, sizeof(device_id
) - (ptr
- device_id
), "CMD:%s;",
1261 device
->device_id
= strdup(device_id
);
1263 device
->device_id
= NULL
;
1265 if (device
->make_and_model
)
1266 free(device
->make_and_model
);
1268 if (make_and_model
[0])
1270 strlcat(make_and_model
, " ", sizeof(make_and_model
));
1271 strlcat(make_and_model
, model
, sizeof(make_and_model
));
1273 device
->make_and_model
= strdup(make_and_model
);
1276 device
->make_and_model
= strdup(model
);
1278 #endif /* HAVE_DNSSD || HAVE_AVAHI */
1282 * 'sigterm_handler()' - Handle termination signals.
1286 sigterm_handler(int sig
) /* I - Signal number (unused) */
1291 exit(CUPS_BACKEND_OK
);
1298 * 'unquote()' - Unquote a name string.
1302 unquote(char *dst
, /* I - Destination buffer */
1303 const char *src
, /* I - Source string */
1304 size_t dstsize
) /* I - Size of destination buffer */
1306 char *dstend
= dst
+ dstsize
- 1; /* End of destination buffer */
1309 while (*src
&& dst
< dstend
)
1314 if (isdigit(src
[0] & 255) && isdigit(src
[1] & 255) &&
1315 isdigit(src
[2] & 255))
1317 *dst
++ = ((((src
[0] - '0') * 10) + src
[1] - '0') * 10) + src
[2] - '0';
1332 * End of "$Id: dnssd.c 11623 2014-02-19 20:18:10Z msweet $".