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 testfd_or_openfd(int fd
, const char *path
, int flags
);
175 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
);
176 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
);
179 static size_t total_children
= 0;
180 static pid_t readcfg_pid
= 0;
181 static pid_t launchd_proper_pid
= 0;
182 static bool launchd_inited
= false;
183 static bool shutdown_in_progress
= false;
184 static pthread_t mach_server_loop_thread
;
185 mach_port_t launchd_bootstrap_port
= MACH_PORT_NULL
;
186 sigset_t blocked_signals
= 0;
187 static char *pending_stdout
= NULL
;
188 static char *pending_stderr
= NULL
;
190 int main(int argc
, char *argv
[])
192 static const int sigigns
[] = { SIGHUP
, SIGINT
, SIGPIPE
, SIGALRM
,
193 SIGTERM
, SIGURG
, SIGTSTP
, SIGTSTP
, SIGCONT
, /*SIGCHLD,*/
194 SIGTTIN
, SIGTTOU
, SIGIO
, SIGXCPU
, SIGXFSZ
, SIGVTALRM
, SIGPROF
,
195 SIGWINCH
, SIGINFO
, SIGUSR1
, SIGUSR2
};
198 bool sflag
= false, xflag
= false, vflag
= false, dflag
= false;
202 workaround3048875(argc
, argv
);
207 testfd_or_openfd(STDIN_FILENO
, _PATH_DEVNULL
, O_RDONLY
);
208 testfd_or_openfd(STDOUT_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
209 testfd_or_openfd(STDERR_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
211 openlog(getprogname(), LOG_CONS
|(getpid() != 1 ? LOG_PID
|LOG_PERROR
: 0), LOG_LAUNCHD
);
212 setlogmask(LOG_UPTO(LOG_NOTICE
));
214 while ((ch
= getopt(argc
, argv
, "dhsvx")) != -1) {
216 case 'd': dflag
= true; break;
217 case 's': sflag
= true; break;
218 case 'x': xflag
= true; break;
219 case 'v': vflag
= true; break;
220 case 'h': usage(stdout
); break;
222 syslog(LOG_WARNING
, "ignoring unknown arguments");
230 if (dflag
&& daemon(0, 0) == -1)
231 syslog(LOG_WARNING
, "couldn't daemonize: %m");
233 if ((mainkq
= kqueue()) == -1) {
234 syslog(LOG_EMERG
, "kqueue(): %m");
238 if ((asynckq
= kqueue()) == -1) {
239 syslog(LOG_ERR
, "kqueue(): %m");
243 if (kevent_mod(asynckq
, EVFILT_READ
, EV_ADD
, 0, 0, &kqasync_callback
) == -1) {
244 syslog(LOG_ERR
, "kevent_mod(asynckq, EVFILT_READ): %m");
248 sigemptyset(&blocked_signals
);
250 for (i
= 0; i
< (sizeof(sigigns
) / sizeof(int)); i
++) {
251 if (kevent_mod(sigigns
[i
], EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
252 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", sigigns
[i
]);
253 sigaddset(&blocked_signals
, sigigns
[i
]);
254 signal(sigigns
[i
], SIG_IGN
);
257 /* sigh... ignoring SIGCHLD has side effects: we can't call wait*() */
258 if (kevent_mod(SIGCHLD
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
259 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", SIGCHLD
);
262 pid1_magic_init(sflag
, vflag
, xflag
);
264 launchd_bootstrap_port
= bootstrap_port
;
265 launchd_server_init(argv
[0] ? true : false);
268 /* do this after pid1_magic_init() to not catch ourselves mounting stuff */
269 if (kevent_mod(0, EVFILT_FS
, EV_ADD
, 0, 0, &kqfs_callback
) == -1)
270 syslog(LOG_ERR
, "kevent_mod(EVFILT_FS, &kqfs_callback): %m");
274 conceive_firstborn(argv
);
276 reload_launchd_config();
279 job_start(TAILQ_FIRST(&jobs
));
282 static struct timespec timeout
= { 30, 0 };
283 struct timespec
*timeoutp
= NULL
;
286 if (readcfg_pid
== 0)
289 if (TAILQ_EMPTY(&jobs
)) {
290 /* launched on demand */
292 } else if (shutdown_in_progress
&& total_children
== 0) {
297 switch (kevent(mainkq
, NULL
, 0, &kev
, 1, timeoutp
)) {
299 syslog(LOG_DEBUG
, "kevent(): %m");
302 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
308 syslog(LOG_DEBUG
, "kevent(): spurious return with infinite timeout");
311 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
317 static void pid1_magic_init(bool sflag
, bool vflag
, bool xflag
)
320 int memmib
[2] = { CTL_HW
, HW_PHYSMEM
};
321 int mvnmib
[2] = { CTL_KERN
, KERN_MAXVNODES
};
322 int hnmib
[2] = { CTL_KERN
, KERN_HOSTNAME
};
323 int tfp_r_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_READ_GROUP
};
324 int tfp_rw_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_RW_GROUP
};
326 gid_t tfp_rw_gid
= 0;
327 struct group
*tfp_gr
;
330 size_t memsz
= sizeof(mem
);
333 if ((tfp_gr
= getgrnam("procview"))) {
334 tfp_r_gid
= tfp_gr
->gr_gid
;
335 sysctl(tfp_r_mib
, 3, NULL
, NULL
, &tfp_r_gid
, sizeof(tfp_r_gid
));
338 if ((tfp_gr
= getgrnam("procmod"))) {
339 tfp_rw_gid
= tfp_gr
->gr_gid
;
340 sysctl(tfp_rw_mib
, 3, NULL
, NULL
, &tfp_rw_gid
, sizeof(tfp_rw_gid
));
343 setpriority(PRIO_PROCESS
, 0, -1);
346 syslog(LOG_ERR
, "setsid(): %m");
348 if (chdir("/") == -1)
349 syslog(LOG_ERR
, "chdir(\"/\"): %m");
351 if (sysctl(memmib
, 2, &mem
, &memsz
, NULL
, 0) == -1) {
352 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "hw.physmem");
354 /* The following assignment of mem to itself if the size
355 * of data returned is 32 bits instead of 64 is a clever
356 * C trick to move the 32 bits on big endian systems to
357 * the least significant bytes of the 64 mem variable.
359 * On little endian systems, this is effectively a no-op.
362 mem
= *(uint32_t *)&mem
;
363 mvn
= mem
/ (64 * 1024) + 1024;
364 if (sysctl(mvnmib
, 2, NULL
, NULL
, &mvn
, sizeof(mvn
)) == -1)
365 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.maxvnodes");
367 if (sysctl(hnmib
, 2, NULL
, NULL
, "localhost", sizeof("localhost")) == -1)
368 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.hostname");
370 if (setlogin("root") == -1)
371 syslog(LOG_ERR
, "setlogin(\"root\"): %m");
375 if (mount("fdesc", "/dev", MNT_UNION
, NULL
) == -1)
376 syslog(LOG_ERR
, "mount(\"%s\", \"%s\", ...): %m", "fdesc", "/dev/");
378 setenv("PATH", _PATH_STDPATH
, 1);
380 launchd_bootstrap_port
= mach_init_init();
381 task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port
);
382 bootstrap_port
= MACH_PORT_NULL
;
384 pthread_attr_init(&attr
);
385 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
387 pthr_r
= pthread_create(&mach_server_loop_thread
, &attr
, mach_server_loop
, NULL
);
389 syslog(LOG_ERR
, "pthread_create(mach_server_loop): %s", strerror(pthr_r
));
393 pthread_attr_destroy(&attr
);
395 init_boot(sflag
, vflag
, xflag
);
399 #ifdef PID1_REAP_ADOPTED_CHILDREN
400 static bool launchd_check_pid(pid_t p
)
405 TAILQ_FOREACH(j
, &jobs
, tqe
) {
407 EV_SET(&kev
, p
, EVFILT_PROC
, 0, 0, 0, j
);
408 j
->kqjob_callback(j
, &kev
);
413 if (p
== readcfg_pid
) {
414 readcfg_callback(NULL
, NULL
);
422 static char *sockdir
= NULL
;
423 static char *sockpath
= NULL
;
425 static void launchd_clean_up(void)
427 if (launchd_proper_pid
!= getpid())
433 if (-1 == unlink(sockpath
))
434 syslog(LOG_WARNING
, "unlink(\"%s\"): %m", sockpath
);
435 else if (-1 == rmdir(sockdir
))
436 syslog(LOG_WARNING
, "rmdir(\"%s\"): %m", sockdir
);
442 static void launchd_server_init(bool create_session
)
444 struct sockaddr_un sun
;
446 int r
, fd
= -1, ourdirfd
= -1;
449 memset(&sun
, 0, sizeof(sun
));
450 sun
.sun_family
= AF_UNIX
;
452 if (create_session
) {
453 snprintf(ourdir
, sizeof(ourdir
), "%s/%u.%u", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
454 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u.%u/sock", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
455 setenv(LAUNCHD_SOCKET_ENV
, sun
.sun_path
, 1);
457 snprintf(ourdir
, sizeof(ourdir
), "%s/%u", LAUNCHD_SOCK_PREFIX
, getuid());
458 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u/sock", LAUNCHD_SOCK_PREFIX
, getuid());
464 if (mkdir(LAUNCHD_SOCK_PREFIX
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) == -1) {
465 if (errno
== EROFS
) {
467 } else if (errno
== EEXIST
) {
469 stat(LAUNCHD_SOCK_PREFIX
, &sb
);
470 if (!S_ISDIR(sb
.st_mode
)) {
472 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
476 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
482 if (mkdir(ourdir
, S_IRWXU
) == -1) {
483 if (errno
== EROFS
) {
485 } else if (errno
== EEXIST
) {
488 if (!S_ISDIR(sb
.st_mode
)) {
490 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
494 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", ourdir
);
499 if (chown(ourdir
, getuid(), getgid()) == -1)
500 syslog(LOG_WARNING
, "chown(\"%s\"): %m", ourdir
);
505 ourdirfd
= _fd(open(ourdir
, O_RDONLY
));
506 if (ourdirfd
== -1) {
507 syslog(LOG_ERR
, "open(\"%s\"): %m", ourdir
);
511 if (flock(ourdirfd
, LOCK_EX
|LOCK_NB
) == -1) {
512 if (errno
== EWOULDBLOCK
) {
515 syslog(LOG_ERR
, "flock(\"%s\"): %m", ourdir
);
520 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
522 syslog(LOG_ERR
, "unlink(\"thesocket\"): %m");
525 if ((fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) == -1) {
526 syslog(LOG_ERR
, "socket(\"thesocket\"): %m");
529 oldmask
= umask(077);
530 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
534 syslog(LOG_ERR
, "bind(\"thesocket\"): %m");
538 if (listen(fd
, SOMAXCONN
) == -1) {
539 syslog(LOG_ERR
, "listen(\"thesocket\"): %m");
543 if (kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqlisten_callback
) == -1) {
544 syslog(LOG_ERR
, "kevent_mod(\"thesocket\", EVFILT_READ): %m");
548 launchd_inited
= true;
550 sockdir
= strdup(ourdir
);
551 sockpath
= strdup(sun
.sun_path
);
553 launchd_proper_pid
= getpid();
554 atexit(launchd_clean_up
);
560 if (!launchd_inited
) {
568 static long long job_get_integer(launch_data_t j
, const char *key
)
570 launch_data_t t
= launch_data_dict_lookup(j
, key
);
572 return launch_data_get_integer(t
);
577 static const char *job_get_string(launch_data_t j
, const char *key
)
579 launch_data_t t
= launch_data_dict_lookup(j
, key
);
581 return launch_data_get_string(t
);
586 static const char *job_get_file2exec(launch_data_t j
)
588 launch_data_t tmpi
, tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAM
);
591 return launch_data_get_string(tmp
);
593 tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
595 tmpi
= launch_data_array_get_index(tmp
, 0);
597 return launch_data_get_string(tmpi
);
603 static bool job_get_bool(launch_data_t j
, const char *key
)
605 launch_data_t t
= launch_data_dict_lookup(j
, key
);
607 return launch_data_get_bool(t
);
612 static void ipc_open(int fd
, struct jobcb
*j
)
614 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
616 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
618 c
->kqconn_callback
= ipc_callback
;
619 c
->conn
= launchd_fdopen(fd
);
621 TAILQ_INSERT_TAIL(&connections
, c
, tqe
);
622 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
);
625 static void simple_zombie_reaper(void *obj
__attribute__((unused
)), struct kevent
*kev
)
627 waitpid(kev
->ident
, NULL
, 0);
630 static void listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
632 struct sockaddr_un sun
;
633 socklen_t sl
= sizeof(sun
);
636 if ((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) == -1) {
643 static void ipc_callback(void *obj
, struct kevent
*kev
)
645 struct conncb
*c
= obj
;
648 if (kev
->filter
== EVFILT_READ
) {
649 if (launchd_msg_recv(c
->conn
, ipc_readmsg
, c
) == -1 && errno
!= EAGAIN
) {
650 if (errno
!= ECONNRESET
)
651 syslog(LOG_DEBUG
, "%s(): recv: %m", __func__
);
654 } else if (kev
->filter
== EVFILT_WRITE
) {
655 r
= launchd_msg_send(c
->conn
, NULL
);
657 if (errno
!= EAGAIN
) {
658 syslog(LOG_DEBUG
, "%s(): send: %m", __func__
);
662 kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
);
665 syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
670 static void set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
672 setenv(key
, launch_data_get_string(obj
), 1);
675 static void launch_data_close_fds(launch_data_t o
)
679 switch (launch_data_get_type(o
)) {
680 case LAUNCH_DATA_DICTIONARY
:
681 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_close_fds
, NULL
);
683 case LAUNCH_DATA_ARRAY
:
684 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
685 launch_data_close_fds(launch_data_array_get_index(o
, i
));
688 if (launch_data_get_fd(o
) != -1)
689 close(launch_data_get_fd(o
));
696 static void launch_data_revoke_fds(launch_data_t o
)
700 switch (launch_data_get_type(o
)) {
701 case LAUNCH_DATA_DICTIONARY
:
702 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_revoke_fds
, NULL
);
704 case LAUNCH_DATA_ARRAY
:
705 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
706 launch_data_revoke_fds(launch_data_array_get_index(o
, i
));
709 launch_data_set_fd(o
, -1);
716 static void job_ignore_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
718 struct jobcb
*j
= cookie
;
722 switch (launch_data_get_type(o
)) {
723 case LAUNCH_DATA_DICTIONARY
:
724 launch_data_dict_iterate(o
, job_ignore_fds
, cookie
);
726 case LAUNCH_DATA_ARRAY
:
727 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
728 job_ignore_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
731 fd
= launch_data_get_fd(o
);
733 job_log(j
, LOG_DEBUG
, "Ignoring FD: %d", fd
);
734 kevent_mod(fd
, EVFILT_READ
, EV_DELETE
, 0, 0, NULL
);
742 static void job_ignore(struct jobcb
*j
)
744 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
748 job_ignore_fds(j_sockets
, NULL
, j
);
750 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
751 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
753 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
754 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
758 static void job_watch_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
760 struct jobcb
*j
= cookie
;
764 switch (launch_data_get_type(o
)) {
765 case LAUNCH_DATA_DICTIONARY
:
766 launch_data_dict_iterate(o
, job_watch_fds
, cookie
);
768 case LAUNCH_DATA_ARRAY
:
769 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
770 job_watch_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
773 fd
= launch_data_get_fd(o
);
775 job_log(j
, LOG_DEBUG
, "Watching FD: %d", fd
);
776 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, cookie
);
784 static void job_watch(struct jobcb
*j
)
786 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
787 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
788 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
792 job_watch_fds(j_sockets
, NULL
, &j
->kqjob_callback
);
794 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
795 if (-1 == j
->vnodes
[i
]) {
796 launch_data_t ld_idx
= launch_data_array_get_index(ld_vnodes
, i
);
797 const char *thepath
= launch_data_get_string(ld_idx
);
799 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
800 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
802 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
803 NOTE_WRITE
|NOTE_EXTEND
|NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
|NOTE_ATTRIB
|NOTE_LINK
,
804 0, &j
->kqjob_callback
);
807 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
808 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
809 NOTE_WRITE
|NOTE_EXTEND
|NOTE_ATTRIB
|NOTE_LINK
, 0, &j
->kqjob_callback
);
812 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
813 launch_data_t ld_idx
= launch_data_array_get_index(ld_qdirs
, i
);
814 const char *thepath
= launch_data_get_string(ld_idx
);
817 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
818 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
819 } else if (dcc_r
> 0 && !shutdown_in_progress
) {
826 static void job_stop(struct jobcb
*j
)
832 static void job_remove(struct jobcb
*j
)
837 job_log(j
, LOG_DEBUG
, "Removed");
839 TAILQ_REMOVE(&jobs
, j
, tqe
);
841 if (kevent_mod(j
->p
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqsimple_zombie_reaper
) == -1) {
847 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
848 launch_data_dict_iterate(tmp
, unsetup_job_env
, NULL
);
849 launch_data_close_fds(j
->ldj
);
850 launch_data_free(j
->ldj
);
853 for (i
= 0; i
< j
->vnodes_cnt
; i
++)
854 if (-1 != j
->vnodes
[i
])
858 for (i
= 0; i
< j
->qdirs_cnt
; i
++)
859 if (-1 != j
->qdirs
[i
])
863 if (j
->start_interval
)
864 kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
865 if (j
->start_cal_interval
) {
866 kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
867 free(j
->start_cal_interval
);
869 kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
873 struct readmsg_context
{
878 static void ipc_readmsg(launch_data_t msg
, void *context
)
880 struct readmsg_context rmc
= { context
, NULL
};
882 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(msg
)) {
883 launch_data_dict_iterate(msg
, ipc_readmsg2
, &rmc
);
884 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(msg
)) {
885 ipc_readmsg2(NULL
, launch_data_get_string(msg
), &rmc
);
887 rmc
.resp
= launch_data_new_errno(EINVAL
);
890 if (NULL
== rmc
.resp
)
891 rmc
.resp
= launch_data_new_errno(ENOSYS
);
893 launch_data_close_fds(msg
);
895 if (launchd_msg_send(rmc
.c
->conn
, rmc
.resp
) == -1) {
896 if (errno
== EAGAIN
) {
897 kevent_mod(launchd_getfd(rmc
.c
->conn
), EVFILT_WRITE
, EV_ADD
, 0, 0, &rmc
.c
->kqconn_callback
);
899 syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %m");
903 launch_data_free(rmc
.resp
);
907 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
909 struct readmsg_context
*rmc
= context
;
910 launch_data_t resp
= NULL
;
916 if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
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_STOPJOB
)) {
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_REMOVEJOB
)) {
935 TAILQ_FOREACH(j
, &jobs
, tqe
) {
936 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
938 resp
= launch_data_new_errno(0);
942 resp
= launch_data_new_errno(ESRCH
);
943 } else if (!strcmp(cmd
, LAUNCH_KEY_SUBMITJOB
)) {
944 if (launch_data_get_type(data
) == LAUNCH_DATA_ARRAY
) {
948 resp
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
949 for (i
= 0; i
< launch_data_array_get_count(data
); i
++) {
950 tmp
= load_job(launch_data_array_get_index(data
, i
));
951 launch_data_array_set_index(resp
, tmp
, i
);
954 resp
= load_job(data
);
956 } else if (!strcmp(cmd
, LAUNCH_KEY_UNSETUSERENVIRONMENT
)) {
957 unsetenv(launch_data_get_string(data
));
958 resp
= launch_data_new_errno(0);
959 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUSERENVIRONMENT
)) {
960 char **tmpenviron
= environ
;
961 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
962 for (; *tmpenviron
; tmpenviron
++) {
964 launch_data_t s
= launch_data_alloc(LAUNCH_DATA_STRING
);
965 launch_data_set_string(s
, strchr(*tmpenviron
, '=') + 1);
966 strncpy(envkey
, *tmpenviron
, sizeof(envkey
));
967 *(strchr(envkey
, '=')) = '\0';
968 launch_data_dict_insert(resp
, s
, envkey
);
970 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUSERENVIRONMENT
)) {
971 launch_data_dict_iterate(data
, set_user_env
, NULL
);
972 resp
= launch_data_new_errno(0);
973 } else if (!strcmp(cmd
, LAUNCH_KEY_CHECKIN
)) {
975 resp
= launch_data_copy(rmc
->c
->j
->ldj
);
976 if (NULL
== launch_data_dict_lookup(resp
, LAUNCH_JOBKEY_TIMEOUT
)) {
977 launch_data_t to
= launch_data_new_integer(LAUNCHD_MIN_JOB_RUN_TIME
);
978 launch_data_dict_insert(resp
, to
, LAUNCH_JOBKEY_TIMEOUT
);
980 rmc
->c
->j
->checkedin
= true;
982 resp
= launch_data_new_errno(EACCES
);
984 } else if (!strcmp(cmd
, LAUNCH_KEY_RELOADTTYS
)) {
986 resp
= launch_data_new_errno(0);
987 } else if (!strcmp(cmd
, LAUNCH_KEY_SHUTDOWN
)) {
989 resp
= launch_data_new_errno(0);
990 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBS
)) {
991 resp
= get_jobs(NULL
);
992 launch_data_revoke_fds(resp
);
993 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRESOURCELIMITS
)) {
994 resp
= adjust_rlimits(NULL
);
995 } else if (!strcmp(cmd
, LAUNCH_KEY_SETRESOURCELIMITS
)) {
996 resp
= adjust_rlimits(data
);
997 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOB
)) {
998 resp
= get_jobs(launch_data_get_string(data
));
999 launch_data_revoke_fds(resp
);
1000 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBWITHHANDLES
)) {
1001 resp
= get_jobs(launch_data_get_string(data
));
1002 } else if (!strcmp(cmd
, LAUNCH_KEY_SETLOGMASK
)) {
1003 resp
= launch_data_new_integer(setlogmask(launch_data_get_integer(data
)));
1004 } else if (!strcmp(cmd
, LAUNCH_KEY_GETLOGMASK
)) {
1005 int oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1006 resp
= launch_data_new_integer(oldmask
);
1007 setlogmask(oldmask
);
1008 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUMASK
)) {
1009 resp
= launch_data_new_integer(umask(launch_data_get_integer(data
)));
1010 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUMASK
)) {
1011 mode_t oldmask
= umask(0);
1012 resp
= launch_data_new_integer(oldmask
);
1014 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGESELF
)) {
1015 struct rusage rusage
;
1016 getrusage(RUSAGE_SELF
, &rusage
);
1017 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1018 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGECHILDREN
)) {
1019 struct rusage rusage
;
1020 getrusage(RUSAGE_CHILDREN
, &rusage
);
1021 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1022 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDOUT
)) {
1023 resp
= setstdio(STDOUT_FILENO
, data
);
1024 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDERR
)) {
1025 resp
= setstdio(STDERR_FILENO
, data
);
1026 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHCONTROL
)) {
1027 batch_job_enable(launch_data_get_bool(data
), rmc
->c
);
1028 resp
= launch_data_new_errno(0);
1029 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHQUERY
)) {
1030 resp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1031 launch_data_set_bool(resp
, batch_disabler_count
== 0);
1037 static launch_data_t
setstdio(int d
, launch_data_t o
)
1039 launch_data_t resp
= launch_data_new_errno(0);
1041 if (launch_data_get_type(o
) == LAUNCH_DATA_STRING
) {
1042 char **where
= &pending_stderr
;
1043 if (d
== STDOUT_FILENO
)
1044 where
= &pending_stdout
;
1047 *where
= strdup(launch_data_get_string(o
));
1048 } else if (launch_data_get_type(o
) == LAUNCH_DATA_FD
) {
1049 dup2(launch_data_get_fd(o
), d
);
1051 launch_data_set_errno(resp
, EINVAL
);
1057 static void batch_job_enable(bool e
, struct conncb
*c
)
1059 if (e
&& c
->disabled_batch
) {
1060 batch_disabler_count
--;
1061 c
->disabled_batch
= 0;
1062 if (batch_disabler_count
== 0)
1063 kevent_mod(asynckq
, EVFILT_READ
, EV_ENABLE
, 0, 0, &kqasync_callback
);
1064 } else if (!e
&& !c
->disabled_batch
) {
1065 if (batch_disabler_count
== 0)
1066 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1067 batch_disabler_count
++;
1068 c
->disabled_batch
= 1;
1072 static launch_data_t
load_job(launch_data_t pload
)
1074 launch_data_t tmp
, resp
;
1077 bool startnow
, hasprog
= false, hasprogargs
= false;
1079 if ((label
= job_get_string(pload
, LAUNCH_JOBKEY_LABEL
))) {
1080 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1081 if (!strcmp(j
->label
, label
)) {
1082 resp
= launch_data_new_errno(EEXIST
);
1087 resp
= launch_data_new_errno(EINVAL
);
1091 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAM
))
1093 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
))
1096 if (!hasprog
&& !hasprogargs
) {
1097 resp
= launch_data_new_errno(EINVAL
);
1101 j
= calloc(1, sizeof(struct jobcb
) + strlen(label
) + 1);
1102 strcpy(j
->label
, label
);
1103 j
->ldj
= launch_data_copy(pload
);
1104 launch_data_revoke_fds(pload
);
1105 j
->kqjob_callback
= job_callback
;
1108 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
) == NULL
) {
1109 tmp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1110 launch_data_set_bool(tmp
, true);
1111 launch_data_dict_insert(j
->ldj
, tmp
, LAUNCH_JOBKEY_ONDEMAND
);
1114 TAILQ_INSERT_TAIL(&jobs
, j
, tqe
);
1116 j
->debug
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_DEBUG
);
1118 startnow
= !job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1120 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_RUNATLOAD
))
1123 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
))) {
1126 j
->qdirs_cnt
= launch_data_array_get_count(tmp
);
1127 j
->qdirs
= malloc(sizeof(int) * j
->qdirs_cnt
);
1129 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1130 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1132 if (-1 == (j
->qdirs
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1133 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1138 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTINTERVAL
))) {
1139 j
->start_interval
= launch_data_get_integer(tmp
);
1141 if (j
->start_interval
== 0)
1142 job_log(j
, LOG_WARNING
, "StartInterval is zero, ignoring");
1143 else if (-1 == kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_SECONDS
, j
->start_interval
, &j
->kqjob_callback
))
1144 job_log_error(j
, LOG_ERR
, "adding kevent timer");
1147 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTCALENDARINTERVAL
))) {
1148 launch_data_t tmp_k
;
1150 j
->start_cal_interval
= calloc(1, sizeof(struct tm
));
1151 j
->start_cal_interval
->tm_min
= -1;
1152 j
->start_cal_interval
->tm_hour
= -1;
1153 j
->start_cal_interval
->tm_mday
= -1;
1154 j
->start_cal_interval
->tm_wday
= -1;
1155 j
->start_cal_interval
->tm_mon
= -1;
1157 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(tmp
)) {
1158 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MINUTE
)))
1159 j
->start_cal_interval
->tm_min
= launch_data_get_integer(tmp_k
);
1160 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_HOUR
)))
1161 j
->start_cal_interval
->tm_hour
= launch_data_get_integer(tmp_k
);
1162 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_DAY
)))
1163 j
->start_cal_interval
->tm_mday
= launch_data_get_integer(tmp_k
);
1164 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_WEEKDAY
)))
1165 j
->start_cal_interval
->tm_wday
= launch_data_get_integer(tmp_k
);
1166 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MONTH
)))
1167 j
->start_cal_interval
->tm_mon
= launch_data_get_integer(tmp_k
);
1173 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
))) {
1176 j
->vnodes_cnt
= launch_data_array_get_count(tmp
);
1177 j
->vnodes
= malloc(sizeof(int) * j
->vnodes_cnt
);
1179 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1180 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1182 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1183 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1188 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
1189 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1191 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1197 resp
= launch_data_new_errno(0);
1202 static launch_data_t
get_jobs(const char *which
)
1205 launch_data_t tmp
, resp
= NULL
;
1208 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1209 if (!strcmp(which
, j
->label
))
1210 resp
= launch_data_copy(j
->ldj
);
1213 resp
= launch_data_new_errno(ESRCH
);
1215 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1217 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1218 tmp
= launch_data_copy(j
->ldj
);
1219 launch_data_dict_insert(resp
, tmp
, j
->label
);
1226 static void usage(FILE *where
)
1228 fprintf(where
, "%s: [-d] [-- command [args ...]]\n", getprogname());
1229 fprintf(where
, "\t-d\tdaemonize\n");
1230 fprintf(where
, "\t-h\tthis usage statement\n");
1232 if (where
== stdout
)
1236 #ifdef EVFILT_MACH_IMPLEMENTED
1237 static void **machcbtable
= NULL
;
1238 static size_t machcbtable_cnt
= 0;
1239 static int machcbreadfd
= -1;
1240 static int machcbwritefd
= -1;
1241 static mach_port_t mach_demand_port_set
= MACH_PORT_NULL
;
1242 static pthread_t mach_demand_thread
;
1244 static void mach_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1249 read(machcbreadfd
, &mp
, sizeof(mp
));
1251 EV_SET(&mkev
, mp
, EVFILT_MACHPORT
, 0, 0, 0, machcbtable
[MACH_PORT_INDEX(mp
)]);
1253 (*((kq_callback
*)mkev
.udata
))(mkev
.udata
, &mkev
);
1257 int kevent_mod(uintptr_t ident
, short filter
, u_short flags
, u_int fflags
, intptr_t data
, void *udata
)
1261 #ifdef EVFILT_MACH_IMPLEMENTED
1263 pthread_attr_t attr
;
1264 int pthr_r
, pfds
[2];
1267 if (EVFILT_TIMER
== filter
|| EVFILT_VNODE
== filter
)
1270 if (flags
& EV_ADD
&& NULL
== udata
) {
1271 syslog(LOG_ERR
, "%s(): kev.udata == NULL!!!", __func__
);
1272 syslog(LOG_ERR
, "kev: ident %d filter %d flags 0x%x fflags 0x%x",
1273 ident
, filter
, flags
, fflags
);
1278 #ifdef EVFILT_MACH_IMPLEMENTED
1279 if (filter
!= EVFILT_MACHPORT
) {
1281 #ifdef PID1_REAP_ADOPTED_CHILDREN
1282 if (filter
== EVFILT_PROC
&& getpid() == 1)
1285 EV_SET(&kev
, ident
, filter
, flags
, fflags
, data
, udata
);
1286 return kevent(q
, &kev
, 1, NULL
, 0, NULL
);
1287 #ifdef EVFILT_MACH_IMPLEMENTED
1290 if (machcbtable
== NULL
) {
1291 pthread_attr_init(&attr
);
1292 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1294 pthr_r
= pthread_create(&mach_demand_thread
, &attr
, mach_demand_loop
, NULL
);
1296 syslog(LOG_ERR
, "pthread_create(mach_demand_loop): %s", strerror(pthr_r
));
1300 pthread_attr_destroy(&attr
);
1302 machcbtable
= malloc(0);
1304 machcbwritefd
= _fd(pfds
[1]);
1305 machcbreadfd
= _fd(pfds
[0]);
1306 kevent_mod(machcbreadfd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqmach_callback
);
1307 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET
, &mach_demand_port_set
);
1308 if (kr
!= KERN_SUCCESS
) {
1309 syslog(LOG_ERR
, "mach_port_allocate(demand_port_set): %s", mach_error_string(kr
));
1314 if (flags
& EV_ADD
) {
1315 kr
= mach_port_move_member(mach_task_self(), ident
, mach_demand_port_set
);
1316 if (kr
!= KERN_SUCCESS
) {
1317 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1321 if (MACH_PORT_INDEX(ident
) > machcbtable_cnt
)
1322 machcbtable
= realloc(machcbtable
, MACH_PORT_INDEX(ident
) * sizeof(void *));
1324 machcbtable
[MACH_PORT_INDEX(ident
)] = udata
;
1325 } else if (flags
& EV_DELETE
) {
1326 kr
= mach_port_move_member(mach_task_self(), ident
, MACH_PORT_NULL
);
1327 if (kr
!= KERN_SUCCESS
) {
1328 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1332 syslog(LOG_DEBUG
, "kevent_mod(EVFILT_MACHPORT) with flags: %d", flags
);
1341 static int _fd(int fd
)
1344 fcntl(fd
, F_SETFD
, 1);
1348 static void ipc_close(struct conncb
*c
)
1350 batch_job_enable(true, c
);
1352 TAILQ_REMOVE(&connections
, c
, tqe
);
1353 launchd_close(c
->conn
);
1357 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1359 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1360 setenv(key
, launch_data_get_string(obj
), 1);
1363 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1365 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1369 static void job_reap(struct jobcb
*j
)
1371 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1372 time_t td
= time(NULL
) - j
->start_time
;
1373 bool bad_exit
= false;
1376 job_log(j
, LOG_DEBUG
, "Reaping");
1383 #ifdef PID1_REAP_ADOPTED_CHILDREN
1385 status
= pid1_child_exit_status
;
1388 if (-1 == waitpid(j
->p
, &status
, 0)) {
1389 job_log_error(j
, LOG_ERR
, "waitpid(%d, ...)", j
->p
);
1393 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
1394 job_log(j
, LOG_WARNING
, "exited with exit code: %d", WEXITSTATUS(status
));
1398 if (WIFSIGNALED(status
)) {
1399 int s
= WTERMSIG(status
);
1400 if (SIGKILL
== s
|| SIGTERM
== s
) {
1401 job_log(j
, LOG_NOTICE
, "exited: %s", strsignal(s
));
1403 job_log(j
, LOG_WARNING
, "exited abnormally: %s", strsignal(s
));
1409 if (td
< LAUNCHD_MIN_JOB_RUN_TIME
) {
1410 job_log(j
, LOG_WARNING
, "respawning too quickly! throttling");
1413 } else if (td
>= LAUNCHD_REWARD_JOB_RUN_TIME
) {
1414 job_log(j
, LOG_INFO
, "lived long enough, forgiving past exit failures");
1415 j
->failed_exits
= 0;
1422 if (j
->failed_exits
> 0) {
1423 int failures_left
= LAUNCHD_FAILED_EXITS_THRESHOLD
- j
->failed_exits
;
1425 job_log(j
, LOG_WARNING
, "%d more failure%s without living at least %d seconds will cause job removal",
1426 failures_left
, failures_left
> 1 ? "s" : "", LAUNCHD_REWARD_JOB_RUN_TIME
);
1433 static bool job_restart_fitness_test(struct jobcb
*j
)
1435 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1438 job_log(j
, LOG_DEBUG
, "first born died, begin shutdown");
1441 } else if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
) && !j
->checkedin
) {
1442 job_log(j
, LOG_WARNING
, "failed to checkin");
1445 } else if (j
->failed_exits
>= LAUNCHD_FAILED_EXITS_THRESHOLD
) {
1446 job_log(j
, LOG_WARNING
, "too many failures in succession");
1449 } else if (od
|| shutdown_in_progress
) {
1450 if (!od
&& shutdown_in_progress
)
1451 job_log(j
, LOG_NOTICE
, "exited while shutdown is in progress, will not restart unless demand requires it");
1459 static void job_callback(void *obj
, struct kevent
*kev
)
1461 struct jobcb
*j
= obj
;
1463 bool startnow
= true;
1467 oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1468 job_log(j
, LOG_DEBUG
, "log level debug temporarily enabled while processing job");
1471 if (kev
->filter
== EVFILT_PROC
) {
1474 startnow
= job_restart_fitness_test(j
);
1476 if (startnow
&& j
->throttle
) {
1477 j
->throttle
= false;
1478 job_log(j
, LOG_WARNING
, "will restart in %d seconds", LAUNCHD_MIN_JOB_RUN_TIME
);
1479 if (-1 == kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_ADD
|EV_ONESHOT
,
1480 NOTE_SECONDS
, LAUNCHD_MIN_JOB_RUN_TIME
, &j
->kqjob_callback
)) {
1481 job_log_error(j
, LOG_WARNING
, "failed to setup timer callback!, starting now!");
1486 } else if (kev
->filter
== EVFILT_TIMER
&& (void *)kev
->ident
== j
->start_cal_interval
) {
1488 } else if (kev
->filter
== EVFILT_VNODE
) {
1490 const char *thepath
= NULL
;
1492 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1493 if (j
->vnodes
[i
] == (int)kev
->ident
) {
1494 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
1496 thepath
= launch_data_get_string(launch_data_array_get_index(ld_vnodes
, i
));
1498 job_log(j
, LOG_DEBUG
, "watch path modified: %s", thepath
);
1500 if ((NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
) & kev
->fflags
) {
1501 job_log(j
, LOG_DEBUG
, "watch path invalidated: %s", thepath
);
1502 close(j
->vnodes
[i
]);
1503 j
->vnodes
[i
] = -1; /* this will get fixed in job_watch() */
1508 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1509 if (j
->qdirs
[i
] == (int)kev
->ident
) {
1510 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
1513 thepath
= launch_data_get_string(launch_data_array_get_index(ld_qdirs
, i
));
1515 job_log(j
, LOG_DEBUG
, "queue directory modified: %s", thepath
);
1517 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
1518 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
1519 } else if (0 == dcc_r
) {
1520 job_log(j
, LOG_DEBUG
, "spurious wake up, directory empty: %s", thepath
);
1525 /* if we get here, then the vnodes either wasn't a qdir, or if it was, it has entries in it */
1526 } else if (kev
->filter
== EVFILT_READ
&& (int)kev
->ident
== j
->execfd
) {
1527 if (kev
->data
> 0) {
1530 read(j
->execfd
, &e
, sizeof(e
));
1532 job_log_error(j
, LOG_ERR
, "execve()");
1547 /* the job might have been removed, must not call job_log() */
1548 syslog(LOG_DEBUG
, "restoring original log mask");
1549 setlogmask(oldmask
);
1553 static void job_start(struct jobcb
*j
)
1561 job_log(j
, LOG_DEBUG
, "Starting");
1564 job_log(j
, LOG_DEBUG
, "already running");
1568 j
->checkedin
= false;
1570 sipc
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
);
1572 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
))
1576 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
1578 socketpair(AF_UNIX
, SOCK_STREAM
, 0, execspair
);
1580 time(&j
->start_time
);
1582 switch (c
= fork_with_bootstrap_port(launchd_bootstrap_port
)) {
1584 job_log_error(j
, LOG_ERR
, "fork() failed, will try again in one second");
1585 close(execspair
[0]);
1586 close(execspair
[1]);
1591 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1595 close(execspair
[0]);
1596 /* wait for our parent to say they've attached a kevent to us */
1597 read(_fd(execspair
[1]), &c
, sizeof(c
));
1599 setpgid(getpid(), getpid());
1600 if (isatty(STDIN_FILENO
)) {
1601 if (tcsetpgrp(STDIN_FILENO
, getpid()) == -1)
1602 job_log_error(j
, LOG_WARNING
, "tcsetpgrp()");
1608 sprintf(nbuf
, "%d", spair
[1]);
1609 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
1611 job_start_child(j
, execspair
[1]);
1614 close(execspair
[1]);
1615 j
->execfd
= _fd(execspair
[0]);
1618 ipc_open(_fd(spair
[0]), j
);
1620 if (kevent_mod(j
->execfd
, EVFILT_READ
, EV_ADD
, 0, 0, &j
->kqjob_callback
) == -1)
1621 job_log_error(j
, LOG_ERR
, "kevent_mod(j->execfd): %m");
1622 if (kevent_mod(c
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &j
->kqjob_callback
) == -1) {
1623 job_log_error(j
, LOG_ERR
, "kevent()");
1628 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1631 /* this unblocks the child and avoids a race
1632 * between the above fork() and the kevent_mod() */
1633 write(j
->execfd
, &c
, sizeof(c
));
1638 static void job_start_child(struct jobcb
*j
, int execfd
)
1640 launch_data_t ldpa
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
1641 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1643 const char **argv
, *file2exec
= "/usr/libexec/launchproxy";
1645 bool hasprog
= false;
1647 job_setup_attributes(j
);
1650 argv_cnt
= launch_data_array_get_count(ldpa
);
1651 argv
= alloca((argv_cnt
+ 2) * sizeof(char *));
1652 for (i
= 0; i
< argv_cnt
; i
++)
1653 argv
[i
+ 1] = launch_data_get_string(launch_data_array_get_index(ldpa
, i
));
1654 argv
[argv_cnt
+ 1] = NULL
;
1656 argv
= alloca(3 * sizeof(char *));
1657 argv
[1] = job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
);
1661 if (job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
))
1665 argv
[0] = file2exec
;
1668 file2exec
= job_get_file2exec(j
->ldj
);
1672 r
= execv(file2exec
, (char *const*)argv
);
1674 r
= execvp(file2exec
, (char *const*)argv
);
1678 write(execfd
, &errno
, sizeof(errno
));
1679 job_log_error(j
, LOG_ERR
, "execv%s(\"%s\", ...)", hasprog
? "" : "p", file2exec
);
1684 static void job_setup_attributes(struct jobcb
*j
)
1686 launch_data_t srl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOFTRESOURCELIMITS
);
1687 launch_data_t hrl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_HARDRESOURCELIMITS
);
1688 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1692 struct group
*gre
= NULL
;
1694 static const struct {
1698 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE
, RLIMIT_CORE
},
1699 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU
, RLIMIT_CPU
},
1700 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA
, RLIMIT_DATA
},
1701 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE
, RLIMIT_FSIZE
},
1702 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK
, RLIMIT_MEMLOCK
},
1703 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE
, RLIMIT_NOFILE
},
1704 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC
, RLIMIT_NPROC
},
1705 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS
, RLIMIT_RSS
},
1706 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK
, RLIMIT_STACK
},
1709 setpriority(PRIO_PROCESS
, 0, job_get_integer(j
->ldj
, LAUNCH_JOBKEY_NICE
));
1712 for (i
= 0; i
< (sizeof(limits
) / sizeof(limits
[0])); i
++) {
1715 if (getrlimit(limits
[i
].val
, &rl
) == -1) {
1716 job_log_error(j
, LOG_WARNING
, "getrlimit()");
1721 rl
.rlim_max
= job_get_integer(hrl
, limits
[i
].key
);
1723 rl
.rlim_cur
= job_get_integer(srl
, limits
[i
].key
);
1725 if (setrlimit(limits
[i
].val
, &rl
) == -1)
1726 job_log_error(j
, LOG_WARNING
, "setrlimit()");
1730 if (!inetcompat
&& job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SESSIONCREATE
))
1731 launchd_SessionCreate(job_get_file2exec(j
->ldj
));
1733 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_LOWPRIORITYIO
)) {
1734 int lowprimib
[] = { CTL_KERN
, KERN_PROC_LOW_PRI_IO
};
1737 if (sysctl(lowprimib
, sizeof(lowprimib
) / sizeof(lowprimib
[0]), NULL
, NULL
, &val
, sizeof(val
)) == -1)
1738 job_log_error(j
, LOG_WARNING
, "sysctl(\"%s\")", "kern.proc_low_pri_io");
1740 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_ROOTDIRECTORY
))) {
1744 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_GROUPNAME
))) {
1745 gre
= getgrnam(tmpstr
);
1747 gre_g
= gre
->gr_gid
;
1748 if (-1 == setgid(gre_g
)) {
1749 job_log_error(j
, LOG_ERR
, "setgid(%d)", gre_g
);
1753 job_log(j
, LOG_ERR
, "getgrnam(\"%s\") failed", tmpstr
);
1757 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_USERNAME
))) {
1758 struct passwd
*pwe
= getpwnam(tmpstr
);
1760 uid_t pwe_u
= pwe
->pw_uid
;
1761 uid_t pwe_g
= pwe
->pw_gid
;
1763 if (pwe
->pw_expire
&& time(NULL
) >= pwe
->pw_expire
) {
1764 job_log(j
, LOG_ERR
, "expired account: %s", tmpstr
);
1767 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INITGROUPS
)) {
1768 if (-1 == initgroups(tmpstr
, gre
? gre_g
: pwe_g
)) {
1769 job_log_error(j
, LOG_ERR
, "initgroups()");
1774 if (-1 == setgid(pwe_g
)) {
1775 job_log_error(j
, LOG_ERR
, "setgid(%d)", pwe_g
);
1779 if (-1 == setuid(pwe_u
)) {
1780 job_log_error(j
, LOG_ERR
, "setuid(%d)", pwe_u
);
1784 job_log(j
, LOG_WARNING
, "getpwnam(\"%s\") failed", tmpstr
);
1788 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_WORKINGDIRECTORY
)))
1790 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_UMASK
))
1791 umask(job_get_integer(j
->ldj
, LAUNCH_JOBKEY_UMASK
));
1792 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDOUTPATH
))) {
1793 int sofd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1795 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1797 dup2(sofd
, STDOUT_FILENO
);
1801 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDERRORPATH
))) {
1802 int sefd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1804 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1806 dup2(sefd
, STDERR_FILENO
);
1810 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
)))
1811 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1816 #ifdef PID1_REAP_ADOPTED_CHILDREN
1817 __private_extern__
int pid1_child_exit_status
= 0;
1818 static void pid1waitpid(void)
1822 while ((p
= waitpid(-1, &pid1_child_exit_status
, WNOHANG
)) > 0) {
1823 if (!launchd_check_pid(p
))
1829 static void do_shutdown(void)
1833 shutdown_in_progress
= true;
1835 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1837 TAILQ_FOREACH(j
, &jobs
, tqe
)
1840 if (getpid() == 1) {
1842 mach_start_shutdown(SIGTERM
);
1846 static void signal_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
1848 switch (kev
->ident
) {
1851 reload_launchd_config();
1856 #ifdef PID1_REAP_ADOPTED_CHILDREN
1858 /* <rdar://problem/3632556> Please automatically reap processes reparented to PID 1 */
1868 static void fs_callback(void)
1870 static bool mounted_volfs
= false;
1873 mounted_volfs
= true;
1875 if (pending_stdout
) {
1876 int fd
= open(pending_stdout
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1878 dup2(fd
, STDOUT_FILENO
);
1880 free(pending_stdout
);
1881 pending_stdout
= NULL
;
1884 if (pending_stderr
) {
1885 int fd
= open(pending_stderr
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1887 dup2(fd
, STDERR_FILENO
);
1889 free(pending_stderr
);
1890 pending_stderr
= NULL
;
1894 if (!mounted_volfs
) {
1895 int r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1897 if (-1 == r
&& errno
== ENOENT
) {
1898 mkdir(VOLFSDIR
, ACCESSPERMS
& ~(S_IWUSR
|S_IWGRP
|S_IWOTH
));
1899 r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1903 syslog(LOG_WARNING
, "mount(\"%s\", \"%s\", ...): %m", "volfs", VOLFSDIR
);
1905 mounted_volfs
= true;
1909 if (!launchd_inited
)
1910 launchd_server_init(false);
1913 static void readcfg_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1917 #ifdef PID1_REAP_ADOPTED_CHILDREN
1919 status
= pid1_child_exit_status
;
1922 if (-1 == waitpid(readcfg_pid
, &status
, 0)) {
1923 syslog(LOG_WARNING
, "waitpid(readcfg_pid, ...): %m");
1929 if (WIFEXITED(status
)) {
1930 if (WEXITSTATUS(status
))
1931 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited with status: %d", WEXITSTATUS(status
));
1932 } else if (WIFSIGNALED(status
)) {
1933 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally: %s", strsignal(WTERMSIG(status
)));
1935 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally");
1939 #ifdef EVFILT_MACH_IMPLEMENTED
1940 static void *mach_demand_loop(void *arg
__attribute__((unused
)))
1942 mach_msg_empty_rcv_t dummy
;
1944 mach_port_name_array_t members
;
1945 mach_msg_type_number_t membersCnt
;
1946 mach_port_status_t status
;
1947 mach_msg_type_number_t statusCnt
;
1953 * Receive indication of message on demand service
1954 * ports without actually receiving the message (we'll
1955 * let the actual server do that.
1957 kr
= mach_msg(&dummy
.header
, MACH_RCV_MSG
|MACH_RCV_LARGE
,
1958 0, 0, mach_demand_port_set
, 0, MACH_PORT_NULL
);
1959 if (kr
!= MACH_RCV_TOO_LARGE
) {
1960 syslog(LOG_WARNING
, "%s(): mach_msg(): %s", __func__
, mach_error_string(kr
));
1965 * Some port(s) now have messages on them, find out
1966 * which ones (there is no indication of which port
1967 * triggered in the MACH_RCV_TOO_LARGE indication).
1969 kr
= mach_port_get_set_status(mach_task_self(),
1970 mach_demand_port_set
, &members
, &membersCnt
);
1971 if (kr
!= KERN_SUCCESS
) {
1972 syslog(LOG_WARNING
, "%s(): mach_port_get_set_status(): %s", __func__
, mach_error_string(kr
));
1976 for (i
= 0; i
< membersCnt
; i
++) {
1977 statusCnt
= MACH_PORT_RECEIVE_STATUS_COUNT
;
1978 kr
= mach_port_get_attributes(mach_task_self(), members
[i
],
1979 MACH_PORT_RECEIVE_STATUS
, (mach_port_info_t
)&status
, &statusCnt
);
1980 if (kr
!= KERN_SUCCESS
) {
1981 syslog(LOG_WARNING
, "%s(): mach_port_get_attributes(): %s", __func__
, mach_error_string(kr
));
1986 * For each port with messages, take it out of the
1987 * demand service portset, and inform the main thread
1988 * that it might have to start the server responsible
1991 if (status
.mps_msgcount
) {
1992 kr
= mach_port_move_member(mach_task_self(), members
[i
], MACH_PORT_NULL
);
1993 if (kr
!= KERN_SUCCESS
) {
1994 syslog(LOG_WARNING
, "%s(): mach_port_move_member(): %s", __func__
, mach_error_string(kr
));
1997 write(machcbwritefd
, &(members
[i
]), sizeof(members
[i
]));
2001 kr
= vm_deallocate(mach_task_self(), (vm_address_t
) members
,
2002 (vm_size_t
) membersCnt
* sizeof(mach_port_name_t
));
2003 if (kr
!= KERN_SUCCESS
) {
2004 syslog(LOG_WARNING
, "%s(): vm_deallocate(): %s", __func__
, mach_error_string(kr
));
2013 static void reload_launchd_config(void)
2016 static char *ldconf
= PID1LAUNCHD_CONF
;
2017 const char *h
= getenv("HOME");
2019 if (h
&& ldconf
== PID1LAUNCHD_CONF
)
2020 asprintf(&ldconf
, "%s/%s", h
, LAUNCHD_CONF
);
2025 if (lstat(ldconf
, &sb
) == 0) {
2027 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
2028 readcfg_pid
= fork_with_bootstrap_port(launchd_bootstrap_port
);
2029 if (readcfg_pid
== 0) {
2032 sprintf(nbuf
, "%d", spair
[1]);
2033 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
2034 int fd
= open(ldconf
, O_RDONLY
);
2036 syslog(LOG_ERR
, "open(\"%s\"): %m", ldconf
);
2039 dup2(fd
, STDIN_FILENO
);
2041 execl(LAUNCHCTL_PATH
, LAUNCHCTL_PATH
, NULL
);
2042 syslog(LOG_ERR
, "execl(\"%s\", ...): %m", LAUNCHCTL_PATH
);
2044 } else if (readcfg_pid
== -1) {
2047 syslog(LOG_ERR
, "fork(): %m");
2051 ipc_open(_fd(spair
[0]), NULL
);
2052 if (kevent_mod(readcfg_pid
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqreadcfg_callback
) == -1)
2053 syslog(LOG_ERR
, "kevent_mod(EVFILT_PROC, &kqreadcfg_callback): %m");
2058 static void conceive_firstborn(char *argv
[])
2060 launch_data_t r
, d
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2061 launch_data_t args
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
2062 launch_data_t l
= launch_data_new_string("com.apple.launchd.firstborn");
2065 for (i
= 0; *argv
; argv
++, i
++)
2066 launch_data_array_set_index(args
, launch_data_new_string(*argv
), i
);
2068 launch_data_dict_insert(d
, args
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
2069 launch_data_dict_insert(d
, l
, LAUNCH_JOBKEY_LABEL
);
2073 launch_data_free(r
);
2074 launch_data_free(d
);
2076 TAILQ_FIRST(&jobs
)->firstborn
= true;
2079 static void loopback_setup(void)
2081 struct ifaliasreq ifra
;
2082 struct in6_aliasreq ifra6
;
2086 memset(&ifr
, 0, sizeof(ifr
));
2087 strcpy(ifr
.ifr_name
, "lo0");
2089 if (-1 == (s
= socket(AF_INET
, SOCK_DGRAM
, 0)))
2090 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET");
2091 if (-1 == (s6
= socket(AF_INET6
, SOCK_DGRAM
, 0)))
2092 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET6");
2094 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) == -1) {
2095 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2097 ifr
.ifr_flags
|= IFF_UP
;
2099 if (ioctl(s
, SIOCSIFFLAGS
, &ifr
) == -1)
2100 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2103 memset(&ifr
, 0, sizeof(ifr
));
2104 strcpy(ifr
.ifr_name
, "lo0");
2106 if (ioctl(s6
, SIOCGIFFLAGS
, &ifr
) == -1) {
2107 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2109 ifr
.ifr_flags
|= IFF_UP
;
2111 if (ioctl(s6
, SIOCSIFFLAGS
, &ifr
) == -1)
2112 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2115 memset(&ifra
, 0, sizeof(ifra
));
2116 strcpy(ifra
.ifra_name
, "lo0");
2118 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_family
= AF_INET
;
2119 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
2120 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_len
= sizeof(struct sockaddr_in
);
2121 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_family
= AF_INET
;
2122 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
.s_addr
= htonl(IN_CLASSA_NET
);
2123 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_len
= sizeof(struct sockaddr_in
);
2125 if (ioctl(s
, SIOCAIFADDR
, &ifra
) == -1)
2126 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv4): %m");
2128 memset(&ifra6
, 0, sizeof(ifra6
));
2129 strcpy(ifra6
.ifra_name
, "lo0");
2131 ifra6
.ifra_addr
.sin6_family
= AF_INET6
;
2132 ifra6
.ifra_addr
.sin6_addr
= in6addr_loopback
;
2133 ifra6
.ifra_addr
.sin6_len
= sizeof(struct sockaddr_in6
);
2134 ifra6
.ifra_prefixmask
.sin6_family
= AF_INET6
;
2135 memset(&ifra6
.ifra_prefixmask
.sin6_addr
, 0xff, sizeof(struct in6_addr
));
2136 ifra6
.ifra_prefixmask
.sin6_len
= sizeof(struct sockaddr_in6
);
2137 ifra6
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
2138 ifra6
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
2140 if (ioctl(s6
, SIOCAIFADDR_IN6
, &ifra6
) == -1)
2141 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv6): %m");
2147 static void workaround3048875(int argc
, char *argv
[])
2150 char **ap
, *newargv
[100], *p
= argv
[1];
2152 if (argc
== 1 || argc
> 2)
2155 newargv
[0] = argv
[0];
2156 for (ap
= newargv
+ 1, i
= 1; ap
< &newargv
[100]; ap
++, i
++) {
2157 if ((*ap
= strsep(&p
, " \t")) == NULL
)
2168 execv(newargv
[0], newargv
);
2171 static launch_data_t
adjust_rlimits(launch_data_t in
)
2173 static struct rlimit
*l
= NULL
;
2174 static size_t lsz
= sizeof(struct rlimit
) * RLIM_NLIMITS
;
2175 struct rlimit
*ltmp
;
2180 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
2181 if (getrlimit(i
, l
+ i
) == -1)
2182 syslog(LOG_WARNING
, "getrlimit(): %m");
2187 ltmp
= launch_data_get_opaque(in
);
2188 ltmpsz
= launch_data_get_opaque_size(in
);
2191 syslog(LOG_WARNING
, "Too much rlimit data sent!");
2195 for (i
= 0; i
< (ltmpsz
/ sizeof(struct rlimit
)); i
++) {
2196 if (ltmp
[i
].rlim_cur
== l
[i
].rlim_cur
&& ltmp
[i
].rlim_max
== l
[i
].rlim_max
)
2199 if (readcfg_pid
&& getpid() == 1) {
2200 int gmib
[] = { CTL_KERN
, KERN_MAXPROC
};
2201 int pmib
[] = { CTL_KERN
, KERN_MAXPROCPERUID
};
2202 const char *gstr
= "kern.maxproc";
2203 const char *pstr
= "kern.maxprocperuid";
2204 int gval
= ltmp
[i
].rlim_max
;
2205 int pval
= ltmp
[i
].rlim_cur
;
2208 gmib
[1] = KERN_MAXFILES
;
2209 pmib
[1] = KERN_MAXFILESPERPROC
;
2210 gstr
= "kern.maxfiles";
2211 pstr
= "kern.maxfilesperproc";
2214 /* kernel will not clamp to this value, we must */
2215 if (gval
> (2048 + 20))
2221 if (sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)) == -1)
2222 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", gstr
);
2223 if (sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)) == -1)
2224 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", pstr
);
2226 if (setrlimit(i
, ltmp
+ i
) == -1)
2227 syslog(LOG_WARNING
, "setrlimit(): %m");
2228 /* the kernel may have clamped the values we gave it */
2229 if (getrlimit(i
, l
+ i
) == -1)
2230 syslog(LOG_WARNING
, "getrlimit(): %m");
2234 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);
2237 __private_extern__
void launchd_SessionCreate(const char *who
)
2239 void *seclib
= dlopen(SECURITY_LIB
, RTLD_LAZY
);
2240 OSStatus (*sescr
)(SessionCreationFlags flags
, SessionAttributeBits attributes
);
2243 sescr
= dlsym(seclib
, "SessionCreate");
2246 OSStatus scr
= sescr(0, 0);
2248 syslog(LOG_WARNING
, "%s: SessionCreate() failed: %d", who
, scr
);
2250 syslog(LOG_WARNING
, "%s: couldn't find SessionCreate() in %s", who
, SECURITY_LIB
);
2255 syslog(LOG_WARNING
, "%s: dlopen(\"%s\",...): %s", who
, SECURITY_LIB
, dlerror());
2259 static int dir_has_files(const char *path
)
2261 DIR *dd
= opendir(path
);
2268 while ((de
= readdir(dd
))) {
2269 if (strcmp(de
->d_name
, ".") && strcmp(de
->d_name
, "..")) {
2279 static void job_set_alarm(struct jobcb
*j
)
2281 struct tm otherlatertm
, latertm
, *nowtm
;
2282 time_t later
, otherlater
= 0, now
= time(NULL
);
2284 nowtm
= localtime(&now
);
2289 latertm
.tm_isdst
= -1;
2292 if (-1 != j
->start_cal_interval
->tm_min
)
2293 latertm
.tm_min
= j
->start_cal_interval
->tm_min
;
2294 if (-1 != j
->start_cal_interval
->tm_hour
)
2295 latertm
.tm_hour
= j
->start_cal_interval
->tm_hour
;
2297 otherlatertm
= latertm
;
2299 if (-1 != j
->start_cal_interval
->tm_mday
)
2300 latertm
.tm_mday
= j
->start_cal_interval
->tm_mday
;
2301 if (-1 != j
->start_cal_interval
->tm_mon
)
2302 latertm
.tm_mon
= j
->start_cal_interval
->tm_mon
;
2304 /* cron semantics are fun */
2305 if (-1 != j
->start_cal_interval
->tm_wday
) {
2306 int delta
, realwday
= j
->start_cal_interval
->tm_wday
;
2311 delta
= realwday
- nowtm
->tm_wday
;
2313 /* Now Later Delta Desired
2320 otherlatertm
.tm_mday
+= delta
;
2322 otherlatertm
.tm_mday
+= 7 + delta
;
2323 else if (-1 != j
->start_cal_interval
->tm_hour
&& otherlatertm
.tm_hour
<= nowtm
->tm_hour
)
2324 otherlatertm
.tm_mday
+= 7;
2325 else if (-1 != j
->start_cal_interval
->tm_min
&& otherlatertm
.tm_min
<= nowtm
->tm_min
)
2326 otherlatertm
.tm_hour
++;
2328 otherlatertm
.tm_min
++;
2330 otherlater
= mktime(&otherlatertm
);
2333 if (-1 != j
->start_cal_interval
->tm_mon
&& latertm
.tm_mon
<= nowtm
->tm_mon
) {
2335 } else if (-1 != j
->start_cal_interval
->tm_mday
&& latertm
.tm_mday
<= nowtm
->tm_mday
) {
2337 } else if (-1 != j
->start_cal_interval
->tm_hour
&& latertm
.tm_hour
<= nowtm
->tm_hour
) {
2339 } else if (-1 != j
->start_cal_interval
->tm_min
&& latertm
.tm_min
<= nowtm
->tm_min
) {
2345 later
= mktime(&latertm
);
2348 if (-1 != j
->start_cal_interval
->tm_mday
)
2349 later
= later
< otherlater
? later
: otherlater
;
2354 if (-1 == kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_ABSOLUTE
|NOTE_SECONDS
, later
, &j
->kqjob_callback
))
2355 job_log_error(j
, LOG_ERR
, "adding kevent alarm");
2358 static void job_log_error(struct jobcb
*j
, int pri
, const char *msg
, ...)
2360 size_t newmsg_sz
= strlen(msg
) + strlen(j
->label
) + 200;
2361 char *newmsg
= alloca(newmsg_sz
);
2364 sprintf(newmsg
, "%s: %s: %s", j
->label
, msg
, strerror(errno
));
2368 vsyslog(pri
, newmsg
, ap
);
2373 static void job_log(struct jobcb
*j
, int pri
, const char *msg
, ...)
2375 size_t newmsg_sz
= strlen(msg
) + sizeof(": ") + strlen(j
->label
);
2376 char *newmsg
= alloca(newmsg_sz
);
2379 sprintf(newmsg
, "%s: %s", j
->label
, msg
);
2383 vsyslog(pri
, newmsg
, ap
);
2388 static void async_callback(void)
2390 struct timespec timeout
= { 0, 0 };
2393 switch (kevent(asynckq
, NULL
, 0, &kev
, 1, &timeout
)) {
2395 syslog(LOG_DEBUG
, "kevent(): %m");
2398 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
2402 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
2406 static void testfd_or_openfd(int fd
, const char *path
, int flags
)
2410 if (-1 != (tmpfd
= dup(fd
))) {
2413 if (-1 == (tmpfd
= open(path
, flags
))) {
2414 syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
2415 } else if (tmpfd
!= fd
) {