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