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