]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/launchd.c
7078394b94d7b8fcc65abaf036c6175488ba5dd7
[apple/launchd.git] / launchd / src / launchd.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 #include <Security/Authorization.h>
24 #include <Security/AuthorizationTags.h>
25 #include <Security/AuthSession.h>
26 #ifdef EVFILT_MACH_IMPLEMENTED
27 #include <mach/mach_error.h>
28 #include <mach/port.h>
29 #endif
30 #include <sys/types.h>
31 #include <sys/queue.h>
32 #include <sys/event.h>
33 #include <sys/stat.h>
34 #include <sys/ucred.h>
35 #include <sys/fcntl.h>
36 #include <sys/un.h>
37 #include <sys/wait.h>
38 #include <sys/sysctl.h>
39 #include <sys/sockio.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42 #include <sys/ioctl.h>
43 #include <sys/mount.h>
44 #include <net/if.h>
45 #include <netinet/in.h>
46 #include <netinet/in_var.h>
47 #include <netinet6/nd6.h>
48 #include <unistd.h>
49 #include <signal.h>
50 #include <errno.h>
51 #include <syslog.h>
52 #include <libgen.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <stdarg.h>
56 #include <stdbool.h>
57 #include <pthread.h>
58 #include <paths.h>
59 #include <pwd.h>
60 #include <grp.h>
61 #include <dlfcn.h>
62 #include <dirent.h>
63
64 #include "launch.h"
65 #include "launch_priv.h"
66 #include "launchd.h"
67
68 #include "bootstrap_internal.h"
69
70 #define LAUNCHD_MIN_JOB_RUN_TIME 10
71 #define LAUNCHD_REWARD_JOB_RUN_TIME 60
72 #define LAUNCHD_FAILED_EXITS_THRESHOLD 10
73 #define PID1LAUNCHD_CONF "/etc/launchd.conf"
74 #define LAUNCHD_CONF ".launchd.conf"
75 #define LAUNCHCTL_PATH "/bin/launchctl"
76 #define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security"
77 #define VOLFSDIR "/.vol"
78
79 extern char **environ;
80
81 struct jobcb {
82 kq_callback kqjob_callback;
83 TAILQ_ENTRY(jobcb) tqe;
84 launch_data_t ldj;
85 pid_t p;
86 int execfd;
87 time_t start_time;
88 size_t failed_exits;
89 int *vnodes;
90 size_t vnodes_cnt;
91 int *qdirs;
92 size_t qdirs_cnt;
93 unsigned int start_interval;
94 struct tm *start_cal_interval;
95 unsigned int checkedin:1, firstborn:1, debug:1, throttle:1, futureflags:28;
96 char label[0];
97 };
98
99 struct conncb {
100 kq_callback kqconn_callback;
101 TAILQ_ENTRY(conncb) tqe;
102 launch_t conn;
103 struct jobcb *j;
104 int disabled_batch:1, futureflags:31;
105 };
106
107 static TAILQ_HEAD(jobcbhead, jobcb) jobs = TAILQ_HEAD_INITIALIZER(jobs);
108 static TAILQ_HEAD(conncbhead, conncb) connections = TAILQ_HEAD_INITIALIZER(connections);
109 static int mainkq = 0;
110 static int asynckq = 0;
111 static int batch_disabler_count = 0;
112
113 static launch_data_t load_job(launch_data_t pload);
114 static launch_data_t get_jobs(const char *which);
115 static launch_data_t setstdio(int d, launch_data_t o);
116 static launch_data_t adjust_rlimits(launch_data_t in);
117 static void batch_job_enable(bool e, struct conncb *c);
118 static void do_shutdown(void);
119
120 static void listen_callback(void *, struct kevent *);
121 static void async_callback(void);
122 static void signal_callback(void *, struct kevent *);
123 static void fs_callback(void);
124 static void simple_zombie_reaper(void *, struct kevent *);
125 static void readcfg_callback(void *, struct kevent *);
126
127 static kq_callback kqlisten_callback = listen_callback;
128 static kq_callback kqasync_callback = (kq_callback)async_callback;
129 static kq_callback kqsignal_callback = signal_callback;
130 static kq_callback kqfs_callback = (kq_callback)fs_callback;
131 static kq_callback kqreadcfg_callback = readcfg_callback;
132 kq_callback kqsimple_zombie_reaper = simple_zombie_reaper;
133
134 static void job_watch(struct jobcb *j);
135 static void job_ignore(struct jobcb *j);
136 static void job_start(struct jobcb *j);
137 static void job_start_child(struct jobcb *j, int execfd);
138 static void job_setup_attributes(struct jobcb *j);
139 static void job_stop(struct jobcb *j);
140 static void job_reap(struct jobcb *j);
141 static void job_remove(struct jobcb *j);
142 static void job_set_alarm(struct jobcb *j);
143 static void job_callback(void *obj, struct kevent *kev);
144 static void job_log(struct jobcb *j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
145 static void job_log_error(struct jobcb *j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
146
147 static void ipc_open(int fd, struct jobcb *j);
148 static void ipc_close(struct conncb *c);
149 static void ipc_callback(void *, struct kevent *);
150 static void ipc_readmsg(launch_data_t msg, void *context);
151 static void ipc_readmsg2(launch_data_t data, const char *cmd, void *context);
152
153 #ifdef PID1_REAP_ADOPTED_CHILDREN
154 static void pid1waitpid(void);
155 static bool launchd_check_pid(pid_t p);
156 #endif
157 static void pid1_magic_init(bool sflag, bool vflag, bool xflag);
158 static void launchd_server_init(bool create_session);
159 static void conceive_firstborn(char *argv[]);
160
161 #ifdef EVFILT_MACH_IMPLEMENTED
162 static void *mach_demand_loop(void *);
163 static void mach_callback(void *, struct kevent *);
164 static kq_callback kqmach_callback = mach_callback;
165 #endif
166
167 static void usage(FILE *where);
168 static int _fd(int fd);
169
170 static void loopback_setup(void);
171 static void workaround3048875(int argc, char *argv[]);
172 static void reload_launchd_config(void);
173 static int dir_has_files(const char *path);
174 static void setup_job_env(launch_data_t obj, const char *key, void *context);
175 static void unsetup_job_env(launch_data_t obj, const char *key, void *context);
176
177
178 static size_t total_children = 0;
179 static pid_t readcfg_pid = 0;
180 static bool launchd_inited = false;
181 static bool shutdown_in_progress = false;
182 static pthread_t mach_server_loop_thread;
183 mach_port_t launchd_bootstrap_port = MACH_PORT_NULL;
184 sigset_t blocked_signals = 0;
185 static char *pending_stdout = NULL;
186 static char *pending_stderr = NULL;
187
188 int main(int argc, char *argv[])
189 {
190 static const int sigigns[] = { SIGHUP, SIGINT, SIGPIPE, SIGALRM,
191 SIGTERM, SIGURG, SIGTSTP, SIGTSTP, SIGCONT, /*SIGCHLD,*/
192 SIGTTIN, SIGTTOU, SIGIO, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF,
193 SIGWINCH, SIGINFO, SIGUSR1, SIGUSR2 };
194 void testfd_or_openfd(int fd, const char *path, int flags) {
195 int tmpfd;
196
197 if (-1 != (tmpfd = dup(fd))) {
198 close(tmpfd);
199 } else {
200 if (-1 == (tmpfd = open(path, flags))) {
201 syslog(LOG_ERR, "open(\"%s\", ...): %m", path);
202 } else if (tmpfd != fd) {
203 dup2(tmpfd, fd);
204 close(tmpfd);
205 }
206 }
207 };
208 struct kevent kev;
209 size_t i;
210 bool sflag = false, xflag = false, vflag = false, dflag = false;
211 int ch;
212
213 if (getpid() == 1)
214 workaround3048875(argc, argv);
215
216 setegid(getgid());
217 seteuid(getuid());
218
219 testfd_or_openfd(STDIN_FILENO, _PATH_DEVNULL, O_RDONLY);
220 testfd_or_openfd(STDOUT_FILENO, _PATH_DEVNULL, O_WRONLY);
221 testfd_or_openfd(STDERR_FILENO, _PATH_DEVNULL, O_WRONLY);
222
223 openlog(getprogname(), LOG_CONS|(getpid() != 1 ? LOG_PID|LOG_PERROR : 0), LOG_LAUNCHD);
224 setlogmask(LOG_UPTO(LOG_NOTICE));
225
226 while ((ch = getopt(argc, argv, "dhsvx")) != -1) {
227 switch (ch) {
228 case 'd': dflag = true; break;
229 case 's': sflag = true; break;
230 case 'x': xflag = true; break;
231 case 'v': vflag = true; break;
232 case 'h': usage(stdout); break;
233 default:
234 syslog(LOG_WARNING, "ignoring unknown arguments");
235 usage(stderr);
236 break;
237 }
238 }
239 argc -= optind;
240 argv += optind;
241
242 if (dflag && daemon(0, 0) == -1)
243 syslog(LOG_WARNING, "couldn't daemonize: %m");
244
245 if ((mainkq = kqueue()) == -1) {
246 syslog(LOG_EMERG, "kqueue(): %m");
247 abort();
248 }
249
250 if ((asynckq = kqueue()) == -1) {
251 syslog(LOG_ERR, "kqueue(): %m");
252 abort();
253 }
254
255 if (kevent_mod(asynckq, EVFILT_READ, EV_ADD, 0, 0, &kqasync_callback) == -1) {
256 syslog(LOG_ERR, "kevent_mod(asynckq, EVFILT_READ): %m");
257 abort();
258 }
259
260 sigemptyset(&blocked_signals);
261
262 for (i = 0; i < (sizeof(sigigns) / sizeof(int)); i++) {
263 if (kevent_mod(sigigns[i], EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) == -1)
264 syslog(LOG_ERR, "failed to add kevent for signal: %d: %m", sigigns[i]);
265 sigaddset(&blocked_signals, sigigns[i]);
266 signal(sigigns[i], SIG_IGN);
267 }
268
269 /* sigh... ignoring SIGCHLD has side effects: we can't call wait*() */
270 if (kevent_mod(SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, &kqsignal_callback) == -1)
271 syslog(LOG_ERR, "failed to add kevent for signal: %d: %m", SIGCHLD);
272
273 if (getpid() == 1) {
274 pid1_magic_init(sflag, vflag, xflag);
275 } else {
276 launchd_bootstrap_port = bootstrap_port;
277 launchd_server_init(argv[0] ? true : false);
278 }
279
280 /* do this after pid1_magic_init() to not catch ourselves mounting stuff */
281 if (kevent_mod(0, EVFILT_FS, EV_ADD, 0, 0, &kqfs_callback) == -1)
282 syslog(LOG_ERR, "kevent_mod(EVFILT_FS, &kqfs_callback): %m");
283
284
285 if (argv[0])
286 conceive_firstborn(argv);
287
288 reload_launchd_config();
289
290 if (argv[0])
291 job_start(TAILQ_FIRST(&jobs));
292
293 for (;;) {
294 static struct timespec timeout = { 30, 0 };
295 struct timespec *timeoutp = NULL;
296
297 if (getpid() == 1) {
298 if (readcfg_pid == 0)
299 init_pre_kevent();
300 } else {
301 if (TAILQ_EMPTY(&jobs)) {
302 /* launched on demand */
303 timeoutp = &timeout;
304 } else if (shutdown_in_progress && total_children == 0) {
305 exit(EXIT_SUCCESS);
306 }
307 }
308
309 switch (kevent(mainkq, NULL, 0, &kev, 1, timeoutp)) {
310 case -1:
311 syslog(LOG_DEBUG, "kevent(): %m");
312 break;
313 case 1:
314 (*((kq_callback *)kev.udata))(kev.udata, &kev);
315 break;
316 case 0:
317 if (timeoutp)
318 exit(EXIT_SUCCESS);
319 else
320 syslog(LOG_DEBUG, "kevent(): spurious return with infinite timeout");
321 break;
322 default:
323 syslog(LOG_DEBUG, "unexpected: kevent() returned something != 0, -1 or 1");
324 break;
325 }
326 }
327 }
328
329 static void pid1_magic_init(bool sflag, bool vflag, bool xflag)
330 {
331 pthread_attr_t attr;
332 int memmib[2] = { CTL_HW, HW_PHYSMEM };
333 int mvnmib[2] = { CTL_KERN, KERN_MAXVNODES };
334 int hnmib[2] = { CTL_KERN, KERN_HOSTNAME };
335 uint64_t mem = 0;
336 uint32_t mvn;
337 size_t memsz = sizeof(mem);
338 int pthr_r;
339
340 setpriority(PRIO_PROCESS, 0, -1);
341
342 if (setsid() == -1)
343 syslog(LOG_ERR, "setsid(): %m");
344
345 if (chdir("/") == -1)
346 syslog(LOG_ERR, "chdir(\"/\"): %m");
347
348 if (sysctl(memmib, 2, &mem, &memsz, NULL, 0) == -1) {
349 syslog(LOG_WARNING, "sysctl(\"%s\"): %m", "hw.physmem");
350 } else {
351 /* The following assignment of mem to itself if the size
352 * of data returned is 32 bits instead of 64 is a clever
353 * C trick to move the 32 bits on big endian systems to
354 * the least significant bytes of the 64 mem variable.
355 *
356 * On little endian systems, this is effectively a no-op.
357 */
358 if (memsz == 4)
359 mem = *(uint32_t *)&mem;
360 mvn = mem / (64 * 1024) + 1024;
361 if (sysctl(mvnmib, 2, NULL, NULL, &mvn, sizeof(mvn)) == -1)
362 syslog(LOG_WARNING, "sysctl(\"%s\"): %m", "kern.maxvnodes");
363 }
364 if (sysctl(hnmib, 2, NULL, NULL, "localhost", sizeof("localhost")) == -1)
365 syslog(LOG_WARNING, "sysctl(\"%s\"): %m", "kern.hostname");
366
367 if (setlogin("root") == -1)
368 syslog(LOG_ERR, "setlogin(\"root\"): %m");
369
370 loopback_setup();
371
372 if (mount("fdesc", "/dev", MNT_UNION, NULL) == -1)
373 syslog(LOG_ERR, "mount(\"%s\", \"%s\", ...): %m", "fdesc", "/dev/");
374
375 setenv("PATH", _PATH_STDPATH, 1);
376
377 launchd_bootstrap_port = mach_init_init();
378 task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port);
379 bootstrap_port = MACH_PORT_NULL;
380
381 pthread_attr_init(&attr);
382 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
383
384 pthr_r = pthread_create(&mach_server_loop_thread, &attr, mach_server_loop, NULL);
385 if (pthr_r != 0) {
386 syslog(LOG_ERR, "pthread_create(mach_server_loop): %s", strerror(pthr_r));
387 exit(EXIT_FAILURE);
388 }
389
390 pthread_attr_destroy(&attr);
391
392 init_boot(sflag, vflag, xflag);
393 }
394
395
396 #ifdef PID1_REAP_ADOPTED_CHILDREN
397 static bool launchd_check_pid(pid_t p)
398 {
399 struct kevent kev;
400 struct jobcb *j;
401
402 TAILQ_FOREACH(j, &jobs, tqe) {
403 if (j->p == p) {
404 EV_SET(&kev, p, EVFILT_PROC, 0, 0, 0, j);
405 j->kqjob_callback(j, &kev);
406 return true;
407 }
408 }
409
410 if (p == readcfg_pid) {
411 readcfg_callback(NULL, NULL);
412 return true;
413 }
414
415 return false;
416 }
417 #endif
418
419 static char *sockdir = NULL;
420 static char *sockpath = NULL;
421
422 static void launchd_clean_up(void)
423 {
424 seteuid(0);
425 setegid(0);
426
427 if (-1 == unlink(sockpath))
428 syslog(LOG_WARNING, "unlink(\"%s\"): %m", sockpath);
429 else if (-1 == rmdir(sockdir))
430 syslog(LOG_WARNING, "rmdir(\"%s\"): %m", sockdir);
431
432 setegid(getgid());
433 seteuid(getuid());
434 }
435
436 static void launchd_server_init(bool create_session)
437 {
438 struct sockaddr_un sun;
439 mode_t oldmask;
440 int r, fd = -1, ourdirfd = -1;
441 char ourdir[1024];
442
443 memset(&sun, 0, sizeof(sun));
444 sun.sun_family = AF_UNIX;
445
446 if (create_session) {
447 snprintf(ourdir, sizeof(ourdir), "%s/%u.%u", LAUNCHD_SOCK_PREFIX, getuid(), getpid());
448 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%u.%u/sock", LAUNCHD_SOCK_PREFIX, getuid(), getpid());
449 setenv(LAUNCHD_SOCKET_ENV, sun.sun_path, 1);
450 } else {
451 snprintf(ourdir, sizeof(ourdir), "%s/%u", LAUNCHD_SOCK_PREFIX, getuid());
452 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%u/sock", LAUNCHD_SOCK_PREFIX, getuid());
453 }
454
455 seteuid(0);
456 setegid(0);
457
458 if (mkdir(LAUNCHD_SOCK_PREFIX, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) == -1) {
459 if (errno == EROFS) {
460 goto out_bad;
461 } else if (errno == EEXIST) {
462 struct stat sb;
463 stat(LAUNCHD_SOCK_PREFIX, &sb);
464 if (!S_ISDIR(sb.st_mode)) {
465 errno = EEXIST;
466 syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX);
467 goto out_bad;
468 }
469 } else {
470 syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX);
471 goto out_bad;
472 }
473 }
474
475 unlink(ourdir);
476 if (mkdir(ourdir, S_IRWXU) == -1) {
477 if (errno == EROFS) {
478 goto out_bad;
479 } else if (errno == EEXIST) {
480 struct stat sb;
481 stat(ourdir, &sb);
482 if (!S_ISDIR(sb.st_mode)) {
483 errno = EEXIST;
484 syslog(LOG_ERR, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX);
485 goto out_bad;
486 }
487 } else {
488 syslog(LOG_ERR, "mkdir(\"%s\"): %m", ourdir);
489 goto out_bad;
490 }
491 }
492 if (chown(ourdir, getuid(), getgid()) == -1)
493 syslog(LOG_WARNING, "chown(\"%s\"): %m", ourdir);
494
495 ourdirfd = _fd(open(ourdir, O_RDONLY));
496 if (ourdirfd == -1) {
497 syslog(LOG_ERR, "open(\"%s\"): %m", ourdir);
498 goto out_bad;
499 }
500
501 if (flock(ourdirfd, LOCK_EX|LOCK_NB) == -1) {
502 if (errno == EWOULDBLOCK) {
503 exit(EXIT_SUCCESS);
504 } else {
505 syslog(LOG_ERR, "flock(\"%s\"): %m", ourdir);
506 goto out_bad;
507 }
508 }
509
510 if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
511 if (errno != EROFS)
512 syslog(LOG_ERR, "unlink(\"thesocket\"): %m");
513 goto out_bad;
514 }
515 if ((fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
516 syslog(LOG_ERR, "socket(\"thesocket\"): %m");
517 goto out_bad;
518 }
519 oldmask = umask(077);
520 r = bind(fd, (struct sockaddr *)&sun, sizeof(sun));
521 umask(oldmask);
522 if (r == -1) {
523 if (errno != EROFS)
524 syslog(LOG_ERR, "bind(\"thesocket\"): %m");
525 goto out_bad;
526 }
527 if (chown(sun.sun_path, getuid(), getgid()) == -1)
528 syslog(LOG_WARNING, "chown(\"thesocket\"): %m");
529
530 if (listen(fd, SOMAXCONN) == -1) {
531 syslog(LOG_ERR, "listen(\"thesocket\"): %m");
532 goto out_bad;
533 }
534
535 if (kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &kqlisten_callback) == -1) {
536 syslog(LOG_ERR, "kevent_mod(\"thesocket\", EVFILT_READ): %m");
537 goto out_bad;
538 }
539
540 launchd_inited = true;
541
542 sockdir = strdup(ourdir);
543 sockpath = strdup(sun.sun_path);
544
545 atexit(launchd_clean_up);
546
547 out_bad:
548 setegid(getgid());
549 seteuid(getuid());
550
551 if (!launchd_inited) {
552 if (fd != -1)
553 close(fd);
554 if (ourdirfd != -1)
555 close(ourdirfd);
556 }
557 }
558
559 static long long job_get_integer(launch_data_t j, const char *key)
560 {
561 launch_data_t t = launch_data_dict_lookup(j, key);
562 if (t)
563 return launch_data_get_integer(t);
564 else
565 return 0;
566 }
567
568 static const char *job_get_string(launch_data_t j, const char *key)
569 {
570 launch_data_t t = launch_data_dict_lookup(j, key);
571 if (t)
572 return launch_data_get_string(t);
573 else
574 return NULL;
575 }
576
577 static const char *job_get_file2exec(launch_data_t j)
578 {
579 launch_data_t tmpi, tmp = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PROGRAM);
580
581 if (tmp) {
582 return launch_data_get_string(tmp);
583 } else {
584 tmp = launch_data_dict_lookup(j, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
585 if (tmp) {
586 tmpi = launch_data_array_get_index(tmp, 0);
587 if (tmpi)
588 return launch_data_get_string(tmpi);
589 }
590 return NULL;
591 }
592 }
593
594 static bool job_get_bool(launch_data_t j, const char *key)
595 {
596 launch_data_t t = launch_data_dict_lookup(j, key);
597 if (t)
598 return launch_data_get_bool(t);
599 else
600 return false;
601 }
602
603 static void ipc_open(int fd, struct jobcb *j)
604 {
605 struct conncb *c = calloc(1, sizeof(struct conncb));
606
607 fcntl(fd, F_SETFL, O_NONBLOCK);
608
609 c->kqconn_callback = ipc_callback;
610 c->conn = launchd_fdopen(fd);
611 c->j = j;
612 TAILQ_INSERT_TAIL(&connections, c, tqe);
613 kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &c->kqconn_callback);
614 }
615
616 static void simple_zombie_reaper(void *obj __attribute__((unused)), struct kevent *kev)
617 {
618 waitpid(kev->ident, NULL, 0);
619 }
620
621 static void listen_callback(void *obj __attribute__((unused)), struct kevent *kev)
622 {
623 struct sockaddr_un sun;
624 socklen_t sl = sizeof(sun);
625 int cfd;
626
627 if ((cfd = _fd(accept(kev->ident, (struct sockaddr *)&sun, &sl))) == -1) {
628 return;
629 }
630
631 ipc_open(cfd, NULL);
632 }
633
634 static void ipc_callback(void *obj, struct kevent *kev)
635 {
636 struct conncb *c = obj;
637 int r;
638
639 if (kev->filter == EVFILT_READ) {
640 if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) {
641 if (errno != ECONNRESET)
642 syslog(LOG_DEBUG, "%s(): recv: %m", __func__);
643 ipc_close(c);
644 }
645 } else if (kev->filter == EVFILT_WRITE) {
646 r = launchd_msg_send(c->conn, NULL);
647 if (r == -1) {
648 if (errno != EAGAIN) {
649 syslog(LOG_DEBUG, "%s(): send: %m", __func__);
650 ipc_close(c);
651 }
652 } else if (r == 0) {
653 kevent_mod(launchd_getfd(c->conn), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
654 }
655 } else {
656 syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__);
657 ipc_close(c);
658 }
659 }
660
661 static void set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
662 {
663 setenv(key, launch_data_get_string(obj), 1);
664 }
665
666 static void launch_data_close_fds(launch_data_t o)
667 {
668 size_t i;
669
670 switch (launch_data_get_type(o)) {
671 case LAUNCH_DATA_DICTIONARY:
672 launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))launch_data_close_fds, NULL);
673 break;
674 case LAUNCH_DATA_ARRAY:
675 for (i = 0; i < launch_data_array_get_count(o); i++)
676 launch_data_close_fds(launch_data_array_get_index(o, i));
677 break;
678 case LAUNCH_DATA_FD:
679 if (launch_data_get_fd(o) != -1)
680 close(launch_data_get_fd(o));
681 break;
682 default:
683 break;
684 }
685 }
686
687 static void launch_data_revoke_fds(launch_data_t o)
688 {
689 size_t i;
690
691 switch (launch_data_get_type(o)) {
692 case LAUNCH_DATA_DICTIONARY:
693 launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))launch_data_revoke_fds, NULL);
694 break;
695 case LAUNCH_DATA_ARRAY:
696 for (i = 0; i < launch_data_array_get_count(o); i++)
697 launch_data_revoke_fds(launch_data_array_get_index(o, i));
698 break;
699 case LAUNCH_DATA_FD:
700 launch_data_set_fd(o, -1);
701 break;
702 default:
703 break;
704 }
705 }
706
707 static void job_ignore_fds(launch_data_t o, const char *key __attribute__((unused)), void *cookie)
708 {
709 struct jobcb *j = cookie;
710 size_t i;
711 int fd;
712
713 switch (launch_data_get_type(o)) {
714 case LAUNCH_DATA_DICTIONARY:
715 launch_data_dict_iterate(o, job_ignore_fds, cookie);
716 break;
717 case LAUNCH_DATA_ARRAY:
718 for (i = 0; i < launch_data_array_get_count(o); i++)
719 job_ignore_fds(launch_data_array_get_index(o, i), NULL, cookie);
720 break;
721 case LAUNCH_DATA_FD:
722 fd = launch_data_get_fd(o);
723 if (-1 != fd) {
724 job_log(j, LOG_DEBUG, "Ignoring FD: %d", fd);
725 kevent_mod(fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
726 }
727 break;
728 default:
729 break;
730 }
731 }
732
733 static void job_ignore(struct jobcb *j)
734 {
735 launch_data_t j_sockets = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_SOCKETS);
736 size_t i;
737
738 if (j_sockets)
739 job_ignore_fds(j_sockets, NULL, j);
740
741 for (i = 0; i < j->vnodes_cnt; i++) {
742 kevent_mod(j->vnodes[i], EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
743 }
744 for (i = 0; i < j->qdirs_cnt; i++) {
745 kevent_mod(j->qdirs[i], EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
746 }
747 }
748
749 static void job_watch_fds(launch_data_t o, const char *key __attribute__((unused)), void *cookie)
750 {
751 struct jobcb *j = cookie;
752 size_t i;
753 int fd;
754
755 switch (launch_data_get_type(o)) {
756 case LAUNCH_DATA_DICTIONARY:
757 launch_data_dict_iterate(o, job_watch_fds, cookie);
758 break;
759 case LAUNCH_DATA_ARRAY:
760 for (i = 0; i < launch_data_array_get_count(o); i++)
761 job_watch_fds(launch_data_array_get_index(o, i), NULL, cookie);
762 break;
763 case LAUNCH_DATA_FD:
764 fd = launch_data_get_fd(o);
765 if (-1 != fd) {
766 job_log(j, LOG_DEBUG, "Watching FD: %d", fd);
767 kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, cookie);
768 }
769 break;
770 default:
771 break;
772 }
773 }
774
775 static void job_watch(struct jobcb *j)
776 {
777 launch_data_t ld_qdirs = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_QUEUEDIRECTORIES);
778 launch_data_t ld_vnodes = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_WATCHPATHS);
779 launch_data_t j_sockets = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_SOCKETS);
780 size_t i;
781
782 if (j_sockets)
783 job_watch_fds(j_sockets, NULL, &j->kqjob_callback);
784
785 for (i = 0; i < j->vnodes_cnt; i++) {
786 if (-1 == j->vnodes[i]) {
787 launch_data_t ld_idx = launch_data_array_get_index(ld_vnodes, i);
788 const char *thepath = launch_data_get_string(ld_idx);
789
790 if (-1 == (j->vnodes[i] = _fd(open(thepath, O_EVTONLY))))
791 job_log_error(j, LOG_ERR, "open(\"%s\", O_EVTONLY)", thepath);
792 }
793 kevent_mod(j->vnodes[i], EVFILT_VNODE, EV_ADD|EV_CLEAR,
794 NOTE_WRITE|NOTE_EXTEND|NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE|NOTE_ATTRIB|NOTE_LINK,
795 0, &j->kqjob_callback);
796 }
797
798 for (i = 0; i < j->qdirs_cnt; i++) {
799 kevent_mod(j->qdirs[i], EVFILT_VNODE, EV_ADD|EV_CLEAR,
800 NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK, 0, &j->kqjob_callback);
801 }
802
803 for (i = 0; i < j->qdirs_cnt; i++) {
804 launch_data_t ld_idx = launch_data_array_get_index(ld_qdirs, i);
805 const char *thepath = launch_data_get_string(ld_idx);
806 int dcc_r;
807
808 if (-1 == (dcc_r = dir_has_files(thepath))) {
809 job_log_error(j, LOG_ERR, "dir_has_files(\"%s\", ...)", thepath);
810 } else if (dcc_r > 0) {
811 job_start(j);
812 break;
813 }
814 }
815 }
816
817 static void job_stop(struct jobcb *j)
818 {
819 if (j->p)
820 kill(j->p, SIGTERM);
821 }
822
823 static void job_remove(struct jobcb *j)
824 {
825 launch_data_t tmp;
826 size_t i;
827
828 job_log(j, LOG_DEBUG, "Removed");
829
830 TAILQ_REMOVE(&jobs, j, tqe);
831 if (j->p) {
832 if (kevent_mod(j->p, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &kqsimple_zombie_reaper) == -1) {
833 job_reap(j);
834 } else {
835 job_stop(j);
836 }
837 }
838 if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES)))
839 launch_data_dict_iterate(tmp, unsetup_job_env, NULL);
840 launch_data_close_fds(j->ldj);
841 launch_data_free(j->ldj);
842 if (j->execfd)
843 close(j->execfd);
844 for (i = 0; i < j->vnodes_cnt; i++)
845 if (-1 != j->vnodes[i])
846 close(j->vnodes[i]);
847 if (j->vnodes)
848 free(j->vnodes);
849 for (i = 0; i < j->qdirs_cnt; i++)
850 if (-1 != j->qdirs[i])
851 close(j->qdirs[i]);
852 if (j->qdirs)
853 free(j->qdirs);
854 if (j->start_interval)
855 kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
856 if (j->start_cal_interval) {
857 kevent_mod((uintptr_t)j->start_cal_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
858 free(j->start_cal_interval);
859 }
860 kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
861 free(j);
862 }
863
864 struct readmsg_context {
865 struct conncb *c;
866 launch_data_t resp;
867 };
868
869 static void ipc_readmsg(launch_data_t msg, void *context)
870 {
871 struct readmsg_context rmc = { context, NULL };
872
873 if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(msg)) {
874 launch_data_dict_iterate(msg, ipc_readmsg2, &rmc);
875 } else if (LAUNCH_DATA_STRING == launch_data_get_type(msg)) {
876 ipc_readmsg2(NULL, launch_data_get_string(msg), &rmc);
877 } else {
878 rmc.resp = launch_data_new_errno(EINVAL);
879 }
880
881 if (NULL == rmc.resp)
882 rmc.resp = launch_data_new_errno(ENOSYS);
883
884 launch_data_close_fds(msg);
885
886 if (launchd_msg_send(rmc.c->conn, rmc.resp) == -1) {
887 if (errno == EAGAIN) {
888 kevent_mod(launchd_getfd(rmc.c->conn), EVFILT_WRITE, EV_ADD, 0, 0, &rmc.c->kqconn_callback);
889 } else {
890 syslog(LOG_DEBUG, "launchd_msg_send() == -1: %m");
891 ipc_close(rmc.c);
892 }
893 }
894 launch_data_free(rmc.resp);
895 }
896
897
898 static void ipc_readmsg2(launch_data_t data, const char *cmd, void *context)
899 {
900 struct readmsg_context *rmc = context;
901 launch_data_t resp = NULL;
902 struct jobcb *j;
903
904 if (rmc->resp)
905 return;
906
907 if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) {
908 TAILQ_FOREACH(j, &jobs, tqe) {
909 if (!strcmp(j->label, launch_data_get_string(data))) {
910 job_start(j);
911 resp = launch_data_new_errno(0);
912 }
913 }
914 if (NULL == resp)
915 resp = launch_data_new_errno(ESRCH);
916 } else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) {
917 TAILQ_FOREACH(j, &jobs, tqe) {
918 if (!strcmp(j->label, launch_data_get_string(data))) {
919 job_stop(j);
920 resp = launch_data_new_errno(0);
921 }
922 }
923 if (NULL == resp)
924 resp = launch_data_new_errno(ESRCH);
925 } else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) {
926 TAILQ_FOREACH(j, &jobs, tqe) {
927 if (!strcmp(j->label, launch_data_get_string(data))) {
928 job_remove(j);
929 resp = launch_data_new_errno(0);
930 }
931 }
932 if (NULL == resp)
933 resp = launch_data_new_errno(ESRCH);
934 } else if (!strcmp(cmd, LAUNCH_KEY_SUBMITJOB)) {
935 if (launch_data_get_type(data) == LAUNCH_DATA_ARRAY) {
936 launch_data_t tmp;
937 size_t i;
938
939 resp = launch_data_alloc(LAUNCH_DATA_ARRAY);
940 for (i = 0; i < launch_data_array_get_count(data); i++) {
941 tmp = load_job(launch_data_array_get_index(data, i));
942 launch_data_array_set_index(resp, tmp, i);
943 }
944 } else {
945 resp = load_job(data);
946 }
947 } else if (!strcmp(cmd, LAUNCH_KEY_UNSETUSERENVIRONMENT)) {
948 unsetenv(launch_data_get_string(data));
949 resp = launch_data_new_errno(0);
950 } else if (!strcmp(cmd, LAUNCH_KEY_GETUSERENVIRONMENT)) {
951 char **tmpenviron = environ;
952 resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
953 for (; *tmpenviron; tmpenviron++) {
954 char envkey[1024];
955 launch_data_t s = launch_data_alloc(LAUNCH_DATA_STRING);
956 launch_data_set_string(s, strchr(*tmpenviron, '=') + 1);
957 strncpy(envkey, *tmpenviron, sizeof(envkey));
958 *(strchr(envkey, '=')) = '\0';
959 launch_data_dict_insert(resp, s, envkey);
960 }
961 } else if (!strcmp(cmd, LAUNCH_KEY_SETUSERENVIRONMENT)) {
962 launch_data_dict_iterate(data, set_user_env, NULL);
963 resp = launch_data_new_errno(0);
964 } else if (!strcmp(cmd, LAUNCH_KEY_CHECKIN)) {
965 if (rmc->c->j) {
966 resp = launch_data_copy(rmc->c->j->ldj);
967 if (NULL == launch_data_dict_lookup(resp, LAUNCH_JOBKEY_TIMEOUT)) {
968 launch_data_t to = launch_data_new_integer(LAUNCHD_MIN_JOB_RUN_TIME);
969 launch_data_dict_insert(resp, to, LAUNCH_JOBKEY_TIMEOUT);
970 }
971 rmc->c->j->checkedin = true;
972 } else {
973 resp = launch_data_new_errno(EACCES);
974 }
975 } else if (!strcmp(cmd, LAUNCH_KEY_RELOADTTYS)) {
976 update_ttys();
977 resp = launch_data_new_errno(0);
978 } else if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) {
979 do_shutdown();
980 resp = launch_data_new_errno(0);
981 } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBS)) {
982 resp = get_jobs(NULL);
983 launch_data_revoke_fds(resp);
984 } else if (!strcmp(cmd, LAUNCH_KEY_GETRESOURCELIMITS)) {
985 resp = adjust_rlimits(NULL);
986 } else if (!strcmp(cmd, LAUNCH_KEY_SETRESOURCELIMITS)) {
987 resp = adjust_rlimits(data);
988 } else if (!strcmp(cmd, LAUNCH_KEY_GETJOB)) {
989 resp = get_jobs(launch_data_get_string(data));
990 launch_data_revoke_fds(resp);
991 } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBWITHHANDLES)) {
992 resp = get_jobs(launch_data_get_string(data));
993 } else if (!strcmp(cmd, LAUNCH_KEY_SETLOGMASK)) {
994 resp = launch_data_new_integer(setlogmask(launch_data_get_integer(data)));
995 } else if (!strcmp(cmd, LAUNCH_KEY_GETLOGMASK)) {
996 int oldmask = setlogmask(LOG_UPTO(LOG_DEBUG));
997 resp = launch_data_new_integer(oldmask);
998 setlogmask(oldmask);
999 } else if (!strcmp(cmd, LAUNCH_KEY_SETUMASK)) {
1000 resp = launch_data_new_integer(umask(launch_data_get_integer(data)));
1001 } else if (!strcmp(cmd, LAUNCH_KEY_GETUMASK)) {
1002 mode_t oldmask = umask(0);
1003 resp = launch_data_new_integer(oldmask);
1004 umask(oldmask);
1005 } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGESELF)) {
1006 struct rusage rusage;
1007 getrusage(RUSAGE_SELF, &rusage);
1008 resp = launch_data_new_opaque(&rusage, sizeof(rusage));
1009 } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGECHILDREN)) {
1010 struct rusage rusage;
1011 getrusage(RUSAGE_CHILDREN, &rusage);
1012 resp = launch_data_new_opaque(&rusage, sizeof(rusage));
1013 } else if (!strcmp(cmd, LAUNCH_KEY_SETSTDOUT)) {
1014 resp = setstdio(STDOUT_FILENO, data);
1015 } else if (!strcmp(cmd, LAUNCH_KEY_SETSTDERR)) {
1016 resp = setstdio(STDERR_FILENO, data);
1017 } else if (!strcmp(cmd, LAUNCH_KEY_BATCHCONTROL)) {
1018 batch_job_enable(launch_data_get_bool(data), rmc->c);
1019 resp = launch_data_new_errno(0);
1020 } else if (!strcmp(cmd, LAUNCH_KEY_BATCHQUERY)) {
1021 resp = launch_data_alloc(LAUNCH_DATA_BOOL);
1022 launch_data_set_bool(resp, batch_disabler_count == 0);
1023 }
1024
1025 rmc->resp = resp;
1026 }
1027
1028 static launch_data_t setstdio(int d, launch_data_t o)
1029 {
1030 launch_data_t resp = launch_data_new_errno(0);
1031
1032 if (launch_data_get_type(o) == LAUNCH_DATA_STRING) {
1033 char **where = &pending_stderr;
1034 if (d == STDOUT_FILENO)
1035 where = &pending_stdout;
1036 if (*where)
1037 free(*where);
1038 *where = strdup(launch_data_get_string(o));
1039 } else if (launch_data_get_type(o) == LAUNCH_DATA_FD) {
1040 dup2(launch_data_get_fd(o), d);
1041 } else {
1042 launch_data_set_errno(resp, EINVAL);
1043 }
1044
1045 return resp;
1046 }
1047
1048 static void batch_job_enable(bool e, struct conncb *c)
1049 {
1050 if (e && c->disabled_batch) {
1051 batch_disabler_count--;
1052 c->disabled_batch = 0;
1053 if (batch_disabler_count == 0)
1054 kevent_mod(asynckq, EVFILT_READ, EV_ENABLE, 0, 0, &kqasync_callback);
1055 } else if (!e && !c->disabled_batch) {
1056 if (batch_disabler_count == 0)
1057 kevent_mod(asynckq, EVFILT_READ, EV_DISABLE, 0, 0, &kqasync_callback);
1058 batch_disabler_count++;
1059 c->disabled_batch = 1;
1060 }
1061 }
1062
1063 static launch_data_t load_job(launch_data_t pload)
1064 {
1065 launch_data_t tmp, resp;
1066 const char *label;
1067 struct jobcb *j;
1068 bool startnow;
1069
1070 if ((label = job_get_string(pload, LAUNCH_JOBKEY_LABEL))) {
1071 TAILQ_FOREACH(j, &jobs, tqe) {
1072 if (!strcmp(j->label, label)) {
1073 resp = launch_data_new_errno(EEXIST);
1074 goto out;
1075 }
1076 }
1077 } else {
1078 resp = launch_data_new_errno(EINVAL);
1079 goto out;
1080 }
1081 if (launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAMARGUMENTS) == NULL) {
1082 resp = launch_data_new_errno(EINVAL);
1083 goto out;
1084 }
1085
1086 j = calloc(1, sizeof(struct jobcb) + strlen(label) + 1);
1087 strcpy(j->label, label);
1088 j->ldj = launch_data_copy(pload);
1089 launch_data_revoke_fds(pload);
1090 j->kqjob_callback = job_callback;
1091
1092
1093 if (launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_ONDEMAND) == NULL) {
1094 tmp = launch_data_alloc(LAUNCH_DATA_BOOL);
1095 launch_data_set_bool(tmp, true);
1096 launch_data_dict_insert(j->ldj, tmp, LAUNCH_JOBKEY_ONDEMAND);
1097 }
1098
1099 TAILQ_INSERT_TAIL(&jobs, j, tqe);
1100
1101 j->debug = job_get_bool(j->ldj, LAUNCH_JOBKEY_DEBUG);
1102
1103 startnow = !job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND);
1104
1105 if (job_get_bool(j->ldj, LAUNCH_JOBKEY_RUNATLOAD))
1106 startnow = true;
1107
1108 if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_QUEUEDIRECTORIES))) {
1109 size_t i;
1110
1111 j->qdirs_cnt = launch_data_array_get_count(tmp);
1112 j->qdirs = malloc(sizeof(int) * j->qdirs_cnt);
1113
1114 for (i = 0; i < j->qdirs_cnt; i++) {
1115 const char *thepath = launch_data_get_string(launch_data_array_get_index(tmp, i));
1116
1117 if (-1 == (j->qdirs[i] = _fd(open(thepath, O_EVTONLY))))
1118 job_log_error(j, LOG_ERR, "open(\"%s\", O_EVTONLY)", thepath);
1119 }
1120
1121 }
1122
1123 if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_STARTINTERVAL))) {
1124 j->start_interval = launch_data_get_integer(tmp);
1125
1126 if (j->start_interval == 0)
1127 job_log(j, LOG_WARNING, "StartInterval is zero, ignoring");
1128 else if (-1 == kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, &j->kqjob_callback))
1129 job_log_error(j, LOG_ERR, "adding kevent timer");
1130 }
1131
1132 if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_STARTCALENDARINTERVAL))) {
1133 launch_data_t tmp_k;
1134
1135 j->start_cal_interval = calloc(1, sizeof(struct tm));
1136
1137 if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(tmp)) {
1138 if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_MINUTE)))
1139 j->start_cal_interval->tm_min = launch_data_get_integer(tmp_k);
1140 if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_HOUR)))
1141 j->start_cal_interval->tm_hour = launch_data_get_integer(tmp_k);
1142 if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_DAY)))
1143 j->start_cal_interval->tm_mday = launch_data_get_integer(tmp_k);
1144 if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_WEEKDAY))) {
1145 j->start_cal_interval->tm_wday = launch_data_get_integer(tmp_k);
1146 if (j->start_cal_interval->tm_wday == 0)
1147 j->start_cal_interval->tm_wday = 7;
1148 }
1149 if ((tmp_k = launch_data_dict_lookup(tmp, LAUNCH_JOBKEY_CAL_MONTH)))
1150 j->start_cal_interval->tm_mon = launch_data_get_integer(tmp_k);
1151 }
1152
1153 job_set_alarm(j);
1154 }
1155
1156 if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_WATCHPATHS))) {
1157 size_t i;
1158
1159 j->vnodes_cnt = launch_data_array_get_count(tmp);
1160 j->vnodes = malloc(sizeof(int) * j->vnodes_cnt);
1161
1162 for (i = 0; i < j->vnodes_cnt; i++) {
1163 const char *thepath = launch_data_get_string(launch_data_array_get_index(tmp, i));
1164
1165 if (-1 == (j->vnodes[i] = _fd(open(thepath, O_EVTONLY))))
1166 job_log_error(j, LOG_ERR, "open(\"%s\", O_EVTONLY)", thepath);
1167 }
1168
1169 }
1170
1171 if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES)))
1172 launch_data_dict_iterate(tmp, setup_job_env, NULL);
1173
1174 if (job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND))
1175 job_watch(j);
1176
1177 if (startnow)
1178 job_start(j);
1179
1180 resp = launch_data_new_errno(0);
1181 out:
1182 return resp;
1183 }
1184
1185 static launch_data_t get_jobs(const char *which)
1186 {
1187 struct jobcb *j;
1188 launch_data_t tmp, resp = NULL;
1189
1190 if (which) {
1191 TAILQ_FOREACH(j, &jobs, tqe) {
1192 if (!strcmp(which, j->label))
1193 resp = launch_data_copy(j->ldj);
1194 }
1195 if (resp == NULL)
1196 resp = launch_data_new_errno(ESRCH);
1197 } else {
1198 resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
1199
1200 TAILQ_FOREACH(j, &jobs, tqe) {
1201 tmp = launch_data_copy(j->ldj);
1202 launch_data_dict_insert(resp, tmp, j->label);
1203 }
1204 }
1205
1206 return resp;
1207 }
1208
1209 static void usage(FILE *where)
1210 {
1211 fprintf(where, "%s: [-d] [-- command [args ...]]\n", getprogname());
1212 fprintf(where, "\t-d\tdaemonize\n");
1213 fprintf(where, "\t-h\tthis usage statement\n");
1214
1215 if (where == stdout)
1216 exit(EXIT_SUCCESS);
1217 }
1218
1219 #ifdef EVFILT_MACH_IMPLEMENTED
1220 static void **machcbtable = NULL;
1221 static size_t machcbtable_cnt = 0;
1222 static int machcbreadfd = -1;
1223 static int machcbwritefd = -1;
1224 static mach_port_t mach_demand_port_set = MACH_PORT_NULL;
1225 static pthread_t mach_demand_thread;
1226
1227 static void mach_callback(void *obj __attribute__((unused)), struct kevent *kev __attribute__((unused)))
1228 {
1229 struct kevent mkev;
1230 mach_port_t mp;
1231
1232 read(machcbreadfd, &mp, sizeof(mp));
1233
1234 EV_SET(&mkev, mp, EVFILT_MACHPORT, 0, 0, 0, machcbtable[MACH_PORT_INDEX(mp)]);
1235
1236 (*((kq_callback *)mkev.udata))(mkev.udata, &mkev);
1237 }
1238 #endif
1239
1240 int kevent_mod(uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata)
1241 {
1242 struct kevent kev;
1243 int q = mainkq;
1244 #ifdef EVFILT_MACH_IMPLEMENTED
1245 kern_return_t kr;
1246 pthread_attr_t attr;
1247 int pthr_r, pfds[2];
1248 #endif
1249
1250 if (EVFILT_TIMER == filter || EVFILT_VNODE == filter)
1251 q = asynckq;
1252
1253 if (flags & EV_ADD && NULL == udata) {
1254 syslog(LOG_ERR, "%s(): kev.udata == NULL!!!", __func__);
1255 syslog(LOG_ERR, "kev: ident %d filter %d flags 0x%x fflags 0x%x",
1256 ident, filter, flags, fflags);
1257 errno = EINVAL;
1258 return -1;
1259 }
1260
1261 #ifdef EVFILT_MACH_IMPLEMENTED
1262 if (filter != EVFILT_MACHPORT) {
1263 #endif
1264 #ifdef PID1_REAP_ADOPTED_CHILDREN
1265 if (filter == EVFILT_PROC && getpid() == 1)
1266 return 0;
1267 #endif
1268 EV_SET(&kev, ident, filter, flags, fflags, data, udata);
1269 return kevent(q, &kev, 1, NULL, 0, NULL);
1270 #ifdef EVFILT_MACH_IMPLEMENTED
1271 }
1272
1273 if (machcbtable == NULL) {
1274 pthread_attr_init(&attr);
1275 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1276
1277 pthr_r = pthread_create(&mach_demand_thread, &attr, mach_demand_loop, NULL);
1278 if (pthr_r != 0) {
1279 syslog(LOG_ERR, "pthread_create(mach_demand_loop): %s", strerror(pthr_r));
1280 exit(EXIT_FAILURE);
1281 }
1282
1283 pthread_attr_destroy(&attr);
1284
1285 machcbtable = malloc(0);
1286 pipe(pfds);
1287 machcbwritefd = _fd(pfds[1]);
1288 machcbreadfd = _fd(pfds[0]);
1289 kevent_mod(machcbreadfd, EVFILT_READ, EV_ADD, 0, 0, &kqmach_callback);
1290 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &mach_demand_port_set);
1291 if (kr != KERN_SUCCESS) {
1292 syslog(LOG_ERR, "mach_port_allocate(demand_port_set): %s", mach_error_string(kr));
1293 exit(EXIT_FAILURE);
1294 }
1295 }
1296
1297 if (flags & EV_ADD) {
1298 kr = mach_port_move_member(mach_task_self(), ident, mach_demand_port_set);
1299 if (kr != KERN_SUCCESS) {
1300 syslog(LOG_ERR, "mach_port_move_member(): %s", mach_error_string(kr));
1301 exit(EXIT_FAILURE);
1302 }
1303
1304 if (MACH_PORT_INDEX(ident) > machcbtable_cnt)
1305 machcbtable = realloc(machcbtable, MACH_PORT_INDEX(ident) * sizeof(void *));
1306
1307 machcbtable[MACH_PORT_INDEX(ident)] = udata;
1308 } else if (flags & EV_DELETE) {
1309 kr = mach_port_move_member(mach_task_self(), ident, MACH_PORT_NULL);
1310 if (kr != KERN_SUCCESS) {
1311 syslog(LOG_ERR, "mach_port_move_member(): %s", mach_error_string(kr));
1312 exit(EXIT_FAILURE);
1313 }
1314 } else {
1315 syslog(LOG_DEBUG, "kevent_mod(EVFILT_MACHPORT) with flags: %d", flags);
1316 errno = EINVAL;
1317 return -1;
1318 }
1319
1320 return 0;
1321 #endif
1322 }
1323
1324 static int _fd(int fd)
1325 {
1326 if (fd >= 0)
1327 fcntl(fd, F_SETFD, 1);
1328 return fd;
1329 }
1330
1331 static void ipc_close(struct conncb *c)
1332 {
1333 batch_job_enable(true, c);
1334
1335 TAILQ_REMOVE(&connections, c, tqe);
1336 launchd_close(c->conn);
1337 free(c);
1338 }
1339
1340 static void setup_job_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
1341 {
1342 if (LAUNCH_DATA_STRING == launch_data_get_type(obj))
1343 setenv(key, launch_data_get_string(obj), 1);
1344 }
1345
1346 static void unsetup_job_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
1347 {
1348 if (LAUNCH_DATA_STRING == launch_data_get_type(obj))
1349 unsetenv(key);
1350 }
1351
1352 static void job_reap(struct jobcb *j)
1353 {
1354 bool od = job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND);
1355 time_t td = time(NULL) - j->start_time;
1356 bool bad_exit = false;
1357 int status;
1358
1359 job_log(j, LOG_DEBUG, "Reaping");
1360
1361 if (j->execfd) {
1362 close(j->execfd);
1363 j->execfd = 0;
1364 }
1365
1366 #ifdef PID1_REAP_ADOPTED_CHILDREN
1367 if (getpid() == 1)
1368 status = pid1_child_exit_status;
1369 else
1370 #endif
1371 if (-1 == waitpid(j->p, &status, 0)) {
1372 job_log_error(j, LOG_ERR, "waitpid(%d, ...)", j->p);
1373 return;
1374 }
1375
1376 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
1377 job_log(j, LOG_WARNING, "exited with exit code: %d", WEXITSTATUS(status));
1378 bad_exit = true;
1379 }
1380
1381 if (WIFSIGNALED(status)) {
1382 int s = WTERMSIG(status);
1383 if (SIGKILL == s || SIGTERM == s) {
1384 job_log(j, LOG_NOTICE, "exited: %s", strsignal(s));
1385 } else {
1386 job_log(j, LOG_WARNING, "exited abnormally: %s", strsignal(s));
1387 bad_exit = true;
1388 }
1389 }
1390
1391 if (!od) {
1392 if (td < LAUNCHD_MIN_JOB_RUN_TIME) {
1393 job_log(j, LOG_WARNING, "respawning too quickly! throttling");
1394 bad_exit = true;
1395 j->throttle = true;
1396 } else if (td >= LAUNCHD_REWARD_JOB_RUN_TIME) {
1397 job_log(j, LOG_INFO, "lived long enough, forgiving past exit failures");
1398 j->failed_exits = 0;
1399 }
1400 }
1401
1402 if (bad_exit)
1403 j->failed_exits++;
1404
1405 if (j->failed_exits > 0) {
1406 int failures_left = LAUNCHD_FAILED_EXITS_THRESHOLD - j->failed_exits;
1407 if (failures_left)
1408 job_log(j, LOG_WARNING, "%d more failure%s without living at least %d seconds will cause job removal",
1409 failures_left, failures_left > 1 ? "s" : "", LAUNCHD_REWARD_JOB_RUN_TIME);
1410 }
1411
1412 total_children--;
1413 j->p = 0;
1414 }
1415
1416 static bool job_restart_fitness_test(struct jobcb *j)
1417 {
1418 bool od = job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND);
1419
1420 if (j->firstborn) {
1421 job_log(j, LOG_DEBUG, "first born died, begin shutdown");
1422 do_shutdown();
1423 return false;
1424 } else if (job_get_bool(j->ldj, LAUNCH_JOBKEY_SERVICEIPC) && !j->checkedin) {
1425 job_log(j, LOG_WARNING, "failed to checkin");
1426 job_remove(j);
1427 return false;
1428 } else if (od || shutdown_in_progress) {
1429 if (!od && shutdown_in_progress)
1430 job_log(j, LOG_NOTICE, "exited while shutdown is in progress, will not restart unless demand requires it");
1431 job_watch(j);
1432 return false;
1433 } else if (j->failed_exits >= LAUNCHD_FAILED_EXITS_THRESHOLD) {
1434 job_log(j, LOG_WARNING, "too many failures in a row for a job that should be alive all the time");
1435 job_remove(j);
1436 return false;
1437 }
1438
1439 return true;
1440 }
1441
1442 static void job_callback(void *obj, struct kevent *kev)
1443 {
1444 struct jobcb *j = obj;
1445 bool d = j->debug;
1446 bool startnow = true;
1447 int oldmask = 0;
1448
1449 if (d) {
1450 oldmask = setlogmask(LOG_UPTO(LOG_DEBUG));
1451 job_log(j, LOG_DEBUG, "log level debug temporarily enabled while processing job");
1452 }
1453
1454 if (kev->filter == EVFILT_PROC) {
1455 job_reap(j);
1456
1457 startnow = job_restart_fitness_test(j);
1458
1459 if (startnow && j->throttle) {
1460 j->throttle = false;
1461 job_log(j, LOG_WARNING, "will restart in %d seconds", LAUNCHD_MIN_JOB_RUN_TIME);
1462 if (-1 == kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT,
1463 NOTE_SECONDS, LAUNCHD_MIN_JOB_RUN_TIME, &j->kqjob_callback)) {
1464 job_log_error(j, LOG_WARNING, "failed to setup timer callback!, starting now!");
1465 } else {
1466 startnow = false;
1467 }
1468 }
1469 } else if (kev->filter == EVFILT_TIMER && kev->fflags & NOTE_ABSOLUTE) {
1470 job_set_alarm(j);
1471 } else if (kev->filter == EVFILT_VNODE) {
1472 size_t i;
1473 const char *thepath = NULL;
1474
1475 for (i = 0; i < j->vnodes_cnt; i++) {
1476 if (j->vnodes[i] == (int)kev->ident) {
1477 launch_data_t ld_vnodes = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_WATCHPATHS);
1478
1479 thepath = launch_data_get_string(launch_data_array_get_index(ld_vnodes, i));
1480
1481 job_log(j, LOG_DEBUG, "watch path modified: %s", thepath);
1482
1483 if ((NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE) & kev->fflags) {
1484 job_log(j, LOG_DEBUG, "watch path invalidated: %s", thepath);
1485 close(j->vnodes[i]);
1486 j->vnodes[i] = -1; /* this will get fixed in job_watch() */
1487 }
1488 }
1489 }
1490
1491 for (i = 0; i < j->qdirs_cnt; i++) {
1492 if (j->qdirs[i] == (int)kev->ident) {
1493 launch_data_t ld_qdirs = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_QUEUEDIRECTORIES);
1494 int dcc_r;
1495
1496 thepath = launch_data_get_string(launch_data_array_get_index(ld_qdirs, i));
1497
1498 job_log(j, LOG_DEBUG, "queue directory modified: %s", thepath);
1499
1500 if (-1 == (dcc_r = dir_has_files(thepath))) {
1501 job_log_error(j, LOG_ERR, "dir_has_files(\"%s\", ...)", thepath);
1502 } else if (0 == dcc_r) {
1503 job_log(j, LOG_DEBUG, "spurious wake up, directory empty: %s", thepath);
1504 startnow = false;
1505 }
1506 }
1507 }
1508 /* if we get here, then the vnodes either wasn't a qdir, or if it was, it has entries in it */
1509 } else if (kev->filter == EVFILT_READ && (int)kev->ident == j->execfd) {
1510 if (kev->data > 0) {
1511 int e;
1512
1513 read(j->execfd, &e, sizeof(e));
1514 errno = e;
1515 job_log_error(j, LOG_ERR, "execve()");
1516 job_remove(j);
1517 j = NULL;
1518 startnow = false;
1519 } else {
1520 close(j->execfd);
1521 j->execfd = 0;
1522 }
1523 startnow = false;
1524 }
1525
1526 if (startnow)
1527 job_start(j);
1528
1529 if (d) {
1530 /* the job might have been removed, must not call job_log() */
1531 syslog(LOG_DEBUG, "restoring original log mask");
1532 setlogmask(oldmask);
1533 }
1534 }
1535
1536 static void job_start(struct jobcb *j)
1537 {
1538 int spair[2];
1539 int execspair[2];
1540 bool sipc;
1541 char nbuf[64];
1542 pid_t c;
1543
1544 job_log(j, LOG_DEBUG, "Starting");
1545
1546 if (j->p) {
1547 job_log(j, LOG_DEBUG, "already running");
1548 return;
1549 }
1550
1551 j->checkedin = false;
1552
1553 sipc = job_get_bool(j->ldj, LAUNCH_JOBKEY_SERVICEIPC);
1554
1555 if (job_get_bool(j->ldj, LAUNCH_JOBKEY_INETDCOMPATIBILITY))
1556 sipc = true;
1557
1558 if (sipc)
1559 socketpair(AF_UNIX, SOCK_STREAM, 0, spair);
1560
1561 socketpair(AF_UNIX, SOCK_STREAM, 0, execspair);
1562
1563 time(&j->start_time);
1564
1565 switch (c = fork_with_bootstrap_port(launchd_bootstrap_port)) {
1566 case -1:
1567 job_log_error(j, LOG_ERR, "fork() failed, will try again in one second");
1568 close(execspair[0]);
1569 close(execspair[1]);
1570 if (sipc) {
1571 close(spair[0]);
1572 close(spair[1]);
1573 }
1574 if (job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND))
1575 job_ignore(j);
1576 break;
1577 case 0:
1578 close(execspair[0]);
1579 /* wait for our parent to say they've attached a kevent to us */
1580 read(_fd(execspair[1]), &c, sizeof(c));
1581 if (j->firstborn) {
1582 setpgid(getpid(), getpid());
1583 if (isatty(STDIN_FILENO)) {
1584 if (tcsetpgrp(STDIN_FILENO, getpid()) == -1)
1585 job_log_error(j, LOG_WARNING, "tcsetpgrp()");
1586 }
1587 }
1588
1589 if (sipc) {
1590 close(spair[0]);
1591 sprintf(nbuf, "%d", spair[1]);
1592 setenv(LAUNCHD_TRUSTED_FD_ENV, nbuf, 1);
1593 }
1594 job_start_child(j, execspair[1]);
1595 break;
1596 default:
1597 close(execspair[1]);
1598 j->execfd = _fd(execspair[0]);
1599 if (sipc) {
1600 close(spair[1]);
1601 ipc_open(_fd(spair[0]), j);
1602 }
1603 if (kevent_mod(j->execfd, EVFILT_READ, EV_ADD, 0, 0, &j->kqjob_callback) == -1)
1604 job_log_error(j, LOG_ERR, "kevent_mod(j->execfd): %m");
1605 if (kevent_mod(c, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &j->kqjob_callback) == -1) {
1606 job_log_error(j, LOG_ERR, "kevent()");
1607 job_reap(j);
1608 } else {
1609 j->p = c;
1610 total_children++;
1611 if (job_get_bool(j->ldj, LAUNCH_JOBKEY_ONDEMAND))
1612 job_ignore(j);
1613 }
1614 /* this unblocks the child and avoids a race
1615 * between the above fork() and the kevent_mod() */
1616 write(j->execfd, &c, sizeof(c));
1617 break;
1618 }
1619 }
1620
1621 static void job_start_child(struct jobcb *j, int execfd)
1622 {
1623 launch_data_t ldpa = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
1624 bool inetcompat = job_get_bool(j->ldj, LAUNCH_JOBKEY_INETDCOMPATIBILITY);
1625 size_t i, argv_cnt;
1626 const char **argv, *file2exec = "/usr/libexec/launchproxy";
1627
1628 job_setup_attributes(j);
1629
1630 argv_cnt = launch_data_array_get_count(ldpa);
1631 argv = alloca((argv_cnt + 2) * sizeof(char *));
1632 for (i = 0; i < argv_cnt; i++)
1633 argv[i + 1] = launch_data_get_string(launch_data_array_get_index(ldpa, i));
1634 argv[argv_cnt + 1] = NULL;
1635
1636 if (inetcompat) {
1637 argv[0] = file2exec;
1638 } else {
1639 argv++;
1640 file2exec = job_get_file2exec(j->ldj);
1641 }
1642
1643 if (-1 == execvp(file2exec, (char *const*)argv)) {
1644 int e = errno; /* errno is a macro that expands, best not to take the address of it */
1645 write(execfd, &e, sizeof(e));
1646 job_log_error(j, LOG_ERR, "execvp(\"%s\", ...)", file2exec);
1647 }
1648 _exit(EXIT_FAILURE);
1649 }
1650
1651 static void job_setup_attributes(struct jobcb *j)
1652 {
1653 launch_data_t srl = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_SOFTRESOURCELIMITS);
1654 launch_data_t hrl = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_HARDRESOURCELIMITS);
1655 bool inetcompat = job_get_bool(j->ldj, LAUNCH_JOBKEY_INETDCOMPATIBILITY);
1656 launch_data_t tmp;
1657 size_t i;
1658 const char *tmpstr;
1659 struct group *gre = NULL;
1660 gid_t gre_g = 0;
1661 static const struct {
1662 const char *key;
1663 int val;
1664 } limits[] = {
1665 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE, RLIMIT_CORE },
1666 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU, RLIMIT_CPU },
1667 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA, RLIMIT_DATA },
1668 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE, RLIMIT_FSIZE },
1669 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK, RLIMIT_MEMLOCK },
1670 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE, RLIMIT_NOFILE },
1671 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC, RLIMIT_NPROC },
1672 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS, RLIMIT_RSS },
1673 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK, RLIMIT_STACK },
1674 };
1675
1676 setpriority(PRIO_PROCESS, 0, job_get_integer(j->ldj, LAUNCH_JOBKEY_NICE));
1677
1678 if (srl || hrl) {
1679 for (i = 0; i < (sizeof(limits) / sizeof(limits[0])); i++) {
1680 struct rlimit rl;
1681
1682 if (getrlimit(limits[i].val, &rl) == -1) {
1683 job_log_error(j, LOG_WARNING, "getrlimit()");
1684 continue;
1685 }
1686
1687 if (hrl)
1688 rl.rlim_max = job_get_integer(hrl, limits[i].key);
1689 if (srl)
1690 rl.rlim_cur = job_get_integer(srl, limits[i].key);
1691
1692 if (setrlimit(limits[i].val, &rl) == -1)
1693 job_log_error(j, LOG_WARNING, "setrlimit()");
1694 }
1695 }
1696
1697 if (!inetcompat && job_get_bool(j->ldj, LAUNCH_JOBKEY_SESSIONCREATE))
1698 launchd_SessionCreate(job_get_file2exec(j->ldj));
1699
1700 if (job_get_bool(j->ldj, LAUNCH_JOBKEY_LOWPRIORITYIO)) {
1701 int lowprimib[] = { CTL_KERN, KERN_PROC_LOW_PRI_IO };
1702 int val = 1;
1703
1704 if (sysctl(lowprimib, sizeof(lowprimib) / sizeof(lowprimib[0]), NULL, NULL, &val, sizeof(val)) == -1)
1705 job_log_error(j, LOG_WARNING, "sysctl(\"%s\")", "kern.proc_low_pri_io");
1706 }
1707 if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_ROOTDIRECTORY))) {
1708 chroot(tmpstr);
1709 chdir(".");
1710 }
1711 if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_GROUPNAME))) {
1712 gre = getgrnam(tmpstr);
1713 if (gre) {
1714 gre_g = gre->gr_gid;
1715 if (-1 == setgid(gre_g)) {
1716 job_log_error(j, LOG_ERR, "setgid(%d)", gre_g);
1717 _exit(EXIT_FAILURE);
1718 }
1719 } else {
1720 job_log(j, LOG_ERR, "getgrnam(\"%s\") failed", tmpstr);
1721 _exit(EXIT_FAILURE);
1722 }
1723 }
1724 if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_USERNAME))) {
1725 struct passwd *pwe = getpwnam(tmpstr);
1726 if (pwe) {
1727 uid_t pwe_u = pwe->pw_uid;
1728 uid_t pwe_g = pwe->pw_gid;
1729
1730 if (pwe->pw_expire && time(NULL) >= pwe->pw_expire) {
1731 job_log(j, LOG_ERR, "expired account: %s", tmpstr);
1732 _exit(EXIT_FAILURE);
1733 }
1734 if (job_get_bool(j->ldj, LAUNCH_JOBKEY_INITGROUPS)) {
1735 if (-1 == initgroups(tmpstr, gre ? gre_g : pwe_g)) {
1736 job_log_error(j, LOG_ERR, "initgroups()");
1737 _exit(EXIT_FAILURE);
1738 }
1739 }
1740 if (!gre) {
1741 if (-1 == setgid(pwe_g)) {
1742 job_log_error(j, LOG_ERR, "setgid(%d)", pwe_g);
1743 _exit(EXIT_FAILURE);
1744 }
1745 }
1746 if (-1 == setuid(pwe_u)) {
1747 job_log_error(j, LOG_ERR, "setuid(%d)", pwe_u);
1748 _exit(EXIT_FAILURE);
1749 }
1750 } else {
1751 job_log(j, LOG_WARNING, "getpwnam(\"%s\") failed", tmpstr);
1752 _exit(EXIT_FAILURE);
1753 }
1754 }
1755 if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_WORKINGDIRECTORY)))
1756 chdir(tmpstr);
1757 if (launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_UMASK))
1758 umask(job_get_integer(j->ldj, LAUNCH_JOBKEY_UMASK));
1759 if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_STANDARDOUTPATH))) {
1760 int sofd = open(tmpstr, O_WRONLY|O_APPEND|O_CREAT, DEFFILEMODE);
1761 if (sofd == -1) {
1762 job_log_error(j, LOG_WARNING, "open(\"%s\", ...)", tmpstr);
1763 } else {
1764 dup2(sofd, STDOUT_FILENO);
1765 close(sofd);
1766 }
1767 }
1768 if ((tmpstr = job_get_string(j->ldj, LAUNCH_JOBKEY_STANDARDERRORPATH))) {
1769 int sefd = open(tmpstr, O_WRONLY|O_APPEND|O_CREAT, DEFFILEMODE);
1770 if (sefd == -1) {
1771 job_log_error(j, LOG_WARNING, "open(\"%s\", ...)", tmpstr);
1772 } else {
1773 dup2(sefd, STDERR_FILENO);
1774 close(sefd);
1775 }
1776 }
1777 if ((tmp = launch_data_dict_lookup(j->ldj, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES)))
1778 launch_data_dict_iterate(tmp, setup_job_env, NULL);
1779
1780 setsid();
1781 }
1782
1783 #ifdef PID1_REAP_ADOPTED_CHILDREN
1784 __private_extern__ int pid1_child_exit_status = 0;
1785 static void pid1waitpid(void)
1786 {
1787 pid_t p;
1788
1789 while ((p = waitpid(-1, &pid1_child_exit_status, WNOHANG)) > 0) {
1790 if (!launchd_check_pid(p))
1791 init_check_pid(p);
1792 }
1793 }
1794 #endif
1795
1796 static void do_shutdown(void)
1797 {
1798 struct jobcb *j;
1799
1800 shutdown_in_progress = true;
1801
1802 kevent_mod(asynckq, EVFILT_READ, EV_DISABLE, 0, 0, &kqasync_callback);
1803
1804 TAILQ_FOREACH(j, &jobs, tqe)
1805 job_stop(j);
1806
1807 if (getpid() == 1) {
1808 catatonia();
1809 mach_start_shutdown(SIGTERM);
1810 }
1811 }
1812
1813 static void signal_callback(void *obj __attribute__((unused)), struct kevent *kev)
1814 {
1815 switch (kev->ident) {
1816 case SIGHUP:
1817 update_ttys();
1818 reload_launchd_config();
1819 break;
1820 case SIGTERM:
1821 do_shutdown();
1822 break;
1823 #ifdef PID1_REAP_ADOPTED_CHILDREN
1824 case SIGCHLD:
1825 /* <rdar://problem/3632556> Please automatically reap processes reparented to PID 1 */
1826 if (getpid() == 1)
1827 pid1waitpid();
1828 break;
1829 #endif
1830 default:
1831 break;
1832 }
1833 }
1834
1835 static void fs_callback(void)
1836 {
1837 static bool mounted_volfs = false;
1838
1839 if (1 != getpid())
1840 mounted_volfs = true;
1841
1842 if (pending_stdout) {
1843 int fd = open(pending_stdout, O_CREAT|O_APPEND|O_WRONLY, DEFFILEMODE);
1844 if (fd != -1) {
1845 dup2(fd, STDOUT_FILENO);
1846 close(fd);
1847 free(pending_stdout);
1848 pending_stdout = NULL;
1849 }
1850 }
1851 if (pending_stderr) {
1852 int fd = open(pending_stderr, O_CREAT|O_APPEND|O_WRONLY, DEFFILEMODE);
1853 if (fd != -1) {
1854 dup2(fd, STDERR_FILENO);
1855 close(fd);
1856 free(pending_stderr);
1857 pending_stderr = NULL;
1858 }
1859 }
1860
1861 if (!mounted_volfs) {
1862 int r = mount("volfs", VOLFSDIR, MNT_RDONLY, NULL);
1863
1864 if (-1 == r && errno == ENOENT) {
1865 mkdir(VOLFSDIR, ACCESSPERMS & ~(S_IWUSR|S_IWGRP|S_IWOTH));
1866 r = mount("volfs", VOLFSDIR, MNT_RDONLY, NULL);
1867 }
1868
1869 if (-1 == r) {
1870 syslog(LOG_WARNING, "mount(\"%s\", \"%s\", ...): %m", "volfs", VOLFSDIR);
1871 } else {
1872 mounted_volfs = true;
1873 }
1874 }
1875
1876 if (!launchd_inited)
1877 launchd_server_init(false);
1878 }
1879
1880 static void readcfg_callback(void *obj __attribute__((unused)), struct kevent *kev __attribute__((unused)))
1881 {
1882 int status;
1883
1884 #ifdef PID1_REAP_ADOPTED_CHILDREN
1885 if (getpid() == 1)
1886 status = pid1_child_exit_status;
1887 else
1888 #endif
1889 if (-1 == waitpid(readcfg_pid, &status, 0)) {
1890 syslog(LOG_WARNING, "waitpid(readcfg_pid, ...): %m");
1891 return;
1892 }
1893
1894 readcfg_pid = 0;
1895
1896 if (WIFEXITED(status)) {
1897 if (WEXITSTATUS(status))
1898 syslog(LOG_WARNING, "Unable to read launchd.conf: launchctl exited with status: %d", WEXITSTATUS(status));
1899 } else if (WIFSIGNALED(status)) {
1900 syslog(LOG_WARNING, "Unable to read launchd.conf: launchctl exited abnormally: %s", strsignal(WTERMSIG(status)));
1901 } else {
1902 syslog(LOG_WARNING, "Unable to read launchd.conf: launchctl exited abnormally");
1903 }
1904 }
1905
1906 #ifdef EVFILT_MACH_IMPLEMENTED
1907 static void *mach_demand_loop(void *arg __attribute__((unused)))
1908 {
1909 mach_msg_empty_rcv_t dummy;
1910 kern_return_t kr;
1911 mach_port_name_array_t members;
1912 mach_msg_type_number_t membersCnt;
1913 mach_port_status_t status;
1914 mach_msg_type_number_t statusCnt;
1915 unsigned int i;
1916
1917 for (;;) {
1918
1919 /*
1920 * Receive indication of message on demand service
1921 * ports without actually receiving the message (we'll
1922 * let the actual server do that.
1923 */
1924 kr = mach_msg(&dummy.header, MACH_RCV_MSG|MACH_RCV_LARGE,
1925 0, 0, mach_demand_port_set, 0, MACH_PORT_NULL);
1926 if (kr != MACH_RCV_TOO_LARGE) {
1927 syslog(LOG_WARNING, "%s(): mach_msg(): %s", __func__, mach_error_string(kr));
1928 continue;
1929 }
1930
1931 /*
1932 * Some port(s) now have messages on them, find out
1933 * which ones (there is no indication of which port
1934 * triggered in the MACH_RCV_TOO_LARGE indication).
1935 */
1936 kr = mach_port_get_set_status(mach_task_self(),
1937 mach_demand_port_set, &members, &membersCnt);
1938 if (kr != KERN_SUCCESS) {
1939 syslog(LOG_WARNING, "%s(): mach_port_get_set_status(): %s", __func__, mach_error_string(kr));
1940 continue;
1941 }
1942
1943 for (i = 0; i < membersCnt; i++) {
1944 statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT;
1945 kr = mach_port_get_attributes(mach_task_self(), members[i],
1946 MACH_PORT_RECEIVE_STATUS, (mach_port_info_t)&status, &statusCnt);
1947 if (kr != KERN_SUCCESS) {
1948 syslog(LOG_WARNING, "%s(): mach_port_get_attributes(): %s", __func__, mach_error_string(kr));
1949 continue;
1950 }
1951
1952 /*
1953 * For each port with messages, take it out of the
1954 * demand service portset, and inform the main thread
1955 * that it might have to start the server responsible
1956 * for it.
1957 */
1958 if (status.mps_msgcount) {
1959 kr = mach_port_move_member(mach_task_self(), members[i], MACH_PORT_NULL);
1960 if (kr != KERN_SUCCESS) {
1961 syslog(LOG_WARNING, "%s(): mach_port_move_member(): %s", __func__, mach_error_string(kr));
1962 continue;
1963 }
1964 write(machcbwritefd, &(members[i]), sizeof(members[i]));
1965 }
1966 }
1967
1968 kr = vm_deallocate(mach_task_self(), (vm_address_t) members,
1969 (vm_size_t) membersCnt * sizeof(mach_port_name_t));
1970 if (kr != KERN_SUCCESS) {
1971 syslog(LOG_WARNING, "%s(): vm_deallocate(): %s", __func__, mach_error_string(kr));
1972 continue;
1973 }
1974 }
1975
1976 return NULL;
1977 }
1978 #endif
1979
1980 static void reload_launchd_config(void)
1981 {
1982 struct stat sb;
1983 static char *ldconf = PID1LAUNCHD_CONF;
1984 const char *h = getenv("HOME");
1985
1986 if (h && ldconf == PID1LAUNCHD_CONF)
1987 asprintf(&ldconf, "%s/%s", h, LAUNCHD_CONF);
1988
1989 if (!ldconf)
1990 return;
1991
1992 if (lstat(ldconf, &sb) == 0) {
1993 int spair[2];
1994 socketpair(AF_UNIX, SOCK_STREAM, 0, spair);
1995 readcfg_pid = fork_with_bootstrap_port(launchd_bootstrap_port);
1996 if (readcfg_pid == 0) {
1997 char nbuf[100];
1998 close(spair[0]);
1999 sprintf(nbuf, "%d", spair[1]);
2000 setenv(LAUNCHD_TRUSTED_FD_ENV, nbuf, 1);
2001 int fd = open(ldconf, O_RDONLY);
2002 if (fd == -1) {
2003 syslog(LOG_ERR, "open(\"%s\"): %m", ldconf);
2004 _exit(EXIT_FAILURE);
2005 }
2006 dup2(fd, STDIN_FILENO);
2007 close(fd);
2008 execl(LAUNCHCTL_PATH, LAUNCHCTL_PATH, NULL);
2009 syslog(LOG_ERR, "execl(\"%s\", ...): %m", LAUNCHCTL_PATH);
2010 _exit(EXIT_FAILURE);
2011 } else if (readcfg_pid == -1) {
2012 close(spair[0]);
2013 close(spair[1]);
2014 syslog(LOG_ERR, "fork(): %m");
2015 readcfg_pid = 0;
2016 } else {
2017 close(spair[1]);
2018 ipc_open(_fd(spair[0]), NULL);
2019 if (kevent_mod(readcfg_pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, &kqreadcfg_callback) == -1)
2020 syslog(LOG_ERR, "kevent_mod(EVFILT_PROC, &kqreadcfg_callback): %m");
2021 }
2022 }
2023 }
2024
2025 static void conceive_firstborn(char *argv[])
2026 {
2027 launch_data_t r, d = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2028 launch_data_t args = launch_data_alloc(LAUNCH_DATA_ARRAY);
2029 launch_data_t l = launch_data_new_string("com.apple.launchd.firstborn");
2030 size_t i;
2031
2032 for (i = 0; *argv; argv++, i++)
2033 launch_data_array_set_index(args, launch_data_new_string(*argv), i);
2034
2035 launch_data_dict_insert(d, args, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
2036 launch_data_dict_insert(d, l, LAUNCH_JOBKEY_LABEL);
2037
2038 r = load_job(d);
2039
2040 launch_data_free(r);
2041 launch_data_free(d);
2042
2043 TAILQ_FIRST(&jobs)->firstborn = true;
2044 }
2045
2046 static void loopback_setup(void)
2047 {
2048 struct ifaliasreq ifra;
2049 struct in6_aliasreq ifra6;
2050 struct ifreq ifr;
2051 int s, s6;
2052
2053 memset(&ifr, 0, sizeof(ifr));
2054 strcpy(ifr.ifr_name, "lo0");
2055
2056 if (-1 == (s = socket(AF_INET, SOCK_DGRAM, 0)))
2057 syslog(LOG_ERR, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__, "AF_INET");
2058 if (-1 == (s6 = socket(AF_INET6, SOCK_DGRAM, 0)))
2059 syslog(LOG_ERR, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__, "AF_INET6");
2060
2061 if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
2062 syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS): %m");
2063 } else {
2064 ifr.ifr_flags |= IFF_UP;
2065
2066 if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
2067 syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
2068 }
2069
2070 memset(&ifr, 0, sizeof(ifr));
2071 strcpy(ifr.ifr_name, "lo0");
2072
2073 if (ioctl(s6, SIOCGIFFLAGS, &ifr) == -1) {
2074 syslog(LOG_ERR, "ioctl(SIOCGIFFLAGS): %m");
2075 } else {
2076 ifr.ifr_flags |= IFF_UP;
2077
2078 if (ioctl(s6, SIOCSIFFLAGS, &ifr) == -1)
2079 syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
2080 }
2081
2082 memset(&ifra, 0, sizeof(ifra));
2083 strcpy(ifra.ifra_name, "lo0");
2084
2085 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_family = AF_INET;
2086 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
2087 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_len = sizeof(struct sockaddr_in);
2088 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_family = AF_INET;
2089 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr.s_addr = htonl(IN_CLASSA_NET);
2090 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_len = sizeof(struct sockaddr_in);
2091
2092 if (ioctl(s, SIOCAIFADDR, &ifra) == -1)
2093 syslog(LOG_ERR, "ioctl(SIOCAIFADDR ipv4): %m");
2094
2095 memset(&ifra6, 0, sizeof(ifra6));
2096 strcpy(ifra6.ifra_name, "lo0");
2097
2098 ifra6.ifra_addr.sin6_family = AF_INET6;
2099 ifra6.ifra_addr.sin6_addr = in6addr_loopback;
2100 ifra6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
2101 ifra6.ifra_prefixmask.sin6_family = AF_INET6;
2102 memset(&ifra6.ifra_prefixmask.sin6_addr, 0xff, sizeof(struct in6_addr));
2103 ifra6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
2104 ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
2105 ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
2106
2107 if (ioctl(s6, SIOCAIFADDR_IN6, &ifra6) == -1)
2108 syslog(LOG_ERR, "ioctl(SIOCAIFADDR ipv6): %m");
2109
2110 close(s);
2111 close(s6);
2112 }
2113
2114 static void workaround3048875(int argc, char *argv[])
2115 {
2116 int i;
2117 char **ap, *newargv[100], *p = argv[1];
2118
2119 if (argc == 1 || argc > 2)
2120 return;
2121
2122 newargv[0] = argv[0];
2123 for (ap = newargv + 1, i = 1; ap < &newargv[100]; ap++, i++) {
2124 if ((*ap = strsep(&p, " \t")) == NULL)
2125 break;
2126 if (**ap == '\0') {
2127 *ap = NULL;
2128 break;
2129 }
2130 }
2131
2132 if (argc == i)
2133 return;
2134
2135 execv(newargv[0], newargv);
2136 }
2137
2138 static launch_data_t adjust_rlimits(launch_data_t in)
2139 {
2140 static struct rlimit *l = NULL;
2141 static size_t lsz = sizeof(struct rlimit) * RLIM_NLIMITS;
2142 struct rlimit *ltmp;
2143 size_t i,ltmpsz;
2144
2145 if (l == NULL) {
2146 l = malloc(lsz);
2147 for (i = 0; i < RLIM_NLIMITS; i++) {
2148 if (getrlimit(i, l + i) == -1)
2149 syslog(LOG_WARNING, "getrlimit(): %m");
2150 }
2151 }
2152
2153 if (in) {
2154 ltmp = launch_data_get_opaque(in);
2155 ltmpsz = launch_data_get_opaque_size(in);
2156
2157 if (ltmpsz > lsz) {
2158 syslog(LOG_WARNING, "Too much rlimit data sent!");
2159 ltmpsz = lsz;
2160 }
2161
2162 for (i = 0; i < (ltmpsz / sizeof(struct rlimit)); i++) {
2163 if (ltmp[i].rlim_cur == l[i].rlim_cur && ltmp[i].rlim_max == l[i].rlim_max)
2164 continue;
2165
2166 if (readcfg_pid && getpid() == 1) {
2167 int gmib[] = { CTL_KERN, KERN_MAXPROC };
2168 int pmib[] = { CTL_KERN, KERN_MAXPROCPERUID };
2169 const char *gstr = "kern.maxproc";
2170 const char *pstr = "kern.maxprocperuid";
2171 int gval = ltmp[i].rlim_max;
2172 int pval = ltmp[i].rlim_cur;
2173 switch (i) {
2174 case RLIMIT_NOFILE:
2175 gmib[1] = KERN_MAXFILES;
2176 pmib[1] = KERN_MAXFILESPERPROC;
2177 gstr = "kern.maxfiles";
2178 pstr = "kern.maxfilesperproc";
2179 break;
2180 case RLIMIT_NPROC:
2181 /* kernel will not clamp to this value, we must */
2182 if (gval > (2048 + 20))
2183 gval = 2048 + 20;
2184 break;
2185 default:
2186 break;
2187 }
2188 if (sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval)) == -1)
2189 syslog(LOG_WARNING, "sysctl(\"%s\"): %m", gstr);
2190 if (sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval)) == -1)
2191 syslog(LOG_WARNING, "sysctl(\"%s\"): %m", pstr);
2192 }
2193 if (setrlimit(i, ltmp + i) == -1)
2194 syslog(LOG_WARNING, "setrlimit(): %m");
2195 /* the kernel may have clamped the values we gave it */
2196 if (getrlimit(i, l + i) == -1)
2197 syslog(LOG_WARNING, "getrlimit(): %m");
2198 }
2199 }
2200
2201 return launch_data_new_opaque(l, sizeof(struct rlimit) * RLIM_NLIMITS);
2202 }
2203
2204 __private_extern__ void launchd_SessionCreate(const char *who)
2205 {
2206 void *seclib = dlopen(SECURITY_LIB, RTLD_LAZY);
2207 OSStatus (*sescr)(SessionCreationFlags flags, SessionAttributeBits attributes);
2208
2209 if (seclib) {
2210 sescr = dlsym(seclib, "SessionCreate");
2211
2212 if (sescr) {
2213 OSStatus scr = sescr(0, 0);
2214 if (scr != noErr)
2215 syslog(LOG_WARNING, "%s: SessionCreate() failed: %d", who, scr);
2216 } else {
2217 syslog(LOG_WARNING, "%s: couldn't find SessionCreate() in %s", who, SECURITY_LIB);
2218 }
2219
2220 dlclose(seclib);
2221 } else {
2222 syslog(LOG_WARNING, "%s: dlopen(\"%s\",...): %s", who, SECURITY_LIB, dlerror());
2223 }
2224 }
2225
2226 static int dir_has_files(const char *path)
2227 {
2228 DIR *dd = opendir(path);
2229 struct dirent *de;
2230 bool r = 0;
2231
2232 if (!dd)
2233 return -1;
2234
2235 while ((de = readdir(dd))) {
2236 if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
2237 r = 1;
2238 break;
2239 }
2240 }
2241
2242 closedir(dd);
2243 return r;
2244 }
2245
2246 static void job_set_alarm(struct jobcb *j)
2247 {
2248 struct tm otherlatertm, latertm, *nowtm;
2249 time_t later, otherlater = 0, now = time(NULL);
2250
2251 nowtm = localtime(&now);
2252
2253 latertm = *nowtm;
2254
2255 latertm.tm_sec = 0;
2256 latertm.tm_isdst = -1;
2257
2258
2259 if (j->start_cal_interval->tm_min)
2260 latertm.tm_min = j->start_cal_interval->tm_min;
2261 if (j->start_cal_interval->tm_hour)
2262 latertm.tm_hour = j->start_cal_interval->tm_hour;
2263
2264 otherlatertm = latertm;
2265
2266 if (j->start_cal_interval->tm_mday)
2267 latertm.tm_mday = j->start_cal_interval->tm_mday;
2268 if (j->start_cal_interval->tm_mon)
2269 latertm.tm_mon = j->start_cal_interval->tm_mon;
2270
2271 /* cron semantics are fun */
2272 if (j->start_cal_interval->tm_wday) {
2273 int delta, realwday = j->start_cal_interval->tm_wday;
2274
2275 if (realwday == 7)
2276 realwday = 0;
2277
2278 delta = realwday - nowtm->tm_wday;
2279
2280 /* Now Later Delta Desired
2281 * 0 6 6 6
2282 * 6 0 -6 7 + -6
2283 * 1 5 4 4
2284 * 5 1 -4 7 + -4
2285 */
2286 if (delta > 0)
2287 otherlatertm.tm_mday += delta;
2288 else if (delta < 0)
2289 otherlatertm.tm_mday += 7 + delta;
2290 else if (otherlatertm.tm_hour < nowtm->tm_hour)
2291 otherlatertm.tm_mday += 7;
2292 else if (otherlatertm.tm_min < nowtm->tm_min)
2293 otherlatertm.tm_hour++;
2294 else
2295 otherlatertm.tm_min++;
2296
2297 otherlater = mktime(&otherlatertm);
2298 }
2299
2300 if (latertm.tm_mon < nowtm->tm_mon) {
2301 latertm.tm_year++;
2302 } else if (latertm.tm_mday < nowtm->tm_mday) {
2303 latertm.tm_mon++;
2304 } else if (latertm.tm_hour < nowtm->tm_hour) {
2305 latertm.tm_mday++;
2306 } else if (latertm.tm_min < nowtm->tm_min) {
2307 latertm.tm_hour++;
2308 } else {
2309 latertm.tm_min++;
2310 }
2311
2312 later = mktime(&latertm);
2313
2314 if (otherlater) {
2315 if (j->start_cal_interval->tm_mday)
2316 later = later < otherlater ? later : otherlater;
2317 else
2318 later = otherlater;
2319 }
2320
2321 if (-1 == kevent_mod((uintptr_t)j->start_cal_interval, EVFILT_TIMER, EV_ADD, NOTE_ABSOLUTE|NOTE_SECONDS, later, &j->kqjob_callback))
2322 job_log_error(j, LOG_ERR, "adding kevent alarm");
2323 }
2324
2325 static void job_log_error(struct jobcb *j, int pri, const char *msg, ...)
2326 {
2327 size_t newmsg_sz = strlen(msg) + strlen(j->label) + 200;
2328 char *newmsg = alloca(newmsg_sz);
2329 va_list ap;
2330
2331 sprintf(newmsg, "%s: %s: %s", j->label, msg, strerror(errno));
2332
2333 va_start(ap, msg);
2334
2335 vsyslog(pri, newmsg, ap);
2336
2337 va_end(ap);
2338 }
2339
2340 static void job_log(struct jobcb *j, int pri, const char *msg, ...)
2341 {
2342 size_t newmsg_sz = strlen(msg) + sizeof(": ") + strlen(j->label);
2343 char *newmsg = alloca(newmsg_sz);
2344 va_list ap;
2345
2346 sprintf(newmsg, "%s: %s", j->label, msg);
2347
2348 va_start(ap, msg);
2349
2350 vsyslog(pri, newmsg, ap);
2351
2352 va_end(ap);
2353 }
2354
2355 static void async_callback(void)
2356 {
2357 struct timespec timeout = { 0, 0 };
2358 struct kevent kev;
2359
2360 switch (kevent(asynckq, NULL, 0, &kev, 1, &timeout)) {
2361 case -1:
2362 syslog(LOG_DEBUG, "kevent(): %m");
2363 break;
2364 case 1:
2365 (*((kq_callback *)kev.udata))(kev.udata, &kev);
2366 case 0:
2367 break;
2368 default:
2369 syslog(LOG_DEBUG, "unexpected: kevent() returned something != 0, -1 or 1");
2370 }
2371 }