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 #ifdef EVFILT_MACH_IMPLEMENTED
27 #include <mach/mach_error.h>
28 #include <mach/port.h>
30 #include <sys/types.h>
31 #include <sys/queue.h>
32 #include <sys/event.h>
34 #include <sys/ucred.h>
35 #include <sys/fcntl.h>
38 #include <sys/sysctl.h>
39 #include <sys/sockio.h>
41 #include <sys/resource.h>
42 #include <sys/ioctl.h>
43 #include <sys/mount.h>
45 #include <netinet/in.h>
46 #include <netinet/in_var.h>
47 #include <netinet6/nd6.h>
65 #include "launch_priv.h"
68 #include "bootstrap_internal.h"
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"
79 extern char **environ
;
82 kq_callback kqjob_callback
;
83 TAILQ_ENTRY(jobcb
) tqe
;
93 unsigned int start_interval
;
94 struct tm
*start_cal_interval
;
95 unsigned int checkedin
:1, firstborn
:1, debug
:1, throttle
:1, futureflags
:28;
100 kq_callback kqconn_callback
;
101 TAILQ_ENTRY(conncb
) tqe
;
104 int disabled_batch
:1, futureflags
:31;
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;
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);
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
*);
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
;
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)));
147 static void ipc_open(int fd
, struct jobcb
*j
);
148 static void ipc_close(struct conncb
*c
);
149 static void ipc_callback(void *, struct kevent
*);
150 static void ipc_readmsg(launch_data_t msg
, void *context
);
151 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
);
153 #ifdef PID1_REAP_ADOPTED_CHILDREN
154 static void pid1waitpid(void);
155 static bool launchd_check_pid(pid_t p
);
157 static void pid1_magic_init(bool sflag
, bool vflag
, bool xflag
);
158 static void launchd_server_init(bool create_session
);
159 static void conceive_firstborn(char *argv
[]);
161 #ifdef EVFILT_MACH_IMPLEMENTED
162 static void *mach_demand_loop(void *);
163 static void mach_callback(void *, struct kevent
*);
164 static kq_callback kqmach_callback
= mach_callback
;
167 static void usage(FILE *where
);
168 static int _fd(int fd
);
170 static void loopback_setup(void);
171 static void workaround3048875(int argc
, char *argv
[]);
172 static void reload_launchd_config(void);
173 static int dir_has_files(const char *path
);
174 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
);
175 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
);
178 static size_t total_children
= 0;
179 static pid_t readcfg_pid
= 0;
180 static bool launchd_inited
= false;
181 static bool shutdown_in_progress
= false;
182 static pthread_t mach_server_loop_thread
;
183 mach_port_t launchd_bootstrap_port
= MACH_PORT_NULL
;
184 sigset_t blocked_signals
= 0;
185 static char *pending_stdout
= NULL
;
186 static char *pending_stderr
= NULL
;
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
};
194 void testfd_or_openfd(int fd
, const char *path
, int flags
) {
197 if (-1 != (tmpfd
= dup(fd
))) {
200 if (-1 == (tmpfd
= open(path
, flags
))) {
201 syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
202 } else if (tmpfd
!= fd
) {
210 bool sflag
= false, xflag
= false, vflag
= false, dflag
= false;
214 workaround3048875(argc
, argv
);
219 testfd_or_openfd(STDIN_FILENO
, _PATH_DEVNULL
, O_RDONLY
);
220 testfd_or_openfd(STDOUT_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
221 testfd_or_openfd(STDERR_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
223 openlog(getprogname(), LOG_CONS
|(getpid() != 1 ? LOG_PID
|LOG_PERROR
: 0), LOG_LAUNCHD
);
224 setlogmask(LOG_UPTO(LOG_NOTICE
));
226 while ((ch
= getopt(argc
, argv
, "dhsvx")) != -1) {
228 case 'd': dflag
= true; break;
229 case 's': sflag
= true; break;
230 case 'x': xflag
= true; break;
231 case 'v': vflag
= true; break;
232 case 'h': usage(stdout
); break;
234 syslog(LOG_WARNING
, "ignoring unknown arguments");
242 if (dflag
&& daemon(0, 0) == -1)
243 syslog(LOG_WARNING
, "couldn't daemonize: %m");
245 if ((mainkq
= kqueue()) == -1) {
246 syslog(LOG_EMERG
, "kqueue(): %m");
250 if ((asynckq
= kqueue()) == -1) {
251 syslog(LOG_ERR
, "kqueue(): %m");
255 if (kevent_mod(asynckq
, EVFILT_READ
, EV_ADD
, 0, 0, &kqasync_callback
) == -1) {
256 syslog(LOG_ERR
, "kevent_mod(asynckq, EVFILT_READ): %m");
260 sigemptyset(&blocked_signals
);
262 for (i
= 0; i
< (sizeof(sigigns
) / sizeof(int)); i
++) {
263 if (kevent_mod(sigigns
[i
], EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
264 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", sigigns
[i
]);
265 sigaddset(&blocked_signals
, sigigns
[i
]);
266 signal(sigigns
[i
], SIG_IGN
);
269 /* sigh... ignoring SIGCHLD has side effects: we can't call wait*() */
270 if (kevent_mod(SIGCHLD
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
271 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", SIGCHLD
);
274 pid1_magic_init(sflag
, vflag
, xflag
);
276 launchd_bootstrap_port
= bootstrap_port
;
277 launchd_server_init(argv
[0] ? true : false);
280 /* do this after pid1_magic_init() to not catch ourselves mounting stuff */
281 if (kevent_mod(0, EVFILT_FS
, EV_ADD
, 0, 0, &kqfs_callback
) == -1)
282 syslog(LOG_ERR
, "kevent_mod(EVFILT_FS, &kqfs_callback): %m");
286 conceive_firstborn(argv
);
288 reload_launchd_config();
291 job_start(TAILQ_FIRST(&jobs
));
294 static struct timespec timeout
= { 30, 0 };
295 struct timespec
*timeoutp
= NULL
;
298 if (readcfg_pid
== 0)
301 if (TAILQ_EMPTY(&jobs
)) {
302 /* launched on demand */
304 } else if (shutdown_in_progress
&& total_children
== 0) {
309 switch (kevent(mainkq
, NULL
, 0, &kev
, 1, timeoutp
)) {
311 syslog(LOG_DEBUG
, "kevent(): %m");
314 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
320 syslog(LOG_DEBUG
, "kevent(): spurious return with infinite timeout");
323 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
329 static void pid1_magic_init(bool sflag
, bool vflag
, bool xflag
)
332 int memmib
[2] = { CTL_HW
, HW_PHYSMEM
};
333 int mvnmib
[2] = { CTL_KERN
, KERN_MAXVNODES
};
334 int hnmib
[2] = { CTL_KERN
, KERN_HOSTNAME
};
337 size_t memsz
= sizeof(mem
);
340 setpriority(PRIO_PROCESS
, 0, -1);
343 syslog(LOG_ERR
, "setsid(): %m");
345 if (chdir("/") == -1)
346 syslog(LOG_ERR
, "chdir(\"/\"): %m");
348 if (sysctl(memmib
, 2, &mem
, &memsz
, NULL
, 0) == -1) {
349 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "hw.physmem");
351 /* The following assignment of mem to itself if the size
352 * of data returned is 32 bits instead of 64 is a clever
353 * C trick to move the 32 bits on big endian systems to
354 * the least significant bytes of the 64 mem variable.
356 * On little endian systems, this is effectively a no-op.
359 mem
= *(uint32_t *)&mem
;
360 mvn
= mem
/ (64 * 1024) + 1024;
361 if (sysctl(mvnmib
, 2, NULL
, NULL
, &mvn
, sizeof(mvn
)) == -1)
362 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.maxvnodes");
364 if (sysctl(hnmib
, 2, NULL
, NULL
, "localhost", sizeof("localhost")) == -1)
365 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.hostname");
367 if (setlogin("root") == -1)
368 syslog(LOG_ERR
, "setlogin(\"root\"): %m");
372 if (mount("fdesc", "/dev", MNT_UNION
, NULL
) == -1)
373 syslog(LOG_ERR
, "mount(\"%s\", \"%s\", ...): %m", "fdesc", "/dev/");
375 setenv("PATH", _PATH_STDPATH
, 1);
377 launchd_bootstrap_port
= mach_init_init();
378 task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port
);
379 bootstrap_port
= MACH_PORT_NULL
;
381 pthread_attr_init(&attr
);
382 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
384 pthr_r
= pthread_create(&mach_server_loop_thread
, &attr
, mach_server_loop
, NULL
);
386 syslog(LOG_ERR
, "pthread_create(mach_server_loop): %s", strerror(pthr_r
));
390 pthread_attr_destroy(&attr
);
392 init_boot(sflag
, vflag
, xflag
);
396 #ifdef PID1_REAP_ADOPTED_CHILDREN
397 static bool launchd_check_pid(pid_t p
)
402 TAILQ_FOREACH(j
, &jobs
, tqe
) {
404 EV_SET(&kev
, p
, EVFILT_PROC
, 0, 0, 0, j
);
405 j
->kqjob_callback(j
, &kev
);
410 if (p
== readcfg_pid
) {
411 readcfg_callback(NULL
, NULL
);
419 static char *sockdir
= NULL
;
420 static char *sockpath
= NULL
;
422 static void launchd_clean_up(void)
427 if (-1 == unlink(sockpath
))
428 syslog(LOG_WARNING
, "unlink(\"%s\"): %m", sockpath
);
429 else if (-1 == rmdir(sockdir
))
430 syslog(LOG_WARNING
, "rmdir(\"%s\"): %m", sockdir
);
436 static void launchd_server_init(bool create_session
)
438 struct sockaddr_un sun
;
440 int r
, fd
= -1, ourdirfd
= -1;
443 memset(&sun
, 0, sizeof(sun
));
444 sun
.sun_family
= AF_UNIX
;
446 if (create_session
) {
447 snprintf(ourdir
, sizeof(ourdir
), "%s/%u.%u", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
448 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u.%u/sock", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
449 setenv(LAUNCHD_SOCKET_ENV
, sun
.sun_path
, 1);
451 snprintf(ourdir
, sizeof(ourdir
), "%s/%u", LAUNCHD_SOCK_PREFIX
, getuid());
452 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u/sock", LAUNCHD_SOCK_PREFIX
, getuid());
458 if (mkdir(LAUNCHD_SOCK_PREFIX
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) == -1) {
459 if (errno
== EROFS
) {
461 } else if (errno
== EEXIST
) {
463 stat(LAUNCHD_SOCK_PREFIX
, &sb
);
464 if (!S_ISDIR(sb
.st_mode
)) {
466 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
470 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
476 if (mkdir(ourdir
, S_IRWXU
) == -1) {
477 if (errno
== EROFS
) {
479 } else if (errno
== EEXIST
) {
482 if (!S_ISDIR(sb
.st_mode
)) {
484 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
488 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", ourdir
);
492 if (chown(ourdir
, getuid(), getgid()) == -1)
493 syslog(LOG_WARNING
, "chown(\"%s\"): %m", ourdir
);
495 ourdirfd
= _fd(open(ourdir
, O_RDONLY
));
496 if (ourdirfd
== -1) {
497 syslog(LOG_ERR
, "open(\"%s\"): %m", ourdir
);
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 ((fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) == -1) {
516 syslog(LOG_ERR
, "socket(\"thesocket\"): %m");
519 oldmask
= umask(077);
520 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
524 syslog(LOG_ERR
, "bind(\"thesocket\"): %m");
527 if (chown(sun
.sun_path
, getuid(), getgid()) == -1)
528 syslog(LOG_WARNING
, "chown(\"thesocket\"): %m");
530 if (listen(fd
, SOMAXCONN
) == -1) {
531 syslog(LOG_ERR
, "listen(\"thesocket\"): %m");
535 if (kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqlisten_callback
) == -1) {
536 syslog(LOG_ERR
, "kevent_mod(\"thesocket\", EVFILT_READ): %m");
540 launchd_inited
= true;
542 sockdir
= strdup(ourdir
);
543 sockpath
= strdup(sun
.sun_path
);
545 atexit(launchd_clean_up
);
551 if (!launchd_inited
) {
559 static long long job_get_integer(launch_data_t j
, const char *key
)
561 launch_data_t t
= launch_data_dict_lookup(j
, key
);
563 return launch_data_get_integer(t
);
568 static const char *job_get_string(launch_data_t j
, const char *key
)
570 launch_data_t t
= launch_data_dict_lookup(j
, key
);
572 return launch_data_get_string(t
);
577 static const char *job_get_file2exec(launch_data_t j
)
579 launch_data_t tmpi
, tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAM
);
582 return launch_data_get_string(tmp
);
584 tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
586 tmpi
= launch_data_array_get_index(tmp
, 0);
588 return launch_data_get_string(tmpi
);
594 static bool job_get_bool(launch_data_t j
, const char *key
)
596 launch_data_t t
= launch_data_dict_lookup(j
, key
);
598 return launch_data_get_bool(t
);
603 static void ipc_open(int fd
, struct jobcb
*j
)
605 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
607 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
609 c
->kqconn_callback
= ipc_callback
;
610 c
->conn
= launchd_fdopen(fd
);
612 TAILQ_INSERT_TAIL(&connections
, c
, tqe
);
613 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
);
616 static void simple_zombie_reaper(void *obj
__attribute__((unused
)), struct kevent
*kev
)
618 waitpid(kev
->ident
, NULL
, 0);
621 static void listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
623 struct sockaddr_un sun
;
624 socklen_t sl
= sizeof(sun
);
627 if ((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) == -1) {
634 static void ipc_callback(void *obj
, struct kevent
*kev
)
636 struct conncb
*c
= obj
;
639 if (kev
->filter
== EVFILT_READ
) {
640 if (launchd_msg_recv(c
->conn
, ipc_readmsg
, c
) == -1 && errno
!= EAGAIN
) {
641 if (errno
!= ECONNRESET
)
642 syslog(LOG_DEBUG
, "%s(): recv: %m", __func__
);
645 } else if (kev
->filter
== EVFILT_WRITE
) {
646 r
= launchd_msg_send(c
->conn
, NULL
);
648 if (errno
!= EAGAIN
) {
649 syslog(LOG_DEBUG
, "%s(): send: %m", __func__
);
653 kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
);
656 syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
661 static void set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
663 setenv(key
, launch_data_get_string(obj
), 1);
666 static void launch_data_close_fds(launch_data_t o
)
670 switch (launch_data_get_type(o
)) {
671 case LAUNCH_DATA_DICTIONARY
:
672 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_close_fds
, NULL
);
674 case LAUNCH_DATA_ARRAY
:
675 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
676 launch_data_close_fds(launch_data_array_get_index(o
, i
));
679 if (launch_data_get_fd(o
) != -1)
680 close(launch_data_get_fd(o
));
687 static void launch_data_revoke_fds(launch_data_t o
)
691 switch (launch_data_get_type(o
)) {
692 case LAUNCH_DATA_DICTIONARY
:
693 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_revoke_fds
, NULL
);
695 case LAUNCH_DATA_ARRAY
:
696 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
697 launch_data_revoke_fds(launch_data_array_get_index(o
, i
));
700 launch_data_set_fd(o
, -1);
707 static void job_ignore_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
709 struct jobcb
*j
= cookie
;
713 switch (launch_data_get_type(o
)) {
714 case LAUNCH_DATA_DICTIONARY
:
715 launch_data_dict_iterate(o
, job_ignore_fds
, cookie
);
717 case LAUNCH_DATA_ARRAY
:
718 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
719 job_ignore_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
722 fd
= launch_data_get_fd(o
);
724 job_log(j
, LOG_DEBUG
, "Ignoring FD: %d", fd
);
725 kevent_mod(fd
, EVFILT_READ
, EV_DELETE
, 0, 0, NULL
);
733 static void job_ignore(struct jobcb
*j
)
735 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
739 job_ignore_fds(j_sockets
, NULL
, j
);
741 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
742 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
744 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
745 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
749 static void job_watch_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
751 struct jobcb
*j
= cookie
;
755 switch (launch_data_get_type(o
)) {
756 case LAUNCH_DATA_DICTIONARY
:
757 launch_data_dict_iterate(o
, job_watch_fds
, cookie
);
759 case LAUNCH_DATA_ARRAY
:
760 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
761 job_watch_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
764 fd
= launch_data_get_fd(o
);
766 job_log(j
, LOG_DEBUG
, "Watching FD: %d", fd
);
767 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, cookie
);
775 static void job_watch(struct jobcb
*j
)
777 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
778 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
779 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
783 job_watch_fds(j_sockets
, NULL
, &j
->kqjob_callback
);
785 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
786 if (-1 == j
->vnodes
[i
]) {
787 launch_data_t ld_idx
= launch_data_array_get_index(ld_vnodes
, i
);
788 const char *thepath
= launch_data_get_string(ld_idx
);
790 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
791 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
793 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
794 NOTE_WRITE
|NOTE_EXTEND
|NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
|NOTE_ATTRIB
|NOTE_LINK
,
795 0, &j
->kqjob_callback
);
798 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
799 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
800 NOTE_WRITE
|NOTE_EXTEND
|NOTE_ATTRIB
|NOTE_LINK
, 0, &j
->kqjob_callback
);
803 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
804 launch_data_t ld_idx
= launch_data_array_get_index(ld_qdirs
, i
);
805 const char *thepath
= launch_data_get_string(ld_idx
);
808 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
809 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
810 } else if (dcc_r
> 0) {
817 static void job_stop(struct jobcb
*j
)
823 static void job_remove(struct jobcb
*j
)
828 job_log(j
, LOG_DEBUG
, "Removed");
830 TAILQ_REMOVE(&jobs
, j
, tqe
);
832 if (kevent_mod(j
->p
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqsimple_zombie_reaper
) == -1) {
838 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
839 launch_data_dict_iterate(tmp
, unsetup_job_env
, NULL
);
840 launch_data_close_fds(j
->ldj
);
841 launch_data_free(j
->ldj
);
844 for (i
= 0; i
< j
->vnodes_cnt
; i
++)
845 if (-1 != j
->vnodes
[i
])
849 for (i
= 0; i
< j
->qdirs_cnt
; i
++)
850 if (-1 != j
->qdirs
[i
])
854 if (j
->start_interval
)
855 kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
856 if (j
->start_cal_interval
) {
857 kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
858 free(j
->start_cal_interval
);
860 kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
864 struct readmsg_context
{
869 static void ipc_readmsg(launch_data_t msg
, void *context
)
871 struct readmsg_context rmc
= { context
, NULL
};
873 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(msg
)) {
874 launch_data_dict_iterate(msg
, ipc_readmsg2
, &rmc
);
875 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(msg
)) {
876 ipc_readmsg2(NULL
, launch_data_get_string(msg
), &rmc
);
878 rmc
.resp
= launch_data_new_errno(EINVAL
);
881 if (NULL
== rmc
.resp
)
882 rmc
.resp
= launch_data_new_errno(ENOSYS
);
884 launch_data_close_fds(msg
);
886 if (launchd_msg_send(rmc
.c
->conn
, rmc
.resp
) == -1) {
887 if (errno
== EAGAIN
) {
888 kevent_mod(launchd_getfd(rmc
.c
->conn
), EVFILT_WRITE
, EV_ADD
, 0, 0, &rmc
.c
->kqconn_callback
);
890 syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %m");
894 launch_data_free(rmc
.resp
);
898 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
900 struct readmsg_context
*rmc
= context
;
901 launch_data_t resp
= NULL
;
907 if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
908 TAILQ_FOREACH(j
, &jobs
, tqe
) {
909 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
911 resp
= launch_data_new_errno(0);
915 resp
= launch_data_new_errno(ESRCH
);
916 } else if (!strcmp(cmd
, LAUNCH_KEY_STOPJOB
)) {
917 TAILQ_FOREACH(j
, &jobs
, tqe
) {
918 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
920 resp
= launch_data_new_errno(0);
924 resp
= launch_data_new_errno(ESRCH
);
925 } else if (!strcmp(cmd
, LAUNCH_KEY_REMOVEJOB
)) {
926 TAILQ_FOREACH(j
, &jobs
, tqe
) {
927 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
929 resp
= launch_data_new_errno(0);
933 resp
= launch_data_new_errno(ESRCH
);
934 } else if (!strcmp(cmd
, LAUNCH_KEY_SUBMITJOB
)) {
935 if (launch_data_get_type(data
) == LAUNCH_DATA_ARRAY
) {
939 resp
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
940 for (i
= 0; i
< launch_data_array_get_count(data
); i
++) {
941 tmp
= load_job(launch_data_array_get_index(data
, i
));
942 launch_data_array_set_index(resp
, tmp
, i
);
945 resp
= load_job(data
);
947 } else if (!strcmp(cmd
, LAUNCH_KEY_UNSETUSERENVIRONMENT
)) {
948 unsetenv(launch_data_get_string(data
));
949 resp
= launch_data_new_errno(0);
950 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUSERENVIRONMENT
)) {
951 char **tmpenviron
= environ
;
952 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
953 for (; *tmpenviron
; tmpenviron
++) {
955 launch_data_t s
= launch_data_alloc(LAUNCH_DATA_STRING
);
956 launch_data_set_string(s
, strchr(*tmpenviron
, '=') + 1);
957 strncpy(envkey
, *tmpenviron
, sizeof(envkey
));
958 *(strchr(envkey
, '=')) = '\0';
959 launch_data_dict_insert(resp
, s
, envkey
);
961 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUSERENVIRONMENT
)) {
962 launch_data_dict_iterate(data
, set_user_env
, NULL
);
963 resp
= launch_data_new_errno(0);
964 } else if (!strcmp(cmd
, LAUNCH_KEY_CHECKIN
)) {
966 resp
= launch_data_copy(rmc
->c
->j
->ldj
);
967 if (NULL
== launch_data_dict_lookup(resp
, LAUNCH_JOBKEY_TIMEOUT
)) {
968 launch_data_t to
= launch_data_new_integer(LAUNCHD_MIN_JOB_RUN_TIME
);
969 launch_data_dict_insert(resp
, to
, LAUNCH_JOBKEY_TIMEOUT
);
971 rmc
->c
->j
->checkedin
= true;
973 resp
= launch_data_new_errno(EACCES
);
975 } else if (!strcmp(cmd
, LAUNCH_KEY_RELOADTTYS
)) {
977 resp
= launch_data_new_errno(0);
978 } else if (!strcmp(cmd
, LAUNCH_KEY_SHUTDOWN
)) {
980 resp
= launch_data_new_errno(0);
981 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBS
)) {
982 resp
= get_jobs(NULL
);
983 launch_data_revoke_fds(resp
);
984 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRESOURCELIMITS
)) {
985 resp
= adjust_rlimits(NULL
);
986 } else if (!strcmp(cmd
, LAUNCH_KEY_SETRESOURCELIMITS
)) {
987 resp
= adjust_rlimits(data
);
988 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOB
)) {
989 resp
= get_jobs(launch_data_get_string(data
));
990 launch_data_revoke_fds(resp
);
991 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBWITHHANDLES
)) {
992 resp
= get_jobs(launch_data_get_string(data
));
993 } else if (!strcmp(cmd
, LAUNCH_KEY_SETLOGMASK
)) {
994 resp
= launch_data_new_integer(setlogmask(launch_data_get_integer(data
)));
995 } else if (!strcmp(cmd
, LAUNCH_KEY_GETLOGMASK
)) {
996 int oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
997 resp
= launch_data_new_integer(oldmask
);
999 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUMASK
)) {
1000 resp
= launch_data_new_integer(umask(launch_data_get_integer(data
)));
1001 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUMASK
)) {
1002 mode_t oldmask
= umask(0);
1003 resp
= launch_data_new_integer(oldmask
);
1005 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGESELF
)) {
1006 struct rusage rusage
;
1007 getrusage(RUSAGE_SELF
, &rusage
);
1008 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1009 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGECHILDREN
)) {
1010 struct rusage rusage
;
1011 getrusage(RUSAGE_CHILDREN
, &rusage
);
1012 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1013 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDOUT
)) {
1014 resp
= setstdio(STDOUT_FILENO
, data
);
1015 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDERR
)) {
1016 resp
= setstdio(STDERR_FILENO
, data
);
1017 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHCONTROL
)) {
1018 batch_job_enable(launch_data_get_bool(data
), rmc
->c
);
1019 resp
= launch_data_new_errno(0);
1020 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHQUERY
)) {
1021 resp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1022 launch_data_set_bool(resp
, batch_disabler_count
== 0);
1028 static launch_data_t
setstdio(int d
, launch_data_t o
)
1030 launch_data_t resp
= launch_data_new_errno(0);
1032 if (launch_data_get_type(o
) == LAUNCH_DATA_STRING
) {
1033 char **where
= &pending_stderr
;
1034 if (d
== STDOUT_FILENO
)
1035 where
= &pending_stdout
;
1038 *where
= strdup(launch_data_get_string(o
));
1039 } else if (launch_data_get_type(o
) == LAUNCH_DATA_FD
) {
1040 dup2(launch_data_get_fd(o
), d
);
1042 launch_data_set_errno(resp
, EINVAL
);
1048 static void batch_job_enable(bool e
, struct conncb
*c
)
1050 if (e
&& c
->disabled_batch
) {
1051 batch_disabler_count
--;
1052 c
->disabled_batch
= 0;
1053 if (batch_disabler_count
== 0)
1054 kevent_mod(asynckq
, EVFILT_READ
, EV_ENABLE
, 0, 0, &kqasync_callback
);
1055 } else if (!e
&& !c
->disabled_batch
) {
1056 if (batch_disabler_count
== 0)
1057 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1058 batch_disabler_count
++;
1059 c
->disabled_batch
= 1;
1063 static launch_data_t
load_job(launch_data_t pload
)
1065 launch_data_t tmp
, resp
;
1070 if ((label
= job_get_string(pload
, LAUNCH_JOBKEY_LABEL
))) {
1071 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1072 if (!strcmp(j
->label
, label
)) {
1073 resp
= launch_data_new_errno(EEXIST
);
1078 resp
= launch_data_new_errno(EINVAL
);
1081 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
) == NULL
) {
1082 resp
= launch_data_new_errno(EINVAL
);
1086 j
= calloc(1, sizeof(struct jobcb
) + strlen(label
) + 1);
1087 strcpy(j
->label
, label
);
1088 j
->ldj
= launch_data_copy(pload
);
1089 launch_data_revoke_fds(pload
);
1090 j
->kqjob_callback
= job_callback
;
1093 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
) == NULL
) {
1094 tmp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1095 launch_data_set_bool(tmp
, true);
1096 launch_data_dict_insert(j
->ldj
, tmp
, LAUNCH_JOBKEY_ONDEMAND
);
1099 TAILQ_INSERT_TAIL(&jobs
, j
, tqe
);
1101 j
->debug
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_DEBUG
);
1103 startnow
= !job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1105 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_RUNATLOAD
))
1108 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
))) {
1111 j
->qdirs_cnt
= launch_data_array_get_count(tmp
);
1112 j
->qdirs
= malloc(sizeof(int) * j
->qdirs_cnt
);
1114 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1115 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1117 if (-1 == (j
->qdirs
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1118 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1123 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTINTERVAL
))) {
1124 j
->start_interval
= launch_data_get_integer(tmp
);
1126 if (j
->start_interval
== 0)
1127 job_log(j
, LOG_WARNING
, "StartInterval is zero, ignoring");
1128 else if (-1 == kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_SECONDS
, j
->start_interval
, &j
->kqjob_callback
))
1129 job_log_error(j
, LOG_ERR
, "adding kevent timer");
1132 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTCALENDARINTERVAL
))) {
1133 launch_data_t tmp_k
;
1135 j
->start_cal_interval
= calloc(1, sizeof(struct tm
));
1137 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(tmp
)) {
1138 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MINUTE
)))
1139 j
->start_cal_interval
->tm_min
= launch_data_get_integer(tmp_k
);
1140 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_HOUR
)))
1141 j
->start_cal_interval
->tm_hour
= launch_data_get_integer(tmp_k
);
1142 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_DAY
)))
1143 j
->start_cal_interval
->tm_mday
= launch_data_get_integer(tmp_k
);
1144 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_WEEKDAY
))) {
1145 j
->start_cal_interval
->tm_wday
= launch_data_get_integer(tmp_k
);
1146 if (j
->start_cal_interval
->tm_wday
== 0)
1147 j
->start_cal_interval
->tm_wday
= 7;
1149 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MONTH
)))
1150 j
->start_cal_interval
->tm_mon
= launch_data_get_integer(tmp_k
);
1156 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
))) {
1159 j
->vnodes_cnt
= launch_data_array_get_count(tmp
);
1160 j
->vnodes
= malloc(sizeof(int) * j
->vnodes_cnt
);
1162 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1163 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1165 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1166 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1171 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
1172 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1174 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1180 resp
= launch_data_new_errno(0);
1185 static launch_data_t
get_jobs(const char *which
)
1188 launch_data_t tmp
, resp
= NULL
;
1191 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1192 if (!strcmp(which
, j
->label
))
1193 resp
= launch_data_copy(j
->ldj
);
1196 resp
= launch_data_new_errno(ESRCH
);
1198 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1200 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1201 tmp
= launch_data_copy(j
->ldj
);
1202 launch_data_dict_insert(resp
, tmp
, j
->label
);
1209 static void usage(FILE *where
)
1211 fprintf(where
, "%s: [-d] [-- command [args ...]]\n", getprogname());
1212 fprintf(where
, "\t-d\tdaemonize\n");
1213 fprintf(where
, "\t-h\tthis usage statement\n");
1215 if (where
== stdout
)
1219 #ifdef EVFILT_MACH_IMPLEMENTED
1220 static void **machcbtable
= NULL
;
1221 static size_t machcbtable_cnt
= 0;
1222 static int machcbreadfd
= -1;
1223 static int machcbwritefd
= -1;
1224 static mach_port_t mach_demand_port_set
= MACH_PORT_NULL
;
1225 static pthread_t mach_demand_thread
;
1227 static void mach_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1232 read(machcbreadfd
, &mp
, sizeof(mp
));
1234 EV_SET(&mkev
, mp
, EVFILT_MACHPORT
, 0, 0, 0, machcbtable
[MACH_PORT_INDEX(mp
)]);
1236 (*((kq_callback
*)mkev
.udata
))(mkev
.udata
, &mkev
);
1240 int kevent_mod(uintptr_t ident
, short filter
, u_short flags
, u_int fflags
, intptr_t data
, void *udata
)
1244 #ifdef EVFILT_MACH_IMPLEMENTED
1246 pthread_attr_t attr
;
1247 int pthr_r
, pfds
[2];
1250 if (EVFILT_TIMER
== filter
|| EVFILT_VNODE
== filter
)
1253 if (flags
& EV_ADD
&& NULL
== udata
) {
1254 syslog(LOG_ERR
, "%s(): kev.udata == NULL!!!", __func__
);
1255 syslog(LOG_ERR
, "kev: ident %d filter %d flags 0x%x fflags 0x%x",
1256 ident
, filter
, flags
, fflags
);
1261 #ifdef EVFILT_MACH_IMPLEMENTED
1262 if (filter
!= EVFILT_MACHPORT
) {
1264 #ifdef PID1_REAP_ADOPTED_CHILDREN
1265 if (filter
== EVFILT_PROC
&& getpid() == 1)
1268 EV_SET(&kev
, ident
, filter
, flags
, fflags
, data
, udata
);
1269 return kevent(q
, &kev
, 1, NULL
, 0, NULL
);
1270 #ifdef EVFILT_MACH_IMPLEMENTED
1273 if (machcbtable
== NULL
) {
1274 pthread_attr_init(&attr
);
1275 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1277 pthr_r
= pthread_create(&mach_demand_thread
, &attr
, mach_demand_loop
, NULL
);
1279 syslog(LOG_ERR
, "pthread_create(mach_demand_loop): %s", strerror(pthr_r
));
1283 pthread_attr_destroy(&attr
);
1285 machcbtable
= malloc(0);
1287 machcbwritefd
= _fd(pfds
[1]);
1288 machcbreadfd
= _fd(pfds
[0]);
1289 kevent_mod(machcbreadfd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqmach_callback
);
1290 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET
, &mach_demand_port_set
);
1291 if (kr
!= KERN_SUCCESS
) {
1292 syslog(LOG_ERR
, "mach_port_allocate(demand_port_set): %s", mach_error_string(kr
));
1297 if (flags
& EV_ADD
) {
1298 kr
= mach_port_move_member(mach_task_self(), ident
, mach_demand_port_set
);
1299 if (kr
!= KERN_SUCCESS
) {
1300 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1304 if (MACH_PORT_INDEX(ident
) > machcbtable_cnt
)
1305 machcbtable
= realloc(machcbtable
, MACH_PORT_INDEX(ident
) * sizeof(void *));
1307 machcbtable
[MACH_PORT_INDEX(ident
)] = udata
;
1308 } else if (flags
& EV_DELETE
) {
1309 kr
= mach_port_move_member(mach_task_self(), ident
, MACH_PORT_NULL
);
1310 if (kr
!= KERN_SUCCESS
) {
1311 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1315 syslog(LOG_DEBUG
, "kevent_mod(EVFILT_MACHPORT) with flags: %d", flags
);
1324 static int _fd(int fd
)
1327 fcntl(fd
, F_SETFD
, 1);
1331 static void ipc_close(struct conncb
*c
)
1333 batch_job_enable(true, c
);
1335 TAILQ_REMOVE(&connections
, c
, tqe
);
1336 launchd_close(c
->conn
);
1340 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1342 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1343 setenv(key
, launch_data_get_string(obj
), 1);
1346 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1348 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1352 static void job_reap(struct jobcb
*j
)
1354 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1355 time_t td
= time(NULL
) - j
->start_time
;
1356 bool bad_exit
= false;
1359 job_log(j
, LOG_DEBUG
, "Reaping");
1366 #ifdef PID1_REAP_ADOPTED_CHILDREN
1368 status
= pid1_child_exit_status
;
1371 if (-1 == waitpid(j
->p
, &status
, 0)) {
1372 job_log_error(j
, LOG_ERR
, "waitpid(%d, ...)", j
->p
);
1376 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
1377 job_log(j
, LOG_WARNING
, "exited with exit code: %d", WEXITSTATUS(status
));
1381 if (WIFSIGNALED(status
)) {
1382 int s
= WTERMSIG(status
);
1383 if (SIGKILL
== s
|| SIGTERM
== s
) {
1384 job_log(j
, LOG_NOTICE
, "exited: %s", strsignal(s
));
1386 job_log(j
, LOG_WARNING
, "exited abnormally: %s", strsignal(s
));
1392 if (td
< LAUNCHD_MIN_JOB_RUN_TIME
) {
1393 job_log(j
, LOG_WARNING
, "respawning too quickly! throttling");
1396 } else if (td
>= LAUNCHD_REWARD_JOB_RUN_TIME
) {
1397 job_log(j
, LOG_INFO
, "lived long enough, forgiving past exit failures");
1398 j
->failed_exits
= 0;
1405 if (j
->failed_exits
> 0) {
1406 int failures_left
= LAUNCHD_FAILED_EXITS_THRESHOLD
- j
->failed_exits
;
1408 job_log(j
, LOG_WARNING
, "%d more failure%s without living at least %d seconds will cause job removal",
1409 failures_left
, failures_left
> 1 ? "s" : "", LAUNCHD_REWARD_JOB_RUN_TIME
);
1416 static bool job_restart_fitness_test(struct jobcb
*j
)
1418 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1421 job_log(j
, LOG_DEBUG
, "first born died, begin shutdown");
1424 } else if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
) && !j
->checkedin
) {
1425 job_log(j
, LOG_WARNING
, "failed to checkin");
1428 } else if (od
|| shutdown_in_progress
) {
1429 if (!od
&& shutdown_in_progress
)
1430 job_log(j
, LOG_NOTICE
, "exited while shutdown is in progress, will not restart unless demand requires it");
1433 } else if (j
->failed_exits
>= LAUNCHD_FAILED_EXITS_THRESHOLD
) {
1434 job_log(j
, LOG_WARNING
, "too many failures in a row for a job that should be alive all the time");
1442 static void job_callback(void *obj
, struct kevent
*kev
)
1444 struct jobcb
*j
= obj
;
1446 bool startnow
= true;
1450 oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1451 job_log(j
, LOG_DEBUG
, "log level debug temporarily enabled while processing job");
1454 if (kev
->filter
== EVFILT_PROC
) {
1457 startnow
= job_restart_fitness_test(j
);
1459 if (startnow
&& j
->throttle
) {
1460 j
->throttle
= false;
1461 job_log(j
, LOG_WARNING
, "will restart in %d seconds", LAUNCHD_MIN_JOB_RUN_TIME
);
1462 if (-1 == kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_ADD
|EV_ONESHOT
,
1463 NOTE_SECONDS
, LAUNCHD_MIN_JOB_RUN_TIME
, &j
->kqjob_callback
)) {
1464 job_log_error(j
, LOG_WARNING
, "failed to setup timer callback!, starting now!");
1469 } else if (kev
->filter
== EVFILT_TIMER
&& kev
->fflags
& NOTE_ABSOLUTE
) {
1471 } else if (kev
->filter
== EVFILT_VNODE
) {
1473 const char *thepath
= NULL
;
1475 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1476 if (j
->vnodes
[i
] == (int)kev
->ident
) {
1477 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
1479 thepath
= launch_data_get_string(launch_data_array_get_index(ld_vnodes
, i
));
1481 job_log(j
, LOG_DEBUG
, "watch path modified: %s", thepath
);
1483 if ((NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
) & kev
->fflags
) {
1484 job_log(j
, LOG_DEBUG
, "watch path invalidated: %s", thepath
);
1485 close(j
->vnodes
[i
]);
1486 j
->vnodes
[i
] = -1; /* this will get fixed in job_watch() */
1491 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1492 if (j
->qdirs
[i
] == (int)kev
->ident
) {
1493 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
1496 thepath
= launch_data_get_string(launch_data_array_get_index(ld_qdirs
, i
));
1498 job_log(j
, LOG_DEBUG
, "queue directory modified: %s", thepath
);
1500 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
1501 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
1502 } else if (0 == dcc_r
) {
1503 job_log(j
, LOG_DEBUG
, "spurious wake up, directory empty: %s", thepath
);
1508 /* if we get here, then the vnodes either wasn't a qdir, or if it was, it has entries in it */
1509 } else if (kev
->filter
== EVFILT_READ
&& (int)kev
->ident
== j
->execfd
) {
1510 if (kev
->data
> 0) {
1513 read(j
->execfd
, &e
, sizeof(e
));
1515 job_log_error(j
, LOG_ERR
, "execve()");
1530 /* the job might have been removed, must not call job_log() */
1531 syslog(LOG_DEBUG
, "restoring original log mask");
1532 setlogmask(oldmask
);
1536 static void job_start(struct jobcb
*j
)
1544 job_log(j
, LOG_DEBUG
, "Starting");
1547 job_log(j
, LOG_DEBUG
, "already running");
1551 j
->checkedin
= false;
1553 sipc
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
);
1555 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
))
1559 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
1561 socketpair(AF_UNIX
, SOCK_STREAM
, 0, execspair
);
1563 time(&j
->start_time
);
1565 switch (c
= fork_with_bootstrap_port(launchd_bootstrap_port
)) {
1567 job_log_error(j
, LOG_ERR
, "fork() failed, will try again in one second");
1568 close(execspair
[0]);
1569 close(execspair
[1]);
1574 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1578 close(execspair
[0]);
1579 /* wait for our parent to say they've attached a kevent to us */
1580 read(_fd(execspair
[1]), &c
, sizeof(c
));
1582 setpgid(getpid(), getpid());
1583 if (isatty(STDIN_FILENO
)) {
1584 if (tcsetpgrp(STDIN_FILENO
, getpid()) == -1)
1585 job_log_error(j
, LOG_WARNING
, "tcsetpgrp()");
1591 sprintf(nbuf
, "%d", spair
[1]);
1592 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
1594 job_start_child(j
, execspair
[1]);
1597 close(execspair
[1]);
1598 j
->execfd
= _fd(execspair
[0]);
1601 ipc_open(_fd(spair
[0]), j
);
1603 if (kevent_mod(j
->execfd
, EVFILT_READ
, EV_ADD
, 0, 0, &j
->kqjob_callback
) == -1)
1604 job_log_error(j
, LOG_ERR
, "kevent_mod(j->execfd): %m");
1605 if (kevent_mod(c
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &j
->kqjob_callback
) == -1) {
1606 job_log_error(j
, LOG_ERR
, "kevent()");
1611 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1614 /* this unblocks the child and avoids a race
1615 * between the above fork() and the kevent_mod() */
1616 write(j
->execfd
, &c
, sizeof(c
));
1621 static void job_start_child(struct jobcb
*j
, int execfd
)
1623 launch_data_t ldpa
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
1624 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1626 const char **argv
, *file2exec
= "/usr/libexec/launchproxy";
1628 job_setup_attributes(j
);
1630 argv_cnt
= launch_data_array_get_count(ldpa
);
1631 argv
= alloca((argv_cnt
+ 2) * sizeof(char *));
1632 for (i
= 0; i
< argv_cnt
; i
++)
1633 argv
[i
+ 1] = launch_data_get_string(launch_data_array_get_index(ldpa
, i
));
1634 argv
[argv_cnt
+ 1] = NULL
;
1637 argv
[0] = file2exec
;
1640 file2exec
= job_get_file2exec(j
->ldj
);
1643 if (-1 == execvp(file2exec
, (char *const*)argv
)) {
1644 int e
= errno
; /* errno is a macro that expands, best not to take the address of it */
1645 write(execfd
, &e
, sizeof(e
));
1646 job_log_error(j
, LOG_ERR
, "execvp(\"%s\", ...)", file2exec
);
1648 _exit(EXIT_FAILURE
);
1651 static void job_setup_attributes(struct jobcb
*j
)
1653 launch_data_t srl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOFTRESOURCELIMITS
);
1654 launch_data_t hrl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_HARDRESOURCELIMITS
);
1655 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1659 struct group
*gre
= NULL
;
1661 static const struct {
1665 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE
, RLIMIT_CORE
},
1666 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU
, RLIMIT_CPU
},
1667 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA
, RLIMIT_DATA
},
1668 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE
, RLIMIT_FSIZE
},
1669 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK
, RLIMIT_MEMLOCK
},
1670 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE
, RLIMIT_NOFILE
},
1671 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC
, RLIMIT_NPROC
},
1672 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS
, RLIMIT_RSS
},
1673 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK
, RLIMIT_STACK
},
1676 setpriority(PRIO_PROCESS
, 0, job_get_integer(j
->ldj
, LAUNCH_JOBKEY_NICE
));
1679 for (i
= 0; i
< (sizeof(limits
) / sizeof(limits
[0])); i
++) {
1682 if (getrlimit(limits
[i
].val
, &rl
) == -1) {
1683 job_log_error(j
, LOG_WARNING
, "getrlimit()");
1688 rl
.rlim_max
= job_get_integer(hrl
, limits
[i
].key
);
1690 rl
.rlim_cur
= job_get_integer(srl
, limits
[i
].key
);
1692 if (setrlimit(limits
[i
].val
, &rl
) == -1)
1693 job_log_error(j
, LOG_WARNING
, "setrlimit()");
1697 if (!inetcompat
&& job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SESSIONCREATE
))
1698 launchd_SessionCreate(job_get_file2exec(j
->ldj
));
1700 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_LOWPRIORITYIO
)) {
1701 int lowprimib
[] = { CTL_KERN
, KERN_PROC_LOW_PRI_IO
};
1704 if (sysctl(lowprimib
, sizeof(lowprimib
) / sizeof(lowprimib
[0]), NULL
, NULL
, &val
, sizeof(val
)) == -1)
1705 job_log_error(j
, LOG_WARNING
, "sysctl(\"%s\")", "kern.proc_low_pri_io");
1707 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_ROOTDIRECTORY
))) {
1711 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_GROUPNAME
))) {
1712 gre
= getgrnam(tmpstr
);
1714 gre_g
= gre
->gr_gid
;
1715 if (-1 == setgid(gre_g
)) {
1716 job_log_error(j
, LOG_ERR
, "setgid(%d)", gre_g
);
1717 _exit(EXIT_FAILURE
);
1720 job_log(j
, LOG_ERR
, "getgrnam(\"%s\") failed", tmpstr
);
1721 _exit(EXIT_FAILURE
);
1724 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_USERNAME
))) {
1725 struct passwd
*pwe
= getpwnam(tmpstr
);
1727 uid_t pwe_u
= pwe
->pw_uid
;
1728 uid_t pwe_g
= pwe
->pw_gid
;
1730 if (pwe
->pw_expire
&& time(NULL
) >= pwe
->pw_expire
) {
1731 job_log(j
, LOG_ERR
, "expired account: %s", tmpstr
);
1732 _exit(EXIT_FAILURE
);
1734 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INITGROUPS
)) {
1735 if (-1 == initgroups(tmpstr
, gre
? gre_g
: pwe_g
)) {
1736 job_log_error(j
, LOG_ERR
, "initgroups()");
1737 _exit(EXIT_FAILURE
);
1741 if (-1 == setgid(pwe_g
)) {
1742 job_log_error(j
, LOG_ERR
, "setgid(%d)", pwe_g
);
1743 _exit(EXIT_FAILURE
);
1746 if (-1 == setuid(pwe_u
)) {
1747 job_log_error(j
, LOG_ERR
, "setuid(%d)", pwe_u
);
1748 _exit(EXIT_FAILURE
);
1751 job_log(j
, LOG_WARNING
, "getpwnam(\"%s\") failed", tmpstr
);
1752 _exit(EXIT_FAILURE
);
1755 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_WORKINGDIRECTORY
)))
1757 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_UMASK
))
1758 umask(job_get_integer(j
->ldj
, LAUNCH_JOBKEY_UMASK
));
1759 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDOUTPATH
))) {
1760 int sofd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1762 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1764 dup2(sofd
, STDOUT_FILENO
);
1768 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDERRORPATH
))) {
1769 int sefd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1771 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1773 dup2(sefd
, STDERR_FILENO
);
1777 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
)))
1778 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1783 #ifdef PID1_REAP_ADOPTED_CHILDREN
1784 __private_extern__
int pid1_child_exit_status
= 0;
1785 static void pid1waitpid(void)
1789 while ((p
= waitpid(-1, &pid1_child_exit_status
, WNOHANG
)) > 0) {
1790 if (!launchd_check_pid(p
))
1796 static void do_shutdown(void)
1800 shutdown_in_progress
= true;
1802 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1804 TAILQ_FOREACH(j
, &jobs
, tqe
)
1807 if (getpid() == 1) {
1809 mach_start_shutdown(SIGTERM
);
1813 static void signal_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
1815 switch (kev
->ident
) {
1818 reload_launchd_config();
1823 #ifdef PID1_REAP_ADOPTED_CHILDREN
1825 /* <rdar://problem/3632556> Please automatically reap processes reparented to PID 1 */
1835 static void fs_callback(void)
1837 static bool mounted_volfs
= false;
1840 mounted_volfs
= true;
1842 if (pending_stdout
) {
1843 int fd
= open(pending_stdout
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1845 dup2(fd
, STDOUT_FILENO
);
1847 free(pending_stdout
);
1848 pending_stdout
= NULL
;
1851 if (pending_stderr
) {
1852 int fd
= open(pending_stderr
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1854 dup2(fd
, STDERR_FILENO
);
1856 free(pending_stderr
);
1857 pending_stderr
= NULL
;
1861 if (!mounted_volfs
) {
1862 int r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1864 if (-1 == r
&& errno
== ENOENT
) {
1865 mkdir(VOLFSDIR
, ACCESSPERMS
& ~(S_IWUSR
|S_IWGRP
|S_IWOTH
));
1866 r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1870 syslog(LOG_WARNING
, "mount(\"%s\", \"%s\", ...): %m", "volfs", VOLFSDIR
);
1872 mounted_volfs
= true;
1876 if (!launchd_inited
)
1877 launchd_server_init(false);
1880 static void readcfg_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1884 #ifdef PID1_REAP_ADOPTED_CHILDREN
1886 status
= pid1_child_exit_status
;
1889 if (-1 == waitpid(readcfg_pid
, &status
, 0)) {
1890 syslog(LOG_WARNING
, "waitpid(readcfg_pid, ...): %m");
1896 if (WIFEXITED(status
)) {
1897 if (WEXITSTATUS(status
))
1898 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited with status: %d", WEXITSTATUS(status
));
1899 } else if (WIFSIGNALED(status
)) {
1900 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally: %s", strsignal(WTERMSIG(status
)));
1902 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally");
1906 #ifdef EVFILT_MACH_IMPLEMENTED
1907 static void *mach_demand_loop(void *arg
__attribute__((unused
)))
1909 mach_msg_empty_rcv_t dummy
;
1911 mach_port_name_array_t members
;
1912 mach_msg_type_number_t membersCnt
;
1913 mach_port_status_t status
;
1914 mach_msg_type_number_t statusCnt
;
1920 * Receive indication of message on demand service
1921 * ports without actually receiving the message (we'll
1922 * let the actual server do that.
1924 kr
= mach_msg(&dummy
.header
, MACH_RCV_MSG
|MACH_RCV_LARGE
,
1925 0, 0, mach_demand_port_set
, 0, MACH_PORT_NULL
);
1926 if (kr
!= MACH_RCV_TOO_LARGE
) {
1927 syslog(LOG_WARNING
, "%s(): mach_msg(): %s", __func__
, mach_error_string(kr
));
1932 * Some port(s) now have messages on them, find out
1933 * which ones (there is no indication of which port
1934 * triggered in the MACH_RCV_TOO_LARGE indication).
1936 kr
= mach_port_get_set_status(mach_task_self(),
1937 mach_demand_port_set
, &members
, &membersCnt
);
1938 if (kr
!= KERN_SUCCESS
) {
1939 syslog(LOG_WARNING
, "%s(): mach_port_get_set_status(): %s", __func__
, mach_error_string(kr
));
1943 for (i
= 0; i
< membersCnt
; i
++) {
1944 statusCnt
= MACH_PORT_RECEIVE_STATUS_COUNT
;
1945 kr
= mach_port_get_attributes(mach_task_self(), members
[i
],
1946 MACH_PORT_RECEIVE_STATUS
, (mach_port_info_t
)&status
, &statusCnt
);
1947 if (kr
!= KERN_SUCCESS
) {
1948 syslog(LOG_WARNING
, "%s(): mach_port_get_attributes(): %s", __func__
, mach_error_string(kr
));
1953 * For each port with messages, take it out of the
1954 * demand service portset, and inform the main thread
1955 * that it might have to start the server responsible
1958 if (status
.mps_msgcount
) {
1959 kr
= mach_port_move_member(mach_task_self(), members
[i
], MACH_PORT_NULL
);
1960 if (kr
!= KERN_SUCCESS
) {
1961 syslog(LOG_WARNING
, "%s(): mach_port_move_member(): %s", __func__
, mach_error_string(kr
));
1964 write(machcbwritefd
, &(members
[i
]), sizeof(members
[i
]));
1968 kr
= vm_deallocate(mach_task_self(), (vm_address_t
) members
,
1969 (vm_size_t
) membersCnt
* sizeof(mach_port_name_t
));
1970 if (kr
!= KERN_SUCCESS
) {
1971 syslog(LOG_WARNING
, "%s(): vm_deallocate(): %s", __func__
, mach_error_string(kr
));
1980 static void reload_launchd_config(void)
1983 static char *ldconf
= PID1LAUNCHD_CONF
;
1984 const char *h
= getenv("HOME");
1986 if (h
&& ldconf
== PID1LAUNCHD_CONF
)
1987 asprintf(&ldconf
, "%s/%s", h
, LAUNCHD_CONF
);
1992 if (lstat(ldconf
, &sb
) == 0) {
1994 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
1995 readcfg_pid
= fork_with_bootstrap_port(launchd_bootstrap_port
);
1996 if (readcfg_pid
== 0) {
1999 sprintf(nbuf
, "%d", spair
[1]);
2000 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
2001 int fd
= open(ldconf
, O_RDONLY
);
2003 syslog(LOG_ERR
, "open(\"%s\"): %m", ldconf
);
2004 _exit(EXIT_FAILURE
);
2006 dup2(fd
, STDIN_FILENO
);
2008 execl(LAUNCHCTL_PATH
, LAUNCHCTL_PATH
, NULL
);
2009 syslog(LOG_ERR
, "execl(\"%s\", ...): %m", LAUNCHCTL_PATH
);
2010 _exit(EXIT_FAILURE
);
2011 } else if (readcfg_pid
== -1) {
2014 syslog(LOG_ERR
, "fork(): %m");
2018 ipc_open(_fd(spair
[0]), NULL
);
2019 if (kevent_mod(readcfg_pid
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqreadcfg_callback
) == -1)
2020 syslog(LOG_ERR
, "kevent_mod(EVFILT_PROC, &kqreadcfg_callback): %m");
2025 static void conceive_firstborn(char *argv
[])
2027 launch_data_t r
, d
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2028 launch_data_t args
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
2029 launch_data_t l
= launch_data_new_string("com.apple.launchd.firstborn");
2032 for (i
= 0; *argv
; argv
++, i
++)
2033 launch_data_array_set_index(args
, launch_data_new_string(*argv
), i
);
2035 launch_data_dict_insert(d
, args
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
2036 launch_data_dict_insert(d
, l
, LAUNCH_JOBKEY_LABEL
);
2040 launch_data_free(r
);
2041 launch_data_free(d
);
2043 TAILQ_FIRST(&jobs
)->firstborn
= true;
2046 static void loopback_setup(void)
2048 struct ifaliasreq ifra
;
2049 struct in6_aliasreq ifra6
;
2053 memset(&ifr
, 0, sizeof(ifr
));
2054 strcpy(ifr
.ifr_name
, "lo0");
2056 if (-1 == (s
= socket(AF_INET
, SOCK_DGRAM
, 0)))
2057 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET");
2058 if (-1 == (s6
= socket(AF_INET6
, SOCK_DGRAM
, 0)))
2059 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET6");
2061 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) == -1) {
2062 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2064 ifr
.ifr_flags
|= IFF_UP
;
2066 if (ioctl(s
, SIOCSIFFLAGS
, &ifr
) == -1)
2067 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2070 memset(&ifr
, 0, sizeof(ifr
));
2071 strcpy(ifr
.ifr_name
, "lo0");
2073 if (ioctl(s6
, SIOCGIFFLAGS
, &ifr
) == -1) {
2074 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2076 ifr
.ifr_flags
|= IFF_UP
;
2078 if (ioctl(s6
, SIOCSIFFLAGS
, &ifr
) == -1)
2079 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2082 memset(&ifra
, 0, sizeof(ifra
));
2083 strcpy(ifra
.ifra_name
, "lo0");
2085 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_family
= AF_INET
;
2086 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
2087 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_len
= sizeof(struct sockaddr_in
);
2088 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_family
= AF_INET
;
2089 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
.s_addr
= htonl(IN_CLASSA_NET
);
2090 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_len
= sizeof(struct sockaddr_in
);
2092 if (ioctl(s
, SIOCAIFADDR
, &ifra
) == -1)
2093 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv4): %m");
2095 memset(&ifra6
, 0, sizeof(ifra6
));
2096 strcpy(ifra6
.ifra_name
, "lo0");
2098 ifra6
.ifra_addr
.sin6_family
= AF_INET6
;
2099 ifra6
.ifra_addr
.sin6_addr
= in6addr_loopback
;
2100 ifra6
.ifra_addr
.sin6_len
= sizeof(struct sockaddr_in6
);
2101 ifra6
.ifra_prefixmask
.sin6_family
= AF_INET6
;
2102 memset(&ifra6
.ifra_prefixmask
.sin6_addr
, 0xff, sizeof(struct in6_addr
));
2103 ifra6
.ifra_prefixmask
.sin6_len
= sizeof(struct sockaddr_in6
);
2104 ifra6
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
2105 ifra6
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
2107 if (ioctl(s6
, SIOCAIFADDR_IN6
, &ifra6
) == -1)
2108 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv6): %m");
2114 static void workaround3048875(int argc
, char *argv
[])
2117 char **ap
, *newargv
[100], *p
= argv
[1];
2119 if (argc
== 1 || argc
> 2)
2122 newargv
[0] = argv
[0];
2123 for (ap
= newargv
+ 1, i
= 1; ap
< &newargv
[100]; ap
++, i
++) {
2124 if ((*ap
= strsep(&p
, " \t")) == NULL
)
2135 execv(newargv
[0], newargv
);
2138 static launch_data_t
adjust_rlimits(launch_data_t in
)
2140 static struct rlimit
*l
= NULL
;
2141 static size_t lsz
= sizeof(struct rlimit
) * RLIM_NLIMITS
;
2142 struct rlimit
*ltmp
;
2147 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
2148 if (getrlimit(i
, l
+ i
) == -1)
2149 syslog(LOG_WARNING
, "getrlimit(): %m");
2154 ltmp
= launch_data_get_opaque(in
);
2155 ltmpsz
= launch_data_get_opaque_size(in
);
2158 syslog(LOG_WARNING
, "Too much rlimit data sent!");
2162 for (i
= 0; i
< (ltmpsz
/ sizeof(struct rlimit
)); i
++) {
2163 if (ltmp
[i
].rlim_cur
== l
[i
].rlim_cur
&& ltmp
[i
].rlim_max
== l
[i
].rlim_max
)
2166 if (readcfg_pid
&& getpid() == 1) {
2167 int gmib
[] = { CTL_KERN
, KERN_MAXPROC
};
2168 int pmib
[] = { CTL_KERN
, KERN_MAXPROCPERUID
};
2169 const char *gstr
= "kern.maxproc";
2170 const char *pstr
= "kern.maxprocperuid";
2171 int gval
= ltmp
[i
].rlim_max
;
2172 int pval
= ltmp
[i
].rlim_cur
;
2175 gmib
[1] = KERN_MAXFILES
;
2176 pmib
[1] = KERN_MAXFILESPERPROC
;
2177 gstr
= "kern.maxfiles";
2178 pstr
= "kern.maxfilesperproc";
2181 /* kernel will not clamp to this value, we must */
2182 if (gval
> (2048 + 20))
2188 if (sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)) == -1)
2189 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", gstr
);
2190 if (sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)) == -1)
2191 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", pstr
);
2193 if (setrlimit(i
, ltmp
+ i
) == -1)
2194 syslog(LOG_WARNING
, "setrlimit(): %m");
2195 /* the kernel may have clamped the values we gave it */
2196 if (getrlimit(i
, l
+ i
) == -1)
2197 syslog(LOG_WARNING
, "getrlimit(): %m");
2201 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);
2204 __private_extern__
void launchd_SessionCreate(const char *who
)
2206 void *seclib
= dlopen(SECURITY_LIB
, RTLD_LAZY
);
2207 OSStatus (*sescr
)(SessionCreationFlags flags
, SessionAttributeBits attributes
);
2210 sescr
= dlsym(seclib
, "SessionCreate");
2213 OSStatus scr
= sescr(0, 0);
2215 syslog(LOG_WARNING
, "%s: SessionCreate() failed: %d", who
, scr
);
2217 syslog(LOG_WARNING
, "%s: couldn't find SessionCreate() in %s", who
, SECURITY_LIB
);
2222 syslog(LOG_WARNING
, "%s: dlopen(\"%s\",...): %s", who
, SECURITY_LIB
, dlerror());
2226 static int dir_has_files(const char *path
)
2228 DIR *dd
= opendir(path
);
2235 while ((de
= readdir(dd
))) {
2236 if (strcmp(de
->d_name
, ".") && strcmp(de
->d_name
, "..")) {
2246 static void job_set_alarm(struct jobcb
*j
)
2248 struct tm otherlatertm
, latertm
, *nowtm
;
2249 time_t later
, otherlater
= 0, now
= time(NULL
);
2251 nowtm
= localtime(&now
);
2256 latertm
.tm_isdst
= -1;
2259 if (j
->start_cal_interval
->tm_min
)
2260 latertm
.tm_min
= j
->start_cal_interval
->tm_min
;
2261 if (j
->start_cal_interval
->tm_hour
)
2262 latertm
.tm_hour
= j
->start_cal_interval
->tm_hour
;
2264 otherlatertm
= latertm
;
2266 if (j
->start_cal_interval
->tm_mday
)
2267 latertm
.tm_mday
= j
->start_cal_interval
->tm_mday
;
2268 if (j
->start_cal_interval
->tm_mon
)
2269 latertm
.tm_mon
= j
->start_cal_interval
->tm_mon
;
2271 /* cron semantics are fun */
2272 if (j
->start_cal_interval
->tm_wday
) {
2273 int delta
, realwday
= j
->start_cal_interval
->tm_wday
;
2278 delta
= realwday
- nowtm
->tm_wday
;
2280 /* Now Later Delta Desired
2287 otherlatertm
.tm_mday
+= delta
;
2289 otherlatertm
.tm_mday
+= 7 + delta
;
2290 else if (otherlatertm
.tm_hour
< nowtm
->tm_hour
)
2291 otherlatertm
.tm_mday
+= 7;
2292 else if (otherlatertm
.tm_min
< nowtm
->tm_min
)
2293 otherlatertm
.tm_hour
++;
2295 otherlatertm
.tm_min
++;
2297 otherlater
= mktime(&otherlatertm
);
2300 if (latertm
.tm_mon
< nowtm
->tm_mon
) {
2302 } else if (latertm
.tm_mday
< nowtm
->tm_mday
) {
2304 } else if (latertm
.tm_hour
< nowtm
->tm_hour
) {
2306 } else if (latertm
.tm_min
< nowtm
->tm_min
) {
2312 later
= mktime(&latertm
);
2315 if (j
->start_cal_interval
->tm_mday
)
2316 later
= later
< otherlater
? later
: otherlater
;
2321 if (-1 == kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_ABSOLUTE
|NOTE_SECONDS
, later
, &j
->kqjob_callback
))
2322 job_log_error(j
, LOG_ERR
, "adding kevent alarm");
2325 static void job_log_error(struct jobcb
*j
, int pri
, const char *msg
, ...)
2327 size_t newmsg_sz
= strlen(msg
) + strlen(j
->label
) + 200;
2328 char *newmsg
= alloca(newmsg_sz
);
2331 sprintf(newmsg
, "%s: %s: %s", j
->label
, msg
, strerror(errno
));
2335 vsyslog(pri
, newmsg
, ap
);
2340 static void job_log(struct jobcb
*j
, int pri
, const char *msg
, ...)
2342 size_t newmsg_sz
= strlen(msg
) + sizeof(": ") + strlen(j
->label
);
2343 char *newmsg
= alloca(newmsg_sz
);
2346 sprintf(newmsg
, "%s: %s", j
->label
, msg
);
2350 vsyslog(pri
, newmsg
, ap
);
2355 static void async_callback(void)
2357 struct timespec timeout
= { 0, 0 };
2360 switch (kevent(asynckq
, NULL
, 0, &kev
, 1, &timeout
)) {
2362 syslog(LOG_DEBUG
, "kevent(): %m");
2365 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
2369 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");