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