]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/main.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / main.c
1 /*
2 * "$Id: main.c 5231 2006-03-05 17:22:27Z mike $"
3 *
4 * Scheduler main loop for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
7 *
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
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
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
36 * listening fds.
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
45 * scheduler.
46 * sigterm_handler() - Handle 'terminate' signals that stop the
47 * scheduler.
48 * select_timeout() - Calculate the select timeout value.
49 * usage() - Show scheduler usage.
50 */
51
52 /*
53 * Include necessary headers...
54 */
55
56 #define _MAIN_C_
57 #include "cupsd.h"
58 #include <sys/resource.h>
59 #include <syslog.h>
60 #include <grp.h>
61
62 #ifdef HAVE_LAUNCH_H
63 # include <launch.h>
64 # include <libgen.h>
65 #endif /* HAVE_LAUNCH_H */
66
67 #if defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
68 # include <malloc.h>
69 #endif /* HAVE_MALLOC_H && HAVE_MALLINFO */
70 #ifdef HAVE_NOTIFY_H
71 # include <notify.h>
72 #endif /* HAVE_NOTIFY_H */
73
74
75 /*
76 * Local functions...
77 */
78
79 #ifdef HAVE_LAUNCHD
80 static void launchd_checkin(void);
81 static void launchd_reload(void);
82 static int launchd_sync_conf(void);
83 #endif /* HAVE_LAUNCHD */
84
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);
92
93
94 /*
95 * Local globals...
96 */
97
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? */
105
106
107 /*
108 * 'main()' - Main entry for the CUPS scheduler.
109 */
110
111 int /* O - Exit status */
112 main(int argc, /* I - Number of command-line args */
113 char *argv[]) /* I - Command-line arguments */
114 {
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 */
138 #ifdef __sgi
139 cups_file_t *fp; /* Fake lpsched lock file */
140 struct stat statbuf; /* Needed for checking lpsched FIFO */
141 #endif /* __sgi */
142 #if HAVE_LAUNCHD
143 int launchd_idle_exit;
144 /* Idle exit on select timeout? */
145 #endif /* HAVE_LAUNCHD */
146
147
148 /*
149 * Check for command-line arguments...
150 */
151
152 fg = 0;
153
154 for (i = 1; i < argc; i ++)
155 if (argv[i][0] == '-')
156 for (opt = argv[i] + 1; *opt != '\0'; opt ++)
157 switch (*opt)
158 {
159 case 'c' : /* Configuration file */
160 i ++;
161 if (i >= argc)
162 {
163 _cupsLangPuts(stderr, _("cupsd: Expected config filename "
164 "after \"-c\" option!\n"));
165 usage(1);
166 }
167
168 if (argv[i][0] == '/')
169 {
170 /*
171 * Absolute directory...
172 */
173
174 cupsdSetString(&ConfigurationFile, argv[i]);
175 }
176 else
177 {
178 /*
179 * Relative directory...
180 */
181
182 char *current; /* Current directory */
183
184
185 /*
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.
190 */
191
192 current = malloc(1024);
193 getcwd(current, 1024);
194
195 cupsdSetStringf(&ConfigurationFile, "%s/%s", current, argv[i]);
196
197 free(current);
198 }
199 break;
200
201 case 'f' : /* Run in foreground... */
202 fg = 1;
203 break;
204
205 case 'F' : /* Run in foreground, but disconnect from terminal... */
206 fg = -1;
207 break;
208
209 case 'h' : /* Show usage/help */
210 usage(0);
211 break;
212
213 case 'l' : /* Started by launchd... */
214 #ifdef HAVE_LAUNCHD
215 Launchd = 1;
216 fg = 1;
217 #else
218 _cupsLangPuts(stderr, _("cupsd: launchd(8) support not compiled "
219 "in, running in normal mode.\n"));
220 fg = 0;
221 #endif /* HAVE_LAUNCHD */
222 break;
223
224 default : /* Unknown option */
225 _cupsLangPrintf(stderr, _("cupsd: Unknown option \"%c\" - "
226 "aborting!\n"), *opt);
227 usage(1);
228 break;
229 }
230 else
231 {
232 _cupsLangPrintf(stderr, _("cupsd: Unknown argument \"%s\" - aborting!\n"),
233 argv[i]);
234 usage(1);
235 }
236
237 if (!ConfigurationFile)
238 cupsdSetString(&ConfigurationFile, CUPS_SERVERROOT "/cupsd.conf");
239
240 /*
241 * If the user hasn't specified "-f", run in the background...
242 */
243
244 if (!fg)
245 {
246 /*
247 * Setup signal handlers for the parent...
248 */
249
250 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
251 sigset(SIGUSR1, parent_handler);
252 sigset(SIGCHLD, parent_handler);
253
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);
262
263 sigemptyset(&action.sa_mask);
264 action.sa_handler = SIG_IGN;
265 sigaction(SIGHUP, &action, NULL);
266 #else
267 signal(SIGUSR1, parent_handler);
268 signal(SIGCLD, parent_handler);
269
270 signal(SIGHUP, SIG_IGN);
271 #endif /* HAVE_SIGSET */
272
273 if (fork() > 0)
274 {
275 /*
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...
279 */
280
281 for (; parent_signal == 0;)
282 sleep(1);
283
284 if (parent_signal == SIGUSR1)
285 return (0);
286
287 if (wait(&i) < 0)
288 {
289 perror("cupsd");
290 return (1);
291 }
292 else if (WIFEXITED(i))
293 {
294 fprintf(stderr, "cupsd: Child exited with status %d!\n",
295 WEXITSTATUS(i));
296 return (2);
297 }
298 else
299 {
300 fprintf(stderr, "cupsd: Child exited on signal %d!\n", WTERMSIG(i));
301 return (3);
302 }
303 }
304 }
305
306 if (fg < 1)
307 {
308 /*
309 * Make sure we aren't tying up any filesystems...
310 */
311
312 chdir("/");
313
314 #ifndef DEBUG
315 /*
316 * Disable core dumps...
317 */
318
319 getrlimit(RLIMIT_CORE, &limit);
320 limit.rlim_cur = 0;
321 setrlimit(RLIMIT_CORE, &limit);
322
323 /*
324 * Disconnect from the controlling terminal...
325 */
326
327 setsid();
328
329 /*
330 * Close all open files...
331 */
332
333 getrlimit(RLIMIT_NOFILE, &limit);
334
335 for (i = 0; i < limit.rlim_cur; i ++)
336 close(i);
337 #endif /* DEBUG */
338 }
339
340 /*
341 * Set the timezone info...
342 */
343
344 tzset();
345
346 #ifdef LC_TIME
347 setlocale(LC_TIME, "");
348 #endif /* LC_TIME */
349
350 /*
351 * Set the maximum number of files...
352 */
353
354 getrlimit(RLIMIT_NOFILE, &limit);
355
356 if (limit.rlim_max > CUPS_MAX_FDS)
357 MaxFDs = CUPS_MAX_FDS;
358 else
359 MaxFDs = limit.rlim_max;
360
361 limit.rlim_cur = MaxFDs;
362
363 setrlimit(RLIMIT_NOFILE, &limit);
364
365 /*
366 * Allocate memory for the input and output sets...
367 */
368
369 SetSize = (MaxFDs + 31) / 8 + 4;
370 if (SetSize < sizeof(fd_set))
371 SetSize = sizeof(fd_set);
372
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);
377
378 if (InputSet == NULL || OutputSet == NULL || input == NULL || output == NULL)
379 {
380 syslog(LOG_LPR, "Unable to allocate memory for select() sets - exiting!");
381 return (1);
382 }
383
384 /*
385 * Read configuration...
386 */
387
388 if (!cupsdReadConfiguration())
389 {
390 syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
391 ConfigurationFile);
392 return (1);
393 }
394
395 #if HAVE_LAUNCHD
396 if (Launchd)
397 {
398 /*
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...
402 */
403
404 if (launchd_sync_conf())
405 {
406 launchd_reload();
407
408 /*
409 * Until rdar://3854821 is fixed we have to exit after the reload...
410 */
411
412 cupsdLogMessage(CUPSD_LOG_DEBUG2, "Exiting on launchd_reload");
413 exit(0);
414 }
415
416 launchd_checkin();
417 }
418 #endif /* HAVE_LAUNCHD */
419
420 /*
421 * Startup the server...
422 */
423
424 cupsdStartServer();
425
426 /*
427 * Catch hangup and child signals and ignore broken pipes...
428 */
429
430 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
431 sigset(SIGHUP, sighup_handler);
432 sigset(SIGPIPE, SIG_IGN);
433 sigset(SIGTERM, sigterm_handler);
434 #elif defined(HAVE_SIGACTION)
435 memset(&action, 0, sizeof(action));
436
437 sigemptyset(&action.sa_mask);
438 sigaddset(&action.sa_mask, SIGHUP);
439 action.sa_handler = sighup_handler;
440 sigaction(SIGHUP, &action, NULL);
441
442 sigemptyset(&action.sa_mask);
443 action.sa_handler = SIG_IGN;
444 sigaction(SIGPIPE, &action, NULL);
445
446 sigemptyset(&action.sa_mask);
447 sigaddset(&action.sa_mask, SIGTERM);
448 sigaddset(&action.sa_mask, SIGCHLD);
449 action.sa_handler = sigterm_handler;
450 sigaction(SIGTERM, &action, NULL);
451 #else
452 signal(SIGHUP, sighup_handler);
453 signal(SIGPIPE, SIG_IGN);
454 signal(SIGTERM, sigterm_handler);
455 #endif /* HAVE_SIGSET */
456
457 #ifdef __sgi
458 /*
459 * Try to create a fake lpsched lock file if one is not already there.
460 * Some Adobe applications need it under IRIX in order to enable
461 * printing...
462 */
463
464 if ((fp = cupsFileOpen("/var/spool/lp/SCHEDLOCK", "w")) == NULL)
465 {
466 syslog(LOG_LPR, "Unable to create fake lpsched lock file "
467 "\"/var/spool/lp/SCHEDLOCK\"\' - %s!",
468 strerror(errno));
469 }
470 else
471 {
472 fchmod(cupsFileNumber(fp), 0644);
473 fchown(cupsFileNumber(fp), User, Group);
474
475 cupsFileClose(fp);
476 }
477 #endif /* __sgi */
478
479 /*
480 * Initialize authentication certificates...
481 */
482
483 cupsdInitCerts();
484
485 /*
486 * If we are running in the background, signal the parent process that
487 * we are up and running...
488 */
489
490 if (!fg)
491 {
492 /*
493 * Send a signal to the parent process, but only if the parent is
494 * not PID 1 (init). This avoids accidentally shutting down the
495 * system on OpenBSD if you CTRL-C the server before it is up...
496 */
497
498 i = getppid(); /* Save parent PID to avoid race condition */
499
500 if (i != 1)
501 kill(i, SIGUSR1);
502 }
503
504 /*
505 * Start power management framework...
506 */
507
508 cupsdStartSystemMonitor();
509
510 /*
511 * Catch signals...
512 */
513
514 cupsdCatchChildSignals();
515
516 /*
517 * Start any pending print jobs...
518 */
519
520 cupsdCheckJobs();
521
522 /*
523 * Loop forever...
524 */
525
526 mallinfo_time = 0;
527 browse_time = time(NULL);
528 senddoc_time = time(NULL);
529 expire_time = time(NULL);
530 fds = 1;
531
532 while (!stop_scheduler)
533 {
534 #ifdef DEBUG
535 cupsdLogMessage(CUPSD_LOG_DEBUG2,
536 "main: Top of loop, dead_children=%d, NeedReload=%d",
537 dead_children, NeedReload);
538 #endif /* DEBUG */
539
540 /*
541 * Check if there are dead children to handle...
542 */
543
544 if (dead_children)
545 process_children();
546
547 /*
548 * Check if we need to load the server configuration file...
549 */
550
551 if (NeedReload)
552 {
553 /*
554 * Close any idle clients...
555 */
556
557 if (cupsArrayCount(Clients) > 0)
558 {
559 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
560 con;
561 con = (cupsd_client_t *)cupsArrayNext(Clients))
562 if (con->http.state == HTTP_WAITING)
563 cupsdCloseClient(con);
564 else
565 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
566
567 cupsdPauseListening();
568 }
569
570 /*
571 * Check for any active jobs...
572 */
573
574 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
575 job;
576 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
577 if (job->state_value == IPP_JOB_PROCESSING)
578 break;
579
580 /*
581 * Restart if all clients are closed and all jobs finished, or
582 * if the reload timeout has elapsed...
583 */
584
585 if ((cupsArrayCount(Clients) == 0 &&
586 (!job || NeedReload != RELOAD_ALL)) ||
587 (time(NULL) - ReloadTime) >= ReloadTimeout)
588 {
589 /*
590 * Shutdown the server...
591 */
592
593 cupsdStopServer();
594
595 /*
596 * Read configuration...
597 */
598
599 if (!cupsdReadConfiguration())
600 {
601 syslog(LOG_LPR, "Unable to read configuration file \'%s\' - exiting!",
602 ConfigurationFile);
603 break;
604 }
605
606 #if HAVE_LAUNCHD
607 if (Launchd)
608 {
609 if (launchd_sync_conf())
610 {
611 launchd_reload();
612
613 /*
614 * Until rdar://3854821 is fixed we have to exit after the reload...
615 */
616
617 cupsdLogMessage(CUPSD_LOG_DEBUG2, "Exiting on launchd_reload");
618 stop_scheduler = 1;
619 break;
620 }
621
622 launchd_checkin();
623 }
624 #endif /* HAVE_LAUNCHD */
625
626 /*
627 * Startup the server...
628 */
629
630 cupsdStartServer();
631 }
632 }
633
634 /*
635 * Check for available input or ready output. If select() returns
636 * 0 or -1, something bad happened and we should exit immediately.
637 *
638 * Note that we at least have one listening socket open at all
639 * times.
640 */
641
642 memcpy(input, InputSet, SetSize);
643 memcpy(output, OutputSet, SetSize);
644
645 timeout.tv_sec = select_timeout(fds);
646 timeout.tv_usec = 0;
647
648 #if HAVE_LAUNCHD
649 /*
650 * If no other work is scheduled and we're being controlled by
651 * launchd(8) then timeout after 'LaunchdTimeout' seconds of
652 * inactivity...
653 */
654
655 if (timeout.tv_sec == 86400 && Launchd && LaunchdTimeout &&
656 (!Browsing || !(BrowseLocalProtocols & BROWSE_DNSSD) ||
657 cupsArrayCount(Printers) == 0))
658 {
659 timeout.tv_sec = LaunchdTimeout;
660 launchd_idle_exit = 1;
661 }
662 else
663 launchd_idle_exit = 0;
664 #endif /* HAVE_LAUNCHD */
665
666 if (timeout.tv_sec < 86400) /* Only use timeout for < 1 day */
667 fds = select(MaxFDs, input, output, NULL, &timeout);
668 else
669 fds = select(MaxFDs, input, output, NULL, NULL);
670
671 if (fds < 0)
672 {
673 char s[16384], /* String buffer */
674 *sptr; /* Pointer into buffer */
675 int slen; /* Length of string buffer */
676
677
678 /*
679 * Got an error from select!
680 */
681
682 if (errno == EINTR) /* Just interrupted by a signal */
683 continue;
684
685 /*
686 * Log all sorts of debug info to help track down the problem.
687 */
688
689 cupsdLogMessage(CUPSD_LOG_EMERG, "select() failed - %s!",
690 strerror(errno));
691
692 strcpy(s, "InputSet =");
693 slen = 10;
694 sptr = s + 10;
695
696 for (i = 0; i < MaxFDs; i ++)
697 if (FD_ISSET(i, InputSet))
698 {
699 snprintf(sptr, sizeof(s) - slen, " %d", i);
700 slen += strlen(sptr);
701 sptr += strlen(sptr);
702 }
703
704 cupsdLogMessage(CUPSD_LOG_EMERG, s);
705
706 strcpy(s, "OutputSet =");
707 slen = 11;
708 sptr = s + 11;
709
710 for (i = 0; i < MaxFDs; i ++)
711 if (FD_ISSET(i, OutputSet))
712 {
713 snprintf(sptr, sizeof(s) - slen, " %d", i);
714 slen += strlen(sptr);
715 sptr += strlen(sptr);
716 }
717
718 cupsdLogMessage(CUPSD_LOG_EMERG, s);
719
720 for (i = 0, con = (cupsd_client_t *)cupsArrayFirst(Clients);
721 con;
722 i ++, con = (cupsd_client_t *)cupsArrayNext(Clients))
723 cupsdLogMessage(CUPSD_LOG_EMERG,
724 "Clients[%d] = %d, file = %d, state = %d",
725 i, con->http.fd, con->file, con->http.state);
726
727 for (i = 0, lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
728 lis;
729 i ++, lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
730 cupsdLogMessage(CUPSD_LOG_EMERG, "Listeners[%d] = %d", i, lis->fd);
731
732 cupsdLogMessage(CUPSD_LOG_EMERG, "BrowseSocket = %d", BrowseSocket);
733
734 cupsdLogMessage(CUPSD_LOG_EMERG, "CGIPipes[0] = %d", CGIPipes[0]);
735
736 #ifdef __APPLE__
737 cupsdLogMessage(CUPSD_LOG_EMERG, "SysEventPipes[0] = %d",
738 SysEventPipes[0]);
739 #endif /* __APPLE__ */
740
741 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
742 job;
743 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
744 cupsdLogMessage(CUPSD_LOG_EMERG, "Jobs[%d] = %d < [%d %d] > [%d %d]",
745 job->id,
746 job->status_buffer ? job->status_buffer->fd : -1,
747 job->print_pipes[0], job->print_pipes[1],
748 job->back_pipes[0], job->back_pipes[1]);
749 break;
750 }
751
752 current_time = time(NULL);
753
754 #if HAVE_LAUNCHD
755 /*
756 * If no other work was scheduled and we're being controlled by launchd(8)
757 * then timeout after 'LaunchdTimeout' seconds of inactivity...
758 */
759
760 if (!fds && launchd_idle_exit)
761 {
762 cupsdLogMessage(CUPSD_LOG_INFO,
763 "Printer sharing is off and there are no jobs pending, "
764 "will restart on demand.");
765 stop_scheduler = 1;
766 break;
767 }
768 #endif /* HAVE_LAUNCHD */
769
770 /*
771 * Check for status info from job filters...
772 */
773
774 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
775 job;
776 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
777 if (job->status_buffer && FD_ISSET(job->status_buffer->fd, input))
778 {
779 /*
780 * Clear the input bit to avoid updating the next job
781 * using the same status pipe file descriptor...
782 */
783
784 FD_CLR(job->status_buffer->fd, input);
785
786 /*
787 * Read any status messages from the filters...
788 */
789
790 cupsdUpdateJob(job);
791 }
792
793 /*
794 * Update CGI messages as needed...
795 */
796
797 if (CGIPipes[0] >= 0 && FD_ISSET(CGIPipes[0], input))
798 cupsdUpdateCGI();
799
800 /*
801 * Handle system management events as needed...
802 */
803
804 #ifdef __APPLE__
805 if (SysEventPipes[0] >= 0 && FD_ISSET(SysEventPipes[0], input))
806 cupsdUpdateSystemMonitor();
807 #endif /* __APPLE__ */
808
809 /*
810 * Update notifier messages as needed...
811 */
812
813 if (NotifierPipes[0] >= 0 && FD_ISSET(NotifierPipes[0], input))
814 cupsdUpdateNotifierStatus();
815
816 /*
817 * Expire subscriptions and unload completed jobs as needed...
818 */
819
820 if (current_time > expire_time)
821 {
822 if (cupsArrayCount(Subscriptions) > 0)
823 cupsdExpireSubscriptions(NULL, NULL);
824
825 cupsdUnloadCompletedJobs();
826
827 expire_time = current_time;
828 }
829
830 /*
831 * Update the browse list as needed...
832 */
833
834 if (Browsing && BrowseRemoteProtocols)
835 {
836 if (BrowseSocket >= 0 && FD_ISSET(BrowseSocket, input))
837 cupsdUpdateCUPSBrowse();
838
839 if (PollPipe >= 0 && FD_ISSET(PollPipe, input))
840 cupsdUpdatePolling();
841
842 #ifdef HAVE_LIBSLP
843 if ((BrowseRemoteProtocols & BROWSE_SLP) &&
844 BrowseSLPRefresh <= current_time)
845 cupsdUpdateSLPBrowse();
846 #endif /* HAVE_LIBSLP */
847
848 #ifdef HAVE_LDAP
849 if ((BrowseRemoteProtocols & BROWSE_LDAP) &&
850 BrowseLDAPRefresh <= current_time)
851 cupsdUpdateLDAPBrowse();
852 #endif /* HAVE_LDAP */
853 }
854
855 if (Browsing && BrowseLocalProtocols && current_time > browse_time)
856 {
857 cupsdSendBrowseList();
858 browse_time = current_time;
859 }
860
861 /*
862 * Check for new connections on the "listen" sockets...
863 */
864
865 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
866 lis;
867 lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
868 if (lis->fd >= 0 && FD_ISSET(lis->fd, input))
869 {
870 FD_CLR(lis->fd, input);
871 cupsdAcceptClient(lis);
872 }
873
874 /*
875 * Check for new data on the client sockets...
876 */
877
878 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
879 con;
880 con = (cupsd_client_t *)cupsArrayNext(Clients))
881 {
882 /*
883 * Process the input buffer...
884 */
885
886 if (FD_ISSET(con->http.fd, input) || con->http.used)
887 {
888 int fd = con->file;
889
890
891 FD_CLR(con->http.fd, input);
892
893 if (!cupsdReadClient(con))
894 {
895 if (fd >= 0)
896 FD_CLR(fd, input);
897
898 continue;
899 }
900 }
901
902 /*
903 * Write data as needed...
904 */
905
906 if (con->pipe_pid && FD_ISSET(con->file, input))
907 {
908 /*
909 * Keep track of pending input from the file/pipe separately
910 * so that we don't needlessly spin on select() when the web
911 * client is not ready to receive data...
912 */
913
914 FD_CLR(con->file, input);
915 con->file_ready = 1;
916
917 #ifdef DEBUG
918 cupsdLogMessage(CUPSD_LOG_DEBUG2, "main: Data ready file %d!",
919 con->file);
920 #endif /* DEBUG */
921
922 if (!FD_ISSET(con->http.fd, output))
923 {
924 cupsdLogMessage(CUPSD_LOG_DEBUG2,
925 "main: Removing fd %d from InputSet...", con->file);
926 FD_CLR(con->file, input);
927 FD_CLR(con->file, InputSet);
928 }
929 }
930
931 if (FD_ISSET(con->http.fd, output))
932 {
933 FD_CLR(con->http.fd, output);
934
935 if (!con->pipe_pid || con->file_ready)
936 if (!cupsdWriteClient(con))
937 continue;
938 }
939
940 /*
941 * Check the activity and close old clients...
942 */
943
944 activity = current_time - Timeout;
945 if (con->http.activity < activity && !con->pipe_pid)
946 {
947 cupsdLogMessage(CUPSD_LOG_DEBUG,
948 "Closing client %d after %d seconds of inactivity...",
949 con->http.fd, Timeout);
950
951 cupsdCloseClient(con);
952 continue;
953 }
954 }
955
956 /*
957 * Update any pending multi-file documents...
958 */
959
960 if ((current_time - senddoc_time) >= 10)
961 {
962 cupsdCheckJobs();
963 senddoc_time = current_time;
964 }
965
966 /*
967 * Log memory usage every minute...
968 */
969
970 if ((current_time - mallinfo_time) >= 60 && LogLevel >= CUPSD_LOG_DEBUG2)
971 {
972 #ifdef HAVE_MALLINFO
973 struct mallinfo mem; /* Malloc information */
974
975
976 mem = mallinfo();
977 cupsdLogMessage(CUPSD_LOG_DEBUG2,
978 "mallinfo: arena = %d, used = %d, free = %d\n",
979 mem.arena, mem.usmblks + mem.uordblks,
980 mem.fsmblks + mem.fordblks);
981 #endif /* HAVE_MALLINFO */
982
983 string_count = _cupsStrStatistics(&alloc_bytes, &total_bytes);
984 cupsdLogMessage(CUPSD_LOG_DEBUG2,
985 "stringpool: " CUPS_LLFMT " strings, "
986 CUPS_LLFMT " allocated, " CUPS_LLFMT " total bytes",
987 CUPS_LLCAST string_count, CUPS_LLCAST alloc_bytes,
988 CUPS_LLCAST total_bytes);
989
990 mallinfo_time = current_time;
991 }
992
993 /*
994 * Update the root certificate once every 5 minutes...
995 */
996
997 if ((current_time - RootCertTime) >= RootCertDuration && RootCertDuration &&
998 !RunUser)
999 {
1000 /*
1001 * Update the root certificate...
1002 */
1003
1004 cupsdDeleteCert(0);
1005 cupsdAddCert(0, "root");
1006 }
1007
1008 /*
1009 * Handle OS-specific event notification for any events that have
1010 * accumulated. Don't send these more than once a second...
1011 */
1012
1013 if (LastEvent && (time(NULL) - LastEventTime) > 1)
1014 {
1015 #ifdef HAVE_NOTIFY_POST
1016 if (LastEvent & CUPSD_EVENT_PRINTER_CHANGED)
1017 {
1018 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1019 "notify_post(\"com.apple.printerListChange\")");
1020 notify_post("com.apple.printerListChange");
1021 }
1022
1023 if (LastEvent & CUPSD_EVENT_PRINTER_STATE_CHANGED)
1024 {
1025 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1026 "notify_post(\"com.apple.printerHistoryChange\")");
1027 notify_post("com.apple.printerHistoryChange");
1028 }
1029
1030 if (LastEvent & (CUPSD_EVENT_JOB_STATE_CHANGED |
1031 CUPSD_EVENT_JOB_CONFIG_CHANGED |
1032 CUPSD_EVENT_JOB_PROGRESS))
1033 {
1034 cupsdLogMessage(CUPSD_LOG_DEBUG2,
1035 "notify_post(\"com.apple.jobChange\")");
1036 notify_post("com.apple.jobChange");
1037 }
1038 #endif /* HAVE_NOTIFY_POST */
1039
1040 /*
1041 * Reset the accumulated events and notification time...
1042 */
1043
1044 LastEventTime = time(NULL);
1045 LastEvent = CUPSD_EVENT_NONE;
1046 }
1047 }
1048
1049 /*
1050 * Log a message based on what happened...
1051 */
1052
1053 if (stop_scheduler)
1054 cupsdLogMessage(CUPSD_LOG_INFO, "Scheduler shutting down normally.");
1055 else
1056 cupsdLogMessage(CUPSD_LOG_ERROR,
1057 "Scheduler shutting down due to program error.");
1058
1059 /*
1060 * Close all network clients and stop all jobs...
1061 */
1062
1063 cupsdStopServer();
1064
1065 cupsdFreeAllJobs();
1066
1067 cupsdStopSystemMonitor();
1068
1069 #ifdef HAVE_LAUNCHD
1070 /*
1071 * Update the launchd config file as needed...
1072 */
1073
1074 launchd_sync_conf();
1075 #endif /* HAVE_LAUNCHD */
1076
1077 #ifdef __sgi
1078 /*
1079 * Remove the fake IRIX lpsched lock file, but only if the existing
1080 * file is not a FIFO which indicates that the real IRIX lpsched is
1081 * running...
1082 */
1083
1084 if (!stat("/var/spool/lp/FIFO", &statbuf))
1085 if (!S_ISFIFO(statbuf.st_mode))
1086 unlink("/var/spool/lp/SCHEDLOCK");
1087 #endif /* __sgi */
1088
1089 /*
1090 * Free memory used by FD sets and return...
1091 */
1092
1093 free(InputSet);
1094 free(OutputSet);
1095 free(input);
1096 free(output);
1097
1098 return (!stop_scheduler);
1099 }
1100
1101
1102 /*
1103 * 'cupsdClosePipe()' - Close a pipe as necessary.
1104 */
1105
1106 void
1107 cupsdClosePipe(int *fds) /* I - Pipe file descriptors (2) */
1108 {
1109 /*
1110 * Close file descriptors as needed...
1111 */
1112
1113 if (fds[0] >= 0)
1114 {
1115 close(fds[0]);
1116 fds[0] = -1;
1117 }
1118
1119 if (fds[1] >= 0)
1120 {
1121 close(fds[1]);
1122 fds[1] = -1;
1123 }
1124 }
1125
1126
1127 /*
1128 * 'cupsdOpenPipe()' - Create a pipe which is closed on exec.
1129 */
1130
1131 int /* O - 0 on success, -1 on error */
1132 cupsdOpenPipe(int *fds) /* O - Pipe file descriptors (2) */
1133 {
1134 /*
1135 * Create the pipe...
1136 */
1137
1138 if (pipe(fds))
1139 return (-1);
1140
1141 /*
1142 * Set the "close on exec" flag on each end of the pipe...
1143 */
1144
1145 if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
1146 {
1147 close(fds[0]);
1148 close(fds[1]);
1149 return (-1);
1150 }
1151
1152 if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
1153 {
1154 close(fds[0]);
1155 close(fds[1]);
1156 return (-1);
1157 }
1158
1159 /*
1160 * Return 0 indicating success...
1161 */
1162
1163 return (0);
1164 }
1165
1166
1167 /*
1168 * 'cupsdCatchChildSignals()' - Catch SIGCHLD signals...
1169 */
1170
1171 void
1172 cupsdCatchChildSignals(void)
1173 {
1174 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1175 struct sigaction action; /* Actions for POSIX signals */
1176 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1177
1178
1179 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1180 sigset(SIGCHLD, sigchld_handler);
1181 #elif defined(HAVE_SIGACTION)
1182 memset(&action, 0, sizeof(action));
1183
1184 sigemptyset(&action.sa_mask);
1185 sigaddset(&action.sa_mask, SIGTERM);
1186 sigaddset(&action.sa_mask, SIGCHLD);
1187 action.sa_handler = sigchld_handler;
1188 sigaction(SIGCHLD, &action, NULL);
1189 #else
1190 signal(SIGCLD, sigchld_handler); /* No, SIGCLD isn't a typo... */
1191 #endif /* HAVE_SIGSET */
1192 }
1193
1194
1195 /*
1196 * 'cupsdClearString()' - Clear a string.
1197 */
1198
1199 void
1200 cupsdClearString(char **s) /* O - String value */
1201 {
1202 if (s && *s)
1203 {
1204 free(*s);
1205 *s = NULL;
1206 }
1207 }
1208
1209
1210 /*
1211 * 'cupsdHoldSignals()' - Hold child and termination signals.
1212 */
1213
1214 void
1215 cupsdHoldSignals(void)
1216 {
1217 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1218 sigset_t newmask; /* New POSIX signal mask */
1219 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1220
1221
1222 holdcount ++;
1223 if (holdcount > 1)
1224 return;
1225
1226 #ifdef HAVE_SIGSET
1227 sighold(SIGTERM);
1228 sighold(SIGCHLD);
1229 #elif defined(HAVE_SIGACTION)
1230 sigemptyset(&newmask);
1231 sigaddset(&newmask, SIGTERM);
1232 sigaddset(&newmask, SIGCHLD);
1233 sigprocmask(SIG_BLOCK, &newmask, &holdmask);
1234 #endif /* HAVE_SIGSET */
1235 }
1236
1237
1238 /*
1239 * 'cupsdIgnoreChildSignals()' - Ignore SIGCHLD signals...
1240 *
1241 * We don't really ignore them, we set the signal handler to SIG_DFL,
1242 * since some OS's rely on signals for the wait4() function to work.
1243 */
1244
1245 void
1246 cupsdIgnoreChildSignals(void)
1247 {
1248 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
1249 struct sigaction action; /* Actions for POSIX signals */
1250 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
1251
1252
1253 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
1254 sigset(SIGCHLD, SIG_DFL);
1255 #elif defined(HAVE_SIGACTION)
1256 memset(&action, 0, sizeof(action));
1257
1258 sigemptyset(&action.sa_mask);
1259 sigaddset(&action.sa_mask, SIGCHLD);
1260 action.sa_handler = SIG_DFL;
1261 sigaction(SIGCHLD, &action, NULL);
1262 #else
1263 signal(SIGCLD, SIG_DFL); /* No, SIGCLD isn't a typo... */
1264 #endif /* HAVE_SIGSET */
1265 }
1266
1267
1268 /*
1269 * 'cupsdReleaseSignals()' - Release signals for delivery.
1270 */
1271
1272 void
1273 cupsdReleaseSignals(void)
1274 {
1275 holdcount --;
1276 if (holdcount > 0)
1277 return;
1278
1279 #ifdef HAVE_SIGSET
1280 sigrelse(SIGTERM);
1281 sigrelse(SIGCHLD);
1282 #elif defined(HAVE_SIGACTION)
1283 sigprocmask(SIG_SETMASK, &holdmask, NULL);
1284 #endif /* HAVE_SIGSET */
1285 }
1286
1287
1288 /*
1289 * 'cupsdSetString()' - Set a string value.
1290 */
1291
1292 void
1293 cupsdSetString(char **s, /* O - New string */
1294 const char *v) /* I - String value */
1295 {
1296 if (!s || *s == v)
1297 return;
1298
1299 if (*s)
1300 free(*s);
1301
1302 if (v)
1303 *s = strdup(v);
1304 else
1305 *s = NULL;
1306 }
1307
1308
1309 /*
1310 * 'cupsdSetStringf()' - Set a formatted string value.
1311 */
1312
1313 void
1314 cupsdSetStringf(char **s, /* O - New string */
1315 const char *f, /* I - Printf-style format string */
1316 ...) /* I - Additional args as needed */
1317 {
1318 char v[4096]; /* Formatting string value */
1319 va_list ap; /* Argument pointer */
1320 char *olds; /* Old string */
1321
1322
1323 if (!s)
1324 return;
1325
1326 olds = *s;
1327
1328 if (f)
1329 {
1330 va_start(ap, f);
1331 vsnprintf(v, sizeof(v), f, ap);
1332 va_end(ap);
1333
1334 *s = strdup(v);
1335 }
1336 else
1337 *s = NULL;
1338
1339 if (olds)
1340 free(olds);
1341 }
1342
1343
1344 #ifdef HAVE_LAUNCHD
1345 /*
1346 * 'launchd_checkin()' - Check-in with launchd and collect the listening fds.
1347 */
1348
1349 static void
1350 launchd_checkin(void)
1351 {
1352 int i, /* Looping var */
1353 count, /* Numebr of listeners */
1354 portnum; /* Port number */
1355 launch_data_t ld_msg, /* Launch data message */
1356 ld_resp, /* Launch data response */
1357 ld_array, /* Launch data array */
1358 ld_sockets, /* Launch data sockets dictionary */
1359 ld_runatload, /* Run-at-load setting */
1360 tmp; /* Launch data */
1361 cupsd_listener_t *lis; /* Listeners array */
1362 http_addr_t addr; /* Address variable */
1363 socklen_t addrlen; /* Length of address */
1364 bool runatload; /* Run-at-load setting value */
1365
1366
1367 cupsdLogMessage(CUPSD_LOG_DEBUG, "launchd_checkin: pid=%d", (int)getpid());
1368
1369 /*
1370 * Check-in with launchd...
1371 */
1372
1373 ld_msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
1374 if ((ld_resp = launch_msg(ld_msg)) == NULL)
1375 {
1376 cupsdLogMessage(CUPSD_LOG_ERROR,
1377 "launchd_checkin: launch_msg(\"" LAUNCH_KEY_CHECKIN
1378 "\") IPC failure");
1379 exit(EXIT_FAILURE);
1380 }
1381
1382 if (launch_data_get_type(ld_resp) == LAUNCH_DATA_ERRNO)
1383 {
1384 errno = launch_data_get_errno(ld_resp);
1385 cupsdLogMessage(CUPSD_LOG_ERROR, "launchd_checkin: Check-in failed: %s",
1386 strerror(errno));
1387 exit(EXIT_FAILURE);
1388 }
1389
1390 /*
1391 * Get the "run-at-load" setting...
1392 */
1393
1394 if ((ld_runatload =
1395 launch_data_dict_lookup(ld_resp, LAUNCH_JOBKEY_RUNATLOAD)) != NULL &&
1396 launch_data_get_type(ld_runatload) == LAUNCH_DATA_BOOL)
1397 runatload = launch_data_get_bool(ld_runatload);
1398 else
1399 {
1400 errno = launch_data_get_errno(ld_resp);
1401 cupsdLogMessage(CUPSD_LOG_ERROR,
1402 "launchd_checkin: Unable to find Run-at-load setting: %s",
1403 strerror(errno));
1404 exit(EXIT_FAILURE);
1405 }
1406
1407 cupsdLogMessage(CUPSD_LOG_DEBUG, "launchd_checkin: Run-at-load=%s",
1408 runatload ? "true" : "false");
1409
1410 /*
1411 * Get the sockets dictionary...
1412 */
1413
1414 if (!(ld_sockets = launch_data_dict_lookup(ld_resp, LAUNCH_JOBKEY_SOCKETS)))
1415 {
1416 cupsdLogMessage(CUPSD_LOG_ERROR,
1417 "launchd_checkin: No sockets found to answer requests on!");
1418 exit(EXIT_FAILURE);
1419 }
1420
1421 /*
1422 * Get the array of listener sockets...
1423 */
1424
1425 if (!(ld_array = launch_data_dict_lookup(ld_sockets, "Listeners")))
1426 {
1427 cupsdLogMessage(CUPSD_LOG_ERROR,
1428 "launchd_checkin: No sockets found to answer requests on!");
1429 exit(EXIT_FAILURE);
1430 }
1431
1432 /*
1433 * Add listening fd(s) to the Listener array...
1434 */
1435
1436 if (launch_data_get_type(ld_array) == LAUNCH_DATA_ARRAY)
1437 {
1438 /*
1439 * Free the listeners array built from cupsd.conf...
1440 */
1441
1442 cupsdDeleteAllListeners();
1443
1444 /*
1445 * Create a new array of listeners from the launchd data...
1446 */
1447
1448 Listeners = cupsArrayNew(NULL, NULL);
1449 count = launch_data_array_get_count(ld_array);
1450
1451 for (i = 0; i < count; i ++)
1452 {
1453 /*
1454 * Copy the current address and log it...
1455 */
1456
1457 if ((lis = calloc(1, sizeof(cupsd_listener_t))) == NULL)
1458 {
1459 cupsdLogMessage(CUPSD_LOG_ERROR,
1460 "launchd_checkin: Unable to allocate listener - %s.",
1461 strerror(errno));
1462 exit(EXIT_FAILURE);
1463 }
1464
1465 cupsArrayAdd(Listeners, lis);
1466
1467 tmp = launch_data_array_get_index(ld_array, i);
1468 lis->fd = launch_data_get_fd(tmp);
1469 addrlen = sizeof(lis->address);
1470
1471 if (getsockname(lis->fd, (struct sockaddr *)&(lis->address), &addrlen))
1472 {
1473 cupsdLogMessage(CUPSD_LOG_ERROR,
1474 "launchd_checkin: Unable to get local address - %s",
1475 strerror(errno));
1476 }
1477
1478 # ifdef HAVE_SSL
1479 portnum = 0;
1480
1481 # ifdef AF_INET6
1482 if (addr.addr.sa_family == AF_INET6)
1483 portnum = ntohs(addr.ipv6.sin6_port);
1484 else
1485 # endif /* AF_INET6 */
1486 if (addr.addr.sa_family == AF_INET)
1487 portnum = ntohs(addr.ipv4.sin_port);
1488
1489 if (portnum == 443)
1490 lis->encryption = HTTP_ENCRYPT_ALWAYS;
1491 # endif /* HAVE_SSL */
1492 }
1493 }
1494
1495 /*
1496 * Collect the browse socket (if there is one)...
1497 */
1498
1499 if ((ld_array = launch_data_dict_lookup(ld_sockets, "BrowseSockets")))
1500 {
1501 if (launch_data_get_type(ld_array) == LAUNCH_DATA_ARRAY)
1502 {
1503 tmp = launch_data_array_get_index(ld_array, 0);
1504
1505 if (launch_data_get_type(tmp) == LAUNCH_DATA_FD)
1506 {
1507 if (BrowseSocket != -1)
1508 close(BrowseSocket);
1509
1510 BrowseSocket = launch_data_get_fd(tmp);
1511 }
1512 else
1513 cupsdLogMessage(CUPSD_LOG_WARN,
1514 "launchd_checkin: BrowseSocket not a fd!");
1515 }
1516 else
1517 cupsdLogMessage(CUPSD_LOG_WARN,
1518 "launchd_checkin: BrowseSockets is not an array!");
1519 }
1520 else
1521 cupsdLogMessage(CUPSD_LOG_DEBUG, "launchd_checkin: No BrowseSockets");
1522
1523 launch_data_free(ld_msg);
1524 launch_data_free(ld_resp);
1525 }
1526
1527
1528 /*
1529 * 'launchd_reload()' - Tell launchd to reload the configuration file to pick
1530 * up the new listening directives.
1531 */
1532
1533 static void
1534 launchd_reload(void)
1535 {
1536 int child_status; /* Exit status of child process */
1537 pid_t child_pid, /* Child PID */
1538 waitpid_status; /* Child process exit status */
1539 char *argv[4]; /* Argument strings */
1540
1541
1542 /*
1543 * The current launchd doesn't support a reload option (rdar://3854821).
1544 * Until this is fixed we need to reload the config file by execing launchctl
1545 * twice (to unload then load). NOTE: This will cause us to exit on SIGTERM
1546 * which will cancel all client & job activity.
1547 *
1548 * After this is fixed we'll be able to tell launchd to reload the file
1549 * and pick up the new listening descriptors without disrupting current
1550 * activity.
1551 */
1552
1553 /*
1554 * Unloading the current configuration will cause launchd to send us a SIGTERM;
1555 * block it for now so we can get our work done...
1556 */
1557
1558 cupsdHoldSignals();
1559
1560 /*
1561 * Set up the unload arguments to launchctl...
1562 */
1563
1564 argv[0] = "/bin/launchctl";
1565 argv[1] = "unload";
1566 argv[2] = LaunchdConf;
1567 argv[3] = NULL;
1568
1569 if (cupsdStartProcess(argv[0], argv, NULL, -1, -1, -1, -1, 1, &child_pid) < 0)
1570 cupsdLogMessage(CUPSD_LOG_ERROR,
1571 "launchd_reload: Unable to execute %s - %s", argv[0],
1572 strerror(errno));
1573 else
1574 {
1575 do
1576 {
1577 waitpid_status = waitpid(child_pid, &child_status, 0);
1578 }
1579 while (waitpid_status == (pid_t)-1 && errno == EINTR);
1580
1581 if (WIFSIGNALED(child_status))
1582 cupsdLogMessage(CUPSD_LOG_DEBUG,
1583 "launchd_reload: %s pid %d crashed on signal %d!",
1584 basename(argv[0]), child_pid, WTERMSIG(child_status));
1585 else
1586 cupsdLogMessage(CUPSD_LOG_DEBUG,
1587 "launchd_reload: %s pid %d stopped with status %d!",
1588 basename(argv[0]), child_pid, WEXITSTATUS(child_status));
1589
1590 /*
1591 * Do it again with the load command...
1592 */
1593
1594 argv[1] = "load";
1595
1596 if (cupsdStartProcess(argv[0], argv, NULL, -1, -1, -1, -1, 1,
1597 &child_pid) < 0)
1598 {
1599 cupsdLogMessage(CUPSD_LOG_ERROR,
1600 "launchd_reload: Unable to fork for %s - %s", argv[0],
1601 strerror(errno));
1602 }
1603 else
1604 {
1605 do
1606 {
1607 waitpid_status = waitpid(child_pid, &child_status, 0);
1608 } while (waitpid_status == (pid_t)-1 && errno == EINTR);
1609
1610 if (WIFSIGNALED(child_status))
1611 cupsdLogMessage(CUPSD_LOG_DEBUG,
1612 "launchd_reload: %s pid %d crashed on signal %d!",
1613 basename(argv[0]), child_pid, WTERMSIG(child_status));
1614 else
1615 cupsdLogMessage(CUPSD_LOG_DEBUG,
1616 "launchd_reload: %s pid %d stopped with status %d",
1617 basename(argv[0]), child_pid,
1618 WEXITSTATUS(child_status));
1619 }
1620 }
1621
1622 /*
1623 * Leave signals blocked since exit() will be called momentarily anyways...
1624 */
1625 }
1626
1627
1628 /*
1629 * 'launchd_sync_conf()' - Re-write the launchd(8) config file
1630 * org.cups.cupsd.plist based on cupsd.conf.
1631 */
1632
1633 static int /* O - 1 if the file was updated */
1634 launchd_sync_conf(void)
1635 {
1636 int portnum; /* Port number */
1637 CFMutableDictionaryRef cupsd_dict, /* org.cups.cupsd.plist dictionary */
1638 sockets, /* Sockets dictionary */
1639 listener; /* Listener dictionary */
1640 CFDataRef resourceData; /* XML property list */
1641 CFMutableArrayRef array; /* Array */
1642 CFNumberRef socket_mode; /* Domain socket mode bits */
1643 CFStringRef socket_path; /* Domain socket path */
1644 CFTypeRef value; /* CF value */
1645 CFURLRef fileURL; /* File URL */
1646 SInt32 errorCode; /* Error code */
1647 cupsd_listener_t *lis; /* Current listening socket */
1648 struct servent *service; /* Services data base entry */
1649 char temp[1024]; /* Temporary buffer for value */
1650 struct stat cupsd_sb, /* File info for cupsd.conf */
1651 launchd_sb; /* File info for org.cups.cupsd.plist */
1652
1653
1654 /*
1655 * If the launchd conf file modification time is newer than the cupsd.conf
1656 * time then there's nothing to do...
1657 */
1658
1659 if (!stat(ConfigurationFile, &cupsd_sb) &&
1660 !stat(LaunchdConf, &launchd_sb) &&
1661 launchd_sb.st_mtimespec.tv_sec >= cupsd_sb.st_mtimespec.tv_sec)
1662 {
1663 cupsdLogMessage(CUPSD_LOG_DEBUG,
1664 "launchd_sync_conf: Nothing to do, pid=%d.",
1665 (int)getpid());
1666 return (0);
1667 }
1668
1669 /*
1670 * Time to write a new 'org.cups.cupsd.plist' file.
1671 * Create the new dictionary and populate it with values...
1672 */
1673
1674 if ((cupsd_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1675 &kCFTypeDictionaryKeyCallBacks,
1676 &kCFTypeDictionaryValueCallBacks)) != NULL)
1677 {
1678 CFDictionaryAddValue(cupsd_dict, CFSTR(LAUNCH_JOBKEY_LABEL),
1679 CFSTR("org.cups.cupsd"));
1680 CFDictionaryAddValue(cupsd_dict, CFSTR(LAUNCH_JOBKEY_ONDEMAND),
1681 kCFBooleanTrue);
1682
1683 if ((Browsing && BrowseLocalProtocols && cupsArrayCount(Printers)) ||
1684 cupsArrayCount(ActiveJobs))
1685 CFDictionaryAddValue(cupsd_dict, CFSTR(LAUNCH_JOBKEY_RUNATLOAD),
1686 kCFBooleanTrue);
1687 else
1688 CFDictionaryAddValue(cupsd_dict, CFSTR(LAUNCH_JOBKEY_RUNATLOAD),
1689 kCFBooleanFalse);
1690
1691 #ifdef LAUNCH_JOBKEY_SERVICEIPC
1692 CFDictionaryAddValue(cupsd_dict, CFSTR(LAUNCH_JOBKEY_SERVICEIPC),
1693 kCFBooleanTrue);
1694 #endif /* LAUNCH_JOBKEY_SERVICEIPC */
1695
1696 if ((array = CFArrayCreateMutable(kCFAllocatorDefault, 2,
1697 &kCFTypeArrayCallBacks)) != NULL)
1698 {
1699 CFDictionaryAddValue(cupsd_dict, CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS),
1700 array);
1701 CFArrayAppendValue(array, CFSTR("/usr/sbin/cupsd"));
1702 CFArrayAppendValue(array, CFSTR("-l"));
1703 CFRelease(array);
1704 }
1705
1706 /*
1707 * Add a sockets dictionary...
1708 */
1709
1710 if ((sockets = (CFMutableDictionaryRef)CFDictionaryCreateMutable(
1711 kCFAllocatorDefault, 0,
1712 &kCFTypeDictionaryKeyCallBacks,
1713 &kCFTypeDictionaryValueCallBacks)) != NULL)
1714 {
1715 CFDictionaryAddValue(cupsd_dict, CFSTR(LAUNCH_JOBKEY_SOCKETS), sockets);
1716
1717 /*
1718 * Add a Listeners array to the sockets dictionary...
1719 */
1720
1721 if ((array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
1722 &kCFTypeArrayCallBacks)) != NULL)
1723 {
1724 CFDictionaryAddValue(sockets, CFSTR("Listeners"), array);
1725
1726 /*
1727 * For each listener add a dictionary to the listeners array...
1728 */
1729
1730 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
1731 lis;
1732 lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
1733 {
1734 if ((listener = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1735 &kCFTypeDictionaryKeyCallBacks,
1736 &kCFTypeDictionaryValueCallBacks)) != NULL)
1737 {
1738 CFArrayAppendValue(array, listener);
1739
1740 # ifdef AF_LOCAL
1741 if (lis->address.addr.sa_family == AF_LOCAL)
1742 {
1743 if ((socket_path = CFStringCreateWithCString(kCFAllocatorDefault,
1744 lis->address.un.sun_path,
1745 kCFStringEncodingUTF8)))
1746 {
1747 CFDictionaryAddValue(listener,
1748 CFSTR(LAUNCH_JOBSOCKETKEY_PATHNAME),
1749 socket_path);
1750 CFRelease(socket_path);
1751 }
1752 portnum = 0140777; /* (S_IFSOCK|S_IRWXU|S_IRWXG|S_IRWXO) or *
1753 * 49663d decimal */
1754 if ((socket_mode = CFNumberCreate(kCFAllocatorDefault,
1755 kCFNumberIntType, &portnum)))
1756 {
1757 CFDictionaryAddValue(listener, CFSTR("SockPathMode"),
1758 socket_mode);
1759 CFRelease(socket_mode);
1760 }
1761 }
1762 else
1763 # endif /* AF_LOCAL */
1764 {
1765 # ifdef AF_INET6
1766 if (lis->address.addr.sa_family == AF_INET6)
1767 {
1768 CFDictionaryAddValue(listener,
1769 CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY),
1770 CFSTR("IPv6"));
1771 portnum = lis->address.ipv6.sin6_port;
1772 }
1773 else
1774 # endif /* AF_INET6 */
1775 {
1776 CFDictionaryAddValue(listener,
1777 CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY),
1778 CFSTR("IPv4"));
1779 portnum = lis->address.ipv4.sin_port;
1780 }
1781
1782 if ((service = getservbyport(portnum, NULL)))
1783 value = CFStringCreateWithCString(kCFAllocatorDefault,
1784 service->s_name,
1785 kCFStringEncodingUTF8);
1786 else
1787 value = CFNumberCreate(kCFAllocatorDefault,
1788 kCFNumberIntType, &portnum);
1789
1790 if (value)
1791 {
1792 CFDictionaryAddValue(listener,
1793 CFSTR(LAUNCH_JOBSOCKETKEY_SERVICENAME),
1794 value);
1795 CFRelease(value);
1796 }
1797
1798 httpAddrString(&lis->address, temp, sizeof(temp));
1799 if ((value = CFStringCreateWithCString(kCFAllocatorDefault, temp,
1800 kCFStringEncodingUTF8)))
1801 {
1802 CFDictionaryAddValue(listener,
1803 CFSTR(LAUNCH_JOBSOCKETKEY_NODENAME),
1804 value);
1805 CFRelease(value);
1806 }
1807 }
1808
1809 CFRelease(listener);
1810 }
1811 }
1812
1813 CFRelease(array);
1814 }
1815
1816 /*
1817 * Add the BrowseSocket to the sockets dictionary...
1818 */
1819
1820 if (Browsing && (BrowseRemoteProtocols & BROWSE_CUPS))
1821 {
1822 if ((array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
1823 &kCFTypeArrayCallBacks)) != NULL)
1824 {
1825 CFDictionaryAddValue(sockets, CFSTR("BrowseSockets"), array);
1826
1827 if ((listener = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1828 &kCFTypeDictionaryKeyCallBacks,
1829 &kCFTypeDictionaryValueCallBacks)) != NULL)
1830 {
1831 CFArrayAppendValue(array, listener);
1832
1833 CFDictionaryAddValue(listener, CFSTR(LAUNCH_JOBSOCKETKEY_FAMILY),
1834 CFSTR("IPv4"));
1835 CFDictionaryAddValue(listener, CFSTR(LAUNCH_JOBSOCKETKEY_TYPE),
1836 CFSTR("dgram"));
1837
1838 if ((service = getservbyport(BrowsePort, NULL)))
1839 value = CFStringCreateWithCString(kCFAllocatorDefault,
1840 service->s_name,
1841 kCFStringEncodingUTF8);
1842 else
1843 value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType,
1844 &BrowsePort);
1845
1846 CFDictionaryAddValue(listener,
1847 CFSTR(LAUNCH_JOBSOCKETKEY_SERVICENAME), value);
1848 CFRelease(value);
1849
1850 CFRelease(listener);
1851 }
1852
1853 CFRelease(array);
1854 }
1855 }
1856
1857 CFRelease(sockets);
1858 }
1859
1860 cupsdLogMessage(CUPSD_LOG_DEBUG,
1861 "launchd_sync_conf: Updating \"%s\", pid=%d\n",
1862 LaunchdConf, (int)getpid());
1863
1864 if ((fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
1865 (const unsigned char *)LaunchdConf,
1866 strlen(LaunchdConf), false)))
1867 {
1868 if ((resourceData = CFPropertyListCreateXMLData(kCFAllocatorDefault,
1869 cupsd_dict)))
1870 {
1871 if (!CFURLWriteDataAndPropertiesToResource(fileURL, resourceData,
1872 NULL, &errorCode))
1873 {
1874 cupsdLogMessage(CUPSD_LOG_WARN,
1875 "launchd_sync_conf: "
1876 "CFURLWriteDataAndPropertiesToResource(\"%s\") "
1877 "failed: %d\n",
1878 LaunchdConf, (int)errorCode);
1879 }
1880
1881 CFRelease(resourceData);
1882 }
1883
1884 CFRelease(fileURL);
1885 }
1886
1887 CFRelease(cupsd_dict);
1888 }
1889
1890 /*
1891 * Let the caller know we updated the file...
1892 */
1893
1894 return (1);
1895 }
1896 #endif /* HAVE_LAUNCHD */
1897
1898
1899 /*
1900 * 'parent_handler()' - Catch USR1/CHLD signals...
1901 */
1902
1903 static void
1904 parent_handler(int sig) /* I - Signal */
1905 {
1906 /*
1907 * Store the signal we got from the OS and return...
1908 */
1909
1910 parent_signal = sig;
1911 }
1912
1913
1914 /*
1915 * 'process_children()' - Process all dead children...
1916 */
1917
1918 static void
1919 process_children(void)
1920 {
1921 int status; /* Exit status of child */
1922 int pid; /* Process ID of child */
1923 cupsd_job_t *job; /* Current job */
1924 int i; /* Looping var */
1925 char name[1024]; /* Process name */
1926
1927
1928 cupsdLogMessage(CUPSD_LOG_DEBUG2, "process_children()");
1929
1930 /*
1931 * Reset the dead_children flag...
1932 */
1933
1934 dead_children = 0;
1935
1936 /*
1937 * Collect the exit status of some children...
1938 */
1939
1940 #ifdef HAVE_WAITPID
1941 while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
1942 #elif defined(HAVE_WAIT3)
1943 while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
1944 #else
1945 if ((pid = wait(&status)) > 0)
1946 #endif /* HAVE_WAITPID */
1947 {
1948 /*
1949 * Ignore SIGTERM errors - that comes when a job is cancelled...
1950 */
1951
1952 cupsdFinishProcess(pid, name, sizeof(name));
1953
1954 if (status == SIGTERM)
1955 status = 0;
1956
1957 if (status)
1958 {
1959 if (WIFEXITED(status))
1960 cupsdLogMessage(CUPSD_LOG_ERROR, "PID %d (%s) stopped with status %d!",
1961 pid, name, WEXITSTATUS(status));
1962 else
1963 cupsdLogMessage(CUPSD_LOG_ERROR, "PID %d (%s) crashed on signal %d!",
1964 pid, name, WTERMSIG(status));
1965
1966 if (LogLevel < CUPSD_LOG_DEBUG)
1967 cupsdLogMessage(CUPSD_LOG_INFO,
1968 "Hint: Try setting the LogLevel to \"debug\" to find "
1969 "out more.");
1970 }
1971 else
1972 cupsdLogMessage(CUPSD_LOG_DEBUG, "PID %d (%s) exited with no errors.",
1973 pid, name);
1974
1975 /*
1976 * Delete certificates for CGI processes...
1977 */
1978
1979 if (pid)
1980 cupsdDeleteCert(pid);
1981
1982 /*
1983 * Lookup the PID in the jobs list...
1984 */
1985
1986 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
1987 job;
1988 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
1989 if (job->state_value == IPP_JOB_PROCESSING)
1990 {
1991 for (i = 0; job->filters[i]; i ++)
1992 if (job->filters[i] == pid)
1993 break;
1994
1995 if (job->filters[i] || job->backend == pid)
1996 {
1997 /*
1998 * OK, this process has gone away; what's left?
1999 */
2000
2001 if (job->filters[i])
2002 job->filters[i] = -pid;
2003 else
2004 job->backend = -pid;
2005
2006 if (status && job->status >= 0)
2007 {
2008 /*
2009 * An error occurred; save the exit status so we know to stop
2010 * the printer or cancel the job when all of the filters finish...
2011 *
2012 * A negative status indicates that the backend failed and the
2013 * printer needs to be stopped.
2014 */
2015
2016 if (job->filters[i])
2017 job->status = status; /* Filter failed */
2018 else
2019 job->status = -status; /* Backend failed */
2020
2021 if (job->printer && !(job->printer->type & CUPS_PRINTER_FAX))
2022 {
2023 snprintf(job->printer->state_message,
2024 sizeof(job->printer->state_message), "%s failed", name);
2025 cupsdAddPrinterHistory(job->printer);
2026 }
2027 }
2028
2029 /*
2030 * If this is not the last file in a job, see if all of the
2031 * filters are done, and if so move to the next file.
2032 */
2033
2034 if (job->current_file < job->num_files)
2035 {
2036 for (i = 0; job->filters[i] < 0; i ++);
2037
2038 if (!job->filters[i])
2039 {
2040 /*
2041 * Process the next file...
2042 */
2043
2044 cupsdFinishJob(job);
2045 }
2046 }
2047 break;
2048 }
2049 }
2050 }
2051 }
2052
2053
2054 /*
2055 * 'sigchld_handler()' - Handle 'child' signals from old processes.
2056 */
2057
2058 static void
2059 sigchld_handler(int sig) /* I - Signal number */
2060 {
2061 (void)sig;
2062
2063 /*
2064 * Flag that we have dead children...
2065 */
2066
2067 dead_children = 1;
2068
2069 /*
2070 * Reset the signal handler as needed...
2071 */
2072
2073 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
2074 signal(SIGCLD, sigchld_handler);
2075 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
2076 }
2077
2078
2079 /*
2080 * 'sighup_handler()' - Handle 'hangup' signals to reconfigure the scheduler.
2081 */
2082
2083 static void
2084 sighup_handler(int sig) /* I - Signal number */
2085 {
2086 (void)sig;
2087
2088 NeedReload = RELOAD_ALL;
2089 ReloadTime = time(NULL);
2090
2091 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
2092 signal(SIGHUP, sighup_handler);
2093 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
2094 }
2095
2096
2097 /*
2098 * 'sigterm_handler()' - Handle 'terminate' signals that stop the scheduler.
2099 */
2100
2101 static void
2102 sigterm_handler(int sig) /* I - Signal */
2103 {
2104 (void)sig; /* remove compiler warnings... */
2105
2106 /*
2107 * Flag that we should stop and return...
2108 */
2109
2110 stop_scheduler = 1;
2111 }
2112
2113
2114 /*
2115 * 'select_timeout()' - Calculate the select timeout value.
2116 *
2117 */
2118
2119 static long /* O - Number of seconds */
2120 select_timeout(int fds) /* I - Number of descriptors returned */
2121 {
2122 long timeout; /* Timeout for select */
2123 time_t now; /* Current time */
2124 cupsd_client_t *con; /* Client information */
2125 cupsd_printer_t *p; /* Printer information */
2126 cupsd_job_t *job; /* Job information */
2127 cupsd_subscription_t *sub; /* Subscription information */
2128 const char *why; /* Debugging aid */
2129
2130
2131 /*
2132 * Check to see if any of the clients have pending data to be
2133 * processed; if so, the timeout should be 0...
2134 */
2135
2136 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
2137 con;
2138 con = (cupsd_client_t *)cupsArrayNext(Clients))
2139 if (con->http.used > 0)
2140 return (0);
2141
2142 /*
2143 * If select has been active in the last second (fds != 0) or we have
2144 * many resources in use then don't bother trying to optimize the
2145 * timeout, just make it 1 second.
2146 */
2147
2148 if (fds || cupsArrayCount(Clients) > 50)
2149 return (1);
2150
2151 /*
2152 * If we had a recent event notification, timeout in 1 second...
2153 */
2154
2155 if (LastEvent)
2156 return (1);
2157
2158 /*
2159 * Otherwise, check all of the possible events that we need to wake for...
2160 */
2161
2162 now = time(NULL);
2163 timeout = now + 86400; /* 86400 == 1 day */
2164 why = "do nothing";
2165
2166 /*
2167 * Check the activity and close old clients...
2168 */
2169
2170 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
2171 con;
2172 con = (cupsd_client_t *)cupsArrayNext(Clients))
2173 if ((con->http.activity + Timeout) < timeout)
2174 {
2175 timeout = con->http.activity + Timeout;
2176 why = "timeout a client connection";
2177 }
2178
2179 /*
2180 * Update the browse list as needed...
2181 */
2182
2183 if (Browsing && BrowseLocalProtocols)
2184 {
2185 #ifdef HAVE_LIBSLP
2186 if ((BrowseLocalProtocols & BROWSE_SLP) && (BrowseSLPRefresh < timeout))
2187 {
2188 timeout = BrowseSLPRefresh;
2189 why = "update SLP browsing";
2190 }
2191 #endif /* HAVE_LIBSLP */
2192
2193 #ifdef HAVE_LDAP
2194 if ((BrowseLocalProtocols & BROWSE_LDAP) && (BrowseLDAPRefresh < timeout))
2195 {
2196 timeout = BrowseLDAPRefresh;
2197 why = "update LDAP browsing";
2198 }
2199 #endif /* HAVE_LDAP */
2200
2201 if (BrowseLocalProtocols & BROWSE_CUPS)
2202 {
2203 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
2204 p;
2205 p = (cupsd_printer_t *)cupsArrayNext(Printers))
2206 {
2207 if (p->type & CUPS_PRINTER_REMOTE)
2208 {
2209 if ((p->browse_time + BrowseTimeout) < timeout)
2210 {
2211 timeout = p->browse_time + BrowseTimeout;
2212 why = "browse timeout a printer";
2213 }
2214 }
2215 else if (!(p->type & CUPS_PRINTER_IMPLICIT))
2216 {
2217 if (BrowseInterval && (p->browse_time + BrowseInterval) < timeout)
2218 {
2219 timeout = p->browse_time + BrowseInterval;
2220 why = "send browse update";
2221 }
2222 }
2223 }
2224 }
2225 }
2226
2227 /*
2228 * Check for any active jobs...
2229 */
2230
2231 if (timeout > (now + 10) && ActiveJobs)
2232 {
2233 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
2234 job;
2235 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
2236 if (job->state_value <= IPP_JOB_PROCESSING)
2237 {
2238 timeout = now + 10;
2239 why = "process active jobs";
2240 break;
2241 }
2242 }
2243
2244 #ifdef HAVE_MALLINFO
2245 /*
2246 * Log memory usage every minute...
2247 */
2248
2249 if (LogLevel >= CUPSD_LOG_DEBUG && (mallinfo_time + 60) < timeout)
2250 {
2251 timeout = mallinfo_time + 60;
2252 why = "display memory usage";
2253 }
2254 #endif /* HAVE_MALLINFO */
2255
2256 /*
2257 * Expire subscriptions as needed...
2258 */
2259
2260 for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
2261 sub;
2262 sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
2263 if (!sub->job && sub->expire && sub->expire < timeout)
2264 {
2265 timeout = sub->expire;
2266 why = "expire subscription";
2267 }
2268
2269 /*
2270 * Adjust from absolute to relative time. If p->browse_time above
2271 * was 0 then we can end up with a negative value here, so check.
2272 * We add 1 second to the timeout since events occur after the
2273 * timeout expires, and limit the timeout to 86400 seconds (1 day)
2274 * to avoid select() timeout limits present on some operating
2275 * systems...
2276 */
2277
2278 timeout = timeout - now + 1;
2279
2280 if (timeout < 1)
2281 timeout = 1;
2282 else if (timeout > 86400)
2283 timeout = 86400;
2284
2285 /*
2286 * Log and return the timeout value...
2287 */
2288
2289 cupsdLogMessage(CUPSD_LOG_DEBUG2, "select_timeout: %ld seconds to %s",
2290 timeout, why);
2291
2292 return (timeout);
2293 }
2294
2295
2296 /*
2297 * 'usage()' - Show scheduler usage.
2298 */
2299
2300 static void
2301 usage(int status) /* O - Exit status */
2302 {
2303 _cupsLangPuts(status ? stderr : stdout,
2304 _("Usage: cupsd [-c config-file] [-f] [-F] [-h] [-l]\n"
2305 "\n"
2306 "-c config-file Load alternate configuration file\n"
2307 "-f Run in the foreground\n"
2308 "-F Run in the foreground but detach\n"
2309 "-h Show this usage message\n"
2310 "-l Run cupsd from launchd(8)\n"));
2311 exit(status);
2312 }
2313
2314
2315 /*
2316 * End of "$Id: main.c 5231 2006-03-05 17:22:27Z mike $".
2317 */