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