]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/dnssd.c
Don't allow multiple jobs to be started on the same printer.
[thirdparty/cups.git] / backend / dnssd.c
CommitLineData
ad9fc101 1/*
2 * "$Id$"
3 *
3929e7fd 4 * DNS-SD discovery backend for CUPS.
ad9fc101 5 *
9a766dc2 6 * Copyright 2008-2012 by Apple Inc.
ad9fc101 7 *
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/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 *
16 * Contents:
17 *
ee9e056f 18 * main() - Browse for printers.
19 * browse_callback() - Browse devices.
ad9fc101 20 * browse_local_callback() - Browse local devices.
ee9e056f 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.
ad9fc101 31 */
32
33/*
34 * Include necessary headers.
35 */
36
37#include "backend-private.h"
38#include <cups/array.h>
ee9e056f 39#ifdef HAVE_DNSSD
40# include <dns_sd.h>
41#endif /* HAVE_DNSSD */
42#ifdef HAVE_AVAHI
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 */
ad9fc101 51
52
53/*
54 * Device structure...
55 */
56
57typedef enum
58{
59 CUPS_DEVICE_PRINTER = 0, /* lpd://... */
07055d0c 60 CUPS_DEVICE_IPPS, /* ipps://... */
9a766dc2 61 CUPS_DEVICE_IPP, /* ipp://... */
ad9fc101 62 CUPS_DEVICE_FAX_IPP, /* ipp://... */
63 CUPS_DEVICE_PDL_DATASTREAM, /* socket://... */
64 CUPS_DEVICE_RIOUSBPRINT /* riousbprint://... */
65} cups_devtype_t;
66
67
68typedef struct
69{
ee9e056f 70#ifdef HAVE_DNSSD
71 DNSServiceRef ref; /* Service reference for query */
72#endif /* HAVE_DNSSD */
73#ifdef HAVE_AVAHI
74 AvahiRecordBrowser *ref; /* Browser for query */
75#endif /* HAVE_AVAHI */
ad9fc101 76 char *name, /* Service name */
77 *domain, /* Domain name */
78 *fullName, /* Full name */
a306e19f 79 *make_and_model, /* Make and model from TXT record */
80 *device_id; /* 1284 device ID from TXT record */
ad9fc101 81 cups_devtype_t type; /* Device registration type */
0f1b6cf2 82 int priority, /* Priority associated with type */
83 cups_shared, /* CUPS shared printer? */
ad9fc101 84 sent; /* Did we list the device? */
85} cups_device_t;
86
87
f0dce363 88/*
89 * Local globals...
90 */
91
92static int job_canceled = 0;
93 /* Set to 1 on SIGTERM */
ee9e056f 94#ifdef HAVE_AVAHI
47afb329 95static AvahiSimplePoll *simple_poll = NULL;
96 /* Poll information */
c4897187 97static int got_data = 0; /* Got data from poll? */
ee9e056f 98#endif /* HAVE_AVAHI */
f0dce363 99
100
ad9fc101 101/*
102 * Local functions...
103 */
104
ee9e056f 105#ifdef HAVE_DNSSD
ad9fc101 106static void browse_callback(DNSServiceRef sdRef,
107 DNSServiceFlags flags,
108 uint32_t interfaceIndex,
109 DNSServiceErrorType errorCode,
110 const char *serviceName,
111 const char *regtype,
75d1b92c 112 const char *replyDomain, void *context)
113 __attribute__((nonnull(1,5,6,7,8)));
ad9fc101 114static void browse_local_callback(DNSServiceRef sdRef,
115 DNSServiceFlags flags,
116 uint32_t interfaceIndex,
117 DNSServiceErrorType errorCode,
118 const char *serviceName,
119 const char *regtype,
120 const char *replyDomain,
75d1b92c 121 void *context)
122 __attribute__((nonnull(1,5,6,7,8)));
ee9e056f 123#endif /* HAVE_DNSSD */
124#ifdef HAVE_AVAHI
125static void browse_callback(AvahiServiceBrowser *browser,
126 AvahiIfIndex interface,
127 AvahiProtocol protocol,
128 AvahiBrowserEvent event,
129 const char *serviceName,
130 const char *regtype,
131 const char *replyDomain,
132 AvahiLookupResultFlags flags,
133 void *context);
134static void client_callback(AvahiClient *client,
135 AvahiClientState state,
136 void *context);
137#endif /* HAVE_AVAHI */
138
ad9fc101 139static int compare_devices(cups_device_t *a, cups_device_t *b);
140static void exec_backend(char **argv);
141static cups_device_t *get_device(cups_array_t *devices,
142 const char *serviceName,
143 const char *regtype,
75d1b92c 144 const char *replyDomain)
145 __attribute__((nonnull(1,2,3,4)));
ee9e056f 146#ifdef HAVE_DNSSD
ad9fc101 147static void query_callback(DNSServiceRef sdRef,
148 DNSServiceFlags flags,
149 uint32_t interfaceIndex,
150 DNSServiceErrorType errorCode,
151 const char *fullName, uint16_t rrtype,
152 uint16_t rrclass, uint16_t rdlen,
153 const void *rdata, uint32_t ttl,
75d1b92c 154 void *context)
155 __attribute__((nonnull(1,5,9,11)));
c4897187 156#elif defined(HAVE_AVAHI)
157static int poll_callback(struct pollfd *pollfds,
158 unsigned int num_pollfds, int timeout,
159 void *context);
47afb329 160static void query_callback(AvahiRecordBrowser *browser,
ee9e056f 161 AvahiIfIndex interface,
162 AvahiProtocol protocol,
47afb329 163 AvahiBrowserEvent event,
ee9e056f 164 const char *name, uint16_t rrclass,
165 uint16_t rrtype, const void *rdata,
166 size_t rdlen,
167 AvahiLookupResultFlags flags,
168 void *context);
c4897187 169#endif /* HAVE_DNSSD */
f0dce363 170static void sigterm_handler(int sig);
75d1b92c 171static void unquote(char *dst, const char *src, size_t dstsize)
172 __attribute__((nonnull(1,2)));
ad9fc101 173
174
175/*
176 * 'main()' - Browse for printers.
177 */
178
179int /* O - Exit status */
180main(int argc, /* I - Number of command-line args */
181 char *argv[]) /* I - Command-line arguments */
182{
e57340ae 183 const char *name; /* Backend name */
ee9e056f 184 cups_array_t *devices; /* Device array */
185 cups_device_t *device; /* Current device */
186 char uriName[1024]; /* Unquoted fullName for URI */
187#ifdef HAVE_DNSSD
188 int fd; /* Main file descriptor */
189 fd_set input; /* Input set for select() */
190 struct timeval timeout; /* Timeout for select() */
ad9fc101 191 DNSServiceRef main_ref, /* Main service reference */
192 fax_ipp_ref, /* IPP fax service reference */
193 ipp_ref, /* IPP service reference */
194 ipp_tls_ref, /* IPP w/TLS service reference */
07055d0c 195 ipps_ref, /* IPP service reference */
ad9fc101 196 local_fax_ipp_ref, /* Local IPP fax service reference */
197 local_ipp_ref, /* Local IPP service reference */
198 local_ipp_tls_ref, /* Local IPP w/TLS service reference */
07055d0c 199 local_ipps_ref, /* Local IPP service reference */
ad9fc101 200 local_printer_ref, /* Local LPD service reference */
201 pdl_datastream_ref, /* AppSocket service reference */
202 printer_ref, /* LPD service reference */
203 riousbprint_ref; /* Remote IO service reference */
ee9e056f 204#endif /* HAVE_DNSSD */
205#ifdef HAVE_AVAHI
ee9e056f 206 AvahiClient *client; /* Client information */
207 int error; /* Error code, if any */
208#endif /* HAVE_AVAHI */
f0dce363 209#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
210 struct sigaction action; /* Actions for POSIX signals */
211#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
ad9fc101 212
213
214 /*
f0dce363 215 * Don't buffer stderr, and catch SIGTERM...
ad9fc101 216 */
217
218 setbuf(stderr, NULL);
219
f0dce363 220#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
221 sigset(SIGTERM, sigterm_handler);
222#elif defined(HAVE_SIGACTION)
223 memset(&action, 0, sizeof(action));
224
225 sigemptyset(&action.sa_mask);
1c026e33 226 action.sa_handler = sigterm_handler;
227 sigaction(SIGTERM, &action, NULL);
f0dce363 228#else
229 signal(SIGTERM, sigterm_handler);
230#endif /* HAVE_SIGSET */
231
232 /*
233 * Check command-line...
234 */
235
ad9fc101 236 if (argc >= 6)
237 exec_backend(argv);
238 else if (argc != 1)
239 {
4c4eea89 240 _cupsLangPrintf(stderr,
241 _("Usage: %s job-id user title copies options [file]"),
242 argv[0]);
ad9fc101 243 return (1);
244 }
245
e57340ae 246 /*
247 * Only do discovery when run as "dnssd"...
248 */
249
250 if ((name = strrchr(argv[0], '/')) != NULL)
251 name ++;
252 else
253 name = argv[0];
254
255 if (strcmp(name, "dnssd"))
256 return (0);
257
ad9fc101 258 /*
259 * Create an array to track devices...
260 */
261
262 devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
263
264 /*
265 * Browse for different kinds of printers...
266 */
267
ee9e056f 268#ifdef HAVE_DNSSD
ad9fc101 269 if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
270 {
271 perror("ERROR: Unable to create service connection");
272 return (1);
273 }
274
275 fd = DNSServiceRefSockFD(main_ref);
276
277 fax_ipp_ref = main_ref;
278 DNSServiceBrowse(&fax_ipp_ref, kDNSServiceFlagsShareConnection, 0,
279 "_fax-ipp._tcp", NULL, browse_callback, devices);
280
281 ipp_ref = main_ref;
282 DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
283 "_ipp._tcp", NULL, browse_callback, devices);
284
285 ipp_tls_ref = main_ref;
286 DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0,
287 "_ipp-tls._tcp", NULL, browse_callback, devices);
288
07055d0c 289 ipps_ref = main_ref;
56391af7 290 DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
07055d0c 291 "_ipps._tcp", NULL, browse_callback, devices);
292
ad9fc101 293 local_fax_ipp_ref = main_ref;
294 DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection,
295 kDNSServiceInterfaceIndexLocalOnly,
296 "_fax-ipp._tcp", NULL, browse_local_callback, devices);
297
298 local_ipp_ref = main_ref;
299 DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
300 kDNSServiceInterfaceIndexLocalOnly,
301 "_ipp._tcp", NULL, browse_local_callback, devices);
302
303 local_ipp_tls_ref = main_ref;
304 DNSServiceBrowse(&local_ipp_tls_ref, kDNSServiceFlagsShareConnection,
305 kDNSServiceInterfaceIndexLocalOnly,
306 "_ipp-tls._tcp", NULL, browse_local_callback, devices);
307
07055d0c 308 local_ipps_ref = main_ref;
56391af7 309 DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
07055d0c 310 kDNSServiceInterfaceIndexLocalOnly,
311 "_ipps._tcp", NULL, browse_local_callback, devices);
312
ad9fc101 313 local_printer_ref = main_ref;
314 DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection,
315 kDNSServiceInterfaceIndexLocalOnly,
316 "_printer._tcp", NULL, browse_local_callback, devices);
317
c6fab96f 318 pdl_datastream_ref = main_ref;
ad9fc101 319 DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0,
320 "_pdl-datastream._tcp", NULL, browse_callback, devices);
321
322 printer_ref = main_ref;
323 DNSServiceBrowse(&printer_ref, kDNSServiceFlagsShareConnection, 0,
324 "_printer._tcp", NULL, browse_callback, devices);
325
326 riousbprint_ref = main_ref;
327 DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0,
328 "_riousbprint._tcp", NULL, browse_callback, devices);
ee9e056f 329#endif /* HAVE_DNSSD */
330
331#ifdef HAVE_AVAHI
332 if ((simple_poll = avahi_simple_poll_new()) == NULL)
333 {
334 fputs("DEBUG: Unable to create avahi simple poll object.\n", stderr);
335 return (1);
336 }
337
c4897187 338 avahi_simple_poll_set_func(simple_poll, poll_callback, NULL);
339
ee9e056f 340 client = avahi_client_new(avahi_simple_poll_get(simple_poll),
341 0, client_callback, simple_poll, &error);
342 if (!client)
343 {
344 fputs("DEBUG: Unable to create avahi client.\n", stderr);
345 return (1);
346 }
347
348 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
349 AVAHI_PROTO_UNSPEC,
350 "_fax-ipp._tcp", NULL, 0,
351 browse_callback, devices);
352 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
353 AVAHI_PROTO_UNSPEC,
354 "_ipp._tcp", NULL, 0,
355 browse_callback, devices);
356 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
357 AVAHI_PROTO_UNSPEC,
358 "_ipp-tls._tcp", NULL, 0,
359 browse_callback, devices);
360 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
361 AVAHI_PROTO_UNSPEC,
362 "_ipps._tcp", NULL, 0,
363 browse_callback, devices);
364 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
365 AVAHI_PROTO_UNSPEC,
366 "_pdl-datastream._tcp",
367 NULL, 0,
368 browse_callback,
369 devices);
370 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
371 AVAHI_PROTO_UNSPEC,
372 "_printer._tcp", NULL, 0,
373 browse_callback, devices);
374#endif /* HAVE_AVAHI */
ad9fc101 375
376 /*
377 * Loop until we are killed...
378 */
379
f0dce363 380 while (!job_canceled)
ad9fc101 381 {
ee9e056f 382 int announce = 0; /* Announce printers? */
383
384#ifdef HAVE_DNSSD
ad9fc101 385 FD_ZERO(&input);
386 FD_SET(fd, &input);
387
11fabd31 388 timeout.tv_sec = 0;
c4897187 389 timeout.tv_usec = 500000;
ad9fc101 390
391 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
392 continue;
393
394 if (FD_ISSET(fd, &input))
395 {
396 /*
397 * Process results of our browsing...
398 */
399
400 DNSServiceProcessResult(main_ref);
401 }
402 else
ee9e056f 403 announce = 1;
404
405#elif defined(HAVE_AVAHI)
c4897187 406 got_data = 0;
ee9e056f 407
11fabd31 408 if ((error = avahi_simple_poll_iterate(simple_poll, 500)) > 0)
ee9e056f 409 {
410 /*
411 * We've been told to exit the loop. Perhaps the connection to
412 * Avahi failed.
413 */
414
415 break;
416 }
417
c4897187 418 if (!got_data)
ee9e056f 419 announce = 1;
420#endif /* HAVE_DNSSD */
421
c4897187 422/* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
423
ee9e056f 424 if (announce)
ad9fc101 425 {
426 /*
427 * Announce any devices we've found...
428 */
429
ee9e056f 430#ifdef HAVE_DNSSD
5169bfee 431 DNSServiceErrorType status; /* DNS query status */
ee9e056f 432#endif /* HAVE_DNSSD */
0f1b6cf2 433 cups_device_t *best; /* Best matching device */
ad9fc101 434 char device_uri[1024]; /* Device URI */
012d7a28 435 int count; /* Number of queries */
ba58cd0c 436 int sent; /* Number of sent */
ad9fc101 437
0f1b6cf2 438 for (device = (cups_device_t *)cupsArrayFirst(devices),
ba58cd0c 439 best = NULL, count = 0, sent = 0;
ad9fc101 440 device;
441 device = (cups_device_t *)cupsArrayNext(devices))
ba58cd0c 442 {
443 if (device->sent)
444 sent ++;
445
446 if (device->ref)
447 count ++;
448
ad9fc101 449 if (!device->ref && !device->sent)
450 {
451 /*
452 * Found the device, now get the TXT record(s) for it...
453 */
454
c4897187 455 if (count < 50)
012d7a28 456 {
012d7a28 457 fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName);
458
47afb329 459#ifdef HAVE_DNSSD
460 device->ref = main_ref;
461
5169bfee 462 status = DNSServiceQueryRecord(&(device->ref),
463 kDNSServiceFlagsShareConnection,
464 0, device->fullName,
465 kDNSServiceType_TXT,
466 kDNSServiceClass_IN, query_callback,
47afb329 467 device);
5169bfee 468 if (status != kDNSServiceErr_NoError)
47afb329 469 fprintf(stderr,
470 "ERROR: Unable to query \"%s\" for TXT records: %d\n",
471 device->fullName, status);
472 /* Users never see this */
473 else
474 count ++;
475
476#else
477 if ((device->ref = avahi_record_browser_new(client, AVAHI_IF_UNSPEC,
478 AVAHI_PROTO_UNSPEC,
479 device->fullName,
480 AVAHI_DNS_CLASS_IN,
481 AVAHI_DNS_TYPE_TXT,
be24b036 482 0,
47afb329 483 query_callback,
484 device)) == NULL)
485 fprintf(stderr,
486 "ERROR: Unable to query \"%s\" for TXT records: %s\n",
487 device->fullName,
488 avahi_strerror(avahi_client_errno(client)));
489 /* Users never see this */
5169bfee 490 else
012d7a28 491 count ++;
47afb329 492#endif /* HAVE_AVAHI */
012d7a28 493 }
ad9fc101 494 }
47afb329 495 else if (!device->sent)
ad9fc101 496 {
ee9e056f 497#ifdef HAVE_DNSSD
ad9fc101 498 /*
499 * Got the TXT records, now report the device...
500 */
501
502 DNSServiceRefDeallocate(device->ref);
47afb329 503#else
504 avahi_record_browser_free(device->ref);
ee9e056f 505#endif /* HAVE_DNSSD */
ad9fc101 506
47afb329 507 device->ref = NULL;
508
0f1b6cf2 509 if (!best)
510 best = device;
c6fab96f 511 else if (_cups_strcasecmp(best->name, device->name) ||
512 _cups_strcasecmp(best->domain, device->domain))
0f1b6cf2 513 {
855b21a1 514 unquote(uriName, best->fullName, sizeof(uriName));
515
0f1b6cf2 516 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
49b3ea56 517 "dnssd", NULL, uriName, 0,
0f1b6cf2 518 best->cups_shared ? "/cups" : "/");
519
2317ac1e 520 cupsBackendReport("network", device_uri, best->make_and_model,
a306e19f 521 best->name, best->device_id, NULL);
0f1b6cf2 522 best->sent = 1;
523 best = device;
ba58cd0c 524
525 sent ++;
0f1b6cf2 526 }
527 else if (best->priority > device->priority ||
528 (best->priority == device->priority &&
529 best->type < device->type))
530 {
531 best->sent = 1;
532 best = device;
ba58cd0c 533
534 sent ++;
0f1b6cf2 535 }
536 else
ba58cd0c 537 {
0f1b6cf2 538 device->sent = 1;
ba58cd0c 539
540 sent ++;
541 }
0f1b6cf2 542 }
ba58cd0c 543 }
ad9fc101 544
0f1b6cf2 545 if (best)
546 {
855b21a1 547 unquote(uriName, best->fullName, sizeof(uriName));
548
0f1b6cf2 549 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
49b3ea56 550 "dnssd", NULL, uriName, 0,
0f1b6cf2 551 best->cups_shared ? "/cups" : "/");
ad9fc101 552
2317ac1e 553 cupsBackendReport("network", device_uri, best->make_and_model,
a306e19f 554 best->name, best->device_id, NULL);
0f1b6cf2 555 best->sent = 1;
ba58cd0c 556 sent ++;
0f1b6cf2 557 }
ba58cd0c 558
c4897187 559 fprintf(stderr, "DEBUG: sent=%d, count=%d\n", sent, count);
560
ba58cd0c 561 if (sent == cupsArrayCount(devices))
562 break;
ad9fc101 563 }
564 }
f0dce363 565
566 return (CUPS_BACKEND_OK);
ad9fc101 567}
568
569
ee9e056f 570#ifdef HAVE_DNSSD
ad9fc101 571/*
572 * 'browse_callback()' - Browse devices.
573 */
574
575static void
576browse_callback(
577 DNSServiceRef sdRef, /* I - Service reference */
578 DNSServiceFlags flags, /* I - Option flags */
579 uint32_t interfaceIndex, /* I - Interface number */
580 DNSServiceErrorType errorCode, /* I - Error, if any */
581 const char *serviceName, /* I - Name of service/device */
582 const char *regtype, /* I - Type of service */
583 const char *replyDomain, /* I - Service domain */
584 void *context) /* I - Devices array */
585{
586 fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
587 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
588 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
589 sdRef, flags, interfaceIndex, errorCode,
590 serviceName ? serviceName : "(null)",
591 regtype ? regtype : "(null)",
592 replyDomain ? replyDomain : "(null)",
593 context);
594
595 /*
596 * Only process "add" data...
597 */
598
599 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
600 return;
601
602 /*
603 * Get the device...
604 */
605
606 get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
607}
608
609
610/*
611 * 'browse_local_callback()' - Browse local devices.
612 */
613
614static void
615browse_local_callback(
616 DNSServiceRef sdRef, /* I - Service reference */
617 DNSServiceFlags flags, /* I - Option flags */
618 uint32_t interfaceIndex, /* I - Interface number */
619 DNSServiceErrorType errorCode, /* I - Error, if any */
620 const char *serviceName, /* I - Name of service/device */
621 const char *regtype, /* I - Type of service */
622 const char *replyDomain, /* I - Service domain */
623 void *context) /* I - Devices array */
624{
625 cups_device_t *device; /* Device */
626
627
628 fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
629 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
630 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
631 sdRef, flags, interfaceIndex, errorCode,
632 serviceName ? serviceName : "(null)",
633 regtype ? regtype : "(null)",
634 replyDomain ? replyDomain : "(null)",
635 context);
636
637 /*
638 * Only process "add" data...
639 */
640
641 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
642 return;
643
644 /*
645 * Get the device...
646 */
647
648 device = get_device((cups_array_t *)context, serviceName, regtype,
649 replyDomain);
650
651 /*
652 * Hide locally-registered devices...
653 */
654
655 fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n",
656 device->fullName);
657 device->sent = 1;
658}
ee9e056f 659#endif /* HAVE_DNSSD */
660
661
662#ifdef HAVE_AVAHI
663/*
664 * 'browse_callback()' - Browse devices.
665 */
666
667static void
668browse_callback(
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 */
47afb329 677 void *context) /* I - Devices array */
ee9e056f 678{
679 AvahiClient *client = avahi_service_browser_get_client(browser);
680 /* Client information */
681
682
683 (void)interface;
684 (void)protocol;
685 (void)context;
686
687 switch (event)
688 {
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);
693 break;
694
695 case AVAHI_BROWSER_NEW:
696 /*
697 * This object is new on the network.
698 */
699
700 if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
701 {
702 /*
703 * This comes from the local machine so ignore it.
704 */
705
706 fprintf(stderr, "DEBUG: Ignoring local service %s.\n", name);
707 }
708 else
709 {
710 /*
711 * Create a device entry for it if it doesn't yet exist.
712 */
713
714 get_device((cups_array_t *)context, name, type, domain);
7f941776 715 }
ee9e056f 716 break;
717
718 case AVAHI_BROWSER_REMOVE:
719 case AVAHI_BROWSER_ALL_FOR_NOW:
720 case AVAHI_BROWSER_CACHE_EXHAUSTED:
721 break;
722 }
723}
724
725
726/*
727 * 'client_callback()' - Avahi client callback function.
728 */
729
730static void
731client_callback(
732 AvahiClient *client, /* I - Client information (unused) */
733 AvahiClientState state, /* I - Current state */
734 void *context) /* I - User data (unused) */
735{
736 (void)client;
737 (void)context;
738
739 /*
740 * If the connection drops, quit.
741 */
742
743 if (state == AVAHI_CLIENT_FAILURE)
744 {
745 fputs("DEBUG: Avahi connection failed.\n", stderr);
746 avahi_simple_poll_quit(simple_poll);
747 }
748}
749#endif /* HAVE_AVAHI */
ad9fc101 750
751
752/*
753 * 'compare_devices()' - Compare two devices.
754 */
755
756static int /* O - Result of comparison */
757compare_devices(cups_device_t *a, /* I - First device */
758 cups_device_t *b) /* I - Second device */
759{
49b3ea56 760 return (strcmp(a->name, b->name));
ad9fc101 761}
762
763
764/*
765 * 'exec_backend()' - Execute the backend that corresponds to the
766 * resolved service name.
767 */
768
769static void
770exec_backend(char **argv) /* I - Command-line arguments */
771{
772 const char *resolved_uri, /* Resolved device URI */
773 *cups_serverbin; /* Location of programs */
774 char scheme[1024], /* Scheme from URI */
775 *ptr, /* Pointer into scheme */
776 filename[1024]; /* Backend filename */
777
778
779 /*
780 * Resolve the device URI...
781 */
782
90d07b87 783 job_canceled = -1;
784
3929e7fd 785 while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL)
786 {
787 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
788 sleep(10);
789
790 if (getenv("CLASS") != NULL)
79646d87 791 exit(CUPS_BACKEND_FAILED);
3929e7fd 792 }
ad9fc101 793
794 /*
795 * Extract the scheme from the URI...
796 */
797
798 strlcpy(scheme, resolved_uri, sizeof(scheme));
799 if ((ptr = strchr(scheme, ':')) != NULL)
800 *ptr = '\0';
801
802 /*
803 * Get the filename of the backend...
804 */
805
806 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
807 cups_serverbin = CUPS_SERVERBIN;
808
809 snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme);
810
811 /*
f0dce363 812 * Overwrite the device URI and run the new backend...
ad9fc101 813 */
814
815 setenv("DEVICE_URI", resolved_uri, 1);
816
817 argv[0] = (char *)resolved_uri;
818
819 fprintf(stderr, "DEBUG: Executing backend \"%s\"...\n", filename);
820
821 execv(filename, argv);
822
823 fprintf(stderr, "ERROR: Unable to execute backend \"%s\": %s\n", filename,
824 strerror(errno));
825 exit(CUPS_BACKEND_STOP);
826}
827
828
ee9e056f 829/*
830 * 'device_type()' - Get DNS-SD type enumeration from string.
831 */
832
833static int
834device_type(const char *regtype)
835{
836#ifdef HAVE_AVAHI
837 if (!strcmp(regtype, "_ipp._tcp"))
838 return (CUPS_DEVICE_IPP);
839 else if (!strcmp(regtype, "_ipps._tcp") ||
840 !strcmp(regtype, "_ipp-tls._tcp"))
841 return (CUPS_DEVICE_IPPS);
842 else if (!strcmp(regtype, "_fax-ipp._tcp"))
843 return (CUPS_DEVICE_FAX_IPP);
844 else if (!strcmp(regtype, "_printer._tcp"))
845 return (CUPS_DEVICE_PDL_DATASTREAM);
846#else
847 if (!strcmp(regtype, "_ipp._tcp."))
848 return (CUPS_DEVICE_IPP);
849 else if (!strcmp(regtype, "_ipps._tcp.") ||
850 !strcmp(regtype, "_ipp-tls._tcp."))
851 return (CUPS_DEVICE_IPPS);
852 else if (!strcmp(regtype, "_fax-ipp._tcp."))
853 return (CUPS_DEVICE_FAX_IPP);
854 else if (!strcmp(regtype, "_printer._tcp."))
855 return (CUPS_DEVICE_PRINTER);
856 else if (!strcmp(regtype, "_pdl-datastream._tcp."))
857 return (CUPS_DEVICE_PDL_DATASTREAM);
858#endif /* HAVE_AVAHI */
859
860 return (CUPS_DEVICE_RIOUSBPRINT);
861}
862
863
ad9fc101 864/*
865 * 'get_device()' - Create or update a device.
866 */
867
868static cups_device_t * /* O - Device */
869get_device(cups_array_t *devices, /* I - Device array */
870 const char *serviceName, /* I - Name of service/device */
871 const char *regtype, /* I - Type of service */
872 const char *replyDomain) /* I - Service domain */
873{
874 cups_device_t key, /* Search key */
875 *device; /* Device */
5169bfee 876 char fullName[kDNSServiceMaxDomainName];
877 /* Full name for query */
ad9fc101 878
879
880 /*
881 * See if this is a new device...
882 */
883
49b3ea56 884 key.name = (char *)serviceName;
ee9e056f 885 key.type = device_type(regtype);
ad9fc101 886
0f1b6cf2 887 for (device = cupsArrayFind(devices, &key);
888 device;
889 device = cupsArrayNext(devices))
c6fab96f 890 if (_cups_strcasecmp(device->name, key.name))
0f1b6cf2 891 break;
892 else if (device->type == key.type)
49b3ea56 893 {
c6fab96f 894 if (!_cups_strcasecmp(device->domain, "local.") &&
895 _cups_strcasecmp(device->domain, replyDomain))
49b3ea56 896 {
897 /*
898 * Update the .local listing to use the "global" domain name instead.
899 * The backend will try local lookups first, then the global domain name.
900 */
901
902 free(device->domain);
903 device->domain = strdup(replyDomain);
904
ee9e056f 905#ifdef HAVE_DNSSD
49b3ea56 906 DNSServiceConstructFullName(fullName, device->name, regtype,
907 replyDomain);
ee9e056f 908#else /* HAVE_AVAHI */
909 avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
910 serviceName, regtype, replyDomain);
911#endif /* HAVE_DNSSD */
912
49b3ea56 913 free(device->fullName);
914 device->fullName = strdup(fullName);
915 }
916
0f1b6cf2 917 return (device);
49b3ea56 918 }
ad9fc101 919
0f1b6cf2 920 /*
921 * Yes, add the device...
922 */
ad9fc101 923
0f1b6cf2 924 fprintf(stderr, "DEBUG: Found \"%s.%s%s\"...\n", serviceName, regtype,
925 replyDomain);
ad9fc101 926
0f1b6cf2 927 device = calloc(sizeof(cups_device_t), 1);
928 device->name = strdup(serviceName);
929 device->domain = strdup(replyDomain);
930 device->type = key.type;
931 device->priority = 50;
ad9fc101 932
0f1b6cf2 933 cupsArrayAdd(devices, device);
ad9fc101 934
935 /*
0f1b6cf2 936 * Set the "full name" of this service, which is used for queries...
ad9fc101 937 */
938
ee9e056f 939#ifdef HAVE_DNSSD
5169bfee 940 DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
ee9e056f 941#else /* HAVE_AVAHI */
942 avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
943 serviceName, regtype, replyDomain);
944#endif /* HAVE_DNSSD */
945
ad9fc101 946 device->fullName = strdup(fullName);
947
948 return (device);
949}
950
951
c4897187 952#ifdef HAVE_AVAHI
953/*
954 * 'poll_callback()' - Wait for input on the specified file descriptors.
955 *
956 * Note: This function is needed because avahi_simple_poll_iterate is broken
957 * and always uses a timeout of 0 (!) milliseconds.
958 * (Avahi Ticket #364)
959 */
960
961static int /* O - Number of file descriptors matching */
962poll_callback(
963 struct pollfd *pollfds, /* I - File descriptors */
964 unsigned int num_pollfds, /* I - Number of file descriptors */
26751de9 965 int timeout, /* I - Timeout in milliseconds (unused) */
c4897187 966 void *context) /* I - User data (unused) */
967{
968 int val; /* Return value */
969
970
26751de9 971 (void)timeout;
972 (void)context;
973
11fabd31 974 val = poll(pollfds, num_pollfds, 500);
c4897187 975
976 if (val < 0)
977 fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno));
978 else if (val > 0)
979 got_data = 1;
980
981 return (val);
982}
983#endif /* HAVE_AVAHI */
984
985
ee9e056f 986#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
987# ifdef HAVE_DNSSD
ad9fc101 988/*
989 * 'query_callback()' - Process query data.
990 */
991
992static void
993query_callback(
994 DNSServiceRef sdRef, /* I - Service reference */
995 DNSServiceFlags flags, /* I - Data flags */
996 uint32_t interfaceIndex, /* I - Interface */
997 DNSServiceErrorType errorCode, /* I - Error, if any */
998 const char *fullName, /* I - Full service name */
999 uint16_t rrtype, /* I - Record type */
1000 uint16_t rrclass, /* I - Record class */
1001 uint16_t rdlen, /* I - Length of record data */
1002 const void *rdata, /* I - Record data */
1003 uint32_t ttl, /* I - Time-to-live */
47afb329 1004 void *context) /* I - Device */
ad9fc101 1005{
ee9e056f 1006# else
1007/*
1008 * 'query_callback()' - Process query data.
1009 */
1010
1011static void
1012query_callback(
47afb329 1013 AvahiRecordBrowser *browser, /* I - Record browser */
1014 AvahiIfIndex interfaceIndex,
1015 /* I - Interface index (unused) */
ee9e056f 1016 AvahiProtocol protocol, /* I - Network protocol (unused) */
47afb329 1017 AvahiBrowserEvent event, /* I - What happened? */
ee9e056f 1018 const char *fullName, /* I - Service name */
1019 uint16_t rrclass, /* I - Record class */
1020 uint16_t rrtype, /* I - Record type */
1021 const void *rdata, /* I - TXT record */
1022 size_t rdlen, /* I - Length of TXT record */
1023 AvahiLookupResultFlags flags, /* I - Flags */
47afb329 1024 void *context) /* I - Device */
ee9e056f 1025{
47afb329 1026 AvahiClient *client = avahi_record_browser_get_client(browser);
ee9e056f 1027 /* Client information */
1028# endif /* HAVE_DNSSD */
47afb329 1029 char *ptr; /* Pointer into string */
1030 cups_device_t *device = (cups_device_t *)context;
1031 /* Device */
c0eb794c 1032 const uint8_t *data, /* Pointer into data */
1033 *datanext, /* Next key/value pair */
1034 *dataend; /* End of entire TXT record */
1035 uint8_t datalen; /* Length of current key/value pair */
1036 char key[256], /* Key string */
1037 value[256], /* Value string */
1038 make_and_model[512], /* Manufacturer and model */
1039 model[256], /* Model */
1040 device_id[2048]; /* 1284 device ID */
ad9fc101 1041
1042
ee9e056f 1043# ifdef HAVE_DNSSD
ad9fc101 1044 fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
1045 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
1046 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
1047 "context=%p)\n",
1048 sdRef, flags, interfaceIndex, errorCode,
1049 fullName ? fullName : "(null)", rrtype, rrclass, rdlen, rdata, ttl,
1050 context);
1051
1052 /*
1053 * Only process "add" data...
1054 */
1055
1056 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
1057 return;
1058
ee9e056f 1059# else
47afb329 1060 fprintf(stderr, "DEBUG2: query_callback(browser=%p, interfaceIndex=%d, "
ee9e056f 1061 "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
1062 "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
47afb329 1063 browser, interfaceIndex, protocol, event,
1064 fullName ? fullName : "(null)", rrclass, rrtype, rdata,
1065 (unsigned)rdlen, flags, context);
ee9e056f 1066
1067 /*
1068 * Only process "add" data...
1069 */
1070
47afb329 1071 if (event != AVAHI_BROWSER_NEW)
ee9e056f 1072 {
47afb329 1073 if (event == AVAHI_BROWSER_FAILURE)
ee9e056f 1074 fprintf(stderr, "ERROR: %s\n",
9c1ba6d5 1075 avahi_strerror(avahi_client_errno(client)));
ee9e056f 1076
ee9e056f 1077 return;
1078 }
1079# endif /* HAVE_DNSSD */
1080
ad9fc101 1081 /*
47afb329 1082 * Pull out the priority and make and model from the TXT
1083 * record and save it...
ad9fc101 1084 */
1085
47afb329 1086 device_id[0] = '\0';
1087 make_and_model[0] = '\0';
1088
1089 strcpy(model, "Unknown");
1090
1091 for (data = rdata, dataend = data + rdlen;
1092 data < dataend;
1093 data = datanext)
1094 {
1095 /*
1096 * Read a key/value pair starting with an 8-bit length. Since the
1097 * length is 8 bits and the size of the key/value buffers is 256, we
1098 * don't need to check for overflow...
1099 */
1100
1101 datalen = *data++;
1102
1103 if (!datalen || (data + datalen) > dataend)
1104 break;
1105
1106 datanext = data + datalen;
ad9fc101 1107
47afb329 1108 for (ptr = key; data < datanext && *data != '='; data ++)
1109 *ptr++ = *data;
ad9fc101 1110 *ptr = '\0';
1111
47afb329 1112 if (data < datanext && *data == '=')
1113 {
1114 data ++;
0f1b6cf2 1115
47afb329 1116 if (data < datanext)
1117 memcpy(value, data, datanext - data);
1118 value[datanext - data] = '\0';
1119
1120 fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n",
1121 key, value);
1122 }
1123 else
0f1b6cf2 1124 {
47afb329 1125 fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n",
1126 key);
1127 continue;
0f1b6cf2 1128 }
47afb329 1129
1130 if (!_cups_strncasecmp(key, "usb_", 4))
0f1b6cf2 1131 {
1132 /*
47afb329 1133 * Add USB device ID information...
0f1b6cf2 1134 */
ad9fc101 1135
47afb329 1136 ptr = device_id + strlen(device_id);
1137 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%s:%s;",
1138 key + 4, value);
1139 }
a306e19f 1140
47afb329 1141 if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") ||
1142 !_cups_strcasecmp(key, "usb_MANUFACTURER"))
1143 strcpy(make_and_model, value);
1144 else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL"))
1145 strcpy(model, value);
1146 else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
1147 {
1148 if (value[0] == '(')
0f1b6cf2 1149 {
a306e19f 1150 /*
47afb329 1151 * Strip parenthesis...
a306e19f 1152 */
ad9fc101 1153
47afb329 1154 if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
1155 *ptr = '\0';
ad9fc101 1156
47afb329 1157 strcpy(model, value + 1);
1158 }
1159 else
1160 strcpy(model, value);
1161 }
1162 else if (!_cups_strcasecmp(key, "ty"))
1163 {
1164 strcpy(model, value);
0f1b6cf2 1165
47afb329 1166 if ((ptr = strchr(model, ',')) != NULL)
a306e19f 1167 *ptr = '\0';
47afb329 1168 }
1169 else if (!_cups_strcasecmp(key, "priority"))
1170 device->priority = atoi(value);
1171 else if ((device->type == CUPS_DEVICE_IPP ||
1172 device->type == CUPS_DEVICE_IPPS ||
1173 device->type == CUPS_DEVICE_PRINTER) &&
1174 !_cups_strcasecmp(key, "printer-type"))
1175 {
1176 /*
1177 * This is a CUPS printer!
1178 */
a306e19f 1179
47afb329 1180 device->cups_shared = 1;
a306e19f 1181
47afb329 1182 if (device->type == CUPS_DEVICE_PRINTER)
1183 device->sent = 1;
1184 }
1185 }
a306e19f 1186
47afb329 1187 if (device->device_id)
1188 free(device->device_id);
a306e19f 1189
47afb329 1190 if (!device_id[0] && strcmp(model, "Unknown"))
1191 {
1192 if (make_and_model[0])
1193 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
1194 make_and_model, model);
1195 else if (!_cups_strncasecmp(model, "designjet ", 10))
1196 snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s", model + 10);
1197 else if (!_cups_strncasecmp(model, "stylus ", 7))
1198 snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s", model + 7);
1199 else if ((ptr = strchr(model, ' ')) != NULL)
1200 {
1201 /*
1202 * Assume the first word is the make...
1203 */
a306e19f 1204
47afb329 1205 memcpy(make_and_model, model, ptr - model);
1206 make_and_model[ptr - model] = '\0';
a306e19f 1207
47afb329 1208 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s",
1209 make_and_model, ptr + 1);
1210 }
1211 }
a306e19f 1212
47afb329 1213 if (device_id[0])
1214 device->device_id = strdup(device_id);
1215 else
1216 device->device_id = NULL;
ad9fc101 1217
47afb329 1218 if (device->make_and_model)
1219 free(device->make_and_model);
ad9fc101 1220
47afb329 1221 if (make_and_model[0])
1222 {
1223 strlcat(make_and_model, " ", sizeof(make_and_model));
1224 strlcat(make_and_model, model, sizeof(make_and_model));
a306e19f 1225
47afb329 1226 device->make_and_model = strdup(make_and_model);
1227 }
1228 else
1229 device->make_and_model = strdup(model);
ad9fc101 1230}
ee9e056f 1231#endif /* HAVE_DNSSD || HAVE_AVAHI */
ad9fc101 1232
1233
f0dce363 1234/*
ee9e056f 1235 * 'sigterm_handler()' - Handle termination signals.
f0dce363 1236 */
1237
1238static void
1239sigterm_handler(int sig) /* I - Signal number (unused) */
1240{
4a241564 1241 (void)sig;
1242
90d07b87 1243 if (job_canceled)
1244 exit(CUPS_BACKEND_OK);
1245 else
1246 job_canceled = 1;
f0dce363 1247}
1248
1249
ad9fc101 1250/*
1251 * 'unquote()' - Unquote a name string.
1252 */
1253
1254static void
1255unquote(char *dst, /* I - Destination buffer */
1256 const char *src, /* I - Source string */
1257 size_t dstsize) /* I - Size of destination buffer */
1258{
1259 char *dstend = dst + dstsize - 1; /* End of destination buffer */
1260
1261
1262 while (*src && dst < dstend)
1263 {
1264 if (*src == '\\')
1265 {
1266 src ++;
1267 if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
1268 isdigit(src[2] & 255))
1269 {
1270 *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
1271 src += 3;
1272 }
1273 else
1274 *dst++ = *src++;
1275 }
1276 else
1277 *dst++ = *src ++;
1278 }
1279
1280 *dst = '\0';
1281}
1282
1283
1284/*
1285 * End of "$Id$".
1286 */