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