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