]> 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 5871 2006-08-23 20:55:33Z mike $"
3 *
4 * Polling daemon for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
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 *
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.
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>
43 #include <signal.h>
44
45
46 /*
47 * Local globals...
48 */
49
50 static int restart_polling = 1;
51
52
53 /*
54 * Local functions...
55 */
56
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,
59 const char *prefix);
60 static void sighup_handler(int sig);
61
62
63 /*
64 * 'main()' - Open sockets and poll until we are killed...
65 */
66
67 int /* O - Exit status */
68 main(int argc, /* I - Number of command-line args */
69 char *argv[]) /* I - Command-line arguments */
70 {
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 */
79 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
80 struct sigaction action; /* Actions for POSIX signals */
81 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
82
83
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
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 /*
153 * Loop forever, asking for available printers and classes...
154 */
155
156 for (http = NULL;;)
157 {
158 /*
159 * Open a connection to the server...
160 */
161
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 }
175
176 /*
177 * Get the printers and classes...
178 */
179
180 remain = interval;
181
182 if (http && (seconds = poll_server(http, sock, port, interval, prefix)) > 0)
183 remain -= seconds;
184
185 /*
186 * Sleep for any remaining time...
187 */
188
189 if (remain > 0 && !restart_polling)
190 sleep(remain);
191 }
192 }
193
194
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
225 /*
226 * 'poll_server()' - Poll the server for the given set of printers or classes.
227 */
228
229 static int /* O - Number of seconds or -1 on error */
230 poll_server(http_t *http, /* I - HTTP connection */
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 */
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 */
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 {
255 "job-sheets-default",
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 /*
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.
280 */
281
282 request = ippNewRequest(CUPS_GET_PRINTERS);
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
299 seconds = time(NULL);
300 response = cupsDoRequest(http, request, "/");
301
302 if (cupsLastError() > IPP_OK_CONFLICT)
303 {
304 fprintf(stderr, "ERROR: %s CUPS-Get-Printers failed: %s\n", prefix,
305 cupsLastErrorString());
306 ippDelete(response);
307 return (-1);
308 }
309
310 if (response)
311 {
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
322 fprintf(stderr, "DEBUG: %s Found %d printers.\n", prefix, max_count);
323
324 count = 0;
325 max_count = max_count / interval + 1;
326
327 /*
328 * Loop through the printers or classes returned in the list...
329 */
330
331 for (attr = response->attrs; attr; attr = attr->next)
332 {
333 /*
334 * Skip leading attributes until we hit a printer...
335 */
336
337 while (attr && attr->group_tag != IPP_TAG_PRINTER)
338 attr = attr->next;
339
340 if (!attr)
341 break;
342
343 /*
344 * Pull the needed attributes from this printer...
345 */
346
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;
355
356 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
357 {
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)
372 uri = attr->values[0].string.text;
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)
378 accepting = attr->values[0].boolean;
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)
387 state = (ipp_pstate_t)attr->values[0].integer;
388 else if (!strcmp(attr->name, "printer-type") &&
389 attr->value_tag == IPP_TAG_ENUM)
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
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);
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;
444
445 sleep(1);
446 }
447
448 if (!attr || restart_polling)
449 break;
450 }
451
452 ippDelete(response);
453 }
454
455 /*
456 * Return the number of seconds we used...
457 */
458
459 return (time(NULL) - seconds);
460 }
461
462
463 /*
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 /*
481 * End of "$Id: cups-polld.c 5871 2006-08-23 20:55:33Z mike $".
482 */