]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/cups-polld.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / cups-polld.c
CommitLineData
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
50static int restart_polling = 1;
ef416fc2 51
52
53/*
54 * Local functions...
55 */
56
8ca02f3c 57static char *dequote(char *d, const char *s, int dlen);
58static int poll_server(http_t *http, int sock, int port, int interval,
e1d6a774 59 const char *prefix);
d09495fa 60static void sighup_handler(int sig);
ef416fc2 61
62
63/*
64 * 'main()' - Open sockets and poll until we are killed...
65 */
66
67int /* O - Exit status */
8ca02f3c 68main(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
199static char * /* O - Dequoted string */
200dequote(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 229static int /* O - Number of seconds or -1 on error */
ef416fc2 230poll_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
467static void
468sighup_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 */