2 * "$Id: main.c 6090 2006-11-14 16:35:27Z 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 * cupsdHoldSignals() - Hold child and termination signals.
30 * cupsdReleaseSignals() - Release signals for delivery.
31 * cupsdSetString() - Set a string value.
32 * cupsdSetStringf() - Set a formatted string value.
33 * launchd_checkin() - Check-in with launchd and collect the
35 * launchd_create_dict() - Create a dictionary representing the launchd
36 * config file org.cups.cupsd.plist.
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 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 CFDictionaryRef
launchd_create_dict(void);
82 static void launchd_reload(void);
83 static int launchd_sync_conf(void);
84 #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;
99 /* Set to signal number from child */
100 static int holdcount
= 0; /* Number of times "hold" was called */
101 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
102 static sigset_t holdmask
; /* Old POSIX signal mask */
103 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
104 static int dead_children
= 0;
106 static int stop_scheduler
= 0;
107 /* Should the scheduler stop? */
110 static CFURLRef launchd_conf_url
= NULL
;
111 /* org.cups.cupsd.plist url */
112 static CFDictionaryRef launchd_conf_dict
= NULL
;
113 /* org.cups.cupsd.plist dict */
114 #endif /* HAVE_LAUNCHD */
118 * 'main()' - Main entry for the CUPS scheduler.
121 int /* O - Exit status */
122 main(int argc
, /* I - Number of command-line args */
123 char *argv
[]) /* I - Command-line arguments */
125 int i
; /* Looping var */
126 char *opt
; /* Option character */
127 int fg
; /* Run in the foreground */
128 int fds
; /* Number of ready descriptors */
129 fd_set
*input
, /* Input set for select() */
130 *output
; /* Output set for select() */
131 cupsd_client_t
*con
; /* Current client */
132 cupsd_job_t
*job
; /* Current job */
133 cupsd_listener_t
*lis
; /* Current listener */
134 time_t current_time
, /* Current time */
135 activity
, /* Client activity timer */
136 browse_time
, /* Next browse send time */
137 senddoc_time
, /* Send-Document time */
138 expire_time
, /* Subscription expire time */
140 netif_time
, /* Network interface poll time */
141 #endif /* !__APPLE__ */
142 mallinfo_time
; /* Malloc information time */
143 size_t string_count
, /* String count */
144 alloc_bytes
, /* Allocated string bytes */
145 total_bytes
; /* Total string bytes */
146 struct timeval timeout
; /* select() timeout */
147 struct rlimit limit
; /* Runtime limit */
148 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
149 struct sigaction action
; /* Actions for POSIX signals */
150 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
152 cups_file_t
*fp
; /* Fake lpsched lock file */
153 struct stat statbuf
; /* Needed for checking lpsched FIFO */
156 int launchd_idle_exit
;
157 /* Idle exit on select timeout? */
158 #endif /* HAVE_LAUNCHD */
162 * Check for command-line arguments...
167 for (i
= 1; i
< argc
; i
++)
168 if (argv
[i
][0] == '-')
169 for (opt
= argv
[i
] + 1; *opt
!= '\0'; opt
++)
172 case 'c' : /* Configuration file */
176 _cupsLangPuts(stderr
, _("cupsd: Expected config filename "
177 "after \"-c\" option!\n"));
181 if (argv
[i
][0] == '/')
184 * Absolute directory...
187 cupsdSetString(&ConfigurationFile
, argv
[i
]);
192 * Relative directory...
195 char *current
; /* Current directory */
199 * Allocate a buffer for the current working directory to
200 * reduce run-time stack usage; this approximates the
201 * behavior of some implementations of getcwd() when they
202 * are passed a NULL pointer.
205 current
= malloc(1024);
206 getcwd(current
, 1024);
208 cupsdSetStringf(&ConfigurationFile
, "%s/%s", current
, argv
[i
]);
214 case 'f' : /* Run in foreground... */
218 case 'F' : /* Run in foreground, but disconnect from terminal... */
222 case 'h' : /* Show usage/help */
226 case 'l' : /* Started by launchd... */
231 _cupsLangPuts(stderr
, _("cupsd: launchd(8) support not compiled "
232 "in, running in normal mode.\n"));
234 #endif /* HAVE_LAUNCHD */
237 case 'p' : /* Stop immediately for profiling */
238 puts("Warning: -p option is for internal testing use only!");
243 default : /* Unknown option */
244 _cupsLangPrintf(stderr
, _("cupsd: Unknown option \"%c\" - "
245 "aborting!\n"), *opt
);
251 _cupsLangPrintf(stderr
, _("cupsd: Unknown argument \"%s\" - aborting!\n"),
256 if (!ConfigurationFile
)
257 cupsdSetString(&ConfigurationFile
, CUPS_SERVERROOT
"/cupsd.conf");
260 * If the user hasn't specified "-f", run in the background...
266 * Setup signal handlers for the parent...
269 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
270 sigset(SIGUSR1
, parent_handler
);
271 sigset(SIGCHLD
, parent_handler
);
273 sigset(SIGHUP
, SIG_IGN
);
274 #elif defined(HAVE_SIGACTION)
275 memset(&action
, 0, sizeof(action
));
276 sigemptyset(&action
.sa_mask
);
277 sigaddset(&action
.sa_mask
, SIGUSR1
);
278 action
.sa_handler
= parent_handler
;
279 sigaction(SIGUSR1
, &action
, NULL
);
280 sigaction(SIGCHLD
, &action
, NULL
);
282 sigemptyset(&action
.sa_mask
);
283 action
.sa_handler
= SIG_IGN
;
284 sigaction(SIGHUP
, &action
, NULL
);
286 signal(SIGUSR1
, parent_handler
);
287 signal(SIGCLD
, parent_handler
);
289 signal(SIGHUP
, SIG_IGN
);
290 #endif /* HAVE_SIGSET */
295 * OK, wait for the child to startup and send us SIGUSR1 or to crash
296 * and the OS send us SIGCHLD... We also need to ignore SIGHUP which
297 * might be sent by the init script to restart the scheduler...
300 for (; parent_signal
== 0;)
303 if (parent_signal
== SIGUSR1
)
311 else if (WIFEXITED(i
))
313 fprintf(stderr
, "cupsd: Child exited with status %d!\n",
319 fprintf(stderr
, "cupsd: Child exited on signal %d!\n", WTERMSIG(i
));
328 * Make sure we aren't tying up any filesystems...
335 * Disable core dumps...
338 getrlimit(RLIMIT_CORE
, &limit
);
340 setrlimit(RLIMIT_CORE
, &limit
);
343 * Disconnect from the controlling terminal...
349 * Close all open files...
352 getrlimit(RLIMIT_NOFILE
, &limit
);
354 for (i
= 0; i
< limit
.rlim_cur
; i
++)
360 * Set the timezone info...
366 setlocale(LC_TIME
, "");
370 * Set the maximum number of files...
373 getrlimit(RLIMIT_NOFILE
, &limit
);
375 if (limit
.rlim_max
> FD_SETSIZE
)
378 MaxFDs
= limit
.rlim_max
;
380 limit
.rlim_cur
= MaxFDs
;
382 setrlimit(RLIMIT_NOFILE
, &limit
);
385 * Allocate memory for the input and output sets...
388 SetSize
= (MaxFDs
+ 31) / 8 + 4;
389 if (SetSize
< sizeof(fd_set
))
390 SetSize
= sizeof(fd_set
);
392 InputSet
= (fd_set
*)calloc(1, SetSize
);
393 OutputSet
= (fd_set
*)calloc(1, SetSize
);
394 input
= (fd_set
*)calloc(1, SetSize
);
395 output
= (fd_set
*)calloc(1, SetSize
);
397 if (InputSet
== NULL
|| OutputSet
== NULL
|| input
== NULL
|| output
== NULL
)
399 syslog(LOG_LPR
, "Unable to allocate memory for select() sets - exiting!");
404 * Read configuration...
407 if (!cupsdReadConfiguration())
409 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
418 * If we were started by launchd make sure the cupsd plist file contains the
419 * same listeners as cupsd.conf; If it didn't then reload it before getting
420 * the list of listening file descriptors...
423 if (launchd_sync_conf())
428 * Until rdar://3854821 is fixed we have to exit after the reload...
431 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "Exiting on launchd_reload");
437 #endif /* HAVE_LAUNCHD */
440 * Startup the server...
446 * Catch hangup and child signals and ignore broken pipes...
449 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
450 sigset(SIGCHLD
, sigchld_handler
);
451 sigset(SIGHUP
, sighup_handler
);
452 sigset(SIGPIPE
, SIG_IGN
);
453 sigset(SIGTERM
, sigterm_handler
);
454 #elif defined(HAVE_SIGACTION)
455 memset(&action
, 0, sizeof(action
));
457 sigemptyset(&action
.sa_mask
);
458 sigaddset(&action
.sa_mask
, SIGTERM
);
459 sigaddset(&action
.sa_mask
, SIGCHLD
);
460 action
.sa_handler
= sigchld_handler
;
461 sigaction(SIGCHLD
, &action
, NULL
);
463 sigemptyset(&action
.sa_mask
);
464 sigaddset(&action
.sa_mask
, SIGHUP
);
465 action
.sa_handler
= sighup_handler
;
466 sigaction(SIGHUP
, &action
, NULL
);
468 sigemptyset(&action
.sa_mask
);
469 action
.sa_handler
= SIG_IGN
;
470 sigaction(SIGPIPE
, &action
, NULL
);
472 sigemptyset(&action
.sa_mask
);
473 sigaddset(&action
.sa_mask
, SIGTERM
);
474 sigaddset(&action
.sa_mask
, SIGCHLD
);
475 action
.sa_handler
= sigterm_handler
;
476 sigaction(SIGTERM
, &action
, NULL
);
478 signal(SIGCLD
, sigchld_handler
); /* No, SIGCLD isn't a typo... */
479 signal(SIGHUP
, sighup_handler
);
480 signal(SIGPIPE
, SIG_IGN
);
481 signal(SIGTERM
, sigterm_handler
);
482 #endif /* HAVE_SIGSET */
486 * Try to create a fake lpsched lock file if one is not already there.
487 * Some Adobe applications need it under IRIX in order to enable
491 if ((fp
= cupsFileOpen("/var/spool/lp/SCHEDLOCK", "w")) == NULL
)
493 syslog(LOG_LPR
, "Unable to create fake lpsched lock file "
494 "\"/var/spool/lp/SCHEDLOCK\"\' - %s!",
499 fchmod(cupsFileNumber(fp
), 0644);
500 fchown(cupsFileNumber(fp
), User
, Group
);
507 * Initialize authentication certificates...
513 * If we are running in the background, signal the parent process that
514 * we are up and running...
520 * Send a signal to the parent process, but only if the parent is
521 * not PID 1 (init). This avoids accidentally shutting down the
522 * system on OpenBSD if you CTRL-C the server before it is up...
525 i
= getppid(); /* Save parent PID to avoid race condition */
533 * Start power management framework...
536 cupsdStartSystemMonitor();
537 #endif /* __APPLE__ */
540 * Start any pending print jobs...
550 browse_time
= time(NULL
);
551 senddoc_time
= time(NULL
);
552 expire_time
= time(NULL
);
556 #endif /* !__APPLE__ */
558 while (!stop_scheduler
)
561 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
562 "main: Top of loop, dead_children=%d, NeedReload=%d",
563 dead_children
, NeedReload
);
567 * Check if there are dead children to handle...
574 * Check if we need to load the server configuration file...
580 * Close any idle clients...
583 if (cupsArrayCount(Clients
) > 0)
585 for (con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
587 con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
588 if (con
->http
.state
== HTTP_WAITING
)
589 cupsdCloseClient(con
);
591 con
->http
.keep_alive
= HTTP_KEEPALIVE_OFF
;
593 cupsdPauseListening();
597 * Check for any active jobs...
600 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
602 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
603 if (job
->state_value
== IPP_JOB_PROCESSING
)
607 * Restart if all clients are closed and all jobs finished, or
608 * if the reload timeout has elapsed...
611 if ((cupsArrayCount(Clients
) == 0 &&
612 (!job
|| NeedReload
!= RELOAD_ALL
)) ||
613 (time(NULL
) - ReloadTime
) >= ReloadTimeout
)
616 * Shutdown the server...
622 * Read configuration...
625 if (!cupsdReadConfiguration())
627 syslog(LOG_LPR
, "Unable to read configuration file \'%s\' - exiting!",
635 if (launchd_sync_conf())
640 * Until rdar://3854821 is fixed we have to exit after the reload...
643 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "Exiting on launchd_reload");
650 #endif /* HAVE_LAUNCHD */
653 * Startup the server...
661 * Check for available input or ready output. If select() returns
662 * 0 or -1, something bad happened and we should exit immediately.
664 * Note that we at least have one listening socket open at all
668 memcpy(input
, InputSet
, SetSize
);
669 memcpy(output
, OutputSet
, SetSize
);
671 timeout
.tv_sec
= select_timeout(fds
);
676 * If no other work is scheduled and we're being controlled by
677 * launchd then timeout after 'LaunchdTimeout' seconds of
681 if (timeout
.tv_sec
== 86400 && Launchd
&& LaunchdTimeout
&& !NumPolled
&&
682 (!Browsing
|| !(BrowseLocalProtocols
& BROWSE_DNSSD
) ||
683 cupsArrayCount(Printers
) == 0))
685 timeout
.tv_sec
= LaunchdTimeout
;
686 launchd_idle_exit
= 1;
689 launchd_idle_exit
= 0;
690 #endif /* HAVE_LAUNCHD */
692 if (timeout
.tv_sec
< 86400) /* Only use timeout for < 1 day */
693 fds
= select(MaxFDs
, input
, output
, NULL
, &timeout
);
695 fds
= select(MaxFDs
, input
, output
, NULL
, NULL
);
699 char s
[16384], /* String buffer */
700 *sptr
; /* Pointer into buffer */
701 int slen
; /* Length of string buffer */
705 * Got an error from select!
708 if (errno
== EINTR
) /* Just interrupted by a signal */
712 * Log all sorts of debug info to help track down the problem.
715 cupsdLogMessage(CUPSD_LOG_EMERG
, "select() failed - %s!",
718 strcpy(s
, "InputSet =");
722 for (i
= 0; i
< MaxFDs
; i
++)
723 if (FD_ISSET(i
, InputSet
))
725 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
726 slen
+= strlen(sptr
);
727 sptr
+= strlen(sptr
);
730 cupsdLogMessage(CUPSD_LOG_EMERG
, "%s", s
);
732 strcpy(s
, "OutputSet =");
736 for (i
= 0; i
< MaxFDs
; i
++)
737 if (FD_ISSET(i
, OutputSet
))
739 snprintf(sptr
, sizeof(s
) - slen
, " %d", i
);
740 slen
+= strlen(sptr
);
741 sptr
+= strlen(sptr
);
744 cupsdLogMessage(CUPSD_LOG_EMERG
, "%s", s
);
746 for (i
= 0, con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
748 i
++, con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
749 cupsdLogMessage(CUPSD_LOG_EMERG
,
750 "Clients[%d] = %d, file = %d, state = %d",
751 i
, con
->http
.fd
, con
->file
, con
->http
.state
);
753 for (i
= 0, lis
= (cupsd_listener_t
*)cupsArrayFirst(Listeners
);
755 i
++, lis
= (cupsd_listener_t
*)cupsArrayNext(Listeners
))
756 cupsdLogMessage(CUPSD_LOG_EMERG
, "Listeners[%d] = %d", i
, lis
->fd
);
758 cupsdLogMessage(CUPSD_LOG_EMERG
, "BrowseSocket = %d", BrowseSocket
);
760 cupsdLogMessage(CUPSD_LOG_EMERG
, "CGIPipes[0] = %d", CGIPipes
[0]);
763 cupsdLogMessage(CUPSD_LOG_EMERG
, "SysEventPipes[0] = %d",
765 #endif /* __APPLE__ */
767 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
769 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
770 cupsdLogMessage(CUPSD_LOG_EMERG
, "Jobs[%d] = %d < [%d %d] > [%d %d]",
772 job
->status_buffer
? job
->status_buffer
->fd
: -1,
773 job
->print_pipes
[0], job
->print_pipes
[1],
774 job
->back_pipes
[0], job
->back_pipes
[1]);
778 current_time
= time(NULL
);
782 * If no other work was scheduled and we're being controlled by launchd
783 * then timeout after 'LaunchdTimeout' seconds of inactivity...
786 if (!fds
&& launchd_idle_exit
)
788 cupsdLogMessage(CUPSD_LOG_INFO
,
789 "Printer sharing is off and there are no jobs pending, "
790 "will restart on demand.");
794 #endif /* HAVE_LAUNCHD */
797 * Check for status info from job filters...
800 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
802 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
803 if (job
->status_buffer
&& FD_ISSET(job
->status_buffer
->fd
, input
))
806 * Clear the input bit to avoid updating the next job
807 * using the same status pipe file descriptor...
810 FD_CLR(job
->status_buffer
->fd
, input
);
813 * Read any status messages from the filters...
820 * Update CGI messages as needed...
823 if (CGIPipes
[0] >= 0 && FD_ISSET(CGIPipes
[0], input
))
827 * Handle system management events as needed...
832 * Mac OS X provides the SystemConfiguration framework for system
833 * configuration change events...
836 if (SysEventPipes
[0] >= 0 && FD_ISSET(SysEventPipes
[0], input
))
837 cupsdUpdateSystemMonitor();
840 * All other operating systems need to poll for changes...
843 if ((current_time
- netif_time
) >= 60)
846 netif_time
= current_time
;
848 #endif /* __APPLE__ */
851 * Update notifier messages as needed...
854 if (NotifierPipes
[0] >= 0 && FD_ISSET(NotifierPipes
[0], input
))
855 cupsdUpdateNotifierStatus();
858 * Expire subscriptions and unload completed jobs as needed...
861 if (current_time
> expire_time
)
863 if (cupsArrayCount(Subscriptions
) > 0)
864 cupsdExpireSubscriptions(NULL
, NULL
);
866 cupsdUnloadCompletedJobs();
868 expire_time
= current_time
;
872 * Update the browse list as needed...
875 if (Browsing
&& BrowseRemoteProtocols
)
877 if (BrowseSocket
>= 0 && FD_ISSET(BrowseSocket
, input
))
878 cupsdUpdateCUPSBrowse();
880 if (PollPipe
>= 0 && FD_ISSET(PollPipe
, input
))
881 cupsdUpdatePolling();
884 if ((BrowseRemoteProtocols
& BROWSE_SLP
) &&
885 BrowseSLPRefresh
<= current_time
)
886 cupsdUpdateSLPBrowse();
887 #endif /* HAVE_LIBSLP */
890 if ((BrowseRemoteProtocols
& BROWSE_LDAP
) &&
891 BrowseLDAPRefresh
<= current_time
)
892 cupsdUpdateLDAPBrowse();
893 #endif /* HAVE_LDAP */
896 if (Browsing
&& BrowseLocalProtocols
&& current_time
> browse_time
)
898 cupsdSendBrowseList();
899 browse_time
= current_time
;
903 * Check for new connections on the "listen" sockets...
906 for (lis
= (cupsd_listener_t
*)cupsArrayFirst(Listeners
);
908 lis
= (cupsd_listener_t
*)cupsArrayNext(Listeners
))
909 if (lis
->fd
>= 0 && FD_ISSET(lis
->fd
, input
))
911 FD_CLR(lis
->fd
, input
);
912 cupsdAcceptClient(lis
);
916 * Check for new data on the client sockets...
919 for (con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
921 con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
924 * Process the input buffer...
927 if (FD_ISSET(con
->http
.fd
, input
) || con
->http
.used
)
932 FD_CLR(con
->http
.fd
, input
);
934 if (!cupsdReadClient(con
))
944 * Write data as needed...
947 if (con
->pipe_pid
&& FD_ISSET(con
->file
, input
))
950 * Keep track of pending input from the file/pipe separately
951 * so that we don't needlessly spin on select() when the web
952 * client is not ready to receive data...
955 FD_CLR(con
->file
, input
);
959 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "main: Data ready file %d!",
963 if (!FD_ISSET(con
->http
.fd
, output
))
965 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
966 "main: Removing fd %d from InputSet...", con
->file
);
967 FD_CLR(con
->file
, input
);
968 FD_CLR(con
->file
, InputSet
);
972 if (FD_ISSET(con
->http
.fd
, output
))
974 FD_CLR(con
->http
.fd
, output
);
976 if (!con
->pipe_pid
|| con
->file_ready
)
977 if (!cupsdWriteClient(con
))
982 * Check the activity and close old clients...
985 activity
= current_time
- Timeout
;
986 if (con
->http
.activity
< activity
&& !con
->pipe_pid
)
988 cupsdLogMessage(CUPSD_LOG_DEBUG
,
989 "Closing client %d after %d seconds of inactivity...",
990 con
->http
.fd
, Timeout
);
992 cupsdCloseClient(con
);
998 * Update any pending multi-file documents...
1001 if ((current_time
- senddoc_time
) >= 10)
1004 senddoc_time
= current_time
;
1008 * Log memory usage every minute...
1011 if ((current_time
- mallinfo_time
) >= 60 && LogLevel
>= CUPSD_LOG_DEBUG2
)
1013 #ifdef HAVE_MALLINFO
1014 struct mallinfo mem
; /* Malloc information */
1018 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1019 "mallinfo: arena = %d, used = %d, free = %d\n",
1020 mem
.arena
, mem
.usmblks
+ mem
.uordblks
,
1021 mem
.fsmblks
+ mem
.fordblks
);
1022 #endif /* HAVE_MALLINFO */
1024 string_count
= _cupsStrStatistics(&alloc_bytes
, &total_bytes
);
1025 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1026 "stringpool: " CUPS_LLFMT
" strings, "
1027 CUPS_LLFMT
" allocated, " CUPS_LLFMT
" total bytes",
1028 CUPS_LLCAST string_count
, CUPS_LLCAST alloc_bytes
,
1029 CUPS_LLCAST total_bytes
);
1031 mallinfo_time
= current_time
;
1035 * Update the root certificate once every 5 minutes if we have client
1039 if ((current_time
- RootCertTime
) >= RootCertDuration
&& RootCertDuration
&&
1040 !RunUser
&& cupsArrayCount(Clients
))
1043 * Update the root certificate...
1047 cupsdAddCert(0, "root");
1051 * Handle OS-specific event notification for any events that have
1052 * accumulated. Don't send these more than once a second...
1057 #ifdef HAVE_NOTIFY_POST
1058 if (LastEvent
& CUPSD_EVENT_PRINTER_CHANGED
)
1060 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1061 "notify_post(\"com.apple.printerListChange\")");
1062 notify_post("com.apple.printerListChange");
1065 if (LastEvent
& CUPSD_EVENT_PRINTER_STATE_CHANGED
)
1067 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1068 "notify_post(\"com.apple.printerHistoryChange\")");
1069 notify_post("com.apple.printerHistoryChange");
1072 if (LastEvent
& (CUPSD_EVENT_JOB_STATE_CHANGED
|
1073 CUPSD_EVENT_JOB_CONFIG_CHANGED
|
1074 CUPSD_EVENT_JOB_PROGRESS
))
1076 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1077 "notify_post(\"com.apple.jobChange\")");
1078 notify_post("com.apple.jobChange");
1080 #endif /* HAVE_NOTIFY_POST */
1083 * Reset the accumulated events...
1086 LastEvent
= CUPSD_EVENT_NONE
;
1091 * Log a message based on what happened...
1095 cupsdLogMessage(CUPSD_LOG_INFO
, "Scheduler shutting down normally.");
1097 cupsdLogMessage(CUPSD_LOG_ERROR
,
1098 "Scheduler shutting down due to program error.");
1101 * Close all network clients and stop all jobs...
1109 cupsdStopSystemMonitor();
1110 #endif /* __APPLE__ */
1114 * Update the launchd config file as needed...
1117 launchd_sync_conf();
1119 if (launchd_conf_url
)
1120 CFRelease(launchd_conf_url
);
1122 if (launchd_conf_dict
)
1123 CFRelease(launchd_conf_dict
);
1124 #endif /* HAVE_LAUNCHD */
1128 * Remove the fake IRIX lpsched lock file, but only if the existing
1129 * file is not a FIFO which indicates that the real IRIX lpsched is
1133 if (!stat("/var/spool/lp/FIFO", &statbuf
))
1134 if (!S_ISFIFO(statbuf
.st_mode
))
1135 unlink("/var/spool/lp/SCHEDLOCK");
1139 * Free memory used by FD sets and return...
1147 return (!stop_scheduler
);
1152 * 'cupsdClosePipe()' - Close a pipe as necessary.
1156 cupsdClosePipe(int *fds
) /* I - Pipe file descriptors (2) */
1159 * Close file descriptors as needed...
1177 * 'cupsdOpenPipe()' - Create a pipe which is closed on exec.
1180 int /* O - 0 on success, -1 on error */
1181 cupsdOpenPipe(int *fds
) /* O - Pipe file descriptors (2) */
1184 * Create the pipe...
1196 * Set the "close on exec" flag on each end of the pipe...
1199 if (fcntl(fds
[0], F_SETFD
, fcntl(fds
[0], F_GETFD
) | FD_CLOEXEC
))
1210 if (fcntl(fds
[1], F_SETFD
, fcntl(fds
[1], F_GETFD
) | FD_CLOEXEC
))
1222 * Return 0 indicating success...
1230 * 'cupsdClearString()' - Clear a string.
1234 cupsdClearString(char **s
) /* O - String value */
1245 * 'cupsdHoldSignals()' - Hold child and termination signals.
1249 cupsdHoldSignals(void)
1251 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1252 sigset_t newmask
; /* New POSIX signal mask */
1253 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1263 #elif defined(HAVE_SIGACTION)
1264 sigemptyset(&newmask
);
1265 sigaddset(&newmask
, SIGTERM
);
1266 sigaddset(&newmask
, SIGCHLD
);
1267 sigprocmask(SIG_BLOCK
, &newmask
, &holdmask
);
1268 #endif /* HAVE_SIGSET */
1273 * 'cupsdReleaseSignals()' - Release signals for delivery.
1277 cupsdReleaseSignals(void)
1286 #elif defined(HAVE_SIGACTION)
1287 sigprocmask(SIG_SETMASK
, &holdmask
, NULL
);
1288 #endif /* HAVE_SIGSET */
1293 * 'cupsdSetString()' - Set a string value.
1297 cupsdSetString(char **s
, /* O - New string */
1298 const char *v
) /* I - String value */
1307 *s
= _cupsStrAlloc(v
);
1314 * 'cupsdSetStringf()' - Set a formatted string value.
1318 cupsdSetStringf(char **s
, /* O - New string */
1319 const char *f
, /* I - Printf-style format string */
1320 ...) /* I - Additional args as needed */
1322 char v
[4096]; /* Formatting string value */
1323 va_list ap
; /* Argument pointer */
1324 char *olds
; /* Old string */
1335 vsnprintf(v
, sizeof(v
), f
, ap
);
1338 *s
= _cupsStrAlloc(v
);
1350 * 'launchd_checkin()' - Check-in with launchd and collect the listening fds.
1354 launchd_checkin(void)
1356 int i
, /* Looping var */
1357 count
, /* Numebr of listeners */
1358 portnum
; /* Port number */
1359 launch_data_t ld_msg
, /* Launch data message */
1360 ld_resp
, /* Launch data response */
1361 ld_array
, /* Launch data array */
1362 ld_sockets
, /* Launch data sockets dictionary */
1363 tmp
; /* Launch data */
1364 cupsd_listener_t
*lis
; /* Listeners array */
1365 http_addr_t addr
; /* Address variable */
1366 socklen_t addrlen
; /* Length of address */
1369 cupsdLogMessage(CUPSD_LOG_DEBUG
, "launchd_checkin: pid=%d", (int)getpid());
1372 * Check-in with launchd...
1375 ld_msg
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
1376 if ((ld_resp
= launch_msg(ld_msg
)) == NULL
)
1378 cupsdLogMessage(CUPSD_LOG_ERROR
,
1379 "launchd_checkin: launch_msg(\"" LAUNCH_KEY_CHECKIN
1384 if (launch_data_get_type(ld_resp
) == LAUNCH_DATA_ERRNO
)
1386 errno
= launch_data_get_errno(ld_resp
);
1387 cupsdLogMessage(CUPSD_LOG_ERROR
, "launchd_checkin: Check-in failed: %s",
1393 * Get the sockets dictionary...
1396 if (!(ld_sockets
= launch_data_dict_lookup(ld_resp
, LAUNCH_JOBKEY_SOCKETS
)))
1398 cupsdLogMessage(CUPSD_LOG_ERROR
,
1399 "launchd_checkin: No sockets found to answer requests on!");
1404 * Get the array of listener sockets...
1407 if (!(ld_array
= launch_data_dict_lookup(ld_sockets
, "Listeners")))
1409 cupsdLogMessage(CUPSD_LOG_ERROR
,
1410 "launchd_checkin: No sockets found to answer requests on!");
1415 * Add listening fd(s) to the Listener array...
1418 if (launch_data_get_type(ld_array
) == LAUNCH_DATA_ARRAY
)
1421 * Free the listeners array built from cupsd.conf...
1424 cupsdDeleteAllListeners();
1427 * Create a new array of listeners from the launchd data...
1430 Listeners
= cupsArrayNew(NULL
, NULL
);
1431 count
= launch_data_array_get_count(ld_array
);
1433 for (i
= 0; i
< count
; i
++)
1436 * Copy the current address and log it...
1439 if ((lis
= calloc(1, sizeof(cupsd_listener_t
))) == NULL
)
1441 cupsdLogMessage(CUPSD_LOG_ERROR
,
1442 "launchd_checkin: Unable to allocate listener - %s.",
1447 cupsArrayAdd(Listeners
, lis
);
1449 tmp
= launch_data_array_get_index(ld_array
, i
);
1450 lis
->fd
= launch_data_get_fd(tmp
);
1451 addrlen
= sizeof(lis
->address
);
1453 if (getsockname(lis
->fd
, (struct sockaddr
*)&(lis
->address
), &addrlen
))
1455 cupsdLogMessage(CUPSD_LOG_ERROR
,
1456 "launchd_checkin: Unable to get local address - %s",
1464 if (addr
.addr
.sa_family
== AF_INET6
)
1465 portnum
= ntohs(addr
.ipv6
.sin6_port
);
1467 # endif /* AF_INET6 */
1468 if (addr
.addr
.sa_family
== AF_INET
)
1469 portnum
= ntohs(addr
.ipv4
.sin_port
);
1472 lis
->encryption
= HTTP_ENCRYPT_ALWAYS
;
1473 # endif /* HAVE_SSL */
1478 * Collect the browse socket (if there is one)...
1481 if ((ld_array
= launch_data_dict_lookup(ld_sockets
, "BrowseSockets")))
1483 if (launch_data_get_type(ld_array
) == LAUNCH_DATA_ARRAY
)
1485 if ((tmp
= launch_data_array_get_index(ld_array
, 0)))
1487 if (launch_data_get_type(tmp
) == LAUNCH_DATA_FD
)
1489 if (BrowseSocket
!= -1)
1490 close(BrowseSocket
);
1492 BrowseSocket
= launch_data_get_fd(tmp
);
1495 cupsdLogMessage(CUPSD_LOG_WARN
,
1496 "launchd_checkin: BrowseSocket not a fd!");
1499 cupsdLogMessage(CUPSD_LOG_WARN
,
1500 "launchd_checkin: BrowseSockets is an empty array!");
1503 cupsdLogMessage(CUPSD_LOG_WARN
,
1504 "launchd_checkin: BrowseSockets is not an array!");
1507 cupsdLogMessage(CUPSD_LOG_DEBUG
, "launchd_checkin: No BrowseSockets");
1509 launch_data_free(ld_msg
);
1510 launch_data_free(ld_resp
);
1515 * 'launchd_create_dict()' - Create a dictionary representing the launchd
1516 * config file org.cups.cupsd.plist.
1519 static CFDictionaryRef
/* O - CFDictionary */
1520 launchd_create_dict(void)
1522 int portnum
; /* Port number */
1523 bool runatload
; /* Run at load? */
1524 CFMutableDictionaryRef cupsd_dict
, /* org.cups.cupsd.plist dictionary */
1525 sockets
, /* Sockets dictionary */
1526 listener
; /* Listener dictionary */
1527 CFMutableArrayRef array
; /* Array */
1528 CFNumberRef socket_mode
; /* Domain socket mode bits */
1529 CFStringRef socket_path
; /* Domain socket path */
1530 CFTypeRef value
; /* CF values */
1531 cupsd_listener_t
*lis
; /* Current listening socket */
1532 struct servent
*service
; /* Services data base entry */
1533 char temp
[1024]; /* Temporary buffer for value */
1536 if ((cupsd_dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1537 &kCFTypeDictionaryKeyCallBacks
,
1538 &kCFTypeDictionaryValueCallBacks
)) == NULL
)
1541 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_LABEL
),
1542 CFSTR("org.cups.cupsd"));
1543 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_ONDEMAND
),
1547 * Run-at-load if there are active jobs, polling or shared printers
1551 runatload
= (cupsArrayCount(ActiveJobs
) || NumPolled
||
1552 (Browsing
&& BrowseLocalProtocols
&&
1553 NumBrowsers
&& cupsArrayCount(Printers
))) ? true : false;
1555 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_RUNATLOAD
),
1556 runatload
? kCFBooleanTrue
: kCFBooleanFalse
);
1557 # ifdef LAUNCH_JOBKEY_SERVICEIPC
1558 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_SERVICEIPC
),
1560 # endif /* LAUNCH_JOBKEY_SERVICEIPC */
1562 if ((array
= CFArrayCreateMutable(kCFAllocatorDefault
, 2,
1563 &kCFTypeArrayCallBacks
)) != NULL
)
1565 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS
),
1567 CFArrayAppendValue(array
, CFSTR("/usr/sbin/cupsd"));
1568 CFArrayAppendValue(array
, CFSTR("-l"));
1573 * Add a sockets dictionary...
1576 if ((sockets
= (CFMutableDictionaryRef
)CFDictionaryCreateMutable(
1577 kCFAllocatorDefault
, 0,
1578 &kCFTypeDictionaryKeyCallBacks
,
1579 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1581 CFDictionaryAddValue(cupsd_dict
, CFSTR(LAUNCH_JOBKEY_SOCKETS
), sockets
);
1584 * Add a Listeners array to the sockets dictionary...
1587 if ((array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1588 &kCFTypeArrayCallBacks
)) != NULL
)
1590 CFDictionaryAddValue(sockets
, CFSTR("Listeners"), array
);
1593 * For each listener add a dictionary to the listeners array...
1596 for (lis
= (cupsd_listener_t
*)cupsArrayFirst(Listeners
);
1598 lis
= (cupsd_listener_t
*)cupsArrayNext(Listeners
))
1600 if ((listener
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1601 &kCFTypeDictionaryKeyCallBacks
,
1602 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1604 CFArrayAppendValue(array
, listener
);
1607 if (lis
->address
.addr
.sa_family
== AF_LOCAL
)
1609 if ((socket_path
= CFStringCreateWithCString(kCFAllocatorDefault
,
1610 lis
->address
.un
.sun_path
,
1611 kCFStringEncodingUTF8
)))
1613 CFDictionaryAddValue(listener
,
1614 CFSTR(LAUNCH_JOBSOCKETKEY_PATHNAME
),
1616 CFRelease(socket_path
);
1618 portnum
= 0140777; /* (S_IFSOCK|S_IRWXU|S_IRWXG|S_IRWXO) or *
1620 if ((socket_mode
= CFNumberCreate(kCFAllocatorDefault
,
1621 kCFNumberIntType
, &portnum
)))
1623 CFDictionaryAddValue(listener
, CFSTR("SockPathMode"),
1625 CFRelease(socket_mode
);
1629 # endif /* AF_LOCAL */
1632 if (lis
->address
.addr
.sa_family
== AF_INET6
)
1634 CFDictionaryAddValue(listener
,
1635 CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY
),
1637 portnum
= lis
->address
.ipv6
.sin6_port
;
1640 # endif /* AF_INET6 */
1642 CFDictionaryAddValue(listener
,
1643 CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY
),
1645 portnum
= lis
->address
.ipv4
.sin_port
;
1648 if ((service
= getservbyport(portnum
, NULL
)))
1649 value
= CFStringCreateWithCString(kCFAllocatorDefault
,
1651 kCFStringEncodingUTF8
);
1653 value
= CFNumberCreate(kCFAllocatorDefault
,
1654 kCFNumberIntType
, &portnum
);
1658 CFDictionaryAddValue(listener
,
1659 CFSTR(LAUNCH_JOBSOCKETKEY_SERVICENAME
),
1664 httpAddrString(&lis
->address
, temp
, sizeof(temp
));
1665 if ((value
= CFStringCreateWithCString(kCFAllocatorDefault
, temp
,
1666 kCFStringEncodingUTF8
)))
1668 CFDictionaryAddValue(listener
,
1669 CFSTR(LAUNCH_JOBSOCKETKEY_NODENAME
),
1675 CFRelease(listener
);
1683 * Add the BrowseSocket to the sockets dictionary...
1686 if (Browsing
&& (BrowseRemoteProtocols
& BROWSE_CUPS
))
1688 if ((array
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1689 &kCFTypeArrayCallBacks
)) != NULL
)
1691 CFDictionaryAddValue(sockets
, CFSTR("BrowseSockets"), array
);
1693 if ((listener
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
1694 &kCFTypeDictionaryKeyCallBacks
,
1695 &kCFTypeDictionaryValueCallBacks
)) != NULL
)
1697 CFArrayAppendValue(array
, listener
);
1699 CFDictionaryAddValue(listener
, CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY
),
1701 CFDictionaryAddValue(listener
, CFSTR(LAUNCH_JOBSOCKETKEY_TYPE
),
1704 if ((service
= getservbyport(BrowsePort
, NULL
)))
1705 value
= CFStringCreateWithCString(kCFAllocatorDefault
,
1707 kCFStringEncodingUTF8
);
1709 value
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberIntType
,
1712 CFDictionaryAddValue(listener
,
1713 CFSTR(LAUNCH_JOBSOCKETKEY_SERVICENAME
), value
);
1716 CFRelease(listener
);
1726 return (cupsd_dict
);
1731 * 'launchd_reload()' - Tell launchd to reload the configuration file to pick
1732 * up the new listening directives.
1736 launchd_reload(void)
1738 int child_status
; /* Exit status of child process */
1739 pid_t child_pid
, /* Child PID */
1740 waitpid_status
; /* Child process exit status */
1741 char *argv
[4]; /* Argument strings */
1745 * The current launchd doesn't support a reload option (rdar://3854821).
1746 * Until this is fixed we need to reload the config file by execing launchctl
1747 * twice (to unload then load). NOTE: This will cause us to exit on SIGTERM
1748 * which will cancel all client & job activity.
1750 * After this is fixed we'll be able to tell launchd to reload the file
1751 * and pick up the new listening descriptors without disrupting current
1756 * Unloading the current configuration will cause launchd to send us a SIGTERM;
1757 * block it for now so we can get our work done...
1763 * Set up the unload arguments to launchctl...
1766 argv
[0] = "/bin/launchctl";
1768 argv
[2] = LaunchdConf
;
1771 if (cupsdStartProcess(argv
[0], argv
, NULL
, -1, -1, -1, -1, 1, &child_pid
) < 0)
1772 cupsdLogMessage(CUPSD_LOG_ERROR
,
1773 "launchd_reload: Unable to execute %s - %s", argv
[0],
1779 waitpid_status
= waitpid(child_pid
, &child_status
, 0);
1781 while (waitpid_status
== (pid_t
)-1 && errno
== EINTR
);
1783 if (WIFSIGNALED(child_status
))
1784 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1785 "launchd_reload: %s pid %d crashed on signal %d!",
1786 basename(argv
[0]), child_pid
, WTERMSIG(child_status
));
1788 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1789 "launchd_reload: %s pid %d stopped with status %d!",
1790 basename(argv
[0]), child_pid
, WEXITSTATUS(child_status
));
1793 * Do it again with the load command...
1798 if (cupsdStartProcess(argv
[0], argv
, NULL
, -1, -1, -1, -1, 1,
1801 cupsdLogMessage(CUPSD_LOG_ERROR
,
1802 "launchd_reload: Unable to fork for %s - %s", argv
[0],
1809 waitpid_status
= waitpid(child_pid
, &child_status
, 0);
1810 } while (waitpid_status
== (pid_t
)-1 && errno
== EINTR
);
1812 if (WIFSIGNALED(child_status
))
1813 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1814 "launchd_reload: %s pid %d crashed on signal %d!",
1815 basename(argv
[0]), child_pid
, WTERMSIG(child_status
));
1817 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1818 "launchd_reload: %s pid %d stopped with status %d",
1819 basename(argv
[0]), child_pid
,
1820 WEXITSTATUS(child_status
));
1825 * Leave signals blocked since exit() will be called momentarily anyways...
1831 * 'launchd_sync_conf()' - Rewrite the launchd config file
1832 * org.cups.cupsd.plist based on cupsd.conf.
1835 static int /* O - 1 if the file was updated */
1836 launchd_sync_conf(void)
1838 SInt32 errorCode
; /* Error code */
1839 CFDataRef resourceData
; /* XML property list */
1840 CFDictionaryRef cupsd_dict
; /* New org.cups.cupsd.plist dict */
1844 * If needed reconstitute the existing org.cups.cupsd.plist...
1847 if (!launchd_conf_url
&&
1848 !(launchd_conf_url
= CFURLCreateFromFileSystemRepresentation(
1849 kCFAllocatorDefault
,
1850 (const unsigned char *)LaunchdConf
,
1851 strlen(LaunchdConf
), false)))
1853 cupsdLogMessage(CUPSD_LOG_ERROR
, "launchd_sync_conf: "
1854 "Unable to create file URL for \"%s\"\n", LaunchdConf
);
1858 if (!launchd_conf_dict
)
1860 if (CFURLCreateDataAndPropertiesFromResource(NULL
, launchd_conf_url
,
1861 &resourceData
, NULL
, NULL
, &errorCode
))
1863 launchd_conf_dict
= CFPropertyListCreateFromXMLData(NULL
, resourceData
,
1864 kCFPropertyListImmutable
, NULL
);
1865 CFRelease(resourceData
);
1868 if (!launchd_conf_dict
)
1870 cupsdLogMessage(CUPSD_LOG_ERROR
, "launchd_sync_conf: "
1871 "Unable to create dictionary for \"%s\"\n", LaunchdConf
);
1876 * Create a new org.cups.cupsd.plist dictionary...
1879 if ((cupsd_dict
= launchd_create_dict()) == NULL
)
1881 cupsdLogMessage(CUPSD_LOG_ERROR
, "launchd_sync_conf: "
1882 "Unable to create file URL for \"%s\"\n", LaunchdConf
);
1887 * If the dictionaries are different write a new org.cups.cupsd.plist...
1890 if (!CFEqual(cupsd_dict
, launchd_conf_dict
))
1892 if ((resourceData
= CFPropertyListCreateXMLData(kCFAllocatorDefault
,
1895 if (CFURLWriteDataAndPropertiesToResource(launchd_conf_url
, resourceData
,
1899 * The new cupsd dictionary becomes the on-disk launchd dictionary...
1902 if (launchd_conf_dict
)
1903 CFRelease(launchd_conf_dict
);
1905 launchd_conf_dict
= cupsd_dict
;
1909 cupsdLogMessage(CUPSD_LOG_WARN
,
1910 "launchd_sync_conf: "
1911 "CFURLWriteDataAndPropertiesToResource(\"%s\") "
1913 LaunchdConf
, (int)errorCode
);
1915 CFRelease(cupsd_dict
);
1918 CFRelease(resourceData
);
1922 * Let the caller know we updated the file...
1930 #endif /* HAVE_LAUNCHD */
1934 * 'parent_handler()' - Catch USR1/CHLD signals...
1938 parent_handler(int sig
) /* I - Signal */
1941 * Store the signal we got from the OS and return...
1944 parent_signal
= sig
;
1949 * 'process_children()' - Process all dead children...
1953 process_children(void)
1955 int status
; /* Exit status of child */
1956 int pid
; /* Process ID of child */
1957 cupsd_job_t
*job
; /* Current job */
1958 int i
; /* Looping var */
1959 char name
[1024]; /* Process name */
1962 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "process_children()");
1965 * Reset the dead_children flag...
1971 * Collect the exit status of some children...
1975 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0)
1976 #elif defined(HAVE_WAIT3)
1977 while ((pid
= wait3(&status
, WNOHANG
, NULL
)) > 0)
1979 if ((pid
= wait(&status
)) > 0)
1980 #endif /* HAVE_WAITPID */
1983 * Ignore SIGTERM errors - that comes when a job is canceled...
1986 cupsdFinishProcess(pid
, name
, sizeof(name
));
1988 if (status
== SIGTERM
)
1993 if (WIFEXITED(status
))
1994 cupsdLogMessage(CUPSD_LOG_ERROR
, "PID %d (%s) stopped with status %d!",
1995 pid
, name
, WEXITSTATUS(status
));
1997 cupsdLogMessage(CUPSD_LOG_ERROR
, "PID %d (%s) crashed on signal %d!",
1998 pid
, name
, WTERMSIG(status
));
2000 if (LogLevel
< CUPSD_LOG_DEBUG
)
2001 cupsdLogMessage(CUPSD_LOG_INFO
,
2002 "Hint: Try setting the LogLevel to \"debug\" to find "
2006 cupsdLogMessage(CUPSD_LOG_DEBUG
, "PID %d (%s) exited with no errors.",
2010 * Delete certificates for CGI processes...
2014 cupsdDeleteCert(pid
);
2017 * Lookup the PID in the jobs list...
2020 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2022 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2023 if (job
->state_value
== IPP_JOB_PROCESSING
)
2025 for (i
= 0; job
->filters
[i
]; i
++)
2026 if (job
->filters
[i
] == pid
)
2029 if (job
->filters
[i
] || job
->backend
== pid
)
2032 * OK, this process has gone away; what's left?
2035 if (job
->filters
[i
])
2036 job
->filters
[i
] = -pid
;
2038 job
->backend
= -pid
;
2040 if (status
&& job
->status
>= 0)
2043 * An error occurred; save the exit status so we know to stop
2044 * the printer or cancel the job when all of the filters finish...
2046 * A negative status indicates that the backend failed and the
2047 * printer needs to be stopped.
2050 if (job
->filters
[i
])
2051 job
->status
= status
; /* Filter failed */
2053 job
->status
= -status
; /* Backend failed */
2055 if (job
->printer
&& !(job
->printer
->type
& CUPS_PRINTER_FAX
))
2057 snprintf(job
->printer
->state_message
,
2058 sizeof(job
->printer
->state_message
), "%s failed", name
);
2059 cupsdAddPrinterHistory(job
->printer
);
2064 * If this is not the last file in a job, see if all of the
2065 * filters are done, and if so move to the next file.
2068 if (job
->current_file
< job
->num_files
)
2070 for (i
= 0; job
->filters
[i
] < 0; i
++);
2072 if (!job
->filters
[i
])
2075 * Process the next file...
2078 cupsdFinishJob(job
);
2089 * 'sigchld_handler()' - Handle 'child' signals from old processes.
2093 sigchld_handler(int sig
) /* I - Signal number */
2098 * Flag that we have dead children...
2104 * Reset the signal handler as needed...
2107 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
2108 signal(SIGCLD
, sigchld_handler
);
2109 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
2114 * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler.
2118 sighup_handler(int sig
) /* I - Signal number */
2122 NeedReload
= RELOAD_ALL
;
2123 ReloadTime
= time(NULL
);
2125 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
2126 signal(SIGHUP
, sighup_handler
);
2127 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
2132 * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler.
2136 sigterm_handler(int sig
) /* I - Signal */
2138 (void)sig
; /* remove compiler warnings... */
2141 * Flag that we should stop and return...
2149 * 'select_timeout()' - Calculate the select timeout value.
2153 static long /* O - Number of seconds */
2154 select_timeout(int fds
) /* I - Number of descriptors returned */
2156 long timeout
; /* Timeout for select */
2157 time_t now
; /* Current time */
2158 cupsd_client_t
*con
; /* Client information */
2159 cupsd_printer_t
*p
; /* Printer information */
2160 cupsd_job_t
*job
; /* Job information */
2161 cupsd_subscription_t
*sub
; /* Subscription information */
2162 const char *why
; /* Debugging aid */
2166 * Check to see if any of the clients have pending data to be
2167 * processed; if so, the timeout should be 0...
2170 for (con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
2172 con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
2173 if (con
->http
.used
> 0)
2177 * If select has been active in the last second (fds != 0) or we have
2178 * many resources in use then don't bother trying to optimize the
2179 * timeout, just make it 1 second.
2182 if (fds
|| cupsArrayCount(Clients
) > 50)
2186 * Otherwise, check all of the possible events that we need to wake for...
2190 timeout
= now
+ 86400; /* 86400 == 1 day */
2194 * Check the activity and close old clients...
2197 for (con
= (cupsd_client_t
*)cupsArrayFirst(Clients
);
2199 con
= (cupsd_client_t
*)cupsArrayNext(Clients
))
2200 if ((con
->http
.activity
+ Timeout
) < timeout
)
2202 timeout
= con
->http
.activity
+ Timeout
;
2203 why
= "timeout a client connection";
2207 * Update the browse list as needed...
2210 if (Browsing
&& BrowseLocalProtocols
)
2213 if ((BrowseLocalProtocols
& BROWSE_SLP
) && (BrowseSLPRefresh
< timeout
))
2215 timeout
= BrowseSLPRefresh
;
2216 why
= "update SLP browsing";
2218 #endif /* HAVE_LIBSLP */
2221 if ((BrowseLocalProtocols
& BROWSE_LDAP
) && (BrowseLDAPRefresh
< timeout
))
2223 timeout
= BrowseLDAPRefresh
;
2224 why
= "update LDAP browsing";
2226 #endif /* HAVE_LDAP */
2228 if (BrowseLocalProtocols
& BROWSE_CUPS
)
2230 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
2232 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
2234 if (p
->type
& CUPS_PRINTER_REMOTE
)
2236 if ((p
->browse_time
+ BrowseTimeout
) < timeout
)
2238 timeout
= p
->browse_time
+ BrowseTimeout
;
2239 why
= "browse timeout a printer";
2242 else if (!(p
->type
& CUPS_PRINTER_IMPLICIT
))
2244 if (BrowseInterval
&& (p
->browse_time
+ BrowseInterval
) < timeout
)
2246 timeout
= p
->browse_time
+ BrowseInterval
;
2247 why
= "send browse update";
2255 * Check for any active jobs...
2258 if (timeout
> (now
+ 10) && ActiveJobs
)
2260 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2262 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2263 if (job
->state_value
<= IPP_JOB_PROCESSING
)
2266 why
= "process active jobs";
2271 #ifdef HAVE_MALLINFO
2273 * Log memory usage every minute...
2276 if (LogLevel
>= CUPSD_LOG_DEBUG
&& (mallinfo_time
+ 60) < timeout
)
2278 timeout
= mallinfo_time
+ 60;
2279 why
= "display memory usage";
2281 #endif /* HAVE_MALLINFO */
2284 * Expire subscriptions as needed...
2287 for (sub
= (cupsd_subscription_t
*)cupsArrayFirst(Subscriptions
);
2289 sub
= (cupsd_subscription_t
*)cupsArrayNext(Subscriptions
))
2290 if (!sub
->job
&& sub
->expire
&& sub
->expire
< timeout
)
2292 timeout
= sub
->expire
;
2293 why
= "expire subscription";
2297 * Adjust from absolute to relative time. If p->browse_time above
2298 * was 0 then we can end up with a negative value here, so check.
2299 * We add 1 second to the timeout since events occur after the
2300 * timeout expires, and limit the timeout to 86400 seconds (1 day)
2301 * to avoid select() timeout limits present on some operating
2305 timeout
= timeout
- now
+ 1;
2309 else if (timeout
> 86400)
2313 * Log and return the timeout value...
2316 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "select_timeout: %ld seconds to %s",
2324 * 'usage()' - Show scheduler usage.
2328 usage(int status
) /* O - Exit status */
2330 _cupsLangPuts(status
? stderr
: stdout
,
2331 _("Usage: cupsd [-c config-file] [-f] [-F] [-h] [-l]\n"
2333 "-c config-file Load alternate configuration file\n"
2334 "-f Run in the foreground\n"
2335 "-F Run in the foreground but detach\n"
2336 "-h Show this usage message\n"
2337 "-l Run cupsd from launchd(8)\n"));
2343 * End of "$Id: main.c 6090 2006-11-14 16:35:27Z mike $".