2 * "$Id: main.c 5042 2006-02-01 18:17:34Z mike $"
4 * Scheduler main loop for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2006 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 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
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 * launchd_checkin() - Check-in with launchd and collect the
37 * launchd_reload() - Tell launchd to reload the configuration
38 * file to pick up the new listening directives.
39 * launchd_sync_conf() - Re-write the launchd(8) config file
40 * com.easysw.cupsd.plist based on cupsd.conf.
41 * parent_handler() - Catch USR1/CHLD signals...
42 * process_children() - Process all dead children...
43 * sigchld_handler() - Handle 'child' signals from old processes.
44 * sighup_handler() - Handle 'hangup' signals to reconfigure the
46 * sigterm_handler() - Handle 'terminate' signals that stop the
48 * select_timeout() - Calculate the select timeout value.
49 * usage() - Show scheduler usage.
53 * Include necessary headers...
58 #include <sys/resource.h>
65 #endif /* HAVE_LAUNCH_H */
67 #if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
69 #endif /* HAVE_MALLOC_H && HAVE_MALLINFO */
72 #endif /* HAVE_NOTIFY_H */
80 static void launchd_checkin(void);
81 static void launchd_reload(void);
82 static int launchd_sync_conf(void);
83 #endif /* HAVE_LAUNCHD */
85 static void parent_handler(int sig
);
86 static void process_children(void);
87 static void sigchld_handler(int sig
);
88 static void sighup_handler(int sig
);
89 static void sigterm_handler(int sig
);
90 static long select_timeout(int fds
);
91 static void usage(int status
);
98 static int parent_signal
= 0; /* Set to signal number from child */
99 static int holdcount
= 0; /* Number of times "hold" was called */
100 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
101 static sigset_t holdmask
; /* Old POSIX signal mask */
102 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
103 static int dead_children
= 0; /* Dead children? */
104 static int stop_scheduler
= 0; /* Should the scheduler stop? */
108 * 'main()' - Main entry for the CUPS scheduler.
111 int /* O - Exit status */
112 main(int argc
, /* I - Number of command-line arguments */
113 char *argv
[]) /* I - Command-line arguments */
115 int i
; /* Looping var */
116 char *opt
; /* Option character */
117 int fg
; /* Run in the foreground */
118 int fds
; /* Number of ready descriptors select returns */
119 fd_set
*input
, /* Input set for select() */
120 *output
; /* Output set for select() */
121 cupsd_client_t
*con
; /* Current client */
122 cupsd_job_t
*job
; /* Current job */
123 cupsd_listener_t
*lis
; /* Current listener */
124 time_t current_time
, /* Current time */
125 activity
, /* Client activity timer */
126 browse_time
, /* Next browse send time */
127 senddoc_time
, /* Send-Document time */
128 expire_time
; /* Subscription expire time */
130 time_t mallinfo_time
; /* Malloc information time */
131 #endif /* HAVE_MALLINFO */
132 struct timeval timeout
; /* select() timeout */
133 struct rlimit limit
; /* Runtime limit */
134 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
135 struct sigaction action
; /* Actions for POSIX signals */
136 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
138 cups_file_t
*fp
; /* Fake lpsched lock file */
139 struct stat statbuf
; /* Needed for checking lpsched FIFO */
142 int launchd
, /* Started with the -l option? */
144 /* Idle exit on select timeout? */
145 #endif /* HAVE_LAUNCHD */
149 * Check for command-line arguments...
155 #endif /* HAVE_LAUNCHD */
157 for (i
= 1; i
< argc
; i
++)
158 if (argv
[i
][0] == '-')
159 for (opt
= argv
[i
] + 1; *opt
!= '\0'; opt
++)
162 case 'c' : /* Configuration file */
166 _cupsLangPuts(stderr
, _("cupsd: Expected config filename "
167 "after \"-c\" option!\n"));
171 if (argv
[i
][0] == '/')
174 * Absolute directory...
177 cupsdSetString(&ConfigurationFile
, argv
[i
]);
182 * Relative directory...
185 char *current
; /* Current directory */
189 * Allocate a buffer for the current working directory to
190 * reduce run-time stack usage; this approximates the
191 * behavior of some implementations of getcwd() when they
192 * are passed a NULL pointer.
195 current
= malloc(1024);
196 getcwd(current
, 1024);
198 cupsdSetStringf(&ConfigurationFile
, "%s/%s", current
, argv
[i
]);
204 case 'f' : /* Run in foreground... */
208 case 'F' : /* Run in foreground, but still disconnect from terminal... */
212 case 'h' : /* Show usage/help */
216 case 'l' : /* Started by launchd... */
221 _cupsLangPuts(stderr
, _("cupsd: launchd(8) support not compiled "
222 "in, running in normal mode.\n"));
224 #endif /* HAVE_LAUNCHD */
227 default : /* Unknown option */
228 _cupsLangPrintf(stderr
, _("cupsd: Unknown option \"%c\" - "
229 "aborting!\n"), *opt
);
235 _cupsLangPrintf(stderr
, _("cupsd: Unknown argument \"%s\" - aborting!\n"),
240 if (!ConfigurationFile
)
241 cupsdSetString(&ConfigurationFile
, CUPS_SERVERROOT
"/cupsd.conf");
244 * If the user hasn't specified "-f", run in the background...
250 * Setup signal handlers for the parent...
253 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
254 sigset(SIGUSR1
, parent_handler
);
255 sigset(SIGCHLD
, parent_handler
);
257 sigset(SIGHUP
, SIG_IGN
);
258 #elif defined(HAVE_SIGACTION)
259 memset(&action
, 0, sizeof(action
));
260 sigemptyset(&action
.sa_mask
);
261 sigaddset(&action
.sa_mask
, SIGUSR1
);
262 action
.sa_handler
= parent_handler
;
263 sigaction(SIGUSR1
, &action
, NULL
);
264 sigaction(SIGCHLD
, &action
, NULL
);
266 sigemptyset(&action
.sa_mask
);
267 action
.sa_handler
= SIG_IGN
;
268 sigaction(SIGHUP
, &action
, NULL
);
270 signal(SIGUSR1
, parent_handler
);
271 signal(SIGCLD
, parent_handler
);
273 signal(SIGHUP
, SIG_IGN
);
274 #endif /* HAVE_SIGSET */
279 * OK, wait for the child to startup and send us SIGUSR1 or to crash
280 * and the OS send us SIGCHLD... We also need to ignore SIGHUP which
281 * might be sent by the init script to restart the scheduler...
284 for (; parent_signal
== 0;)
287 if (parent_signal
== SIGUSR1
)
295 else if (WIFEXITED(i
))
297 fprintf(stderr
, "cupsd: Child exited with status %d!\n", WEXITSTATUS(i
));
302 fprintf(stderr
, "cupsd: Child exited on signal %d!\n", WTERMSIG(i
));
311 * Make sure we aren't tying up any filesystems...
318 * Disable core dumps...
321 getrlimit(RLIMIT_CORE
, &limit
);
323 setrlimit(RLIMIT_CORE
, &limit
);
326 * Disconnect from the controlling terminal...
332 * Close all open files...
335 getrlimit(RLIMIT_NOFILE
, &limit
);
337 for (i
= 0; i
< limit
.rlim_cur
; i
++)
343 * Set the timezone info...
349 setlocale(LC_TIME
, "");
353 * Set the maximum number of files...
356 getrlimit(RLIMIT_NOFILE
, &limit
);
358 if (limit
.rlim_max
> CUPS_MAX_FDS
)
359 MaxFDs
= CUPS_MAX_FDS
;
361 MaxFDs
= limit
.rlim_max
;
363 limit
.rlim_cur
= MaxFDs
;
365 setrlimit(RLIMIT_NOFILE
, &limit
);
368 * Allocate memory for the input and output sets...
371 SetSize
= (MaxFDs
+ 31) / 8 + 4;
372 if (SetSize
< sizeof(fd_set
))
373 SetSize
= sizeof(fd_set
);
375 InputSet
= (fd_set
*)calloc(1, SetSize
);
376 OutputSet
= (fd_set
*)calloc(1, SetSize
);
377 input
= (fd_set
*)calloc(1, SetSize
);
378 output
= (fd_set
*)calloc(1, SetSize
);
380 if (InputSet
== NULL
|| OutputSet
== NULL
|| input
== NULL
|| output
== NULL
)
382 syslog(LOG_LPR
, "Unable to allocate memory for select() sets - exiting!");
387 * Read configuration...
390 if (!cupsdReadConfiguration())
392 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
401 * If we were started by launchd make sure the cupsd plist file contains the
402 * same listeners as cupsd.conf; If it didn't then reload it before getting
403 * the list of listening file descriptors...
406 if (launchd_sync_conf())
411 * Until rdar://3854821 is fixed we have to exit after the reload...
414 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "Exiting on launchd_reload");
420 #endif /* HAVE_LAUNCHD */
423 * Startup the server...
429 * Catch hangup and child signals and ignore broken pipes...
432 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
434 sigset(SIGHUP
, sigterm_handler
);
436 sigset(SIGHUP
, sighup_handler
);
438 sigset(SIGPIPE
, SIG_IGN
);
439 sigset(SIGTERM
, sigterm_handler
);
440 #elif defined(HAVE_SIGACTION)
441 memset(&action
, 0, sizeof(action
));
443 sigemptyset(&action
.sa_mask
);
444 sigaddset(&action
.sa_mask
, SIGHUP
);
447 action
.sa_handler
= sigterm_handler
;
449 action
.sa_handler
= sighup_handler
;
451 sigaction(SIGHUP
, &action
, NULL
);
453 sigemptyset(&action
.sa_mask
);
454 action
.sa_handler
= SIG_IGN
;
455 sigaction(SIGPIPE
, &action
, NULL
);
457 sigemptyset(&action
.sa_mask
);
458 sigaddset(&action
.sa_mask
, SIGTERM
);
459 sigaddset(&action
.sa_mask
, SIGCHLD
);
460 action
.sa_handler
= sigterm_handler
;
461 sigaction(SIGTERM
, &action
, NULL
);
464 signal(SIGHUP
, sigterm_handler
);
466 signal(SIGHUP
, sighup_handler
);
468 signal(SIGPIPE
, SIG_IGN
);
469 signal(SIGTERM
, sigterm_handler
);
470 #endif /* HAVE_SIGSET */
474 * Try to create a fake lpsched lock file if one is not already there.
475 * Some Adobe applications need it under IRIX in order to enable
479 if ((fp
= cupsFileOpen("/var/spool/lp/SCHEDLOCK", "w")) == NULL
)
481 syslog(LOG_LPR
, "Unable to create fake lpsched lock file "
482 "\"/var/spool/lp/SCHEDLOCK\"\' - %s!",
487 fchmod(cupsFileNumber(fp
), 0644);
488 fchown(cupsFileNumber(fp
), User
, Group
);
495 * Initialize authentication certificates...
501 * If we are running in the background, signal the parent process that
502 * we are up and running...
508 * Send a signal to the parent process, but only if the parent is
509 * not PID 1 (init). This avoids accidentally shutting down the
510 * system on OpenBSD if you CTRL-C the server before it is up...
513 i
= getppid(); /* Save parent PID to avoid race condition */
520 * Start power management framework...
523 cupsdStartSystemMonitor();
526 * If the administrator has configured the server to run as an unpriviledged
527 * user, change to that user now...
533 setgroups(1, &Group
);
541 cupsdCatchChildSignals();
544 * Start any pending print jobs...
555 #endif /* HAVE_MALLINFO */
556 browse_time
= time(NULL
);
557 senddoc_time
= time(NULL
);
558 expire_time
= time(NULL
);
561 while (!stop_scheduler
)
564 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
565 "main: Top of loop, dead_children=%d, NeedReload=%d",
566 dead_children
, NeedReload
);
570 * Check if there are dead children to handle...
577 * Check if we need to load the server configuration file...
583 * Close any idle clients...
588 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
589 if (con
->http
.state
== HTTP_WAITING
)
591 cupsdCloseClient(con
);
595 con
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
597 cupsdPauseListening();
601 * Check for any active jobs...
604 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
606 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
607 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
611 * Restart if all clients are closed and all jobs finished, or
612 * if the reload timeout has elapsed...
615 if ((NumClients
== 0 && (!job
|| NeedReload
!= RELOAD_ALL
)) ||
616 (time(NULL
) - ReloadTime
) >= ReloadTimeout
)
619 * Shutdown the server...
625 * Read configuration...
628 if (!cupsdReadConfiguration())
630 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
638 if (launchd_sync_conf())
643 * Until rdar://3854821 is fixed we have to exit after the reload...
646 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "Exiting on launchd_reload");
653 #endif /* HAVE_LAUNCHD */
656 * Startup the server...
664 * Check for available input or ready output. If select() returns
665 * 0 or -1, something bad happened and we should exit immediately.
667 * Note that we at least have one listening socket open at all
671 memcpy(input
, InputSet
, SetSize
);
672 memcpy(output
, OutputSet
, SetSize
);
674 timeout
.tv_sec
= select_timeout(fds
);
679 * If no other work is scheduled and we're being controlled by
680 * launchd(8) then timeout after 'LaunchdTimeout' seconds of
684 if (timeout
.tv_sec
== 86400 && launchd
&& LaunchdTimeout
&&
685 (!Browsing
|| !(BrowseLocalProtocols
& BROWSE_DNSSD
) ||
686 cupsArrayCount(Printers
) == 0))
688 timeout
.tv_sec
= LaunchdTimeout
;
689 launchd_idle_exit
= 1;
692 launchd_idle_exit
= 0;
693 #endif /* HAVE_LAUNCHD */
695 if (timeout
.tv_sec
< 86400) /* Only use timeout for < 1 day */
696 fds
= select(MaxFDs
, input
, output
, NULL
, &timeout
);
698 fds
= select(MaxFDs
, input
, output
, NULL
, NULL
);
702 char s
[16384], /* String buffer */
703 *sptr
; /* Pointer into buffer */
704 int slen
; /* Length of string buffer */
708 * Got an error from select!
711 if (errno
== EINTR
) /* Just interrupted by a signal */
715 * Log all sorts of debug info to help track down the problem.
718 cupsdLogMessage(CUPSD_LOG_EMERG
, "select() failed - %s!",
721 strcpy(s
, "InputSet =");
725 for (i
= 0; i
< MaxFDs
; i
++)
726 if (FD_ISSET(i
, InputSet
))
728 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
729 slen
+= strlen(sptr
);
730 sptr
+= strlen(sptr
);
733 cupsdLogMessage(CUPSD_LOG_EMERG
, s
);
735 strcpy(s
, "OutputSet =");
739 for (i
= 0; i
< MaxFDs
; i
++)
740 if (FD_ISSET(i
, OutputSet
))
742 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
743 slen
+= strlen(sptr
);
744 sptr
+= strlen(sptr
);
747 cupsdLogMessage(CUPSD_LOG_EMERG
, s
);
749 for (i
= 0, con
= Clients
; i
< NumClients
; i
++, con
++)
750 cupsdLogMessage(CUPSD_LOG_EMERG
,
751 "Clients[%d] = %d, file = %d, state = %d",
752 i
, con
->http
.fd
, con
->file
, con
->http
.state
);
754 for (i
= 0, lis
= Listeners
; i
< NumListeners
; i
++, lis
++)
755 cupsdLogMessage(CUPSD_LOG_EMERG
, "Listeners[%d] = %d", i
, lis
->fd
);
757 cupsdLogMessage(CUPSD_LOG_EMERG
, "BrowseSocket = %d", BrowseSocket
);
759 cupsdLogMessage(CUPSD_LOG_EMERG
, "CGIPipes[0] = %d", CGIPipes
[0]);
762 cupsdLogMessage(CUPSD_LOG_EMERG
, "SysEventPipes[0] = %d",
764 #endif /* __APPLE__ */
766 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
768 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
769 cupsdLogMessage(CUPSD_LOG_EMERG
, "Jobs[%d] = %d < [%d %d] > [%d %d]",
771 job
->status_buffer
? job
->status_buffer
->fd
: -1,
772 job
->print_pipes
[0], job
->print_pipes
[1],
773 job
->back_pipes
[0], job
->back_pipes
[1]);
777 current_time
= time(NULL
);
781 * If no other work was scheduled and we're being controlled by launchd(8)
782 * then timeout after 'LaunchdTimeout' seconds of inactivity...
785 if (!fds
&& launchd_idle_exit
)
787 cupsdLogMessage(CUPSD_LOG_INFO
,
788 "Printer sharing is off and there are no jobs pending, "
789 "will restart on demand.");
793 #endif /* HAVE_LAUNCHD */
796 * Check for status info from job filters...
799 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
801 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
802 if (job
->status_buffer
&& FD_ISSET(job
->status_buffer
->fd
, input
))
805 * Clear the input bit to avoid updating the next job
806 * using the same status pipe file descriptor...
809 FD_CLR(job
->status_buffer
->fd
, input
);
812 * Read any status messages from the filters...
819 * Update CGI messages as needed...
822 if (CGIPipes
[0] >= 0 && FD_ISSET(CGIPipes
[0], input
))
826 * Handle system management events as needed...
830 if (SysEventPipes
[0] >= 0 && FD_ISSET(SysEventPipes
[0], input
))
831 cupsdUpdateSystemMonitor();
832 #endif /* __APPLE__ */
835 * Update notifier messages as needed...
838 if (NotifierPipes
[0] >= 0 && FD_ISSET(NotifierPipes
[0], input
))
839 cupsdUpdateNotifierStatus();
842 * Expire subscriptions as needed...
845 if (cupsArrayCount(Subscriptions
) > 0 && current_time
> expire_time
)
847 cupsdExpireSubscriptions(NULL
, NULL
);
849 expire_time
= current_time
;
853 * Update the browse list as needed...
856 if (Browsing
&& BrowseRemoteProtocols
)
858 if (BrowseSocket
>= 0 && FD_ISSET(BrowseSocket
, input
))
859 cupsdUpdateCUPSBrowse();
861 if (PollPipe
>= 0 && FD_ISSET(PollPipe
, input
))
862 cupsdUpdatePolling();
865 if (((BrowseLocalProtocols
| BrowseRemoteProtocols
) & BROWSE_SLP
) &&
866 BrowseSLPRefresh
<= current_time
)
867 cupsdUpdateSLPBrowse();
868 #endif /* HAVE_LIBSLP */
871 if (Browsing
&& BrowseLocalProtocols
&& current_time
> browse_time
)
873 cupsdSendBrowseList();
874 browse_time
= current_time
;
878 * Check for new connections on the "listen" sockets...
881 for (i
= NumListeners
, lis
= Listeners
; i
> 0; i
--, lis
++)
882 if (lis
->fd
>= 0 && FD_ISSET(lis
->fd
, input
))
884 FD_CLR(lis
->fd
, input
);
885 cupsdAcceptClient(lis
);
889 * Check for new data on the client sockets...
892 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
895 * Process the input buffer...
898 if (FD_ISSET(con
->http
.fd
, input
) || con
->http
.used
)
900 FD_CLR(con
->http
.fd
, input
);
902 if (!cupsdReadClient(con
))
905 FD_CLR(con
->file
, input
);
913 * Write data as needed...
916 if (con
->pipe_pid
&& FD_ISSET(con
->file
, input
))
919 * Keep track of pending input from the file/pipe separately
920 * so that we don't needlessly spin on select() when the web
921 * client is not ready to receive data...
924 FD_CLR(con
->file
, input
);
928 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "main: Data ready file %d!",
932 if (!FD_ISSET(con
->http
.fd
, output
))
934 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
935 "main: Removing fd %d from InputSet...", con
->file
);
936 FD_CLR(con
->file
, input
);
937 FD_CLR(con
->file
, InputSet
);
941 if (FD_ISSET(con
->http
.fd
, output
))
943 FD_CLR(con
->http
.fd
, output
);
945 if (!con
->pipe_pid
|| con
->file_ready
)
946 if (!cupsdWriteClient(con
))
954 * Check the activity and close old clients...
957 activity
= current_time
- Timeout
;
958 if (con
->http
.activity
< activity
&& !con
->pipe_pid
)
960 cupsdLogMessage(CUPSD_LOG_DEBUG
,
961 "Closing client %d after %d seconds of inactivity...",
962 con
->http
.fd
, Timeout
);
964 cupsdCloseClient(con
);
971 * Update any pending multi-file documents...
974 if ((current_time
- senddoc_time
) >= 10)
977 senddoc_time
= current_time
;
982 * Log memory usage every minute...
985 if ((current_time
- mallinfo_time
) >= 60 && LogLevel
>= CUPSD_LOG_DEBUG
)
987 struct mallinfo mem
; /* Malloc information */
991 cupsdLogMessage(CUPSD_LOG_DEBUG
,
992 "mallinfo: arena = %d, used = %d, free = %d\n",
993 mem
.arena
, mem
.usmblks
+ mem
.uordblks
,
994 mem
.fsmblks
+ mem
.fordblks
);
995 mallinfo_time
= current_time
;
997 #endif /* HAVE_MALLINFO */
1000 * Update the root certificate once every 5 minutes...
1003 if ((current_time
- RootCertTime
) >= RootCertDuration
&& RootCertDuration
&&
1007 * Update the root certificate...
1011 cupsdAddCert(0, "root");
1015 * Handle OS-specific event notification for any events that have
1016 * accumulated. Don't send these more than once a second...
1019 if (LastEvent
&& (time(NULL
) - LastEventTime
) > 1)
1021 #ifdef HAVE_NOTIFY_POST
1022 if (LastEvent
& CUPSD_EVENT_PRINTER_CHANGED
)
1024 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1025 "notify_post(\"com.apple.printerListChange\")");
1026 notify_post("com.apple.printerListChange");
1029 if (LastEvent
& CUPSD_EVENT_PRINTER_STATE_CHANGED
)
1031 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1032 "notify_post(\"com.apple.printerHistoryChange\")");
1033 notify_post("com.apple.printerHistoryChange");
1036 if (LastEvent
& (CUPSD_EVENT_JOB_STATE_CHANGED
|
1037 CUPSD_EVENT_JOB_CONFIG_CHANGED
|
1038 CUPSD_EVENT_JOB_PROGRESS
))
1040 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1041 "notify_post(\"com.apple.jobChange\")");
1042 notify_post("com.apple.jobChange");
1044 #endif /* HAVE_NOTIFY_POST */
1047 * Reset the accumulated events and notification time...
1050 LastEventTime
= time(NULL
);
1051 LastEvent
= CUPSD_EVENT_NONE
;
1056 * Log a message based on what happened...
1060 cupsdLogMessage(CUPSD_LOG_INFO
, "Scheduler shutting down normally.");
1062 cupsdLogMessage(CUPSD_LOG_ERROR
,
1063 "Scheduler shutting down due to program error.");
1066 * Close all network clients and stop all jobs...
1073 cupsdStopSystemMonitor();
1077 * Update the launchd config file as needed...
1080 launchd_sync_conf();
1081 #endif /* HAVE_LAUNCHD */
1085 * Remove the fake IRIX lpsched lock file, but only if the existing
1086 * file is not a FIFO which indicates that the real IRIX lpsched is
1090 if (!stat("/var/spool/lp/FIFO", &statbuf
))
1091 if (!S_ISFIFO(statbuf
.st_mode
))
1092 unlink("/var/spool/lp/SCHEDLOCK");
1096 * Free memory used by FD sets and return...
1104 return (!stop_scheduler
);
1109 * 'cupsdClosePipe()' - Close a pipe as necessary.
1113 cupsdClosePipe(int *fds
) /* I - Pipe file descriptors (2) */
1116 * Close file descriptors as needed...
1134 * 'cupsdOpenPipe()' - Create a pipe which is closed on exec.
1137 int /* O - 0 on success, -1 on error */
1138 cupsdOpenPipe(int *fds
) /* O - Pipe file descriptors (2) */
1141 * Create the pipe...
1148 * Set the "close on exec" flag on each end of the pipe...
1151 if (fcntl(fds
[0], F_SETFD
, fcntl(fds
[0], F_GETFD
) | FD_CLOEXEC
))
1158 if (fcntl(fds
[1], F_SETFD
, fcntl(fds
[1], F_GETFD
) | FD_CLOEXEC
))
1166 * Return 0 indicating success...
1174 * 'cupsdCatchChildSignals()' - Catch SIGCHLD signals...
1178 cupsdCatchChildSignals(void)
1180 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1181 struct sigaction action
; /* Actions for POSIX signals */
1182 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1185 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1186 sigset(SIGCHLD
, sigchld_handler
);
1187 #elif defined(HAVE_SIGACTION)
1188 memset(&action
, 0, sizeof(action
));
1190 sigemptyset(&action
.sa_mask
);
1191 sigaddset(&action
.sa_mask
, SIGTERM
);
1192 sigaddset(&action
.sa_mask
, SIGCHLD
);
1193 action
.sa_handler
= sigchld_handler
;
1194 sigaction(SIGCHLD
, &action
, NULL
);
1196 signal(SIGCLD
, sigchld_handler
); /* No, SIGCLD isn't a typo... */
1197 #endif /* HAVE_SIGSET */
1202 * 'cupsdClearString()' - Clear a string.
1206 cupsdClearString(char **s
) /* O - String value */
1217 * 'cupsdHoldSignals()' - Hold child and termination signals.
1221 cupsdHoldSignals(void)
1223 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1224 sigset_t newmask
; /* New POSIX signal mask */
1225 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1235 #elif defined(HAVE_SIGACTION)
1236 sigemptyset(&newmask
);
1237 sigaddset(&newmask
, SIGTERM
);
1238 sigaddset(&newmask
, SIGCHLD
);
1239 sigprocmask(SIG_BLOCK
, &newmask
, &holdmask
);
1240 #endif /* HAVE_SIGSET */
1245 * 'cupsdIgnoreChildSignals()' - Ignore SIGCHLD signals...
1247 * We don't really ignore them, we set the signal handler to SIG_DFL,
1248 * since some OS's rely on signals for the wait4() function to work.
1252 cupsdIgnoreChildSignals(void)
1254 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1255 struct sigaction action
; /* Actions for POSIX signals */
1256 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1259 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1260 sigset(SIGCHLD
, SIG_DFL
);
1261 #elif defined(HAVE_SIGACTION)
1262 memset(&action
, 0, sizeof(action
));
1264 sigemptyset(&action
.sa_mask
);
1265 sigaddset(&action
.sa_mask
, SIGCHLD
);
1266 action
.sa_handler
= SIG_DFL
;
1267 sigaction(SIGCHLD
, &action
, NULL
);
1269 signal(SIGCLD
, SIG_DFL
); /* No, SIGCLD isn't a typo... */
1270 #endif /* HAVE_SIGSET */
1275 * 'cupsdReleaseSignals()' - Release signals for delivery.
1279 cupsdReleaseSignals(void)
1288 #elif defined(HAVE_SIGACTION)
1289 sigprocmask(SIG_SETMASK
, &holdmask
, NULL
);
1290 #endif /* HAVE_SIGSET */
1295 * 'cupsdSetString()' - Set a string value.
1299 cupsdSetString(char **s
, /* O - New string */
1300 const char *v
) /* I - String value */
1316 * 'cupsdSetStringf()' - Set a formatted string value.
1320 cupsdSetStringf(char **s
, /* O - New string */
1321 const char *f
, /* I - Printf-style format string */
1322 ...) /* I - Additional args as needed */
1324 char v
[4096]; /* Formatting string value */
1325 va_list ap
; /* Argument pointer */
1326 char *olds
; /* Old string */
1337 vsnprintf(v
, sizeof(v
), f
, ap
);
1352 * 'launchd_checkin()' - Check-in with launchd and collect the listening fds.
1356 launchd_checkin(void)
1358 int i
, /* Looping var */
1359 portnum
; /* Port number */
1360 launch_data_t ld_msg
, /* Launch data message */
1361 ld_resp
, /* Launch data response */
1362 ld_array
, /* Launch data array */
1363 ld_sockets
, /* Launch data sockets dictionary */
1364 ld_runatload
, /* Run-at-load setting */
1365 tmp
; /* Launch data */
1366 cupsd_listener_t
*lis
; /* Listeners array */
1367 http_addr_t addr
; /* Address variable */
1368 socklen_t addrlen
; /* Length of address */
1369 bool runatload
; /* Run-at-load setting value */
1372 cupsdLogMessage(CUPSD_LOG_DEBUG
, "launchd_checkin: pid=%d", (int)getpid());
1375 * Check-in with launchd...
1378 ld_msg
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
1379 if ((ld_resp
= launch_msg(ld_msg
)) == NULL
)
1381 cupsdLogMessage(CUPSD_LOG_ERROR
,
1382 "launchd_checkin: launch_msg(\"" LAUNCH_KEY_CHECKIN
1387 if (launch_data_get_type(ld_resp
) == LAUNCH_DATA_ERRNO
)
1389 errno
= launch_data_get_errno(ld_resp
);
1390 cupsdLogMessage(CUPSD_LOG_ERROR
, "launchd_checkin: Check-in failed: %s",
1396 * Get the "run-at-load" setting...
1399 if ((ld_runatload
= launch_data_dict_lookup(ld_resp
,
1400 LAUNCH_JOBKEY_RUNATLOAD
)) != NULL
&&
1401 launch_data_get_type(ld_runatload
) == LAUNCH_DATA_BOOL
)
1402 runatload
= launch_data_get_bool(ld_runatload
);
1405 errno
= launch_data_get_errno(ld_resp
);
1406 cupsdLogMessage(CUPSD_LOG_ERROR
,
1407 "launchd_checkin: Unable to find Run-at-load setting: %s",
1412 cupsdLogMessage(CUPSD_LOG_DEBUG
, "launchd_checkin: Run-at-load=%s",
1413 runatload
? "true" : "false");
1416 * Get the sockets dictionary...
1419 if (!(ld_sockets
= launch_data_dict_lookup(ld_resp
, LAUNCH_JOBKEY_SOCKETS
)))
1421 cupsdLogMessage(CUPSD_LOG_ERROR
,
1422 "launchd_checkin: No sockets found to answer requests on!");
1427 * Get the array of listener sockets...
1430 if (!(ld_array
= launch_data_dict_lookup(ld_sockets
, "Listeners")))
1432 cupsdLogMessage(CUPSD_LOG_ERROR
,
1433 "launchd_checkin: No sockets found to answer requests on!");
1438 * Add listening fd(s) to the Listener array...
1441 if (launch_data_get_type(ld_array
) == LAUNCH_DATA_ARRAY
)
1444 * Free the listeners array built from cupsd.conf...
1447 if (NumListeners
> 0)
1450 NumListeners
= launch_data_array_get_count(ld_array
);
1451 Listeners
= calloc(NumListeners
, sizeof(cupsd_listener_t
));
1455 cupsdLogMessage(CUPSD_LOG_ERROR
,
1456 "launchd_checkin: Unable to allocate new Listeners - %s.",
1462 * Note: launchd wants us to access the array in ascending order,
1463 * thus "i" counts up and not down as we normally do elsewhere...
1466 for (i
= 0, lis
= Listeners
; i
< NumListeners
; i
++, lis
++)
1469 * Copy the current address and log it...
1472 tmp
= launch_data_array_get_index(ld_array
, i
);
1473 lis
->fd
= launch_data_get_fd(tmp
);
1474 addrlen
= sizeof(lis
->address
);
1476 if (getsockname(lis
->fd
, (struct sockaddr
*)&(lis
->address
), &addrlen
))
1478 cupsdLogMessage(CUPSD_LOG_ERROR
,
1479 "launchd_checkin: Unable to get local address - %s",
1487 if (addr
.addr
.sa_family
== AF_INET6
)
1488 portnum
= ntohs(addr
.ipv6
.sin6_port
);
1490 # endif /* AF_INET6 */
1492 if (addr
.addr
.sa_family
== AF_LOCAL
)
1495 * Make sure the domain socket is accessible to all...
1498 fchmod(lis
->fd
, 0140777);
1501 # endif /* AF_LOCAL */
1502 if (addr
.addr
.sa_family
== AF_INET
)
1503 portnum
= ntohs(addr
.ipv4
.sin_port
);
1506 lis
->encryption
= HTTP_ENCRYPT_ALWAYS
;
1507 # endif /* HAVE_SSL */
1512 * Collect the browse socket (if there is one)...
1515 if ((ld_array
= launch_data_dict_lookup(ld_sockets
, "BrowseSockets")))
1517 if (launch_data_get_type(ld_array
) == LAUNCH_DATA_ARRAY
)
1519 tmp
= launch_data_array_get_index(ld_array
, 0);
1521 if (launch_data_get_type(tmp
) == LAUNCH_DATA_FD
)
1523 if (BrowseSocket
!= -1)
1524 close(BrowseSocket
);
1526 BrowseSocket
= launch_data_get_fd(tmp
);
1529 cupsdLogMessage(CUPSD_LOG_WARN
,
1530 "launchd_checkin: BrowseSocket not a fd!");
1533 cupsdLogMessage(CUPSD_LOG_WARN
,
1534 "launchd_checkin: BrowseSockets is not an array!");
1537 cupsdLogMessage(CUPSD_LOG_DEBUG
, "launchd_checkin: No BrowseSockets");
1539 launch_data_free(ld_msg
);
1540 launch_data_free(ld_resp
);
1545 * 'launchd_reload()' - Tell launchd to reload the configuration file to pick
1546 * up the new listening directives.
1550 launchd_reload(void)
1552 int child_status
; /* Exit status of child process */
1553 pid_t child_pid
, /* Child PID */
1554 waitpid_status
; /* Child process exit status */
1555 char *argv
[4]; /* Argument strings */
1559 * The current launchd doesn't support a reload option (rdar://3854821).
1560 * Until this is fixed we need to reload the config file by execing launchctl
1561 * twice (to unload then load). NOTE: This will cause us to exit on SIGTERM
1562 * which will cancel all client & job activity.
1564 * After this is fixed we'll be able to tell launchd to reload the file
1565 * and pick up the new listening descriptors without disrupting current
1570 * Unloading the current configuration will cause launchd to send us a SIGTERM;
1571 * block it for now so we can get our work done...
1577 * Set up the unload arguments to launchctl...
1580 argv
[0] = "/bin/launchctl";
1582 argv
[2] = LaunchdConf
;
1585 if (cupsdStartProcess(argv
[0], argv
, NULL
, -1, -1, -1, -1, 1, &child_pid
) < 0)
1586 cupsdLogMessage(CUPSD_LOG_ERROR
,
1587 "launchd_reload: Unable to execute %s - %s", argv
[0],
1593 waitpid_status
= waitpid(child_pid
, &child_status
, 0);
1595 while (waitpid_status
== (pid_t
)-1 && errno
== EINTR
);
1597 if (WIFSIGNALED(child_status
))
1598 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1599 "launchd_reload: %s pid %d crashed on signal %d!",
1600 basename(argv
[0]), child_pid
, WTERMSIG(child_status
));
1602 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1603 "launchd_reload: %s pid %d stopped with status %d!",
1604 basename(argv
[0]), child_pid
, WEXITSTATUS(child_status
));
1607 * Do it again with the load command...
1612 if (cupsdStartProcess(argv
[0], argv
, NULL
, -1, -1, -1, -1, 1,
1615 cupsdLogMessage(CUPSD_LOG_ERROR
,
1616 "launchd_reload: Unable to fork for %s - %s", argv
[0],
1623 waitpid_status
= waitpid(child_pid
, &child_status
, 0);
1624 } while (waitpid_status
== (pid_t
)-1 && errno
== EINTR
);
1626 if (WIFSIGNALED(child_status
))
1627 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1628 "launchd_reload: %s pid %d crashed on signal %d!",
1629 basename(argv
[0]), child_pid
, WTERMSIG(child_status
));
1631 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1632 "launchd_reload: %s pid %d stopped with status %d",
1633 basename(argv
[0]), child_pid
,
1634 WEXITSTATUS(child_status
));
1639 * Leave signals blocked since exit() will be called momentarily anyways...
1645 * 'launchd_sync_conf()' - Re-write the launchd(8) config file
1646 * org.cups.cupsd.plist based on cupsd.conf.
1649 static int /* O - 1 if the file was updated */
1650 launchd_sync_conf(void)
1652 int i
, /* Looping var */
1653 portnum
; /* Port number */
1654 CFMutableDictionaryRef cupsd_dict
, /* com.easysw.cupsd.plist dictionary */
1655 sockets
, /* Sockets dictionary */
1656 listener
; /* Listener dictionary */
1657 CFDataRef resourceData
; /* XML representation of the property list */
1658 CFMutableArrayRef array
; /* Array */
1659 CFNumberRef socket_mode
; /* Domain socket mode bits */
1660 CFStringRef socket_path
; /* Domain socket path */
1661 CFTypeRef value
; /* CF value */
1662 CFURLRef fileURL
; /* File URL */
1663 SInt32 errorCode
; /* Error code */
1664 cupsd_listener_t
*lis
; /* Current listening socket */
1665 struct servent
*service
; /* Services data base entry */
1666 char temp
[1024]; /* Temporary buffer for value */
1667 struct stat cupsd_sb
, /* File info for cupsd.conf */
1668 launchd_sb
; /* File info for com.easysw.cupsd.plist */
1672 * If the launchd conf file modification time is newer than the cupsd.conf
1673 * time then there's nothing to do...
1676 if (!stat(ConfigurationFile
, &cupsd_sb
) &&
1677 !stat(LaunchdConf
, &launchd_sb
) &&
1678 launchd_sb
.st_mtimespec
.tv_sec
>= cupsd_sb
.st_mtimespec
.tv_sec
)
1680 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1681 "launchd_sync_conf: Nothing to do, pid=%d.",
1687 * Time to write a new 'com.easysw.cupsd.plist' file.
1688 * Create the new dictionary and populate it with values...
1691 if ((cupsd_dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1692 &kCFTypeDictionaryKeyCallBacks
,
1693 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1695 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_LABEL
),
1696 CFSTR("org.cups.cupsd"));
1697 CFDictionaryAddValue(cupsd_dict
, CFSTR("Enabled"), kCFBooleanTrue
);
1698 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_ONDEMAND
),
1701 if ((Browsing
&& BrowseLocalProtocols
&& cupsArrayCount(Printers
)) ||
1702 cupsArrayCount(ActiveJobs
))
1703 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_RUNATLOAD
),
1706 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_RUNATLOAD
),
1709 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_SERVICEIPC
),
1712 if ((array
= CFArrayCreateMutable(kCFAllocatorDefault
, 2,
1713 &kCFTypeArrayCallBacks
)) != NULL
)
1715 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS
),
1717 CFArrayAppendValue(array
, CFSTR("/usr/sbin/cupsd"));
1718 CFArrayAppendValue(array
, CFSTR("-l"));
1723 * Add a sockets dictionary...
1726 if ((sockets
= (CFMutableDictionaryRef
)CFDictionaryCreateMutable(
1727 kCFAllocatorDefault
, 0,
1728 &kCFTypeDictionaryKeyCallBacks
,
1729 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1731 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_SOCKETS
), sockets
);
1734 * Add a Listeners array to the sockets dictionary...
1737 if ((array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1738 &kCFTypeArrayCallBacks
)) != NULL
)
1740 CFDictionaryAddValue(sockets
, CFSTR("Listeners"), array
);
1743 * For each listener add a dictionary to the listeners array...
1746 for (i
= NumListeners
, lis
= Listeners
; i
> 0; i
--, lis
++)
1748 if ((listener
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1749 &kCFTypeDictionaryKeyCallBacks
,
1750 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1752 CFArrayAppendValue(array
, listener
);
1755 if (lis
->address
.addr
.sa_family
== AF_LOCAL
)
1757 if ((socket_path
= CFStringCreateWithCString(kCFAllocatorDefault
,
1758 lis
->address
.un
.sun_path
,
1759 kCFStringEncodingUTF8
)))
1761 CFDictionaryAddValue(listener
,
1762 CFSTR(LAUNCH_JOBSOCKETKEY_PATHNAME
),
1764 CFRelease(socket_path
);
1766 portnum
= 0140777; /* (S_IFSOCK|S_IRWXU|S_IRWXG|S_IRWXO) or *
1768 if ((socket_mode
= CFNumberCreate(kCFAllocatorDefault
,
1769 kCFNumberIntType
, &portnum
)))
1771 CFDictionaryAddValue(listener
, CFSTR("SockPathMode"),
1773 CFRelease(socket_mode
);
1777 # endif /* AF_LOCAL */
1780 if (lis
->address
.addr
.sa_family
== AF_INET6
)
1782 CFDictionaryAddValue(listener
,
1783 CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY
),
1785 portnum
= lis
->address
.ipv6
.sin6_port
;
1788 # endif /* AF_INET6 */
1790 CFDictionaryAddValue(listener
,
1791 CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY
),
1793 portnum
= lis
->address
.ipv4
.sin_port
;
1796 if ((service
= getservbyport(portnum
, NULL
)))
1797 value
= CFStringCreateWithCString(kCFAllocatorDefault
,
1799 kCFStringEncodingUTF8
);
1801 value
= CFNumberCreate(kCFAllocatorDefault
,
1802 kCFNumberIntType
, &portnum
);
1806 CFDictionaryAddValue(listener
,
1807 CFSTR(LAUNCH_JOBSOCKETKEY_SERVICENAME
),
1812 httpAddrString(&lis
->address
, temp
, sizeof(temp
));
1813 if ((value
= CFStringCreateWithCString(kCFAllocatorDefault
, temp
,
1814 kCFStringEncodingUTF8
)))
1816 CFDictionaryAddValue(listener
,
1817 CFSTR(LAUNCH_JOBSOCKETKEY_NODENAME
),
1823 CFRelease(listener
);
1831 * Add the BrowseSocket to the sockets dictionary...
1834 if (Browsing
&& (BrowseRemoteProtocols
& BROWSE_CUPS
))
1836 if ((array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1837 &kCFTypeArrayCallBacks
)) != NULL
)
1839 CFDictionaryAddValue(sockets
, CFSTR("BrowseSockets"), array
);
1841 if ((listener
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1842 &kCFTypeDictionaryKeyCallBacks
,
1843 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1845 CFArrayAppendValue(array
, listener
);
1847 CFDictionaryAddValue(listener
, CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY
),
1849 CFDictionaryAddValue(listener
, CFSTR(LAUNCH_JOBSOCKETKEY_TYPE
),
1852 if ((service
= getservbyport(BrowsePort
, NULL
)))
1853 value
= CFStringCreateWithCString(kCFAllocatorDefault
,
1855 kCFStringEncodingUTF8
);
1857 value
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
,
1860 CFDictionaryAddValue(listener
,
1861 CFSTR(LAUNCH_JOBSOCKETKEY_SERVICENAME
), value
);
1864 CFRelease(listener
);
1874 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1875 "launchd_sync_conf: Updating \"%s\", pid=%d\n",
1876 LaunchdConf
, (int)getpid());
1878 if ((fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
1879 (const unsigned char *)LaunchdConf
,
1880 strlen(LaunchdConf
), false)))
1882 if ((resourceData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
,
1885 if (!CFURLWriteDataAndPropertiesToResource(fileURL
, resourceData
,
1888 cupsdLogMessage(CUPSD_LOG_WARN
,
1889 "launchd_sync_conf: "
1890 "CFURLWriteDataAndPropertiesToResource(\"%s\") "
1892 LaunchdConf
, (int)errorCode
);
1895 CFRelease(resourceData
);
1901 CFRelease(cupsd_dict
);
1905 * Let the caller know we updated the file...
1910 #endif /* HAVE_LAUNCHD */
1914 * 'parent_handler()' - Catch USR1/CHLD signals...
1918 parent_handler(int sig
) /* I - Signal */
1921 * Store the signal we got from the OS and return...
1924 parent_signal
= sig
;
1929 * 'process_children()' - Process all dead children...
1933 process_children(void)
1935 int status
; /* Exit status of child */
1936 int pid
; /* Process ID of child */
1937 cupsd_job_t
*job
; /* Current job */
1938 int i
; /* Looping var */
1939 char name
[1024]; /* Process name */
1942 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "process_children()");
1945 * Reset the dead_children flag...
1951 * Collect the exit status of some children...
1955 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0)
1956 #elif defined(HAVE_WAIT3)
1957 while ((pid
= wait3(&status
, WNOHANG
, NULL
)) > 0)
1959 if ((pid
= wait(&status
)) > 0)
1960 #endif /* HAVE_WAITPID */
1963 * Ignore SIGTERM errors - that comes when a job is cancelled...
1966 cupsdFinishProcess(pid
, name
, sizeof(name
));
1968 if (status
== SIGTERM
)
1973 if (WIFEXITED(status
))
1974 cupsdLogMessage(CUPSD_LOG_ERROR
, "PID %d (%s) stopped with status %d!",
1975 pid
, name
, WEXITSTATUS(status
));
1977 cupsdLogMessage(CUPSD_LOG_ERROR
, "PID %d (%s) crashed on signal %d!",
1978 pid
, name
, WTERMSIG(status
));
1980 if (LogLevel
< CUPSD_LOG_DEBUG
)
1981 cupsdLogMessage(CUPSD_LOG_INFO
,
1982 "Hint: Try setting the LogLevel to \"debug\" to find out more.");
1985 cupsdLogMessage(CUPSD_LOG_DEBUG
, "PID %d (%s) exited with no errors.",
1989 * Delete certificates for CGI processes...
1993 cupsdDeleteCert(pid
);
1996 * Lookup the PID in the jobs list...
1999 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2001 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2002 if (job
->state
!= NULL
&&
2003 job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
2005 for (i
= 0; job
->filters
[i
]; i
++)
2006 if (job
->filters
[i
] == pid
)
2009 if (job
->filters
[i
] || job
->backend
== pid
)
2012 * OK, this process has gone away; what's left?
2015 if (job
->filters
[i
])
2016 job
->filters
[i
] = -pid
;
2018 job
->backend
= -pid
;
2020 if (status
&& job
->status
>= 0)
2023 * An error occurred; save the exit status so we know to stop
2024 * the printer or cancel the job when all of the filters finish...
2026 * A negative status indicates that the backend failed and the
2027 * printer needs to be stopped.
2030 if (job
->filters
[i
])
2031 job
->status
= status
; /* Filter failed */
2033 job
->status
= -status
; /* Backend failed */
2035 if (job
->printer
&& !(job
->printer
->type
& CUPS_PRINTER_FAX
))
2037 snprintf(job
->printer
->state_message
,
2038 sizeof(job
->printer
->state_message
), "%s failed", name
);
2039 cupsdAddPrinterHistory(job
->printer
);
2044 * If this is not the last file in a job, see if all of the
2045 * filters are done, and if so move to the next file.
2048 if (job
->current_file
< job
->num_files
)
2050 for (i
= 0; job
->filters
[i
] < 0; i
++);
2052 if (!job
->filters
[i
])
2055 * Process the next file...
2058 cupsdFinishJob(job
);
2069 * 'sigchld_handler()' - Handle 'child' signals from old processes.
2073 sigchld_handler(int sig
) /* I - Signal number */
2078 * Flag that we have dead children...
2084 * Reset the signal handler as needed...
2087 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
2088 signal(SIGCLD
, sigchld_handler
);
2089 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
2094 * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler.
2098 sighup_handler(int sig
) /* I - Signal number */
2102 NeedReload
= RELOAD_ALL
;
2103 ReloadTime
= time(NULL
);
2105 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
2106 signal(SIGHUP
, sighup_handler
);
2107 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
2112 * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler.
2116 sigterm_handler(int sig
) /* I - Signal */
2118 (void)sig
; /* remove compiler warnings... */
2121 * Flag that we should stop and return...
2129 * 'select_timeout()' - Calculate the select timeout value.
2133 static long /* O - Number of seconds */
2134 select_timeout(int fds
) /* I - Number of ready descriptors select returned */
2136 int i
; /* Looping var */
2137 long timeout
; /* Timeout for select */
2138 time_t now
; /* Current time */
2139 cupsd_client_t
*con
; /* Client information */
2140 cupsd_printer_t
*p
; /* Printer information */
2141 cupsd_job_t
*job
; /* Job information */
2142 cupsd_subscription_t
*sub
; /* Subscription information */
2143 const char *why
; /* Debugging aid */
2147 * Check to see if any of the clients have pending data to be
2148 * processed; if so, the timeout should be 0...
2151 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
2152 if (con
->http
.used
> 0)
2156 * If select has been active in the last second (fds != 0) or we have
2157 * many resources in use then don't bother trying to optimize the
2158 * timeout, just make it 1 second.
2161 if (fds
|| NumClients
> 50)
2165 * If we had a recent event notification, timeout in 1 second...
2172 * Otherwise, check all of the possible events that we need to wake for...
2176 timeout
= now
+ 86400; /* 86400 == 1 day */
2180 * Check the activity and close old clients...
2183 for (i
= NumClients
, con
= Clients
; i
> 0; i
--, con
++)
2184 if ((con
->http
.activity
+ Timeout
) < timeout
)
2186 timeout
= con
->http
.activity
+ Timeout
;
2187 why
= "timeout a client connection";
2191 * Update the browse list as needed...
2194 if (Browsing
&& BrowseLocalProtocols
)
2197 if ((BrowseLocalProtocols
& BROWSE_SLP
) && (BrowseSLPRefresh
< timeout
))
2199 timeout
= BrowseSLPRefresh
;
2200 why
= "update SLP browsing";
2202 #endif /* HAVE_LIBSLP */
2204 if (BrowseLocalProtocols
& BROWSE_CUPS
)
2206 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
2208 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
2210 if (p
->type
& CUPS_PRINTER_REMOTE
)
2212 if ((p
->browse_time
+ BrowseTimeout
) < timeout
)
2214 timeout
= p
->browse_time
+ BrowseTimeout
;
2215 why
= "browse timeout a printer";
2218 else if (!(p
->type
& CUPS_PRINTER_IMPLICIT
))
2220 if (BrowseInterval
&& (p
->browse_time
+ BrowseInterval
) < timeout
)
2222 timeout
= p
->browse_time
+ BrowseInterval
;
2223 why
= "send browse update";
2231 * Check for any active jobs...
2234 if (timeout
> (now
+ 10) && ActiveJobs
)
2236 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2238 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2239 if (job
->state
->values
[0].integer
<= IPP_JOB_PROCESSING
)
2242 why
= "process active jobs";
2247 #ifdef HAVE_MALLINFO
2249 * Log memory usage every minute...
2252 if (LogLevel
>= CUPSD_LOG_DEBUG
&& (mallinfo_time
+ 60) < timeout
)
2254 timeout
= mallinfo_time
+ 60;
2255 why
= "display memory usage";
2257 #endif /* HAVE_MALLINFO */
2260 * Expire subscriptions as needed...
2263 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
2265 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
2266 if (!sub
->job
&& sub
->expire
< timeout
)
2268 timeout
= sub
->expire
;
2269 why
= "expire subscription";
2273 * Adjust from absolute to relative time. If p->browse_time above
2274 * was 0 then we can end up with a negative value here, so check.
2275 * We add 1 second to the timeout since events occur after the
2276 * timeout expires, and limit the timeout to 86400 seconds (1 day)
2277 * to avoid select() timeout limits present on some operating
2281 timeout
= timeout
- now
+ 1;
2285 else if (timeout
> 86400)
2289 * Log and return the timeout value...
2292 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "select_timeout: %ld seconds to %s",
2300 * 'usage()' - Show scheduler usage.
2304 usage(int status
) /* O - Exit status */
2306 _cupsLangPuts(status
? stderr
: stdout
,
2307 _("Usage: cupsd [-c config-file] [-f] [-F] [-h] [-l]\n"
2309 "-c config-file Load alternate configuration file\n"
2310 "-f Run in the foreground\n"
2311 "-F Run in the foreground but detach\n"
2312 "-h Show this usage message\n"
2313 "-l Run cupsd from launchd(8)\n"));
2319 * End of "$Id: main.c 5042 2006-02-01 18:17:34Z mike $".