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
);
178 static time_t cronemu(int mon
, int mday
, int hour
, int min
);
179 static time_t cronemu_wday(int wday
, int hour
, int min
);
180 static bool cronemu_mon(struct tm
*wtm
, int mon
, int mday
, int hour
, int min
);
181 static bool cronemu_mday(struct tm
*wtm
, int mday
, int hour
, int min
);
182 static bool cronemu_hour(struct tm
*wtm
, int hour
, int min
);
183 static bool cronemu_min(struct tm
*wtm
, int min
);
185 static size_t total_children
= 0;
186 static pid_t readcfg_pid
= 0;
187 static pid_t launchd_proper_pid
= 0;
188 static bool launchd_inited
= false;
189 static bool shutdown_in_progress
= false;
190 static pthread_t mach_server_loop_thread
;
191 mach_port_t launchd_bootstrap_port
= MACH_PORT_NULL
;
192 sigset_t blocked_signals
= 0;
193 static char *pending_stdout
= NULL
;
194 static char *pending_stderr
= NULL
;
196 int main(int argc
, char *argv
[])
198 static const int sigigns
[] = { SIGHUP
, SIGINT
, SIGPIPE
, SIGALRM
,
199 SIGTERM
, SIGURG
, SIGTSTP
, SIGTSTP
, SIGCONT
, /*SIGCHLD,*/
200 SIGTTIN
, SIGTTOU
, SIGIO
, SIGXCPU
, SIGXFSZ
, SIGVTALRM
, SIGPROF
,
201 SIGWINCH
, SIGINFO
, SIGUSR1
, SIGUSR2
};
204 bool sflag
= false, xflag
= false, vflag
= false, dflag
= false;
208 workaround3048875(argc
, argv
);
213 testfd_or_openfd(STDIN_FILENO
, _PATH_DEVNULL
, O_RDONLY
);
214 testfd_or_openfd(STDOUT_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
215 testfd_or_openfd(STDERR_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
217 openlog(getprogname(), LOG_CONS
|(getpid() != 1 ? LOG_PID
|LOG_PERROR
: 0), LOG_LAUNCHD
);
218 setlogmask(LOG_UPTO(LOG_NOTICE
));
220 while ((ch
= getopt(argc
, argv
, "dhsvx")) != -1) {
222 case 'd': dflag
= true; break;
223 case 's': sflag
= true; break;
224 case 'x': xflag
= true; break;
225 case 'v': vflag
= true; break;
226 case 'h': usage(stdout
); break;
228 syslog(LOG_WARNING
, "ignoring unknown arguments");
236 if (dflag
&& daemon(0, 0) == -1)
237 syslog(LOG_WARNING
, "couldn't daemonize: %m");
239 if ((mainkq
= kqueue()) == -1) {
240 syslog(LOG_EMERG
, "kqueue(): %m");
244 if ((asynckq
= kqueue()) == -1) {
245 syslog(LOG_ERR
, "kqueue(): %m");
249 if (kevent_mod(asynckq
, EVFILT_READ
, EV_ADD
, 0, 0, &kqasync_callback
) == -1) {
250 syslog(LOG_ERR
, "kevent_mod(asynckq, EVFILT_READ): %m");
254 sigemptyset(&blocked_signals
);
256 for (i
= 0; i
< (sizeof(sigigns
) / sizeof(int)); i
++) {
257 if (kevent_mod(sigigns
[i
], EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
258 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", sigigns
[i
]);
259 sigaddset(&blocked_signals
, sigigns
[i
]);
260 signal(sigigns
[i
], SIG_IGN
);
263 /* sigh... ignoring SIGCHLD has side effects: we can't call wait*() */
264 if (kevent_mod(SIGCHLD
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
265 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", SIGCHLD
);
268 pid1_magic_init(sflag
, vflag
, xflag
);
270 launchd_bootstrap_port
= bootstrap_port
;
271 launchd_server_init(argv
[0] ? true : false);
274 /* do this after pid1_magic_init() to not catch ourselves mounting stuff */
275 if (kevent_mod(0, EVFILT_FS
, EV_ADD
, 0, 0, &kqfs_callback
) == -1)
276 syslog(LOG_ERR
, "kevent_mod(EVFILT_FS, &kqfs_callback): %m");
280 conceive_firstborn(argv
);
282 reload_launchd_config();
285 job_start(TAILQ_FIRST(&jobs
));
288 static struct timespec timeout
= { 30, 0 };
289 struct timespec
*timeoutp
= NULL
;
292 if (readcfg_pid
== 0)
295 if (TAILQ_EMPTY(&jobs
)) {
296 /* launched on demand */
298 } else if (shutdown_in_progress
&& total_children
== 0) {
303 switch (kevent(mainkq
, NULL
, 0, &kev
, 1, timeoutp
)) {
305 syslog(LOG_DEBUG
, "kevent(): %m");
308 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
314 syslog(LOG_DEBUG
, "kevent(): spurious return with infinite timeout");
317 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
323 static void pid1_magic_init(bool sflag
, bool vflag
, bool xflag
)
326 int memmib
[2] = { CTL_HW
, HW_PHYSMEM
};
327 int mvnmib
[2] = { CTL_KERN
, KERN_MAXVNODES
};
328 int hnmib
[2] = { CTL_KERN
, KERN_HOSTNAME
};
330 int tfp_r_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_READ_GROUP
};
331 int tfp_rw_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_RW_GROUP
};
333 gid_t tfp_rw_gid
= 0;
334 struct group
*tfp_gr
;
338 size_t memsz
= sizeof(mem
);
342 if ((tfp_gr
= getgrnam("procview"))) {
343 tfp_r_gid
= tfp_gr
->gr_gid
;
344 sysctl(tfp_r_mib
, 3, NULL
, NULL
, &tfp_r_gid
, sizeof(tfp_r_gid
));
347 if ((tfp_gr
= getgrnam("procmod"))) {
348 tfp_rw_gid
= tfp_gr
->gr_gid
;
349 sysctl(tfp_rw_mib
, 3, NULL
, NULL
, &tfp_rw_gid
, sizeof(tfp_rw_gid
));
353 setpriority(PRIO_PROCESS
, 0, -1);
356 syslog(LOG_ERR
, "setsid(): %m");
358 if (chdir("/") == -1)
359 syslog(LOG_ERR
, "chdir(\"/\"): %m");
361 if (sysctl(memmib
, 2, &mem
, &memsz
, NULL
, 0) == -1) {
362 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "hw.physmem");
364 /* The following assignment of mem to itself if the size
365 * of data returned is 32 bits instead of 64 is a clever
366 * C trick to move the 32 bits on big endian systems to
367 * the least significant bytes of the 64 mem variable.
369 * On little endian systems, this is effectively a no-op.
372 mem
= *(uint32_t *)&mem
;
373 mvn
= mem
/ (64 * 1024) + 1024;
374 if (sysctl(mvnmib
, 2, NULL
, NULL
, &mvn
, sizeof(mvn
)) == -1)
375 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.maxvnodes");
377 if (sysctl(hnmib
, 2, NULL
, NULL
, "localhost", sizeof("localhost")) == -1)
378 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.hostname");
380 if (setlogin("root") == -1)
381 syslog(LOG_ERR
, "setlogin(\"root\"): %m");
385 if (mount("fdesc", "/dev", MNT_UNION
, NULL
) == -1)
386 syslog(LOG_ERR
, "mount(\"%s\", \"%s\", ...): %m", "fdesc", "/dev/");
388 setenv("PATH", _PATH_STDPATH
, 1);
390 launchd_bootstrap_port
= mach_init_init();
391 task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port
);
392 bootstrap_port
= MACH_PORT_NULL
;
394 pthread_attr_init(&attr
);
395 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
397 pthr_r
= pthread_create(&mach_server_loop_thread
, &attr
, mach_server_loop
, NULL
);
399 syslog(LOG_ERR
, "pthread_create(mach_server_loop): %s", strerror(pthr_r
));
403 pthread_attr_destroy(&attr
);
405 init_boot(sflag
, vflag
, xflag
);
409 #ifdef PID1_REAP_ADOPTED_CHILDREN
410 static bool launchd_check_pid(pid_t p
)
415 TAILQ_FOREACH(j
, &jobs
, tqe
) {
417 EV_SET(&kev
, p
, EVFILT_PROC
, 0, 0, 0, j
);
418 j
->kqjob_callback(j
, &kev
);
423 if (p
== readcfg_pid
) {
424 readcfg_callback(NULL
, NULL
);
432 static char *sockdir
= NULL
;
433 static char *sockpath
= NULL
;
435 static void launchd_clean_up(void)
437 if (launchd_proper_pid
!= getpid())
443 if (-1 == unlink(sockpath
))
444 syslog(LOG_WARNING
, "unlink(\"%s\"): %m", sockpath
);
445 else if (-1 == rmdir(sockdir
))
446 syslog(LOG_WARNING
, "rmdir(\"%s\"): %m", sockdir
);
452 static void launchd_server_init(bool create_session
)
454 struct sockaddr_un sun
;
456 int r
, fd
= -1, ourdirfd
= -1;
459 memset(&sun
, 0, sizeof(sun
));
460 sun
.sun_family
= AF_UNIX
;
462 if (create_session
) {
463 snprintf(ourdir
, sizeof(ourdir
), "%s/%u.%u", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
464 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u.%u/sock", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
465 setenv(LAUNCHD_SOCKET_ENV
, sun
.sun_path
, 1);
467 snprintf(ourdir
, sizeof(ourdir
), "%s/%u", LAUNCHD_SOCK_PREFIX
, getuid());
468 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u/sock", LAUNCHD_SOCK_PREFIX
, getuid());
474 if (mkdir(LAUNCHD_SOCK_PREFIX
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) == -1) {
475 if (errno
== EROFS
) {
477 } else if (errno
== EEXIST
) {
479 stat(LAUNCHD_SOCK_PREFIX
, &sb
);
480 if (!S_ISDIR(sb
.st_mode
)) {
482 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
486 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
492 if (mkdir(ourdir
, S_IRWXU
) == -1) {
493 if (errno
== EROFS
) {
495 } else if (errno
== EEXIST
) {
498 if (!S_ISDIR(sb
.st_mode
)) {
500 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
504 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", ourdir
);
509 if (chown(ourdir
, getuid(), getgid()) == -1)
510 syslog(LOG_WARNING
, "chown(\"%s\"): %m", ourdir
);
515 ourdirfd
= _fd(open(ourdir
, O_RDONLY
));
516 if (ourdirfd
== -1) {
517 syslog(LOG_ERR
, "open(\"%s\"): %m", ourdir
);
521 if (flock(ourdirfd
, LOCK_EX
|LOCK_NB
) == -1) {
522 if (errno
== EWOULDBLOCK
) {
525 syslog(LOG_ERR
, "flock(\"%s\"): %m", ourdir
);
530 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
532 syslog(LOG_ERR
, "unlink(\"thesocket\"): %m");
535 if ((fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) == -1) {
536 syslog(LOG_ERR
, "socket(\"thesocket\"): %m");
539 oldmask
= umask(077);
540 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
544 syslog(LOG_ERR
, "bind(\"thesocket\"): %m");
548 if (listen(fd
, SOMAXCONN
) == -1) {
549 syslog(LOG_ERR
, "listen(\"thesocket\"): %m");
553 if (kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqlisten_callback
) == -1) {
554 syslog(LOG_ERR
, "kevent_mod(\"thesocket\", EVFILT_READ): %m");
558 launchd_inited
= true;
560 sockdir
= strdup(ourdir
);
561 sockpath
= strdup(sun
.sun_path
);
563 launchd_proper_pid
= getpid();
564 atexit(launchd_clean_up
);
570 if (!launchd_inited
) {
578 static long long job_get_integer(launch_data_t j
, const char *key
)
580 launch_data_t t
= launch_data_dict_lookup(j
, key
);
582 return launch_data_get_integer(t
);
587 static const char *job_get_string(launch_data_t j
, const char *key
)
589 launch_data_t t
= launch_data_dict_lookup(j
, key
);
591 return launch_data_get_string(t
);
596 static const char *job_get_file2exec(launch_data_t j
)
598 launch_data_t tmpi
, tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAM
);
601 return launch_data_get_string(tmp
);
603 tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
605 tmpi
= launch_data_array_get_index(tmp
, 0);
607 return launch_data_get_string(tmpi
);
613 static bool job_get_bool(launch_data_t j
, const char *key
)
615 launch_data_t t
= launch_data_dict_lookup(j
, key
);
617 return launch_data_get_bool(t
);
622 static void ipc_open(int fd
, struct jobcb
*j
)
624 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
626 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
628 c
->kqconn_callback
= ipc_callback
;
629 c
->conn
= launchd_fdopen(fd
);
631 TAILQ_INSERT_TAIL(&connections
, c
, tqe
);
632 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
);
635 static void simple_zombie_reaper(void *obj
__attribute__((unused
)), struct kevent
*kev
)
637 waitpid(kev
->ident
, NULL
, 0);
640 static void listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
642 struct sockaddr_un sun
;
643 socklen_t sl
= sizeof(sun
);
646 if ((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) == -1) {
653 static void ipc_callback(void *obj
, struct kevent
*kev
)
655 struct conncb
*c
= obj
;
658 if (kev
->filter
== EVFILT_READ
) {
659 if (launchd_msg_recv(c
->conn
, ipc_readmsg
, c
) == -1 && errno
!= EAGAIN
) {
660 if (errno
!= ECONNRESET
)
661 syslog(LOG_DEBUG
, "%s(): recv: %m", __func__
);
664 } else if (kev
->filter
== EVFILT_WRITE
) {
665 r
= launchd_msg_send(c
->conn
, NULL
);
667 if (errno
!= EAGAIN
) {
668 syslog(LOG_DEBUG
, "%s(): send: %m", __func__
);
672 kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
);
675 syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
680 static void set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
682 setenv(key
, launch_data_get_string(obj
), 1);
685 static void launch_data_close_fds(launch_data_t o
)
689 switch (launch_data_get_type(o
)) {
690 case LAUNCH_DATA_DICTIONARY
:
691 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_close_fds
, NULL
);
693 case LAUNCH_DATA_ARRAY
:
694 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
695 launch_data_close_fds(launch_data_array_get_index(o
, i
));
698 if (launch_data_get_fd(o
) != -1)
699 close(launch_data_get_fd(o
));
706 static void launch_data_revoke_fds(launch_data_t o
)
710 switch (launch_data_get_type(o
)) {
711 case LAUNCH_DATA_DICTIONARY
:
712 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_revoke_fds
, NULL
);
714 case LAUNCH_DATA_ARRAY
:
715 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
716 launch_data_revoke_fds(launch_data_array_get_index(o
, i
));
719 launch_data_set_fd(o
, -1);
726 static void job_ignore_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
728 struct jobcb
*j
= cookie
;
732 switch (launch_data_get_type(o
)) {
733 case LAUNCH_DATA_DICTIONARY
:
734 launch_data_dict_iterate(o
, job_ignore_fds
, cookie
);
736 case LAUNCH_DATA_ARRAY
:
737 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
738 job_ignore_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
741 fd
= launch_data_get_fd(o
);
743 job_log(j
, LOG_DEBUG
, "Ignoring FD: %d", fd
);
744 kevent_mod(fd
, EVFILT_READ
, EV_DELETE
, 0, 0, NULL
);
752 static void job_ignore(struct jobcb
*j
)
754 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
758 job_ignore_fds(j_sockets
, NULL
, j
);
760 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
761 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
763 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
764 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
768 static void job_watch_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
770 struct jobcb
*j
= cookie
;
774 switch (launch_data_get_type(o
)) {
775 case LAUNCH_DATA_DICTIONARY
:
776 launch_data_dict_iterate(o
, job_watch_fds
, cookie
);
778 case LAUNCH_DATA_ARRAY
:
779 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
780 job_watch_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
783 fd
= launch_data_get_fd(o
);
785 job_log(j
, LOG_DEBUG
, "Watching FD: %d", fd
);
786 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, cookie
);
794 static void job_watch(struct jobcb
*j
)
796 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
797 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
798 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
802 job_watch_fds(j_sockets
, NULL
, &j
->kqjob_callback
);
804 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
805 if (-1 == j
->vnodes
[i
]) {
806 launch_data_t ld_idx
= launch_data_array_get_index(ld_vnodes
, i
);
807 const char *thepath
= launch_data_get_string(ld_idx
);
809 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
810 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
812 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
813 NOTE_WRITE
|NOTE_EXTEND
|NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
|NOTE_ATTRIB
|NOTE_LINK
,
814 0, &j
->kqjob_callback
);
817 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
818 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
819 NOTE_WRITE
|NOTE_EXTEND
|NOTE_ATTRIB
|NOTE_LINK
, 0, &j
->kqjob_callback
);
822 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
823 launch_data_t ld_idx
= launch_data_array_get_index(ld_qdirs
, i
);
824 const char *thepath
= launch_data_get_string(ld_idx
);
827 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
828 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
829 } else if (dcc_r
> 0 && !shutdown_in_progress
) {
836 static void job_stop(struct jobcb
*j
)
842 static void job_remove(struct jobcb
*j
)
847 job_log(j
, LOG_DEBUG
, "Removed");
849 TAILQ_REMOVE(&jobs
, j
, tqe
);
851 if (kevent_mod(j
->p
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqsimple_zombie_reaper
) == -1) {
857 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
858 launch_data_dict_iterate(tmp
, unsetup_job_env
, NULL
);
859 launch_data_close_fds(j
->ldj
);
860 launch_data_free(j
->ldj
);
863 for (i
= 0; i
< j
->vnodes_cnt
; i
++)
864 if (-1 != j
->vnodes
[i
])
868 for (i
= 0; i
< j
->qdirs_cnt
; i
++)
869 if (-1 != j
->qdirs
[i
])
873 if (j
->start_interval
)
874 kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
875 if (j
->start_cal_interval
) {
876 kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
877 free(j
->start_cal_interval
);
879 kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
883 struct readmsg_context
{
888 static void ipc_readmsg(launch_data_t msg
, void *context
)
890 struct readmsg_context rmc
= { context
, NULL
};
892 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(msg
)) {
893 launch_data_dict_iterate(msg
, ipc_readmsg2
, &rmc
);
894 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(msg
)) {
895 ipc_readmsg2(NULL
, launch_data_get_string(msg
), &rmc
);
897 rmc
.resp
= launch_data_new_errno(EINVAL
);
900 if (NULL
== rmc
.resp
)
901 rmc
.resp
= launch_data_new_errno(ENOSYS
);
903 launch_data_close_fds(msg
);
905 if (launchd_msg_send(rmc
.c
->conn
, rmc
.resp
) == -1) {
906 if (errno
== EAGAIN
) {
907 kevent_mod(launchd_getfd(rmc
.c
->conn
), EVFILT_WRITE
, EV_ADD
, 0, 0, &rmc
.c
->kqconn_callback
);
909 syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %m");
913 launch_data_free(rmc
.resp
);
917 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
919 struct readmsg_context
*rmc
= context
;
920 launch_data_t resp
= NULL
;
926 if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
927 TAILQ_FOREACH(j
, &jobs
, tqe
) {
928 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
930 resp
= launch_data_new_errno(0);
934 resp
= launch_data_new_errno(ESRCH
);
935 } else if (!strcmp(cmd
, LAUNCH_KEY_STOPJOB
)) {
936 TAILQ_FOREACH(j
, &jobs
, tqe
) {
937 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
939 resp
= launch_data_new_errno(0);
943 resp
= launch_data_new_errno(ESRCH
);
944 } else if (!strcmp(cmd
, LAUNCH_KEY_REMOVEJOB
)) {
945 TAILQ_FOREACH(j
, &jobs
, tqe
) {
946 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
948 resp
= launch_data_new_errno(0);
952 resp
= launch_data_new_errno(ESRCH
);
953 } else if (!strcmp(cmd
, LAUNCH_KEY_SUBMITJOB
)) {
954 if (launch_data_get_type(data
) == LAUNCH_DATA_ARRAY
) {
958 resp
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
959 for (i
= 0; i
< launch_data_array_get_count(data
); i
++) {
960 tmp
= load_job(launch_data_array_get_index(data
, i
));
961 launch_data_array_set_index(resp
, tmp
, i
);
964 resp
= load_job(data
);
966 } else if (!strcmp(cmd
, LAUNCH_KEY_UNSETUSERENVIRONMENT
)) {
967 unsetenv(launch_data_get_string(data
));
968 resp
= launch_data_new_errno(0);
969 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUSERENVIRONMENT
)) {
970 char **tmpenviron
= environ
;
971 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
972 for (; *tmpenviron
; tmpenviron
++) {
974 launch_data_t s
= launch_data_alloc(LAUNCH_DATA_STRING
);
975 launch_data_set_string(s
, strchr(*tmpenviron
, '=') + 1);
976 strncpy(envkey
, *tmpenviron
, sizeof(envkey
));
977 *(strchr(envkey
, '=')) = '\0';
978 launch_data_dict_insert(resp
, s
, envkey
);
980 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUSERENVIRONMENT
)) {
981 launch_data_dict_iterate(data
, set_user_env
, NULL
);
982 resp
= launch_data_new_errno(0);
983 } else if (!strcmp(cmd
, LAUNCH_KEY_CHECKIN
)) {
985 resp
= launch_data_copy(rmc
->c
->j
->ldj
);
986 if (NULL
== launch_data_dict_lookup(resp
, LAUNCH_JOBKEY_TIMEOUT
)) {
987 launch_data_t to
= launch_data_new_integer(LAUNCHD_MIN_JOB_RUN_TIME
);
988 launch_data_dict_insert(resp
, to
, LAUNCH_JOBKEY_TIMEOUT
);
990 rmc
->c
->j
->checkedin
= true;
992 resp
= launch_data_new_errno(EACCES
);
994 } else if (!strcmp(cmd
, LAUNCH_KEY_RELOADTTYS
)) {
996 resp
= launch_data_new_errno(0);
997 } else if (!strcmp(cmd
, LAUNCH_KEY_SHUTDOWN
)) {
999 resp
= launch_data_new_errno(0);
1000 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBS
)) {
1001 resp
= get_jobs(NULL
);
1002 launch_data_revoke_fds(resp
);
1003 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRESOURCELIMITS
)) {
1004 resp
= adjust_rlimits(NULL
);
1005 } else if (!strcmp(cmd
, LAUNCH_KEY_SETRESOURCELIMITS
)) {
1006 resp
= adjust_rlimits(data
);
1007 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOB
)) {
1008 resp
= get_jobs(launch_data_get_string(data
));
1009 launch_data_revoke_fds(resp
);
1010 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBWITHHANDLES
)) {
1011 resp
= get_jobs(launch_data_get_string(data
));
1012 } else if (!strcmp(cmd
, LAUNCH_KEY_SETLOGMASK
)) {
1013 resp
= launch_data_new_integer(setlogmask(launch_data_get_integer(data
)));
1014 } else if (!strcmp(cmd
, LAUNCH_KEY_GETLOGMASK
)) {
1015 int oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1016 resp
= launch_data_new_integer(oldmask
);
1017 setlogmask(oldmask
);
1018 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUMASK
)) {
1019 resp
= launch_data_new_integer(umask(launch_data_get_integer(data
)));
1020 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUMASK
)) {
1021 mode_t oldmask
= umask(0);
1022 resp
= launch_data_new_integer(oldmask
);
1024 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGESELF
)) {
1025 struct rusage rusage
;
1026 getrusage(RUSAGE_SELF
, &rusage
);
1027 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1028 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGECHILDREN
)) {
1029 struct rusage rusage
;
1030 getrusage(RUSAGE_CHILDREN
, &rusage
);
1031 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1032 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDOUT
)) {
1033 resp
= setstdio(STDOUT_FILENO
, data
);
1034 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDERR
)) {
1035 resp
= setstdio(STDERR_FILENO
, data
);
1036 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHCONTROL
)) {
1037 batch_job_enable(launch_data_get_bool(data
), rmc
->c
);
1038 resp
= launch_data_new_errno(0);
1039 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHQUERY
)) {
1040 resp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1041 launch_data_set_bool(resp
, batch_disabler_count
== 0);
1047 static launch_data_t
setstdio(int d
, launch_data_t o
)
1049 launch_data_t resp
= launch_data_new_errno(0);
1051 if (launch_data_get_type(o
) == LAUNCH_DATA_STRING
) {
1052 char **where
= &pending_stderr
;
1053 if (d
== STDOUT_FILENO
)
1054 where
= &pending_stdout
;
1057 *where
= strdup(launch_data_get_string(o
));
1058 } else if (launch_data_get_type(o
) == LAUNCH_DATA_FD
) {
1059 dup2(launch_data_get_fd(o
), d
);
1061 launch_data_set_errno(resp
, EINVAL
);
1067 static void batch_job_enable(bool e
, struct conncb
*c
)
1069 if (e
&& c
->disabled_batch
) {
1070 batch_disabler_count
--;
1071 c
->disabled_batch
= 0;
1072 if (batch_disabler_count
== 0)
1073 kevent_mod(asynckq
, EVFILT_READ
, EV_ENABLE
, 0, 0, &kqasync_callback
);
1074 } else if (!e
&& !c
->disabled_batch
) {
1075 if (batch_disabler_count
== 0)
1076 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1077 batch_disabler_count
++;
1078 c
->disabled_batch
= 1;
1082 static launch_data_t
load_job(launch_data_t pload
)
1084 launch_data_t tmp
, resp
;
1087 bool startnow
, hasprog
= false, hasprogargs
= false;
1089 if ((label
= job_get_string(pload
, LAUNCH_JOBKEY_LABEL
))) {
1090 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1091 if (!strcmp(j
->label
, label
)) {
1092 resp
= launch_data_new_errno(EEXIST
);
1097 resp
= launch_data_new_errno(EINVAL
);
1101 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAM
))
1103 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
))
1106 if (!hasprog
&& !hasprogargs
) {
1107 resp
= launch_data_new_errno(EINVAL
);
1111 j
= calloc(1, sizeof(struct jobcb
) + strlen(label
) + 1);
1112 strcpy(j
->label
, label
);
1113 j
->ldj
= launch_data_copy(pload
);
1114 launch_data_revoke_fds(pload
);
1115 j
->kqjob_callback
= job_callback
;
1118 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
) == NULL
) {
1119 tmp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1120 launch_data_set_bool(tmp
, true);
1121 launch_data_dict_insert(j
->ldj
, tmp
, LAUNCH_JOBKEY_ONDEMAND
);
1124 TAILQ_INSERT_TAIL(&jobs
, j
, tqe
);
1126 j
->debug
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_DEBUG
);
1128 startnow
= !job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1130 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_RUNATLOAD
))
1133 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
))) {
1136 j
->qdirs_cnt
= launch_data_array_get_count(tmp
);
1137 j
->qdirs
= malloc(sizeof(int) * j
->qdirs_cnt
);
1139 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1140 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1142 if (-1 == (j
->qdirs
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1143 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1148 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTINTERVAL
))) {
1149 j
->start_interval
= launch_data_get_integer(tmp
);
1151 if (j
->start_interval
== 0)
1152 job_log(j
, LOG_WARNING
, "StartInterval is zero, ignoring");
1153 else if (-1 == kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_SECONDS
, j
->start_interval
, &j
->kqjob_callback
))
1154 job_log_error(j
, LOG_ERR
, "adding kevent timer");
1157 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTCALENDARINTERVAL
))) {
1158 launch_data_t tmp_k
;
1160 j
->start_cal_interval
= calloc(1, sizeof(struct tm
));
1161 j
->start_cal_interval
->tm_min
= -1;
1162 j
->start_cal_interval
->tm_hour
= -1;
1163 j
->start_cal_interval
->tm_mday
= -1;
1164 j
->start_cal_interval
->tm_wday
= -1;
1165 j
->start_cal_interval
->tm_mon
= -1;
1167 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(tmp
)) {
1168 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MINUTE
)))
1169 j
->start_cal_interval
->tm_min
= launch_data_get_integer(tmp_k
);
1170 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_HOUR
)))
1171 j
->start_cal_interval
->tm_hour
= launch_data_get_integer(tmp_k
);
1172 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_DAY
)))
1173 j
->start_cal_interval
->tm_mday
= launch_data_get_integer(tmp_k
);
1174 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_WEEKDAY
)))
1175 j
->start_cal_interval
->tm_wday
= launch_data_get_integer(tmp_k
);
1176 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MONTH
)))
1177 j
->start_cal_interval
->tm_mon
= launch_data_get_integer(tmp_k
);
1183 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
))) {
1186 j
->vnodes_cnt
= launch_data_array_get_count(tmp
);
1187 j
->vnodes
= malloc(sizeof(int) * j
->vnodes_cnt
);
1189 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1190 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1192 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1193 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1198 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
1199 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1201 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1207 resp
= launch_data_new_errno(0);
1212 static launch_data_t
get_jobs(const char *which
)
1215 launch_data_t tmp
, resp
= NULL
;
1218 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1219 if (!strcmp(which
, j
->label
))
1220 resp
= launch_data_copy(j
->ldj
);
1223 resp
= launch_data_new_errno(ESRCH
);
1225 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1227 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1228 tmp
= launch_data_copy(j
->ldj
);
1229 launch_data_dict_insert(resp
, tmp
, j
->label
);
1236 static void usage(FILE *where
)
1238 fprintf(where
, "%s: [-d] [-- command [args ...]]\n", getprogname());
1239 fprintf(where
, "\t-d\tdaemonize\n");
1240 fprintf(where
, "\t-h\tthis usage statement\n");
1242 if (where
== stdout
)
1246 #ifdef EVFILT_MACH_IMPLEMENTED
1247 static void **machcbtable
= NULL
;
1248 static size_t machcbtable_cnt
= 0;
1249 static int machcbreadfd
= -1;
1250 static int machcbwritefd
= -1;
1251 static mach_port_t mach_demand_port_set
= MACH_PORT_NULL
;
1252 static pthread_t mach_demand_thread
;
1254 static void mach_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1259 read(machcbreadfd
, &mp
, sizeof(mp
));
1261 EV_SET(&mkev
, mp
, EVFILT_MACHPORT
, 0, 0, 0, machcbtable
[MACH_PORT_INDEX(mp
)]);
1263 (*((kq_callback
*)mkev
.udata
))(mkev
.udata
, &mkev
);
1267 int kevent_mod(uintptr_t ident
, short filter
, u_short flags
, u_int fflags
, intptr_t data
, void *udata
)
1271 #ifdef EVFILT_MACH_IMPLEMENTED
1273 pthread_attr_t attr
;
1274 int pthr_r
, pfds
[2];
1277 if (EVFILT_TIMER
== filter
|| EVFILT_VNODE
== filter
)
1280 if (flags
& EV_ADD
&& NULL
== udata
) {
1281 syslog(LOG_ERR
, "%s(): kev.udata == NULL!!!", __func__
);
1282 syslog(LOG_ERR
, "kev: ident %d filter %d flags 0x%x fflags 0x%x",
1283 ident
, filter
, flags
, fflags
);
1288 #ifdef EVFILT_MACH_IMPLEMENTED
1289 if (filter
!= EVFILT_MACHPORT
) {
1291 #ifdef PID1_REAP_ADOPTED_CHILDREN
1292 if (filter
== EVFILT_PROC
&& getpid() == 1)
1295 EV_SET(&kev
, ident
, filter
, flags
, fflags
, data
, udata
);
1296 return kevent(q
, &kev
, 1, NULL
, 0, NULL
);
1297 #ifdef EVFILT_MACH_IMPLEMENTED
1300 if (machcbtable
== NULL
) {
1301 pthread_attr_init(&attr
);
1302 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1304 pthr_r
= pthread_create(&mach_demand_thread
, &attr
, mach_demand_loop
, NULL
);
1306 syslog(LOG_ERR
, "pthread_create(mach_demand_loop): %s", strerror(pthr_r
));
1310 pthread_attr_destroy(&attr
);
1312 machcbtable
= malloc(0);
1314 machcbwritefd
= _fd(pfds
[1]);
1315 machcbreadfd
= _fd(pfds
[0]);
1316 kevent_mod(machcbreadfd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqmach_callback
);
1317 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET
, &mach_demand_port_set
);
1318 if (kr
!= KERN_SUCCESS
) {
1319 syslog(LOG_ERR
, "mach_port_allocate(demand_port_set): %s", mach_error_string(kr
));
1324 if (flags
& EV_ADD
) {
1325 kr
= mach_port_move_member(mach_task_self(), ident
, mach_demand_port_set
);
1326 if (kr
!= KERN_SUCCESS
) {
1327 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1331 if (MACH_PORT_INDEX(ident
) > machcbtable_cnt
)
1332 machcbtable
= realloc(machcbtable
, MACH_PORT_INDEX(ident
) * sizeof(void *));
1334 machcbtable
[MACH_PORT_INDEX(ident
)] = udata
;
1335 } else if (flags
& EV_DELETE
) {
1336 kr
= mach_port_move_member(mach_task_self(), ident
, MACH_PORT_NULL
);
1337 if (kr
!= KERN_SUCCESS
) {
1338 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1342 syslog(LOG_DEBUG
, "kevent_mod(EVFILT_MACHPORT) with flags: %d", flags
);
1351 static int _fd(int fd
)
1354 fcntl(fd
, F_SETFD
, 1);
1358 static void ipc_close(struct conncb
*c
)
1360 batch_job_enable(true, c
);
1362 TAILQ_REMOVE(&connections
, c
, tqe
);
1363 launchd_close(c
->conn
);
1367 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1369 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1370 setenv(key
, launch_data_get_string(obj
), 1);
1373 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1375 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1379 static void job_reap(struct jobcb
*j
)
1381 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1382 time_t td
= time(NULL
) - j
->start_time
;
1383 bool bad_exit
= false;
1386 job_log(j
, LOG_DEBUG
, "Reaping");
1393 #ifdef PID1_REAP_ADOPTED_CHILDREN
1395 status
= pid1_child_exit_status
;
1398 if (-1 == waitpid(j
->p
, &status
, 0)) {
1399 job_log_error(j
, LOG_ERR
, "waitpid(%d, ...)", j
->p
);
1403 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
1404 job_log(j
, LOG_WARNING
, "exited with exit code: %d", WEXITSTATUS(status
));
1408 if (WIFSIGNALED(status
)) {
1409 int s
= WTERMSIG(status
);
1410 if (SIGKILL
== s
|| SIGTERM
== s
) {
1411 job_log(j
, LOG_NOTICE
, "exited: %s", strsignal(s
));
1413 job_log(j
, LOG_WARNING
, "exited abnormally: %s", strsignal(s
));
1419 if (td
< LAUNCHD_MIN_JOB_RUN_TIME
) {
1420 job_log(j
, LOG_WARNING
, "respawning too quickly! throttling");
1423 } else if (td
>= LAUNCHD_REWARD_JOB_RUN_TIME
) {
1424 job_log(j
, LOG_INFO
, "lived long enough, forgiving past exit failures");
1425 j
->failed_exits
= 0;
1432 if (j
->failed_exits
> 0) {
1433 int failures_left
= LAUNCHD_FAILED_EXITS_THRESHOLD
- j
->failed_exits
;
1435 job_log(j
, LOG_WARNING
, "%d more failure%s without living at least %d seconds will cause job removal",
1436 failures_left
, failures_left
> 1 ? "s" : "", LAUNCHD_REWARD_JOB_RUN_TIME
);
1443 static bool job_restart_fitness_test(struct jobcb
*j
)
1445 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1448 job_log(j
, LOG_DEBUG
, "first born died, begin shutdown");
1451 } else if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
) && !j
->checkedin
) {
1452 job_log(j
, LOG_WARNING
, "failed to checkin");
1455 } else if (j
->failed_exits
>= LAUNCHD_FAILED_EXITS_THRESHOLD
) {
1456 job_log(j
, LOG_WARNING
, "too many failures in succession");
1459 } else if (od
|| shutdown_in_progress
) {
1460 if (!od
&& shutdown_in_progress
)
1461 job_log(j
, LOG_NOTICE
, "exited while shutdown is in progress, will not restart unless demand requires it");
1469 static void job_callback(void *obj
, struct kevent
*kev
)
1471 struct jobcb
*j
= obj
;
1473 bool startnow
= true;
1477 oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1478 job_log(j
, LOG_DEBUG
, "log level debug temporarily enabled while processing job");
1481 if (kev
->filter
== EVFILT_PROC
) {
1484 startnow
= job_restart_fitness_test(j
);
1486 if (startnow
&& j
->throttle
) {
1487 j
->throttle
= false;
1488 job_log(j
, LOG_WARNING
, "will restart in %d seconds", LAUNCHD_MIN_JOB_RUN_TIME
);
1489 if (-1 == kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_ADD
|EV_ONESHOT
,
1490 NOTE_SECONDS
, LAUNCHD_MIN_JOB_RUN_TIME
, &j
->kqjob_callback
)) {
1491 job_log_error(j
, LOG_WARNING
, "failed to setup timer callback!, starting now!");
1496 } else if (kev
->filter
== EVFILT_TIMER
&& (void *)kev
->ident
== j
->start_cal_interval
) {
1498 } else if (kev
->filter
== EVFILT_VNODE
) {
1500 const char *thepath
= NULL
;
1502 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1503 if (j
->vnodes
[i
] == (int)kev
->ident
) {
1504 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
1506 thepath
= launch_data_get_string(launch_data_array_get_index(ld_vnodes
, i
));
1508 job_log(j
, LOG_DEBUG
, "watch path modified: %s", thepath
);
1510 if ((NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
) & kev
->fflags
) {
1511 job_log(j
, LOG_DEBUG
, "watch path invalidated: %s", thepath
);
1512 close(j
->vnodes
[i
]);
1513 j
->vnodes
[i
] = -1; /* this will get fixed in job_watch() */
1518 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1519 if (j
->qdirs
[i
] == (int)kev
->ident
) {
1520 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
1523 thepath
= launch_data_get_string(launch_data_array_get_index(ld_qdirs
, i
));
1525 job_log(j
, LOG_DEBUG
, "queue directory modified: %s", thepath
);
1527 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
1528 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
1529 } else if (0 == dcc_r
) {
1530 job_log(j
, LOG_DEBUG
, "spurious wake up, directory empty: %s", thepath
);
1535 /* if we get here, then the vnodes either wasn't a qdir, or if it was, it has entries in it */
1536 } else if (kev
->filter
== EVFILT_READ
&& (int)kev
->ident
== j
->execfd
) {
1537 if (kev
->data
> 0) {
1540 read(j
->execfd
, &e
, sizeof(e
));
1542 job_log_error(j
, LOG_ERR
, "execve()");
1557 /* the job might have been removed, must not call job_log() */
1558 syslog(LOG_DEBUG
, "restoring original log mask");
1559 setlogmask(oldmask
);
1563 static void job_start(struct jobcb
*j
)
1571 job_log(j
, LOG_DEBUG
, "Starting");
1574 job_log(j
, LOG_DEBUG
, "already running");
1578 j
->checkedin
= false;
1580 sipc
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
);
1582 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
))
1586 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
1588 socketpair(AF_UNIX
, SOCK_STREAM
, 0, execspair
);
1590 time(&j
->start_time
);
1592 switch (c
= fork_with_bootstrap_port(launchd_bootstrap_port
)) {
1594 job_log_error(j
, LOG_ERR
, "fork() failed, will try again in one second");
1595 close(execspair
[0]);
1596 close(execspair
[1]);
1601 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1605 close(execspair
[0]);
1606 /* wait for our parent to say they've attached a kevent to us */
1607 read(_fd(execspair
[1]), &c
, sizeof(c
));
1609 setpgid(getpid(), getpid());
1610 if (isatty(STDIN_FILENO
)) {
1611 if (tcsetpgrp(STDIN_FILENO
, getpid()) == -1)
1612 job_log_error(j
, LOG_WARNING
, "tcsetpgrp()");
1618 sprintf(nbuf
, "%d", spair
[1]);
1619 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
1621 job_start_child(j
, execspair
[1]);
1624 close(execspair
[1]);
1625 j
->execfd
= _fd(execspair
[0]);
1628 ipc_open(_fd(spair
[0]), j
);
1630 if (kevent_mod(j
->execfd
, EVFILT_READ
, EV_ADD
, 0, 0, &j
->kqjob_callback
) == -1)
1631 job_log_error(j
, LOG_ERR
, "kevent_mod(j->execfd): %m");
1632 if (kevent_mod(c
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &j
->kqjob_callback
) == -1) {
1633 job_log_error(j
, LOG_ERR
, "kevent()");
1638 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1641 /* this unblocks the child and avoids a race
1642 * between the above fork() and the kevent_mod() */
1643 write(j
->execfd
, &c
, sizeof(c
));
1648 static void job_start_child(struct jobcb
*j
, int execfd
)
1650 launch_data_t ldpa
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
1651 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1653 const char **argv
, *file2exec
= "/usr/libexec/launchproxy";
1655 bool hasprog
= false;
1657 job_setup_attributes(j
);
1660 argv_cnt
= launch_data_array_get_count(ldpa
);
1661 argv
= alloca((argv_cnt
+ 2) * sizeof(char *));
1662 for (i
= 0; i
< argv_cnt
; i
++)
1663 argv
[i
+ 1] = launch_data_get_string(launch_data_array_get_index(ldpa
, i
));
1664 argv
[argv_cnt
+ 1] = NULL
;
1666 argv
= alloca(3 * sizeof(char *));
1667 argv
[1] = job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
);
1671 if (job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
))
1675 argv
[0] = file2exec
;
1678 file2exec
= job_get_file2exec(j
->ldj
);
1682 r
= execv(file2exec
, (char *const*)argv
);
1684 r
= execvp(file2exec
, (char *const*)argv
);
1688 write(execfd
, &errno
, sizeof(errno
));
1689 job_log_error(j
, LOG_ERR
, "execv%s(\"%s\", ...)", hasprog
? "" : "p", file2exec
);
1694 static void job_setup_attributes(struct jobcb
*j
)
1696 launch_data_t srl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOFTRESOURCELIMITS
);
1697 launch_data_t hrl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_HARDRESOURCELIMITS
);
1698 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1702 struct group
*gre
= NULL
;
1704 static const struct {
1708 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE
, RLIMIT_CORE
},
1709 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU
, RLIMIT_CPU
},
1710 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA
, RLIMIT_DATA
},
1711 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE
, RLIMIT_FSIZE
},
1712 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK
, RLIMIT_MEMLOCK
},
1713 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE
, RLIMIT_NOFILE
},
1714 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC
, RLIMIT_NPROC
},
1715 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS
, RLIMIT_RSS
},
1716 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK
, RLIMIT_STACK
},
1719 setpriority(PRIO_PROCESS
, 0, job_get_integer(j
->ldj
, LAUNCH_JOBKEY_NICE
));
1722 for (i
= 0; i
< (sizeof(limits
) / sizeof(limits
[0])); i
++) {
1725 if (getrlimit(limits
[i
].val
, &rl
) == -1) {
1726 job_log_error(j
, LOG_WARNING
, "getrlimit()");
1731 rl
.rlim_max
= job_get_integer(hrl
, limits
[i
].key
);
1733 rl
.rlim_cur
= job_get_integer(srl
, limits
[i
].key
);
1735 if (setrlimit(limits
[i
].val
, &rl
) == -1)
1736 job_log_error(j
, LOG_WARNING
, "setrlimit()");
1740 if (!inetcompat
&& job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SESSIONCREATE
))
1741 launchd_SessionCreate(job_get_file2exec(j
->ldj
));
1743 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_LOWPRIORITYIO
)) {
1744 int lowprimib
[] = { CTL_KERN
, KERN_PROC_LOW_PRI_IO
};
1747 if (sysctl(lowprimib
, sizeof(lowprimib
) / sizeof(lowprimib
[0]), NULL
, NULL
, &val
, sizeof(val
)) == -1)
1748 job_log_error(j
, LOG_WARNING
, "sysctl(\"%s\")", "kern.proc_low_pri_io");
1750 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_ROOTDIRECTORY
))) {
1754 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_GROUPNAME
))) {
1755 gre
= getgrnam(tmpstr
);
1757 gre_g
= gre
->gr_gid
;
1758 if (-1 == setgid(gre_g
)) {
1759 job_log_error(j
, LOG_ERR
, "setgid(%d)", gre_g
);
1763 job_log(j
, LOG_ERR
, "getgrnam(\"%s\") failed", tmpstr
);
1767 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_USERNAME
))) {
1768 struct passwd
*pwe
= getpwnam(tmpstr
);
1770 uid_t pwe_u
= pwe
->pw_uid
;
1771 uid_t pwe_g
= pwe
->pw_gid
;
1773 if (pwe
->pw_expire
&& time(NULL
) >= pwe
->pw_expire
) {
1774 job_log(j
, LOG_ERR
, "expired account: %s", tmpstr
);
1777 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INITGROUPS
)) {
1778 if (-1 == initgroups(tmpstr
, gre
? gre_g
: pwe_g
)) {
1779 job_log_error(j
, LOG_ERR
, "initgroups()");
1784 if (-1 == setgid(pwe_g
)) {
1785 job_log_error(j
, LOG_ERR
, "setgid(%d)", pwe_g
);
1789 if (-1 == setuid(pwe_u
)) {
1790 job_log_error(j
, LOG_ERR
, "setuid(%d)", pwe_u
);
1794 job_log(j
, LOG_WARNING
, "getpwnam(\"%s\") failed", tmpstr
);
1798 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_WORKINGDIRECTORY
)))
1800 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_UMASK
))
1801 umask(job_get_integer(j
->ldj
, LAUNCH_JOBKEY_UMASK
));
1802 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDOUTPATH
))) {
1803 int sofd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1805 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1807 dup2(sofd
, STDOUT_FILENO
);
1811 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDERRORPATH
))) {
1812 int sefd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1814 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1816 dup2(sefd
, STDERR_FILENO
);
1820 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
)))
1821 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1826 #ifdef PID1_REAP_ADOPTED_CHILDREN
1827 __private_extern__
int pid1_child_exit_status
= 0;
1828 static void pid1waitpid(void)
1832 while ((p
= waitpid(-1, &pid1_child_exit_status
, WNOHANG
)) > 0) {
1833 if (!launchd_check_pid(p
))
1839 static void do_shutdown(void)
1843 shutdown_in_progress
= true;
1845 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1847 TAILQ_FOREACH(j
, &jobs
, tqe
)
1850 if (getpid() == 1) {
1852 mach_start_shutdown(SIGTERM
);
1856 static void signal_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
1858 switch (kev
->ident
) {
1861 reload_launchd_config();
1866 #ifdef PID1_REAP_ADOPTED_CHILDREN
1868 /* <rdar://problem/3632556> Please automatically reap processes reparented to PID 1 */
1878 static void fs_callback(void)
1880 static bool mounted_volfs
= false;
1883 mounted_volfs
= true;
1885 if (pending_stdout
) {
1886 int fd
= open(pending_stdout
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1888 dup2(fd
, STDOUT_FILENO
);
1890 free(pending_stdout
);
1891 pending_stdout
= NULL
;
1894 if (pending_stderr
) {
1895 int fd
= open(pending_stderr
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1897 dup2(fd
, STDERR_FILENO
);
1899 free(pending_stderr
);
1900 pending_stderr
= NULL
;
1904 if (!mounted_volfs
) {
1905 int r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1907 if (-1 == r
&& errno
== ENOENT
) {
1908 mkdir(VOLFSDIR
, ACCESSPERMS
& ~(S_IWUSR
|S_IWGRP
|S_IWOTH
));
1909 r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1913 syslog(LOG_WARNING
, "mount(\"%s\", \"%s\", ...): %m", "volfs", VOLFSDIR
);
1915 mounted_volfs
= true;
1919 if (!launchd_inited
)
1920 launchd_server_init(false);
1923 static void readcfg_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1927 #ifdef PID1_REAP_ADOPTED_CHILDREN
1929 status
= pid1_child_exit_status
;
1932 if (-1 == waitpid(readcfg_pid
, &status
, 0)) {
1933 syslog(LOG_WARNING
, "waitpid(readcfg_pid, ...): %m");
1939 if (WIFEXITED(status
)) {
1940 if (WEXITSTATUS(status
))
1941 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited with status: %d", WEXITSTATUS(status
));
1942 } else if (WIFSIGNALED(status
)) {
1943 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally: %s", strsignal(WTERMSIG(status
)));
1945 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally");
1949 #ifdef EVFILT_MACH_IMPLEMENTED
1950 static void *mach_demand_loop(void *arg
__attribute__((unused
)))
1952 mach_msg_empty_rcv_t dummy
;
1954 mach_port_name_array_t members
;
1955 mach_msg_type_number_t membersCnt
;
1956 mach_port_status_t status
;
1957 mach_msg_type_number_t statusCnt
;
1963 * Receive indication of message on demand service
1964 * ports without actually receiving the message (we'll
1965 * let the actual server do that.
1967 kr
= mach_msg(&dummy
.header
, MACH_RCV_MSG
|MACH_RCV_LARGE
,
1968 0, 0, mach_demand_port_set
, 0, MACH_PORT_NULL
);
1969 if (kr
!= MACH_RCV_TOO_LARGE
) {
1970 syslog(LOG_WARNING
, "%s(): mach_msg(): %s", __func__
, mach_error_string(kr
));
1975 * Some port(s) now have messages on them, find out
1976 * which ones (there is no indication of which port
1977 * triggered in the MACH_RCV_TOO_LARGE indication).
1979 kr
= mach_port_get_set_status(mach_task_self(),
1980 mach_demand_port_set
, &members
, &membersCnt
);
1981 if (kr
!= KERN_SUCCESS
) {
1982 syslog(LOG_WARNING
, "%s(): mach_port_get_set_status(): %s", __func__
, mach_error_string(kr
));
1986 for (i
= 0; i
< membersCnt
; i
++) {
1987 statusCnt
= MACH_PORT_RECEIVE_STATUS_COUNT
;
1988 kr
= mach_port_get_attributes(mach_task_self(), members
[i
],
1989 MACH_PORT_RECEIVE_STATUS
, (mach_port_info_t
)&status
, &statusCnt
);
1990 if (kr
!= KERN_SUCCESS
) {
1991 syslog(LOG_WARNING
, "%s(): mach_port_get_attributes(): %s", __func__
, mach_error_string(kr
));
1996 * For each port with messages, take it out of the
1997 * demand service portset, and inform the main thread
1998 * that it might have to start the server responsible
2001 if (status
.mps_msgcount
) {
2002 kr
= mach_port_move_member(mach_task_self(), members
[i
], MACH_PORT_NULL
);
2003 if (kr
!= KERN_SUCCESS
) {
2004 syslog(LOG_WARNING
, "%s(): mach_port_move_member(): %s", __func__
, mach_error_string(kr
));
2007 write(machcbwritefd
, &(members
[i
]), sizeof(members
[i
]));
2011 kr
= vm_deallocate(mach_task_self(), (vm_address_t
) members
,
2012 (vm_size_t
) membersCnt
* sizeof(mach_port_name_t
));
2013 if (kr
!= KERN_SUCCESS
) {
2014 syslog(LOG_WARNING
, "%s(): vm_deallocate(): %s", __func__
, mach_error_string(kr
));
2023 static void reload_launchd_config(void)
2026 static char *ldconf
= PID1LAUNCHD_CONF
;
2027 const char *h
= getenv("HOME");
2029 if (h
&& ldconf
== PID1LAUNCHD_CONF
)
2030 asprintf(&ldconf
, "%s/%s", h
, LAUNCHD_CONF
);
2035 if (lstat(ldconf
, &sb
) == 0) {
2037 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
2038 readcfg_pid
= fork_with_bootstrap_port(launchd_bootstrap_port
);
2039 if (readcfg_pid
== 0) {
2042 sprintf(nbuf
, "%d", spair
[1]);
2043 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
2044 int fd
= open(ldconf
, O_RDONLY
);
2046 syslog(LOG_ERR
, "open(\"%s\"): %m", ldconf
);
2049 dup2(fd
, STDIN_FILENO
);
2051 execl(LAUNCHCTL_PATH
, LAUNCHCTL_PATH
, NULL
);
2052 syslog(LOG_ERR
, "execl(\"%s\", ...): %m", LAUNCHCTL_PATH
);
2054 } else if (readcfg_pid
== -1) {
2057 syslog(LOG_ERR
, "fork(): %m");
2061 ipc_open(_fd(spair
[0]), NULL
);
2062 if (kevent_mod(readcfg_pid
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqreadcfg_callback
) == -1)
2063 syslog(LOG_ERR
, "kevent_mod(EVFILT_PROC, &kqreadcfg_callback): %m");
2068 static void conceive_firstborn(char *argv
[])
2070 launch_data_t r
, d
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2071 launch_data_t args
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
2072 launch_data_t l
= launch_data_new_string("com.apple.launchd.firstborn");
2075 for (i
= 0; *argv
; argv
++, i
++)
2076 launch_data_array_set_index(args
, launch_data_new_string(*argv
), i
);
2078 launch_data_dict_insert(d
, args
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
2079 launch_data_dict_insert(d
, l
, LAUNCH_JOBKEY_LABEL
);
2083 launch_data_free(r
);
2084 launch_data_free(d
);
2086 TAILQ_FIRST(&jobs
)->firstborn
= true;
2089 static void loopback_setup(void)
2091 struct ifaliasreq ifra
;
2092 struct in6_aliasreq ifra6
;
2096 memset(&ifr
, 0, sizeof(ifr
));
2097 strcpy(ifr
.ifr_name
, "lo0");
2099 if (-1 == (s
= socket(AF_INET
, SOCK_DGRAM
, 0)))
2100 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET");
2101 if (-1 == (s6
= socket(AF_INET6
, SOCK_DGRAM
, 0)))
2102 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET6");
2104 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) == -1) {
2105 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2107 ifr
.ifr_flags
|= IFF_UP
;
2109 if (ioctl(s
, SIOCSIFFLAGS
, &ifr
) == -1)
2110 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2113 memset(&ifr
, 0, sizeof(ifr
));
2114 strcpy(ifr
.ifr_name
, "lo0");
2116 if (ioctl(s6
, SIOCGIFFLAGS
, &ifr
) == -1) {
2117 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2119 ifr
.ifr_flags
|= IFF_UP
;
2121 if (ioctl(s6
, SIOCSIFFLAGS
, &ifr
) == -1)
2122 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2125 memset(&ifra
, 0, sizeof(ifra
));
2126 strcpy(ifra
.ifra_name
, "lo0");
2128 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_family
= AF_INET
;
2129 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
2130 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_len
= sizeof(struct sockaddr_in
);
2131 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_family
= AF_INET
;
2132 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
.s_addr
= htonl(IN_CLASSA_NET
);
2133 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_len
= sizeof(struct sockaddr_in
);
2135 if (ioctl(s
, SIOCAIFADDR
, &ifra
) == -1)
2136 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv4): %m");
2138 memset(&ifra6
, 0, sizeof(ifra6
));
2139 strcpy(ifra6
.ifra_name
, "lo0");
2141 ifra6
.ifra_addr
.sin6_family
= AF_INET6
;
2142 ifra6
.ifra_addr
.sin6_addr
= in6addr_loopback
;
2143 ifra6
.ifra_addr
.sin6_len
= sizeof(struct sockaddr_in6
);
2144 ifra6
.ifra_prefixmask
.sin6_family
= AF_INET6
;
2145 memset(&ifra6
.ifra_prefixmask
.sin6_addr
, 0xff, sizeof(struct in6_addr
));
2146 ifra6
.ifra_prefixmask
.sin6_len
= sizeof(struct sockaddr_in6
);
2147 ifra6
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
2148 ifra6
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
2150 if (ioctl(s6
, SIOCAIFADDR_IN6
, &ifra6
) == -1)
2151 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv6): %m");
2157 static void workaround3048875(int argc
, char *argv
[])
2160 char **ap
, *newargv
[100], *p
= argv
[1];
2162 if (argc
== 1 || argc
> 2)
2165 newargv
[0] = argv
[0];
2166 for (ap
= newargv
+ 1, i
= 1; ap
< &newargv
[100]; ap
++, i
++) {
2167 if ((*ap
= strsep(&p
, " \t")) == NULL
)
2178 execv(newargv
[0], newargv
);
2181 static launch_data_t
adjust_rlimits(launch_data_t in
)
2183 static struct rlimit
*l
= NULL
;
2184 static size_t lsz
= sizeof(struct rlimit
) * RLIM_NLIMITS
;
2185 struct rlimit
*ltmp
;
2190 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
2191 if (getrlimit(i
, l
+ i
) == -1)
2192 syslog(LOG_WARNING
, "getrlimit(): %m");
2197 ltmp
= launch_data_get_opaque(in
);
2198 ltmpsz
= launch_data_get_opaque_size(in
);
2201 syslog(LOG_WARNING
, "Too much rlimit data sent!");
2205 for (i
= 0; i
< (ltmpsz
/ sizeof(struct rlimit
)); i
++) {
2206 if (ltmp
[i
].rlim_cur
== l
[i
].rlim_cur
&& ltmp
[i
].rlim_max
== l
[i
].rlim_max
)
2209 if (readcfg_pid
&& getpid() == 1) {
2210 int gmib
[] = { CTL_KERN
, KERN_MAXPROC
};
2211 int pmib
[] = { CTL_KERN
, KERN_MAXPROCPERUID
};
2212 const char *gstr
= "kern.maxproc";
2213 const char *pstr
= "kern.maxprocperuid";
2214 int gval
= ltmp
[i
].rlim_max
;
2215 int pval
= ltmp
[i
].rlim_cur
;
2218 gmib
[1] = KERN_MAXFILES
;
2219 pmib
[1] = KERN_MAXFILESPERPROC
;
2220 gstr
= "kern.maxfiles";
2221 pstr
= "kern.maxfilesperproc";
2224 /* kernel will not clamp to this value, we must */
2225 if (gval
> (2048 + 20))
2231 if (sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)) == -1)
2232 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", gstr
);
2233 if (sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)) == -1)
2234 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", pstr
);
2236 if (setrlimit(i
, ltmp
+ i
) == -1)
2237 syslog(LOG_WARNING
, "setrlimit(): %m");
2238 /* the kernel may have clamped the values we gave it */
2239 if (getrlimit(i
, l
+ i
) == -1)
2240 syslog(LOG_WARNING
, "getrlimit(): %m");
2244 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);
2247 __private_extern__
void launchd_SessionCreate(const char *who
)
2249 void *seclib
= dlopen(SECURITY_LIB
, RTLD_LAZY
);
2250 OSStatus (*sescr
)(SessionCreationFlags flags
, SessionAttributeBits attributes
);
2253 sescr
= dlsym(seclib
, "SessionCreate");
2256 OSStatus scr
= sescr(0, 0);
2258 syslog(LOG_WARNING
, "%s: SessionCreate() failed: %d", who
, scr
);
2260 syslog(LOG_WARNING
, "%s: couldn't find SessionCreate() in %s", who
, SECURITY_LIB
);
2265 syslog(LOG_WARNING
, "%s: dlopen(\"%s\",...): %s", who
, SECURITY_LIB
, dlerror());
2269 static int dir_has_files(const char *path
)
2271 DIR *dd
= opendir(path
);
2278 while ((de
= readdir(dd
))) {
2279 if (strcmp(de
->d_name
, ".") && strcmp(de
->d_name
, "..")) {
2289 static void job_set_alarm(struct jobcb
*j
)
2293 later
= cronemu(j
->start_cal_interval
->tm_mon
, j
->start_cal_interval
->tm_mday
,
2294 j
->start_cal_interval
->tm_hour
, j
->start_cal_interval
->tm_min
);
2296 if (j
->start_cal_interval
->tm_wday
!= -1) {
2297 time_t otherlater
= cronemu_wday(j
->start_cal_interval
->tm_wday
,
2298 j
->start_cal_interval
->tm_hour
, j
->start_cal_interval
->tm_min
);
2300 if (-1 != j
->start_cal_interval
->tm_mday
) {
2301 later
= later
< otherlater
? later
: otherlater
;
2307 if (-1 == kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_ABSOLUTE
|NOTE_SECONDS
, later
, j
)) {
2308 job_log_error(j
, LOG_ERR
, "adding kevent alarm");
2310 job_log(j
, LOG_INFO
, "scheduled to run again at: %s", ctime(&later
));
2314 static void job_log_error(struct jobcb
*j
, int pri
, const char *msg
, ...)
2316 size_t newmsg_sz
= strlen(msg
) + strlen(j
->label
) + 200;
2317 char *newmsg
= alloca(newmsg_sz
);
2320 sprintf(newmsg
, "%s: %s: %s", j
->label
, msg
, strerror(errno
));
2324 vsyslog(pri
, newmsg
, ap
);
2329 static void job_log(struct jobcb
*j
, int pri
, const char *msg
, ...)
2331 size_t newmsg_sz
= strlen(msg
) + sizeof(": ") + strlen(j
->label
);
2332 char *newmsg
= alloca(newmsg_sz
);
2335 sprintf(newmsg
, "%s: %s", j
->label
, msg
);
2339 vsyslog(pri
, newmsg
, ap
);
2344 static void async_callback(void)
2346 struct timespec timeout
= { 0, 0 };
2349 switch (kevent(asynckq
, NULL
, 0, &kev
, 1, &timeout
)) {
2351 syslog(LOG_DEBUG
, "kevent(): %m");
2354 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
2358 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
2362 static void testfd_or_openfd(int fd
, const char *path
, int flags
)
2366 if (-1 != (tmpfd
= dup(fd
))) {
2369 if (-1 == (tmpfd
= open(path
, flags
))) {
2370 syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
2371 } else if (tmpfd
!= fd
) {
2379 cronemu(int mon
, int mday
, int hour
, int min
)
2381 struct tm workingtm
;
2385 workingtm
= *localtime(&now
);
2387 workingtm
.tm_isdst
= -1;
2388 workingtm
.tm_sec
= 0;
2391 while (!cronemu_mon(&workingtm
, mon
, mday
, hour
, min
)) {
2392 workingtm
.tm_year
++;
2393 workingtm
.tm_mon
= 0;
2394 workingtm
.tm_mday
= 1;
2395 workingtm
.tm_hour
= 0;
2396 workingtm
.tm_min
= 0;
2400 return mktime(&workingtm
);
2404 cronemu_wday(int wday
, int hour
, int min
)
2406 struct tm workingtm
;
2410 workingtm
= *localtime(&now
);
2412 workingtm
.tm_isdst
= -1;
2413 workingtm
.tm_sec
= 0;
2419 while (workingtm
.tm_wday
!= wday
|| !cronemu_hour(&workingtm
, hour
, min
)) {
2420 workingtm
.tm_mday
++;
2421 workingtm
.tm_hour
= 0;
2422 workingtm
.tm_min
= 0;
2423 cronemu_hour(&workingtm
, hour
, min
);
2427 return mktime(&workingtm
);
2431 cronemu_mon(struct tm
*wtm
, int mon
, int mday
, int hour
, int min
)
2434 struct tm workingtm
= *wtm
;
2437 while (!cronemu_mday(&workingtm
, mday
, hour
, min
)) {
2439 workingtm
.tm_mday
= 1;
2440 workingtm
.tm_hour
= 0;
2441 workingtm
.tm_min
= 0;
2442 carrytest
= workingtm
.tm_mon
;
2444 if (carrytest
!= workingtm
.tm_mon
)
2451 if (mon
< wtm
->tm_mon
)
2454 if (mon
> wtm
->tm_mon
) {
2461 return cronemu_mday(wtm
, mday
, hour
, min
);
2465 cronemu_mday(struct tm
*wtm
, int mday
, int hour
, int min
)
2468 struct tm workingtm
= *wtm
;
2471 while (!cronemu_hour(&workingtm
, hour
, min
)) {
2472 workingtm
.tm_mday
++;
2473 workingtm
.tm_hour
= 0;
2474 workingtm
.tm_min
= 0;
2475 carrytest
= workingtm
.tm_mday
;
2477 if (carrytest
!= workingtm
.tm_mday
)
2484 if (mday
< wtm
->tm_mday
)
2487 if (mday
> wtm
->tm_mday
) {
2488 wtm
->tm_mday
= mday
;
2493 return cronemu_hour(wtm
, hour
, min
);
2497 cronemu_hour(struct tm
*wtm
, int hour
, int min
)
2500 struct tm workingtm
= *wtm
;
2503 while (!cronemu_min(&workingtm
, min
)) {
2504 workingtm
.tm_hour
++;
2505 workingtm
.tm_min
= 0;
2506 carrytest
= workingtm
.tm_hour
;
2508 if (carrytest
!= workingtm
.tm_hour
)
2515 if (hour
< wtm
->tm_hour
)
2518 if (hour
> wtm
->tm_hour
) {
2519 wtm
->tm_hour
= hour
;
2523 return cronemu_min(wtm
, min
);
2527 cronemu_min(struct tm
*wtm
, int min
)
2532 if (min
< wtm
->tm_min
)
2535 if (min
> wtm
->tm_min
) {