]>
git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/main.c
2 * "$Id: main.c,v 1.57.2.19 2002/08/21 02:06:27 mike Exp $"
4 * Scheduler main loop for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2002 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 * IgnoreChildSignals() - Ignore SIGCHLD signals...
29 * sigchld_handler() - Handle 'child' signals from old processes.
30 * sighup_handler() - Handle 'hangup' signals to reconfigure the scheduler.
31 * sigterm_handler() - Handle 'terminate' signals that stop the scheduler.
32 * usage() - Show scheduler usage.
36 * Include necessary headers...
41 #include <sys/resource.h>
44 #if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
46 #endif /* HAVE_MALLOC_H && HAVE_MALLINFO */
49 # define FD_SETSIZE 1024
50 #endif /* !FD_SETSIZE */
57 static void sigchld_handler(int sig
);
58 static void sighup_handler(int sig
);
59 static void sigterm_handler(int sig
);
60 static void sigusr1_handler(int sig
);
61 static void usage(void);
65 * 'main()' - Main entry for the CUPS scheduler.
68 int /* O - Exit status */
69 main(int argc
, /* I - Number of command-line arguments */
70 char *argv
[]) /* I - Command-line arguments */
72 int i
; /* Looping var */
73 char *opt
; /* Option character */
74 int fg
; /* Run in the foreground */
75 fd_set input
, /* Input set for select() */
76 output
; /* Output set for select() */
77 client_t
*con
; /* Current client */
78 job_t
*job
, /* Current job */
80 listener_t
*lis
; /* Current listener */
81 time_t activity
; /* Activity timer */
82 time_t senddoc_time
; /* Send-Document time */
84 time_t mallinfo_time
; /* Malloc information time */
85 #endif /* HAVE_MALLINFO */
86 struct timeval timeout
; /* select() timeout */
87 struct rlimit limit
; /* Runtime limit */
88 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
89 struct sigaction action
; /* Actions for POSIX signals */
90 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
92 FILE *fp
; /* Fake lpsched lock file */
97 * Check for command-line arguments...
102 for (i
= 1; i
< argc
; i
++)
103 if (argv
[i
][0] == '-')
104 for (opt
= argv
[i
] + 1; *opt
!= '\0'; opt
++)
107 case 'c' : /* Configuration file */
112 if (argv
[i
][0] == '/')
115 * Absolute directory...
118 strlcpy(ConfigurationFile
, argv
[i
], sizeof(ConfigurationFile
));
123 * Relative directory...
126 getcwd(ConfigurationFile
, sizeof(ConfigurationFile
));
127 strlcat(ConfigurationFile
, "/", sizeof(ConfigurationFile
));
128 strlcat(ConfigurationFile
, argv
[i
], sizeof(ConfigurationFile
));
132 case 'f' : /* Run in foreground... */
136 case 'F' : /* Run in foreground, but still disconnect from terminal... */
140 default : /* Unknown option */
141 fprintf(stderr
, "cupsd: Unknown option \'%c\' - aborting!\n", *opt
);
147 fprintf(stderr
, "cupsd: Unknown argument \'%s\' - aborting!\n", argv
[i
]);
152 * If the user hasn't specified "-f", run in the background...
160 * OK, wait for the child to startup and send us SIGUSR1... We
161 * also need to ignore SIGHUP which might be sent by the init
162 * script to restart the scheduler...
165 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
166 sigset(SIGUSR1
, sigusr1_handler
);
168 sigset(SIGHUP
, SIG_IGN
);
169 #elif defined(HAVE_SIGACTION)
170 sigemptyset(&action
.sa_mask
);
171 sigaddset(&action
.sa_mask
, SIGUSR1
);
172 action
.sa_handler
= sigusr1_handler
;
173 sigaction(SIGUSR1
, &action
, NULL
);
175 sigemptyset(&action
.sa_mask
);
176 action
.sa_handler
= SIG_IGN
;
177 sigaction(SIGHUP
, &action
, NULL
);
179 signal(SIGUSR1
, sigusr1_handler
);
181 signal(SIGHUP
, SIG_IGN
);
182 #endif /* HAVE_SIGSET */
191 fprintf(stderr
, "cupsd: Child exited with status %d!\n", i
/ 256);
193 fprintf(stderr
, "cupsd: Child exited on signal %d!\n", i
);
202 * Make sure we aren't tying up any filesystems...
209 * Disable core dumps...
212 getrlimit(RLIMIT_CORE
, &limit
);
214 setrlimit(RLIMIT_CORE
, &limit
);
217 * Disconnect from the controlling terminal...
229 * Set the timezone info...
232 if (getenv("TZ") != NULL
)
233 snprintf(TZ
, sizeof(TZ
), "TZ=%s", getenv("TZ"));
238 * Set the maximum number of files...
241 getrlimit(RLIMIT_NOFILE
, &limit
);
242 if (limit
.rlim_max
> FD_SETSIZE
) /* Can't exceed size of FD set! */
245 MaxFDs
= limit
.rlim_max
;
247 limit
.rlim_cur
= MaxFDs
;
248 setrlimit(RLIMIT_NOFILE
, &limit
);
251 * Catch hangup and child signals and ignore broken pipes...
254 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
256 sigset(SIGHUP
, sigterm_handler
);
258 sigset(SIGHUP
, sighup_handler
);
259 sigset(SIGPIPE
, SIG_IGN
);
260 sigset(SIGTERM
, sigterm_handler
);
261 #elif defined(HAVE_SIGACTION)
262 memset(&action
, 0, sizeof(action
));
264 sigemptyset(&action
.sa_mask
);
265 sigaddset(&action
.sa_mask
, SIGHUP
);
268 action
.sa_handler
= sigterm_handler
;
270 action
.sa_handler
= sighup_handler
;
272 sigaction(SIGHUP
, &action
, NULL
);
274 sigemptyset(&action
.sa_mask
);
275 action
.sa_handler
= SIG_IGN
;
276 sigaction(SIGPIPE
, &action
, NULL
);
278 sigemptyset(&action
.sa_mask
);
279 sigaddset(&action
.sa_mask
, SIGTERM
);
280 sigaddset(&action
.sa_mask
, SIGCHLD
);
281 action
.sa_handler
= sigterm_handler
;
282 sigaction(SIGTERM
, &action
, NULL
);
285 signal(SIGHUP
, sigterm_handler
);
287 signal(SIGHUP
, sighup_handler
);
289 signal(SIGPIPE
, SIG_IGN
);
290 signal(SIGTERM
, sigterm_handler
);
291 #endif /* HAVE_SIGSET */
294 * Read configuration...
297 if (!ReadConfiguration())
299 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
306 * Try to create a fake lpsched lock file if one is not already there.
307 * Some Adobe applications need it under IRIX in order to enable
311 if ((fp
= fopen("/var/spool/lp/SCHEDLOCK", "a")) == NULL
)
313 syslog(LOG_LPR
, "Unable to create fake lpsched lock file "
314 "\"/var/spool/lp/SCHEDLOCK\"\' - %s!",
321 chmod("/var/spool/lp/SCHEDLOCK", 0644);
322 chown("/var/spool/lp/SCHEDLOCK", User
, Group
);
327 * Initialize authentication certificates...
333 * If we are running in the background, signal the parent process that
334 * we are up and running...
338 kill(getppid(), SIGUSR1
);
344 senddoc_time
= time(NULL
);
348 #endif /* HAVE_MALLINFO */
353 * Check if we need to load the server configuration file...
360 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
361 if (con
->http
.state
== HTTP_WAITING
)
367 con
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
371 else if (!ReadConfiguration())
373 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
380 * Check for available input or ready output. If select() returns
381 * 0 or -1, something bad happened and we should exit immediately.
383 * Note that we at least have one listening socket open at all
390 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
391 if (con
->http
.used
> 0)
402 * If we have no pending data from a client, see when we really
410 if ((i
= select(MaxFDs
, &input
, &output
, NULL
, &timeout
)) < 0)
412 char s
[16384], /* String buffer */
413 *sptr
; /* Pointer into buffer */
414 int slen
; /* Length of string buffer */
418 * Got an error from select!
421 if (errno
== EINTR
) /* Just interrupted by a signal */
425 * Log all sorts of debug info to help track down the problem.
428 LogMessage(L_EMERG
, "select() failed - %s!", strerror(errno
));
430 strcpy(s
, "InputSet =");
434 for (i
= 0; i
< MaxFDs
; i
++)
435 if (FD_ISSET(i
, &InputSet
))
437 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
438 slen
+= strlen(sptr
);
439 sptr
+= strlen(sptr
);
442 LogMessage(L_EMERG
, s
);
444 strcpy(s
, "OutputSet =");
448 for (i
= 0; i
< MaxFDs
; i
++)
449 if (FD_ISSET(i
, &OutputSet
))
451 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
452 slen
+= strlen(sptr
);
453 sptr
+= strlen(sptr
);
456 LogMessage(L_EMERG
, s
);
458 for (i
= 0, con
= Clients
; i
< NumClients
; i
++, con
++)
459 LogMessage(L_EMERG
, "Clients[%d] = %d, file = %d, state = %d",
460 i
, con
->http
.fd
, con
->file
, con
->http
.state
);
462 for (i
= 0, lis
= Listeners
; i
< NumListeners
; i
++, lis
++)
463 LogMessage(L_EMERG
, "Listeners[%d] = %d", i
, lis
->fd
);
465 LogMessage(L_EMERG
, "BrowseSocket = %d", BrowseSocket
);
467 for (job
= Jobs
; job
!= NULL
; job
= job
->next
)
468 LogMessage(L_EMERG
, "Jobs[%d] = %d < [%d %d] > [%d %d]",
469 job
->id
, job
->status_pipe
,
470 job
->print_pipes
[0], job
->print_pipes
[1],
471 job
->back_pipes
[0], job
->back_pipes
[1]);
476 for (i
= NumListeners
, lis
= Listeners
; i
> 0; i
--, lis
++)
477 if (FD_ISSET(lis
->fd
, &input
))
480 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
483 * Process the input buffer...
486 if (FD_ISSET(con
->http
.fd
, &input
) || con
->http
.used
)
487 if (!ReadClient(con
))
494 * Write data as needed...
497 if (FD_ISSET(con
->http
.fd
, &output
) &&
498 (!con
->pipe_pid
|| FD_ISSET(con
->file
, &input
)))
499 if (!WriteClient(con
))
506 * Check the activity and close old clients...
509 activity
= time(NULL
) - Timeout
;
510 if (con
->http
.activity
< activity
&& !con
->pipe_pid
)
519 * Check for status info from job filters...
522 for (job
= Jobs
; job
!= NULL
; job
= next
)
526 if (job
->status_pipe
>= 0 && FD_ISSET(job
->status_pipe
, &input
))
529 * Clear the input bit to avoid updating the next job
530 * using the same status pipe file descriptor...
533 FD_CLR(job
->status_pipe
, &input
);
536 * Read any status messages from the filters...
544 * Update the browse list as needed...
547 if (Browsing
&& BrowseProtocols
)
549 if (BrowseSocket
>= 0 && FD_ISSET(BrowseSocket
, &input
))
552 if (PollPipe
>= 0 && FD_ISSET(PollPipe
, &input
))
556 if ((BrowseProtocols
& BROWSE_SLP
) && BrowseSLPRefresh
<= time(NULL
))
558 #endif /* HAVE_LIBSLP */
564 * Update any pending multi-file documents...
567 if ((time(NULL
) - senddoc_time
) >= 10)
570 senddoc_time
= time(NULL
);
575 * Log memory usage every minute...
578 if ((time(NULL
) - mallinfo_time
) >= 60 && LogLevel
>= L_DEBUG
)
580 struct mallinfo mem
; /* Malloc information */
584 LogMessage(L_DEBUG
, "mallinfo: arena = %d, used = %d, free = %d\n",
585 mem
.arena
, mem
.usmblks
+ mem
.uordblks
,
586 mem
.fsmblks
+ mem
.fordblks
);
587 mallinfo_time
= time(NULL
);
589 #endif /* HAVE_MALLINFO */
592 * Update the root certificate once every 5 minutes...
595 if ((time(NULL
) - RootCertTime
) >= RootCertDuration
&& RootCertDuration
)
598 * Update the root certificate...
607 * If we get here something very bad happened and we need to exit
622 * 'CatchChildSignals()' - Catch SIGCHLD signals...
626 CatchChildSignals(void)
628 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
629 struct sigaction action
; /* Actions for POSIX signals */
630 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
633 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
634 sigset(SIGCHLD
, sigchld_handler
);
635 #elif defined(HAVE_SIGACTION)
636 memset(&action
, 0, sizeof(action
));
638 sigemptyset(&action
.sa_mask
);
639 sigaddset(&action
.sa_mask
, SIGTERM
);
640 sigaddset(&action
.sa_mask
, SIGCHLD
);
641 action
.sa_handler
= sigchld_handler
;
642 sigaction(SIGCHLD
, &action
, NULL
);
644 signal(SIGCLD
, sigchld_handler
); /* No, SIGCLD isn't a typo... */
645 #endif /* HAVE_SIGSET */
650 * 'IgnoreChildSignals()' - Ignore SIGCHLD signals...
654 IgnoreChildSignals(void)
656 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
657 struct sigaction action
; /* Actions for POSIX signals */
658 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
660 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
661 sigset(SIGCHLD
, SIG_IGN
);
662 #elif defined(HAVE_SIGACTION)
663 memset(&action
, 0, sizeof(action
));
665 sigemptyset(&action
.sa_mask
);
666 sigaddset(&action
.sa_mask
, SIGCHLD
);
667 action
.sa_handler
= SIG_IGN
;
668 sigaction(SIGCHLD
, &action
, NULL
);
670 signal(SIGCLD
, SIG_IGN
); /* No, SIGCLD isn't a typo... */
671 #endif /* HAVE_SIGSET */
676 * 'sigchld_handler()' - Handle 'child' signals from old processes.
680 sigchld_handler(int sig
) /* I - Signal number */
682 int status
; /* Exit status of child */
683 int pid
; /* Process ID of child */
684 job_t
*job
; /* Current job */
685 int i
; /* Looping var */
691 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0)
692 #elif defined(HAVE_WAIT3)
693 while ((pid
= wait3(&status
, WNOHANG
, NULL
)) > 0)
695 if ((pid
= wait(&status
)) > 0)
696 #endif /* HAVE_WAITPID */
698 DEBUG_printf(("sigchld_handler: pid = %d, status = %d\n", pid
, status
));
701 * Delete certificates for CGI processes...
708 * Ignore SIGTERM errors - that comes when a job is cancelled...
711 if (status
== SIGTERM
)
716 if (WIFEXITED(status
))
717 LogMessage(L_ERROR
, "PID %d stopped with status %d!", pid
,
718 WEXITSTATUS(status
));
720 LogMessage(L_ERROR
, "PID %d crashed on signal %d!", pid
,
723 if (LogLevel
< L_DEBUG
)
724 LogMessage(L_INFO
, "Hint: Try setting the LogLevel to \"debug\" to find out more.");
727 for (job
= Jobs
; job
!= NULL
; job
= job
->next
)
728 if (job
->state
!= NULL
&&
729 job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
731 for (i
= 0; job
->filters
[i
]; i
++)
732 if (job
->filters
[i
] == pid
)
735 if (job
->filters
[i
] || job
->backend
== pid
)
738 * OK, this process has gone away; what's left?
742 job
->filters
[i
] = -pid
;
746 if (status
&& job
->status
>= 0)
749 * An error occurred; save the exit status so we know to stop
750 * the printer or cancel the job when all of the filters finish...
752 * A negative status indicates that the backend failed and the
753 * printer needs to be stopped.
757 job
->status
= status
; /* Filter failed */
759 job
->status
= -status
; /* Backend failed */
767 sigset(SIGCHLD
, sigchld_handler
);
768 #elif !defined(HAVE_SIGACTION)
769 signal(SIGCLD
, sigchld_handler
);
770 #endif /* HAVE_SIGSET */
775 * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler.
779 sighup_handler(int sig
) /* I - Signal number */
786 sigset(SIGHUP
, sighup_handler
);
787 #elif !defined(HAVE_SIGACTION)
788 signal(SIGHUP
, sighup_handler
);
789 #endif /* HAVE_SIGSET */
794 * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler.
798 sigterm_handler(int sig
) /* I - Signal */
801 struct stat statbuf
; /* Needed for checking lpsched FIFO */
805 (void)sig
; /* remove compiler warnings... */
811 LogMessage(L_ERROR
, "Scheduler shutting down due to SIGTERM.");
814 * Close all network clients and stop all jobs...
827 if (AccessFile
!= NULL
)
830 if (ErrorFile
!= NULL
)
833 if (PageFile
!= NULL
)
836 DeleteAllLocations();
848 if (MimeDatabase
!= NULL
)
849 mimeDelete(MimeDatabase
);
853 * Remove the fake IRIX lpsched lock file, but only if the existing
854 * file is not a FIFO which indicates that the real IRIX lpsched is
858 if (!stat("/var/spool/lp/FIFO", &statbuf
))
859 if (!S_ISFIFO(statbuf
.st_mode
))
860 unlink("/var/spool/lp/SCHEDLOCK");
868 * 'sigusr1_handler()' - Catch USR1 signals...
872 sigusr1_handler(int sig
) /* I - Signal */
874 (void)sig
; /* remove compiler warnings... */
879 * 'usage()' - Show scheduler usage.
885 fputs("Usage: cupsd [-c config-file] [-f]\n", stderr
);
891 * End of "$Id: main.c,v 1.57.2.19 2002/08/21 02:06:27 mike Exp $".