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