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