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