]> git.saurik.com Git - apple/launchd.git/blame - launchd/src/launchd_core_logic.c
launchd-258.25.tar.gz
[apple/launchd.git] / launchd / src / launchd_core_logic.c
CommitLineData
ed34e3c3 1/*
ed34e3c3
A
2 * @APPLE_APACHE_LICENSE_HEADER_START@
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * @APPLE_APACHE_LICENSE_HEADER_END@
17 */
18
26b346da 19static const char *const __rcs_file_version__ = "$Revision: 23923 $";
5b0a4722
A
20
21#include "config.h"
22#include "launchd_core_logic.h"
ed34e3c3 23
f36da725 24#include <TargetConditionals.h>
ed34e3c3
A
25#include <mach/mach.h>
26#include <mach/mach_error.h>
5b0a4722 27#include <mach/mach_time.h>
ed34e3c3
A
28#include <mach/boolean.h>
29#include <mach/message.h>
30#include <mach/notify.h>
31#include <mach/mig_errors.h>
32#include <mach/mach_traps.h>
33#include <mach/mach_interface.h>
34#include <mach/host_info.h>
35#include <mach/mach_host.h>
36#include <mach/exception.h>
5b0a4722 37#include <mach/host_reboot.h>
ed34e3c3
A
38#include <sys/types.h>
39#include <sys/queue.h>
40#include <sys/event.h>
ed34e3c3
A
41#include <sys/stat.h>
42#include <sys/ucred.h>
43#include <sys/fcntl.h>
44#include <sys/un.h>
5b0a4722 45#include <sys/reboot.h>
ed34e3c3
A
46#include <sys/wait.h>
47#include <sys/sysctl.h>
48#include <sys/sockio.h>
49#include <sys/time.h>
50#include <sys/resource.h>
51#include <sys/ioctl.h>
52#include <sys/mount.h>
5b0a4722 53#include <sys/pipe.h>
ed34e3c3
A
54#include <net/if.h>
55#include <netinet/in.h>
56#include <netinet/in_var.h>
57#include <netinet6/nd6.h>
5b0a4722 58#include <bsm/libbsm.h>
ed34e3c3
A
59#include <unistd.h>
60#include <signal.h>
61#include <errno.h>
ed34e3c3
A
62#include <libgen.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <stdarg.h>
66#include <stdbool.h>
67#include <paths.h>
68#include <pwd.h>
69#include <grp.h>
70#include <ttyent.h>
71#include <dlfcn.h>
72#include <dirent.h>
73#include <string.h>
74#include <ctype.h>
75#include <glob.h>
5b0a4722 76#include <spawn.h>
f36da725 77#if HAVE_SANDBOX
5b0a4722 78#include <sandbox.h>
f36da725
A
79#endif
80#if HAVE_QUARANTINE
81#include <quarantine.h>
82#endif
5b0a4722 83
ef398931
A
84#include "launch.h"
85#include "launch_priv.h"
86#include "launch_internal.h"
87#include "bootstrap.h"
88#include "bootstrap_priv.h"
89#include "vproc.h"
90#include "vproc_internal.h"
5b0a4722
A
91
92#include "reboot2.h"
ed34e3c3 93
ed34e3c3 94#include "launchd.h"
5b0a4722 95#include "launchd_runtime.h"
ed34e3c3 96#include "launchd_unix_ipc.h"
5b0a4722
A
97#include "protocol_vproc.h"
98#include "protocol_vprocServer.h"
ef398931 99#include "protocol_job_reply.h"
5b0a4722
A
100
101#define LAUNCHD_MIN_JOB_RUN_TIME 10
102#define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20
103#define LAUNCHD_SIGKILL_TIMER 5
104
105
106#define TAKE_SUBSET_NAME "TakeSubsetName"
107#define TAKE_SUBSET_PID "TakeSubsetPID"
108#define TAKE_SUBSET_PERPID "TakeSubsetPerPID"
109
110#define IS_POWER_OF_TWO(v) (!(v & (v - 1)) && v)
111
112extern char **environ;
113
114struct waiting_for_removal {
115 SLIST_ENTRY(waiting_for_removal) sle;
116 mach_port_t reply_port;
117};
118
119static bool waiting4removal_new(job_t j, mach_port_t rp);
120static void waiting4removal_delete(job_t j, struct waiting_for_removal *w4r);
121
122struct mspolicy {
123 SLIST_ENTRY(mspolicy) sle;
124 unsigned int allow:1, per_pid:1;
125 const char name[0];
126};
127
128static bool mspolicy_new(job_t j, const char *name, bool allow, bool pid_local, bool skip_check);
129static bool mspolicy_copy(job_t j_to, job_t j_from);
130static void mspolicy_setup(launch_data_t obj, const char *key, void *context);
131static bool mspolicy_check(job_t j, const char *name, bool pid_local);
132static void mspolicy_delete(job_t j, struct mspolicy *msp);
ed34e3c3
A
133
134struct machservice {
135 SLIST_ENTRY(machservice) sle;
5b0a4722
A
136 SLIST_ENTRY(machservice) special_port_sle;
137 LIST_ENTRY(machservice) name_hash_sle;
138 LIST_ENTRY(machservice) port_hash_sle;
139 job_t job;
140 uint64_t bad_perf_cnt;
141 unsigned int gen_num;
ed34e3c3 142 mach_port_name_t port;
5b0a4722
A
143 unsigned int isActive:1, reset:1, recv:1, hide:1, kUNCServer:1, per_user_hack:1, debug_on_close:1, per_pid:1, special_port_num:10;
144 const char name[0];
ed34e3c3
A
145};
146
5b0a4722
A
147static SLIST_HEAD(, machservice) special_ports; /* hack, this should be per jobmgr_t */
148
149#define PORT_HASH_SIZE 32
150#define HASH_PORT(x) (IS_POWER_OF_TWO(PORT_HASH_SIZE) ? (MACH_PORT_INDEX(x) & (PORT_HASH_SIZE - 1)) : (MACH_PORT_INDEX(x) % PORT_HASH_SIZE))
151
152static LIST_HEAD(, machservice) port_hash[PORT_HASH_SIZE];
153
ed34e3c3
A
154static void machservice_setup(launch_data_t obj, const char *key, void *context);
155static void machservice_setup_options(launch_data_t obj, const char *key, void *context);
5b0a4722
A
156static void machservice_resetport(job_t j, struct machservice *ms);
157static struct machservice *machservice_new(job_t j, const char *name, mach_port_t *serviceport, bool pid_local);
158static void machservice_ignore(job_t j, struct machservice *ms);
159static void machservice_watch(job_t j, struct machservice *ms);
160static void machservice_delete(job_t j, struct machservice *, bool port_died);
161static void machservice_request_notifications(struct machservice *);
162static mach_port_t machservice_port(struct machservice *);
163static job_t machservice_job(struct machservice *);
164static bool machservice_hidden(struct machservice *);
165static bool machservice_active(struct machservice *);
166static const char *machservice_name(struct machservice *);
167static bootstrap_status_t machservice_status(struct machservice *);
ed34e3c3
A
168
169struct socketgroup {
170 SLIST_ENTRY(socketgroup) sle;
171 int *fds;
172 unsigned int junkfds:1, fd_cnt:31;
f36da725
A
173 union {
174 const char name[0];
175 char name_init[0];
176 };
ed34e3c3
A
177};
178
5b0a4722
A
179static bool socketgroup_new(job_t j, const char *name, int *fds, unsigned int fd_cnt, bool junkfds);
180static void socketgroup_delete(job_t j, struct socketgroup *sg);
181static void socketgroup_watch(job_t j, struct socketgroup *sg);
182static void socketgroup_ignore(job_t j, struct socketgroup *sg);
183static void socketgroup_callback(job_t j);
ed34e3c3 184static void socketgroup_setup(launch_data_t obj, const char *key, void *context);
5b0a4722 185static void socketgroup_kevent_mod(job_t j, struct socketgroup *sg, bool do_add);
ed34e3c3
A
186
187struct calendarinterval {
5b0a4722 188 LIST_ENTRY(calendarinterval) global_sle;
ed34e3c3 189 SLIST_ENTRY(calendarinterval) sle;
5b0a4722 190 job_t job;
ed34e3c3 191 struct tm when;
5b0a4722 192 time_t when_next;
ed34e3c3
A
193};
194
5b0a4722
A
195static LIST_HEAD(, calendarinterval) sorted_calendar_events;
196
197static bool calendarinterval_new(job_t j, struct tm *w);
198static bool calendarinterval_new_from_obj(job_t j, launch_data_t obj);
199static void calendarinterval_new_from_obj_dict_walk(launch_data_t obj, const char *key, void *context);
200static void calendarinterval_delete(job_t j, struct calendarinterval *ci);
201static void calendarinterval_setalarm(job_t j, struct calendarinterval *ci);
202static void calendarinterval_callback(void);
203static void calendarinterval_sanity_check(void);
ed34e3c3
A
204
205struct envitem {
206 SLIST_ENTRY(envitem) sle;
207 char *value;
f36da725
A
208 union {
209 const char key[0];
210 char key_init[0];
211 };
ed34e3c3
A
212};
213
5b0a4722
A
214static bool envitem_new(job_t j, const char *k, const char *v, bool global);
215static void envitem_delete(job_t j, struct envitem *ei, bool global);
ed34e3c3
A
216static void envitem_setup(launch_data_t obj, const char *key, void *context);
217
218struct limititem {
219 SLIST_ENTRY(limititem) sle;
220 struct rlimit lim;
221 unsigned int setsoft:1, sethard:1, which:30;
222};
223
5b0a4722
A
224static bool limititem_update(job_t j, int w, rlim_t r);
225static void limititem_delete(job_t j, struct limititem *li);
ed34e3c3 226static void limititem_setup(launch_data_t obj, const char *key, void *context);
f36da725 227#if HAVE_SANDBOX
5b0a4722 228static void seatbelt_setup_flags(launch_data_t obj, const char *key, void *context);
f36da725 229#endif
ed34e3c3
A
230
231typedef enum {
232 NETWORK_UP = 1,
233 NETWORK_DOWN,
234 SUCCESSFUL_EXIT,
235 FAILED_EXIT,
236 PATH_EXISTS,
237 PATH_MISSING,
5b0a4722
A
238 OTHER_JOB_ENABLED,
239 OTHER_JOB_DISABLED,
240 OTHER_JOB_ACTIVE,
241 OTHER_JOB_INACTIVE,
242 PATH_CHANGES,
243 DIR_NOT_EMPTY,
ed34e3c3
A
244 // FILESYSTEMTYPE_IS_MOUNTED, /* for nfsiod, but maybe others */
245} semaphore_reason_t;
246
247struct semaphoreitem {
248 SLIST_ENTRY(semaphoreitem) sle;
249 semaphore_reason_t why;
5b0a4722 250 int fd;
f36da725
A
251 union {
252 const char what[0];
253 char what_init[0];
254 };
ed34e3c3
A
255};
256
5b0a4722
A
257struct semaphoreitem_dict_iter_context {
258 job_t j;
259 semaphore_reason_t why_true;
260 semaphore_reason_t why_false;
261};
ed34e3c3 262
5b0a4722
A
263static bool semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what);
264static void semaphoreitem_delete(job_t j, struct semaphoreitem *si);
265static void semaphoreitem_setup(launch_data_t obj, const char *key, void *context);
266static void semaphoreitem_setup_dict_iter(launch_data_t obj, const char *key, void *context);
267static void semaphoreitem_callback(job_t j, struct kevent *kev);
268static void semaphoreitem_watch(job_t j, struct semaphoreitem *si);
269static void semaphoreitem_ignore(job_t j, struct semaphoreitem *si);
270static void semaphoreitem_runtime_mod_ref(struct semaphoreitem *si, bool add);
271
272#define ACTIVE_JOB_HASH_SIZE 32
273#define ACTIVE_JOB_HASH(x) (IS_POWER_OF_TWO(ACTIVE_JOB_HASH_SIZE) ? (x & (ACTIVE_JOB_HASH_SIZE - 1)) : (x % ACTIVE_JOB_HASH_SIZE))
274#define MACHSERVICE_HASH_SIZE 37
275
276struct jobmgr_s {
277 kq_callback kqjobmgr_callback;
278 SLIST_ENTRY(jobmgr_s) sle;
279 SLIST_HEAD(, jobmgr_s) submgrs;
280 LIST_HEAD(, job_s) jobs;
281 LIST_HEAD(, job_s) active_jobs[ACTIVE_JOB_HASH_SIZE];
282 LIST_HEAD(, machservice) ms_hash[MACHSERVICE_HASH_SIZE];
283 mach_port_t jm_port;
284 mach_port_t req_port;
285 jobmgr_t parentmgr;
286 int reboot_flags;
287 unsigned int global_on_demand_cnt;
288 unsigned int hopefully_first_cnt;
289 unsigned int normal_active_cnt;
290 unsigned int sent_stop_to_normal_jobs:1, sent_stop_to_hopefully_last_jobs:1, shutting_down:1, session_initialized:1;
f36da725
A
291 union {
292 const char name[0];
293 char name_init[0];
294 };
5b0a4722 295};
ed34e3c3 296
5b0a4722
A
297#define jobmgr_assumes(jm, e) \
298 (__builtin_expect(!(e), 0) ? jobmgr_log_bug(jm, __rcs_file_version__, __FILE__, __LINE__, #e), false : true)
299
300static jobmgr_t jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name);
301static job_t jobmgr_import2(jobmgr_t jm, launch_data_t pload);
302static jobmgr_t jobmgr_parent(jobmgr_t jm);
303static jobmgr_t jobmgr_do_garbage_collection(jobmgr_t jm);
f36da725 304static bool jobmgr_label_test(jobmgr_t jm, const char *str);
5b0a4722
A
305static void jobmgr_reap_bulk(jobmgr_t jm, struct kevent *kev);
306static void jobmgr_log_stray_children(jobmgr_t jm);
307static void jobmgr_remove(jobmgr_t jm);
308static void jobmgr_dispatch_all(jobmgr_t jm, bool newmounthack);
309static job_t jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag);
310static job_t jobmgr_find_by_pid(jobmgr_t jm, pid_t p, bool create_anon);
311static job_t job_mig_intran2(jobmgr_t jm, mach_port_t mport, pid_t upid);
312static void job_export_all2(jobmgr_t jm, launch_data_t where);
313static void jobmgr_callback(void *obj, struct kevent *kev);
314static void jobmgr_setup_env_from_other_jobs(jobmgr_t jm);
315static void jobmgr_export_env_from_other_jobs(jobmgr_t jm, launch_data_t dict);
316static struct machservice *jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent, pid_t target_pid);
317static void jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap) __attribute__((format(printf, 4, 0)));
318static void jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
319/* static void jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); */
320static void jobmgr_log_bug(jobmgr_t jm, const char *rcs_rev, const char *path, unsigned int line, const char *test);
321
322#define DO_RUSAGE_SUMMATION 0
323
324#define AUTO_PICK_LEGACY_LABEL (const char *)(~0)
325
326struct job_s {
ed34e3c3 327 kq_callback kqjob_callback;
5b0a4722
A
328 LIST_ENTRY(job_s) sle;
329 LIST_ENTRY(job_s) pid_hash_sle;
330 LIST_ENTRY(job_s) label_hash_sle;
ed34e3c3 331 SLIST_HEAD(, socketgroup) sockets;
ed34e3c3
A
332 SLIST_HEAD(, calendarinterval) cal_intervals;
333 SLIST_HEAD(, envitem) global_env;
334 SLIST_HEAD(, envitem) env;
335 SLIST_HEAD(, limititem) limits;
5b0a4722 336 SLIST_HEAD(, mspolicy) mspolicies;
ed34e3c3
A
337 SLIST_HEAD(, machservice) machservices;
338 SLIST_HEAD(, semaphoreitem) semaphores;
5b0a4722
A
339 SLIST_HEAD(, waiting_for_removal) removal_watchers;
340#if DO_RUSAGE_SUMMATION
ed34e3c3 341 struct rusage ru;
5b0a4722
A
342#endif
343 cpu_type_t *j_binpref;
344 size_t j_binpref_cnt;
345 mach_port_t j_port;
f36da725 346 mach_port_t wait_reply_port; /* we probably should switch to a list of waiters */
ed34e3c3 347 uid_t mach_uid;
5b0a4722 348 jobmgr_t mgr;
ed34e3c3
A
349 char **argv;
350 char *prog;
351 char *rootdir;
352 char *workingdir;
353 char *username;
354 char *groupname;
ed34e3c3
A
355 char *stdoutpath;
356 char *stderrpath;
fe044cc9 357 char *alt_exc_handler;
5b0a4722
A
358 struct machservice *lastlookup;
359 unsigned int lastlookup_gennum;
f36da725 360#if HAVE_SANDBOX
5b0a4722
A
361 char *seatbelt_profile;
362 uint64_t seatbelt_flags;
f36da725
A
363#endif
364#if HAVE_QUARANTINE
5b0a4722
A
365 void *quarantine_data;
366 size_t quarantine_data_sz;
f36da725 367#endif
ed34e3c3
A
368 pid_t p;
369 int argc;
370 int last_exit_status;
5b0a4722
A
371 int forkfd;
372 int log_redirect_fd;
ed34e3c3 373 int nice;
5b0a4722
A
374 unsigned int timeout;
375 unsigned int exit_timeout;
376 int stdout_err_fd;
377 uint64_t sent_sigterm_time;
378 uint64_t start_time;
379 uint32_t min_run_time;
380 uint32_t start_interval;
381 unsigned int checkedin:1, anonymous:1, debug:1, inetcompat:1, inetcompat_wait:1,
382 ondemand:1, session_create:1, low_pri_io:1, no_init_groups:1, priv_port_has_senders:1,
383 importing_global_env:1, importing_hard_limits:1, setmask:1, legacy_mach_job:1, start_pending:1;
ed34e3c3 384 mode_t mask;
5b0a4722
A
385 unsigned int globargv:1, wait4debugger:1, unload_at_exit:1, stall_before_exec:1, only_once:1,
386 currently_ignored:1, forced_peers_to_demand_mode:1, setnice:1, hopefully_exits_last:1, removal_pending:1,
387 wait4pipe_eof:1, sent_sigkill:1, debug_before_kill:1, weird_bootstrap:1, start_on_mount:1,
388 per_user:1, hopefully_exits_first:1, deny_unknown_mslookups:1, unload_at_mig_return:1, abandon_pg:1,
f36da725 389 poll_for_vfs_changes:1, internal_exc_handler:1, deny_job_creation:1;
5b0a4722 390 const char label[0];
ed34e3c3
A
391};
392
5b0a4722
A
393#define LABEL_HASH_SIZE 53
394
395static LIST_HEAD(, job_s) label_hash[LABEL_HASH_SIZE];
396static size_t hash_label(const char *label) __attribute__((pure));
397static size_t hash_ms(const char *msstr) __attribute__((pure));
398
399
400#define job_assumes(j, e) \
401 (__builtin_expect(!(e), 0) ? job_log_bug(j, __rcs_file_version__, __FILE__, __LINE__, #e), false : true)
402
ed34e3c3 403static void job_import_keys(launch_data_t obj, const char *key, void *context);
5b0a4722
A
404static void job_import_bool(job_t j, const char *key, bool value);
405static void job_import_string(job_t j, const char *key, const char *value);
406static void job_import_integer(job_t j, const char *key, long long value);
407static void job_import_dictionary(job_t j, const char *key, launch_data_t value);
408static void job_import_array(job_t j, const char *key, launch_data_t value);
409static void job_import_opaque(job_t j, const char *key, launch_data_t value);
410static bool job_set_global_on_demand(job_t j, bool val);
411static const char *job_active(job_t j);
412static void job_watch(job_t j);
413static void job_ignore(job_t j);
414static void job_reap(job_t j);
415static bool job_useless(job_t j);
416static bool job_keepalive(job_t j);
417static void job_start(job_t j);
418static void job_start_child(job_t j) __attribute__((noreturn));
419static void job_setup_attributes(job_t j);
420static bool job_setup_machport(job_t j);
421static void job_setup_fd(job_t j, int target_fd, const char *path, int flags);
422static void job_postfork_become_user(job_t j);
ef398931 423#if !TARGET_OS_EMBEDDED
cf0bacfd 424static void job_enable_audit_for_user(job_t j, uid_t u, char *name);
ef398931 425#endif
5b0a4722
A
426static void job_find_and_blame_pids_with_weird_uids(job_t j);
427static void job_force_sampletool(job_t j);
428static void job_setup_exception_port(job_t j, task_t target_task);
429static void job_reparent_hack(job_t j, const char *where);
ed34e3c3 430static void job_callback(void *obj, struct kevent *kev);
5b0a4722
A
431static void job_callback_proc(job_t j, int flags, int fflags);
432static void job_callback_timer(job_t j, void *ident);
433static void job_callback_read(job_t j, int ident);
434static void job_log_stray_pg(job_t j);
435static job_t job_new_anonymous(jobmgr_t jm, pid_t anonpid);
436static job_t job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *argv);
437static job_t job_new_via_mach_init(job_t j, const char *cmd, uid_t uid, bool ond);
438static const char *job_prog(job_t j);
439static jobmgr_t job_get_bs(job_t j);
440static void job_kill(job_t j);
441static void job_uncork_fork(job_t j);
442static void job_log_stdouterr(job_t j);
443static void job_logv(job_t j, int pri, int err, const char *msg, va_list ap) __attribute__((format(printf, 4, 0)));
444static void job_log_error(job_t j, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4)));
445static void job_log_bug(job_t j, const char *rcs_rev, const char *path, unsigned int line, const char *test);
446static void job_log_stdouterr2(job_t j, const char *msg, ...);
447static void job_set_exeception_port(job_t j, mach_port_t port);
448static kern_return_t job_handle_mpm_wait(job_t j, mach_port_t srp, int *waitstatus);
449
ed34e3c3
A
450
451
452static const struct {
453 const char *key;
454 int val;
455} launchd_keys2limits[] = {
5b0a4722
A
456 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE, RLIMIT_CORE },
457 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU, RLIMIT_CPU },
458 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA, RLIMIT_DATA },
459 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE, RLIMIT_FSIZE },
460 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK, RLIMIT_MEMLOCK },
461 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE, RLIMIT_NOFILE },
462 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC, RLIMIT_NPROC },
463 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS, RLIMIT_RSS },
464 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK, RLIMIT_STACK },
ed34e3c3
A
465};
466
467static time_t cronemu(int mon, int mday, int hour, int min);
468static time_t cronemu_wday(int wday, int hour, int min);
469static bool cronemu_mon(struct tm *wtm, int mon, int mday, int hour, int min);
470static bool cronemu_mday(struct tm *wtm, int mday, int hour, int min);
471static bool cronemu_hour(struct tm *wtm, int hour, int min);
472static bool cronemu_min(struct tm *wtm, int min);
473
5b0a4722
A
474/* miscellaneous file local functions */
475static void ensure_root_bkgd_setup(void);
476static int dir_has_files(job_t j, const char *path);
ed34e3c3 477static char **mach_cmd2argv(const char *string);
5b0a4722
A
478static size_t our_strhash(const char *s) __attribute__((pure));
479static void extract_rcsid_substr(const char *i, char *o, size_t osz);
480static void do_first_per_user_launchd_hack(void);
f36da725 481static size_t get_kern_max_proc(void);
5b0a4722
A
482static void do_file_init(void) __attribute__((constructor));
483
484/* file local globals */
f36da725 485static bool do_apple_internal_magic;
5b0a4722
A
486static size_t total_children;
487static size_t total_anon_children;
488static mach_port_t the_exception_server;
489static bool did_first_per_user_launchd_BootCache_hack;
490#define JOB_BOOTCACHE_HACK_CHECK(j) (j->per_user && !did_first_per_user_launchd_BootCache_hack && (j->mach_uid >= 500) && (j->mach_uid != (uid_t)-2))
491static jobmgr_t background_jobmgr;
492static job_t workaround_5477111;
493static mach_timebase_info_data_t tbi;
494
495/* process wide globals */
496mach_port_t inherited_bootstrap_port;
497jobmgr_t root_jobmgr;
ed34e3c3 498
ed34e3c3
A
499
500void
5b0a4722 501job_ignore(job_t j)
ed34e3c3 502{
5b0a4722 503 struct semaphoreitem *si;
ed34e3c3
A
504 struct socketgroup *sg;
505 struct machservice *ms;
ed34e3c3 506
5b0a4722
A
507 if (j->currently_ignored) {
508 return;
509 }
510
511 job_log(j, LOG_DEBUG, "Ignoring...");
512
513 j->currently_ignored = true;
514
515 if (j->poll_for_vfs_changes) {
516 j->poll_for_vfs_changes = false;
517 job_assumes(j, kevent_mod((uintptr_t)&j->semaphores, EVFILT_TIMER, EV_DELETE, 0, 0, j) != -1);
518 }
519
520 SLIST_FOREACH(sg, &j->sockets, sle) {
ed34e3c3 521 socketgroup_ignore(j, sg);
5b0a4722 522 }
ed34e3c3 523
5b0a4722
A
524 SLIST_FOREACH(ms, &j->machservices, sle) {
525 machservice_ignore(j, ms);
526 }
ed34e3c3 527
5b0a4722
A
528 SLIST_FOREACH(si, &j->semaphores, sle) {
529 semaphoreitem_ignore(j, si);
530 }
ed34e3c3
A
531}
532
533void
5b0a4722 534job_watch(job_t j)
ed34e3c3 535{
5b0a4722 536 struct semaphoreitem *si;
ed34e3c3
A
537 struct socketgroup *sg;
538 struct machservice *ms;
ed34e3c3 539
5b0a4722
A
540 if (!j->currently_ignored) {
541 return;
542 }
543
544 job_log(j, LOG_DEBUG, "Watching...");
545
546 j->currently_ignored = false;
547
548 SLIST_FOREACH(sg, &j->sockets, sle) {
ed34e3c3 549 socketgroup_watch(j, sg);
5b0a4722 550 }
ed34e3c3 551
5b0a4722
A
552 SLIST_FOREACH(ms, &j->machservices, sle) {
553 machservice_watch(j, ms);
554 }
ed34e3c3 555
5b0a4722
A
556 SLIST_FOREACH(si, &j->semaphores, sle) {
557 semaphoreitem_watch(j, si);
558 }
ed34e3c3
A
559}
560
561void
5b0a4722 562job_stop(job_t j)
ed34e3c3 563{
5b0a4722
A
564 if (!j->p || j->anonymous) {
565 return;
566 }
ed34e3c3 567
5b0a4722
A
568 job_assumes(j, runtime_kill(j->p, SIGTERM) != -1);
569 j->sent_sigterm_time = mach_absolute_time();
570
571 if (j->exit_timeout) {
572 job_assumes(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER,
573 EV_ADD|EV_ONESHOT, NOTE_SECONDS, j->exit_timeout, j) != -1);
574 }
575
576 job_log(j, LOG_DEBUG, "Sent SIGTERM signal");
ed34e3c3
A
577}
578
579launch_data_t
5b0a4722 580job_export(job_t j)
ed34e3c3
A
581{
582 launch_data_t tmp, tmp2, tmp3, r = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
583
5b0a4722 584 if (r == NULL) {
ed34e3c3 585 return NULL;
5b0a4722 586 }
ed34e3c3 587
5b0a4722 588 if ((tmp = launch_data_new_string(j->label))) {
ed34e3c3 589 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_LABEL);
5b0a4722
A
590 }
591 if ((tmp = launch_data_new_string(j->mgr->name))) {
592 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
593 }
594 if ((tmp = launch_data_new_bool(j->ondemand))) {
ed34e3c3 595 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_ONDEMAND);
5b0a4722
A
596 }
597 if ((tmp = launch_data_new_integer(j->last_exit_status))) {
ed34e3c3 598 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_LASTEXITSTATUS);
5b0a4722
A
599 }
600 if (j->p && (tmp = launch_data_new_integer(j->p))) {
ed34e3c3 601 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PID);
5b0a4722
A
602 }
603 if ((tmp = launch_data_new_integer(j->timeout))) {
ed34e3c3 604 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_TIMEOUT);
5b0a4722
A
605 }
606 if (j->prog && (tmp = launch_data_new_string(j->prog))) {
ed34e3c3 607 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PROGRAM);
5b0a4722
A
608 }
609 if (j->stdoutpath && (tmp = launch_data_new_string(j->stdoutpath))) {
ed34e3c3 610 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_STANDARDOUTPATH);
5b0a4722
A
611 }
612 if (j->stderrpath && (tmp = launch_data_new_string(j->stderrpath))) {
ed34e3c3 613 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_STANDARDERRORPATH);
5b0a4722 614 }
ed34e3c3
A
615 if (j->argv && (tmp = launch_data_alloc(LAUNCH_DATA_ARRAY))) {
616 int i;
617
618 for (i = 0; i < j->argc; i++) {
5b0a4722 619 if ((tmp2 = launch_data_new_string(j->argv[i]))) {
ed34e3c3 620 launch_data_array_set_index(tmp, tmp2, i);
5b0a4722 621 }
ed34e3c3
A
622 }
623
624 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
625 }
626
5b0a4722
A
627 if (j->session_create && (tmp = launch_data_new_bool(true))) {
628 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_SESSIONCREATE);
629 }
630
ed34e3c3 631 if (j->inetcompat && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) {
5b0a4722 632 if ((tmp2 = launch_data_new_bool(j->inetcompat_wait))) {
ed34e3c3 633 launch_data_dict_insert(tmp, tmp2, LAUNCH_JOBINETDCOMPATIBILITY_WAIT);
5b0a4722 634 }
ed34e3c3
A
635 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_INETDCOMPATIBILITY);
636 }
637
638 if (!SLIST_EMPTY(&j->sockets) && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) {
639 struct socketgroup *sg;
640 int i;
641
642 SLIST_FOREACH(sg, &j->sockets, sle) {
5b0a4722
A
643 if (sg->junkfds) {
644 continue;
645 }
ed34e3c3
A
646 if ((tmp2 = launch_data_alloc(LAUNCH_DATA_ARRAY))) {
647 for (i = 0; i < sg->fd_cnt; i++) {
5b0a4722 648 if ((tmp3 = launch_data_new_fd(sg->fds[i]))) {
ed34e3c3 649 launch_data_array_set_index(tmp2, tmp3, i);
5b0a4722 650 }
ed34e3c3
A
651 }
652 launch_data_dict_insert(tmp, tmp2, sg->name);
653 }
654 }
655
656 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_SOCKETS);
657 }
658
659 if (!SLIST_EMPTY(&j->machservices) && (tmp = launch_data_alloc(LAUNCH_DATA_DICTIONARY))) {
660 struct machservice *ms;
5b0a4722
A
661
662 tmp3 = NULL;
ed34e3c3
A
663
664 SLIST_FOREACH(ms, &j->machservices, sle) {
5b0a4722
A
665 if (ms->per_pid) {
666 if (tmp3 == NULL) {
667 tmp3 = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
668 }
669 if (tmp3) {
670 tmp2 = launch_data_new_machport(MACH_PORT_NULL);
671 launch_data_dict_insert(tmp3, tmp2, ms->name);
672 }
673 } else {
674 tmp2 = launch_data_new_machport(MACH_PORT_NULL);
675 launch_data_dict_insert(tmp, tmp2, ms->name);
676 }
ed34e3c3
A
677 }
678
679 launch_data_dict_insert(r, tmp, LAUNCH_JOBKEY_MACHSERVICES);
5b0a4722
A
680
681 if (tmp3) {
682 launch_data_dict_insert(r, tmp3, LAUNCH_JOBKEY_PERJOBMACHSERVICES);
683 }
684 }
685
686 return r;
687}
688
689static void
690jobmgr_log_active_jobs(jobmgr_t jm)
691{
692 const char *why_active;
693 jobmgr_t jmi;
694 job_t ji;
695
696 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
697 jobmgr_log_active_jobs(jmi);
698 }
699
700 LIST_FOREACH(ji, &jm->jobs, sle) {
701 why_active = job_active(ji);
702
703 job_log(ji, LOG_DEBUG, "%s", why_active ? why_active : "Inactive");
ed34e3c3
A
704 }
705
5b0a4722
A
706}
707
708static void
709still_alive_with_check(void)
710{
711 jobmgr_log(root_jobmgr, LOG_NOTICE, "Still alive with %lu/%lu children", total_children, total_anon_children);
712
713 jobmgr_log_active_jobs(root_jobmgr);
714
715 runtime_closelog(); /* hack to flush logs */
716}
717
718jobmgr_t
719jobmgr_shutdown(jobmgr_t jm)
720{
721 jobmgr_t jmi, jmn;
722 job_t ji;
723
724 jobmgr_log(jm, LOG_DEBUG, "Beginning job manager shutdown with flags: %s", reboot_flags_to_C_names(jm->reboot_flags));
725
726 jm->shutting_down = true;
727
728 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
729 jobmgr_shutdown(jmi);
730 }
ed34e3c3 731
5b0a4722
A
732 if (jm->hopefully_first_cnt) {
733 LIST_FOREACH(ji, &jm->jobs, sle) {
734 if (ji->p && ji->hopefully_exits_first) {
735 job_stop(ji);
736 }
ed34e3c3 737 }
5b0a4722 738 }
ed34e3c3 739
5b0a4722
A
740 if (debug_shutdown_hangs && jm->parentmgr == NULL && getpid() == 1) {
741 runtime_set_timeout(still_alive_with_check, 5);
ed34e3c3
A
742 }
743
5b0a4722 744 return jobmgr_do_garbage_collection(jm);
ed34e3c3
A
745}
746
747void
5b0a4722 748jobmgr_remove(jobmgr_t jm)
ed34e3c3 749{
5b0a4722
A
750 jobmgr_t jmi;
751 job_t ji;
ed34e3c3 752
5b0a4722 753 jobmgr_log(jm, LOG_DEBUG, "Removed job manager");
ed34e3c3 754
5b0a4722
A
755 if (!jobmgr_assumes(jm, SLIST_EMPTY(&jm->submgrs))) {
756 while ((jmi = SLIST_FIRST(&jm->submgrs))) {
757 jobmgr_remove(jmi);
758 }
759 }
760
761 while ((ji = LIST_FIRST(&jm->jobs))) {
762 /* We should only have anonymous jobs left */
763 job_assumes(ji, ji->anonymous);
764 job_remove(ji);
765 }
766
767 if (jm->req_port) {
768 jobmgr_assumes(jm, launchd_mport_deallocate(jm->req_port) == KERN_SUCCESS);
769 }
770
771 if (jm->jm_port) {
772 jobmgr_assumes(jm, launchd_mport_close_recv(jm->jm_port) == KERN_SUCCESS);
773 }
774
775 if (jm == background_jobmgr) {
776 background_jobmgr = NULL;
ed34e3c3 777 }
5b0a4722
A
778
779 if (jm->parentmgr) {
780 runtime_del_ref();
781 SLIST_REMOVE(&jm->parentmgr->submgrs, jm, jobmgr_s, sle);
782 } else if (getpid() == 1) {
783 jobmgr_log(jm, LOG_DEBUG, "About to call: reboot(%s)", reboot_flags_to_C_names(jm->reboot_flags));
784 runtime_closelog();
785 jobmgr_assumes(jm, reboot(jm->reboot_flags) != -1);
786 runtime_closelog();
787 } else {
788 runtime_closelog();
789 jobmgr_log(jm, LOG_DEBUG, "About to exit");
790 exit(EXIT_SUCCESS);
791 }
792
793 free(jm);
ed34e3c3
A
794}
795
796void
5b0a4722 797job_remove(job_t j)
ed34e3c3 798{
5b0a4722 799 struct waiting_for_removal *w4r;
ed34e3c3 800 struct calendarinterval *ci;
5b0a4722 801 struct semaphoreitem *si;
ed34e3c3 802 struct socketgroup *sg;
5b0a4722 803 struct machservice *ms;
ed34e3c3 804 struct limititem *li;
5b0a4722 805 struct mspolicy *msp;
ed34e3c3 806 struct envitem *ei;
ed34e3c3 807
5b0a4722
A
808 if (j->p && j->anonymous) {
809 job_reap(j);
810 } else if (j->p) {
811 job_log(j, LOG_DEBUG, "Removal pended until the job exits");
ed34e3c3 812
5b0a4722
A
813 if (!j->removal_pending) {
814 j->removal_pending = true;
ed34e3c3
A
815 job_stop(j);
816 }
5b0a4722
A
817
818 return;
ed34e3c3
A
819 }
820
5b0a4722 821 ipc_close_all_with_job(j);
ed34e3c3 822
5b0a4722
A
823 if (j->forced_peers_to_demand_mode) {
824 job_set_global_on_demand(j, false);
825 }
ed34e3c3 826
5b0a4722
A
827 if (!job_assumes(j, j->forkfd == 0)) {
828 job_assumes(j, runtime_close(j->forkfd) != -1);
ed34e3c3
A
829 }
830
5b0a4722
A
831 if (!job_assumes(j, j->log_redirect_fd == 0)) {
832 job_assumes(j, runtime_close(j->log_redirect_fd) != -1);
833 }
ed34e3c3 834
5b0a4722
A
835 if (j->j_port) {
836 job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS);
ed34e3c3 837 }
ed34e3c3 838
5b0a4722
A
839 if (!job_assumes(j, j->wait_reply_port == MACH_PORT_NULL)) {
840 job_assumes(j, launchd_mport_deallocate(j->wait_reply_port) == KERN_SUCCESS);
841 }
ed34e3c3 842
5b0a4722
A
843 while ((msp = SLIST_FIRST(&j->mspolicies))) {
844 mspolicy_delete(j, msp);
845 }
846 while ((sg = SLIST_FIRST(&j->sockets))) {
ed34e3c3 847 socketgroup_delete(j, sg);
5b0a4722
A
848 }
849 while ((ci = SLIST_FIRST(&j->cal_intervals))) {
ed34e3c3 850 calendarinterval_delete(j, ci);
5b0a4722
A
851 }
852 while ((ei = SLIST_FIRST(&j->env))) {
ed34e3c3 853 envitem_delete(j, ei, false);
5b0a4722
A
854 }
855 while ((ei = SLIST_FIRST(&j->global_env))) {
ed34e3c3 856 envitem_delete(j, ei, true);
5b0a4722
A
857 }
858 while ((li = SLIST_FIRST(&j->limits))) {
ed34e3c3 859 limititem_delete(j, li);
5b0a4722
A
860 }
861 while ((ms = SLIST_FIRST(&j->machservices))) {
862 machservice_delete(j, ms, false);
863 }
864 while ((si = SLIST_FIRST(&j->semaphores))) {
ed34e3c3 865 semaphoreitem_delete(j, si);
5b0a4722
A
866 }
867 while ((w4r = SLIST_FIRST(&j->removal_watchers))) {
868 waiting4removal_delete(j, w4r);
869 }
ed34e3c3 870
5b0a4722 871 if (j->prog) {
ed34e3c3 872 free(j->prog);
5b0a4722
A
873 }
874 if (j->argv) {
ed34e3c3 875 free(j->argv);
5b0a4722
A
876 }
877 if (j->rootdir) {
ed34e3c3 878 free(j->rootdir);
5b0a4722
A
879 }
880 if (j->workingdir) {
ed34e3c3 881 free(j->workingdir);
5b0a4722
A
882 }
883 if (j->username) {
ed34e3c3 884 free(j->username);
5b0a4722
A
885 }
886 if (j->groupname) {
ed34e3c3 887 free(j->groupname);
5b0a4722
A
888 }
889 if (j->stdoutpath) {
ed34e3c3 890 free(j->stdoutpath);
5b0a4722
A
891 }
892 if (j->stderrpath) {
ed34e3c3 893 free(j->stderrpath);
5b0a4722 894 }
fe044cc9
A
895 if (j->alt_exc_handler) {
896 free(j->alt_exc_handler);
897 }
f36da725 898#if HAVE_SANDBOX
5b0a4722
A
899 if (j->seatbelt_profile) {
900 free(j->seatbelt_profile);
901 }
f36da725
A
902#endif
903#if HAVE_QUARANTINE
5b0a4722
A
904 if (j->quarantine_data) {
905 free(j->quarantine_data);
906 }
f36da725 907#endif
5b0a4722
A
908 if (j->j_binpref) {
909 free(j->j_binpref);
910 }
911 if (j->start_interval) {
912 runtime_del_ref();
913 job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1);
914 }
915 if (j->poll_for_vfs_changes) {
916 job_assumes(j, kevent_mod((uintptr_t)&j->semaphores, EVFILT_TIMER, EV_DELETE, 0, 0, j) != -1);
917 }
ed34e3c3
A
918
919 kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
5b0a4722
A
920
921 LIST_REMOVE(j, sle);
922 LIST_REMOVE(j, label_hash_sle);
923
924 job_log(j, LOG_DEBUG, "Removed");
925
ed34e3c3
A
926 free(j);
927}
928
929void
930socketgroup_setup(launch_data_t obj, const char *key, void *context)
931{
932 launch_data_t tmp_oai;
5b0a4722 933 job_t j = context;
ed34e3c3
A
934 unsigned int i, fd_cnt = 1;
935 int *fds;
936
5b0a4722 937 if (launch_data_get_type(obj) == LAUNCH_DATA_ARRAY) {
ed34e3c3 938 fd_cnt = launch_data_array_get_count(obj);
5b0a4722 939 }
ed34e3c3
A
940
941 fds = alloca(fd_cnt * sizeof(int));
942
943 for (i = 0; i < fd_cnt; i++) {
5b0a4722 944 if (launch_data_get_type(obj) == LAUNCH_DATA_ARRAY) {
ed34e3c3 945 tmp_oai = launch_data_array_get_index(obj, i);
5b0a4722 946 } else {
ed34e3c3 947 tmp_oai = obj;
5b0a4722 948 }
ed34e3c3
A
949
950 fds[i] = launch_data_get_fd(tmp_oai);
951 }
952
953 socketgroup_new(j, key, fds, fd_cnt, strcmp(key, LAUNCH_JOBKEY_BONJOURFDS) == 0);
954
955 ipc_revoke_fds(obj);
956}
957
958bool
5b0a4722
A
959job_set_global_on_demand(job_t j, bool val)
960{
961 if (j->forced_peers_to_demand_mode && val) {
962 return false;
963 } else if (!j->forced_peers_to_demand_mode && !val) {
964 return false;
965 }
966
967 if ((j->forced_peers_to_demand_mode = val)) {
968 j->mgr->global_on_demand_cnt++;
969 } else {
970 j->mgr->global_on_demand_cnt--;
971 }
972
973 if (j->mgr->global_on_demand_cnt == 0) {
974 jobmgr_dispatch_all(j->mgr, false);
975 }
976
977 return true;
978}
979
980bool
981job_setup_machport(job_t j)
ed34e3c3 982{
5b0a4722
A
983 mach_msg_size_t mxmsgsz;
984
985 if (!job_assumes(j, launchd_mport_create_recv(&j->j_port) == KERN_SUCCESS)) {
ed34e3c3 986 goto out_bad;
5b0a4722
A
987 }
988
989 /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */
990 mxmsgsz = sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem);
991 if (job_mig_protocol_vproc_subsystem.maxsize > mxmsgsz) {
992 mxmsgsz = job_mig_protocol_vproc_subsystem.maxsize;
993 }
ed34e3c3 994
5b0a4722 995 if (!job_assumes(j, runtime_add_mport(j->j_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) {
ed34e3c3 996 goto out_bad2;
5b0a4722
A
997 }
998
999 if (!job_assumes(j, launchd_mport_notify_req(j->j_port, MACH_NOTIFY_NO_SENDERS) == KERN_SUCCESS)) {
1000 job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS);
1001 goto out_bad;
1002 }
ed34e3c3
A
1003
1004 return true;
1005out_bad2:
5b0a4722 1006 job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS);
ed34e3c3
A
1007out_bad:
1008 return false;
1009}
1010
5b0a4722
A
1011job_t
1012job_new_via_mach_init(job_t j, const char *cmd, uid_t uid, bool ond)
ed34e3c3
A
1013{
1014 const char **argv = (const char **)mach_cmd2argv(cmd);
5b0a4722 1015 job_t jr = NULL;
ed34e3c3 1016
5b0a4722 1017 if (!job_assumes(j, argv != NULL)) {
ed34e3c3 1018 goto out_bad;
5b0a4722 1019 }
ed34e3c3 1020
5b0a4722 1021 jr = job_new(j->mgr, AUTO_PICK_LEGACY_LABEL, NULL, argv);
ed34e3c3
A
1022
1023 free(argv);
1024
5b0a4722
A
1025 /* jobs can easily be denied creation during shutdown */
1026 if (!jr) {
ed34e3c3 1027 goto out_bad;
5b0a4722 1028 }
ed34e3c3 1029
5b0a4722
A
1030 jr->mach_uid = uid;
1031 jr->ondemand = ond;
1032 jr->legacy_mach_job = true;
1033 jr->abandon_pg = true;
1034 jr->priv_port_has_senders = true; /* the IPC that called us will make-send on this port */
ed34e3c3 1035
5b0a4722 1036 if (!job_setup_machport(jr)) {
ed34e3c3
A
1037 goto out_bad;
1038 }
1039
5b0a4722 1040 job_log(jr, LOG_INFO, "Legacy%s server created", ond ? " on-demand" : "");
ed34e3c3 1041
5b0a4722 1042 return jr;
ed34e3c3
A
1043
1044out_bad:
5b0a4722
A
1045 if (jr) {
1046 job_remove(jr);
1047 }
ed34e3c3
A
1048 return NULL;
1049}
1050
1051kern_return_t
5b0a4722 1052job_handle_mpm_wait(job_t j, mach_port_t srp, int *waitstatus)
ed34e3c3
A
1053{
1054 if (j->p) {
1055 j->wait_reply_port = srp;
1056 return MIG_NO_REPLY;
1057 }
1058
1059 *waitstatus = j->last_exit_status;
1060
1061 return 0;
1062}
1063
5b0a4722
A
1064job_t
1065job_new_anonymous(jobmgr_t jm, pid_t anonpid)
ed34e3c3 1066{
5b0a4722
A
1067 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, anonpid };
1068 struct kinfo_proc kp;
1069 size_t len = sizeof(kp);
1070 const char *zombie = NULL;
1071 bool shutdown_state;
1072 job_t jp = NULL, jr = NULL;
1073
1074 if (!jobmgr_assumes(jm, anonpid != 0)) {
ed34e3c3
A
1075 return NULL;
1076 }
5b0a4722
A
1077
1078 if (!jobmgr_assumes(jm, anonpid < 100000)) {
1079 /* The kernel current defines PID_MAX to be 99999, but that define isn't exported */
ed34e3c3 1080 return NULL;
5b0a4722 1081 }
ed34e3c3 1082
5b0a4722
A
1083 if (!jobmgr_assumes(jm, sysctl(mib, 4, &kp, &len, NULL, 0) != -1)) {
1084 return NULL;
1085 }
ed34e3c3 1086
5b0a4722
A
1087 if (len != sizeof(kp)) {
1088 jobmgr_log(jm, LOG_DEBUG, "Tried to create an anonymous job for nonexistent PID: %u", anonpid);
ed34e3c3
A
1089 return NULL;
1090 }
1091
5b0a4722
A
1092 if (!jobmgr_assumes(jm, kp.kp_proc.p_comm[0] != '\0')) {
1093 return NULL;
1094 }
ed34e3c3 1095
5b0a4722
A
1096 if (kp.kp_proc.p_stat == SZOMB) {
1097 jobmgr_log(jm, LOG_DEBUG, "Tried to create an anonymous job for zombie PID: %u", anonpid);
1098 zombie = "zombie";
ed34e3c3
A
1099 }
1100
5b0a4722
A
1101 switch (kp.kp_eproc.e_ppid) {
1102 case 0:
1103 /* the kernel */
1104 break;
1105 case 1:
1106 if (getpid() != 1) {
1107 /* we cannot possibly find a parent job_t that is useful in this function */
1108 break;
1109 }
1110 /* fall through */
1111 default:
1112 jp = jobmgr_find_by_pid(jm, kp.kp_eproc.e_ppid, true);
1113 jobmgr_assumes(jm, jp != NULL);
1114 break;
1115 }
1116
1117 /* A total hack: Normally, job_new() returns an error during shutdown, but anonymous jobs are special. */
1118 if ((shutdown_state = jm->shutting_down)) {
1119 jm->shutting_down = false;
1120 }
1121
1122 if (jobmgr_assumes(jm, (jr = job_new(jm, AUTO_PICK_LEGACY_LABEL, zombie ? zombie : kp.kp_proc.p_comm, NULL)) != NULL)) {
1123 u_int proc_fflags = NOTE_EXEC|NOTE_EXIT /* |NOTE_REAP */;
1124
1125 total_anon_children++;
1126 jr->anonymous = true;
1127 jr->p = anonpid;
1128
1129 /* anonymous process reaping is messy */
1130 LIST_INSERT_HEAD(&jm->active_jobs[ACTIVE_JOB_HASH(jr->p)], jr, pid_hash_sle);
1131
1132 if (kevent_mod(jr->p, EVFILT_PROC, EV_ADD, proc_fflags, 0, root_jobmgr) == -1 && job_assumes(jr, errno == ESRCH)) {
1133 /* zombies are weird */
1134 job_log(jr, LOG_ERR, "Failed to add kevent for PID %u. Will unload at MIG return", jr->p);
1135 jr->unload_at_mig_return = true;
1136 }
1137
1138 if (jp) {
1139 job_assumes(jr, mspolicy_copy(jr, jp));
1140 }
1141
1142 if (shutdown_state && jm->hopefully_first_cnt == 0) {
1143 job_log(jr, LOG_APPLEONLY, "This process showed up to the party while all the guests were leaving. Odds are that it will have a miserable time");
ed34e3c3 1144 }
5b0a4722
A
1145
1146 job_log(jr, LOG_DEBUG, "Created PID %u anonymously by PPID %u%s%s", anonpid, kp.kp_eproc.e_ppid, jp ? ": " : "", jp ? jp->label : "");
ed34e3c3
A
1147 }
1148
5b0a4722
A
1149 if (shutdown_state) {
1150 jm->shutting_down = true;
1151 }
ed34e3c3
A
1152
1153 return jr;
1154}
1155
5b0a4722
A
1156job_t
1157job_new(jobmgr_t jm, const char *label, const char *prog, const char *const *argv)
ed34e3c3
A
1158{
1159 const char *const *argv_tmp = argv;
5b0a4722
A
1160 char auto_label[1000];
1161 const char *bn = NULL;
ed34e3c3 1162 char *co;
5b0a4722 1163 size_t minlabel_len;
ed34e3c3 1164 int i, cc = 0;
5b0a4722
A
1165 job_t j;
1166
1167 launchd_assert(offsetof(struct job_s, kqjob_callback) == 0);
1168
1169 if (jm->shutting_down) {
1170 errno = EINVAL;
1171 return NULL;
1172 }
ed34e3c3 1173
5b0a4722 1174 if (prog == NULL && argv == NULL) {
ed34e3c3
A
1175 errno = EINVAL;
1176 return NULL;
1177 }
1178
5b0a4722
A
1179 if (label == AUTO_PICK_LEGACY_LABEL) {
1180 bn = prog ? prog : basename((char *)argv[0]); /* prog for auto labels is kp.kp_kproc.p_comm */
1181 snprintf(auto_label, sizeof(auto_label), "%s.%s", sizeof(void *) == 8 ? "0xdeadbeeffeedface" : "0xbabecafe", bn);
1182 label = auto_label;
1183 /* This is so we can do gross things later. See NOTE_EXEC for anonymous jobs */
1184 minlabel_len = strlen(label) + MAXCOMLEN;
1185 } else {
1186 minlabel_len = strlen(label);
1187 }
1188
1189 j = calloc(1, sizeof(struct job_s) + minlabel_len + 1);
ed34e3c3 1190
5b0a4722
A
1191 if (!jobmgr_assumes(jm, j != NULL)) {
1192 return NULL;
1193 }
ed34e3c3 1194
5b0a4722
A
1195 if (label == auto_label) {
1196 snprintf((char *)j->label, strlen(label) + 1, "%p.%s", j, bn);
1197 } else {
1198 strcpy((char *)j->label, label);
1199 }
ed34e3c3 1200 j->kqjob_callback = job_callback;
5b0a4722
A
1201 j->mgr = jm;
1202 j->min_run_time = LAUNCHD_MIN_JOB_RUN_TIME;
1203 j->timeout = RUNTIME_ADVISABLE_IDLE_TIMEOUT;
1204 j->exit_timeout = LAUNCHD_DEFAULT_EXIT_TIMEOUT;
1205 j->currently_ignored = true;
ed34e3c3
A
1206 j->ondemand = true;
1207 j->checkedin = true;
ed34e3c3
A
1208
1209 if (prog) {
1210 j->prog = strdup(prog);
5b0a4722 1211 if (!job_assumes(j, j->prog != NULL)) {
ed34e3c3 1212 goto out_bad;
5b0a4722 1213 }
ed34e3c3
A
1214 }
1215
1216 if (argv) {
1217 while (*argv_tmp++)
1218 j->argc++;
1219
5b0a4722 1220 for (i = 0; i < j->argc; i++) {
ed34e3c3 1221 cc += strlen(argv[i]) + 1;
5b0a4722 1222 }
ed34e3c3
A
1223
1224 j->argv = malloc((j->argc + 1) * sizeof(char *) + cc);
1225
5b0a4722 1226 if (!job_assumes(j, j->argv != NULL)) {
ed34e3c3 1227 goto out_bad;
5b0a4722 1228 }
ed34e3c3
A
1229
1230 co = ((char *)j->argv) + ((j->argc + 1) * sizeof(char *));
1231
1232 for (i = 0; i < j->argc; i++) {
1233 j->argv[i] = co;
1234 strcpy(co, argv[i]);
1235 co += strlen(argv[i]) + 1;
1236 }
1237 j->argv[i] = NULL;
1238 }
1239
5b0a4722
A
1240 LIST_INSERT_HEAD(&jm->jobs, j, sle);
1241 LIST_INSERT_HEAD(&label_hash[hash_label(j->label)], j, label_hash_sle);
1242
1243 job_log(j, LOG_DEBUG, "Conceived");
ed34e3c3
A
1244
1245 return j;
1246
1247out_bad:
5b0a4722
A
1248 if (j->prog) {
1249 free(j->prog);
ed34e3c3 1250 }
5b0a4722
A
1251 free(j);
1252
ed34e3c3
A
1253 return NULL;
1254}
1255
5b0a4722 1256job_t
ed34e3c3
A
1257job_import(launch_data_t pload)
1258{
5b0a4722 1259 job_t j = jobmgr_import2(root_jobmgr, pload);
ed34e3c3 1260
5b0a4722 1261 if (j == NULL) {
ed34e3c3 1262 return NULL;
5b0a4722 1263 }
ed34e3c3 1264
5b0a4722 1265 return job_dispatch(j, false);
ed34e3c3
A
1266}
1267
1268launch_data_t
1269job_import_bulk(launch_data_t pload)
1270{
1271 launch_data_t resp = launch_data_alloc(LAUNCH_DATA_ARRAY);
5b0a4722 1272 job_t *ja;
ed34e3c3
A
1273 size_t i, c = launch_data_array_get_count(pload);
1274
5b0a4722 1275 ja = alloca(c * sizeof(job_t ));
ed34e3c3
A
1276
1277 for (i = 0; i < c; i++) {
5b0a4722 1278 if ((ja[i] = jobmgr_import2(root_jobmgr, launch_data_array_get_index(pload, i)))) {
ed34e3c3 1279 errno = 0;
5b0a4722 1280 }
ed34e3c3
A
1281 launch_data_array_set_index(resp, launch_data_new_errno(errno), i);
1282 }
1283
1284 for (i = 0; i < c; i++) {
5b0a4722 1285 if (ja[i] == NULL) {
ed34e3c3 1286 continue;
5b0a4722
A
1287 }
1288 job_dispatch(ja[i], false);
ed34e3c3
A
1289 }
1290
1291 return resp;
1292}
1293
1294void
5b0a4722 1295job_import_bool(job_t j, const char *key, bool value)
ed34e3c3 1296{
5b0a4722
A
1297 bool found_key = false;
1298
ed34e3c3 1299 switch (key[0]) {
5b0a4722
A
1300 case 'a':
1301 case 'A':
1302 if (strcasecmp(key, LAUNCH_JOBKEY_ABANDONPROCESSGROUP) == 0) {
1303 j->abandon_pg = value;
1304 found_key = true;
1305 }
ed34e3c3
A
1306 break;
1307 case 'k':
1308 case 'K':
5b0a4722 1309 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE) == 0) {
ed34e3c3 1310 j->ondemand = !value;
5b0a4722
A
1311 found_key = true;
1312 }
ed34e3c3
A
1313 break;
1314 case 'o':
1315 case 'O':
5b0a4722 1316 if (strcasecmp(key, LAUNCH_JOBKEY_ONDEMAND) == 0) {
ed34e3c3 1317 j->ondemand = value;
5b0a4722
A
1318 found_key = true;
1319 }
ed34e3c3
A
1320 break;
1321 case 'd':
1322 case 'D':
5b0a4722 1323 if (strcasecmp(key, LAUNCH_JOBKEY_DEBUG) == 0) {
ed34e3c3 1324 j->debug = value;
5b0a4722
A
1325 found_key = true;
1326 } else if (strcasecmp(key, LAUNCH_JOBKEY_DISABLED) == 0) {
1327 job_assumes(j, !value);
1328 found_key = true;
1329 }
1330 break;
1331 case 'h':
1332 case 'H':
1333 if (strcasecmp(key, LAUNCH_JOBKEY_HOPEFULLYEXITSLAST) == 0) {
1334 j->hopefully_exits_last = value;
1335 found_key = true;
1336 } else if (strcasecmp(key, LAUNCH_JOBKEY_HOPEFULLYEXITSFIRST) == 0) {
1337 j->hopefully_exits_first = value;
1338 found_key = true;
1339 }
ed34e3c3
A
1340 break;
1341 case 's':
1342 case 'S':
5b0a4722 1343 if (strcasecmp(key, LAUNCH_JOBKEY_SESSIONCREATE) == 0) {
ed34e3c3 1344 j->session_create = value;
5b0a4722
A
1345 found_key = true;
1346 } else if (strcasecmp(key, LAUNCH_JOBKEY_STARTONMOUNT) == 0) {
1347 j->start_on_mount = value;
1348 found_key = true;
1349 } else if (strcasecmp(key, LAUNCH_JOBKEY_SERVICEIPC) == 0) {
1350 /* this only does something on Mac OS X 10.4 "Tiger" */
1351 found_key = true;
1352 }
ed34e3c3
A
1353 break;
1354 case 'l':
1355 case 'L':
5b0a4722 1356 if (strcasecmp(key, LAUNCH_JOBKEY_LOWPRIORITYIO) == 0) {
ed34e3c3 1357 j->low_pri_io = value;
5b0a4722
A
1358 found_key = true;
1359 } else if (strcasecmp(key, LAUNCH_JOBKEY_LAUNCHONLYONCE) == 0) {
1360 j->only_once = value;
1361 found_key = true;
1362 }
ed34e3c3 1363 break;
fe044cc9
A
1364 case 'm':
1365 case 'M':
1366 if (strcasecmp(key, LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER) == 0) {
1367 j->internal_exc_handler = value;
1368 found_key = true;
1369 }
1370 break;
ed34e3c3
A
1371 case 'i':
1372 case 'I':
5b0a4722
A
1373 if (strcasecmp(key, LAUNCH_JOBKEY_INITGROUPS) == 0) {
1374 if (getuid() != 0) {
1375 job_log(j, LOG_WARNING, "Ignored this key: %s", key);
1376 return;
1377 }
1378 j->no_init_groups = !value;
1379 found_key = true;
1380 }
ed34e3c3
A
1381 break;
1382 case 'r':
1383 case 'R':
5b0a4722
A
1384 if (strcasecmp(key, LAUNCH_JOBKEY_RUNATLOAD) == 0) {
1385 if (value) {
1386 /* We don't want value == false to change j->start_pending */
1387 j->start_pending = true;
1388 }
1389 found_key = true;
1390 }
ed34e3c3
A
1391 break;
1392 case 'e':
1393 case 'E':
5b0a4722 1394 if (strcasecmp(key, LAUNCH_JOBKEY_ENABLEGLOBBING) == 0) {
ed34e3c3 1395 j->globargv = value;
5b0a4722
A
1396 found_key = true;
1397 } else if (strcasecmp(key, LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL) == 0) {
1398 j->debug_before_kill = value;
1399 found_key = true;
1400 }
ed34e3c3
A
1401 break;
1402 case 'w':
1403 case 'W':
5b0a4722 1404 if (strcasecmp(key, LAUNCH_JOBKEY_WAITFORDEBUGGER) == 0) {
ed34e3c3 1405 j->wait4debugger = value;
5b0a4722
A
1406 found_key = true;
1407 }
ed34e3c3
A
1408 break;
1409 default:
1410 break;
1411 }
5b0a4722
A
1412
1413 if (!found_key) {
1414 job_log(j, LOG_WARNING, "Unknown key for boolean: %s", key);
1415 }
ed34e3c3
A
1416}
1417
1418void
5b0a4722 1419job_import_string(job_t j, const char *key, const char *value)
ed34e3c3
A
1420{
1421 char **where2put = NULL;
ed34e3c3
A
1422
1423 switch (key[0]) {
fe044cc9
A
1424 case 'm':
1425 case 'M':
1426 if (strcasecmp(key, LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER) == 0) {
1427 where2put = &j->alt_exc_handler;
1428 }
1429 break;
ed34e3c3
A
1430 case 'p':
1431 case 'P':
5b0a4722
A
1432 if (strcasecmp(key, LAUNCH_JOBKEY_PROGRAM) == 0) {
1433 return;
1434 }
ed34e3c3
A
1435 break;
1436 case 'l':
1437 case 'L':
5b0a4722
A
1438 if (strcasecmp(key, LAUNCH_JOBKEY_LABEL) == 0) {
1439 return;
1440 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADTOHOSTS) == 0) {
1441 return;
1442 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS) == 0) {
1443 return;
1444 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE) == 0) {
1445 job_reparent_hack(j, value);
1446 return;
1447 }
ed34e3c3
A
1448 break;
1449 case 'r':
1450 case 'R':
5b0a4722
A
1451 if (strcasecmp(key, LAUNCH_JOBKEY_ROOTDIRECTORY) == 0) {
1452 if (getuid() != 0) {
1453 job_log(j, LOG_WARNING, "Ignored this key: %s", key);
1454 return;
1455 }
ed34e3c3 1456 where2put = &j->rootdir;
5b0a4722 1457 }
ed34e3c3
A
1458 break;
1459 case 'w':
1460 case 'W':
5b0a4722 1461 if (strcasecmp(key, LAUNCH_JOBKEY_WORKINGDIRECTORY) == 0) {
ed34e3c3 1462 where2put = &j->workingdir;
5b0a4722 1463 }
ed34e3c3
A
1464 break;
1465 case 'u':
1466 case 'U':
5b0a4722
A
1467 if (strcasecmp(key, LAUNCH_JOBKEY_USERNAME) == 0) {
1468 if (getuid() != 0) {
1469 job_log(j, LOG_WARNING, "Ignored this key: %s", key);
1470 return;
1471 } else if (strcmp(value, "root") == 0) {
1472 return;
1473 }
ed34e3c3 1474 where2put = &j->username;
5b0a4722 1475 }
ed34e3c3
A
1476 break;
1477 case 'g':
1478 case 'G':
5b0a4722
A
1479 if (strcasecmp(key, LAUNCH_JOBKEY_GROUPNAME) == 0) {
1480 if (getuid() != 0) {
1481 job_log(j, LOG_WARNING, "Ignored this key: %s", key);
1482 return;
1483 } else if (strcmp(value, "wheel") == 0) {
1484 return;
1485 }
ed34e3c3 1486 where2put = &j->groupname;
5b0a4722 1487 }
ed34e3c3
A
1488 break;
1489 case 's':
1490 case 'S':
1491 if (strcasecmp(key, LAUNCH_JOBKEY_STANDARDOUTPATH) == 0) {
1492 where2put = &j->stdoutpath;
1493 } else if (strcasecmp(key, LAUNCH_JOBKEY_STANDARDERRORPATH) == 0) {
1494 where2put = &j->stderrpath;
f36da725 1495#if HAVE_SANDBOX
5b0a4722
A
1496 } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXPROFILE) == 0) {
1497 where2put = &j->seatbelt_profile;
f36da725 1498#endif
ed34e3c3
A
1499 }
1500 break;
1501 default:
5b0a4722 1502 job_log(j, LOG_WARNING, "Unknown key for string: %s", key);
ed34e3c3
A
1503 break;
1504 }
1505
1506 if (where2put) {
5b0a4722 1507 job_assumes(j, (*where2put = strdup(value)) != NULL);
ed34e3c3 1508 } else {
5b0a4722 1509 job_log(j, LOG_WARNING, "Unknown key: %s", key);
ed34e3c3
A
1510 }
1511}
1512
1513void
5b0a4722 1514job_import_integer(job_t j, const char *key, long long value)
ed34e3c3
A
1515{
1516 switch (key[0]) {
5b0a4722
A
1517 case 'e':
1518 case 'E':
1519 if (strcasecmp(key, LAUNCH_JOBKEY_EXITTIMEOUT) == 0) {
1520 if (value < 0) {
1521 job_log(j, LOG_WARNING, "%s less than zero. Ignoring.", LAUNCH_JOBKEY_EXITTIMEOUT);
1522 } else if (value > UINT32_MAX) {
1523 job_log(j, LOG_WARNING, "%s is too large. Ignoring.", LAUNCH_JOBKEY_EXITTIMEOUT);
1524 } else {
1525 j->exit_timeout = value;
1526 }
1527 }
1528 break;
ed34e3c3
A
1529 case 'n':
1530 case 'N':
5b0a4722 1531 if (strcasecmp(key, LAUNCH_JOBKEY_NICE) == 0) {
ed34e3c3 1532 j->nice = value;
5b0a4722
A
1533 j->setnice = true;
1534 }
ed34e3c3
A
1535 break;
1536 case 't':
1537 case 'T':
1538 if (strcasecmp(key, LAUNCH_JOBKEY_TIMEOUT) == 0) {
5b0a4722
A
1539 if (value < 0) {
1540 job_log(j, LOG_WARNING, "%s less than zero. Ignoring.", LAUNCH_JOBKEY_TIMEOUT);
1541 } else if (value > UINT32_MAX) {
1542 job_log(j, LOG_WARNING, "%s is too large. Ignoring.", LAUNCH_JOBKEY_TIMEOUT);
1543 } else {
ed34e3c3 1544 j->timeout = value;
5b0a4722
A
1545 }
1546 } else if (strcasecmp(key, LAUNCH_JOBKEY_THROTTLEINTERVAL) == 0) {
1547 if (value < 0) {
1548 job_log(j, LOG_WARNING, "%s less than zero. Ignoring.", LAUNCH_JOBKEY_THROTTLEINTERVAL);
1549 } else if (value > UINT32_MAX) {
1550 job_log(j, LOG_WARNING, "%s is too large. Ignoring.", LAUNCH_JOBKEY_THROTTLEINTERVAL);
1551 } else {
1552 j->min_run_time = value;
1553 }
ed34e3c3
A
1554 }
1555 break;
1556 case 'u':
1557 case 'U':
1558 if (strcasecmp(key, LAUNCH_JOBKEY_UMASK) == 0) {
1559 j->mask = value;
1560 j->setmask = true;
1561 }
1562 break;
1563 case 's':
1564 case 'S':
1565 if (strcasecmp(key, LAUNCH_JOBKEY_STARTINTERVAL) == 0) {
5b0a4722
A
1566 if (value <= 0) {
1567 job_log(j, LOG_WARNING, "%s is not greater than zero. Ignoring.", LAUNCH_JOBKEY_STARTINTERVAL);
1568 } else if (value > UINT32_MAX) {
1569 job_log(j, LOG_WARNING, "%s is too large. Ignoring.", LAUNCH_JOBKEY_STARTINTERVAL);
1570 } else {
1571 runtime_add_ref();
ed34e3c3 1572 j->start_interval = value;
5b0a4722
A
1573
1574 job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, value, j) != -1);
1575 }
f36da725 1576#if HAVE_SANDBOX
5b0a4722
A
1577 } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXFLAGS) == 0) {
1578 j->seatbelt_flags = value;
f36da725 1579#endif
5b0a4722
A
1580 }
1581
1582 break;
1583 default:
1584 job_log(j, LOG_WARNING, "Unknown key for integer: %s", key);
1585 break;
1586 }
1587}
1588
1589void
ef398931
A
1590job_import_opaque(job_t j __attribute__((unused)),
1591 const char *key, launch_data_t value __attribute__((unused)))
5b0a4722
A
1592{
1593 switch (key[0]) {
1594 case 'q':
1595 case 'Q':
f36da725 1596#if HAVE_QUARANTINE
5b0a4722
A
1597 if (strcasecmp(key, LAUNCH_JOBKEY_QUARANTINEDATA) == 0) {
1598 size_t tmpsz = launch_data_get_opaque_size(value);
1599
1600 if (job_assumes(j, j->quarantine_data = malloc(tmpsz))) {
1601 memcpy(j->quarantine_data, launch_data_get_opaque(value), tmpsz);
1602 j->quarantine_data_sz = tmpsz;
1603 }
ed34e3c3 1604 }
f36da725 1605#endif
ed34e3c3
A
1606 break;
1607 default:
1608 break;
1609 }
1610}
1611
f36da725
A
1612static void
1613policy_setup(launch_data_t obj, const char *key, void *context)
1614{
1615 job_t j = context;
1616 bool found_key = false;
1617
1618 switch (key[0]) {
1619 case 'd':
1620 case 'D':
1621 if (strcasecmp(key, LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS) == 0) {
1622 j->deny_job_creation = launch_data_get_bool(obj);
1623 found_key = true;
1624 }
1625 break;
1626 default:
1627 break;
1628 }
1629
1630 if (unlikely(!found_key)) {
1631 job_log(j, LOG_WARNING, "Unknown policy: %s", key);
1632 }
1633}
1634
ed34e3c3 1635void
5b0a4722 1636job_import_dictionary(job_t j, const char *key, launch_data_t value)
ed34e3c3
A
1637{
1638 launch_data_t tmp;
1639
1640 switch (key[0]) {
f36da725
A
1641 case 'p':
1642 case 'P':
1643 if (strcasecmp(key, LAUNCH_JOBKEY_POLICIES) == 0) {
1644 launch_data_dict_iterate(value, policy_setup, j);
1645 }
1646 break;
ed34e3c3
A
1647 case 'k':
1648 case 'K':
5b0a4722 1649 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE) == 0) {
ed34e3c3 1650 launch_data_dict_iterate(value, semaphoreitem_setup, j);
5b0a4722 1651 }
ed34e3c3
A
1652 break;
1653 case 'i':
1654 case 'I':
1655 if (strcasecmp(key, LAUNCH_JOBKEY_INETDCOMPATIBILITY) == 0) {
1656 j->inetcompat = true;
5b0a4722
A
1657 j->abandon_pg = true;
1658 if ((tmp = launch_data_dict_lookup(value, LAUNCH_JOBINETDCOMPATIBILITY_WAIT))) {
ed34e3c3 1659 j->inetcompat_wait = launch_data_get_bool(tmp);
5b0a4722 1660 }
ed34e3c3
A
1661 }
1662 break;
1663 case 'e':
1664 case 'E':
5b0a4722 1665 if (strcasecmp(key, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES) == 0) {
ed34e3c3 1666 launch_data_dict_iterate(value, envitem_setup, j);
5b0a4722 1667 }
ed34e3c3
A
1668 break;
1669 case 'u':
1670 case 'U':
1671 if (strcasecmp(key, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES) == 0) {
1672 j->importing_global_env = true;
1673 launch_data_dict_iterate(value, envitem_setup, j);
1674 j->importing_global_env = false;
1675 }
1676 break;
1677 case 's':
1678 case 'S':
1679 if (strcasecmp(key, LAUNCH_JOBKEY_SOCKETS) == 0) {
1680 launch_data_dict_iterate(value, socketgroup_setup, j);
1681 } else if (strcasecmp(key, LAUNCH_JOBKEY_STARTCALENDARINTERVAL) == 0) {
1682 calendarinterval_new_from_obj(j, value);
1683 } else if (strcasecmp(key, LAUNCH_JOBKEY_SOFTRESOURCELIMITS) == 0) {
1684 launch_data_dict_iterate(value, limititem_setup, j);
f36da725 1685#if HAVE_SANDBOX
5b0a4722
A
1686 } else if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOXFLAGS) == 0) {
1687 launch_data_dict_iterate(value, seatbelt_setup_flags, j);
f36da725 1688#endif
ed34e3c3
A
1689 }
1690 break;
1691 case 'h':
1692 case 'H':
1693 if (strcasecmp(key, LAUNCH_JOBKEY_HARDRESOURCELIMITS) == 0) {
1694 j->importing_hard_limits = true;
1695 launch_data_dict_iterate(value, limititem_setup, j);
1696 j->importing_hard_limits = false;
1697 }
1698 break;
1699 case 'm':
1700 case 'M':
1701 if (strcasecmp(key, LAUNCH_JOBKEY_MACHSERVICES) == 0) {
1702 launch_data_dict_iterate(value, machservice_setup, j);
5b0a4722
A
1703 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACHSERVICELOOKUPPOLICIES) == 0) {
1704 launch_data_dict_iterate(value, mspolicy_setup, j);
ed34e3c3
A
1705 }
1706 break;
1707 default:
5b0a4722 1708 job_log(j, LOG_WARNING, "Unknown key for dictionary: %s", key);
ed34e3c3
A
1709 break;
1710 }
1711}
1712
1713void
5b0a4722 1714job_import_array(job_t j, const char *key, launch_data_t value)
ed34e3c3 1715{
5b0a4722
A
1716 size_t i, value_cnt = launch_data_array_get_count(value);
1717 const char *str;
ed34e3c3
A
1718
1719 switch (key[0]) {
5b0a4722
A
1720 case 'p':
1721 case 'P':
1722 if (strcasecmp(key, LAUNCH_JOBKEY_PROGRAMARGUMENTS) == 0) {
1723 return;
1724 }
1725 break;
1726 case 'l':
1727 case 'L':
1728 if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADTOHOSTS) == 0) {
1729 return;
1730 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS) == 0) {
1731 return;
1732 } else if (strcasecmp(key, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE) == 0) {
1733 job_log(j, LOG_NOTICE, "launchctl should have transformed the \"%s\" array to a string", LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE);
1734 return;
1735 }
1736 break;
ed34e3c3
A
1737 case 'q':
1738 case 'Q':
1739 if (strcasecmp(key, LAUNCH_JOBKEY_QUEUEDIRECTORIES) == 0) {
5b0a4722
A
1740 for (i = 0; i < value_cnt; i++) {
1741 str = launch_data_get_string(launch_data_array_get_index(value, i));
1742 if (job_assumes(j, str != NULL)) {
1743 semaphoreitem_new(j, DIR_NOT_EMPTY, str);
1744 }
1745 }
1746
ed34e3c3
A
1747 }
1748 break;
1749 case 'w':
1750 case 'W':
5b0a4722
A
1751 if (strcasecmp(key, LAUNCH_JOBKEY_WATCHPATHS) == 0) {
1752 for (i = 0; i < value_cnt; i++) {
1753 str = launch_data_get_string(launch_data_array_get_index(value, i));
1754 if (job_assumes(j, str != NULL)) {
1755 semaphoreitem_new(j, PATH_CHANGES, str);
1756 }
1757 }
1758 }
ed34e3c3
A
1759 break;
1760 case 'b':
1761 case 'B':
5b0a4722 1762 if (strcasecmp(key, LAUNCH_JOBKEY_BONJOURFDS) == 0) {
ed34e3c3 1763 socketgroup_setup(value, LAUNCH_JOBKEY_BONJOURFDS, j);
5b0a4722
A
1764 } else if (strcasecmp(key, LAUNCH_JOBKEY_BINARYORDERPREFERENCE) == 0) {
1765 if (job_assumes(j, j->j_binpref = malloc(value_cnt * sizeof(*j->j_binpref)))) {
1766 j->j_binpref_cnt = value_cnt;
1767 for (i = 0; i < value_cnt; i++) {
1768 j->j_binpref[i] = launch_data_get_integer(launch_data_array_get_index(value, i));
1769 }
1770 }
1771 }
ed34e3c3
A
1772 break;
1773 case 's':
1774 case 'S':
1775 if (strcasecmp(key, LAUNCH_JOBKEY_STARTCALENDARINTERVAL) == 0) {
5b0a4722 1776 for (i = 0; i < value_cnt; i++) {
ed34e3c3 1777 calendarinterval_new_from_obj(j, launch_data_array_get_index(value, i));
5b0a4722 1778 }
ed34e3c3
A
1779 }
1780 break;
1781 default:
5b0a4722 1782 job_log(j, LOG_WARNING, "Unknown key for array: %s", key);
ed34e3c3
A
1783 break;
1784 }
ed34e3c3
A
1785}
1786
1787void
1788job_import_keys(launch_data_t obj, const char *key, void *context)
1789{
5b0a4722 1790 job_t j = context;
ed34e3c3
A
1791 launch_data_type_t kind;
1792
5b0a4722 1793 if (obj == NULL) {
ed34e3c3 1794 return;
5b0a4722 1795 }
ed34e3c3
A
1796
1797 kind = launch_data_get_type(obj);
1798
1799 switch (kind) {
1800 case LAUNCH_DATA_BOOL:
1801 job_import_bool(j, key, launch_data_get_bool(obj));
1802 break;
1803 case LAUNCH_DATA_STRING:
1804 job_import_string(j, key, launch_data_get_string(obj));
1805 break;
1806 case LAUNCH_DATA_INTEGER:
1807 job_import_integer(j, key, launch_data_get_integer(obj));
1808 break;
1809 case LAUNCH_DATA_DICTIONARY:
1810 job_import_dictionary(j, key, obj);
1811 break;
1812 case LAUNCH_DATA_ARRAY:
1813 job_import_array(j, key, obj);
1814 break;
5b0a4722
A
1815 case LAUNCH_DATA_OPAQUE:
1816 job_import_opaque(j, key, obj);
1817 break;
ed34e3c3
A
1818 default:
1819 job_log(j, LOG_WARNING, "Unknown value type '%d' for key: %s", kind, key);
1820 break;
1821 }
1822}
1823
5b0a4722
A
1824job_t
1825jobmgr_import2(jobmgr_t jm, launch_data_t pload)
ed34e3c3
A
1826{
1827 launch_data_t tmp, ldpa;
1828 const char *label = NULL, *prog = NULL;
1829 const char **argv = NULL;
5b0a4722
A
1830 job_t j;
1831
1832 if (pload == NULL) {
1833 errno = EINVAL;
1834 return NULL;
1835 }
ed34e3c3 1836
5b0a4722
A
1837 if (launch_data_get_type(pload) != LAUNCH_DATA_DICTIONARY) {
1838 errno = EINVAL;
ed34e3c3 1839 return NULL;
5b0a4722 1840 }
ed34e3c3 1841
5b0a4722
A
1842 if (!(tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_LABEL))) {
1843 errno = EINVAL;
ed34e3c3 1844 return NULL;
5b0a4722 1845 }
ed34e3c3 1846
5b0a4722
A
1847 if (launch_data_get_type(tmp) != LAUNCH_DATA_STRING) {
1848 errno = EINVAL;
1849 return NULL;
ed34e3c3 1850 }
5b0a4722
A
1851
1852 if (!(label = launch_data_get_string(tmp))) {
1853 errno = EINVAL;
1854 return NULL;
1855 }
1856
ed34e3c3
A
1857 if ((tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAM)) &&
1858 (launch_data_get_type(tmp) == LAUNCH_DATA_STRING)) {
1859 prog = launch_data_get_string(tmp);
1860 }
ed34e3c3 1861
5b0a4722
A
1862 if ((ldpa = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAMARGUMENTS))) {
1863 size_t i, c;
1864
1865 if (launch_data_get_type(ldpa) != LAUNCH_DATA_ARRAY) {
1866 errno = EINVAL;
1867 return NULL;
1868 }
1869
1870 c = launch_data_array_get_count(ldpa);
1871
1872 argv = alloca((c + 1) * sizeof(char *));
1873
1874 for (i = 0; i < c; i++) {
1875 tmp = launch_data_array_get_index(ldpa, i);
1876
1877 if (launch_data_get_type(tmp) != LAUNCH_DATA_STRING) {
1878 errno = EINVAL;
1879 return NULL;
1880 }
1881
1882 argv[i] = launch_data_get_string(tmp);
1883 }
1884
1885 argv[i] = NULL;
1886 }
1887
1888 if ((j = job_find(label)) != NULL) {
ed34e3c3
A
1889 errno = EEXIST;
1890 return NULL;
f36da725 1891 } else if (!jobmgr_label_test(jm, label)) {
ed34e3c3
A
1892 errno = EINVAL;
1893 return NULL;
1894 }
1895
5b0a4722
A
1896 if ((j = job_new(jm, label, prog, argv))) {
1897 launch_data_dict_iterate(pload, job_import_keys, j);
1898 }
ed34e3c3 1899
5b0a4722
A
1900 return j;
1901}
ed34e3c3 1902
f36da725
A
1903bool
1904jobmgr_label_test(jobmgr_t jm, const char *str)
1905{
1906 char *endstr = NULL;
1907 const char *ptr;
1908
1909 if (str[0] == '\0') {
1910 jobmgr_log(jm, LOG_ERR, "Empty job labels are not allowed");
1911 return false;
1912 }
1913
1914 for (ptr = str; *ptr; ptr++) {
1915 if (iscntrl(*ptr)) {
1916 jobmgr_log(jm, LOG_ERR, "ASCII control characters are not allowed in job labels. Index: %td Value: 0x%hhx", ptr - str, *ptr);
1917 return false;
1918 }
1919 }
1920
1921 strtoll(str, &endstr, 0);
1922
1923 if (str != endstr) {
1924 jobmgr_log(jm, LOG_ERR, "Job labels are not allowed to begin with numbers: %s", str);
1925 return false;
1926 }
1927
1928 if ((strncasecmp(str, "com.apple.launchd", strlen("com.apple.launchd")) == 0) ||
1929 (strncasecmp(str, "com.apple.launchctl", strlen("com.apple.launchctl")) == 0)) {
1930 jobmgr_log(jm, LOG_ERR, "Job labels are not allowed to use a reserved prefix: %s", str);
1931 return false;
1932 }
1933
1934 return true;
1935}
1936
5b0a4722
A
1937job_t
1938job_find(const char *label)
1939{
1940 job_t ji;
1941
1942 LIST_FOREACH(ji, &label_hash[hash_label(label)], label_hash_sle) {
1943 if (ji->removal_pending) {
1944 continue; /* 5351245 */
1945 } else if (ji->mgr->shutting_down) {
1946 continue; /* 5488633 */
1947 }
1948
1949 if (strcmp(ji->label, label) == 0) {
1950 return ji;
1951 }
ed34e3c3
A
1952 }
1953
5b0a4722
A
1954 errno = ESRCH;
1955 return NULL;
1956}
ed34e3c3 1957
5b0a4722
A
1958job_t
1959jobmgr_find_by_pid(jobmgr_t jm, pid_t p, bool create_anon)
1960{
1961 job_t ji = NULL;
1962
1963 LIST_FOREACH(ji, &jm->active_jobs[ACTIVE_JOB_HASH(p)], pid_hash_sle) {
1964 if (ji->p == p) {
1965 break;
1966 }
1967 }
1968
1969 if (ji) {
1970 return ji;
1971 } else if (create_anon) {
1972 return job_new_anonymous(jm, p);
1973 } else {
1974 return NULL;
1975 }
ed34e3c3
A
1976}
1977
5b0a4722
A
1978job_t
1979job_mig_intran2(jobmgr_t jm, mach_port_t mport, pid_t upid)
ed34e3c3 1980{
5b0a4722
A
1981 jobmgr_t jmi;
1982 job_t ji;
ed34e3c3 1983
5b0a4722
A
1984 if (jm->jm_port == mport) {
1985 jobmgr_assumes(jm, (ji = jobmgr_find_by_pid(jm, upid, true)) != NULL);
1986 return ji;
1987 }
ed34e3c3 1988
5b0a4722
A
1989 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
1990 job_t jr;
ed34e3c3 1991
5b0a4722 1992 if ((jr = job_mig_intran2(jmi, mport, upid))) {
ed34e3c3 1993 return jr;
5b0a4722
A
1994 }
1995 }
1996
1997 LIST_FOREACH(ji, &jm->jobs, sle) {
1998 if (ji->j_port == mport) {
1999 return ji;
2000 }
ed34e3c3
A
2001 }
2002
ed34e3c3
A
2003 return NULL;
2004}
2005
5b0a4722
A
2006job_t
2007job_mig_intran(mach_port_t p)
ed34e3c3 2008{
5b0a4722
A
2009 struct ldcred ldc;
2010 job_t jr;
ed34e3c3 2011
5b0a4722 2012 runtime_get_caller_creds(&ldc);
ed34e3c3 2013
5b0a4722
A
2014 jr = job_mig_intran2(root_jobmgr, p, ldc.pid);
2015
2016 if (!jobmgr_assumes(root_jobmgr, jr != NULL)) {
2017 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 };
2018 struct kinfo_proc kp;
2019 size_t len = sizeof(kp);
2020
2021 mib[3] = ldc.pid;
2022
2023 if (jobmgr_assumes(root_jobmgr, sysctl(mib, 4, &kp, &len, NULL, 0) != -1) && jobmgr_assumes(root_jobmgr, len == sizeof(kp))) {
2024 jobmgr_log(root_jobmgr, LOG_ERR, "%s() was confused by PID %u UID %u EUID %u Mach Port 0x%x: %s", __func__, ldc.pid, ldc.uid, ldc.euid, p, kp.kp_proc.p_comm);
2025 }
2026 }
2027
2028 return jr;
2029}
2030
2031job_t
2032job_find_by_service_port(mach_port_t p)
2033{
2034 struct machservice *ms;
2035
2036 LIST_FOREACH(ms, &port_hash[HASH_PORT(p)], port_hash_sle) {
2037 if (ms->recv && (ms->port == p)) {
2038 return ms->job;
2039 }
ed34e3c3
A
2040 }
2041
ed34e3c3
A
2042 return NULL;
2043}
2044
2045void
5b0a4722 2046job_mig_destructor(job_t j)
ed34e3c3 2047{
5b0a4722
A
2048 /*
2049 * 5477111
2050 *
2051 * 'j' can be invalid at this point. We should fix this up after Leopard ships.
2052 */
2053
2054 if (j && j != workaround_5477111 && j->unload_at_mig_return) {
2055 job_log(j, LOG_NOTICE, "Unloading PID %u at MIG return.", j->p);
2056 job_remove(j);
2057 }
ed34e3c3 2058
5b0a4722 2059 workaround_5477111 = NULL;
ed34e3c3 2060
5b0a4722
A
2061 calendarinterval_sanity_check();
2062}
2063
2064void
2065job_export_all2(jobmgr_t jm, launch_data_t where)
2066{
2067 jobmgr_t jmi;
2068 job_t ji;
2069
2070 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
2071 job_export_all2(jmi, where);
2072 }
2073
2074 LIST_FOREACH(ji, &jm->jobs, sle) {
2075 launch_data_t tmp;
2076
2077 if (jobmgr_assumes(jm, (tmp = job_export(ji)) != NULL)) {
2078 launch_data_dict_insert(where, tmp, ji->label);
2079 }
2080 }
ed34e3c3
A
2081}
2082
2083launch_data_t
2084job_export_all(void)
2085{
2086 launch_data_t resp = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
2087
5b0a4722
A
2088 if (launchd_assumes(resp != NULL)) {
2089 job_export_all2(root_jobmgr, resp);
2090 }
ed34e3c3
A
2091
2092 return resp;
2093}
2094
2095void
5b0a4722 2096job_log_stray_pg(job_t j)
ed34e3c3 2097{
5b0a4722 2098 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, j->p };
f36da725 2099 size_t i, kp_cnt, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
5b0a4722 2100 struct kinfo_proc *kp;
ed34e3c3 2101
f36da725
A
2102#if TARGET_OS_EMBEDDED
2103 if (!do_apple_internal_magic) {
2104 return;
2105 }
2106#endif
2107
5b0a4722
A
2108 if (!job_assumes(j, (kp = malloc(len)) != NULL)) {
2109 return;
2110 }
2111 if (!job_assumes(j, sysctl(mib, 4, kp, &len, NULL, 0) != -1)) {
2112 goto out;
ed34e3c3
A
2113 }
2114
5b0a4722
A
2115 kp_cnt = len / sizeof(struct kinfo_proc);
2116
2117 for (i = 0; i < kp_cnt; i++) {
2118 pid_t p_i = kp[i].kp_proc.p_pid;
2119 pid_t pp_i = kp[i].kp_eproc.e_ppid;
2120 const char *z = (kp[i].kp_proc.p_stat == SZOMB) ? "zombie " : "";
2121 const char *n = kp[i].kp_proc.p_comm;
2122
2123 if (p_i == j->p) {
2124 continue;
2125 } else if (!job_assumes(j, p_i != 0 && p_i != 1)) {
2126 continue;
2127 }
2128
2129 job_log(j, LOG_WARNING, "Stray %sprocess with PGID equal to this dead job: PID %u PPID %u %s", z, p_i, pp_i, n);
2130 }
2131
2132out:
2133 free(kp);
2134}
2135
2136void
2137job_reap(job_t j)
2138{
2139 struct rusage ru;
2140 int status;
2141
2142 job_log(j, LOG_DEBUG, "Reaping");
2143
2144 if (j->weird_bootstrap) {
2145 mach_msg_size_t mxmsgsz = sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem);
2146
2147 if (job_mig_protocol_vproc_subsystem.maxsize > mxmsgsz) {
2148 mxmsgsz = job_mig_protocol_vproc_subsystem.maxsize;
2149 }
2150
2151 job_assumes(j, runtime_add_mport(j->mgr->jm_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS);
2152 j->weird_bootstrap = false;
2153 }
2154
2155 if (j->log_redirect_fd && !j->wait4pipe_eof) {
2156 job_assumes(j, runtime_close(j->log_redirect_fd) != -1);
2157 j->log_redirect_fd = 0;
2158 }
2159
2160 if (j->forkfd) {
2161 job_assumes(j, runtime_close(j->forkfd) != -1);
2162 j->forkfd = 0;
2163 }
2164
2165 if (j->anonymous) {
2166 status = 0;
2167 memset(&ru, 0, sizeof(ru));
2168 } else {
2169 /*
2170 * The job is dead. While the PID/PGID is still known to be
2171 * valid, try to kill abandoned descendant processes.
2172 */
2173 job_log_stray_pg(j);
2174 if (!j->abandon_pg) {
2175 job_assumes(j, runtime_killpg(j->p, SIGTERM) != -1 || errno == ESRCH);
2176 }
2177
2178 /*
2179 * 5020256
2180 *
2181 * The current implementation of ptrace() causes the traced process to
2182 * be abducted away from the true parent and adopted by the tracer.
2183 *
2184 * Once the tracing process relinquishes control, the kernel then
2185 * restores the true parent/child relationship.
2186 *
2187 * Unfortunately, the wait*() family of APIs is unaware of the temporarily
2188 * data structures changes, and they return an error if reality hasn't
2189 * been restored by the time they are called.
2190 */
2191 if (!job_assumes(j, wait4(j->p, &status, 0, &ru) != -1)) {
2192 job_log(j, LOG_NOTICE, "Working around 5020256. Assuming the job crashed.");
2193
2194 status = W_EXITCODE(0, SIGSEGV);
2195 memset(&ru, 0, sizeof(ru));
2196 }
ed34e3c3
A
2197 }
2198
5b0a4722
A
2199 if (j->exit_timeout) {
2200 kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
2201 }
2202
2203 if (j->anonymous) {
2204 total_anon_children--;
2205 } else {
2206 runtime_del_ref();
2207 total_children--;
2208 }
2209 LIST_REMOVE(j, pid_hash_sle);
2210
ed34e3c3
A
2211 if (j->wait_reply_port) {
2212 job_log(j, LOG_DEBUG, "MPM wait reply being sent");
5b0a4722 2213 job_assumes(j, job_mig_wait_reply(j->wait_reply_port, 0, status) == 0);
ed34e3c3
A
2214 j->wait_reply_port = MACH_PORT_NULL;
2215 }
2216
5b0a4722
A
2217 if (j->sent_sigterm_time) {
2218 uint64_t td_sec, td_usec, td = (mach_absolute_time() - j->sent_sigterm_time) * tbi.numer / tbi.denom;
2219
2220 td_sec = td / NSEC_PER_SEC;
2221 td_usec = (td % NSEC_PER_SEC) / NSEC_PER_USEC;
2222
2223 job_log(j, LOG_INFO, "Exited %lld.%06lld seconds after %s was sent",
2224 td_sec, td_usec, signal_to_C_name(j->sent_sigkill ? SIGKILL : SIGTERM));
2225 }
2226
2227#if DO_RUSAGE_SUMMATION
ed34e3c3
A
2228 timeradd(&ru.ru_utime, &j->ru.ru_utime, &j->ru.ru_utime);
2229 timeradd(&ru.ru_stime, &j->ru.ru_stime, &j->ru.ru_stime);
2230 j->ru.ru_maxrss += ru.ru_maxrss;
2231 j->ru.ru_ixrss += ru.ru_ixrss;
2232 j->ru.ru_idrss += ru.ru_idrss;
2233 j->ru.ru_isrss += ru.ru_isrss;
2234 j->ru.ru_minflt += ru.ru_minflt;
2235 j->ru.ru_majflt += ru.ru_majflt;
2236 j->ru.ru_nswap += ru.ru_nswap;
2237 j->ru.ru_inblock += ru.ru_inblock;
2238 j->ru.ru_oublock += ru.ru_oublock;
2239 j->ru.ru_msgsnd += ru.ru_msgsnd;
2240 j->ru.ru_msgrcv += ru.ru_msgrcv;
2241 j->ru.ru_nsignals += ru.ru_nsignals;
2242 j->ru.ru_nvcsw += ru.ru_nvcsw;
2243 j->ru.ru_nivcsw += ru.ru_nivcsw;
5b0a4722 2244#endif
ed34e3c3
A
2245
2246 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
5b0a4722 2247 job_log(j, LOG_WARNING, "Exited with exit code: %d", WEXITSTATUS(status));
ed34e3c3
A
2248 }
2249
2250 if (WIFSIGNALED(status)) {
2251 int s = WTERMSIG(status);
2252 if (SIGKILL == s || SIGTERM == s) {
2253 job_log(j, LOG_NOTICE, "Exited: %s", strsignal(s));
2254 } else {
2255 job_log(j, LOG_WARNING, "Exited abnormally: %s", strsignal(s));
ed34e3c3
A
2256 }
2257 }
2258
5b0a4722
A
2259 if (j->hopefully_exits_first) {
2260 j->mgr->hopefully_first_cnt--;
2261 } else if (!j->anonymous && !j->hopefully_exits_last) {
2262 j->mgr->normal_active_cnt--;
2263 }
2264 j->last_exit_status = status;
2265 j->sent_sigkill = false;
cf0bacfd
A
2266 j->lastlookup = NULL;
2267 j->lastlookup_gennum = 0;
5b0a4722
A
2268 j->p = 0;
2269
2270 /*
2271 * We need to someday evaluate other jobs and find those who wish to track the
2272 * active/inactive state of this job. The current job_dispatch() logic makes
2273 * this messy, given that jobs can be deleted at dispatch.
2274 */
2275}
2276
2277void
2278jobmgr_dispatch_all(jobmgr_t jm, bool newmounthack)
2279{
2280 jobmgr_t jmi, jmn;
2281 job_t ji, jn;
2282
2283 if (jm->shutting_down) {
2284 return;
2285 }
2286
2287 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
2288 jobmgr_dispatch_all(jmi, newmounthack);
ed34e3c3
A
2289 }
2290
5b0a4722
A
2291 LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
2292 if (newmounthack && ji->start_on_mount) {
2293 ji->start_pending = true;
2294 }
ed34e3c3 2295
5b0a4722 2296 job_dispatch(ji, false);
ed34e3c3 2297 }
5b0a4722 2298}
ed34e3c3 2299
5b0a4722
A
2300job_t
2301job_dispatch(job_t j, bool kickstart)
2302{
2303 /*
2304 * The whole job removal logic needs to be consolidated. The fact that
2305 * a job can be removed from just about anywhere makes it easy to have
2306 * stale pointers left behind somewhere on the stack that might get
2307 * used after the deallocation. In particular, during job iteration.
2308 *
2309 * This is a classic example. The act of dispatching a job may delete it.
2310 */
2311 if (!job_active(j)) {
2312 if (job_useless(j)) {
2313 job_remove(j);
2314 return NULL;
2315 } else if (kickstart || job_keepalive(j)) {
2316 job_start(j);
2317 } else {
2318 job_watch(j);
2319
2320 /*
2321 * 5455720
2322 *
2323 * Path checking and monitoring is really racy right now.
2324 * We should clean this up post Leopard.
2325 */
2326 if (job_keepalive(j)) {
2327 job_start(j);
2328 }
2329 }
2330 } else {
2331 job_log(j, LOG_DEBUG, "Tried to dispatch an already active job.");
2332 }
2333
2334 return j;
ed34e3c3
A
2335}
2336
2337void
5b0a4722 2338job_log_stdouterr2(job_t j, const char *msg, ...)
ed34e3c3 2339{
5b0a4722
A
2340 struct runtime_syslog_attr attr = { j->label, j->label, j->mgr->name, LOG_NOTICE, getuid(), j->p, j->p };
2341 va_list ap;
2342
2343 va_start(ap, msg);
2344 runtime_vsyslog(&attr, msg, ap);
2345 va_end(ap);
2346}
2347
2348void
2349job_log_stdouterr(job_t j)
2350{
2351 char *msg, *bufindex, *buf = malloc(BIG_PIPE_SIZE + 1);
2352 bool close_log_redir = false;
2353 ssize_t rsz;
2354
2355 if (!job_assumes(j, buf != NULL)) {
ed34e3c3 2356 return;
5b0a4722
A
2357 }
2358
2359 bufindex = buf;
2360
2361 rsz = read(j->log_redirect_fd, buf, BIG_PIPE_SIZE);
2362
2363 if (rsz == 0) {
2364 job_log(j, LOG_DEBUG, "Standard out/error pipe closed");
2365 close_log_redir = true;
2366 } else if (!job_assumes(j, rsz != -1)) {
2367 close_log_redir = true;
ed34e3c3 2368 } else {
5b0a4722
A
2369 buf[rsz] = '\0';
2370
2371 while ((msg = strsep(&bufindex, "\n\r"))) {
2372 if (msg[0]) {
2373 job_log_stdouterr2(j, "%s", msg);
2374 }
2375 }
2376 }
2377
2378 free(buf);
2379
2380 if (close_log_redir) {
2381 job_assumes(j, runtime_close(j->log_redirect_fd) != -1);
2382 j->log_redirect_fd = 0;
2383 job_dispatch(j, false);
ed34e3c3
A
2384 }
2385}
2386
2387void
5b0a4722 2388job_kill(job_t j)
ed34e3c3 2389{
5b0a4722
A
2390 if (!j->p || j->anonymous) {
2391 return;
2392 }
ed34e3c3 2393
5b0a4722
A
2394 job_assumes(j, runtime_kill(j->p, SIGKILL) != -1);
2395
2396 j->sent_sigkill = true;
2397
2398 job_assumes(j, kevent_mod((uintptr_t)&j->exit_timeout, EVFILT_TIMER,
2399 EV_ADD, NOTE_SECONDS, LAUNCHD_SIGKILL_TIMER, j) != -1);
2400
2401 job_log(j, LOG_DEBUG, "Sent SIGKILL signal.");
2402}
2403
2404void
ef398931 2405job_callback_proc(job_t j, int flags __attribute__((unused)), int fflags)
5b0a4722
A
2406{
2407 if ((fflags & NOTE_EXEC) && j->anonymous) {
2408 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, j->p };
2409 struct kinfo_proc kp;
2410 size_t len = sizeof(kp);
2411
2412 if (job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1)) {
2413 char newlabel[1000];
2414
2415 snprintf(newlabel, sizeof(newlabel), "%p.%s", j, kp.kp_proc.p_comm);
2416
2417 job_log(j, LOG_DEBUG, "Program changed. Updating the label to: %s", newlabel);
2418
2419 LIST_REMOVE(j, label_hash_sle);
2420 strcpy((char *)j->label, newlabel);
2421 LIST_INSERT_HEAD(&label_hash[hash_label(j->label)], j, label_hash_sle);
2422 }
ed34e3c3
A
2423 }
2424
5b0a4722
A
2425 if (fflags & NOTE_FORK) {
2426 job_log(j, LOG_DEBUG, "Called fork()");
2427 }
2428
2429 if (fflags & NOTE_EXIT) {
ed34e3c3
A
2430 job_reap(j);
2431
5b0a4722
A
2432 if (j->anonymous) {
2433 job_remove(j);
2434 j = NULL;
ed34e3c3 2435 } else {
5b0a4722 2436 j = job_dispatch(j, false);
ed34e3c3 2437 }
5b0a4722
A
2438 }
2439
2440 /* NOTE_REAP sanity checking is disabled for now while we try and diagnose 5289559 */
2441#if 0
2442 if (j && (fflags & NOTE_REAP)) {
2443 job_assumes(j, flags & EV_ONESHOT);
2444 job_assumes(j, flags & EV_EOF);
2445
2446 job_assumes(j, j->p == 0);
2447 }
2448#endif
2449}
2450
2451void
2452job_callback_timer(job_t j, void *ident)
2453{
2454 if (j == ident) {
2455 job_dispatch(j, true);
2456 } else if (&j->semaphores == ident) {
2457 job_dispatch(j, false);
2458 } else if (&j->start_interval == ident) {
2459 j->start_pending = true;
2460 job_dispatch(j, false);
2461 } else if (&j->exit_timeout == ident) {
2462 if (j->sent_sigkill) {
2463 uint64_t td = (mach_absolute_time() - j->sent_sigterm_time) * tbi.numer / tbi.denom;
2464
2465 td /= NSEC_PER_SEC;
2466 td -= j->exit_timeout;
2467
2468 job_log(j, LOG_ERR, "Did not die after sending SIGKILL %llu seconds ago...", td);
ed34e3c3 2469 } else {
5b0a4722
A
2470 job_force_sampletool(j);
2471 if (j->debug_before_kill) {
2472 job_log(j, LOG_NOTICE, "Exit timeout elapsed. Entering the kernel debugger.");
2473 job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS);
2474 }
2475 job_log(j, LOG_WARNING, "Exit timeout elapsed (%u seconds). Killing.", j->exit_timeout);
2476 job_kill(j);
ed34e3c3 2477 }
5b0a4722
A
2478 } else {
2479 job_assumes(j, false);
2480 }
2481}
2482
2483void
2484job_callback_read(job_t j, int ident)
2485{
2486 if (ident == j->log_redirect_fd) {
2487 job_log_stdouterr(j);
2488 } else {
2489 socketgroup_callback(j);
2490 }
2491}
2492
2493void
2494jobmgr_reap_bulk(jobmgr_t jm, struct kevent *kev)
2495{
2496 jobmgr_t jmi;
2497 job_t j;
2498
2499 SLIST_FOREACH(jmi, &jm->submgrs, sle) {
2500 jobmgr_reap_bulk(jmi, kev);
2501 }
2502
2503 if ((j = jobmgr_find_by_pid(jm, kev->ident, false))) {
2504 kev->udata = j;
2505 job_callback(j, kev);
2506 }
2507}
2508
2509void
2510jobmgr_callback(void *obj, struct kevent *kev)
2511{
2512 jobmgr_t jm = obj;
2513
2514 switch (kev->filter) {
2515 case EVFILT_PROC:
2516 jobmgr_reap_bulk(jm, kev);
2517 if (launchd_assumes(root_jobmgr != NULL)) {
2518 root_jobmgr = jobmgr_do_garbage_collection(root_jobmgr);
ed34e3c3 2519 }
5b0a4722
A
2520 break;
2521 case EVFILT_SIGNAL:
2522 switch (kev->ident) {
2523 case SIGTERM:
2524 return launchd_shutdown();
2525 case SIGUSR1:
2526 return calendarinterval_callback();
2527 default:
2528 return (void)jobmgr_assumes(jm, false);
ed34e3c3 2529 }
5b0a4722
A
2530 break;
2531 case EVFILT_FS:
2532 if (kev->fflags & VQ_MOUNT) {
2533 jobmgr_dispatch_all(jm, true);
ed34e3c3 2534 }
5b0a4722 2535 jobmgr_dispatch_all_semaphores(jm);
ed34e3c3 2536 break;
5b0a4722
A
2537 case EVFILT_TIMER:
2538 if (jobmgr_assumes(jm, kev->ident == (uintptr_t)&sorted_calendar_events)) {
2539 calendarinterval_callback();
2540 }
ed34e3c3
A
2541 break;
2542 default:
5b0a4722 2543 return (void)jobmgr_assumes(jm, false);
ed34e3c3 2544 }
5b0a4722 2545}
ed34e3c3 2546
5b0a4722
A
2547void
2548job_callback(void *obj, struct kevent *kev)
2549{
2550 job_t j = obj;
2551
2552 job_log(j, LOG_DEBUG, "Dispatching kevent callback.");
2553
2554 switch (kev->filter) {
2555 case EVFILT_PROC:
2556 return job_callback_proc(j, kev->flags, kev->fflags);
2557 case EVFILT_TIMER:
2558 return job_callback_timer(j, (void *)kev->ident);
2559 case EVFILT_VNODE:
2560 return semaphoreitem_callback(j, kev);
2561 case EVFILT_READ:
2562 return job_callback_read(j, kev->ident);
2563 case EVFILT_MACHPORT:
2564 return (void)job_dispatch(j, true);
2565 default:
2566 return (void)job_assumes(j, false);
ed34e3c3
A
2567 }
2568}
2569
2570void
5b0a4722 2571job_start(job_t j)
ed34e3c3 2572{
5b0a4722 2573 uint64_t td, tnow = mach_absolute_time();
ed34e3c3
A
2574 int spair[2];
2575 int execspair[2];
5b0a4722 2576 int oepair[2];
ed34e3c3
A
2577 char nbuf[64];
2578 pid_t c;
2579 bool sipc = false;
5b0a4722 2580 u_int proc_fflags = /* NOTE_EXEC|NOTE_FORK| */ NOTE_EXIT /* |NOTE_REAP */;
ed34e3c3 2581
5b0a4722 2582 if (!job_assumes(j, j->mgr != NULL)) {
ed34e3c3 2583 return;
5b0a4722 2584 }
ed34e3c3
A
2585
2586 if (job_active(j)) {
2587 job_log(j, LOG_DEBUG, "Already started");
2588 return;
5b0a4722
A
2589 }
2590
2591 job_assumes(j, tnow > j->start_time);
2592
2593 /*
2594 * Some users adjust the wall-clock and then expect software to not notice.
2595 * Therefore, launchd must use an absolute clock instead of gettimeofday()
2596 * or time() wherever possible.
2597 */
2598 td = (tnow - j->start_time) * tbi.numer / tbi.denom;
2599 td /= NSEC_PER_SEC;
2600
2601 if (j->start_time && (td < j->min_run_time) && !j->legacy_mach_job && !j->inetcompat) {
2602 time_t respawn_delta = j->min_run_time - (uint32_t)td;
2603
2604 /*
2605 * We technically should ref-count throttled jobs to prevent idle exit,
2606 * but we're not directly tracking the 'throttled' state at the moment.
2607 */
2608
2609 job_log(j, LOG_WARNING, "Throttling respawn: Will start in %ld seconds", respawn_delta);
2610 job_assumes(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, respawn_delta, j) != -1);
2611 job_ignore(j);
ed34e3c3
A
2612 return;
2613 }
2614
5b0a4722 2615 j->sent_sigterm_time = 0;
ed34e3c3 2616
5b0a4722 2617 if (!j->legacy_mach_job) {
ed34e3c3 2618 sipc = (!SLIST_EMPTY(&j->sockets) || !SLIST_EMPTY(&j->machservices));
cf0bacfd
A
2619#if TARGET_OS_EMBEDDED
2620 if (j->username && strcmp(j->username, "mobile") == 0 && strncmp(j->label, "com.apple.", strlen("com.apple.")) != 0) {
2621 sipc = false;
2622 }
2623#endif
5b0a4722 2624 }
ed34e3c3
A
2625
2626 j->checkedin = false;
2627
5b0a4722
A
2628 if (sipc) {
2629 job_assumes(j, socketpair(AF_UNIX, SOCK_STREAM, 0, spair) != -1);
2630 }
ed34e3c3 2631
5b0a4722 2632 job_assumes(j, socketpair(AF_UNIX, SOCK_STREAM, 0, execspair) != -1);
ed34e3c3 2633
5b0a4722
A
2634 if (!j->legacy_mach_job && job_assumes(j, pipe(oepair) != -1)) {
2635 j->log_redirect_fd = _fd(oepair[0]);
2636 job_assumes(j, fcntl(j->log_redirect_fd, F_SETFL, O_NONBLOCK) != -1);
2637 job_assumes(j, kevent_mod(j->log_redirect_fd, EVFILT_READ, EV_ADD, 0, 0, j) != -1);
ed34e3c3
A
2638 }
2639
5b0a4722
A
2640 j->start_time = tnow;
2641
2642 switch (c = runtime_fork(j->weird_bootstrap ? j->j_port : j->mgr->jm_port)) {
ed34e3c3
A
2643 case -1:
2644 job_log_error(j, LOG_ERR, "fork() failed, will try again in one second");
5b0a4722
A
2645 job_assumes(j, runtime_close(execspair[0]) == 0);
2646 job_assumes(j, runtime_close(execspair[1]) == 0);
ed34e3c3 2647 if (sipc) {
5b0a4722
A
2648 job_assumes(j, runtime_close(spair[0]) == 0);
2649 job_assumes(j, runtime_close(spair[1]) == 0);
ed34e3c3 2650 }
fe044cc9
A
2651 if (!j->legacy_mach_job) {
2652 job_assumes(j, runtime_close(oepair[0]) != -1);
2653 job_assumes(j, runtime_close(oepair[1]) != -1);
2654 j->log_redirect_fd = 0;
2655 }
26b346da
A
2656 job_assumes(j, kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_ADD|EV_ONESHOT, NOTE_SECONDS, 1, j) != -1);
2657 job_ignore(j);
ed34e3c3
A
2658 break;
2659 case 0:
5b0a4722
A
2660 if (_vproc_post_fork_ping()) {
2661 _exit(EXIT_FAILURE);
2662 }
2663 if (!j->legacy_mach_job) {
2664 job_assumes(j, dup2(oepair[1], STDOUT_FILENO) != -1);
2665 job_assumes(j, dup2(oepair[1], STDERR_FILENO) != -1);
2666 job_assumes(j, runtime_close(oepair[1]) != -1);
2667 }
2668 job_assumes(j, runtime_close(execspair[0]) == 0);
ed34e3c3
A
2669 /* wait for our parent to say they've attached a kevent to us */
2670 read(_fd(execspair[1]), &c, sizeof(c));
ed34e3c3
A
2671
2672 if (sipc) {
5b0a4722
A
2673 job_assumes(j, runtime_close(spair[0]) == 0);
2674 snprintf(nbuf, sizeof(nbuf), "%d", spair[1]);
ed34e3c3
A
2675 setenv(LAUNCHD_TRUSTED_FD_ENV, nbuf, 1);
2676 }
5b0a4722 2677 job_start_child(j);
ed34e3c3
A
2678 break;
2679 default:
5b0a4722
A
2680 job_log(j, LOG_DEBUG, "Started as PID: %u", c);
2681
2682 j->start_pending = false;
2683
2684 runtime_add_ref();
ed34e3c3 2685 total_children++;
5b0a4722
A
2686 LIST_INSERT_HEAD(&j->mgr->active_jobs[ACTIVE_JOB_HASH(c)], j, pid_hash_sle);
2687
2688 if (JOB_BOOTCACHE_HACK_CHECK(j)) {
2689 did_first_per_user_launchd_BootCache_hack = true;
2690 }
2691
2692 if (!j->legacy_mach_job) {
2693 job_assumes(j, runtime_close(oepair[1]) != -1);
2694 }
2695 j->p = c;
2696 if (j->hopefully_exits_first) {
2697 j->mgr->hopefully_first_cnt++;
2698 } else if (!j->hopefully_exits_last) {
2699 j->mgr->normal_active_cnt++;
2700 }
2701 j->forkfd = _fd(execspair[0]);
2702 job_assumes(j, runtime_close(execspair[1]) == 0);
ed34e3c3 2703 if (sipc) {
5b0a4722 2704 job_assumes(j, runtime_close(spair[1]) == 0);
ed34e3c3
A
2705 ipc_open(_fd(spair[0]), j);
2706 }
5b0a4722
A
2707 if (job_assumes(j, kevent_mod(c, EVFILT_PROC, EV_ADD, proc_fflags, 0, root_jobmgr ? root_jobmgr : j->mgr) != -1)) {
2708 job_ignore(j);
ed34e3c3 2709 } else {
5b0a4722 2710 job_reap(j);
ed34e3c3
A
2711 }
2712
2713 if (!j->stall_before_exec) {
5b0a4722 2714 job_uncork_fork(j);
ed34e3c3
A
2715 }
2716 break;
2717 }
2718}
2719
2720void
5b0a4722
A
2721do_first_per_user_launchd_hack(void)
2722{
2723 char *bcct_tool[] = { "/usr/sbin/BootCacheControl", "tag", NULL };
2724 int dummystatus;
2725 pid_t bcp;
2726
2727 if (launchd_assumes((bcp = vfork()) != -1)) {
2728 if (bcp == 0) {
2729 execve(bcct_tool[0], bcct_tool, environ);
2730 _exit(EXIT_FAILURE);
2731 } else {
2732 launchd_assumes(waitpid(bcp, &dummystatus, 0) != -1);
2733 }
2734 }
2735}
2736
2737void
2738job_start_child(job_t j)
ed34e3c3
A
2739{
2740 const char *file2exec = "/usr/libexec/launchproxy";
2741 const char **argv;
5b0a4722 2742 posix_spawnattr_t spattr;
ed34e3c3 2743 int gflags = GLOB_NOSORT|GLOB_NOCHECK|GLOB_TILDE|GLOB_DOOFFS;
5b0a4722 2744 pid_t junk_pid;
ed34e3c3 2745 glob_t g;
5b0a4722
A
2746 short spflags = POSIX_SPAWN_SETEXEC;
2747 size_t binpref_out_cnt = 0;
ed34e3c3
A
2748 int i;
2749
5b0a4722
A
2750 if (JOB_BOOTCACHE_HACK_CHECK(j)) {
2751 do_first_per_user_launchd_hack();
2752 }
2753
2754 job_assumes(j, posix_spawnattr_init(&spattr) == 0);
2755
ed34e3c3
A
2756 job_setup_attributes(j);
2757
2758 if (j->argv && j->globargv) {
2759 g.gl_offs = 1;
2760 for (i = 0; i < j->argc; i++) {
5b0a4722 2761 if (i > 0) {
ed34e3c3 2762 gflags |= GLOB_APPEND;
5b0a4722 2763 }
ed34e3c3
A
2764 if (glob(j->argv[i], gflags, NULL, &g) != 0) {
2765 job_log_error(j, LOG_ERR, "glob(\"%s\")", j->argv[i]);
2766 exit(EXIT_FAILURE);
2767 }
2768 }
2769 g.gl_pathv[0] = (char *)file2exec;
2770 argv = (const char **)g.gl_pathv;
2771 } else if (j->argv) {
2772 argv = alloca((j->argc + 2) * sizeof(char *));
2773 argv[0] = file2exec;
5b0a4722 2774 for (i = 0; i < j->argc; i++) {
ed34e3c3 2775 argv[i + 1] = j->argv[i];
5b0a4722 2776 }
ed34e3c3
A
2777 argv[i + 1] = NULL;
2778 } else {
2779 argv = alloca(3 * sizeof(char *));
2780 argv[0] = file2exec;
2781 argv[1] = j->prog;
2782 argv[2] = NULL;
2783 }
2784
5b0a4722 2785 if (!j->inetcompat) {
ed34e3c3 2786 argv++;
5b0a4722
A
2787 }
2788
2789 if (j->wait4debugger) {
2790 job_log(j, LOG_WARNING, "Spawned and waiting for the debugger to attach before continuing...");
2791 spflags |= POSIX_SPAWN_START_SUSPENDED;
2792 }
2793
2794 job_assumes(j, posix_spawnattr_setflags(&spattr, spflags) == 0);
2795
2796 if (j->j_binpref_cnt) {
2797 job_assumes(j, posix_spawnattr_setbinpref_np(&spattr, j->j_binpref_cnt, j->j_binpref, &binpref_out_cnt) == 0);
2798 job_assumes(j, binpref_out_cnt == j->j_binpref_cnt);
2799 }
2800
f36da725 2801#if HAVE_QUARANTINE
5b0a4722
A
2802 if (j->quarantine_data) {
2803 qtn_proc_t qp;
ed34e3c3 2804
5b0a4722
A
2805 if (job_assumes(j, qp = qtn_proc_alloc())) {
2806 if (job_assumes(j, qtn_proc_init_with_data(qp, j->quarantine_data, j->quarantine_data_sz) == 0)) {
2807 job_assumes(j, qtn_proc_apply_to_self(qp) == 0);
2808 }
2809 }
2810 }
f36da725 2811#endif
ed34e3c3 2812
f36da725 2813#if HAVE_SANDBOX
5b0a4722
A
2814 if (j->seatbelt_profile) {
2815 char *seatbelt_err_buf = NULL;
ed34e3c3 2816
5b0a4722
A
2817 if (!job_assumes(j, sandbox_init(j->seatbelt_profile, j->seatbelt_flags, &seatbelt_err_buf) != -1)) {
2818 if (seatbelt_err_buf) {
2819 job_log(j, LOG_ERR, "Sandbox failed to init: %s", seatbelt_err_buf);
2820 }
2821 goto out_bad;
2822 }
ed34e3c3 2823 }
f36da725 2824#endif
ed34e3c3
A
2825
2826 if (j->prog) {
5b0a4722
A
2827 errno = posix_spawn(&junk_pid, j->inetcompat ? file2exec : j->prog, NULL, &spattr, (char *const*)argv, environ);
2828 job_log_error(j, LOG_ERR, "posix_spawn(\"%s\", ...)", j->prog);
ed34e3c3 2829 } else {
5b0a4722
A
2830 errno = posix_spawnp(&junk_pid, j->inetcompat ? file2exec : argv[0], NULL, &spattr, (char *const*)argv, environ);
2831 job_log_error(j, LOG_ERR, "posix_spawnp(\"%s\", ...)", argv[0]);
ed34e3c3
A
2832 }
2833
ef398931 2834#if HAVE_SANDBOX
5b0a4722 2835out_bad:
ef398931 2836#endif
5b0a4722 2837 _exit(EXIT_FAILURE);
ed34e3c3
A
2838}
2839
5b0a4722
A
2840void
2841jobmgr_export_env_from_other_jobs(jobmgr_t jm, launch_data_t dict)
ed34e3c3 2842{
5b0a4722 2843 launch_data_t tmp;
ed34e3c3 2844 struct envitem *ei;
5b0a4722 2845 job_t ji;
ed34e3c3 2846
5b0a4722
A
2847 if (jm->parentmgr) {
2848 jobmgr_export_env_from_other_jobs(jm->parentmgr, dict);
2849 } else {
2850 char **tmpenviron = environ;
2851 for (; *tmpenviron; tmpenviron++) {
2852 char envkey[1024];
2853 launch_data_t s = launch_data_alloc(LAUNCH_DATA_STRING);
2854 launch_data_set_string(s, strchr(*tmpenviron, '=') + 1);
2855 strncpy(envkey, *tmpenviron, sizeof(envkey));
2856 *(strchr(envkey, '=')) = '\0';
2857 launch_data_dict_insert(dict, s, envkey);
2858 }
2859 }
ed34e3c3 2860
5b0a4722
A
2861 LIST_FOREACH(ji, &jm->jobs, sle) {
2862 SLIST_FOREACH(ei, &ji->global_env, sle) {
2863 if ((tmp = launch_data_new_string(ei->value))) {
2864 launch_data_dict_insert(dict, tmp, ei->key);
2865 }
2866 }
2867 }
ed34e3c3
A
2868}
2869
2870void
5b0a4722 2871jobmgr_setup_env_from_other_jobs(jobmgr_t jm)
ed34e3c3 2872{
ed34e3c3 2873 struct envitem *ei;
5b0a4722 2874 job_t ji;
ed34e3c3 2875
5b0a4722
A
2876 if (jm->parentmgr) {
2877 jobmgr_setup_env_from_other_jobs(jm->parentmgr);
2878 }
ed34e3c3 2879
5b0a4722
A
2880 LIST_FOREACH(ji, &jm->jobs, sle) {
2881 SLIST_FOREACH(ei, &ji->global_env, sle) {
2882 setenv(ei->key, ei->value, 1);
ed34e3c3 2883 }
5b0a4722
A
2884 }
2885}
ed34e3c3 2886
5b0a4722
A
2887void
2888job_find_and_blame_pids_with_weird_uids(job_t j)
2889{
2890 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
f36da725
A
2891 size_t i, kp_cnt, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
2892 struct kinfo_proc *kp;
5b0a4722 2893 uid_t u = j->mach_uid;
ed34e3c3 2894
f36da725
A
2895#if TARGET_OS_EMBEDDED
2896 if (!do_apple_internal_magic) {
2897 return;
2898 }
2899#endif
2900 kp = malloc(len);
2901
5b0a4722
A
2902 if (!job_assumes(j, kp != NULL)) {
2903 return;
2904 }
2905 if (!job_assumes(j, sysctl(mib, 3, kp, &len, NULL, 0) != -1)) {
2906 goto out;
ed34e3c3
A
2907 }
2908
5b0a4722 2909 kp_cnt = len / sizeof(struct kinfo_proc);
ed34e3c3 2910
5b0a4722
A
2911 for (i = 0; i < kp_cnt; i++) {
2912 uid_t i_euid = kp[i].kp_eproc.e_ucred.cr_uid;
2913 uid_t i_uid = kp[i].kp_eproc.e_pcred.p_ruid;
2914 uid_t i_svuid = kp[i].kp_eproc.e_pcred.p_svuid;
2915 pid_t i_pid = kp[i].kp_proc.p_pid;
2916
2917 if (i_euid != u && i_uid != u && i_svuid != u) {
2918 continue;
2919 }
ed34e3c3 2920
5b0a4722
A
2921 job_log(j, LOG_ERR, "PID %u \"%s\" has no account to back it! Real/effective/saved UIDs: %u/%u/%u",
2922 i_pid, kp[i].kp_proc.p_comm, i_uid, i_euid, i_svuid);
2923
2924/* Temporarily disabled due to 5423935 and 4946119. */
2925#if 0
2926 /* Ask the accountless process to exit. */
2927 job_assumes(j, runtime_kill(i_pid, SIGTERM) != -1);
2928#endif
ed34e3c3 2929 }
5b0a4722
A
2930
2931out:
2932 free(kp);
2933}
2934
ef398931 2935#if !TARGET_OS_EMBEDDED
cf0bacfd
A
2936void
2937job_enable_audit_for_user(job_t j, uid_t u, char *name)
2938{
2939 auditinfo_t auinfo = {
2940 .ai_auid = u,
2941 .ai_asid = j->p,
2942 };
2943 long au_cond;
2944
2945 if (!job_assumes(j, auditon(A_GETCOND, &au_cond, sizeof(long)) == 0)) {
2946 _exit(EXIT_FAILURE);
2947 }
2948
2949 if (au_cond != AUC_NOAUDIT) {
2950 if (!job_assumes(j, au_user_mask(name, &auinfo.ai_mask) == 0)) {
2951 _exit(EXIT_FAILURE);
2952 } else if (!job_assumes(j, setaudit(&auinfo) == 0)) {
2953 _exit(EXIT_FAILURE);
2954 }
2955 }
2956}
ef398931 2957#endif
cf0bacfd 2958
5b0a4722
A
2959void
2960job_postfork_become_user(job_t j)
2961{
2962 char loginname[2000];
2963 char tmpdirpath[PATH_MAX];
2964 char shellpath[PATH_MAX];
2965 char homedir[PATH_MAX];
2966 struct passwd *pwe;
2967 size_t r;
2968 gid_t desired_gid = -1;
2969 uid_t desired_uid = -1;
2970
2971 if (getuid() != 0) {
2972 return;
2973 }
2974
2975 /*
2976 * I contend that having UID == 0 and GID != 0 is of dubious value.
2977 * Nevertheless, this used to work in Tiger. See: 5425348
2978 */
2979 if (j->groupname && !j->username) {
2980 j->username = "root";
2981 }
2982
2983 if (j->username) {
2984 if ((pwe = getpwnam(j->username)) == NULL) {
2985 job_log(j, LOG_ERR, "getpwnam(\"%s\") failed", j->username);
2986 _exit(EXIT_FAILURE);
2987 }
2988 } else if (j->mach_uid) {
2989 if ((pwe = getpwuid(j->mach_uid)) == NULL) {
2990 job_log(j, LOG_ERR, "getpwuid(\"%u\") failed", j->mach_uid);
2991 job_find_and_blame_pids_with_weird_uids(j);
2992 _exit(EXIT_FAILURE);
2993 }
2994 } else {
2995 return;
2996 }
2997
2998 /*
2999 * We must copy the results of getpw*().
3000 *
3001 * Why? Because subsequent API calls may call getpw*() as a part of
3002 * their implementation. Since getpw*() returns a [now thread scoped]
3003 * global, we must therefore cache the results before continuing.
3004 */
3005
3006 desired_uid = pwe->pw_uid;
3007 desired_gid = pwe->pw_gid;
3008
3009 strlcpy(shellpath, pwe->pw_shell, sizeof(shellpath));
3010 strlcpy(loginname, pwe->pw_name, sizeof(loginname));
3011 strlcpy(homedir, pwe->pw_dir, sizeof(homedir));
3012
3013 if (pwe->pw_expire && time(NULL) >= pwe->pw_expire) {
3014 job_log(j, LOG_ERR, "Expired account");
3015 _exit(EXIT_FAILURE);
3016 }
3017
3018
3019 if (j->username && strcmp(j->username, loginname) != 0) {
3020 job_log(j, LOG_WARNING, "Suspicious setup: User \"%s\" maps to user: %s", j->username, loginname);
3021 } else if (j->mach_uid && (j->mach_uid != desired_uid)) {
3022 job_log(j, LOG_WARNING, "Suspicious setup: UID %u maps to UID %u", j->mach_uid, desired_uid);
ed34e3c3 3023 }
5b0a4722 3024
ed34e3c3 3025 if (j->groupname) {
5b0a4722
A
3026 struct group *gre;
3027
3028 if ((gre = getgrnam(j->groupname)) == NULL) {
ed34e3c3 3029 job_log(j, LOG_ERR, "getgrnam(\"%s\") failed", j->groupname);
5b0a4722 3030 _exit(EXIT_FAILURE);
ed34e3c3 3031 }
5b0a4722
A
3032
3033 desired_gid = gre->gr_gid;
3034 }
3035
ef398931 3036#if !TARGET_OS_EMBEDDED
cf0bacfd 3037 job_enable_audit_for_user(j, desired_uid, loginname);
ef398931 3038#endif
cf0bacfd 3039
5b0a4722
A
3040 if (!job_assumes(j, setlogin(loginname) != -1)) {
3041 _exit(EXIT_FAILURE);
ed34e3c3 3042 }
ed34e3c3 3043
5b0a4722
A
3044 if (!job_assumes(j, setgid(desired_gid) != -1)) {
3045 _exit(EXIT_FAILURE);
3046 }
ed34e3c3 3047
5b0a4722
A
3048 /*
3049 * The kernel team and the DirectoryServices team want initgroups()
3050 * called after setgid(). See 4616864 for more information.
3051 */
ed34e3c3 3052
5b0a4722
A
3053 if (!j->no_init_groups) {
3054 if (!job_assumes(j, initgroups(loginname, desired_gid) != -1)) {
3055 _exit(EXIT_FAILURE);
ed34e3c3
A
3056 }
3057 }
5b0a4722
A
3058
3059 if (!job_assumes(j, setuid(desired_uid) != -1)) {
3060 _exit(EXIT_FAILURE);
ed34e3c3 3061 }
5b0a4722
A
3062
3063 r = confstr(_CS_DARWIN_USER_TEMP_DIR, tmpdirpath, sizeof(tmpdirpath));
3064
3065 if (r > 0 && r < sizeof(tmpdirpath)) {
3066 setenv("TMPDIR", tmpdirpath, 0);
ed34e3c3 3067 }
5b0a4722
A
3068
3069 setenv("SHELL", shellpath, 0);
3070 setenv("HOME", homedir, 0);
3071 setenv("USER", loginname, 0);
3072 setenv("LOGNAME", loginname, 0);
3073}
3074
3075void
3076job_setup_attributes(job_t j)
3077{
3078 struct limititem *li;
3079 struct envitem *ei;
3080
3081 if (j->setnice) {
3082 job_assumes(j, setpriority(PRIO_PROCESS, 0, j->nice) != -1);
3083 }
3084
3085 SLIST_FOREACH(li, &j->limits, sle) {
3086 struct rlimit rl;
3087
3088 if (!job_assumes(j, getrlimit(li->which, &rl) != -1)) {
3089 continue;
3090 }
3091
3092 if (li->sethard) {
3093 rl.rlim_max = li->lim.rlim_max;
3094 }
3095 if (li->setsoft) {
3096 rl.rlim_cur = li->lim.rlim_cur;
3097 }
3098
3099 if (setrlimit(li->which, &rl) == -1) {
3100 job_log_error(j, LOG_WARNING, "setrlimit()");
ed34e3c3
A
3101 }
3102 }
3103
5b0a4722
A
3104 if (!j->inetcompat && j->session_create) {
3105 launchd_SessionCreate();
3106 }
3107
3108 if (j->low_pri_io) {
3109 job_assumes(j, setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE) != -1);
3110 }
3111 if (j->rootdir) {
3112 job_assumes(j, chroot(j->rootdir) != -1);
3113 job_assumes(j, chdir(".") != -1);
3114 }
3115
3116 job_postfork_become_user(j);
3117
3118 if (j->workingdir) {
3119 job_assumes(j, chdir(j->workingdir) != -1);
3120 }
3121
3122 if (j->setmask) {
3123 umask(j->mask);
3124 }
3125
3126 job_setup_fd(j, STDOUT_FILENO, j->stdoutpath, O_WRONLY|O_APPEND|O_CREAT);
3127 job_setup_fd(j, STDERR_FILENO, j->stderrpath, O_WRONLY|O_APPEND|O_CREAT);
ed34e3c3 3128
5b0a4722
A
3129 jobmgr_setup_env_from_other_jobs(j->mgr);
3130
3131 SLIST_FOREACH(ei, &j->env, sle) {
ed34e3c3 3132 setenv(ei->key, ei->value, 1);
5b0a4722
A
3133 }
3134
3135 /*
3136 * We'd like to call setsid() unconditionally, but we have reason to
3137 * believe that prevents launchd from being able to send signals to
3138 * setuid children. We'll settle for process-groups.
3139 */
3140 if (getppid() != 1) {
3141 job_assumes(j, setpgid(0, 0) != -1);
3142 } else {
3143 job_assumes(j, setsid() != -1);
3144 }
3145}
3146
3147void
3148job_setup_fd(job_t j, int target_fd, const char *path, int flags)
3149{
3150 int fd;
3151
3152 if (!path) {
3153 return;
3154 }
3155
3156 if ((fd = open(path, flags|O_NOCTTY, DEFFILEMODE)) == -1) {
3157 job_log_error(j, LOG_WARNING, "open(\"%s\", ...)", path);
3158 return;
3159 }
ed34e3c3 3160
5b0a4722
A
3161 job_assumes(j, dup2(fd, target_fd) != -1);
3162 job_assumes(j, runtime_close(fd) == 0);
ed34e3c3
A
3163}
3164
3165int
5b0a4722 3166dir_has_files(job_t j, const char *path)
ed34e3c3
A
3167{
3168 DIR *dd = opendir(path);
3169 struct dirent *de;
3170 bool r = 0;
3171
5b0a4722 3172 if (!dd) {
ed34e3c3 3173 return -1;
5b0a4722 3174 }
ed34e3c3
A
3175
3176 while ((de = readdir(dd))) {
3177 if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
3178 r = 1;
3179 break;
3180 }
3181 }
3182
5b0a4722 3183 job_assumes(j, closedir(dd) == 0);
ed34e3c3
A
3184 return r;
3185}
3186
3187void
5b0a4722 3188calendarinterval_setalarm(job_t j, struct calendarinterval *ci)
ed34e3c3 3189{
5b0a4722
A
3190 struct calendarinterval *ci_iter, *ci_prev = NULL;
3191 time_t later, head_later;
ed34e3c3
A
3192
3193 later = cronemu(ci->when.tm_mon, ci->when.tm_mday, ci->when.tm_hour, ci->when.tm_min);
3194
3195 if (ci->when.tm_wday != -1) {
3196 time_t otherlater = cronemu_wday(ci->when.tm_wday, ci->when.tm_hour, ci->when.tm_min);
3197
3198 if (ci->when.tm_mday == -1) {
3199 later = otherlater;
3200 } else {
3201 later = later < otherlater ? later : otherlater;
3202 }
3203 }
3204
5b0a4722
A
3205 ci->when_next = later;
3206
3207 LIST_FOREACH(ci_iter, &sorted_calendar_events, global_sle) {
3208 if (ci->when_next < ci_iter->when_next) {
3209 LIST_INSERT_BEFORE(ci_iter, ci, global_sle);
3210 break;
3211 }
3212
3213 ci_prev = ci_iter;
ed34e3c3 3214 }
ed34e3c3 3215
5b0a4722
A
3216 if (ci_iter == NULL) {
3217 /* ci must want to fire after every other timer, or there are no timers */
ed34e3c3 3218
5b0a4722
A
3219 if (LIST_EMPTY(&sorted_calendar_events)) {
3220 LIST_INSERT_HEAD(&sorted_calendar_events, ci, global_sle);
ed34e3c3 3221 } else {
5b0a4722 3222 LIST_INSERT_AFTER(ci_prev, ci, global_sle);
ed34e3c3
A
3223 }
3224 }
ed34e3c3 3225
5b0a4722 3226 head_later = LIST_FIRST(&sorted_calendar_events)->when_next;
ed34e3c3 3227
5b0a4722
A
3228 /* Workaround 5225889 */
3229 kevent_mod((uintptr_t)&sorted_calendar_events, EVFILT_TIMER, EV_DELETE, 0, 0, root_jobmgr);
ed34e3c3 3230
5b0a4722
A
3231 if (job_assumes(j, kevent_mod((uintptr_t)&sorted_calendar_events, EVFILT_TIMER, EV_ADD, NOTE_ABSOLUTE|NOTE_SECONDS, head_later, root_jobmgr) != -1)) {
3232 char time_string[100];
3233 size_t time_string_len;
ed34e3c3 3234
5b0a4722
A
3235 ctime_r(&later, time_string);
3236 time_string_len = strlen(time_string);
ed34e3c3 3237
5b0a4722
A
3238 if (time_string_len && time_string[time_string_len - 1] == '\n') {
3239 time_string[time_string_len - 1] = '\0';
3240 }
ed34e3c3 3241
5b0a4722
A
3242 job_log(j, LOG_INFO, "Scheduled to run again at %s", time_string);
3243 }
ed34e3c3
A
3244}
3245
3246void
5b0a4722 3247extract_rcsid_substr(const char *i, char *o, size_t osz)
ed34e3c3 3248{
5b0a4722 3249 char *rcs_rev_tmp = strchr(i, ' ');
ed34e3c3 3250
5b0a4722
A
3251 if (!rcs_rev_tmp) {
3252 strlcpy(o, i, osz);
3253 } else {
3254 strlcpy(o, rcs_rev_tmp + 1, osz);
3255 rcs_rev_tmp = strchr(o, ' ');
3256 if (rcs_rev_tmp) {
3257 *rcs_rev_tmp = '\0';
3258 }
3259 }
ed34e3c3
A
3260}
3261
5b0a4722
A
3262void
3263jobmgr_log_bug(jobmgr_t jm, const char *rcs_rev, const char *path, unsigned int line, const char *test)
ed34e3c3 3264{
5b0a4722
A
3265 int saved_errno = errno;
3266 const char *file = strrchr(path, '/');
3267 char buf[100];
ed34e3c3 3268
5b0a4722 3269 extract_rcsid_substr(rcs_rev, buf, sizeof(buf));
ed34e3c3 3270
5b0a4722
A
3271 if (!file) {
3272 file = path;
3273 } else {
3274 file += 1;
3275 }
ed34e3c3 3276
5b0a4722
A
3277 jobmgr_log(jm, LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test);
3278}
ed34e3c3
A
3279
3280void
5b0a4722 3281job_log_bug(job_t j, const char *rcs_rev, const char *path, unsigned int line, const char *test)
ed34e3c3 3282{
5b0a4722
A
3283 int saved_errno = errno;
3284 const char *file = strrchr(path, '/');
3285 char buf[100];
ed34e3c3 3286
5b0a4722 3287 extract_rcsid_substr(rcs_rev, buf, sizeof(buf));
ed34e3c3 3288
5b0a4722
A
3289 if (!file) {
3290 file = path;
3291 } else {
3292 file += 1;
ed34e3c3 3293 }
5b0a4722
A
3294
3295 job_log(j, LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test);
ed34e3c3
A
3296}
3297
3298void
5b0a4722 3299job_logv(job_t j, int pri, int err, const char *msg, va_list ap)
ed34e3c3 3300{
5b0a4722
A
3301 struct runtime_syslog_attr attr = { "com.apple.launchd", j->label, j->mgr->name, pri, getuid(), getpid(), j->p };
3302 char *newmsg;
3303 int oldmask = 0;
3304 size_t newmsgsz;
ed34e3c3 3305
5b0a4722
A
3306 /*
3307 * Hack: If bootstrap_port is set, we must be on the child side of a
3308 * fork(), but before the exec*(). Let's route the log message back to
3309 * launchd proper.
3310 */
3311 if (bootstrap_port) {
3312 return _vproc_logv(pri, err, msg, ap);
3313 }
ed34e3c3 3314
5b0a4722
A
3315 newmsgsz = strlen(msg) + 200;
3316 newmsg = alloca(newmsgsz);
ed34e3c3 3317
5b0a4722
A
3318 if (err) {
3319 snprintf(newmsg, newmsgsz, "%s: %s", msg, strerror(err));
3320 } else {
3321 snprintf(newmsg, newmsgsz, "%s", msg);
3322 }
ed34e3c3 3323
5b0a4722
A
3324 if (j->debug) {
3325 oldmask = setlogmask(LOG_UPTO(LOG_DEBUG));
3326 }
ed34e3c3 3327
5b0a4722 3328 runtime_vsyslog(&attr, newmsg, ap);
ed34e3c3 3329
5b0a4722
A
3330 if (j->debug) {
3331 setlogmask(oldmask);
ed34e3c3
A
3332 }
3333}
3334
3335void
5b0a4722 3336job_log_error(job_t j, int pri, const char *msg, ...)
ed34e3c3 3337{
5b0a4722 3338 va_list ap;
ed34e3c3 3339
5b0a4722
A
3340 va_start(ap, msg);
3341 job_logv(j, pri, errno, msg, ap);
3342 va_end(ap);
ed34e3c3
A
3343}
3344
5b0a4722
A
3345void
3346job_log(job_t j, int pri, const char *msg, ...)
ed34e3c3 3347{
5b0a4722 3348 va_list ap;
ed34e3c3 3349
5b0a4722
A
3350 va_start(ap, msg);
3351 job_logv(j, pri, 0, msg, ap);
3352 va_end(ap);
3353}
ed34e3c3 3354
5b0a4722
A
3355#if 0
3356void
3357jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...)
3358{
3359 va_list ap;
ed34e3c3 3360
5b0a4722
A
3361 va_start(ap, msg);
3362 jobmgr_logv(jm, pri, errno, msg, ap);
3363 va_end(ap);
3364}
3365#endif
ed34e3c3 3366
5b0a4722
A
3367void
3368jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...)
3369{
3370 va_list ap;
ed34e3c3 3371
5b0a4722
A
3372 va_start(ap, msg);
3373 jobmgr_logv(jm, pri, 0, msg, ap);
3374 va_end(ap);
ed34e3c3
A
3375}
3376
5b0a4722
A
3377void
3378jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap)
ed34e3c3 3379{
5b0a4722
A
3380 char *newmsg;
3381 char *newname;
3382 size_t i, o, jmname_len = strlen(jm->name), newmsgsz;
ed34e3c3 3383
5b0a4722
A
3384 newname = alloca((jmname_len + 1) * 2);
3385 newmsgsz = (jmname_len + 1) * 2 + strlen(msg) + 100;
3386 newmsg = alloca(newmsgsz);
ed34e3c3 3387
5b0a4722
A
3388 for (i = 0, o = 0; i < jmname_len; i++, o++) {
3389 if (jm->name[i] == '%') {
3390 newname[o] = '%';
3391 o++;
3392 }
3393 newname[o] = jm->name[i];
3394 }
3395 newname[o] = '\0';
ed34e3c3 3396
5b0a4722
A
3397 if (err) {
3398 snprintf(newmsg, newmsgsz, "%s: %s: %s", newname, msg, strerror(err));
3399 } else {
3400 snprintf(newmsg, newmsgsz, "%s: %s", newname, msg);
3401 }
ed34e3c3 3402
5b0a4722
A
3403 if (jm->parentmgr) {
3404 jobmgr_logv(jm->parentmgr, pri, 0, newmsg, ap);
3405 } else {
3406 struct runtime_syslog_attr attr = { "com.apple.launchd", "com.apple.launchd", jm->name, pri, getuid(), getpid(), getpid() };
ed34e3c3 3407
5b0a4722
A
3408 runtime_vsyslog(&attr, newmsg, ap);
3409 }
ed34e3c3
A
3410}
3411
3412void
5b0a4722 3413semaphoreitem_ignore(job_t j, struct semaphoreitem *si)
ed34e3c3 3414{
5b0a4722
A
3415 if (si->fd != -1) {
3416 job_log(j, LOG_DEBUG, "Ignoring Vnode: %d", si->fd);
3417 job_assumes(j, kevent_mod(si->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL) != -1);
3418 }
ed34e3c3
A
3419}
3420
3421void
5b0a4722 3422semaphoreitem_watch(job_t j, struct semaphoreitem *si)
ed34e3c3 3423{
f36da725
A
3424 char *parentdir, tmp_path[PATH_MAX];
3425 const char *which_path = si->what;
5b0a4722
A
3426 int saved_errno = 0;
3427 int fflags = 0;
ed34e3c3 3428
5b0a4722
A
3429 switch (si->why) {
3430 case PATH_EXISTS:
3431 fflags = NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE|NOTE_EXTEND|NOTE_WRITE;
3432 break;
3433 case PATH_MISSING:
3434 fflags = NOTE_DELETE|NOTE_RENAME;
3435 break;
3436 case DIR_NOT_EMPTY:
3437 case PATH_CHANGES:
3438 fflags = NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE|NOTE_EXTEND|NOTE_WRITE|NOTE_ATTRIB|NOTE_LINK;
3439 break;
3440 default:
3441 return;
ed34e3c3
A
3442 }
3443
f36da725
A
3444 /* dirname() may modify tmp_path */
3445 strlcpy(tmp_path, si->what, sizeof(tmp_path));
3446
3447 if (!job_assumes(j, (parentdir = dirname(tmp_path)))) {
3448 return;
3449 }
3450
5b0a4722
A
3451 /* See 5321044 for why we do the do-while loop and 5415523 for why ENOENT is checked */
3452 do {
3453 if (si->fd == -1) {
3454 if ((si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY))) == -1) {
f36da725 3455 which_path = parentdir;
5b0a4722
A
3456 si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY));
3457 }
3458 }
3459
3460 if (si->fd == -1) {
3461 return job_log_error(j, LOG_ERR, "Path monitoring failed on \"%s\"", which_path);
3462 }
3463
3464 job_log(j, LOG_DEBUG, "Watching Vnode: %d", si->fd);
3465
3466 if (kevent_mod(si->fd, EVFILT_VNODE, EV_ADD, fflags, 0, j) == -1) {
3467 saved_errno = errno;
3468 /*
3469 * The FD can be revoked between the open() and kevent().
3470 * This is similar to the inability for kevents to be
3471 * attached to short lived zombie processes after fork()
3472 * but before kevent().
3473 */
3474 job_assumes(j, runtime_close(si->fd) == 0);
3475 si->fd = -1;
3476 }
3477 } while ((si->fd == -1) && (saved_errno == ENOENT));
3478
3479 if (saved_errno == ENOTSUP) {
3480 /*
3481 * 3524219 NFS needs kqueue support
3482 * 4124079 VFS needs generic kqueue support
3483 * 5226811 EVFILT: Launchd EVFILT_VNODE doesn't work on /dev
3484 */
3485 job_log(j, LOG_DEBUG, "Falling back to polling for path: %s", si->what);
3486
3487 if (!j->poll_for_vfs_changes) {
3488 j->poll_for_vfs_changes = true;
3489 job_assumes(j, kevent_mod((uintptr_t)&j->semaphores, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 3, j) != -1);
3490 }
ed34e3c3
A
3491 }
3492}
3493
5b0a4722
A
3494void
3495semaphoreitem_callback(job_t j, struct kevent *kev)
ed34e3c3 3496{
5b0a4722
A
3497 char invalidation_reason[100] = "";
3498 struct semaphoreitem *si;
3499
3500 SLIST_FOREACH(si, &j->semaphores, sle) {
3501 switch (si->why) {
3502 case PATH_CHANGES:
3503 case PATH_EXISTS:
3504 case PATH_MISSING:
3505 case DIR_NOT_EMPTY:
3506 break;
3507 default:
3508 continue;
3509 }
3510
3511 if (si->fd == (int)kev->ident) {
3512 break;
3513 }
3514 }
3515
3516 if (!job_assumes(j, si != NULL)) {
3517 return;
3518 }
3519
3520 if (NOTE_DELETE & kev->fflags) {
3521 strcat(invalidation_reason, "deleted");
3522 }
3523
3524 if (NOTE_RENAME & kev->fflags) {
3525 if (invalidation_reason[0]) {
3526 strcat(invalidation_reason, "/renamed");
3527 } else {
3528 strcat(invalidation_reason, "renamed");
3529 }
3530 }
3531
3532 if (NOTE_REVOKE & kev->fflags) {
3533 if (invalidation_reason[0]) {
3534 strcat(invalidation_reason, "/revoked");
3535 } else {
3536 strcat(invalidation_reason, "revoked");
3537 }
3538 }
3539
3540 if (invalidation_reason[0]) {
3541 job_log(j, LOG_DEBUG, "Path %s: %s", invalidation_reason, si->what);
3542 job_assumes(j, runtime_close(si->fd) == 0);
3543 si->fd = -1; /* this will get fixed in semaphoreitem_watch() */
3544 }
3545
3546 job_log(j, LOG_DEBUG, "Watch path modified: %s", si->what);
3547
3548 if (si->why == PATH_CHANGES) {
3549 j->start_pending = true;
3550 }
3551
3552 job_dispatch(j, false);
3553}
3554
3555void
3556calendarinterval_new_from_obj_dict_walk(launch_data_t obj, const char *key, void *context)
3557{
3558 struct tm *tmptm = context;
3559 int64_t val;
3560
3561 if (LAUNCH_DATA_INTEGER != launch_data_get_type(obj)) {
3562 /* hack to let caller know something went wrong */
3563 tmptm->tm_sec = -1;
3564 return;
3565 }
3566
3567 val = launch_data_get_integer(obj);
3568
3569 if (strcasecmp(key, LAUNCH_JOBKEY_CAL_MINUTE) == 0) {
3570 tmptm->tm_min = val;
3571 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_HOUR) == 0) {
3572 tmptm->tm_hour = val;
3573 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_DAY) == 0) {
3574 tmptm->tm_mday = val;
3575 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_WEEKDAY) == 0) {
3576 tmptm->tm_wday = val;
3577 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_MONTH) == 0) {
3578 tmptm->tm_mon = val;
3579 tmptm->tm_mon -= 1; /* 4798263 cron compatibility */
3580 }
3581}
3582
3583bool
3584calendarinterval_new_from_obj(job_t j, launch_data_t obj)
3585{
3586 struct tm tmptm;
3587
3588 memset(&tmptm, 0, sizeof(0));
3589
3590 tmptm.tm_min = -1;
3591 tmptm.tm_hour = -1;
3592 tmptm.tm_mday = -1;
3593 tmptm.tm_wday = -1;
3594 tmptm.tm_mon = -1;
3595
3596 if (!job_assumes(j, obj != NULL)) {
3597 return false;
3598 }
3599
3600 if (LAUNCH_DATA_DICTIONARY != launch_data_get_type(obj)) {
3601 return false;
3602 }
3603
3604 launch_data_dict_iterate(obj, calendarinterval_new_from_obj_dict_walk, &tmptm);
3605
3606 if (tmptm.tm_sec == -1) {
3607 return false;
3608 }
3609
3610 return calendarinterval_new(j, &tmptm);
3611}
3612
3613bool
3614calendarinterval_new(job_t j, struct tm *w)
3615{
3616 struct calendarinterval *ci = calloc(1, sizeof(struct calendarinterval));
3617
3618 if (!job_assumes(j, ci != NULL)) {
3619 return false;
3620 }
3621
3622 ci->when = *w;
3623 ci->job = j;
3624
3625 SLIST_INSERT_HEAD(&j->cal_intervals, ci, sle);
3626
3627 calendarinterval_setalarm(j, ci);
3628
3629 runtime_add_ref();
3630
3631 return true;
3632}
3633
3634void
3635calendarinterval_delete(job_t j, struct calendarinterval *ci)
3636{
3637 SLIST_REMOVE(&j->cal_intervals, ci, calendarinterval, sle);
3638 LIST_REMOVE(ci, global_sle);
3639
3640 free(ci);
3641
3642 runtime_del_ref();
3643}
3644
3645void
3646calendarinterval_sanity_check(void)
3647{
3648 struct calendarinterval *ci = LIST_FIRST(&sorted_calendar_events);
3649 time_t now = time(NULL);
3650
3651 if (ci && (ci->when_next < now)) {
3652 jobmgr_assumes(root_jobmgr, raise(SIGUSR1) != -1);
3653 }
3654}
3655
3656void
3657calendarinterval_callback(void)
3658{
3659 struct calendarinterval *ci, *ci_next;
3660 time_t now = time(NULL);
3661
3662 LIST_FOREACH_SAFE(ci, &sorted_calendar_events, global_sle, ci_next) {
3663 job_t j = ci->job;
3664
3665 if (ci->when_next > now) {
3666 break;
3667 }
3668
3669 LIST_REMOVE(ci, global_sle);
3670 calendarinterval_setalarm(j, ci);
3671
3672 j->start_pending = true;
3673 job_dispatch(j, false);
3674 }
3675}
3676
3677bool
3678socketgroup_new(job_t j, const char *name, int *fds, unsigned int fd_cnt, bool junkfds)
3679{
3680 struct socketgroup *sg = calloc(1, sizeof(struct socketgroup) + strlen(name) + 1);
3681
3682 if (!job_assumes(j, sg != NULL)) {
3683 return false;
3684 }
ed34e3c3
A
3685
3686 sg->fds = calloc(1, fd_cnt * sizeof(int));
3687 sg->fd_cnt = fd_cnt;
3688 sg->junkfds = junkfds;
3689
5b0a4722 3690 if (!job_assumes(j, sg->fds != NULL)) {
ed34e3c3
A
3691 free(sg);
3692 return false;
3693 }
3694
3695 memcpy(sg->fds, fds, fd_cnt * sizeof(int));
f36da725 3696 strcpy(sg->name_init, name);
ed34e3c3
A
3697
3698 SLIST_INSERT_HEAD(&j->sockets, sg, sle);
3699
5b0a4722
A
3700 runtime_add_ref();
3701
ed34e3c3
A
3702 return true;
3703}
3704
3705void
5b0a4722 3706socketgroup_delete(job_t j, struct socketgroup *sg)
ed34e3c3
A
3707{
3708 unsigned int i;
3709
5b0a4722
A
3710 for (i = 0; i < sg->fd_cnt; i++) {
3711#if 0
3712 struct sockaddr_storage ss;
3713 struct sockaddr_un *sun = (struct sockaddr_un *)&ss;
3714 socklen_t ss_len = sizeof(ss);
3715
3716 /* 5480306 */
3717 if (job_assumes(j, getsockname(sg->fds[i], (struct sockaddr *)&ss, &ss_len) != -1)
3718 && job_assumes(j, ss_len > 0) && (ss.ss_family == AF_UNIX)) {
3719 job_assumes(j, unlink(sun->sun_path) != -1);
3720 /* We might conditionally need to delete a directory here */
3721 }
3722#endif
3723 job_assumes(j, runtime_close(sg->fds[i]) != -1);
3724 }
ed34e3c3
A
3725
3726 SLIST_REMOVE(&j->sockets, sg, socketgroup, sle);
3727
3728 free(sg->fds);
3729 free(sg);
5b0a4722
A
3730
3731 runtime_del_ref();
ed34e3c3
A
3732}
3733
3734void
5b0a4722 3735socketgroup_kevent_mod(job_t j, struct socketgroup *sg, bool do_add)
ed34e3c3 3736{
5b0a4722 3737 struct kevent kev[sg->fd_cnt];
ed34e3c3
A
3738 char buf[10000];
3739 unsigned int i, buf_off = 0;
3740
5b0a4722 3741 if (sg->junkfds) {
ed34e3c3 3742 return;
5b0a4722
A
3743 }
3744
3745 for (i = 0; i < sg->fd_cnt; i++) {
3746 EV_SET(&kev[i], sg->fds[i], EVFILT_READ, do_add ? EV_ADD : EV_DELETE, 0, 0, j);
3747 buf_off += snprintf(buf + buf_off, sizeof(buf) - buf_off, " %d", sg->fds[i]);
3748 }
ed34e3c3 3749
5b0a4722 3750 job_log(j, LOG_DEBUG, "%s Sockets:%s", do_add ? "Watching" : "Ignoring", buf);
ed34e3c3 3751
5b0a4722 3752 job_assumes(j, kevent_bulk_mod(kev, sg->fd_cnt) != -1);
ed34e3c3 3753
5b0a4722
A
3754 for (i = 0; i < sg->fd_cnt; i++) {
3755 job_assumes(j, kev[i].flags & EV_ERROR);
3756 errno = kev[i].data;
3757 job_assumes(j, kev[i].data == 0);
3758 }
ed34e3c3
A
3759}
3760
3761void
5b0a4722 3762socketgroup_ignore(job_t j, struct socketgroup *sg)
ed34e3c3 3763{
5b0a4722
A
3764 socketgroup_kevent_mod(j, sg, false);
3765}
ed34e3c3 3766
5b0a4722
A
3767void
3768socketgroup_watch(job_t j, struct socketgroup *sg)
3769{
3770 socketgroup_kevent_mod(j, sg, true);
ed34e3c3
A
3771}
3772
3773void
5b0a4722 3774socketgroup_callback(job_t j)
ed34e3c3 3775{
5b0a4722 3776 job_dispatch(j, true);
ed34e3c3
A
3777}
3778
3779bool
5b0a4722 3780envitem_new(job_t j, const char *k, const char *v, bool global)
ed34e3c3
A
3781{
3782 struct envitem *ei = calloc(1, sizeof(struct envitem) + strlen(k) + 1 + strlen(v) + 1);
3783
5b0a4722 3784 if (!job_assumes(j, ei != NULL)) {
ed34e3c3 3785 return false;
5b0a4722 3786 }
ed34e3c3 3787
f36da725
A
3788 strcpy(ei->key_init, k);
3789 ei->value = ei->key_init + strlen(k) + 1;
ed34e3c3
A
3790 strcpy(ei->value, v);
3791
3792 if (global) {
3793 SLIST_INSERT_HEAD(&j->global_env, ei, sle);
3794 } else {
3795 SLIST_INSERT_HEAD(&j->env, ei, sle);
3796 }
3797
5b0a4722
A
3798 job_log(j, LOG_DEBUG, "Added environmental variable: %s=%s", k, v);
3799
ed34e3c3
A
3800 return true;
3801}
3802
3803void
5b0a4722 3804envitem_delete(job_t j, struct envitem *ei, bool global)
ed34e3c3
A
3805{
3806 if (global) {
3807 SLIST_REMOVE(&j->global_env, ei, envitem, sle);
3808 } else {
3809 SLIST_REMOVE(&j->env, ei, envitem, sle);
3810 }
3811
3812 free(ei);
3813}
3814
3815void
3816envitem_setup(launch_data_t obj, const char *key, void *context)
3817{
5b0a4722 3818 job_t j = context;
ed34e3c3 3819
5b0a4722 3820 if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) {
ed34e3c3 3821 return;
5b0a4722 3822 }
ed34e3c3
A
3823
3824 envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env);
3825}
3826
3827bool
5b0a4722 3828limititem_update(job_t j, int w, rlim_t r)
ed34e3c3
A
3829{
3830 struct limititem *li;
3831
3832 SLIST_FOREACH(li, &j->limits, sle) {
5b0a4722 3833 if (li->which == w) {
ed34e3c3 3834 break;
5b0a4722 3835 }
ed34e3c3
A
3836 }
3837
3838 if (li == NULL) {
3839 li = calloc(1, sizeof(struct limititem));
3840
5b0a4722 3841 if (!job_assumes(j, li != NULL)) {
ed34e3c3 3842 return false;
5b0a4722
A
3843 }
3844
3845 SLIST_INSERT_HEAD(&j->limits, li, sle);
ed34e3c3
A
3846
3847 li->which = w;
3848 }
3849
3850 if (j->importing_hard_limits) {
3851 li->lim.rlim_max = r;
3852 li->sethard = true;
3853 } else {
3854 li->lim.rlim_cur = r;
3855 li->setsoft = true;
3856 }
3857
3858 return true;
3859}
3860
3861void
5b0a4722 3862limititem_delete(job_t j, struct limititem *li)
ed34e3c3
A
3863{
3864 SLIST_REMOVE(&j->limits, li, limititem, sle);
3865
3866 free(li);
3867}
3868
f36da725 3869#if HAVE_SANDBOX
5b0a4722
A
3870void
3871seatbelt_setup_flags(launch_data_t obj, const char *key, void *context)
3872{
3873 job_t j = context;
3874
3875 if (launch_data_get_type(obj) != LAUNCH_DATA_BOOL) {
3876 job_log(j, LOG_WARNING, "Sandbox flag value must be boolean: %s", key);
3877 return;
3878 }
3879
3880 if (launch_data_get_bool(obj) == false) {
3881 return;
3882 }
3883
3884 if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOX_NAMED) == 0) {
3885 j->seatbelt_flags |= SANDBOX_NAMED;
3886 }
3887}
f36da725 3888#endif
5b0a4722 3889
ed34e3c3
A
3890void
3891limititem_setup(launch_data_t obj, const char *key, void *context)
3892{
5b0a4722 3893 job_t j = context;
ed34e3c3
A
3894 int i, limits_cnt = (sizeof(launchd_keys2limits) / sizeof(launchd_keys2limits[0]));
3895 rlim_t rl;
3896
5b0a4722 3897 if (launch_data_get_type(obj) != LAUNCH_DATA_INTEGER) {
ed34e3c3 3898 return;
5b0a4722 3899 }
ed34e3c3
A
3900
3901 rl = launch_data_get_integer(obj);
3902
3903 for (i = 0; i < limits_cnt; i++) {
5b0a4722 3904 if (strcasecmp(launchd_keys2limits[i].key, key) == 0) {
ed34e3c3 3905 break;
5b0a4722 3906 }
ed34e3c3
A
3907 }
3908
5b0a4722 3909 if (i == limits_cnt) {
ed34e3c3 3910 return;
5b0a4722 3911 }
ed34e3c3
A
3912
3913 limititem_update(j, launchd_keys2limits[i].val, rl);
3914}
3915
3916bool
5b0a4722 3917job_useless(job_t j)
ed34e3c3 3918{
5b0a4722
A
3919 /* Yes, j->unload_at_exit and j->only_once seem the same, but they'll differ someday... */
3920
3921 if ((j->unload_at_exit || j->only_once) && j->start_time != 0) {
3922 if (j->unload_at_exit && j->j_port) {
3923 return false;
3924 }
ed34e3c3
A
3925 job_log(j, LOG_INFO, "Exited. Was only configured to run once.");
3926 return true;
5b0a4722
A
3927 } else if (j->removal_pending) {
3928 job_log(j, LOG_DEBUG, "Exited while removal was pending.");
ed34e3c3 3929 return true;
f36da725 3930 } else if (j->mgr->shutting_down && (j->hopefully_exits_first || j->mgr->hopefully_first_cnt == 0)) {
5b0a4722 3931 job_log(j, LOG_DEBUG, "Exited while shutdown in progress. Processes remaining: %lu/%lu", total_children, total_anon_children);
ed34e3c3 3932 return true;
5b0a4722
A
3933 } else if (j->legacy_mach_job) {
3934 if (SLIST_EMPTY(&j->machservices)) {
3935 job_log(j, LOG_INFO, "Garbage collecting");
3936 return true;
3937 } else if (!j->checkedin) {
3938 job_log(j, LOG_WARNING, "Failed to check-in!");
3939 return true;
3940 }
ed34e3c3
A
3941 }
3942
3943 return false;
3944}
3945
3946bool
5b0a4722 3947job_keepalive(job_t j)
ed34e3c3
A
3948{
3949 mach_msg_type_number_t statusCnt;
3950 mach_port_status_t status;
3951 struct semaphoreitem *si;
3952 struct machservice *ms;
3953 struct stat sb;
3954 bool good_exit = (WIFEXITED(j->last_exit_status) && WEXITSTATUS(j->last_exit_status) == 0);
ed34e3c3 3955
f36da725
A
3956 if (j->mgr->shutting_down) {
3957 return false;
3958 }
3959
5b0a4722
A
3960 /*
3961 * 5066316
3962 *
3963 * We definitely need to revisit this after Leopard ships. Please see
3964 * launchctl.c for the other half of this hack.
3965 */
3966 if (j->mgr->global_on_demand_cnt > 0 && strcmp(j->label, "com.apple.kextd") != 0) {
3967 return false;
3968 }
3969
3970 if (j->start_pending) {
3971 job_log(j, LOG_DEBUG, "KeepAlive check: Pent-up non-IPC launch criteria.");
ed34e3c3
A
3972 return true;
3973 }
3974
3975 if (!j->ondemand) {
3976 job_log(j, LOG_DEBUG, "KeepAlive check: job configured to run continuously.");
3977 return true;
3978 }
3979
3980 SLIST_FOREACH(ms, &j->machservices, sle) {
3981 statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT;
3982 if (mach_port_get_attributes(mach_task_self(), ms->port, MACH_PORT_RECEIVE_STATUS,
5b0a4722 3983 (mach_port_info_t)&status, &statusCnt) != KERN_SUCCESS) {
ed34e3c3 3984 continue;
5b0a4722 3985 }
ed34e3c3
A
3986 if (status.mps_msgcount) {
3987 job_log(j, LOG_DEBUG, "KeepAlive check: job restarted due to %d queued Mach messages on service: %s",
3988 status.mps_msgcount, ms->name);
3989 return true;
3990 }
3991 }
3992
3993
3994 SLIST_FOREACH(si, &j->semaphores, sle) {
3995 bool wanted_state = false;
5b0a4722
A
3996 int qdir_file_cnt;
3997 job_t other_j;
3998
ed34e3c3
A
3999 switch (si->why) {
4000 case NETWORK_UP:
4001 wanted_state = true;
4002 case NETWORK_DOWN:
4003 if (network_up == wanted_state) {
5b0a4722 4004 job_log(j, LOG_DEBUG, "KeepAlive: The network is %s.", wanted_state ? "up" : "down");
ed34e3c3
A
4005 return true;
4006 }
4007 break;
4008 case SUCCESSFUL_EXIT:
4009 wanted_state = true;
4010 case FAILED_EXIT:
4011 if (good_exit == wanted_state) {
5b0a4722
A
4012 job_log(j, LOG_DEBUG, "KeepAlive: The exit state was %s.", wanted_state ? "successful" : "failure");
4013 return true;
4014 }
4015 break;
4016 case OTHER_JOB_ENABLED:
4017 wanted_state = true;
4018 case OTHER_JOB_DISABLED:
4019 if ((bool)job_find(si->what) == wanted_state) {
4020 job_log(j, LOG_DEBUG, "KeepAlive: The following job is %s: %s", wanted_state ? "enabled" : "disabled", si->what);
ed34e3c3
A
4021 return true;
4022 }
4023 break;
5b0a4722
A
4024 case OTHER_JOB_ACTIVE:
4025 wanted_state = true;
4026 case OTHER_JOB_INACTIVE:
4027 if ((other_j = job_find(si->what))) {
4028 if ((bool)other_j->p == wanted_state) {
4029 job_log(j, LOG_DEBUG, "KeepAlive: The following job is %s: %s", wanted_state ? "active" : "inactive", si->what);
4030 return true;
4031 }
4032 }
4033 break;
ed34e3c3
A
4034 case PATH_EXISTS:
4035 wanted_state = true;
4036 case PATH_MISSING:
4037 if ((bool)(stat(si->what, &sb) == 0) == wanted_state) {
5b0a4722
A
4038 if (si->fd != -1) {
4039 job_assumes(j, runtime_close(si->fd) == 0);
4040 si->fd = -1;
4041 }
4042 job_log(j, LOG_DEBUG, "KeepAlive: The following path %s: %s", wanted_state ? "exists" : "is missing", si->what);
4043 return true;
4044 }
4045 break;
4046 case PATH_CHANGES:
4047 break;
4048 case DIR_NOT_EMPTY:
4049 if (-1 == (qdir_file_cnt = dir_has_files(j, si->what))) {
4050 job_log_error(j, LOG_ERR, "Failed to count the number of files in \"%s\"", si->what);
4051 } else if (qdir_file_cnt > 0) {
4052 job_log(j, LOG_DEBUG, "KeepAlive: Directory is not empty: %s", si->what);
ed34e3c3
A
4053 return true;
4054 }
ed34e3c3
A
4055 break;
4056 }
4057 }
4058
ed34e3c3
A
4059 return false;
4060}
4061
4062const char *
5b0a4722 4063job_prog(job_t j)
ed34e3c3
A
4064{
4065 if (j->prog) {
4066 return j->prog;
4067 } else if (j->argv) {
4068 return j->argv[0];
4069 } else {
4070 return "";
4071 }
4072}
4073
5b0a4722
A
4074const char *
4075job_active(job_t j)
ed34e3c3
A
4076{
4077 struct machservice *ms;
4078
5b0a4722
A
4079 if (j->p) {
4080 return "PID is still valid";
4081 }
ed34e3c3 4082
5b0a4722
A
4083 if (j->mgr->shutting_down && j->log_redirect_fd) {
4084 job_assumes(j, runtime_close(j->log_redirect_fd) != -1);
4085 j->log_redirect_fd = 0;
4086 }
ed34e3c3 4087
5b0a4722
A
4088 if (j->log_redirect_fd) {
4089 if (job_assumes(j, j->wait4pipe_eof)) {
4090 return "Standard out/error is still valid";
4091 } else {
4092 job_assumes(j, runtime_close(j->log_redirect_fd) != -1);
4093 j->log_redirect_fd = 0;
ed34e3c3 4094 }
5b0a4722
A
4095 }
4096
4097 if (j->priv_port_has_senders) {
4098 return "Privileged Port still has outstanding senders";
ed34e3c3
A
4099 }
4100
4101 SLIST_FOREACH(ms, &j->machservices, sle) {
5b0a4722
A
4102 if (ms->recv && ms->isActive) {
4103 return "Mach service is still active";
4104 }
ed34e3c3
A
4105 }
4106
5b0a4722 4107 return NULL;
ed34e3c3
A
4108}
4109
5b0a4722
A
4110void
4111machservice_watch(job_t j, struct machservice *ms)
ed34e3c3 4112{
5b0a4722
A
4113 if (ms->recv) {
4114 job_assumes(j, runtime_add_mport(ms->port, NULL, 0) == KERN_SUCCESS);
4115 }
ed34e3c3
A
4116}
4117
5b0a4722
A
4118void
4119machservice_ignore(job_t j, struct machservice *ms)
ed34e3c3 4120{
5b0a4722 4121 job_assumes(j, runtime_remove_mport(ms->port) == KERN_SUCCESS);
ed34e3c3
A
4122}
4123
4124void
5b0a4722 4125machservice_resetport(job_t j, struct machservice *ms)
ed34e3c3 4126{
5b0a4722
A
4127 LIST_REMOVE(ms, port_hash_sle);
4128 job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS);
4129 job_assumes(j, launchd_mport_deallocate(ms->port) == KERN_SUCCESS);
4130 ms->gen_num++;
4131 job_assumes(j, launchd_mport_create_recv(&ms->port) == KERN_SUCCESS);
4132 job_assumes(j, launchd_mport_make_send(ms->port) == KERN_SUCCESS);
4133 LIST_INSERT_HEAD(&port_hash[HASH_PORT(ms->port)], ms, port_hash_sle);
ed34e3c3
A
4134}
4135
4136struct machservice *
5b0a4722 4137machservice_new(job_t j, const char *name, mach_port_t *serviceport, bool pid_local)
ed34e3c3
A
4138{
4139 struct machservice *ms;
4140
5b0a4722 4141 if ((ms = calloc(1, sizeof(struct machservice) + strlen(name) + 1)) == NULL) {
ed34e3c3 4142 return NULL;
5b0a4722 4143 }
ed34e3c3 4144
5b0a4722 4145 strcpy((char *)ms->name, name);
ed34e3c3 4146 ms->job = j;
5b0a4722 4147 ms->per_pid = pid_local;
ed34e3c3
A
4148
4149 if (*serviceport == MACH_PORT_NULL) {
5b0a4722 4150 if (!job_assumes(j, launchd_mport_create_recv(&ms->port) == KERN_SUCCESS)) {
ed34e3c3 4151 goto out_bad;
5b0a4722 4152 }
ed34e3c3 4153
5b0a4722 4154 if (!job_assumes(j, launchd_mport_make_send(ms->port) == KERN_SUCCESS)) {
ed34e3c3 4155 goto out_bad2;
5b0a4722 4156 }
ed34e3c3 4157 *serviceport = ms->port;
ed34e3c3
A
4158 ms->recv = true;
4159 } else {
4160 ms->port = *serviceport;
4161 ms->isActive = true;
4162 }
4163
4164 SLIST_INSERT_HEAD(&j->machservices, ms, sle);
5b0a4722
A
4165 LIST_INSERT_HEAD(&j->mgr->ms_hash[hash_ms(ms->name)], ms, name_hash_sle);
4166 LIST_INSERT_HEAD(&port_hash[HASH_PORT(ms->port)], ms, port_hash_sle);
ed34e3c3
A
4167
4168 job_log(j, LOG_INFO, "Mach service added: %s", name);
4169
4170 return ms;
4171out_bad2:
5b0a4722 4172 job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS);
ed34e3c3
A
4173out_bad:
4174 free(ms);
4175 return NULL;
4176}
4177
4178bootstrap_status_t
4179machservice_status(struct machservice *ms)
4180{
4181 if (ms->isActive) {
4182 return BOOTSTRAP_STATUS_ACTIVE;
4183 } else if (ms->job->ondemand) {
4184 return BOOTSTRAP_STATUS_ON_DEMAND;
4185 } else {
4186 return BOOTSTRAP_STATUS_INACTIVE;
4187 }
4188}
4189
4190void
5b0a4722 4191job_setup_exception_port(job_t j, task_t target_task)
ed34e3c3 4192{
fe044cc9 4193 struct machservice *ms;
ed34e3c3 4194 thread_state_flavor_t f = 0;
fe044cc9 4195 mach_port_t exc_port = the_exception_server;
5b0a4722 4196
fe044cc9
A
4197 if (j->alt_exc_handler) {
4198 ms = jobmgr_lookup_service(j->mgr, j->alt_exc_handler, true, 0);
4199 if (ms) {
4200 exc_port = machservice_port(ms);
4201 } else {
4202 job_log(j, LOG_WARNING, "Falling back to default Mach exception handler. Could not find: %s", j->alt_exc_handler);
4203 }
4204 } else if (j->internal_exc_handler) {
4205 exc_port = runtime_get_kernel_port();
4206 } else if (!exc_port) {
5b0a4722
A
4207 return;
4208 }
ed34e3c3 4209
ef398931 4210#if defined (__ppc__) || defined (__ppc64__)
ed34e3c3 4211 f = PPC_THREAD_STATE64;
ef398931 4212#elif defined(__i386__) || defined(__x86_64__)
ed34e3c3 4213 f = x86_THREAD_STATE;
f36da725
A
4214#elif defined(__arm__)
4215 f = ARM_THREAD_STATE;
4216#else
4217#error "unknown architecture"
ed34e3c3
A
4218#endif
4219
5b0a4722 4220 if (target_task) {
fe044cc9 4221 job_assumes(j, task_set_exception_ports(target_task, EXC_MASK_CRASH, exc_port,
5b0a4722 4222 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f) == KERN_SUCCESS);
fe044cc9 4223 } else if (getpid() == 1 && the_exception_server) {
5b0a4722
A
4224 mach_port_t mhp = mach_host_self();
4225 job_assumes(j, host_set_exception_ports(mhp, EXC_MASK_CRASH, the_exception_server,
4226 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f) == KERN_SUCCESS);
4227 job_assumes(j, launchd_mport_deallocate(mhp) == KERN_SUCCESS);
4228 }
4229
4230}
4231
4232void
4233job_set_exeception_port(job_t j, mach_port_t port)
4234{
4235 if (!the_exception_server) {
4236 the_exception_server = port;
4237 job_setup_exception_port(j, 0);
4238 } else {
4239 job_log(j, LOG_WARNING, "The exception server is already claimed!");
4240 }
4241}
4242
4243void
4244machservice_setup_options(launch_data_t obj, const char *key, void *context)
4245{
4246 struct machservice *ms = context;
4247 mach_port_t mhp = mach_host_self();
4248 int which_port;
4249 bool b;
4250
4251 if (!job_assumes(ms->job, mhp != MACH_PORT_NULL)) {
ed34e3c3
A
4252 return;
4253 }
4254
4255 switch (launch_data_get_type(obj)) {
4256 case LAUNCH_DATA_INTEGER:
4257 which_port = launch_data_get_integer(obj);
4258 if (strcasecmp(key, LAUNCH_JOBKEY_MACH_TASKSPECIALPORT) == 0) {
5b0a4722
A
4259 switch (which_port) {
4260 case TASK_KERNEL_PORT:
4261 case TASK_HOST_PORT:
4262 case TASK_NAME_PORT:
4263 case TASK_BOOTSTRAP_PORT:
4264 /* I find it a little odd that zero isn't reserved in the header */
4265 case 0:
4266 job_log(ms->job, LOG_WARNING, "Tried to set a reserved task special port: %d", which_port);
4267 break;
4268 default:
4269 ms->special_port_num = which_port;
4270 SLIST_INSERT_HEAD(&special_ports, ms, special_port_sle);
4271 break;
4272 }
ed34e3c3 4273 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HOSTSPECIALPORT) == 0 && getpid() == 1) {
5b0a4722
A
4274 if (which_port > HOST_MAX_SPECIAL_KERNEL_PORT) {
4275 job_assumes(ms->job, (errno = host_set_special_port(mhp, which_port, ms->port)) == KERN_SUCCESS);
4276 } else {
4277 job_log(ms->job, LOG_WARNING, "Tried to set a reserved host special port: %d", which_port);
4278 }
ed34e3c3
A
4279 }
4280 case LAUNCH_DATA_BOOL:
4281 b = launch_data_get_bool(obj);
5b0a4722
A
4282 if (strcasecmp(key, LAUNCH_JOBKEY_MACH_ENTERKERNELDEBUGGERONCLOSE) == 0) {
4283 ms->debug_on_close = b;
4284 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_RESETATCLOSE) == 0) {
ed34e3c3
A
4285 ms->reset = b;
4286 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN) == 0) {
4287 ms->hide = b;
4288 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_EXCEPTIONSERVER) == 0) {
5b0a4722 4289 job_set_exeception_port(ms->job, ms->port);
ed34e3c3
A
4290 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_KUNCSERVER) == 0) {
4291 ms->kUNCServer = b;
5b0a4722 4292 job_assumes(ms->job, host_set_UNDServer(mhp, ms->port) == KERN_SUCCESS);
ed34e3c3
A
4293 }
4294 break;
5b0a4722
A
4295 case LAUNCH_DATA_DICTIONARY:
4296 job_set_exeception_port(ms->job, ms->port);
4297 break;
ed34e3c3
A
4298 default:
4299 break;
4300 }
4301
5b0a4722 4302 job_assumes(ms->job, launchd_mport_deallocate(mhp) == KERN_SUCCESS);
ed34e3c3
A
4303}
4304
4305void
4306machservice_setup(launch_data_t obj, const char *key, void *context)
4307{
5b0a4722 4308 job_t j = context;
ed34e3c3
A
4309 struct machservice *ms;
4310 mach_port_t p = MACH_PORT_NULL;
4311
5b0a4722 4312 if ((ms = jobmgr_lookup_service(j->mgr, key, false, 0))) {
ed34e3c3
A
4313 job_log(j, LOG_WARNING, "Conflict with job: %s over Mach service: %s", ms->job->label, key);
4314 return;
4315 }
4316
5b0a4722 4317 if ((ms = machservice_new(j, key, &p, false)) == NULL) {
ed34e3c3
A
4318 job_log_error(j, LOG_WARNING, "Cannot add service: %s", key);
4319 return;
4320 }
4321
4322 ms->isActive = false;
4323
4324 if (launch_data_get_type(obj) == LAUNCH_DATA_DICTIONARY) {
4325 launch_data_dict_iterate(obj, machservice_setup_options, ms);
4326 }
4327}
4328
5b0a4722
A
4329jobmgr_t
4330jobmgr_do_garbage_collection(jobmgr_t jm)
ed34e3c3 4331{
5b0a4722
A
4332 jobmgr_t jmi, jmn;
4333 job_t ji, jn;
ed34e3c3 4334
5b0a4722
A
4335 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
4336 jobmgr_do_garbage_collection(jmi);
4337 }
ed34e3c3 4338
5b0a4722
A
4339 if (!jm->shutting_down) {
4340 return jm;
4341 }
4342
4343 jobmgr_log(jm, LOG_DEBUG, "Garbage collecting.");
4344
4345 /*
4346 * Normally, we wait for all resources of a job (Unix PIDs/FDs and Mach ports)
4347 * to reset before we conider the job truly dead and ready to be spawned again.
4348 *
4349 * In order to work around 5487724 and 3456090, we're going to call reboot()
4350 * when the last PID dies and not wait for the associated resources to reset.
4351 */
4352 if (getpid() == 1 && jm->parentmgr == NULL && total_children == 0) {
4353 jobmgr_log(jm, LOG_DEBUG, "About to force a call to: reboot(%s)", reboot_flags_to_C_names(jm->reboot_flags));
4354 runtime_closelog();
4355 jobmgr_assumes(jm, reboot(jm->reboot_flags) != -1);
4356 }
4357
4358 if (jm->hopefully_first_cnt) {
4359 return jm;
4360 }
4361
4362 if (jm->parentmgr && jm->parentmgr->shutting_down && jm->parentmgr->hopefully_first_cnt) {
4363 return jm;
4364 }
4365
4366 if (!jm->sent_stop_to_normal_jobs) {
4367 jobmgr_log(jm, LOG_DEBUG, "Asking \"normal\" jobs to exit.");
4368
4369 LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
4370 if (!job_active(ji)) {
4371 job_remove(ji);
4372 } else if (!ji->hopefully_exits_last) {
4373 job_stop(ji);
4374 }
4375 }
4376
4377 jm->sent_stop_to_normal_jobs = true;
ed34e3c3 4378 }
5b0a4722
A
4379
4380 if (jm->normal_active_cnt) {
4381 return jm;
4382 }
4383
4384 if (!jm->sent_stop_to_hopefully_last_jobs) {
4385 jobmgr_log(jm, LOG_DEBUG, "Asking \"hopefully last\" jobs to exit.");
4386
4387 LIST_FOREACH(ji, &jm->jobs, sle) {
4388 if (ji->p && ji->anonymous) {
4389 continue;
4390 } else if (ji->p && job_assumes(ji, ji->hopefully_exits_last)) {
4391 job_stop(ji);
4392 }
4393 }
4394
4395 jm->sent_stop_to_hopefully_last_jobs = true;
4396 }
4397
4398 if (!SLIST_EMPTY(&jm->submgrs)) {
4399 return jm;
4400 }
4401
4402 LIST_FOREACH(ji, &jm->jobs, sle) {
4403 if (!ji->anonymous) {
4404 return jm;
4405 }
4406 }
4407
4408 jobmgr_log_stray_children(jm);
4409 jobmgr_remove(jm);
4410 return NULL;
ed34e3c3
A
4411}
4412
4413void
5b0a4722 4414jobmgr_log_stray_children(jobmgr_t jm)
ed34e3c3 4415{
5b0a4722 4416 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
f36da725 4417 size_t i, kp_cnt, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
5b0a4722 4418 struct kinfo_proc *kp;
ed34e3c3 4419
f36da725
A
4420#if TARGET_OS_EMBEDDED
4421 if (!do_apple_internal_magic) {
4422 return;
4423 }
4424#endif
5b0a4722
A
4425 if (jm->parentmgr || getpid() != 1) {
4426 return;
4427 }
ed34e3c3 4428
5b0a4722
A
4429 if (!jobmgr_assumes(jm, (kp = malloc(len)) != NULL)) {
4430 return;
4431 }
4432 if (!jobmgr_assumes(jm, sysctl(mib, 3, kp, &len, NULL, 0) != -1)) {
4433 goto out;
4434 }
4435
4436 kp_cnt = len / sizeof(struct kinfo_proc);
ed34e3c3 4437
5b0a4722
A
4438 for (i = 0; i < kp_cnt; i++) {
4439 pid_t p_i = kp[i].kp_proc.p_pid;
4440 pid_t pp_i = kp[i].kp_eproc.e_ppid;
4441 pid_t pg_i = kp[i].kp_eproc.e_pgid;
4442 const char *z = (kp[i].kp_proc.p_stat == SZOMB) ? "zombie " : "";
4443 const char *n = kp[i].kp_proc.p_comm;
4444
4445 if (p_i == 0 || p_i == 1) {
4446 continue;
ed34e3c3 4447 }
5b0a4722
A
4448
4449 jobmgr_log(jm, LOG_WARNING, "Stray %sprocess at shutdown: PID %u PPID %u PGID %u %s", z, p_i, pp_i, pg_i, n);
4450
4451 /*
4452 * The kernel team requested that launchd not do this for Leopard.
4453 * jobmgr_assumes(jm, runtime_kill(p_i, SIGKILL) != -1);
4454 */
ed34e3c3
A
4455 }
4456
5b0a4722
A
4457out:
4458 free(kp);
ed34e3c3
A
4459}
4460
5b0a4722
A
4461jobmgr_t
4462jobmgr_parent(jobmgr_t jm)
ed34e3c3 4463{
5b0a4722
A
4464 return jm->parentmgr;
4465}
ed34e3c3 4466
5b0a4722
A
4467void
4468job_uncork_fork(job_t j)
4469{
4470 pid_t c = j->p;
4471
4472 job_log(j, LOG_DEBUG, "Uncorking the fork().");
4473 /* this unblocks the child and avoids a race
4474 * between the above fork() and the kevent_mod() */
4475 job_assumes(j, write(j->forkfd, &c, sizeof(c)) == sizeof(c));
4476 job_assumes(j, runtime_close(j->forkfd) != -1);
4477 j->forkfd = 0;
4478}
4479
4480jobmgr_t
4481jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name)
4482{
4483 mach_msg_size_t mxmsgsz;
4484 job_t bootstrapper = NULL;
4485 jobmgr_t jmr;
4486
4487 launchd_assert(offsetof(struct jobmgr_s, kqjobmgr_callback) == 0);
4488
4489 if (jm && requestorport == MACH_PORT_NULL) {
4490 jobmgr_log(jm, LOG_ERR, "Mach sub-bootstrap create request requires a requester port");
ed34e3c3
A
4491 return NULL;
4492 }
4493
5b0a4722 4494 jmr = calloc(1, sizeof(struct jobmgr_s) + (name ? (strlen(name) + 1) : 128));
ed34e3c3 4495
5b0a4722 4496 if (jmr == NULL) {
ed34e3c3 4497 return NULL;
5b0a4722 4498 }
ed34e3c3 4499
5b0a4722 4500 jmr->kqjobmgr_callback = jobmgr_callback;
f36da725 4501 strcpy(jmr->name_init, name ? name : "Under construction");
5b0a4722
A
4502
4503 jmr->req_port = requestorport;
4504
4505 if ((jmr->parentmgr = jm)) {
4506 SLIST_INSERT_HEAD(&jm->submgrs, jmr, sle);
4507 }
4508
4509 if (jm && !jobmgr_assumes(jmr, launchd_mport_notify_req(jmr->req_port, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS)) {
ed34e3c3
A
4510 goto out_bad;
4511 }
4512
5b0a4722
A
4513 if (transfer_port != MACH_PORT_NULL) {
4514 jobmgr_assumes(jmr, jm != NULL);
4515 jmr->jm_port = transfer_port;
4516 } else if (!jm && getpid() != 1) {
4517 char *trusted_fd = getenv(LAUNCHD_TRUSTED_FD_ENV);
4518 name_t service_buf;
4519
4520 snprintf(service_buf, sizeof(service_buf), "com.apple.launchd.peruser.%u", getuid());
4521
4522 if (!jobmgr_assumes(jmr, bootstrap_check_in(bootstrap_port, service_buf, &jmr->jm_port) == 0)) {
4523 goto out_bad;
4524 }
4525
4526 if (trusted_fd) {
4527 int dfd, lfd = strtol(trusted_fd, NULL, 10);
4528
4529 if ((dfd = dup(lfd)) >= 0) {
4530 jobmgr_assumes(jmr, runtime_close(dfd) != -1);
4531 jobmgr_assumes(jmr, runtime_close(lfd) != -1);
4532 }
4533
4534 unsetenv(LAUNCHD_TRUSTED_FD_ENV);
4535 }
4536
4537 /* cut off the Libc cache, we don't want to deadlock against ourself */
4538 inherited_bootstrap_port = bootstrap_port;
4539 bootstrap_port = MACH_PORT_NULL;
4540 launchd_assert(launchd_mport_notify_req(inherited_bootstrap_port, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS);
ed34e3c3 4541
5b0a4722
A
4542 /* We set this explicitly as we start each child */
4543 launchd_assert(launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS);
4544 } else if (!jobmgr_assumes(jmr, launchd_mport_create_recv(&jmr->jm_port) == KERN_SUCCESS)) {
ed34e3c3 4545 goto out_bad;
5b0a4722 4546 }
ed34e3c3 4547
5b0a4722 4548 if (!name) {
f36da725 4549 sprintf(jmr->name_init, "%u", MACH_PORT_INDEX(jmr->jm_port));
ed34e3c3
A
4550 }
4551
5b0a4722
A
4552 /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */
4553 mxmsgsz = sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem);
4554 if (job_mig_protocol_vproc_subsystem.maxsize > mxmsgsz) {
4555 mxmsgsz = job_mig_protocol_vproc_subsystem.maxsize;
4556 }
4557
4558 if (!jm) {
4559 jobmgr_assumes(jmr, kevent_mod(SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr) != -1);
4560 jobmgr_assumes(jmr, kevent_mod(SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr) != -1);
4561 jobmgr_assumes(jmr, kevent_mod(0, EVFILT_FS, EV_ADD, VQ_MOUNT|VQ_UNMOUNT|VQ_UPDATE, 0, jmr) != -1);
4562 }
4563
4564 if (name) {
4565 bootstrapper = jobmgr_init_session(jmr, name, sflag);
4566 }
4567
4568 if (!bootstrapper || !bootstrapper->weird_bootstrap) {
4569 if (!jobmgr_assumes(jmr, runtime_add_mport(jmr->jm_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) {
4570 goto out_bad;
4571 }
4572 }
4573
4574 jobmgr_log(jmr, LOG_DEBUG, "Created job manager%s%s", jm ? " with parent: " : ".", jm ? jm->name : "");
4575
4576 if (bootstrapper) {
4577 jobmgr_assumes(jmr, job_dispatch(bootstrapper, true) != NULL);
4578 }
4579
4580 if (jmr->parentmgr) {
4581 runtime_add_ref();
4582 }
4583
4584 return jmr;
ed34e3c3
A
4585
4586out_bad:
5b0a4722
A
4587 if (jmr) {
4588 jobmgr_remove(jmr);
4589 }
ed34e3c3
A
4590 return NULL;
4591}
4592
5b0a4722
A
4593job_t
4594jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag)
4595{
4596 const char *bootstrap_tool[] = { "/bin/launchctl", "bootstrap", "-S", session_type, sflag ? "-s" : NULL, NULL };
4597 char thelabel[1000];
4598 job_t bootstrapper;
4599
4600 snprintf(thelabel, sizeof(thelabel), "com.apple.launchctl.%s", session_type);
4601 bootstrapper = job_new(jm, thelabel, NULL, bootstrap_tool);
4602 if (jobmgr_assumes(jm, bootstrapper != NULL) && (jm->parentmgr || getuid())) {
4603 char buf[100];
4604
4605 /* <rdar://problem/5042202> launchd-201: can't ssh in with AFP OD account (hangs) */
4606 snprintf(buf, sizeof(buf), "0x%X:0:0", getuid());
4607 envitem_new(bootstrapper, "__CF_USER_TEXT_ENCODING", buf, false);
4608 bootstrapper->weird_bootstrap = true;
4609 jobmgr_assumes(jm, job_setup_machport(bootstrapper));
4610 }
4611
4612 jm->session_initialized = true;
4613
4614 return bootstrapper;
4615}
4616
4617jobmgr_t
4618jobmgr_delete_anything_with_port(jobmgr_t jm, mach_port_t port)
ed34e3c3
A
4619{
4620 struct machservice *ms, *next_ms;
5b0a4722 4621 jobmgr_t jmi, jmn;
ed34e3c3
A
4622
4623 /* Mach ports, unlike Unix descriptors, are reference counted. In other
4624 * words, when some program hands us a second or subsequent send right
4625 * to a port we already have open, the Mach kernel gives us the same
4626 * port number back and increments an reference count associated with
4627 * the port. This forces us, when discovering that a receive right at
4628 * the other end has been deleted, to wander all of our objects to see
4629 * what weird places clients might have handed us the same send right
4630 * to use.
4631 */
4632
5b0a4722
A
4633 if (jm == root_jobmgr) {
4634 if (port == inherited_bootstrap_port) {
4635 launchd_assumes(launchd_mport_deallocate(port) == KERN_SUCCESS);
4636 inherited_bootstrap_port = MACH_PORT_NULL;
ed34e3c3 4637
5b0a4722
A
4638 return jobmgr_shutdown(jm);
4639 }
4640
4641 LIST_FOREACH_SAFE(ms, &port_hash[HASH_PORT(port)], port_hash_sle, next_ms) {
4642 if (ms->port == port) {
4643 machservice_delete(ms->job, ms, true);
4644 }
4645 }
4646 }
4647
4648 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
4649 jobmgr_delete_anything_with_port(jmi, port);
4650 }
ed34e3c3 4651
5b0a4722
A
4652 if (jm->req_port == port) {
4653 jobmgr_log(jm, LOG_DEBUG, "Request port died: 0x%x", port);
4654 return jobmgr_shutdown(jm);
ed34e3c3 4655 }
5b0a4722
A
4656
4657 return jm;
ed34e3c3
A
4658}
4659
4660struct machservice *
5b0a4722 4661jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent, pid_t target_pid)
ed34e3c3
A
4662{
4663 struct machservice *ms;
ed34e3c3 4664
5b0a4722
A
4665 if (target_pid) {
4666 jobmgr_assumes(jm, !check_parent);
4667 }
ed34e3c3 4668
5b0a4722
A
4669 LIST_FOREACH(ms, &jm->ms_hash[hash_ms(name)], name_hash_sle) {
4670 if ((target_pid && ms->per_pid && ms->job->p == target_pid) || (!target_pid && !ms->per_pid)) {
4671 if (strcmp(name, ms->name) == 0) {
ed34e3c3 4672 return ms;
5b0a4722 4673 }
ed34e3c3
A
4674 }
4675 }
4676
5b0a4722 4677 if (jm->parentmgr == NULL) {
ed34e3c3 4678 return NULL;
5b0a4722 4679 }
ed34e3c3 4680
5b0a4722 4681 if (!check_parent) {
ed34e3c3 4682 return NULL;
5b0a4722 4683 }
ed34e3c3 4684
5b0a4722 4685 return jobmgr_lookup_service(jm->parentmgr, name, true, 0);
ed34e3c3
A
4686}
4687
4688mach_port_t
4689machservice_port(struct machservice *ms)
4690{
4691 return ms->port;
4692}
4693
5b0a4722 4694job_t
ed34e3c3
A
4695machservice_job(struct machservice *ms)
4696{
4697 return ms->job;
4698}
4699
4700bool
4701machservice_hidden(struct machservice *ms)
4702{
4703 return ms->hide;
4704}
4705
4706bool
4707machservice_active(struct machservice *ms)
4708{
4709 return ms->isActive;
4710}
4711
4712const char *
4713machservice_name(struct machservice *ms)
4714{
4715 return ms->name;
4716}
4717
4718void
5b0a4722 4719machservice_delete(job_t j, struct machservice *ms, bool port_died)
ed34e3c3 4720{
5b0a4722
A
4721 if (ms->debug_on_close) {
4722 job_log(j, LOG_NOTICE, "About to enter kernel debugger because of Mach port: 0x%x", ms->port);
4723 job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS);
4724 }
4725
4726 if (ms->recv && job_assumes(j, !ms->isActive)) {
4727 job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS);
4728 }
4729
4730 job_assumes(j, launchd_mport_deallocate(ms->port) == KERN_SUCCESS);
4731
4732 if (ms->port == the_exception_server) {
4733 the_exception_server = 0;
ed34e3c3
A
4734 }
4735
5b0a4722 4736 job_log(j, LOG_INFO, "Mach service deleted%s: %s", port_died ? " (port died)" : "", ms->name);
ed34e3c3 4737
5b0a4722
A
4738 if (ms->special_port_num) {
4739 SLIST_REMOVE(&special_ports, ms, machservice, special_port_sle);
4740 }
ed34e3c3 4741
5b0a4722
A
4742 SLIST_REMOVE(&j->machservices, ms, machservice, sle);
4743 LIST_REMOVE(ms, name_hash_sle);
4744 LIST_REMOVE(ms, port_hash_sle);
ed34e3c3
A
4745
4746 free(ms);
4747}
4748
4749void
5b0a4722 4750machservice_request_notifications(struct machservice *ms)
ed34e3c3
A
4751{
4752 mach_msg_id_t which = MACH_NOTIFY_DEAD_NAME;
4753
4754 ms->isActive = true;
4755
5b0a4722 4756 if (ms->recv) {
ed34e3c3
A
4757 which = MACH_NOTIFY_PORT_DESTROYED;
4758 job_checkin(ms->job);
4759 }
4760
5b0a4722 4761 job_assumes(ms->job, launchd_mport_notify_req(ms->port, which) == KERN_SUCCESS);
ed34e3c3
A
4762}
4763
5b0a4722
A
4764#define NELEM(x) (sizeof(x)/sizeof(x[0]))
4765#define END_OF(x) (&(x)[NELEM(x)])
ed34e3c3
A
4766
4767char **
4768mach_cmd2argv(const char *string)
4769{
4770 char *argv[100], args[1000];
4771 const char *cp;
4772 char *argp = args, term, **argv_ret, *co;
4773 unsigned int nargs = 0, i;
4774
4775 for (cp = string; *cp;) {
4776 while (isspace(*cp))
4777 cp++;
4778 term = (*cp == '"') ? *cp++ : '\0';
5b0a4722 4779 if (nargs < NELEM(argv)) {
ed34e3c3 4780 argv[nargs++] = argp;
5b0a4722 4781 }
ed34e3c3 4782 while (*cp && (term ? *cp != term : !isspace(*cp)) && argp < END_OF(args)) {
5b0a4722 4783 if (*cp == '\\') {
ed34e3c3 4784 cp++;
5b0a4722 4785 }
ed34e3c3 4786 *argp++ = *cp;
5b0a4722 4787 if (*cp) {
ed34e3c3 4788 cp++;
5b0a4722 4789 }
ed34e3c3
A
4790 }
4791 *argp++ = '\0';
4792 }
4793 argv[nargs] = NULL;
4794
5b0a4722 4795 if (nargs == 0) {
ed34e3c3 4796 return NULL;
5b0a4722 4797 }
ed34e3c3
A
4798
4799 argv_ret = malloc((nargs + 1) * sizeof(char *) + strlen(string) + 1);
4800
5b0a4722 4801 if (!launchd_assumes(argv_ret != NULL)) {
ed34e3c3 4802 return NULL;
5b0a4722 4803 }
ed34e3c3
A
4804
4805 co = (char *)argv_ret + (nargs + 1) * sizeof(char *);
4806
4807 for (i = 0; i < nargs; i++) {
4808 strcpy(co, argv[i]);
4809 argv_ret[i] = co;
4810 co += strlen(argv[i]) + 1;
4811 }
4812 argv_ret[i] = NULL;
4813
4814 return argv_ret;
4815}
4816
4817void
5b0a4722 4818job_checkin(job_t j)
ed34e3c3
A
4819{
4820 j->checkedin = true;
4821}
4822
4823bool
5b0a4722 4824job_ack_port_destruction(mach_port_t p)
ed34e3c3 4825{
ed34e3c3
A
4826 struct machservice *ms;
4827
5b0a4722
A
4828 LIST_FOREACH(ms, &port_hash[HASH_PORT(p)], port_hash_sle) {
4829 if (ms->recv && (ms->port == p)) {
ed34e3c3 4830 break;
5b0a4722 4831 }
ed34e3c3
A
4832 }
4833
5b0a4722 4834 if (!ms) {
ed34e3c3 4835 return false;
5b0a4722 4836 }
ed34e3c3
A
4837
4838 ms->isActive = false;
4839
5b0a4722
A
4840 if (ms->reset) {
4841 machservice_resetport(ms->job, ms);
4842 }
ed34e3c3 4843
5b0a4722
A
4844 job_log(ms->job, LOG_DEBUG, "Receive right returned to us: %s", ms->name);
4845 job_dispatch(ms->job, false);
ed34e3c3 4846
5b0a4722 4847 root_jobmgr = jobmgr_do_garbage_collection(root_jobmgr);
ed34e3c3
A
4848
4849 return true;
4850}
4851
4852void
5b0a4722 4853job_ack_no_senders(job_t j)
ed34e3c3
A
4854{
4855 j->priv_port_has_senders = false;
4856
5b0a4722
A
4857 job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS);
4858 j->j_port = 0;
4859
ed34e3c3
A
4860 job_log(j, LOG_DEBUG, "No more senders on privileged Mach bootstrap port");
4861
5b0a4722 4862 job_dispatch(j, false);
ed34e3c3
A
4863}
4864
5b0a4722
A
4865jobmgr_t
4866job_get_bs(job_t j)
ed34e3c3 4867{
5b0a4722
A
4868 if (job_assumes(j, j->mgr != NULL)) {
4869 return j->mgr;
4870 }
ed34e3c3 4871
5b0a4722 4872 return NULL;
ed34e3c3
A
4873}
4874
5b0a4722
A
4875bool
4876job_is_anonymous(job_t j)
ed34e3c3 4877{
5b0a4722 4878 return j->anonymous;
ed34e3c3
A
4879}
4880
5b0a4722
A
4881void
4882job_force_sampletool(job_t j)
ed34e3c3 4883{
5b0a4722
A
4884 struct stat sb;
4885 char logfile[PATH_MAX];
4886 char pidstr[100];
4887 char *sample_args[] = { "sample", pidstr, "1", "-mayDie", "-file", logfile, NULL };
4888 char *contents = NULL;
4889 int logfile_fd = -1;
4890 int console_fd = -1;
4891 int wstatus;
4892 pid_t sp;
4893
4894 if (!debug_shutdown_hangs) {
4895 return;
4896 }
4897
4898 snprintf(pidstr, sizeof(pidstr), "%u", j->p);
4899 snprintf(logfile, sizeof(logfile), SHUTDOWN_LOG_DIR "/%s-%u.sample.txt", j->label, j->p);
ed34e3c3 4900
5b0a4722
A
4901 if (!job_assumes(j, unlink(logfile) != -1 || errno == ENOENT)) {
4902 goto out;
4903 }
ed34e3c3 4904
5b0a4722
A
4905 /*
4906 * This will stall launchd for as long as the 'sample' tool runs.
4907 *
4908 * We didn't give the 'sample' tool a bootstrap port, so it therefore
4909 * can't deadlock against launchd.
4910 */
4911 if (!job_assumes(j, (errno = posix_spawnp(&sp, sample_args[0], NULL, NULL, sample_args, environ)) == 0)) {
4912 goto out;
4913 }
ed34e3c3 4914
5b0a4722
A
4915 job_log(j, LOG_DEBUG, "Waiting for 'sample' to finish.");
4916
4917 if (!job_assumes(j, waitpid(sp, &wstatus, 0) != -1)) {
4918 goto out;
4919 }
4920
4921 /*
4922 * This won't work if the VFS or filesystems are sick:
4923 * sync();
4924 */
4925
4926 if (!job_assumes(j, WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)) {
4927 goto out;
4928 }
4929
4930 if (!job_assumes(j, (logfile_fd = open(logfile, O_RDONLY|O_NOCTTY)) != -1)) {
4931 goto out;
4932 }
4933
4934 if (!job_assumes(j, (console_fd = open(_PATH_CONSOLE, O_WRONLY|O_APPEND|O_NOCTTY)) != -1)) {
4935 goto out;
4936 }
4937
4938 if (!job_assumes(j, fstat(logfile_fd, &sb) != -1)) {
4939 goto out;
4940 }
4941
4942 contents = malloc(sb.st_size);
4943
4944 if (!job_assumes(j, contents != NULL)) {
4945 goto out;
4946 }
4947
4948 if (!job_assumes(j, read(logfile_fd, contents, sb.st_size) == sb.st_size)) {
4949 goto out;
4950 }
4951
4952 job_assumes(j, write(console_fd, contents, sb.st_size) == sb.st_size);
4953
4954out:
4955 if (contents) {
4956 free(contents);
4957 }
4958
4959 if (logfile_fd != -1) {
4960 job_assumes(j, runtime_fsync(logfile_fd) != -1);
4961 job_assumes(j, runtime_close(logfile_fd) != -1);
4962 }
4963
4964 if (console_fd != -1) {
4965 job_assumes(j, runtime_close(console_fd) != -1);
4966 }
4967
4968 job_log(j, LOG_DEBUG, "Finished sampling.");
ed34e3c3
A
4969}
4970
4971bool
5b0a4722 4972semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what)
ed34e3c3
A
4973{
4974 struct semaphoreitem *si;
4975 size_t alloc_sz = sizeof(struct semaphoreitem);
4976
5b0a4722 4977 if (what) {
ed34e3c3 4978 alloc_sz += strlen(what) + 1;
5b0a4722 4979 }
ed34e3c3 4980
5b0a4722 4981 if (!job_assumes(j, si = calloc(1, alloc_sz))) {
ed34e3c3 4982 return false;
5b0a4722 4983 }
ed34e3c3 4984
5b0a4722 4985 si->fd = -1;
ed34e3c3
A
4986 si->why = why;
4987
5b0a4722 4988 if (what) {
f36da725 4989 strcpy(si->what_init, what);
5b0a4722 4990 }
ed34e3c3
A
4991
4992 SLIST_INSERT_HEAD(&j->semaphores, si, sle);
4993
5b0a4722
A
4994 semaphoreitem_runtime_mod_ref(si, true);
4995
ed34e3c3
A
4996 return true;
4997}
4998
4999void
5b0a4722
A
5000semaphoreitem_runtime_mod_ref(struct semaphoreitem *si, bool add)
5001{
5002 /*
5003 * External events need to be tracked.
5004 * Internal events do NOT need to be tracked.
5005 */
5006
5007 switch (si->why) {
5008 case SUCCESSFUL_EXIT:
5009 case FAILED_EXIT:
5010 case OTHER_JOB_ENABLED:
5011 case OTHER_JOB_DISABLED:
5012 case OTHER_JOB_ACTIVE:
5013 case OTHER_JOB_INACTIVE:
5014 return;
5015 default:
5016 break;
5017 }
5018
5019 if (add) {
5020 runtime_add_ref();
5021 } else {
5022 runtime_del_ref();
5023 }
5024}
5025
5026void
5027semaphoreitem_delete(job_t j, struct semaphoreitem *si)
ed34e3c3 5028{
5b0a4722
A
5029 semaphoreitem_runtime_mod_ref(si, false);
5030
5031 SLIST_REMOVE(&j->semaphores, si, semaphoreitem, sle);
ed34e3c3 5032
5b0a4722
A
5033 if (si->fd != -1) {
5034 job_assumes(j, runtime_close(si->fd) != -1);
5035 }
5036
5037 free(si);
ed34e3c3
A
5038}
5039
5040void
5b0a4722 5041semaphoreitem_setup_dict_iter(launch_data_t obj, const char *key, void *context)
ed34e3c3 5042{
5b0a4722 5043 struct semaphoreitem_dict_iter_context *sdic = context;
ed34e3c3
A
5044 semaphore_reason_t why;
5045
5b0a4722 5046 why = launch_data_get_bool(obj) ? sdic->why_true : sdic->why_false;
ed34e3c3 5047
5b0a4722 5048 semaphoreitem_new(sdic->j, why, key);
ed34e3c3
A
5049}
5050
5051void
5052semaphoreitem_setup(launch_data_t obj, const char *key, void *context)
5053{
5b0a4722
A
5054 struct semaphoreitem_dict_iter_context sdic = { context, 0, 0 };
5055 job_t j = context;
ed34e3c3
A
5056 semaphore_reason_t why;
5057
5b0a4722
A
5058 switch (launch_data_get_type(obj)) {
5059 case LAUNCH_DATA_BOOL:
5060 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE) == 0) {
5061 why = launch_data_get_bool(obj) ? NETWORK_UP : NETWORK_DOWN;
5062 semaphoreitem_new(j, why, NULL);
5063 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT) == 0) {
5064 why = launch_data_get_bool(obj) ? SUCCESSFUL_EXIT : FAILED_EXIT;
5065 semaphoreitem_new(j, why, NULL);
5066 j->start_pending = true;
5067 } else {
5068 job_assumes(j, false);
5069 }
5070 break;
5071 case LAUNCH_DATA_DICTIONARY:
5072 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE) == 0) {
5073 sdic.why_true = PATH_EXISTS;
5074 sdic.why_false = PATH_MISSING;
5075 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE) == 0) {
5076 sdic.why_true = OTHER_JOB_ACTIVE;
5077 sdic.why_false = OTHER_JOB_INACTIVE;
5078 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED) == 0) {
5079 sdic.why_true = OTHER_JOB_ENABLED;
5080 sdic.why_false = OTHER_JOB_DISABLED;
5081 } else {
5082 job_assumes(j, false);
5083 break;
5084 }
5085
5086 launch_data_dict_iterate(obj, semaphoreitem_setup_dict_iter, &sdic);
5087 break;
5088 default:
5089 job_assumes(j, false);
5090 break;
ed34e3c3
A
5091 }
5092}
5093
5094void
5b0a4722 5095jobmgr_dispatch_all_semaphores(jobmgr_t jm)
ed34e3c3 5096{
5b0a4722
A
5097 jobmgr_t jmi, jmn;
5098 job_t ji, jn;
ed34e3c3 5099
ed34e3c3 5100
5b0a4722
A
5101 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
5102 jobmgr_dispatch_all_semaphores(jmi);
5103 }
ed34e3c3 5104
5b0a4722
A
5105 LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
5106 if (!SLIST_EMPTY(&ji->semaphores)) {
5107 job_dispatch(ji, false);
5108 }
5109 }
ed34e3c3
A
5110}
5111
5112time_t
5113cronemu(int mon, int mday, int hour, int min)
5114{
5115 struct tm workingtm;
5116 time_t now;
5117
5118 now = time(NULL);
5119 workingtm = *localtime(&now);
5120
5121 workingtm.tm_isdst = -1;
5122 workingtm.tm_sec = 0;
5123 workingtm.tm_min++;
5124
5125 while (!cronemu_mon(&workingtm, mon, mday, hour, min)) {
5126 workingtm.tm_year++;
5127 workingtm.tm_mon = 0;
5128 workingtm.tm_mday = 1;
5129 workingtm.tm_hour = 0;
5130 workingtm.tm_min = 0;
5131 mktime(&workingtm);
5132 }
5133
5134 return mktime(&workingtm);
5135}
5136
5137time_t
5138cronemu_wday(int wday, int hour, int min)
5139{
5140 struct tm workingtm;
5141 time_t now;
5142
5143 now = time(NULL);
5144 workingtm = *localtime(&now);
5145
5146 workingtm.tm_isdst = -1;
5147 workingtm.tm_sec = 0;
5148 workingtm.tm_min++;
5149
5b0a4722 5150 if (wday == 7) {
ed34e3c3 5151 wday = 0;
5b0a4722 5152 }
ed34e3c3
A
5153
5154 while (!(workingtm.tm_wday == wday && cronemu_hour(&workingtm, hour, min))) {
5155 workingtm.tm_mday++;
5156 workingtm.tm_hour = 0;
5157 workingtm.tm_min = 0;
5158 mktime(&workingtm);
5159 }
5160
5161 return mktime(&workingtm);
5162}
5163
5164bool
5165cronemu_mon(struct tm *wtm, int mon, int mday, int hour, int min)
5166{
5167 if (mon == -1) {
5168 struct tm workingtm = *wtm;
5169 int carrytest;
5170
5171 while (!cronemu_mday(&workingtm, mday, hour, min)) {
5172 workingtm.tm_mon++;
5173 workingtm.tm_mday = 1;
5174 workingtm.tm_hour = 0;
5175 workingtm.tm_min = 0;
5176 carrytest = workingtm.tm_mon;
5177 mktime(&workingtm);
5b0a4722 5178 if (carrytest != workingtm.tm_mon) {
ed34e3c3 5179 return false;
5b0a4722 5180 }
ed34e3c3
A
5181 }
5182 *wtm = workingtm;
5183 return true;
5184 }
5185
5b0a4722 5186 if (mon < wtm->tm_mon) {
ed34e3c3 5187 return false;
5b0a4722 5188 }
ed34e3c3 5189
5b0a4722 5190 if (mon > wtm->tm_mon) {
ed34e3c3
A
5191 wtm->tm_mon = mon;
5192 wtm->tm_mday = 1;
5193 wtm->tm_hour = 0;
5194 wtm->tm_min = 0;
5195 }
5196
5197 return cronemu_mday(wtm, mday, hour, min);
5198}
5199
5200bool
5201cronemu_mday(struct tm *wtm, int mday, int hour, int min)
5202{
5203 if (mday == -1) {
5204 struct tm workingtm = *wtm;
5205 int carrytest;
5206
5207 while (!cronemu_hour(&workingtm, hour, min)) {
5208 workingtm.tm_mday++;
5209 workingtm.tm_hour = 0;
5210 workingtm.tm_min = 0;
5211 carrytest = workingtm.tm_mday;
5212 mktime(&workingtm);
5b0a4722 5213 if (carrytest != workingtm.tm_mday) {
ed34e3c3 5214 return false;
5b0a4722 5215 }
ed34e3c3
A
5216 }
5217 *wtm = workingtm;
5218 return true;
5219 }
5220
5b0a4722 5221 if (mday < wtm->tm_mday) {
ed34e3c3 5222 return false;
5b0a4722 5223 }
ed34e3c3 5224
5b0a4722 5225 if (mday > wtm->tm_mday) {
ed34e3c3
A
5226 wtm->tm_mday = mday;
5227 wtm->tm_hour = 0;
5228 wtm->tm_min = 0;
5229 }
5230
5231 return cronemu_hour(wtm, hour, min);
5232}
5233
5234bool
5235cronemu_hour(struct tm *wtm, int hour, int min)
5236{
5237 if (hour == -1) {
5238 struct tm workingtm = *wtm;
5239 int carrytest;
5240
5241 while (!cronemu_min(&workingtm, min)) {
5242 workingtm.tm_hour++;
5243 workingtm.tm_min = 0;
5244 carrytest = workingtm.tm_hour;
5245 mktime(&workingtm);
5b0a4722 5246 if (carrytest != workingtm.tm_hour) {
ed34e3c3 5247 return false;
5b0a4722 5248 }
ed34e3c3
A
5249 }
5250 *wtm = workingtm;
5251 return true;
5252 }
5253
5b0a4722 5254 if (hour < wtm->tm_hour) {
ed34e3c3 5255 return false;
5b0a4722 5256 }
ed34e3c3
A
5257
5258 if (hour > wtm->tm_hour) {
5259 wtm->tm_hour = hour;
5260 wtm->tm_min = 0;
5261 }
5262
5263 return cronemu_min(wtm, min);
5264}
5265
5266bool
5267cronemu_min(struct tm *wtm, int min)
5268{
5b0a4722 5269 if (min == -1) {
ed34e3c3 5270 return true;
5b0a4722 5271 }
ed34e3c3 5272
5b0a4722 5273 if (min < wtm->tm_min) {
ed34e3c3 5274 return false;
5b0a4722 5275 }
ed34e3c3
A
5276
5277 if (min > wtm->tm_min) {
5278 wtm->tm_min = min;
5279 }
5280
5281 return true;
5282}
5b0a4722
A
5283
5284kern_return_t
5285job_mig_create_server(job_t j, cmd_t server_cmd, uid_t server_uid, boolean_t on_demand, mach_port_t *server_portp)
5286{
5287 struct ldcred ldc;
5288 job_t js;
5289
cf0bacfd
A
5290#if TARGET_OS_EMBEDDED
5291 return BOOTSTRAP_NOT_PRIVILEGED;
5292#endif
5293
5b0a4722
A
5294 if (!launchd_assumes(j != NULL)) {
5295 return BOOTSTRAP_NO_MEMORY;
5296 }
5297
f36da725
A
5298 if (unlikely(j->deny_job_creation)) {
5299 return BOOTSTRAP_NOT_PRIVILEGED;
5300 }
5301
5b0a4722
A
5302 runtime_get_caller_creds(&ldc);
5303
5304 job_log(j, LOG_DEBUG, "Server create attempt: %s", server_cmd);
5305
5306#define LET_MERE_MORTALS_ADD_SERVERS_TO_PID1
5307 /* XXX - This code should go away once the per session launchd is integrated with the rest of the system */
5308#ifdef LET_MERE_MORTALS_ADD_SERVERS_TO_PID1
5309 if (getpid() == 1) {
5310 if (ldc.euid && server_uid && (ldc.euid != server_uid)) {
5311 job_log(j, LOG_WARNING, "Server create: \"%s\": Will run as UID %d, not UID %d as they told us to",
5312 server_cmd, ldc.euid, server_uid);
5313 server_uid = ldc.euid;
5314 }
5315 } else
5316#endif
5317 if (getuid()) {
5318 if (server_uid != getuid()) {
5319 job_log(j, LOG_WARNING, "Server create: \"%s\": As UID %d, we will not be able to switch to UID %d",
5320 server_cmd, getuid(), server_uid);
5321 }
5322 server_uid = 0; /* zero means "do nothing" */
5323 }
5324
5325 js = job_new_via_mach_init(j, server_cmd, server_uid, on_demand);
5326
5327 if (js == NULL) {
5328 return BOOTSTRAP_NO_MEMORY;
5329 }
5330
5331 *server_portp = js->j_port;
5332 return BOOTSTRAP_SUCCESS;
5333}
5334
5335kern_return_t
5336job_mig_send_signal(job_t j, mach_port_t srp, name_t targetlabel, int sig)
5337{
5338 struct ldcred ldc;
5339 job_t otherj;
5340
5341 if (!launchd_assumes(j != NULL)) {
5342 return BOOTSTRAP_NO_MEMORY;
5343 }
5344
5345 runtime_get_caller_creds(&ldc);
5346
5347 if (ldc.euid != 0 && ldc.euid != getuid()) {
5348 return BOOTSTRAP_NOT_PRIVILEGED;
5349 }
5350
5351 if (!(otherj = job_find(targetlabel))) {
5352 return BOOTSTRAP_UNKNOWN_SERVICE;
5353 }
5354
5355 if (sig == VPROC_MAGIC_UNLOAD_SIGNAL) {
5356 bool do_block = otherj->p;
5357
5358 if (otherj->anonymous) {
5359 return BOOTSTRAP_NOT_PRIVILEGED;
5360 }
5361
5362 job_remove(otherj);
5363
5364 if (do_block) {
5365 job_log(j, LOG_DEBUG, "Blocking MIG return of job_remove(): %s", otherj->label);
5366 /* this is messy. We shouldn't access 'otherj' after job_remove(), but we check otherj->p first... */
5367 job_assumes(otherj, waiting4removal_new(otherj, srp));
5368 return MIG_NO_REPLY;
5369 } else {
5370 return 0;
5371 }
5372 } else if (otherj->p) {
5373 job_assumes(j, runtime_kill(otherj->p, sig) != -1);
5374 }
5375
5376 return 0;
5377}
5378
5379kern_return_t
5380job_mig_log_forward(job_t j, vm_offset_t inval, mach_msg_type_number_t invalCnt)
5381{
5382 struct ldcred ldc;
5383
5384 if (!launchd_assumes(j != NULL)) {
5385 return BOOTSTRAP_NO_MEMORY;
5386 }
5387
5388 if (!job_assumes(j, j->per_user)) {
5389 return BOOTSTRAP_NOT_PRIVILEGED;
5390 }
5391
5392 runtime_get_caller_creds(&ldc);
5393
5394 return runtime_log_forward(ldc.euid, ldc.egid, inval, invalCnt);
5395}
5396
5397kern_return_t
5398job_mig_log_drain(job_t j, mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
5399{
5400 struct ldcred ldc;
5401
5402 if (!launchd_assumes(j != NULL)) {
5403 return BOOTSTRAP_NO_MEMORY;
5404 }
5405
5406 runtime_get_caller_creds(&ldc);
5407
5408 if (ldc.euid) {
5409 return BOOTSTRAP_NOT_PRIVILEGED;
5410 }
5411
5412 return runtime_log_drain(srp, outval, outvalCnt);
5413}
5414
5415kern_return_t
5416job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey,
5417 vm_offset_t inval, mach_msg_type_number_t invalCnt,
5418 vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
5419{
5420 const char *action;
5421 launch_data_t input_obj, output_obj;
5422 size_t data_offset = 0;
5423 size_t packed_size;
5424 struct ldcred ldc;
5425
5426 runtime_get_caller_creds(&ldc);
5427
5428 if (!launchd_assumes(j != NULL)) {
5429 return BOOTSTRAP_NO_MEMORY;
5430 }
5431
5432 if (inkey && ldc.euid && ldc.euid != getuid()) {
5433 return BOOTSTRAP_NOT_PRIVILEGED;
5434 }
5435
5436 if (inkey && outkey && !job_assumes(j, inkey == outkey)) {
5437 return 1;
5438 }
5439
5440 if (inkey && outkey) {
5441 action = "Swapping";
5442 } else if (inkey) {
5443 action = "Setting";
5444 } else {
5445 action = "Getting";
5446 }
5447
5448 job_log(j, LOG_DEBUG, "%s key: %u", action, inkey ? inkey : outkey);
5449
5450 *outvalCnt = 20 * 1024 * 1024;
5451 mig_allocate(outval, *outvalCnt);
5452 if (!job_assumes(j, *outval != 0)) {
5453 return 1;
5454 }
5455
5456 if (invalCnt && !job_assumes(j, (input_obj = launch_data_unpack((void *)inval, invalCnt, NULL, 0, &data_offset, NULL)) != NULL)) {
5457 goto out_bad;
5458 }
5459
5460 switch (outkey) {
5461 case VPROC_GSK_ENVIRONMENT:
5462 if (!job_assumes(j, (output_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)))) {
5463 goto out_bad;
5464 }
5465 jobmgr_export_env_from_other_jobs(j->mgr, output_obj);
5466 if (!job_assumes(j, launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL) != 0)) {
5467 goto out_bad;
5468 }
5469 launch_data_free(output_obj);
5470 break;
5471 case VPROC_GSK_ALLJOBS:
5472 if (!job_assumes(j, (output_obj = job_export_all()) != NULL)) {
5473 goto out_bad;
5474 }
5475 ipc_revoke_fds(output_obj);
5476 packed_size = launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL);
5477 if (!job_assumes(j, packed_size != 0)) {
5478 goto out_bad;
5479 }
5480 launch_data_free(output_obj);
5481 break;
5482 case 0:
5483 mig_deallocate(*outval, *outvalCnt);
5484 *outval = 0;
5485 *outvalCnt = 0;
5486 break;
5487 default:
5488 goto out_bad;
5489 }
5490
5491 if (invalCnt) switch (inkey) {
5492 case VPROC_GSK_ENVIRONMENT:
5493 job_assumes(j, false);
5494 break;
5495 case 0:
5496 break;
5497 default:
5498 goto out_bad;
5499 }
5500
5501 mig_deallocate(inval, invalCnt);
5502
5503 return 0;
5504
5505out_bad:
5506 if (*outval) {
5507 mig_deallocate(*outval, *outvalCnt);
5508 }
5509 return 1;
5510}
5511
5512kern_return_t
5513job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inval, int64_t *outval)
5514{
5515 const char *action;
5516 kern_return_t kr = 0;
5517 struct ldcred ldc;
5518 int oldmask;
5519
5520 runtime_get_caller_creds(&ldc);
5521
5522 if (!launchd_assumes(j != NULL)) {
5523 return BOOTSTRAP_NO_MEMORY;
5524 }
5525
5526 if (inkey && ldc.euid && ldc.euid != getuid()) {
5527 return BOOTSTRAP_NOT_PRIVILEGED;
5528 }
5529
5530 if (inkey && outkey && !job_assumes(j, inkey == outkey)) {
5531 return 1;
5532 }
5533
5534 if (inkey && outkey) {
5535 action = "Swapping";
5536 } else if (inkey) {
5537 action = "Setting";
5538 } else {
5539 action = "Getting";
5540 }
5541
5542 job_log(j, LOG_DEBUG, "%s key: %u", action, inkey ? inkey : outkey);
5543
5544 switch (outkey) {
5545 case VPROC_GSK_LAST_EXIT_STATUS:
5546 *outval = j->last_exit_status;
5547 break;
5548 case VPROC_GSK_MGR_UID:
5549 *outval = getuid();
5550 break;
5551 case VPROC_GSK_MGR_PID:
5552 *outval = getpid();
5553 break;
5554 case VPROC_GSK_IS_MANAGED:
5555 *outval = j->anonymous ? 0 : 1;
5556 break;
5557 case VPROC_GSK_BASIC_KEEPALIVE:
5558 *outval = !j->ondemand;
5559 break;
5560 case VPROC_GSK_START_INTERVAL:
5561 *outval = j->start_interval;
5562 break;
5563 case VPROC_GSK_IDLE_TIMEOUT:
5564 *outval = j->timeout;
5565 break;
5566 case VPROC_GSK_EXIT_TIMEOUT:
5567 *outval = j->exit_timeout;
5568 break;
5569 case VPROC_GSK_GLOBAL_LOG_MASK:
5570 oldmask = runtime_setlogmask(LOG_UPTO(LOG_DEBUG));
5571 *outval = oldmask;
5572 runtime_setlogmask(oldmask);
5573 break;
5574 case VPROC_GSK_GLOBAL_UMASK:
5575 oldmask = umask(0);
5576 *outval = oldmask;
5577 umask(oldmask);
5578 break;
5579 case 0:
5580 *outval = 0;
5581 break;
5582 default:
5583 kr = 1;
5584 break;
5585 }
5586
5587 switch (inkey) {
5588 case VPROC_GSK_GLOBAL_ON_DEMAND:
5589 kr = job_set_global_on_demand(j, (bool)inval) ? 0 : 1;
5590 break;
5591 case VPROC_GSK_BASIC_KEEPALIVE:
5592 j->ondemand = !inval;
5593 break;
5594 case VPROC_GSK_START_INTERVAL:
5595 if ((uint64_t)inval > UINT32_MAX) {
5596 kr = 1;
5597 } else if (inval) {
5598 if (j->start_interval == 0) {
5599 runtime_add_ref();
5600 } else {
5601 /* Workaround 5225889 */
5602 job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, j) != -1);
5603 }
5604 j->start_interval = inval;
5605 job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j) != -1);
5606 } else if (j->start_interval) {
5607 job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1);
5608 if (j->start_interval != 0) {
5609 runtime_del_ref();
5610 }
5611 j->start_interval = 0;
5612 }
5613 break;
5614 case VPROC_GSK_IDLE_TIMEOUT:
5615 if ((unsigned int)inval > 0) {
5616 j->timeout = inval;
5617 }
5618 break;
5619 case VPROC_GSK_EXIT_TIMEOUT:
5620 if ((unsigned int)inval > 0) {
5621 j->exit_timeout = inval;
5622 }
5623 break;
5624 case VPROC_GSK_GLOBAL_LOG_MASK:
5625 runtime_setlogmask(inval);
5626 break;
5627 case VPROC_GSK_GLOBAL_UMASK:
5628 umask(inval);
5629 break;
5630 case 0:
5631 break;
5632 default:
5633 kr = 1;
5634 break;
5635 }
5636
5637 return kr;
5638}
5639
5640kern_return_t
5641job_mig_post_fork_ping(job_t j, task_t child_task)
5642{
5643 struct machservice *ms;
5644
5645 if (!launchd_assumes(j != NULL)) {
5646 return BOOTSTRAP_NO_MEMORY;
5647 }
5648
5649 job_log(j, LOG_DEBUG, "Post fork ping.");
5650
5651 job_setup_exception_port(j, child_task);
5652
5653 SLIST_FOREACH(ms, &special_ports, special_port_sle) {
5654 if (j->per_user && (ms->special_port_num != TASK_ACCESS_PORT)) {
5655 /* The TASK_ACCESS_PORT funny business is to workaround 5325399. */
5656 continue;
5657 }
5658
5659 errno = task_set_special_port(child_task, ms->special_port_num, ms->port);
5660
5661 if (errno) {
5662 int desired_log_level = LOG_ERR;
5663
5664 if (j->anonymous) {
5665 /* 5338127 */
5666
5667 desired_log_level = LOG_WARNING;
5668
5669 if (ms->special_port_num == TASK_SEATBELT_PORT) {
5670 desired_log_level = LOG_DEBUG;
5671 }
5672 }
5673
5674 job_log(j, desired_log_level, "Could not setup Mach task special port %u: %s", ms->special_port_num, mach_error_string(errno));
5675 }
5676 }
5677
5678 job_assumes(j, launchd_mport_deallocate(child_task) == KERN_SUCCESS);
5679
5680 return 0;
5681}
5682
5683kern_return_t
5684job_mig_reboot2(job_t j, uint64_t flags)
5685{
5686 char who_started_the_reboot[2048] = "";
5687 struct kinfo_proc kp;
5688 struct ldcred ldc;
5689 pid_t pid_to_log;
5690
5691 if (!launchd_assumes(j != NULL)) {
5692 return BOOTSTRAP_NO_MEMORY;
5693 }
5694
5695 if (getpid() != 1) {
5696 return BOOTSTRAP_NOT_PRIVILEGED;
5697 }
5698
5699 runtime_get_caller_creds(&ldc);
5700
5701 if (ldc.euid) {
5702 return BOOTSTRAP_NOT_PRIVILEGED;
5703 }
5704
5705 for (pid_to_log = ldc.pid; pid_to_log; pid_to_log = kp.kp_eproc.e_ppid) {
5706 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid_to_log };
5707 size_t who_offset, len = sizeof(kp);
5708
5709 if (!job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1)) {
5710 return 1;
5711 }
5712
5713 who_offset = strlen(who_started_the_reboot);
5714 snprintf(who_started_the_reboot + who_offset, sizeof(who_started_the_reboot) - who_offset,
5715 " %s[%u]%s", kp.kp_proc.p_comm, pid_to_log, kp.kp_eproc.e_ppid ? " ->" : "");
5716 }
5717
5718 root_jobmgr->reboot_flags = (int)flags;
5719
5720 launchd_shutdown();
5721
5722 job_log(j, LOG_DEBUG, "reboot2() initiated by:%s", who_started_the_reboot);
5723
5724 return 0;
5725}
5726
5727kern_return_t
5728job_mig_getsocket(job_t j, name_t spr)
5729{
5730 if (!launchd_assumes(j != NULL)) {
5731 return BOOTSTRAP_NO_MEMORY;
5732 }
5733
5734 ipc_server_init();
5735
5736 if (!sockpath) {
5737 return BOOTSTRAP_NO_MEMORY;
5738 }
5739
5740 strncpy(spr, sockpath, sizeof(name_t));
5741
5742 return BOOTSTRAP_SUCCESS;
5743}
5744
5745kern_return_t
5746job_mig_log(job_t j, int pri, int err, logmsg_t msg)
5747{
5748 if (!launchd_assumes(j != NULL)) {
5749 return BOOTSTRAP_NO_MEMORY;
5750 }
5751
5752 if ((errno = err)) {
5753 job_log_error(j, pri, "%s", msg);
5754 } else {
5755 job_log(j, pri, "%s", msg);
5756 }
5757
5758 return 0;
5759}
5760
5761void
5762ensure_root_bkgd_setup(void)
5763{
5764 if (background_jobmgr || getpid() != 1) {
5765 return;
5766 }
5767
5768 if (!jobmgr_assumes(root_jobmgr, (background_jobmgr = jobmgr_new(root_jobmgr, mach_task_self(), MACH_PORT_NULL, false, VPROCMGR_SESSION_BACKGROUND)) != NULL)) {
5769 return;
5770 }
5771
5772 background_jobmgr->req_port = 0;
5773 jobmgr_assumes(root_jobmgr, launchd_mport_make_send(background_jobmgr->jm_port) == KERN_SUCCESS);
5774}
5775
5776kern_return_t
5777job_mig_lookup_per_user_context(job_t j, uid_t which_user, mach_port_t *up_cont)
5778{
5779 struct ldcred ldc;
5780 job_t ji;
5781
cf0bacfd
A
5782#if TARGET_OS_EMBEDDED
5783 return BOOTSTRAP_NOT_PRIVILEGED;
5784#endif
5785
5b0a4722
A
5786 if (!launchd_assumes(j != NULL)) {
5787 return BOOTSTRAP_NO_MEMORY;
5788 }
5789
5790 job_log(j, LOG_DEBUG, "Looking up per user launchd for UID: %u", which_user);
5791
5792 runtime_get_caller_creds(&ldc);
5793
5794 if (getpid() != 1) {
5795 job_log(j, LOG_ERR, "Only PID 1 supports per user launchd lookups.");
5796 return BOOTSTRAP_NOT_PRIVILEGED;
5797 }
5798
5799 if (ldc.euid || ldc.uid) {
5800 which_user = ldc.euid ? ldc.euid : ldc.uid;
5801 }
5802
5803 *up_cont = MACH_PORT_NULL;
5804
5805 if (which_user == 0) {
5806 ensure_root_bkgd_setup();
5807
5808 *up_cont = background_jobmgr->jm_port;
5809
5810 return 0;
5811 }
5812
5813 LIST_FOREACH(ji, &root_jobmgr->jobs, sle) {
5814 if (!ji->per_user) {
5815 continue;
5816 }
5817 if (ji->mach_uid != which_user) {
5818 continue;
5819 }
5820 if (SLIST_EMPTY(&ji->machservices)) {
5821 continue;
5822 }
5823 if (!SLIST_FIRST(&ji->machservices)->per_user_hack) {
5824 continue;
5825 }
5826 break;
5827 }
5828
5829 if (ji == NULL) {
5830 struct machservice *ms;
5831 char lbuf[1024];
5832
5833 job_log(j, LOG_DEBUG, "Creating per user launchd job for UID: %u", which_user);
5834
5835 sprintf(lbuf, "com.apple.launchd.peruser.%u", which_user);
5836
5837 ji = job_new(root_jobmgr, lbuf, "/sbin/launchd", NULL);
5838
5839 if (ji == NULL) {
5840 return BOOTSTRAP_NO_MEMORY;
5841 }
5842
5843 ji->mach_uid = which_user;
5844 ji->per_user = true;
5845
5846 if ((ms = machservice_new(ji, lbuf, up_cont, false)) == NULL) {
5847 job_remove(ji);
5848 return BOOTSTRAP_NO_MEMORY;
5849 }
5850
5851 ms->per_user_hack = true;
5852 ms->hide = true;
5853
5854 ji = job_dispatch(ji, false);
5855 } else {
5856 job_log(j, LOG_DEBUG, "Per user launchd job found for UID: %u", which_user);
5857 }
5858
5859 if (job_assumes(j, ji != NULL)) {
5860 *up_cont = machservice_port(SLIST_FIRST(&ji->machservices));
5861 }
5862
5863 return 0;
5864}
5865
5866kern_return_t
5867job_mig_check_in(job_t j, name_t servicename, mach_port_t *serviceportp)
5868{
5869 static pid_t last_warned_pid = 0;
5870 struct machservice *ms;
5871 struct ldcred ldc;
5872
5873 if (!launchd_assumes(j != NULL)) {
5874 return BOOTSTRAP_NO_MEMORY;
5875 }
5876
5877 runtime_get_caller_creds(&ldc);
5878
5879 ms = jobmgr_lookup_service(j->mgr, servicename, true, 0);
5880
5881 if (ms == NULL) {
5882 job_log(j, LOG_DEBUG, "Check-in of Mach service failed. Unknown: %s", servicename);
5883 return BOOTSTRAP_UNKNOWN_SERVICE;
5884 }
5885 if (machservice_job(ms) != j) {
5886 if (last_warned_pid != ldc.pid) {
5887 job_log(j, LOG_NOTICE, "Check-in of Mach service failed. PID %d is not privileged: %s",
5888 ldc.pid, servicename);
5889 last_warned_pid = ldc.pid;
5890 }
5891 return BOOTSTRAP_NOT_PRIVILEGED;
5892 }
5893 if (machservice_active(ms)) {
5894 job_log(j, LOG_WARNING, "Check-in of Mach service failed. Already active: %s", servicename);
5895 return BOOTSTRAP_SERVICE_ACTIVE;
5896 }
5897
5898 machservice_request_notifications(ms);
5899
5900 job_log(j, LOG_INFO, "Check-in of service: %s", servicename);
5901
5902 *serviceportp = machservice_port(ms);
5903 return BOOTSTRAP_SUCCESS;
5904}
5905
5906kern_return_t
5907job_mig_register2(job_t j, name_t servicename, mach_port_t serviceport, uint64_t flags)
5908{
5909 struct machservice *ms;
5910 struct ldcred ldc;
5911
5912 if (!launchd_assumes(j != NULL)) {
5913 return BOOTSTRAP_NO_MEMORY;
5914 }
5915
5916 runtime_get_caller_creds(&ldc);
5917
5918#if 0
5919 job_log(j, LOG_APPLEONLY, "bootstrap_register() is deprecated. Service: %s", servicename);
5920#endif
5921
5922 job_log(j, LOG_DEBUG, "%sMach service registration attempt: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
5923
f36da725
A
5924 /* 5641783 for the embedded hack */
5925#if !TARGET_OS_EMBEDDED
5b0a4722
A
5926 /*
5927 * From a per-user/session launchd's perspective, SecurityAgent (UID
5928 * 92) is a rogue application (not our UID, not root and not a child of
5929 * us). We'll have to reconcile this design friction at a later date.
5930 */
5931 if (j->anonymous && job_get_bs(j)->parentmgr == NULL && ldc.uid != 0 && ldc.uid != getuid() && ldc.uid != 92) {
5932 if (getpid() == 1) {
5933 return VPROC_ERR_TRY_PER_USER;
5934 } else {
5935 return BOOTSTRAP_NOT_PRIVILEGED;
5936 }
5937 }
f36da725 5938#endif
5b0a4722
A
5939
5940 ms = jobmgr_lookup_service(j->mgr, servicename, false, flags & BOOTSTRAP_PER_PID_SERVICE ? ldc.pid : 0);
5941
5942 if (ms) {
5943 if (machservice_job(ms) != j) {
5944 return BOOTSTRAP_NOT_PRIVILEGED;
5945 }
5946 if (machservice_active(ms)) {
5947 job_log(j, LOG_DEBUG, "Mach service registration failed. Already active: %s", servicename);
5948 return BOOTSTRAP_SERVICE_ACTIVE;
5949 }
5950 job_checkin(j);
5951 machservice_delete(j, ms, false);
5952 }
5953
5954 if (serviceport != MACH_PORT_NULL) {
5955 if ((ms = machservice_new(j, servicename, &serviceport, flags & BOOTSTRAP_PER_PID_SERVICE ? true : false))) {
5956 machservice_request_notifications(ms);
5957 } else {
5958 return BOOTSTRAP_NO_MEMORY;
5959 }
5960 }
5961
5962 return BOOTSTRAP_SUCCESS;
5963}
5964
5965kern_return_t
5966job_mig_look_up2(job_t j, name_t servicename, mach_port_t *serviceportp, mach_msg_type_name_t *ptype, pid_t target_pid, uint64_t flags)
5967{
5968 struct machservice *ms;
5969 struct ldcred ldc;
5970 kern_return_t kr;
5971
5972 if (!launchd_assumes(j != NULL)) {
5973 return BOOTSTRAP_NO_MEMORY;
5974 }
5975
5976 runtime_get_caller_creds(&ldc);
5977
f36da725
A
5978 /* 5641783 for the embedded hack */
5979#if !TARGET_OS_EMBEDDED
5b0a4722
A
5980 if (getpid() == 1 && j->anonymous && job_get_bs(j)->parentmgr == NULL && ldc.uid != 0 && ldc.euid != 0) {
5981 return VPROC_ERR_TRY_PER_USER;
5982 }
f36da725 5983#endif
5b0a4722
A
5984
5985 if (!mspolicy_check(j, servicename, flags & BOOTSTRAP_PER_PID_SERVICE)) {
5986 job_log(j, LOG_NOTICE, "Policy denied Mach service lookup: %s", servicename);
5987 return BOOTSTRAP_NOT_PRIVILEGED;
5988 }
5989
5990 if (flags & BOOTSTRAP_PER_PID_SERVICE) {
5991 ms = jobmgr_lookup_service(j->mgr, servicename, false, target_pid);
5992 } else {
5993 ms = jobmgr_lookup_service(j->mgr, servicename, true, 0);
5994 }
5995
cf0bacfd 5996 if (ms && machservice_hidden(ms) && !machservice_active(ms)) {
5b0a4722
A
5997 ms = NULL;
5998 } else if (ms && ms->per_user_hack) {
5999 ms = NULL;
6000 }
6001
6002 if (ms) {
6003 launchd_assumes(machservice_port(ms) != MACH_PORT_NULL);
6004 job_log(j, LOG_DEBUG, "%sMach service lookup: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
6005#if 0
6006 /* After Leopard ships, we should enable this */
6007 if (j->lastlookup == ms && j->lastlookup_gennum == ms->gen_num && !j->per_user) {
6008 ms->bad_perf_cnt++;
6009 job_log(j, LOG_APPLEONLY, "Performance opportunity: Number of bootstrap_lookup(... \"%s\" ...) calls that should have been cached: %llu",
6010 servicename, ms->bad_perf_cnt);
6011 }
6012 j->lastlookup = ms;
6013 j->lastlookup_gennum = ms->gen_num;
6014#endif
6015 *serviceportp = machservice_port(ms);
6016 *ptype = MACH_MSG_TYPE_COPY_SEND;
6017 kr = BOOTSTRAP_SUCCESS;
6018 } else if (!(flags & BOOTSTRAP_PER_PID_SERVICE) && (inherited_bootstrap_port != MACH_PORT_NULL)) {
6019 job_log(j, LOG_DEBUG, "Mach service lookup forwarded: %s", servicename);
6020 *ptype = MACH_MSG_TYPE_MOVE_SEND;
6021 kr = bootstrap_look_up(inherited_bootstrap_port, servicename, serviceportp);
6022 } else if (getpid() == 1 && j->anonymous && ldc.euid >= 500 && strcasecmp(job_get_bs(j)->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
6023 /*
6024 * 5240036 Should start background session when a lookup of CCacheServer occurs
6025 *
6026 * This is a total hack. We sniff out loginwindow session, and attempt to guess what it is up to.
6027 * If we find a EUID that isn't root, we force it over to the per-user context.
6028 */
6029 return VPROC_ERR_TRY_PER_USER;
6030 } else {
6031 job_log(j, LOG_DEBUG, "%sMach service lookup failed: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
6032 kr = BOOTSTRAP_UNKNOWN_SERVICE;
6033 }
6034
6035 return kr;
6036}
6037
6038kern_return_t
6039job_mig_parent(job_t j, mach_port_t *parentport, mach_msg_type_name_t *pptype)
6040{
6041 if (!launchd_assumes(j != NULL)) {
6042 return BOOTSTRAP_NO_MEMORY;
6043 }
6044
6045 job_log(j, LOG_DEBUG, "Requested parent bootstrap port");
6046 jobmgr_t jm = j->mgr;
6047
6048 *pptype = MACH_MSG_TYPE_MAKE_SEND;
6049
6050 if (jobmgr_parent(jm)) {
6051 *parentport = jobmgr_parent(jm)->jm_port;
6052 } else if (MACH_PORT_NULL == inherited_bootstrap_port) {
6053 *parentport = jm->jm_port;
6054 } else {
6055 *pptype = MACH_MSG_TYPE_COPY_SEND;
6056 *parentport = inherited_bootstrap_port;
6057 }
6058 return BOOTSTRAP_SUCCESS;
6059}
6060
6061kern_return_t
6062job_mig_info(job_t j, name_array_t *servicenamesp, unsigned int *servicenames_cnt,
6063 bootstrap_status_array_t *serviceactivesp, unsigned int *serviceactives_cnt)
6064{
6065 name_array_t service_names = NULL;
6066 bootstrap_status_array_t service_actives = NULL;
6067 unsigned int cnt = 0, cnt2 = 0;
6068 struct machservice *ms;
6069 jobmgr_t jm;
6070 job_t ji;
6071
cf0bacfd
A
6072#if TARGET_OS_EMBEDDED
6073 return BOOTSTRAP_NOT_PRIVILEGED;
6074#endif
6075
5b0a4722
A
6076 if (!launchd_assumes(j != NULL)) {
6077 return BOOTSTRAP_NO_MEMORY;
6078 }
6079
6080 jm = j->mgr;
6081
6082 LIST_FOREACH(ji, &jm->jobs, sle) {
6083 SLIST_FOREACH(ms, &ji->machservices, sle) {
6084 if (!ms->per_pid) {
6085 cnt++;
6086 }
6087 }
6088 }
6089
6090 if (cnt == 0) {
6091 goto out;
6092 }
6093
6094 mig_allocate((vm_address_t *)&service_names, cnt * sizeof(service_names[0]));
6095 if (!launchd_assumes(service_names != NULL)) {
6096 goto out_bad;
6097 }
6098
6099 mig_allocate((vm_address_t *)&service_actives, cnt * sizeof(service_actives[0]));
6100 if (!launchd_assumes(service_actives != NULL)) {
6101 goto out_bad;
6102 }
6103
6104 LIST_FOREACH(ji, &jm->jobs, sle) {
6105 SLIST_FOREACH(ms, &ji->machservices, sle) {
6106 if (!ms->per_pid) {
6107 strlcpy(service_names[cnt2], machservice_name(ms), sizeof(service_names[0]));
6108 service_actives[cnt2] = machservice_status(ms);
6109 cnt2++;
6110 }
6111 }
6112 }
6113
6114 launchd_assumes(cnt == cnt2);
6115
6116out:
6117 *servicenamesp = service_names;
6118 *serviceactivesp = service_actives;
6119 *servicenames_cnt = *serviceactives_cnt = cnt;
6120
6121 return BOOTSTRAP_SUCCESS;
6122
6123out_bad:
6124 if (service_names) {
6125 mig_deallocate((vm_address_t)service_names, cnt * sizeof(service_names[0]));
6126 }
6127 if (service_actives) {
6128 mig_deallocate((vm_address_t)service_actives, cnt * sizeof(service_actives[0]));
6129 }
6130
6131 return BOOTSTRAP_NO_MEMORY;
6132}
6133
6134void
6135job_reparent_hack(job_t j, const char *where)
6136{
6137 jobmgr_t jmi, jmi2;
6138
6139 ensure_root_bkgd_setup();
6140
6141 /* NULL is only passed for our custom API for LaunchServices. If that is the case, we do magic. */
6142 if (where == NULL) {
6143 if (strcasecmp(j->mgr->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
6144 where = VPROCMGR_SESSION_LOGINWINDOW;
6145 } else {
6146 where = VPROCMGR_SESSION_AQUA;
6147 }
6148 }
6149
6150 if (strcasecmp(j->mgr->name, where) == 0) {
6151 return;
6152 }
6153
6154 SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
6155 if (jmi->shutting_down) {
6156 continue;
6157 } else if (strcasecmp(jmi->name, where) == 0) {
6158 goto jm_found;
6159 } else if (strcasecmp(jmi->name, VPROCMGR_SESSION_BACKGROUND) == 0 && getpid() == 1) {
6160 SLIST_FOREACH(jmi2, &jmi->submgrs, sle) {
6161 if (strcasecmp(jmi2->name, where) == 0) {
6162 jmi = jmi2;
6163 goto jm_found;
6164 }
6165 }
6166 }
6167 }
6168
6169jm_found:
6170 if (job_assumes(j, jmi != NULL)) {
6171 struct machservice *msi;
6172
6173 SLIST_FOREACH(msi, &j->machservices, sle) {
6174 LIST_REMOVE(msi, name_hash_sle);
6175 }
6176
6177 LIST_REMOVE(j, sle);
6178 LIST_INSERT_HEAD(&jmi->jobs, j, sle);
6179 j->mgr = jmi;
6180
6181 SLIST_FOREACH(msi, &j->machservices, sle) {
6182 LIST_INSERT_HEAD(&j->mgr->ms_hash[hash_ms(msi->name)], msi, name_hash_sle);
6183 }
6184 }
6185}
6186
6187kern_return_t
6188job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type)
6189{
6190 mach_msg_type_number_t l2l_i, l2l_port_cnt = 0;
6191 mach_port_array_t l2l_ports = NULL;
6192 mach_port_t reqport, rcvright;
6193 kern_return_t kr = 1;
6194 launch_data_t out_obj_array = NULL;
6195 struct ldcred ldc;
6196 jobmgr_t jmr = NULL;
6197
cf0bacfd
A
6198#if TARGET_OS_EMBEDDED
6199 return BOOTSTRAP_NOT_PRIVILEGED;
6200#endif
6201
5b0a4722
A
6202 if (!launchd_assumes(j != NULL)) {
6203 return BOOTSTRAP_NO_MEMORY;
6204 }
6205
6206 runtime_get_caller_creds(&ldc);
6207
6208 if (target_subset == MACH_PORT_NULL) {
6209 job_t j2;
6210
6211 if (j->mgr->session_initialized) {
6212 if (ldc.uid == 0 && getpid() == 1) {
6213 if (strcmp(j->mgr->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
6214 job_t ji, jn;
6215
6216 LIST_FOREACH_SAFE(ji, &j->mgr->jobs, sle, jn) {
6217 if (!ji->anonymous) {
6218 job_remove(ji);
6219 }
6220 }
6221
6222 ensure_root_bkgd_setup();
6223
6224 SLIST_REMOVE(&j->mgr->parentmgr->submgrs, j->mgr, jobmgr_s, sle);
6225 j->mgr->parentmgr = background_jobmgr;
6226 SLIST_INSERT_HEAD(&j->mgr->parentmgr->submgrs, j->mgr, sle);
6227
6228 /*
6229 * We really should wait for all the jobs to die before proceeding. See 5351245 for more info.
6230 *
6231 * We have hacked around this in job_find() by ignoring jobs that are pending removal.
6232 */
6233
6234 } else if (strcmp(j->mgr->name, VPROCMGR_SESSION_AQUA) == 0) {
6235 job_log(j, LOG_DEBUG, "Tried to move the Aqua session.");
6236 return 0;
6237 } else if (strcmp(j->mgr->name, VPROCMGR_SESSION_BACKGROUND) == 0) {
6238 job_log(j, LOG_DEBUG, "Tried to move the background session.");
6239 return 0;
6240 } else {
6241 job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
6242 kr = BOOTSTRAP_NOT_PRIVILEGED;
6243 goto out;
6244 }
6245 } else {
6246 job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
6247 kr = BOOTSTRAP_NOT_PRIVILEGED;
6248 goto out;
6249 }
6250 } else if (ldc.uid == 0 && getpid() == 1 && strcmp(session_type, VPROCMGR_SESSION_STANDARDIO) == 0) {
6251 ensure_root_bkgd_setup();
6252
6253 SLIST_REMOVE(&j->mgr->parentmgr->submgrs, j->mgr, jobmgr_s, sle);
6254 j->mgr->parentmgr = background_jobmgr;
6255 SLIST_INSERT_HEAD(&j->mgr->parentmgr->submgrs, j->mgr, sle);
6256 } else if (strcmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
6257 jobmgr_t jmi;
6258
6259 /*
6260 * 5330262
6261 *
6262 * We're working around LoginWindow and the WindowServer.
6263 *
6264 * In practice, there is only one LoginWindow session. Unfortunately, for certain
6265 * scenarios, the WindowServer spawns loginwindow, and in those cases, it frequently
6266 * spawns a replacement loginwindow session before cleaning up the previous one.
6267 *
6268 * We're going to use the creation of a new LoginWindow context as a clue that the
6269 * previous LoginWindow context is on the way out and therefore we should just
6270 * kick-start the shutdown of it.
6271 */
6272
6273 SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
6274 if (jmi->shutting_down) {
6275 continue;
6276 } else if (strcasecmp(jmi->name, session_type) == 0) {
6277 jobmgr_shutdown(jmi);
6278 break;
6279 }
6280 }
6281 }
6282
6283 jobmgr_log(j->mgr, LOG_DEBUG, "Renaming to: %s", session_type);
f36da725 6284 strcpy(j->mgr->name_init, session_type);
5b0a4722
A
6285
6286 if (job_assumes(j, (j2 = jobmgr_init_session(j->mgr, session_type, false)))) {
6287 job_assumes(j, job_dispatch(j2, true));
6288 }
6289
6290 kr = 0;
6291 goto out;
6292 } else if (job_mig_intran2(root_jobmgr, target_subset, ldc.pid)) {
6293 job_log(j, LOG_ERR, "Moving a session to ourself is bogus.");
6294
6295 kr = BOOTSTRAP_NOT_PRIVILEGED;
6296 goto out;
6297 }
6298
6299 job_log(j, LOG_DEBUG, "Move subset attempt: 0x%x", target_subset);
6300
f36da725 6301 errno = kr = _vproc_grab_subset(target_subset, &reqport, &rcvright, &out_obj_array, &l2l_ports, &l2l_port_cnt);
5b0a4722
A
6302
6303 if (!job_assumes(j, kr == 0)) {
6304 goto out;
6305 }
6306
6307 launchd_assert(launch_data_array_get_count(out_obj_array) == l2l_port_cnt);
6308
6309 if (!job_assumes(j, (jmr = jobmgr_new(j->mgr, reqport, rcvright, false, session_type)) != NULL)) {
6310 kr = BOOTSTRAP_NO_MEMORY;
6311 goto out;
6312 }
6313
6314 for (l2l_i = 0; l2l_i < l2l_port_cnt; l2l_i++) {
6315 launch_data_t tmp, obj_at_idx;
6316 struct machservice *ms;
6317 job_t j_for_service;
6318 const char *serv_name;
6319 pid_t target_pid;
6320 bool serv_perpid;
6321
6322 job_assumes(j, obj_at_idx = launch_data_array_get_index(out_obj_array, l2l_i));
6323 job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_PID));
6324 target_pid = (pid_t)launch_data_get_integer(tmp);
6325 job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_PERPID));
6326 serv_perpid = launch_data_get_bool(tmp);
6327 job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_NAME));
6328 serv_name = launch_data_get_string(tmp);
6329
6330 j_for_service = jobmgr_find_by_pid(jmr, target_pid, true);
6331
6332 if (!j_for_service) {
6333 /* The PID probably exited */
6334 job_assumes(j, launchd_mport_deallocate(l2l_ports[l2l_i]) == KERN_SUCCESS);
6335 continue;
6336 }
6337
6338 if ((ms = machservice_new(j_for_service, serv_name, &l2l_ports[l2l_i], serv_perpid))) {
6339 machservice_request_notifications(ms);
6340 }
6341 }
6342
6343 kr = 0;
6344
6345out:
6346 if (out_obj_array) {
6347 launch_data_free(out_obj_array);
6348 }
6349
6350 if (l2l_ports) {
6351 mig_deallocate((vm_address_t)l2l_ports, l2l_port_cnt * sizeof(l2l_ports[0]));
6352 }
6353
6354 if (kr == 0) {
6355 if (target_subset) {
6356 job_assumes(j, launchd_mport_deallocate(target_subset) == KERN_SUCCESS);
6357 }
6358 } else if (jmr) {
6359 jobmgr_shutdown(jmr);
6360 }
6361
6362 return kr;
6363}
6364
6365kern_return_t
6366job_mig_take_subset(job_t j, mach_port_t *reqport, mach_port_t *rcvright,
6367 vm_offset_t *outdata, mach_msg_type_number_t *outdataCnt,
6368 mach_port_array_t *portsp, unsigned int *ports_cnt)
6369{
6370 launch_data_t tmp_obj, tmp_dict, outdata_obj_array = NULL;
6371 mach_port_array_t ports = NULL;
6372 unsigned int cnt = 0, cnt2 = 0;
6373 size_t packed_size;
6374 struct machservice *ms;
6375 jobmgr_t jm;
6376 job_t ji;
6377
cf0bacfd
A
6378#if TARGET_OS_EMBEDDED
6379 return BOOTSTRAP_NOT_PRIVILEGED;
6380#endif
6381
5b0a4722
A
6382 if (!launchd_assumes(j != NULL)) {
6383 return BOOTSTRAP_NO_MEMORY;
6384 }
6385
6386 jm = j->mgr;
6387
6388 if (getpid() != 1) {
6389 job_log(j, LOG_ERR, "Only the system launchd will transfer Mach sub-bootstraps.");
6390 return BOOTSTRAP_NOT_PRIVILEGED;
6391 } else if (jobmgr_parent(jm) == NULL) {
6392 job_log(j, LOG_ERR, "Root Mach bootstrap cannot be transferred.");
6393 return BOOTSTRAP_NOT_PRIVILEGED;
6394 } else if (strcasecmp(jm->name, VPROCMGR_SESSION_AQUA) == 0) {
6395 job_log(j, LOG_ERR, "Cannot transfer a setup GUI session.");
6396 return BOOTSTRAP_NOT_PRIVILEGED;
6397 } else if (!j->anonymous) {
6398 job_log(j, LOG_ERR, "Only the anonymous job can transfer Mach sub-bootstraps.");
6399 return BOOTSTRAP_NOT_PRIVILEGED;
6400 }
6401
6402 job_log(j, LOG_DEBUG, "Transferring sub-bootstrap to the per session launchd.");
6403
6404 outdata_obj_array = launch_data_alloc(LAUNCH_DATA_ARRAY);
6405 if (!job_assumes(j, outdata_obj_array)) {
6406 goto out_bad;
6407 }
6408
6409 *outdataCnt = 20 * 1024 * 1024;
6410 mig_allocate(outdata, *outdataCnt);
6411 if (!job_assumes(j, *outdata != 0)) {
6412 return 1;
6413 }
6414
6415 LIST_FOREACH(ji, &j->mgr->jobs, sle) {
6416 if (!ji->anonymous) {
6417 continue;
6418 }
6419 SLIST_FOREACH(ms, &ji->machservices, sle) {
6420 cnt++;
6421 }
6422 }
6423
6424 mig_allocate((vm_address_t *)&ports, cnt * sizeof(ports[0]));
6425 if (!launchd_assumes(ports != NULL)) {
6426 goto out_bad;
6427 }
6428
6429 LIST_FOREACH(ji, &j->mgr->jobs, sle) {
6430 if (!ji->anonymous) {
6431 continue;
6432 }
6433
6434 SLIST_FOREACH(ms, &ji->machservices, sle) {
6435 if (job_assumes(j, (tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY)))) {
6436 job_assumes(j, launch_data_array_set_index(outdata_obj_array, tmp_dict, cnt2));
6437 } else {
6438 goto out_bad;
6439 }
6440
6441 if (job_assumes(j, (tmp_obj = launch_data_new_string(machservice_name(ms))))) {
6442 job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_NAME));
6443 } else {
6444 goto out_bad;
6445 }
6446
6447 if (job_assumes(j, (tmp_obj = launch_data_new_integer((ms->job->p))))) {
6448 job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_PID));
6449 } else {
6450 goto out_bad;
6451 }
6452
6453 if (job_assumes(j, (tmp_obj = launch_data_new_bool((ms->per_pid))))) {
6454 job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_PERPID));
6455 } else {
6456 goto out_bad;
6457 }
6458
6459 ports[cnt2] = machservice_port(ms);
6460
6461 /* Increment the send right by one so we can shutdown the jobmgr cleanly */
6462 jobmgr_assumes(jm, (errno = mach_port_mod_refs(mach_task_self(), ports[cnt2], MACH_PORT_RIGHT_SEND, 1)) == 0);
6463 cnt2++;
6464 }
6465 }
6466
6467 launchd_assumes(cnt == cnt2);
6468
6469 packed_size = launch_data_pack(outdata_obj_array, (void *)*outdata, *outdataCnt, NULL, NULL);
6470 if (!job_assumes(j, packed_size != 0)) {
6471 goto out_bad;
6472 }
6473
6474 launch_data_free(outdata_obj_array);
6475
6476 *portsp = ports;
6477 *ports_cnt = cnt;
6478
6479 *reqport = jm->req_port;
6480 *rcvright = jm->jm_port;
6481
6482 jm->req_port = 0;
6483 jm->jm_port = 0;
6484
6485 workaround_5477111 = j;
6486
6487 jobmgr_shutdown(jm);
6488
6489 return BOOTSTRAP_SUCCESS;
6490
6491out_bad:
6492 if (outdata_obj_array) {
6493 launch_data_free(outdata_obj_array);
6494 }
6495 if (*outdata) {
6496 mig_deallocate(*outdata, *outdataCnt);
6497 }
6498 if (ports) {
6499 mig_deallocate((vm_address_t)ports, cnt * sizeof(ports[0]));
6500 }
6501
6502 return BOOTSTRAP_NO_MEMORY;
6503}
6504
6505kern_return_t
6506job_mig_subset(job_t j, mach_port_t requestorport, mach_port_t *subsetportp)
6507{
6508 int bsdepth = 0;
6509 jobmgr_t jmr;
6510
6511 if (!launchd_assumes(j != NULL)) {
6512 return BOOTSTRAP_NO_MEMORY;
6513 }
6514
6515 jmr = j->mgr;
6516
6517 while ((jmr = jobmgr_parent(jmr)) != NULL) {
6518 bsdepth++;
6519 }
6520
6521 /* Since we use recursion, we need an artificial depth for subsets */
6522 if (bsdepth > 100) {
6523 job_log(j, LOG_ERR, "Mach sub-bootstrap create request failed. Depth greater than: %d", bsdepth);
6524 return BOOTSTRAP_NO_MEMORY;
6525 }
6526
6527 if ((jmr = jobmgr_new(j->mgr, requestorport, MACH_PORT_NULL, false, NULL)) == NULL) {
6528 if (requestorport == MACH_PORT_NULL) {
6529 return BOOTSTRAP_NOT_PRIVILEGED;
6530 }
6531 return BOOTSTRAP_NO_MEMORY;
6532 }
6533
6534 *subsetportp = jmr->jm_port;
6535 return BOOTSTRAP_SUCCESS;
6536}
6537
6538kern_return_t
6539job_mig_create_service(job_t j, name_t servicename, mach_port_t *serviceportp)
6540{
6541 struct machservice *ms;
6542
6543 if (!launchd_assumes(j != NULL)) {
6544 return BOOTSTRAP_NO_MEMORY;
6545 }
6546
6547 if (job_prog(j)[0] == '\0') {
6548 job_log(j, LOG_ERR, "Mach service creation requires a target server: %s", servicename);
6549 return BOOTSTRAP_NOT_PRIVILEGED;
6550 }
6551
6552 if (!j->legacy_mach_job) {
6553 job_log(j, LOG_ERR, "bootstrap_create_service() is only allowed against legacy Mach jobs: %s", servicename);
6554 return BOOTSTRAP_NOT_PRIVILEGED;
6555 }
6556
6557 ms = jobmgr_lookup_service(j->mgr, servicename, false, 0);
6558 if (ms) {
6559 job_log(j, LOG_DEBUG, "Mach service creation attempt for failed. Already exists: %s", servicename);
6560 return BOOTSTRAP_NAME_IN_USE;
6561 }
6562
6563 job_checkin(j);
6564
6565 *serviceportp = MACH_PORT_NULL;
6566 ms = machservice_new(j, servicename, serviceportp, false);
6567
6568 if (!launchd_assumes(ms != NULL)) {
6569 goto out_bad;
6570 }
6571
6572 return BOOTSTRAP_SUCCESS;
6573
6574out_bad:
6575 launchd_assumes(launchd_mport_close_recv(*serviceportp) == KERN_SUCCESS);
6576 return BOOTSTRAP_NO_MEMORY;
6577}
6578
f36da725
A
6579kern_return_t
6580job_mig_embedded_wait(job_t j, name_t targetlabel, integer_t *waitstatus)
6581{
6582 job_t otherj;
6583
6584 if (!launchd_assumes(j != NULL)) {
6585 return BOOTSTRAP_NO_MEMORY;
6586 }
6587
6588 if (unlikely(!(otherj = job_find(targetlabel)))) {
6589 return BOOTSTRAP_UNKNOWN_SERVICE;
6590 }
6591
6592 *waitstatus = j->last_exit_status;
6593
6594 return 0;
6595}
6596
6597kern_return_t
6598job_mig_embedded_kickstart(job_t j, name_t targetlabel, pid_t *out_pid, mach_port_t *out_name_port)
6599{
6600 struct ldcred ldc;
6601 kern_return_t kr;
6602 job_t otherj;
6603
6604 if (!launchd_assumes(j != NULL)) {
6605 return BOOTSTRAP_NO_MEMORY;
6606 }
6607
6608 if (unlikely(!(otherj = job_find(targetlabel)))) {
6609 return BOOTSTRAP_UNKNOWN_SERVICE;
6610 }
6611
6612 runtime_get_caller_creds(&ldc);
6613
6614 if (ldc.euid != 0 && ldc.euid != geteuid()
6615#if TARGET_OS_EMBEDDED
6616 && j->username && otherj->username
6617 && strcmp(j->username, otherj->username) != 0
6618#endif
6619 ) {
6620 return BOOTSTRAP_NOT_PRIVILEGED;
6621 }
6622
6623 otherj = job_dispatch(otherj, true);
6624
6625 if (!job_assumes(j, otherj && otherj->p)) {
6626 return BOOTSTRAP_NO_MEMORY;
6627 }
6628
6629 kr = task_name_for_pid(mach_task_self(), otherj->p, out_name_port);
6630 if (!job_assumes(j, kr == 0)) {
6631 return kr;
6632 }
6633
6634 *out_pid = otherj->p;
6635
6636 return 0;
6637}
6638
5b0a4722
A
6639kern_return_t
6640job_mig_wait(job_t j, mach_port_t srp, integer_t *waitstatus)
6641{
6642 if (!launchd_assumes(j != NULL)) {
6643 return BOOTSTRAP_NO_MEMORY;
6644 }
6645#if 0
6646 struct ldcred ldc;
6647 runtime_get_caller_creds(&ldc);
6648#endif
6649 return job_handle_mpm_wait(j, srp, waitstatus);
6650}
6651
6652kern_return_t
6653job_mig_uncork_fork(job_t j)
6654{
6655 if (!launchd_assumes(j != NULL)) {
6656 return BOOTSTRAP_NO_MEMORY;
6657 }
6658
6659 if (!j->stall_before_exec) {
6660 job_log(j, LOG_WARNING, "Attempt to uncork a job that isn't in the middle of a fork().");
6661 return 1;
6662 }
6663
6664 job_uncork_fork(j);
6665 j->stall_before_exec = false;
6666 return 0;
6667}
6668
6669kern_return_t
6670job_mig_set_service_policy(job_t j, pid_t target_pid, uint64_t flags, name_t target_service)
6671{
cf0bacfd 6672 struct ldcred ldc;
5b0a4722
A
6673 job_t target_j;
6674
6675 if (!launchd_assumes(j != NULL)) {
6676 return BOOTSTRAP_NO_MEMORY;
6677 }
6678
cf0bacfd
A
6679 runtime_get_caller_creds(&ldc);
6680
6681#if TARGET_OS_EMBEDDED
ededfeb7 6682 if( ldc.euid ) {
cf0bacfd
A
6683 return BOOTSTRAP_NOT_PRIVILEGED;
6684 }
ededfeb7
A
6685#else
6686 if( ldc.euid && (ldc.euid != getuid()) ) {
6687 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, target_pid };
6688 struct kinfo_proc kp;
6689 size_t len = sizeof(kp);
6690
6691 job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1);
6692 job_assumes(j, len == sizeof(kp));
6693
6694 uid_t kp_euid = kp.kp_eproc.e_ucred.cr_uid;
6695 uid_t kp_uid = kp.kp_eproc.e_pcred.p_ruid;
6696
6697 if( ldc.euid == kp_euid ) {
6698 job_log(j, LOG_DEBUG, "Working around rdar://problem/5982485 and allowing job to set policy for PID %u.", target_pid);
6699 } else {
6700 job_log(j, LOG_ERR, "Denied Mach service policy update requested by UID/EUID %u/%u against PID %u with UID/EUID %u/%u due to mismatched credentials.", ldc.uid, ldc.euid, target_pid, kp_uid, kp_euid);
6701
6702 return BOOTSTRAP_NOT_PRIVILEGED;
6703 }
6704 }
6705#endif
cf0bacfd 6706
5b0a4722
A
6707 if (!job_assumes(j, (target_j = jobmgr_find_by_pid(j->mgr, target_pid, true)) != NULL)) {
6708 return BOOTSTRAP_NO_MEMORY;
6709 }
6710
6711 if (SLIST_EMPTY(&j->mspolicies)) {
6712 job_log(j, LOG_DEBUG, "Setting policy on job \"%s\" for Mach service: %s", target_j->label, target_service);
6713 if (target_service[0]) {
6714 job_assumes(j, mspolicy_new(target_j, target_service, flags & BOOTSTRAP_ALLOW_LOOKUP, flags & BOOTSTRAP_PER_PID_SERVICE, false));
6715 } else {
6716 target_j->deny_unknown_mslookups = !(flags & BOOTSTRAP_ALLOW_LOOKUP);
f36da725 6717 target_j->deny_job_creation = (bool)(flags & BOOTSTRAP_DENY_JOB_CREATION);
5b0a4722
A
6718 }
6719 } else {
6720 job_log(j, LOG_WARNING, "Jobs that have policies assigned to them may not set policies.");
6721 return BOOTSTRAP_NOT_PRIVILEGED;
6722 }
6723
6724 return 0;
6725}
6726
6727kern_return_t
6728job_mig_spawn(job_t j, vm_offset_t indata, mach_msg_type_number_t indataCnt, pid_t *child_pid, mach_port_t *obsvr_port)
6729{
6730 launch_data_t input_obj = NULL;
6731 size_t data_offset = 0;
6732 struct ldcred ldc;
6733 job_t jr;
6734
cf0bacfd
A
6735#if TARGET_OS_EMBEDDED
6736 return BOOTSTRAP_NOT_PRIVILEGED;
6737#endif
6738
5b0a4722
A
6739 runtime_get_caller_creds(&ldc);
6740
6741 if (!launchd_assumes(j != NULL)) {
6742 return BOOTSTRAP_NO_MEMORY;
6743 }
6744
f36da725
A
6745 if (unlikely(j->deny_job_creation)) {
6746 return BOOTSTRAP_NOT_PRIVILEGED;
6747 }
6748
5b0a4722
A
6749 if (getpid() == 1 && ldc.euid && ldc.uid) {
6750 job_log(j, LOG_DEBUG, "Punting spawn to per-user-context");
6751 return VPROC_ERR_TRY_PER_USER;
6752 }
6753
6754 if (!job_assumes(j, indataCnt != 0)) {
6755 return 1;
6756 }
6757
6758 if (!job_assumes(j, (input_obj = launch_data_unpack((void *)indata, indataCnt, NULL, 0, &data_offset, NULL)) != NULL)) {
6759 return 1;
6760 }
6761
6762 jr = jobmgr_import2(j->mgr, input_obj);
6763
6764 if (!job_assumes(j, jr != NULL)) {
6765 switch (errno) {
6766 case EEXIST:
6767 return BOOTSTRAP_NAME_IN_USE;
6768 default:
6769 return BOOTSTRAP_NO_MEMORY;
6770 }
6771 }
6772
6773 job_reparent_hack(jr, NULL);
6774
6775 if (getpid() == 1) {
6776 jr->mach_uid = ldc.uid;
6777 }
6778
6779 jr->unload_at_exit = true;
6780 jr->wait4pipe_eof = true;
6781 jr->abandon_pg = true;
6782 jr->stall_before_exec = jr->wait4debugger;
6783 jr->wait4debugger = false;
6784
6785 jr = job_dispatch(jr, true);
6786
6787 if (!job_assumes(j, jr != NULL)) {
6788 return BOOTSTRAP_NO_MEMORY;
6789 }
6790
fe044cc9
A
6791 if (!job_assumes(jr, jr->p)) {
6792 job_remove(jr);
6793 return BOOTSTRAP_NO_MEMORY;
6794 }
5b0a4722
A
6795
6796 if (!job_setup_machport(jr)) {
6797 job_remove(jr);
6798 return BOOTSTRAP_NO_MEMORY;
6799 }
6800
fe044cc9 6801 job_log(jr, LOG_DEBUG, "Spawned by PID %u: %s", j->p, j->label);
5b0a4722
A
6802
6803 *child_pid = jr->p;
6804 *obsvr_port = jr->j_port;
6805
6806 mig_deallocate(indata, indataCnt);
6807
6808 return BOOTSTRAP_SUCCESS;
6809}
6810
6811void
6812jobmgr_init(bool sflag)
6813{
6814 const char *root_session_type = getpid() == 1 ? VPROCMGR_SESSION_SYSTEM : VPROCMGR_SESSION_BACKGROUND;
6815
6816 launchd_assert((root_jobmgr = jobmgr_new(NULL, MACH_PORT_NULL, MACH_PORT_NULL, sflag, root_session_type)) != NULL);
6817}
6818
6819size_t
6820our_strhash(const char *s)
6821{
6822 size_t c, r = 5381;
6823
6824 /* djb2
6825 * This algorithm was first reported by Dan Bernstein many years ago in comp.lang.c
6826 */
6827
6828 while ((c = *s++)) {
6829 r = ((r << 5) + r) + c; /* hash*33 + c */
6830 }
6831
6832 return r;
6833}
6834
6835size_t
6836hash_label(const char *label)
6837{
6838 return our_strhash(label) % LABEL_HASH_SIZE;
6839}
6840
6841size_t
6842hash_ms(const char *msstr)
6843{
6844 return our_strhash(msstr) % MACHSERVICE_HASH_SIZE;
6845}
6846
6847bool
6848mspolicy_copy(job_t j_to, job_t j_from)
6849{
6850 struct mspolicy *msp;
6851
6852 SLIST_FOREACH(msp, &j_from->mspolicies, sle) {
6853 if (!mspolicy_new(j_to, msp->name, msp->allow, msp->per_pid, true)) {
6854 return false;
6855 }
6856 }
6857
6858 return true;
6859}
6860
6861bool
6862mspolicy_new(job_t j, const char *name, bool allow, bool pid_local, bool skip_check)
6863{
6864 struct mspolicy *msp;
6865
6866 if (!skip_check) SLIST_FOREACH(msp, &j->mspolicies, sle) {
6867 if (msp->per_pid != pid_local) {
6868 continue;
6869 } else if (strcmp(msp->name, name) == 0) {
6870 return false;
6871 }
6872 }
6873
6874 if ((msp = calloc(1, sizeof(struct mspolicy) + strlen(name) + 1)) == NULL) {
6875 return false;
6876 }
6877
6878 strcpy((char *)msp->name, name);
6879 msp->per_pid = pid_local;
6880 msp->allow = allow;
6881
6882 SLIST_INSERT_HEAD(&j->mspolicies, msp, sle);
6883
6884 return true;
6885}
6886
6887void
6888mspolicy_setup(launch_data_t obj, const char *key, void *context)
6889{
6890 job_t j = context;
6891
6892 if (launch_data_get_type(obj) != LAUNCH_DATA_BOOL) {
6893 job_log(j, LOG_WARNING, "Invalid object type for Mach service policy key: %s", key);
6894 return;
6895 }
6896
6897 job_assumes(j, mspolicy_new(j, key, launch_data_get_bool(obj), false, false));
6898}
6899
6900bool
6901mspolicy_check(job_t j, const char *name, bool pid_local)
6902{
6903 struct mspolicy *mspi;
6904
6905 SLIST_FOREACH(mspi, &j->mspolicies, sle) {
6906 if (mspi->per_pid != pid_local) {
6907 continue;
6908 } else if (strcmp(mspi->name, name) != 0) {
6909 continue;
6910 }
6911 return mspi->allow;
6912 }
6913
6914 return !j->deny_unknown_mslookups;
6915}
6916
6917void
6918mspolicy_delete(job_t j, struct mspolicy *msp)
6919{
6920 SLIST_REMOVE(&j->mspolicies, msp, mspolicy, sle);
6921
6922 free(msp);
6923}
6924
6925bool
6926waiting4removal_new(job_t j, mach_port_t rp)
6927{
6928 struct waiting_for_removal *w4r;
6929
6930 if (!job_assumes(j, (w4r = malloc(sizeof(struct waiting_for_removal))) != NULL)) {
6931 return false;
6932 }
6933
6934 w4r->reply_port = rp;
6935
6936 SLIST_INSERT_HEAD(&j->removal_watchers, w4r, sle);
6937
6938 return true;
6939}
6940
6941void
6942waiting4removal_delete(job_t j, struct waiting_for_removal *w4r)
6943{
6944 job_assumes(j, job_mig_send_signal_reply(w4r->reply_port, 0) == 0);
6945
6946 SLIST_REMOVE(&j->removal_watchers, w4r, waiting_for_removal, sle);
6947
6948 free(w4r);
6949}
6950
f36da725
A
6951size_t
6952get_kern_max_proc(void)
6953{
6954 int mib[] = { CTL_KERN, KERN_MAXPROC };
6955 int max = 100;
6956 size_t max_sz = sizeof(max);
6957
6958 launchd_assumes(sysctl(mib, 2, &max, &max_sz, NULL, 0) != -1);
6959
6960 return max;
6961}
6962
5b0a4722
A
6963void
6964do_file_init(void)
6965{
f36da725
A
6966 struct stat sb;
6967
5b0a4722
A
6968 launchd_assert(mach_timebase_info(&tbi) == 0);
6969
f36da725
A
6970 if (stat("/AppleInternal", &sb) == 0) {
6971 do_apple_internal_magic = true;
6972 }
5b0a4722 6973}