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