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