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