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 pid_t launchd_proper_pid
= 0;
181 static bool launchd_inited
= false;
182 static bool shutdown_in_progress
= false;
183 static pthread_t mach_server_loop_thread
;
184 mach_port_t launchd_bootstrap_port
= MACH_PORT_NULL
;
185 sigset_t blocked_signals
= 0;
186 static char *pending_stdout
= NULL
;
187 static char *pending_stderr
= NULL
;
189 int main(int argc
, char *argv
[])
191 static const int sigigns
[] = { SIGHUP
, SIGINT
, SIGPIPE
, SIGALRM
,
192 SIGTERM
, SIGURG
, SIGTSTP
, SIGTSTP
, SIGCONT
, /*SIGCHLD,*/
193 SIGTTIN
, SIGTTOU
, SIGIO
, SIGXCPU
, SIGXFSZ
, SIGVTALRM
, SIGPROF
,
194 SIGWINCH
, SIGINFO
, SIGUSR1
, SIGUSR2
};
195 void testfd_or_openfd(int fd
, const char *path
, int flags
) {
198 if (-1 != (tmpfd
= dup(fd
))) {
201 if (-1 == (tmpfd
= open(path
, flags
))) {
202 syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
203 } else if (tmpfd
!= fd
) {
211 bool sflag
= false, xflag
= false, vflag
= false, dflag
= false;
215 workaround3048875(argc
, argv
);
220 testfd_or_openfd(STDIN_FILENO
, _PATH_DEVNULL
, O_RDONLY
);
221 testfd_or_openfd(STDOUT_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
222 testfd_or_openfd(STDERR_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
224 openlog(getprogname(), LOG_CONS
|(getpid() != 1 ? LOG_PID
|LOG_PERROR
: 0), LOG_LAUNCHD
);
225 setlogmask(LOG_UPTO(LOG_NOTICE
));
227 while ((ch
= getopt(argc
, argv
, "dhsvx")) != -1) {
229 case 'd': dflag
= true; break;
230 case 's': sflag
= true; break;
231 case 'x': xflag
= true; break;
232 case 'v': vflag
= true; break;
233 case 'h': usage(stdout
); break;
235 syslog(LOG_WARNING
, "ignoring unknown arguments");
243 if (dflag
&& daemon(0, 0) == -1)
244 syslog(LOG_WARNING
, "couldn't daemonize: %m");
246 if ((mainkq
= kqueue()) == -1) {
247 syslog(LOG_EMERG
, "kqueue(): %m");
251 if ((asynckq
= kqueue()) == -1) {
252 syslog(LOG_ERR
, "kqueue(): %m");
256 if (kevent_mod(asynckq
, EVFILT_READ
, EV_ADD
, 0, 0, &kqasync_callback
) == -1) {
257 syslog(LOG_ERR
, "kevent_mod(asynckq, EVFILT_READ): %m");
261 sigemptyset(&blocked_signals
);
263 for (i
= 0; i
< (sizeof(sigigns
) / sizeof(int)); i
++) {
264 if (kevent_mod(sigigns
[i
], EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
265 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", sigigns
[i
]);
266 sigaddset(&blocked_signals
, sigigns
[i
]);
267 signal(sigigns
[i
], SIG_IGN
);
270 /* sigh... ignoring SIGCHLD has side effects: we can't call wait*() */
271 if (kevent_mod(SIGCHLD
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
272 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", SIGCHLD
);
275 pid1_magic_init(sflag
, vflag
, xflag
);
277 launchd_bootstrap_port
= bootstrap_port
;
278 launchd_server_init(argv
[0] ? true : false);
281 /* do this after pid1_magic_init() to not catch ourselves mounting stuff */
282 if (kevent_mod(0, EVFILT_FS
, EV_ADD
, 0, 0, &kqfs_callback
) == -1)
283 syslog(LOG_ERR
, "kevent_mod(EVFILT_FS, &kqfs_callback): %m");
287 conceive_firstborn(argv
);
289 reload_launchd_config();
292 job_start(TAILQ_FIRST(&jobs
));
295 static struct timespec timeout
= { 30, 0 };
296 struct timespec
*timeoutp
= NULL
;
299 if (readcfg_pid
== 0)
302 if (TAILQ_EMPTY(&jobs
)) {
303 /* launched on demand */
305 } else if (shutdown_in_progress
&& total_children
== 0) {
310 switch (kevent(mainkq
, NULL
, 0, &kev
, 1, timeoutp
)) {
312 syslog(LOG_DEBUG
, "kevent(): %m");
315 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
321 syslog(LOG_DEBUG
, "kevent(): spurious return with infinite timeout");
324 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
330 static void pid1_magic_init(bool sflag
, bool vflag
, bool xflag
)
333 int memmib
[2] = { CTL_HW
, HW_PHYSMEM
};
334 int mvnmib
[2] = { CTL_KERN
, KERN_MAXVNODES
};
335 int hnmib
[2] = { CTL_KERN
, KERN_HOSTNAME
};
338 size_t memsz
= sizeof(mem
);
341 setpriority(PRIO_PROCESS
, 0, -1);
344 syslog(LOG_ERR
, "setsid(): %m");
346 if (chdir("/") == -1)
347 syslog(LOG_ERR
, "chdir(\"/\"): %m");
349 if (sysctl(memmib
, 2, &mem
, &memsz
, NULL
, 0) == -1) {
350 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "hw.physmem");
352 /* The following assignment of mem to itself if the size
353 * of data returned is 32 bits instead of 64 is a clever
354 * C trick to move the 32 bits on big endian systems to
355 * the least significant bytes of the 64 mem variable.
357 * On little endian systems, this is effectively a no-op.
360 mem
= *(uint32_t *)&mem
;
361 mvn
= mem
/ (64 * 1024) + 1024;
362 if (sysctl(mvnmib
, 2, NULL
, NULL
, &mvn
, sizeof(mvn
)) == -1)
363 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.maxvnodes");
365 if (sysctl(hnmib
, 2, NULL
, NULL
, "localhost", sizeof("localhost")) == -1)
366 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.hostname");
368 if (setlogin("root") == -1)
369 syslog(LOG_ERR
, "setlogin(\"root\"): %m");
373 if (mount("fdesc", "/dev", MNT_UNION
, NULL
) == -1)
374 syslog(LOG_ERR
, "mount(\"%s\", \"%s\", ...): %m", "fdesc", "/dev/");
376 setenv("PATH", _PATH_STDPATH
, 1);
378 launchd_bootstrap_port
= mach_init_init();
379 task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port
);
380 bootstrap_port
= MACH_PORT_NULL
;
382 pthread_attr_init(&attr
);
383 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
385 pthr_r
= pthread_create(&mach_server_loop_thread
, &attr
, mach_server_loop
, NULL
);
387 syslog(LOG_ERR
, "pthread_create(mach_server_loop): %s", strerror(pthr_r
));
391 pthread_attr_destroy(&attr
);
393 init_boot(sflag
, vflag
, xflag
);
397 #ifdef PID1_REAP_ADOPTED_CHILDREN
398 static bool launchd_check_pid(pid_t p
)
403 TAILQ_FOREACH(j
, &jobs
, tqe
) {
405 EV_SET(&kev
, p
, EVFILT_PROC
, 0, 0, 0, j
);
406 j
->kqjob_callback(j
, &kev
);
411 if (p
== readcfg_pid
) {
412 readcfg_callback(NULL
, NULL
);
420 static char *sockdir
= NULL
;
421 static char *sockpath
= NULL
;
423 static void launchd_clean_up(void)
425 if (launchd_proper_pid
!= getpid())
431 if (-1 == unlink(sockpath
))
432 syslog(LOG_WARNING
, "unlink(\"%s\"): %m", sockpath
);
433 else if (-1 == rmdir(sockdir
))
434 syslog(LOG_WARNING
, "rmdir(\"%s\"): %m", sockdir
);
440 static void launchd_server_init(bool create_session
)
442 struct sockaddr_un sun
;
444 int r
, fd
= -1, ourdirfd
= -1;
447 memset(&sun
, 0, sizeof(sun
));
448 sun
.sun_family
= AF_UNIX
;
450 if (create_session
) {
451 snprintf(ourdir
, sizeof(ourdir
), "%s/%u.%u", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
452 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u.%u/sock", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
453 setenv(LAUNCHD_SOCKET_ENV
, sun
.sun_path
, 1);
455 snprintf(ourdir
, sizeof(ourdir
), "%s/%u", LAUNCHD_SOCK_PREFIX
, getuid());
456 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u/sock", LAUNCHD_SOCK_PREFIX
, getuid());
462 if (mkdir(LAUNCHD_SOCK_PREFIX
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) == -1) {
463 if (errno
== EROFS
) {
465 } else if (errno
== EEXIST
) {
467 stat(LAUNCHD_SOCK_PREFIX
, &sb
);
468 if (!S_ISDIR(sb
.st_mode
)) {
470 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
474 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
480 if (mkdir(ourdir
, S_IRWXU
) == -1) {
481 if (errno
== EROFS
) {
483 } else if (errno
== EEXIST
) {
486 if (!S_ISDIR(sb
.st_mode
)) {
488 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
492 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", ourdir
);
497 if (chown(ourdir
, getuid(), getgid()) == -1)
498 syslog(LOG_WARNING
, "chown(\"%s\"): %m", ourdir
);
503 ourdirfd
= _fd(open(ourdir
, O_RDONLY
));
504 if (ourdirfd
== -1) {
505 syslog(LOG_ERR
, "open(\"%s\"): %m", ourdir
);
509 if (flock(ourdirfd
, LOCK_EX
|LOCK_NB
) == -1) {
510 if (errno
== EWOULDBLOCK
) {
513 syslog(LOG_ERR
, "flock(\"%s\"): %m", ourdir
);
518 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
520 syslog(LOG_ERR
, "unlink(\"thesocket\"): %m");
523 if ((fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) == -1) {
524 syslog(LOG_ERR
, "socket(\"thesocket\"): %m");
527 oldmask
= umask(077);
528 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
532 syslog(LOG_ERR
, "bind(\"thesocket\"): %m");
536 if (listen(fd
, SOMAXCONN
) == -1) {
537 syslog(LOG_ERR
, "listen(\"thesocket\"): %m");
541 if (kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqlisten_callback
) == -1) {
542 syslog(LOG_ERR
, "kevent_mod(\"thesocket\", EVFILT_READ): %m");
546 launchd_inited
= true;
548 sockdir
= strdup(ourdir
);
549 sockpath
= strdup(sun
.sun_path
);
551 launchd_proper_pid
= getpid();
552 atexit(launchd_clean_up
);
558 if (!launchd_inited
) {
566 static long long job_get_integer(launch_data_t j
, const char *key
)
568 launch_data_t t
= launch_data_dict_lookup(j
, key
);
570 return launch_data_get_integer(t
);
575 static const char *job_get_string(launch_data_t j
, const char *key
)
577 launch_data_t t
= launch_data_dict_lookup(j
, key
);
579 return launch_data_get_string(t
);
584 static const char *job_get_file2exec(launch_data_t j
)
586 launch_data_t tmpi
, tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAM
);
589 return launch_data_get_string(tmp
);
591 tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
593 tmpi
= launch_data_array_get_index(tmp
, 0);
595 return launch_data_get_string(tmpi
);
601 static bool job_get_bool(launch_data_t j
, const char *key
)
603 launch_data_t t
= launch_data_dict_lookup(j
, key
);
605 return launch_data_get_bool(t
);
610 static void ipc_open(int fd
, struct jobcb
*j
)
612 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
614 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
616 c
->kqconn_callback
= ipc_callback
;
617 c
->conn
= launchd_fdopen(fd
);
619 TAILQ_INSERT_TAIL(&connections
, c
, tqe
);
620 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
);
623 static void simple_zombie_reaper(void *obj
__attribute__((unused
)), struct kevent
*kev
)
625 waitpid(kev
->ident
, NULL
, 0);
628 static void listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
630 struct sockaddr_un sun
;
631 socklen_t sl
= sizeof(sun
);
634 if ((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) == -1) {
641 static void ipc_callback(void *obj
, struct kevent
*kev
)
643 struct conncb
*c
= obj
;
646 if (kev
->filter
== EVFILT_READ
) {
647 if (launchd_msg_recv(c
->conn
, ipc_readmsg
, c
) == -1 && errno
!= EAGAIN
) {
648 if (errno
!= ECONNRESET
)
649 syslog(LOG_DEBUG
, "%s(): recv: %m", __func__
);
652 } else if (kev
->filter
== EVFILT_WRITE
) {
653 r
= launchd_msg_send(c
->conn
, NULL
);
655 if (errno
!= EAGAIN
) {
656 syslog(LOG_DEBUG
, "%s(): send: %m", __func__
);
660 kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
);
663 syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
668 static void set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
670 setenv(key
, launch_data_get_string(obj
), 1);
673 static void launch_data_close_fds(launch_data_t o
)
677 switch (launch_data_get_type(o
)) {
678 case LAUNCH_DATA_DICTIONARY
:
679 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_close_fds
, NULL
);
681 case LAUNCH_DATA_ARRAY
:
682 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
683 launch_data_close_fds(launch_data_array_get_index(o
, i
));
686 if (launch_data_get_fd(o
) != -1)
687 close(launch_data_get_fd(o
));
694 static void launch_data_revoke_fds(launch_data_t o
)
698 switch (launch_data_get_type(o
)) {
699 case LAUNCH_DATA_DICTIONARY
:
700 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_revoke_fds
, NULL
);
702 case LAUNCH_DATA_ARRAY
:
703 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
704 launch_data_revoke_fds(launch_data_array_get_index(o
, i
));
707 launch_data_set_fd(o
, -1);
714 static void job_ignore_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
716 struct jobcb
*j
= cookie
;
720 switch (launch_data_get_type(o
)) {
721 case LAUNCH_DATA_DICTIONARY
:
722 launch_data_dict_iterate(o
, job_ignore_fds
, cookie
);
724 case LAUNCH_DATA_ARRAY
:
725 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
726 job_ignore_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
729 fd
= launch_data_get_fd(o
);
731 job_log(j
, LOG_DEBUG
, "Ignoring FD: %d", fd
);
732 kevent_mod(fd
, EVFILT_READ
, EV_DELETE
, 0, 0, NULL
);
740 static void job_ignore(struct jobcb
*j
)
742 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
746 job_ignore_fds(j_sockets
, NULL
, j
);
748 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
749 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
751 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
752 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
756 static void job_watch_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
758 struct jobcb
*j
= cookie
;
762 switch (launch_data_get_type(o
)) {
763 case LAUNCH_DATA_DICTIONARY
:
764 launch_data_dict_iterate(o
, job_watch_fds
, cookie
);
766 case LAUNCH_DATA_ARRAY
:
767 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
768 job_watch_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
771 fd
= launch_data_get_fd(o
);
773 job_log(j
, LOG_DEBUG
, "Watching FD: %d", fd
);
774 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, cookie
);
782 static void job_watch(struct jobcb
*j
)
784 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
785 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
786 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
790 job_watch_fds(j_sockets
, NULL
, &j
->kqjob_callback
);
792 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
793 if (-1 == j
->vnodes
[i
]) {
794 launch_data_t ld_idx
= launch_data_array_get_index(ld_vnodes
, i
);
795 const char *thepath
= launch_data_get_string(ld_idx
);
797 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
798 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
800 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
801 NOTE_WRITE
|NOTE_EXTEND
|NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
|NOTE_ATTRIB
|NOTE_LINK
,
802 0, &j
->kqjob_callback
);
805 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
806 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
807 NOTE_WRITE
|NOTE_EXTEND
|NOTE_ATTRIB
|NOTE_LINK
, 0, &j
->kqjob_callback
);
810 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
811 launch_data_t ld_idx
= launch_data_array_get_index(ld_qdirs
, i
);
812 const char *thepath
= launch_data_get_string(ld_idx
);
815 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
816 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
817 } else if (dcc_r
> 0 && !shutdown_in_progress
) {
824 static void job_stop(struct jobcb
*j
)
830 static void job_remove(struct jobcb
*j
)
835 job_log(j
, LOG_DEBUG
, "Removed");
837 TAILQ_REMOVE(&jobs
, j
, tqe
);
839 if (kevent_mod(j
->p
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqsimple_zombie_reaper
) == -1) {
845 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
846 launch_data_dict_iterate(tmp
, unsetup_job_env
, NULL
);
847 launch_data_close_fds(j
->ldj
);
848 launch_data_free(j
->ldj
);
851 for (i
= 0; i
< j
->vnodes_cnt
; i
++)
852 if (-1 != j
->vnodes
[i
])
856 for (i
= 0; i
< j
->qdirs_cnt
; i
++)
857 if (-1 != j
->qdirs
[i
])
861 if (j
->start_interval
)
862 kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
863 if (j
->start_cal_interval
) {
864 kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
865 free(j
->start_cal_interval
);
867 kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
871 struct readmsg_context
{
876 static void ipc_readmsg(launch_data_t msg
, void *context
)
878 struct readmsg_context rmc
= { context
, NULL
};
880 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(msg
)) {
881 launch_data_dict_iterate(msg
, ipc_readmsg2
, &rmc
);
882 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(msg
)) {
883 ipc_readmsg2(NULL
, launch_data_get_string(msg
), &rmc
);
885 rmc
.resp
= launch_data_new_errno(EINVAL
);
888 if (NULL
== rmc
.resp
)
889 rmc
.resp
= launch_data_new_errno(ENOSYS
);
891 launch_data_close_fds(msg
);
893 if (launchd_msg_send(rmc
.c
->conn
, rmc
.resp
) == -1) {
894 if (errno
== EAGAIN
) {
895 kevent_mod(launchd_getfd(rmc
.c
->conn
), EVFILT_WRITE
, EV_ADD
, 0, 0, &rmc
.c
->kqconn_callback
);
897 syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %m");
901 launch_data_free(rmc
.resp
);
905 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
907 struct readmsg_context
*rmc
= context
;
908 launch_data_t resp
= NULL
;
914 if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
915 TAILQ_FOREACH(j
, &jobs
, tqe
) {
916 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
918 resp
= launch_data_new_errno(0);
922 resp
= launch_data_new_errno(ESRCH
);
923 } else if (!strcmp(cmd
, LAUNCH_KEY_STOPJOB
)) {
924 TAILQ_FOREACH(j
, &jobs
, tqe
) {
925 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
927 resp
= launch_data_new_errno(0);
931 resp
= launch_data_new_errno(ESRCH
);
932 } else if (!strcmp(cmd
, LAUNCH_KEY_REMOVEJOB
)) {
933 TAILQ_FOREACH(j
, &jobs
, tqe
) {
934 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
936 resp
= launch_data_new_errno(0);
940 resp
= launch_data_new_errno(ESRCH
);
941 } else if (!strcmp(cmd
, LAUNCH_KEY_SUBMITJOB
)) {
942 if (launch_data_get_type(data
) == LAUNCH_DATA_ARRAY
) {
946 resp
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
947 for (i
= 0; i
< launch_data_array_get_count(data
); i
++) {
948 tmp
= load_job(launch_data_array_get_index(data
, i
));
949 launch_data_array_set_index(resp
, tmp
, i
);
952 resp
= load_job(data
);
954 } else if (!strcmp(cmd
, LAUNCH_KEY_UNSETUSERENVIRONMENT
)) {
955 unsetenv(launch_data_get_string(data
));
956 resp
= launch_data_new_errno(0);
957 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUSERENVIRONMENT
)) {
958 char **tmpenviron
= environ
;
959 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
960 for (; *tmpenviron
; tmpenviron
++) {
962 launch_data_t s
= launch_data_alloc(LAUNCH_DATA_STRING
);
963 launch_data_set_string(s
, strchr(*tmpenviron
, '=') + 1);
964 strncpy(envkey
, *tmpenviron
, sizeof(envkey
));
965 *(strchr(envkey
, '=')) = '\0';
966 launch_data_dict_insert(resp
, s
, envkey
);
968 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUSERENVIRONMENT
)) {
969 launch_data_dict_iterate(data
, set_user_env
, NULL
);
970 resp
= launch_data_new_errno(0);
971 } else if (!strcmp(cmd
, LAUNCH_KEY_CHECKIN
)) {
973 resp
= launch_data_copy(rmc
->c
->j
->ldj
);
974 if (NULL
== launch_data_dict_lookup(resp
, LAUNCH_JOBKEY_TIMEOUT
)) {
975 launch_data_t to
= launch_data_new_integer(LAUNCHD_MIN_JOB_RUN_TIME
);
976 launch_data_dict_insert(resp
, to
, LAUNCH_JOBKEY_TIMEOUT
);
978 rmc
->c
->j
->checkedin
= true;
980 resp
= launch_data_new_errno(EACCES
);
982 } else if (!strcmp(cmd
, LAUNCH_KEY_RELOADTTYS
)) {
984 resp
= launch_data_new_errno(0);
985 } else if (!strcmp(cmd
, LAUNCH_KEY_SHUTDOWN
)) {
987 resp
= launch_data_new_errno(0);
988 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBS
)) {
989 resp
= get_jobs(NULL
);
990 launch_data_revoke_fds(resp
);
991 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRESOURCELIMITS
)) {
992 resp
= adjust_rlimits(NULL
);
993 } else if (!strcmp(cmd
, LAUNCH_KEY_SETRESOURCELIMITS
)) {
994 resp
= adjust_rlimits(data
);
995 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOB
)) {
996 resp
= get_jobs(launch_data_get_string(data
));
997 launch_data_revoke_fds(resp
);
998 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBWITHHANDLES
)) {
999 resp
= get_jobs(launch_data_get_string(data
));
1000 } else if (!strcmp(cmd
, LAUNCH_KEY_SETLOGMASK
)) {
1001 resp
= launch_data_new_integer(setlogmask(launch_data_get_integer(data
)));
1002 } else if (!strcmp(cmd
, LAUNCH_KEY_GETLOGMASK
)) {
1003 int oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1004 resp
= launch_data_new_integer(oldmask
);
1005 setlogmask(oldmask
);
1006 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUMASK
)) {
1007 resp
= launch_data_new_integer(umask(launch_data_get_integer(data
)));
1008 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUMASK
)) {
1009 mode_t oldmask
= umask(0);
1010 resp
= launch_data_new_integer(oldmask
);
1012 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGESELF
)) {
1013 struct rusage rusage
;
1014 getrusage(RUSAGE_SELF
, &rusage
);
1015 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1016 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGECHILDREN
)) {
1017 struct rusage rusage
;
1018 getrusage(RUSAGE_CHILDREN
, &rusage
);
1019 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1020 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDOUT
)) {
1021 resp
= setstdio(STDOUT_FILENO
, data
);
1022 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDERR
)) {
1023 resp
= setstdio(STDERR_FILENO
, data
);
1024 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHCONTROL
)) {
1025 batch_job_enable(launch_data_get_bool(data
), rmc
->c
);
1026 resp
= launch_data_new_errno(0);
1027 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHQUERY
)) {
1028 resp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1029 launch_data_set_bool(resp
, batch_disabler_count
== 0);
1035 static launch_data_t
setstdio(int d
, launch_data_t o
)
1037 launch_data_t resp
= launch_data_new_errno(0);
1039 if (launch_data_get_type(o
) == LAUNCH_DATA_STRING
) {
1040 char **where
= &pending_stderr
;
1041 if (d
== STDOUT_FILENO
)
1042 where
= &pending_stdout
;
1045 *where
= strdup(launch_data_get_string(o
));
1046 } else if (launch_data_get_type(o
) == LAUNCH_DATA_FD
) {
1047 dup2(launch_data_get_fd(o
), d
);
1049 launch_data_set_errno(resp
, EINVAL
);
1055 static void batch_job_enable(bool e
, struct conncb
*c
)
1057 if (e
&& c
->disabled_batch
) {
1058 batch_disabler_count
--;
1059 c
->disabled_batch
= 0;
1060 if (batch_disabler_count
== 0)
1061 kevent_mod(asynckq
, EVFILT_READ
, EV_ENABLE
, 0, 0, &kqasync_callback
);
1062 } else if (!e
&& !c
->disabled_batch
) {
1063 if (batch_disabler_count
== 0)
1064 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1065 batch_disabler_count
++;
1066 c
->disabled_batch
= 1;
1070 static launch_data_t
load_job(launch_data_t pload
)
1072 launch_data_t tmp
, resp
;
1075 bool startnow
, hasprog
= false, hasprogargs
= false;
1077 if ((label
= job_get_string(pload
, LAUNCH_JOBKEY_LABEL
))) {
1078 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1079 if (!strcmp(j
->label
, label
)) {
1080 resp
= launch_data_new_errno(EEXIST
);
1085 resp
= launch_data_new_errno(EINVAL
);
1089 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAM
))
1091 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
))
1094 if (!hasprog
&& !hasprogargs
) {
1095 resp
= launch_data_new_errno(EINVAL
);
1099 j
= calloc(1, sizeof(struct jobcb
) + strlen(label
) + 1);
1100 strcpy(j
->label
, label
);
1101 j
->ldj
= launch_data_copy(pload
);
1102 launch_data_revoke_fds(pload
);
1103 j
->kqjob_callback
= job_callback
;
1106 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
) == NULL
) {
1107 tmp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1108 launch_data_set_bool(tmp
, true);
1109 launch_data_dict_insert(j
->ldj
, tmp
, LAUNCH_JOBKEY_ONDEMAND
);
1112 TAILQ_INSERT_TAIL(&jobs
, j
, tqe
);
1114 j
->debug
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_DEBUG
);
1116 startnow
= !job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1118 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_RUNATLOAD
))
1121 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
))) {
1124 j
->qdirs_cnt
= launch_data_array_get_count(tmp
);
1125 j
->qdirs
= malloc(sizeof(int) * j
->qdirs_cnt
);
1127 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1128 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1130 if (-1 == (j
->qdirs
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1131 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1136 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTINTERVAL
))) {
1137 j
->start_interval
= launch_data_get_integer(tmp
);
1139 if (j
->start_interval
== 0)
1140 job_log(j
, LOG_WARNING
, "StartInterval is zero, ignoring");
1141 else if (-1 == kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_SECONDS
, j
->start_interval
, &j
->kqjob_callback
))
1142 job_log_error(j
, LOG_ERR
, "adding kevent timer");
1145 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTCALENDARINTERVAL
))) {
1146 launch_data_t tmp_k
;
1148 j
->start_cal_interval
= calloc(1, sizeof(struct tm
));
1149 j
->start_cal_interval
->tm_min
= -1;
1150 j
->start_cal_interval
->tm_hour
= -1;
1151 j
->start_cal_interval
->tm_mday
= -1;
1152 j
->start_cal_interval
->tm_wday
= -1;
1153 j
->start_cal_interval
->tm_mon
= -1;
1155 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(tmp
)) {
1156 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MINUTE
)))
1157 j
->start_cal_interval
->tm_min
= launch_data_get_integer(tmp_k
);
1158 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_HOUR
)))
1159 j
->start_cal_interval
->tm_hour
= launch_data_get_integer(tmp_k
);
1160 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_DAY
)))
1161 j
->start_cal_interval
->tm_mday
= launch_data_get_integer(tmp_k
);
1162 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_WEEKDAY
)))
1163 j
->start_cal_interval
->tm_wday
= launch_data_get_integer(tmp_k
);
1164 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MONTH
)))
1165 j
->start_cal_interval
->tm_mon
= launch_data_get_integer(tmp_k
);
1171 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
))) {
1174 j
->vnodes_cnt
= launch_data_array_get_count(tmp
);
1175 j
->vnodes
= malloc(sizeof(int) * j
->vnodes_cnt
);
1177 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1178 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1180 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1181 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1186 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
1187 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1189 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1195 resp
= launch_data_new_errno(0);
1200 static launch_data_t
get_jobs(const char *which
)
1203 launch_data_t tmp
, resp
= NULL
;
1206 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1207 if (!strcmp(which
, j
->label
))
1208 resp
= launch_data_copy(j
->ldj
);
1211 resp
= launch_data_new_errno(ESRCH
);
1213 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1215 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1216 tmp
= launch_data_copy(j
->ldj
);
1217 launch_data_dict_insert(resp
, tmp
, j
->label
);
1224 static void usage(FILE *where
)
1226 fprintf(where
, "%s: [-d] [-- command [args ...]]\n", getprogname());
1227 fprintf(where
, "\t-d\tdaemonize\n");
1228 fprintf(where
, "\t-h\tthis usage statement\n");
1230 if (where
== stdout
)
1234 #ifdef EVFILT_MACH_IMPLEMENTED
1235 static void **machcbtable
= NULL
;
1236 static size_t machcbtable_cnt
= 0;
1237 static int machcbreadfd
= -1;
1238 static int machcbwritefd
= -1;
1239 static mach_port_t mach_demand_port_set
= MACH_PORT_NULL
;
1240 static pthread_t mach_demand_thread
;
1242 static void mach_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1247 read(machcbreadfd
, &mp
, sizeof(mp
));
1249 EV_SET(&mkev
, mp
, EVFILT_MACHPORT
, 0, 0, 0, machcbtable
[MACH_PORT_INDEX(mp
)]);
1251 (*((kq_callback
*)mkev
.udata
))(mkev
.udata
, &mkev
);
1255 int kevent_mod(uintptr_t ident
, short filter
, u_short flags
, u_int fflags
, intptr_t data
, void *udata
)
1259 #ifdef EVFILT_MACH_IMPLEMENTED
1261 pthread_attr_t attr
;
1262 int pthr_r
, pfds
[2];
1265 if (EVFILT_TIMER
== filter
|| EVFILT_VNODE
== filter
)
1268 if (flags
& EV_ADD
&& NULL
== udata
) {
1269 syslog(LOG_ERR
, "%s(): kev.udata == NULL!!!", __func__
);
1270 syslog(LOG_ERR
, "kev: ident %d filter %d flags 0x%x fflags 0x%x",
1271 ident
, filter
, flags
, fflags
);
1276 #ifdef EVFILT_MACH_IMPLEMENTED
1277 if (filter
!= EVFILT_MACHPORT
) {
1279 #ifdef PID1_REAP_ADOPTED_CHILDREN
1280 if (filter
== EVFILT_PROC
&& getpid() == 1)
1283 EV_SET(&kev
, ident
, filter
, flags
, fflags
, data
, udata
);
1284 return kevent(q
, &kev
, 1, NULL
, 0, NULL
);
1285 #ifdef EVFILT_MACH_IMPLEMENTED
1288 if (machcbtable
== NULL
) {
1289 pthread_attr_init(&attr
);
1290 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1292 pthr_r
= pthread_create(&mach_demand_thread
, &attr
, mach_demand_loop
, NULL
);
1294 syslog(LOG_ERR
, "pthread_create(mach_demand_loop): %s", strerror(pthr_r
));
1298 pthread_attr_destroy(&attr
);
1300 machcbtable
= malloc(0);
1302 machcbwritefd
= _fd(pfds
[1]);
1303 machcbreadfd
= _fd(pfds
[0]);
1304 kevent_mod(machcbreadfd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqmach_callback
);
1305 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET
, &mach_demand_port_set
);
1306 if (kr
!= KERN_SUCCESS
) {
1307 syslog(LOG_ERR
, "mach_port_allocate(demand_port_set): %s", mach_error_string(kr
));
1312 if (flags
& EV_ADD
) {
1313 kr
= mach_port_move_member(mach_task_self(), ident
, mach_demand_port_set
);
1314 if (kr
!= KERN_SUCCESS
) {
1315 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1319 if (MACH_PORT_INDEX(ident
) > machcbtable_cnt
)
1320 machcbtable
= realloc(machcbtable
, MACH_PORT_INDEX(ident
) * sizeof(void *));
1322 machcbtable
[MACH_PORT_INDEX(ident
)] = udata
;
1323 } else if (flags
& EV_DELETE
) {
1324 kr
= mach_port_move_member(mach_task_self(), ident
, MACH_PORT_NULL
);
1325 if (kr
!= KERN_SUCCESS
) {
1326 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1330 syslog(LOG_DEBUG
, "kevent_mod(EVFILT_MACHPORT) with flags: %d", flags
);
1339 static int _fd(int fd
)
1342 fcntl(fd
, F_SETFD
, 1);
1346 static void ipc_close(struct conncb
*c
)
1348 batch_job_enable(true, c
);
1350 TAILQ_REMOVE(&connections
, c
, tqe
);
1351 launchd_close(c
->conn
);
1355 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1357 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1358 setenv(key
, launch_data_get_string(obj
), 1);
1361 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1363 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1367 static void job_reap(struct jobcb
*j
)
1369 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1370 time_t td
= time(NULL
) - j
->start_time
;
1371 bool bad_exit
= false;
1374 job_log(j
, LOG_DEBUG
, "Reaping");
1381 #ifdef PID1_REAP_ADOPTED_CHILDREN
1383 status
= pid1_child_exit_status
;
1386 if (-1 == waitpid(j
->p
, &status
, 0)) {
1387 job_log_error(j
, LOG_ERR
, "waitpid(%d, ...)", j
->p
);
1391 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
1392 job_log(j
, LOG_WARNING
, "exited with exit code: %d", WEXITSTATUS(status
));
1396 if (WIFSIGNALED(status
)) {
1397 int s
= WTERMSIG(status
);
1398 if (SIGKILL
== s
|| SIGTERM
== s
) {
1399 job_log(j
, LOG_NOTICE
, "exited: %s", strsignal(s
));
1401 job_log(j
, LOG_WARNING
, "exited abnormally: %s", strsignal(s
));
1407 if (td
< LAUNCHD_MIN_JOB_RUN_TIME
) {
1408 job_log(j
, LOG_WARNING
, "respawning too quickly! throttling");
1411 } else if (td
>= LAUNCHD_REWARD_JOB_RUN_TIME
) {
1412 job_log(j
, LOG_INFO
, "lived long enough, forgiving past exit failures");
1413 j
->failed_exits
= 0;
1420 if (j
->failed_exits
> 0) {
1421 int failures_left
= LAUNCHD_FAILED_EXITS_THRESHOLD
- j
->failed_exits
;
1423 job_log(j
, LOG_WARNING
, "%d more failure%s without living at least %d seconds will cause job removal",
1424 failures_left
, failures_left
> 1 ? "s" : "", LAUNCHD_REWARD_JOB_RUN_TIME
);
1431 static bool job_restart_fitness_test(struct jobcb
*j
)
1433 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1436 job_log(j
, LOG_DEBUG
, "first born died, begin shutdown");
1439 } else if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
) && !j
->checkedin
) {
1440 job_log(j
, LOG_WARNING
, "failed to checkin");
1443 } else if (j
->failed_exits
>= LAUNCHD_FAILED_EXITS_THRESHOLD
) {
1444 job_log(j
, LOG_WARNING
, "too many failures in succession");
1447 } else if (od
|| shutdown_in_progress
) {
1448 if (!od
&& shutdown_in_progress
)
1449 job_log(j
, LOG_NOTICE
, "exited while shutdown is in progress, will not restart unless demand requires it");
1457 static void job_callback(void *obj
, struct kevent
*kev
)
1459 struct jobcb
*j
= obj
;
1461 bool startnow
= true;
1465 oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1466 job_log(j
, LOG_DEBUG
, "log level debug temporarily enabled while processing job");
1469 if (kev
->filter
== EVFILT_PROC
) {
1472 startnow
= job_restart_fitness_test(j
);
1474 if (startnow
&& j
->throttle
) {
1475 j
->throttle
= false;
1476 job_log(j
, LOG_WARNING
, "will restart in %d seconds", LAUNCHD_MIN_JOB_RUN_TIME
);
1477 if (-1 == kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_ADD
|EV_ONESHOT
,
1478 NOTE_SECONDS
, LAUNCHD_MIN_JOB_RUN_TIME
, &j
->kqjob_callback
)) {
1479 job_log_error(j
, LOG_WARNING
, "failed to setup timer callback!, starting now!");
1484 } else if (kev
->filter
== EVFILT_TIMER
&& (void *)kev
->ident
== j
->start_cal_interval
) {
1486 } else if (kev
->filter
== EVFILT_VNODE
) {
1488 const char *thepath
= NULL
;
1490 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1491 if (j
->vnodes
[i
] == (int)kev
->ident
) {
1492 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
1494 thepath
= launch_data_get_string(launch_data_array_get_index(ld_vnodes
, i
));
1496 job_log(j
, LOG_DEBUG
, "watch path modified: %s", thepath
);
1498 if ((NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
) & kev
->fflags
) {
1499 job_log(j
, LOG_DEBUG
, "watch path invalidated: %s", thepath
);
1500 close(j
->vnodes
[i
]);
1501 j
->vnodes
[i
] = -1; /* this will get fixed in job_watch() */
1506 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1507 if (j
->qdirs
[i
] == (int)kev
->ident
) {
1508 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
1511 thepath
= launch_data_get_string(launch_data_array_get_index(ld_qdirs
, i
));
1513 job_log(j
, LOG_DEBUG
, "queue directory modified: %s", thepath
);
1515 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
1516 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
1517 } else if (0 == dcc_r
) {
1518 job_log(j
, LOG_DEBUG
, "spurious wake up, directory empty: %s", thepath
);
1523 /* if we get here, then the vnodes either wasn't a qdir, or if it was, it has entries in it */
1524 } else if (kev
->filter
== EVFILT_READ
&& (int)kev
->ident
== j
->execfd
) {
1525 if (kev
->data
> 0) {
1528 read(j
->execfd
, &e
, sizeof(e
));
1530 job_log_error(j
, LOG_ERR
, "execve()");
1545 /* the job might have been removed, must not call job_log() */
1546 syslog(LOG_DEBUG
, "restoring original log mask");
1547 setlogmask(oldmask
);
1551 static void job_start(struct jobcb
*j
)
1559 job_log(j
, LOG_DEBUG
, "Starting");
1562 job_log(j
, LOG_DEBUG
, "already running");
1566 j
->checkedin
= false;
1568 sipc
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
);
1570 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
))
1574 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
1576 socketpair(AF_UNIX
, SOCK_STREAM
, 0, execspair
);
1578 time(&j
->start_time
);
1580 switch (c
= fork_with_bootstrap_port(launchd_bootstrap_port
)) {
1582 job_log_error(j
, LOG_ERR
, "fork() failed, will try again in one second");
1583 close(execspair
[0]);
1584 close(execspair
[1]);
1589 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1593 close(execspair
[0]);
1594 /* wait for our parent to say they've attached a kevent to us */
1595 read(_fd(execspair
[1]), &c
, sizeof(c
));
1597 setpgid(getpid(), getpid());
1598 if (isatty(STDIN_FILENO
)) {
1599 if (tcsetpgrp(STDIN_FILENO
, getpid()) == -1)
1600 job_log_error(j
, LOG_WARNING
, "tcsetpgrp()");
1606 sprintf(nbuf
, "%d", spair
[1]);
1607 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
1609 job_start_child(j
, execspair
[1]);
1612 close(execspair
[1]);
1613 j
->execfd
= _fd(execspair
[0]);
1616 ipc_open(_fd(spair
[0]), j
);
1618 if (kevent_mod(j
->execfd
, EVFILT_READ
, EV_ADD
, 0, 0, &j
->kqjob_callback
) == -1)
1619 job_log_error(j
, LOG_ERR
, "kevent_mod(j->execfd): %m");
1620 if (kevent_mod(c
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &j
->kqjob_callback
) == -1) {
1621 job_log_error(j
, LOG_ERR
, "kevent()");
1626 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1629 /* this unblocks the child and avoids a race
1630 * between the above fork() and the kevent_mod() */
1631 write(j
->execfd
, &c
, sizeof(c
));
1636 static void job_start_child(struct jobcb
*j
, int execfd
)
1638 launch_data_t ldpa
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
1639 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1641 const char **argv
, *file2exec
= "/usr/libexec/launchproxy";
1643 bool hasprog
= false;
1645 job_setup_attributes(j
);
1648 argv_cnt
= launch_data_array_get_count(ldpa
);
1649 argv
= alloca((argv_cnt
+ 2) * sizeof(char *));
1650 for (i
= 0; i
< argv_cnt
; i
++)
1651 argv
[i
+ 1] = launch_data_get_string(launch_data_array_get_index(ldpa
, i
));
1652 argv
[argv_cnt
+ 1] = NULL
;
1654 argv
= alloca(3 * sizeof(char *));
1655 argv
[1] = job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
);
1659 if (job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
))
1663 argv
[0] = file2exec
;
1666 file2exec
= job_get_file2exec(j
->ldj
);
1670 r
= execv(file2exec
, (char *const*)argv
);
1672 r
= execvp(file2exec
, (char *const*)argv
);
1676 write(execfd
, &errno
, sizeof(errno
));
1677 job_log_error(j
, LOG_ERR
, "execv%s(\"%s\", ...)", hasprog
? "" : "p", file2exec
);
1682 static void job_setup_attributes(struct jobcb
*j
)
1684 launch_data_t srl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOFTRESOURCELIMITS
);
1685 launch_data_t hrl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_HARDRESOURCELIMITS
);
1686 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1690 struct group
*gre
= NULL
;
1692 static const struct {
1696 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE
, RLIMIT_CORE
},
1697 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU
, RLIMIT_CPU
},
1698 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA
, RLIMIT_DATA
},
1699 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE
, RLIMIT_FSIZE
},
1700 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK
, RLIMIT_MEMLOCK
},
1701 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE
, RLIMIT_NOFILE
},
1702 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC
, RLIMIT_NPROC
},
1703 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS
, RLIMIT_RSS
},
1704 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK
, RLIMIT_STACK
},
1707 setpriority(PRIO_PROCESS
, 0, job_get_integer(j
->ldj
, LAUNCH_JOBKEY_NICE
));
1710 for (i
= 0; i
< (sizeof(limits
) / sizeof(limits
[0])); i
++) {
1713 if (getrlimit(limits
[i
].val
, &rl
) == -1) {
1714 job_log_error(j
, LOG_WARNING
, "getrlimit()");
1719 rl
.rlim_max
= job_get_integer(hrl
, limits
[i
].key
);
1721 rl
.rlim_cur
= job_get_integer(srl
, limits
[i
].key
);
1723 if (setrlimit(limits
[i
].val
, &rl
) == -1)
1724 job_log_error(j
, LOG_WARNING
, "setrlimit()");
1728 if (!inetcompat
&& job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SESSIONCREATE
))
1729 launchd_SessionCreate(job_get_file2exec(j
->ldj
));
1731 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_LOWPRIORITYIO
)) {
1732 int lowprimib
[] = { CTL_KERN
, KERN_PROC_LOW_PRI_IO
};
1735 if (sysctl(lowprimib
, sizeof(lowprimib
) / sizeof(lowprimib
[0]), NULL
, NULL
, &val
, sizeof(val
)) == -1)
1736 job_log_error(j
, LOG_WARNING
, "sysctl(\"%s\")", "kern.proc_low_pri_io");
1738 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_ROOTDIRECTORY
))) {
1742 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_GROUPNAME
))) {
1743 gre
= getgrnam(tmpstr
);
1745 gre_g
= gre
->gr_gid
;
1746 if (-1 == setgid(gre_g
)) {
1747 job_log_error(j
, LOG_ERR
, "setgid(%d)", gre_g
);
1751 job_log(j
, LOG_ERR
, "getgrnam(\"%s\") failed", tmpstr
);
1755 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_USERNAME
))) {
1756 struct passwd
*pwe
= getpwnam(tmpstr
);
1758 uid_t pwe_u
= pwe
->pw_uid
;
1759 uid_t pwe_g
= pwe
->pw_gid
;
1761 if (pwe
->pw_expire
&& time(NULL
) >= pwe
->pw_expire
) {
1762 job_log(j
, LOG_ERR
, "expired account: %s", tmpstr
);
1765 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INITGROUPS
)) {
1766 if (-1 == initgroups(tmpstr
, gre
? gre_g
: pwe_g
)) {
1767 job_log_error(j
, LOG_ERR
, "initgroups()");
1772 if (-1 == setgid(pwe_g
)) {
1773 job_log_error(j
, LOG_ERR
, "setgid(%d)", pwe_g
);
1777 if (-1 == setuid(pwe_u
)) {
1778 job_log_error(j
, LOG_ERR
, "setuid(%d)", pwe_u
);
1782 job_log(j
, LOG_WARNING
, "getpwnam(\"%s\") failed", tmpstr
);
1786 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_WORKINGDIRECTORY
)))
1788 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_UMASK
))
1789 umask(job_get_integer(j
->ldj
, LAUNCH_JOBKEY_UMASK
));
1790 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDOUTPATH
))) {
1791 int sofd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1793 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1795 dup2(sofd
, STDOUT_FILENO
);
1799 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDERRORPATH
))) {
1800 int sefd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1802 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1804 dup2(sefd
, STDERR_FILENO
);
1808 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
)))
1809 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1814 #ifdef PID1_REAP_ADOPTED_CHILDREN
1815 __private_extern__
int pid1_child_exit_status
= 0;
1816 static void pid1waitpid(void)
1820 while ((p
= waitpid(-1, &pid1_child_exit_status
, WNOHANG
)) > 0) {
1821 if (!launchd_check_pid(p
))
1827 static void do_shutdown(void)
1831 shutdown_in_progress
= true;
1833 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1835 TAILQ_FOREACH(j
, &jobs
, tqe
)
1838 if (getpid() == 1) {
1840 mach_start_shutdown(SIGTERM
);
1844 static void signal_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
1846 switch (kev
->ident
) {
1849 reload_launchd_config();
1854 #ifdef PID1_REAP_ADOPTED_CHILDREN
1856 /* <rdar://problem/3632556> Please automatically reap processes reparented to PID 1 */
1866 static void fs_callback(void)
1868 static bool mounted_volfs
= false;
1871 mounted_volfs
= true;
1873 if (pending_stdout
) {
1874 int fd
= open(pending_stdout
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1876 dup2(fd
, STDOUT_FILENO
);
1878 free(pending_stdout
);
1879 pending_stdout
= NULL
;
1882 if (pending_stderr
) {
1883 int fd
= open(pending_stderr
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1885 dup2(fd
, STDERR_FILENO
);
1887 free(pending_stderr
);
1888 pending_stderr
= NULL
;
1892 if (!mounted_volfs
) {
1893 int r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1895 if (-1 == r
&& errno
== ENOENT
) {
1896 mkdir(VOLFSDIR
, ACCESSPERMS
& ~(S_IWUSR
|S_IWGRP
|S_IWOTH
));
1897 r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1901 syslog(LOG_WARNING
, "mount(\"%s\", \"%s\", ...): %m", "volfs", VOLFSDIR
);
1903 mounted_volfs
= true;
1907 if (!launchd_inited
)
1908 launchd_server_init(false);
1911 static void readcfg_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1915 #ifdef PID1_REAP_ADOPTED_CHILDREN
1917 status
= pid1_child_exit_status
;
1920 if (-1 == waitpid(readcfg_pid
, &status
, 0)) {
1921 syslog(LOG_WARNING
, "waitpid(readcfg_pid, ...): %m");
1927 if (WIFEXITED(status
)) {
1928 if (WEXITSTATUS(status
))
1929 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited with status: %d", WEXITSTATUS(status
));
1930 } else if (WIFSIGNALED(status
)) {
1931 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally: %s", strsignal(WTERMSIG(status
)));
1933 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally");
1937 #ifdef EVFILT_MACH_IMPLEMENTED
1938 static void *mach_demand_loop(void *arg
__attribute__((unused
)))
1940 mach_msg_empty_rcv_t dummy
;
1942 mach_port_name_array_t members
;
1943 mach_msg_type_number_t membersCnt
;
1944 mach_port_status_t status
;
1945 mach_msg_type_number_t statusCnt
;
1951 * Receive indication of message on demand service
1952 * ports without actually receiving the message (we'll
1953 * let the actual server do that.
1955 kr
= mach_msg(&dummy
.header
, MACH_RCV_MSG
|MACH_RCV_LARGE
,
1956 0, 0, mach_demand_port_set
, 0, MACH_PORT_NULL
);
1957 if (kr
!= MACH_RCV_TOO_LARGE
) {
1958 syslog(LOG_WARNING
, "%s(): mach_msg(): %s", __func__
, mach_error_string(kr
));
1963 * Some port(s) now have messages on them, find out
1964 * which ones (there is no indication of which port
1965 * triggered in the MACH_RCV_TOO_LARGE indication).
1967 kr
= mach_port_get_set_status(mach_task_self(),
1968 mach_demand_port_set
, &members
, &membersCnt
);
1969 if (kr
!= KERN_SUCCESS
) {
1970 syslog(LOG_WARNING
, "%s(): mach_port_get_set_status(): %s", __func__
, mach_error_string(kr
));
1974 for (i
= 0; i
< membersCnt
; i
++) {
1975 statusCnt
= MACH_PORT_RECEIVE_STATUS_COUNT
;
1976 kr
= mach_port_get_attributes(mach_task_self(), members
[i
],
1977 MACH_PORT_RECEIVE_STATUS
, (mach_port_info_t
)&status
, &statusCnt
);
1978 if (kr
!= KERN_SUCCESS
) {
1979 syslog(LOG_WARNING
, "%s(): mach_port_get_attributes(): %s", __func__
, mach_error_string(kr
));
1984 * For each port with messages, take it out of the
1985 * demand service portset, and inform the main thread
1986 * that it might have to start the server responsible
1989 if (status
.mps_msgcount
) {
1990 kr
= mach_port_move_member(mach_task_self(), members
[i
], MACH_PORT_NULL
);
1991 if (kr
!= KERN_SUCCESS
) {
1992 syslog(LOG_WARNING
, "%s(): mach_port_move_member(): %s", __func__
, mach_error_string(kr
));
1995 write(machcbwritefd
, &(members
[i
]), sizeof(members
[i
]));
1999 kr
= vm_deallocate(mach_task_self(), (vm_address_t
) members
,
2000 (vm_size_t
) membersCnt
* sizeof(mach_port_name_t
));
2001 if (kr
!= KERN_SUCCESS
) {
2002 syslog(LOG_WARNING
, "%s(): vm_deallocate(): %s", __func__
, mach_error_string(kr
));
2011 static void reload_launchd_config(void)
2014 static char *ldconf
= PID1LAUNCHD_CONF
;
2015 const char *h
= getenv("HOME");
2017 if (h
&& ldconf
== PID1LAUNCHD_CONF
)
2018 asprintf(&ldconf
, "%s/%s", h
, LAUNCHD_CONF
);
2023 if (lstat(ldconf
, &sb
) == 0) {
2025 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
2026 readcfg_pid
= fork_with_bootstrap_port(launchd_bootstrap_port
);
2027 if (readcfg_pid
== 0) {
2030 sprintf(nbuf
, "%d", spair
[1]);
2031 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
2032 int fd
= open(ldconf
, O_RDONLY
);
2034 syslog(LOG_ERR
, "open(\"%s\"): %m", ldconf
);
2037 dup2(fd
, STDIN_FILENO
);
2039 execl(LAUNCHCTL_PATH
, LAUNCHCTL_PATH
, NULL
);
2040 syslog(LOG_ERR
, "execl(\"%s\", ...): %m", LAUNCHCTL_PATH
);
2042 } else if (readcfg_pid
== -1) {
2045 syslog(LOG_ERR
, "fork(): %m");
2049 ipc_open(_fd(spair
[0]), NULL
);
2050 if (kevent_mod(readcfg_pid
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqreadcfg_callback
) == -1)
2051 syslog(LOG_ERR
, "kevent_mod(EVFILT_PROC, &kqreadcfg_callback): %m");
2056 static void conceive_firstborn(char *argv
[])
2058 launch_data_t r
, d
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2059 launch_data_t args
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
2060 launch_data_t l
= launch_data_new_string("com.apple.launchd.firstborn");
2063 for (i
= 0; *argv
; argv
++, i
++)
2064 launch_data_array_set_index(args
, launch_data_new_string(*argv
), i
);
2066 launch_data_dict_insert(d
, args
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
2067 launch_data_dict_insert(d
, l
, LAUNCH_JOBKEY_LABEL
);
2071 launch_data_free(r
);
2072 launch_data_free(d
);
2074 TAILQ_FIRST(&jobs
)->firstborn
= true;
2077 static void loopback_setup(void)
2079 struct ifaliasreq ifra
;
2080 struct in6_aliasreq ifra6
;
2084 memset(&ifr
, 0, sizeof(ifr
));
2085 strcpy(ifr
.ifr_name
, "lo0");
2087 if (-1 == (s
= socket(AF_INET
, SOCK_DGRAM
, 0)))
2088 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET");
2089 if (-1 == (s6
= socket(AF_INET6
, SOCK_DGRAM
, 0)))
2090 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET6");
2092 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) == -1) {
2093 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2095 ifr
.ifr_flags
|= IFF_UP
;
2097 if (ioctl(s
, SIOCSIFFLAGS
, &ifr
) == -1)
2098 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2101 memset(&ifr
, 0, sizeof(ifr
));
2102 strcpy(ifr
.ifr_name
, "lo0");
2104 if (ioctl(s6
, SIOCGIFFLAGS
, &ifr
) == -1) {
2105 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2107 ifr
.ifr_flags
|= IFF_UP
;
2109 if (ioctl(s6
, SIOCSIFFLAGS
, &ifr
) == -1)
2110 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2113 memset(&ifra
, 0, sizeof(ifra
));
2114 strcpy(ifra
.ifra_name
, "lo0");
2116 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_family
= AF_INET
;
2117 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
2118 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_len
= sizeof(struct sockaddr_in
);
2119 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_family
= AF_INET
;
2120 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
.s_addr
= htonl(IN_CLASSA_NET
);
2121 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_len
= sizeof(struct sockaddr_in
);
2123 if (ioctl(s
, SIOCAIFADDR
, &ifra
) == -1)
2124 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv4): %m");
2126 memset(&ifra6
, 0, sizeof(ifra6
));
2127 strcpy(ifra6
.ifra_name
, "lo0");
2129 ifra6
.ifra_addr
.sin6_family
= AF_INET6
;
2130 ifra6
.ifra_addr
.sin6_addr
= in6addr_loopback
;
2131 ifra6
.ifra_addr
.sin6_len
= sizeof(struct sockaddr_in6
);
2132 ifra6
.ifra_prefixmask
.sin6_family
= AF_INET6
;
2133 memset(&ifra6
.ifra_prefixmask
.sin6_addr
, 0xff, sizeof(struct in6_addr
));
2134 ifra6
.ifra_prefixmask
.sin6_len
= sizeof(struct sockaddr_in6
);
2135 ifra6
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
2136 ifra6
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
2138 if (ioctl(s6
, SIOCAIFADDR_IN6
, &ifra6
) == -1)
2139 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv6): %m");
2145 static void workaround3048875(int argc
, char *argv
[])
2148 char **ap
, *newargv
[100], *p
= argv
[1];
2150 if (argc
== 1 || argc
> 2)
2153 newargv
[0] = argv
[0];
2154 for (ap
= newargv
+ 1, i
= 1; ap
< &newargv
[100]; ap
++, i
++) {
2155 if ((*ap
= strsep(&p
, " \t")) == NULL
)
2166 execv(newargv
[0], newargv
);
2169 static launch_data_t
adjust_rlimits(launch_data_t in
)
2171 static struct rlimit
*l
= NULL
;
2172 static size_t lsz
= sizeof(struct rlimit
) * RLIM_NLIMITS
;
2173 struct rlimit
*ltmp
;
2178 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
2179 if (getrlimit(i
, l
+ i
) == -1)
2180 syslog(LOG_WARNING
, "getrlimit(): %m");
2185 ltmp
= launch_data_get_opaque(in
);
2186 ltmpsz
= launch_data_get_opaque_size(in
);
2189 syslog(LOG_WARNING
, "Too much rlimit data sent!");
2193 for (i
= 0; i
< (ltmpsz
/ sizeof(struct rlimit
)); i
++) {
2194 if (ltmp
[i
].rlim_cur
== l
[i
].rlim_cur
&& ltmp
[i
].rlim_max
== l
[i
].rlim_max
)
2197 if (readcfg_pid
&& getpid() == 1) {
2198 int gmib
[] = { CTL_KERN
, KERN_MAXPROC
};
2199 int pmib
[] = { CTL_KERN
, KERN_MAXPROCPERUID
};
2200 const char *gstr
= "kern.maxproc";
2201 const char *pstr
= "kern.maxprocperuid";
2202 int gval
= ltmp
[i
].rlim_max
;
2203 int pval
= ltmp
[i
].rlim_cur
;
2206 gmib
[1] = KERN_MAXFILES
;
2207 pmib
[1] = KERN_MAXFILESPERPROC
;
2208 gstr
= "kern.maxfiles";
2209 pstr
= "kern.maxfilesperproc";
2212 /* kernel will not clamp to this value, we must */
2213 if (gval
> (2048 + 20))
2219 if (sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)) == -1)
2220 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", gstr
);
2221 if (sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)) == -1)
2222 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", pstr
);
2224 if (setrlimit(i
, ltmp
+ i
) == -1)
2225 syslog(LOG_WARNING
, "setrlimit(): %m");
2226 /* the kernel may have clamped the values we gave it */
2227 if (getrlimit(i
, l
+ i
) == -1)
2228 syslog(LOG_WARNING
, "getrlimit(): %m");
2232 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);
2235 __private_extern__
void launchd_SessionCreate(const char *who
)
2237 void *seclib
= dlopen(SECURITY_LIB
, RTLD_LAZY
);
2238 OSStatus (*sescr
)(SessionCreationFlags flags
, SessionAttributeBits attributes
);
2241 sescr
= dlsym(seclib
, "SessionCreate");
2244 OSStatus scr
= sescr(0, 0);
2246 syslog(LOG_WARNING
, "%s: SessionCreate() failed: %d", who
, scr
);
2248 syslog(LOG_WARNING
, "%s: couldn't find SessionCreate() in %s", who
, SECURITY_LIB
);
2253 syslog(LOG_WARNING
, "%s: dlopen(\"%s\",...): %s", who
, SECURITY_LIB
, dlerror());
2257 static int dir_has_files(const char *path
)
2259 DIR *dd
= opendir(path
);
2266 while ((de
= readdir(dd
))) {
2267 if (strcmp(de
->d_name
, ".") && strcmp(de
->d_name
, "..")) {
2277 static void job_set_alarm(struct jobcb
*j
)
2279 struct tm otherlatertm
, latertm
, *nowtm
;
2280 time_t later
, otherlater
= 0, now
= time(NULL
);
2282 nowtm
= localtime(&now
);
2287 latertm
.tm_isdst
= -1;
2290 if (-1 != j
->start_cal_interval
->tm_min
)
2291 latertm
.tm_min
= j
->start_cal_interval
->tm_min
;
2292 if (-1 != j
->start_cal_interval
->tm_hour
)
2293 latertm
.tm_hour
= j
->start_cal_interval
->tm_hour
;
2295 otherlatertm
= latertm
;
2297 if (-1 != j
->start_cal_interval
->tm_mday
)
2298 latertm
.tm_mday
= j
->start_cal_interval
->tm_mday
;
2299 if (-1 != j
->start_cal_interval
->tm_mon
)
2300 latertm
.tm_mon
= j
->start_cal_interval
->tm_mon
;
2302 /* cron semantics are fun */
2303 if (-1 != j
->start_cal_interval
->tm_wday
) {
2304 int delta
, realwday
= j
->start_cal_interval
->tm_wday
;
2309 delta
= realwday
- nowtm
->tm_wday
;
2311 /* Now Later Delta Desired
2318 otherlatertm
.tm_mday
+= delta
;
2320 otherlatertm
.tm_mday
+= 7 + delta
;
2321 else if (-1 != j
->start_cal_interval
->tm_hour
&& otherlatertm
.tm_hour
<= nowtm
->tm_hour
)
2322 otherlatertm
.tm_mday
+= 7;
2323 else if (-1 != j
->start_cal_interval
->tm_min
&& otherlatertm
.tm_min
<= nowtm
->tm_min
)
2324 otherlatertm
.tm_hour
++;
2326 otherlatertm
.tm_min
++;
2328 otherlater
= mktime(&otherlatertm
);
2331 if (-1 != j
->start_cal_interval
->tm_mon
&& latertm
.tm_mon
<= nowtm
->tm_mon
) {
2333 } else if (-1 != j
->start_cal_interval
->tm_mday
&& latertm
.tm_mday
<= nowtm
->tm_mday
) {
2335 } else if (-1 != j
->start_cal_interval
->tm_hour
&& latertm
.tm_hour
<= nowtm
->tm_hour
) {
2337 } else if (-1 != j
->start_cal_interval
->tm_min
&& latertm
.tm_min
<= nowtm
->tm_min
) {
2343 later
= mktime(&latertm
);
2346 if (-1 != j
->start_cal_interval
->tm_mday
)
2347 later
= later
< otherlater
? later
: otherlater
;
2352 if (-1 == kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_ABSOLUTE
|NOTE_SECONDS
, later
, &j
->kqjob_callback
))
2353 job_log_error(j
, LOG_ERR
, "adding kevent alarm");
2356 static void job_log_error(struct jobcb
*j
, int pri
, const char *msg
, ...)
2358 size_t newmsg_sz
= strlen(msg
) + strlen(j
->label
) + 200;
2359 char *newmsg
= alloca(newmsg_sz
);
2362 sprintf(newmsg
, "%s: %s: %s", j
->label
, msg
, strerror(errno
));
2366 vsyslog(pri
, newmsg
, ap
);
2371 static void job_log(struct jobcb
*j
, int pri
, const char *msg
, ...)
2373 size_t newmsg_sz
= strlen(msg
) + sizeof(": ") + strlen(j
->label
);
2374 char *newmsg
= alloca(newmsg_sz
);
2377 sprintf(newmsg
, "%s: %s", j
->label
, msg
);
2381 vsyslog(pri
, newmsg
, ap
);
2386 static void async_callback(void)
2388 struct timespec timeout
= { 0, 0 };
2391 switch (kevent(asynckq
, NULL
, 0, &kev
, 1, &timeout
)) {
2393 syslog(LOG_DEBUG
, "kevent(): %m");
2396 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
2400 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");