2 * @APPLE_APACHE_LICENSE_HEADER_START@
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 * @APPLE_APACHE_LICENSE_HEADER_END@
19 static const char *const __rcs_file_version__
= "$Revision: 23792 $";
22 #include "launchd_core_logic.h"
24 #include <TargetConditionals.h>
25 #include <mach/mach.h>
26 #include <mach/mach_error.h>
27 #include <mach/mach_time.h>
28 #include <mach/boolean.h>
29 #include <mach/message.h>
30 #include <mach/notify.h>
31 #include <mach/mig_errors.h>
32 #include <mach/mach_traps.h>
33 #include <mach/mach_interface.h>
34 #include <mach/host_info.h>
35 #include <mach/mach_host.h>
36 #include <mach/exception.h>
37 #include <mach/host_reboot.h>
38 #include <sys/types.h>
39 #include <sys/queue.h>
40 #include <sys/event.h>
42 #include <sys/ucred.h>
43 #include <sys/fcntl.h>
45 #include <sys/reboot.h>
47 #include <sys/sysctl.h>
48 #include <sys/sockio.h>
50 #include <sys/resource.h>
51 #include <sys/ioctl.h>
52 #include <sys/mount.h>
55 #include <netinet/in.h>
56 #include <netinet/in_var.h>
57 #include <netinet6/nd6.h>
58 #include <bsm/libbsm.h>
81 #include <quarantine.h>
85 #include "launch_priv.h"
86 #include "launch_internal.h"
87 #include "bootstrap.h"
88 #include "bootstrap_priv.h"
90 #include "vproc_internal.h"
95 #include "launchd_runtime.h"
96 #include "launchd_unix_ipc.h"
97 #include "protocol_vproc.h"
98 #include "protocol_vprocServer.h"
99 #include "protocol_job_reply.h"
101 #define LAUNCHD_MIN_JOB_RUN_TIME 10
102 #define LAUNCHD_DEFAULT_EXIT_TIMEOUT 20
103 #define LAUNCHD_SIGKILL_TIMER 5
106 #define TAKE_SUBSET_NAME "TakeSubsetName"
107 #define TAKE_SUBSET_PID "TakeSubsetPID"
108 #define TAKE_SUBSET_PERPID "TakeSubsetPerPID"
110 #define IS_POWER_OF_TWO(v) (!(v & (v - 1)) && v)
112 extern char **environ
;
114 struct waiting_for_removal
{
115 SLIST_ENTRY(waiting_for_removal
) sle
;
116 mach_port_t reply_port
;
119 static bool waiting4removal_new(job_t j
, mach_port_t rp
);
120 static void waiting4removal_delete(job_t j
, struct waiting_for_removal
*w4r
);
123 SLIST_ENTRY(mspolicy
) sle
;
124 unsigned int allow
:1, per_pid
:1;
128 static bool mspolicy_new(job_t j
, const char *name
, bool allow
, bool pid_local
, bool skip_check
);
129 static bool mspolicy_copy(job_t j_to
, job_t j_from
);
130 static void mspolicy_setup(launch_data_t obj
, const char *key
, void *context
);
131 static bool mspolicy_check(job_t j
, const char *name
, bool pid_local
);
132 static void mspolicy_delete(job_t j
, struct mspolicy
*msp
);
135 SLIST_ENTRY(machservice
) sle
;
136 SLIST_ENTRY(machservice
) special_port_sle
;
137 LIST_ENTRY(machservice
) name_hash_sle
;
138 LIST_ENTRY(machservice
) port_hash_sle
;
140 uint64_t bad_perf_cnt
;
141 unsigned int gen_num
;
142 mach_port_name_t port
;
143 unsigned int isActive
:1, reset
:1, recv
:1, hide
:1, kUNCServer
:1, per_user_hack
:1, debug_on_close
:1, per_pid
:1, special_port_num
:10;
147 static SLIST_HEAD(, machservice
) special_ports
; /* hack, this should be per jobmgr_t */
149 #define PORT_HASH_SIZE 32
150 #define HASH_PORT(x) (IS_POWER_OF_TWO(PORT_HASH_SIZE) ? (MACH_PORT_INDEX(x) & (PORT_HASH_SIZE - 1)) : (MACH_PORT_INDEX(x) % PORT_HASH_SIZE))
152 static LIST_HEAD(, machservice
) port_hash
[PORT_HASH_SIZE
];
154 static void machservice_setup(launch_data_t obj
, const char *key
, void *context
);
155 static void machservice_setup_options(launch_data_t obj
, const char *key
, void *context
);
156 static void machservice_resetport(job_t j
, struct machservice
*ms
);
157 static struct machservice
*machservice_new(job_t j
, const char *name
, mach_port_t
*serviceport
, bool pid_local
);
158 static void machservice_ignore(job_t j
, struct machservice
*ms
);
159 static void machservice_watch(job_t j
, struct machservice
*ms
);
160 static void machservice_delete(job_t j
, struct machservice
*, bool port_died
);
161 static void machservice_request_notifications(struct machservice
*);
162 static mach_port_t
machservice_port(struct machservice
*);
163 static job_t
machservice_job(struct machservice
*);
164 static bool machservice_hidden(struct machservice
*);
165 static bool machservice_active(struct machservice
*);
166 static const char *machservice_name(struct machservice
*);
167 static bootstrap_status_t
machservice_status(struct machservice
*);
170 SLIST_ENTRY(socketgroup
) sle
;
172 unsigned int junkfds
:1, fd_cnt
:31;
179 static bool socketgroup_new(job_t j
, const char *name
, int *fds
, unsigned int fd_cnt
, bool junkfds
);
180 static void socketgroup_delete(job_t j
, struct socketgroup
*sg
);
181 static void socketgroup_watch(job_t j
, struct socketgroup
*sg
);
182 static void socketgroup_ignore(job_t j
, struct socketgroup
*sg
);
183 static void socketgroup_callback(job_t j
);
184 static void socketgroup_setup(launch_data_t obj
, const char *key
, void *context
);
185 static void socketgroup_kevent_mod(job_t j
, struct socketgroup
*sg
, bool do_add
);
187 struct calendarinterval
{
188 LIST_ENTRY(calendarinterval
) global_sle
;
189 SLIST_ENTRY(calendarinterval
) sle
;
195 static LIST_HEAD(, calendarinterval
) sorted_calendar_events
;
197 static bool calendarinterval_new(job_t j
, struct tm
*w
);
198 static bool calendarinterval_new_from_obj(job_t j
, launch_data_t obj
);
199 static void calendarinterval_new_from_obj_dict_walk(launch_data_t obj
, const char *key
, void *context
);
200 static void calendarinterval_delete(job_t j
, struct calendarinterval
*ci
);
201 static void calendarinterval_setalarm(job_t j
, struct calendarinterval
*ci
);
202 static void calendarinterval_callback(void);
203 static void calendarinterval_sanity_check(void);
206 SLIST_ENTRY(envitem
) sle
;
214 static bool envitem_new(job_t j
, const char *k
, const char *v
, bool global
);
215 static void envitem_delete(job_t j
, struct envitem
*ei
, bool global
);
216 static void envitem_setup(launch_data_t obj
, const char *key
, void *context
);
219 SLIST_ENTRY(limititem
) sle
;
221 unsigned int setsoft
:1, sethard
:1, which
:30;
224 static bool limititem_update(job_t j
, int w
, rlim_t r
);
225 static void limititem_delete(job_t j
, struct limititem
*li
);
226 static void limititem_setup(launch_data_t obj
, const char *key
, void *context
);
228 static void seatbelt_setup_flags(launch_data_t obj
, const char *key
, void *context
);
244 // FILESYSTEMTYPE_IS_MOUNTED, /* for nfsiod, but maybe others */
245 } semaphore_reason_t
;
247 struct semaphoreitem
{
248 SLIST_ENTRY(semaphoreitem
) sle
;
249 semaphore_reason_t why
;
257 struct semaphoreitem_dict_iter_context
{
259 semaphore_reason_t why_true
;
260 semaphore_reason_t why_false
;
263 static bool semaphoreitem_new(job_t j
, semaphore_reason_t why
, const char *what
);
264 static void semaphoreitem_delete(job_t j
, struct semaphoreitem
*si
);
265 static void semaphoreitem_setup(launch_data_t obj
, const char *key
, void *context
);
266 static void semaphoreitem_setup_dict_iter(launch_data_t obj
, const char *key
, void *context
);
267 static void semaphoreitem_callback(job_t j
, struct kevent
*kev
);
268 static void semaphoreitem_watch(job_t j
, struct semaphoreitem
*si
);
269 static void semaphoreitem_ignore(job_t j
, struct semaphoreitem
*si
);
270 static void semaphoreitem_runtime_mod_ref(struct semaphoreitem
*si
, bool add
);
272 #define ACTIVE_JOB_HASH_SIZE 32
273 #define ACTIVE_JOB_HASH(x) (IS_POWER_OF_TWO(ACTIVE_JOB_HASH_SIZE) ? (x & (ACTIVE_JOB_HASH_SIZE - 1)) : (x % ACTIVE_JOB_HASH_SIZE))
274 #define MACHSERVICE_HASH_SIZE 37
277 kq_callback kqjobmgr_callback
;
278 SLIST_ENTRY(jobmgr_s
) sle
;
279 SLIST_HEAD(, jobmgr_s
) submgrs
;
280 LIST_HEAD(, job_s
) jobs
;
281 LIST_HEAD(, job_s
) active_jobs
[ACTIVE_JOB_HASH_SIZE
];
282 LIST_HEAD(, machservice
) ms_hash
[MACHSERVICE_HASH_SIZE
];
284 mach_port_t req_port
;
287 unsigned int global_on_demand_cnt
;
288 unsigned int hopefully_first_cnt
;
289 unsigned int normal_active_cnt
;
290 unsigned int sent_stop_to_normal_jobs
:1, sent_stop_to_hopefully_last_jobs
:1, shutting_down
:1, session_initialized
:1;
297 #define jobmgr_assumes(jm, e) \
298 (__builtin_expect(!(e), 0) ? jobmgr_log_bug(jm, __rcs_file_version__, __FILE__, __LINE__, #e), false : true)
300 static jobmgr_t
jobmgr_new(jobmgr_t jm
, mach_port_t requestorport
, mach_port_t transfer_port
, bool sflag
, const char *name
);
301 static job_t
jobmgr_import2(jobmgr_t jm
, launch_data_t pload
);
302 static jobmgr_t
jobmgr_parent(jobmgr_t jm
);
303 static jobmgr_t
jobmgr_do_garbage_collection(jobmgr_t jm
);
304 static bool jobmgr_label_test(jobmgr_t jm
, const char *str
);
305 static void jobmgr_reap_bulk(jobmgr_t jm
, struct kevent
*kev
);
306 static void jobmgr_log_stray_children(jobmgr_t jm
);
307 static void jobmgr_remove(jobmgr_t jm
);
308 static void jobmgr_dispatch_all(jobmgr_t jm
, bool newmounthack
);
309 static job_t
jobmgr_init_session(jobmgr_t jm
, const char *session_type
, bool sflag
);
310 static job_t
jobmgr_find_by_pid(jobmgr_t jm
, pid_t p
, bool create_anon
);
311 static job_t
job_mig_intran2(jobmgr_t jm
, mach_port_t mport
, pid_t upid
);
312 static void job_export_all2(jobmgr_t jm
, launch_data_t where
);
313 static void jobmgr_callback(void *obj
, struct kevent
*kev
);
314 static void jobmgr_setup_env_from_other_jobs(jobmgr_t jm
);
315 static void jobmgr_export_env_from_other_jobs(jobmgr_t jm
, launch_data_t dict
);
316 static struct machservice
*jobmgr_lookup_service(jobmgr_t jm
, const char *name
, bool check_parent
, pid_t target_pid
);
317 static void jobmgr_logv(jobmgr_t jm
, int pri
, int err
, const char *msg
, va_list ap
) __attribute__((format(printf
, 4, 0)));
318 static void jobmgr_log(jobmgr_t jm
, int pri
, const char *msg
, ...) __attribute__((format(printf
, 3, 4)));
319 /* static void jobmgr_log_error(jobmgr_t jm, int pri, const char *msg, ...) __attribute__((format(printf, 3, 4))); */
320 static void jobmgr_log_bug(jobmgr_t jm
, const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
);
322 #define DO_RUSAGE_SUMMATION 0
324 #define AUTO_PICK_LEGACY_LABEL (const char *)(~0)
327 kq_callback kqjob_callback
;
328 LIST_ENTRY(job_s
) sle
;
329 LIST_ENTRY(job_s
) pid_hash_sle
;
330 LIST_ENTRY(job_s
) label_hash_sle
;
331 SLIST_HEAD(, socketgroup
) sockets
;
332 SLIST_HEAD(, calendarinterval
) cal_intervals
;
333 SLIST_HEAD(, envitem
) global_env
;
334 SLIST_HEAD(, envitem
) env
;
335 SLIST_HEAD(, limititem
) limits
;
336 SLIST_HEAD(, mspolicy
) mspolicies
;
337 SLIST_HEAD(, machservice
) machservices
;
338 SLIST_HEAD(, semaphoreitem
) semaphores
;
339 SLIST_HEAD(, waiting_for_removal
) removal_watchers
;
340 #if DO_RUSAGE_SUMMATION
343 cpu_type_t
*j_binpref
;
344 size_t j_binpref_cnt
;
346 mach_port_t wait_reply_port
; /* we probably should switch to a list of waiters */
357 char *alt_exc_handler
;
358 struct machservice
*lastlookup
;
359 unsigned int lastlookup_gennum
;
361 char *seatbelt_profile
;
362 uint64_t seatbelt_flags
;
365 void *quarantine_data
;
366 size_t quarantine_data_sz
;
370 int last_exit_status
;
374 unsigned int timeout
;
375 unsigned int exit_timeout
;
377 uint64_t sent_sigterm_time
;
379 uint32_t min_run_time
;
380 uint32_t start_interval
;
381 unsigned int checkedin
:1, anonymous
:1, debug
:1, inetcompat
:1, inetcompat_wait
:1,
382 ondemand
:1, session_create
:1, low_pri_io
:1, no_init_groups
:1, priv_port_has_senders
:1,
383 importing_global_env
:1, importing_hard_limits
:1, setmask
:1, legacy_mach_job
:1, start_pending
:1;
385 unsigned int globargv
:1, wait4debugger
:1, unload_at_exit
:1, stall_before_exec
:1, only_once
:1,
386 currently_ignored
:1, forced_peers_to_demand_mode
:1, setnice
:1, hopefully_exits_last
:1, removal_pending
:1,
387 wait4pipe_eof
:1, sent_sigkill
:1, debug_before_kill
:1, weird_bootstrap
:1, start_on_mount
:1,
388 per_user
:1, hopefully_exits_first
:1, deny_unknown_mslookups
:1, unload_at_mig_return
:1, abandon_pg
:1,
389 poll_for_vfs_changes
:1, internal_exc_handler
:1, deny_job_creation
:1;
393 #define LABEL_HASH_SIZE 53
395 static LIST_HEAD(, job_s
) label_hash
[LABEL_HASH_SIZE
];
396 static size_t hash_label(const char *label
) __attribute__((pure
));
397 static size_t hash_ms(const char *msstr
) __attribute__((pure
));
400 #define job_assumes(j, e) \
401 (__builtin_expect(!(e), 0) ? job_log_bug(j, __rcs_file_version__, __FILE__, __LINE__, #e), false : true)
403 static void job_import_keys(launch_data_t obj
, const char *key
, void *context
);
404 static void job_import_bool(job_t j
, const char *key
, bool value
);
405 static void job_import_string(job_t j
, const char *key
, const char *value
);
406 static void job_import_integer(job_t j
, const char *key
, long long value
);
407 static void job_import_dictionary(job_t j
, const char *key
, launch_data_t value
);
408 static void job_import_array(job_t j
, const char *key
, launch_data_t value
);
409 static void job_import_opaque(job_t j
, const char *key
, launch_data_t value
);
410 static bool job_set_global_on_demand(job_t j
, bool val
);
411 static const char *job_active(job_t j
);
412 static void job_watch(job_t j
);
413 static void job_ignore(job_t j
);
414 static void job_reap(job_t j
);
415 static bool job_useless(job_t j
);
416 static bool job_keepalive(job_t j
);
417 static void job_start(job_t j
);
418 static void job_start_child(job_t j
) __attribute__((noreturn
));
419 static void job_setup_attributes(job_t j
);
420 static bool job_setup_machport(job_t j
);
421 static void job_setup_fd(job_t j
, int target_fd
, const char *path
, int flags
);
422 static void job_postfork_become_user(job_t j
);
423 #if !TARGET_OS_EMBEDDED
424 static void job_enable_audit_for_user(job_t j
, uid_t u
, char *name
);
426 static void job_find_and_blame_pids_with_weird_uids(job_t j
);
427 static void job_force_sampletool(job_t j
);
428 static void job_setup_exception_port(job_t j
, task_t target_task
);
429 static void job_reparent_hack(job_t j
, const char *where
);
430 static void job_callback(void *obj
, struct kevent
*kev
);
431 static void job_callback_proc(job_t j
, int flags
, int fflags
);
432 static void job_callback_timer(job_t j
, void *ident
);
433 static void job_callback_read(job_t j
, int ident
);
434 static void job_log_stray_pg(job_t j
);
435 static job_t
job_new_anonymous(jobmgr_t jm
, pid_t anonpid
);
436 static job_t
job_new(jobmgr_t jm
, const char *label
, const char *prog
, const char *const *argv
);
437 static job_t
job_new_via_mach_init(job_t j
, const char *cmd
, uid_t uid
, bool ond
);
438 static const char *job_prog(job_t j
);
439 static jobmgr_t
job_get_bs(job_t j
);
440 static void job_kill(job_t j
);
441 static void job_uncork_fork(job_t j
);
442 static void job_log_stdouterr(job_t j
);
443 static void job_logv(job_t j
, int pri
, int err
, const char *msg
, va_list ap
) __attribute__((format(printf
, 4, 0)));
444 static void job_log_error(job_t j
, int pri
, const char *msg
, ...) __attribute__((format(printf
, 3, 4)));
445 static void job_log_bug(job_t j
, const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
);
446 static void job_log_stdouterr2(job_t j
, const char *msg
, ...);
447 static void job_set_exeception_port(job_t j
, mach_port_t port
);
448 static kern_return_t
job_handle_mpm_wait(job_t j
, mach_port_t srp
, int *waitstatus
);
452 static const struct {
455 } launchd_keys2limits
[] = {
456 { LAUNCH_JOBKEY_RESOURCELIMIT_CORE
, RLIMIT_CORE
},
457 { LAUNCH_JOBKEY_RESOURCELIMIT_CPU
, RLIMIT_CPU
},
458 { LAUNCH_JOBKEY_RESOURCELIMIT_DATA
, RLIMIT_DATA
},
459 { LAUNCH_JOBKEY_RESOURCELIMIT_FSIZE
, RLIMIT_FSIZE
},
460 { LAUNCH_JOBKEY_RESOURCELIMIT_MEMLOCK
, RLIMIT_MEMLOCK
},
461 { LAUNCH_JOBKEY_RESOURCELIMIT_NOFILE
, RLIMIT_NOFILE
},
462 { LAUNCH_JOBKEY_RESOURCELIMIT_NPROC
, RLIMIT_NPROC
},
463 { LAUNCH_JOBKEY_RESOURCELIMIT_RSS
, RLIMIT_RSS
},
464 { LAUNCH_JOBKEY_RESOURCELIMIT_STACK
, RLIMIT_STACK
},
467 static time_t cronemu(int mon
, int mday
, int hour
, int min
);
468 static time_t cronemu_wday(int wday
, int hour
, int min
);
469 static bool cronemu_mon(struct tm
*wtm
, int mon
, int mday
, int hour
, int min
);
470 static bool cronemu_mday(struct tm
*wtm
, int mday
, int hour
, int min
);
471 static bool cronemu_hour(struct tm
*wtm
, int hour
, int min
);
472 static bool cronemu_min(struct tm
*wtm
, int min
);
474 /* miscellaneous file local functions */
475 static void ensure_root_bkgd_setup(void);
476 static int dir_has_files(job_t j
, const char *path
);
477 static char **mach_cmd2argv(const char *string
);
478 static size_t our_strhash(const char *s
) __attribute__((pure
));
479 static void extract_rcsid_substr(const char *i
, char *o
, size_t osz
);
480 static void do_first_per_user_launchd_hack(void);
481 static size_t get_kern_max_proc(void);
482 static void do_file_init(void) __attribute__((constructor
));
484 /* file local globals */
485 static bool do_apple_internal_magic
;
486 static size_t total_children
;
487 static size_t total_anon_children
;
488 static mach_port_t the_exception_server
;
489 static bool did_first_per_user_launchd_BootCache_hack
;
490 #define JOB_BOOTCACHE_HACK_CHECK(j) (j->per_user && !did_first_per_user_launchd_BootCache_hack && (j->mach_uid >= 500) && (j->mach_uid != (uid_t)-2))
491 static jobmgr_t background_jobmgr
;
492 static job_t workaround_5477111
;
493 static mach_timebase_info_data_t tbi
;
495 /* process wide globals */
496 mach_port_t inherited_bootstrap_port
;
497 jobmgr_t root_jobmgr
;
503 struct semaphoreitem
*si
;
504 struct socketgroup
*sg
;
505 struct machservice
*ms
;
507 if (j
->currently_ignored
) {
511 job_log(j
, LOG_DEBUG
, "Ignoring...");
513 j
->currently_ignored
= true;
515 if (j
->poll_for_vfs_changes
) {
516 j
->poll_for_vfs_changes
= false;
517 job_assumes(j
, kevent_mod((uintptr_t)&j
->semaphores
, EVFILT_TIMER
, EV_DELETE
, 0, 0, j
) != -1);
520 SLIST_FOREACH(sg
, &j
->sockets
, sle
) {
521 socketgroup_ignore(j
, sg
);
524 SLIST_FOREACH(ms
, &j
->machservices
, sle
) {
525 machservice_ignore(j
, ms
);
528 SLIST_FOREACH(si
, &j
->semaphores
, sle
) {
529 semaphoreitem_ignore(j
, si
);
536 struct semaphoreitem
*si
;
537 struct socketgroup
*sg
;
538 struct machservice
*ms
;
540 if (!j
->currently_ignored
) {
544 job_log(j
, LOG_DEBUG
, "Watching...");
546 j
->currently_ignored
= false;
548 SLIST_FOREACH(sg
, &j
->sockets
, sle
) {
549 socketgroup_watch(j
, sg
);
552 SLIST_FOREACH(ms
, &j
->machservices
, sle
) {
553 machservice_watch(j
, ms
);
556 SLIST_FOREACH(si
, &j
->semaphores
, sle
) {
557 semaphoreitem_watch(j
, si
);
564 if (!j
->p
|| j
->anonymous
) {
568 job_assumes(j
, runtime_kill(j
->p
, SIGTERM
) != -1);
569 j
->sent_sigterm_time
= mach_absolute_time();
571 if (j
->exit_timeout
) {
572 job_assumes(j
, kevent_mod((uintptr_t)&j
->exit_timeout
, EVFILT_TIMER
,
573 EV_ADD
|EV_ONESHOT
, NOTE_SECONDS
, j
->exit_timeout
, j
) != -1);
576 job_log(j
, LOG_DEBUG
, "Sent SIGTERM signal");
582 launch_data_t tmp
, tmp2
, tmp3
, r
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
588 if ((tmp
= launch_data_new_string(j
->label
))) {
589 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_LABEL
);
591 if ((tmp
= launch_data_new_string(j
->mgr
->name
))) {
592 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE
);
594 if ((tmp
= launch_data_new_bool(j
->ondemand
))) {
595 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_ONDEMAND
);
597 if ((tmp
= launch_data_new_integer(j
->last_exit_status
))) {
598 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_LASTEXITSTATUS
);
600 if (j
->p
&& (tmp
= launch_data_new_integer(j
->p
))) {
601 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_PID
);
603 if ((tmp
= launch_data_new_integer(j
->timeout
))) {
604 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_TIMEOUT
);
606 if (j
->prog
&& (tmp
= launch_data_new_string(j
->prog
))) {
607 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_PROGRAM
);
609 if (j
->stdoutpath
&& (tmp
= launch_data_new_string(j
->stdoutpath
))) {
610 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_STANDARDOUTPATH
);
612 if (j
->stderrpath
&& (tmp
= launch_data_new_string(j
->stderrpath
))) {
613 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_STANDARDERRORPATH
);
615 if (j
->argv
&& (tmp
= launch_data_alloc(LAUNCH_DATA_ARRAY
))) {
618 for (i
= 0; i
< j
->argc
; i
++) {
619 if ((tmp2
= launch_data_new_string(j
->argv
[i
]))) {
620 launch_data_array_set_index(tmp
, tmp2
, i
);
624 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
627 if (j
->session_create
&& (tmp
= launch_data_new_bool(true))) {
628 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_SESSIONCREATE
);
631 if (j
->inetcompat
&& (tmp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
))) {
632 if ((tmp2
= launch_data_new_bool(j
->inetcompat_wait
))) {
633 launch_data_dict_insert(tmp
, tmp2
, LAUNCH_JOBINETDCOMPATIBILITY_WAIT
);
635 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
);
638 if (!SLIST_EMPTY(&j
->sockets
) && (tmp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
))) {
639 struct socketgroup
*sg
;
642 SLIST_FOREACH(sg
, &j
->sockets
, sle
) {
646 if ((tmp2
= launch_data_alloc(LAUNCH_DATA_ARRAY
))) {
647 for (i
= 0; i
< sg
->fd_cnt
; i
++) {
648 if ((tmp3
= launch_data_new_fd(sg
->fds
[i
]))) {
649 launch_data_array_set_index(tmp2
, tmp3
, i
);
652 launch_data_dict_insert(tmp
, tmp2
, sg
->name
);
656 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_SOCKETS
);
659 if (!SLIST_EMPTY(&j
->machservices
) && (tmp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
))) {
660 struct machservice
*ms
;
664 SLIST_FOREACH(ms
, &j
->machservices
, sle
) {
667 tmp3
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
670 tmp2
= launch_data_new_machport(MACH_PORT_NULL
);
671 launch_data_dict_insert(tmp3
, tmp2
, ms
->name
);
674 tmp2
= launch_data_new_machport(MACH_PORT_NULL
);
675 launch_data_dict_insert(tmp
, tmp2
, ms
->name
);
679 launch_data_dict_insert(r
, tmp
, LAUNCH_JOBKEY_MACHSERVICES
);
682 launch_data_dict_insert(r
, tmp3
, LAUNCH_JOBKEY_PERJOBMACHSERVICES
);
690 jobmgr_log_active_jobs(jobmgr_t jm
)
692 const char *why_active
;
696 SLIST_FOREACH(jmi
, &jm
->submgrs
, sle
) {
697 jobmgr_log_active_jobs(jmi
);
700 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
701 why_active
= job_active(ji
);
703 job_log(ji
, LOG_DEBUG
, "%s", why_active
? why_active
: "Inactive");
709 still_alive_with_check(void)
711 jobmgr_log(root_jobmgr
, LOG_NOTICE
, "Still alive with %lu/%lu children", total_children
, total_anon_children
);
713 jobmgr_log_active_jobs(root_jobmgr
);
715 runtime_closelog(); /* hack to flush logs */
719 jobmgr_shutdown(jobmgr_t jm
)
724 jobmgr_log(jm
, LOG_DEBUG
, "Beginning job manager shutdown with flags: %s", reboot_flags_to_C_names(jm
->reboot_flags
));
726 jm
->shutting_down
= true;
728 SLIST_FOREACH_SAFE(jmi
, &jm
->submgrs
, sle
, jmn
) {
729 jobmgr_shutdown(jmi
);
732 if (jm
->hopefully_first_cnt
) {
733 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
734 if (ji
->p
&& ji
->hopefully_exits_first
) {
740 if (debug_shutdown_hangs
&& jm
->parentmgr
== NULL
&& getpid() == 1) {
741 runtime_set_timeout(still_alive_with_check
, 5);
744 return jobmgr_do_garbage_collection(jm
);
748 jobmgr_remove(jobmgr_t jm
)
753 jobmgr_log(jm
, LOG_DEBUG
, "Removed job manager");
755 if (!jobmgr_assumes(jm
, SLIST_EMPTY(&jm
->submgrs
))) {
756 while ((jmi
= SLIST_FIRST(&jm
->submgrs
))) {
761 while ((ji
= LIST_FIRST(&jm
->jobs
))) {
762 /* We should only have anonymous jobs left */
763 job_assumes(ji
, ji
->anonymous
);
768 jobmgr_assumes(jm
, launchd_mport_deallocate(jm
->req_port
) == KERN_SUCCESS
);
772 jobmgr_assumes(jm
, launchd_mport_close_recv(jm
->jm_port
) == KERN_SUCCESS
);
775 if (jm
== background_jobmgr
) {
776 background_jobmgr
= NULL
;
781 SLIST_REMOVE(&jm
->parentmgr
->submgrs
, jm
, jobmgr_s
, sle
);
782 } else if (getpid() == 1) {
783 jobmgr_log(jm
, LOG_DEBUG
, "About to call: reboot(%s)", reboot_flags_to_C_names(jm
->reboot_flags
));
785 jobmgr_assumes(jm
, reboot(jm
->reboot_flags
) != -1);
789 jobmgr_log(jm
, LOG_DEBUG
, "About to exit");
799 struct waiting_for_removal
*w4r
;
800 struct calendarinterval
*ci
;
801 struct semaphoreitem
*si
;
802 struct socketgroup
*sg
;
803 struct machservice
*ms
;
804 struct limititem
*li
;
805 struct mspolicy
*msp
;
808 if (j
->p
&& j
->anonymous
) {
811 job_log(j
, LOG_DEBUG
, "Removal pended until the job exits");
813 if (!j
->removal_pending
) {
814 j
->removal_pending
= true;
821 ipc_close_all_with_job(j
);
823 if (j
->forced_peers_to_demand_mode
) {
824 job_set_global_on_demand(j
, false);
827 if (!job_assumes(j
, j
->forkfd
== 0)) {
828 job_assumes(j
, runtime_close(j
->forkfd
) != -1);
831 if (!job_assumes(j
, j
->log_redirect_fd
== 0)) {
832 job_assumes(j
, runtime_close(j
->log_redirect_fd
) != -1);
836 job_assumes(j
, launchd_mport_close_recv(j
->j_port
) == KERN_SUCCESS
);
839 if (!job_assumes(j
, j
->wait_reply_port
== MACH_PORT_NULL
)) {
840 job_assumes(j
, launchd_mport_deallocate(j
->wait_reply_port
) == KERN_SUCCESS
);
843 while ((msp
= SLIST_FIRST(&j
->mspolicies
))) {
844 mspolicy_delete(j
, msp
);
846 while ((sg
= SLIST_FIRST(&j
->sockets
))) {
847 socketgroup_delete(j
, sg
);
849 while ((ci
= SLIST_FIRST(&j
->cal_intervals
))) {
850 calendarinterval_delete(j
, ci
);
852 while ((ei
= SLIST_FIRST(&j
->env
))) {
853 envitem_delete(j
, ei
, false);
855 while ((ei
= SLIST_FIRST(&j
->global_env
))) {
856 envitem_delete(j
, ei
, true);
858 while ((li
= SLIST_FIRST(&j
->limits
))) {
859 limititem_delete(j
, li
);
861 while ((ms
= SLIST_FIRST(&j
->machservices
))) {
862 machservice_delete(j
, ms
, false);
864 while ((si
= SLIST_FIRST(&j
->semaphores
))) {
865 semaphoreitem_delete(j
, si
);
867 while ((w4r
= SLIST_FIRST(&j
->removal_watchers
))) {
868 waiting4removal_delete(j
, w4r
);
895 if (j
->alt_exc_handler
) {
896 free(j
->alt_exc_handler
);
899 if (j
->seatbelt_profile
) {
900 free(j
->seatbelt_profile
);
904 if (j
->quarantine_data
) {
905 free(j
->quarantine_data
);
911 if (j
->start_interval
) {
913 job_assumes(j
, kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
) != -1);
915 if (j
->poll_for_vfs_changes
) {
916 job_assumes(j
, kevent_mod((uintptr_t)&j
->semaphores
, EVFILT_TIMER
, EV_DELETE
, 0, 0, j
) != -1);
919 kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
922 LIST_REMOVE(j
, label_hash_sle
);
924 job_log(j
, LOG_DEBUG
, "Removed");
930 socketgroup_setup(launch_data_t obj
, const char *key
, void *context
)
932 launch_data_t tmp_oai
;
934 unsigned int i
, fd_cnt
= 1;
937 if (launch_data_get_type(obj
) == LAUNCH_DATA_ARRAY
) {
938 fd_cnt
= launch_data_array_get_count(obj
);
941 fds
= alloca(fd_cnt
* sizeof(int));
943 for (i
= 0; i
< fd_cnt
; i
++) {
944 if (launch_data_get_type(obj
) == LAUNCH_DATA_ARRAY
) {
945 tmp_oai
= launch_data_array_get_index(obj
, i
);
950 fds
[i
] = launch_data_get_fd(tmp_oai
);
953 socketgroup_new(j
, key
, fds
, fd_cnt
, strcmp(key
, LAUNCH_JOBKEY_BONJOURFDS
) == 0);
959 job_set_global_on_demand(job_t j
, bool val
)
961 if (j
->forced_peers_to_demand_mode
&& val
) {
963 } else if (!j
->forced_peers_to_demand_mode
&& !val
) {
967 if ((j
->forced_peers_to_demand_mode
= val
)) {
968 j
->mgr
->global_on_demand_cnt
++;
970 j
->mgr
->global_on_demand_cnt
--;
973 if (j
->mgr
->global_on_demand_cnt
== 0) {
974 jobmgr_dispatch_all(j
->mgr
, false);
981 job_setup_machport(job_t j
)
983 mach_msg_size_t mxmsgsz
;
985 if (!job_assumes(j
, launchd_mport_create_recv(&j
->j_port
) == KERN_SUCCESS
)) {
989 /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */
990 mxmsgsz
= sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem
);
991 if (job_mig_protocol_vproc_subsystem
.maxsize
> mxmsgsz
) {
992 mxmsgsz
= job_mig_protocol_vproc_subsystem
.maxsize
;
995 if (!job_assumes(j
, runtime_add_mport(j
->j_port
, protocol_vproc_server
, mxmsgsz
) == KERN_SUCCESS
)) {
999 if (!job_assumes(j
, launchd_mport_notify_req(j
->j_port
, MACH_NOTIFY_NO_SENDERS
) == KERN_SUCCESS
)) {
1000 job_assumes(j
, launchd_mport_close_recv(j
->j_port
) == KERN_SUCCESS
);
1006 job_assumes(j
, launchd_mport_close_recv(j
->j_port
) == KERN_SUCCESS
);
1012 job_new_via_mach_init(job_t j
, const char *cmd
, uid_t uid
, bool ond
)
1014 const char **argv
= (const char **)mach_cmd2argv(cmd
);
1017 if (!job_assumes(j
, argv
!= NULL
)) {
1021 jr
= job_new(j
->mgr
, AUTO_PICK_LEGACY_LABEL
, NULL
, argv
);
1025 /* jobs can easily be denied creation during shutdown */
1032 jr
->legacy_mach_job
= true;
1033 jr
->abandon_pg
= true;
1034 jr
->priv_port_has_senders
= true; /* the IPC that called us will make-send on this port */
1036 if (!job_setup_machport(jr
)) {
1040 job_log(jr
, LOG_INFO
, "Legacy%s server created", ond
? " on-demand" : "");
1052 job_handle_mpm_wait(job_t j
, mach_port_t srp
, int *waitstatus
)
1055 j
->wait_reply_port
= srp
;
1056 return MIG_NO_REPLY
;
1059 *waitstatus
= j
->last_exit_status
;
1065 job_new_anonymous(jobmgr_t jm
, pid_t anonpid
)
1067 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, anonpid
};
1068 struct kinfo_proc kp
;
1069 size_t len
= sizeof(kp
);
1070 const char *zombie
= NULL
;
1071 bool shutdown_state
;
1072 job_t jp
= NULL
, jr
= NULL
;
1074 if (!jobmgr_assumes(jm
, anonpid
!= 0)) {
1078 if (!jobmgr_assumes(jm
, anonpid
< 100000)) {
1079 /* The kernel current defines PID_MAX to be 99999, but that define isn't exported */
1083 if (!jobmgr_assumes(jm
, sysctl(mib
, 4, &kp
, &len
, NULL
, 0) != -1)) {
1087 if (len
!= sizeof(kp
)) {
1088 jobmgr_log(jm
, LOG_DEBUG
, "Tried to create an anonymous job for nonexistent PID: %u", anonpid
);
1092 if (!jobmgr_assumes(jm
, kp
.kp_proc
.p_comm
[0] != '\0')) {
1096 if (kp
.kp_proc
.p_stat
== SZOMB
) {
1097 jobmgr_log(jm
, LOG_DEBUG
, "Tried to create an anonymous job for zombie PID: %u", anonpid
);
1101 switch (kp
.kp_eproc
.e_ppid
) {
1106 if (getpid() != 1) {
1107 /* we cannot possibly find a parent job_t that is useful in this function */
1112 jp
= jobmgr_find_by_pid(jm
, kp
.kp_eproc
.e_ppid
, true);
1113 jobmgr_assumes(jm
, jp
!= NULL
);
1117 /* A total hack: Normally, job_new() returns an error during shutdown, but anonymous jobs are special. */
1118 if ((shutdown_state
= jm
->shutting_down
)) {
1119 jm
->shutting_down
= false;
1122 if (jobmgr_assumes(jm
, (jr
= job_new(jm
, AUTO_PICK_LEGACY_LABEL
, zombie
? zombie
: kp
.kp_proc
.p_comm
, NULL
)) != NULL
)) {
1123 u_int proc_fflags
= NOTE_EXEC
|NOTE_EXIT
/* |NOTE_REAP */;
1125 total_anon_children
++;
1126 jr
->anonymous
= true;
1129 /* anonymous process reaping is messy */
1130 LIST_INSERT_HEAD(&jm
->active_jobs
[ACTIVE_JOB_HASH(jr
->p
)], jr
, pid_hash_sle
);
1132 if (kevent_mod(jr
->p
, EVFILT_PROC
, EV_ADD
, proc_fflags
, 0, root_jobmgr
) == -1 && job_assumes(jr
, errno
== ESRCH
)) {
1133 /* zombies are weird */
1134 job_log(jr
, LOG_ERR
, "Failed to add kevent for PID %u. Will unload at MIG return", jr
->p
);
1135 jr
->unload_at_mig_return
= true;
1139 job_assumes(jr
, mspolicy_copy(jr
, jp
));
1142 if (shutdown_state
&& jm
->hopefully_first_cnt
== 0) {
1143 job_log(jr
, LOG_APPLEONLY
, "This process showed up to the party while all the guests were leaving. Odds are that it will have a miserable time");
1146 job_log(jr
, LOG_DEBUG
, "Created PID %u anonymously by PPID %u%s%s", anonpid
, kp
.kp_eproc
.e_ppid
, jp
? ": " : "", jp
? jp
->label
: "");
1149 if (shutdown_state
) {
1150 jm
->shutting_down
= true;
1157 job_new(jobmgr_t jm
, const char *label
, const char *prog
, const char *const *argv
)
1159 const char *const *argv_tmp
= argv
;
1160 char auto_label
[1000];
1161 const char *bn
= NULL
;
1163 size_t minlabel_len
;
1167 launchd_assert(offsetof(struct job_s
, kqjob_callback
) == 0);
1169 if (jm
->shutting_down
) {
1174 if (prog
== NULL
&& argv
== NULL
) {
1179 if (label
== AUTO_PICK_LEGACY_LABEL
) {
1180 bn
= prog
? prog
: basename((char *)argv
[0]); /* prog for auto labels is kp.kp_kproc.p_comm */
1181 snprintf(auto_label
, sizeof(auto_label
), "%s.%s", sizeof(void *) == 8 ? "0xdeadbeeffeedface" : "0xbabecafe", bn
);
1183 /* This is so we can do gross things later. See NOTE_EXEC for anonymous jobs */
1184 minlabel_len
= strlen(label
) + MAXCOMLEN
;
1186 minlabel_len
= strlen(label
);
1189 j
= calloc(1, sizeof(struct job_s
) + minlabel_len
+ 1);
1191 if (!jobmgr_assumes(jm
, j
!= NULL
)) {
1195 if (label
== auto_label
) {
1196 snprintf((char *)j
->label
, strlen(label
) + 1, "%p.%s", j
, bn
);
1198 strcpy((char *)j
->label
, label
);
1200 j
->kqjob_callback
= job_callback
;
1202 j
->min_run_time
= LAUNCHD_MIN_JOB_RUN_TIME
;
1203 j
->timeout
= RUNTIME_ADVISABLE_IDLE_TIMEOUT
;
1204 j
->exit_timeout
= LAUNCHD_DEFAULT_EXIT_TIMEOUT
;
1205 j
->currently_ignored
= true;
1207 j
->checkedin
= true;
1210 j
->prog
= strdup(prog
);
1211 if (!job_assumes(j
, j
->prog
!= NULL
)) {
1220 for (i
= 0; i
< j
->argc
; i
++) {
1221 cc
+= strlen(argv
[i
]) + 1;
1224 j
->argv
= malloc((j
->argc
+ 1) * sizeof(char *) + cc
);
1226 if (!job_assumes(j
, j
->argv
!= NULL
)) {
1230 co
= ((char *)j
->argv
) + ((j
->argc
+ 1) * sizeof(char *));
1232 for (i
= 0; i
< j
->argc
; i
++) {
1234 strcpy(co
, argv
[i
]);
1235 co
+= strlen(argv
[i
]) + 1;
1240 LIST_INSERT_HEAD(&jm
->jobs
, j
, sle
);
1241 LIST_INSERT_HEAD(&label_hash
[hash_label(j
->label
)], j
, label_hash_sle
);
1243 job_log(j
, LOG_DEBUG
, "Conceived");
1257 job_import(launch_data_t pload
)
1259 job_t j
= jobmgr_import2(root_jobmgr
, pload
);
1265 return job_dispatch(j
, false);
1269 job_import_bulk(launch_data_t pload
)
1271 launch_data_t resp
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
1273 size_t i
, c
= launch_data_array_get_count(pload
);
1275 ja
= alloca(c
* sizeof(job_t
));
1277 for (i
= 0; i
< c
; i
++) {
1278 if ((ja
[i
] = jobmgr_import2(root_jobmgr
, launch_data_array_get_index(pload
, i
)))) {
1281 launch_data_array_set_index(resp
, launch_data_new_errno(errno
), i
);
1284 for (i
= 0; i
< c
; i
++) {
1285 if (ja
[i
] == NULL
) {
1288 job_dispatch(ja
[i
], false);
1295 job_import_bool(job_t j
, const char *key
, bool value
)
1297 bool found_key
= false;
1302 if (strcasecmp(key
, LAUNCH_JOBKEY_ABANDONPROCESSGROUP
) == 0) {
1303 j
->abandon_pg
= value
;
1309 if (strcasecmp(key
, LAUNCH_JOBKEY_KEEPALIVE
) == 0) {
1310 j
->ondemand
= !value
;
1316 if (strcasecmp(key
, LAUNCH_JOBKEY_ONDEMAND
) == 0) {
1317 j
->ondemand
= value
;
1323 if (strcasecmp(key
, LAUNCH_JOBKEY_DEBUG
) == 0) {
1326 } else if (strcasecmp(key
, LAUNCH_JOBKEY_DISABLED
) == 0) {
1327 job_assumes(j
, !value
);
1333 if (strcasecmp(key
, LAUNCH_JOBKEY_HOPEFULLYEXITSLAST
) == 0) {
1334 j
->hopefully_exits_last
= value
;
1336 } else if (strcasecmp(key
, LAUNCH_JOBKEY_HOPEFULLYEXITSFIRST
) == 0) {
1337 j
->hopefully_exits_first
= value
;
1343 if (strcasecmp(key
, LAUNCH_JOBKEY_SESSIONCREATE
) == 0) {
1344 j
->session_create
= value
;
1346 } else if (strcasecmp(key
, LAUNCH_JOBKEY_STARTONMOUNT
) == 0) {
1347 j
->start_on_mount
= value
;
1349 } else if (strcasecmp(key
, LAUNCH_JOBKEY_SERVICEIPC
) == 0) {
1350 /* this only does something on Mac OS X 10.4 "Tiger" */
1356 if (strcasecmp(key
, LAUNCH_JOBKEY_LOWPRIORITYIO
) == 0) {
1357 j
->low_pri_io
= value
;
1359 } else if (strcasecmp(key
, LAUNCH_JOBKEY_LAUNCHONLYONCE
) == 0) {
1360 j
->only_once
= value
;
1366 if (strcasecmp(key
, LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER
) == 0) {
1367 j
->internal_exc_handler
= value
;
1373 if (strcasecmp(key
, LAUNCH_JOBKEY_INITGROUPS
) == 0) {
1374 if (getuid() != 0) {
1375 job_log(j
, LOG_WARNING
, "Ignored this key: %s", key
);
1378 j
->no_init_groups
= !value
;
1384 if (strcasecmp(key
, LAUNCH_JOBKEY_RUNATLOAD
) == 0) {
1386 /* We don't want value == false to change j->start_pending */
1387 j
->start_pending
= true;
1394 if (strcasecmp(key
, LAUNCH_JOBKEY_ENABLEGLOBBING
) == 0) {
1395 j
->globargv
= value
;
1397 } else if (strcasecmp(key
, LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL
) == 0) {
1398 j
->debug_before_kill
= value
;
1404 if (strcasecmp(key
, LAUNCH_JOBKEY_WAITFORDEBUGGER
) == 0) {
1405 j
->wait4debugger
= value
;
1414 job_log(j
, LOG_WARNING
, "Unknown key for boolean: %s", key
);
1419 job_import_string(job_t j
, const char *key
, const char *value
)
1421 char **where2put
= NULL
;
1426 if (strcasecmp(key
, LAUNCH_JOBKEY_MACHEXCEPTIONHANDLER
) == 0) {
1427 where2put
= &j
->alt_exc_handler
;
1432 if (strcasecmp(key
, LAUNCH_JOBKEY_PROGRAM
) == 0) {
1438 if (strcasecmp(key
, LAUNCH_JOBKEY_LABEL
) == 0) {
1440 } else if (strcasecmp(key
, LAUNCH_JOBKEY_LIMITLOADTOHOSTS
) == 0) {
1442 } else if (strcasecmp(key
, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS
) == 0) {
1444 } else if (strcasecmp(key
, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE
) == 0) {
1445 job_reparent_hack(j
, value
);
1451 if (strcasecmp(key
, LAUNCH_JOBKEY_ROOTDIRECTORY
) == 0) {
1452 if (getuid() != 0) {
1453 job_log(j
, LOG_WARNING
, "Ignored this key: %s", key
);
1456 where2put
= &j
->rootdir
;
1461 if (strcasecmp(key
, LAUNCH_JOBKEY_WORKINGDIRECTORY
) == 0) {
1462 where2put
= &j
->workingdir
;
1467 if (strcasecmp(key
, LAUNCH_JOBKEY_USERNAME
) == 0) {
1468 if (getuid() != 0) {
1469 job_log(j
, LOG_WARNING
, "Ignored this key: %s", key
);
1471 } else if (strcmp(value
, "root") == 0) {
1474 where2put
= &j
->username
;
1479 if (strcasecmp(key
, LAUNCH_JOBKEY_GROUPNAME
) == 0) {
1480 if (getuid() != 0) {
1481 job_log(j
, LOG_WARNING
, "Ignored this key: %s", key
);
1483 } else if (strcmp(value
, "wheel") == 0) {
1486 where2put
= &j
->groupname
;
1491 if (strcasecmp(key
, LAUNCH_JOBKEY_STANDARDOUTPATH
) == 0) {
1492 where2put
= &j
->stdoutpath
;
1493 } else if (strcasecmp(key
, LAUNCH_JOBKEY_STANDARDERRORPATH
) == 0) {
1494 where2put
= &j
->stderrpath
;
1496 } else if (strcasecmp(key
, LAUNCH_JOBKEY_SANDBOXPROFILE
) == 0) {
1497 where2put
= &j
->seatbelt_profile
;
1502 job_log(j
, LOG_WARNING
, "Unknown key for string: %s", key
);
1507 job_assumes(j
, (*where2put
= strdup(value
)) != NULL
);
1509 job_log(j
, LOG_WARNING
, "Unknown key: %s", key
);
1514 job_import_integer(job_t j
, const char *key
, long long value
)
1519 if (strcasecmp(key
, LAUNCH_JOBKEY_EXITTIMEOUT
) == 0) {
1521 job_log(j
, LOG_WARNING
, "%s less than zero. Ignoring.", LAUNCH_JOBKEY_EXITTIMEOUT
);
1522 } else if (value
> UINT32_MAX
) {
1523 job_log(j
, LOG_WARNING
, "%s is too large. Ignoring.", LAUNCH_JOBKEY_EXITTIMEOUT
);
1525 j
->exit_timeout
= value
;
1531 if (strcasecmp(key
, LAUNCH_JOBKEY_NICE
) == 0) {
1538 if (strcasecmp(key
, LAUNCH_JOBKEY_TIMEOUT
) == 0) {
1540 job_log(j
, LOG_WARNING
, "%s less than zero. Ignoring.", LAUNCH_JOBKEY_TIMEOUT
);
1541 } else if (value
> UINT32_MAX
) {
1542 job_log(j
, LOG_WARNING
, "%s is too large. Ignoring.", LAUNCH_JOBKEY_TIMEOUT
);
1546 } else if (strcasecmp(key
, LAUNCH_JOBKEY_THROTTLEINTERVAL
) == 0) {
1548 job_log(j
, LOG_WARNING
, "%s less than zero. Ignoring.", LAUNCH_JOBKEY_THROTTLEINTERVAL
);
1549 } else if (value
> UINT32_MAX
) {
1550 job_log(j
, LOG_WARNING
, "%s is too large. Ignoring.", LAUNCH_JOBKEY_THROTTLEINTERVAL
);
1552 j
->min_run_time
= value
;
1558 if (strcasecmp(key
, LAUNCH_JOBKEY_UMASK
) == 0) {
1565 if (strcasecmp(key
, LAUNCH_JOBKEY_STARTINTERVAL
) == 0) {
1567 job_log(j
, LOG_WARNING
, "%s is not greater than zero. Ignoring.", LAUNCH_JOBKEY_STARTINTERVAL
);
1568 } else if (value
> UINT32_MAX
) {
1569 job_log(j
, LOG_WARNING
, "%s is too large. Ignoring.", LAUNCH_JOBKEY_STARTINTERVAL
);
1572 j
->start_interval
= value
;
1574 job_assumes(j
, kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_SECONDS
, value
, j
) != -1);
1577 } else if (strcasecmp(key
, LAUNCH_JOBKEY_SANDBOXFLAGS
) == 0) {
1578 j
->seatbelt_flags
= value
;
1584 job_log(j
, LOG_WARNING
, "Unknown key for integer: %s", key
);
1590 job_import_opaque(job_t j
__attribute__((unused
)),
1591 const char *key
, launch_data_t value
__attribute__((unused
)))
1597 if (strcasecmp(key
, LAUNCH_JOBKEY_QUARANTINEDATA
) == 0) {
1598 size_t tmpsz
= launch_data_get_opaque_size(value
);
1600 if (job_assumes(j
, j
->quarantine_data
= malloc(tmpsz
))) {
1601 memcpy(j
->quarantine_data
, launch_data_get_opaque(value
), tmpsz
);
1602 j
->quarantine_data_sz
= tmpsz
;
1613 policy_setup(launch_data_t obj
, const char *key
, void *context
)
1616 bool found_key
= false;
1621 if (strcasecmp(key
, LAUNCH_JOBPOLICY_DENYCREATINGOTHERJOBS
) == 0) {
1622 j
->deny_job_creation
= launch_data_get_bool(obj
);
1630 if (unlikely(!found_key
)) {
1631 job_log(j
, LOG_WARNING
, "Unknown policy: %s", key
);
1636 job_import_dictionary(job_t j
, const char *key
, launch_data_t value
)
1643 if (strcasecmp(key
, LAUNCH_JOBKEY_POLICIES
) == 0) {
1644 launch_data_dict_iterate(value
, policy_setup
, j
);
1649 if (strcasecmp(key
, LAUNCH_JOBKEY_KEEPALIVE
) == 0) {
1650 launch_data_dict_iterate(value
, semaphoreitem_setup
, j
);
1655 if (strcasecmp(key
, LAUNCH_JOBKEY_INETDCOMPATIBILITY
) == 0) {
1656 j
->inetcompat
= true;
1657 j
->abandon_pg
= true;
1658 if ((tmp
= launch_data_dict_lookup(value
, LAUNCH_JOBINETDCOMPATIBILITY_WAIT
))) {
1659 j
->inetcompat_wait
= launch_data_get_bool(tmp
);
1665 if (strcasecmp(key
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
) == 0) {
1666 launch_data_dict_iterate(value
, envitem_setup
, j
);
1671 if (strcasecmp(key
, LAUNCH_JOBKEY_USERENVIRONMENTVARIABLES
) == 0) {
1672 j
->importing_global_env
= true;
1673 launch_data_dict_iterate(value
, envitem_setup
, j
);
1674 j
->importing_global_env
= false;
1679 if (strcasecmp(key
, LAUNCH_JOBKEY_SOCKETS
) == 0) {
1680 launch_data_dict_iterate(value
, socketgroup_setup
, j
);
1681 } else if (strcasecmp(key
, LAUNCH_JOBKEY_STARTCALENDARINTERVAL
) == 0) {
1682 calendarinterval_new_from_obj(j
, value
);
1683 } else if (strcasecmp(key
, LAUNCH_JOBKEY_SOFTRESOURCELIMITS
) == 0) {
1684 launch_data_dict_iterate(value
, limititem_setup
, j
);
1686 } else if (strcasecmp(key
, LAUNCH_JOBKEY_SANDBOXFLAGS
) == 0) {
1687 launch_data_dict_iterate(value
, seatbelt_setup_flags
, j
);
1693 if (strcasecmp(key
, LAUNCH_JOBKEY_HARDRESOURCELIMITS
) == 0) {
1694 j
->importing_hard_limits
= true;
1695 launch_data_dict_iterate(value
, limititem_setup
, j
);
1696 j
->importing_hard_limits
= false;
1701 if (strcasecmp(key
, LAUNCH_JOBKEY_MACHSERVICES
) == 0) {
1702 launch_data_dict_iterate(value
, machservice_setup
, j
);
1703 } else if (strcasecmp(key
, LAUNCH_JOBKEY_MACHSERVICELOOKUPPOLICIES
) == 0) {
1704 launch_data_dict_iterate(value
, mspolicy_setup
, j
);
1708 job_log(j
, LOG_WARNING
, "Unknown key for dictionary: %s", key
);
1714 job_import_array(job_t j
, const char *key
, launch_data_t value
)
1716 size_t i
, value_cnt
= launch_data_array_get_count(value
);
1722 if (strcasecmp(key
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
) == 0) {
1728 if (strcasecmp(key
, LAUNCH_JOBKEY_LIMITLOADTOHOSTS
) == 0) {
1730 } else if (strcasecmp(key
, LAUNCH_JOBKEY_LIMITLOADFROMHOSTS
) == 0) {
1732 } else if (strcasecmp(key
, LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE
) == 0) {
1733 job_log(j
, LOG_NOTICE
, "launchctl should have transformed the \"%s\" array to a string", LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE
);
1739 if (strcasecmp(key
, LAUNCH_JOBKEY_QUEUEDIRECTORIES
) == 0) {
1740 for (i
= 0; i
< value_cnt
; i
++) {
1741 str
= launch_data_get_string(launch_data_array_get_index(value
, i
));
1742 if (job_assumes(j
, str
!= NULL
)) {
1743 semaphoreitem_new(j
, DIR_NOT_EMPTY
, str
);
1751 if (strcasecmp(key
, LAUNCH_JOBKEY_WATCHPATHS
) == 0) {
1752 for (i
= 0; i
< value_cnt
; i
++) {
1753 str
= launch_data_get_string(launch_data_array_get_index(value
, i
));
1754 if (job_assumes(j
, str
!= NULL
)) {
1755 semaphoreitem_new(j
, PATH_CHANGES
, str
);
1762 if (strcasecmp(key
, LAUNCH_JOBKEY_BONJOURFDS
) == 0) {
1763 socketgroup_setup(value
, LAUNCH_JOBKEY_BONJOURFDS
, j
);
1764 } else if (strcasecmp(key
, LAUNCH_JOBKEY_BINARYORDERPREFERENCE
) == 0) {
1765 if (job_assumes(j
, j
->j_binpref
= malloc(value_cnt
* sizeof(*j
->j_binpref
)))) {
1766 j
->j_binpref_cnt
= value_cnt
;
1767 for (i
= 0; i
< value_cnt
; i
++) {
1768 j
->j_binpref
[i
] = launch_data_get_integer(launch_data_array_get_index(value
, i
));
1775 if (strcasecmp(key
, LAUNCH_JOBKEY_STARTCALENDARINTERVAL
) == 0) {
1776 for (i
= 0; i
< value_cnt
; i
++) {
1777 calendarinterval_new_from_obj(j
, launch_data_array_get_index(value
, i
));
1782 job_log(j
, LOG_WARNING
, "Unknown key for array: %s", key
);
1788 job_import_keys(launch_data_t obj
, const char *key
, void *context
)
1791 launch_data_type_t kind
;
1797 kind
= launch_data_get_type(obj
);
1800 case LAUNCH_DATA_BOOL
:
1801 job_import_bool(j
, key
, launch_data_get_bool(obj
));
1803 case LAUNCH_DATA_STRING
:
1804 job_import_string(j
, key
, launch_data_get_string(obj
));
1806 case LAUNCH_DATA_INTEGER
:
1807 job_import_integer(j
, key
, launch_data_get_integer(obj
));
1809 case LAUNCH_DATA_DICTIONARY
:
1810 job_import_dictionary(j
, key
, obj
);
1812 case LAUNCH_DATA_ARRAY
:
1813 job_import_array(j
, key
, obj
);
1815 case LAUNCH_DATA_OPAQUE
:
1816 job_import_opaque(j
, key
, obj
);
1819 job_log(j
, LOG_WARNING
, "Unknown value type '%d' for key: %s", kind
, key
);
1825 jobmgr_import2(jobmgr_t jm
, launch_data_t pload
)
1827 launch_data_t tmp
, ldpa
;
1828 const char *label
= NULL
, *prog
= NULL
;
1829 const char **argv
= NULL
;
1832 if (pload
== NULL
) {
1837 if (launch_data_get_type(pload
) != LAUNCH_DATA_DICTIONARY
) {
1842 if (!(tmp
= launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_LABEL
))) {
1847 if (launch_data_get_type(tmp
) != LAUNCH_DATA_STRING
) {
1852 if (!(label
= launch_data_get_string(tmp
))) {
1857 if ((tmp
= launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAM
)) &&
1858 (launch_data_get_type(tmp
) == LAUNCH_DATA_STRING
)) {
1859 prog
= launch_data_get_string(tmp
);
1862 if ((ldpa
= launch_data_dict_lookup(pload
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
))) {
1865 if (launch_data_get_type(ldpa
) != LAUNCH_DATA_ARRAY
) {
1870 c
= launch_data_array_get_count(ldpa
);
1872 argv
= alloca((c
+ 1) * sizeof(char *));
1874 for (i
= 0; i
< c
; i
++) {
1875 tmp
= launch_data_array_get_index(ldpa
, i
);
1877 if (launch_data_get_type(tmp
) != LAUNCH_DATA_STRING
) {
1882 argv
[i
] = launch_data_get_string(tmp
);
1888 if ((j
= job_find(label
)) != NULL
) {
1891 } else if (!jobmgr_label_test(jm
, label
)) {
1896 if ((j
= job_new(jm
, label
, prog
, argv
))) {
1897 launch_data_dict_iterate(pload
, job_import_keys
, j
);
1904 jobmgr_label_test(jobmgr_t jm
, const char *str
)
1906 char *endstr
= NULL
;
1909 if (str
[0] == '\0') {
1910 jobmgr_log(jm
, LOG_ERR
, "Empty job labels are not allowed");
1914 for (ptr
= str
; *ptr
; ptr
++) {
1915 if (iscntrl(*ptr
)) {
1916 jobmgr_log(jm
, LOG_ERR
, "ASCII control characters are not allowed in job labels. Index: %td Value: 0x%hhx", ptr
- str
, *ptr
);
1921 strtoll(str
, &endstr
, 0);
1923 if (str
!= endstr
) {
1924 jobmgr_log(jm
, LOG_ERR
, "Job labels are not allowed to begin with numbers: %s", str
);
1928 if ((strncasecmp(str
, "com.apple.launchd", strlen("com.apple.launchd")) == 0) ||
1929 (strncasecmp(str
, "com.apple.launchctl", strlen("com.apple.launchctl")) == 0)) {
1930 jobmgr_log(jm
, LOG_ERR
, "Job labels are not allowed to use a reserved prefix: %s", str
);
1938 job_find(const char *label
)
1942 LIST_FOREACH(ji
, &label_hash
[hash_label(label
)], label_hash_sle
) {
1943 if (ji
->removal_pending
) {
1944 continue; /* 5351245 */
1945 } else if (ji
->mgr
->shutting_down
) {
1946 continue; /* 5488633 */
1949 if (strcmp(ji
->label
, label
) == 0) {
1959 jobmgr_find_by_pid(jobmgr_t jm
, pid_t p
, bool create_anon
)
1963 LIST_FOREACH(ji
, &jm
->active_jobs
[ACTIVE_JOB_HASH(p
)], pid_hash_sle
) {
1971 } else if (create_anon
) {
1972 return job_new_anonymous(jm
, p
);
1979 job_mig_intran2(jobmgr_t jm
, mach_port_t mport
, pid_t upid
)
1984 if (jm
->jm_port
== mport
) {
1985 jobmgr_assumes(jm
, (ji
= jobmgr_find_by_pid(jm
, upid
, true)) != NULL
);
1989 SLIST_FOREACH(jmi
, &jm
->submgrs
, sle
) {
1992 if ((jr
= job_mig_intran2(jmi
, mport
, upid
))) {
1997 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
1998 if (ji
->j_port
== mport
) {
2007 job_mig_intran(mach_port_t p
)
2012 runtime_get_caller_creds(&ldc
);
2014 jr
= job_mig_intran2(root_jobmgr
, p
, ldc
.pid
);
2016 if (!jobmgr_assumes(root_jobmgr
, jr
!= NULL
)) {
2017 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, 0 };
2018 struct kinfo_proc kp
;
2019 size_t len
= sizeof(kp
);
2023 if (jobmgr_assumes(root_jobmgr
, sysctl(mib
, 4, &kp
, &len
, NULL
, 0) != -1) && jobmgr_assumes(root_jobmgr
, len
== sizeof(kp
))) {
2024 jobmgr_log(root_jobmgr
, LOG_ERR
, "%s() was confused by PID %u UID %u EUID %u Mach Port 0x%x: %s", __func__
, ldc
.pid
, ldc
.uid
, ldc
.euid
, p
, kp
.kp_proc
.p_comm
);
2032 job_find_by_service_port(mach_port_t p
)
2034 struct machservice
*ms
;
2036 LIST_FOREACH(ms
, &port_hash
[HASH_PORT(p
)], port_hash_sle
) {
2037 if (ms
->recv
&& (ms
->port
== p
)) {
2046 job_mig_destructor(job_t j
)
2051 * 'j' can be invalid at this point. We should fix this up after Leopard ships.
2054 if (j
&& j
!= workaround_5477111
&& j
->unload_at_mig_return
) {
2055 job_log(j
, LOG_NOTICE
, "Unloading PID %u at MIG return.", j
->p
);
2059 workaround_5477111
= NULL
;
2061 calendarinterval_sanity_check();
2065 job_export_all2(jobmgr_t jm
, launch_data_t where
)
2070 SLIST_FOREACH(jmi
, &jm
->submgrs
, sle
) {
2071 job_export_all2(jmi
, where
);
2074 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
2077 if (jobmgr_assumes(jm
, (tmp
= job_export(ji
)) != NULL
)) {
2078 launch_data_dict_insert(where
, tmp
, ji
->label
);
2084 job_export_all(void)
2086 launch_data_t resp
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
2088 if (launchd_assumes(resp
!= NULL
)) {
2089 job_export_all2(root_jobmgr
, resp
);
2096 job_log_stray_pg(job_t j
)
2098 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PGRP
, j
->p
};
2099 size_t i
, kp_cnt
, len
= sizeof(struct kinfo_proc
) * get_kern_max_proc();
2100 struct kinfo_proc
*kp
;
2102 #if TARGET_OS_EMBEDDED
2103 if (!do_apple_internal_magic
) {
2108 if (!job_assumes(j
, (kp
= malloc(len
)) != NULL
)) {
2111 if (!job_assumes(j
, sysctl(mib
, 4, kp
, &len
, NULL
, 0) != -1)) {
2115 kp_cnt
= len
/ sizeof(struct kinfo_proc
);
2117 for (i
= 0; i
< kp_cnt
; i
++) {
2118 pid_t p_i
= kp
[i
].kp_proc
.p_pid
;
2119 pid_t pp_i
= kp
[i
].kp_eproc
.e_ppid
;
2120 const char *z
= (kp
[i
].kp_proc
.p_stat
== SZOMB
) ? "zombie " : "";
2121 const char *n
= kp
[i
].kp_proc
.p_comm
;
2125 } else if (!job_assumes(j
, p_i
!= 0 && p_i
!= 1)) {
2129 job_log(j
, LOG_WARNING
, "Stray %sprocess with PGID equal to this dead job: PID %u PPID %u %s", z
, p_i
, pp_i
, n
);
2142 job_log(j
, LOG_DEBUG
, "Reaping");
2144 if (j
->weird_bootstrap
) {
2145 mach_msg_size_t mxmsgsz
= sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem
);
2147 if (job_mig_protocol_vproc_subsystem
.maxsize
> mxmsgsz
) {
2148 mxmsgsz
= job_mig_protocol_vproc_subsystem
.maxsize
;
2151 job_assumes(j
, runtime_add_mport(j
->mgr
->jm_port
, protocol_vproc_server
, mxmsgsz
) == KERN_SUCCESS
);
2152 j
->weird_bootstrap
= false;
2155 if (j
->log_redirect_fd
&& !j
->wait4pipe_eof
) {
2156 job_assumes(j
, runtime_close(j
->log_redirect_fd
) != -1);
2157 j
->log_redirect_fd
= 0;
2161 job_assumes(j
, runtime_close(j
->forkfd
) != -1);
2167 memset(&ru
, 0, sizeof(ru
));
2170 * The job is dead. While the PID/PGID is still known to be
2171 * valid, try to kill abandoned descendant processes.
2173 job_log_stray_pg(j
);
2174 if (!j
->abandon_pg
) {
2175 job_assumes(j
, runtime_killpg(j
->p
, SIGTERM
) != -1 || errno
== ESRCH
);
2181 * The current implementation of ptrace() causes the traced process to
2182 * be abducted away from the true parent and adopted by the tracer.
2184 * Once the tracing process relinquishes control, the kernel then
2185 * restores the true parent/child relationship.
2187 * Unfortunately, the wait*() family of APIs is unaware of the temporarily
2188 * data structures changes, and they return an error if reality hasn't
2189 * been restored by the time they are called.
2191 if (!job_assumes(j
, wait4(j
->p
, &status
, 0, &ru
) != -1)) {
2192 job_log(j
, LOG_NOTICE
, "Working around 5020256. Assuming the job crashed.");
2194 status
= W_EXITCODE(0, SIGSEGV
);
2195 memset(&ru
, 0, sizeof(ru
));
2199 if (j
->exit_timeout
) {
2200 kevent_mod((uintptr_t)&j
->exit_timeout
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
);
2204 total_anon_children
--;
2209 LIST_REMOVE(j
, pid_hash_sle
);
2211 if (j
->wait_reply_port
) {
2212 job_log(j
, LOG_DEBUG
, "MPM wait reply being sent");
2213 job_assumes(j
, job_mig_wait_reply(j
->wait_reply_port
, 0, status
) == 0);
2214 j
->wait_reply_port
= MACH_PORT_NULL
;
2217 if (j
->sent_sigterm_time
) {
2218 uint64_t td_sec
, td_usec
, td
= (mach_absolute_time() - j
->sent_sigterm_time
) * tbi
.numer
/ tbi
.denom
;
2220 td_sec
= td
/ NSEC_PER_SEC
;
2221 td_usec
= (td
% NSEC_PER_SEC
) / NSEC_PER_USEC
;
2223 job_log(j
, LOG_INFO
, "Exited %lld.%06lld seconds after %s was sent",
2224 td_sec
, td_usec
, signal_to_C_name(j
->sent_sigkill
? SIGKILL
: SIGTERM
));
2227 #if DO_RUSAGE_SUMMATION
2228 timeradd(&ru
.ru_utime
, &j
->ru
.ru_utime
, &j
->ru
.ru_utime
);
2229 timeradd(&ru
.ru_stime
, &j
->ru
.ru_stime
, &j
->ru
.ru_stime
);
2230 j
->ru
.ru_maxrss
+= ru
.ru_maxrss
;
2231 j
->ru
.ru_ixrss
+= ru
.ru_ixrss
;
2232 j
->ru
.ru_idrss
+= ru
.ru_idrss
;
2233 j
->ru
.ru_isrss
+= ru
.ru_isrss
;
2234 j
->ru
.ru_minflt
+= ru
.ru_minflt
;
2235 j
->ru
.ru_majflt
+= ru
.ru_majflt
;
2236 j
->ru
.ru_nswap
+= ru
.ru_nswap
;
2237 j
->ru
.ru_inblock
+= ru
.ru_inblock
;
2238 j
->ru
.ru_oublock
+= ru
.ru_oublock
;
2239 j
->ru
.ru_msgsnd
+= ru
.ru_msgsnd
;
2240 j
->ru
.ru_msgrcv
+= ru
.ru_msgrcv
;
2241 j
->ru
.ru_nsignals
+= ru
.ru_nsignals
;
2242 j
->ru
.ru_nvcsw
+= ru
.ru_nvcsw
;
2243 j
->ru
.ru_nivcsw
+= ru
.ru_nivcsw
;
2246 if (WIFEXITED(status
) && WEXITSTATUS(status
) != 0) {
2247 job_log(j
, LOG_WARNING
, "Exited with exit code: %d", WEXITSTATUS(status
));
2250 if (WIFSIGNALED(status
)) {
2251 int s
= WTERMSIG(status
);
2252 if (SIGKILL
== s
|| SIGTERM
== s
) {
2253 job_log(j
, LOG_NOTICE
, "Exited: %s", strsignal(s
));
2255 job_log(j
, LOG_WARNING
, "Exited abnormally: %s", strsignal(s
));
2259 if (j
->hopefully_exits_first
) {
2260 j
->mgr
->hopefully_first_cnt
--;
2261 } else if (!j
->anonymous
&& !j
->hopefully_exits_last
) {
2262 j
->mgr
->normal_active_cnt
--;
2264 j
->last_exit_status
= status
;
2265 j
->sent_sigkill
= false;
2266 j
->lastlookup
= NULL
;
2267 j
->lastlookup_gennum
= 0;
2271 * We need to someday evaluate other jobs and find those who wish to track the
2272 * active/inactive state of this job. The current job_dispatch() logic makes
2273 * this messy, given that jobs can be deleted at dispatch.
2278 jobmgr_dispatch_all(jobmgr_t jm
, bool newmounthack
)
2283 if (jm
->shutting_down
) {
2287 SLIST_FOREACH_SAFE(jmi
, &jm
->submgrs
, sle
, jmn
) {
2288 jobmgr_dispatch_all(jmi
, newmounthack
);
2291 LIST_FOREACH_SAFE(ji
, &jm
->jobs
, sle
, jn
) {
2292 if (newmounthack
&& ji
->start_on_mount
) {
2293 ji
->start_pending
= true;
2296 job_dispatch(ji
, false);
2301 job_dispatch(job_t j
, bool kickstart
)
2304 * The whole job removal logic needs to be consolidated. The fact that
2305 * a job can be removed from just about anywhere makes it easy to have
2306 * stale pointers left behind somewhere on the stack that might get
2307 * used after the deallocation. In particular, during job iteration.
2309 * This is a classic example. The act of dispatching a job may delete it.
2311 if (!job_active(j
)) {
2312 if (job_useless(j
)) {
2315 } else if (kickstart
|| job_keepalive(j
)) {
2323 * Path checking and monitoring is really racy right now.
2324 * We should clean this up post Leopard.
2326 if (job_keepalive(j
)) {
2331 job_log(j
, LOG_DEBUG
, "Tried to dispatch an already active job.");
2338 job_log_stdouterr2(job_t j
, const char *msg
, ...)
2340 struct runtime_syslog_attr attr
= { j
->label
, j
->label
, j
->mgr
->name
, LOG_NOTICE
, getuid(), j
->p
, j
->p
};
2344 runtime_vsyslog(&attr
, msg
, ap
);
2349 job_log_stdouterr(job_t j
)
2351 char *msg
, *bufindex
, *buf
= malloc(BIG_PIPE_SIZE
+ 1);
2352 bool close_log_redir
= false;
2355 if (!job_assumes(j
, buf
!= NULL
)) {
2361 rsz
= read(j
->log_redirect_fd
, buf
, BIG_PIPE_SIZE
);
2364 job_log(j
, LOG_DEBUG
, "Standard out/error pipe closed");
2365 close_log_redir
= true;
2366 } else if (!job_assumes(j
, rsz
!= -1)) {
2367 close_log_redir
= true;
2371 while ((msg
= strsep(&bufindex
, "\n\r"))) {
2373 job_log_stdouterr2(j
, "%s", msg
);
2380 if (close_log_redir
) {
2381 job_assumes(j
, runtime_close(j
->log_redirect_fd
) != -1);
2382 j
->log_redirect_fd
= 0;
2383 job_dispatch(j
, false);
2390 if (!j
->p
|| j
->anonymous
) {
2394 job_assumes(j
, runtime_kill(j
->p
, SIGKILL
) != -1);
2396 j
->sent_sigkill
= true;
2398 job_assumes(j
, kevent_mod((uintptr_t)&j
->exit_timeout
, EVFILT_TIMER
,
2399 EV_ADD
, NOTE_SECONDS
, LAUNCHD_SIGKILL_TIMER
, j
) != -1);
2401 job_log(j
, LOG_DEBUG
, "Sent SIGKILL signal.");
2405 job_callback_proc(job_t j
, int flags
__attribute__((unused
)), int fflags
)
2407 if ((fflags
& NOTE_EXEC
) && j
->anonymous
) {
2408 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, j
->p
};
2409 struct kinfo_proc kp
;
2410 size_t len
= sizeof(kp
);
2412 if (job_assumes(j
, sysctl(mib
, 4, &kp
, &len
, NULL
, 0) != -1)) {
2413 char newlabel
[1000];
2415 snprintf(newlabel
, sizeof(newlabel
), "%p.%s", j
, kp
.kp_proc
.p_comm
);
2417 job_log(j
, LOG_DEBUG
, "Program changed. Updating the label to: %s", newlabel
);
2419 LIST_REMOVE(j
, label_hash_sle
);
2420 strcpy((char *)j
->label
, newlabel
);
2421 LIST_INSERT_HEAD(&label_hash
[hash_label(j
->label
)], j
, label_hash_sle
);
2425 if (fflags
& NOTE_FORK
) {
2426 job_log(j
, LOG_DEBUG
, "Called fork()");
2429 if (fflags
& NOTE_EXIT
) {
2436 j
= job_dispatch(j
, false);
2440 /* NOTE_REAP sanity checking is disabled for now while we try and diagnose 5289559 */
2442 if (j
&& (fflags
& NOTE_REAP
)) {
2443 job_assumes(j
, flags
& EV_ONESHOT
);
2444 job_assumes(j
, flags
& EV_EOF
);
2446 job_assumes(j
, j
->p
== 0);
2452 job_callback_timer(job_t j
, void *ident
)
2455 job_dispatch(j
, true);
2456 } else if (&j
->semaphores
== ident
) {
2457 job_dispatch(j
, false);
2458 } else if (&j
->start_interval
== ident
) {
2459 j
->start_pending
= true;
2460 job_dispatch(j
, false);
2461 } else if (&j
->exit_timeout
== ident
) {
2462 if (j
->sent_sigkill
) {
2463 uint64_t td
= (mach_absolute_time() - j
->sent_sigterm_time
) * tbi
.numer
/ tbi
.denom
;
2466 td
-= j
->exit_timeout
;
2468 job_log(j
, LOG_ERR
, "Did not die after sending SIGKILL %llu seconds ago...", td
);
2470 job_force_sampletool(j
);
2471 if (j
->debug_before_kill
) {
2472 job_log(j
, LOG_NOTICE
, "Exit timeout elapsed. Entering the kernel debugger.");
2473 job_assumes(j
, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER
) == KERN_SUCCESS
);
2475 job_log(j
, LOG_WARNING
, "Exit timeout elapsed (%u seconds). Killing.", j
->exit_timeout
);
2479 job_assumes(j
, false);
2484 job_callback_read(job_t j
, int ident
)
2486 if (ident
== j
->log_redirect_fd
) {
2487 job_log_stdouterr(j
);
2489 socketgroup_callback(j
);
2494 jobmgr_reap_bulk(jobmgr_t jm
, struct kevent
*kev
)
2499 SLIST_FOREACH(jmi
, &jm
->submgrs
, sle
) {
2500 jobmgr_reap_bulk(jmi
, kev
);
2503 if ((j
= jobmgr_find_by_pid(jm
, kev
->ident
, false))) {
2505 job_callback(j
, kev
);
2510 jobmgr_callback(void *obj
, struct kevent
*kev
)
2514 switch (kev
->filter
) {
2516 jobmgr_reap_bulk(jm
, kev
);
2517 if (launchd_assumes(root_jobmgr
!= NULL
)) {
2518 root_jobmgr
= jobmgr_do_garbage_collection(root_jobmgr
);
2522 switch (kev
->ident
) {
2524 return launchd_shutdown();
2526 return calendarinterval_callback();
2528 return (void)jobmgr_assumes(jm
, false);
2532 if (kev
->fflags
& VQ_MOUNT
) {
2533 jobmgr_dispatch_all(jm
, true);
2535 jobmgr_dispatch_all_semaphores(jm
);
2538 if (jobmgr_assumes(jm
, kev
->ident
== (uintptr_t)&sorted_calendar_events
)) {
2539 calendarinterval_callback();
2543 return (void)jobmgr_assumes(jm
, false);
2548 job_callback(void *obj
, struct kevent
*kev
)
2552 job_log(j
, LOG_DEBUG
, "Dispatching kevent callback.");
2554 switch (kev
->filter
) {
2556 return job_callback_proc(j
, kev
->flags
, kev
->fflags
);
2558 return job_callback_timer(j
, (void *)kev
->ident
);
2560 return semaphoreitem_callback(j
, kev
);
2562 return job_callback_read(j
, kev
->ident
);
2563 case EVFILT_MACHPORT
:
2564 return (void)job_dispatch(j
, true);
2566 return (void)job_assumes(j
, false);
2573 uint64_t td
, tnow
= mach_absolute_time();
2580 u_int proc_fflags
= /* NOTE_EXEC|NOTE_FORK| */ NOTE_EXIT
/* |NOTE_REAP */;
2582 if (!job_assumes(j
, j
->mgr
!= NULL
)) {
2586 if (job_active(j
)) {
2587 job_log(j
, LOG_DEBUG
, "Already started");
2591 job_assumes(j
, tnow
> j
->start_time
);
2594 * Some users adjust the wall-clock and then expect software to not notice.
2595 * Therefore, launchd must use an absolute clock instead of gettimeofday()
2596 * or time() wherever possible.
2598 td
= (tnow
- j
->start_time
) * tbi
.numer
/ tbi
.denom
;
2601 if (j
->start_time
&& (td
< j
->min_run_time
) && !j
->legacy_mach_job
&& !j
->inetcompat
) {
2602 time_t respawn_delta
= j
->min_run_time
- (uint32_t)td
;
2605 * We technically should ref-count throttled jobs to prevent idle exit,
2606 * but we're not directly tracking the 'throttled' state at the moment.
2609 job_log(j
, LOG_WARNING
, "Throttling respawn: Will start in %ld seconds", respawn_delta
);
2610 job_assumes(j
, kevent_mod((uintptr_t)j
, EVFILT_TIMER
, EV_ADD
|EV_ONESHOT
, NOTE_SECONDS
, respawn_delta
, j
) != -1);
2615 j
->sent_sigterm_time
= 0;
2617 if (!j
->legacy_mach_job
) {
2618 sipc
= (!SLIST_EMPTY(&j
->sockets
) || !SLIST_EMPTY(&j
->machservices
));
2619 #if TARGET_OS_EMBEDDED
2620 if (j
->username
&& strcmp(j
->username
, "mobile") == 0 && strncmp(j
->label
, "com.apple.", strlen("com.apple.")) != 0) {
2626 j
->checkedin
= false;
2629 job_assumes(j
, socketpair(AF_UNIX
, SOCK_STREAM
, 0, spair
) != -1);
2632 job_assumes(j
, socketpair(AF_UNIX
, SOCK_STREAM
, 0, execspair
) != -1);
2634 if (!j
->legacy_mach_job
&& job_assumes(j
, pipe(oepair
) != -1)) {
2635 j
->log_redirect_fd
= _fd(oepair
[0]);
2636 job_assumes(j
, fcntl(j
->log_redirect_fd
, F_SETFL
, O_NONBLOCK
) != -1);
2637 job_assumes(j
, kevent_mod(j
->log_redirect_fd
, EVFILT_READ
, EV_ADD
, 0, 0, j
) != -1);
2640 j
->start_time
= tnow
;
2642 switch (c
= runtime_fork(j
->weird_bootstrap
? j
->j_port
: j
->mgr
->jm_port
)) {
2644 job_log_error(j
, LOG_ERR
, "fork() failed, will try again in one second");
2645 job_assumes(j
, runtime_close(execspair
[0]) == 0);
2646 job_assumes(j
, runtime_close(execspair
[1]) == 0);
2648 job_assumes(j
, runtime_close(spair
[0]) == 0);
2649 job_assumes(j
, runtime_close(spair
[1]) == 0);
2651 if (!j
->legacy_mach_job
) {
2652 job_assumes(j
, runtime_close(oepair
[0]) != -1);
2653 job_assumes(j
, runtime_close(oepair
[1]) != -1);
2654 j
->log_redirect_fd
= 0;
2658 if (_vproc_post_fork_ping()) {
2659 _exit(EXIT_FAILURE
);
2661 if (!j
->legacy_mach_job
) {
2662 job_assumes(j
, dup2(oepair
[1], STDOUT_FILENO
) != -1);
2663 job_assumes(j
, dup2(oepair
[1], STDERR_FILENO
) != -1);
2664 job_assumes(j
, runtime_close(oepair
[1]) != -1);
2666 job_assumes(j
, runtime_close(execspair
[0]) == 0);
2667 /* wait for our parent to say they've attached a kevent to us */
2668 read(_fd(execspair
[1]), &c
, sizeof(c
));
2671 job_assumes(j
, runtime_close(spair
[0]) == 0);
2672 snprintf(nbuf
, sizeof(nbuf
), "%d", spair
[1]);
2673 setenv(LAUNCHD_TRUSTED_FD_ENV
, nbuf
, 1);
2678 job_log(j
, LOG_DEBUG
, "Started as PID: %u", c
);
2680 j
->start_pending
= false;
2684 LIST_INSERT_HEAD(&j
->mgr
->active_jobs
[ACTIVE_JOB_HASH(c
)], j
, pid_hash_sle
);
2686 if (JOB_BOOTCACHE_HACK_CHECK(j
)) {
2687 did_first_per_user_launchd_BootCache_hack
= true;
2690 if (!j
->legacy_mach_job
) {
2691 job_assumes(j
, runtime_close(oepair
[1]) != -1);
2694 if (j
->hopefully_exits_first
) {
2695 j
->mgr
->hopefully_first_cnt
++;
2696 } else if (!j
->hopefully_exits_last
) {
2697 j
->mgr
->normal_active_cnt
++;
2699 j
->forkfd
= _fd(execspair
[0]);
2700 job_assumes(j
, runtime_close(execspair
[1]) == 0);
2702 job_assumes(j
, runtime_close(spair
[1]) == 0);
2703 ipc_open(_fd(spair
[0]), j
);
2705 if (job_assumes(j
, kevent_mod(c
, EVFILT_PROC
, EV_ADD
, proc_fflags
, 0, root_jobmgr
? root_jobmgr
: j
->mgr
) != -1)) {
2711 if (!j
->stall_before_exec
) {
2719 do_first_per_user_launchd_hack(void)
2721 char *bcct_tool
[] = { "/usr/sbin/BootCacheControl", "tag", NULL
};
2725 if (launchd_assumes((bcp
= vfork()) != -1)) {
2727 execve(bcct_tool
[0], bcct_tool
, environ
);
2728 _exit(EXIT_FAILURE
);
2730 launchd_assumes(waitpid(bcp
, &dummystatus
, 0) != -1);
2736 job_start_child(job_t j
)
2738 const char *file2exec
= "/usr/libexec/launchproxy";
2740 posix_spawnattr_t spattr
;
2741 int gflags
= GLOB_NOSORT
|GLOB_NOCHECK
|GLOB_TILDE
|GLOB_DOOFFS
;
2744 short spflags
= POSIX_SPAWN_SETEXEC
;
2745 size_t binpref_out_cnt
= 0;
2748 if (JOB_BOOTCACHE_HACK_CHECK(j
)) {
2749 do_first_per_user_launchd_hack();
2752 job_assumes(j
, posix_spawnattr_init(&spattr
) == 0);
2754 job_setup_attributes(j
);
2756 if (j
->argv
&& j
->globargv
) {
2758 for (i
= 0; i
< j
->argc
; i
++) {
2760 gflags
|= GLOB_APPEND
;
2762 if (glob(j
->argv
[i
], gflags
, NULL
, &g
) != 0) {
2763 job_log_error(j
, LOG_ERR
, "glob(\"%s\")", j
->argv
[i
]);
2767 g
.gl_pathv
[0] = (char *)file2exec
;
2768 argv
= (const char **)g
.gl_pathv
;
2769 } else if (j
->argv
) {
2770 argv
= alloca((j
->argc
+ 2) * sizeof(char *));
2771 argv
[0] = file2exec
;
2772 for (i
= 0; i
< j
->argc
; i
++) {
2773 argv
[i
+ 1] = j
->argv
[i
];
2777 argv
= alloca(3 * sizeof(char *));
2778 argv
[0] = file2exec
;
2783 if (!j
->inetcompat
) {
2787 if (j
->wait4debugger
) {
2788 job_log(j
, LOG_WARNING
, "Spawned and waiting for the debugger to attach before continuing...");
2789 spflags
|= POSIX_SPAWN_START_SUSPENDED
;
2792 job_assumes(j
, posix_spawnattr_setflags(&spattr
, spflags
) == 0);
2794 if (j
->j_binpref_cnt
) {
2795 job_assumes(j
, posix_spawnattr_setbinpref_np(&spattr
, j
->j_binpref_cnt
, j
->j_binpref
, &binpref_out_cnt
) == 0);
2796 job_assumes(j
, binpref_out_cnt
== j
->j_binpref_cnt
);
2800 if (j
->quarantine_data
) {
2803 if (job_assumes(j
, qp
= qtn_proc_alloc())) {
2804 if (job_assumes(j
, qtn_proc_init_with_data(qp
, j
->quarantine_data
, j
->quarantine_data_sz
) == 0)) {
2805 job_assumes(j
, qtn_proc_apply_to_self(qp
) == 0);
2812 if (j
->seatbelt_profile
) {
2813 char *seatbelt_err_buf
= NULL
;
2815 if (!job_assumes(j
, sandbox_init(j
->seatbelt_profile
, j
->seatbelt_flags
, &seatbelt_err_buf
) != -1)) {
2816 if (seatbelt_err_buf
) {
2817 job_log(j
, LOG_ERR
, "Sandbox failed to init: %s", seatbelt_err_buf
);
2825 errno
= posix_spawn(&junk_pid
, j
->inetcompat
? file2exec
: j
->prog
, NULL
, &spattr
, (char *const*)argv
, environ
);
2826 job_log_error(j
, LOG_ERR
, "posix_spawn(\"%s\", ...)", j
->prog
);
2828 errno
= posix_spawnp(&junk_pid
, j
->inetcompat
? file2exec
: argv
[0], NULL
, &spattr
, (char *const*)argv
, environ
);
2829 job_log_error(j
, LOG_ERR
, "posix_spawnp(\"%s\", ...)", argv
[0]);
2835 _exit(EXIT_FAILURE
);
2839 jobmgr_export_env_from_other_jobs(jobmgr_t jm
, launch_data_t dict
)
2845 if (jm
->parentmgr
) {
2846 jobmgr_export_env_from_other_jobs(jm
->parentmgr
, dict
);
2848 char **tmpenviron
= environ
;
2849 for (; *tmpenviron
; tmpenviron
++) {
2851 launch_data_t s
= launch_data_alloc(LAUNCH_DATA_STRING
);
2852 launch_data_set_string(s
, strchr(*tmpenviron
, '=') + 1);
2853 strncpy(envkey
, *tmpenviron
, sizeof(envkey
));
2854 *(strchr(envkey
, '=')) = '\0';
2855 launch_data_dict_insert(dict
, s
, envkey
);
2859 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
2860 SLIST_FOREACH(ei
, &ji
->global_env
, sle
) {
2861 if ((tmp
= launch_data_new_string(ei
->value
))) {
2862 launch_data_dict_insert(dict
, tmp
, ei
->key
);
2869 jobmgr_setup_env_from_other_jobs(jobmgr_t jm
)
2874 if (jm
->parentmgr
) {
2875 jobmgr_setup_env_from_other_jobs(jm
->parentmgr
);
2878 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
2879 SLIST_FOREACH(ei
, &ji
->global_env
, sle
) {
2880 setenv(ei
->key
, ei
->value
, 1);
2886 job_find_and_blame_pids_with_weird_uids(job_t j
)
2888 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_ALL
};
2889 size_t i
, kp_cnt
, len
= sizeof(struct kinfo_proc
) * get_kern_max_proc();
2890 struct kinfo_proc
*kp
;
2891 uid_t u
= j
->mach_uid
;
2893 #if TARGET_OS_EMBEDDED
2894 if (!do_apple_internal_magic
) {
2900 if (!job_assumes(j
, kp
!= NULL
)) {
2903 if (!job_assumes(j
, sysctl(mib
, 3, kp
, &len
, NULL
, 0) != -1)) {
2907 kp_cnt
= len
/ sizeof(struct kinfo_proc
);
2909 for (i
= 0; i
< kp_cnt
; i
++) {
2910 uid_t i_euid
= kp
[i
].kp_eproc
.e_ucred
.cr_uid
;
2911 uid_t i_uid
= kp
[i
].kp_eproc
.e_pcred
.p_ruid
;
2912 uid_t i_svuid
= kp
[i
].kp_eproc
.e_pcred
.p_svuid
;
2913 pid_t i_pid
= kp
[i
].kp_proc
.p_pid
;
2915 if (i_euid
!= u
&& i_uid
!= u
&& i_svuid
!= u
) {
2919 job_log(j
, LOG_ERR
, "PID %u \"%s\" has no account to back it! Real/effective/saved UIDs: %u/%u/%u",
2920 i_pid
, kp
[i
].kp_proc
.p_comm
, i_uid
, i_euid
, i_svuid
);
2922 /* Temporarily disabled due to 5423935 and 4946119. */
2924 /* Ask the accountless process to exit. */
2925 job_assumes(j
, runtime_kill(i_pid
, SIGTERM
) != -1);
2933 #if !TARGET_OS_EMBEDDED
2935 job_enable_audit_for_user(job_t j
, uid_t u
, char *name
)
2937 auditinfo_t auinfo
= {
2943 if (!job_assumes(j
, auditon(A_GETCOND
, &au_cond
, sizeof(long)) == 0)) {
2944 _exit(EXIT_FAILURE
);
2947 if (au_cond
!= AUC_NOAUDIT
) {
2948 if (!job_assumes(j
, au_user_mask(name
, &auinfo
.ai_mask
) == 0)) {
2949 _exit(EXIT_FAILURE
);
2950 } else if (!job_assumes(j
, setaudit(&auinfo
) == 0)) {
2951 _exit(EXIT_FAILURE
);
2958 job_postfork_become_user(job_t j
)
2960 char loginname
[2000];
2961 char tmpdirpath
[PATH_MAX
];
2962 char shellpath
[PATH_MAX
];
2963 char homedir
[PATH_MAX
];
2966 gid_t desired_gid
= -1;
2967 uid_t desired_uid
= -1;
2969 if (getuid() != 0) {
2974 * I contend that having UID == 0 and GID != 0 is of dubious value.
2975 * Nevertheless, this used to work in Tiger. See: 5425348
2977 if (j
->groupname
&& !j
->username
) {
2978 j
->username
= "root";
2982 if ((pwe
= getpwnam(j
->username
)) == NULL
) {
2983 job_log(j
, LOG_ERR
, "getpwnam(\"%s\") failed", j
->username
);
2984 _exit(EXIT_FAILURE
);
2986 } else if (j
->mach_uid
) {
2987 if ((pwe
= getpwuid(j
->mach_uid
)) == NULL
) {
2988 job_log(j
, LOG_ERR
, "getpwuid(\"%u\") failed", j
->mach_uid
);
2989 job_find_and_blame_pids_with_weird_uids(j
);
2990 _exit(EXIT_FAILURE
);
2997 * We must copy the results of getpw*().
2999 * Why? Because subsequent API calls may call getpw*() as a part of
3000 * their implementation. Since getpw*() returns a [now thread scoped]
3001 * global, we must therefore cache the results before continuing.
3004 desired_uid
= pwe
->pw_uid
;
3005 desired_gid
= pwe
->pw_gid
;
3007 strlcpy(shellpath
, pwe
->pw_shell
, sizeof(shellpath
));
3008 strlcpy(loginname
, pwe
->pw_name
, sizeof(loginname
));
3009 strlcpy(homedir
, pwe
->pw_dir
, sizeof(homedir
));
3011 if (pwe
->pw_expire
&& time(NULL
) >= pwe
->pw_expire
) {
3012 job_log(j
, LOG_ERR
, "Expired account");
3013 _exit(EXIT_FAILURE
);
3017 if (j
->username
&& strcmp(j
->username
, loginname
) != 0) {
3018 job_log(j
, LOG_WARNING
, "Suspicious setup: User \"%s\" maps to user: %s", j
->username
, loginname
);
3019 } else if (j
->mach_uid
&& (j
->mach_uid
!= desired_uid
)) {
3020 job_log(j
, LOG_WARNING
, "Suspicious setup: UID %u maps to UID %u", j
->mach_uid
, desired_uid
);
3026 if ((gre
= getgrnam(j
->groupname
)) == NULL
) {
3027 job_log(j
, LOG_ERR
, "getgrnam(\"%s\") failed", j
->groupname
);
3028 _exit(EXIT_FAILURE
);
3031 desired_gid
= gre
->gr_gid
;
3034 #if !TARGET_OS_EMBEDDED
3035 job_enable_audit_for_user(j
, desired_uid
, loginname
);
3038 if (!job_assumes(j
, setlogin(loginname
) != -1)) {
3039 _exit(EXIT_FAILURE
);
3042 if (!job_assumes(j
, setgid(desired_gid
) != -1)) {
3043 _exit(EXIT_FAILURE
);
3047 * The kernel team and the DirectoryServices team want initgroups()
3048 * called after setgid(). See 4616864 for more information.
3051 if (!j
->no_init_groups
) {
3052 if (!job_assumes(j
, initgroups(loginname
, desired_gid
) != -1)) {
3053 _exit(EXIT_FAILURE
);
3057 if (!job_assumes(j
, setuid(desired_uid
) != -1)) {
3058 _exit(EXIT_FAILURE
);
3061 r
= confstr(_CS_DARWIN_USER_TEMP_DIR
, tmpdirpath
, sizeof(tmpdirpath
));
3063 if (r
> 0 && r
< sizeof(tmpdirpath
)) {
3064 setenv("TMPDIR", tmpdirpath
, 0);
3067 setenv("SHELL", shellpath
, 0);
3068 setenv("HOME", homedir
, 0);
3069 setenv("USER", loginname
, 0);
3070 setenv("LOGNAME", loginname
, 0);
3074 job_setup_attributes(job_t j
)
3076 struct limititem
*li
;
3080 job_assumes(j
, setpriority(PRIO_PROCESS
, 0, j
->nice
) != -1);
3083 SLIST_FOREACH(li
, &j
->limits
, sle
) {
3086 if (!job_assumes(j
, getrlimit(li
->which
, &rl
) != -1)) {
3091 rl
.rlim_max
= li
->lim
.rlim_max
;
3094 rl
.rlim_cur
= li
->lim
.rlim_cur
;
3097 if (setrlimit(li
->which
, &rl
) == -1) {
3098 job_log_error(j
, LOG_WARNING
, "setrlimit()");
3102 if (!j
->inetcompat
&& j
->session_create
) {
3103 launchd_SessionCreate();
3106 if (j
->low_pri_io
) {
3107 job_assumes(j
, setiopolicy_np(IOPOL_TYPE_DISK
, IOPOL_SCOPE_PROCESS
, IOPOL_THROTTLE
) != -1);
3110 job_assumes(j
, chroot(j
->rootdir
) != -1);
3111 job_assumes(j
, chdir(".") != -1);
3114 job_postfork_become_user(j
);
3116 if (j
->workingdir
) {
3117 job_assumes(j
, chdir(j
->workingdir
) != -1);
3124 job_setup_fd(j
, STDOUT_FILENO
, j
->stdoutpath
, O_WRONLY
|O_APPEND
|O_CREAT
);
3125 job_setup_fd(j
, STDERR_FILENO
, j
->stderrpath
, O_WRONLY
|O_APPEND
|O_CREAT
);
3127 jobmgr_setup_env_from_other_jobs(j
->mgr
);
3129 SLIST_FOREACH(ei
, &j
->env
, sle
) {
3130 setenv(ei
->key
, ei
->value
, 1);
3134 * We'd like to call setsid() unconditionally, but we have reason to
3135 * believe that prevents launchd from being able to send signals to
3136 * setuid children. We'll settle for process-groups.
3138 if (getppid() != 1) {
3139 job_assumes(j
, setpgid(0, 0) != -1);
3141 job_assumes(j
, setsid() != -1);
3146 job_setup_fd(job_t j
, int target_fd
, const char *path
, int flags
)
3154 if ((fd
= open(path
, flags
|O_NOCTTY
, DEFFILEMODE
)) == -1) {
3155 job_log_error(j
, LOG_WARNING
, "open(\"%s\", ...)", path
);
3159 job_assumes(j
, dup2(fd
, target_fd
) != -1);
3160 job_assumes(j
, runtime_close(fd
) == 0);
3164 dir_has_files(job_t j
, const char *path
)
3166 DIR *dd
= opendir(path
);
3174 while ((de
= readdir(dd
))) {
3175 if (strcmp(de
->d_name
, ".") && strcmp(de
->d_name
, "..")) {
3181 job_assumes(j
, closedir(dd
) == 0);
3186 calendarinterval_setalarm(job_t j
, struct calendarinterval
*ci
)
3188 struct calendarinterval
*ci_iter
, *ci_prev
= NULL
;
3189 time_t later
, head_later
;
3191 later
= cronemu(ci
->when
.tm_mon
, ci
->when
.tm_mday
, ci
->when
.tm_hour
, ci
->when
.tm_min
);
3193 if (ci
->when
.tm_wday
!= -1) {
3194 time_t otherlater
= cronemu_wday(ci
->when
.tm_wday
, ci
->when
.tm_hour
, ci
->when
.tm_min
);
3196 if (ci
->when
.tm_mday
== -1) {
3199 later
= later
< otherlater
? later
: otherlater
;
3203 ci
->when_next
= later
;
3205 LIST_FOREACH(ci_iter
, &sorted_calendar_events
, global_sle
) {
3206 if (ci
->when_next
< ci_iter
->when_next
) {
3207 LIST_INSERT_BEFORE(ci_iter
, ci
, global_sle
);
3214 if (ci_iter
== NULL
) {
3215 /* ci must want to fire after every other timer, or there are no timers */
3217 if (LIST_EMPTY(&sorted_calendar_events
)) {
3218 LIST_INSERT_HEAD(&sorted_calendar_events
, ci
, global_sle
);
3220 LIST_INSERT_AFTER(ci_prev
, ci
, global_sle
);
3224 head_later
= LIST_FIRST(&sorted_calendar_events
)->when_next
;
3226 /* Workaround 5225889 */
3227 kevent_mod((uintptr_t)&sorted_calendar_events
, EVFILT_TIMER
, EV_DELETE
, 0, 0, root_jobmgr
);
3229 if (job_assumes(j
, kevent_mod((uintptr_t)&sorted_calendar_events
, EVFILT_TIMER
, EV_ADD
, NOTE_ABSOLUTE
|NOTE_SECONDS
, head_later
, root_jobmgr
) != -1)) {
3230 char time_string
[100];
3231 size_t time_string_len
;
3233 ctime_r(&later
, time_string
);
3234 time_string_len
= strlen(time_string
);
3236 if (time_string_len
&& time_string
[time_string_len
- 1] == '\n') {
3237 time_string
[time_string_len
- 1] = '\0';
3240 job_log(j
, LOG_INFO
, "Scheduled to run again at %s", time_string
);
3245 extract_rcsid_substr(const char *i
, char *o
, size_t osz
)
3247 char *rcs_rev_tmp
= strchr(i
, ' ');
3252 strlcpy(o
, rcs_rev_tmp
+ 1, osz
);
3253 rcs_rev_tmp
= strchr(o
, ' ');
3255 *rcs_rev_tmp
= '\0';
3261 jobmgr_log_bug(jobmgr_t jm
, const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
)
3263 int saved_errno
= errno
;
3264 const char *file
= strrchr(path
, '/');
3267 extract_rcsid_substr(rcs_rev
, buf
, sizeof(buf
));
3275 jobmgr_log(jm
, LOG_NOTICE
, "Bug: %s:%u (%s):%u: %s", file
, line
, buf
, saved_errno
, test
);
3279 job_log_bug(job_t j
, const char *rcs_rev
, const char *path
, unsigned int line
, const char *test
)
3281 int saved_errno
= errno
;
3282 const char *file
= strrchr(path
, '/');
3285 extract_rcsid_substr(rcs_rev
, buf
, sizeof(buf
));
3293 job_log(j
, LOG_NOTICE
, "Bug: %s:%u (%s):%u: %s", file
, line
, buf
, saved_errno
, test
);
3297 job_logv(job_t j
, int pri
, int err
, const char *msg
, va_list ap
)
3299 struct runtime_syslog_attr attr
= { "com.apple.launchd", j
->label
, j
->mgr
->name
, pri
, getuid(), getpid(), j
->p
};
3305 * Hack: If bootstrap_port is set, we must be on the child side of a
3306 * fork(), but before the exec*(). Let's route the log message back to
3309 if (bootstrap_port
) {
3310 return _vproc_logv(pri
, err
, msg
, ap
);
3313 newmsgsz
= strlen(msg
) + 200;
3314 newmsg
= alloca(newmsgsz
);
3317 snprintf(newmsg
, newmsgsz
, "%s: %s", msg
, strerror(err
));
3319 snprintf(newmsg
, newmsgsz
, "%s", msg
);
3323 oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
3326 runtime_vsyslog(&attr
, newmsg
, ap
);
3329 setlogmask(oldmask
);
3334 job_log_error(job_t j
, int pri
, const char *msg
, ...)
3339 job_logv(j
, pri
, errno
, msg
, ap
);
3344 job_log(job_t j
, int pri
, const char *msg
, ...)
3349 job_logv(j
, pri
, 0, msg
, ap
);
3355 jobmgr_log_error(jobmgr_t jm
, int pri
, const char *msg
, ...)
3360 jobmgr_logv(jm
, pri
, errno
, msg
, ap
);
3366 jobmgr_log(jobmgr_t jm
, int pri
, const char *msg
, ...)
3371 jobmgr_logv(jm
, pri
, 0, msg
, ap
);
3376 jobmgr_logv(jobmgr_t jm
, int pri
, int err
, const char *msg
, va_list ap
)
3380 size_t i
, o
, jmname_len
= strlen(jm
->name
), newmsgsz
;
3382 newname
= alloca((jmname_len
+ 1) * 2);
3383 newmsgsz
= (jmname_len
+ 1) * 2 + strlen(msg
) + 100;
3384 newmsg
= alloca(newmsgsz
);
3386 for (i
= 0, o
= 0; i
< jmname_len
; i
++, o
++) {
3387 if (jm
->name
[i
] == '%') {
3391 newname
[o
] = jm
->name
[i
];
3396 snprintf(newmsg
, newmsgsz
, "%s: %s: %s", newname
, msg
, strerror(err
));
3398 snprintf(newmsg
, newmsgsz
, "%s: %s", newname
, msg
);
3401 if (jm
->parentmgr
) {
3402 jobmgr_logv(jm
->parentmgr
, pri
, 0, newmsg
, ap
);
3404 struct runtime_syslog_attr attr
= { "com.apple.launchd", "com.apple.launchd", jm
->name
, pri
, getuid(), getpid(), getpid() };
3406 runtime_vsyslog(&attr
, newmsg
, ap
);
3411 semaphoreitem_ignore(job_t j
, struct semaphoreitem
*si
)
3414 job_log(j
, LOG_DEBUG
, "Ignoring Vnode: %d", si
->fd
);
3415 job_assumes(j
, kevent_mod(si
->fd
, EVFILT_VNODE
, EV_DELETE
, 0, 0, NULL
) != -1);
3420 semaphoreitem_watch(job_t j
, struct semaphoreitem
*si
)
3422 char *parentdir
, tmp_path
[PATH_MAX
];
3423 const char *which_path
= si
->what
;
3424 int saved_errno
= 0;
3429 fflags
= NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
|NOTE_EXTEND
|NOTE_WRITE
;
3432 fflags
= NOTE_DELETE
|NOTE_RENAME
;
3436 fflags
= NOTE_DELETE
|NOTE_RENAME
|NOTE_REVOKE
|NOTE_EXTEND
|NOTE_WRITE
|NOTE_ATTRIB
|NOTE_LINK
;
3442 /* dirname() may modify tmp_path */
3443 strlcpy(tmp_path
, si
->what
, sizeof(tmp_path
));
3445 if (!job_assumes(j
, (parentdir
= dirname(tmp_path
)))) {
3449 /* See 5321044 for why we do the do-while loop and 5415523 for why ENOENT is checked */
3452 if ((si
->fd
= _fd(open(which_path
, O_EVTONLY
|O_NOCTTY
))) == -1) {
3453 which_path
= parentdir
;
3454 si
->fd
= _fd(open(which_path
, O_EVTONLY
|O_NOCTTY
));
3459 return job_log_error(j
, LOG_ERR
, "Path monitoring failed on \"%s\"", which_path
);
3462 job_log(j
, LOG_DEBUG
, "Watching Vnode: %d", si
->fd
);
3464 if (kevent_mod(si
->fd
, EVFILT_VNODE
, EV_ADD
, fflags
, 0, j
) == -1) {
3465 saved_errno
= errno
;
3467 * The FD can be revoked between the open() and kevent().
3468 * This is similar to the inability for kevents to be
3469 * attached to short lived zombie processes after fork()
3470 * but before kevent().
3472 job_assumes(j
, runtime_close(si
->fd
) == 0);
3475 } while ((si
->fd
== -1) && (saved_errno
== ENOENT
));
3477 if (saved_errno
== ENOTSUP
) {
3479 * 3524219 NFS needs kqueue support
3480 * 4124079 VFS needs generic kqueue support
3481 * 5226811 EVFILT: Launchd EVFILT_VNODE doesn't work on /dev
3483 job_log(j
, LOG_DEBUG
, "Falling back to polling for path: %s", si
->what
);
3485 if (!j
->poll_for_vfs_changes
) {
3486 j
->poll_for_vfs_changes
= true;
3487 job_assumes(j
, kevent_mod((uintptr_t)&j
->semaphores
, EVFILT_TIMER
, EV_ADD
, NOTE_SECONDS
, 3, j
) != -1);
3493 semaphoreitem_callback(job_t j
, struct kevent
*kev
)
3495 char invalidation_reason
[100] = "";
3496 struct semaphoreitem
*si
;
3498 SLIST_FOREACH(si
, &j
->semaphores
, sle
) {
3509 if (si
->fd
== (int)kev
->ident
) {
3514 if (!job_assumes(j
, si
!= NULL
)) {
3518 if (NOTE_DELETE
& kev
->fflags
) {
3519 strcat(invalidation_reason
, "deleted");
3522 if (NOTE_RENAME
& kev
->fflags
) {
3523 if (invalidation_reason
[0]) {
3524 strcat(invalidation_reason
, "/renamed");
3526 strcat(invalidation_reason
, "renamed");
3530 if (NOTE_REVOKE
& kev
->fflags
) {
3531 if (invalidation_reason
[0]) {
3532 strcat(invalidation_reason
, "/revoked");
3534 strcat(invalidation_reason
, "revoked");
3538 if (invalidation_reason
[0]) {
3539 job_log(j
, LOG_DEBUG
, "Path %s: %s", invalidation_reason
, si
->what
);
3540 job_assumes(j
, runtime_close(si
->fd
) == 0);
3541 si
->fd
= -1; /* this will get fixed in semaphoreitem_watch() */
3544 job_log(j
, LOG_DEBUG
, "Watch path modified: %s", si
->what
);
3546 if (si
->why
== PATH_CHANGES
) {
3547 j
->start_pending
= true;
3550 job_dispatch(j
, false);
3554 calendarinterval_new_from_obj_dict_walk(launch_data_t obj
, const char *key
, void *context
)
3556 struct tm
*tmptm
= context
;
3559 if (LAUNCH_DATA_INTEGER
!= launch_data_get_type(obj
)) {
3560 /* hack to let caller know something went wrong */
3565 val
= launch_data_get_integer(obj
);
3567 if (strcasecmp(key
, LAUNCH_JOBKEY_CAL_MINUTE
) == 0) {
3568 tmptm
->tm_min
= val
;
3569 } else if (strcasecmp(key
, LAUNCH_JOBKEY_CAL_HOUR
) == 0) {
3570 tmptm
->tm_hour
= val
;
3571 } else if (strcasecmp(key
, LAUNCH_JOBKEY_CAL_DAY
) == 0) {
3572 tmptm
->tm_mday
= val
;
3573 } else if (strcasecmp(key
, LAUNCH_JOBKEY_CAL_WEEKDAY
) == 0) {
3574 tmptm
->tm_wday
= val
;
3575 } else if (strcasecmp(key
, LAUNCH_JOBKEY_CAL_MONTH
) == 0) {
3576 tmptm
->tm_mon
= val
;
3577 tmptm
->tm_mon
-= 1; /* 4798263 cron compatibility */
3582 calendarinterval_new_from_obj(job_t j
, launch_data_t obj
)
3586 memset(&tmptm
, 0, sizeof(0));
3594 if (!job_assumes(j
, obj
!= NULL
)) {
3598 if (LAUNCH_DATA_DICTIONARY
!= launch_data_get_type(obj
)) {
3602 launch_data_dict_iterate(obj
, calendarinterval_new_from_obj_dict_walk
, &tmptm
);
3604 if (tmptm
.tm_sec
== -1) {
3608 return calendarinterval_new(j
, &tmptm
);
3612 calendarinterval_new(job_t j
, struct tm
*w
)
3614 struct calendarinterval
*ci
= calloc(1, sizeof(struct calendarinterval
));
3616 if (!job_assumes(j
, ci
!= NULL
)) {
3623 SLIST_INSERT_HEAD(&j
->cal_intervals
, ci
, sle
);
3625 calendarinterval_setalarm(j
, ci
);
3633 calendarinterval_delete(job_t j
, struct calendarinterval
*ci
)
3635 SLIST_REMOVE(&j
->cal_intervals
, ci
, calendarinterval
, sle
);
3636 LIST_REMOVE(ci
, global_sle
);
3644 calendarinterval_sanity_check(void)
3646 struct calendarinterval
*ci
= LIST_FIRST(&sorted_calendar_events
);
3647 time_t now
= time(NULL
);
3649 if (ci
&& (ci
->when_next
< now
)) {
3650 jobmgr_assumes(root_jobmgr
, raise(SIGUSR1
) != -1);
3655 calendarinterval_callback(void)
3657 struct calendarinterval
*ci
, *ci_next
;
3658 time_t now
= time(NULL
);
3660 LIST_FOREACH_SAFE(ci
, &sorted_calendar_events
, global_sle
, ci_next
) {
3663 if (ci
->when_next
> now
) {
3667 LIST_REMOVE(ci
, global_sle
);
3668 calendarinterval_setalarm(j
, ci
);
3670 j
->start_pending
= true;
3671 job_dispatch(j
, false);
3676 socketgroup_new(job_t j
, const char *name
, int *fds
, unsigned int fd_cnt
, bool junkfds
)
3678 struct socketgroup
*sg
= calloc(1, sizeof(struct socketgroup
) + strlen(name
) + 1);
3680 if (!job_assumes(j
, sg
!= NULL
)) {
3684 sg
->fds
= calloc(1, fd_cnt
* sizeof(int));
3685 sg
->fd_cnt
= fd_cnt
;
3686 sg
->junkfds
= junkfds
;
3688 if (!job_assumes(j
, sg
->fds
!= NULL
)) {
3693 memcpy(sg
->fds
, fds
, fd_cnt
* sizeof(int));
3694 strcpy(sg
->name_init
, name
);
3696 SLIST_INSERT_HEAD(&j
->sockets
, sg
, sle
);
3704 socketgroup_delete(job_t j
, struct socketgroup
*sg
)
3708 for (i
= 0; i
< sg
->fd_cnt
; i
++) {
3710 struct sockaddr_storage ss
;
3711 struct sockaddr_un
*sun
= (struct sockaddr_un
*)&ss
;
3712 socklen_t ss_len
= sizeof(ss
);
3715 if (job_assumes(j
, getsockname(sg
->fds
[i
], (struct sockaddr
*)&ss
, &ss_len
) != -1)
3716 && job_assumes(j
, ss_len
> 0) && (ss
.ss_family
== AF_UNIX
)) {
3717 job_assumes(j
, unlink(sun
->sun_path
) != -1);
3718 /* We might conditionally need to delete a directory here */
3721 job_assumes(j
, runtime_close(sg
->fds
[i
]) != -1);
3724 SLIST_REMOVE(&j
->sockets
, sg
, socketgroup
, sle
);
3733 socketgroup_kevent_mod(job_t j
, struct socketgroup
*sg
, bool do_add
)
3735 struct kevent kev
[sg
->fd_cnt
];
3737 unsigned int i
, buf_off
= 0;
3743 for (i
= 0; i
< sg
->fd_cnt
; i
++) {
3744 EV_SET(&kev
[i
], sg
->fds
[i
], EVFILT_READ
, do_add
? EV_ADD
: EV_DELETE
, 0, 0, j
);
3745 buf_off
+= snprintf(buf
+ buf_off
, sizeof(buf
) - buf_off
, " %d", sg
->fds
[i
]);
3748 job_log(j
, LOG_DEBUG
, "%s Sockets:%s", do_add
? "Watching" : "Ignoring", buf
);
3750 job_assumes(j
, kevent_bulk_mod(kev
, sg
->fd_cnt
) != -1);
3752 for (i
= 0; i
< sg
->fd_cnt
; i
++) {
3753 job_assumes(j
, kev
[i
].flags
& EV_ERROR
);
3754 errno
= kev
[i
].data
;
3755 job_assumes(j
, kev
[i
].data
== 0);
3760 socketgroup_ignore(job_t j
, struct socketgroup
*sg
)
3762 socketgroup_kevent_mod(j
, sg
, false);
3766 socketgroup_watch(job_t j
, struct socketgroup
*sg
)
3768 socketgroup_kevent_mod(j
, sg
, true);
3772 socketgroup_callback(job_t j
)
3774 job_dispatch(j
, true);
3778 envitem_new(job_t j
, const char *k
, const char *v
, bool global
)
3780 struct envitem
*ei
= calloc(1, sizeof(struct envitem
) + strlen(k
) + 1 + strlen(v
) + 1);
3782 if (!job_assumes(j
, ei
!= NULL
)) {
3786 strcpy(ei
->key_init
, k
);
3787 ei
->value
= ei
->key_init
+ strlen(k
) + 1;
3788 strcpy(ei
->value
, v
);
3791 SLIST_INSERT_HEAD(&j
->global_env
, ei
, sle
);
3793 SLIST_INSERT_HEAD(&j
->env
, ei
, sle
);
3796 job_log(j
, LOG_DEBUG
, "Added environmental variable: %s=%s", k
, v
);
3802 envitem_delete(job_t j
, struct envitem
*ei
, bool global
)
3805 SLIST_REMOVE(&j
->global_env
, ei
, envitem
, sle
);
3807 SLIST_REMOVE(&j
->env
, ei
, envitem
, sle
);
3814 envitem_setup(launch_data_t obj
, const char *key
, void *context
)
3818 if (launch_data_get_type(obj
) != LAUNCH_DATA_STRING
) {
3822 envitem_new(j
, key
, launch_data_get_string(obj
), j
->importing_global_env
);
3826 limititem_update(job_t j
, int w
, rlim_t r
)
3828 struct limititem
*li
;
3830 SLIST_FOREACH(li
, &j
->limits
, sle
) {
3831 if (li
->which
== w
) {
3837 li
= calloc(1, sizeof(struct limititem
));
3839 if (!job_assumes(j
, li
!= NULL
)) {
3843 SLIST_INSERT_HEAD(&j
->limits
, li
, sle
);
3848 if (j
->importing_hard_limits
) {
3849 li
->lim
.rlim_max
= r
;
3852 li
->lim
.rlim_cur
= r
;
3860 limititem_delete(job_t j
, struct limititem
*li
)
3862 SLIST_REMOVE(&j
->limits
, li
, limititem
, sle
);
3869 seatbelt_setup_flags(launch_data_t obj
, const char *key
, void *context
)
3873 if (launch_data_get_type(obj
) != LAUNCH_DATA_BOOL
) {
3874 job_log(j
, LOG_WARNING
, "Sandbox flag value must be boolean: %s", key
);
3878 if (launch_data_get_bool(obj
) == false) {
3882 if (strcasecmp(key
, LAUNCH_JOBKEY_SANDBOX_NAMED
) == 0) {
3883 j
->seatbelt_flags
|= SANDBOX_NAMED
;
3889 limititem_setup(launch_data_t obj
, const char *key
, void *context
)
3892 int i
, limits_cnt
= (sizeof(launchd_keys2limits
) / sizeof(launchd_keys2limits
[0]));
3895 if (launch_data_get_type(obj
) != LAUNCH_DATA_INTEGER
) {
3899 rl
= launch_data_get_integer(obj
);
3901 for (i
= 0; i
< limits_cnt
; i
++) {
3902 if (strcasecmp(launchd_keys2limits
[i
].key
, key
) == 0) {
3907 if (i
== limits_cnt
) {
3911 limititem_update(j
, launchd_keys2limits
[i
].val
, rl
);
3915 job_useless(job_t j
)
3917 /* Yes, j->unload_at_exit and j->only_once seem the same, but they'll differ someday... */
3919 if ((j
->unload_at_exit
|| j
->only_once
) && j
->start_time
!= 0) {
3920 if (j
->unload_at_exit
&& j
->j_port
) {
3923 job_log(j
, LOG_INFO
, "Exited. Was only configured to run once.");
3925 } else if (j
->removal_pending
) {
3926 job_log(j
, LOG_DEBUG
, "Exited while removal was pending.");
3928 } else if (j
->mgr
->shutting_down
&& (j
->hopefully_exits_first
|| j
->mgr
->hopefully_first_cnt
== 0)) {
3929 job_log(j
, LOG_DEBUG
, "Exited while shutdown in progress. Processes remaining: %lu/%lu", total_children
, total_anon_children
);
3931 } else if (j
->legacy_mach_job
) {
3932 if (SLIST_EMPTY(&j
->machservices
)) {
3933 job_log(j
, LOG_INFO
, "Garbage collecting");
3935 } else if (!j
->checkedin
) {
3936 job_log(j
, LOG_WARNING
, "Failed to check-in!");
3945 job_keepalive(job_t j
)
3947 mach_msg_type_number_t statusCnt
;
3948 mach_port_status_t status
;
3949 struct semaphoreitem
*si
;
3950 struct machservice
*ms
;
3952 bool good_exit
= (WIFEXITED(j
->last_exit_status
) && WEXITSTATUS(j
->last_exit_status
) == 0);
3954 if (j
->mgr
->shutting_down
) {
3961 * We definitely need to revisit this after Leopard ships. Please see
3962 * launchctl.c for the other half of this hack.
3964 if (j
->mgr
->global_on_demand_cnt
> 0 && strcmp(j
->label
, "com.apple.kextd") != 0) {
3968 if (j
->start_pending
) {
3969 job_log(j
, LOG_DEBUG
, "KeepAlive check: Pent-up non-IPC launch criteria.");
3974 job_log(j
, LOG_DEBUG
, "KeepAlive check: job configured to run continuously.");
3978 SLIST_FOREACH(ms
, &j
->machservices
, sle
) {
3979 statusCnt
= MACH_PORT_RECEIVE_STATUS_COUNT
;
3980 if (mach_port_get_attributes(mach_task_self(), ms
->port
, MACH_PORT_RECEIVE_STATUS
,
3981 (mach_port_info_t
)&status
, &statusCnt
) != KERN_SUCCESS
) {
3984 if (status
.mps_msgcount
) {
3985 job_log(j
, LOG_DEBUG
, "KeepAlive check: job restarted due to %d queued Mach messages on service: %s",
3986 status
.mps_msgcount
, ms
->name
);
3992 SLIST_FOREACH(si
, &j
->semaphores
, sle
) {
3993 bool wanted_state
= false;
3999 wanted_state
= true;
4001 if (network_up
== wanted_state
) {
4002 job_log(j
, LOG_DEBUG
, "KeepAlive: The network is %s.", wanted_state
? "up" : "down");
4006 case SUCCESSFUL_EXIT
:
4007 wanted_state
= true;
4009 if (good_exit
== wanted_state
) {
4010 job_log(j
, LOG_DEBUG
, "KeepAlive: The exit state was %s.", wanted_state
? "successful" : "failure");
4014 case OTHER_JOB_ENABLED
:
4015 wanted_state
= true;
4016 case OTHER_JOB_DISABLED
:
4017 if ((bool)job_find(si
->what
) == wanted_state
) {
4018 job_log(j
, LOG_DEBUG
, "KeepAlive: The following job is %s: %s", wanted_state
? "enabled" : "disabled", si
->what
);
4022 case OTHER_JOB_ACTIVE
:
4023 wanted_state
= true;
4024 case OTHER_JOB_INACTIVE
:
4025 if ((other_j
= job_find(si
->what
))) {
4026 if ((bool)other_j
->p
== wanted_state
) {
4027 job_log(j
, LOG_DEBUG
, "KeepAlive: The following job is %s: %s", wanted_state
? "active" : "inactive", si
->what
);
4033 wanted_state
= true;
4035 if ((bool)(stat(si
->what
, &sb
) == 0) == wanted_state
) {
4037 job_assumes(j
, runtime_close(si
->fd
) == 0);
4040 job_log(j
, LOG_DEBUG
, "KeepAlive: The following path %s: %s", wanted_state
? "exists" : "is missing", si
->what
);
4047 if (-1 == (qdir_file_cnt
= dir_has_files(j
, si
->what
))) {
4048 job_log_error(j
, LOG_ERR
, "Failed to count the number of files in \"%s\"", si
->what
);
4049 } else if (qdir_file_cnt
> 0) {
4050 job_log(j
, LOG_DEBUG
, "KeepAlive: Directory is not empty: %s", si
->what
);
4065 } else if (j
->argv
) {
4075 struct machservice
*ms
;
4078 return "PID is still valid";
4081 if (j
->mgr
->shutting_down
&& j
->log_redirect_fd
) {
4082 job_assumes(j
, runtime_close(j
->log_redirect_fd
) != -1);
4083 j
->log_redirect_fd
= 0;
4086 if (j
->log_redirect_fd
) {
4087 if (job_assumes(j
, j
->wait4pipe_eof
)) {
4088 return "Standard out/error is still valid";
4090 job_assumes(j
, runtime_close(j
->log_redirect_fd
) != -1);
4091 j
->log_redirect_fd
= 0;
4095 if (j
->priv_port_has_senders
) {
4096 return "Privileged Port still has outstanding senders";
4099 SLIST_FOREACH(ms
, &j
->machservices
, sle
) {
4100 if (ms
->recv
&& ms
->isActive
) {
4101 return "Mach service is still active";
4109 machservice_watch(job_t j
, struct machservice
*ms
)
4112 job_assumes(j
, runtime_add_mport(ms
->port
, NULL
, 0) == KERN_SUCCESS
);
4117 machservice_ignore(job_t j
, struct machservice
*ms
)
4119 job_assumes(j
, runtime_remove_mport(ms
->port
) == KERN_SUCCESS
);
4123 machservice_resetport(job_t j
, struct machservice
*ms
)
4125 LIST_REMOVE(ms
, port_hash_sle
);
4126 job_assumes(j
, launchd_mport_close_recv(ms
->port
) == KERN_SUCCESS
);
4127 job_assumes(j
, launchd_mport_deallocate(ms
->port
) == KERN_SUCCESS
);
4129 job_assumes(j
, launchd_mport_create_recv(&ms
->port
) == KERN_SUCCESS
);
4130 job_assumes(j
, launchd_mport_make_send(ms
->port
) == KERN_SUCCESS
);
4131 LIST_INSERT_HEAD(&port_hash
[HASH_PORT(ms
->port
)], ms
, port_hash_sle
);
4134 struct machservice
*
4135 machservice_new(job_t j
, const char *name
, mach_port_t
*serviceport
, bool pid_local
)
4137 struct machservice
*ms
;
4139 if ((ms
= calloc(1, sizeof(struct machservice
) + strlen(name
) + 1)) == NULL
) {
4143 strcpy((char *)ms
->name
, name
);
4145 ms
->per_pid
= pid_local
;
4147 if (*serviceport
== MACH_PORT_NULL
) {
4148 if (!job_assumes(j
, launchd_mport_create_recv(&ms
->port
) == KERN_SUCCESS
)) {
4152 if (!job_assumes(j
, launchd_mport_make_send(ms
->port
) == KERN_SUCCESS
)) {
4155 *serviceport
= ms
->port
;
4158 ms
->port
= *serviceport
;
4159 ms
->isActive
= true;
4162 SLIST_INSERT_HEAD(&j
->machservices
, ms
, sle
);
4163 LIST_INSERT_HEAD(&j
->mgr
->ms_hash
[hash_ms(ms
->name
)], ms
, name_hash_sle
);
4164 LIST_INSERT_HEAD(&port_hash
[HASH_PORT(ms
->port
)], ms
, port_hash_sle
);
4166 job_log(j
, LOG_INFO
, "Mach service added: %s", name
);
4170 job_assumes(j
, launchd_mport_close_recv(ms
->port
) == KERN_SUCCESS
);
4177 machservice_status(struct machservice
*ms
)
4180 return BOOTSTRAP_STATUS_ACTIVE
;
4181 } else if (ms
->job
->ondemand
) {
4182 return BOOTSTRAP_STATUS_ON_DEMAND
;
4184 return BOOTSTRAP_STATUS_INACTIVE
;
4189 job_setup_exception_port(job_t j
, task_t target_task
)
4191 struct machservice
*ms
;
4192 thread_state_flavor_t f
= 0;
4193 mach_port_t exc_port
= the_exception_server
;
4195 if (j
->alt_exc_handler
) {
4196 ms
= jobmgr_lookup_service(j
->mgr
, j
->alt_exc_handler
, true, 0);
4198 exc_port
= machservice_port(ms
);
4200 job_log(j
, LOG_WARNING
, "Falling back to default Mach exception handler. Could not find: %s", j
->alt_exc_handler
);
4202 } else if (j
->internal_exc_handler
) {
4203 exc_port
= runtime_get_kernel_port();
4204 } else if (!exc_port
) {
4208 #if defined (__ppc__) || defined (__ppc64__)
4209 f
= PPC_THREAD_STATE64
;
4210 #elif defined(__i386__) || defined(__x86_64__)
4211 f
= x86_THREAD_STATE
;
4212 #elif defined(__arm__)
4213 f
= ARM_THREAD_STATE
;
4215 #error "unknown architecture"
4219 job_assumes(j
, task_set_exception_ports(target_task
, EXC_MASK_CRASH
, exc_port
,
4220 EXCEPTION_STATE_IDENTITY
| MACH_EXCEPTION_CODES
, f
) == KERN_SUCCESS
);
4221 } else if (getpid() == 1 && the_exception_server
) {
4222 mach_port_t mhp
= mach_host_self();
4223 job_assumes(j
, host_set_exception_ports(mhp
, EXC_MASK_CRASH
, the_exception_server
,
4224 EXCEPTION_STATE_IDENTITY
| MACH_EXCEPTION_CODES
, f
) == KERN_SUCCESS
);
4225 job_assumes(j
, launchd_mport_deallocate(mhp
) == KERN_SUCCESS
);
4231 job_set_exeception_port(job_t j
, mach_port_t port
)
4233 if (!the_exception_server
) {
4234 the_exception_server
= port
;
4235 job_setup_exception_port(j
, 0);
4237 job_log(j
, LOG_WARNING
, "The exception server is already claimed!");
4242 machservice_setup_options(launch_data_t obj
, const char *key
, void *context
)
4244 struct machservice
*ms
= context
;
4245 mach_port_t mhp
= mach_host_self();
4249 if (!job_assumes(ms
->job
, mhp
!= MACH_PORT_NULL
)) {
4253 switch (launch_data_get_type(obj
)) {
4254 case LAUNCH_DATA_INTEGER
:
4255 which_port
= launch_data_get_integer(obj
);
4256 if (strcasecmp(key
, LAUNCH_JOBKEY_MACH_TASKSPECIALPORT
) == 0) {
4257 switch (which_port
) {
4258 case TASK_KERNEL_PORT
:
4259 case TASK_HOST_PORT
:
4260 case TASK_NAME_PORT
:
4261 case TASK_BOOTSTRAP_PORT
:
4262 /* I find it a little odd that zero isn't reserved in the header */
4264 job_log(ms
->job
, LOG_WARNING
, "Tried to set a reserved task special port: %d", which_port
);
4267 ms
->special_port_num
= which_port
;
4268 SLIST_INSERT_HEAD(&special_ports
, ms
, special_port_sle
);
4271 } else if (strcasecmp(key
, LAUNCH_JOBKEY_MACH_HOSTSPECIALPORT
) == 0 && getpid() == 1) {
4272 if (which_port
> HOST_MAX_SPECIAL_KERNEL_PORT
) {
4273 job_assumes(ms
->job
, (errno
= host_set_special_port(mhp
, which_port
, ms
->port
)) == KERN_SUCCESS
);
4275 job_log(ms
->job
, LOG_WARNING
, "Tried to set a reserved host special port: %d", which_port
);
4278 case LAUNCH_DATA_BOOL
:
4279 b
= launch_data_get_bool(obj
);
4280 if (strcasecmp(key
, LAUNCH_JOBKEY_MACH_ENTERKERNELDEBUGGERONCLOSE
) == 0) {
4281 ms
->debug_on_close
= b
;
4282 } else if (strcasecmp(key
, LAUNCH_JOBKEY_MACH_RESETATCLOSE
) == 0) {
4284 } else if (strcasecmp(key
, LAUNCH_JOBKEY_MACH_HIDEUNTILCHECKIN
) == 0) {
4286 } else if (strcasecmp(key
, LAUNCH_JOBKEY_MACH_EXCEPTIONSERVER
) == 0) {
4287 job_set_exeception_port(ms
->job
, ms
->port
);
4288 } else if (strcasecmp(key
, LAUNCH_JOBKEY_MACH_KUNCSERVER
) == 0) {
4290 job_assumes(ms
->job
, host_set_UNDServer(mhp
, ms
->port
) == KERN_SUCCESS
);
4293 case LAUNCH_DATA_DICTIONARY
:
4294 job_set_exeception_port(ms
->job
, ms
->port
);
4300 job_assumes(ms
->job
, launchd_mport_deallocate(mhp
) == KERN_SUCCESS
);
4304 machservice_setup(launch_data_t obj
, const char *key
, void *context
)
4307 struct machservice
*ms
;
4308 mach_port_t p
= MACH_PORT_NULL
;
4310 if ((ms
= jobmgr_lookup_service(j
->mgr
, key
, false, 0))) {
4311 job_log(j
, LOG_WARNING
, "Conflict with job: %s over Mach service: %s", ms
->job
->label
, key
);
4315 if ((ms
= machservice_new(j
, key
, &p
, false)) == NULL
) {
4316 job_log_error(j
, LOG_WARNING
, "Cannot add service: %s", key
);
4320 ms
->isActive
= false;
4322 if (launch_data_get_type(obj
) == LAUNCH_DATA_DICTIONARY
) {
4323 launch_data_dict_iterate(obj
, machservice_setup_options
, ms
);
4328 jobmgr_do_garbage_collection(jobmgr_t jm
)
4333 SLIST_FOREACH_SAFE(jmi
, &jm
->submgrs
, sle
, jmn
) {
4334 jobmgr_do_garbage_collection(jmi
);
4337 if (!jm
->shutting_down
) {
4341 jobmgr_log(jm
, LOG_DEBUG
, "Garbage collecting.");
4344 * Normally, we wait for all resources of a job (Unix PIDs/FDs and Mach ports)
4345 * to reset before we conider the job truly dead and ready to be spawned again.
4347 * In order to work around 5487724 and 3456090, we're going to call reboot()
4348 * when the last PID dies and not wait for the associated resources to reset.
4350 if (getpid() == 1 && jm
->parentmgr
== NULL
&& total_children
== 0) {
4351 jobmgr_log(jm
, LOG_DEBUG
, "About to force a call to: reboot(%s)", reboot_flags_to_C_names(jm
->reboot_flags
));
4353 jobmgr_assumes(jm
, reboot(jm
->reboot_flags
) != -1);
4356 if (jm
->hopefully_first_cnt
) {
4360 if (jm
->parentmgr
&& jm
->parentmgr
->shutting_down
&& jm
->parentmgr
->hopefully_first_cnt
) {
4364 if (!jm
->sent_stop_to_normal_jobs
) {
4365 jobmgr_log(jm
, LOG_DEBUG
, "Asking \"normal\" jobs to exit.");
4367 LIST_FOREACH_SAFE(ji
, &jm
->jobs
, sle
, jn
) {
4368 if (!job_active(ji
)) {
4370 } else if (!ji
->hopefully_exits_last
) {
4375 jm
->sent_stop_to_normal_jobs
= true;
4378 if (jm
->normal_active_cnt
) {
4382 if (!jm
->sent_stop_to_hopefully_last_jobs
) {
4383 jobmgr_log(jm
, LOG_DEBUG
, "Asking \"hopefully last\" jobs to exit.");
4385 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
4386 if (ji
->p
&& ji
->anonymous
) {
4388 } else if (ji
->p
&& job_assumes(ji
, ji
->hopefully_exits_last
)) {
4393 jm
->sent_stop_to_hopefully_last_jobs
= true;
4396 if (!SLIST_EMPTY(&jm
->submgrs
)) {
4400 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
4401 if (!ji
->anonymous
) {
4406 jobmgr_log_stray_children(jm
);
4412 jobmgr_log_stray_children(jobmgr_t jm
)
4414 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_ALL
};
4415 size_t i
, kp_cnt
, len
= sizeof(struct kinfo_proc
) * get_kern_max_proc();
4416 struct kinfo_proc
*kp
;
4418 #if TARGET_OS_EMBEDDED
4419 if (!do_apple_internal_magic
) {
4423 if (jm
->parentmgr
|| getpid() != 1) {
4427 if (!jobmgr_assumes(jm
, (kp
= malloc(len
)) != NULL
)) {
4430 if (!jobmgr_assumes(jm
, sysctl(mib
, 3, kp
, &len
, NULL
, 0) != -1)) {
4434 kp_cnt
= len
/ sizeof(struct kinfo_proc
);
4436 for (i
= 0; i
< kp_cnt
; i
++) {
4437 pid_t p_i
= kp
[i
].kp_proc
.p_pid
;
4438 pid_t pp_i
= kp
[i
].kp_eproc
.e_ppid
;
4439 pid_t pg_i
= kp
[i
].kp_eproc
.e_pgid
;
4440 const char *z
= (kp
[i
].kp_proc
.p_stat
== SZOMB
) ? "zombie " : "";
4441 const char *n
= kp
[i
].kp_proc
.p_comm
;
4443 if (p_i
== 0 || p_i
== 1) {
4447 jobmgr_log(jm
, LOG_WARNING
, "Stray %sprocess at shutdown: PID %u PPID %u PGID %u %s", z
, p_i
, pp_i
, pg_i
, n
);
4450 * The kernel team requested that launchd not do this for Leopard.
4451 * jobmgr_assumes(jm, runtime_kill(p_i, SIGKILL) != -1);
4460 jobmgr_parent(jobmgr_t jm
)
4462 return jm
->parentmgr
;
4466 job_uncork_fork(job_t j
)
4470 job_log(j
, LOG_DEBUG
, "Uncorking the fork().");
4471 /* this unblocks the child and avoids a race
4472 * between the above fork() and the kevent_mod() */
4473 job_assumes(j
, write(j
->forkfd
, &c
, sizeof(c
)) == sizeof(c
));
4474 job_assumes(j
, runtime_close(j
->forkfd
) != -1);
4479 jobmgr_new(jobmgr_t jm
, mach_port_t requestorport
, mach_port_t transfer_port
, bool sflag
, const char *name
)
4481 mach_msg_size_t mxmsgsz
;
4482 job_t bootstrapper
= NULL
;
4485 launchd_assert(offsetof(struct jobmgr_s
, kqjobmgr_callback
) == 0);
4487 if (jm
&& requestorport
== MACH_PORT_NULL
) {
4488 jobmgr_log(jm
, LOG_ERR
, "Mach sub-bootstrap create request requires a requester port");
4492 jmr
= calloc(1, sizeof(struct jobmgr_s
) + (name
? (strlen(name
) + 1) : 128));
4498 jmr
->kqjobmgr_callback
= jobmgr_callback
;
4499 strcpy(jmr
->name_init
, name
? name
: "Under construction");
4501 jmr
->req_port
= requestorport
;
4503 if ((jmr
->parentmgr
= jm
)) {
4504 SLIST_INSERT_HEAD(&jm
->submgrs
, jmr
, sle
);
4507 if (jm
&& !jobmgr_assumes(jmr
, launchd_mport_notify_req(jmr
->req_port
, MACH_NOTIFY_DEAD_NAME
) == KERN_SUCCESS
)) {
4511 if (transfer_port
!= MACH_PORT_NULL
) {
4512 jobmgr_assumes(jmr
, jm
!= NULL
);
4513 jmr
->jm_port
= transfer_port
;
4514 } else if (!jm
&& getpid() != 1) {
4515 char *trusted_fd
= getenv(LAUNCHD_TRUSTED_FD_ENV
);
4518 snprintf(service_buf
, sizeof(service_buf
), "com.apple.launchd.peruser.%u", getuid());
4520 if (!jobmgr_assumes(jmr
, bootstrap_check_in(bootstrap_port
, service_buf
, &jmr
->jm_port
) == 0)) {
4525 int dfd
, lfd
= strtol(trusted_fd
, NULL
, 10);
4527 if ((dfd
= dup(lfd
)) >= 0) {
4528 jobmgr_assumes(jmr
, runtime_close(dfd
) != -1);
4529 jobmgr_assumes(jmr
, runtime_close(lfd
) != -1);
4532 unsetenv(LAUNCHD_TRUSTED_FD_ENV
);
4535 /* cut off the Libc cache, we don't want to deadlock against ourself */
4536 inherited_bootstrap_port
= bootstrap_port
;
4537 bootstrap_port
= MACH_PORT_NULL
;
4538 launchd_assert(launchd_mport_notify_req(inherited_bootstrap_port
, MACH_NOTIFY_DEAD_NAME
) == KERN_SUCCESS
);
4540 /* We set this explicitly as we start each child */
4541 launchd_assert(launchd_set_bport(MACH_PORT_NULL
) == KERN_SUCCESS
);
4542 } else if (!jobmgr_assumes(jmr
, launchd_mport_create_recv(&jmr
->jm_port
) == KERN_SUCCESS
)) {
4547 sprintf(jmr
->name_init
, "%u", MACH_PORT_INDEX(jmr
->jm_port
));
4550 /* Sigh... at the moment, MIG has maxsize == sizeof(reply union) */
4551 mxmsgsz
= sizeof(union __RequestUnion__job_mig_protocol_vproc_subsystem
);
4552 if (job_mig_protocol_vproc_subsystem
.maxsize
> mxmsgsz
) {
4553 mxmsgsz
= job_mig_protocol_vproc_subsystem
.maxsize
;
4557 jobmgr_assumes(jmr
, kevent_mod(SIGTERM
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, jmr
) != -1);
4558 jobmgr_assumes(jmr
, kevent_mod(SIGUSR1
, EVFILT_SIGNAL
, EV_ADD
, 0, 0, jmr
) != -1);
4559 jobmgr_assumes(jmr
, kevent_mod(0, EVFILT_FS
, EV_ADD
, VQ_MOUNT
|VQ_UNMOUNT
|VQ_UPDATE
, 0, jmr
) != -1);
4563 bootstrapper
= jobmgr_init_session(jmr
, name
, sflag
);
4566 if (!bootstrapper
|| !bootstrapper
->weird_bootstrap
) {
4567 if (!jobmgr_assumes(jmr
, runtime_add_mport(jmr
->jm_port
, protocol_vproc_server
, mxmsgsz
) == KERN_SUCCESS
)) {
4572 jobmgr_log(jmr
, LOG_DEBUG
, "Created job manager%s%s", jm
? " with parent: " : ".", jm
? jm
->name
: "");
4575 jobmgr_assumes(jmr
, job_dispatch(bootstrapper
, true) != NULL
);
4578 if (jmr
->parentmgr
) {
4592 jobmgr_init_session(jobmgr_t jm
, const char *session_type
, bool sflag
)
4594 const char *bootstrap_tool
[] = { "/bin/launchctl", "bootstrap", "-S", session_type
, sflag
? "-s" : NULL
, NULL
};
4595 char thelabel
[1000];
4598 snprintf(thelabel
, sizeof(thelabel
), "com.apple.launchctl.%s", session_type
);
4599 bootstrapper
= job_new(jm
, thelabel
, NULL
, bootstrap_tool
);
4600 if (jobmgr_assumes(jm
, bootstrapper
!= NULL
) && (jm
->parentmgr
|| getuid())) {
4603 /* <rdar://problem/5042202> launchd-201: can't ssh in with AFP OD account (hangs) */
4604 snprintf(buf
, sizeof(buf
), "0x%X:0:0", getuid());
4605 envitem_new(bootstrapper
, "__CF_USER_TEXT_ENCODING", buf
, false);
4606 bootstrapper
->weird_bootstrap
= true;
4607 jobmgr_assumes(jm
, job_setup_machport(bootstrapper
));
4610 jm
->session_initialized
= true;
4612 return bootstrapper
;
4616 jobmgr_delete_anything_with_port(jobmgr_t jm
, mach_port_t port
)
4618 struct machservice
*ms
, *next_ms
;
4621 /* Mach ports, unlike Unix descriptors, are reference counted. In other
4622 * words, when some program hands us a second or subsequent send right
4623 * to a port we already have open, the Mach kernel gives us the same
4624 * port number back and increments an reference count associated with
4625 * the port. This forces us, when discovering that a receive right at
4626 * the other end has been deleted, to wander all of our objects to see
4627 * what weird places clients might have handed us the same send right
4631 if (jm
== root_jobmgr
) {
4632 if (port
== inherited_bootstrap_port
) {
4633 launchd_assumes(launchd_mport_deallocate(port
) == KERN_SUCCESS
);
4634 inherited_bootstrap_port
= MACH_PORT_NULL
;
4636 return jobmgr_shutdown(jm
);
4639 LIST_FOREACH_SAFE(ms
, &port_hash
[HASH_PORT(port
)], port_hash_sle
, next_ms
) {
4640 if (ms
->port
== port
) {
4641 machservice_delete(ms
->job
, ms
, true);
4646 SLIST_FOREACH_SAFE(jmi
, &jm
->submgrs
, sle
, jmn
) {
4647 jobmgr_delete_anything_with_port(jmi
, port
);
4650 if (jm
->req_port
== port
) {
4651 jobmgr_log(jm
, LOG_DEBUG
, "Request port died: 0x%x", port
);
4652 return jobmgr_shutdown(jm
);
4658 struct machservice
*
4659 jobmgr_lookup_service(jobmgr_t jm
, const char *name
, bool check_parent
, pid_t target_pid
)
4661 struct machservice
*ms
;
4664 jobmgr_assumes(jm
, !check_parent
);
4667 LIST_FOREACH(ms
, &jm
->ms_hash
[hash_ms(name
)], name_hash_sle
) {
4668 if ((target_pid
&& ms
->per_pid
&& ms
->job
->p
== target_pid
) || (!target_pid
&& !ms
->per_pid
)) {
4669 if (strcmp(name
, ms
->name
) == 0) {
4675 if (jm
->parentmgr
== NULL
) {
4679 if (!check_parent
) {
4683 return jobmgr_lookup_service(jm
->parentmgr
, name
, true, 0);
4687 machservice_port(struct machservice
*ms
)
4693 machservice_job(struct machservice
*ms
)
4699 machservice_hidden(struct machservice
*ms
)
4705 machservice_active(struct machservice
*ms
)
4707 return ms
->isActive
;
4711 machservice_name(struct machservice
*ms
)
4717 machservice_delete(job_t j
, struct machservice
*ms
, bool port_died
)
4719 if (ms
->debug_on_close
) {
4720 job_log(j
, LOG_NOTICE
, "About to enter kernel debugger because of Mach port: 0x%x", ms
->port
);
4721 job_assumes(j
, host_reboot(mach_host_self(), HOST_REBOOT_DEBUGGER
) == KERN_SUCCESS
);
4724 if (ms
->recv
&& job_assumes(j
, !ms
->isActive
)) {
4725 job_assumes(j
, launchd_mport_close_recv(ms
->port
) == KERN_SUCCESS
);
4728 job_assumes(j
, launchd_mport_deallocate(ms
->port
) == KERN_SUCCESS
);
4730 if (ms
->port
== the_exception_server
) {
4731 the_exception_server
= 0;
4734 job_log(j
, LOG_INFO
, "Mach service deleted%s: %s", port_died
? " (port died)" : "", ms
->name
);
4736 if (ms
->special_port_num
) {
4737 SLIST_REMOVE(&special_ports
, ms
, machservice
, special_port_sle
);
4740 SLIST_REMOVE(&j
->machservices
, ms
, machservice
, sle
);
4741 LIST_REMOVE(ms
, name_hash_sle
);
4742 LIST_REMOVE(ms
, port_hash_sle
);
4748 machservice_request_notifications(struct machservice
*ms
)
4750 mach_msg_id_t which
= MACH_NOTIFY_DEAD_NAME
;
4752 ms
->isActive
= true;
4755 which
= MACH_NOTIFY_PORT_DESTROYED
;
4756 job_checkin(ms
->job
);
4759 job_assumes(ms
->job
, launchd_mport_notify_req(ms
->port
, which
) == KERN_SUCCESS
);
4762 #define NELEM(x) (sizeof(x)/sizeof(x[0]))
4763 #define END_OF(x) (&(x)[NELEM(x)])
4766 mach_cmd2argv(const char *string
)
4768 char *argv
[100], args
[1000];
4770 char *argp
= args
, term
, **argv_ret
, *co
;
4771 unsigned int nargs
= 0, i
;
4773 for (cp
= string
; *cp
;) {
4774 while (isspace(*cp
))
4776 term
= (*cp
== '"') ? *cp
++ : '\0';
4777 if (nargs
< NELEM(argv
)) {
4778 argv
[nargs
++] = argp
;
4780 while (*cp
&& (term
? *cp
!= term
: !isspace(*cp
)) && argp
< END_OF(args
)) {
4797 argv_ret
= malloc((nargs
+ 1) * sizeof(char *) + strlen(string
) + 1);
4799 if (!launchd_assumes(argv_ret
!= NULL
)) {
4803 co
= (char *)argv_ret
+ (nargs
+ 1) * sizeof(char *);
4805 for (i
= 0; i
< nargs
; i
++) {
4806 strcpy(co
, argv
[i
]);
4808 co
+= strlen(argv
[i
]) + 1;
4816 job_checkin(job_t j
)
4818 j
->checkedin
= true;
4822 job_ack_port_destruction(mach_port_t p
)
4824 struct machservice
*ms
;
4826 LIST_FOREACH(ms
, &port_hash
[HASH_PORT(p
)], port_hash_sle
) {
4827 if (ms
->recv
&& (ms
->port
== p
)) {
4836 ms
->isActive
= false;
4839 machservice_resetport(ms
->job
, ms
);
4842 job_log(ms
->job
, LOG_DEBUG
, "Receive right returned to us: %s", ms
->name
);
4843 job_dispatch(ms
->job
, false);
4845 root_jobmgr
= jobmgr_do_garbage_collection(root_jobmgr
);
4851 job_ack_no_senders(job_t j
)
4853 j
->priv_port_has_senders
= false;
4855 job_assumes(j
, launchd_mport_close_recv(j
->j_port
) == KERN_SUCCESS
);
4858 job_log(j
, LOG_DEBUG
, "No more senders on privileged Mach bootstrap port");
4860 job_dispatch(j
, false);
4866 if (job_assumes(j
, j
->mgr
!= NULL
)) {
4874 job_is_anonymous(job_t j
)
4876 return j
->anonymous
;
4880 job_force_sampletool(job_t j
)
4883 char logfile
[PATH_MAX
];
4885 char *sample_args
[] = { "sample", pidstr
, "1", "-mayDie", "-file", logfile
, NULL
};
4886 char *contents
= NULL
;
4887 int logfile_fd
= -1;
4888 int console_fd
= -1;
4892 if (!debug_shutdown_hangs
) {
4896 snprintf(pidstr
, sizeof(pidstr
), "%u", j
->p
);
4897 snprintf(logfile
, sizeof(logfile
), SHUTDOWN_LOG_DIR
"/%s-%u.sample.txt", j
->label
, j
->p
);
4899 if (!job_assumes(j
, unlink(logfile
) != -1 || errno
== ENOENT
)) {
4904 * This will stall launchd for as long as the 'sample' tool runs.
4906 * We didn't give the 'sample' tool a bootstrap port, so it therefore
4907 * can't deadlock against launchd.
4909 if (!job_assumes(j
, (errno
= posix_spawnp(&sp
, sample_args
[0], NULL
, NULL
, sample_args
, environ
)) == 0)) {
4913 job_log(j
, LOG_DEBUG
, "Waiting for 'sample' to finish.");
4915 if (!job_assumes(j
, waitpid(sp
, &wstatus
, 0) != -1)) {
4920 * This won't work if the VFS or filesystems are sick:
4924 if (!job_assumes(j
, WIFEXITED(wstatus
) && WEXITSTATUS(wstatus
) == 0)) {
4928 if (!job_assumes(j
, (logfile_fd
= open(logfile
, O_RDONLY
|O_NOCTTY
)) != -1)) {
4932 if (!job_assumes(j
, (console_fd
= open(_PATH_CONSOLE
, O_WRONLY
|O_APPEND
|O_NOCTTY
)) != -1)) {
4936 if (!job_assumes(j
, fstat(logfile_fd
, &sb
) != -1)) {
4940 contents
= malloc(sb
.st_size
);
4942 if (!job_assumes(j
, contents
!= NULL
)) {
4946 if (!job_assumes(j
, read(logfile_fd
, contents
, sb
.st_size
) == sb
.st_size
)) {
4950 job_assumes(j
, write(console_fd
, contents
, sb
.st_size
) == sb
.st_size
);
4957 if (logfile_fd
!= -1) {
4958 job_assumes(j
, runtime_fsync(logfile_fd
) != -1);
4959 job_assumes(j
, runtime_close(logfile_fd
) != -1);
4962 if (console_fd
!= -1) {
4963 job_assumes(j
, runtime_close(console_fd
) != -1);
4966 job_log(j
, LOG_DEBUG
, "Finished sampling.");
4970 semaphoreitem_new(job_t j
, semaphore_reason_t why
, const char *what
)
4972 struct semaphoreitem
*si
;
4973 size_t alloc_sz
= sizeof(struct semaphoreitem
);
4976 alloc_sz
+= strlen(what
) + 1;
4979 if (!job_assumes(j
, si
= calloc(1, alloc_sz
))) {
4987 strcpy(si
->what_init
, what
);
4990 SLIST_INSERT_HEAD(&j
->semaphores
, si
, sle
);
4992 semaphoreitem_runtime_mod_ref(si
, true);
4998 semaphoreitem_runtime_mod_ref(struct semaphoreitem
*si
, bool add
)
5001 * External events need to be tracked.
5002 * Internal events do NOT need to be tracked.
5006 case SUCCESSFUL_EXIT
:
5008 case OTHER_JOB_ENABLED
:
5009 case OTHER_JOB_DISABLED
:
5010 case OTHER_JOB_ACTIVE
:
5011 case OTHER_JOB_INACTIVE
:
5025 semaphoreitem_delete(job_t j
, struct semaphoreitem
*si
)
5027 semaphoreitem_runtime_mod_ref(si
, false);
5029 SLIST_REMOVE(&j
->semaphores
, si
, semaphoreitem
, sle
);
5032 job_assumes(j
, runtime_close(si
->fd
) != -1);
5039 semaphoreitem_setup_dict_iter(launch_data_t obj
, const char *key
, void *context
)
5041 struct semaphoreitem_dict_iter_context
*sdic
= context
;
5042 semaphore_reason_t why
;
5044 why
= launch_data_get_bool(obj
) ? sdic
->why_true
: sdic
->why_false
;
5046 semaphoreitem_new(sdic
->j
, why
, key
);
5050 semaphoreitem_setup(launch_data_t obj
, const char *key
, void *context
)
5052 struct semaphoreitem_dict_iter_context sdic
= { context
, 0, 0 };
5054 semaphore_reason_t why
;
5056 switch (launch_data_get_type(obj
)) {
5057 case LAUNCH_DATA_BOOL
:
5058 if (strcasecmp(key
, LAUNCH_JOBKEY_KEEPALIVE_NETWORKSTATE
) == 0) {
5059 why
= launch_data_get_bool(obj
) ? NETWORK_UP
: NETWORK_DOWN
;
5060 semaphoreitem_new(j
, why
, NULL
);
5061 } else if (strcasecmp(key
, LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT
) == 0) {
5062 why
= launch_data_get_bool(obj
) ? SUCCESSFUL_EXIT
: FAILED_EXIT
;
5063 semaphoreitem_new(j
, why
, NULL
);
5064 j
->start_pending
= true;
5066 job_assumes(j
, false);
5069 case LAUNCH_DATA_DICTIONARY
:
5070 if (strcasecmp(key
, LAUNCH_JOBKEY_KEEPALIVE_PATHSTATE
) == 0) {
5071 sdic
.why_true
= PATH_EXISTS
;
5072 sdic
.why_false
= PATH_MISSING
;
5073 } else if (strcasecmp(key
, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBACTIVE
) == 0) {
5074 sdic
.why_true
= OTHER_JOB_ACTIVE
;
5075 sdic
.why_false
= OTHER_JOB_INACTIVE
;
5076 } else if (strcasecmp(key
, LAUNCH_JOBKEY_KEEPALIVE_OTHERJOBENABLED
) == 0) {
5077 sdic
.why_true
= OTHER_JOB_ENABLED
;
5078 sdic
.why_false
= OTHER_JOB_DISABLED
;
5080 job_assumes(j
, false);
5084 launch_data_dict_iterate(obj
, semaphoreitem_setup_dict_iter
, &sdic
);
5087 job_assumes(j
, false);
5093 jobmgr_dispatch_all_semaphores(jobmgr_t jm
)
5099 SLIST_FOREACH_SAFE(jmi
, &jm
->submgrs
, sle
, jmn
) {
5100 jobmgr_dispatch_all_semaphores(jmi
);
5103 LIST_FOREACH_SAFE(ji
, &jm
->jobs
, sle
, jn
) {
5104 if (!SLIST_EMPTY(&ji
->semaphores
)) {
5105 job_dispatch(ji
, false);
5111 cronemu(int mon
, int mday
, int hour
, int min
)
5113 struct tm workingtm
;
5117 workingtm
= *localtime(&now
);
5119 workingtm
.tm_isdst
= -1;
5120 workingtm
.tm_sec
= 0;
5123 while (!cronemu_mon(&workingtm
, mon
, mday
, hour
, min
)) {
5124 workingtm
.tm_year
++;
5125 workingtm
.tm_mon
= 0;
5126 workingtm
.tm_mday
= 1;
5127 workingtm
.tm_hour
= 0;
5128 workingtm
.tm_min
= 0;
5132 return mktime(&workingtm
);
5136 cronemu_wday(int wday
, int hour
, int min
)
5138 struct tm workingtm
;
5142 workingtm
= *localtime(&now
);
5144 workingtm
.tm_isdst
= -1;
5145 workingtm
.tm_sec
= 0;
5152 while (!(workingtm
.tm_wday
== wday
&& cronemu_hour(&workingtm
, hour
, min
))) {
5153 workingtm
.tm_mday
++;
5154 workingtm
.tm_hour
= 0;
5155 workingtm
.tm_min
= 0;
5159 return mktime(&workingtm
);
5163 cronemu_mon(struct tm
*wtm
, int mon
, int mday
, int hour
, int min
)
5166 struct tm workingtm
= *wtm
;
5169 while (!cronemu_mday(&workingtm
, mday
, hour
, min
)) {
5171 workingtm
.tm_mday
= 1;
5172 workingtm
.tm_hour
= 0;
5173 workingtm
.tm_min
= 0;
5174 carrytest
= workingtm
.tm_mon
;
5176 if (carrytest
!= workingtm
.tm_mon
) {
5184 if (mon
< wtm
->tm_mon
) {
5188 if (mon
> wtm
->tm_mon
) {
5195 return cronemu_mday(wtm
, mday
, hour
, min
);
5199 cronemu_mday(struct tm
*wtm
, int mday
, int hour
, int min
)
5202 struct tm workingtm
= *wtm
;
5205 while (!cronemu_hour(&workingtm
, hour
, min
)) {
5206 workingtm
.tm_mday
++;
5207 workingtm
.tm_hour
= 0;
5208 workingtm
.tm_min
= 0;
5209 carrytest
= workingtm
.tm_mday
;
5211 if (carrytest
!= workingtm
.tm_mday
) {
5219 if (mday
< wtm
->tm_mday
) {
5223 if (mday
> wtm
->tm_mday
) {
5224 wtm
->tm_mday
= mday
;
5229 return cronemu_hour(wtm
, hour
, min
);
5233 cronemu_hour(struct tm
*wtm
, int hour
, int min
)
5236 struct tm workingtm
= *wtm
;
5239 while (!cronemu_min(&workingtm
, min
)) {
5240 workingtm
.tm_hour
++;
5241 workingtm
.tm_min
= 0;
5242 carrytest
= workingtm
.tm_hour
;
5244 if (carrytest
!= workingtm
.tm_hour
) {
5252 if (hour
< wtm
->tm_hour
) {
5256 if (hour
> wtm
->tm_hour
) {
5257 wtm
->tm_hour
= hour
;
5261 return cronemu_min(wtm
, min
);
5265 cronemu_min(struct tm
*wtm
, int min
)
5271 if (min
< wtm
->tm_min
) {
5275 if (min
> wtm
->tm_min
) {
5283 job_mig_create_server(job_t j
, cmd_t server_cmd
, uid_t server_uid
, boolean_t on_demand
, mach_port_t
*server_portp
)
5288 #if TARGET_OS_EMBEDDED
5289 return BOOTSTRAP_NOT_PRIVILEGED
;
5292 if (!launchd_assumes(j
!= NULL
)) {
5293 return BOOTSTRAP_NO_MEMORY
;
5296 if (unlikely(j
->deny_job_creation
)) {
5297 return BOOTSTRAP_NOT_PRIVILEGED
;
5300 runtime_get_caller_creds(&ldc
);
5302 job_log(j
, LOG_DEBUG
, "Server create attempt: %s", server_cmd
);
5304 #define LET_MERE_MORTALS_ADD_SERVERS_TO_PID1
5305 /* XXX - This code should go away once the per session launchd is integrated with the rest of the system */
5306 #ifdef LET_MERE_MORTALS_ADD_SERVERS_TO_PID1
5307 if (getpid() == 1) {
5308 if (ldc
.euid
&& server_uid
&& (ldc
.euid
!= server_uid
)) {
5309 job_log(j
, LOG_WARNING
, "Server create: \"%s\": Will run as UID %d, not UID %d as they told us to",
5310 server_cmd
, ldc
.euid
, server_uid
);
5311 server_uid
= ldc
.euid
;
5316 if (server_uid
!= getuid()) {
5317 job_log(j
, LOG_WARNING
, "Server create: \"%s\": As UID %d, we will not be able to switch to UID %d",
5318 server_cmd
, getuid(), server_uid
);
5320 server_uid
= 0; /* zero means "do nothing" */
5323 js
= job_new_via_mach_init(j
, server_cmd
, server_uid
, on_demand
);
5326 return BOOTSTRAP_NO_MEMORY
;
5329 *server_portp
= js
->j_port
;
5330 return BOOTSTRAP_SUCCESS
;
5334 job_mig_send_signal(job_t j
, mach_port_t srp
, name_t targetlabel
, int sig
)
5339 if (!launchd_assumes(j
!= NULL
)) {
5340 return BOOTSTRAP_NO_MEMORY
;
5343 runtime_get_caller_creds(&ldc
);
5345 if (ldc
.euid
!= 0 && ldc
.euid
!= getuid()) {
5346 return BOOTSTRAP_NOT_PRIVILEGED
;
5349 if (!(otherj
= job_find(targetlabel
))) {
5350 return BOOTSTRAP_UNKNOWN_SERVICE
;
5353 if (sig
== VPROC_MAGIC_UNLOAD_SIGNAL
) {
5354 bool do_block
= otherj
->p
;
5356 if (otherj
->anonymous
) {
5357 return BOOTSTRAP_NOT_PRIVILEGED
;
5363 job_log(j
, LOG_DEBUG
, "Blocking MIG return of job_remove(): %s", otherj
->label
);
5364 /* this is messy. We shouldn't access 'otherj' after job_remove(), but we check otherj->p first... */
5365 job_assumes(otherj
, waiting4removal_new(otherj
, srp
));
5366 return MIG_NO_REPLY
;
5370 } else if (otherj
->p
) {
5371 job_assumes(j
, runtime_kill(otherj
->p
, sig
) != -1);
5378 job_mig_log_forward(job_t j
, vm_offset_t inval
, mach_msg_type_number_t invalCnt
)
5382 if (!launchd_assumes(j
!= NULL
)) {
5383 return BOOTSTRAP_NO_MEMORY
;
5386 if (!job_assumes(j
, j
->per_user
)) {
5387 return BOOTSTRAP_NOT_PRIVILEGED
;
5390 runtime_get_caller_creds(&ldc
);
5392 return runtime_log_forward(ldc
.euid
, ldc
.egid
, inval
, invalCnt
);
5396 job_mig_log_drain(job_t j
, mach_port_t srp
, vm_offset_t
*outval
, mach_msg_type_number_t
*outvalCnt
)
5400 if (!launchd_assumes(j
!= NULL
)) {
5401 return BOOTSTRAP_NO_MEMORY
;
5404 runtime_get_caller_creds(&ldc
);
5407 return BOOTSTRAP_NOT_PRIVILEGED
;
5410 return runtime_log_drain(srp
, outval
, outvalCnt
);
5414 job_mig_swap_complex(job_t j
, vproc_gsk_t inkey
, vproc_gsk_t outkey
,
5415 vm_offset_t inval
, mach_msg_type_number_t invalCnt
,
5416 vm_offset_t
*outval
, mach_msg_type_number_t
*outvalCnt
)
5419 launch_data_t input_obj
, output_obj
;
5420 size_t data_offset
= 0;
5424 runtime_get_caller_creds(&ldc
);
5426 if (!launchd_assumes(j
!= NULL
)) {
5427 return BOOTSTRAP_NO_MEMORY
;
5430 if (inkey
&& ldc
.euid
&& ldc
.euid
!= getuid()) {
5431 return BOOTSTRAP_NOT_PRIVILEGED
;
5434 if (inkey
&& outkey
&& !job_assumes(j
, inkey
== outkey
)) {
5438 if (inkey
&& outkey
) {
5439 action
= "Swapping";
5446 job_log(j
, LOG_DEBUG
, "%s key: %u", action
, inkey
? inkey
: outkey
);
5448 *outvalCnt
= 20 * 1024 * 1024;
5449 mig_allocate(outval
, *outvalCnt
);
5450 if (!job_assumes(j
, *outval
!= 0)) {
5454 if (invalCnt
&& !job_assumes(j
, (input_obj
= launch_data_unpack((void *)inval
, invalCnt
, NULL
, 0, &data_offset
, NULL
)) != NULL
)) {
5459 case VPROC_GSK_ENVIRONMENT
:
5460 if (!job_assumes(j
, (output_obj
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
)))) {
5463 jobmgr_export_env_from_other_jobs(j
->mgr
, output_obj
);
5464 if (!job_assumes(j
, launch_data_pack(output_obj
, (void *)*outval
, *outvalCnt
, NULL
, NULL
) != 0)) {
5467 launch_data_free(output_obj
);
5469 case VPROC_GSK_ALLJOBS
:
5470 if (!job_assumes(j
, (output_obj
= job_export_all()) != NULL
)) {
5473 ipc_revoke_fds(output_obj
);
5474 packed_size
= launch_data_pack(output_obj
, (void *)*outval
, *outvalCnt
, NULL
, NULL
);
5475 if (!job_assumes(j
, packed_size
!= 0)) {
5478 launch_data_free(output_obj
);
5481 mig_deallocate(*outval
, *outvalCnt
);
5489 if (invalCnt
) switch (inkey
) {
5490 case VPROC_GSK_ENVIRONMENT
:
5491 job_assumes(j
, false);
5499 mig_deallocate(inval
, invalCnt
);
5505 mig_deallocate(*outval
, *outvalCnt
);
5511 job_mig_swap_integer(job_t j
, vproc_gsk_t inkey
, vproc_gsk_t outkey
, int64_t inval
, int64_t *outval
)
5514 kern_return_t kr
= 0;
5518 runtime_get_caller_creds(&ldc
);
5520 if (!launchd_assumes(j
!= NULL
)) {
5521 return BOOTSTRAP_NO_MEMORY
;
5524 if (inkey
&& ldc
.euid
&& ldc
.euid
!= getuid()) {
5525 return BOOTSTRAP_NOT_PRIVILEGED
;
5528 if (inkey
&& outkey
&& !job_assumes(j
, inkey
== outkey
)) {
5532 if (inkey
&& outkey
) {
5533 action
= "Swapping";
5540 job_log(j
, LOG_DEBUG
, "%s key: %u", action
, inkey
? inkey
: outkey
);
5543 case VPROC_GSK_LAST_EXIT_STATUS
:
5544 *outval
= j
->last_exit_status
;
5546 case VPROC_GSK_MGR_UID
:
5549 case VPROC_GSK_MGR_PID
:
5552 case VPROC_GSK_IS_MANAGED
:
5553 *outval
= j
->anonymous
? 0 : 1;
5555 case VPROC_GSK_BASIC_KEEPALIVE
:
5556 *outval
= !j
->ondemand
;
5558 case VPROC_GSK_START_INTERVAL
:
5559 *outval
= j
->start_interval
;
5561 case VPROC_GSK_IDLE_TIMEOUT
:
5562 *outval
= j
->timeout
;
5564 case VPROC_GSK_EXIT_TIMEOUT
:
5565 *outval
= j
->exit_timeout
;
5567 case VPROC_GSK_GLOBAL_LOG_MASK
:
5568 oldmask
= runtime_setlogmask(LOG_UPTO(LOG_DEBUG
));
5570 runtime_setlogmask(oldmask
);
5572 case VPROC_GSK_GLOBAL_UMASK
:
5586 case VPROC_GSK_GLOBAL_ON_DEMAND
:
5587 kr
= job_set_global_on_demand(j
, (bool)inval
) ? 0 : 1;
5589 case VPROC_GSK_BASIC_KEEPALIVE
:
5590 j
->ondemand
= !inval
;
5592 case VPROC_GSK_START_INTERVAL
:
5593 if ((uint64_t)inval
> UINT32_MAX
) {
5596 if (j
->start_interval
== 0) {
5599 /* Workaround 5225889 */
5600 job_assumes(j
, kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, j
) != -1);
5602 j
->start_interval
= inval
;
5603 job_assumes(j
, kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_ADD
, NOTE_SECONDS
, j
->start_interval
, j
) != -1);
5604 } else if (j
->start_interval
) {
5605 job_assumes(j
, kevent_mod((uintptr_t)&j
->start_interval
, EVFILT_TIMER
, EV_DELETE
, 0, 0, NULL
) != -1);
5606 if (j
->start_interval
!= 0) {
5609 j
->start_interval
= 0;
5612 case VPROC_GSK_IDLE_TIMEOUT
:
5613 if ((unsigned int)inval
> 0) {
5617 case VPROC_GSK_EXIT_TIMEOUT
:
5618 if ((unsigned int)inval
> 0) {
5619 j
->exit_timeout
= inval
;
5622 case VPROC_GSK_GLOBAL_LOG_MASK
:
5623 runtime_setlogmask(inval
);
5625 case VPROC_GSK_GLOBAL_UMASK
:
5639 job_mig_post_fork_ping(job_t j
, task_t child_task
)
5641 struct machservice
*ms
;
5643 if (!launchd_assumes(j
!= NULL
)) {
5644 return BOOTSTRAP_NO_MEMORY
;
5647 job_log(j
, LOG_DEBUG
, "Post fork ping.");
5649 job_setup_exception_port(j
, child_task
);
5651 SLIST_FOREACH(ms
, &special_ports
, special_port_sle
) {
5652 if (j
->per_user
&& (ms
->special_port_num
!= TASK_ACCESS_PORT
)) {
5653 /* The TASK_ACCESS_PORT funny business is to workaround 5325399. */
5657 errno
= task_set_special_port(child_task
, ms
->special_port_num
, ms
->port
);
5660 int desired_log_level
= LOG_ERR
;
5665 desired_log_level
= LOG_WARNING
;
5667 if (ms
->special_port_num
== TASK_SEATBELT_PORT
) {
5668 desired_log_level
= LOG_DEBUG
;
5672 job_log(j
, desired_log_level
, "Could not setup Mach task special port %u: %s", ms
->special_port_num
, mach_error_string(errno
));
5676 job_assumes(j
, launchd_mport_deallocate(child_task
) == KERN_SUCCESS
);
5682 job_mig_reboot2(job_t j
, uint64_t flags
)
5684 char who_started_the_reboot
[2048] = "";
5685 struct kinfo_proc kp
;
5689 if (!launchd_assumes(j
!= NULL
)) {
5690 return BOOTSTRAP_NO_MEMORY
;
5693 if (getpid() != 1) {
5694 return BOOTSTRAP_NOT_PRIVILEGED
;
5697 runtime_get_caller_creds(&ldc
);
5700 return BOOTSTRAP_NOT_PRIVILEGED
;
5703 for (pid_to_log
= ldc
.pid
; pid_to_log
; pid_to_log
= kp
.kp_eproc
.e_ppid
) {
5704 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid_to_log
};
5705 size_t who_offset
, len
= sizeof(kp
);
5707 if (!job_assumes(j
, sysctl(mib
, 4, &kp
, &len
, NULL
, 0) != -1)) {
5711 who_offset
= strlen(who_started_the_reboot
);
5712 snprintf(who_started_the_reboot
+ who_offset
, sizeof(who_started_the_reboot
) - who_offset
,
5713 " %s[%u]%s", kp
.kp_proc
.p_comm
, pid_to_log
, kp
.kp_eproc
.e_ppid
? " ->" : "");
5716 root_jobmgr
->reboot_flags
= (int)flags
;
5720 job_log(j
, LOG_DEBUG
, "reboot2() initiated by:%s", who_started_the_reboot
);
5726 job_mig_getsocket(job_t j
, name_t spr
)
5728 if (!launchd_assumes(j
!= NULL
)) {
5729 return BOOTSTRAP_NO_MEMORY
;
5735 return BOOTSTRAP_NO_MEMORY
;
5738 strncpy(spr
, sockpath
, sizeof(name_t
));
5740 return BOOTSTRAP_SUCCESS
;
5744 job_mig_log(job_t j
, int pri
, int err
, logmsg_t msg
)
5746 if (!launchd_assumes(j
!= NULL
)) {
5747 return BOOTSTRAP_NO_MEMORY
;
5750 if ((errno
= err
)) {
5751 job_log_error(j
, pri
, "%s", msg
);
5753 job_log(j
, pri
, "%s", msg
);
5760 ensure_root_bkgd_setup(void)
5762 if (background_jobmgr
|| getpid() != 1) {
5766 if (!jobmgr_assumes(root_jobmgr
, (background_jobmgr
= jobmgr_new(root_jobmgr
, mach_task_self(), MACH_PORT_NULL
, false, VPROCMGR_SESSION_BACKGROUND
)) != NULL
)) {
5770 background_jobmgr
->req_port
= 0;
5771 jobmgr_assumes(root_jobmgr
, launchd_mport_make_send(background_jobmgr
->jm_port
) == KERN_SUCCESS
);
5775 job_mig_lookup_per_user_context(job_t j
, uid_t which_user
, mach_port_t
*up_cont
)
5780 #if TARGET_OS_EMBEDDED
5781 return BOOTSTRAP_NOT_PRIVILEGED
;
5784 if (!launchd_assumes(j
!= NULL
)) {
5785 return BOOTSTRAP_NO_MEMORY
;
5788 job_log(j
, LOG_DEBUG
, "Looking up per user launchd for UID: %u", which_user
);
5790 runtime_get_caller_creds(&ldc
);
5792 if (getpid() != 1) {
5793 job_log(j
, LOG_ERR
, "Only PID 1 supports per user launchd lookups.");
5794 return BOOTSTRAP_NOT_PRIVILEGED
;
5797 if (ldc
.euid
|| ldc
.uid
) {
5798 which_user
= ldc
.euid
? ldc
.euid
: ldc
.uid
;
5801 *up_cont
= MACH_PORT_NULL
;
5803 if (which_user
== 0) {
5804 ensure_root_bkgd_setup();
5806 *up_cont
= background_jobmgr
->jm_port
;
5811 LIST_FOREACH(ji
, &root_jobmgr
->jobs
, sle
) {
5812 if (!ji
->per_user
) {
5815 if (ji
->mach_uid
!= which_user
) {
5818 if (SLIST_EMPTY(&ji
->machservices
)) {
5821 if (!SLIST_FIRST(&ji
->machservices
)->per_user_hack
) {
5828 struct machservice
*ms
;
5831 job_log(j
, LOG_DEBUG
, "Creating per user launchd job for UID: %u", which_user
);
5833 sprintf(lbuf
, "com.apple.launchd.peruser.%u", which_user
);
5835 ji
= job_new(root_jobmgr
, lbuf
, "/sbin/launchd", NULL
);
5838 return BOOTSTRAP_NO_MEMORY
;
5841 ji
->mach_uid
= which_user
;
5842 ji
->per_user
= true;
5844 if ((ms
= machservice_new(ji
, lbuf
, up_cont
, false)) == NULL
) {
5846 return BOOTSTRAP_NO_MEMORY
;
5849 ms
->per_user_hack
= true;
5852 ji
= job_dispatch(ji
, false);
5854 job_log(j
, LOG_DEBUG
, "Per user launchd job found for UID: %u", which_user
);
5857 if (job_assumes(j
, ji
!= NULL
)) {
5858 *up_cont
= machservice_port(SLIST_FIRST(&ji
->machservices
));
5865 job_mig_check_in(job_t j
, name_t servicename
, mach_port_t
*serviceportp
)
5867 static pid_t last_warned_pid
= 0;
5868 struct machservice
*ms
;
5871 if (!launchd_assumes(j
!= NULL
)) {
5872 return BOOTSTRAP_NO_MEMORY
;
5875 runtime_get_caller_creds(&ldc
);
5877 ms
= jobmgr_lookup_service(j
->mgr
, servicename
, true, 0);
5880 job_log(j
, LOG_DEBUG
, "Check-in of Mach service failed. Unknown: %s", servicename
);
5881 return BOOTSTRAP_UNKNOWN_SERVICE
;
5883 if (machservice_job(ms
) != j
) {
5884 if (last_warned_pid
!= ldc
.pid
) {
5885 job_log(j
, LOG_NOTICE
, "Check-in of Mach service failed. PID %d is not privileged: %s",
5886 ldc
.pid
, servicename
);
5887 last_warned_pid
= ldc
.pid
;
5889 return BOOTSTRAP_NOT_PRIVILEGED
;
5891 if (machservice_active(ms
)) {
5892 job_log(j
, LOG_WARNING
, "Check-in of Mach service failed. Already active: %s", servicename
);
5893 return BOOTSTRAP_SERVICE_ACTIVE
;
5896 machservice_request_notifications(ms
);
5898 job_log(j
, LOG_INFO
, "Check-in of service: %s", servicename
);
5900 *serviceportp
= machservice_port(ms
);
5901 return BOOTSTRAP_SUCCESS
;
5905 job_mig_register2(job_t j
, name_t servicename
, mach_port_t serviceport
, uint64_t flags
)
5907 struct machservice
*ms
;
5910 if (!launchd_assumes(j
!= NULL
)) {
5911 return BOOTSTRAP_NO_MEMORY
;
5914 runtime_get_caller_creds(&ldc
);
5917 job_log(j
, LOG_APPLEONLY
, "bootstrap_register() is deprecated. Service: %s", servicename
);
5920 job_log(j
, LOG_DEBUG
, "%sMach service registration attempt: %s", flags
& BOOTSTRAP_PER_PID_SERVICE
? "Per PID " : "", servicename
);
5922 /* 5641783 for the embedded hack */
5923 #if !TARGET_OS_EMBEDDED
5925 * From a per-user/session launchd's perspective, SecurityAgent (UID
5926 * 92) is a rogue application (not our UID, not root and not a child of
5927 * us). We'll have to reconcile this design friction at a later date.
5929 if (j
->anonymous
&& job_get_bs(j
)->parentmgr
== NULL
&& ldc
.uid
!= 0 && ldc
.uid
!= getuid() && ldc
.uid
!= 92) {
5930 if (getpid() == 1) {
5931 return VPROC_ERR_TRY_PER_USER
;
5933 return BOOTSTRAP_NOT_PRIVILEGED
;
5938 ms
= jobmgr_lookup_service(j
->mgr
, servicename
, false, flags
& BOOTSTRAP_PER_PID_SERVICE
? ldc
.pid
: 0);
5941 if (machservice_job(ms
) != j
) {
5942 return BOOTSTRAP_NOT_PRIVILEGED
;
5944 if (machservice_active(ms
)) {
5945 job_log(j
, LOG_DEBUG
, "Mach service registration failed. Already active: %s", servicename
);
5946 return BOOTSTRAP_SERVICE_ACTIVE
;
5949 machservice_delete(j
, ms
, false);
5952 if (serviceport
!= MACH_PORT_NULL
) {
5953 if ((ms
= machservice_new(j
, servicename
, &serviceport
, flags
& BOOTSTRAP_PER_PID_SERVICE
? true : false))) {
5954 machservice_request_notifications(ms
);
5956 return BOOTSTRAP_NO_MEMORY
;
5960 return BOOTSTRAP_SUCCESS
;
5964 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
)
5966 struct machservice
*ms
;
5970 if (!launchd_assumes(j
!= NULL
)) {
5971 return BOOTSTRAP_NO_MEMORY
;
5974 runtime_get_caller_creds(&ldc
);
5976 /* 5641783 for the embedded hack */
5977 #if !TARGET_OS_EMBEDDED
5978 if (getpid() == 1 && j
->anonymous
&& job_get_bs(j
)->parentmgr
== NULL
&& ldc
.uid
!= 0 && ldc
.euid
!= 0) {
5979 return VPROC_ERR_TRY_PER_USER
;
5983 if (!mspolicy_check(j
, servicename
, flags
& BOOTSTRAP_PER_PID_SERVICE
)) {
5984 job_log(j
, LOG_NOTICE
, "Policy denied Mach service lookup: %s", servicename
);
5985 return BOOTSTRAP_NOT_PRIVILEGED
;
5988 if (flags
& BOOTSTRAP_PER_PID_SERVICE
) {
5989 ms
= jobmgr_lookup_service(j
->mgr
, servicename
, false, target_pid
);
5991 ms
= jobmgr_lookup_service(j
->mgr
, servicename
, true, 0);
5994 if (ms
&& machservice_hidden(ms
) && !machservice_active(ms
)) {
5996 } else if (ms
&& ms
->per_user_hack
) {
6001 launchd_assumes(machservice_port(ms
) != MACH_PORT_NULL
);
6002 job_log(j
, LOG_DEBUG
, "%sMach service lookup: %s", flags
& BOOTSTRAP_PER_PID_SERVICE
? "Per PID " : "", servicename
);
6004 /* After Leopard ships, we should enable this */
6005 if (j
->lastlookup
== ms
&& j
->lastlookup_gennum
== ms
->gen_num
&& !j
->per_user
) {
6007 job_log(j
, LOG_APPLEONLY
, "Performance opportunity: Number of bootstrap_lookup(... \"%s\" ...) calls that should have been cached: %llu",
6008 servicename
, ms
->bad_perf_cnt
);
6011 j
->lastlookup_gennum
= ms
->gen_num
;
6013 *serviceportp
= machservice_port(ms
);
6014 *ptype
= MACH_MSG_TYPE_COPY_SEND
;
6015 kr
= BOOTSTRAP_SUCCESS
;
6016 } else if (!(flags
& BOOTSTRAP_PER_PID_SERVICE
) && (inherited_bootstrap_port
!= MACH_PORT_NULL
)) {
6017 job_log(j
, LOG_DEBUG
, "Mach service lookup forwarded: %s", servicename
);
6018 *ptype
= MACH_MSG_TYPE_MOVE_SEND
;
6019 kr
= bootstrap_look_up(inherited_bootstrap_port
, servicename
, serviceportp
);
6020 } else if (getpid() == 1 && j
->anonymous
&& ldc
.euid
>= 500 && strcasecmp(job_get_bs(j
)->name
, VPROCMGR_SESSION_LOGINWINDOW
) == 0) {
6022 * 5240036 Should start background session when a lookup of CCacheServer occurs
6024 * This is a total hack. We sniff out loginwindow session, and attempt to guess what it is up to.
6025 * If we find a EUID that isn't root, we force it over to the per-user context.
6027 return VPROC_ERR_TRY_PER_USER
;
6029 job_log(j
, LOG_DEBUG
, "%sMach service lookup failed: %s", flags
& BOOTSTRAP_PER_PID_SERVICE
? "Per PID " : "", servicename
);
6030 kr
= BOOTSTRAP_UNKNOWN_SERVICE
;
6037 job_mig_parent(job_t j
, mach_port_t
*parentport
, mach_msg_type_name_t
*pptype
)
6039 if (!launchd_assumes(j
!= NULL
)) {
6040 return BOOTSTRAP_NO_MEMORY
;
6043 job_log(j
, LOG_DEBUG
, "Requested parent bootstrap port");
6044 jobmgr_t jm
= j
->mgr
;
6046 *pptype
= MACH_MSG_TYPE_MAKE_SEND
;
6048 if (jobmgr_parent(jm
)) {
6049 *parentport
= jobmgr_parent(jm
)->jm_port
;
6050 } else if (MACH_PORT_NULL
== inherited_bootstrap_port
) {
6051 *parentport
= jm
->jm_port
;
6053 *pptype
= MACH_MSG_TYPE_COPY_SEND
;
6054 *parentport
= inherited_bootstrap_port
;
6056 return BOOTSTRAP_SUCCESS
;
6060 job_mig_info(job_t j
, name_array_t
*servicenamesp
, unsigned int *servicenames_cnt
,
6061 bootstrap_status_array_t
*serviceactivesp
, unsigned int *serviceactives_cnt
)
6063 name_array_t service_names
= NULL
;
6064 bootstrap_status_array_t service_actives
= NULL
;
6065 unsigned int cnt
= 0, cnt2
= 0;
6066 struct machservice
*ms
;
6070 #if TARGET_OS_EMBEDDED
6071 return BOOTSTRAP_NOT_PRIVILEGED
;
6074 if (!launchd_assumes(j
!= NULL
)) {
6075 return BOOTSTRAP_NO_MEMORY
;
6080 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
6081 SLIST_FOREACH(ms
, &ji
->machservices
, sle
) {
6092 mig_allocate((vm_address_t
*)&service_names
, cnt
* sizeof(service_names
[0]));
6093 if (!launchd_assumes(service_names
!= NULL
)) {
6097 mig_allocate((vm_address_t
*)&service_actives
, cnt
* sizeof(service_actives
[0]));
6098 if (!launchd_assumes(service_actives
!= NULL
)) {
6102 LIST_FOREACH(ji
, &jm
->jobs
, sle
) {
6103 SLIST_FOREACH(ms
, &ji
->machservices
, sle
) {
6105 strlcpy(service_names
[cnt2
], machservice_name(ms
), sizeof(service_names
[0]));
6106 service_actives
[cnt2
] = machservice_status(ms
);
6112 launchd_assumes(cnt
== cnt2
);
6115 *servicenamesp
= service_names
;
6116 *serviceactivesp
= service_actives
;
6117 *servicenames_cnt
= *serviceactives_cnt
= cnt
;
6119 return BOOTSTRAP_SUCCESS
;
6122 if (service_names
) {
6123 mig_deallocate((vm_address_t
)service_names
, cnt
* sizeof(service_names
[0]));
6125 if (service_actives
) {
6126 mig_deallocate((vm_address_t
)service_actives
, cnt
* sizeof(service_actives
[0]));
6129 return BOOTSTRAP_NO_MEMORY
;
6133 job_reparent_hack(job_t j
, const char *where
)
6137 ensure_root_bkgd_setup();
6139 /* NULL is only passed for our custom API for LaunchServices. If that is the case, we do magic. */
6140 if (where
== NULL
) {
6141 if (strcasecmp(j
->mgr
->name
, VPROCMGR_SESSION_LOGINWINDOW
) == 0) {
6142 where
= VPROCMGR_SESSION_LOGINWINDOW
;
6144 where
= VPROCMGR_SESSION_AQUA
;
6148 if (strcasecmp(j
->mgr
->name
, where
) == 0) {
6152 SLIST_FOREACH(jmi
, &root_jobmgr
->submgrs
, sle
) {
6153 if (jmi
->shutting_down
) {
6155 } else if (strcasecmp(jmi
->name
, where
) == 0) {
6157 } else if (strcasecmp(jmi
->name
, VPROCMGR_SESSION_BACKGROUND
) == 0 && getpid() == 1) {
6158 SLIST_FOREACH(jmi2
, &jmi
->submgrs
, sle
) {
6159 if (strcasecmp(jmi2
->name
, where
) == 0) {
6168 if (job_assumes(j
, jmi
!= NULL
)) {
6169 struct machservice
*msi
;
6171 SLIST_FOREACH(msi
, &j
->machservices
, sle
) {
6172 LIST_REMOVE(msi
, name_hash_sle
);
6175 LIST_REMOVE(j
, sle
);
6176 LIST_INSERT_HEAD(&jmi
->jobs
, j
, sle
);
6179 SLIST_FOREACH(msi
, &j
->machservices
, sle
) {
6180 LIST_INSERT_HEAD(&j
->mgr
->ms_hash
[hash_ms(msi
->name
)], msi
, name_hash_sle
);
6186 job_mig_move_subset(job_t j
, mach_port_t target_subset
, name_t session_type
)
6188 mach_msg_type_number_t l2l_i
, l2l_port_cnt
= 0;
6189 mach_port_array_t l2l_ports
= NULL
;
6190 mach_port_t reqport
, rcvright
;
6191 kern_return_t kr
= 1;
6192 launch_data_t out_obj_array
= NULL
;
6194 jobmgr_t jmr
= NULL
;
6196 #if TARGET_OS_EMBEDDED
6197 return BOOTSTRAP_NOT_PRIVILEGED
;
6200 if (!launchd_assumes(j
!= NULL
)) {
6201 return BOOTSTRAP_NO_MEMORY
;
6204 runtime_get_caller_creds(&ldc
);
6206 if (target_subset
== MACH_PORT_NULL
) {
6209 if (j
->mgr
->session_initialized
) {
6210 if (ldc
.uid
== 0 && getpid() == 1) {
6211 if (strcmp(j
->mgr
->name
, VPROCMGR_SESSION_LOGINWINDOW
) == 0) {
6214 LIST_FOREACH_SAFE(ji
, &j
->mgr
->jobs
, sle
, jn
) {
6215 if (!ji
->anonymous
) {
6220 ensure_root_bkgd_setup();
6222 SLIST_REMOVE(&j
->mgr
->parentmgr
->submgrs
, j
->mgr
, jobmgr_s
, sle
);
6223 j
->mgr
->parentmgr
= background_jobmgr
;
6224 SLIST_INSERT_HEAD(&j
->mgr
->parentmgr
->submgrs
, j
->mgr
, sle
);
6227 * We really should wait for all the jobs to die before proceeding. See 5351245 for more info.
6229 * We have hacked around this in job_find() by ignoring jobs that are pending removal.
6232 } else if (strcmp(j
->mgr
->name
, VPROCMGR_SESSION_AQUA
) == 0) {
6233 job_log(j
, LOG_DEBUG
, "Tried to move the Aqua session.");
6235 } else if (strcmp(j
->mgr
->name
, VPROCMGR_SESSION_BACKGROUND
) == 0) {
6236 job_log(j
, LOG_DEBUG
, "Tried to move the background session.");
6239 job_log(j
, LOG_ERR
, "Tried to initialize an already setup session!");
6240 kr
= BOOTSTRAP_NOT_PRIVILEGED
;
6244 job_log(j
, LOG_ERR
, "Tried to initialize an already setup session!");
6245 kr
= BOOTSTRAP_NOT_PRIVILEGED
;
6248 } else if (ldc
.uid
== 0 && getpid() == 1 && strcmp(session_type
, VPROCMGR_SESSION_STANDARDIO
) == 0) {
6249 ensure_root_bkgd_setup();
6251 SLIST_REMOVE(&j
->mgr
->parentmgr
->submgrs
, j
->mgr
, jobmgr_s
, sle
);
6252 j
->mgr
->parentmgr
= background_jobmgr
;
6253 SLIST_INSERT_HEAD(&j
->mgr
->parentmgr
->submgrs
, j
->mgr
, sle
);
6254 } else if (strcmp(session_type
, VPROCMGR_SESSION_LOGINWINDOW
) == 0) {
6260 * We're working around LoginWindow and the WindowServer.
6262 * In practice, there is only one LoginWindow session. Unfortunately, for certain
6263 * scenarios, the WindowServer spawns loginwindow, and in those cases, it frequently
6264 * spawns a replacement loginwindow session before cleaning up the previous one.
6266 * We're going to use the creation of a new LoginWindow context as a clue that the
6267 * previous LoginWindow context is on the way out and therefore we should just
6268 * kick-start the shutdown of it.
6271 SLIST_FOREACH(jmi
, &root_jobmgr
->submgrs
, sle
) {
6272 if (jmi
->shutting_down
) {
6274 } else if (strcasecmp(jmi
->name
, session_type
) == 0) {
6275 jobmgr_shutdown(jmi
);
6281 jobmgr_log(j
->mgr
, LOG_DEBUG
, "Renaming to: %s", session_type
);
6282 strcpy(j
->mgr
->name_init
, session_type
);
6284 if (job_assumes(j
, (j2
= jobmgr_init_session(j
->mgr
, session_type
, false)))) {
6285 job_assumes(j
, job_dispatch(j2
, true));
6290 } else if (job_mig_intran2(root_jobmgr
, target_subset
, ldc
.pid
)) {
6291 job_log(j
, LOG_ERR
, "Moving a session to ourself is bogus.");
6293 kr
= BOOTSTRAP_NOT_PRIVILEGED
;
6297 job_log(j
, LOG_DEBUG
, "Move subset attempt: 0x%x", target_subset
);
6299 errno
= kr
= _vproc_grab_subset(target_subset
, &reqport
, &rcvright
, &out_obj_array
, &l2l_ports
, &l2l_port_cnt
);
6301 if (!job_assumes(j
, kr
== 0)) {
6305 launchd_assert(launch_data_array_get_count(out_obj_array
) == l2l_port_cnt
);
6307 if (!job_assumes(j
, (jmr
= jobmgr_new(j
->mgr
, reqport
, rcvright
, false, session_type
)) != NULL
)) {
6308 kr
= BOOTSTRAP_NO_MEMORY
;
6312 for (l2l_i
= 0; l2l_i
< l2l_port_cnt
; l2l_i
++) {
6313 launch_data_t tmp
, obj_at_idx
;
6314 struct machservice
*ms
;
6315 job_t j_for_service
;
6316 const char *serv_name
;
6320 job_assumes(j
, obj_at_idx
= launch_data_array_get_index(out_obj_array
, l2l_i
));
6321 job_assumes(j
, tmp
= launch_data_dict_lookup(obj_at_idx
, TAKE_SUBSET_PID
));
6322 target_pid
= (pid_t
)launch_data_get_integer(tmp
);
6323 job_assumes(j
, tmp
= launch_data_dict_lookup(obj_at_idx
, TAKE_SUBSET_PERPID
));
6324 serv_perpid
= launch_data_get_bool(tmp
);
6325 job_assumes(j
, tmp
= launch_data_dict_lookup(obj_at_idx
, TAKE_SUBSET_NAME
));
6326 serv_name
= launch_data_get_string(tmp
);
6328 j_for_service
= jobmgr_find_by_pid(jmr
, target_pid
, true);
6330 if (!j_for_service
) {
6331 /* The PID probably exited */
6332 job_assumes(j
, launchd_mport_deallocate(l2l_ports
[l2l_i
]) == KERN_SUCCESS
);
6336 if ((ms
= machservice_new(j_for_service
, serv_name
, &l2l_ports
[l2l_i
], serv_perpid
))) {
6337 machservice_request_notifications(ms
);
6344 if (out_obj_array
) {
6345 launch_data_free(out_obj_array
);
6349 mig_deallocate((vm_address_t
)l2l_ports
, l2l_port_cnt
* sizeof(l2l_ports
[0]));
6353 if (target_subset
) {
6354 job_assumes(j
, launchd_mport_deallocate(target_subset
) == KERN_SUCCESS
);
6357 jobmgr_shutdown(jmr
);
6364 job_mig_take_subset(job_t j
, mach_port_t
*reqport
, mach_port_t
*rcvright
,
6365 vm_offset_t
*outdata
, mach_msg_type_number_t
*outdataCnt
,
6366 mach_port_array_t
*portsp
, unsigned int *ports_cnt
)
6368 launch_data_t tmp_obj
, tmp_dict
, outdata_obj_array
= NULL
;
6369 mach_port_array_t ports
= NULL
;
6370 unsigned int cnt
= 0, cnt2
= 0;
6372 struct machservice
*ms
;
6376 #if TARGET_OS_EMBEDDED
6377 return BOOTSTRAP_NOT_PRIVILEGED
;
6380 if (!launchd_assumes(j
!= NULL
)) {
6381 return BOOTSTRAP_NO_MEMORY
;
6386 if (getpid() != 1) {
6387 job_log(j
, LOG_ERR
, "Only the system launchd will transfer Mach sub-bootstraps.");
6388 return BOOTSTRAP_NOT_PRIVILEGED
;
6389 } else if (jobmgr_parent(jm
) == NULL
) {
6390 job_log(j
, LOG_ERR
, "Root Mach bootstrap cannot be transferred.");
6391 return BOOTSTRAP_NOT_PRIVILEGED
;
6392 } else if (strcasecmp(jm
->name
, VPROCMGR_SESSION_AQUA
) == 0) {
6393 job_log(j
, LOG_ERR
, "Cannot transfer a setup GUI session.");
6394 return BOOTSTRAP_NOT_PRIVILEGED
;
6395 } else if (!j
->anonymous
) {
6396 job_log(j
, LOG_ERR
, "Only the anonymous job can transfer Mach sub-bootstraps.");
6397 return BOOTSTRAP_NOT_PRIVILEGED
;
6400 job_log(j
, LOG_DEBUG
, "Transferring sub-bootstrap to the per session launchd.");
6402 outdata_obj_array
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
6403 if (!job_assumes(j
, outdata_obj_array
)) {
6407 *outdataCnt
= 20 * 1024 * 1024;
6408 mig_allocate(outdata
, *outdataCnt
);
6409 if (!job_assumes(j
, *outdata
!= 0)) {
6413 LIST_FOREACH(ji
, &j
->mgr
->jobs
, sle
) {
6414 if (!ji
->anonymous
) {
6417 SLIST_FOREACH(ms
, &ji
->machservices
, sle
) {
6422 mig_allocate((vm_address_t
*)&ports
, cnt
* sizeof(ports
[0]));
6423 if (!launchd_assumes(ports
!= NULL
)) {
6427 LIST_FOREACH(ji
, &j
->mgr
->jobs
, sle
) {
6428 if (!ji
->anonymous
) {
6432 SLIST_FOREACH(ms
, &ji
->machservices
, sle
) {
6433 if (job_assumes(j
, (tmp_dict
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
)))) {
6434 job_assumes(j
, launch_data_array_set_index(outdata_obj_array
, tmp_dict
, cnt2
));
6439 if (job_assumes(j
, (tmp_obj
= launch_data_new_string(machservice_name(ms
))))) {
6440 job_assumes(j
, launch_data_dict_insert(tmp_dict
, tmp_obj
, TAKE_SUBSET_NAME
));
6445 if (job_assumes(j
, (tmp_obj
= launch_data_new_integer((ms
->job
->p
))))) {
6446 job_assumes(j
, launch_data_dict_insert(tmp_dict
, tmp_obj
, TAKE_SUBSET_PID
));
6451 if (job_assumes(j
, (tmp_obj
= launch_data_new_bool((ms
->per_pid
))))) {
6452 job_assumes(j
, launch_data_dict_insert(tmp_dict
, tmp_obj
, TAKE_SUBSET_PERPID
));
6457 ports
[cnt2
] = machservice_port(ms
);
6459 /* Increment the send right by one so we can shutdown the jobmgr cleanly */
6460 jobmgr_assumes(jm
, (errno
= mach_port_mod_refs(mach_task_self(), ports
[cnt2
], MACH_PORT_RIGHT_SEND
, 1)) == 0);
6465 launchd_assumes(cnt
== cnt2
);
6467 packed_size
= launch_data_pack(outdata_obj_array
, (void *)*outdata
, *outdataCnt
, NULL
, NULL
);
6468 if (!job_assumes(j
, packed_size
!= 0)) {
6472 launch_data_free(outdata_obj_array
);
6477 *reqport
= jm
->req_port
;
6478 *rcvright
= jm
->jm_port
;
6483 workaround_5477111
= j
;
6485 jobmgr_shutdown(jm
);
6487 return BOOTSTRAP_SUCCESS
;
6490 if (outdata_obj_array
) {
6491 launch_data_free(outdata_obj_array
);
6494 mig_deallocate(*outdata
, *outdataCnt
);
6497 mig_deallocate((vm_address_t
)ports
, cnt
* sizeof(ports
[0]));
6500 return BOOTSTRAP_NO_MEMORY
;
6504 job_mig_subset(job_t j
, mach_port_t requestorport
, mach_port_t
*subsetportp
)
6509 if (!launchd_assumes(j
!= NULL
)) {
6510 return BOOTSTRAP_NO_MEMORY
;
6515 while ((jmr
= jobmgr_parent(jmr
)) != NULL
) {
6519 /* Since we use recursion, we need an artificial depth for subsets */
6520 if (bsdepth
> 100) {
6521 job_log(j
, LOG_ERR
, "Mach sub-bootstrap create request failed. Depth greater than: %d", bsdepth
);
6522 return BOOTSTRAP_NO_MEMORY
;
6525 if ((jmr
= jobmgr_new(j
->mgr
, requestorport
, MACH_PORT_NULL
, false, NULL
)) == NULL
) {
6526 if (requestorport
== MACH_PORT_NULL
) {
6527 return BOOTSTRAP_NOT_PRIVILEGED
;
6529 return BOOTSTRAP_NO_MEMORY
;
6532 *subsetportp
= jmr
->jm_port
;
6533 return BOOTSTRAP_SUCCESS
;
6537 job_mig_create_service(job_t j
, name_t servicename
, mach_port_t
*serviceportp
)
6539 struct machservice
*ms
;
6541 if (!launchd_assumes(j
!= NULL
)) {
6542 return BOOTSTRAP_NO_MEMORY
;
6545 if (job_prog(j
)[0] == '\0') {
6546 job_log(j
, LOG_ERR
, "Mach service creation requires a target server: %s", servicename
);
6547 return BOOTSTRAP_NOT_PRIVILEGED
;
6550 if (!j
->legacy_mach_job
) {
6551 job_log(j
, LOG_ERR
, "bootstrap_create_service() is only allowed against legacy Mach jobs: %s", servicename
);
6552 return BOOTSTRAP_NOT_PRIVILEGED
;
6555 ms
= jobmgr_lookup_service(j
->mgr
, servicename
, false, 0);
6557 job_log(j
, LOG_DEBUG
, "Mach service creation attempt for failed. Already exists: %s", servicename
);
6558 return BOOTSTRAP_NAME_IN_USE
;
6563 *serviceportp
= MACH_PORT_NULL
;
6564 ms
= machservice_new(j
, servicename
, serviceportp
, false);
6566 if (!launchd_assumes(ms
!= NULL
)) {
6570 return BOOTSTRAP_SUCCESS
;
6573 launchd_assumes(launchd_mport_close_recv(*serviceportp
) == KERN_SUCCESS
);
6574 return BOOTSTRAP_NO_MEMORY
;
6578 job_mig_embedded_wait(job_t j
, name_t targetlabel
, integer_t
*waitstatus
)
6582 if (!launchd_assumes(j
!= NULL
)) {
6583 return BOOTSTRAP_NO_MEMORY
;
6586 if (unlikely(!(otherj
= job_find(targetlabel
)))) {
6587 return BOOTSTRAP_UNKNOWN_SERVICE
;
6590 *waitstatus
= j
->last_exit_status
;
6596 job_mig_embedded_kickstart(job_t j
, name_t targetlabel
, pid_t
*out_pid
, mach_port_t
*out_name_port
)
6602 if (!launchd_assumes(j
!= NULL
)) {
6603 return BOOTSTRAP_NO_MEMORY
;
6606 if (unlikely(!(otherj
= job_find(targetlabel
)))) {
6607 return BOOTSTRAP_UNKNOWN_SERVICE
;
6610 runtime_get_caller_creds(&ldc
);
6612 if (ldc
.euid
!= 0 && ldc
.euid
!= geteuid()
6613 #if TARGET_OS_EMBEDDED
6614 && j
->username
&& otherj
->username
6615 && strcmp(j
->username
, otherj
->username
) != 0
6618 return BOOTSTRAP_NOT_PRIVILEGED
;
6621 otherj
= job_dispatch(otherj
, true);
6623 if (!job_assumes(j
, otherj
&& otherj
->p
)) {
6624 return BOOTSTRAP_NO_MEMORY
;
6627 kr
= task_name_for_pid(mach_task_self(), otherj
->p
, out_name_port
);
6628 if (!job_assumes(j
, kr
== 0)) {
6632 *out_pid
= otherj
->p
;
6638 job_mig_wait(job_t j
, mach_port_t srp
, integer_t
*waitstatus
)
6640 if (!launchd_assumes(j
!= NULL
)) {
6641 return BOOTSTRAP_NO_MEMORY
;
6645 runtime_get_caller_creds(&ldc
);
6647 return job_handle_mpm_wait(j
, srp
, waitstatus
);
6651 job_mig_uncork_fork(job_t j
)
6653 if (!launchd_assumes(j
!= NULL
)) {
6654 return BOOTSTRAP_NO_MEMORY
;
6657 if (!j
->stall_before_exec
) {
6658 job_log(j
, LOG_WARNING
, "Attempt to uncork a job that isn't in the middle of a fork().");
6663 j
->stall_before_exec
= false;
6668 job_mig_set_service_policy(job_t j
, pid_t target_pid
, uint64_t flags
, name_t target_service
)
6673 if (!launchd_assumes(j
!= NULL
)) {
6674 return BOOTSTRAP_NO_MEMORY
;
6677 runtime_get_caller_creds(&ldc
);
6679 #if TARGET_OS_EMBEDDED
6681 return BOOTSTRAP_NOT_PRIVILEGED
;
6684 if( ldc
.euid
&& (ldc
.euid
!= getuid()) ) {
6685 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, target_pid
};
6686 struct kinfo_proc kp
;
6687 size_t len
= sizeof(kp
);
6689 job_assumes(j
, sysctl(mib
, 4, &kp
, &len
, NULL
, 0) != -1);
6690 job_assumes(j
, len
== sizeof(kp
));
6692 uid_t kp_euid
= kp
.kp_eproc
.e_ucred
.cr_uid
;
6693 uid_t kp_uid
= kp
.kp_eproc
.e_pcred
.p_ruid
;
6695 if( ldc
.euid
== kp_euid
) {
6696 job_log(j
, LOG_DEBUG
, "Working around rdar://problem/5982485 and allowing job to set policy for PID %u.", target_pid
);
6698 job_log(j
, LOG_ERR
, "Denied Mach service policy update requested by UID/EUID %u/%u against PID %u with UID/EUID %u/%u due to mismatched credentials.", ldc
.uid
, ldc
.euid
, target_pid
, kp_uid
, kp_euid
);
6700 return BOOTSTRAP_NOT_PRIVILEGED
;
6705 if (!job_assumes(j
, (target_j
= jobmgr_find_by_pid(j
->mgr
, target_pid
, true)) != NULL
)) {
6706 return BOOTSTRAP_NO_MEMORY
;
6709 if (SLIST_EMPTY(&j
->mspolicies
)) {
6710 job_log(j
, LOG_DEBUG
, "Setting policy on job \"%s\" for Mach service: %s", target_j
->label
, target_service
);
6711 if (target_service
[0]) {
6712 job_assumes(j
, mspolicy_new(target_j
, target_service
, flags
& BOOTSTRAP_ALLOW_LOOKUP
, flags
& BOOTSTRAP_PER_PID_SERVICE
, false));
6714 target_j
->deny_unknown_mslookups
= !(flags
& BOOTSTRAP_ALLOW_LOOKUP
);
6715 target_j
->deny_job_creation
= (bool)(flags
& BOOTSTRAP_DENY_JOB_CREATION
);
6718 job_log(j
, LOG_WARNING
, "Jobs that have policies assigned to them may not set policies.");
6719 return BOOTSTRAP_NOT_PRIVILEGED
;
6726 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
)
6728 launch_data_t input_obj
= NULL
;
6729 size_t data_offset
= 0;
6733 #if TARGET_OS_EMBEDDED
6734 return BOOTSTRAP_NOT_PRIVILEGED
;
6737 runtime_get_caller_creds(&ldc
);
6739 if (!launchd_assumes(j
!= NULL
)) {
6740 return BOOTSTRAP_NO_MEMORY
;
6743 if (unlikely(j
->deny_job_creation
)) {
6744 return BOOTSTRAP_NOT_PRIVILEGED
;
6747 if (getpid() == 1 && ldc
.euid
&& ldc
.uid
) {
6748 job_log(j
, LOG_DEBUG
, "Punting spawn to per-user-context");
6749 return VPROC_ERR_TRY_PER_USER
;
6752 if (!job_assumes(j
, indataCnt
!= 0)) {
6756 if (!job_assumes(j
, (input_obj
= launch_data_unpack((void *)indata
, indataCnt
, NULL
, 0, &data_offset
, NULL
)) != NULL
)) {
6760 jr
= jobmgr_import2(j
->mgr
, input_obj
);
6762 if (!job_assumes(j
, jr
!= NULL
)) {
6765 return BOOTSTRAP_NAME_IN_USE
;
6767 return BOOTSTRAP_NO_MEMORY
;
6771 job_reparent_hack(jr
, NULL
);
6773 if (getpid() == 1) {
6774 jr
->mach_uid
= ldc
.uid
;
6777 jr
->unload_at_exit
= true;
6778 jr
->wait4pipe_eof
= true;
6779 jr
->abandon_pg
= true;
6780 jr
->stall_before_exec
= jr
->wait4debugger
;
6781 jr
->wait4debugger
= false;
6783 jr
= job_dispatch(jr
, true);
6785 if (!job_assumes(j
, jr
!= NULL
)) {
6786 return BOOTSTRAP_NO_MEMORY
;
6789 if (!job_assumes(jr
, jr
->p
)) {
6791 return BOOTSTRAP_NO_MEMORY
;
6794 if (!job_setup_machport(jr
)) {
6796 return BOOTSTRAP_NO_MEMORY
;
6799 job_log(jr
, LOG_DEBUG
, "Spawned by PID %u: %s", j
->p
, j
->label
);
6802 *obsvr_port
= jr
->j_port
;
6804 mig_deallocate(indata
, indataCnt
);
6806 return BOOTSTRAP_SUCCESS
;
6810 jobmgr_init(bool sflag
)
6812 const char *root_session_type
= getpid() == 1 ? VPROCMGR_SESSION_SYSTEM
: VPROCMGR_SESSION_BACKGROUND
;
6814 launchd_assert((root_jobmgr
= jobmgr_new(NULL
, MACH_PORT_NULL
, MACH_PORT_NULL
, sflag
, root_session_type
)) != NULL
);
6818 our_strhash(const char *s
)
6823 * This algorithm was first reported by Dan Bernstein many years ago in comp.lang.c
6826 while ((c
= *s
++)) {
6827 r
= ((r
<< 5) + r
) + c
; /* hash*33 + c */
6834 hash_label(const char *label
)
6836 return our_strhash(label
) % LABEL_HASH_SIZE
;
6840 hash_ms(const char *msstr
)
6842 return our_strhash(msstr
) % MACHSERVICE_HASH_SIZE
;
6846 mspolicy_copy(job_t j_to
, job_t j_from
)
6848 struct mspolicy
*msp
;
6850 SLIST_FOREACH(msp
, &j_from
->mspolicies
, sle
) {
6851 if (!mspolicy_new(j_to
, msp
->name
, msp
->allow
, msp
->per_pid
, true)) {
6860 mspolicy_new(job_t j
, const char *name
, bool allow
, bool pid_local
, bool skip_check
)
6862 struct mspolicy
*msp
;
6864 if (!skip_check
) SLIST_FOREACH(msp
, &j
->mspolicies
, sle
) {
6865 if (msp
->per_pid
!= pid_local
) {
6867 } else if (strcmp(msp
->name
, name
) == 0) {
6872 if ((msp
= calloc(1, sizeof(struct mspolicy
) + strlen(name
) + 1)) == NULL
) {
6876 strcpy((char *)msp
->name
, name
);
6877 msp
->per_pid
= pid_local
;
6880 SLIST_INSERT_HEAD(&j
->mspolicies
, msp
, sle
);
6886 mspolicy_setup(launch_data_t obj
, const char *key
, void *context
)
6890 if (launch_data_get_type(obj
) != LAUNCH_DATA_BOOL
) {
6891 job_log(j
, LOG_WARNING
, "Invalid object type for Mach service policy key: %s", key
);
6895 job_assumes(j
, mspolicy_new(j
, key
, launch_data_get_bool(obj
), false, false));
6899 mspolicy_check(job_t j
, const char *name
, bool pid_local
)
6901 struct mspolicy
*mspi
;
6903 SLIST_FOREACH(mspi
, &j
->mspolicies
, sle
) {
6904 if (mspi
->per_pid
!= pid_local
) {
6906 } else if (strcmp(mspi
->name
, name
) != 0) {
6912 return !j
->deny_unknown_mslookups
;
6916 mspolicy_delete(job_t j
, struct mspolicy
*msp
)
6918 SLIST_REMOVE(&j
->mspolicies
, msp
, mspolicy
, sle
);
6924 waiting4removal_new(job_t j
, mach_port_t rp
)
6926 struct waiting_for_removal
*w4r
;
6928 if (!job_assumes(j
, (w4r
= malloc(sizeof(struct waiting_for_removal
))) != NULL
)) {
6932 w4r
->reply_port
= rp
;
6934 SLIST_INSERT_HEAD(&j
->removal_watchers
, w4r
, sle
);
6940 waiting4removal_delete(job_t j
, struct waiting_for_removal
*w4r
)
6942 job_assumes(j
, job_mig_send_signal_reply(w4r
->reply_port
, 0) == 0);
6944 SLIST_REMOVE(&j
->removal_watchers
, w4r
, waiting_for_removal
, sle
);
6950 get_kern_max_proc(void)
6952 int mib
[] = { CTL_KERN
, KERN_MAXPROC
};
6954 size_t max_sz
= sizeof(max
);
6956 launchd_assumes(sysctl(mib
, 2, &max
, &max_sz
, NULL
, 0) != -1);
6966 launchd_assert(mach_timebase_info(&tbi
) == 0);
6968 if (stat("/AppleInternal", &sb
) == 0) {
6969 do_apple_internal_magic
= true;