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