]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/main.c
2 * "$Id: main.c,v 1.57.2.42 2003/04/10 20:15:54 mike Exp $"
4 * Scheduler main loop for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
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
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * main() - Main entry for the CUPS scheduler.
27 * CatchChildSignals() - Catch SIGCHLD signals...
28 * HoldSignals() - Hold child and termination signals.
29 * IgnoreChildSignals() - Ignore SIGCHLD signals...
30 * ReleaseSignals() - Release signals for delivery.
31 * SetString() - Set a string value.
32 * SetStringf() - Set a formatted string value.
33 * sigchld_handler() - Handle 'child' signals from old processes.
34 * sighup_handler() - Handle 'hangup' signals to reconfigure the scheduler.
35 * sigterm_handler() - Handle 'terminate' signals that stop the scheduler.
36 * usage() - Show scheduler usage.
40 * Include necessary headers...
45 #include <sys/resource.h>
49 #if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
51 #endif /* HAVE_MALLOC_H && HAVE_MALLINFO */
58 static void sigchld_handler(int sig
);
59 static void sighup_handler(int sig
);
60 static void sigterm_handler(int sig
);
61 static void sigusr1_handler(int sig
);
62 static void usage(void);
69 static int holdcount
= 0; /* Number of time "hold" was called */
70 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
71 static sigset_t holdmask
; /* Old POSIX signal mask */
72 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
76 * 'main()' - Main entry for the CUPS scheduler.
79 int /* O - Exit status */
80 main(int argc
, /* I - Number of command-line arguments */
81 char *argv
[]) /* I - Command-line arguments */
83 int i
; /* Looping var */
84 char *opt
; /* Option character */
85 int fg
; /* Run in the foreground */
86 fd_set
*input
, /* Input set for select() */
87 *output
; /* Output set for select() */
88 client_t
*con
; /* Current client */
89 job_t
*job
, /* Current job */
91 listener_t
*lis
; /* Current listener */
92 time_t activity
; /* Activity timer */
93 time_t senddoc_time
; /* Send-Document time */
95 time_t mallinfo_time
; /* Malloc information time */
96 #endif /* HAVE_MALLINFO */
97 struct timeval timeout
; /* select() timeout */
98 struct rlimit limit
; /* Runtime limit */
99 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
100 struct sigaction action
; /* Actions for POSIX signals */
101 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
103 cups_file_t
*fp
; /* Fake lpsched lock file */
108 * Check for command-line arguments...
113 for (i
= 1; i
< argc
; i
++)
114 if (argv
[i
][0] == '-')
115 for (opt
= argv
[i
] + 1; *opt
!= '\0'; opt
++)
118 case 'c' : /* Configuration file */
123 if (argv
[i
][0] == '/')
126 * Absolute directory...
129 SetString(&ConfigurationFile
, argv
[i
]);
134 * Relative directory...
137 char current
[1024]; /* Current directory */
140 getcwd(current
, sizeof(current
));
141 SetStringf(&ConfigurationFile
, "%s/%s", current
, argv
[i
]);
145 case 'f' : /* Run in foreground... */
149 case 'F' : /* Run in foreground, but still disconnect from terminal... */
153 default : /* Unknown option */
154 fprintf(stderr
, "cupsd: Unknown option \'%c\' - aborting!\n", *opt
);
160 fprintf(stderr
, "cupsd: Unknown argument \'%s\' - aborting!\n", argv
[i
]);
164 if (!ConfigurationFile
)
165 SetString(&ConfigurationFile
, CUPS_SERVERROOT
"/cupsd.conf");
168 * If the user hasn't specified "-f", run in the background...
176 * OK, wait for the child to startup and send us SIGUSR1... We
177 * also need to ignore SIGHUP which might be sent by the init
178 * script to restart the scheduler...
181 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
182 sigset(SIGUSR1
, sigusr1_handler
);
184 sigset(SIGHUP
, SIG_IGN
);
185 #elif defined(HAVE_SIGACTION)
186 memset(&action
, 0, sizeof(action
));
187 sigemptyset(&action
.sa_mask
);
188 sigaddset(&action
.sa_mask
, SIGUSR1
);
189 action
.sa_handler
= sigusr1_handler
;
190 sigaction(SIGUSR1
, &action
, NULL
);
192 sigemptyset(&action
.sa_mask
);
193 action
.sa_handler
= SIG_IGN
;
194 sigaction(SIGHUP
, &action
, NULL
);
196 signal(SIGUSR1
, sigusr1_handler
);
198 signal(SIGHUP
, SIG_IGN
);
199 #endif /* HAVE_SIGSET */
208 fprintf(stderr
, "cupsd: Child exited with status %d!\n", i
/ 256);
210 fprintf(stderr
, "cupsd: Child exited on signal %d!\n", i
);
219 * Make sure we aren't tying up any filesystems...
226 * Disable core dumps...
229 getrlimit(RLIMIT_CORE
, &limit
);
231 setrlimit(RLIMIT_CORE
, &limit
);
234 * Disconnect from the controlling terminal...
246 * Set the timezone info...
249 if (getenv("TZ") != NULL
)
250 SetStringf(&TZ
, "TZ=%s", getenv("TZ"));
257 setlocale(LC_TIME
, "");
261 * Set the maximum number of files...
264 getrlimit(RLIMIT_NOFILE
, &limit
);
266 if (limit
.rlim_max
> CUPS_MAX_FDS
)
267 MaxFDs
= CUPS_MAX_FDS
;
269 MaxFDs
= limit
.rlim_max
;
271 limit
.rlim_cur
= MaxFDs
;
273 setrlimit(RLIMIT_NOFILE
, &limit
);
276 * Allocate memory for the input and output sets...
279 SetSize
= (MaxFDs
+ 7) / 8;
280 InputSet
= (fd_set
*)calloc(1, SetSize
);
281 OutputSet
= (fd_set
*)calloc(1, SetSize
);
282 input
= (fd_set
*)calloc(1, SetSize
);
283 output
= (fd_set
*)calloc(1, SetSize
);
285 if (InputSet
== NULL
|| OutputSet
== NULL
|| input
== NULL
|| output
== NULL
)
287 syslog(LOG_LPR
, "Unable to allocate memory for select() sets - exiting!");
292 * Catch hangup and child signals and ignore broken pipes...
295 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
297 sigset(SIGHUP
, sigterm_handler
);
299 sigset(SIGHUP
, sighup_handler
);
301 sigset(SIGPIPE
, SIG_IGN
);
302 sigset(SIGTERM
, sigterm_handler
);
303 #elif defined(HAVE_SIGACTION)
304 memset(&action
, 0, sizeof(action
));
306 sigemptyset(&action
.sa_mask
);
307 sigaddset(&action
.sa_mask
, SIGHUP
);
310 action
.sa_handler
= sigterm_handler
;
312 action
.sa_handler
= sighup_handler
;
314 sigaction(SIGHUP
, &action
, NULL
);
316 sigemptyset(&action
.sa_mask
);
317 action
.sa_handler
= SIG_IGN
;
318 sigaction(SIGPIPE
, &action
, NULL
);
320 sigemptyset(&action
.sa_mask
);
321 sigaddset(&action
.sa_mask
, SIGTERM
);
322 sigaddset(&action
.sa_mask
, SIGCHLD
);
323 action
.sa_handler
= sigterm_handler
;
324 sigaction(SIGTERM
, &action
, NULL
);
327 signal(SIGHUP
, sigterm_handler
);
329 signal(SIGHUP
, sighup_handler
);
331 signal(SIGPIPE
, SIG_IGN
);
332 signal(SIGTERM
, sigterm_handler
);
333 #endif /* HAVE_SIGSET */
336 * Read configuration...
339 if (!ReadConfiguration())
341 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
348 * Try to create a fake lpsched lock file if one is not already there.
349 * Some Adobe applications need it under IRIX in order to enable
353 if ((fp
= cupsFileOpen("/var/spool/lp/SCHEDLOCK", "w")) == NULL
)
355 syslog(LOG_LPR
, "Unable to create fake lpsched lock file "
356 "\"/var/spool/lp/SCHEDLOCK\"\' - %s!",
361 fchmod(cupsFileNumber(fp
), 0644);
362 fchown(cupsFileNumber(fp
), User
, Group
);
369 * Initialize authentication certificates...
375 * If we are running in the background, signal the parent process that
376 * we are up and running...
380 kill(getppid(), SIGUSR1
);
383 * If the administrator has configured the server to run as an unpriviledged
384 * user, change to that user now...
395 * Start any pending print jobs...
404 senddoc_time
= time(NULL
);
408 #endif /* HAVE_MALLINFO */
413 * Check if we need to load the server configuration file...
420 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
421 if (con
->http
.state
== HTTP_WAITING
)
427 con
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
431 else if (!ReadConfiguration())
433 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
440 * Check for available input or ready output. If select() returns
441 * 0 or -1, something bad happened and we should exit immediately.
443 * Note that we at least have one listening socket open at all
447 memcpy(input
, InputSet
, SetSize
);
448 memcpy(output
, OutputSet
, SetSize
);
450 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
451 if (con
->http
.used
> 0)
462 * If we have no pending data from a client, see when we really
470 if ((i
= select(MaxFDs
, input
, output
, NULL
, &timeout
)) < 0)
472 char s
[16384], /* String buffer */
473 *sptr
; /* Pointer into buffer */
474 int slen
; /* Length of string buffer */
478 * Got an error from select!
481 if (errno
== EINTR
) /* Just interrupted by a signal */
485 * Log all sorts of debug info to help track down the problem.
488 LogMessage(L_EMERG
, "select() failed - %s!", strerror(errno
));
490 strcpy(s
, "InputSet =");
494 for (i
= 0; i
< MaxFDs
; i
++)
495 if (FD_ISSET(i
, InputSet
))
497 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
498 slen
+= strlen(sptr
);
499 sptr
+= strlen(sptr
);
502 LogMessage(L_EMERG
, s
);
504 strcpy(s
, "OutputSet =");
508 for (i
= 0; i
< MaxFDs
; i
++)
509 if (FD_ISSET(i
, OutputSet
))
511 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
512 slen
+= strlen(sptr
);
513 sptr
+= strlen(sptr
);
516 LogMessage(L_EMERG
, s
);
518 for (i
= 0, con
= Clients
; i
< NumClients
; i
++, con
++)
519 LogMessage(L_EMERG
, "Clients[%d] = %d, file = %d, state = %d",
520 i
, con
->http
.fd
, con
->file
, con
->http
.state
);
522 for (i
= 0, lis
= Listeners
; i
< NumListeners
; i
++, lis
++)
523 LogMessage(L_EMERG
, "Listeners[%d] = %d", i
, lis
->fd
);
525 LogMessage(L_EMERG
, "BrowseSocket = %d", BrowseSocket
);
527 for (job
= Jobs
; job
!= NULL
; job
= job
->next
)
528 LogMessage(L_EMERG
, "Jobs[%d] = %d < [%d %d] > [%d %d]",
529 job
->id
, job
->status_pipe
,
530 job
->print_pipes
[0], job
->print_pipes
[1],
531 job
->back_pipes
[0], job
->back_pipes
[1]);
536 for (i
= NumListeners
, lis
= Listeners
; i
> 0; i
--, lis
++)
537 if (FD_ISSET(lis
->fd
, input
))
540 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
543 * Process the input buffer...
546 if (FD_ISSET(con
->http
.fd
, input
) || con
->http
.used
)
547 if (!ReadClient(con
))
554 * Write data as needed...
557 if (FD_ISSET(con
->http
.fd
, output
) &&
558 (!con
->pipe_pid
|| FD_ISSET(con
->file
, input
)))
559 if (!WriteClient(con
))
566 * Check the activity and close old clients...
569 activity
= time(NULL
) - Timeout
;
570 if (con
->http
.activity
< activity
&& !con
->pipe_pid
)
572 LogMessage(L_DEBUG
, "Closing client %d after %d seconds of inactivity...",
573 con
->http
.fd
, Timeout
);
582 * Check for status info from job filters...
585 for (job
= Jobs
; job
!= NULL
; job
= next
)
589 if (job
->status_pipe
>= 0 && FD_ISSET(job
->status_pipe
, input
))
592 * Clear the input bit to avoid updating the next job
593 * using the same status pipe file descriptor...
596 FD_CLR(job
->status_pipe
, input
);
599 * Read any status messages from the filters...
607 * Update CGI messages as needed...
610 if (CGIPipes
[0] >= 0 && FD_ISSET(CGIPipes
[0], input
))
614 * Update the browse list as needed...
617 if (Browsing
&& BrowseProtocols
)
619 if (BrowseSocket
>= 0 && FD_ISSET(BrowseSocket
, input
))
622 if (PollPipe
>= 0 && FD_ISSET(PollPipe
, input
))
626 if ((BrowseProtocols
& BROWSE_SLP
) && BrowseSLPRefresh
<= time(NULL
))
628 #endif /* HAVE_LIBSLP */
634 * Update any pending multi-file documents...
637 if ((time(NULL
) - senddoc_time
) >= 10)
640 senddoc_time
= time(NULL
);
645 * Log memory usage every minute...
648 if ((time(NULL
) - mallinfo_time
) >= 60 && LogLevel
>= L_DEBUG
)
650 struct mallinfo mem
; /* Malloc information */
654 LogMessage(L_DEBUG
, "mallinfo: arena = %d, used = %d, free = %d\n",
655 mem
.arena
, mem
.usmblks
+ mem
.uordblks
,
656 mem
.fsmblks
+ mem
.fordblks
);
657 mallinfo_time
= time(NULL
);
659 #endif /* HAVE_MALLINFO */
662 * Update the root certificate once every 5 minutes...
665 if ((time(NULL
) - RootCertTime
) >= RootCertDuration
&& RootCertDuration
)
668 * Update the root certificate...
677 * If we get here something very bad happened and we need to exit
692 * 'CatchChildSignals()' - Catch SIGCHLD signals...
696 CatchChildSignals(void)
698 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
699 struct sigaction action
; /* Actions for POSIX signals */
700 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
703 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
704 sigset(SIGCHLD
, sigchld_handler
);
705 #elif defined(HAVE_SIGACTION)
706 memset(&action
, 0, sizeof(action
));
708 sigemptyset(&action
.sa_mask
);
709 sigaddset(&action
.sa_mask
, SIGTERM
);
710 sigaddset(&action
.sa_mask
, SIGCHLD
);
711 action
.sa_handler
= sigchld_handler
;
712 sigaction(SIGCHLD
, &action
, NULL
);
714 signal(SIGCLD
, sigchld_handler
); /* No, SIGCLD isn't a typo... */
715 #endif /* HAVE_SIGSET */
720 * 'ClearString()' - Clear a string.
724 ClearString(char **s
) /* O - String value */
735 * 'HoldSignals()' - Hold child and termination signals.
741 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
742 sigset_t newmask
; /* New POSIX signal mask */
743 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
753 #elif defined(HAVE_SIGACTION)
754 sigemptyset(&newmask
);
755 sigaddset(&newmask
, SIGTERM
);
756 sigaddset(&newmask
, SIGCHLD
);
757 sigprocmask(SIG_BLOCK
, &newmask
, &holdmask
);
758 #endif /* HAVE_SIGSET */
763 * 'IgnoreChildSignals()' - Ignore SIGCHLD signals...
765 * We don't really ignore them, we set the signal handler to SIG_DFL,
766 * since some OS's rely on signals for the wait4() function to work.
770 IgnoreChildSignals(void)
772 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
773 struct sigaction action
; /* Actions for POSIX signals */
774 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
777 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
778 sigset(SIGCHLD
, SIG_DFL
);
779 #elif defined(HAVE_SIGACTION)
780 memset(&action
, 0, sizeof(action
));
782 sigemptyset(&action
.sa_mask
);
783 sigaddset(&action
.sa_mask
, SIGCHLD
);
784 action
.sa_handler
= SIG_DFL
;
785 sigaction(SIGCHLD
, &action
, NULL
);
787 signal(SIGCLD
, SIG_DFL
); /* No, SIGCLD isn't a typo... */
788 #endif /* HAVE_SIGSET */
793 * 'ReleaseSignals()' - Release signals for delivery.
806 #elif defined(HAVE_SIGACTION)
807 sigprocmask(SIG_SETMASK
, &holdmask
, NULL
);
808 #endif /* HAVE_SIGSET */
813 * 'SetString()' - Set a string value.
817 SetString(char **s
, /* O - New string */
818 const char *v
) /* I - String value */
834 * 'SetStringf()' - Set a formatted string value.
838 SetStringf(char **s
, /* O - New string */
839 const char *f
, /* I - Printf-style format string */
840 ...) /* I - Additional args as needed */
842 char v
[1024]; /* Formatting string value */
843 va_list ap
; /* Argument pointer */
844 char *olds
; /* Old string */
855 vsnprintf(v
, sizeof(v
), f
, ap
);
869 * 'sigchld_handler()' - Handle 'child' signals from old processes.
873 sigchld_handler(int sig
) /* I - Signal number */
875 int olderrno
; /* Old errno value */
876 int status
; /* Exit status of child */
877 int pid
; /* Process ID of child */
878 job_t
*job
; /* Current job */
879 int i
; /* Looping var */
885 * Save the original error value (wait might overwrite it...)
891 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0)
892 #elif defined(HAVE_WAIT3)
893 while ((pid
= wait3(&status
, WNOHANG
, NULL
)) > 0)
895 if ((pid
= wait(&status
)) > 0)
896 #endif /* HAVE_WAITPID */
898 DEBUG_printf(("sigchld_handler: pid = %d, status = %d\n", pid
, status
));
901 * Ignore SIGTERM errors - that comes when a job is cancelled...
904 if (status
== SIGTERM
)
909 if (WIFEXITED(status
))
910 LogMessage(L_ERROR
, "PID %d stopped with status %d!", pid
,
911 WEXITSTATUS(status
));
913 LogMessage(L_ERROR
, "PID %d crashed on signal %d!", pid
,
916 if (LogLevel
< L_DEBUG
)
917 LogMessage(L_INFO
, "Hint: Try setting the LogLevel to \"debug\" to find out more.");
920 LogMessage(L_DEBUG2
, "PID %d exited with no errors.", pid
);
923 * Delete certificates for CGI processes...
930 * Lookup the PID in the jobs list...
933 for (job
= Jobs
; job
!= NULL
; job
= job
->next
)
934 if (job
->state
!= NULL
&&
935 job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
937 for (i
= 0; job
->filters
[i
]; i
++)
938 if (job
->filters
[i
] == pid
)
941 if (job
->filters
[i
] || job
->backend
== pid
)
944 * OK, this process has gone away; what's left?
948 job
->filters
[i
] = -pid
;
952 if (status
&& job
->status
>= 0)
955 * An error occurred; save the exit status so we know to stop
956 * the printer or cancel the job when all of the filters finish...
958 * A negative status indicates that the backend failed and the
959 * printer needs to be stopped.
963 job
->status
= status
; /* Filter failed */
965 job
->status
= -status
; /* Backend failed */
973 * Restore the original error value...
979 sigset(SIGCHLD
, sigchld_handler
);
980 #elif !defined(HAVE_SIGACTION)
981 signal(SIGCLD
, sigchld_handler
);
982 #endif /* HAVE_SIGSET */
987 * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler.
991 sighup_handler(int sig
) /* I - Signal number */
995 NeedReload
= RELOAD_ALL
;
998 sigset(SIGHUP
, sighup_handler
);
999 #elif !defined(HAVE_SIGACTION)
1000 signal(SIGHUP
, sighup_handler
);
1001 #endif /* HAVE_SIGSET */
1006 * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler.
1010 sigterm_handler(int sig
) /* I - Signal */
1013 struct stat statbuf
; /* Needed for checking lpsched FIFO */
1017 (void)sig
; /* remove compiler warnings... */
1023 LogMessage(L_ERROR
, "Scheduler shutting down due to SIGTERM.");
1026 * Close all network clients and stop all jobs...
1035 * Remove the fake IRIX lpsched lock file, but only if the existing
1036 * file is not a FIFO which indicates that the real IRIX lpsched is
1040 if (!stat("/var/spool/lp/FIFO", &statbuf
))
1041 if (!S_ISFIFO(statbuf
.st_mode
))
1042 unlink("/var/spool/lp/SCHEDLOCK");
1050 * 'sigusr1_handler()' - Catch USR1 signals...
1054 sigusr1_handler(int sig
) /* I - Signal */
1056 (void)sig
; /* remove compiler warnings... */
1061 * 'usage()' - Show scheduler usage.
1067 fputs("Usage: cupsd [-c config-file] [-f] [-F]\n", stderr
);
1073 * End of "$Id: main.c,v 1.57.2.42 2003/04/10 20:15:54 mike Exp $".