]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/cups-polld.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / cups-polld.c
1 /*
2 * "$Id: cups-polld.c 6649 2007-07-11 21:46:42Z mike $"
3 *
4 * Polling daemon for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
8 *
9 * These coded instructions, statements, and computer programs are the
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/".
14 *
15 * Contents:
16 *
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.
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>
34 #include <signal.h>
35
36
37 /*
38 * Local globals...
39 */
40
41 static int restart_polling = 1;
42
43
44 /*
45 * Local functions...
46 */
47
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,
50 const char *prefix);
51 static void sighup_handler(int sig);
52
53
54 /*
55 * 'main()' - Open sockets and poll until we are killed...
56 */
57
58 int /* O - Exit status */
59 main(int argc, /* I - Number of command-line args */
60 char *argv[]) /* I - Command-line arguments */
61 {
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 */
70 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
71 struct sigaction action; /* Actions for POSIX signals */
72 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
73
74
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
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 /*
144 * Loop forever, asking for available printers and classes...
145 */
146
147 for (http = NULL;;)
148 {
149 /*
150 * Open a connection to the server...
151 */
152
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 }
166
167 /*
168 * Get the printers and classes...
169 */
170
171 remain = interval;
172
173 if (http && (seconds = poll_server(http, sock, port, interval, prefix)) > 0)
174 remain -= seconds;
175
176 /*
177 * Sleep for any remaining time...
178 */
179
180 if (remain > 0 && !restart_polling)
181 sleep(remain);
182 }
183 }
184
185
186 /*
187 * 'dequote()' - Remote quotes from a string.
188 */
189
190 static char * /* O - Dequoted string */
191 dequote(char *d, /* I - Destination string */
192 const char *s, /* I - Source string */
193 int dlen) /* I - Destination length */
194 {
195 char *dptr; /* Pointer into destination */
196
197
198 if (s)
199 {
200 for (dptr = d, dlen --; *s && dlen > 0; s ++)
201 if (*s != '\"')
202 {
203 *dptr++ = *s;
204 dlen --;
205 }
206
207 *dptr = '\0';
208 }
209 else
210 *d = '\0';
211
212 return (d);
213 }
214
215
216 /*
217 * 'poll_server()' - Poll the server for the given set of printers or classes.
218 */
219
220 static int /* O - Number of seconds or -1 on error */
221 poll_server(http_t *http, /* I - HTTP connection */
222 int sock, /* I - Broadcast sock */
223 int port, /* I - Broadcast port */
224 int interval, /* I - Polling interval */
225 const char *prefix) /* I - Prefix for log messages */
226 {
227 int seconds; /* Number of seconds */
228 int count, /* Current number of printers/classes */
229 max_count; /* Maximum printers/classes per second */
230 ipp_t *request, /* Request data */
231 *response; /* Response data */
232 ipp_attribute_t *attr; /* Current attribute */
233 const char *uri; /* printer-uri */
234 char info[1024], /* printer-info */
235 job_sheets[1024],/* job-sheets-default */
236 location[1024], /* printer-location */
237 make_model[1024];
238 /* printer-make-and-model */
239 cups_ptype_t type; /* printer-type */
240 ipp_pstate_t state; /* printer-state */
241 int accepting; /* printer-is-accepting-jobs */
242 struct sockaddr_in addr; /* Broadcast address */
243 char packet[1540]; /* Data packet */
244 static const char * const attrs[] = /* Requested attributes */
245 {
246 "job-sheets-default",
247 "printer-info",
248 "printer-is-accepting-jobs",
249 "printer-location",
250 "printer-make-and-model",
251 "printer-name",
252 "printer-state",
253 "printer-type",
254 "printer-uri-supported"
255 };
256
257
258 /*
259 * Broadcast to 127.0.0.1 (localhost)
260 */
261
262 memset(&addr, 0, sizeof(addr));
263 addr.sin_addr.s_addr = htonl(0x7f000001);
264 addr.sin_family = AF_INET;
265 addr.sin_port = htons(port);
266
267 /*
268 * Build a CUPS_GET_PRINTERS request and pass along a list of the
269 * attributes we are interested in along with the types of printers
270 * (and classes) we want.
271 */
272
273 request = ippNewRequest(CUPS_GET_PRINTERS);
274
275 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
276 "requested-attributes", sizeof(attrs) / sizeof(attrs[0]),
277 NULL, attrs);
278
279 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
280 "printer-type", 0);
281 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
282 "printer-type-mask",
283 CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
284 CUPS_PRINTER_NOT_SHARED);
285
286 /*
287 * Do the request and get back a response...
288 */
289
290 seconds = time(NULL);
291 response = cupsDoRequest(http, request, "/");
292
293 if (cupsLastError() > IPP_OK_CONFLICT)
294 {
295 fprintf(stderr, "ERROR: %s CUPS-Get-Printers failed: %s\n", prefix,
296 cupsLastErrorString());
297 ippDelete(response);
298 return (-1);
299 }
300
301 if (response)
302 {
303 /*
304 * Figure out how many printers/classes we have...
305 */
306
307 for (attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME),
308 max_count = 0;
309 attr != NULL;
310 attr = ippFindNextAttribute(response, "printer-name", IPP_TAG_NAME),
311 max_count ++);
312
313 fprintf(stderr, "DEBUG: %s Found %d printers.\n", prefix, max_count);
314
315 count = 0;
316 max_count = max_count / interval + 1;
317
318 /*
319 * Loop through the printers or classes returned in the list...
320 */
321
322 for (attr = response->attrs; attr; attr = attr->next)
323 {
324 /*
325 * Skip leading attributes until we hit a printer...
326 */
327
328 while (attr && attr->group_tag != IPP_TAG_PRINTER)
329 attr = attr->next;
330
331 if (!attr)
332 break;
333
334 /*
335 * Pull the needed attributes from this printer...
336 */
337
338 uri = NULL;
339 info[0] = '\0';
340 job_sheets[0] = '\0';
341 location[0] = '\0';
342 make_model[0] = '\0';
343 type = CUPS_PRINTER_REMOTE;
344 accepting = 1;
345 state = IPP_PRINTER_IDLE;
346
347 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
348 {
349 if (!strcmp(attr->name, "job-sheets-default") &&
350 (attr->value_tag == IPP_TAG_NAME ||
351 attr->value_tag == IPP_TAG_KEYWORD))
352 {
353 if (attr->num_values == 1)
354 snprintf(job_sheets, sizeof(job_sheets), " job-sheets=%s",
355 attr->values[0].string.text);
356 else
357 snprintf(job_sheets, sizeof(job_sheets), " job-sheets=%s,%s",
358 attr->values[0].string.text,
359 attr->values[1].string.text);
360 }
361 else if (!strcmp(attr->name, "printer-uri-supported") &&
362 attr->value_tag == IPP_TAG_URI)
363 uri = attr->values[0].string.text;
364 else if (!strcmp(attr->name, "printer-info") &&
365 attr->value_tag == IPP_TAG_TEXT)
366 dequote(info, attr->values[0].string.text, sizeof(info));
367 else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
368 attr->value_tag == IPP_TAG_BOOLEAN)
369 accepting = attr->values[0].boolean;
370 else if (!strcmp(attr->name, "printer-location") &&
371 attr->value_tag == IPP_TAG_TEXT)
372 dequote(location, attr->values[0].string.text, sizeof(location));
373 else if (!strcmp(attr->name, "printer-make-and-model") &&
374 attr->value_tag == IPP_TAG_TEXT)
375 dequote(make_model, attr->values[0].string.text, sizeof(location));
376 else if (!strcmp(attr->name, "printer-state") &&
377 attr->value_tag == IPP_TAG_ENUM)
378 state = (ipp_pstate_t)attr->values[0].integer;
379 else if (!strcmp(attr->name, "printer-type") &&
380 attr->value_tag == IPP_TAG_ENUM)
381 type = (cups_ptype_t)attr->values[0].integer;
382
383 attr = attr->next;
384 }
385
386 /*
387 * See if we have everything needed...
388 */
389
390 if (uri == NULL)
391 {
392 if (attr == NULL)
393 break;
394 else
395 continue;
396 }
397
398 /*
399 * Send the printer information...
400 */
401
402 type |= CUPS_PRINTER_REMOTE;
403
404 if (!accepting)
405 type |= CUPS_PRINTER_REJECTING;
406
407 snprintf(packet, sizeof(packet),
408 "%x %x %s \"%s\" \"%s\" \"%s\" lease-duration=%d%s\n",
409 type, state, uri, location, info, make_model, interval * 2,
410 job_sheets);
411
412 fprintf(stderr, "DEBUG2: %s Sending %s", prefix, packet);
413
414 if (sendto(sock, packet, strlen(packet), 0,
415 (struct sockaddr *)&addr, sizeof(addr)) <= 0)
416 {
417 ippDelete(response);
418 perror("cups-polld");
419 return (-1);
420 }
421
422 /*
423 * Throttle the local broadcasts as needed so that we don't
424 * overwhelm the local server...
425 */
426
427 count ++;
428 if (count >= max_count)
429 {
430 /*
431 * Sleep for a second...
432 */
433
434 count = 0;
435
436 sleep(1);
437 }
438
439 if (!attr || restart_polling)
440 break;
441 }
442
443 ippDelete(response);
444 }
445
446 /*
447 * Return the number of seconds we used...
448 */
449
450 return (time(NULL) - seconds);
451 }
452
453
454 /*
455 * 'sighup_handler()' - Handle 'hangup' signals to restart polling.
456 */
457
458 static void
459 sighup_handler(int sig) /* I - Signal number */
460 {
461 (void)sig;
462
463 restart_polling = 1;
464
465 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
466 signal(SIGHUP, sighup_handler);
467 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
468 }
469
470
471 /*
472 * End of "$Id: cups-polld.c 6649 2007-07-11 21:46:42Z mike $".
473 */