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)));
146 static void job_prep_log_msg(struct jobcb
*j
, char *buf
, const char *msg
, int err
);
148 static void ipc_open(int fd
, struct jobcb
*j
);
149 static void ipc_close(struct conncb
*c
);
150 static void ipc_callback(void *, struct kevent
*);
151 static void ipc_readmsg(launch_data_t msg
, void *context
);
152 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
);
154 #ifdef PID1_REAP_ADOPTED_CHILDREN
155 static void pid1waitpid(void);
156 static bool launchd_check_pid(pid_t p
);
158 static void pid1_magic_init(bool sflag
, bool vflag
, bool xflag
);
159 static void launchd_server_init(bool create_session
);
160 static void conceive_firstborn(char *argv
[]);
162 #ifdef EVFILT_MACH_IMPLEMENTED
163 static void *mach_demand_loop(void *);
164 static void mach_callback(void *, struct kevent
*);
165 static kq_callback kqmach_callback
= mach_callback
;
168 static void usage(FILE *where
);
169 static int _fd(int fd
);
171 static void loopback_setup(void);
172 static void workaround3048875(int argc
, char *argv
[]);
173 static void reload_launchd_config(void);
174 static int dir_has_files(const char *path
);
175 static void testfd_or_openfd(int fd
, const char *path
, int flags
);
176 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
);
177 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
);
179 static time_t cronemu(int mon
, int mday
, int hour
, int min
);
180 static time_t cronemu_wday(int wday
, int hour
, int min
);
181 static bool cronemu_mon(struct tm
*wtm
, int mon
, int mday
, int hour
, int min
);
182 static bool cronemu_mday(struct tm
*wtm
, int mday
, int hour
, int min
);
183 static bool cronemu_hour(struct tm
*wtm
, int hour
, int min
);
184 static bool cronemu_min(struct tm
*wtm
, int min
);
186 static size_t total_children
= 0;
187 static pid_t readcfg_pid
= 0;
188 static pid_t launchd_proper_pid
= 0;
189 static bool launchd_inited
= false;
190 static bool shutdown_in_progress
= false;
191 static pthread_t mach_server_loop_thread
;
192 mach_port_t launchd_bootstrap_port
= MACH_PORT_NULL
;
193 sigset_t blocked_signals
= 0;
194 static char *pending_stdout
= NULL
;
195 static char *pending_stderr
= NULL
;
197 int main(int argc
, char *argv
[])
199 static const int sigigns
[] = { SIGHUP
, SIGINT
, SIGPIPE
, SIGALRM
,
200 SIGTERM
, SIGURG
, SIGTSTP
, SIGTSTP
, SIGCONT
, /*SIGCHLD,*/
201 SIGTTIN
, SIGTTOU
, SIGIO
, SIGXCPU
, SIGXFSZ
, SIGVTALRM
, SIGPROF
,
202 SIGWINCH
, SIGINFO
, SIGUSR1
, SIGUSR2
};
205 bool sflag
= false, xflag
= false, vflag
= false, dflag
= false;
209 workaround3048875(argc
, argv
);
214 testfd_or_openfd(STDIN_FILENO
, _PATH_DEVNULL
, O_RDONLY
);
215 testfd_or_openfd(STDOUT_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
216 testfd_or_openfd(STDERR_FILENO
, _PATH_DEVNULL
, O_WRONLY
);
218 openlog(getprogname(), LOG_CONS
|(getpid() != 1 ? LOG_PID
|LOG_PERROR
: 0), LOG_LAUNCHD
);
219 setlogmask(LOG_UPTO(LOG_NOTICE
));
221 while ((ch
= getopt(argc
, argv
, "dhsvx")) != -1) {
223 case 'd': dflag
= true; break;
224 case 's': sflag
= true; break;
225 case 'x': xflag
= true; break;
226 case 'v': vflag
= true; break;
227 case 'h': usage(stdout
); break;
229 syslog(LOG_WARNING
, "ignoring unknown arguments");
237 if (dflag
&& daemon(0, 0) == -1)
238 syslog(LOG_WARNING
, "couldn't daemonize: %m");
240 if ((mainkq
= kqueue()) == -1) {
241 syslog(LOG_EMERG
, "kqueue(): %m");
245 if ((asynckq
= kqueue()) == -1) {
246 syslog(LOG_ERR
, "kqueue(): %m");
250 if (kevent_mod(asynckq
, EVFILT_READ
, EV_ADD
, 0, 0, &kqasync_callback
) == -1) {
251 syslog(LOG_ERR
, "kevent_mod(asynckq, EVFILT_READ): %m");
255 sigemptyset(&blocked_signals
);
257 for (i
= 0; i
< (sizeof(sigigns
) / sizeof(int)); i
++) {
258 if (kevent_mod(sigigns
[i
], EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
259 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", sigigns
[i
]);
260 sigaddset(&blocked_signals
, sigigns
[i
]);
261 signal(sigigns
[i
], SIG_IGN
);
264 /* sigh... ignoring SIGCHLD has side effects: we can't call wait*() */
265 if (kevent_mod(SIGCHLD
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, &kqsignal_callback
) == -1)
266 syslog(LOG_ERR
, "failed to add kevent for signal: %d: %m", SIGCHLD
);
269 pid1_magic_init(sflag
, vflag
, xflag
);
271 launchd_bootstrap_port
= bootstrap_port
;
272 launchd_server_init(argv
[0] ? true : false);
275 /* do this after pid1_magic_init() to not catch ourselves mounting stuff */
276 if (kevent_mod(0, EVFILT_FS
, EV_ADD
, 0, 0, &kqfs_callback
) == -1)
277 syslog(LOG_ERR
, "kevent_mod(EVFILT_FS, &kqfs_callback): %m");
281 conceive_firstborn(argv
);
283 reload_launchd_config();
286 job_start(TAILQ_FIRST(&jobs
));
289 static struct timespec timeout
= { 30, 0 };
290 struct timespec
*timeoutp
= NULL
;
293 if (readcfg_pid
== 0)
296 if (TAILQ_EMPTY(&jobs
)) {
297 /* launched on demand */
299 } else if (shutdown_in_progress
&& total_children
== 0) {
304 switch (kevent(mainkq
, NULL
, 0, &kev
, 1, timeoutp
)) {
306 syslog(LOG_DEBUG
, "kevent(): %m");
309 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
315 syslog(LOG_DEBUG
, "kevent(): spurious return with infinite timeout");
318 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
324 static void pid1_magic_init(bool sflag
, bool vflag
, bool xflag
)
327 int memmib
[2] = { CTL_HW
, HW_MEMSIZE
};
328 int mvnmib
[2] = { CTL_KERN
, KERN_MAXVNODES
};
329 int hnmib
[2] = { CTL_KERN
, KERN_HOSTNAME
};
331 int tfp_r_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_READ_GROUP
};
332 int tfp_rw_mib
[3] = { CTL_KERN
, KERN_TFP
, KERN_TFP_RW_GROUP
};
334 gid_t tfp_rw_gid
= 0;
335 struct group
*tfp_gr
;
339 size_t memsz
= sizeof(mem
);
343 if ((tfp_gr
= getgrnam("procview"))) {
344 tfp_r_gid
= tfp_gr
->gr_gid
;
345 sysctl(tfp_r_mib
, 3, NULL
, NULL
, &tfp_r_gid
, sizeof(tfp_r_gid
));
348 if ((tfp_gr
= getgrnam("procmod"))) {
349 tfp_rw_gid
= tfp_gr
->gr_gid
;
350 sysctl(tfp_rw_mib
, 3, NULL
, NULL
, &tfp_rw_gid
, sizeof(tfp_rw_gid
));
354 setpriority(PRIO_PROCESS
, 0, -1);
357 syslog(LOG_ERR
, "setsid(): %m");
359 if (chdir("/") == -1)
360 syslog(LOG_ERR
, "chdir(\"/\"): %m");
362 if (sysctl(memmib
, 2, &mem
, &memsz
, NULL
, 0) == -1) {
363 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "hw.physmem");
365 /* The following assignment of mem to itself if the size
366 * of data returned is 32 bits instead of 64 is a clever
367 * C trick to move the 32 bits on big endian systems to
368 * the least significant bytes of the 64 mem variable.
370 * On little endian systems, this is effectively a no-op.
373 mem
= *(uint32_t *)&mem
;
374 mvn
= mem
/ (64 * 1024) + 1024;
375 if (sysctl(mvnmib
, 2, NULL
, NULL
, &mvn
, sizeof(mvn
)) == -1)
376 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.maxvnodes");
378 if (sysctl(hnmib
, 2, NULL
, NULL
, "localhost", sizeof("localhost")) == -1)
379 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", "kern.hostname");
381 if (setlogin("root") == -1)
382 syslog(LOG_ERR
, "setlogin(\"root\"): %m");
386 if (mount("fdesc", "/dev", MNT_UNION
, NULL
) == -1)
387 syslog(LOG_ERR
, "mount(\"%s\", \"%s\", ...): %m", "fdesc", "/dev/");
389 setenv("PATH", _PATH_STDPATH
, 1);
391 launchd_bootstrap_port
= mach_init_init();
392 task_set_bootstrap_port(mach_task_self(), launchd_bootstrap_port
);
393 bootstrap_port
= MACH_PORT_NULL
;
395 pthread_attr_init(&attr
);
396 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
398 pthr_r
= pthread_create(&mach_server_loop_thread
, &attr
, mach_server_loop
, NULL
);
400 syslog(LOG_ERR
, "pthread_create(mach_server_loop): %s", strerror(pthr_r
));
404 pthread_attr_destroy(&attr
);
406 init_boot(sflag
, vflag
, xflag
);
410 #ifdef PID1_REAP_ADOPTED_CHILDREN
411 static bool launchd_check_pid(pid_t p
)
416 TAILQ_FOREACH(j
, &jobs
, tqe
) {
418 EV_SET(&kev
, p
, EVFILT_PROC
, 0, 0, 0, j
);
419 j
->kqjob_callback(j
, &kev
);
424 if (p
== readcfg_pid
) {
425 readcfg_callback(NULL
, NULL
);
433 static char *sockdir
= NULL
;
434 static char *sockpath
= NULL
;
436 static void launchd_clean_up(void)
438 if (launchd_proper_pid
!= getpid())
444 if (-1 == unlink(sockpath
))
445 syslog(LOG_WARNING
, "unlink(\"%s\"): %m", sockpath
);
446 else if (-1 == rmdir(sockdir
))
447 syslog(LOG_WARNING
, "rmdir(\"%s\"): %m", sockdir
);
453 static void launchd_server_init(bool create_session
)
455 struct sockaddr_un sun
;
457 int r
, fd
= -1, ourdirfd
= -1;
460 memset(&sun
, 0, sizeof(sun
));
461 sun
.sun_family
= AF_UNIX
;
463 if (create_session
) {
464 snprintf(ourdir
, sizeof(ourdir
), "%s/%u.%u", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
465 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u.%u/sock", LAUNCHD_SOCK_PREFIX
, getuid(), getpid());
466 setenv(LAUNCHD_SOCKET_ENV
, sun
.sun_path
, 1);
468 snprintf(ourdir
, sizeof(ourdir
), "%s/%u", LAUNCHD_SOCK_PREFIX
, getuid());
469 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/%u/sock", LAUNCHD_SOCK_PREFIX
, getuid());
475 if (mkdir(LAUNCHD_SOCK_PREFIX
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) == -1) {
476 if (errno
== EROFS
) {
478 } else if (errno
== EEXIST
) {
480 stat(LAUNCHD_SOCK_PREFIX
, &sb
);
481 if (!S_ISDIR(sb
.st_mode
)) {
483 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
487 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
493 if (mkdir(ourdir
, S_IRWXU
) == -1) {
494 if (errno
== EROFS
) {
496 } else if (errno
== EEXIST
) {
499 if (!S_ISDIR(sb
.st_mode
)) {
501 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
505 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", ourdir
);
510 if (chown(ourdir
, getuid(), getgid()) == -1)
511 syslog(LOG_WARNING
, "chown(\"%s\"): %m", ourdir
);
516 ourdirfd
= _fd(open(ourdir
, O_RDONLY
));
517 if (ourdirfd
== -1) {
518 syslog(LOG_ERR
, "open(\"%s\"): %m", ourdir
);
522 if (flock(ourdirfd
, LOCK_EX
|LOCK_NB
) == -1) {
523 if (errno
== EWOULDBLOCK
) {
526 syslog(LOG_ERR
, "flock(\"%s\"): %m", ourdir
);
531 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
533 syslog(LOG_ERR
, "unlink(\"thesocket\"): %m");
536 if ((fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) == -1) {
537 syslog(LOG_ERR
, "socket(\"thesocket\"): %m");
540 oldmask
= umask(077);
541 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
545 syslog(LOG_ERR
, "bind(\"thesocket\"): %m");
549 if (listen(fd
, SOMAXCONN
) == -1) {
550 syslog(LOG_ERR
, "listen(\"thesocket\"): %m");
554 if (kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqlisten_callback
) == -1) {
555 syslog(LOG_ERR
, "kevent_mod(\"thesocket\", EVFILT_READ): %m");
559 launchd_inited
= true;
561 sockdir
= strdup(ourdir
);
562 sockpath
= strdup(sun
.sun_path
);
564 launchd_proper_pid
= getpid();
565 atexit(launchd_clean_up
);
571 if (!launchd_inited
) {
579 static long long job_get_integer(launch_data_t j
, const char *key
)
581 launch_data_t t
= launch_data_dict_lookup(j
, key
);
583 return launch_data_get_integer(t
);
588 static const char *job_get_string(launch_data_t j
, const char *key
)
590 launch_data_t t
= launch_data_dict_lookup(j
, key
);
592 return launch_data_get_string(t
);
597 static const char *job_get_file2exec(launch_data_t j
)
599 launch_data_t tmpi
, tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAM
);
602 return launch_data_get_string(tmp
);
604 tmp
= launch_data_dict_lookup(j
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
606 tmpi
= launch_data_array_get_index(tmp
, 0);
608 return launch_data_get_string(tmpi
);
614 static bool job_get_bool(launch_data_t j
, const char *key
)
616 launch_data_t t
= launch_data_dict_lookup(j
, key
);
618 return launch_data_get_bool(t
);
623 static void ipc_open(int fd
, struct jobcb
*j
)
625 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
627 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
629 c
->kqconn_callback
= ipc_callback
;
630 c
->conn
= launchd_fdopen(fd
);
632 TAILQ_INSERT_TAIL(&connections
, c
, tqe
);
633 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
);
636 static void simple_zombie_reaper(void *obj
__attribute__((unused
)), struct kevent
*kev
)
638 waitpid(kev
->ident
, NULL
, 0);
641 static void listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
643 struct sockaddr_un sun
;
644 socklen_t sl
= sizeof(sun
);
647 if ((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) == -1) {
654 static void ipc_callback(void *obj
, struct kevent
*kev
)
656 struct conncb
*c
= obj
;
659 if (kev
->filter
== EVFILT_READ
) {
660 if (launchd_msg_recv(c
->conn
, ipc_readmsg
, c
) == -1 && errno
!= EAGAIN
) {
661 if (errno
!= ECONNRESET
)
662 syslog(LOG_DEBUG
, "%s(): recv: %m", __func__
);
665 } else if (kev
->filter
== EVFILT_WRITE
) {
666 r
= launchd_msg_send(c
->conn
, NULL
);
668 if (errno
!= EAGAIN
) {
669 syslog(LOG_DEBUG
, "%s(): send: %m", __func__
);
673 kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
);
676 syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
681 static void set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
683 setenv(key
, launch_data_get_string(obj
), 1);
686 static void launch_data_close_fds(launch_data_t o
)
690 switch (launch_data_get_type(o
)) {
691 case LAUNCH_DATA_DICTIONARY
:
692 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_close_fds
, NULL
);
694 case LAUNCH_DATA_ARRAY
:
695 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
696 launch_data_close_fds(launch_data_array_get_index(o
, i
));
699 if (launch_data_get_fd(o
) != -1)
700 close(launch_data_get_fd(o
));
707 static void launch_data_revoke_fds(launch_data_t o
)
711 switch (launch_data_get_type(o
)) {
712 case LAUNCH_DATA_DICTIONARY
:
713 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))launch_data_revoke_fds
, NULL
);
715 case LAUNCH_DATA_ARRAY
:
716 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
717 launch_data_revoke_fds(launch_data_array_get_index(o
, i
));
720 launch_data_set_fd(o
, -1);
727 static void job_ignore_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
729 struct jobcb
*j
= cookie
;
733 switch (launch_data_get_type(o
)) {
734 case LAUNCH_DATA_DICTIONARY
:
735 launch_data_dict_iterate(o
, job_ignore_fds
, cookie
);
737 case LAUNCH_DATA_ARRAY
:
738 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
739 job_ignore_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
742 fd
= launch_data_get_fd(o
);
744 job_log(j
, LOG_DEBUG
, "Ignoring FD: %d", fd
);
745 kevent_mod(fd
, EVFILT_READ
, EV_DELETE
, 0, 0, NULL
);
753 static void job_ignore(struct jobcb
*j
)
755 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
759 job_ignore_fds(j_sockets
, NULL
, j
);
761 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
762 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
764 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
765 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
);
769 static void job_watch_fds(launch_data_t o
, const char *key
__attribute__((unused
)), void *cookie
)
771 struct jobcb
*j
= cookie
;
775 switch (launch_data_get_type(o
)) {
776 case LAUNCH_DATA_DICTIONARY
:
777 launch_data_dict_iterate(o
, job_watch_fds
, cookie
);
779 case LAUNCH_DATA_ARRAY
:
780 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
781 job_watch_fds(launch_data_array_get_index(o
, i
), NULL
, cookie
);
784 fd
= launch_data_get_fd(o
);
786 job_log(j
, LOG_DEBUG
, "Watching FD: %d", fd
);
787 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, cookie
);
795 static void job_watch(struct jobcb
*j
)
797 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
798 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
799 launch_data_t j_sockets
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOCKETS
);
803 job_watch_fds(j_sockets
, NULL
, &j
->kqjob_callback
);
805 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
806 if (-1 == j
->vnodes
[i
]) {
807 launch_data_t ld_idx
= launch_data_array_get_index(ld_vnodes
, i
);
808 const char *thepath
= launch_data_get_string(ld_idx
);
810 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
811 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
813 kevent_mod(j
->vnodes
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
814 NOTE_WRITE
|NOTE_EXTEND
|NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
|NOTE_ATTRIB
|NOTE_LINK
,
815 0, &j
->kqjob_callback
);
818 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
819 kevent_mod(j
->qdirs
[i
], EVFILT_VNODE
, EV_ADD
|EV_CLEAR
,
820 NOTE_WRITE
|NOTE_EXTEND
|NOTE_ATTRIB
|NOTE_LINK
, 0, &j
->kqjob_callback
);
823 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
824 launch_data_t ld_idx
= launch_data_array_get_index(ld_qdirs
, i
);
825 const char *thepath
= launch_data_get_string(ld_idx
);
828 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
829 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
830 } else if (dcc_r
> 0 && !shutdown_in_progress
) {
837 static void job_stop(struct jobcb
*j
)
843 static void job_remove(struct jobcb
*j
)
848 job_log(j
, LOG_DEBUG
, "Removed");
850 TAILQ_REMOVE(&jobs
, j
, tqe
);
852 if (kevent_mod(j
->p
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqsimple_zombie_reaper
) == -1) {
858 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
859 launch_data_dict_iterate(tmp
, unsetup_job_env
, NULL
);
860 launch_data_close_fds(j
->ldj
);
861 launch_data_free(j
->ldj
);
864 for (i
= 0; i
< j
->vnodes_cnt
; i
++)
865 if (-1 != j
->vnodes
[i
])
869 for (i
= 0; i
< j
->qdirs_cnt
; i
++)
870 if (-1 != j
->qdirs
[i
])
874 if (j
->start_interval
)
875 kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
876 if (j
->start_cal_interval
) {
877 kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
878 free(j
->start_cal_interval
);
880 kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
884 struct readmsg_context
{
889 static void ipc_readmsg(launch_data_t msg
, void *context
)
891 struct readmsg_context rmc
= { context
, NULL
};
893 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(msg
)) {
894 launch_data_dict_iterate(msg
, ipc_readmsg2
, &rmc
);
895 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(msg
)) {
896 ipc_readmsg2(NULL
, launch_data_get_string(msg
), &rmc
);
898 rmc
.resp
= launch_data_new_errno(EINVAL
);
901 if (NULL
== rmc
.resp
)
902 rmc
.resp
= launch_data_new_errno(ENOSYS
);
904 launch_data_close_fds(msg
);
906 if (launchd_msg_send(rmc
.c
->conn
, rmc
.resp
) == -1) {
907 if (errno
== EAGAIN
) {
908 kevent_mod(launchd_getfd(rmc
.c
->conn
), EVFILT_WRITE
, EV_ADD
, 0, 0, &rmc
.c
->kqconn_callback
);
910 syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %m");
914 launch_data_free(rmc
.resp
);
918 attach_bonjourfds_to_job(launch_data_t o
, const char *key
, void *context
__attribute__((unused
)))
920 struct jobcb
*j
= NULL
;
922 TAILQ_FOREACH(j
, &jobs
, tqe
) {
923 if (strcmp(j
->label
, key
) == 0)
930 launch_data_dict_insert(j
->ldj
, launch_data_copy(o
), LAUNCH_JOBKEY_BONJOURFDS
);
931 launch_data_revoke_fds(o
);
934 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
936 struct readmsg_context
*rmc
= context
;
937 launch_data_t resp
= NULL
;
943 if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
944 TAILQ_FOREACH(j
, &jobs
, tqe
) {
945 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
947 resp
= launch_data_new_errno(0);
951 resp
= launch_data_new_errno(ESRCH
);
952 } else if (!strcmp(cmd
, LAUNCH_KEY_STOPJOB
)) {
953 TAILQ_FOREACH(j
, &jobs
, tqe
) {
954 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
956 resp
= launch_data_new_errno(0);
960 resp
= launch_data_new_errno(ESRCH
);
961 } else if (!strcmp(cmd
, LAUNCH_KEY_REMOVEJOB
)) {
962 TAILQ_FOREACH(j
, &jobs
, tqe
) {
963 if (!strcmp(j
->label
, launch_data_get_string(data
))) {
965 resp
= launch_data_new_errno(0);
969 resp
= launch_data_new_errno(ESRCH
);
970 } else if (!strcmp(cmd
, LAUNCH_KEY_SUBMITJOB
)) {
971 if (launch_data_get_type(data
) == LAUNCH_DATA_ARRAY
) {
975 resp
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
976 for (i
= 0; i
< launch_data_array_get_count(data
); i
++) {
977 tmp
= load_job(launch_data_array_get_index(data
, i
));
978 launch_data_array_set_index(resp
, tmp
, i
);
981 resp
= load_job(data
);
983 } else if (!strcmp(cmd
, LAUNCH_KEY_WORKAROUNDBONJOUR
)) {
984 launch_data_dict_iterate(data
, attach_bonjourfds_to_job
, NULL
);
985 resp
= launch_data_new_errno(0);
986 } else if (!strcmp(cmd
, LAUNCH_KEY_UNSETUSERENVIRONMENT
)) {
987 unsetenv(launch_data_get_string(data
));
988 resp
= launch_data_new_errno(0);
989 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUSERENVIRONMENT
)) {
990 char **tmpenviron
= environ
;
991 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
992 for (; *tmpenviron
; tmpenviron
++) {
994 launch_data_t s
= launch_data_alloc(LAUNCH_DATA_STRING
);
995 launch_data_set_string(s
, strchr(*tmpenviron
, '=') + 1);
996 strncpy(envkey
, *tmpenviron
, sizeof(envkey
));
997 *(strchr(envkey
, '=')) = '\0';
998 launch_data_dict_insert(resp
, s
, envkey
);
1000 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUSERENVIRONMENT
)) {
1001 launch_data_dict_iterate(data
, set_user_env
, NULL
);
1002 resp
= launch_data_new_errno(0);
1003 } else if (!strcmp(cmd
, LAUNCH_KEY_CHECKIN
)) {
1005 resp
= launch_data_copy(rmc
->c
->j
->ldj
);
1006 if (NULL
== launch_data_dict_lookup(resp
, LAUNCH_JOBKEY_TIMEOUT
)) {
1007 launch_data_t to
= launch_data_new_integer(LAUNCHD_MIN_JOB_RUN_TIME
);
1008 launch_data_dict_insert(resp
, to
, LAUNCH_JOBKEY_TIMEOUT
);
1010 rmc
->c
->j
->checkedin
= true;
1012 resp
= launch_data_new_errno(EACCES
);
1014 } else if (!strcmp(cmd
, LAUNCH_KEY_RELOADTTYS
)) {
1016 resp
= launch_data_new_errno(0);
1017 } else if (!strcmp(cmd
, LAUNCH_KEY_SHUTDOWN
)) {
1019 resp
= launch_data_new_errno(0);
1020 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBS
)) {
1021 resp
= get_jobs(NULL
);
1022 launch_data_revoke_fds(resp
);
1023 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRESOURCELIMITS
)) {
1024 resp
= adjust_rlimits(NULL
);
1025 } else if (!strcmp(cmd
, LAUNCH_KEY_SETRESOURCELIMITS
)) {
1026 resp
= adjust_rlimits(data
);
1027 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOB
)) {
1028 resp
= get_jobs(launch_data_get_string(data
));
1029 launch_data_revoke_fds(resp
);
1030 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBWITHHANDLES
)) {
1031 resp
= get_jobs(launch_data_get_string(data
));
1032 } else if (!strcmp(cmd
, LAUNCH_KEY_SETLOGMASK
)) {
1033 resp
= launch_data_new_integer(setlogmask(launch_data_get_integer(data
)));
1034 } else if (!strcmp(cmd
, LAUNCH_KEY_GETLOGMASK
)) {
1035 int oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1036 resp
= launch_data_new_integer(oldmask
);
1037 setlogmask(oldmask
);
1038 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUMASK
)) {
1039 resp
= launch_data_new_integer(umask(launch_data_get_integer(data
)));
1040 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUMASK
)) {
1041 mode_t oldmask
= umask(0);
1042 resp
= launch_data_new_integer(oldmask
);
1044 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGESELF
)) {
1045 struct rusage rusage
;
1046 getrusage(RUSAGE_SELF
, &rusage
);
1047 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1048 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGECHILDREN
)) {
1049 struct rusage rusage
;
1050 getrusage(RUSAGE_CHILDREN
, &rusage
);
1051 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
1052 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDOUT
)) {
1053 resp
= setstdio(STDOUT_FILENO
, data
);
1054 } else if (!strcmp(cmd
, LAUNCH_KEY_SETSTDERR
)) {
1055 resp
= setstdio(STDERR_FILENO
, data
);
1056 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHCONTROL
)) {
1057 batch_job_enable(launch_data_get_bool(data
), rmc
->c
);
1058 resp
= launch_data_new_errno(0);
1059 } else if (!strcmp(cmd
, LAUNCH_KEY_BATCHQUERY
)) {
1060 resp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1061 launch_data_set_bool(resp
, batch_disabler_count
== 0);
1067 static launch_data_t
setstdio(int d
, launch_data_t o
)
1069 launch_data_t resp
= launch_data_new_errno(0);
1071 if (launch_data_get_type(o
) == LAUNCH_DATA_STRING
) {
1072 char **where
= &pending_stderr
;
1073 if (d
== STDOUT_FILENO
)
1074 where
= &pending_stdout
;
1077 *where
= strdup(launch_data_get_string(o
));
1078 } else if (launch_data_get_type(o
) == LAUNCH_DATA_FD
) {
1079 dup2(launch_data_get_fd(o
), d
);
1081 launch_data_set_errno(resp
, EINVAL
);
1087 static void batch_job_enable(bool e
, struct conncb
*c
)
1089 if (e
&& c
->disabled_batch
) {
1090 batch_disabler_count
--;
1091 c
->disabled_batch
= 0;
1092 if (batch_disabler_count
== 0)
1093 kevent_mod(asynckq
, EVFILT_READ
, EV_ENABLE
, 0, 0, &kqasync_callback
);
1094 } else if (!e
&& !c
->disabled_batch
) {
1095 if (batch_disabler_count
== 0)
1096 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1097 batch_disabler_count
++;
1098 c
->disabled_batch
= 1;
1102 static launch_data_t
load_job(launch_data_t pload
)
1104 launch_data_t tmp
, resp
;
1107 bool startnow
, hasprog
= false, hasprogargs
= false;
1109 if ((label
= job_get_string(pload
, LAUNCH_JOBKEY_LABEL
))) {
1110 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1111 if (!strcmp(j
->label
, label
)) {
1112 resp
= launch_data_new_errno(EEXIST
);
1117 resp
= launch_data_new_errno(EINVAL
);
1121 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAM
))
1123 if (launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
))
1126 if (!hasprog
&& !hasprogargs
) {
1127 resp
= launch_data_new_errno(EINVAL
);
1131 j
= calloc(1, sizeof(struct jobcb
) + strlen(label
) + 1);
1132 strcpy(j
->label
, label
);
1133 j
->ldj
= launch_data_copy(pload
);
1134 launch_data_revoke_fds(pload
);
1135 j
->kqjob_callback
= job_callback
;
1138 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
) == NULL
) {
1139 tmp
= launch_data_alloc(LAUNCH_DATA_BOOL
);
1140 launch_data_set_bool(tmp
, true);
1141 launch_data_dict_insert(j
->ldj
, tmp
, LAUNCH_JOBKEY_ONDEMAND
);
1144 TAILQ_INSERT_TAIL(&jobs
, j
, tqe
);
1146 j
->debug
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_DEBUG
);
1148 startnow
= !job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1150 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_RUNATLOAD
))
1153 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
))) {
1156 j
->qdirs_cnt
= launch_data_array_get_count(tmp
);
1157 j
->qdirs
= malloc(sizeof(int) * j
->qdirs_cnt
);
1159 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1160 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1162 if (-1 == (j
->qdirs
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1163 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1168 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTINTERVAL
))) {
1169 j
->start_interval
= launch_data_get_integer(tmp
);
1171 if (j
->start_interval
== 0)
1172 job_log(j
, LOG_WARNING
, "StartInterval is zero, ignoring");
1173 else if (-1 == kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_SECONDS
, j
->start_interval
, &j
->kqjob_callback
))
1174 job_log_error(j
, LOG_ERR
, "adding kevent timer");
1177 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_STARTCALENDARINTERVAL
))) {
1178 launch_data_t tmp_k
;
1180 j
->start_cal_interval
= calloc(1, sizeof(struct tm
));
1181 j
->start_cal_interval
->tm_min
= -1;
1182 j
->start_cal_interval
->tm_hour
= -1;
1183 j
->start_cal_interval
->tm_mday
= -1;
1184 j
->start_cal_interval
->tm_wday
= -1;
1185 j
->start_cal_interval
->tm_mon
= -1;
1187 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(tmp
)) {
1188 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MINUTE
)))
1189 j
->start_cal_interval
->tm_min
= launch_data_get_integer(tmp_k
);
1190 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_HOUR
)))
1191 j
->start_cal_interval
->tm_hour
= launch_data_get_integer(tmp_k
);
1192 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_DAY
)))
1193 j
->start_cal_interval
->tm_mday
= launch_data_get_integer(tmp_k
);
1194 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_WEEKDAY
)))
1195 j
->start_cal_interval
->tm_wday
= launch_data_get_integer(tmp_k
);
1196 if ((tmp_k
= launch_data_dict_lookup(tmp
, LAUNCH_JOBKEY_CAL_MONTH
)))
1197 j
->start_cal_interval
->tm_mon
= launch_data_get_integer(tmp_k
);
1203 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
))) {
1206 j
->vnodes_cnt
= launch_data_array_get_count(tmp
);
1207 j
->vnodes
= malloc(sizeof(int) * j
->vnodes_cnt
);
1209 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1210 const char *thepath
= launch_data_get_string(launch_data_array_get_index(tmp
, i
));
1212 if (-1 == (j
->vnodes
[i
] = _fd(open(thepath
, O_EVTONLY
))))
1213 job_log_error(j
, LOG_ERR
, "open(\"%s\", O_EVTONLY)", thepath
);
1218 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
)))
1219 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1221 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1227 resp
= launch_data_new_errno(0);
1232 static launch_data_t
get_jobs(const char *which
)
1235 launch_data_t tmp
, resp
= NULL
;
1238 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1239 if (!strcmp(which
, j
->label
))
1240 resp
= launch_data_copy(j
->ldj
);
1243 resp
= launch_data_new_errno(ESRCH
);
1245 resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
1247 TAILQ_FOREACH(j
, &jobs
, tqe
) {
1248 tmp
= launch_data_copy(j
->ldj
);
1249 launch_data_dict_insert(resp
, tmp
, j
->label
);
1256 static void usage(FILE *where
)
1258 fprintf(where
, "%s: [-d] [-- command [args ...]]\n", getprogname());
1259 fprintf(where
, "\t-d\tdaemonize\n");
1260 fprintf(where
, "\t-h\tthis usage statement\n");
1262 if (where
== stdout
)
1266 #ifdef EVFILT_MACH_IMPLEMENTED
1267 static void **machcbtable
= NULL
;
1268 static size_t machcbtable_cnt
= 0;
1269 static int machcbreadfd
= -1;
1270 static int machcbwritefd
= -1;
1271 static mach_port_t mach_demand_port_set
= MACH_PORT_NULL
;
1272 static pthread_t mach_demand_thread
;
1274 static void mach_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1279 read(machcbreadfd
, &mp
, sizeof(mp
));
1281 EV_SET(&mkev
, mp
, EVFILT_MACHPORT
, 0, 0, 0, machcbtable
[MACH_PORT_INDEX(mp
)]);
1283 (*((kq_callback
*)mkev
.udata
))(mkev
.udata
, &mkev
);
1287 int kevent_mod(uintptr_t ident
, short filter
, u_short flags
, u_int fflags
, intptr_t data
, void *udata
)
1291 #ifdef EVFILT_MACH_IMPLEMENTED
1293 pthread_attr_t attr
;
1294 int pthr_r
, pfds
[2];
1297 if (EVFILT_TIMER
== filter
|| EVFILT_VNODE
== filter
)
1300 if (flags
& EV_ADD
&& NULL
== udata
) {
1301 syslog(LOG_ERR
, "%s(): kev.udata == NULL!!!", __func__
);
1302 syslog(LOG_ERR
, "kev: ident %d filter %d flags 0x%x fflags 0x%x",
1303 ident
, filter
, flags
, fflags
);
1308 #ifdef EVFILT_MACH_IMPLEMENTED
1309 if (filter
!= EVFILT_MACHPORT
) {
1311 #ifdef PID1_REAP_ADOPTED_CHILDREN
1312 if (filter
== EVFILT_PROC
&& getpid() == 1)
1315 EV_SET(&kev
, ident
, filter
, flags
, fflags
, data
, udata
);
1316 return kevent(q
, &kev
, 1, NULL
, 0, NULL
);
1317 #ifdef EVFILT_MACH_IMPLEMENTED
1320 if (machcbtable
== NULL
) {
1321 pthread_attr_init(&attr
);
1322 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
1324 pthr_r
= pthread_create(&mach_demand_thread
, &attr
, mach_demand_loop
, NULL
);
1326 syslog(LOG_ERR
, "pthread_create(mach_demand_loop): %s", strerror(pthr_r
));
1330 pthread_attr_destroy(&attr
);
1332 machcbtable
= malloc(0);
1334 machcbwritefd
= _fd(pfds
[1]);
1335 machcbreadfd
= _fd(pfds
[0]);
1336 kevent_mod(machcbreadfd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqmach_callback
);
1337 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET
, &mach_demand_port_set
);
1338 if (kr
!= KERN_SUCCESS
) {
1339 syslog(LOG_ERR
, "mach_port_allocate(demand_port_set): %s", mach_error_string(kr
));
1344 if (flags
& EV_ADD
) {
1345 kr
= mach_port_move_member(mach_task_self(), ident
, mach_demand_port_set
);
1346 if (kr
!= KERN_SUCCESS
) {
1347 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1351 if (MACH_PORT_INDEX(ident
) > machcbtable_cnt
)
1352 machcbtable
= realloc(machcbtable
, MACH_PORT_INDEX(ident
) * sizeof(void *));
1354 machcbtable
[MACH_PORT_INDEX(ident
)] = udata
;
1355 } else if (flags
& EV_DELETE
) {
1356 kr
= mach_port_move_member(mach_task_self(), ident
, MACH_PORT_NULL
);
1357 if (kr
!= KERN_SUCCESS
) {
1358 syslog(LOG_ERR
, "mach_port_move_member(): %s", mach_error_string(kr
));
1362 syslog(LOG_DEBUG
, "kevent_mod(EVFILT_MACHPORT) with flags: %d", flags
);
1371 static int _fd(int fd
)
1374 fcntl(fd
, F_SETFD
, 1);
1378 static void ipc_close(struct conncb
*c
)
1380 batch_job_enable(true, c
);
1382 TAILQ_REMOVE(&connections
, c
, tqe
);
1383 launchd_close(c
->conn
);
1387 static void setup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1389 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1390 setenv(key
, launch_data_get_string(obj
), 1);
1393 static void unsetup_job_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
1395 if (LAUNCH_DATA_STRING
== launch_data_get_type(obj
))
1399 static void job_reap(struct jobcb
*j
)
1401 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1402 time_t td
= time(NULL
) - j
->start_time
;
1403 bool bad_exit
= false;
1406 job_log(j
, LOG_DEBUG
, "Reaping");
1413 #ifdef PID1_REAP_ADOPTED_CHILDREN
1415 status
= pid1_child_exit_status
;
1418 if (-1 == waitpid(j
->p
, &status
, 0)) {
1419 job_log_error(j
, LOG_ERR
, "waitpid(%d, ...)", j
->p
);
1423 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
1424 job_log(j
, LOG_WARNING
, "exited with exit code: %d", WEXITSTATUS(status
));
1428 if (WIFSIGNALED(status
)) {
1429 int s
= WTERMSIG(status
);
1430 if (SIGKILL
== s
|| SIGTERM
== s
) {
1431 job_log(j
, LOG_NOTICE
, "exited: %s", strsignal(s
));
1433 job_log(j
, LOG_WARNING
, "exited abnormally: %s", strsignal(s
));
1439 if (td
< LAUNCHD_MIN_JOB_RUN_TIME
) {
1440 job_log(j
, LOG_WARNING
, "respawning too quickly! throttling");
1443 } else if (td
>= LAUNCHD_REWARD_JOB_RUN_TIME
) {
1444 job_log(j
, LOG_INFO
, "lived long enough, forgiving past exit failures");
1445 j
->failed_exits
= 0;
1452 if (j
->failed_exits
> 0) {
1453 int failures_left
= LAUNCHD_FAILED_EXITS_THRESHOLD
- j
->failed_exits
;
1455 job_log(j
, LOG_WARNING
, "%d more failure%s without living at least %d seconds will cause job removal",
1456 failures_left
, failures_left
> 1 ? "s" : "", LAUNCHD_REWARD_JOB_RUN_TIME
);
1463 static bool job_restart_fitness_test(struct jobcb
*j
)
1465 bool od
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
);
1468 job_log(j
, LOG_DEBUG
, "first born died, begin shutdown");
1471 } else if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
) && !j
->checkedin
) {
1472 job_log(j
, LOG_WARNING
, "failed to checkin");
1475 } else if (j
->failed_exits
>= LAUNCHD_FAILED_EXITS_THRESHOLD
) {
1476 job_log(j
, LOG_WARNING
, "too many failures in succession");
1479 } else if (od
|| shutdown_in_progress
) {
1480 if (!od
&& shutdown_in_progress
)
1481 job_log(j
, LOG_NOTICE
, "exited while shutdown is in progress, will not restart unless demand requires it");
1489 static void job_callback(void *obj
, struct kevent
*kev
)
1491 struct jobcb
*j
= obj
;
1493 bool startnow
= true;
1497 oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
1498 job_log(j
, LOG_DEBUG
, "log level debug temporarily enabled while processing job");
1501 if (kev
->filter
== EVFILT_PROC
) {
1504 startnow
= job_restart_fitness_test(j
);
1506 if (startnow
&& j
->throttle
) {
1507 j
->throttle
= false;
1508 job_log(j
, LOG_WARNING
, "will restart in %d seconds", LAUNCHD_MIN_JOB_RUN_TIME
);
1509 if (-1 == kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_ADD
|EV_ONESHOT
,
1510 NOTE_SECONDS
, LAUNCHD_MIN_JOB_RUN_TIME
, &j
->kqjob_callback
)) {
1511 job_log_error(j
, LOG_WARNING
, "failed to setup timer callback!, starting now!");
1516 } else if (kev
->filter
== EVFILT_TIMER
&& (void *)kev
->ident
== j
->start_cal_interval
) {
1518 } else if (kev
->filter
== EVFILT_VNODE
) {
1520 const char *thepath
= NULL
;
1522 for (i
= 0; i
< j
->vnodes_cnt
; i
++) {
1523 if (j
->vnodes
[i
] == (int)kev
->ident
) {
1524 launch_data_t ld_vnodes
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_WATCHPATHS
);
1526 thepath
= launch_data_get_string(launch_data_array_get_index(ld_vnodes
, i
));
1528 job_log(j
, LOG_DEBUG
, "watch path modified: %s", thepath
);
1530 if ((NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
) & kev
->fflags
) {
1531 job_log(j
, LOG_DEBUG
, "watch path invalidated: %s", thepath
);
1532 close(j
->vnodes
[i
]);
1533 j
->vnodes
[i
] = -1; /* this will get fixed in job_watch() */
1538 for (i
= 0; i
< j
->qdirs_cnt
; i
++) {
1539 if (j
->qdirs
[i
] == (int)kev
->ident
) {
1540 launch_data_t ld_qdirs
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
);
1543 thepath
= launch_data_get_string(launch_data_array_get_index(ld_qdirs
, i
));
1545 job_log(j
, LOG_DEBUG
, "queue directory modified: %s", thepath
);
1547 if (-1 == (dcc_r
= dir_has_files(thepath
))) {
1548 job_log_error(j
, LOG_ERR
, "dir_has_files(\"%s\", ...)", thepath
);
1549 } else if (0 == dcc_r
) {
1550 job_log(j
, LOG_DEBUG
, "spurious wake up, directory empty: %s", thepath
);
1555 /* if we get here, then the vnodes either wasn't a qdir, or if it was, it has entries in it */
1556 } else if (kev
->filter
== EVFILT_READ
&& (int)kev
->ident
== j
->execfd
) {
1557 if (kev
->data
> 0) {
1560 read(j
->execfd
, &e
, sizeof(e
));
1562 job_log_error(j
, LOG_ERR
, "execve()");
1577 /* the job might have been removed, must not call job_log() */
1578 syslog(LOG_DEBUG
, "restoring original log mask");
1579 setlogmask(oldmask
);
1583 static void job_start(struct jobcb
*j
)
1591 job_log(j
, LOG_DEBUG
, "Starting");
1594 job_log(j
, LOG_DEBUG
, "already running");
1598 j
->checkedin
= false;
1600 sipc
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SERVICEIPC
);
1602 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
))
1606 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
1608 socketpair(AF_UNIX
, SOCK_STREAM
, 0, execspair
);
1610 time(&j
->start_time
);
1612 switch (c
= fork_with_bootstrap_port(launchd_bootstrap_port
)) {
1614 job_log_error(j
, LOG_ERR
, "fork() failed, will try again in one second");
1615 close(execspair
[0]);
1616 close(execspair
[1]);
1621 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1625 close(execspair
[0]);
1626 /* wait for our parent to say they've attached a kevent to us */
1627 read(_fd(execspair
[1]), &c
, sizeof(c
));
1629 setpgid(getpid(), getpid());
1630 if (isatty(STDIN_FILENO
)) {
1631 if (tcsetpgrp(STDIN_FILENO
, getpid()) == -1)
1632 job_log_error(j
, LOG_WARNING
, "tcsetpgrp()");
1638 sprintf(nbuf
, "%d", spair
[1]);
1639 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
1641 job_start_child(j
, execspair
[1]);
1644 close(execspair
[1]);
1645 j
->execfd
= _fd(execspair
[0]);
1648 ipc_open(_fd(spair
[0]), j
);
1650 if (kevent_mod(j
->execfd
, EVFILT_READ
, EV_ADD
, 0, 0, &j
->kqjob_callback
) == -1)
1651 job_log_error(j
, LOG_ERR
, "kevent_mod(j->execfd): %m");
1652 if (kevent_mod(c
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &j
->kqjob_callback
) == -1) {
1653 job_log_error(j
, LOG_ERR
, "kevent()");
1658 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_ONDEMAND
))
1661 /* this unblocks the child and avoids a race
1662 * between the above fork() and the kevent_mod() */
1663 write(j
->execfd
, &c
, sizeof(c
));
1668 static void job_start_child(struct jobcb
*j
, int execfd
)
1670 launch_data_t ldpa
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
1671 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1673 const char **argv
, *file2exec
= "/usr/libexec/launchproxy";
1675 bool hasprog
= false;
1677 job_setup_attributes(j
);
1680 argv_cnt
= launch_data_array_get_count(ldpa
);
1681 argv
= alloca((argv_cnt
+ 2) * sizeof(char *));
1682 for (i
= 0; i
< argv_cnt
; i
++)
1683 argv
[i
+ 1] = launch_data_get_string(launch_data_array_get_index(ldpa
, i
));
1684 argv
[argv_cnt
+ 1] = NULL
;
1686 argv
= alloca(3 * sizeof(char *));
1687 argv
[1] = job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
);
1691 if (job_get_string(j
->ldj
, LAUNCH_JOBKEY_PROGRAM
))
1695 argv
[0] = file2exec
;
1698 file2exec
= job_get_file2exec(j
->ldj
);
1702 r
= execv(file2exec
, (char *const*)argv
);
1704 r
= execvp(file2exec
, (char *const*)argv
);
1708 write(execfd
, &errno
, sizeof(errno
));
1709 job_log_error(j
, LOG_ERR
, "execv%s(\"%s\", ...)", hasprog
? "" : "p", file2exec
);
1714 static void job_setup_attributes(struct jobcb
*j
)
1716 launch_data_t srl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_SOFTRESOURCELIMITS
);
1717 launch_data_t hrl
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_HARDRESOURCELIMITS
);
1718 bool inetcompat
= job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
1722 struct group
*gre
= NULL
;
1724 static const struct {
1728 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE
, RLIMIT_CORE
},
1729 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU
, RLIMIT_CPU
},
1730 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA
, RLIMIT_DATA
},
1731 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE
, RLIMIT_FSIZE
},
1732 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK
, RLIMIT_MEMLOCK
},
1733 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE
, RLIMIT_NOFILE
},
1734 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC
, RLIMIT_NPROC
},
1735 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS
, RLIMIT_RSS
},
1736 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK
, RLIMIT_STACK
},
1739 setpriority(PRIO_PROCESS
, 0, job_get_integer(j
->ldj
, LAUNCH_JOBKEY_NICE
));
1742 for (i
= 0; i
< (sizeof(limits
) / sizeof(limits
[0])); i
++) {
1745 if (getrlimit(limits
[i
].val
, &rl
) == -1) {
1746 job_log_error(j
, LOG_WARNING
, "getrlimit()");
1751 rl
.rlim_max
= job_get_integer(hrl
, limits
[i
].key
);
1753 rl
.rlim_cur
= job_get_integer(srl
, limits
[i
].key
);
1755 if (setrlimit(limits
[i
].val
, &rl
) == -1)
1756 job_log_error(j
, LOG_WARNING
, "setrlimit()");
1760 if (!inetcompat
&& job_get_bool(j
->ldj
, LAUNCH_JOBKEY_SESSIONCREATE
))
1761 launchd_SessionCreate(job_get_file2exec(j
->ldj
));
1763 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_LOWPRIORITYIO
)) {
1764 int lowprimib
[] = { CTL_KERN
, KERN_PROC_LOW_PRI_IO
};
1767 if (sysctl(lowprimib
, sizeof(lowprimib
) / sizeof(lowprimib
[0]), NULL
, NULL
, &val
, sizeof(val
)) == -1)
1768 job_log_error(j
, LOG_WARNING
, "sysctl(\"%s\")", "kern.proc_low_pri_io");
1770 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_ROOTDIRECTORY
))) {
1774 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_GROUPNAME
))) {
1775 gre
= getgrnam(tmpstr
);
1777 gre_g
= gre
->gr_gid
;
1778 if (-1 == setgid(gre_g
)) {
1779 job_log_error(j
, LOG_ERR
, "setgid(%d)", gre_g
);
1783 job_log(j
, LOG_ERR
, "getgrnam(\"%s\") failed", tmpstr
);
1787 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_USERNAME
))) {
1788 struct passwd
*pwe
= getpwnam(tmpstr
);
1790 uid_t pwe_u
= pwe
->pw_uid
;
1791 uid_t pwe_g
= pwe
->pw_gid
;
1793 if (pwe
->pw_expire
&& time(NULL
) >= pwe
->pw_expire
) {
1794 job_log(j
, LOG_ERR
, "expired account: %s", tmpstr
);
1797 if (job_get_bool(j
->ldj
, LAUNCH_JOBKEY_INITGROUPS
)) {
1798 if (-1 == initgroups(tmpstr
, gre
? gre_g
: pwe_g
)) {
1799 job_log_error(j
, LOG_ERR
, "initgroups()");
1804 if (-1 == setgid(pwe_g
)) {
1805 job_log_error(j
, LOG_ERR
, "setgid(%d)", pwe_g
);
1809 if (-1 == setuid(pwe_u
)) {
1810 job_log_error(j
, LOG_ERR
, "setuid(%d)", pwe_u
);
1814 job_log(j
, LOG_WARNING
, "getpwnam(\"%s\") failed", tmpstr
);
1818 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_WORKINGDIRECTORY
)))
1820 if (launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_UMASK
))
1821 umask(job_get_integer(j
->ldj
, LAUNCH_JOBKEY_UMASK
));
1822 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDOUTPATH
))) {
1823 int sofd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1825 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1827 dup2(sofd
, STDOUT_FILENO
);
1831 if ((tmpstr
= job_get_string(j
->ldj
, LAUNCH_JOBKEY_STANDARDERRORPATH
))) {
1832 int sefd
= open(tmpstr
, O_WRONLY
|O_APPEND
|O_CREAT
, DEFFILEMODE
);
1834 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", tmpstr
);
1836 dup2(sefd
, STDERR_FILENO
);
1840 if ((tmp
= launch_data_dict_lookup(j
->ldj
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
)))
1841 launch_data_dict_iterate(tmp
, setup_job_env
, NULL
);
1846 #ifdef PID1_REAP_ADOPTED_CHILDREN
1847 __private_extern__
int pid1_child_exit_status
= 0;
1848 static void pid1waitpid(void)
1852 while ((p
= waitpid(-1, &pid1_child_exit_status
, WNOHANG
)) > 0) {
1853 if (!launchd_check_pid(p
))
1859 static void do_shutdown(void)
1863 shutdown_in_progress
= true;
1865 kevent_mod(asynckq
, EVFILT_READ
, EV_DISABLE
, 0, 0, &kqasync_callback
);
1867 TAILQ_FOREACH(j
, &jobs
, tqe
)
1870 if (getpid() == 1) {
1872 mach_start_shutdown(SIGTERM
);
1876 static void signal_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
1878 switch (kev
->ident
) {
1881 reload_launchd_config();
1886 #ifdef PID1_REAP_ADOPTED_CHILDREN
1888 /* <rdar://problem/3632556> Please automatically reap processes reparented to PID 1 */
1898 static void fs_callback(void)
1900 static bool mounted_volfs
= false;
1903 mounted_volfs
= true;
1905 if (pending_stdout
) {
1906 int fd
= open(pending_stdout
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1908 dup2(fd
, STDOUT_FILENO
);
1910 free(pending_stdout
);
1911 pending_stdout
= NULL
;
1914 if (pending_stderr
) {
1915 int fd
= open(pending_stderr
, O_CREAT
|O_APPEND
|O_WRONLY
, DEFFILEMODE
);
1917 dup2(fd
, STDERR_FILENO
);
1919 free(pending_stderr
);
1920 pending_stderr
= NULL
;
1924 if (!mounted_volfs
) {
1925 int r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1927 if (-1 == r
&& errno
== ENOENT
) {
1928 mkdir(VOLFSDIR
, ACCESSPERMS
& ~(S_IWUSR
|S_IWGRP
|S_IWOTH
));
1929 r
= mount("volfs", VOLFSDIR
, MNT_RDONLY
, NULL
);
1933 syslog(LOG_WARNING
, "mount(\"%s\", \"%s\", ...): %m", "volfs", VOLFSDIR
);
1935 mounted_volfs
= true;
1939 if (!launchd_inited
)
1940 launchd_server_init(false);
1943 static void readcfg_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
__attribute__((unused
)))
1947 #ifdef PID1_REAP_ADOPTED_CHILDREN
1949 status
= pid1_child_exit_status
;
1952 if (-1 == waitpid(readcfg_pid
, &status
, 0)) {
1953 syslog(LOG_WARNING
, "waitpid(readcfg_pid, ...): %m");
1959 if (WIFEXITED(status
)) {
1960 if (WEXITSTATUS(status
))
1961 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited with status: %d", WEXITSTATUS(status
));
1962 } else if (WIFSIGNALED(status
)) {
1963 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally: %s", strsignal(WTERMSIG(status
)));
1965 syslog(LOG_WARNING
, "Unable to read launchd.conf: launchctl exited abnormally");
1969 #ifdef EVFILT_MACH_IMPLEMENTED
1970 static void *mach_demand_loop(void *arg
__attribute__((unused
)))
1972 mach_msg_empty_rcv_t dummy
;
1974 mach_port_name_array_t members
;
1975 mach_msg_type_number_t membersCnt
;
1976 mach_port_status_t status
;
1977 mach_msg_type_number_t statusCnt
;
1983 * Receive indication of message on demand service
1984 * ports without actually receiving the message (we'll
1985 * let the actual server do that.
1987 kr
= mach_msg(&dummy
.header
, MACH_RCV_MSG
|MACH_RCV_LARGE
,
1988 0, 0, mach_demand_port_set
, 0, MACH_PORT_NULL
);
1989 if (kr
!= MACH_RCV_TOO_LARGE
) {
1990 syslog(LOG_WARNING
, "%s(): mach_msg(): %s", __func__
, mach_error_string(kr
));
1995 * Some port(s) now have messages on them, find out
1996 * which ones (there is no indication of which port
1997 * triggered in the MACH_RCV_TOO_LARGE indication).
1999 kr
= mach_port_get_set_status(mach_task_self(),
2000 mach_demand_port_set
, &members
, &membersCnt
);
2001 if (kr
!= KERN_SUCCESS
) {
2002 syslog(LOG_WARNING
, "%s(): mach_port_get_set_status(): %s", __func__
, mach_error_string(kr
));
2006 for (i
= 0; i
< membersCnt
; i
++) {
2007 statusCnt
= MACH_PORT_RECEIVE_STATUS_COUNT
;
2008 kr
= mach_port_get_attributes(mach_task_self(), members
[i
],
2009 MACH_PORT_RECEIVE_STATUS
, (mach_port_info_t
)&status
, &statusCnt
);
2010 if (kr
!= KERN_SUCCESS
) {
2011 syslog(LOG_WARNING
, "%s(): mach_port_get_attributes(): %s", __func__
, mach_error_string(kr
));
2016 * For each port with messages, take it out of the
2017 * demand service portset, and inform the main thread
2018 * that it might have to start the server responsible
2021 if (status
.mps_msgcount
) {
2022 kr
= mach_port_move_member(mach_task_self(), members
[i
], MACH_PORT_NULL
);
2023 if (kr
!= KERN_SUCCESS
) {
2024 syslog(LOG_WARNING
, "%s(): mach_port_move_member(): %s", __func__
, mach_error_string(kr
));
2027 write(machcbwritefd
, &(members
[i
]), sizeof(members
[i
]));
2031 kr
= vm_deallocate(mach_task_self(), (vm_address_t
) members
,
2032 (vm_size_t
) membersCnt
* sizeof(mach_port_name_t
));
2033 if (kr
!= KERN_SUCCESS
) {
2034 syslog(LOG_WARNING
, "%s(): vm_deallocate(): %s", __func__
, mach_error_string(kr
));
2043 static void reload_launchd_config(void)
2046 static char *ldconf
= PID1LAUNCHD_CONF
;
2047 const char *h
= getenv("HOME");
2049 if (h
&& ldconf
== PID1LAUNCHD_CONF
)
2050 asprintf(&ldconf
, "%s/%s", h
, LAUNCHD_CONF
);
2055 if (lstat(ldconf
, &sb
) == 0) {
2057 socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
);
2058 readcfg_pid
= fork_with_bootstrap_port(launchd_bootstrap_port
);
2059 if (readcfg_pid
== 0) {
2062 sprintf(nbuf
, "%d", spair
[1]);
2063 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
2064 int fd
= open(ldconf
, O_RDONLY
);
2066 syslog(LOG_ERR
, "open(\"%s\"): %m", ldconf
);
2069 dup2(fd
, STDIN_FILENO
);
2071 execl(LAUNCHCTL_PATH
, LAUNCHCTL_PATH
, NULL
);
2072 syslog(LOG_ERR
, "execl(\"%s\", ...): %m", LAUNCHCTL_PATH
);
2074 } else if (readcfg_pid
== -1) {
2077 syslog(LOG_ERR
, "fork(): %m");
2081 ipc_open(_fd(spair
[0]), NULL
);
2082 if (kevent_mod(readcfg_pid
, EVFILT_PROC
, EV_ADD
, NOTE_EXIT
, 0, &kqreadcfg_callback
) == -1)
2083 syslog(LOG_ERR
, "kevent_mod(EVFILT_PROC, &kqreadcfg_callback): %m");
2088 static void conceive_firstborn(char *argv
[])
2090 launch_data_t r
, d
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2091 launch_data_t args
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
2092 launch_data_t l
= launch_data_new_string("com.apple.launchd.firstborn");
2095 for (i
= 0; *argv
; argv
++, i
++)
2096 launch_data_array_set_index(args
, launch_data_new_string(*argv
), i
);
2098 launch_data_dict_insert(d
, args
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
2099 launch_data_dict_insert(d
, l
, LAUNCH_JOBKEY_LABEL
);
2103 launch_data_free(r
);
2104 launch_data_free(d
);
2106 TAILQ_FIRST(&jobs
)->firstborn
= true;
2109 static void loopback_setup(void)
2111 struct ifaliasreq ifra
;
2112 struct in6_aliasreq ifra6
;
2116 memset(&ifr
, 0, sizeof(ifr
));
2117 strcpy(ifr
.ifr_name
, "lo0");
2119 if (-1 == (s
= socket(AF_INET
, SOCK_DGRAM
, 0)))
2120 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET");
2121 if (-1 == (s6
= socket(AF_INET6
, SOCK_DGRAM
, 0)))
2122 syslog(LOG_ERR
, "%s: socket(%s, ...): %m", __PRETTY_FUNCTION__
, "AF_INET6");
2124 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) == -1) {
2125 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2127 ifr
.ifr_flags
|= IFF_UP
;
2129 if (ioctl(s
, SIOCSIFFLAGS
, &ifr
) == -1)
2130 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2133 memset(&ifr
, 0, sizeof(ifr
));
2134 strcpy(ifr
.ifr_name
, "lo0");
2136 if (ioctl(s6
, SIOCGIFFLAGS
, &ifr
) == -1) {
2137 syslog(LOG_ERR
, "ioctl(SIOCGIFFLAGS): %m");
2139 ifr
.ifr_flags
|= IFF_UP
;
2141 if (ioctl(s6
, SIOCSIFFLAGS
, &ifr
) == -1)
2142 syslog(LOG_ERR
, "ioctl(SIOCSIFFLAGS): %m");
2145 memset(&ifra
, 0, sizeof(ifra
));
2146 strcpy(ifra
.ifra_name
, "lo0");
2148 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_family
= AF_INET
;
2149 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
2150 ((struct sockaddr_in
*)&ifra
.ifra_addr
)->sin_len
= sizeof(struct sockaddr_in
);
2151 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_family
= AF_INET
;
2152 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_addr
.s_addr
= htonl(IN_CLASSA_NET
);
2153 ((struct sockaddr_in
*)&ifra
.ifra_mask
)->sin_len
= sizeof(struct sockaddr_in
);
2155 if (ioctl(s
, SIOCAIFADDR
, &ifra
) == -1)
2156 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv4): %m");
2158 memset(&ifra6
, 0, sizeof(ifra6
));
2159 strcpy(ifra6
.ifra_name
, "lo0");
2161 ifra6
.ifra_addr
.sin6_family
= AF_INET6
;
2162 ifra6
.ifra_addr
.sin6_addr
= in6addr_loopback
;
2163 ifra6
.ifra_addr
.sin6_len
= sizeof(struct sockaddr_in6
);
2164 ifra6
.ifra_prefixmask
.sin6_family
= AF_INET6
;
2165 memset(&ifra6
.ifra_prefixmask
.sin6_addr
, 0xff, sizeof(struct in6_addr
));
2166 ifra6
.ifra_prefixmask
.sin6_len
= sizeof(struct sockaddr_in6
);
2167 ifra6
.ifra_lifetime
.ia6t_vltime
= ND6_INFINITE_LIFETIME
;
2168 ifra6
.ifra_lifetime
.ia6t_pltime
= ND6_INFINITE_LIFETIME
;
2170 if (ioctl(s6
, SIOCAIFADDR_IN6
, &ifra6
) == -1)
2171 syslog(LOG_ERR
, "ioctl(SIOCAIFADDR ipv6): %m");
2177 static void workaround3048875(int argc
, char *argv
[])
2180 char **ap
, *newargv
[100], *p
= argv
[1];
2182 if (argc
== 1 || argc
> 2)
2185 newargv
[0] = argv
[0];
2186 for (ap
= newargv
+ 1, i
= 1; ap
< &newargv
[100]; ap
++, i
++) {
2187 if ((*ap
= strsep(&p
, " \t")) == NULL
)
2198 execv(newargv
[0], newargv
);
2201 static launch_data_t
adjust_rlimits(launch_data_t in
)
2203 static struct rlimit
*l
= NULL
;
2204 static size_t lsz
= sizeof(struct rlimit
) * RLIM_NLIMITS
;
2205 struct rlimit
*ltmp
;
2210 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
2211 if (getrlimit(i
, l
+ i
) == -1)
2212 syslog(LOG_WARNING
, "getrlimit(): %m");
2217 ltmp
= launch_data_get_opaque(in
);
2218 ltmpsz
= launch_data_get_opaque_size(in
);
2221 syslog(LOG_WARNING
, "Too much rlimit data sent!");
2225 for (i
= 0; i
< (ltmpsz
/ sizeof(struct rlimit
)); i
++) {
2226 if (ltmp
[i
].rlim_cur
== l
[i
].rlim_cur
&& ltmp
[i
].rlim_max
== l
[i
].rlim_max
)
2229 if (readcfg_pid
&& getpid() == 1) {
2230 int gmib
[] = { CTL_KERN
, KERN_MAXPROC
};
2231 int pmib
[] = { CTL_KERN
, KERN_MAXPROCPERUID
};
2232 const char *gstr
= "kern.maxproc";
2233 const char *pstr
= "kern.maxprocperuid";
2234 int gval
= ltmp
[i
].rlim_max
;
2235 int pval
= ltmp
[i
].rlim_cur
;
2238 gmib
[1] = KERN_MAXFILES
;
2239 pmib
[1] = KERN_MAXFILESPERPROC
;
2240 gstr
= "kern.maxfiles";
2241 pstr
= "kern.maxfilesperproc";
2244 /* kernel will not clamp to this value, we must */
2245 if (gval
> (2048 + 20))
2251 if (sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)) == -1)
2252 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", gstr
);
2253 if (sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)) == -1)
2254 syslog(LOG_WARNING
, "sysctl(\"%s\"): %m", pstr
);
2256 if (setrlimit(i
, ltmp
+ i
) == -1)
2257 syslog(LOG_WARNING
, "setrlimit(): %m");
2258 /* the kernel may have clamped the values we gave it */
2259 if (getrlimit(i
, l
+ i
) == -1)
2260 syslog(LOG_WARNING
, "getrlimit(): %m");
2264 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);
2267 __private_extern__
void launchd_SessionCreate(const char *who
)
2269 void *seclib
= dlopen(SECURITY_LIB
, RTLD_LAZY
);
2270 OSStatus (*sescr
)(SessionCreationFlags flags
, SessionAttributeBits attributes
);
2273 sescr
= dlsym(seclib
, "SessionCreate");
2276 OSStatus scr
= sescr(0, 0);
2278 syslog(LOG_WARNING
, "%s: SessionCreate() failed: %d", who
, scr
);
2280 syslog(LOG_WARNING
, "%s: couldn't find SessionCreate() in %s", who
, SECURITY_LIB
);
2285 syslog(LOG_WARNING
, "%s: dlopen(\"%s\",...): %s", who
, SECURITY_LIB
, dlerror());
2289 static int dir_has_files(const char *path
)
2291 DIR *dd
= opendir(path
);
2298 while ((de
= readdir(dd
))) {
2299 if (strcmp(de
->d_name
, ".") && strcmp(de
->d_name
, "..")) {
2309 static void job_set_alarm(struct jobcb
*j
)
2313 later
= cronemu(j
->start_cal_interval
->tm_mon
, j
->start_cal_interval
->tm_mday
,
2314 j
->start_cal_interval
->tm_hour
, j
->start_cal_interval
->tm_min
);
2316 if (j
->start_cal_interval
->tm_wday
!= -1) {
2317 time_t otherlater
= cronemu_wday(j
->start_cal_interval
->tm_wday
,
2318 j
->start_cal_interval
->tm_hour
, j
->start_cal_interval
->tm_min
);
2320 if (-1 != j
->start_cal_interval
->tm_mday
) {
2321 later
= later
< otherlater
? later
: otherlater
;
2327 if (-1 == kevent_mod((uintptr_t)j
->start_cal_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_ABSOLUTE
|NOTE_SECONDS
, later
, j
)) {
2328 job_log_error(j
, LOG_ERR
, "adding kevent alarm");
2330 job_log(j
, LOG_INFO
, "scheduled to run again at: %s", ctime(&later
));
2335 job_prep_log_msg(struct jobcb
*j
, char *buf
, const char *msg
, int err
)
2337 size_t lsz
= strlen(j
->label
);
2340 for (i
= 0, o
= 0; i
< lsz
; i
++, o
++) {
2341 if (j
->label
[i
] == '%') {
2346 buf
[o
] = j
->label
[i
];
2354 sprintf(buf
+ o
, "%s: %s", msg
, strerror(err
));
2356 strcpy(buf
+ o
, msg
);
2361 job_log_error(struct jobcb
*j
, int pri
, const char *msg
, ...)
2366 job_prep_log_msg(j
, newmsg
, msg
, errno
);
2370 vsyslog(pri
, newmsg
, ap
);
2376 job_log(struct jobcb
*j
, int pri
, const char *msg
, ...)
2381 job_prep_log_msg(j
, newmsg
, msg
, 0);
2385 vsyslog(pri
, newmsg
, ap
);
2390 static void async_callback(void)
2392 struct timespec timeout
= { 0, 0 };
2395 switch (kevent(asynckq
, NULL
, 0, &kev
, 1, &timeout
)) {
2397 syslog(LOG_DEBUG
, "kevent(): %m");
2400 (*((kq_callback
*)kev
.udata
))(kev
.udata
, &kev
);
2404 syslog(LOG_DEBUG
, "unexpected: kevent() returned something != 0, -1 or 1");
2408 static void testfd_or_openfd(int fd
, const char *path
, int flags
)
2412 if (-1 != (tmpfd
= dup(fd
))) {
2415 if (-1 == (tmpfd
= open(path
, flags
))) {
2416 syslog(LOG_ERR
, "open(\"%s\", ...): %m", path
);
2417 } else if (tmpfd
!= fd
) {
2425 cronemu(int mon
, int mday
, int hour
, int min
)
2427 struct tm workingtm
;
2431 workingtm
= *localtime(&now
);
2433 workingtm
.tm_isdst
= -1;
2434 workingtm
.tm_sec
= 0;
2437 while (!cronemu_mon(&workingtm
, mon
, mday
, hour
, min
)) {
2438 workingtm
.tm_year
++;
2439 workingtm
.tm_mon
= 0;
2440 workingtm
.tm_mday
= 1;
2441 workingtm
.tm_hour
= 0;
2442 workingtm
.tm_min
= 0;
2446 return mktime(&workingtm
);
2450 cronemu_wday(int wday
, int hour
, int min
)
2452 struct tm workingtm
;
2456 workingtm
= *localtime(&now
);
2458 workingtm
.tm_isdst
= -1;
2459 workingtm
.tm_sec
= 0;
2465 while (workingtm
.tm_wday
!= wday
|| !cronemu_hour(&workingtm
, hour
, min
)) {
2466 workingtm
.tm_mday
++;
2467 workingtm
.tm_hour
= 0;
2468 workingtm
.tm_min
= 0;
2469 cronemu_hour(&workingtm
, hour
, min
);
2473 return mktime(&workingtm
);
2477 cronemu_mon(struct tm
*wtm
, int mon
, int mday
, int hour
, int min
)
2480 struct tm workingtm
= *wtm
;
2483 while (!cronemu_mday(&workingtm
, mday
, hour
, min
)) {
2485 workingtm
.tm_mday
= 1;
2486 workingtm
.tm_hour
= 0;
2487 workingtm
.tm_min
= 0;
2488 carrytest
= workingtm
.tm_mon
;
2490 if (carrytest
!= workingtm
.tm_mon
)
2497 if (mon
< wtm
->tm_mon
)
2500 if (mon
> wtm
->tm_mon
) {
2507 return cronemu_mday(wtm
, mday
, hour
, min
);
2511 cronemu_mday(struct tm
*wtm
, int mday
, int hour
, int min
)
2514 struct tm workingtm
= *wtm
;
2517 while (!cronemu_hour(&workingtm
, hour
, min
)) {
2518 workingtm
.tm_mday
++;
2519 workingtm
.tm_hour
= 0;
2520 workingtm
.tm_min
= 0;
2521 carrytest
= workingtm
.tm_mday
;
2523 if (carrytest
!= workingtm
.tm_mday
)
2530 if (mday
< wtm
->tm_mday
)
2533 if (mday
> wtm
->tm_mday
) {
2534 wtm
->tm_mday
= mday
;
2539 return cronemu_hour(wtm
, hour
, min
);
2543 cronemu_hour(struct tm
*wtm
, int hour
, int min
)
2546 struct tm workingtm
= *wtm
;
2549 while (!cronemu_min(&workingtm
, min
)) {
2550 workingtm
.tm_hour
++;
2551 workingtm
.tm_min
= 0;
2552 carrytest
= workingtm
.tm_hour
;
2554 if (carrytest
!= workingtm
.tm_hour
)
2561 if (hour
< wtm
->tm_hour
)
2564 if (hour
> wtm
->tm_hour
) {
2565 wtm
->tm_hour
= hour
;
2569 return cronemu_min(wtm
, min
);
2573 cronemu_min(struct tm
*wtm
, int min
)
2578 if (min
< wtm
->tm_min
)
2581 if (min
> wtm
->tm_min
) {