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