]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/main.c
Update scheduler to use new environment variable API - this reduces code
[thirdparty/cups.git] / scheduler / main.c
CommitLineData
3840d6ba 1/*
b2e10895 2 * "$Id$"
3840d6ba 3 *
e5cf24b2 4 * Scheduler main loop for the Common UNIX Printing System (CUPS).
3840d6ba 5 *
c9d3f842 6 * Copyright 1997-2005 by Easy Software Products, all rights reserved.
3840d6ba 7 *
fd8b1cf8 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" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
3840d6ba 14 *
fd8b1cf8 15 * Attn: CUPS Licensing Information
16 * Easy Software Products
8784b6a6 17 * 44141 Airport View Drive, Suite 204
bcd9e019 18 * Hollywood, Maryland 20636 USA
3840d6ba 19 *
edfd3c3d 20 * Voice: (301) 373-9600
fd8b1cf8 21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
992cf15a 23 *
fd8b1cf8 24 * Contents:
3840d6ba 25 *
d7845573 26 * main() - Main entry for the CUPS scheduler.
8650fcf2 27 * cupsdClosePipe() - Close a pipe as necessary.
28 * cupsdOpenPipe() - Create a pipe which is closed on exec.
d7845573 29 * CatchChildSignals() - Catch SIGCHLD signals...
38743560 30 * HoldSignals() - Hold child and termination signals.
d7845573 31 * IgnoreChildSignals() - Ignore SIGCHLD signals...
38743560 32 * ReleaseSignals() - Release signals for delivery.
36992080 33 * SetString() - Set a string value.
34 * SetStringf() - Set a formatted string value.
af57319f 35 * parent_handler() - Catch USR1/CHLD signals...
2d417cb3 36 * process_children() - Process all dead children...
d7845573 37 * sigchld_handler() - Handle 'child' signals from old processes.
38 * sighup_handler() - Handle 'hangup' signals to reconfigure the scheduler.
39 * sigterm_handler() - Handle 'terminate' signals that stop the scheduler.
045a79ad 40 * select_timeout() - Calculate the select timeout value.
d7845573 41 * usage() - Show scheduler usage.
3840d6ba 42 */
43
44/*
45 * Include necessary headers...
46 */
47
48#define _MAIN_C_
49#include "cupsd.h"
8b43895a 50#include <sys/resource.h>
a3e17a89 51#include <syslog.h>
daeeb91c 52#include <grp.h>
3840d6ba 53
2bdd1992 54#if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
8c57ced1 55# include <malloc.h>
2bdd1992 56#endif /* HAVE_MALLOC_H && HAVE_MALLINFO */
8c57ced1 57
3840d6ba 58
59/*
fd8b1cf8 60 * Local functions...
61 */
62
af57319f 63static void parent_handler(int sig);
2d417cb3 64static void process_children(void);
e5cf24b2 65static void sigchld_handler(int sig);
fd8b1cf8 66static void sighup_handler(int sig);
a3e17a89 67static void sigterm_handler(int sig);
045a79ad 68static long select_timeout(int fds);
fd8b1cf8 69static void usage(void);
70
71
38743560 72/*
73 * Local globals...
74 */
75
af57319f 76static int parent_signal = 0; /* Set to signal number from child */
daf8f5f0 77static int holdcount = 0; /* Number of times "hold" was called */
38743560 78#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
3d72d58a 79static sigset_t holdmask; /* Old POSIX signal mask */
38743560 80#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
2d417cb3 81static int dead_children = 0; /* Dead children? */
52a9a175 82static int stop_scheduler = 0; /* Should the scheduler stop? */
38743560 83
84
fd8b1cf8 85/*
86 * 'main()' - Main entry for the CUPS scheduler.
3840d6ba 87 */
88
38743560 89int /* O - Exit status */
90main(int argc, /* I - Number of command-line arguments */
91 char *argv[]) /* I - Command-line arguments */
3840d6ba 92{
93 int i; /* Looping var */
fd8b1cf8 94 char *opt; /* Option character */
a3e17a89 95 int fg; /* Run in the foreground */
045a79ad 96 int fds; /* Number of ready descriptors select returns */
f3bc1068 97 fd_set *input, /* Input set for select() */
98 *output; /* Output set for select() */
fd8b1cf8 99 client_t *con; /* Current client */
6abc7437 100 job_t *job, /* Current job */
101 *next; /* Next job */
fd8b1cf8 102 listener_t *lis; /* Current listener */
992cf15a 103 time_t activity; /* Activity timer */
c2179d0f 104 time_t browse_time; /* Next browse send time */
05ca02bc 105 time_t senddoc_time; /* Send-Document time */
2bdd1992 106#ifdef HAVE_MALLINFO
107 time_t mallinfo_time; /* Malloc information time */
108#endif /* HAVE_MALLINFO */
3840d6ba 109 struct timeval timeout; /* select() timeout */
8b43895a 110 struct rlimit limit; /* Runtime limit */
8a2c2126 111#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
e5cf24b2 112 struct sigaction action; /* Actions for POSIX signals */
8a2c2126 113#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
cb9d2abc 114#ifdef __sgi
7b0fde61 115 cups_file_t *fp; /* Fake lpsched lock file */
52a9a175 116 struct stat statbuf; /* Needed for checking lpsched FIFO */
cb9d2abc 117#endif /* __sgi */
3840d6ba 118
119
fd8b1cf8 120 /*
121 * Check for command-line arguments...
122 */
123
a3e17a89 124 fg = 0;
125
fd8b1cf8 126 for (i = 1; i < argc; i ++)
127 if (argv[i][0] == '-')
128 for (opt = argv[i] + 1; *opt != '\0'; opt ++)
129 switch (*opt)
130 {
131 case 'c' : /* Configuration file */
132 i ++;
133 if (i >= argc)
134 usage();
135
a3e17a89 136 if (argv[i][0] == '/')
137 {
138 /*
139 * Absolute directory...
140 */
141
36992080 142 SetString(&ConfigurationFile, argv[i]);
a3e17a89 143 }
144 else
145 {
146 /*
147 * Relative directory...
148 */
149
36992080 150 char current[1024]; /* Current directory */
151
152
153 getcwd(current, sizeof(current));
154 SetStringf(&ConfigurationFile, "%s/%s", current, argv[i]);
a3e17a89 155 }
156 break;
157
158 case 'f' : /* Run in foreground... */
159 fg = 1;
fd8b1cf8 160 break;
161
5fe35f41 162 case 'F' : /* Run in foreground, but still disconnect from terminal... */
163 fg = -1;
164 break;
165
fd8b1cf8 166 default : /* Unknown option */
167 fprintf(stderr, "cupsd: Unknown option \'%c\' - aborting!\n", *opt);
168 usage();
169 break;
170 }
171 else
172 {
173 fprintf(stderr, "cupsd: Unknown argument \'%s\' - aborting!\n", argv[i]);
174 usage();
175 }
176
beb575a9 177 if (!ConfigurationFile)
178 SetString(&ConfigurationFile, CUPS_SERVERROOT "/cupsd.conf");
179
992cf15a 180 /*
a3e17a89 181 * If the user hasn't specified "-f", run in the background...
992cf15a 182 */
183
a3e17a89 184 if (!fg)
185 {
7f797921 186 /*
187 * Setup signal handlers for the parent...
188 */
05e63c18 189
5fe35f41 190#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
af57319f 191 sigset(SIGUSR1, parent_handler);
192 sigset(SIGCHLD, parent_handler);
58c40fb3 193
7f797921 194 sigset(SIGHUP, SIG_IGN);
5fe35f41 195#elif defined(HAVE_SIGACTION)
7f797921 196 memset(&action, 0, sizeof(action));
197 sigemptyset(&action.sa_mask);
198 sigaddset(&action.sa_mask, SIGUSR1);
af57319f 199 action.sa_handler = parent_handler;
7f797921 200 sigaction(SIGUSR1, &action, NULL);
af57319f 201 sigaction(SIGCHLD, &action, NULL);
7f797921 202
203 sigemptyset(&action.sa_mask);
204 action.sa_handler = SIG_IGN;
205 sigaction(SIGHUP, &action, NULL);
5fe35f41 206#else
af57319f 207 signal(SIGUSR1, parent_handler);
208 signal(SIGCLD, parent_handler);
58c40fb3 209
7f797921 210 signal(SIGHUP, SIG_IGN);
5fe35f41 211#endif /* HAVE_SIGSET */
212
7f797921 213 if (fork() > 0)
214 {
215 /*
af57319f 216 * OK, wait for the child to startup and send us SIGUSR1 or to crash
217 * and the OS send us SIGCHLD... We also need to ignore SIGHUP which
218 * might be sent by the init script to restart the scheduler...
7f797921 219 */
220
af57319f 221 for (; parent_signal == 0;)
222 sleep(1);
5fe35f41 223
af57319f 224 if (parent_signal == SIGUSR1)
5fe35f41 225 return (0);
226
af57319f 227 if (wait(&i) < 0)
228 {
229 perror("cupsd");
f2d1c59d 230 return (1);
231 }
232 else if (WIFEXITED(i))
233 {
234 fprintf(stderr, "cupsd: Child exited with status %d!\n", WEXITSTATUS(i));
235 return (2);
af57319f 236 }
5fe35f41 237 else
f2d1c59d 238 {
239 fprintf(stderr, "cupsd: Child exited on signal %d!\n", WTERMSIG(i));
240 return (3);
241 }
5fe35f41 242 }
243 }
244
245 if (fg < 1)
246 {
a3e17a89 247 /*
248 * Make sure we aren't tying up any filesystems...
249 */
250
251 chdir("/");
992cf15a 252
8b43895a 253#ifndef DEBUG
a3e17a89 254 /*
255 * Disable core dumps...
256 */
257
258 getrlimit(RLIMIT_CORE, &limit);
259 limit.rlim_cur = 0;
260 setrlimit(RLIMIT_CORE, &limit);
261
262 /*
263 * Disconnect from the controlling terminal...
264 */
265
a3e17a89 266 setsid();
da77a142 267
268 /*
269 * Close all open files...
270 */
271
272 getrlimit(RLIMIT_NOFILE, &limit);
273
274 for (i = 0; i < limit.rlim_cur; i ++)
275 close(i);
a3e17a89 276#endif /* DEBUG */
277 }
147ddbb6 278
279 /*
a3e17a89 280 * Set the timezone info...
147ddbb6 281 */
282
a3e17a89 283 tzset();
8b43895a 284
d0e4469d 285#ifdef LC_TIME
286 setlocale(LC_TIME, "");
287#endif /* LC_TIME */
288
8b43895a 289 /*
290 * Set the maximum number of files...
291 */
292
293 getrlimit(RLIMIT_NOFILE, &limit);
a351bcb0 294
295 if (limit.rlim_max > CUPS_MAX_FDS)
296 MaxFDs = CUPS_MAX_FDS;
297 else
298 MaxFDs = limit.rlim_max;
299
300 limit.rlim_cur = MaxFDs;
301
8b43895a 302 setrlimit(RLIMIT_NOFILE, &limit);
303
f3bc1068 304 /*
305 * Allocate memory for the input and output sets...
306 */
307
cf859e99 308 SetSize = (MaxFDs + 31) / 8 + 4;
7250da12 309 if (SetSize < sizeof(fd_set))
310 SetSize = sizeof(fd_set);
311
f3bc1068 312 InputSet = (fd_set *)calloc(1, SetSize);
313 OutputSet = (fd_set *)calloc(1, SetSize);
314 input = (fd_set *)calloc(1, SetSize);
315 output = (fd_set *)calloc(1, SetSize);
316
317 if (InputSet == NULL || OutputSet == NULL || input == NULL || output == NULL)
318 {
319 syslog(LOG_LPR, "Unable to allocate memory for select() sets - exiting!");
320 return (1);
321 }
322
2e6bce36 323 /*
324 * Read configuration...
325 */
326
327 if (!ReadConfiguration())
328 {
329 syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
330 ConfigurationFile);
331 return (1);
332 }
333
3840d6ba 334 /*
2bffb563 335 * Catch hangup and child signals and ignore broken pipes...
3840d6ba 336 */
337
e5cf24b2 338#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
f3890b95 339 if (RunAsUser)
340 sigset(SIGHUP, sigterm_handler);
341 else
0b50ddbc 342 sigset(SIGHUP, sighup_handler);
f3bc1068 343
e5cf24b2 344 sigset(SIGPIPE, SIG_IGN);
a3e17a89 345 sigset(SIGTERM, sigterm_handler);
e5cf24b2 346#elif defined(HAVE_SIGACTION)
2bffb563 347 memset(&action, 0, sizeof(action));
348
f3890b95 349 sigemptyset(&action.sa_mask);
350 sigaddset(&action.sa_mask, SIGHUP);
351
352 if (RunAsUser)
353 action.sa_handler = sigterm_handler;
354 else
0b50ddbc 355 action.sa_handler = sighup_handler;
f3890b95 356
357 sigaction(SIGHUP, &action, NULL);
2bffb563 358
e5cf24b2 359 sigemptyset(&action.sa_mask);
2bffb563 360 action.sa_handler = SIG_IGN;
361 sigaction(SIGPIPE, &action, NULL);
a3e17a89 362
363 sigemptyset(&action.sa_mask);
364 sigaddset(&action.sa_mask, SIGTERM);
879062a9 365 sigaddset(&action.sa_mask, SIGCHLD);
a3e17a89 366 action.sa_handler = sigterm_handler;
367 sigaction(SIGTERM, &action, NULL);
e5cf24b2 368#else
f3890b95 369 if (RunAsUser)
370 signal(SIGHUP, sigterm_handler);
371 else
0b50ddbc 372 signal(SIGHUP, sighup_handler);
f3890b95 373
e5cf24b2 374 signal(SIGPIPE, SIG_IGN);
a3e17a89 375 signal(SIGTERM, sigterm_handler);
e5cf24b2 376#endif /* HAVE_SIGSET */
3840d6ba 377
cb9d2abc 378#ifdef __sgi
379 /*
380 * Try to create a fake lpsched lock file if one is not already there.
381 * Some Adobe applications need it under IRIX in order to enable
382 * printing...
383 */
384
7b0fde61 385 if ((fp = cupsFileOpen("/var/spool/lp/SCHEDLOCK", "w")) == NULL)
cb9d2abc 386 {
387 syslog(LOG_LPR, "Unable to create fake lpsched lock file "
388 "\"/var/spool/lp/SCHEDLOCK\"\' - %s!",
389 strerror(errno));
390 }
391 else
392 {
7b0fde61 393 fchmod(cupsFileNumber(fp), 0644);
394 fchown(cupsFileNumber(fp), User, Group);
cb9d2abc 395
7b0fde61 396 cupsFileClose(fp);
cb9d2abc 397 }
398#endif /* __sgi */
399
1049abbe 400 /*
c802720f 401 * Initialize authentication certificates...
1049abbe 402 */
403
23bba0c3 404 InitCerts();
405
c802720f 406 /*
5fe35f41 407 * If we are running in the background, signal the parent process that
408 * we are up and running...
c802720f 409 */
410
5fe35f41 411 if (!fg)
c9ac4d3a 412 {
413 /*
414 * Send a signal to the parent process, but only if the parent is
415 * not PID 1 (init). This avoids accidentally shutting down the
416 * system on OpenBSD if you CTRL-C the server before it is up...
417 */
418
419 i = getppid(); /* Save parent PID to avoid race condition */
420
421 if (i != 1)
422 kill(i, SIGUSR1);
423 }
1049abbe 424
50f63f23 425 /*
426 * If the administrator has configured the server to run as an unpriviledged
427 * user, change to that user now...
428 */
429
430 if (RunAsUser)
431 {
432 setgid(Group);
4342fddc 433 setgroups(1, &Group);
50f63f23 434 setuid(User);
435 }
436
25392f52 437 /*
438 * Catch signals...
439 */
440
441 CatchChildSignals();
442
598aeabc 443 /*
444 * Start any pending print jobs...
445 */
446
447 CheckJobs();
448
3840d6ba 449 /*
450 * Loop forever...
451 */
452
b6ea8f29 453#ifdef HAVE_MALLINFO
454 mallinfo_time = 0;
455#endif /* HAVE_MALLINFO */
045a79ad 456 browse_time = time(NULL);
457 senddoc_time = time(NULL);
458 fds = 1;
b6ea8f29 459
52a9a175 460 while (!stop_scheduler)
3840d6ba 461 {
6a2cceab 462#ifdef DEBUG
463 LogMessage(L_DEBUG2, "main: Top of loop, dead_children=%d, NeedReload=%d",
464 dead_children, NeedReload);
465#endif /* DEBUG */
466
2d417cb3 467 /*
468 * Check if there are dead children to handle...
469 */
470
471 if (dead_children)
472 process_children();
473
fd8b1cf8 474 /*
475 * Check if we need to load the server configuration file...
476 */
477
478 if (NeedReload)
479 {
a782f61f 480 /*
481 * Close any idle clients...
482 */
483
fd8b1cf8 484 if (NumClients > 0)
485 {
486 for (i = NumClients, con = Clients; i > 0; i --, con ++)
a74b005d 487 if (con->http.state == HTTP_WAITING)
fd8b1cf8 488 {
489 CloseClient(con);
490 con --;
491 }
492 else
93894a43 493 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
fd8b1cf8 494
d236cb49 495 PauseListening();
fd8b1cf8 496 }
a782f61f 497
498 /*
499 * Check for any active jobs...
500 */
501
502 for (job = Jobs; job; job = job->next)
503 if (job->state->values[0].integer == IPP_JOB_PROCESSING)
504 break;
505
506 /*
507 * Restart if all clients are closed and all jobs finished, or
bcd9e019 508 * if the reload timeout has elapsed...
a782f61f 509 */
510
c8f54662 511 if ((NumClients == 0 && (!job || NeedReload != RELOAD_ALL)) ||
bcd9e019 512 (time(NULL) - ReloadTime) >= ReloadTimeout)
fd8b1cf8 513 {
a782f61f 514 if (!ReadConfiguration())
515 {
516 syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
517 ConfigurationFile);
518 break;
519 }
fd8b1cf8 520 }
521 }
522
3840d6ba 523 /*
524 * Check for available input or ready output. If select() returns
525 * 0 or -1, something bad happened and we should exit immediately.
526 *
42d48bd2 527 * Note that we at least have one listening socket open at all
3840d6ba 528 * times.
529 */
530
f3bc1068 531 memcpy(input, InputSet, SetSize);
532 memcpy(output, OutputSet, SetSize);
3840d6ba 533
045a79ad 534 timeout.tv_sec = select_timeout(fds);
535 timeout.tv_usec = 0;
d9e72533 536
045a79ad 537 if ((fds = select(MaxFDs, input, output, NULL, &timeout)) < 0)
fd8b1cf8 538 {
d236cb49 539 char s[16384], /* String buffer */
540 *sptr; /* Pointer into buffer */
541 int slen; /* Length of string buffer */
542
543
886c59e2 544 /*
545 * Got an error from select!
546 */
547
548 if (errno == EINTR) /* Just interrupted by a signal */
fd8b1cf8 549 continue;
550
886c59e2 551 /*
d236cb49 552 * Log all sorts of debug info to help track down the problem.
886c59e2 553 */
554
b5cb0608 555 LogMessage(L_EMERG, "select() failed - %s!", strerror(errno));
d236cb49 556
557 strcpy(s, "InputSet =");
6cd03542 558 slen = 10;
559 sptr = s + 10;
d236cb49 560
93336dbc 561 for (i = 0; i < MaxFDs; i ++)
f3bc1068 562 if (FD_ISSET(i, InputSet))
6cd03542 563 {
04de52f8 564 snprintf(sptr, sizeof(s) - slen, " %d", i);
6cd03542 565 slen += strlen(sptr);
566 sptr += strlen(sptr);
567 }
bfa1abf0 568
b5cb0608 569 LogMessage(L_EMERG, s);
bfa1abf0 570
d236cb49 571 strcpy(s, "OutputSet =");
6cd03542 572 slen = 11;
573 sptr = s + 11;
d236cb49 574
93336dbc 575 for (i = 0; i < MaxFDs; i ++)
f3bc1068 576 if (FD_ISSET(i, OutputSet))
6cd03542 577 {
04de52f8 578 snprintf(sptr, sizeof(s) - slen, " %d", i);
6cd03542 579 slen += strlen(sptr);
580 sptr += strlen(sptr);
581 }
d236cb49 582
b5cb0608 583 LogMessage(L_EMERG, s);
886c59e2 584
882031b3 585 for (i = 0, con = Clients; i < NumClients; i ++, con ++)
b5cb0608 586 LogMessage(L_EMERG, "Clients[%d] = %d, file = %d, state = %d",
df47c6ae 587 i, con->http.fd, con->file, con->http.state);
886c59e2 588
589 for (i = 0, lis = Listeners; i < NumListeners; i ++, lis ++)
b5cb0608 590 LogMessage(L_EMERG, "Listeners[%d] = %d", i, lis->fd);
886c59e2 591
b5cb0608 592 LogMessage(L_EMERG, "BrowseSocket = %d", BrowseSocket);
886c59e2 593
594 for (job = Jobs; job != NULL; job = job->next)
a2fc3d31 595 LogMessage(L_EMERG, "Jobs[%d] = %d < [%d %d] > [%d %d]",
294f4cee 596 job->id, job->status_buffer ? job->status_buffer->fd : -1,
a2fc3d31 597 job->print_pipes[0], job->print_pipes[1],
598 job->back_pipes[0], job->back_pipes[1]);
bfa1abf0 599
3840d6ba 600 break;
fd8b1cf8 601 }
3840d6ba 602
ab22a3f1 603 /*
604 * Check for status info from job filters...
605 */
606
607 for (job = Jobs; job != NULL; job = next)
608 {
609 next = job->next;
610
611 if (job->status_buffer && FD_ISSET(job->status_buffer->fd, input))
612 {
613 /*
614 * Clear the input bit to avoid updating the next job
615 * using the same status pipe file descriptor...
616 */
617
618 FD_CLR(job->status_buffer->fd, input);
619
620 /*
621 * Read any status messages from the filters...
622 */
623
624 UpdateJob(job);
625 }
626 }
627
628 /*
629 * Update CGI messages as needed...
630 */
631
632 if (CGIPipes[0] >= 0 && FD_ISSET(CGIPipes[0], input))
633 UpdateCGI();
634
635 /*
636 * Update the browse list as needed...
637 */
638
206d3f94 639 if (Browsing && (BrowseLocalProtocols | BrowseRemoteProtocols))
ab22a3f1 640 {
641 if (BrowseSocket >= 0 && FD_ISSET(BrowseSocket, input))
642 UpdateCUPSBrowse();
643
644 if (PollPipe >= 0 && FD_ISSET(PollPipe, input))
645 UpdatePolling();
646
647#ifdef HAVE_LIBSLP
206d3f94 648 if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_SLP) &&
649 BrowseSLPRefresh <= time(NULL))
ab22a3f1 650 UpdateSLPBrowse();
651#endif /* HAVE_LIBSLP */
652
653 if (time(NULL) > browse_time)
654 {
655 SendBrowseList();
656 browse_time = time(NULL);
657 }
658 }
659
660 /*
661 * Check for new connections on the "listen" sockets...
662 */
663
fd8b1cf8 664 for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
f3bc1068 665 if (FD_ISSET(lis->fd, input))
ab652145 666 {
667 FD_CLR(lis->fd, input);
fd8b1cf8 668 AcceptClient(lis);
ab652145 669 }
3840d6ba 670
ab22a3f1 671 /*
672 * Check for new data on the client sockets...
673 */
674
fd8b1cf8 675 for (i = NumClients, con = Clients; i > 0; i --, con ++)
3840d6ba 676 {
3840d6ba 677 /*
678 * Process the input buffer...
679 */
680
f3bc1068 681 if (FD_ISSET(con->http.fd, input) || con->http.used)
ab652145 682 {
683 FD_CLR(con->http.fd, input);
684
fd8b1cf8 685 if (!ReadClient(con))
3840d6ba 686 {
9ce3d829 687 if (con->pipe_pid)
688 FD_CLR(con->file, input);
689
3840d6ba 690 con --;
3840d6ba 691 continue;
692 }
ab652145 693 }
3840d6ba 694
695 /*
696 * Write data as needed...
697 */
698
6a2cceab 699 if (con->pipe_pid && FD_ISSET(con->file, input))
700 {
701 /*
702 * Keep track of pending input from the file/pipe separately
703 * so that we don't needlessly spin on select() when the web
704 * client is not ready to receive data...
705 */
706
ab652145 707 FD_CLR(con->file, input);
6a2cceab 708 con->file_ready = 1;
709
710#ifdef DEBUG
711 LogMessage(L_DEBUG2, "main: Data ready file %d!", con->file);
712#endif /* DEBUG */
713
714 if (!FD_ISSET(con->http.fd, output))
715 {
716 LogMessage(L_DEBUG2, "main: Removing fd %d from InputSet...", con->file);
717 FD_CLR(con->file, InputSet);
718 }
719 }
720
1c9a0923 721 if (FD_ISSET(con->http.fd, output))
722 {
723 FD_CLR(con->http.fd, output);
724
725 if (!con->pipe_pid || con->file_ready)
726 if (!WriteClient(con))
727 {
728 con --;
729 continue;
730 }
731 }
992cf15a 732
733 /*
fd8b1cf8 734 * Check the activity and close old clients...
992cf15a 735 */
736
df47c6ae 737 activity = time(NULL) - Timeout;
738 if (con->http.activity < activity && !con->pipe_pid)
992cf15a 739 {
de218675 740 LogMessage(L_DEBUG, "Closing client %d after %d seconds of inactivity...",
741 con->http.fd, Timeout);
742
fd8b1cf8 743 CloseClient(con);
992cf15a 744 con --;
992cf15a 745 continue;
746 }
3840d6ba 747 }
6abc7437 748
05ca02bc 749 /*
750 * Update any pending multi-file documents...
751 */
752
f3c3b479 753 if ((time(NULL) - senddoc_time) >= 10)
05ca02bc 754 {
755 CheckJobs();
756 senddoc_time = time(NULL);
757 }
758
2bdd1992 759#ifdef HAVE_MALLINFO
d2122fde 760 /*
2bdd1992 761 * Log memory usage every minute...
d2122fde 762 */
763
2bdd1992 764 if ((time(NULL) - mallinfo_time) >= 60 && LogLevel >= L_DEBUG)
d2122fde 765 {
8c57ced1 766 struct mallinfo mem; /* Malloc information */
767
768
8c57ced1 769 mem = mallinfo();
770 LogMessage(L_DEBUG, "mallinfo: arena = %d, used = %d, free = %d\n",
771 mem.arena, mem.usmblks + mem.uordblks,
772 mem.fsmblks + mem.fordblks);
2bdd1992 773 mallinfo_time = time(NULL);
774 }
775#endif /* HAVE_MALLINFO */
8c57ced1 776
2bdd1992 777 /*
778 * Update the root certificate once every 5 minutes...
779 */
780
d9acc9ab 781 if ((time(NULL) - RootCertTime) >= RootCertDuration && RootCertDuration &&
782 !RunUser)
2bdd1992 783 {
8c57ced1 784 /*
785 * Update the root certificate...
786 */
787
d2122fde 788 DeleteCert(0);
789 AddCert(0, "root");
790 }
3840d6ba 791 }
792
793 /*
52a9a175 794 * Log a message based on what happened...
3840d6ba 795 */
796
52a9a175 797 if (stop_scheduler)
798 LogMessage(L_INFO, "Scheduler shutting down normally.");
799 else
800 LogMessage(L_ERROR, "Scheduler shutting down due to program error.");
801
802 /*
803 * Close all network clients and stop all jobs...
804 */
805
806 StopServer();
807
18fe941f 808 StopAllJobs();
fd8b1cf8 809
52a9a175 810#ifdef __sgi
811 /*
812 * Remove the fake IRIX lpsched lock file, but only if the existing
813 * file is not a FIFO which indicates that the real IRIX lpsched is
814 * running...
815 */
816
817 if (!stat("/var/spool/lp/FIFO", &statbuf))
818 if (!S_ISFIFO(statbuf.st_mode))
819 unlink("/var/spool/lp/SCHEDLOCK");
820#endif /* __sgi */
821
cf859e99 822 /*
823 * Free memory used by FD sets and return...
824 */
825
826 free(InputSet);
827 free(OutputSet);
828 free(input);
829 free(output);
830
52a9a175 831 return (!stop_scheduler);
fd8b1cf8 832}
3840d6ba 833
3840d6ba 834
db3c5bfd 835/*
8650fcf2 836 * 'cupsdClosePipe()' - Close a pipe as necessary.
837 */
838
839void
840cupsdClosePipe(int *fds) /* I - Pipe file descriptors (2) */
841{
842 /*
843 * Close file descriptors as needed...
844 */
845
846 if (fds[0] >= 0)
847 {
848 close(fds[0]);
849 fds[0] = -1;
850 }
851
852 if (fds[1] >= 0)
853 {
854 close(fds[1]);
855 fds[1] = -1;
856 }
857}
858
859
860/*
861 * 'cupsdOpenPipe()' - Create a pipe which is closed on exec.
db3c5bfd 862 */
863
864int /* O - 0 on success, -1 on error */
8650fcf2 865cupsdOpenPipe(int *fds) /* O - Pipe file descriptors (2) */
db3c5bfd 866{
867 /*
868 * Create the pipe...
869 */
870
871 if (pipe(fds))
872 return (-1);
873
874 /*
875 * Set the "close on exec" flag on each end of the pipe...
876 */
877
878 if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
879 {
880 close(fds[0]);
881 close(fds[1]);
882 return (-1);
883 }
884
885 if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
886 {
887 close(fds[0]);
888 close(fds[1]);
889 return (-1);
890 }
891
892 /*
893 * Return 0 indicating success...
894 */
895
896 return (0);
897}
898
899
d7845573 900/*
901 * 'CatchChildSignals()' - Catch SIGCHLD signals...
902 */
903
904void
905CatchChildSignals(void)
906{
907#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
908 struct sigaction action; /* Actions for POSIX signals */
909#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
910
911
912#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
913 sigset(SIGCHLD, sigchld_handler);
914#elif defined(HAVE_SIGACTION)
915 memset(&action, 0, sizeof(action));
916
917 sigemptyset(&action.sa_mask);
879062a9 918 sigaddset(&action.sa_mask, SIGTERM);
d7845573 919 sigaddset(&action.sa_mask, SIGCHLD);
920 action.sa_handler = sigchld_handler;
921 sigaction(SIGCHLD, &action, NULL);
922#else
923 signal(SIGCLD, sigchld_handler); /* No, SIGCLD isn't a typo... */
924#endif /* HAVE_SIGSET */
925}
926
927
36992080 928/*
929 * 'ClearString()' - Clear a string.
930 */
931
932void
933ClearString(char **s) /* O - String value */
934{
935 if (s && *s)
936 {
937 free(*s);
938 *s = NULL;
939 }
940}
941
942
38743560 943/*
944 * 'HoldSignals()' - Hold child and termination signals.
945 */
946
947void
948HoldSignals(void)
949{
950#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
951 sigset_t newmask; /* New POSIX signal mask */
952#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
3d72d58a 953
954
955 holdcount ++;
956 if (holdcount > 1)
957 return;
38743560 958
959#ifdef HAVE_SIGSET
960 sighold(SIGTERM);
961 sighold(SIGCHLD);
962#elif defined(HAVE_SIGACTION)
963 sigemptyset(&newmask);
964 sigaddset(&newmask, SIGTERM);
965 sigaddset(&newmask, SIGCHLD);
3d72d58a 966 sigprocmask(SIG_BLOCK, &newmask, &holdmask);
38743560 967#endif /* HAVE_SIGSET */
968}
969
970
d7845573 971/*
972 * 'IgnoreChildSignals()' - Ignore SIGCHLD signals...
5a0aab69 973 *
974 * We don't really ignore them, we set the signal handler to SIG_DFL,
975 * since some OS's rely on signals for the wait4() function to work.
d7845573 976 */
977
978void
979IgnoreChildSignals(void)
980{
981#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
982 struct sigaction action; /* Actions for POSIX signals */
983#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
984
38743560 985
d7845573 986#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
5a0aab69 987 sigset(SIGCHLD, SIG_DFL);
d7845573 988#elif defined(HAVE_SIGACTION)
989 memset(&action, 0, sizeof(action));
990
991 sigemptyset(&action.sa_mask);
992 sigaddset(&action.sa_mask, SIGCHLD);
5a0aab69 993 action.sa_handler = SIG_DFL;
d7845573 994 sigaction(SIGCHLD, &action, NULL);
995#else
5a0aab69 996 signal(SIGCLD, SIG_DFL); /* No, SIGCLD isn't a typo... */
d7845573 997#endif /* HAVE_SIGSET */
998}
999
1000
38743560 1001/*
1002 * 'ReleaseSignals()' - Release signals for delivery.
1003 */
1004
1005void
1006ReleaseSignals(void)
1007{
3d72d58a 1008 holdcount --;
1009 if (holdcount > 0)
1010 return;
1011
38743560 1012#ifdef HAVE_SIGSET
1013 sigrelse(SIGTERM);
1014 sigrelse(SIGCHLD);
1015#elif defined(HAVE_SIGACTION)
3d72d58a 1016 sigprocmask(SIG_SETMASK, &holdmask, NULL);
38743560 1017#endif /* HAVE_SIGSET */
1018}
1019
1020
36992080 1021/*
1022 * 'SetString()' - Set a string value.
1023 */
1024
1025void
1026SetString(char **s, /* O - New string */
1027 const char *v) /* I - String value */
1028{
471f1564 1029 if (!s || *s == v)
36992080 1030 return;
1031
1032 if (*s)
1033 free(*s);
1034
1035 if (v)
1036 *s = strdup(v);
1037 else
1038 *s = NULL;
1039}
1040
1041
1042/*
1043 * 'SetStringf()' - Set a formatted string value.
1044 */
1045
1046void
1047SetStringf(char **s, /* O - New string */
1048 const char *f, /* I - Printf-style format string */
1049 ...) /* I - Additional args as needed */
1050{
291355eb 1051 char v[4096]; /* Formatting string value */
36992080 1052 va_list ap; /* Argument pointer */
471f1564 1053 char *olds; /* Old string */
36992080 1054
1055
1056 if (!s)
1057 return;
1058
471f1564 1059 olds = *s;
36992080 1060
1061 if (f)
1062 {
1063 va_start(ap, f);
1064 vsnprintf(v, sizeof(v), f, ap);
1065 va_end(ap);
1066
1067 *s = strdup(v);
1068 }
1069 else
1070 *s = NULL;
471f1564 1071
1072 if (olds)
1073 free(olds);
36992080 1074}
1075
1076
af57319f 1077/*
1078 * 'parent_handler()' - Catch USR1/CHLD signals...
1079 */
1080
1081static void
1082parent_handler(int sig) /* I - Signal */
1083{
1084 /*
1085 * Store the signal we got from the OS and return...
1086 */
1087
1088 parent_signal = sig;
1089}
1090
1091
6abc7437 1092/*
2d417cb3 1093 * 'process_children()' - Process all dead children...
6abc7437 1094 */
1095
1096static void
2d417cb3 1097process_children(void)
6abc7437 1098{
e5cf24b2 1099 int status; /* Exit status of child */
1100 int pid; /* Process ID of child */
1101 job_t *job; /* Current job */
1102 int i; /* Looping var */
6abc7437 1103
1104
25392f52 1105 LogMessage(L_DEBUG2, "process_children()");
1106
daf8f5f0 1107 /*
2d417cb3 1108 * Reset the dead_children flag...
daf8f5f0 1109 */
1110
2d417cb3 1111 dead_children = 0;
daf8f5f0 1112
bdbaa675 1113 /*
2d417cb3 1114 * Collect the exit status of some children...
bdbaa675 1115 */
1116
e5cf24b2 1117#ifdef HAVE_WAITPID
1118 while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
1119#elif defined(HAVE_WAIT3)
1120 while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
1121#else
1122 if ((pid = wait(&status)) > 0)
1123#endif /* HAVE_WAITPID */
1124 {
25392f52 1125 LogMessage(L_DEBUG2, "process_children: pid = %d, status = %d\n", pid, status);
6abc7437 1126
d2122fde 1127 /*
1128 * Ignore SIGTERM errors - that comes when a job is cancelled...
1129 */
1130
6bc08d08 1131 if (status == SIGTERM)
1132 status = 0;
1133
1134 if (status)
1135 {
25d21a1e 1136 if (WIFEXITED(status))
6d8b6788 1137 LogMessage(L_ERROR, "PID %d stopped with status %d!", pid,
a2fc3d31 1138 WEXITSTATUS(status));
25d21a1e 1139 else
1140 LogMessage(L_ERROR, "PID %d crashed on signal %d!", pid,
1141 WTERMSIG(status));
deb855a2 1142
1143 if (LogLevel < L_DEBUG)
1144 LogMessage(L_INFO, "Hint: Try setting the LogLevel to \"debug\" to find out more.");
6bc08d08 1145 }
434ddc80 1146 else
1147 LogMessage(L_DEBUG2, "PID %d exited with no errors.", pid);
1148
1149 /*
1150 * Delete certificates for CGI processes...
1151 */
1152
1153 if (pid)
1154 DeleteCert(pid);
1155
1156 /*
1157 * Lookup the PID in the jobs list...
1158 */
e5b7bd9c 1159
e5cf24b2 1160 for (job = Jobs; job != NULL; job = job->next)
879062a9 1161 if (job->state != NULL &&
1162 job->state->values[0].integer == IPP_JOB_PROCESSING)
6abc7437 1163 {
a2fc3d31 1164 for (i = 0; job->filters[i]; i ++)
1165 if (job->filters[i] == pid)
e5cf24b2 1166 break;
6abc7437 1167
a2fc3d31 1168 if (job->filters[i] || job->backend == pid)
bfa1abf0 1169 {
1170 /*
e5cf24b2 1171 * OK, this process has gone away; what's left?
bfa1abf0 1172 */
6abc7437 1173
a2fc3d31 1174 if (job->filters[i])
1175 job->filters[i] = -pid;
1176 else
1177 job->backend = -pid;
6abc7437 1178
bd84e0d1 1179 if (status && job->status >= 0)
e5cf24b2 1180 {
1181 /*
bd84e0d1 1182 * An error occurred; save the exit status so we know to stop
1183 * the printer or cancel the job when all of the filters finish...
1184 *
1185 * A negative status indicates that the backend failed and the
1186 * printer needs to be stopped.
e5cf24b2 1187 */
1188
a2fc3d31 1189 if (job->filters[i])
bd84e0d1 1190 job->status = status; /* Filter failed */
a2fc3d31 1191 else
1192 job->status = -status; /* Backend failed */
e5cf24b2 1193 }
017c50f3 1194
1195 /*
1196 * If this is not the last file in a job, see if all of the
1197 * filters are done, and if so move to the next file.
1198 */
1199
1200 if (job->current_file < job->num_files)
1201 {
1202 for (i = 0; job->filters[i] < 0; i ++);
1203
1204 if (!job->filters[i])
1205 {
1206 /*
1207 * Process the next file...
1208 */
1209
1210 FinishJob(job);
1211 }
1212 }
e5cf24b2 1213 break;
1214 }
6abc7437 1215 }
e5cf24b2 1216 }
2d417cb3 1217}
1218
1219
1220/*
1221 * 'sigchld_handler()' - Handle 'child' signals from old processes.
1222 */
1223
1224static void
1225sigchld_handler(int sig) /* I - Signal number */
1226{
1227 (void)sig;
e5cf24b2 1228
bdbaa675 1229 /*
2d417cb3 1230 * Flag that we have dead children...
bdbaa675 1231 */
1232
2d417cb3 1233 dead_children = 1;
1234
1235 /*
1236 * Reset the signal handler as needed...
1237 */
bdbaa675 1238
52a9a175 1239#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
e5cf24b2 1240 signal(SIGCLD, sigchld_handler);
52a9a175 1241#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
6abc7437 1242}
1243
1244
fd8b1cf8 1245/*
1246 * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler.
1247 */
1248
1249static void
1250sighup_handler(int sig) /* I - Signal number */
1251{
93894a43 1252 (void)sig;
1253
53510eae 1254 NeedReload = RELOAD_ALL;
a782f61f 1255 ReloadTime = time(NULL);
a3e17a89 1256
52a9a175 1257#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
a3e17a89 1258 signal(SIGHUP, sighup_handler);
52a9a175 1259#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
a3e17a89 1260}
1261
1262
1263/*
1264 * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler.
1265 */
1266
1267static void
5fe35f41 1268sigterm_handler(int sig) /* I - Signal */
a3e17a89 1269{
d74efa25 1270 (void)sig; /* remove compiler warnings... */
1271
daf8f5f0 1272 /*
52a9a175 1273 * Flag that we should stop and return...
a3e17a89 1274 */
1275
52a9a175 1276 stop_scheduler = 1;
3840d6ba 1277}
1278
fd8b1cf8 1279
045a79ad 1280/*
1281 * 'select_timeout()' - Calculate the select timeout value.
1282 *
1283 */
1284
1285static long /* O - Number of seconds */
1286select_timeout(int fds) /* I - Number of ready descriptors select returned */
1287{
1288 int i; /* Looping var */
1289 long timeout; /* Timeout for select */
1290 time_t now; /* Current time */
1291 client_t *con; /* Client information */
1292 printer_t *p; /* Printer information */
1293 job_t *job; /* Job information */
1294 const char *why; /* Debugging aid */
1295
1296
1297 /*
1298 * Check to see if any of the clients have pending data to be
1299 * processed; if so, the timeout should be 0...
1300 */
1301
1302 for (i = NumClients, con = Clients; i > 0; i --, con ++)
1303 if (con->http.used > 0)
1304 return (0);
1305
1306 /*
1307 * If select has been active in the last second (fds != 0) or we have
1308 * many resources in use then don't bother trying to optimize the
1309 * timeout, just make it 1 second.
1310 */
1311
1312 if (fds || NumClients > 50)
1313 return (1);
1314
1315 /*
1316 * Otherwise, check all of the possible events that we need to wake for...
1317 */
1318
1319 now = time(NULL);
bcd9e019 1320 timeout = now + 86400; /* 86400 == 1 day */
045a79ad 1321 why = "do nothing";
1322
1323 /*
1324 * Check the activity and close old clients...
1325 */
1326
1327 for (i = NumClients, con = Clients; i > 0; i --, con ++)
1328 if ((con->http.activity + Timeout) < timeout)
1329 {
1330 timeout = con->http.activity + Timeout;
1331 why = "timeout a client connection";
1332 }
1333
1334 /*
1335 * Update the browse list as needed...
1336 */
1337
206d3f94 1338 if (Browsing && BrowseLocalProtocols)
045a79ad 1339 {
1340#ifdef HAVE_LIBSLP
206d3f94 1341 if ((BrowseLocalProtocols & BROWSE_SLP) && (BrowseSLPRefresh < timeout))
045a79ad 1342 {
1343 timeout = BrowseSLPRefresh;
1344 why = "update SLP browsing";
1345 }
1346#endif /* HAVE_LIBSLP */
1347
206d3f94 1348 if (BrowseLocalProtocols & BROWSE_CUPS)
045a79ad 1349 {
1350 for (p = Printers; p != NULL; p = p->next)
1351 {
1352 if (p->type & CUPS_PRINTER_REMOTE)
1353 {
9214a473 1354 if ((p->browse_time + BrowseTimeout) < timeout)
045a79ad 1355 {
1356 timeout = p->browse_time + BrowseTimeout;
1357 why = "browse timeout a printer";
1358 }
1359 }
1360 else if (!(p->type & CUPS_PRINTER_IMPLICIT))
1361 {
9214a473 1362 if (BrowseInterval && (p->browse_time + BrowseInterval) < timeout)
045a79ad 1363 {
1364 timeout = p->browse_time + BrowseInterval;
1365 why = "send browse update";
1366 }
1367 }
1368 }
1369 }
1370 }
1371
1372 /*
1373 * Check for any active jobs...
1374 */
1375
1376 if (timeout > (now + 10))
1377 {
1378 for (job = Jobs; job != NULL; job = job->next)
1379 if (job->state->values[0].integer <= IPP_JOB_PROCESSING)
1380 {
1381 timeout = now + 10;
1382 why = "process active jobs";
1383 break;
1384 }
1385 }
1386
1387#ifdef HAVE_MALLINFO
1388 /*
1389 * Log memory usage every minute...
1390 */
1391
1392 if (LogLevel >= L_DEBUG && (mallinfo_time + 60) < timeout)
1393 {
1394 timeout = mallinfo_time + 60;
1395 why = "display memory usage";
1396 }
1397#endif /* HAVE_MALLINFO */
1398
1399 /*
1400 * Update the root certificate when needed...
1401 */
1402
d9acc9ab 1403 if (!RunUser && RootCertDuration &&
1404 (RootCertTime + RootCertDuration) < timeout)
045a79ad 1405 {
1406 timeout = RootCertTime + RootCertDuration;
1407 why = "update root certificate";
1408 }
1409
1410 /*
bcd9e019 1411 * Adjust from absolute to relative time. If p->browse_time above
1412 * was 0 then we can end up with a negative value here, so check.
1413 * We add 1 second to the timeout since events occur after the
9214a473 1414 * timeout expires, and limit the timeout to 86400 seconds (1 day)
1415 * to avoid select() timeout limits present on some operating
1416 * systems...
045a79ad 1417 */
1418
bcd9e019 1419 timeout = timeout - now + 1;
045a79ad 1420
bcd9e019 1421 if (timeout < 1)
1422 timeout = 1;
9214a473 1423 else if (timeout > 86400)
1424 timeout = 86400;
045a79ad 1425
1426 /*
1427 * Log and return the timeout value...
1428 */
1429
1430 LogMessage(L_DEBUG2, "select_timeout: %ld seconds to %s", timeout, why);
1431
1432 return (timeout);
1433}
1434
1435
fd8b1cf8 1436/*
1437 * 'usage()' - Show scheduler usage.
1438 */
1439
1440static void
1441usage(void)
1442{
53510eae 1443 fputs("Usage: cupsd [-c config-file] [-f] [-F]\n", stderr);
fd8b1cf8 1444 exit(1);
1445}
1446
1447
3840d6ba 1448/*
b2e10895 1449 * End of "$Id$".
3840d6ba 1450 */