2 * "$Id: main.c 5157 2006-02-23 20:58:57Z 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 * org.cups.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 args */
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 */
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 */
129 time_t mallinfo_time
; /* Malloc information time */
130 size_t string_count
, /* String count */
131 alloc_bytes
, /* Allocated string bytes */
132 total_bytes
; /* Total string bytes */
133 struct timeval timeout
; /* select() timeout */
134 struct rlimit limit
; /* Runtime limit */
135 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
136 struct sigaction action
; /* Actions for POSIX signals */
137 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
139 cups_file_t
*fp
; /* Fake lpsched lock file */
140 struct stat statbuf
; /* Needed for checking lpsched FIFO */
143 int launchd_idle_exit
;
144 /* Idle exit on select timeout? */
145 #endif /* HAVE_LAUNCHD */
149 * Check for command-line arguments...
154 for (i
= 1; i
< argc
; i
++)
155 if (argv
[i
][0] == '-')
156 for (opt
= argv
[i
] + 1; *opt
!= '\0'; opt
++)
159 case 'c' : /* Configuration file */
163 _cupsLangPuts(stderr
, _("cupsd: Expected config filename "
164 "after \"-c\" option!\n"));
168 if (argv
[i
][0] == '/')
171 * Absolute directory...
174 cupsdSetString(&ConfigurationFile
, argv
[i
]);
179 * Relative directory...
182 char *current
; /* Current directory */
186 * Allocate a buffer for the current working directory to
187 * reduce run-time stack usage; this approximates the
188 * behavior of some implementations of getcwd() when they
189 * are passed a NULL pointer.
192 current
= malloc(1024);
193 getcwd(current
, 1024);
195 cupsdSetStringf(&ConfigurationFile
, "%s/%s", current
, argv
[i
]);
201 case 'f' : /* Run in foreground... */
205 case 'F' : /* Run in foreground, but disconnect from terminal... */
209 case 'h' : /* Show usage/help */
213 case 'l' : /* Started by launchd... */
218 _cupsLangPuts(stderr
, _("cupsd: launchd(8) support not compiled "
219 "in, running in normal mode.\n"));
221 #endif /* HAVE_LAUNCHD */
224 default : /* Unknown option */
225 _cupsLangPrintf(stderr
, _("cupsd: Unknown option \"%c\" - "
226 "aborting!\n"), *opt
);
232 _cupsLangPrintf(stderr
, _("cupsd: Unknown argument \"%s\" - aborting!\n"),
237 if (!ConfigurationFile
)
238 cupsdSetString(&ConfigurationFile
, CUPS_SERVERROOT
"/cupsd.conf");
241 * If the user hasn't specified "-f", run in the background...
247 * Setup signal handlers for the parent...
250 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
251 sigset(SIGUSR1
, parent_handler
);
252 sigset(SIGCHLD
, parent_handler
);
254 sigset(SIGHUP
, SIG_IGN
);
255 #elif defined(HAVE_SIGACTION)
256 memset(&action
, 0, sizeof(action
));
257 sigemptyset(&action
.sa_mask
);
258 sigaddset(&action
.sa_mask
, SIGUSR1
);
259 action
.sa_handler
= parent_handler
;
260 sigaction(SIGUSR1
, &action
, NULL
);
261 sigaction(SIGCHLD
, &action
, NULL
);
263 sigemptyset(&action
.sa_mask
);
264 action
.sa_handler
= SIG_IGN
;
265 sigaction(SIGHUP
, &action
, NULL
);
267 signal(SIGUSR1
, parent_handler
);
268 signal(SIGCLD
, parent_handler
);
270 signal(SIGHUP
, SIG_IGN
);
271 #endif /* HAVE_SIGSET */
276 * OK, wait for the child to startup and send us SIGUSR1 or to crash
277 * and the OS send us SIGCHLD... We also need to ignore SIGHUP which
278 * might be sent by the init script to restart the scheduler...
281 for (; parent_signal
== 0;)
284 if (parent_signal
== SIGUSR1
)
292 else if (WIFEXITED(i
))
294 fprintf(stderr
, "cupsd: Child exited with status %d!\n",
300 fprintf(stderr
, "cupsd: Child exited on signal %d!\n", WTERMSIG(i
));
309 * Make sure we aren't tying up any filesystems...
316 * Disable core dumps...
319 getrlimit(RLIMIT_CORE
, &limit
);
321 setrlimit(RLIMIT_CORE
, &limit
);
324 * Disconnect from the controlling terminal...
330 * Close all open files...
333 getrlimit(RLIMIT_NOFILE
, &limit
);
335 for (i
= 0; i
< limit
.rlim_cur
; i
++)
341 * Set the timezone info...
347 setlocale(LC_TIME
, "");
351 * Set the maximum number of files...
354 getrlimit(RLIMIT_NOFILE
, &limit
);
356 if (limit
.rlim_max
> CUPS_MAX_FDS
)
357 MaxFDs
= CUPS_MAX_FDS
;
359 MaxFDs
= limit
.rlim_max
;
361 limit
.rlim_cur
= MaxFDs
;
363 setrlimit(RLIMIT_NOFILE
, &limit
);
366 * Allocate memory for the input and output sets...
369 SetSize
= (MaxFDs
+ 31) / 8 + 4;
370 if (SetSize
< sizeof(fd_set
))
371 SetSize
= sizeof(fd_set
);
373 InputSet
= (fd_set
*)calloc(1, SetSize
);
374 OutputSet
= (fd_set
*)calloc(1, SetSize
);
375 input
= (fd_set
*)calloc(1, SetSize
);
376 output
= (fd_set
*)calloc(1, SetSize
);
378 if (InputSet
== NULL
|| OutputSet
== NULL
|| input
== NULL
|| output
== NULL
)
380 syslog(LOG_LPR
, "Unable to allocate memory for select() sets - exiting!");
385 * Read configuration...
388 if (!cupsdReadConfiguration())
390 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
399 * If we were started by launchd make sure the cupsd plist file contains the
400 * same listeners as cupsd.conf; If it didn't then reload it before getting
401 * the list of listening file descriptors...
404 if (launchd_sync_conf())
409 * Until rdar://3854821 is fixed we have to exit after the reload...
412 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "Exiting on launchd_reload");
418 #endif /* HAVE_LAUNCHD */
421 * Startup the server...
427 * Catch hangup and child signals and ignore broken pipes...
430 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
432 sigset(SIGHUP
, sigterm_handler
);
434 sigset(SIGHUP
, sighup_handler
);
436 sigset(SIGPIPE
, SIG_IGN
);
437 sigset(SIGTERM
, sigterm_handler
);
438 #elif defined(HAVE_SIGACTION)
439 memset(&action
, 0, sizeof(action
));
441 sigemptyset(&action
.sa_mask
);
442 sigaddset(&action
.sa_mask
, SIGHUP
);
445 action
.sa_handler
= sigterm_handler
;
447 action
.sa_handler
= sighup_handler
;
449 sigaction(SIGHUP
, &action
, NULL
);
451 sigemptyset(&action
.sa_mask
);
452 action
.sa_handler
= SIG_IGN
;
453 sigaction(SIGPIPE
, &action
, NULL
);
455 sigemptyset(&action
.sa_mask
);
456 sigaddset(&action
.sa_mask
, SIGTERM
);
457 sigaddset(&action
.sa_mask
, SIGCHLD
);
458 action
.sa_handler
= sigterm_handler
;
459 sigaction(SIGTERM
, &action
, NULL
);
462 signal(SIGHUP
, sigterm_handler
);
464 signal(SIGHUP
, sighup_handler
);
466 signal(SIGPIPE
, SIG_IGN
);
467 signal(SIGTERM
, sigterm_handler
);
468 #endif /* HAVE_SIGSET */
472 * Try to create a fake lpsched lock file if one is not already there.
473 * Some Adobe applications need it under IRIX in order to enable
477 if ((fp
= cupsFileOpen("/var/spool/lp/SCHEDLOCK", "w")) == NULL
)
479 syslog(LOG_LPR
, "Unable to create fake lpsched lock file "
480 "\"/var/spool/lp/SCHEDLOCK\"\' - %s!",
485 fchmod(cupsFileNumber(fp
), 0644);
486 fchown(cupsFileNumber(fp
), User
, Group
);
493 * Initialize authentication certificates...
499 * If we are running in the background, signal the parent process that
500 * we are up and running...
506 * Send a signal to the parent process, but only if the parent is
507 * not PID 1 (init). This avoids accidentally shutting down the
508 * system on OpenBSD if you CTRL-C the server before it is up...
511 i
= getppid(); /* Save parent PID to avoid race condition */
518 * Start power management framework...
521 cupsdStartSystemMonitor();
524 * If the administrator has configured the server to run as an unpriviledged
525 * user, change to that user now...
531 setgroups(1, &Group
);
539 cupsdCatchChildSignals();
542 * Start any pending print jobs...
552 browse_time
= time(NULL
);
553 senddoc_time
= time(NULL
);
554 expire_time
= time(NULL
);
557 while (!stop_scheduler
)
560 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
561 "main: Top of loop, dead_children=%d, NeedReload=%d",
562 dead_children
, NeedReload
);
566 * Check if there are dead children to handle...
573 * Check if we need to load the server configuration file...
579 * Close any idle clients...
582 if (cupsArrayCount(Clients
) > 0)
584 for (con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
586 con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
587 if (con
->http
.state
== HTTP_WAITING
)
588 cupsdCloseClient(con
);
590 con
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
592 cupsdPauseListening();
596 * Check for any active jobs...
599 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
601 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
602 if (job
->state_value
== IPP_JOB_PROCESSING
)
606 * Restart if all clients are closed and all jobs finished, or
607 * if the reload timeout has elapsed...
610 if ((cupsArrayCount(Clients
) == 0 &&
611 (!job
|| NeedReload
!= RELOAD_ALL
)) ||
612 (time(NULL
) - ReloadTime
) >= ReloadTimeout
)
615 * Shutdown the server...
621 * Read configuration...
624 if (!cupsdReadConfiguration())
626 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
634 if (launchd_sync_conf())
639 * Until rdar://3854821 is fixed we have to exit after the reload...
642 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "Exiting on launchd_reload");
649 #endif /* HAVE_LAUNCHD */
652 * Startup the server...
660 * Check for available input or ready output. If select() returns
661 * 0 or -1, something bad happened and we should exit immediately.
663 * Note that we at least have one listening socket open at all
667 memcpy(input
, InputSet
, SetSize
);
668 memcpy(output
, OutputSet
, SetSize
);
670 timeout
.tv_sec
= select_timeout(fds
);
675 * If no other work is scheduled and we're being controlled by
676 * launchd(8) then timeout after 'LaunchdTimeout' seconds of
680 if (timeout
.tv_sec
== 86400 && Launchd
&& LaunchdTimeout
&&
681 (!Browsing
|| !(BrowseLocalProtocols
& BROWSE_DNSSD
) ||
682 cupsArrayCount(Printers
) == 0))
684 timeout
.tv_sec
= LaunchdTimeout
;
685 launchd_idle_exit
= 1;
688 launchd_idle_exit
= 0;
689 #endif /* HAVE_LAUNCHD */
691 if (timeout
.tv_sec
< 86400) /* Only use timeout for < 1 day */
692 fds
= select(MaxFDs
, input
, output
, NULL
, &timeout
);
694 fds
= select(MaxFDs
, input
, output
, NULL
, NULL
);
698 char s
[16384], /* String buffer */
699 *sptr
; /* Pointer into buffer */
700 int slen
; /* Length of string buffer */
704 * Got an error from select!
707 if (errno
== EINTR
) /* Just interrupted by a signal */
711 * Log all sorts of debug info to help track down the problem.
714 cupsdLogMessage(CUPSD_LOG_EMERG
, "select() failed - %s!",
717 strcpy(s
, "InputSet =");
721 for (i
= 0; i
< MaxFDs
; i
++)
722 if (FD_ISSET(i
, InputSet
))
724 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
725 slen
+= strlen(sptr
);
726 sptr
+= strlen(sptr
);
729 cupsdLogMessage(CUPSD_LOG_EMERG
, s
);
731 strcpy(s
, "OutputSet =");
735 for (i
= 0; i
< MaxFDs
; i
++)
736 if (FD_ISSET(i
, OutputSet
))
738 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
739 slen
+= strlen(sptr
);
740 sptr
+= strlen(sptr
);
743 cupsdLogMessage(CUPSD_LOG_EMERG
, s
);
745 for (i
= 0, con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
747 i
++, con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
748 cupsdLogMessage(CUPSD_LOG_EMERG
,
749 "Clients[%d] = %d, file = %d, state = %d",
750 i
, con
->http
.fd
, con
->file
, con
->http
.state
);
752 for (i
= 0, lis
= (cupsd_listener_t
*)cupsArrayFirst(Listeners
);
754 i
++, lis
= (cupsd_listener_t
*)cupsArrayNext(Listeners
))
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 and unload completed jobs as needed...
845 if (current_time
> expire_time
)
847 if (cupsArrayCount(Subscriptions
) > 0)
848 cupsdExpireSubscriptions(NULL
, NULL
);
850 cupsdUnloadCompletedJobs();
852 expire_time
= current_time
;
856 * Update the browse list as needed...
859 if (Browsing
&& BrowseRemoteProtocols
)
861 if (BrowseSocket
>= 0 && FD_ISSET(BrowseSocket
, input
))
862 cupsdUpdateCUPSBrowse();
864 if (PollPipe
>= 0 && FD_ISSET(PollPipe
, input
))
865 cupsdUpdatePolling();
868 if ((BrowseRemoteProtocols
& BROWSE_SLP
) &&
869 BrowseSLPRefresh
<= current_time
)
870 cupsdUpdateSLPBrowse();
871 #endif /* HAVE_LIBSLP */
874 if ((BrowseRemoteProtocols
& BROWSE_LDAP
) &&
875 BrowseLDAPRefresh
<= current_time
)
876 cupsdUpdateLDAPBrowse();
877 #endif /* HAVE_LDAP */
880 if (Browsing
&& BrowseLocalProtocols
&& current_time
> browse_time
)
882 cupsdSendBrowseList();
883 browse_time
= current_time
;
887 * Check for new connections on the "listen" sockets...
890 for (lis
= (cupsd_listener_t
*)cupsArrayFirst(Listeners
);
892 lis
= (cupsd_listener_t
*)cupsArrayNext(Listeners
))
893 if (lis
->fd
>= 0 && FD_ISSET(lis
->fd
, input
))
895 FD_CLR(lis
->fd
, input
);
896 cupsdAcceptClient(lis
);
900 * Check for new data on the client sockets...
903 for (con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
905 con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
908 * Process the input buffer...
911 if (FD_ISSET(con
->http
.fd
, input
) || con
->http
.used
)
916 FD_CLR(con
->http
.fd
, input
);
918 if (!cupsdReadClient(con
))
928 * Write data as needed...
931 if (con
->pipe_pid
&& FD_ISSET(con
->file
, input
))
934 * Keep track of pending input from the file/pipe separately
935 * so that we don't needlessly spin on select() when the web
936 * client is not ready to receive data...
939 FD_CLR(con
->file
, input
);
943 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "main: Data ready file %d!",
947 if (!FD_ISSET(con
->http
.fd
, output
))
949 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
950 "main: Removing fd %d from InputSet...", con
->file
);
951 FD_CLR(con
->file
, input
);
952 FD_CLR(con
->file
, InputSet
);
956 if (FD_ISSET(con
->http
.fd
, output
))
958 FD_CLR(con
->http
.fd
, output
);
960 if (!con
->pipe_pid
|| con
->file_ready
)
961 if (!cupsdWriteClient(con
))
966 * Check the activity and close old clients...
969 activity
= current_time
- Timeout
;
970 if (con
->http
.activity
< activity
&& !con
->pipe_pid
)
972 cupsdLogMessage(CUPSD_LOG_DEBUG
,
973 "Closing client %d after %d seconds of inactivity...",
974 con
->http
.fd
, Timeout
);
976 cupsdCloseClient(con
);
982 * Update any pending multi-file documents...
985 if ((current_time
- senddoc_time
) >= 10)
988 senddoc_time
= current_time
;
992 * Log memory usage every minute...
995 if ((current_time
- mallinfo_time
) >= 60 && LogLevel
>= CUPSD_LOG_DEBUG2
)
998 struct mallinfo mem
; /* Malloc information */
1002 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1003 "mallinfo: arena = %d, used = %d, free = %d\n",
1004 mem
.arena
, mem
.usmblks
+ mem
.uordblks
,
1005 mem
.fsmblks
+ mem
.fordblks
);
1006 #endif /* HAVE_MALLINFO */
1008 string_count
= _cups_sp_statistics(&alloc_bytes
, &total_bytes
);
1009 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1010 "stringpool: " CUPS_LLFMT
" strings, "
1011 CUPS_LLFMT
" allocated, " CUPS_LLFMT
" total bytes",
1012 CUPS_LLCAST string_count
, CUPS_LLCAST alloc_bytes
,
1013 CUPS_LLCAST total_bytes
);
1015 mallinfo_time
= current_time
;
1019 * Update the root certificate once every 5 minutes...
1022 if ((current_time
- RootCertTime
) >= RootCertDuration
&& RootCertDuration
&&
1026 * Update the root certificate...
1030 cupsdAddCert(0, "root");
1034 * Handle OS-specific event notification for any events that have
1035 * accumulated. Don't send these more than once a second...
1038 if (LastEvent
&& (time(NULL
) - LastEventTime
) > 1)
1040 #ifdef HAVE_NOTIFY_POST
1041 if (LastEvent
& CUPSD_EVENT_PRINTER_CHANGED
)
1043 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1044 "notify_post(\"com.apple.printerListChange\")");
1045 notify_post("com.apple.printerListChange");
1048 if (LastEvent
& CUPSD_EVENT_PRINTER_STATE_CHANGED
)
1050 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1051 "notify_post(\"com.apple.printerHistoryChange\")");
1052 notify_post("com.apple.printerHistoryChange");
1055 if (LastEvent
& (CUPSD_EVENT_JOB_STATE_CHANGED
|
1056 CUPSD_EVENT_JOB_CONFIG_CHANGED
|
1057 CUPSD_EVENT_JOB_PROGRESS
))
1059 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1060 "notify_post(\"com.apple.jobChange\")");
1061 notify_post("com.apple.jobChange");
1063 #endif /* HAVE_NOTIFY_POST */
1066 * Reset the accumulated events and notification time...
1069 LastEventTime
= time(NULL
);
1070 LastEvent
= CUPSD_EVENT_NONE
;
1075 * Log a message based on what happened...
1079 cupsdLogMessage(CUPSD_LOG_INFO
, "Scheduler shutting down normally.");
1081 cupsdLogMessage(CUPSD_LOG_ERROR
,
1082 "Scheduler shutting down due to program error.");
1085 * Close all network clients and stop all jobs...
1092 cupsdStopSystemMonitor();
1096 * Update the launchd config file as needed...
1099 launchd_sync_conf();
1100 #endif /* HAVE_LAUNCHD */
1104 * Remove the fake IRIX lpsched lock file, but only if the existing
1105 * file is not a FIFO which indicates that the real IRIX lpsched is
1109 if (!stat("/var/spool/lp/FIFO", &statbuf
))
1110 if (!S_ISFIFO(statbuf
.st_mode
))
1111 unlink("/var/spool/lp/SCHEDLOCK");
1115 * Free memory used by FD sets and return...
1123 return (!stop_scheduler
);
1128 * 'cupsdClosePipe()' - Close a pipe as necessary.
1132 cupsdClosePipe(int *fds
) /* I - Pipe file descriptors (2) */
1135 * Close file descriptors as needed...
1153 * 'cupsdOpenPipe()' - Create a pipe which is closed on exec.
1156 int /* O - 0 on success, -1 on error */
1157 cupsdOpenPipe(int *fds
) /* O - Pipe file descriptors (2) */
1160 * Create the pipe...
1167 * Set the "close on exec" flag on each end of the pipe...
1170 if (fcntl(fds
[0], F_SETFD
, fcntl(fds
[0], F_GETFD
) | FD_CLOEXEC
))
1177 if (fcntl(fds
[1], F_SETFD
, fcntl(fds
[1], F_GETFD
) | FD_CLOEXEC
))
1185 * Return 0 indicating success...
1193 * 'cupsdCatchChildSignals()' - Catch SIGCHLD signals...
1197 cupsdCatchChildSignals(void)
1199 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1200 struct sigaction action
; /* Actions for POSIX signals */
1201 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1204 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1205 sigset(SIGCHLD
, sigchld_handler
);
1206 #elif defined(HAVE_SIGACTION)
1207 memset(&action
, 0, sizeof(action
));
1209 sigemptyset(&action
.sa_mask
);
1210 sigaddset(&action
.sa_mask
, SIGTERM
);
1211 sigaddset(&action
.sa_mask
, SIGCHLD
);
1212 action
.sa_handler
= sigchld_handler
;
1213 sigaction(SIGCHLD
, &action
, NULL
);
1215 signal(SIGCLD
, sigchld_handler
); /* No, SIGCLD isn't a typo... */
1216 #endif /* HAVE_SIGSET */
1221 * 'cupsdClearString()' - Clear a string.
1225 cupsdClearString(char **s
) /* O - String value */
1236 * 'cupsdHoldSignals()' - Hold child and termination signals.
1240 cupsdHoldSignals(void)
1242 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1243 sigset_t newmask
; /* New POSIX signal mask */
1244 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1254 #elif defined(HAVE_SIGACTION)
1255 sigemptyset(&newmask
);
1256 sigaddset(&newmask
, SIGTERM
);
1257 sigaddset(&newmask
, SIGCHLD
);
1258 sigprocmask(SIG_BLOCK
, &newmask
, &holdmask
);
1259 #endif /* HAVE_SIGSET */
1264 * 'cupsdIgnoreChildSignals()' - Ignore SIGCHLD signals...
1266 * We don't really ignore them, we set the signal handler to SIG_DFL,
1267 * since some OS's rely on signals for the wait4() function to work.
1271 cupsdIgnoreChildSignals(void)
1273 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1274 struct sigaction action
; /* Actions for POSIX signals */
1275 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1278 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1279 sigset(SIGCHLD
, SIG_DFL
);
1280 #elif defined(HAVE_SIGACTION)
1281 memset(&action
, 0, sizeof(action
));
1283 sigemptyset(&action
.sa_mask
);
1284 sigaddset(&action
.sa_mask
, SIGCHLD
);
1285 action
.sa_handler
= SIG_DFL
;
1286 sigaction(SIGCHLD
, &action
, NULL
);
1288 signal(SIGCLD
, SIG_DFL
); /* No, SIGCLD isn't a typo... */
1289 #endif /* HAVE_SIGSET */
1294 * 'cupsdReleaseSignals()' - Release signals for delivery.
1298 cupsdReleaseSignals(void)
1307 #elif defined(HAVE_SIGACTION)
1308 sigprocmask(SIG_SETMASK
, &holdmask
, NULL
);
1309 #endif /* HAVE_SIGSET */
1314 * 'cupsdSetString()' - Set a string value.
1318 cupsdSetString(char **s
, /* O - New string */
1319 const char *v
) /* I - String value */
1335 * 'cupsdSetStringf()' - Set a formatted string value.
1339 cupsdSetStringf(char **s
, /* O - New string */
1340 const char *f
, /* I - Printf-style format string */
1341 ...) /* I - Additional args as needed */
1343 char v
[4096]; /* Formatting string value */
1344 va_list ap
; /* Argument pointer */
1345 char *olds
; /* Old string */
1356 vsnprintf(v
, sizeof(v
), f
, ap
);
1371 * 'launchd_checkin()' - Check-in with launchd and collect the listening fds.
1375 launchd_checkin(void)
1377 int i
, /* Looping var */
1378 count
, /* Numebr of listeners */
1379 portnum
; /* Port number */
1380 launch_data_t ld_msg
, /* Launch data message */
1381 ld_resp
, /* Launch data response */
1382 ld_array
, /* Launch data array */
1383 ld_sockets
, /* Launch data sockets dictionary */
1384 ld_runatload
, /* Run-at-load setting */
1385 tmp
; /* Launch data */
1386 cupsd_listener_t
*lis
; /* Listeners array */
1387 http_addr_t addr
; /* Address variable */
1388 socklen_t addrlen
; /* Length of address */
1389 bool runatload
; /* Run-at-load setting value */
1392 cupsdLogMessage(CUPSD_LOG_DEBUG
, "launchd_checkin: pid=%d", (int)getpid());
1395 * Check-in with launchd...
1398 ld_msg
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
1399 if ((ld_resp
= launch_msg(ld_msg
)) == NULL
)
1401 cupsdLogMessage(CUPSD_LOG_ERROR
,
1402 "launchd_checkin: launch_msg(\"" LAUNCH_KEY_CHECKIN
1407 if (launch_data_get_type(ld_resp
) == LAUNCH_DATA_ERRNO
)
1409 errno
= launch_data_get_errno(ld_resp
);
1410 cupsdLogMessage(CUPSD_LOG_ERROR
, "launchd_checkin: Check-in failed: %s",
1416 * Get the "run-at-load" setting...
1420 launch_data_dict_lookup(ld_resp
, LAUNCH_JOBKEY_RUNATLOAD
)) != NULL
&&
1421 launch_data_get_type(ld_runatload
) == LAUNCH_DATA_BOOL
)
1422 runatload
= launch_data_get_bool(ld_runatload
);
1425 errno
= launch_data_get_errno(ld_resp
);
1426 cupsdLogMessage(CUPSD_LOG_ERROR
,
1427 "launchd_checkin: Unable to find Run-at-load setting: %s",
1432 cupsdLogMessage(CUPSD_LOG_DEBUG
, "launchd_checkin: Run-at-load=%s",
1433 runatload
? "true" : "false");
1436 * Get the sockets dictionary...
1439 if (!(ld_sockets
= launch_data_dict_lookup(ld_resp
, LAUNCH_JOBKEY_SOCKETS
)))
1441 cupsdLogMessage(CUPSD_LOG_ERROR
,
1442 "launchd_checkin: No sockets found to answer requests on!");
1447 * Get the array of listener sockets...
1450 if (!(ld_array
= launch_data_dict_lookup(ld_sockets
, "Listeners")))
1452 cupsdLogMessage(CUPSD_LOG_ERROR
,
1453 "launchd_checkin: No sockets found to answer requests on!");
1458 * Add listening fd(s) to the Listener array...
1461 if (launch_data_get_type(ld_array
) == LAUNCH_DATA_ARRAY
)
1464 * Free the listeners array built from cupsd.conf...
1467 cupsdDeleteAllListeners();
1470 * Create a new array of listeners from the launchd data...
1473 Listeners
= cupsArrayNew(NULL
, NULL
);
1474 count
= launch_data_array_get_count(ld_array
);
1476 for (i
= 0; i
< count
; i
++)
1479 * Copy the current address and log it...
1482 if ((lis
= calloc(1, sizeof(cupsd_listener_t
))) == NULL
)
1484 cupsdLogMessage(CUPSD_LOG_ERROR
,
1485 "launchd_checkin: Unable to allocate listener - %s.",
1490 cupsArrayAdd(Listeners
, lis
);
1492 tmp
= launch_data_array_get_index(ld_array
, i
);
1493 lis
->fd
= launch_data_get_fd(tmp
);
1494 addrlen
= sizeof(lis
->address
);
1496 if (getsockname(lis
->fd
, (struct sockaddr
*)&(lis
->address
), &addrlen
))
1498 cupsdLogMessage(CUPSD_LOG_ERROR
,
1499 "launchd_checkin: Unable to get local address - %s",
1507 if (addr
.addr
.sa_family
== AF_INET6
)
1508 portnum
= ntohs(addr
.ipv6
.sin6_port
);
1510 # endif /* AF_INET6 */
1511 if (addr
.addr
.sa_family
== AF_INET
)
1512 portnum
= ntohs(addr
.ipv4
.sin_port
);
1515 lis
->encryption
= HTTP_ENCRYPT_ALWAYS
;
1516 # endif /* HAVE_SSL */
1521 * Collect the browse socket (if there is one)...
1524 if ((ld_array
= launch_data_dict_lookup(ld_sockets
, "BrowseSockets")))
1526 if (launch_data_get_type(ld_array
) == LAUNCH_DATA_ARRAY
)
1528 tmp
= launch_data_array_get_index(ld_array
, 0);
1530 if (launch_data_get_type(tmp
) == LAUNCH_DATA_FD
)
1532 if (BrowseSocket
!= -1)
1533 close(BrowseSocket
);
1535 BrowseSocket
= launch_data_get_fd(tmp
);
1538 cupsdLogMessage(CUPSD_LOG_WARN
,
1539 "launchd_checkin: BrowseSocket not a fd!");
1542 cupsdLogMessage(CUPSD_LOG_WARN
,
1543 "launchd_checkin: BrowseSockets is not an array!");
1546 cupsdLogMessage(CUPSD_LOG_DEBUG
, "launchd_checkin: No BrowseSockets");
1548 launch_data_free(ld_msg
);
1549 launch_data_free(ld_resp
);
1554 * 'launchd_reload()' - Tell launchd to reload the configuration file to pick
1555 * up the new listening directives.
1559 launchd_reload(void)
1561 int child_status
; /* Exit status of child process */
1562 pid_t child_pid
, /* Child PID */
1563 waitpid_status
; /* Child process exit status */
1564 char *argv
[4]; /* Argument strings */
1568 * The current launchd doesn't support a reload option (rdar://3854821).
1569 * Until this is fixed we need to reload the config file by execing launchctl
1570 * twice (to unload then load). NOTE: This will cause us to exit on SIGTERM
1571 * which will cancel all client & job activity.
1573 * After this is fixed we'll be able to tell launchd to reload the file
1574 * and pick up the new listening descriptors without disrupting current
1579 * Unloading the current configuration will cause launchd to send us a SIGTERM;
1580 * block it for now so we can get our work done...
1586 * Set up the unload arguments to launchctl...
1589 argv
[0] = "/bin/launchctl";
1591 argv
[2] = LaunchdConf
;
1594 if (cupsdStartProcess(argv
[0], argv
, NULL
, -1, -1, -1, -1, 1, &child_pid
) < 0)
1595 cupsdLogMessage(CUPSD_LOG_ERROR
,
1596 "launchd_reload: Unable to execute %s - %s", argv
[0],
1602 waitpid_status
= waitpid(child_pid
, &child_status
, 0);
1604 while (waitpid_status
== (pid_t
)-1 && errno
== EINTR
);
1606 if (WIFSIGNALED(child_status
))
1607 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1608 "launchd_reload: %s pid %d crashed on signal %d!",
1609 basename(argv
[0]), child_pid
, WTERMSIG(child_status
));
1611 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1612 "launchd_reload: %s pid %d stopped with status %d!",
1613 basename(argv
[0]), child_pid
, WEXITSTATUS(child_status
));
1616 * Do it again with the load command...
1621 if (cupsdStartProcess(argv
[0], argv
, NULL
, -1, -1, -1, -1, 1,
1624 cupsdLogMessage(CUPSD_LOG_ERROR
,
1625 "launchd_reload: Unable to fork for %s - %s", argv
[0],
1632 waitpid_status
= waitpid(child_pid
, &child_status
, 0);
1633 } while (waitpid_status
== (pid_t
)-1 && errno
== EINTR
);
1635 if (WIFSIGNALED(child_status
))
1636 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1637 "launchd_reload: %s pid %d crashed on signal %d!",
1638 basename(argv
[0]), child_pid
, WTERMSIG(child_status
));
1640 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1641 "launchd_reload: %s pid %d stopped with status %d",
1642 basename(argv
[0]), child_pid
,
1643 WEXITSTATUS(child_status
));
1648 * Leave signals blocked since exit() will be called momentarily anyways...
1654 * 'launchd_sync_conf()' - Re-write the launchd(8) config file
1655 * org.cups.cupsd.plist based on cupsd.conf.
1658 static int /* O - 1 if the file was updated */
1659 launchd_sync_conf(void)
1661 int portnum
; /* Port number */
1662 CFMutableDictionaryRef cupsd_dict
, /* org.cups.cupsd.plist dictionary */
1663 sockets
, /* Sockets dictionary */
1664 listener
; /* Listener dictionary */
1665 CFDataRef resourceData
; /* XML property list */
1666 CFMutableArrayRef array
; /* Array */
1667 CFNumberRef socket_mode
; /* Domain socket mode bits */
1668 CFStringRef socket_path
; /* Domain socket path */
1669 CFTypeRef value
; /* CF value */
1670 CFURLRef fileURL
; /* File URL */
1671 SInt32 errorCode
; /* Error code */
1672 cupsd_listener_t
*lis
; /* Current listening socket */
1673 struct servent
*service
; /* Services data base entry */
1674 char temp
[1024]; /* Temporary buffer for value */
1675 struct stat cupsd_sb
, /* File info for cupsd.conf */
1676 launchd_sb
; /* File info for org.cups.cupsd.plist */
1680 * If the launchd conf file modification time is newer than the cupsd.conf
1681 * time then there's nothing to do...
1684 if (!stat(ConfigurationFile
, &cupsd_sb
) &&
1685 !stat(LaunchdConf
, &launchd_sb
) &&
1686 launchd_sb
.st_mtimespec
.tv_sec
>= cupsd_sb
.st_mtimespec
.tv_sec
)
1688 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1689 "launchd_sync_conf: Nothing to do, pid=%d.",
1695 * Time to write a new 'org.cups.cupsd.plist' file.
1696 * Create the new dictionary and populate it with values...
1699 if ((cupsd_dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1700 &kCFTypeDictionaryKeyCallBacks
,
1701 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1703 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_LABEL
),
1704 CFSTR("org.cups.cupsd"));
1705 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_ONDEMAND
),
1708 if ((Browsing
&& BrowseLocalProtocols
&& cupsArrayCount(Printers
)) ||
1709 cupsArrayCount(ActiveJobs
))
1710 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_RUNATLOAD
),
1713 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_RUNATLOAD
),
1716 #ifdef LAUNCH_JOBKEY_SERVICEIPC
1717 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_SERVICEIPC
),
1719 #endif /* LAUNCH_JOBKEY_SERVICEIPC */
1721 if ((array
= CFArrayCreateMutable(kCFAllocatorDefault
, 2,
1722 &kCFTypeArrayCallBacks
)) != NULL
)
1724 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS
),
1726 CFArrayAppendValue(array
, CFSTR("/usr/sbin/cupsd"));
1727 CFArrayAppendValue(array
, CFSTR("-l"));
1732 * Add a sockets dictionary...
1735 if ((sockets
= (CFMutableDictionaryRef
)CFDictionaryCreateMutable(
1736 kCFAllocatorDefault
, 0,
1737 &kCFTypeDictionaryKeyCallBacks
,
1738 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1740 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_SOCKETS
), sockets
);
1743 * Add a Listeners array to the sockets dictionary...
1746 if ((array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1747 &kCFTypeArrayCallBacks
)) != NULL
)
1749 CFDictionaryAddValue(sockets
, CFSTR("Listeners"), array
);
1752 * For each listener add a dictionary to the listeners array...
1755 for (lis
= (cupsd_listener_t
*)cupsArrayFirst(Listeners
);
1757 lis
= (cupsd_listener_t
*)cupsArrayNext(Listeners
))
1759 if ((listener
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1760 &kCFTypeDictionaryKeyCallBacks
,
1761 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1763 CFArrayAppendValue(array
, listener
);
1766 if (lis
->address
.addr
.sa_family
== AF_LOCAL
)
1768 if ((socket_path
= CFStringCreateWithCString(kCFAllocatorDefault
,
1769 lis
->address
.un
.sun_path
,
1770 kCFStringEncodingUTF8
)))
1772 CFDictionaryAddValue(listener
,
1773 CFSTR(LAUNCH_JOBSOCKETKEY_PATHNAME
),
1775 CFRelease(socket_path
);
1777 portnum
= 0140777; /* (S_IFSOCK|S_IRWXU|S_IRWXG|S_IRWXO) or *
1779 if ((socket_mode
= CFNumberCreate(kCFAllocatorDefault
,
1780 kCFNumberIntType
, &portnum
)))
1782 CFDictionaryAddValue(listener
, CFSTR("SockPathMode"),
1784 CFRelease(socket_mode
);
1788 # endif /* AF_LOCAL */
1791 if (lis
->address
.addr
.sa_family
== AF_INET6
)
1793 CFDictionaryAddValue(listener
,
1794 CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY
),
1796 portnum
= lis
->address
.ipv6
.sin6_port
;
1799 # endif /* AF_INET6 */
1801 CFDictionaryAddValue(listener
,
1802 CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY
),
1804 portnum
= lis
->address
.ipv4
.sin_port
;
1807 if ((service
= getservbyport(portnum
, NULL
)))
1808 value
= CFStringCreateWithCString(kCFAllocatorDefault
,
1810 kCFStringEncodingUTF8
);
1812 value
= CFNumberCreate(kCFAllocatorDefault
,
1813 kCFNumberIntType
, &portnum
);
1817 CFDictionaryAddValue(listener
,
1818 CFSTR(LAUNCH_JOBSOCKETKEY_SERVICENAME
),
1823 httpAddrString(&lis
->address
, temp
, sizeof(temp
));
1824 if ((value
= CFStringCreateWithCString(kCFAllocatorDefault
, temp
,
1825 kCFStringEncodingUTF8
)))
1827 CFDictionaryAddValue(listener
,
1828 CFSTR(LAUNCH_JOBSOCKETKEY_NODENAME
),
1834 CFRelease(listener
);
1842 * Add the BrowseSocket to the sockets dictionary...
1845 if (Browsing
&& (BrowseRemoteProtocols
& BROWSE_CUPS
))
1847 if ((array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1848 &kCFTypeArrayCallBacks
)) != NULL
)
1850 CFDictionaryAddValue(sockets
, CFSTR("BrowseSockets"), array
);
1852 if ((listener
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1853 &kCFTypeDictionaryKeyCallBacks
,
1854 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1856 CFArrayAppendValue(array
, listener
);
1858 CFDictionaryAddValue(listener
, CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY
),
1860 CFDictionaryAddValue(listener
, CFSTR(LAUNCH_JOBSOCKETKEY_TYPE
),
1863 if ((service
= getservbyport(BrowsePort
, NULL
)))
1864 value
= CFStringCreateWithCString(kCFAllocatorDefault
,
1866 kCFStringEncodingUTF8
);
1868 value
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
,
1871 CFDictionaryAddValue(listener
,
1872 CFSTR(LAUNCH_JOBSOCKETKEY_SERVICENAME
), value
);
1875 CFRelease(listener
);
1885 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1886 "launchd_sync_conf: Updating \"%s\", pid=%d\n",
1887 LaunchdConf
, (int)getpid());
1889 if ((fileURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
1890 (const unsigned char *)LaunchdConf
,
1891 strlen(LaunchdConf
), false)))
1893 if ((resourceData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
,
1896 if (!CFURLWriteDataAndPropertiesToResource(fileURL
, resourceData
,
1899 cupsdLogMessage(CUPSD_LOG_WARN
,
1900 "launchd_sync_conf: "
1901 "CFURLWriteDataAndPropertiesToResource(\"%s\") "
1903 LaunchdConf
, (int)errorCode
);
1906 CFRelease(resourceData
);
1912 CFRelease(cupsd_dict
);
1916 * Let the caller know we updated the file...
1921 #endif /* HAVE_LAUNCHD */
1925 * 'parent_handler()' - Catch USR1/CHLD signals...
1929 parent_handler(int sig
) /* I - Signal */
1932 * Store the signal we got from the OS and return...
1935 parent_signal
= sig
;
1940 * 'process_children()' - Process all dead children...
1944 process_children(void)
1946 int status
; /* Exit status of child */
1947 int pid
; /* Process ID of child */
1948 cupsd_job_t
*job
; /* Current job */
1949 int i
; /* Looping var */
1950 char name
[1024]; /* Process name */
1953 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "process_children()");
1956 * Reset the dead_children flag...
1962 * Collect the exit status of some children...
1966 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0)
1967 #elif defined(HAVE_WAIT3)
1968 while ((pid
= wait3(&status
, WNOHANG
, NULL
)) > 0)
1970 if ((pid
= wait(&status
)) > 0)
1971 #endif /* HAVE_WAITPID */
1974 * Ignore SIGTERM errors - that comes when a job is cancelled...
1977 cupsdFinishProcess(pid
, name
, sizeof(name
));
1979 if (status
== SIGTERM
)
1984 if (WIFEXITED(status
))
1985 cupsdLogMessage(CUPSD_LOG_ERROR
, "PID %d (%s) stopped with status %d!",
1986 pid
, name
, WEXITSTATUS(status
));
1988 cupsdLogMessage(CUPSD_LOG_ERROR
, "PID %d (%s) crashed on signal %d!",
1989 pid
, name
, WTERMSIG(status
));
1991 if (LogLevel
< CUPSD_LOG_DEBUG
)
1992 cupsdLogMessage(CUPSD_LOG_INFO
,
1993 "Hint: Try setting the LogLevel to \"debug\" to find "
1997 cupsdLogMessage(CUPSD_LOG_DEBUG
, "PID %d (%s) exited with no errors.",
2001 * Delete certificates for CGI processes...
2005 cupsdDeleteCert(pid
);
2008 * Lookup the PID in the jobs list...
2011 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2013 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2014 if (job
->state_value
== IPP_JOB_PROCESSING
)
2016 for (i
= 0; job
->filters
[i
]; i
++)
2017 if (job
->filters
[i
] == pid
)
2020 if (job
->filters
[i
] || job
->backend
== pid
)
2023 * OK, this process has gone away; what's left?
2026 if (job
->filters
[i
])
2027 job
->filters
[i
] = -pid
;
2029 job
->backend
= -pid
;
2031 if (status
&& job
->status
>= 0)
2034 * An error occurred; save the exit status so we know to stop
2035 * the printer or cancel the job when all of the filters finish...
2037 * A negative status indicates that the backend failed and the
2038 * printer needs to be stopped.
2041 if (job
->filters
[i
])
2042 job
->status
= status
; /* Filter failed */
2044 job
->status
= -status
; /* Backend failed */
2046 if (job
->printer
&& !(job
->printer
->type
& CUPS_PRINTER_FAX
))
2048 snprintf(job
->printer
->state_message
,
2049 sizeof(job
->printer
->state_message
), "%s failed", name
);
2050 cupsdAddPrinterHistory(job
->printer
);
2055 * If this is not the last file in a job, see if all of the
2056 * filters are done, and if so move to the next file.
2059 if (job
->current_file
< job
->num_files
)
2061 for (i
= 0; job
->filters
[i
] < 0; i
++);
2063 if (!job
->filters
[i
])
2066 * Process the next file...
2069 cupsdFinishJob(job
);
2080 * 'sigchld_handler()' - Handle 'child' signals from old processes.
2084 sigchld_handler(int sig
) /* I - Signal number */
2089 * Flag that we have dead children...
2095 * Reset the signal handler as needed...
2098 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
2099 signal(SIGCLD
, sigchld_handler
);
2100 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
2105 * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler.
2109 sighup_handler(int sig
) /* I - Signal number */
2113 NeedReload
= RELOAD_ALL
;
2114 ReloadTime
= time(NULL
);
2116 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
2117 signal(SIGHUP
, sighup_handler
);
2118 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
2123 * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler.
2127 sigterm_handler(int sig
) /* I - Signal */
2129 (void)sig
; /* remove compiler warnings... */
2132 * Flag that we should stop and return...
2140 * 'select_timeout()' - Calculate the select timeout value.
2144 static long /* O - Number of seconds */
2145 select_timeout(int fds
) /* I - Number of descriptors returned */
2147 long timeout
; /* Timeout for select */
2148 time_t now
; /* Current time */
2149 cupsd_client_t
*con
; /* Client information */
2150 cupsd_printer_t
*p
; /* Printer information */
2151 cupsd_job_t
*job
; /* Job information */
2152 cupsd_subscription_t
*sub
; /* Subscription information */
2153 const char *why
; /* Debugging aid */
2157 * Check to see if any of the clients have pending data to be
2158 * processed; if so, the timeout should be 0...
2161 for (con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
2163 con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
2164 if (con
->http
.used
> 0)
2168 * If select has been active in the last second (fds != 0) or we have
2169 * many resources in use then don't bother trying to optimize the
2170 * timeout, just make it 1 second.
2173 if (fds
|| cupsArrayCount(Clients
) > 50)
2177 * If we had a recent event notification, timeout in 1 second...
2184 * Otherwise, check all of the possible events that we need to wake for...
2188 timeout
= now
+ 86400; /* 86400 == 1 day */
2192 * Check the activity and close old clients...
2195 for (con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
2197 con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
2198 if ((con
->http
.activity
+ Timeout
) < timeout
)
2200 timeout
= con
->http
.activity
+ Timeout
;
2201 why
= "timeout a client connection";
2205 * Update the browse list as needed...
2208 if (Browsing
&& BrowseLocalProtocols
)
2211 if ((BrowseLocalProtocols
& BROWSE_SLP
) && (BrowseSLPRefresh
< timeout
))
2213 timeout
= BrowseSLPRefresh
;
2214 why
= "update SLP browsing";
2216 #endif /* HAVE_LIBSLP */
2219 if ((BrowseLocalProtocols
& BROWSE_LDAP
) && (BrowseLDAPRefresh
< timeout
))
2221 timeout
= BrowseLDAPRefresh
;
2222 why
= "update LDAP browsing";
2224 #endif /* HAVE_LDAP */
2226 if (BrowseLocalProtocols
& BROWSE_CUPS
)
2228 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
2230 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
2232 if (p
->type
& CUPS_PRINTER_REMOTE
)
2234 if ((p
->browse_time
+ BrowseTimeout
) < timeout
)
2236 timeout
= p
->browse_time
+ BrowseTimeout
;
2237 why
= "browse timeout a printer";
2240 else if (!(p
->type
& CUPS_PRINTER_IMPLICIT
))
2242 if (BrowseInterval
&& (p
->browse_time
+ BrowseInterval
) < timeout
)
2244 timeout
= p
->browse_time
+ BrowseInterval
;
2245 why
= "send browse update";
2253 * Check for any active jobs...
2256 if (timeout
> (now
+ 10) && ActiveJobs
)
2258 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2260 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2261 if (job
->state_value
<= IPP_JOB_PROCESSING
)
2264 why
= "process active jobs";
2269 #ifdef HAVE_MALLINFO
2271 * Log memory usage every minute...
2274 if (LogLevel
>= CUPSD_LOG_DEBUG
&& (mallinfo_time
+ 60) < timeout
)
2276 timeout
= mallinfo_time
+ 60;
2277 why
= "display memory usage";
2279 #endif /* HAVE_MALLINFO */
2282 * Expire subscriptions as needed...
2285 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
2287 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
2288 if (!sub
->job
&& sub
->expire
&& sub
->expire
< timeout
)
2290 timeout
= sub
->expire
;
2291 why
= "expire subscription";
2295 * Adjust from absolute to relative time. If p->browse_time above
2296 * was 0 then we can end up with a negative value here, so check.
2297 * We add 1 second to the timeout since events occur after the
2298 * timeout expires, and limit the timeout to 86400 seconds (1 day)
2299 * to avoid select() timeout limits present on some operating
2303 timeout
= timeout
- now
+ 1;
2307 else if (timeout
> 86400)
2311 * Log and return the timeout value...
2314 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "select_timeout: %ld seconds to %s",
2322 * 'usage()' - Show scheduler usage.
2326 usage(int status
) /* O - Exit status */
2328 _cupsLangPuts(status
? stderr
: stdout
,
2329 _("Usage: cupsd [-c config-file] [-f] [-F] [-h] [-l]\n"
2331 "-c config-file Load alternate configuration file\n"
2332 "-f Run in the foreground\n"
2333 "-F Run in the foreground but detach\n"
2334 "-h Show this usage message\n"
2335 "-l Run cupsd from launchd(8)\n"));
2341 * End of "$Id: main.c 5157 2006-02-23 20:58:57Z mike $".