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