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