2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 #include <Security/Authorization.h>
24 #include <Security/AuthorizationTags.h>
25 #include <Security/AuthSession.h>
26 #include <sys/types.h>
27 #include <sys/queue.h>
28 #include <sys/event.h>
30 #include <sys/ucred.h>
31 #include <sys/fcntl.h>
34 #include <sys/sysctl.h>
35 #include <sys/sockio.h>
37 #include <sys/resource.h>
38 #include <sys/ioctl.h>
39 #include <sys/mount.h>
41 #include <netinet/in.h>
42 #include <netinet/in_var.h>
43 #include <netinet6/nd6.h>
61 #include "launch_priv.h"
64 #include "bootstrap_internal.h"
66 #define LAUNCHD_MIN_JOB_RUN_TIME 10
67 #define LAUNCHD_REWARD_JOB_RUN_TIME 60
68 #define LAUNCHD_FAILED_EXITS_THRESHOLD 10
69 #define PID1LAUNCHD_CONF "/etc/launchd.conf"
70 #define LAUNCHD_CONF ".launchd.conf"
71 #define LAUNCHCTL_PATH "/bin/launchctl"
72 #define SECURITY_LIB "/System/Library/Frameworks/Security.framework/Versions/A/Security"
73 #define VOLFSDIR "/.vol"
75 extern char **environ
;
78 kq_callback kqjob_callback
;
79 TAILQ_ENTRY(jobcb
) tqe
;
89 unsigned int start_interval
;
90 struct tm
*start_cal_interval
;
91 unsigned int checkedin
:1, firstborn
:1, debug
:1, throttle
:1, futureflags
:28;
96 kq_callback kqconn_callback
;
97 TAILQ_ENTRY(conncb
) tqe
;
100 int disabled_batch
:1, futureflags
:31;
103 static TAILQ_HEAD(jobcbhead
, jobcb
) jobs
= TAILQ_HEAD_INITIALIZER(jobs
);
104 static TAILQ_HEAD(conncbhead
, conncb
) connections
= TAILQ_HEAD_INITIALIZER(connections
);
105 static int mainkq
= 0;
106 static int asynckq
= 0;
107 static int batch_disabler_count
= 0;
109 static launch_data_t
load_job(launch_data_t pload
);
110 static launch_data_t
get_jobs(const char *which
);
111 static launch_data_t
setstdio(int d
, launch_data_t o
);
112 static launch_data_t
adjust_rlimits(launch_data_t in
);
113 static void batch_job_enable(bool e
, struct conncb
*c
);
114 static void do_shutdown(void);
116 static void listen_callback(void *, struct kevent
*);
117 static void async_callback(void);
118 static void signal_callback(void *, struct kevent
*);
119 static void fs_callback(void);
120 static void simple_zombie_reaper(void *, struct kevent
*);
121 static void readcfg_callback(void *, struct kevent
*);
123 static kq_callback kqlisten_callback
= listen_callback
;
124 static kq_callback kqasync_callback
= (kq_callback
)async_callback
;
125 static kq_callback kqsignal_callback
= signal_callback
;
126 static kq_callback kqfs_callback
= (kq_callback
)fs_callback
;
127 static kq_callback kqreadcfg_callback
= readcfg_callback
;
128 kq_callback kqsimple_zombie_reaper
= simple_zombie_reaper
;
130 static void job_watch(struct jobcb
*j
);
131 static void job_ignore(struct jobcb
*j
);
132 static void job_start(struct jobcb
*j
);
133 static void job_start_child(struct jobcb
*j
, int execfd
);
134 static void job_setup_attributes(struct jobcb
*j
);
135 static void job_stop(struct jobcb
*j
);
136 static void job_reap(struct jobcb
*j
);
137 static void job_remove(struct jobcb
*j
);
138 static void job_set_alarm(struct jobcb
*j
);
139 static void job_callback(void *obj
, struct kevent
*kev
);
140 static void job_log(struct jobcb
*j
, int pri
, const char *msg
, ...) __attribute__((format(printf
, 3, 4)));
141 static void job_log_error(struct jobcb
*j
, int pri
, const char *msg
, ...) __attribute__((format(printf
, 3, 4)));
142 static void job_prep_log_msg(struct jobcb
*j
, char *buf
, const char *msg
, int err
);
144 static void ipc_open(int fd
, struct jobcb
*j
);
145 static void ipc_close(struct conncb
*c
);
146 static void ipc_callback(void *, struct kevent
*);
147 static void ipc_readmsg(launch_data_t msg
, void *context
);
148 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
);
150 #ifdef PID1_REAP_ADOPTED_CHILDREN
151 static void pid1waitpid(void);
152 static bool launchd_check_pid(pid_t p
);
154 static void pid1_magic_init(bool sflag
, bool vflag
, bool xflag
);
155 static void launchd_server_init(bool create_session
);
156 static void conceive_firstborn(char *argv
[]);
158 static void usage(FILE *where
);
159 static int _fd(int fd
);
161 static void loopback_setup(void);
162 static void workaround3048875(int argc
, char *argv
[]);
163 static void reload_launchd_config(void);
164 static int dir_has_files(const char *path
);
165 static void testfd_or_openfd(int fd
, const char *path
, int flags
);
166 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
);
167 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
);
169 static time_t cronemu(int mon
, int mday
, int hour
, int min
);
170 static time_t cronemu_wday(int wday
, int hour
, int min
);
171 static bool cronemu_mon(struct tm
*wtm
, int mon
, int mday
, int hour
, int min
);
172 static bool cronemu_mday(struct tm
*wtm
, int mday
, int hour
, int min
);
173 static bool cronemu_hour(struct tm
*wtm
, int hour
, int min
);
174 static bool cronemu_min(struct tm
*wtm
, int min
);
176 static size_t total_children
= 0;
177 static pid_t readcfg_pid
= 0;
178 static pid_t launchd_proper_pid
= 0;
179 static bool launchd_inited
= false;
180 static bool shutdown_in_progress
= false;
181 static pthread_t mach_server_loop_thread
;
182 mach_port_t launchd_bootstrap_port
= MACH_PORT_NULL
;
183 sigset_t blocked_signals
= 0;
184 pthread_mutex_t giant_lock
= PTHREAD_MUTEX_INITIALIZER
;
185 static char *pending_stdout
= NULL
;
186 static char *pending_stderr
= NULL
;
188 int main(int argc
, char *argv
[])
190 static const int sigigns
[] = { SIGHUP
, SIGINT
, SIGPIPE
, SIGALRM
,
191 SIGTERM
, SIGURG
, SIGTSTP
, SIGTSTP
, SIGCONT
, /*SIGCHLD,*/
192 SIGTTIN
, SIGTTOU
, SIGIO
, SIGXCPU
, SIGXFSZ
, SIGVTALRM
, SIGPROF
,
193 SIGWINCH
, SIGINFO
, SIGUSR1
, SIGUSR2
};
196 bool sflag
= false, xflag
= false, vflag
= false, dflag
= false;
199 __log_liblaunch_bug
= _log_launchd_bug
;
202 workaround3048875(argc
, argv
);
207 testfd_or_openfd(STDIN_FILENO
, _PATH_DEVNULL
, O_RDONLY
);
208 testfd_or_openfd(STDOUT_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
209 testfd_or_openfd(STDERR_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
211 openlog(getprogname(), LOG_CONS
|(getpid() != 1 ? LOG_PID
|LOG_PERROR
: 0), LOG_LAUNCHD
);
212 setlogmask(LOG_UPTO(LOG_NOTICE
));
214 while ((ch
= getopt(argc
, argv
, "dhsvx")) != -1) {
216 case 'd': dflag
= true; break;
217 case 's': sflag
= true; break;
218 case 'x': xflag
= true; break;
219 case 'v': vflag
= true; break;
220 case 'h': usage(stdout
); break;
222 syslog(LOG_WARNING
, "ignoring unknown arguments");
231 assumes(daemon(0, 0) != -1);
234 if (!assumes((mainkq
= kqueue()) != -1)) {
238 if (!assumes((asynckq
= kqueue()) != -1)) {
242 if (!assumes(kevent_mod(asynckq
, EVFILT_READ
, EV_ADD
, 0, 0, &kqasync_callback
) != -1)) {
246 sigemptyset(&blocked_signals
);
248 for (i
= 0; i
< (sizeof(sigigns
) / sizeof(int)); i
++) {
249 assumes(kevent_mod(sigigns
[i
], EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) != -1);
250 sigaddset(&blocked_signals
, sigigns
[i
]);
251 signal(sigigns
[i
], SIG_IGN
);
254 /* sigh... ignoring SIGCHLD has side effects: we can't call wait*() */
255 assumes(kevent_mod(SIGCHLD
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) != -1);
257 assumes(pthread_mutex_lock(&giant_lock
) == 0);
260 pid1_magic_init(sflag
, vflag
, xflag
);
262 launchd_bootstrap_port
= bootstrap_port
;
263 launchd_server_init(argv
[0] ? true : false);
266 /* do this after pid1_magic_init() to not catch ourselves mounting stuff */
267 assumes(kevent_mod(0, EVFILT_FS
, EV_ADD
, 0, 0, &kqfs_callback
) != -1);
271 conceive_firstborn(argv
);
273 reload_launchd_config();
276 job_start(TAILQ_FIRST(&jobs
));
279 static struct timespec timeout
= { 30, 0 };
280 struct timespec
*timeoutp
= NULL
;
284 if (readcfg_pid
== 0)
287 if (TAILQ_EMPTY(&jobs
)) {
288 /* launched on demand */
290 } else if (shutdown_in_progress
&& total_children
== 0) {
295 assumes(pthread_mutex_unlock(&giant_lock
) == 0);
296 kev_r
= kevent(mainkq
, NULL
, 0, &kev
, 1, timeoutp
);
297 assumes(pthread_mutex_lock(&giant_lock
) == 0);
301 syslog(LOG_DEBUG
, "kevent(): %m");
304 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
310 syslog(LOG_DEBUG
, "kevent(): spurious return with infinite timeout");
313 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
319 static void pid1_magic_init(bool sflag
, bool vflag
, bool xflag
)
322 int memmib
[2] = { CTL_HW
, HW_MEMSIZE
};
323 int mvnmib
[2] = { CTL_KERN
, KERN_MAXVNODES
};
324 int hnmib
[2] = { CTL_KERN
, KERN_HOSTNAME
};
326 int tfp_r_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_READ_GROUP
};
327 int tfp_rw_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_RW_GROUP
};
329 gid_t tfp_rw_gid
= 0;
330 struct group
*tfp_gr
;
334 size_t memsz
= sizeof(mem
);
337 if ((tfp_gr
= getgrnam("procview"))) {
338 tfp_r_gid
= tfp_gr
->gr_gid
;
339 assumes(sysctl(tfp_r_mib
, 3, NULL
, NULL
, &tfp_r_gid
, sizeof(tfp_r_gid
)) != -1);
342 if ((tfp_gr
= getgrnam("procmod"))) {
343 tfp_rw_gid
= tfp_gr
->gr_gid
;
344 assumes(sysctl(tfp_rw_mib
, 3, NULL
, NULL
, &tfp_rw_gid
, sizeof(tfp_rw_gid
)) != -1);
348 setpriority(PRIO_PROCESS
, 0, -1);
350 assumes(setsid() != -1);
352 assumes(chdir("/") != -1);
354 if (assumes(sysctl(memmib
, 2, &mem
, &memsz
, NULL
, 0) != -1)) {
355 /* The following assignment of mem to itself if the size
356 * of data returned is 32 bits instead of 64 is a clever
357 * C trick to move the 32 bits on big endian systems to
358 * the least significant bytes of the 64 mem variable.
360 * On little endian systems, this is effectively a no-op.
363 mem
= *(uint32_t *)&mem
;
364 mvn
= mem
/ (64 * 1024) + 1024;
365 assumes(sysctl(mvnmib
, 2, NULL
, NULL
, &mvn
, sizeof(mvn
)) != -1);
367 assumes(sysctl(hnmib
, 2, NULL
, NULL
, "localhost", sizeof("localhost")) != -1);
369 assumes(setlogin("root") != -1);
373 assumes(mount("fdesc", "/dev", MNT_UNION
, NULL
) != -1);
375 setenv("PATH", _PATH_STDPATH
, 1);
377 launchd_bootstrap_port
= mach_init_init();
378 assumes(task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port
) == KERN_SUCCESS
);
379 bootstrap_port
= MACH_PORT_NULL
;
381 assumes(pthread_attr_init(&attr
) == 0);
382 assumes(pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
) == 0);
384 if (!assumes(pthread_create(&mach_server_loop_thread
, &attr
, mach_server_loop
, NULL
) == 0)) {
388 pthread_attr_destroy(&attr
);
390 init_boot(sflag
, vflag
, xflag
);
394 #ifdef PID1_REAP_ADOPTED_CHILDREN
395 static bool launchd_check_pid(pid_t p
)
400 TAILQ_FOREACH(j
, &jobs
, tqe
) {
402 EV_SET(&kev
, p
, EVFILT_PROC
, 0, 0, 0, j
);
403 j
->kqjob_callback(j
, &kev
);
408 if (p
== readcfg_pid
) {
409 readcfg_callback(NULL
, NULL
);
417 static char *sockdir
= NULL
;
418 static char *sockpath
= NULL
;
420 static void launchd_clean_up(void)
422 if (launchd_proper_pid
!= getpid())
428 if (assumes(unlink(sockpath
) != -1))
429 assumes(rmdir(sockdir
) != -1);
435 static void launchd_server_init(bool create_session
)
437 struct sockaddr_un sun
;
439 int r
, fd
= -1, ourdirfd
= -1;
442 memset(&sun
, 0, sizeof(sun
));
443 sun
.sun_family
= AF_UNIX
;
445 if (create_session
) {
446 snprintf(ourdir
, sizeof(ourdir
), "%s/%u.%u", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
447 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u.%u/sock", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
448 setenv(LAUNCHD_SOCKET_ENV
, sun
.sun_path
, 1);
450 snprintf(ourdir
, sizeof(ourdir
), "%s/%u", LAUNCHD_SOCK_PREFIX
, getuid());
451 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u/sock", LAUNCHD_SOCK_PREFIX
, getuid());
457 if (mkdir(LAUNCHD_SOCK_PREFIX
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) == -1) {
458 if (errno
== EROFS
) {
460 } else if (errno
== EEXIST
) {
462 stat(LAUNCHD_SOCK_PREFIX
, &sb
);
463 if (!S_ISDIR(sb
.st_mode
)) {
465 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
469 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
475 if (mkdir(ourdir
, S_IRWXU
) == -1) {
476 if (errno
== EROFS
) {
478 } else if (errno
== EEXIST
) {
481 if (!S_ISDIR(sb
.st_mode
)) {
483 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
487 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", ourdir
);
492 assumes(chown(ourdir
, getuid(), getgid()) != -1);
497 if (!assumes((ourdirfd
= _fd(open(ourdir
, O_RDONLY
))) != -1)) {
501 if (flock(ourdirfd
, LOCK_EX
|LOCK_NB
) == -1) {
502 if (errno
== EWOULDBLOCK
) {
505 syslog(LOG_ERR
, "flock(\"%s\"): %m", ourdir
);
510 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
512 syslog(LOG_ERR
, "unlink(\"thesocket\"): %m");
515 if (!assumes((fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) != -1)) {
518 oldmask
= umask(077);
519 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
523 syslog(LOG_ERR
, "bind(\"thesocket\"): %m");
527 if (!assumes(listen(fd
, SOMAXCONN
) != -1)) {
531 if (!assumes(kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqlisten_callback
) != -1)) {
535 launchd_inited
= true;
537 sockdir
= strdup(ourdir
);
538 sockpath
= strdup(sun
.sun_path
);
540 launchd_proper_pid
= getpid();
541 atexit(launchd_clean_up
);
547 if (!launchd_inited
) {
549 assumes(close(fd
) != -1);
551 assumes(close(ourdirfd
) != -1);
555 static long long job_get_integer(launch_data_t j
, const char *key
)
559 if (!assumes(j
!= NULL
))
562 if ((t
= launch_data_dict_lookup(j
, key
))) {
563 return launch_data_get_integer(t
);
569 static const char *job_get_string(launch_data_t j
, const char *key
)
573 if (!assumes(j
!= NULL
))
576 if ((t
= launch_data_dict_lookup(j
, key
))) {
577 return launch_data_get_string(t
);
583 static const char *job_get_file2exec(launch_data_t j
)
585 launch_data_t tmpi
, tmp
;
587 if (!assumes(j
!= NULL
))
590 if ((tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAM
))) {
591 return launch_data_get_string(tmp
);
593 tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
594 if (assumes(tmp
!= NULL
)) {
595 tmpi
= launch_data_array_get_index(tmp
, 0);
596 if (assumes(tmpi
!= NULL
))
597 return launch_data_get_string(tmpi
);
603 static bool job_get_bool(launch_data_t j
, const char *key
)
607 if (!assumes(j
!= NULL
))
610 if ((t
= launch_data_dict_lookup(j
, key
))) {
611 return launch_data_get_bool(t
);
617 static void ipc_open(int fd
, struct jobcb
*j
)
619 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
621 if (!assumes(c
!= NULL
))
624 assumes(fcntl(fd
, F_SETFL
, O_NONBLOCK
) != -1);
626 c
->kqconn_callback
= ipc_callback
;
627 c
->conn
= launchd_fdopen(fd
);
629 TAILQ_INSERT_TAIL(&connections
, c
, tqe
);
630 assumes(kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
) != -1);
633 static void simple_zombie_reaper(void *obj
__attribute__((unused
)), struct kevent
*kev
)
635 assumes(waitpid(kev
->ident
, NULL
, 0) != -1);
638 static void listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
640 struct sockaddr_un sun
;
641 socklen_t sl
= sizeof(sun
);
644 if (assumes((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) != -1)) {
649 static void ipc_callback(void *obj
, struct kevent
*kev
)
651 struct conncb
*c
= obj
;
654 if (kev
->filter
== EVFILT_READ
) {
655 if (launchd_msg_recv(c
->conn
, ipc_readmsg
, c
) == -1 && errno
!= EAGAIN
) {
656 if (errno
!= ECONNRESET
)
657 syslog(LOG_DEBUG
, "%s(): recv: %m", __func__
);
660 } else if (kev
->filter
== EVFILT_WRITE
) {
661 r
= launchd_msg_send(c
->conn
, NULL
);
663 if (errno
!= EAGAIN
) {
664 syslog(LOG_DEBUG
, "%s(): send: %m", __func__
);
668 assumes(kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
) != -1);
671 syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
676 static void set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
678 setenv(key
, launch_data_get_string(obj
), 1);
681 static void launch_data_close_fds(launch_data_t o
)
685 switch (launch_data_get_type(o
)) {
686 case LAUNCH_DATA_DICTIONARY
:
687 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_close_fds
, NULL
);
689 case LAUNCH_DATA_ARRAY
:
690 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
691 launch_data_close_fds(launch_data_array_get_index(o
, i
));
694 if (launch_data_get_fd(o
) != -1)
695 assumes(close(launch_data_get_fd(o
)) != -1);
702 static void launch_data_revoke_fds(launch_data_t o
)
706 switch (launch_data_get_type(o
)) {
707 case LAUNCH_DATA_DICTIONARY
:
708 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_revoke_fds
, NULL
);
710 case LAUNCH_DATA_ARRAY
:
711 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
712 launch_data_revoke_fds(launch_data_array_get_index(o
, i
));
715 launch_data_set_fd(o
, -1);
722 static void job_ignore_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
724 struct jobcb
*j
= cookie
;
728 switch (launch_data_get_type(o
)) {
729 case LAUNCH_DATA_DICTIONARY
:
730 launch_data_dict_iterate(o
, job_ignore_fds
, cookie
);
732 case LAUNCH_DATA_ARRAY
:
733 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
734 job_ignore_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
737 fd
= launch_data_get_fd(o
);
739 job_log(j
, LOG_DEBUG
, "Ignoring FD: %d", fd
);
740 assumes(kevent_mod(fd
, EVFILT_READ
, EV_DELETE
, 0, 0, NULL
) != -1);
748 static void job_ignore(struct jobcb
*j
)
750 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
754 job_ignore_fds(j_sockets
, NULL
, j
);
756 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
757 assumes(kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
) != -1);
759 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
760 assumes(kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
) != -1);
764 static void job_watch_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
766 struct jobcb
*j
= cookie
;
770 switch (launch_data_get_type(o
)) {
771 case LAUNCH_DATA_DICTIONARY
:
772 launch_data_dict_iterate(o
, job_watch_fds
, cookie
);
774 case LAUNCH_DATA_ARRAY
:
775 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
776 job_watch_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
779 fd
= launch_data_get_fd(o
);
781 job_log(j
, LOG_DEBUG
, "Watching FD: %d", fd
);
782 assumes(kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, cookie
) != -1);
790 static void job_watch(struct jobcb
*j
)
792 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
793 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
794 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
798 job_watch_fds(j_sockets
, NULL
, &j
->kqjob_callback
);
800 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
801 if (-1 == j
->vnodes
[i
]) {
802 launch_data_t ld_idx
= launch_data_array_get_index(ld_vnodes
, i
);
803 const char *thepath
= launch_data_get_string(ld_idx
);
805 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
806 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
808 assumes(kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
809 NOTE_WRITE
|NOTE_EXTEND
|NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
|NOTE_ATTRIB
|NOTE_LINK
,
810 0, &j
->kqjob_callback
) != -1);
813 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
814 assumes(kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
815 NOTE_WRITE
|NOTE_EXTEND
|NOTE_ATTRIB
|NOTE_LINK
, 0, &j
->kqjob_callback
) != -1);
818 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
819 launch_data_t ld_idx
= launch_data_array_get_index(ld_qdirs
, i
);
820 const char *thepath
= launch_data_get_string(ld_idx
);
823 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
824 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
825 } else if (dcc_r
> 0 && !shutdown_in_progress
) {
832 static void job_stop(struct jobcb
*j
)
835 assumes(kill(j
->p
, SIGTERM
) != -1);
838 static void job_remove(struct jobcb
*j
)
843 job_log(j
, LOG_DEBUG
, "Removed");
845 TAILQ_REMOVE(&jobs
, j
, tqe
);
847 if (kevent_mod(j
->p
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqsimple_zombie_reaper
) == -1) {
853 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
854 launch_data_dict_iterate(tmp
, unsetup_job_env
, NULL
);
855 launch_data_close_fds(j
->ldj
);
856 launch_data_free(j
->ldj
);
859 for (i
= 0; i
< j
->vnodes_cnt
; i
++)
860 if (-1 != j
->vnodes
[i
])
864 for (i
= 0; i
< j
->qdirs_cnt
; i
++)
865 if (-1 != j
->qdirs
[i
])
869 if (j
->start_interval
)
870 assumes(kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
) != -1);
871 if (j
->start_cal_interval
) {
872 assumes(kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
) != -1);
873 free(j
->start_cal_interval
);
875 kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
879 struct readmsg_context
{
884 static void ipc_readmsg(launch_data_t msg
, void *context
)
886 struct readmsg_context rmc
= { context
, NULL
};
888 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(msg
)) {
889 launch_data_dict_iterate(msg
, ipc_readmsg2
, &rmc
);
890 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(msg
)) {
891 ipc_readmsg2(NULL
, launch_data_get_string(msg
), &rmc
);
893 rmc
.resp
= launch_data_new_errno(EINVAL
);
896 if (NULL
== rmc
.resp
)
897 rmc
.resp
= launch_data_new_errno(ENOSYS
);
899 launch_data_close_fds(msg
);
901 if (launchd_msg_send(rmc
.c
->conn
, rmc
.resp
) == -1) {
902 if (errno
== EAGAIN
) {
903 assumes(kevent_mod(launchd_getfd(rmc
.c
->conn
), EVFILT_WRITE
, EV_ADD
, 0, 0, &rmc
.c
->kqconn_callback
) != -1);
905 syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %m");
909 launch_data_free(rmc
.resp
);
913 attach_bonjourfds_to_job(launch_data_t o
, const char *key
, void *context
__attribute__((unused
)))
915 struct jobcb
*j
= NULL
;
917 TAILQ_FOREACH(j
, &jobs
, tqe
) {
918 if (strcmp(j
->label
, key
) == 0)
925 launch_data_dict_insert(j
->ldj
, launch_data_copy(o
), LAUNCH_JOBKEY_BONJOURFDS
);
926 launch_data_revoke_fds(o
);
929 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
931 struct readmsg_context
*rmc
= context
;
932 launch_data_t resp
= NULL
;
938 if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
939 TAILQ_FOREACH(j
, &jobs
, tqe
) {
940 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
942 resp
= launch_data_new_errno(0);
946 resp
= launch_data_new_errno(ESRCH
);
947 } else if (!strcmp(cmd
, LAUNCH_KEY_STOPJOB
)) {
948 TAILQ_FOREACH(j
, &jobs
, tqe
) {
949 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
951 resp
= launch_data_new_errno(0);
955 resp
= launch_data_new_errno(ESRCH
);
956 } else if (!strcmp(cmd
, LAUNCH_KEY_REMOVEJOB
)) {
957 TAILQ_FOREACH(j
, &jobs
, tqe
) {
958 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
960 resp
= launch_data_new_errno(0);
964 resp
= launch_data_new_errno(ESRCH
);
965 } else if (!strcmp(cmd
, LAUNCH_KEY_SUBMITJOB
)) {
966 if (launch_data_get_type(data
) == LAUNCH_DATA_ARRAY
) {
970 resp
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
971 for (i
= 0; i
< launch_data_array_get_count(data
); i
++) {
972 tmp
= load_job(launch_data_array_get_index(data
, i
));
973 launch_data_array_set_index(resp
, tmp
, i
);
976 resp
= load_job(data
);
978 } else if (!strcmp(cmd
, LAUNCH_KEY_WORKAROUNDBONJOUR
)) {
979 launch_data_dict_iterate(data
, attach_bonjourfds_to_job
, NULL
);
980 resp
= launch_data_new_errno(0);
981 } else if (!strcmp(cmd
, LAUNCH_KEY_UNSETUSERENVIRONMENT
)) {
982 unsetenv(launch_data_get_string(data
));
983 resp
= launch_data_new_errno(0);
984 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUSERENVIRONMENT
)) {
985 char **tmpenviron
= environ
;
986 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
987 for (; *tmpenviron
; tmpenviron
++) {
989 launch_data_t s
= launch_data_alloc(LAUNCH_DATA_STRING
);
990 launch_data_set_string(s
, strchr(*tmpenviron
, '=') + 1);
991 strncpy(envkey
, *tmpenviron
, sizeof(envkey
));
992 *(strchr(envkey
, '=')) = '\0';
993 launch_data_dict_insert(resp
, s
, envkey
);
995 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUSERENVIRONMENT
)) {
996 launch_data_dict_iterate(data
, set_user_env
, NULL
);
997 resp
= launch_data_new_errno(0);
998 } else if (!strcmp(cmd
, LAUNCH_KEY_CHECKIN
)) {
1000 if (assumes((resp
= launch_data_copy(rmc
->c
->j
->ldj
)) != NULL
)) {
1001 if (NULL
== launch_data_dict_lookup(resp
, LAUNCH_JOBKEY_TIMEOUT
)) {
1002 launch_data_t to
= launch_data_new_integer(LAUNCHD_MIN_JOB_RUN_TIME
);
1003 launch_data_dict_insert(resp
, to
, LAUNCH_JOBKEY_TIMEOUT
);
1006 rmc
->c
->j
->checkedin
= true;
1008 resp
= launch_data_new_errno(EACCES
);
1010 } else if (!strcmp(cmd
, LAUNCH_KEY_RELOADTTYS
)) {
1012 resp
= launch_data_new_errno(0);
1013 } else if (!strcmp(cmd
, LAUNCH_KEY_SHUTDOWN
)) {
1015 resp
= launch_data_new_errno(0);
1016 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBS
)) {
1017 resp
= get_jobs(NULL
);
1018 launch_data_revoke_fds(resp
);
1019 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRESOURCELIMITS
)) {
1020 resp
= adjust_rlimits(NULL
);
1021 } else if (!strcmp(cmd
, LAUNCH_KEY_SETRESOURCELIMITS
)) {
1022 resp
= adjust_rlimits(data
);
1023 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOB
)) {
1024 resp
= get_jobs(launch_data_get_string(data
));
1025 launch_data_revoke_fds(resp
);
1026 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBWITHHANDLES
)) {
1027 resp
= get_jobs(launch_data_get_string(data
));
1028 } else if (!strcmp(cmd
, LAUNCH_KEY_SETLOGMASK
)) {
1029 resp
= launch_data_new_integer(setlogmask(launch_data_get_integer(data
)));
1030 } else if (!strcmp(cmd
, LAUNCH_KEY_GETLOGMASK
)) {
1031 int oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1032 resp
= launch_data_new_integer(oldmask
);
1033 setlogmask(oldmask
);
1034 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUMASK
)) {
1035 resp
= launch_data_new_integer(umask(launch_data_get_integer(data
)));
1036 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUMASK
)) {
1037 mode_t oldmask
= umask(0);
1038 resp
= launch_data_new_integer(oldmask
);
1040 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGESELF
)) {
1041 struct rusage rusage
;
1042 assumes(getrusage(RUSAGE_SELF
, &rusage
) != -1);
1043 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1044 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGECHILDREN
)) {
1045 struct rusage rusage
;
1046 assumes(getrusage(RUSAGE_CHILDREN
, &rusage
) != -1);
1047 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1048 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDOUT
)) {
1049 resp
= setstdio(STDOUT_FILENO
, data
);
1050 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDERR
)) {
1051 resp
= setstdio(STDERR_FILENO
, data
);
1052 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHCONTROL
)) {
1053 batch_job_enable(launch_data_get_bool(data
), rmc
->c
);
1054 resp
= launch_data_new_errno(0);
1055 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHQUERY
)) {
1056 resp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1057 launch_data_set_bool(resp
, batch_disabler_count
== 0);
1063 static launch_data_t
setstdio(int d
, launch_data_t o
)
1065 launch_data_t resp
= launch_data_new_errno(0);
1067 if (launch_data_get_type(o
) == LAUNCH_DATA_STRING
) {
1068 char **where
= &pending_stderr
;
1069 if (d
== STDOUT_FILENO
)
1070 where
= &pending_stdout
;
1073 *where
= strdup(launch_data_get_string(o
));
1074 } else if (launch_data_get_type(o
) == LAUNCH_DATA_FD
) {
1075 assumes(dup2(launch_data_get_fd(o
), d
) != -1);
1077 launch_data_set_errno(resp
, EINVAL
);
1083 static void batch_job_enable(bool e
, struct conncb
*c
)
1085 if (e
&& c
->disabled_batch
) {
1086 batch_disabler_count
--;
1087 c
->disabled_batch
= 0;
1088 if (batch_disabler_count
== 0)
1089 assumes(kevent_mod(asynckq
, EVFILT_READ
, EV_ENABLE
, 0, 0, &kqasync_callback
) != -1);
1090 } else if (!e
&& !c
->disabled_batch
) {
1091 if (batch_disabler_count
== 0)
1092 assumes(kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
) != -1);
1093 batch_disabler_count
++;
1094 c
->disabled_batch
= 1;
1098 static launch_data_t
load_job(launch_data_t pload
)
1100 launch_data_t tmp
, resp
;
1103 bool startnow
, hasprog
= false, hasprogargs
= false;
1105 if ((label
= job_get_string(pload
, LAUNCH_JOBKEY_LABEL
))) {
1106 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1107 if (!strcmp(j
->label
, label
)) {
1108 resp
= launch_data_new_errno(EEXIST
);
1113 resp
= launch_data_new_errno(EINVAL
);
1117 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAM
))
1119 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
))
1122 if (!hasprog
&& !hasprogargs
) {
1123 resp
= launch_data_new_errno(EINVAL
);
1127 j
= calloc(1, sizeof(struct jobcb
) + strlen(label
) + 1);
1128 strcpy(j
->label
, label
);
1129 j
->ldj
= launch_data_copy(pload
);
1130 launch_data_revoke_fds(pload
);
1131 j
->kqjob_callback
= job_callback
;
1134 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
) == NULL
) {
1135 tmp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1136 launch_data_set_bool(tmp
, true);
1137 launch_data_dict_insert(j
->ldj
, tmp
, LAUNCH_JOBKEY_ONDEMAND
);
1140 TAILQ_INSERT_TAIL(&jobs
, j
, tqe
);
1142 j
->debug
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_DEBUG
);
1144 startnow
= !job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1146 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_RUNATLOAD
))
1149 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
))) {
1152 j
->qdirs_cnt
= launch_data_array_get_count(tmp
);
1153 j
->qdirs
= malloc(sizeof(int) * j
->qdirs_cnt
);
1155 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1156 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1158 if (-1 == (j
->qdirs
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1159 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1164 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTINTERVAL
))) {
1165 j
->start_interval
= launch_data_get_integer(tmp
);
1167 if (j
->start_interval
== 0) {
1168 job_log(j
, LOG_WARNING
, "StartInterval is zero, ignoring");
1170 assumes(kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_SECONDS
, j
->start_interval
, j
) != -1);
1174 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTCALENDARINTERVAL
))) {
1175 launch_data_t tmp_k
;
1177 j
->start_cal_interval
= calloc(1, sizeof(struct tm
));
1178 j
->start_cal_interval
->tm_min
= -1;
1179 j
->start_cal_interval
->tm_hour
= -1;
1180 j
->start_cal_interval
->tm_mday
= -1;
1181 j
->start_cal_interval
->tm_wday
= -1;
1182 j
->start_cal_interval
->tm_mon
= -1;
1184 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(tmp
)) {
1185 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MINUTE
)))
1186 j
->start_cal_interval
->tm_min
= launch_data_get_integer(tmp_k
);
1187 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_HOUR
)))
1188 j
->start_cal_interval
->tm_hour
= launch_data_get_integer(tmp_k
);
1189 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_DAY
)))
1190 j
->start_cal_interval
->tm_mday
= launch_data_get_integer(tmp_k
);
1191 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_WEEKDAY
)))
1192 j
->start_cal_interval
->tm_wday
= launch_data_get_integer(tmp_k
);
1193 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MONTH
)))
1194 j
->start_cal_interval
->tm_mon
= launch_data_get_integer(tmp_k
);
1200 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
))) {
1203 j
->vnodes_cnt
= launch_data_array_get_count(tmp
);
1204 j
->vnodes
= malloc(sizeof(int) * j
->vnodes_cnt
);
1206 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1207 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1209 assumes((j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))) != -1);
1214 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
1215 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1217 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1223 resp
= launch_data_new_errno(0);
1228 static launch_data_t
get_jobs(const char *which
)
1231 launch_data_t tmp
, resp
= NULL
;
1234 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1235 if (!strcmp(which
, j
->label
))
1236 resp
= launch_data_copy(j
->ldj
);
1239 resp
= launch_data_new_errno(ESRCH
);
1241 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1243 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1244 tmp
= launch_data_copy(j
->ldj
);
1245 launch_data_dict_insert(resp
, tmp
, j
->label
);
1252 static void usage(FILE *where
)
1254 fprintf(where
, "%s: [-d] [-- command [args ...]]\n", getprogname());
1255 fprintf(where
, "\t-d\tdaemonize\n");
1256 fprintf(where
, "\t-h\tthis usage statement\n");
1258 if (where
== stdout
)
1262 int kevent_mod(uintptr_t ident
, short filter
, u_short flags
, u_int fflags
, intptr_t data
, void *udata
)
1267 if (EVFILT_TIMER
== filter
|| EVFILT_VNODE
== filter
)
1270 if (flags
& EV_ADD
&& !assumes(udata
!= NULL
)) {
1275 #ifdef PID1_REAP_ADOPTED_CHILDREN
1276 if (filter
== EVFILT_PROC
&& getpid() == 1)
1279 EV_SET(&kev
, ident
, filter
, flags
, fflags
, data
, udata
);
1280 return kevent(q
, &kev
, 1, NULL
, 0, NULL
);
1283 static int _fd(int fd
)
1286 assumes(fcntl(fd
, F_SETFD
, 1) != -1);
1290 static void ipc_close(struct conncb
*c
)
1292 batch_job_enable(true, c
);
1294 TAILQ_REMOVE(&connections
, c
, tqe
);
1295 launchd_close(c
->conn
);
1299 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1301 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1302 setenv(key
, launch_data_get_string(obj
), 1);
1305 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1307 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1311 static void job_reap(struct jobcb
*j
)
1313 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1314 time_t td
= time(NULL
) - j
->start_time
;
1315 bool bad_exit
= false;
1318 job_log(j
, LOG_DEBUG
, "Reaping");
1321 assumes(close(j
->execfd
) != -1);
1325 #ifdef PID1_REAP_ADOPTED_CHILDREN
1327 status
= pid1_child_exit_status
;
1330 if (!assumes(waitpid(j
->p
, &status
, 0) != -1)) {
1334 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
1335 job_log(j
, LOG_WARNING
, "exited with exit code: %d", WEXITSTATUS(status
));
1339 if (WIFSIGNALED(status
)) {
1340 int s
= WTERMSIG(status
);
1341 if (SIGKILL
== s
|| SIGTERM
== s
) {
1342 job_log(j
, LOG_NOTICE
, "exited: %s", strsignal(s
));
1344 job_log(j
, LOG_WARNING
, "exited abnormally: %s", strsignal(s
));
1350 if (td
< LAUNCHD_MIN_JOB_RUN_TIME
) {
1351 job_log(j
, LOG_WARNING
, "respawning too quickly! throttling");
1354 } else if (td
>= LAUNCHD_REWARD_JOB_RUN_TIME
) {
1355 job_log(j
, LOG_INFO
, "lived long enough, forgiving past exit failures");
1356 j
->failed_exits
= 0;
1363 if (j
->failed_exits
> 0) {
1364 int failures_left
= LAUNCHD_FAILED_EXITS_THRESHOLD
- j
->failed_exits
;
1366 job_log(j
, LOG_WARNING
, "%d more failure%s without living at least %d seconds will cause job removal",
1367 failures_left
, failures_left
> 1 ? "s" : "", LAUNCHD_REWARD_JOB_RUN_TIME
);
1374 static bool job_restart_fitness_test(struct jobcb
*j
)
1376 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1379 job_log(j
, LOG_DEBUG
, "first born died, begin shutdown");
1382 } else if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
) && !j
->checkedin
) {
1383 job_log(j
, LOG_WARNING
, "failed to checkin");
1386 } else if (j
->failed_exits
>= LAUNCHD_FAILED_EXITS_THRESHOLD
) {
1387 job_log(j
, LOG_WARNING
, "too many failures in succession");
1390 } else if (od
|| shutdown_in_progress
) {
1391 if (!od
&& shutdown_in_progress
)
1392 job_log(j
, LOG_NOTICE
, "exited while shutdown is in progress, will not restart unless demand requires it");
1400 static void job_callback(void *obj
, struct kevent
*kev
)
1402 struct jobcb
*j
= obj
;
1404 bool startnow
= true;
1408 oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1409 job_log(j
, LOG_DEBUG
, "log level debug temporarily enabled while processing job");
1412 if (kev
->filter
== EVFILT_PROC
) {
1415 startnow
= job_restart_fitness_test(j
);
1417 if (startnow
&& j
->throttle
) {
1418 j
->throttle
= false;
1419 job_log(j
, LOG_WARNING
, "will restart in %d seconds", LAUNCHD_MIN_JOB_RUN_TIME
);
1420 if (assumes(kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_ADD
|EV_ONESHOT
,
1421 NOTE_SECONDS
, LAUNCHD_MIN_JOB_RUN_TIME
, &j
->kqjob_callback
) != -1)) {
1425 } else if (kev
->filter
== EVFILT_TIMER
&& (void *)kev
->ident
== j
->start_cal_interval
) {
1427 } else if (kev
->filter
== EVFILT_VNODE
) {
1429 const char *thepath
= NULL
;
1431 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1432 if (j
->vnodes
[i
] == (int)kev
->ident
) {
1433 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
1435 thepath
= launch_data_get_string(launch_data_array_get_index(ld_vnodes
, i
));
1437 job_log(j
, LOG_DEBUG
, "watch path modified: %s", thepath
);
1439 if ((NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
) & kev
->fflags
) {
1440 job_log(j
, LOG_DEBUG
, "watch path invalidated: %s", thepath
);
1441 assumes(close(j
->vnodes
[i
]) != -1);
1442 j
->vnodes
[i
] = -1; /* this will get fixed in job_watch() */
1447 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1448 if (j
->qdirs
[i
] == (int)kev
->ident
) {
1449 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
1452 thepath
= launch_data_get_string(launch_data_array_get_index(ld_qdirs
, i
));
1454 job_log(j
, LOG_DEBUG
, "queue directory modified: %s", thepath
);
1456 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
1457 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
1458 } else if (0 == dcc_r
) {
1459 job_log(j
, LOG_DEBUG
, "spurious wake up, directory empty: %s", thepath
);
1464 /* if we get here, then the vnodes either wasn't a qdir, or if it was, it has entries in it */
1465 } else if (kev
->filter
== EVFILT_READ
&& (int)kev
->ident
== j
->execfd
) {
1466 if (kev
->data
> 0) {
1469 assumes(read(j
->execfd
, &e
, sizeof(e
)) != -1);
1471 job_log_error(j
, LOG_ERR
, "execve()");
1476 assumes(close(j
->execfd
) != -1);
1486 /* the job might have been removed, must not call job_log() */
1487 syslog(LOG_DEBUG
, "restoring original log mask");
1488 setlogmask(oldmask
);
1492 static void job_start(struct jobcb
*j
)
1500 job_log(j
, LOG_DEBUG
, "Starting");
1503 job_log(j
, LOG_DEBUG
, "already running");
1507 j
->checkedin
= false;
1509 sipc
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
);
1511 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
))
1515 assumes(socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
) != -1);
1517 assumes(socketpair(AF_UNIX
, SOCK_STREAM
, 0, execspair
) != -1);
1519 time(&j
->start_time
);
1521 switch (c
= fork_with_bootstrap_port(launchd_bootstrap_port
)) {
1523 job_log_error(j
, LOG_ERR
, "fork() failed, will try again in one second");
1524 assumes(close(execspair
[0]) != -1);
1525 assumes(close(execspair
[1]) != -1);;
1527 assumes(close(spair
[0]) != -1);
1528 assumes(close(spair
[1]) != -1);
1530 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1534 assumes(close(execspair
[0]) != -1);
1535 /* wait for our parent to say they've attached a kevent to us */
1536 assumes(read(_fd(execspair
[1]), &c
, sizeof(c
)) != -1);
1538 setpgid(getpid(), getpid());
1539 if (isatty(STDIN_FILENO
)) {
1540 if (tcsetpgrp(STDIN_FILENO
, getpid()) == -1)
1541 job_log_error(j
, LOG_WARNING
, "tcsetpgrp()");
1546 assumes(close(spair
[0]) != -1);
1547 sprintf(nbuf
, "%d", spair
[1]);
1548 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
1550 job_start_child(j
, execspair
[1]);
1553 assumes(close(execspair
[1]) != -1);
1554 j
->execfd
= _fd(execspair
[0]);
1556 assumes(close(spair
[1]) != -1);
1557 ipc_open(_fd(spair
[0]), j
);
1559 assumes(kevent_mod(j
->execfd
, EVFILT_READ
, EV_ADD
, 0, 0, j
) != -1);
1561 if (assumes(kevent_mod(c
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, j
) != -1)) {
1564 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1569 /* this unblocks the child and avoids a race
1570 * between the above fork() and the kevent_mod() */
1571 assumes(write(j
->execfd
, &c
, sizeof(c
)) != -1);
1576 static void job_start_child(struct jobcb
*j
, int execfd
)
1578 launch_data_t ldpa
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
1579 bool inetcompat
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
) ? true : false;
1581 const char **argv
, *file2exec
= "/usr/libexec/launchproxy";
1583 bool hasprog
= false;
1585 job_setup_attributes(j
);
1588 argv_cnt
= launch_data_array_get_count(ldpa
);
1589 argv
= alloca((argv_cnt
+ 2) * sizeof(char *));
1590 for (i
= 0; i
< argv_cnt
; i
++)
1591 argv
[i
+ 1] = launch_data_get_string(launch_data_array_get_index(ldpa
, i
));
1592 argv
[argv_cnt
+ 1] = NULL
;
1594 argv
= alloca(3 * sizeof(char *));
1595 argv
[1] = job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
);
1599 if (job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
))
1603 argv
[0] = file2exec
;
1606 file2exec
= job_get_file2exec(j
->ldj
);
1610 r
= execv(file2exec
, (char *const*)argv
);
1612 r
= execvp(file2exec
, (char *const*)argv
);
1616 assumes(write(execfd
, &errno
, sizeof(errno
)) != -1);
1617 job_log_error(j
, LOG_ERR
, "execv%s(\"%s\", ...)", hasprog
? "" : "p", file2exec
);
1619 _exit(EXIT_FAILURE
);
1622 static void job_setup_attributes(struct jobcb
*j
)
1624 launch_data_t srl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOFTRESOURCELIMITS
);
1625 launch_data_t hrl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_HARDRESOURCELIMITS
);
1626 bool inetcompat
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
) ? true : false;
1630 struct group
*gre
= NULL
;
1632 static const struct {
1636 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE
, RLIMIT_CORE
},
1637 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU
, RLIMIT_CPU
},
1638 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA
, RLIMIT_DATA
},
1639 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE
, RLIMIT_FSIZE
},
1640 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK
, RLIMIT_MEMLOCK
},
1641 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE
, RLIMIT_NOFILE
},
1642 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC
, RLIMIT_NPROC
},
1643 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS
, RLIMIT_RSS
},
1644 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK
, RLIMIT_STACK
},
1647 setpriority(PRIO_PROCESS
, 0, job_get_integer(j
->ldj
, LAUNCH_JOBKEY_NICE
));
1650 for (i
= 0; i
< (sizeof(limits
) / sizeof(limits
[0])); i
++) {
1653 if (!assumes(getrlimit(limits
[i
].val
, &rl
) != -1)) {
1658 rl
.rlim_max
= job_get_integer(hrl
, limits
[i
].key
);
1660 rl
.rlim_cur
= job_get_integer(srl
, limits
[i
].key
);
1662 assumes(setrlimit(limits
[i
].val
, &rl
) != -1);
1666 if (!inetcompat
&& job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SESSIONCREATE
))
1667 launchd_SessionCreate(job_get_file2exec(j
->ldj
));
1669 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_LOWPRIORITYIO
)) {
1670 int lowprimib
[] = { CTL_KERN
, KERN_PROC_LOW_PRI_IO
};
1673 assumes(sysctl(lowprimib
, sizeof(lowprimib
) / sizeof(lowprimib
[0]), NULL
, NULL
, &val
, sizeof(val
)) != -1);
1675 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_ROOTDIRECTORY
))) {
1676 assumes(chroot(tmpstr
) != -1);
1677 assumes(chdir(".") != -1);
1679 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_GROUPNAME
))) {
1680 gre
= getgrnam(tmpstr
);
1682 gre_g
= gre
->gr_gid
;
1683 if (-1 == setgid(gre_g
)) {
1684 job_log_error(j
, LOG_ERR
, "setgid(%d)", gre_g
);
1688 job_log(j
, LOG_ERR
, "getgrnam(\"%s\") failed", tmpstr
);
1692 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_USERNAME
))) {
1693 struct passwd
*pwe
= getpwnam(tmpstr
);
1695 uid_t pwe_u
= pwe
->pw_uid
;
1696 uid_t pwe_g
= pwe
->pw_gid
;
1698 if (pwe
->pw_expire
&& time(NULL
) >= pwe
->pw_expire
) {
1699 job_log(j
, LOG_ERR
, "expired account: %s", tmpstr
);
1702 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INITGROUPS
)) {
1703 if (-1 == initgroups(tmpstr
, gre
? gre_g
: pwe_g
)) {
1704 job_log_error(j
, LOG_ERR
, "initgroups()");
1709 if (-1 == setgid(pwe_g
)) {
1710 job_log_error(j
, LOG_ERR
, "setgid(%d)", pwe_g
);
1714 if (-1 == setuid(pwe_u
)) {
1715 job_log_error(j
, LOG_ERR
, "setuid(%d)", pwe_u
);
1719 job_log(j
, LOG_WARNING
, "getpwnam(\"%s\") failed", tmpstr
);
1723 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_WORKINGDIRECTORY
)))
1724 assumes(chdir(tmpstr
) != -1);
1725 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_UMASK
))
1726 umask(job_get_integer(j
->ldj
, LAUNCH_JOBKEY_UMASK
));
1727 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDOUTPATH
))) {
1728 int sofd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1729 if (assumes(sofd
!= -1)) {
1730 assumes(dup2(sofd
, STDOUT_FILENO
) != -1);
1731 assumes(close(sofd
) != -1);
1734 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDERRORPATH
))) {
1735 int sefd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1736 if (assumes(sefd
!= -1)) {
1737 assumes(dup2(sefd
, STDERR_FILENO
) != -1);
1738 assumes(close(sefd
) != -1);
1741 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
)))
1742 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1744 assumes(setsid() != -1);
1747 #ifdef PID1_REAP_ADOPTED_CHILDREN
1748 __private_extern__
int pid1_child_exit_status
= 0;
1749 static void pid1waitpid(void)
1753 while ((p
= waitpid(-1, &pid1_child_exit_status
, WNOHANG
)) > 0) {
1754 if (!launchd_check_pid(p
))
1760 static void do_shutdown(void)
1764 shutdown_in_progress
= true;
1766 assumes(kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
) != -1);
1768 TAILQ_FOREACH(j
, &jobs
, tqe
)
1771 if (getpid() == 1) {
1773 mach_start_shutdown(SIGTERM
);
1777 static void signal_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
1779 switch (kev
->ident
) {
1782 reload_launchd_config();
1787 #ifdef PID1_REAP_ADOPTED_CHILDREN
1789 /* <rdar://problem/3632556> Please automatically reap processes reparented to PID 1 */
1799 static void fs_callback(void)
1801 static bool mounted_volfs
= false;
1804 mounted_volfs
= true;
1806 if (pending_stdout
) {
1807 int fd
= open(pending_stdout
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1809 assumes(dup2(fd
, STDOUT_FILENO
) != -1);
1810 assumes(close(fd
) != -1);
1811 free(pending_stdout
);
1812 pending_stdout
= NULL
;
1815 if (pending_stderr
) {
1816 int fd
= open(pending_stderr
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1818 assumes(dup2(fd
, STDERR_FILENO
) != -1);
1819 assumes(close(fd
) != -1);
1820 free(pending_stderr
);
1821 pending_stderr
= NULL
;
1825 if (!mounted_volfs
) {
1826 int r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1828 if (-1 == r
&& errno
== ENOENT
) {
1829 assumes(mkdir(VOLFSDIR
, ACCESSPERMS
& ~(S_IWUSR
|S_IWGRP
|S_IWOTH
)) != -1);
1830 r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1834 syslog(LOG_WARNING
, "mount(\"%s\", \"%s\", ...): %m", "volfs", VOLFSDIR
);
1836 mounted_volfs
= true;
1840 if (!launchd_inited
)
1841 launchd_server_init(false);
1844 static void readcfg_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1848 #ifdef PID1_REAP_ADOPTED_CHILDREN
1850 status
= pid1_child_exit_status
;
1853 if (-1 == waitpid(readcfg_pid
, &status
, 0)) {
1854 syslog(LOG_WARNING
, "waitpid(readcfg_pid, ...): %m");
1860 if (WIFEXITED(status
)) {
1861 if (WEXITSTATUS(status
))
1862 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited with status: %d", WEXITSTATUS(status
));
1863 } else if (WIFSIGNALED(status
)) {
1864 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally: %s", strsignal(WTERMSIG(status
)));
1866 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally");
1870 static void reload_launchd_config(void)
1873 static char *ldconf
= PID1LAUNCHD_CONF
;
1874 const char *h
= getenv("HOME");
1876 if (h
&& ldconf
== PID1LAUNCHD_CONF
)
1877 asprintf(&ldconf
, "%s/%s", h
, LAUNCHD_CONF
);
1882 if (lstat(ldconf
, &sb
) == 0) {
1884 assumes(socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
) != -1);
1885 readcfg_pid
= fork_with_bootstrap_port(launchd_bootstrap_port
);
1886 if (readcfg_pid
== 0) {
1888 assumes(close(spair
[0]) != -1);
1889 sprintf(nbuf
, "%d", spair
[1]);
1890 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
1891 int fd
= open(ldconf
, O_RDONLY
);
1893 syslog(LOG_ERR
, "open(\"%s\"): %m", ldconf
);
1896 assumes(dup2(fd
, STDIN_FILENO
) != -1);
1897 assumes(close(fd
) != -1);
1898 assumes(execl(LAUNCHCTL_PATH
, LAUNCHCTL_PATH
, NULL
) != -1);
1899 _exit(EXIT_FAILURE
);
1900 } else if (readcfg_pid
== -1) {
1901 assumes(close(spair
[0]) != -1);
1902 assumes(close(spair
[1]) != -1);
1903 syslog(LOG_ERR
, "fork(): %m");
1906 assumes(close(spair
[1]) != -1);
1907 ipc_open(_fd(spair
[0]), NULL
);
1908 assumes(kevent_mod(readcfg_pid
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqreadcfg_callback
) != -1);
1913 static void conceive_firstborn(char *argv
[])
1915 launch_data_t r
, d
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1916 launch_data_t args
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1917 launch_data_t l
= launch_data_new_string("com.apple.launchd.firstborn");
1920 for (i
= 0; *argv
; argv
++, i
++)
1921 launch_data_array_set_index(args
, launch_data_new_string(*argv
), i
);
1923 launch_data_dict_insert(d
, args
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
1924 launch_data_dict_insert(d
, l
, LAUNCH_JOBKEY_LABEL
);
1928 launch_data_free(r
);
1929 launch_data_free(d
);
1931 TAILQ_FIRST(&jobs
)->firstborn
= true;
1934 static void loopback_setup(void)
1936 struct ifaliasreq ifra
;
1937 struct in6_aliasreq ifra6
;
1941 memset(&ifr
, 0, sizeof(ifr
));
1942 strcpy(ifr
.ifr_name
, "lo0");
1944 assumes((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) != -1);
1945 assumes((s6
= socket(AF_INET6
, SOCK_DGRAM
, 0)) != -1);
1947 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) == -1) {
1948 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
1950 ifr
.ifr_flags
|= IFF_UP
;
1952 assumes(ioctl(s
, SIOCSIFFLAGS
, &ifr
) != -1);
1955 memset(&ifr
, 0, sizeof(ifr
));
1956 strcpy(ifr
.ifr_name
, "lo0");
1958 if (ioctl(s6
, SIOCGIFFLAGS
, &ifr
) == -1) {
1959 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
1961 ifr
.ifr_flags
|= IFF_UP
;
1963 assumes(ioctl(s6
, SIOCSIFFLAGS
, &ifr
) != -1);
1966 memset(&ifra
, 0, sizeof(ifra
));
1967 strcpy(ifra
.ifra_name
, "lo0");
1969 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_family
= AF_INET
;
1970 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
1971 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_len
= sizeof(struct sockaddr_in
);
1972 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_family
= AF_INET
;
1973 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
.s_addr
= htonl(IN_CLASSA_NET
);
1974 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_len
= sizeof(struct sockaddr_in
);
1976 assumes(ioctl(s
, SIOCAIFADDR
, &ifra
) != -1);
1978 memset(&ifra6
, 0, sizeof(ifra6
));
1979 strcpy(ifra6
.ifra_name
, "lo0");
1981 ifra6
.ifra_addr
.sin6_family
= AF_INET6
;
1982 ifra6
.ifra_addr
.sin6_addr
= in6addr_loopback
;
1983 ifra6
.ifra_addr
.sin6_len
= sizeof(struct sockaddr_in6
);
1984 ifra6
.ifra_prefixmask
.sin6_family
= AF_INET6
;
1985 memset(&ifra6
.ifra_prefixmask
.sin6_addr
, 0xff, sizeof(struct in6_addr
));
1986 ifra6
.ifra_prefixmask
.sin6_len
= sizeof(struct sockaddr_in6
);
1987 ifra6
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
1988 ifra6
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
1990 assumes(ioctl(s6
, SIOCAIFADDR_IN6
, &ifra6
) != -1);
1992 assumes(close(s
) != -1);
1993 assumes(close(s6
) != -1);
1996 static void workaround3048875(int argc
, char *argv
[])
1999 char **ap
, *newargv
[100], *p
= argv
[1];
2001 if (argc
== 1 || argc
> 2)
2004 newargv
[0] = argv
[0];
2005 for (ap
= newargv
+ 1, i
= 1; ap
< &newargv
[100]; ap
++, i
++) {
2006 if ((*ap
= strsep(&p
, " \t")) == NULL
)
2017 execv(newargv
[0], newargv
);
2020 static launch_data_t
adjust_rlimits(launch_data_t in
)
2022 static struct rlimit
*l
= NULL
;
2023 static size_t lsz
= sizeof(struct rlimit
) * RLIM_NLIMITS
;
2024 struct rlimit
*ltmp
;
2029 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
2030 assumes(getrlimit(i
, l
+ i
) != -1);
2035 ltmp
= launch_data_get_opaque(in
);
2036 ltmpsz
= launch_data_get_opaque_size(in
);
2039 syslog(LOG_WARNING
, "Too much rlimit data sent!");
2043 for (i
= 0; i
< (ltmpsz
/ sizeof(struct rlimit
)); i
++) {
2044 if (ltmp
[i
].rlim_cur
== l
[i
].rlim_cur
&& ltmp
[i
].rlim_max
== l
[i
].rlim_max
)
2047 if (readcfg_pid
&& getpid() == 1) {
2048 int gmib
[] = { CTL_KERN
, KERN_MAXPROC
};
2049 int pmib
[] = { CTL_KERN
, KERN_MAXPROCPERUID
};
2050 const char *gstr
= "kern.maxproc";
2051 const char *pstr
= "kern.maxprocperuid";
2052 int gval
= ltmp
[i
].rlim_max
;
2053 int pval
= ltmp
[i
].rlim_cur
;
2056 gmib
[1] = KERN_MAXFILES
;
2057 pmib
[1] = KERN_MAXFILESPERPROC
;
2058 gstr
= "kern.maxfiles";
2059 pstr
= "kern.maxfilesperproc";
2062 /* kernel will not clamp to this value, we must */
2063 if (gval
> (2048 + 20))
2069 assumes(sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)) != -1);
2070 assumes(sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)) != -1);
2072 assumes(setrlimit(i
, ltmp
+ i
) != -1);
2073 /* the kernel may have clamped the values we gave it */
2074 assumes(getrlimit(i
, l
+ i
) != -1);
2078 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);
2081 __private_extern__
void launchd_SessionCreate(const char *who
)
2083 void *seclib
= dlopen(SECURITY_LIB
, RTLD_LAZY
);
2084 OSStatus (*sescr
)(SessionCreationFlags flags
, SessionAttributeBits attributes
);
2087 sescr
= dlsym(seclib
, "SessionCreate");
2090 OSStatus scr
= sescr(0, 0);
2092 syslog(LOG_WARNING
, "%s: SessionCreate() failed: %d", who
, scr
);
2094 syslog(LOG_WARNING
, "%s: couldn't find SessionCreate() in %s", who
, SECURITY_LIB
);
2099 syslog(LOG_WARNING
, "%s: dlopen(\"%s\",...): %s", who
, SECURITY_LIB
, dlerror());
2103 static int dir_has_files(const char *path
)
2105 DIR *dd
= opendir(path
);
2112 while ((de
= readdir(dd
))) {
2113 if (strcmp(de
->d_name
, ".") && strcmp(de
->d_name
, "..")) {
2123 static void job_set_alarm(struct jobcb
*j
)
2127 later
= cronemu(j
->start_cal_interval
->tm_mon
, j
->start_cal_interval
->tm_mday
,
2128 j
->start_cal_interval
->tm_hour
, j
->start_cal_interval
->tm_min
);
2130 if (j
->start_cal_interval
->tm_wday
!= -1) {
2131 time_t otherlater
= cronemu_wday(j
->start_cal_interval
->tm_wday
,
2132 j
->start_cal_interval
->tm_hour
, j
->start_cal_interval
->tm_min
);
2134 if (-1 != j
->start_cal_interval
->tm_mday
) {
2135 later
= later
< otherlater
? later
: otherlater
;
2141 if (-1 == kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_ABSOLUTE
|NOTE_SECONDS
, later
, j
)) {
2142 job_log_error(j
, LOG_ERR
, "adding kevent alarm");
2144 job_log(j
, LOG_INFO
, "scheduled to run again at: %s", ctime(&later
));
2149 job_prep_log_msg(struct jobcb
*j
, char *buf
, const char *msg
, int err
)
2151 size_t lsz
= strlen(j
->label
);
2154 for (i
= 0, o
= 0; i
< lsz
; i
++, o
++) {
2155 if (j
->label
[i
] == '%') {
2160 buf
[o
] = j
->label
[i
];
2168 sprintf(buf
+ o
, "%s: %s", msg
, strerror(err
));
2170 strcpy(buf
+ o
, msg
);
2175 job_log_error(struct jobcb
*j
, int pri
, const char *msg
, ...)
2180 job_prep_log_msg(j
, newmsg
, msg
, errno
);
2184 vsyslog(pri
, newmsg
, ap
);
2190 job_log(struct jobcb
*j
, int pri
, const char *msg
, ...)
2195 job_prep_log_msg(j
, newmsg
, msg
, 0);
2199 vsyslog(pri
, newmsg
, ap
);
2204 static void async_callback(void)
2206 struct timespec timeout
= { 0, 0 };
2209 switch (kevent(asynckq
, NULL
, 0, &kev
, 1, &timeout
)) {
2211 syslog(LOG_DEBUG
, "kevent(): %m");
2214 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
2218 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
2222 static void testfd_or_openfd(int fd
, const char *path
, int flags
)
2226 if (-1 != (tmpfd
= dup(fd
))) {
2227 assumes(close(tmpfd
) != -1);
2229 if (-1 == (tmpfd
= open(path
, flags
))) {
2230 syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
2231 } else if (tmpfd
!= fd
) {
2232 assumes(dup2(tmpfd
, fd
) != -1);
2233 assumes(close(tmpfd
) != -1);
2239 cronemu(int mon
, int mday
, int hour
, int min
)
2241 struct tm workingtm
;
2245 workingtm
= *localtime(&now
);
2247 workingtm
.tm_isdst
= -1;
2248 workingtm
.tm_sec
= 0;
2251 while (!cronemu_mon(&workingtm
, mon
, mday
, hour
, min
)) {
2252 workingtm
.tm_year
++;
2253 workingtm
.tm_mon
= 0;
2254 workingtm
.tm_mday
= 1;
2255 workingtm
.tm_hour
= 0;
2256 workingtm
.tm_min
= 0;
2260 return mktime(&workingtm
);
2264 cronemu_wday(int wday
, int hour
, int min
)
2266 struct tm workingtm
;
2270 workingtm
= *localtime(&now
);
2272 workingtm
.tm_isdst
= -1;
2273 workingtm
.tm_sec
= 0;
2279 while (workingtm
.tm_wday
!= wday
|| !cronemu_hour(&workingtm
, hour
, min
)) {
2280 workingtm
.tm_mday
++;
2281 workingtm
.tm_hour
= 0;
2282 workingtm
.tm_min
= 0;
2283 cronemu_hour(&workingtm
, hour
, min
);
2287 return mktime(&workingtm
);
2291 cronemu_mon(struct tm
*wtm
, int mon
, int mday
, int hour
, int min
)
2294 struct tm workingtm
= *wtm
;
2297 while (!cronemu_mday(&workingtm
, mday
, hour
, min
)) {
2299 workingtm
.tm_mday
= 1;
2300 workingtm
.tm_hour
= 0;
2301 workingtm
.tm_min
= 0;
2302 carrytest
= workingtm
.tm_mon
;
2304 if (carrytest
!= workingtm
.tm_mon
)
2311 if (mon
< wtm
->tm_mon
)
2314 if (mon
> wtm
->tm_mon
) {
2321 return cronemu_mday(wtm
, mday
, hour
, min
);
2325 cronemu_mday(struct tm
*wtm
, int mday
, int hour
, int min
)
2328 struct tm workingtm
= *wtm
;
2331 while (!cronemu_hour(&workingtm
, hour
, min
)) {
2332 workingtm
.tm_mday
++;
2333 workingtm
.tm_hour
= 0;
2334 workingtm
.tm_min
= 0;
2335 carrytest
= workingtm
.tm_mday
;
2337 if (carrytest
!= workingtm
.tm_mday
)
2344 if (mday
< wtm
->tm_mday
)
2347 if (mday
> wtm
->tm_mday
) {
2348 wtm
->tm_mday
= mday
;
2353 return cronemu_hour(wtm
, hour
, min
);
2357 cronemu_hour(struct tm
*wtm
, int hour
, int min
)
2360 struct tm workingtm
= *wtm
;
2363 while (!cronemu_min(&workingtm
, min
)) {
2364 workingtm
.tm_hour
++;
2365 workingtm
.tm_min
= 0;
2366 carrytest
= workingtm
.tm_hour
;
2368 if (carrytest
!= workingtm
.tm_hour
)
2375 if (hour
< wtm
->tm_hour
)
2378 if (hour
> wtm
->tm_hour
) {
2379 wtm
->tm_hour
= hour
;
2383 return cronemu_min(wtm
, min
);
2387 cronemu_min(struct tm
*wtm
, int min
)
2392 if (min
< wtm
->tm_min
)
2395 if (min
> wtm
->tm_min
) {
2403 _log_launchd_bug(const char *path
, unsigned int line
, const char *test
)
2405 int saved_errno
= errno
;
2406 const char *file
= strrchr(path
, '/');
2414 syslog(LOG_NOTICE
, "Bug: %s:%u:%u: %s", file
, line
, saved_errno
, test
);