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