]> git.saurik.com Git - apple/launchd.git/blame - launchd/src/launchd_core_logic.c
launchd-258.22.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
ef398931 19static const char *const __rcs_file_version__ = "$Revision: 23792 $";
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 }
ed34e3c3
A
2656 break;
2657 case 0:
5b0a4722
A
2658 if (_vproc_post_fork_ping()) {
2659 _exit(EXIT_FAILURE);
2660 }
2661 if (!j->legacy_mach_job) {
2662 job_assumes(j, dup2(oepair[1], STDOUT_FILENO) != -1);
2663 job_assumes(j, dup2(oepair[1], STDERR_FILENO) != -1);
2664 job_assumes(j, runtime_close(oepair[1]) != -1);
2665 }
2666 job_assumes(j, runtime_close(execspair[0]) == 0);
ed34e3c3
A
2667 /* wait for our parent to say they've attached a kevent to us */
2668 read(_fd(execspair[1]), &c, sizeof(c));
ed34e3c3
A
2669
2670 if (sipc) {
5b0a4722
A
2671 job_assumes(j, runtime_close(spair[0]) == 0);
2672 snprintf(nbuf, sizeof(nbuf), "%d", spair[1]);
ed34e3c3
A
2673 setenv(LAUNCHD_TRUSTED_FD_ENV, nbuf, 1);
2674 }
5b0a4722 2675 job_start_child(j);
ed34e3c3
A
2676 break;
2677 default:
5b0a4722
A
2678 job_log(j, LOG_DEBUG, "Started as PID: %u", c);
2679
2680 j->start_pending = false;
2681
2682 runtime_add_ref();
ed34e3c3 2683 total_children++;
5b0a4722
A
2684 LIST_INSERT_HEAD(&j->mgr->active_jobs[ACTIVE_JOB_HASH(c)], j, pid_hash_sle);
2685
2686 if (JOB_BOOTCACHE_HACK_CHECK(j)) {
2687 did_first_per_user_launchd_BootCache_hack = true;
2688 }
2689
2690 if (!j->legacy_mach_job) {
2691 job_assumes(j, runtime_close(oepair[1]) != -1);
2692 }
2693 j->p = c;
2694 if (j->hopefully_exits_first) {
2695 j->mgr->hopefully_first_cnt++;
2696 } else if (!j->hopefully_exits_last) {
2697 j->mgr->normal_active_cnt++;
2698 }
2699 j->forkfd = _fd(execspair[0]);
2700 job_assumes(j, runtime_close(execspair[1]) == 0);
ed34e3c3 2701 if (sipc) {
5b0a4722 2702 job_assumes(j, runtime_close(spair[1]) == 0);
ed34e3c3
A
2703 ipc_open(_fd(spair[0]), j);
2704 }
5b0a4722
A
2705 if (job_assumes(j, kevent_mod(c, EVFILT_PROC, EV_ADD, proc_fflags, 0, root_jobmgr ? root_jobmgr : j->mgr) != -1)) {
2706 job_ignore(j);
ed34e3c3 2707 } else {
5b0a4722 2708 job_reap(j);
ed34e3c3
A
2709 }
2710
2711 if (!j->stall_before_exec) {
5b0a4722 2712 job_uncork_fork(j);
ed34e3c3
A
2713 }
2714 break;
2715 }
2716}
2717
2718void
5b0a4722
A
2719do_first_per_user_launchd_hack(void)
2720{
2721 char *bcct_tool[] = { "/usr/sbin/BootCacheControl", "tag", NULL };
2722 int dummystatus;
2723 pid_t bcp;
2724
2725 if (launchd_assumes((bcp = vfork()) != -1)) {
2726 if (bcp == 0) {
2727 execve(bcct_tool[0], bcct_tool, environ);
2728 _exit(EXIT_FAILURE);
2729 } else {
2730 launchd_assumes(waitpid(bcp, &dummystatus, 0) != -1);
2731 }
2732 }
2733}
2734
2735void
2736job_start_child(job_t j)
ed34e3c3
A
2737{
2738 const char *file2exec = "/usr/libexec/launchproxy";
2739 const char **argv;
5b0a4722 2740 posix_spawnattr_t spattr;
ed34e3c3 2741 int gflags = GLOB_NOSORT|GLOB_NOCHECK|GLOB_TILDE|GLOB_DOOFFS;
5b0a4722 2742 pid_t junk_pid;
ed34e3c3 2743 glob_t g;
5b0a4722
A
2744 short spflags = POSIX_SPAWN_SETEXEC;
2745 size_t binpref_out_cnt = 0;
ed34e3c3
A
2746 int i;
2747
5b0a4722
A
2748 if (JOB_BOOTCACHE_HACK_CHECK(j)) {
2749 do_first_per_user_launchd_hack();
2750 }
2751
2752 job_assumes(j, posix_spawnattr_init(&spattr) == 0);
2753
ed34e3c3
A
2754 job_setup_attributes(j);
2755
2756 if (j->argv && j->globargv) {
2757 g.gl_offs = 1;
2758 for (i = 0; i < j->argc; i++) {
5b0a4722 2759 if (i > 0) {
ed34e3c3 2760 gflags |= GLOB_APPEND;
5b0a4722 2761 }
ed34e3c3
A
2762 if (glob(j->argv[i], gflags, NULL, &g) != 0) {
2763 job_log_error(j, LOG_ERR, "glob(\"%s\")", j->argv[i]);
2764 exit(EXIT_FAILURE);
2765 }
2766 }
2767 g.gl_pathv[0] = (char *)file2exec;
2768 argv = (const char **)g.gl_pathv;
2769 } else if (j->argv) {
2770 argv = alloca((j->argc + 2) * sizeof(char *));
2771 argv[0] = file2exec;
5b0a4722 2772 for (i = 0; i < j->argc; i++) {
ed34e3c3 2773 argv[i + 1] = j->argv[i];
5b0a4722 2774 }
ed34e3c3
A
2775 argv[i + 1] = NULL;
2776 } else {
2777 argv = alloca(3 * sizeof(char *));
2778 argv[0] = file2exec;
2779 argv[1] = j->prog;
2780 argv[2] = NULL;
2781 }
2782
5b0a4722 2783 if (!j->inetcompat) {
ed34e3c3 2784 argv++;
5b0a4722
A
2785 }
2786
2787 if (j->wait4debugger) {
2788 job_log(j, LOG_WARNING, "Spawned and waiting for the debugger to attach before continuing...");
2789 spflags |= POSIX_SPAWN_START_SUSPENDED;
2790 }
2791
2792 job_assumes(j, posix_spawnattr_setflags(&spattr, spflags) == 0);
2793
2794 if (j->j_binpref_cnt) {
2795 job_assumes(j, posix_spawnattr_setbinpref_np(&spattr, j->j_binpref_cnt, j->j_binpref, &binpref_out_cnt) == 0);
2796 job_assumes(j, binpref_out_cnt == j->j_binpref_cnt);
2797 }
2798
f36da725 2799#if HAVE_QUARANTINE
5b0a4722
A
2800 if (j->quarantine_data) {
2801 qtn_proc_t qp;
ed34e3c3 2802
5b0a4722
A
2803 if (job_assumes(j, qp = qtn_proc_alloc())) {
2804 if (job_assumes(j, qtn_proc_init_with_data(qp, j->quarantine_data, j->quarantine_data_sz) == 0)) {
2805 job_assumes(j, qtn_proc_apply_to_self(qp) == 0);
2806 }
2807 }
2808 }
f36da725 2809#endif
ed34e3c3 2810
f36da725 2811#if HAVE_SANDBOX
5b0a4722
A
2812 if (j->seatbelt_profile) {
2813 char *seatbelt_err_buf = NULL;
ed34e3c3 2814
5b0a4722
A
2815 if (!job_assumes(j, sandbox_init(j->seatbelt_profile, j->seatbelt_flags, &seatbelt_err_buf) != -1)) {
2816 if (seatbelt_err_buf) {
2817 job_log(j, LOG_ERR, "Sandbox failed to init: %s", seatbelt_err_buf);
2818 }
2819 goto out_bad;
2820 }
ed34e3c3 2821 }
f36da725 2822#endif
ed34e3c3
A
2823
2824 if (j->prog) {
5b0a4722
A
2825 errno = posix_spawn(&junk_pid, j->inetcompat ? file2exec : j->prog, NULL, &spattr, (char *const*)argv, environ);
2826 job_log_error(j, LOG_ERR, "posix_spawn(\"%s\", ...)", j->prog);
ed34e3c3 2827 } else {
5b0a4722
A
2828 errno = posix_spawnp(&junk_pid, j->inetcompat ? file2exec : argv[0], NULL, &spattr, (char *const*)argv, environ);
2829 job_log_error(j, LOG_ERR, "posix_spawnp(\"%s\", ...)", argv[0]);
ed34e3c3
A
2830 }
2831
ef398931 2832#if HAVE_SANDBOX
5b0a4722 2833out_bad:
ef398931 2834#endif
5b0a4722 2835 _exit(EXIT_FAILURE);
ed34e3c3
A
2836}
2837
5b0a4722
A
2838void
2839jobmgr_export_env_from_other_jobs(jobmgr_t jm, launch_data_t dict)
ed34e3c3 2840{
5b0a4722 2841 launch_data_t tmp;
ed34e3c3 2842 struct envitem *ei;
5b0a4722 2843 job_t ji;
ed34e3c3 2844
5b0a4722
A
2845 if (jm->parentmgr) {
2846 jobmgr_export_env_from_other_jobs(jm->parentmgr, dict);
2847 } else {
2848 char **tmpenviron = environ;
2849 for (; *tmpenviron; tmpenviron++) {
2850 char envkey[1024];
2851 launch_data_t s = launch_data_alloc(LAUNCH_DATA_STRING);
2852 launch_data_set_string(s, strchr(*tmpenviron, '=') + 1);
2853 strncpy(envkey, *tmpenviron, sizeof(envkey));
2854 *(strchr(envkey, '=')) = '\0';
2855 launch_data_dict_insert(dict, s, envkey);
2856 }
2857 }
ed34e3c3 2858
5b0a4722
A
2859 LIST_FOREACH(ji, &jm->jobs, sle) {
2860 SLIST_FOREACH(ei, &ji->global_env, sle) {
2861 if ((tmp = launch_data_new_string(ei->value))) {
2862 launch_data_dict_insert(dict, tmp, ei->key);
2863 }
2864 }
2865 }
ed34e3c3
A
2866}
2867
2868void
5b0a4722 2869jobmgr_setup_env_from_other_jobs(jobmgr_t jm)
ed34e3c3 2870{
ed34e3c3 2871 struct envitem *ei;
5b0a4722 2872 job_t ji;
ed34e3c3 2873
5b0a4722
A
2874 if (jm->parentmgr) {
2875 jobmgr_setup_env_from_other_jobs(jm->parentmgr);
2876 }
ed34e3c3 2877
5b0a4722
A
2878 LIST_FOREACH(ji, &jm->jobs, sle) {
2879 SLIST_FOREACH(ei, &ji->global_env, sle) {
2880 setenv(ei->key, ei->value, 1);
ed34e3c3 2881 }
5b0a4722
A
2882 }
2883}
ed34e3c3 2884
5b0a4722
A
2885void
2886job_find_and_blame_pids_with_weird_uids(job_t j)
2887{
2888 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
f36da725
A
2889 size_t i, kp_cnt, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
2890 struct kinfo_proc *kp;
5b0a4722 2891 uid_t u = j->mach_uid;
ed34e3c3 2892
f36da725
A
2893#if TARGET_OS_EMBEDDED
2894 if (!do_apple_internal_magic) {
2895 return;
2896 }
2897#endif
2898 kp = malloc(len);
2899
5b0a4722
A
2900 if (!job_assumes(j, kp != NULL)) {
2901 return;
2902 }
2903 if (!job_assumes(j, sysctl(mib, 3, kp, &len, NULL, 0) != -1)) {
2904 goto out;
ed34e3c3
A
2905 }
2906
5b0a4722 2907 kp_cnt = len / sizeof(struct kinfo_proc);
ed34e3c3 2908
5b0a4722
A
2909 for (i = 0; i < kp_cnt; i++) {
2910 uid_t i_euid = kp[i].kp_eproc.e_ucred.cr_uid;
2911 uid_t i_uid = kp[i].kp_eproc.e_pcred.p_ruid;
2912 uid_t i_svuid = kp[i].kp_eproc.e_pcred.p_svuid;
2913 pid_t i_pid = kp[i].kp_proc.p_pid;
2914
2915 if (i_euid != u && i_uid != u && i_svuid != u) {
2916 continue;
2917 }
ed34e3c3 2918
5b0a4722
A
2919 job_log(j, LOG_ERR, "PID %u \"%s\" has no account to back it! Real/effective/saved UIDs: %u/%u/%u",
2920 i_pid, kp[i].kp_proc.p_comm, i_uid, i_euid, i_svuid);
2921
2922/* Temporarily disabled due to 5423935 and 4946119. */
2923#if 0
2924 /* Ask the accountless process to exit. */
2925 job_assumes(j, runtime_kill(i_pid, SIGTERM) != -1);
2926#endif
ed34e3c3 2927 }
5b0a4722
A
2928
2929out:
2930 free(kp);
2931}
2932
ef398931 2933#if !TARGET_OS_EMBEDDED
cf0bacfd
A
2934void
2935job_enable_audit_for_user(job_t j, uid_t u, char *name)
2936{
2937 auditinfo_t auinfo = {
2938 .ai_auid = u,
2939 .ai_asid = j->p,
2940 };
2941 long au_cond;
2942
2943 if (!job_assumes(j, auditon(A_GETCOND, &au_cond, sizeof(long)) == 0)) {
2944 _exit(EXIT_FAILURE);
2945 }
2946
2947 if (au_cond != AUC_NOAUDIT) {
2948 if (!job_assumes(j, au_user_mask(name, &auinfo.ai_mask) == 0)) {
2949 _exit(EXIT_FAILURE);
2950 } else if (!job_assumes(j, setaudit(&auinfo) == 0)) {
2951 _exit(EXIT_FAILURE);
2952 }
2953 }
2954}
ef398931 2955#endif
cf0bacfd 2956
5b0a4722
A
2957void
2958job_postfork_become_user(job_t j)
2959{
2960 char loginname[2000];
2961 char tmpdirpath[PATH_MAX];
2962 char shellpath[PATH_MAX];
2963 char homedir[PATH_MAX];
2964 struct passwd *pwe;
2965 size_t r;
2966 gid_t desired_gid = -1;
2967 uid_t desired_uid = -1;
2968
2969 if (getuid() != 0) {
2970 return;
2971 }
2972
2973 /*
2974 * I contend that having UID == 0 and GID != 0 is of dubious value.
2975 * Nevertheless, this used to work in Tiger. See: 5425348
2976 */
2977 if (j->groupname && !j->username) {
2978 j->username = "root";
2979 }
2980
2981 if (j->username) {
2982 if ((pwe = getpwnam(j->username)) == NULL) {
2983 job_log(j, LOG_ERR, "getpwnam(\"%s\") failed", j->username);
2984 _exit(EXIT_FAILURE);
2985 }
2986 } else if (j->mach_uid) {
2987 if ((pwe = getpwuid(j->mach_uid)) == NULL) {
2988 job_log(j, LOG_ERR, "getpwuid(\"%u\") failed", j->mach_uid);
2989 job_find_and_blame_pids_with_weird_uids(j);
2990 _exit(EXIT_FAILURE);
2991 }
2992 } else {
2993 return;
2994 }
2995
2996 /*
2997 * We must copy the results of getpw*().
2998 *
2999 * Why? Because subsequent API calls may call getpw*() as a part of
3000 * their implementation. Since getpw*() returns a [now thread scoped]
3001 * global, we must therefore cache the results before continuing.
3002 */
3003
3004 desired_uid = pwe->pw_uid;
3005 desired_gid = pwe->pw_gid;
3006
3007 strlcpy(shellpath, pwe->pw_shell, sizeof(shellpath));
3008 strlcpy(loginname, pwe->pw_name, sizeof(loginname));
3009 strlcpy(homedir, pwe->pw_dir, sizeof(homedir));
3010
3011 if (pwe->pw_expire && time(NULL) >= pwe->pw_expire) {
3012 job_log(j, LOG_ERR, "Expired account");
3013 _exit(EXIT_FAILURE);
3014 }
3015
3016
3017 if (j->username && strcmp(j->username, loginname) != 0) {
3018 job_log(j, LOG_WARNING, "Suspicious setup: User \"%s\" maps to user: %s", j->username, loginname);
3019 } else if (j->mach_uid && (j->mach_uid != desired_uid)) {
3020 job_log(j, LOG_WARNING, "Suspicious setup: UID %u maps to UID %u", j->mach_uid, desired_uid);
ed34e3c3 3021 }
5b0a4722 3022
ed34e3c3 3023 if (j->groupname) {
5b0a4722
A
3024 struct group *gre;
3025
3026 if ((gre = getgrnam(j->groupname)) == NULL) {
ed34e3c3 3027 job_log(j, LOG_ERR, "getgrnam(\"%s\") failed", j->groupname);
5b0a4722 3028 _exit(EXIT_FAILURE);
ed34e3c3 3029 }
5b0a4722
A
3030
3031 desired_gid = gre->gr_gid;
3032 }
3033
ef398931 3034#if !TARGET_OS_EMBEDDED
cf0bacfd 3035 job_enable_audit_for_user(j, desired_uid, loginname);
ef398931 3036#endif
cf0bacfd 3037
5b0a4722
A
3038 if (!job_assumes(j, setlogin(loginname) != -1)) {
3039 _exit(EXIT_FAILURE);
ed34e3c3 3040 }
ed34e3c3 3041
5b0a4722
A
3042 if (!job_assumes(j, setgid(desired_gid) != -1)) {
3043 _exit(EXIT_FAILURE);
3044 }
ed34e3c3 3045
5b0a4722
A
3046 /*
3047 * The kernel team and the DirectoryServices team want initgroups()
3048 * called after setgid(). See 4616864 for more information.
3049 */
ed34e3c3 3050
5b0a4722
A
3051 if (!j->no_init_groups) {
3052 if (!job_assumes(j, initgroups(loginname, desired_gid) != -1)) {
3053 _exit(EXIT_FAILURE);
ed34e3c3
A
3054 }
3055 }
5b0a4722
A
3056
3057 if (!job_assumes(j, setuid(desired_uid) != -1)) {
3058 _exit(EXIT_FAILURE);
ed34e3c3 3059 }
5b0a4722
A
3060
3061 r = confstr(_CS_DARWIN_USER_TEMP_DIR, tmpdirpath, sizeof(tmpdirpath));
3062
3063 if (r > 0 && r < sizeof(tmpdirpath)) {
3064 setenv("TMPDIR", tmpdirpath, 0);
ed34e3c3 3065 }
5b0a4722
A
3066
3067 setenv("SHELL", shellpath, 0);
3068 setenv("HOME", homedir, 0);
3069 setenv("USER", loginname, 0);
3070 setenv("LOGNAME", loginname, 0);
3071}
3072
3073void
3074job_setup_attributes(job_t j)
3075{
3076 struct limititem *li;
3077 struct envitem *ei;
3078
3079 if (j->setnice) {
3080 job_assumes(j, setpriority(PRIO_PROCESS, 0, j->nice) != -1);
3081 }
3082
3083 SLIST_FOREACH(li, &j->limits, sle) {
3084 struct rlimit rl;
3085
3086 if (!job_assumes(j, getrlimit(li->which, &rl) != -1)) {
3087 continue;
3088 }
3089
3090 if (li->sethard) {
3091 rl.rlim_max = li->lim.rlim_max;
3092 }
3093 if (li->setsoft) {
3094 rl.rlim_cur = li->lim.rlim_cur;
3095 }
3096
3097 if (setrlimit(li->which, &rl) == -1) {
3098 job_log_error(j, LOG_WARNING, "setrlimit()");
ed34e3c3
A
3099 }
3100 }
3101
5b0a4722
A
3102 if (!j->inetcompat && j->session_create) {
3103 launchd_SessionCreate();
3104 }
3105
3106 if (j->low_pri_io) {
3107 job_assumes(j, setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE) != -1);
3108 }
3109 if (j->rootdir) {
3110 job_assumes(j, chroot(j->rootdir) != -1);
3111 job_assumes(j, chdir(".") != -1);
3112 }
3113
3114 job_postfork_become_user(j);
3115
3116 if (j->workingdir) {
3117 job_assumes(j, chdir(j->workingdir) != -1);
3118 }
3119
3120 if (j->setmask) {
3121 umask(j->mask);
3122 }
3123
3124 job_setup_fd(j, STDOUT_FILENO, j->stdoutpath, O_WRONLY|O_APPEND|O_CREAT);
3125 job_setup_fd(j, STDERR_FILENO, j->stderrpath, O_WRONLY|O_APPEND|O_CREAT);
ed34e3c3 3126
5b0a4722
A
3127 jobmgr_setup_env_from_other_jobs(j->mgr);
3128
3129 SLIST_FOREACH(ei, &j->env, sle) {
ed34e3c3 3130 setenv(ei->key, ei->value, 1);
5b0a4722
A
3131 }
3132
3133 /*
3134 * We'd like to call setsid() unconditionally, but we have reason to
3135 * believe that prevents launchd from being able to send signals to
3136 * setuid children. We'll settle for process-groups.
3137 */
3138 if (getppid() != 1) {
3139 job_assumes(j, setpgid(0, 0) != -1);
3140 } else {
3141 job_assumes(j, setsid() != -1);
3142 }
3143}
3144
3145void
3146job_setup_fd(job_t j, int target_fd, const char *path, int flags)
3147{
3148 int fd;
3149
3150 if (!path) {
3151 return;
3152 }
3153
3154 if ((fd = open(path, flags|O_NOCTTY, DEFFILEMODE)) == -1) {
3155 job_log_error(j, LOG_WARNING, "open(\"%s\", ...)", path);
3156 return;
3157 }
ed34e3c3 3158
5b0a4722
A
3159 job_assumes(j, dup2(fd, target_fd) != -1);
3160 job_assumes(j, runtime_close(fd) == 0);
ed34e3c3
A
3161}
3162
3163int
5b0a4722 3164dir_has_files(job_t j, const char *path)
ed34e3c3
A
3165{
3166 DIR *dd = opendir(path);
3167 struct dirent *de;
3168 bool r = 0;
3169
5b0a4722 3170 if (!dd) {
ed34e3c3 3171 return -1;
5b0a4722 3172 }
ed34e3c3
A
3173
3174 while ((de = readdir(dd))) {
3175 if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
3176 r = 1;
3177 break;
3178 }
3179 }
3180
5b0a4722 3181 job_assumes(j, closedir(dd) == 0);
ed34e3c3
A
3182 return r;
3183}
3184
3185void
5b0a4722 3186calendarinterval_setalarm(job_t j, struct calendarinterval *ci)
ed34e3c3 3187{
5b0a4722
A
3188 struct calendarinterval *ci_iter, *ci_prev = NULL;
3189 time_t later, head_later;
ed34e3c3
A
3190
3191 later = cronemu(ci->when.tm_mon, ci->when.tm_mday, ci->when.tm_hour, ci->when.tm_min);
3192
3193 if (ci->when.tm_wday != -1) {
3194 time_t otherlater = cronemu_wday(ci->when.tm_wday, ci->when.tm_hour, ci->when.tm_min);
3195
3196 if (ci->when.tm_mday == -1) {
3197 later = otherlater;
3198 } else {
3199 later = later < otherlater ? later : otherlater;
3200 }
3201 }
3202
5b0a4722
A
3203 ci->when_next = later;
3204
3205 LIST_FOREACH(ci_iter, &sorted_calendar_events, global_sle) {
3206 if (ci->when_next < ci_iter->when_next) {
3207 LIST_INSERT_BEFORE(ci_iter, ci, global_sle);
3208 break;
3209 }
3210
3211 ci_prev = ci_iter;
ed34e3c3 3212 }
ed34e3c3 3213
5b0a4722
A
3214 if (ci_iter == NULL) {
3215 /* ci must want to fire after every other timer, or there are no timers */
ed34e3c3 3216
5b0a4722
A
3217 if (LIST_EMPTY(&sorted_calendar_events)) {
3218 LIST_INSERT_HEAD(&sorted_calendar_events, ci, global_sle);
ed34e3c3 3219 } else {
5b0a4722 3220 LIST_INSERT_AFTER(ci_prev, ci, global_sle);
ed34e3c3
A
3221 }
3222 }
ed34e3c3 3223
5b0a4722 3224 head_later = LIST_FIRST(&sorted_calendar_events)->when_next;
ed34e3c3 3225
5b0a4722
A
3226 /* Workaround 5225889 */
3227 kevent_mod((uintptr_t)&sorted_calendar_events, EVFILT_TIMER, EV_DELETE, 0, 0, root_jobmgr);
ed34e3c3 3228
5b0a4722
A
3229 if (job_assumes(j, kevent_mod((uintptr_t)&sorted_calendar_events, EVFILT_TIMER, EV_ADD, NOTE_ABSOLUTE|NOTE_SECONDS, head_later, root_jobmgr) != -1)) {
3230 char time_string[100];
3231 size_t time_string_len;
ed34e3c3 3232
5b0a4722
A
3233 ctime_r(&later, time_string);
3234 time_string_len = strlen(time_string);
ed34e3c3 3235
5b0a4722
A
3236 if (time_string_len && time_string[time_string_len - 1] == '\n') {
3237 time_string[time_string_len - 1] = '\0';
3238 }
ed34e3c3 3239
5b0a4722
A
3240 job_log(j, LOG_INFO, "Scheduled to run again at %s", time_string);
3241 }
ed34e3c3
A
3242}
3243
3244void
5b0a4722 3245extract_rcsid_substr(const char *i, char *o, size_t osz)
ed34e3c3 3246{
5b0a4722 3247 char *rcs_rev_tmp = strchr(i, ' ');
ed34e3c3 3248
5b0a4722
A
3249 if (!rcs_rev_tmp) {
3250 strlcpy(o, i, osz);
3251 } else {
3252 strlcpy(o, rcs_rev_tmp + 1, osz);
3253 rcs_rev_tmp = strchr(o, ' ');
3254 if (rcs_rev_tmp) {
3255 *rcs_rev_tmp = '\0';
3256 }
3257 }
ed34e3c3
A
3258}
3259
5b0a4722
A
3260void
3261jobmgr_log_bug(jobmgr_t jm, const char *rcs_rev, const char *path, unsigned int line, const char *test)
ed34e3c3 3262{
5b0a4722
A
3263 int saved_errno = errno;
3264 const char *file = strrchr(path, '/');
3265 char buf[100];
ed34e3c3 3266
5b0a4722 3267 extract_rcsid_substr(rcs_rev, buf, sizeof(buf));
ed34e3c3 3268
5b0a4722
A
3269 if (!file) {
3270 file = path;
3271 } else {
3272 file += 1;
3273 }
ed34e3c3 3274
5b0a4722
A
3275 jobmgr_log(jm, LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test);
3276}
ed34e3c3
A
3277
3278void
5b0a4722 3279job_log_bug(job_t j, const char *rcs_rev, const char *path, unsigned int line, const char *test)
ed34e3c3 3280{
5b0a4722
A
3281 int saved_errno = errno;
3282 const char *file = strrchr(path, '/');
3283 char buf[100];
ed34e3c3 3284
5b0a4722 3285 extract_rcsid_substr(rcs_rev, buf, sizeof(buf));
ed34e3c3 3286
5b0a4722
A
3287 if (!file) {
3288 file = path;
3289 } else {
3290 file += 1;
ed34e3c3 3291 }
5b0a4722
A
3292
3293 job_log(j, LOG_NOTICE, "Bug: %s:%u (%s):%u: %s", file, line, buf, saved_errno, test);
ed34e3c3
A
3294}
3295
3296void
5b0a4722 3297job_logv(job_t j, int pri, int err, const char *msg, va_list ap)
ed34e3c3 3298{
5b0a4722
A
3299 struct runtime_syslog_attr attr = { "com.apple.launchd", j->label, j->mgr->name, pri, getuid(), getpid(), j->p };
3300 char *newmsg;
3301 int oldmask = 0;
3302 size_t newmsgsz;
ed34e3c3 3303
5b0a4722
A
3304 /*
3305 * Hack: If bootstrap_port is set, we must be on the child side of a
3306 * fork(), but before the exec*(). Let's route the log message back to
3307 * launchd proper.
3308 */
3309 if (bootstrap_port) {
3310 return _vproc_logv(pri, err, msg, ap);
3311 }
ed34e3c3 3312
5b0a4722
A
3313 newmsgsz = strlen(msg) + 200;
3314 newmsg = alloca(newmsgsz);
ed34e3c3 3315
5b0a4722
A
3316 if (err) {
3317 snprintf(newmsg, newmsgsz, "%s: %s", msg, strerror(err));
3318 } else {
3319 snprintf(newmsg, newmsgsz, "%s", msg);
3320 }
ed34e3c3 3321
5b0a4722
A
3322 if (j->debug) {
3323 oldmask = setlogmask(LOG_UPTO(LOG_DEBUG));
3324 }
ed34e3c3 3325
5b0a4722 3326 runtime_vsyslog(&attr, newmsg, ap);
ed34e3c3 3327
5b0a4722
A
3328 if (j->debug) {
3329 setlogmask(oldmask);
ed34e3c3
A
3330 }
3331}
3332
3333void
5b0a4722 3334job_log_error(job_t j, int pri, const char *msg, ...)
ed34e3c3 3335{
5b0a4722 3336 va_list ap;
ed34e3c3 3337
5b0a4722
A
3338 va_start(ap, msg);
3339 job_logv(j, pri, errno, msg, ap);
3340 va_end(ap);
ed34e3c3
A
3341}
3342
5b0a4722
A
3343void
3344job_log(job_t j, int pri, const char *msg, ...)
ed34e3c3 3345{
5b0a4722 3346 va_list ap;
ed34e3c3 3347
5b0a4722
A
3348 va_start(ap, msg);
3349 job_logv(j, pri, 0, msg, ap);
3350 va_end(ap);
3351}
ed34e3c3 3352
5b0a4722
A
3353#if 0
3354void
3355jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...)
3356{
3357 va_list ap;
ed34e3c3 3358
5b0a4722
A
3359 va_start(ap, msg);
3360 jobmgr_logv(jm, pri, errno, msg, ap);
3361 va_end(ap);
3362}
3363#endif
ed34e3c3 3364
5b0a4722
A
3365void
3366jobmgr_log(jobmgr_t jm, int pri, const char *msg, ...)
3367{
3368 va_list ap;
ed34e3c3 3369
5b0a4722
A
3370 va_start(ap, msg);
3371 jobmgr_logv(jm, pri, 0, msg, ap);
3372 va_end(ap);
ed34e3c3
A
3373}
3374
5b0a4722
A
3375void
3376jobmgr_logv(jobmgr_t jm, int pri, int err, const char *msg, va_list ap)
ed34e3c3 3377{
5b0a4722
A
3378 char *newmsg;
3379 char *newname;
3380 size_t i, o, jmname_len = strlen(jm->name), newmsgsz;
ed34e3c3 3381
5b0a4722
A
3382 newname = alloca((jmname_len + 1) * 2);
3383 newmsgsz = (jmname_len + 1) * 2 + strlen(msg) + 100;
3384 newmsg = alloca(newmsgsz);
ed34e3c3 3385
5b0a4722
A
3386 for (i = 0, o = 0; i < jmname_len; i++, o++) {
3387 if (jm->name[i] == '%') {
3388 newname[o] = '%';
3389 o++;
3390 }
3391 newname[o] = jm->name[i];
3392 }
3393 newname[o] = '\0';
ed34e3c3 3394
5b0a4722
A
3395 if (err) {
3396 snprintf(newmsg, newmsgsz, "%s: %s: %s", newname, msg, strerror(err));
3397 } else {
3398 snprintf(newmsg, newmsgsz, "%s: %s", newname, msg);
3399 }
ed34e3c3 3400
5b0a4722
A
3401 if (jm->parentmgr) {
3402 jobmgr_logv(jm->parentmgr, pri, 0, newmsg, ap);
3403 } else {
3404 struct runtime_syslog_attr attr = { "com.apple.launchd", "com.apple.launchd", jm->name, pri, getuid(), getpid(), getpid() };
ed34e3c3 3405
5b0a4722
A
3406 runtime_vsyslog(&attr, newmsg, ap);
3407 }
ed34e3c3
A
3408}
3409
3410void
5b0a4722 3411semaphoreitem_ignore(job_t j, struct semaphoreitem *si)
ed34e3c3 3412{
5b0a4722
A
3413 if (si->fd != -1) {
3414 job_log(j, LOG_DEBUG, "Ignoring Vnode: %d", si->fd);
3415 job_assumes(j, kevent_mod(si->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL) != -1);
3416 }
ed34e3c3
A
3417}
3418
3419void
5b0a4722 3420semaphoreitem_watch(job_t j, struct semaphoreitem *si)
ed34e3c3 3421{
f36da725
A
3422 char *parentdir, tmp_path[PATH_MAX];
3423 const char *which_path = si->what;
5b0a4722
A
3424 int saved_errno = 0;
3425 int fflags = 0;
ed34e3c3 3426
5b0a4722
A
3427 switch (si->why) {
3428 case PATH_EXISTS:
3429 fflags = NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE|NOTE_EXTEND|NOTE_WRITE;
3430 break;
3431 case PATH_MISSING:
3432 fflags = NOTE_DELETE|NOTE_RENAME;
3433 break;
3434 case DIR_NOT_EMPTY:
3435 case PATH_CHANGES:
3436 fflags = NOTE_DELETE|NOTE_RENAME|NOTE_REVOKE|NOTE_EXTEND|NOTE_WRITE|NOTE_ATTRIB|NOTE_LINK;
3437 break;
3438 default:
3439 return;
ed34e3c3
A
3440 }
3441
f36da725
A
3442 /* dirname() may modify tmp_path */
3443 strlcpy(tmp_path, si->what, sizeof(tmp_path));
3444
3445 if (!job_assumes(j, (parentdir = dirname(tmp_path)))) {
3446 return;
3447 }
3448
5b0a4722
A
3449 /* See 5321044 for why we do the do-while loop and 5415523 for why ENOENT is checked */
3450 do {
3451 if (si->fd == -1) {
3452 if ((si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY))) == -1) {
f36da725 3453 which_path = parentdir;
5b0a4722
A
3454 si->fd = _fd(open(which_path, O_EVTONLY|O_NOCTTY));
3455 }
3456 }
3457
3458 if (si->fd == -1) {
3459 return job_log_error(j, LOG_ERR, "Path monitoring failed on \"%s\"", which_path);
3460 }
3461
3462 job_log(j, LOG_DEBUG, "Watching Vnode: %d", si->fd);
3463
3464 if (kevent_mod(si->fd, EVFILT_VNODE, EV_ADD, fflags, 0, j) == -1) {
3465 saved_errno = errno;
3466 /*
3467 * The FD can be revoked between the open() and kevent().
3468 * This is similar to the inability for kevents to be
3469 * attached to short lived zombie processes after fork()
3470 * but before kevent().
3471 */
3472 job_assumes(j, runtime_close(si->fd) == 0);
3473 si->fd = -1;
3474 }
3475 } while ((si->fd == -1) && (saved_errno == ENOENT));
3476
3477 if (saved_errno == ENOTSUP) {
3478 /*
3479 * 3524219 NFS needs kqueue support
3480 * 4124079 VFS needs generic kqueue support
3481 * 5226811 EVFILT: Launchd EVFILT_VNODE doesn't work on /dev
3482 */
3483 job_log(j, LOG_DEBUG, "Falling back to polling for path: %s", si->what);
3484
3485 if (!j->poll_for_vfs_changes) {
3486 j->poll_for_vfs_changes = true;
3487 job_assumes(j, kevent_mod((uintptr_t)&j->semaphores, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 3, j) != -1);
3488 }
ed34e3c3
A
3489 }
3490}
3491
5b0a4722
A
3492void
3493semaphoreitem_callback(job_t j, struct kevent *kev)
ed34e3c3 3494{
5b0a4722
A
3495 char invalidation_reason[100] = "";
3496 struct semaphoreitem *si;
3497
3498 SLIST_FOREACH(si, &j->semaphores, sle) {
3499 switch (si->why) {
3500 case PATH_CHANGES:
3501 case PATH_EXISTS:
3502 case PATH_MISSING:
3503 case DIR_NOT_EMPTY:
3504 break;
3505 default:
3506 continue;
3507 }
3508
3509 if (si->fd == (int)kev->ident) {
3510 break;
3511 }
3512 }
3513
3514 if (!job_assumes(j, si != NULL)) {
3515 return;
3516 }
3517
3518 if (NOTE_DELETE & kev->fflags) {
3519 strcat(invalidation_reason, "deleted");
3520 }
3521
3522 if (NOTE_RENAME & kev->fflags) {
3523 if (invalidation_reason[0]) {
3524 strcat(invalidation_reason, "/renamed");
3525 } else {
3526 strcat(invalidation_reason, "renamed");
3527 }
3528 }
3529
3530 if (NOTE_REVOKE & kev->fflags) {
3531 if (invalidation_reason[0]) {
3532 strcat(invalidation_reason, "/revoked");
3533 } else {
3534 strcat(invalidation_reason, "revoked");
3535 }
3536 }
3537
3538 if (invalidation_reason[0]) {
3539 job_log(j, LOG_DEBUG, "Path %s: %s", invalidation_reason, si->what);
3540 job_assumes(j, runtime_close(si->fd) == 0);
3541 si->fd = -1; /* this will get fixed in semaphoreitem_watch() */
3542 }
3543
3544 job_log(j, LOG_DEBUG, "Watch path modified: %s", si->what);
3545
3546 if (si->why == PATH_CHANGES) {
3547 j->start_pending = true;
3548 }
3549
3550 job_dispatch(j, false);
3551}
3552
3553void
3554calendarinterval_new_from_obj_dict_walk(launch_data_t obj, const char *key, void *context)
3555{
3556 struct tm *tmptm = context;
3557 int64_t val;
3558
3559 if (LAUNCH_DATA_INTEGER != launch_data_get_type(obj)) {
3560 /* hack to let caller know something went wrong */
3561 tmptm->tm_sec = -1;
3562 return;
3563 }
3564
3565 val = launch_data_get_integer(obj);
3566
3567 if (strcasecmp(key, LAUNCH_JOBKEY_CAL_MINUTE) == 0) {
3568 tmptm->tm_min = val;
3569 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_HOUR) == 0) {
3570 tmptm->tm_hour = val;
3571 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_DAY) == 0) {
3572 tmptm->tm_mday = val;
3573 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_WEEKDAY) == 0) {
3574 tmptm->tm_wday = val;
3575 } else if (strcasecmp(key, LAUNCH_JOBKEY_CAL_MONTH) == 0) {
3576 tmptm->tm_mon = val;
3577 tmptm->tm_mon -= 1; /* 4798263 cron compatibility */
3578 }
3579}
3580
3581bool
3582calendarinterval_new_from_obj(job_t j, launch_data_t obj)
3583{
3584 struct tm tmptm;
3585
3586 memset(&tmptm, 0, sizeof(0));
3587
3588 tmptm.tm_min = -1;
3589 tmptm.tm_hour = -1;
3590 tmptm.tm_mday = -1;
3591 tmptm.tm_wday = -1;
3592 tmptm.tm_mon = -1;
3593
3594 if (!job_assumes(j, obj != NULL)) {
3595 return false;
3596 }
3597
3598 if (LAUNCH_DATA_DICTIONARY != launch_data_get_type(obj)) {
3599 return false;
3600 }
3601
3602 launch_data_dict_iterate(obj, calendarinterval_new_from_obj_dict_walk, &tmptm);
3603
3604 if (tmptm.tm_sec == -1) {
3605 return false;
3606 }
3607
3608 return calendarinterval_new(j, &tmptm);
3609}
3610
3611bool
3612calendarinterval_new(job_t j, struct tm *w)
3613{
3614 struct calendarinterval *ci = calloc(1, sizeof(struct calendarinterval));
3615
3616 if (!job_assumes(j, ci != NULL)) {
3617 return false;
3618 }
3619
3620 ci->when = *w;
3621 ci->job = j;
3622
3623 SLIST_INSERT_HEAD(&j->cal_intervals, ci, sle);
3624
3625 calendarinterval_setalarm(j, ci);
3626
3627 runtime_add_ref();
3628
3629 return true;
3630}
3631
3632void
3633calendarinterval_delete(job_t j, struct calendarinterval *ci)
3634{
3635 SLIST_REMOVE(&j->cal_intervals, ci, calendarinterval, sle);
3636 LIST_REMOVE(ci, global_sle);
3637
3638 free(ci);
3639
3640 runtime_del_ref();
3641}
3642
3643void
3644calendarinterval_sanity_check(void)
3645{
3646 struct calendarinterval *ci = LIST_FIRST(&sorted_calendar_events);
3647 time_t now = time(NULL);
3648
3649 if (ci && (ci->when_next < now)) {
3650 jobmgr_assumes(root_jobmgr, raise(SIGUSR1) != -1);
3651 }
3652}
3653
3654void
3655calendarinterval_callback(void)
3656{
3657 struct calendarinterval *ci, *ci_next;
3658 time_t now = time(NULL);
3659
3660 LIST_FOREACH_SAFE(ci, &sorted_calendar_events, global_sle, ci_next) {
3661 job_t j = ci->job;
3662
3663 if (ci->when_next > now) {
3664 break;
3665 }
3666
3667 LIST_REMOVE(ci, global_sle);
3668 calendarinterval_setalarm(j, ci);
3669
3670 j->start_pending = true;
3671 job_dispatch(j, false);
3672 }
3673}
3674
3675bool
3676socketgroup_new(job_t j, const char *name, int *fds, unsigned int fd_cnt, bool junkfds)
3677{
3678 struct socketgroup *sg = calloc(1, sizeof(struct socketgroup) + strlen(name) + 1);
3679
3680 if (!job_assumes(j, sg != NULL)) {
3681 return false;
3682 }
ed34e3c3
A
3683
3684 sg->fds = calloc(1, fd_cnt * sizeof(int));
3685 sg->fd_cnt = fd_cnt;
3686 sg->junkfds = junkfds;
3687
5b0a4722 3688 if (!job_assumes(j, sg->fds != NULL)) {
ed34e3c3
A
3689 free(sg);
3690 return false;
3691 }
3692
3693 memcpy(sg->fds, fds, fd_cnt * sizeof(int));
f36da725 3694 strcpy(sg->name_init, name);
ed34e3c3
A
3695
3696 SLIST_INSERT_HEAD(&j->sockets, sg, sle);
3697
5b0a4722
A
3698 runtime_add_ref();
3699
ed34e3c3
A
3700 return true;
3701}
3702
3703void
5b0a4722 3704socketgroup_delete(job_t j, struct socketgroup *sg)
ed34e3c3
A
3705{
3706 unsigned int i;
3707
5b0a4722
A
3708 for (i = 0; i < sg->fd_cnt; i++) {
3709#if 0
3710 struct sockaddr_storage ss;
3711 struct sockaddr_un *sun = (struct sockaddr_un *)&ss;
3712 socklen_t ss_len = sizeof(ss);
3713
3714 /* 5480306 */
3715 if (job_assumes(j, getsockname(sg->fds[i], (struct sockaddr *)&ss, &ss_len) != -1)
3716 && job_assumes(j, ss_len > 0) && (ss.ss_family == AF_UNIX)) {
3717 job_assumes(j, unlink(sun->sun_path) != -1);
3718 /* We might conditionally need to delete a directory here */
3719 }
3720#endif
3721 job_assumes(j, runtime_close(sg->fds[i]) != -1);
3722 }
ed34e3c3
A
3723
3724 SLIST_REMOVE(&j->sockets, sg, socketgroup, sle);
3725
3726 free(sg->fds);
3727 free(sg);
5b0a4722
A
3728
3729 runtime_del_ref();
ed34e3c3
A
3730}
3731
3732void
5b0a4722 3733socketgroup_kevent_mod(job_t j, struct socketgroup *sg, bool do_add)
ed34e3c3 3734{
5b0a4722 3735 struct kevent kev[sg->fd_cnt];
ed34e3c3
A
3736 char buf[10000];
3737 unsigned int i, buf_off = 0;
3738
5b0a4722 3739 if (sg->junkfds) {
ed34e3c3 3740 return;
5b0a4722
A
3741 }
3742
3743 for (i = 0; i < sg->fd_cnt; i++) {
3744 EV_SET(&kev[i], sg->fds[i], EVFILT_READ, do_add ? EV_ADD : EV_DELETE, 0, 0, j);
3745 buf_off += snprintf(buf + buf_off, sizeof(buf) - buf_off, " %d", sg->fds[i]);
3746 }
ed34e3c3 3747
5b0a4722 3748 job_log(j, LOG_DEBUG, "%s Sockets:%s", do_add ? "Watching" : "Ignoring", buf);
ed34e3c3 3749
5b0a4722 3750 job_assumes(j, kevent_bulk_mod(kev, sg->fd_cnt) != -1);
ed34e3c3 3751
5b0a4722
A
3752 for (i = 0; i < sg->fd_cnt; i++) {
3753 job_assumes(j, kev[i].flags & EV_ERROR);
3754 errno = kev[i].data;
3755 job_assumes(j, kev[i].data == 0);
3756 }
ed34e3c3
A
3757}
3758
3759void
5b0a4722 3760socketgroup_ignore(job_t j, struct socketgroup *sg)
ed34e3c3 3761{
5b0a4722
A
3762 socketgroup_kevent_mod(j, sg, false);
3763}
ed34e3c3 3764
5b0a4722
A
3765void
3766socketgroup_watch(job_t j, struct socketgroup *sg)
3767{
3768 socketgroup_kevent_mod(j, sg, true);
ed34e3c3
A
3769}
3770
3771void
5b0a4722 3772socketgroup_callback(job_t j)
ed34e3c3 3773{
5b0a4722 3774 job_dispatch(j, true);
ed34e3c3
A
3775}
3776
3777bool
5b0a4722 3778envitem_new(job_t j, const char *k, const char *v, bool global)
ed34e3c3
A
3779{
3780 struct envitem *ei = calloc(1, sizeof(struct envitem) + strlen(k) + 1 + strlen(v) + 1);
3781
5b0a4722 3782 if (!job_assumes(j, ei != NULL)) {
ed34e3c3 3783 return false;
5b0a4722 3784 }
ed34e3c3 3785
f36da725
A
3786 strcpy(ei->key_init, k);
3787 ei->value = ei->key_init + strlen(k) + 1;
ed34e3c3
A
3788 strcpy(ei->value, v);
3789
3790 if (global) {
3791 SLIST_INSERT_HEAD(&j->global_env, ei, sle);
3792 } else {
3793 SLIST_INSERT_HEAD(&j->env, ei, sle);
3794 }
3795
5b0a4722
A
3796 job_log(j, LOG_DEBUG, "Added environmental variable: %s=%s", k, v);
3797
ed34e3c3
A
3798 return true;
3799}
3800
3801void
5b0a4722 3802envitem_delete(job_t j, struct envitem *ei, bool global)
ed34e3c3
A
3803{
3804 if (global) {
3805 SLIST_REMOVE(&j->global_env, ei, envitem, sle);
3806 } else {
3807 SLIST_REMOVE(&j->env, ei, envitem, sle);
3808 }
3809
3810 free(ei);
3811}
3812
3813void
3814envitem_setup(launch_data_t obj, const char *key, void *context)
3815{
5b0a4722 3816 job_t j = context;
ed34e3c3 3817
5b0a4722 3818 if (launch_data_get_type(obj) != LAUNCH_DATA_STRING) {
ed34e3c3 3819 return;
5b0a4722 3820 }
ed34e3c3
A
3821
3822 envitem_new(j, key, launch_data_get_string(obj), j->importing_global_env);
3823}
3824
3825bool
5b0a4722 3826limititem_update(job_t j, int w, rlim_t r)
ed34e3c3
A
3827{
3828 struct limititem *li;
3829
3830 SLIST_FOREACH(li, &j->limits, sle) {
5b0a4722 3831 if (li->which == w) {
ed34e3c3 3832 break;
5b0a4722 3833 }
ed34e3c3
A
3834 }
3835
3836 if (li == NULL) {
3837 li = calloc(1, sizeof(struct limititem));
3838
5b0a4722 3839 if (!job_assumes(j, li != NULL)) {
ed34e3c3 3840 return false;
5b0a4722
A
3841 }
3842
3843 SLIST_INSERT_HEAD(&j->limits, li, sle);
ed34e3c3
A
3844
3845 li->which = w;
3846 }
3847
3848 if (j->importing_hard_limits) {
3849 li->lim.rlim_max = r;
3850 li->sethard = true;
3851 } else {
3852 li->lim.rlim_cur = r;
3853 li->setsoft = true;
3854 }
3855
3856 return true;
3857}
3858
3859void
5b0a4722 3860limititem_delete(job_t j, struct limititem *li)
ed34e3c3
A
3861{
3862 SLIST_REMOVE(&j->limits, li, limititem, sle);
3863
3864 free(li);
3865}
3866
f36da725 3867#if HAVE_SANDBOX
5b0a4722
A
3868void
3869seatbelt_setup_flags(launch_data_t obj, const char *key, void *context)
3870{
3871 job_t j = context;
3872
3873 if (launch_data_get_type(obj) != LAUNCH_DATA_BOOL) {
3874 job_log(j, LOG_WARNING, "Sandbox flag value must be boolean: %s", key);
3875 return;
3876 }
3877
3878 if (launch_data_get_bool(obj) == false) {
3879 return;
3880 }
3881
3882 if (strcasecmp(key, LAUNCH_JOBKEY_SANDBOX_NAMED) == 0) {
3883 j->seatbelt_flags |= SANDBOX_NAMED;
3884 }
3885}
f36da725 3886#endif
5b0a4722 3887
ed34e3c3
A
3888void
3889limititem_setup(launch_data_t obj, const char *key, void *context)
3890{
5b0a4722 3891 job_t j = context;
ed34e3c3
A
3892 int i, limits_cnt = (sizeof(launchd_keys2limits) / sizeof(launchd_keys2limits[0]));
3893 rlim_t rl;
3894
5b0a4722 3895 if (launch_data_get_type(obj) != LAUNCH_DATA_INTEGER) {
ed34e3c3 3896 return;
5b0a4722 3897 }
ed34e3c3
A
3898
3899 rl = launch_data_get_integer(obj);
3900
3901 for (i = 0; i < limits_cnt; i++) {
5b0a4722 3902 if (strcasecmp(launchd_keys2limits[i].key, key) == 0) {
ed34e3c3 3903 break;
5b0a4722 3904 }
ed34e3c3
A
3905 }
3906
5b0a4722 3907 if (i == limits_cnt) {
ed34e3c3 3908 return;
5b0a4722 3909 }
ed34e3c3
A
3910
3911 limititem_update(j, launchd_keys2limits[i].val, rl);
3912}
3913
3914bool
5b0a4722 3915job_useless(job_t j)
ed34e3c3 3916{
5b0a4722
A
3917 /* Yes, j->unload_at_exit and j->only_once seem the same, but they'll differ someday... */
3918
3919 if ((j->unload_at_exit || j->only_once) && j->start_time != 0) {
3920 if (j->unload_at_exit && j->j_port) {
3921 return false;
3922 }
ed34e3c3
A
3923 job_log(j, LOG_INFO, "Exited. Was only configured to run once.");
3924 return true;
5b0a4722
A
3925 } else if (j->removal_pending) {
3926 job_log(j, LOG_DEBUG, "Exited while removal was pending.");
ed34e3c3 3927 return true;
f36da725 3928 } else if (j->mgr->shutting_down && (j->hopefully_exits_first || j->mgr->hopefully_first_cnt == 0)) {
5b0a4722 3929 job_log(j, LOG_DEBUG, "Exited while shutdown in progress. Processes remaining: %lu/%lu", total_children, total_anon_children);
ed34e3c3 3930 return true;
5b0a4722
A
3931 } else if (j->legacy_mach_job) {
3932 if (SLIST_EMPTY(&j->machservices)) {
3933 job_log(j, LOG_INFO, "Garbage collecting");
3934 return true;
3935 } else if (!j->checkedin) {
3936 job_log(j, LOG_WARNING, "Failed to check-in!");
3937 return true;
3938 }
ed34e3c3
A
3939 }
3940
3941 return false;
3942}
3943
3944bool
5b0a4722 3945job_keepalive(job_t j)
ed34e3c3
A
3946{
3947 mach_msg_type_number_t statusCnt;
3948 mach_port_status_t status;
3949 struct semaphoreitem *si;
3950 struct machservice *ms;
3951 struct stat sb;
3952 bool good_exit = (WIFEXITED(j->last_exit_status) && WEXITSTATUS(j->last_exit_status) == 0);
ed34e3c3 3953
f36da725
A
3954 if (j->mgr->shutting_down) {
3955 return false;
3956 }
3957
5b0a4722
A
3958 /*
3959 * 5066316
3960 *
3961 * We definitely need to revisit this after Leopard ships. Please see
3962 * launchctl.c for the other half of this hack.
3963 */
3964 if (j->mgr->global_on_demand_cnt > 0 && strcmp(j->label, "com.apple.kextd") != 0) {
3965 return false;
3966 }
3967
3968 if (j->start_pending) {
3969 job_log(j, LOG_DEBUG, "KeepAlive check: Pent-up non-IPC launch criteria.");
ed34e3c3
A
3970 return true;
3971 }
3972
3973 if (!j->ondemand) {
3974 job_log(j, LOG_DEBUG, "KeepAlive check: job configured to run continuously.");
3975 return true;
3976 }
3977
3978 SLIST_FOREACH(ms, &j->machservices, sle) {
3979 statusCnt = MACH_PORT_RECEIVE_STATUS_COUNT;
3980 if (mach_port_get_attributes(mach_task_self(), ms->port, MACH_PORT_RECEIVE_STATUS,
5b0a4722 3981 (mach_port_info_t)&status, &statusCnt) != KERN_SUCCESS) {
ed34e3c3 3982 continue;
5b0a4722 3983 }
ed34e3c3
A
3984 if (status.mps_msgcount) {
3985 job_log(j, LOG_DEBUG, "KeepAlive check: job restarted due to %d queued Mach messages on service: %s",
3986 status.mps_msgcount, ms->name);
3987 return true;
3988 }
3989 }
3990
3991
3992 SLIST_FOREACH(si, &j->semaphores, sle) {
3993 bool wanted_state = false;
5b0a4722
A
3994 int qdir_file_cnt;
3995 job_t other_j;
3996
ed34e3c3
A
3997 switch (si->why) {
3998 case NETWORK_UP:
3999 wanted_state = true;
4000 case NETWORK_DOWN:
4001 if (network_up == wanted_state) {
5b0a4722 4002 job_log(j, LOG_DEBUG, "KeepAlive: The network is %s.", wanted_state ? "up" : "down");
ed34e3c3
A
4003 return true;
4004 }
4005 break;
4006 case SUCCESSFUL_EXIT:
4007 wanted_state = true;
4008 case FAILED_EXIT:
4009 if (good_exit == wanted_state) {
5b0a4722
A
4010 job_log(j, LOG_DEBUG, "KeepAlive: The exit state was %s.", wanted_state ? "successful" : "failure");
4011 return true;
4012 }
4013 break;
4014 case OTHER_JOB_ENABLED:
4015 wanted_state = true;
4016 case OTHER_JOB_DISABLED:
4017 if ((bool)job_find(si->what) == wanted_state) {
4018 job_log(j, LOG_DEBUG, "KeepAlive: The following job is %s: %s", wanted_state ? "enabled" : "disabled", si->what);
ed34e3c3
A
4019 return true;
4020 }
4021 break;
5b0a4722
A
4022 case OTHER_JOB_ACTIVE:
4023 wanted_state = true;
4024 case OTHER_JOB_INACTIVE:
4025 if ((other_j = job_find(si->what))) {
4026 if ((bool)other_j->p == wanted_state) {
4027 job_log(j, LOG_DEBUG, "KeepAlive: The following job is %s: %s", wanted_state ? "active" : "inactive", si->what);
4028 return true;
4029 }
4030 }
4031 break;
ed34e3c3
A
4032 case PATH_EXISTS:
4033 wanted_state = true;
4034 case PATH_MISSING:
4035 if ((bool)(stat(si->what, &sb) == 0) == wanted_state) {
5b0a4722
A
4036 if (si->fd != -1) {
4037 job_assumes(j, runtime_close(si->fd) == 0);
4038 si->fd = -1;
4039 }
4040 job_log(j, LOG_DEBUG, "KeepAlive: The following path %s: %s", wanted_state ? "exists" : "is missing", si->what);
4041 return true;
4042 }
4043 break;
4044 case PATH_CHANGES:
4045 break;
4046 case DIR_NOT_EMPTY:
4047 if (-1 == (qdir_file_cnt = dir_has_files(j, si->what))) {
4048 job_log_error(j, LOG_ERR, "Failed to count the number of files in \"%s\"", si->what);
4049 } else if (qdir_file_cnt > 0) {
4050 job_log(j, LOG_DEBUG, "KeepAlive: Directory is not empty: %s", si->what);
ed34e3c3
A
4051 return true;
4052 }
ed34e3c3
A
4053 break;
4054 }
4055 }
4056
ed34e3c3
A
4057 return false;
4058}
4059
4060const char *
5b0a4722 4061job_prog(job_t j)
ed34e3c3
A
4062{
4063 if (j->prog) {
4064 return j->prog;
4065 } else if (j->argv) {
4066 return j->argv[0];
4067 } else {
4068 return "";
4069 }
4070}
4071
5b0a4722
A
4072const char *
4073job_active(job_t j)
ed34e3c3
A
4074{
4075 struct machservice *ms;
4076
5b0a4722
A
4077 if (j->p) {
4078 return "PID is still valid";
4079 }
ed34e3c3 4080
5b0a4722
A
4081 if (j->mgr->shutting_down && j->log_redirect_fd) {
4082 job_assumes(j, runtime_close(j->log_redirect_fd) != -1);
4083 j->log_redirect_fd = 0;
4084 }
ed34e3c3 4085
5b0a4722
A
4086 if (j->log_redirect_fd) {
4087 if (job_assumes(j, j->wait4pipe_eof)) {
4088 return "Standard out/error is still valid";
4089 } else {
4090 job_assumes(j, runtime_close(j->log_redirect_fd) != -1);
4091 j->log_redirect_fd = 0;
ed34e3c3 4092 }
5b0a4722
A
4093 }
4094
4095 if (j->priv_port_has_senders) {
4096 return "Privileged Port still has outstanding senders";
ed34e3c3
A
4097 }
4098
4099 SLIST_FOREACH(ms, &j->machservices, sle) {
5b0a4722
A
4100 if (ms->recv && ms->isActive) {
4101 return "Mach service is still active";
4102 }
ed34e3c3
A
4103 }
4104
5b0a4722 4105 return NULL;
ed34e3c3
A
4106}
4107
5b0a4722
A
4108void
4109machservice_watch(job_t j, struct machservice *ms)
ed34e3c3 4110{
5b0a4722
A
4111 if (ms->recv) {
4112 job_assumes(j, runtime_add_mport(ms->port, NULL, 0) == KERN_SUCCESS);
4113 }
ed34e3c3
A
4114}
4115
5b0a4722
A
4116void
4117machservice_ignore(job_t j, struct machservice *ms)
ed34e3c3 4118{
5b0a4722 4119 job_assumes(j, runtime_remove_mport(ms->port) == KERN_SUCCESS);
ed34e3c3
A
4120}
4121
4122void
5b0a4722 4123machservice_resetport(job_t j, struct machservice *ms)
ed34e3c3 4124{
5b0a4722
A
4125 LIST_REMOVE(ms, port_hash_sle);
4126 job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS);
4127 job_assumes(j, launchd_mport_deallocate(ms->port) == KERN_SUCCESS);
4128 ms->gen_num++;
4129 job_assumes(j, launchd_mport_create_recv(&ms->port) == KERN_SUCCESS);
4130 job_assumes(j, launchd_mport_make_send(ms->port) == KERN_SUCCESS);
4131 LIST_INSERT_HEAD(&port_hash[HASH_PORT(ms->port)], ms, port_hash_sle);
ed34e3c3
A
4132}
4133
4134struct machservice *
5b0a4722 4135machservice_new(job_t j, const char *name, mach_port_t *serviceport, bool pid_local)
ed34e3c3
A
4136{
4137 struct machservice *ms;
4138
5b0a4722 4139 if ((ms = calloc(1, sizeof(struct machservice) + strlen(name) + 1)) == NULL) {
ed34e3c3 4140 return NULL;
5b0a4722 4141 }
ed34e3c3 4142
5b0a4722 4143 strcpy((char *)ms->name, name);
ed34e3c3 4144 ms->job = j;
5b0a4722 4145 ms->per_pid = pid_local;
ed34e3c3
A
4146
4147 if (*serviceport == MACH_PORT_NULL) {
5b0a4722 4148 if (!job_assumes(j, launchd_mport_create_recv(&ms->port) == KERN_SUCCESS)) {
ed34e3c3 4149 goto out_bad;
5b0a4722 4150 }
ed34e3c3 4151
5b0a4722 4152 if (!job_assumes(j, launchd_mport_make_send(ms->port) == KERN_SUCCESS)) {
ed34e3c3 4153 goto out_bad2;
5b0a4722 4154 }
ed34e3c3 4155 *serviceport = ms->port;
ed34e3c3
A
4156 ms->recv = true;
4157 } else {
4158 ms->port = *serviceport;
4159 ms->isActive = true;
4160 }
4161
4162 SLIST_INSERT_HEAD(&j->machservices, ms, sle);
5b0a4722
A
4163 LIST_INSERT_HEAD(&j->mgr->ms_hash[hash_ms(ms->name)], ms, name_hash_sle);
4164 LIST_INSERT_HEAD(&port_hash[HASH_PORT(ms->port)], ms, port_hash_sle);
ed34e3c3
A
4165
4166 job_log(j, LOG_INFO, "Mach service added: %s", name);
4167
4168 return ms;
4169out_bad2:
5b0a4722 4170 job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS);
ed34e3c3
A
4171out_bad:
4172 free(ms);
4173 return NULL;
4174}
4175
4176bootstrap_status_t
4177machservice_status(struct machservice *ms)
4178{
4179 if (ms->isActive) {
4180 return BOOTSTRAP_STATUS_ACTIVE;
4181 } else if (ms->job->ondemand) {
4182 return BOOTSTRAP_STATUS_ON_DEMAND;
4183 } else {
4184 return BOOTSTRAP_STATUS_INACTIVE;
4185 }
4186}
4187
4188void
5b0a4722 4189job_setup_exception_port(job_t j, task_t target_task)
ed34e3c3 4190{
fe044cc9 4191 struct machservice *ms;
ed34e3c3 4192 thread_state_flavor_t f = 0;
fe044cc9 4193 mach_port_t exc_port = the_exception_server;
5b0a4722 4194
fe044cc9
A
4195 if (j->alt_exc_handler) {
4196 ms = jobmgr_lookup_service(j->mgr, j->alt_exc_handler, true, 0);
4197 if (ms) {
4198 exc_port = machservice_port(ms);
4199 } else {
4200 job_log(j, LOG_WARNING, "Falling back to default Mach exception handler. Could not find: %s", j->alt_exc_handler);
4201 }
4202 } else if (j->internal_exc_handler) {
4203 exc_port = runtime_get_kernel_port();
4204 } else if (!exc_port) {
5b0a4722
A
4205 return;
4206 }
ed34e3c3 4207
ef398931 4208#if defined (__ppc__) || defined (__ppc64__)
ed34e3c3 4209 f = PPC_THREAD_STATE64;
ef398931 4210#elif defined(__i386__) || defined(__x86_64__)
ed34e3c3 4211 f = x86_THREAD_STATE;
f36da725
A
4212#elif defined(__arm__)
4213 f = ARM_THREAD_STATE;
4214#else
4215#error "unknown architecture"
ed34e3c3
A
4216#endif
4217
5b0a4722 4218 if (target_task) {
fe044cc9 4219 job_assumes(j, task_set_exception_ports(target_task, EXC_MASK_CRASH, exc_port,
5b0a4722 4220 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f) == KERN_SUCCESS);
fe044cc9 4221 } else if (getpid() == 1 && the_exception_server) {
5b0a4722
A
4222 mach_port_t mhp = mach_host_self();
4223 job_assumes(j, host_set_exception_ports(mhp, EXC_MASK_CRASH, the_exception_server,
4224 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, f) == KERN_SUCCESS);
4225 job_assumes(j, launchd_mport_deallocate(mhp) == KERN_SUCCESS);
4226 }
4227
4228}
4229
4230void
4231job_set_exeception_port(job_t j, mach_port_t port)
4232{
4233 if (!the_exception_server) {
4234 the_exception_server = port;
4235 job_setup_exception_port(j, 0);
4236 } else {
4237 job_log(j, LOG_WARNING, "The exception server is already claimed!");
4238 }
4239}
4240
4241void
4242machservice_setup_options(launch_data_t obj, const char *key, void *context)
4243{
4244 struct machservice *ms = context;
4245 mach_port_t mhp = mach_host_self();
4246 int which_port;
4247 bool b;
4248
4249 if (!job_assumes(ms->job, mhp != MACH_PORT_NULL)) {
ed34e3c3
A
4250 return;
4251 }
4252
4253 switch (launch_data_get_type(obj)) {
4254 case LAUNCH_DATA_INTEGER:
4255 which_port = launch_data_get_integer(obj);
4256 if (strcasecmp(key, LAUNCH_JOBKEY_MACH_TASKSPECIALPORT) == 0) {
5b0a4722
A
4257 switch (which_port) {
4258 case TASK_KERNEL_PORT:
4259 case TASK_HOST_PORT:
4260 case TASK_NAME_PORT:
4261 case TASK_BOOTSTRAP_PORT:
4262 /* I find it a little odd that zero isn't reserved in the header */
4263 case 0:
4264 job_log(ms->job, LOG_WARNING, "Tried to set a reserved task special port: %d", which_port);
4265 break;
4266 default:
4267 ms->special_port_num = which_port;
4268 SLIST_INSERT_HEAD(&special_ports, ms, special_port_sle);
4269 break;
4270 }
ed34e3c3 4271 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HOSTSPECIALPORT) == 0 && getpid() == 1) {
5b0a4722
A
4272 if (which_port > HOST_MAX_SPECIAL_KERNEL_PORT) {
4273 job_assumes(ms->job, (errno = host_set_special_port(mhp, which_port, ms->port)) == KERN_SUCCESS);
4274 } else {
4275 job_log(ms->job, LOG_WARNING, "Tried to set a reserved host special port: %d", which_port);
4276 }
ed34e3c3
A
4277 }
4278 case LAUNCH_DATA_BOOL:
4279 b = launch_data_get_bool(obj);
5b0a4722
A
4280 if (strcasecmp(key, LAUNCH_JOBKEY_MACH_ENTERKERNELDEBUGGERONCLOSE) == 0) {
4281 ms->debug_on_close = b;
4282 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_RESETATCLOSE) == 0) {
ed34e3c3
A
4283 ms->reset = b;
4284 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN) == 0) {
4285 ms->hide = b;
4286 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_EXCEPTIONSERVER) == 0) {
5b0a4722 4287 job_set_exeception_port(ms->job, ms->port);
ed34e3c3
A
4288 } else if (strcasecmp(key, LAUNCH_JOBKEY_MACH_KUNCSERVER) == 0) {
4289 ms->kUNCServer = b;
5b0a4722 4290 job_assumes(ms->job, host_set_UNDServer(mhp, ms->port) == KERN_SUCCESS);
ed34e3c3
A
4291 }
4292 break;
5b0a4722
A
4293 case LAUNCH_DATA_DICTIONARY:
4294 job_set_exeception_port(ms->job, ms->port);
4295 break;
ed34e3c3
A
4296 default:
4297 break;
4298 }
4299
5b0a4722 4300 job_assumes(ms->job, launchd_mport_deallocate(mhp) == KERN_SUCCESS);
ed34e3c3
A
4301}
4302
4303void
4304machservice_setup(launch_data_t obj, const char *key, void *context)
4305{
5b0a4722 4306 job_t j = context;
ed34e3c3
A
4307 struct machservice *ms;
4308 mach_port_t p = MACH_PORT_NULL;
4309
5b0a4722 4310 if ((ms = jobmgr_lookup_service(j->mgr, key, false, 0))) {
ed34e3c3
A
4311 job_log(j, LOG_WARNING, "Conflict with job: %s over Mach service: %s", ms->job->label, key);
4312 return;
4313 }
4314
5b0a4722 4315 if ((ms = machservice_new(j, key, &p, false)) == NULL) {
ed34e3c3
A
4316 job_log_error(j, LOG_WARNING, "Cannot add service: %s", key);
4317 return;
4318 }
4319
4320 ms->isActive = false;
4321
4322 if (launch_data_get_type(obj) == LAUNCH_DATA_DICTIONARY) {
4323 launch_data_dict_iterate(obj, machservice_setup_options, ms);
4324 }
4325}
4326
5b0a4722
A
4327jobmgr_t
4328jobmgr_do_garbage_collection(jobmgr_t jm)
ed34e3c3 4329{
5b0a4722
A
4330 jobmgr_t jmi, jmn;
4331 job_t ji, jn;
ed34e3c3 4332
5b0a4722
A
4333 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
4334 jobmgr_do_garbage_collection(jmi);
4335 }
ed34e3c3 4336
5b0a4722
A
4337 if (!jm->shutting_down) {
4338 return jm;
4339 }
4340
4341 jobmgr_log(jm, LOG_DEBUG, "Garbage collecting.");
4342
4343 /*
4344 * Normally, we wait for all resources of a job (Unix PIDs/FDs and Mach ports)
4345 * to reset before we conider the job truly dead and ready to be spawned again.
4346 *
4347 * In order to work around 5487724 and 3456090, we're going to call reboot()
4348 * when the last PID dies and not wait for the associated resources to reset.
4349 */
4350 if (getpid() == 1 && jm->parentmgr == NULL && total_children == 0) {
4351 jobmgr_log(jm, LOG_DEBUG, "About to force a call to: reboot(%s)", reboot_flags_to_C_names(jm->reboot_flags));
4352 runtime_closelog();
4353 jobmgr_assumes(jm, reboot(jm->reboot_flags) != -1);
4354 }
4355
4356 if (jm->hopefully_first_cnt) {
4357 return jm;
4358 }
4359
4360 if (jm->parentmgr && jm->parentmgr->shutting_down && jm->parentmgr->hopefully_first_cnt) {
4361 return jm;
4362 }
4363
4364 if (!jm->sent_stop_to_normal_jobs) {
4365 jobmgr_log(jm, LOG_DEBUG, "Asking \"normal\" jobs to exit.");
4366
4367 LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
4368 if (!job_active(ji)) {
4369 job_remove(ji);
4370 } else if (!ji->hopefully_exits_last) {
4371 job_stop(ji);
4372 }
4373 }
4374
4375 jm->sent_stop_to_normal_jobs = true;
ed34e3c3 4376 }
5b0a4722
A
4377
4378 if (jm->normal_active_cnt) {
4379 return jm;
4380 }
4381
4382 if (!jm->sent_stop_to_hopefully_last_jobs) {
4383 jobmgr_log(jm, LOG_DEBUG, "Asking \"hopefully last\" jobs to exit.");
4384
4385 LIST_FOREACH(ji, &jm->jobs, sle) {
4386 if (ji->p && ji->anonymous) {
4387 continue;
4388 } else if (ji->p && job_assumes(ji, ji->hopefully_exits_last)) {
4389 job_stop(ji);
4390 }
4391 }
4392
4393 jm->sent_stop_to_hopefully_last_jobs = true;
4394 }
4395
4396 if (!SLIST_EMPTY(&jm->submgrs)) {
4397 return jm;
4398 }
4399
4400 LIST_FOREACH(ji, &jm->jobs, sle) {
4401 if (!ji->anonymous) {
4402 return jm;
4403 }
4404 }
4405
4406 jobmgr_log_stray_children(jm);
4407 jobmgr_remove(jm);
4408 return NULL;
ed34e3c3
A
4409}
4410
4411void
5b0a4722 4412jobmgr_log_stray_children(jobmgr_t jm)
ed34e3c3 4413{
5b0a4722 4414 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
f36da725 4415 size_t i, kp_cnt, len = sizeof(struct kinfo_proc) * get_kern_max_proc();
5b0a4722 4416 struct kinfo_proc *kp;
ed34e3c3 4417
f36da725
A
4418#if TARGET_OS_EMBEDDED
4419 if (!do_apple_internal_magic) {
4420 return;
4421 }
4422#endif
5b0a4722
A
4423 if (jm->parentmgr || getpid() != 1) {
4424 return;
4425 }
ed34e3c3 4426
5b0a4722
A
4427 if (!jobmgr_assumes(jm, (kp = malloc(len)) != NULL)) {
4428 return;
4429 }
4430 if (!jobmgr_assumes(jm, sysctl(mib, 3, kp, &len, NULL, 0) != -1)) {
4431 goto out;
4432 }
4433
4434 kp_cnt = len / sizeof(struct kinfo_proc);
ed34e3c3 4435
5b0a4722
A
4436 for (i = 0; i < kp_cnt; i++) {
4437 pid_t p_i = kp[i].kp_proc.p_pid;
4438 pid_t pp_i = kp[i].kp_eproc.e_ppid;
4439 pid_t pg_i = kp[i].kp_eproc.e_pgid;
4440 const char *z = (kp[i].kp_proc.p_stat == SZOMB) ? "zombie " : "";
4441 const char *n = kp[i].kp_proc.p_comm;
4442
4443 if (p_i == 0 || p_i == 1) {
4444 continue;
ed34e3c3 4445 }
5b0a4722
A
4446
4447 jobmgr_log(jm, LOG_WARNING, "Stray %sprocess at shutdown: PID %u PPID %u PGID %u %s", z, p_i, pp_i, pg_i, n);
4448
4449 /*
4450 * The kernel team requested that launchd not do this for Leopard.
4451 * jobmgr_assumes(jm, runtime_kill(p_i, SIGKILL) != -1);
4452 */
ed34e3c3
A
4453 }
4454
5b0a4722
A
4455out:
4456 free(kp);
ed34e3c3
A
4457}
4458
5b0a4722
A
4459jobmgr_t
4460jobmgr_parent(jobmgr_t jm)
ed34e3c3 4461{
5b0a4722
A
4462 return jm->parentmgr;
4463}
ed34e3c3 4464
5b0a4722
A
4465void
4466job_uncork_fork(job_t j)
4467{
4468 pid_t c = j->p;
4469
4470 job_log(j, LOG_DEBUG, "Uncorking the fork().");
4471 /* this unblocks the child and avoids a race
4472 * between the above fork() and the kevent_mod() */
4473 job_assumes(j, write(j->forkfd, &c, sizeof(c)) == sizeof(c));
4474 job_assumes(j, runtime_close(j->forkfd) != -1);
4475 j->forkfd = 0;
4476}
4477
4478jobmgr_t
4479jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, bool sflag, const char *name)
4480{
4481 mach_msg_size_t mxmsgsz;
4482 job_t bootstrapper = NULL;
4483 jobmgr_t jmr;
4484
4485 launchd_assert(offsetof(struct jobmgr_s, kqjobmgr_callback) == 0);
4486
4487 if (jm && requestorport == MACH_PORT_NULL) {
4488 jobmgr_log(jm, LOG_ERR, "Mach sub-bootstrap create request requires a requester port");
ed34e3c3
A
4489 return NULL;
4490 }
4491
5b0a4722 4492 jmr = calloc(1, sizeof(struct jobmgr_s) + (name ? (strlen(name) + 1) : 128));
ed34e3c3 4493
5b0a4722 4494 if (jmr == NULL) {
ed34e3c3 4495 return NULL;
5b0a4722 4496 }
ed34e3c3 4497
5b0a4722 4498 jmr->kqjobmgr_callback = jobmgr_callback;
f36da725 4499 strcpy(jmr->name_init, name ? name : "Under construction");
5b0a4722
A
4500
4501 jmr->req_port = requestorport;
4502
4503 if ((jmr->parentmgr = jm)) {
4504 SLIST_INSERT_HEAD(&jm->submgrs, jmr, sle);
4505 }
4506
4507 if (jm && !jobmgr_assumes(jmr, launchd_mport_notify_req(jmr->req_port, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS)) {
ed34e3c3
A
4508 goto out_bad;
4509 }
4510
5b0a4722
A
4511 if (transfer_port != MACH_PORT_NULL) {
4512 jobmgr_assumes(jmr, jm != NULL);
4513 jmr->jm_port = transfer_port;
4514 } else if (!jm && getpid() != 1) {
4515 char *trusted_fd = getenv(LAUNCHD_TRUSTED_FD_ENV);
4516 name_t service_buf;
4517
4518 snprintf(service_buf, sizeof(service_buf), "com.apple.launchd.peruser.%u", getuid());
4519
4520 if (!jobmgr_assumes(jmr, bootstrap_check_in(bootstrap_port, service_buf, &jmr->jm_port) == 0)) {
4521 goto out_bad;
4522 }
4523
4524 if (trusted_fd) {
4525 int dfd, lfd = strtol(trusted_fd, NULL, 10);
4526
4527 if ((dfd = dup(lfd)) >= 0) {
4528 jobmgr_assumes(jmr, runtime_close(dfd) != -1);
4529 jobmgr_assumes(jmr, runtime_close(lfd) != -1);
4530 }
4531
4532 unsetenv(LAUNCHD_TRUSTED_FD_ENV);
4533 }
4534
4535 /* cut off the Libc cache, we don't want to deadlock against ourself */
4536 inherited_bootstrap_port = bootstrap_port;
4537 bootstrap_port = MACH_PORT_NULL;
4538 launchd_assert(launchd_mport_notify_req(inherited_bootstrap_port, MACH_NOTIFY_DEAD_NAME) == KERN_SUCCESS);
ed34e3c3 4539
5b0a4722
A
4540 /* We set this explicitly as we start each child */
4541 launchd_assert(launchd_set_bport(MACH_PORT_NULL) == KERN_SUCCESS);
4542 } else if (!jobmgr_assumes(jmr, launchd_mport_create_recv(&jmr->jm_port) == KERN_SUCCESS)) {
ed34e3c3 4543 goto out_bad;
5b0a4722 4544 }
ed34e3c3 4545
5b0a4722 4546 if (!name) {
f36da725 4547 sprintf(jmr->name_init, "%u", MACH_PORT_INDEX(jmr->jm_port));
ed34e3c3
A
4548 }
4549
5b0a4722
A
4550 /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */
4551 mxmsgsz = sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem);
4552 if (job_mig_protocol_vproc_subsystem.maxsize > mxmsgsz) {
4553 mxmsgsz = job_mig_protocol_vproc_subsystem.maxsize;
4554 }
4555
4556 if (!jm) {
4557 jobmgr_assumes(jmr, kevent_mod(SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr) != -1);
4558 jobmgr_assumes(jmr, kevent_mod(SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, jmr) != -1);
4559 jobmgr_assumes(jmr, kevent_mod(0, EVFILT_FS, EV_ADD, VQ_MOUNT|VQ_UNMOUNT|VQ_UPDATE, 0, jmr) != -1);
4560 }
4561
4562 if (name) {
4563 bootstrapper = jobmgr_init_session(jmr, name, sflag);
4564 }
4565
4566 if (!bootstrapper || !bootstrapper->weird_bootstrap) {
4567 if (!jobmgr_assumes(jmr, runtime_add_mport(jmr->jm_port, protocol_vproc_server, mxmsgsz) == KERN_SUCCESS)) {
4568 goto out_bad;
4569 }
4570 }
4571
4572 jobmgr_log(jmr, LOG_DEBUG, "Created job manager%s%s", jm ? " with parent: " : ".", jm ? jm->name : "");
4573
4574 if (bootstrapper) {
4575 jobmgr_assumes(jmr, job_dispatch(bootstrapper, true) != NULL);
4576 }
4577
4578 if (jmr->parentmgr) {
4579 runtime_add_ref();
4580 }
4581
4582 return jmr;
ed34e3c3
A
4583
4584out_bad:
5b0a4722
A
4585 if (jmr) {
4586 jobmgr_remove(jmr);
4587 }
ed34e3c3
A
4588 return NULL;
4589}
4590
5b0a4722
A
4591job_t
4592jobmgr_init_session(jobmgr_t jm, const char *session_type, bool sflag)
4593{
4594 const char *bootstrap_tool[] = { "/bin/launchctl", "bootstrap", "-S", session_type, sflag ? "-s" : NULL, NULL };
4595 char thelabel[1000];
4596 job_t bootstrapper;
4597
4598 snprintf(thelabel, sizeof(thelabel), "com.apple.launchctl.%s", session_type);
4599 bootstrapper = job_new(jm, thelabel, NULL, bootstrap_tool);
4600 if (jobmgr_assumes(jm, bootstrapper != NULL) && (jm->parentmgr || getuid())) {
4601 char buf[100];
4602
4603 /* <rdar://problem/5042202> launchd-201: can't ssh in with AFP OD account (hangs) */
4604 snprintf(buf, sizeof(buf), "0x%X:0:0", getuid());
4605 envitem_new(bootstrapper, "__CF_USER_TEXT_ENCODING", buf, false);
4606 bootstrapper->weird_bootstrap = true;
4607 jobmgr_assumes(jm, job_setup_machport(bootstrapper));
4608 }
4609
4610 jm->session_initialized = true;
4611
4612 return bootstrapper;
4613}
4614
4615jobmgr_t
4616jobmgr_delete_anything_with_port(jobmgr_t jm, mach_port_t port)
ed34e3c3
A
4617{
4618 struct machservice *ms, *next_ms;
5b0a4722 4619 jobmgr_t jmi, jmn;
ed34e3c3
A
4620
4621 /* Mach ports, unlike Unix descriptors, are reference counted. In other
4622 * words, when some program hands us a second or subsequent send right
4623 * to a port we already have open, the Mach kernel gives us the same
4624 * port number back and increments an reference count associated with
4625 * the port. This forces us, when discovering that a receive right at
4626 * the other end has been deleted, to wander all of our objects to see
4627 * what weird places clients might have handed us the same send right
4628 * to use.
4629 */
4630
5b0a4722
A
4631 if (jm == root_jobmgr) {
4632 if (port == inherited_bootstrap_port) {
4633 launchd_assumes(launchd_mport_deallocate(port) == KERN_SUCCESS);
4634 inherited_bootstrap_port = MACH_PORT_NULL;
ed34e3c3 4635
5b0a4722
A
4636 return jobmgr_shutdown(jm);
4637 }
4638
4639 LIST_FOREACH_SAFE(ms, &port_hash[HASH_PORT(port)], port_hash_sle, next_ms) {
4640 if (ms->port == port) {
4641 machservice_delete(ms->job, ms, true);
4642 }
4643 }
4644 }
4645
4646 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
4647 jobmgr_delete_anything_with_port(jmi, port);
4648 }
ed34e3c3 4649
5b0a4722
A
4650 if (jm->req_port == port) {
4651 jobmgr_log(jm, LOG_DEBUG, "Request port died: 0x%x", port);
4652 return jobmgr_shutdown(jm);
ed34e3c3 4653 }
5b0a4722
A
4654
4655 return jm;
ed34e3c3
A
4656}
4657
4658struct machservice *
5b0a4722 4659jobmgr_lookup_service(jobmgr_t jm, const char *name, bool check_parent, pid_t target_pid)
ed34e3c3
A
4660{
4661 struct machservice *ms;
ed34e3c3 4662
5b0a4722
A
4663 if (target_pid) {
4664 jobmgr_assumes(jm, !check_parent);
4665 }
ed34e3c3 4666
5b0a4722
A
4667 LIST_FOREACH(ms, &jm->ms_hash[hash_ms(name)], name_hash_sle) {
4668 if ((target_pid && ms->per_pid && ms->job->p == target_pid) || (!target_pid && !ms->per_pid)) {
4669 if (strcmp(name, ms->name) == 0) {
ed34e3c3 4670 return ms;
5b0a4722 4671 }
ed34e3c3
A
4672 }
4673 }
4674
5b0a4722 4675 if (jm->parentmgr == NULL) {
ed34e3c3 4676 return NULL;
5b0a4722 4677 }
ed34e3c3 4678
5b0a4722 4679 if (!check_parent) {
ed34e3c3 4680 return NULL;
5b0a4722 4681 }
ed34e3c3 4682
5b0a4722 4683 return jobmgr_lookup_service(jm->parentmgr, name, true, 0);
ed34e3c3
A
4684}
4685
4686mach_port_t
4687machservice_port(struct machservice *ms)
4688{
4689 return ms->port;
4690}
4691
5b0a4722 4692job_t
ed34e3c3
A
4693machservice_job(struct machservice *ms)
4694{
4695 return ms->job;
4696}
4697
4698bool
4699machservice_hidden(struct machservice *ms)
4700{
4701 return ms->hide;
4702}
4703
4704bool
4705machservice_active(struct machservice *ms)
4706{
4707 return ms->isActive;
4708}
4709
4710const char *
4711machservice_name(struct machservice *ms)
4712{
4713 return ms->name;
4714}
4715
4716void
5b0a4722 4717machservice_delete(job_t j, struct machservice *ms, bool port_died)
ed34e3c3 4718{
5b0a4722
A
4719 if (ms->debug_on_close) {
4720 job_log(j, LOG_NOTICE, "About to enter kernel debugger because of Mach port: 0x%x", ms->port);
4721 job_assumes(j, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER) == KERN_SUCCESS);
4722 }
4723
4724 if (ms->recv && job_assumes(j, !ms->isActive)) {
4725 job_assumes(j, launchd_mport_close_recv(ms->port) == KERN_SUCCESS);
4726 }
4727
4728 job_assumes(j, launchd_mport_deallocate(ms->port) == KERN_SUCCESS);
4729
4730 if (ms->port == the_exception_server) {
4731 the_exception_server = 0;
ed34e3c3
A
4732 }
4733
5b0a4722 4734 job_log(j, LOG_INFO, "Mach service deleted%s: %s", port_died ? " (port died)" : "", ms->name);
ed34e3c3 4735
5b0a4722
A
4736 if (ms->special_port_num) {
4737 SLIST_REMOVE(&special_ports, ms, machservice, special_port_sle);
4738 }
ed34e3c3 4739
5b0a4722
A
4740 SLIST_REMOVE(&j->machservices, ms, machservice, sle);
4741 LIST_REMOVE(ms, name_hash_sle);
4742 LIST_REMOVE(ms, port_hash_sle);
ed34e3c3
A
4743
4744 free(ms);
4745}
4746
4747void
5b0a4722 4748machservice_request_notifications(struct machservice *ms)
ed34e3c3
A
4749{
4750 mach_msg_id_t which = MACH_NOTIFY_DEAD_NAME;
4751
4752 ms->isActive = true;
4753
5b0a4722 4754 if (ms->recv) {
ed34e3c3
A
4755 which = MACH_NOTIFY_PORT_DESTROYED;
4756 job_checkin(ms->job);
4757 }
4758
5b0a4722 4759 job_assumes(ms->job, launchd_mport_notify_req(ms->port, which) == KERN_SUCCESS);
ed34e3c3
A
4760}
4761
5b0a4722
A
4762#define NELEM(x) (sizeof(x)/sizeof(x[0]))
4763#define END_OF(x) (&(x)[NELEM(x)])
ed34e3c3
A
4764
4765char **
4766mach_cmd2argv(const char *string)
4767{
4768 char *argv[100], args[1000];
4769 const char *cp;
4770 char *argp = args, term, **argv_ret, *co;
4771 unsigned int nargs = 0, i;
4772
4773 for (cp = string; *cp;) {
4774 while (isspace(*cp))
4775 cp++;
4776 term = (*cp == '"') ? *cp++ : '\0';
5b0a4722 4777 if (nargs < NELEM(argv)) {
ed34e3c3 4778 argv[nargs++] = argp;
5b0a4722 4779 }
ed34e3c3 4780 while (*cp && (term ? *cp != term : !isspace(*cp)) && argp < END_OF(args)) {
5b0a4722 4781 if (*cp == '\\') {
ed34e3c3 4782 cp++;
5b0a4722 4783 }
ed34e3c3 4784 *argp++ = *cp;
5b0a4722 4785 if (*cp) {
ed34e3c3 4786 cp++;
5b0a4722 4787 }
ed34e3c3
A
4788 }
4789 *argp++ = '\0';
4790 }
4791 argv[nargs] = NULL;
4792
5b0a4722 4793 if (nargs == 0) {
ed34e3c3 4794 return NULL;
5b0a4722 4795 }
ed34e3c3
A
4796
4797 argv_ret = malloc((nargs + 1) * sizeof(char *) + strlen(string) + 1);
4798
5b0a4722 4799 if (!launchd_assumes(argv_ret != NULL)) {
ed34e3c3 4800 return NULL;
5b0a4722 4801 }
ed34e3c3
A
4802
4803 co = (char *)argv_ret + (nargs + 1) * sizeof(char *);
4804
4805 for (i = 0; i < nargs; i++) {
4806 strcpy(co, argv[i]);
4807 argv_ret[i] = co;
4808 co += strlen(argv[i]) + 1;
4809 }
4810 argv_ret[i] = NULL;
4811
4812 return argv_ret;
4813}
4814
4815void
5b0a4722 4816job_checkin(job_t j)
ed34e3c3
A
4817{
4818 j->checkedin = true;
4819}
4820
4821bool
5b0a4722 4822job_ack_port_destruction(mach_port_t p)
ed34e3c3 4823{
ed34e3c3
A
4824 struct machservice *ms;
4825
5b0a4722
A
4826 LIST_FOREACH(ms, &port_hash[HASH_PORT(p)], port_hash_sle) {
4827 if (ms->recv && (ms->port == p)) {
ed34e3c3 4828 break;
5b0a4722 4829 }
ed34e3c3
A
4830 }
4831
5b0a4722 4832 if (!ms) {
ed34e3c3 4833 return false;
5b0a4722 4834 }
ed34e3c3
A
4835
4836 ms->isActive = false;
4837
5b0a4722
A
4838 if (ms->reset) {
4839 machservice_resetport(ms->job, ms);
4840 }
ed34e3c3 4841
5b0a4722
A
4842 job_log(ms->job, LOG_DEBUG, "Receive right returned to us: %s", ms->name);
4843 job_dispatch(ms->job, false);
ed34e3c3 4844
5b0a4722 4845 root_jobmgr = jobmgr_do_garbage_collection(root_jobmgr);
ed34e3c3
A
4846
4847 return true;
4848}
4849
4850void
5b0a4722 4851job_ack_no_senders(job_t j)
ed34e3c3
A
4852{
4853 j->priv_port_has_senders = false;
4854
5b0a4722
A
4855 job_assumes(j, launchd_mport_close_recv(j->j_port) == KERN_SUCCESS);
4856 j->j_port = 0;
4857
ed34e3c3
A
4858 job_log(j, LOG_DEBUG, "No more senders on privileged Mach bootstrap port");
4859
5b0a4722 4860 job_dispatch(j, false);
ed34e3c3
A
4861}
4862
5b0a4722
A
4863jobmgr_t
4864job_get_bs(job_t j)
ed34e3c3 4865{
5b0a4722
A
4866 if (job_assumes(j, j->mgr != NULL)) {
4867 return j->mgr;
4868 }
ed34e3c3 4869
5b0a4722 4870 return NULL;
ed34e3c3
A
4871}
4872
5b0a4722
A
4873bool
4874job_is_anonymous(job_t j)
ed34e3c3 4875{
5b0a4722 4876 return j->anonymous;
ed34e3c3
A
4877}
4878
5b0a4722
A
4879void
4880job_force_sampletool(job_t j)
ed34e3c3 4881{
5b0a4722
A
4882 struct stat sb;
4883 char logfile[PATH_MAX];
4884 char pidstr[100];
4885 char *sample_args[] = { "sample", pidstr, "1", "-mayDie", "-file", logfile, NULL };
4886 char *contents = NULL;
4887 int logfile_fd = -1;
4888 int console_fd = -1;
4889 int wstatus;
4890 pid_t sp;
4891
4892 if (!debug_shutdown_hangs) {
4893 return;
4894 }
4895
4896 snprintf(pidstr, sizeof(pidstr), "%u", j->p);
4897 snprintf(logfile, sizeof(logfile), SHUTDOWN_LOG_DIR "/%s-%u.sample.txt", j->label, j->p);
ed34e3c3 4898
5b0a4722
A
4899 if (!job_assumes(j, unlink(logfile) != -1 || errno == ENOENT)) {
4900 goto out;
4901 }
ed34e3c3 4902
5b0a4722
A
4903 /*
4904 * This will stall launchd for as long as the 'sample' tool runs.
4905 *
4906 * We didn't give the 'sample' tool a bootstrap port, so it therefore
4907 * can't deadlock against launchd.
4908 */
4909 if (!job_assumes(j, (errno = posix_spawnp(&sp, sample_args[0], NULL, NULL, sample_args, environ)) == 0)) {
4910 goto out;
4911 }
ed34e3c3 4912
5b0a4722
A
4913 job_log(j, LOG_DEBUG, "Waiting for 'sample' to finish.");
4914
4915 if (!job_assumes(j, waitpid(sp, &wstatus, 0) != -1)) {
4916 goto out;
4917 }
4918
4919 /*
4920 * This won't work if the VFS or filesystems are sick:
4921 * sync();
4922 */
4923
4924 if (!job_assumes(j, WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == 0)) {
4925 goto out;
4926 }
4927
4928 if (!job_assumes(j, (logfile_fd = open(logfile, O_RDONLY|O_NOCTTY)) != -1)) {
4929 goto out;
4930 }
4931
4932 if (!job_assumes(j, (console_fd = open(_PATH_CONSOLE, O_WRONLY|O_APPEND|O_NOCTTY)) != -1)) {
4933 goto out;
4934 }
4935
4936 if (!job_assumes(j, fstat(logfile_fd, &sb) != -1)) {
4937 goto out;
4938 }
4939
4940 contents = malloc(sb.st_size);
4941
4942 if (!job_assumes(j, contents != NULL)) {
4943 goto out;
4944 }
4945
4946 if (!job_assumes(j, read(logfile_fd, contents, sb.st_size) == sb.st_size)) {
4947 goto out;
4948 }
4949
4950 job_assumes(j, write(console_fd, contents, sb.st_size) == sb.st_size);
4951
4952out:
4953 if (contents) {
4954 free(contents);
4955 }
4956
4957 if (logfile_fd != -1) {
4958 job_assumes(j, runtime_fsync(logfile_fd) != -1);
4959 job_assumes(j, runtime_close(logfile_fd) != -1);
4960 }
4961
4962 if (console_fd != -1) {
4963 job_assumes(j, runtime_close(console_fd) != -1);
4964 }
4965
4966 job_log(j, LOG_DEBUG, "Finished sampling.");
ed34e3c3
A
4967}
4968
4969bool
5b0a4722 4970semaphoreitem_new(job_t j, semaphore_reason_t why, const char *what)
ed34e3c3
A
4971{
4972 struct semaphoreitem *si;
4973 size_t alloc_sz = sizeof(struct semaphoreitem);
4974
5b0a4722 4975 if (what) {
ed34e3c3 4976 alloc_sz += strlen(what) + 1;
5b0a4722 4977 }
ed34e3c3 4978
5b0a4722 4979 if (!job_assumes(j, si = calloc(1, alloc_sz))) {
ed34e3c3 4980 return false;
5b0a4722 4981 }
ed34e3c3 4982
5b0a4722 4983 si->fd = -1;
ed34e3c3
A
4984 si->why = why;
4985
5b0a4722 4986 if (what) {
f36da725 4987 strcpy(si->what_init, what);
5b0a4722 4988 }
ed34e3c3
A
4989
4990 SLIST_INSERT_HEAD(&j->semaphores, si, sle);
4991
5b0a4722
A
4992 semaphoreitem_runtime_mod_ref(si, true);
4993
ed34e3c3
A
4994 return true;
4995}
4996
4997void
5b0a4722
A
4998semaphoreitem_runtime_mod_ref(struct semaphoreitem *si, bool add)
4999{
5000 /*
5001 * External events need to be tracked.
5002 * Internal events do NOT need to be tracked.
5003 */
5004
5005 switch (si->why) {
5006 case SUCCESSFUL_EXIT:
5007 case FAILED_EXIT:
5008 case OTHER_JOB_ENABLED:
5009 case OTHER_JOB_DISABLED:
5010 case OTHER_JOB_ACTIVE:
5011 case OTHER_JOB_INACTIVE:
5012 return;
5013 default:
5014 break;
5015 }
5016
5017 if (add) {
5018 runtime_add_ref();
5019 } else {
5020 runtime_del_ref();
5021 }
5022}
5023
5024void
5025semaphoreitem_delete(job_t j, struct semaphoreitem *si)
ed34e3c3 5026{
5b0a4722
A
5027 semaphoreitem_runtime_mod_ref(si, false);
5028
5029 SLIST_REMOVE(&j->semaphores, si, semaphoreitem, sle);
ed34e3c3 5030
5b0a4722
A
5031 if (si->fd != -1) {
5032 job_assumes(j, runtime_close(si->fd) != -1);
5033 }
5034
5035 free(si);
ed34e3c3
A
5036}
5037
5038void
5b0a4722 5039semaphoreitem_setup_dict_iter(launch_data_t obj, const char *key, void *context)
ed34e3c3 5040{
5b0a4722 5041 struct semaphoreitem_dict_iter_context *sdic = context;
ed34e3c3
A
5042 semaphore_reason_t why;
5043
5b0a4722 5044 why = launch_data_get_bool(obj) ? sdic->why_true : sdic->why_false;
ed34e3c3 5045
5b0a4722 5046 semaphoreitem_new(sdic->j, why, key);
ed34e3c3
A
5047}
5048
5049void
5050semaphoreitem_setup(launch_data_t obj, const char *key, void *context)
5051{
5b0a4722
A
5052 struct semaphoreitem_dict_iter_context sdic = { context, 0, 0 };
5053 job_t j = context;
ed34e3c3
A
5054 semaphore_reason_t why;
5055
5b0a4722
A
5056 switch (launch_data_get_type(obj)) {
5057 case LAUNCH_DATA_BOOL:
5058 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE) == 0) {
5059 why = launch_data_get_bool(obj) ? NETWORK_UP : NETWORK_DOWN;
5060 semaphoreitem_new(j, why, NULL);
5061 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT) == 0) {
5062 why = launch_data_get_bool(obj) ? SUCCESSFUL_EXIT : FAILED_EXIT;
5063 semaphoreitem_new(j, why, NULL);
5064 j->start_pending = true;
5065 } else {
5066 job_assumes(j, false);
5067 }
5068 break;
5069 case LAUNCH_DATA_DICTIONARY:
5070 if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE) == 0) {
5071 sdic.why_true = PATH_EXISTS;
5072 sdic.why_false = PATH_MISSING;
5073 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE) == 0) {
5074 sdic.why_true = OTHER_JOB_ACTIVE;
5075 sdic.why_false = OTHER_JOB_INACTIVE;
5076 } else if (strcasecmp(key, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED) == 0) {
5077 sdic.why_true = OTHER_JOB_ENABLED;
5078 sdic.why_false = OTHER_JOB_DISABLED;
5079 } else {
5080 job_assumes(j, false);
5081 break;
5082 }
5083
5084 launch_data_dict_iterate(obj, semaphoreitem_setup_dict_iter, &sdic);
5085 break;
5086 default:
5087 job_assumes(j, false);
5088 break;
ed34e3c3
A
5089 }
5090}
5091
5092void
5b0a4722 5093jobmgr_dispatch_all_semaphores(jobmgr_t jm)
ed34e3c3 5094{
5b0a4722
A
5095 jobmgr_t jmi, jmn;
5096 job_t ji, jn;
ed34e3c3 5097
ed34e3c3 5098
5b0a4722
A
5099 SLIST_FOREACH_SAFE(jmi, &jm->submgrs, sle, jmn) {
5100 jobmgr_dispatch_all_semaphores(jmi);
5101 }
ed34e3c3 5102
5b0a4722
A
5103 LIST_FOREACH_SAFE(ji, &jm->jobs, sle, jn) {
5104 if (!SLIST_EMPTY(&ji->semaphores)) {
5105 job_dispatch(ji, false);
5106 }
5107 }
ed34e3c3
A
5108}
5109
5110time_t
5111cronemu(int mon, int mday, int hour, int min)
5112{
5113 struct tm workingtm;
5114 time_t now;
5115
5116 now = time(NULL);
5117 workingtm = *localtime(&now);
5118
5119 workingtm.tm_isdst = -1;
5120 workingtm.tm_sec = 0;
5121 workingtm.tm_min++;
5122
5123 while (!cronemu_mon(&workingtm, mon, mday, hour, min)) {
5124 workingtm.tm_year++;
5125 workingtm.tm_mon = 0;
5126 workingtm.tm_mday = 1;
5127 workingtm.tm_hour = 0;
5128 workingtm.tm_min = 0;
5129 mktime(&workingtm);
5130 }
5131
5132 return mktime(&workingtm);
5133}
5134
5135time_t
5136cronemu_wday(int wday, int hour, int min)
5137{
5138 struct tm workingtm;
5139 time_t now;
5140
5141 now = time(NULL);
5142 workingtm = *localtime(&now);
5143
5144 workingtm.tm_isdst = -1;
5145 workingtm.tm_sec = 0;
5146 workingtm.tm_min++;
5147
5b0a4722 5148 if (wday == 7) {
ed34e3c3 5149 wday = 0;
5b0a4722 5150 }
ed34e3c3
A
5151
5152 while (!(workingtm.tm_wday == wday && cronemu_hour(&workingtm, hour, min))) {
5153 workingtm.tm_mday++;
5154 workingtm.tm_hour = 0;
5155 workingtm.tm_min = 0;
5156 mktime(&workingtm);
5157 }
5158
5159 return mktime(&workingtm);
5160}
5161
5162bool
5163cronemu_mon(struct tm *wtm, int mon, int mday, int hour, int min)
5164{
5165 if (mon == -1) {
5166 struct tm workingtm = *wtm;
5167 int carrytest;
5168
5169 while (!cronemu_mday(&workingtm, mday, hour, min)) {
5170 workingtm.tm_mon++;
5171 workingtm.tm_mday = 1;
5172 workingtm.tm_hour = 0;
5173 workingtm.tm_min = 0;
5174 carrytest = workingtm.tm_mon;
5175 mktime(&workingtm);
5b0a4722 5176 if (carrytest != workingtm.tm_mon) {
ed34e3c3 5177 return false;
5b0a4722 5178 }
ed34e3c3
A
5179 }
5180 *wtm = workingtm;
5181 return true;
5182 }
5183
5b0a4722 5184 if (mon < wtm->tm_mon) {
ed34e3c3 5185 return false;
5b0a4722 5186 }
ed34e3c3 5187
5b0a4722 5188 if (mon > wtm->tm_mon) {
ed34e3c3
A
5189 wtm->tm_mon = mon;
5190 wtm->tm_mday = 1;
5191 wtm->tm_hour = 0;
5192 wtm->tm_min = 0;
5193 }
5194
5195 return cronemu_mday(wtm, mday, hour, min);
5196}
5197
5198bool
5199cronemu_mday(struct tm *wtm, int mday, int hour, int min)
5200{
5201 if (mday == -1) {
5202 struct tm workingtm = *wtm;
5203 int carrytest;
5204
5205 while (!cronemu_hour(&workingtm, hour, min)) {
5206 workingtm.tm_mday++;
5207 workingtm.tm_hour = 0;
5208 workingtm.tm_min = 0;
5209 carrytest = workingtm.tm_mday;
5210 mktime(&workingtm);
5b0a4722 5211 if (carrytest != workingtm.tm_mday) {
ed34e3c3 5212 return false;
5b0a4722 5213 }
ed34e3c3
A
5214 }
5215 *wtm = workingtm;
5216 return true;
5217 }
5218
5b0a4722 5219 if (mday < wtm->tm_mday) {
ed34e3c3 5220 return false;
5b0a4722 5221 }
ed34e3c3 5222
5b0a4722 5223 if (mday > wtm->tm_mday) {
ed34e3c3
A
5224 wtm->tm_mday = mday;
5225 wtm->tm_hour = 0;
5226 wtm->tm_min = 0;
5227 }
5228
5229 return cronemu_hour(wtm, hour, min);
5230}
5231
5232bool
5233cronemu_hour(struct tm *wtm, int hour, int min)
5234{
5235 if (hour == -1) {
5236 struct tm workingtm = *wtm;
5237 int carrytest;
5238
5239 while (!cronemu_min(&workingtm, min)) {
5240 workingtm.tm_hour++;
5241 workingtm.tm_min = 0;
5242 carrytest = workingtm.tm_hour;
5243 mktime(&workingtm);
5b0a4722 5244 if (carrytest != workingtm.tm_hour) {
ed34e3c3 5245 return false;
5b0a4722 5246 }
ed34e3c3
A
5247 }
5248 *wtm = workingtm;
5249 return true;
5250 }
5251
5b0a4722 5252 if (hour < wtm->tm_hour) {
ed34e3c3 5253 return false;
5b0a4722 5254 }
ed34e3c3
A
5255
5256 if (hour > wtm->tm_hour) {
5257 wtm->tm_hour = hour;
5258 wtm->tm_min = 0;
5259 }
5260
5261 return cronemu_min(wtm, min);
5262}
5263
5264bool
5265cronemu_min(struct tm *wtm, int min)
5266{
5b0a4722 5267 if (min == -1) {
ed34e3c3 5268 return true;
5b0a4722 5269 }
ed34e3c3 5270
5b0a4722 5271 if (min < wtm->tm_min) {
ed34e3c3 5272 return false;
5b0a4722 5273 }
ed34e3c3
A
5274
5275 if (min > wtm->tm_min) {
5276 wtm->tm_min = min;
5277 }
5278
5279 return true;
5280}
5b0a4722
A
5281
5282kern_return_t
5283job_mig_create_server(job_t j, cmd_t server_cmd, uid_t server_uid, boolean_t on_demand, mach_port_t *server_portp)
5284{
5285 struct ldcred ldc;
5286 job_t js;
5287
cf0bacfd
A
5288#if TARGET_OS_EMBEDDED
5289 return BOOTSTRAP_NOT_PRIVILEGED;
5290#endif
5291
5b0a4722
A
5292 if (!launchd_assumes(j != NULL)) {
5293 return BOOTSTRAP_NO_MEMORY;
5294 }
5295
f36da725
A
5296 if (unlikely(j->deny_job_creation)) {
5297 return BOOTSTRAP_NOT_PRIVILEGED;
5298 }
5299
5b0a4722
A
5300 runtime_get_caller_creds(&ldc);
5301
5302 job_log(j, LOG_DEBUG, "Server create attempt: %s", server_cmd);
5303
5304#define LET_MERE_MORTALS_ADD_SERVERS_TO_PID1
5305 /* XXX - This code should go away once the per session launchd is integrated with the rest of the system */
5306#ifdef LET_MERE_MORTALS_ADD_SERVERS_TO_PID1
5307 if (getpid() == 1) {
5308 if (ldc.euid && server_uid && (ldc.euid != server_uid)) {
5309 job_log(j, LOG_WARNING, "Server create: \"%s\": Will run as UID %d, not UID %d as they told us to",
5310 server_cmd, ldc.euid, server_uid);
5311 server_uid = ldc.euid;
5312 }
5313 } else
5314#endif
5315 if (getuid()) {
5316 if (server_uid != getuid()) {
5317 job_log(j, LOG_WARNING, "Server create: \"%s\": As UID %d, we will not be able to switch to UID %d",
5318 server_cmd, getuid(), server_uid);
5319 }
5320 server_uid = 0; /* zero means "do nothing" */
5321 }
5322
5323 js = job_new_via_mach_init(j, server_cmd, server_uid, on_demand);
5324
5325 if (js == NULL) {
5326 return BOOTSTRAP_NO_MEMORY;
5327 }
5328
5329 *server_portp = js->j_port;
5330 return BOOTSTRAP_SUCCESS;
5331}
5332
5333kern_return_t
5334job_mig_send_signal(job_t j, mach_port_t srp, name_t targetlabel, int sig)
5335{
5336 struct ldcred ldc;
5337 job_t otherj;
5338
5339 if (!launchd_assumes(j != NULL)) {
5340 return BOOTSTRAP_NO_MEMORY;
5341 }
5342
5343 runtime_get_caller_creds(&ldc);
5344
5345 if (ldc.euid != 0 && ldc.euid != getuid()) {
5346 return BOOTSTRAP_NOT_PRIVILEGED;
5347 }
5348
5349 if (!(otherj = job_find(targetlabel))) {
5350 return BOOTSTRAP_UNKNOWN_SERVICE;
5351 }
5352
5353 if (sig == VPROC_MAGIC_UNLOAD_SIGNAL) {
5354 bool do_block = otherj->p;
5355
5356 if (otherj->anonymous) {
5357 return BOOTSTRAP_NOT_PRIVILEGED;
5358 }
5359
5360 job_remove(otherj);
5361
5362 if (do_block) {
5363 job_log(j, LOG_DEBUG, "Blocking MIG return of job_remove(): %s", otherj->label);
5364 /* this is messy. We shouldn't access 'otherj' after job_remove(), but we check otherj->p first... */
5365 job_assumes(otherj, waiting4removal_new(otherj, srp));
5366 return MIG_NO_REPLY;
5367 } else {
5368 return 0;
5369 }
5370 } else if (otherj->p) {
5371 job_assumes(j, runtime_kill(otherj->p, sig) != -1);
5372 }
5373
5374 return 0;
5375}
5376
5377kern_return_t
5378job_mig_log_forward(job_t j, vm_offset_t inval, mach_msg_type_number_t invalCnt)
5379{
5380 struct ldcred ldc;
5381
5382 if (!launchd_assumes(j != NULL)) {
5383 return BOOTSTRAP_NO_MEMORY;
5384 }
5385
5386 if (!job_assumes(j, j->per_user)) {
5387 return BOOTSTRAP_NOT_PRIVILEGED;
5388 }
5389
5390 runtime_get_caller_creds(&ldc);
5391
5392 return runtime_log_forward(ldc.euid, ldc.egid, inval, invalCnt);
5393}
5394
5395kern_return_t
5396job_mig_log_drain(job_t j, mach_port_t srp, vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
5397{
5398 struct ldcred ldc;
5399
5400 if (!launchd_assumes(j != NULL)) {
5401 return BOOTSTRAP_NO_MEMORY;
5402 }
5403
5404 runtime_get_caller_creds(&ldc);
5405
5406 if (ldc.euid) {
5407 return BOOTSTRAP_NOT_PRIVILEGED;
5408 }
5409
5410 return runtime_log_drain(srp, outval, outvalCnt);
5411}
5412
5413kern_return_t
5414job_mig_swap_complex(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey,
5415 vm_offset_t inval, mach_msg_type_number_t invalCnt,
5416 vm_offset_t *outval, mach_msg_type_number_t *outvalCnt)
5417{
5418 const char *action;
5419 launch_data_t input_obj, output_obj;
5420 size_t data_offset = 0;
5421 size_t packed_size;
5422 struct ldcred ldc;
5423
5424 runtime_get_caller_creds(&ldc);
5425
5426 if (!launchd_assumes(j != NULL)) {
5427 return BOOTSTRAP_NO_MEMORY;
5428 }
5429
5430 if (inkey && ldc.euid && ldc.euid != getuid()) {
5431 return BOOTSTRAP_NOT_PRIVILEGED;
5432 }
5433
5434 if (inkey && outkey && !job_assumes(j, inkey == outkey)) {
5435 return 1;
5436 }
5437
5438 if (inkey && outkey) {
5439 action = "Swapping";
5440 } else if (inkey) {
5441 action = "Setting";
5442 } else {
5443 action = "Getting";
5444 }
5445
5446 job_log(j, LOG_DEBUG, "%s key: %u", action, inkey ? inkey : outkey);
5447
5448 *outvalCnt = 20 * 1024 * 1024;
5449 mig_allocate(outval, *outvalCnt);
5450 if (!job_assumes(j, *outval != 0)) {
5451 return 1;
5452 }
5453
5454 if (invalCnt && !job_assumes(j, (input_obj = launch_data_unpack((void *)inval, invalCnt, NULL, 0, &data_offset, NULL)) != NULL)) {
5455 goto out_bad;
5456 }
5457
5458 switch (outkey) {
5459 case VPROC_GSK_ENVIRONMENT:
5460 if (!job_assumes(j, (output_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)))) {
5461 goto out_bad;
5462 }
5463 jobmgr_export_env_from_other_jobs(j->mgr, output_obj);
5464 if (!job_assumes(j, launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL) != 0)) {
5465 goto out_bad;
5466 }
5467 launch_data_free(output_obj);
5468 break;
5469 case VPROC_GSK_ALLJOBS:
5470 if (!job_assumes(j, (output_obj = job_export_all()) != NULL)) {
5471 goto out_bad;
5472 }
5473 ipc_revoke_fds(output_obj);
5474 packed_size = launch_data_pack(output_obj, (void *)*outval, *outvalCnt, NULL, NULL);
5475 if (!job_assumes(j, packed_size != 0)) {
5476 goto out_bad;
5477 }
5478 launch_data_free(output_obj);
5479 break;
5480 case 0:
5481 mig_deallocate(*outval, *outvalCnt);
5482 *outval = 0;
5483 *outvalCnt = 0;
5484 break;
5485 default:
5486 goto out_bad;
5487 }
5488
5489 if (invalCnt) switch (inkey) {
5490 case VPROC_GSK_ENVIRONMENT:
5491 job_assumes(j, false);
5492 break;
5493 case 0:
5494 break;
5495 default:
5496 goto out_bad;
5497 }
5498
5499 mig_deallocate(inval, invalCnt);
5500
5501 return 0;
5502
5503out_bad:
5504 if (*outval) {
5505 mig_deallocate(*outval, *outvalCnt);
5506 }
5507 return 1;
5508}
5509
5510kern_return_t
5511job_mig_swap_integer(job_t j, vproc_gsk_t inkey, vproc_gsk_t outkey, int64_t inval, int64_t *outval)
5512{
5513 const char *action;
5514 kern_return_t kr = 0;
5515 struct ldcred ldc;
5516 int oldmask;
5517
5518 runtime_get_caller_creds(&ldc);
5519
5520 if (!launchd_assumes(j != NULL)) {
5521 return BOOTSTRAP_NO_MEMORY;
5522 }
5523
5524 if (inkey && ldc.euid && ldc.euid != getuid()) {
5525 return BOOTSTRAP_NOT_PRIVILEGED;
5526 }
5527
5528 if (inkey && outkey && !job_assumes(j, inkey == outkey)) {
5529 return 1;
5530 }
5531
5532 if (inkey && outkey) {
5533 action = "Swapping";
5534 } else if (inkey) {
5535 action = "Setting";
5536 } else {
5537 action = "Getting";
5538 }
5539
5540 job_log(j, LOG_DEBUG, "%s key: %u", action, inkey ? inkey : outkey);
5541
5542 switch (outkey) {
5543 case VPROC_GSK_LAST_EXIT_STATUS:
5544 *outval = j->last_exit_status;
5545 break;
5546 case VPROC_GSK_MGR_UID:
5547 *outval = getuid();
5548 break;
5549 case VPROC_GSK_MGR_PID:
5550 *outval = getpid();
5551 break;
5552 case VPROC_GSK_IS_MANAGED:
5553 *outval = j->anonymous ? 0 : 1;
5554 break;
5555 case VPROC_GSK_BASIC_KEEPALIVE:
5556 *outval = !j->ondemand;
5557 break;
5558 case VPROC_GSK_START_INTERVAL:
5559 *outval = j->start_interval;
5560 break;
5561 case VPROC_GSK_IDLE_TIMEOUT:
5562 *outval = j->timeout;
5563 break;
5564 case VPROC_GSK_EXIT_TIMEOUT:
5565 *outval = j->exit_timeout;
5566 break;
5567 case VPROC_GSK_GLOBAL_LOG_MASK:
5568 oldmask = runtime_setlogmask(LOG_UPTO(LOG_DEBUG));
5569 *outval = oldmask;
5570 runtime_setlogmask(oldmask);
5571 break;
5572 case VPROC_GSK_GLOBAL_UMASK:
5573 oldmask = umask(0);
5574 *outval = oldmask;
5575 umask(oldmask);
5576 break;
5577 case 0:
5578 *outval = 0;
5579 break;
5580 default:
5581 kr = 1;
5582 break;
5583 }
5584
5585 switch (inkey) {
5586 case VPROC_GSK_GLOBAL_ON_DEMAND:
5587 kr = job_set_global_on_demand(j, (bool)inval) ? 0 : 1;
5588 break;
5589 case VPROC_GSK_BASIC_KEEPALIVE:
5590 j->ondemand = !inval;
5591 break;
5592 case VPROC_GSK_START_INTERVAL:
5593 if ((uint64_t)inval > UINT32_MAX) {
5594 kr = 1;
5595 } else if (inval) {
5596 if (j->start_interval == 0) {
5597 runtime_add_ref();
5598 } else {
5599 /* Workaround 5225889 */
5600 job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, j) != -1);
5601 }
5602 j->start_interval = inval;
5603 job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, j->start_interval, j) != -1);
5604 } else if (j->start_interval) {
5605 job_assumes(j, kevent_mod((uintptr_t)&j->start_interval, EVFILT_TIMER, EV_DELETE, 0, 0, NULL) != -1);
5606 if (j->start_interval != 0) {
5607 runtime_del_ref();
5608 }
5609 j->start_interval = 0;
5610 }
5611 break;
5612 case VPROC_GSK_IDLE_TIMEOUT:
5613 if ((unsigned int)inval > 0) {
5614 j->timeout = inval;
5615 }
5616 break;
5617 case VPROC_GSK_EXIT_TIMEOUT:
5618 if ((unsigned int)inval > 0) {
5619 j->exit_timeout = inval;
5620 }
5621 break;
5622 case VPROC_GSK_GLOBAL_LOG_MASK:
5623 runtime_setlogmask(inval);
5624 break;
5625 case VPROC_GSK_GLOBAL_UMASK:
5626 umask(inval);
5627 break;
5628 case 0:
5629 break;
5630 default:
5631 kr = 1;
5632 break;
5633 }
5634
5635 return kr;
5636}
5637
5638kern_return_t
5639job_mig_post_fork_ping(job_t j, task_t child_task)
5640{
5641 struct machservice *ms;
5642
5643 if (!launchd_assumes(j != NULL)) {
5644 return BOOTSTRAP_NO_MEMORY;
5645 }
5646
5647 job_log(j, LOG_DEBUG, "Post fork ping.");
5648
5649 job_setup_exception_port(j, child_task);
5650
5651 SLIST_FOREACH(ms, &special_ports, special_port_sle) {
5652 if (j->per_user && (ms->special_port_num != TASK_ACCESS_PORT)) {
5653 /* The TASK_ACCESS_PORT funny business is to workaround 5325399. */
5654 continue;
5655 }
5656
5657 errno = task_set_special_port(child_task, ms->special_port_num, ms->port);
5658
5659 if (errno) {
5660 int desired_log_level = LOG_ERR;
5661
5662 if (j->anonymous) {
5663 /* 5338127 */
5664
5665 desired_log_level = LOG_WARNING;
5666
5667 if (ms->special_port_num == TASK_SEATBELT_PORT) {
5668 desired_log_level = LOG_DEBUG;
5669 }
5670 }
5671
5672 job_log(j, desired_log_level, "Could not setup Mach task special port %u: %s", ms->special_port_num, mach_error_string(errno));
5673 }
5674 }
5675
5676 job_assumes(j, launchd_mport_deallocate(child_task) == KERN_SUCCESS);
5677
5678 return 0;
5679}
5680
5681kern_return_t
5682job_mig_reboot2(job_t j, uint64_t flags)
5683{
5684 char who_started_the_reboot[2048] = "";
5685 struct kinfo_proc kp;
5686 struct ldcred ldc;
5687 pid_t pid_to_log;
5688
5689 if (!launchd_assumes(j != NULL)) {
5690 return BOOTSTRAP_NO_MEMORY;
5691 }
5692
5693 if (getpid() != 1) {
5694 return BOOTSTRAP_NOT_PRIVILEGED;
5695 }
5696
5697 runtime_get_caller_creds(&ldc);
5698
5699 if (ldc.euid) {
5700 return BOOTSTRAP_NOT_PRIVILEGED;
5701 }
5702
5703 for (pid_to_log = ldc.pid; pid_to_log; pid_to_log = kp.kp_eproc.e_ppid) {
5704 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid_to_log };
5705 size_t who_offset, len = sizeof(kp);
5706
5707 if (!job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1)) {
5708 return 1;
5709 }
5710
5711 who_offset = strlen(who_started_the_reboot);
5712 snprintf(who_started_the_reboot + who_offset, sizeof(who_started_the_reboot) - who_offset,
5713 " %s[%u]%s", kp.kp_proc.p_comm, pid_to_log, kp.kp_eproc.e_ppid ? " ->" : "");
5714 }
5715
5716 root_jobmgr->reboot_flags = (int)flags;
5717
5718 launchd_shutdown();
5719
5720 job_log(j, LOG_DEBUG, "reboot2() initiated by:%s", who_started_the_reboot);
5721
5722 return 0;
5723}
5724
5725kern_return_t
5726job_mig_getsocket(job_t j, name_t spr)
5727{
5728 if (!launchd_assumes(j != NULL)) {
5729 return BOOTSTRAP_NO_MEMORY;
5730 }
5731
5732 ipc_server_init();
5733
5734 if (!sockpath) {
5735 return BOOTSTRAP_NO_MEMORY;
5736 }
5737
5738 strncpy(spr, sockpath, sizeof(name_t));
5739
5740 return BOOTSTRAP_SUCCESS;
5741}
5742
5743kern_return_t
5744job_mig_log(job_t j, int pri, int err, logmsg_t msg)
5745{
5746 if (!launchd_assumes(j != NULL)) {
5747 return BOOTSTRAP_NO_MEMORY;
5748 }
5749
5750 if ((errno = err)) {
5751 job_log_error(j, pri, "%s", msg);
5752 } else {
5753 job_log(j, pri, "%s", msg);
5754 }
5755
5756 return 0;
5757}
5758
5759void
5760ensure_root_bkgd_setup(void)
5761{
5762 if (background_jobmgr || getpid() != 1) {
5763 return;
5764 }
5765
5766 if (!jobmgr_assumes(root_jobmgr, (background_jobmgr = jobmgr_new(root_jobmgr, mach_task_self(), MACH_PORT_NULL, false, VPROCMGR_SESSION_BACKGROUND)) != NULL)) {
5767 return;
5768 }
5769
5770 background_jobmgr->req_port = 0;
5771 jobmgr_assumes(root_jobmgr, launchd_mport_make_send(background_jobmgr->jm_port) == KERN_SUCCESS);
5772}
5773
5774kern_return_t
5775job_mig_lookup_per_user_context(job_t j, uid_t which_user, mach_port_t *up_cont)
5776{
5777 struct ldcred ldc;
5778 job_t ji;
5779
cf0bacfd
A
5780#if TARGET_OS_EMBEDDED
5781 return BOOTSTRAP_NOT_PRIVILEGED;
5782#endif
5783
5b0a4722
A
5784 if (!launchd_assumes(j != NULL)) {
5785 return BOOTSTRAP_NO_MEMORY;
5786 }
5787
5788 job_log(j, LOG_DEBUG, "Looking up per user launchd for UID: %u", which_user);
5789
5790 runtime_get_caller_creds(&ldc);
5791
5792 if (getpid() != 1) {
5793 job_log(j, LOG_ERR, "Only PID 1 supports per user launchd lookups.");
5794 return BOOTSTRAP_NOT_PRIVILEGED;
5795 }
5796
5797 if (ldc.euid || ldc.uid) {
5798 which_user = ldc.euid ? ldc.euid : ldc.uid;
5799 }
5800
5801 *up_cont = MACH_PORT_NULL;
5802
5803 if (which_user == 0) {
5804 ensure_root_bkgd_setup();
5805
5806 *up_cont = background_jobmgr->jm_port;
5807
5808 return 0;
5809 }
5810
5811 LIST_FOREACH(ji, &root_jobmgr->jobs, sle) {
5812 if (!ji->per_user) {
5813 continue;
5814 }
5815 if (ji->mach_uid != which_user) {
5816 continue;
5817 }
5818 if (SLIST_EMPTY(&ji->machservices)) {
5819 continue;
5820 }
5821 if (!SLIST_FIRST(&ji->machservices)->per_user_hack) {
5822 continue;
5823 }
5824 break;
5825 }
5826
5827 if (ji == NULL) {
5828 struct machservice *ms;
5829 char lbuf[1024];
5830
5831 job_log(j, LOG_DEBUG, "Creating per user launchd job for UID: %u", which_user);
5832
5833 sprintf(lbuf, "com.apple.launchd.peruser.%u", which_user);
5834
5835 ji = job_new(root_jobmgr, lbuf, "/sbin/launchd", NULL);
5836
5837 if (ji == NULL) {
5838 return BOOTSTRAP_NO_MEMORY;
5839 }
5840
5841 ji->mach_uid = which_user;
5842 ji->per_user = true;
5843
5844 if ((ms = machservice_new(ji, lbuf, up_cont, false)) == NULL) {
5845 job_remove(ji);
5846 return BOOTSTRAP_NO_MEMORY;
5847 }
5848
5849 ms->per_user_hack = true;
5850 ms->hide = true;
5851
5852 ji = job_dispatch(ji, false);
5853 } else {
5854 job_log(j, LOG_DEBUG, "Per user launchd job found for UID: %u", which_user);
5855 }
5856
5857 if (job_assumes(j, ji != NULL)) {
5858 *up_cont = machservice_port(SLIST_FIRST(&ji->machservices));
5859 }
5860
5861 return 0;
5862}
5863
5864kern_return_t
5865job_mig_check_in(job_t j, name_t servicename, mach_port_t *serviceportp)
5866{
5867 static pid_t last_warned_pid = 0;
5868 struct machservice *ms;
5869 struct ldcred ldc;
5870
5871 if (!launchd_assumes(j != NULL)) {
5872 return BOOTSTRAP_NO_MEMORY;
5873 }
5874
5875 runtime_get_caller_creds(&ldc);
5876
5877 ms = jobmgr_lookup_service(j->mgr, servicename, true, 0);
5878
5879 if (ms == NULL) {
5880 job_log(j, LOG_DEBUG, "Check-in of Mach service failed. Unknown: %s", servicename);
5881 return BOOTSTRAP_UNKNOWN_SERVICE;
5882 }
5883 if (machservice_job(ms) != j) {
5884 if (last_warned_pid != ldc.pid) {
5885 job_log(j, LOG_NOTICE, "Check-in of Mach service failed. PID %d is not privileged: %s",
5886 ldc.pid, servicename);
5887 last_warned_pid = ldc.pid;
5888 }
5889 return BOOTSTRAP_NOT_PRIVILEGED;
5890 }
5891 if (machservice_active(ms)) {
5892 job_log(j, LOG_WARNING, "Check-in of Mach service failed. Already active: %s", servicename);
5893 return BOOTSTRAP_SERVICE_ACTIVE;
5894 }
5895
5896 machservice_request_notifications(ms);
5897
5898 job_log(j, LOG_INFO, "Check-in of service: %s", servicename);
5899
5900 *serviceportp = machservice_port(ms);
5901 return BOOTSTRAP_SUCCESS;
5902}
5903
5904kern_return_t
5905job_mig_register2(job_t j, name_t servicename, mach_port_t serviceport, uint64_t flags)
5906{
5907 struct machservice *ms;
5908 struct ldcred ldc;
5909
5910 if (!launchd_assumes(j != NULL)) {
5911 return BOOTSTRAP_NO_MEMORY;
5912 }
5913
5914 runtime_get_caller_creds(&ldc);
5915
5916#if 0
5917 job_log(j, LOG_APPLEONLY, "bootstrap_register() is deprecated. Service: %s", servicename);
5918#endif
5919
5920 job_log(j, LOG_DEBUG, "%sMach service registration attempt: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
5921
f36da725
A
5922 /* 5641783 for the embedded hack */
5923#if !TARGET_OS_EMBEDDED
5b0a4722
A
5924 /*
5925 * From a per-user/session launchd's perspective, SecurityAgent (UID
5926 * 92) is a rogue application (not our UID, not root and not a child of
5927 * us). We'll have to reconcile this design friction at a later date.
5928 */
5929 if (j->anonymous && job_get_bs(j)->parentmgr == NULL && ldc.uid != 0 && ldc.uid != getuid() && ldc.uid != 92) {
5930 if (getpid() == 1) {
5931 return VPROC_ERR_TRY_PER_USER;
5932 } else {
5933 return BOOTSTRAP_NOT_PRIVILEGED;
5934 }
5935 }
f36da725 5936#endif
5b0a4722
A
5937
5938 ms = jobmgr_lookup_service(j->mgr, servicename, false, flags & BOOTSTRAP_PER_PID_SERVICE ? ldc.pid : 0);
5939
5940 if (ms) {
5941 if (machservice_job(ms) != j) {
5942 return BOOTSTRAP_NOT_PRIVILEGED;
5943 }
5944 if (machservice_active(ms)) {
5945 job_log(j, LOG_DEBUG, "Mach service registration failed. Already active: %s", servicename);
5946 return BOOTSTRAP_SERVICE_ACTIVE;
5947 }
5948 job_checkin(j);
5949 machservice_delete(j, ms, false);
5950 }
5951
5952 if (serviceport != MACH_PORT_NULL) {
5953 if ((ms = machservice_new(j, servicename, &serviceport, flags & BOOTSTRAP_PER_PID_SERVICE ? true : false))) {
5954 machservice_request_notifications(ms);
5955 } else {
5956 return BOOTSTRAP_NO_MEMORY;
5957 }
5958 }
5959
5960 return BOOTSTRAP_SUCCESS;
5961}
5962
5963kern_return_t
5964job_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)
5965{
5966 struct machservice *ms;
5967 struct ldcred ldc;
5968 kern_return_t kr;
5969
5970 if (!launchd_assumes(j != NULL)) {
5971 return BOOTSTRAP_NO_MEMORY;
5972 }
5973
5974 runtime_get_caller_creds(&ldc);
5975
f36da725
A
5976 /* 5641783 for the embedded hack */
5977#if !TARGET_OS_EMBEDDED
5b0a4722
A
5978 if (getpid() == 1 && j->anonymous && job_get_bs(j)->parentmgr == NULL && ldc.uid != 0 && ldc.euid != 0) {
5979 return VPROC_ERR_TRY_PER_USER;
5980 }
f36da725 5981#endif
5b0a4722
A
5982
5983 if (!mspolicy_check(j, servicename, flags & BOOTSTRAP_PER_PID_SERVICE)) {
5984 job_log(j, LOG_NOTICE, "Policy denied Mach service lookup: %s", servicename);
5985 return BOOTSTRAP_NOT_PRIVILEGED;
5986 }
5987
5988 if (flags & BOOTSTRAP_PER_PID_SERVICE) {
5989 ms = jobmgr_lookup_service(j->mgr, servicename, false, target_pid);
5990 } else {
5991 ms = jobmgr_lookup_service(j->mgr, servicename, true, 0);
5992 }
5993
cf0bacfd 5994 if (ms && machservice_hidden(ms) && !machservice_active(ms)) {
5b0a4722
A
5995 ms = NULL;
5996 } else if (ms && ms->per_user_hack) {
5997 ms = NULL;
5998 }
5999
6000 if (ms) {
6001 launchd_assumes(machservice_port(ms) != MACH_PORT_NULL);
6002 job_log(j, LOG_DEBUG, "%sMach service lookup: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
6003#if 0
6004 /* After Leopard ships, we should enable this */
6005 if (j->lastlookup == ms && j->lastlookup_gennum == ms->gen_num && !j->per_user) {
6006 ms->bad_perf_cnt++;
6007 job_log(j, LOG_APPLEONLY, "Performance opportunity: Number of bootstrap_lookup(... \"%s\" ...) calls that should have been cached: %llu",
6008 servicename, ms->bad_perf_cnt);
6009 }
6010 j->lastlookup = ms;
6011 j->lastlookup_gennum = ms->gen_num;
6012#endif
6013 *serviceportp = machservice_port(ms);
6014 *ptype = MACH_MSG_TYPE_COPY_SEND;
6015 kr = BOOTSTRAP_SUCCESS;
6016 } else if (!(flags & BOOTSTRAP_PER_PID_SERVICE) && (inherited_bootstrap_port != MACH_PORT_NULL)) {
6017 job_log(j, LOG_DEBUG, "Mach service lookup forwarded: %s", servicename);
6018 *ptype = MACH_MSG_TYPE_MOVE_SEND;
6019 kr = bootstrap_look_up(inherited_bootstrap_port, servicename, serviceportp);
6020 } else if (getpid() == 1 && j->anonymous && ldc.euid >= 500 && strcasecmp(job_get_bs(j)->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
6021 /*
6022 * 5240036 Should start background session when a lookup of CCacheServer occurs
6023 *
6024 * This is a total hack. We sniff out loginwindow session, and attempt to guess what it is up to.
6025 * If we find a EUID that isn't root, we force it over to the per-user context.
6026 */
6027 return VPROC_ERR_TRY_PER_USER;
6028 } else {
6029 job_log(j, LOG_DEBUG, "%sMach service lookup failed: %s", flags & BOOTSTRAP_PER_PID_SERVICE ? "Per PID " : "", servicename);
6030 kr = BOOTSTRAP_UNKNOWN_SERVICE;
6031 }
6032
6033 return kr;
6034}
6035
6036kern_return_t
6037job_mig_parent(job_t j, mach_port_t *parentport, mach_msg_type_name_t *pptype)
6038{
6039 if (!launchd_assumes(j != NULL)) {
6040 return BOOTSTRAP_NO_MEMORY;
6041 }
6042
6043 job_log(j, LOG_DEBUG, "Requested parent bootstrap port");
6044 jobmgr_t jm = j->mgr;
6045
6046 *pptype = MACH_MSG_TYPE_MAKE_SEND;
6047
6048 if (jobmgr_parent(jm)) {
6049 *parentport = jobmgr_parent(jm)->jm_port;
6050 } else if (MACH_PORT_NULL == inherited_bootstrap_port) {
6051 *parentport = jm->jm_port;
6052 } else {
6053 *pptype = MACH_MSG_TYPE_COPY_SEND;
6054 *parentport = inherited_bootstrap_port;
6055 }
6056 return BOOTSTRAP_SUCCESS;
6057}
6058
6059kern_return_t
6060job_mig_info(job_t j, name_array_t *servicenamesp, unsigned int *servicenames_cnt,
6061 bootstrap_status_array_t *serviceactivesp, unsigned int *serviceactives_cnt)
6062{
6063 name_array_t service_names = NULL;
6064 bootstrap_status_array_t service_actives = NULL;
6065 unsigned int cnt = 0, cnt2 = 0;
6066 struct machservice *ms;
6067 jobmgr_t jm;
6068 job_t ji;
6069
cf0bacfd
A
6070#if TARGET_OS_EMBEDDED
6071 return BOOTSTRAP_NOT_PRIVILEGED;
6072#endif
6073
5b0a4722
A
6074 if (!launchd_assumes(j != NULL)) {
6075 return BOOTSTRAP_NO_MEMORY;
6076 }
6077
6078 jm = j->mgr;
6079
6080 LIST_FOREACH(ji, &jm->jobs, sle) {
6081 SLIST_FOREACH(ms, &ji->machservices, sle) {
6082 if (!ms->per_pid) {
6083 cnt++;
6084 }
6085 }
6086 }
6087
6088 if (cnt == 0) {
6089 goto out;
6090 }
6091
6092 mig_allocate((vm_address_t *)&service_names, cnt * sizeof(service_names[0]));
6093 if (!launchd_assumes(service_names != NULL)) {
6094 goto out_bad;
6095 }
6096
6097 mig_allocate((vm_address_t *)&service_actives, cnt * sizeof(service_actives[0]));
6098 if (!launchd_assumes(service_actives != NULL)) {
6099 goto out_bad;
6100 }
6101
6102 LIST_FOREACH(ji, &jm->jobs, sle) {
6103 SLIST_FOREACH(ms, &ji->machservices, sle) {
6104 if (!ms->per_pid) {
6105 strlcpy(service_names[cnt2], machservice_name(ms), sizeof(service_names[0]));
6106 service_actives[cnt2] = machservice_status(ms);
6107 cnt2++;
6108 }
6109 }
6110 }
6111
6112 launchd_assumes(cnt == cnt2);
6113
6114out:
6115 *servicenamesp = service_names;
6116 *serviceactivesp = service_actives;
6117 *servicenames_cnt = *serviceactives_cnt = cnt;
6118
6119 return BOOTSTRAP_SUCCESS;
6120
6121out_bad:
6122 if (service_names) {
6123 mig_deallocate((vm_address_t)service_names, cnt * sizeof(service_names[0]));
6124 }
6125 if (service_actives) {
6126 mig_deallocate((vm_address_t)service_actives, cnt * sizeof(service_actives[0]));
6127 }
6128
6129 return BOOTSTRAP_NO_MEMORY;
6130}
6131
6132void
6133job_reparent_hack(job_t j, const char *where)
6134{
6135 jobmgr_t jmi, jmi2;
6136
6137 ensure_root_bkgd_setup();
6138
6139 /* NULL is only passed for our custom API for LaunchServices. If that is the case, we do magic. */
6140 if (where == NULL) {
6141 if (strcasecmp(j->mgr->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
6142 where = VPROCMGR_SESSION_LOGINWINDOW;
6143 } else {
6144 where = VPROCMGR_SESSION_AQUA;
6145 }
6146 }
6147
6148 if (strcasecmp(j->mgr->name, where) == 0) {
6149 return;
6150 }
6151
6152 SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
6153 if (jmi->shutting_down) {
6154 continue;
6155 } else if (strcasecmp(jmi->name, where) == 0) {
6156 goto jm_found;
6157 } else if (strcasecmp(jmi->name, VPROCMGR_SESSION_BACKGROUND) == 0 && getpid() == 1) {
6158 SLIST_FOREACH(jmi2, &jmi->submgrs, sle) {
6159 if (strcasecmp(jmi2->name, where) == 0) {
6160 jmi = jmi2;
6161 goto jm_found;
6162 }
6163 }
6164 }
6165 }
6166
6167jm_found:
6168 if (job_assumes(j, jmi != NULL)) {
6169 struct machservice *msi;
6170
6171 SLIST_FOREACH(msi, &j->machservices, sle) {
6172 LIST_REMOVE(msi, name_hash_sle);
6173 }
6174
6175 LIST_REMOVE(j, sle);
6176 LIST_INSERT_HEAD(&jmi->jobs, j, sle);
6177 j->mgr = jmi;
6178
6179 SLIST_FOREACH(msi, &j->machservices, sle) {
6180 LIST_INSERT_HEAD(&j->mgr->ms_hash[hash_ms(msi->name)], msi, name_hash_sle);
6181 }
6182 }
6183}
6184
6185kern_return_t
6186job_mig_move_subset(job_t j, mach_port_t target_subset, name_t session_type)
6187{
6188 mach_msg_type_number_t l2l_i, l2l_port_cnt = 0;
6189 mach_port_array_t l2l_ports = NULL;
6190 mach_port_t reqport, rcvright;
6191 kern_return_t kr = 1;
6192 launch_data_t out_obj_array = NULL;
6193 struct ldcred ldc;
6194 jobmgr_t jmr = NULL;
6195
cf0bacfd
A
6196#if TARGET_OS_EMBEDDED
6197 return BOOTSTRAP_NOT_PRIVILEGED;
6198#endif
6199
5b0a4722
A
6200 if (!launchd_assumes(j != NULL)) {
6201 return BOOTSTRAP_NO_MEMORY;
6202 }
6203
6204 runtime_get_caller_creds(&ldc);
6205
6206 if (target_subset == MACH_PORT_NULL) {
6207 job_t j2;
6208
6209 if (j->mgr->session_initialized) {
6210 if (ldc.uid == 0 && getpid() == 1) {
6211 if (strcmp(j->mgr->name, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
6212 job_t ji, jn;
6213
6214 LIST_FOREACH_SAFE(ji, &j->mgr->jobs, sle, jn) {
6215 if (!ji->anonymous) {
6216 job_remove(ji);
6217 }
6218 }
6219
6220 ensure_root_bkgd_setup();
6221
6222 SLIST_REMOVE(&j->mgr->parentmgr->submgrs, j->mgr, jobmgr_s, sle);
6223 j->mgr->parentmgr = background_jobmgr;
6224 SLIST_INSERT_HEAD(&j->mgr->parentmgr->submgrs, j->mgr, sle);
6225
6226 /*
6227 * We really should wait for all the jobs to die before proceeding. See 5351245 for more info.
6228 *
6229 * We have hacked around this in job_find() by ignoring jobs that are pending removal.
6230 */
6231
6232 } else if (strcmp(j->mgr->name, VPROCMGR_SESSION_AQUA) == 0) {
6233 job_log(j, LOG_DEBUG, "Tried to move the Aqua session.");
6234 return 0;
6235 } else if (strcmp(j->mgr->name, VPROCMGR_SESSION_BACKGROUND) == 0) {
6236 job_log(j, LOG_DEBUG, "Tried to move the background session.");
6237 return 0;
6238 } else {
6239 job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
6240 kr = BOOTSTRAP_NOT_PRIVILEGED;
6241 goto out;
6242 }
6243 } else {
6244 job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
6245 kr = BOOTSTRAP_NOT_PRIVILEGED;
6246 goto out;
6247 }
6248 } else if (ldc.uid == 0 && getpid() == 1 && strcmp(session_type, VPROCMGR_SESSION_STANDARDIO) == 0) {
6249 ensure_root_bkgd_setup();
6250
6251 SLIST_REMOVE(&j->mgr->parentmgr->submgrs, j->mgr, jobmgr_s, sle);
6252 j->mgr->parentmgr = background_jobmgr;
6253 SLIST_INSERT_HEAD(&j->mgr->parentmgr->submgrs, j->mgr, sle);
6254 } else if (strcmp(session_type, VPROCMGR_SESSION_LOGINWINDOW) == 0) {
6255 jobmgr_t jmi;
6256
6257 /*
6258 * 5330262
6259 *
6260 * We're working around LoginWindow and the WindowServer.
6261 *
6262 * In practice, there is only one LoginWindow session. Unfortunately, for certain
6263 * scenarios, the WindowServer spawns loginwindow, and in those cases, it frequently
6264 * spawns a replacement loginwindow session before cleaning up the previous one.
6265 *
6266 * We're going to use the creation of a new LoginWindow context as a clue that the
6267 * previous LoginWindow context is on the way out and therefore we should just
6268 * kick-start the shutdown of it.
6269 */
6270
6271 SLIST_FOREACH(jmi, &root_jobmgr->submgrs, sle) {
6272 if (jmi->shutting_down) {
6273 continue;
6274 } else if (strcasecmp(jmi->name, session_type) == 0) {
6275 jobmgr_shutdown(jmi);
6276 break;
6277 }
6278 }
6279 }
6280
6281 jobmgr_log(j->mgr, LOG_DEBUG, "Renaming to: %s", session_type);
f36da725 6282 strcpy(j->mgr->name_init, session_type);
5b0a4722
A
6283
6284 if (job_assumes(j, (j2 = jobmgr_init_session(j->mgr, session_type, false)))) {
6285 job_assumes(j, job_dispatch(j2, true));
6286 }
6287
6288 kr = 0;
6289 goto out;
6290 } else if (job_mig_intran2(root_jobmgr, target_subset, ldc.pid)) {
6291 job_log(j, LOG_ERR, "Moving a session to ourself is bogus.");
6292
6293 kr = BOOTSTRAP_NOT_PRIVILEGED;
6294 goto out;
6295 }
6296
6297 job_log(j, LOG_DEBUG, "Move subset attempt: 0x%x", target_subset);
6298
f36da725 6299 errno = kr = _vproc_grab_subset(target_subset, &reqport, &rcvright, &out_obj_array, &l2l_ports, &l2l_port_cnt);
5b0a4722
A
6300
6301 if (!job_assumes(j, kr == 0)) {
6302 goto out;
6303 }
6304
6305 launchd_assert(launch_data_array_get_count(out_obj_array) == l2l_port_cnt);
6306
6307 if (!job_assumes(j, (jmr = jobmgr_new(j->mgr, reqport, rcvright, false, session_type)) != NULL)) {
6308 kr = BOOTSTRAP_NO_MEMORY;
6309 goto out;
6310 }
6311
6312 for (l2l_i = 0; l2l_i < l2l_port_cnt; l2l_i++) {
6313 launch_data_t tmp, obj_at_idx;
6314 struct machservice *ms;
6315 job_t j_for_service;
6316 const char *serv_name;
6317 pid_t target_pid;
6318 bool serv_perpid;
6319
6320 job_assumes(j, obj_at_idx = launch_data_array_get_index(out_obj_array, l2l_i));
6321 job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_PID));
6322 target_pid = (pid_t)launch_data_get_integer(tmp);
6323 job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_PERPID));
6324 serv_perpid = launch_data_get_bool(tmp);
6325 job_assumes(j, tmp = launch_data_dict_lookup(obj_at_idx, TAKE_SUBSET_NAME));
6326 serv_name = launch_data_get_string(tmp);
6327
6328 j_for_service = jobmgr_find_by_pid(jmr, target_pid, true);
6329
6330 if (!j_for_service) {
6331 /* The PID probably exited */
6332 job_assumes(j, launchd_mport_deallocate(l2l_ports[l2l_i]) == KERN_SUCCESS);
6333 continue;
6334 }
6335
6336 if ((ms = machservice_new(j_for_service, serv_name, &l2l_ports[l2l_i], serv_perpid))) {
6337 machservice_request_notifications(ms);
6338 }
6339 }
6340
6341 kr = 0;
6342
6343out:
6344 if (out_obj_array) {
6345 launch_data_free(out_obj_array);
6346 }
6347
6348 if (l2l_ports) {
6349 mig_deallocate((vm_address_t)l2l_ports, l2l_port_cnt * sizeof(l2l_ports[0]));
6350 }
6351
6352 if (kr == 0) {
6353 if (target_subset) {
6354 job_assumes(j, launchd_mport_deallocate(target_subset) == KERN_SUCCESS);
6355 }
6356 } else if (jmr) {
6357 jobmgr_shutdown(jmr);
6358 }
6359
6360 return kr;
6361}
6362
6363kern_return_t
6364job_mig_take_subset(job_t j, mach_port_t *reqport, mach_port_t *rcvright,
6365 vm_offset_t *outdata, mach_msg_type_number_t *outdataCnt,
6366 mach_port_array_t *portsp, unsigned int *ports_cnt)
6367{
6368 launch_data_t tmp_obj, tmp_dict, outdata_obj_array = NULL;
6369 mach_port_array_t ports = NULL;
6370 unsigned int cnt = 0, cnt2 = 0;
6371 size_t packed_size;
6372 struct machservice *ms;
6373 jobmgr_t jm;
6374 job_t ji;
6375
cf0bacfd
A
6376#if TARGET_OS_EMBEDDED
6377 return BOOTSTRAP_NOT_PRIVILEGED;
6378#endif
6379
5b0a4722
A
6380 if (!launchd_assumes(j != NULL)) {
6381 return BOOTSTRAP_NO_MEMORY;
6382 }
6383
6384 jm = j->mgr;
6385
6386 if (getpid() != 1) {
6387 job_log(j, LOG_ERR, "Only the system launchd will transfer Mach sub-bootstraps.");
6388 return BOOTSTRAP_NOT_PRIVILEGED;
6389 } else if (jobmgr_parent(jm) == NULL) {
6390 job_log(j, LOG_ERR, "Root Mach bootstrap cannot be transferred.");
6391 return BOOTSTRAP_NOT_PRIVILEGED;
6392 } else if (strcasecmp(jm->name, VPROCMGR_SESSION_AQUA) == 0) {
6393 job_log(j, LOG_ERR, "Cannot transfer a setup GUI session.");
6394 return BOOTSTRAP_NOT_PRIVILEGED;
6395 } else if (!j->anonymous) {
6396 job_log(j, LOG_ERR, "Only the anonymous job can transfer Mach sub-bootstraps.");
6397 return BOOTSTRAP_NOT_PRIVILEGED;
6398 }
6399
6400 job_log(j, LOG_DEBUG, "Transferring sub-bootstrap to the per session launchd.");
6401
6402 outdata_obj_array = launch_data_alloc(LAUNCH_DATA_ARRAY);
6403 if (!job_assumes(j, outdata_obj_array)) {
6404 goto out_bad;
6405 }
6406
6407 *outdataCnt = 20 * 1024 * 1024;
6408 mig_allocate(outdata, *outdataCnt);
6409 if (!job_assumes(j, *outdata != 0)) {
6410 return 1;
6411 }
6412
6413 LIST_FOREACH(ji, &j->mgr->jobs, sle) {
6414 if (!ji->anonymous) {
6415 continue;
6416 }
6417 SLIST_FOREACH(ms, &ji->machservices, sle) {
6418 cnt++;
6419 }
6420 }
6421
6422 mig_allocate((vm_address_t *)&ports, cnt * sizeof(ports[0]));
6423 if (!launchd_assumes(ports != NULL)) {
6424 goto out_bad;
6425 }
6426
6427 LIST_FOREACH(ji, &j->mgr->jobs, sle) {
6428 if (!ji->anonymous) {
6429 continue;
6430 }
6431
6432 SLIST_FOREACH(ms, &ji->machservices, sle) {
6433 if (job_assumes(j, (tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY)))) {
6434 job_assumes(j, launch_data_array_set_index(outdata_obj_array, tmp_dict, cnt2));
6435 } else {
6436 goto out_bad;
6437 }
6438
6439 if (job_assumes(j, (tmp_obj = launch_data_new_string(machservice_name(ms))))) {
6440 job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_NAME));
6441 } else {
6442 goto out_bad;
6443 }
6444
6445 if (job_assumes(j, (tmp_obj = launch_data_new_integer((ms->job->p))))) {
6446 job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_PID));
6447 } else {
6448 goto out_bad;
6449 }
6450
6451 if (job_assumes(j, (tmp_obj = launch_data_new_bool((ms->per_pid))))) {
6452 job_assumes(j, launch_data_dict_insert(tmp_dict, tmp_obj, TAKE_SUBSET_PERPID));
6453 } else {
6454 goto out_bad;
6455 }
6456
6457 ports[cnt2] = machservice_port(ms);
6458
6459 /* Increment the send right by one so we can shutdown the jobmgr cleanly */
6460 jobmgr_assumes(jm, (errno = mach_port_mod_refs(mach_task_self(), ports[cnt2], MACH_PORT_RIGHT_SEND, 1)) == 0);
6461 cnt2++;
6462 }
6463 }
6464
6465 launchd_assumes(cnt == cnt2);
6466
6467 packed_size = launch_data_pack(outdata_obj_array, (void *)*outdata, *outdataCnt, NULL, NULL);
6468 if (!job_assumes(j, packed_size != 0)) {
6469 goto out_bad;
6470 }
6471
6472 launch_data_free(outdata_obj_array);
6473
6474 *portsp = ports;
6475 *ports_cnt = cnt;
6476
6477 *reqport = jm->req_port;
6478 *rcvright = jm->jm_port;
6479
6480 jm->req_port = 0;
6481 jm->jm_port = 0;
6482
6483 workaround_5477111 = j;
6484
6485 jobmgr_shutdown(jm);
6486
6487 return BOOTSTRAP_SUCCESS;
6488
6489out_bad:
6490 if (outdata_obj_array) {
6491 launch_data_free(outdata_obj_array);
6492 }
6493 if (*outdata) {
6494 mig_deallocate(*outdata, *outdataCnt);
6495 }
6496 if (ports) {
6497 mig_deallocate((vm_address_t)ports, cnt * sizeof(ports[0]));
6498 }
6499
6500 return BOOTSTRAP_NO_MEMORY;
6501}
6502
6503kern_return_t
6504job_mig_subset(job_t j, mach_port_t requestorport, mach_port_t *subsetportp)
6505{
6506 int bsdepth = 0;
6507 jobmgr_t jmr;
6508
6509 if (!launchd_assumes(j != NULL)) {
6510 return BOOTSTRAP_NO_MEMORY;
6511 }
6512
6513 jmr = j->mgr;
6514
6515 while ((jmr = jobmgr_parent(jmr)) != NULL) {
6516 bsdepth++;
6517 }
6518
6519 /* Since we use recursion, we need an artificial depth for subsets */
6520 if (bsdepth > 100) {
6521 job_log(j, LOG_ERR, "Mach sub-bootstrap create request failed. Depth greater than: %d", bsdepth);
6522 return BOOTSTRAP_NO_MEMORY;
6523 }
6524
6525 if ((jmr = jobmgr_new(j->mgr, requestorport, MACH_PORT_NULL, false, NULL)) == NULL) {
6526 if (requestorport == MACH_PORT_NULL) {
6527 return BOOTSTRAP_NOT_PRIVILEGED;
6528 }
6529 return BOOTSTRAP_NO_MEMORY;
6530 }
6531
6532 *subsetportp = jmr->jm_port;
6533 return BOOTSTRAP_SUCCESS;
6534}
6535
6536kern_return_t
6537job_mig_create_service(job_t j, name_t servicename, mach_port_t *serviceportp)
6538{
6539 struct machservice *ms;
6540
6541 if (!launchd_assumes(j != NULL)) {
6542 return BOOTSTRAP_NO_MEMORY;
6543 }
6544
6545 if (job_prog(j)[0] == '\0') {
6546 job_log(j, LOG_ERR, "Mach service creation requires a target server: %s", servicename);
6547 return BOOTSTRAP_NOT_PRIVILEGED;
6548 }
6549
6550 if (!j->legacy_mach_job) {
6551 job_log(j, LOG_ERR, "bootstrap_create_service() is only allowed against legacy Mach jobs: %s", servicename);
6552 return BOOTSTRAP_NOT_PRIVILEGED;
6553 }
6554
6555 ms = jobmgr_lookup_service(j->mgr, servicename, false, 0);
6556 if (ms) {
6557 job_log(j, LOG_DEBUG, "Mach service creation attempt for failed. Already exists: %s", servicename);
6558 return BOOTSTRAP_NAME_IN_USE;
6559 }
6560
6561 job_checkin(j);
6562
6563 *serviceportp = MACH_PORT_NULL;
6564 ms = machservice_new(j, servicename, serviceportp, false);
6565
6566 if (!launchd_assumes(ms != NULL)) {
6567 goto out_bad;
6568 }
6569
6570 return BOOTSTRAP_SUCCESS;
6571
6572out_bad:
6573 launchd_assumes(launchd_mport_close_recv(*serviceportp) == KERN_SUCCESS);
6574 return BOOTSTRAP_NO_MEMORY;
6575}
6576
f36da725
A
6577kern_return_t
6578job_mig_embedded_wait(job_t j, name_t targetlabel, integer_t *waitstatus)
6579{
6580 job_t otherj;
6581
6582 if (!launchd_assumes(j != NULL)) {
6583 return BOOTSTRAP_NO_MEMORY;
6584 }
6585
6586 if (unlikely(!(otherj = job_find(targetlabel)))) {
6587 return BOOTSTRAP_UNKNOWN_SERVICE;
6588 }
6589
6590 *waitstatus = j->last_exit_status;
6591
6592 return 0;
6593}
6594
6595kern_return_t
6596job_mig_embedded_kickstart(job_t j, name_t targetlabel, pid_t *out_pid, mach_port_t *out_name_port)
6597{
6598 struct ldcred ldc;
6599 kern_return_t kr;
6600 job_t otherj;
6601
6602 if (!launchd_assumes(j != NULL)) {
6603 return BOOTSTRAP_NO_MEMORY;
6604 }
6605
6606 if (unlikely(!(otherj = job_find(targetlabel)))) {
6607 return BOOTSTRAP_UNKNOWN_SERVICE;
6608 }
6609
6610 runtime_get_caller_creds(&ldc);
6611
6612 if (ldc.euid != 0 && ldc.euid != geteuid()
6613#if TARGET_OS_EMBEDDED
6614 && j->username && otherj->username
6615 && strcmp(j->username, otherj->username) != 0
6616#endif
6617 ) {
6618 return BOOTSTRAP_NOT_PRIVILEGED;
6619 }
6620
6621 otherj = job_dispatch(otherj, true);
6622
6623 if (!job_assumes(j, otherj && otherj->p)) {
6624 return BOOTSTRAP_NO_MEMORY;
6625 }
6626
6627 kr = task_name_for_pid(mach_task_self(), otherj->p, out_name_port);
6628 if (!job_assumes(j, kr == 0)) {
6629 return kr;
6630 }
6631
6632 *out_pid = otherj->p;
6633
6634 return 0;
6635}
6636
5b0a4722
A
6637kern_return_t
6638job_mig_wait(job_t j, mach_port_t srp, integer_t *waitstatus)
6639{
6640 if (!launchd_assumes(j != NULL)) {
6641 return BOOTSTRAP_NO_MEMORY;
6642 }
6643#if 0
6644 struct ldcred ldc;
6645 runtime_get_caller_creds(&ldc);
6646#endif
6647 return job_handle_mpm_wait(j, srp, waitstatus);
6648}
6649
6650kern_return_t
6651job_mig_uncork_fork(job_t j)
6652{
6653 if (!launchd_assumes(j != NULL)) {
6654 return BOOTSTRAP_NO_MEMORY;
6655 }
6656
6657 if (!j->stall_before_exec) {
6658 job_log(j, LOG_WARNING, "Attempt to uncork a job that isn't in the middle of a fork().");
6659 return 1;
6660 }
6661
6662 job_uncork_fork(j);
6663 j->stall_before_exec = false;
6664 return 0;
6665}
6666
6667kern_return_t
6668job_mig_set_service_policy(job_t j, pid_t target_pid, uint64_t flags, name_t target_service)
6669{
cf0bacfd 6670 struct ldcred ldc;
5b0a4722
A
6671 job_t target_j;
6672
6673 if (!launchd_assumes(j != NULL)) {
6674 return BOOTSTRAP_NO_MEMORY;
6675 }
6676
cf0bacfd
A
6677 runtime_get_caller_creds(&ldc);
6678
6679#if TARGET_OS_EMBEDDED
ededfeb7 6680 if( ldc.euid ) {
cf0bacfd
A
6681 return BOOTSTRAP_NOT_PRIVILEGED;
6682 }
ededfeb7
A
6683#else
6684 if( ldc.euid && (ldc.euid != getuid()) ) {
6685 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, target_pid };
6686 struct kinfo_proc kp;
6687 size_t len = sizeof(kp);
6688
6689 job_assumes(j, sysctl(mib, 4, &kp, &len, NULL, 0) != -1);
6690 job_assumes(j, len == sizeof(kp));
6691
6692 uid_t kp_euid = kp.kp_eproc.e_ucred.cr_uid;
6693 uid_t kp_uid = kp.kp_eproc.e_pcred.p_ruid;
6694
6695 if( ldc.euid == kp_euid ) {
6696 job_log(j, LOG_DEBUG, "Working around rdar://problem/5982485 and allowing job to set policy for PID %u.", target_pid);
6697 } else {
6698 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);
6699
6700 return BOOTSTRAP_NOT_PRIVILEGED;
6701 }
6702 }
6703#endif
cf0bacfd 6704
5b0a4722
A
6705 if (!job_assumes(j, (target_j = jobmgr_find_by_pid(j->mgr, target_pid, true)) != NULL)) {
6706 return BOOTSTRAP_NO_MEMORY;
6707 }
6708
6709 if (SLIST_EMPTY(&j->mspolicies)) {
6710 job_log(j, LOG_DEBUG, "Setting policy on job \"%s\" for Mach service: %s", target_j->label, target_service);
6711 if (target_service[0]) {
6712 job_assumes(j, mspolicy_new(target_j, target_service, flags & BOOTSTRAP_ALLOW_LOOKUP, flags & BOOTSTRAP_PER_PID_SERVICE, false));
6713 } else {
6714 target_j->deny_unknown_mslookups = !(flags & BOOTSTRAP_ALLOW_LOOKUP);
f36da725 6715 target_j->deny_job_creation = (bool)(flags & BOOTSTRAP_DENY_JOB_CREATION);
5b0a4722
A
6716 }
6717 } else {
6718 job_log(j, LOG_WARNING, "Jobs that have policies assigned to them may not set policies.");
6719 return BOOTSTRAP_NOT_PRIVILEGED;
6720 }
6721
6722 return 0;
6723}
6724
6725kern_return_t
6726job_mig_spawn(job_t j, vm_offset_t indata, mach_msg_type_number_t indataCnt, pid_t *child_pid, mach_port_t *obsvr_port)
6727{
6728 launch_data_t input_obj = NULL;
6729 size_t data_offset = 0;
6730 struct ldcred ldc;
6731 job_t jr;
6732
cf0bacfd
A
6733#if TARGET_OS_EMBEDDED
6734 return BOOTSTRAP_NOT_PRIVILEGED;
6735#endif
6736
5b0a4722
A
6737 runtime_get_caller_creds(&ldc);
6738
6739 if (!launchd_assumes(j != NULL)) {
6740 return BOOTSTRAP_NO_MEMORY;
6741 }
6742
f36da725
A
6743 if (unlikely(j->deny_job_creation)) {
6744 return BOOTSTRAP_NOT_PRIVILEGED;
6745 }
6746
5b0a4722
A
6747 if (getpid() == 1 && ldc.euid && ldc.uid) {
6748 job_log(j, LOG_DEBUG, "Punting spawn to per-user-context");
6749 return VPROC_ERR_TRY_PER_USER;
6750 }
6751
6752 if (!job_assumes(j, indataCnt != 0)) {
6753 return 1;
6754 }
6755
6756 if (!job_assumes(j, (input_obj = launch_data_unpack((void *)indata, indataCnt, NULL, 0, &data_offset, NULL)) != NULL)) {
6757 return 1;
6758 }
6759
6760 jr = jobmgr_import2(j->mgr, input_obj);
6761
6762 if (!job_assumes(j, jr != NULL)) {
6763 switch (errno) {
6764 case EEXIST:
6765 return BOOTSTRAP_NAME_IN_USE;
6766 default:
6767 return BOOTSTRAP_NO_MEMORY;
6768 }
6769 }
6770
6771 job_reparent_hack(jr, NULL);
6772
6773 if (getpid() == 1) {
6774 jr->mach_uid = ldc.uid;
6775 }
6776
6777 jr->unload_at_exit = true;
6778 jr->wait4pipe_eof = true;
6779 jr->abandon_pg = true;
6780 jr->stall_before_exec = jr->wait4debugger;
6781 jr->wait4debugger = false;
6782
6783 jr = job_dispatch(jr, true);
6784
6785 if (!job_assumes(j, jr != NULL)) {
6786 return BOOTSTRAP_NO_MEMORY;
6787 }
6788
fe044cc9
A
6789 if (!job_assumes(jr, jr->p)) {
6790 job_remove(jr);
6791 return BOOTSTRAP_NO_MEMORY;
6792 }
5b0a4722
A
6793
6794 if (!job_setup_machport(jr)) {
6795 job_remove(jr);
6796 return BOOTSTRAP_NO_MEMORY;
6797 }
6798
fe044cc9 6799 job_log(jr, LOG_DEBUG, "Spawned by PID %u: %s", j->p, j->label);
5b0a4722
A
6800
6801 *child_pid = jr->p;
6802 *obsvr_port = jr->j_port;
6803
6804 mig_deallocate(indata, indataCnt);
6805
6806 return BOOTSTRAP_SUCCESS;
6807}
6808
6809void
6810jobmgr_init(bool sflag)
6811{
6812 const char *root_session_type = getpid() == 1 ? VPROCMGR_SESSION_SYSTEM : VPROCMGR_SESSION_BACKGROUND;
6813
6814 launchd_assert((root_jobmgr = jobmgr_new(NULL, MACH_PORT_NULL, MACH_PORT_NULL, sflag, root_session_type)) != NULL);
6815}
6816
6817size_t
6818our_strhash(const char *s)
6819{
6820 size_t c, r = 5381;
6821
6822 /* djb2
6823 * This algorithm was first reported by Dan Bernstein many years ago in comp.lang.c
6824 */
6825
6826 while ((c = *s++)) {
6827 r = ((r << 5) + r) + c; /* hash*33 + c */
6828 }
6829
6830 return r;
6831}
6832
6833size_t
6834hash_label(const char *label)
6835{
6836 return our_strhash(label) % LABEL_HASH_SIZE;
6837}
6838
6839size_t
6840hash_ms(const char *msstr)
6841{
6842 return our_strhash(msstr) % MACHSERVICE_HASH_SIZE;
6843}
6844
6845bool
6846mspolicy_copy(job_t j_to, job_t j_from)
6847{
6848 struct mspolicy *msp;
6849
6850 SLIST_FOREACH(msp, &j_from->mspolicies, sle) {
6851 if (!mspolicy_new(j_to, msp->name, msp->allow, msp->per_pid, true)) {
6852 return false;
6853 }
6854 }
6855
6856 return true;
6857}
6858
6859bool
6860mspolicy_new(job_t j, const char *name, bool allow, bool pid_local, bool skip_check)
6861{
6862 struct mspolicy *msp;
6863
6864 if (!skip_check) SLIST_FOREACH(msp, &j->mspolicies, sle) {
6865 if (msp->per_pid != pid_local) {
6866 continue;
6867 } else if (strcmp(msp->name, name) == 0) {
6868 return false;
6869 }
6870 }
6871
6872 if ((msp = calloc(1, sizeof(struct mspolicy) + strlen(name) + 1)) == NULL) {
6873 return false;
6874 }
6875
6876 strcpy((char *)msp->name, name);
6877 msp->per_pid = pid_local;
6878 msp->allow = allow;
6879
6880 SLIST_INSERT_HEAD(&j->mspolicies, msp, sle);
6881
6882 return true;
6883}
6884
6885void
6886mspolicy_setup(launch_data_t obj, const char *key, void *context)
6887{
6888 job_t j = context;
6889
6890 if (launch_data_get_type(obj) != LAUNCH_DATA_BOOL) {
6891 job_log(j, LOG_WARNING, "Invalid object type for Mach service policy key: %s", key);
6892 return;
6893 }
6894
6895 job_assumes(j, mspolicy_new(j, key, launch_data_get_bool(obj), false, false));
6896}
6897
6898bool
6899mspolicy_check(job_t j, const char *name, bool pid_local)
6900{
6901 struct mspolicy *mspi;
6902
6903 SLIST_FOREACH(mspi, &j->mspolicies, sle) {
6904 if (mspi->per_pid != pid_local) {
6905 continue;
6906 } else if (strcmp(mspi->name, name) != 0) {
6907 continue;
6908 }
6909 return mspi->allow;
6910 }
6911
6912 return !j->deny_unknown_mslookups;
6913}
6914
6915void
6916mspolicy_delete(job_t j, struct mspolicy *msp)
6917{
6918 SLIST_REMOVE(&j->mspolicies, msp, mspolicy, sle);
6919
6920 free(msp);
6921}
6922
6923bool
6924waiting4removal_new(job_t j, mach_port_t rp)
6925{
6926 struct waiting_for_removal *w4r;
6927
6928 if (!job_assumes(j, (w4r = malloc(sizeof(struct waiting_for_removal))) != NULL)) {
6929 return false;
6930 }
6931
6932 w4r->reply_port = rp;
6933
6934 SLIST_INSERT_HEAD(&j->removal_watchers, w4r, sle);
6935
6936 return true;
6937}
6938
6939void
6940waiting4removal_delete(job_t j, struct waiting_for_removal *w4r)
6941{
6942 job_assumes(j, job_mig_send_signal_reply(w4r->reply_port, 0) == 0);
6943
6944 SLIST_REMOVE(&j->removal_watchers, w4r, waiting_for_removal, sle);
6945
6946 free(w4r);
6947}
6948
f36da725
A
6949size_t
6950get_kern_max_proc(void)
6951{
6952 int mib[] = { CTL_KERN, KERN_MAXPROC };
6953 int max = 100;
6954 size_t max_sz = sizeof(max);
6955
6956 launchd_assumes(sysctl(mib, 2, &max, &max_sz, NULL, 0) != -1);
6957
6958 return max;
6959}
6960
5b0a4722
A
6961void
6962do_file_init(void)
6963{
f36da725
A
6964 struct stat sb;
6965
5b0a4722
A
6966 launchd_assert(mach_timebase_info(&tbi) == 0);
6967
f36da725
A
6968 if (stat("/AppleInternal", &sb) == 0) {
6969 do_apple_internal_magic = true;
6970 }
5b0a4722 6971}