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