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