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