]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | /* |
bc44d920 | 2 | * "$Id: cups-polld.c 6649 2007-07-11 21:46:42Z mike $" |
ef416fc2 | 3 | * |
4 | * Polling daemon for the Common UNIX Printing System (CUPS). | |
5 | * | |
bc44d920 | 6 | * Copyright 2007 by Apple Inc. |
8ca02f3c | 7 | * Copyright 1997-2006 by Easy Software Products, all rights reserved. |
ef416fc2 | 8 | * |
9 | * These coded instructions, statements, and computer programs are the | |
bc44d920 | 10 | * property of Apple Inc. and are protected by Federal copyright |
11 | * law. Distribution and use rights are outlined in the file "LICENSE.txt" | |
12 | * which should have been included with this file. If this file is | |
13 | * file is missing or damaged, see the license at "http://www.cups.org/". | |
ef416fc2 | 14 | * |
15 | * Contents: | |
16 | * | |
d09495fa | 17 | * main() - Open sockets and poll until we are killed... |
18 | * dequote() - Remote quotes from a string. | |
19 | * poll_server() - Poll the server for the given set of printers or | |
20 | * classes. | |
21 | * sighup_handler() - Handle 'hangup' signals to restart polling. | |
ef416fc2 | 22 | */ |
23 | ||
24 | /* | |
25 | * Include necessary headers... | |
26 | */ | |
27 | ||
28 | #include <cups/http-private.h> | |
29 | #include <cups/cups.h> | |
30 | #include <stdlib.h> | |
31 | #include <errno.h> | |
32 | #include <cups/language.h> | |
33 | #include <cups/string.h> | |
d09495fa | 34 | #include <signal.h> |
35 | ||
36 | ||
37 | /* | |
38 | * Local globals... | |
39 | */ | |
40 | ||
41 | static int restart_polling = 1; | |
ef416fc2 | 42 | |
43 | ||
44 | /* | |
45 | * Local functions... | |
46 | */ | |
47 | ||
8ca02f3c | 48 | static char *dequote(char *d, const char *s, int dlen); |
49 | static int poll_server(http_t *http, int sock, int port, int interval, | |
e1d6a774 | 50 | const char *prefix); |
d09495fa | 51 | static void sighup_handler(int sig); |
ef416fc2 | 52 | |
53 | ||
54 | /* | |
55 | * 'main()' - Open sockets and poll until we are killed... | |
56 | */ | |
57 | ||
58 | int /* O - Exit status */ | |
8ca02f3c | 59 | main(int argc, /* I - Number of command-line args */ |
ef416fc2 | 60 | char *argv[]) /* I - Command-line arguments */ |
61 | { | |
8ca02f3c | 62 | http_t *http; /* HTTP connection */ |
63 | int interval; /* Polling interval */ | |
64 | int sock; /* Browser sock */ | |
65 | int port; /* Browser port */ | |
66 | int val; /* Socket option value */ | |
67 | int seconds, /* Seconds left from poll */ | |
68 | remain; /* Total remaining time to sleep */ | |
69 | char prefix[1024]; /* Prefix for log messages */ | |
d09495fa | 70 | #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) |
71 | struct sigaction action; /* Actions for POSIX signals */ | |
72 | #endif /* HAVE_SIGACTION && !HAVE_SIGSET */ | |
ef416fc2 | 73 | |
74 | ||
d09495fa | 75 | /* |
76 | * Catch hangup signals for when the network changes... | |
77 | */ | |
78 | ||
79 | #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ | |
80 | sigset(SIGHUP, sighup_handler); | |
81 | #elif defined(HAVE_SIGACTION) | |
82 | memset(&action, 0, sizeof(action)); | |
83 | ||
84 | sigemptyset(&action.sa_mask); | |
85 | sigaddset(&action.sa_mask, SIGHUP); | |
86 | action.sa_handler = sighup_handler; | |
87 | sigaction(SIGHUP, &action, NULL); | |
88 | #else | |
89 | signal(SIGHUP, sighup_handler); | |
90 | #endif /* HAVE_SIGSET */ | |
91 | ||
ef416fc2 | 92 | /* |
93 | * Don't buffer log messages... | |
94 | */ | |
95 | ||
96 | setbuf(stderr, NULL); | |
97 | ||
98 | /* | |
99 | * The command-line must contain the following: | |
100 | * | |
101 | * cups-polld server server-port interval port | |
102 | */ | |
103 | ||
104 | if (argc != 5) | |
105 | { | |
106 | fputs("Usage: cups-polld server server-port interval port\n", stderr); | |
107 | return (1); | |
108 | } | |
109 | ||
110 | interval = atoi(argv[3]); | |
111 | port = atoi(argv[4]); | |
112 | ||
113 | if (interval < 2) | |
114 | interval = 2; | |
115 | ||
116 | snprintf(prefix, sizeof(prefix), "[cups-polld %s:%d]", argv[1], atoi(argv[2])); | |
117 | ||
118 | /* | |
119 | * Open a broadcast socket... | |
120 | */ | |
121 | ||
122 | if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) | |
123 | { | |
124 | fprintf(stderr, "ERROR: %s Unable to open broadcast socket: %s\n", prefix, | |
125 | strerror(errno)); | |
126 | return (1); | |
127 | } | |
128 | ||
129 | /* | |
130 | * Set the "broadcast" flag... | |
131 | */ | |
132 | ||
133 | val = 1; | |
134 | if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val))) | |
135 | { | |
136 | fprintf(stderr, "ERROR: %s Unable to put socket in broadcast mode: %s\n", | |
137 | prefix, strerror(errno)); | |
138 | ||
139 | close(sock); | |
140 | return (1); | |
141 | } | |
142 | ||
143 | /* | |
d09495fa | 144 | * Loop forever, asking for available printers and classes... |
ef416fc2 | 145 | */ |
146 | ||
080811b1 | 147 | for (http = NULL; !ferror(stderr);) |
ef416fc2 | 148 | { |
d09495fa | 149 | /* |
150 | * Open a connection to the server... | |
151 | */ | |
ef416fc2 | 152 | |
d09495fa | 153 | if (restart_polling || !http) |
154 | { | |
155 | restart_polling = 0; | |
156 | httpClose(http); | |
157 | ||
158 | if ((http = httpConnectEncrypt(argv[1], atoi(argv[2]), | |
159 | cupsEncryption())) == NULL) | |
160 | { | |
161 | fprintf(stderr, "ERROR: %s Unable to connect to %s on port %s: %s\n", | |
162 | prefix, argv[1], argv[2], | |
163 | h_errno ? hstrerror(h_errno) : strerror(errno)); | |
164 | } | |
165 | } | |
ef416fc2 | 166 | |
ef416fc2 | 167 | /* |
8ca02f3c | 168 | * Get the printers and classes... |
ef416fc2 | 169 | */ |
170 | ||
171 | remain = interval; | |
172 | ||
d09495fa | 173 | if (http && (seconds = poll_server(http, sock, port, interval, prefix)) > 0) |
ef416fc2 | 174 | remain -= seconds; |
175 | ||
176 | /* | |
177 | * Sleep for any remaining time... | |
178 | */ | |
179 | ||
d09495fa | 180 | if (remain > 0 && !restart_polling) |
ef416fc2 | 181 | sleep(remain); |
182 | } | |
080811b1 MS |
183 | |
184 | return (1); | |
ef416fc2 | 185 | } |
186 | ||
187 | ||
8ca02f3c | 188 | /* |
189 | * 'dequote()' - Remote quotes from a string. | |
190 | */ | |
191 | ||
192 | static char * /* O - Dequoted string */ | |
193 | dequote(char *d, /* I - Destination string */ | |
194 | const char *s, /* I - Source string */ | |
195 | int dlen) /* I - Destination length */ | |
196 | { | |
197 | char *dptr; /* Pointer into destination */ | |
198 | ||
199 | ||
200 | if (s) | |
201 | { | |
202 | for (dptr = d, dlen --; *s && dlen > 0; s ++) | |
203 | if (*s != '\"') | |
204 | { | |
205 | *dptr++ = *s; | |
206 | dlen --; | |
207 | } | |
208 | ||
209 | *dptr = '\0'; | |
210 | } | |
211 | else | |
212 | *d = '\0'; | |
213 | ||
214 | return (d); | |
215 | } | |
216 | ||
217 | ||
ef416fc2 | 218 | /* |
219 | * 'poll_server()' - Poll the server for the given set of printers or classes. | |
220 | */ | |
221 | ||
e1d6a774 | 222 | static int /* O - Number of seconds or -1 on error */ |
ef416fc2 | 223 | poll_server(http_t *http, /* I - HTTP connection */ |
ef416fc2 | 224 | int sock, /* I - Broadcast sock */ |
225 | int port, /* I - Broadcast port */ | |
226 | int interval, /* I - Polling interval */ | |
227 | const char *prefix) /* I - Prefix for log messages */ | |
228 | { | |
229 | int seconds; /* Number of seconds */ | |
230 | int count, /* Current number of printers/classes */ | |
231 | max_count; /* Maximum printers/classes per second */ | |
232 | ipp_t *request, /* Request data */ | |
233 | *response; /* Response data */ | |
234 | ipp_attribute_t *attr; /* Current attribute */ | |
8ca02f3c | 235 | const char *uri; /* printer-uri */ |
236 | char info[1024], /* printer-info */ | |
237 | job_sheets[1024],/* job-sheets-default */ | |
238 | location[1024], /* printer-location */ | |
239 | make_model[1024]; | |
240 | /* printer-make-and-model */ | |
ef416fc2 | 241 | cups_ptype_t type; /* printer-type */ |
242 | ipp_pstate_t state; /* printer-state */ | |
243 | int accepting; /* printer-is-accepting-jobs */ | |
244 | struct sockaddr_in addr; /* Broadcast address */ | |
245 | char packet[1540]; /* Data packet */ | |
246 | static const char * const attrs[] = /* Requested attributes */ | |
247 | { | |
8ca02f3c | 248 | "job-sheets-default", |
ef416fc2 | 249 | "printer-info", |
250 | "printer-is-accepting-jobs", | |
251 | "printer-location", | |
252 | "printer-make-and-model", | |
253 | "printer-name", | |
254 | "printer-state", | |
255 | "printer-type", | |
256 | "printer-uri-supported" | |
257 | }; | |
258 | ||
259 | ||
260 | /* | |
261 | * Broadcast to 127.0.0.1 (localhost) | |
262 | */ | |
263 | ||
264 | memset(&addr, 0, sizeof(addr)); | |
265 | addr.sin_addr.s_addr = htonl(0x7f000001); | |
266 | addr.sin_family = AF_INET; | |
267 | addr.sin_port = htons(port); | |
268 | ||
269 | /* | |
8ca02f3c | 270 | * Build a CUPS_GET_PRINTERS request and pass along a list of the |
271 | * attributes we are interested in along with the types of printers | |
272 | * (and classes) we want. | |
ef416fc2 | 273 | */ |
274 | ||
8ca02f3c | 275 | request = ippNewRequest(CUPS_GET_PRINTERS); |
ef416fc2 | 276 | |
277 | ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
278 | "requested-attributes", sizeof(attrs) / sizeof(attrs[0]), | |
279 | NULL, attrs); | |
280 | ||
281 | ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, | |
282 | "printer-type", 0); | |
283 | ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM, | |
284 | "printer-type-mask", | |
285 | CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT | | |
286 | CUPS_PRINTER_NOT_SHARED); | |
287 | ||
288 | /* | |
289 | * Do the request and get back a response... | |
290 | */ | |
291 | ||
d09495fa | 292 | seconds = time(NULL); |
8ca02f3c | 293 | response = cupsDoRequest(http, request, "/"); |
294 | ||
295 | if (cupsLastError() > IPP_OK_CONFLICT) | |
ef416fc2 | 296 | { |
8ca02f3c | 297 | fprintf(stderr, "ERROR: %s CUPS-Get-Printers failed: %s\n", prefix, |
298 | cupsLastErrorString()); | |
299 | ippDelete(response); | |
300 | return (-1); | |
301 | } | |
ef416fc2 | 302 | |
8ca02f3c | 303 | if (response) |
304 | { | |
ef416fc2 | 305 | /* |
306 | * Figure out how many printers/classes we have... | |
307 | */ | |
308 | ||
309 | for (attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME), | |
310 | max_count = 0; | |
311 | attr != NULL; | |
312 | attr = ippFindNextAttribute(response, "printer-name", IPP_TAG_NAME), | |
313 | max_count ++); | |
314 | ||
8ca02f3c | 315 | fprintf(stderr, "DEBUG: %s Found %d printers.\n", prefix, max_count); |
ef416fc2 | 316 | |
317 | count = 0; | |
ef416fc2 | 318 | max_count = max_count / interval + 1; |
319 | ||
320 | /* | |
321 | * Loop through the printers or classes returned in the list... | |
322 | */ | |
323 | ||
8ca02f3c | 324 | for (attr = response->attrs; attr; attr = attr->next) |
ef416fc2 | 325 | { |
326 | /* | |
327 | * Skip leading attributes until we hit a printer... | |
328 | */ | |
329 | ||
8ca02f3c | 330 | while (attr && attr->group_tag != IPP_TAG_PRINTER) |
ef416fc2 | 331 | attr = attr->next; |
332 | ||
8ca02f3c | 333 | if (!attr) |
ef416fc2 | 334 | break; |
335 | ||
336 | /* | |
337 | * Pull the needed attributes from this printer... | |
338 | */ | |
339 | ||
8ca02f3c | 340 | uri = NULL; |
341 | info[0] = '\0'; | |
342 | job_sheets[0] = '\0'; | |
343 | location[0] = '\0'; | |
344 | make_model[0] = '\0'; | |
345 | type = CUPS_PRINTER_REMOTE; | |
346 | accepting = 1; | |
347 | state = IPP_PRINTER_IDLE; | |
ef416fc2 | 348 | |
349 | while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) | |
350 | { | |
8ca02f3c | 351 | if (!strcmp(attr->name, "job-sheets-default") && |
352 | (attr->value_tag == IPP_TAG_NAME || | |
353 | attr->value_tag == IPP_TAG_KEYWORD)) | |
354 | { | |
355 | if (attr->num_values == 1) | |
356 | snprintf(job_sheets, sizeof(job_sheets), " job-sheets=%s", | |
357 | attr->values[0].string.text); | |
358 | else | |
359 | snprintf(job_sheets, sizeof(job_sheets), " job-sheets=%s,%s", | |
360 | attr->values[0].string.text, | |
361 | attr->values[1].string.text); | |
362 | } | |
363 | else if (!strcmp(attr->name, "printer-uri-supported") && | |
364 | attr->value_tag == IPP_TAG_URI) | |
ef416fc2 | 365 | uri = attr->values[0].string.text; |
8ca02f3c | 366 | else if (!strcmp(attr->name, "printer-info") && |
367 | attr->value_tag == IPP_TAG_TEXT) | |
368 | dequote(info, attr->values[0].string.text, sizeof(info)); | |
369 | else if (!strcmp(attr->name, "printer-is-accepting-jobs") && | |
370 | attr->value_tag == IPP_TAG_BOOLEAN) | |
ef416fc2 | 371 | accepting = attr->values[0].boolean; |
8ca02f3c | 372 | else if (!strcmp(attr->name, "printer-location") && |
373 | attr->value_tag == IPP_TAG_TEXT) | |
374 | dequote(location, attr->values[0].string.text, sizeof(location)); | |
375 | else if (!strcmp(attr->name, "printer-make-and-model") && | |
376 | attr->value_tag == IPP_TAG_TEXT) | |
377 | dequote(make_model, attr->values[0].string.text, sizeof(location)); | |
378 | else if (!strcmp(attr->name, "printer-state") && | |
379 | attr->value_tag == IPP_TAG_ENUM) | |
ef416fc2 | 380 | state = (ipp_pstate_t)attr->values[0].integer; |
8ca02f3c | 381 | else if (!strcmp(attr->name, "printer-type") && |
382 | attr->value_tag == IPP_TAG_ENUM) | |
ef416fc2 | 383 | type = (cups_ptype_t)attr->values[0].integer; |
384 | ||
385 | attr = attr->next; | |
386 | } | |
387 | ||
388 | /* | |
389 | * See if we have everything needed... | |
390 | */ | |
391 | ||
392 | if (uri == NULL) | |
393 | { | |
394 | if (attr == NULL) | |
395 | break; | |
396 | else | |
397 | continue; | |
398 | } | |
399 | ||
400 | /* | |
401 | * Send the printer information... | |
402 | */ | |
403 | ||
404 | type |= CUPS_PRINTER_REMOTE; | |
405 | ||
406 | if (!accepting) | |
407 | type |= CUPS_PRINTER_REJECTING; | |
408 | ||
8ca02f3c | 409 | snprintf(packet, sizeof(packet), |
410 | "%x %x %s \"%s\" \"%s\" \"%s\" lease-duration=%d%s\n", | |
411 | type, state, uri, location, info, make_model, interval * 2, | |
412 | job_sheets); | |
ef416fc2 | 413 | |
414 | fprintf(stderr, "DEBUG2: %s Sending %s", prefix, packet); | |
415 | ||
416 | if (sendto(sock, packet, strlen(packet), 0, | |
417 | (struct sockaddr *)&addr, sizeof(addr)) <= 0) | |
418 | { | |
419 | ippDelete(response); | |
420 | perror("cups-polld"); | |
421 | return (-1); | |
422 | } | |
423 | ||
424 | /* | |
425 | * Throttle the local broadcasts as needed so that we don't | |
426 | * overwhelm the local server... | |
427 | */ | |
428 | ||
429 | count ++; | |
430 | if (count >= max_count) | |
431 | { | |
432 | /* | |
433 | * Sleep for a second... | |
434 | */ | |
435 | ||
436 | count = 0; | |
8ca02f3c | 437 | |
ef416fc2 | 438 | sleep(1); |
439 | } | |
440 | ||
d09495fa | 441 | if (!attr || restart_polling) |
ef416fc2 | 442 | break; |
443 | } | |
444 | ||
445 | ippDelete(response); | |
446 | } | |
ef416fc2 | 447 | |
448 | /* | |
449 | * Return the number of seconds we used... | |
450 | */ | |
451 | ||
452 | return (time(NULL) - seconds); | |
453 | } | |
454 | ||
455 | ||
456 | /* | |
d09495fa | 457 | * 'sighup_handler()' - Handle 'hangup' signals to restart polling. |
458 | */ | |
459 | ||
460 | static void | |
461 | sighup_handler(int sig) /* I - Signal number */ | |
462 | { | |
463 | (void)sig; | |
464 | ||
465 | restart_polling = 1; | |
466 | ||
467 | #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION) | |
468 | signal(SIGHUP, sighup_handler); | |
469 | #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */ | |
470 | } | |
471 | ||
472 | ||
473 | /* | |
bc44d920 | 474 | * End of "$Id: cups-polld.c 6649 2007-07-11 21:46:42Z mike $". |
ef416fc2 | 475 | */ |