]> git.saurik.com Git - apple/launchd.git/blame - launchd/src/libvproc.c
launchd-392.18.tar.gz
[apple/launchd.git] / launchd / src / libvproc.c
CommitLineData
5b0a4722
A
1/*
2 * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21#include "config.h"
ef398931
A
22#include "vproc.h"
23#include "vproc_priv.h"
24#include "vproc_internal.h"
5b0a4722
A
25
26#include <mach/mach.h>
27#include <mach/vm_map.h>
28#include <sys/param.h>
29#include <stdlib.h>
30#include <stdio.h>
31#include <errno.h>
32#include <unistd.h>
33#include <syslog.h>
34#include <pthread.h>
ddbbfbc1
A
35#include <signal.h>
36#include <assert.h>
37#include <libkern/OSAtomic.h>
38#include <sys/syscall.h>
dcace88f 39#include <sys/event.h>
ddbbfbc1 40
f36da725
A
41#if HAVE_QUARANTINE
42#include <quarantine.h>
43#endif
5b0a4722 44
ef398931
A
45#include "launch.h"
46#include "launch_priv.h"
47#include "launch_internal.h"
ddbbfbc1 48#include "launchd_ktrace.h"
5b0a4722
A
49
50#include "protocol_vproc.h"
51
dcace88f
A
52#include "launchd_helper.h"
53#include "launchd_helperServer.h"
54
5b0a4722
A
55#include "reboot2.h"
56
ddbbfbc1
A
57#define likely(x) __builtin_expect((bool)(x), true)
58#define unlikely(x) __builtin_expect((bool)(x), false)
59
5b0a4722
A
60static mach_port_t get_root_bootstrap_port(void);
61
dcace88f 62 #define _vproc_set_crash_log_message(x)
ddbbfbc1 63
5b0a4722 64static int64_t cached_pid = -1;
ddbbfbc1
A
65static struct vproc_shmem_s *vproc_shmem;
66static pthread_once_t shmem_inited = PTHREAD_ONCE_INIT;
67static uint64_t s_cached_transactions_enabled = 0;
68
dcace88f
A
69static _vproc_transaction_callout vproc_gone2zero;
70static _vproc_transaction_callout vproc_gonenonzero;
71
72static vproc_helper_recv_ping_t vprocmgr_helper_callout;
73
ddbbfbc1
A
74struct vproc_s {
75 int32_t refcount;
76 mach_port_t j_port;
77};
78
dcace88f
A
79/* These functions are a total nightmare to get to through headers.
80 * See rdar://problem/8223092.
81 */
82typedef __darwin_mach_port_t fileport_t;
83#define FILEPORT_NULL ((fileport_t)0)
84extern int fileport_makeport(int, fileport_t *);
85extern int fileport_makefd(fileport_t);
86
ddbbfbc1
A
87vproc_t vprocmgr_lookup_vproc(const char *label)
88{
89 struct vproc_s *vp = NULL;
90
91 mach_port_t mp = MACH_PORT_NULL;
92 kern_return_t kr = vproc_mig_port_for_label(bootstrap_port, (char *)label, &mp);
dcace88f 93 if (kr == BOOTSTRAP_SUCCESS) {
ddbbfbc1 94 vp = (struct vproc_s *)calloc(1, sizeof(struct vproc_s));
dcace88f 95 if (vp) {
ddbbfbc1
A
96 vp->refcount = 1;
97 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1);
98 vp->j_port = mp;
99 }
dcace88f 100 (void)mach_port_deallocate(mach_task_self(), mp);
ddbbfbc1
A
101 }
102
103 return vp;
104}
105
106vproc_t vproc_retain(vproc_t vp)
107{
108 int32_t orig = OSAtomicAdd32(1, &vp->refcount) - 1;
dcace88f 109 if (orig <= 0) {
ddbbfbc1 110 /* We've gone from 0 to 1, which means that this object was due to be freed. */
dcace88f 111 _vproc_set_crash_log_message("Under-retain / over-release of vproc_t.");
ddbbfbc1
A
112 abort();
113 }
114
115 return vp;
116}
117
118void vproc_release(vproc_t vp)
119{
120 int32_t newval = OSAtomicAdd32(-1, &vp->refcount);
dcace88f 121 if (newval < 0) {
ddbbfbc1 122 /* We're in negative numbers, which is bad. */
dcace88f 123 _vproc_set_crash_log_message("Over-release of vproc_t.");
ddbbfbc1 124 abort();
dcace88f 125 } else if (newval == 0) {
ddbbfbc1
A
126 mach_port_deallocate(mach_task_self(), vp->j_port);
127 free(vp);
128 }
129}
130
131static void
132vproc_shmem_init(void)
133{
134 vm_address_t vm_addr = 0;
135 mach_port_t shmem_port;
136 kern_return_t kr;
137
138 kr = vproc_mig_setup_shmem(bootstrap_port, &shmem_port);
139
140 //assert(kr == 0);
141 if (kr) {
142 /* rdar://problem/6416724
143 * If we fail to set up a shared memory page, just allocate a local chunk
144 * of memory. This way, processes can still introspect their own transaction
145 * counts if they're being run under a debugger. Moral of the story: Debug
146 * from the environment you intend to run in.
147 */
148 void *_vm_addr = calloc(1, sizeof(struct vproc_shmem_s));
dcace88f 149 if (!_vm_addr) {
ddbbfbc1
A
150 return;
151 }
152
153 vm_addr = (vm_address_t)_vm_addr;
154 } else {
155 kr = vm_map(mach_task_self(), &vm_addr, getpagesize(), 0, true, shmem_port, 0, false,
156 VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE);
157
158 //assert(kr == 0);
159 if (kr) return;
160
161 kr = mach_port_deallocate(mach_task_self(), shmem_port);
162
163 //assert(kr == 0);
164 }
165
166 vproc_shmem = (struct vproc_shmem_s *)vm_addr;
167}
168
169static void
170vproc_client_init(void)
171{
172 char *val = getenv(LAUNCHD_DO_APPLE_INTERNAL_LOGGING);
dcace88f
A
173 if (val) {
174 if (strncmp(val, "true", sizeof("true") - 1) == 0) {
ddbbfbc1
A
175 do_apple_internal_logging = true;
176 }
177 }
178
179 vproc_shmem_init();
180}
181
182vproc_transaction_t
183vproc_transaction_begin(vproc_t vp __attribute__((unused)))
184{
185 vproc_transaction_t vpt = (vproc_transaction_t)vproc_shmem_init; /* we need a "random" variable that is testable */
ddbbfbc1 186 _vproc_transaction_begin();
ddbbfbc1
A
187
188 return vpt;
189}
190
191void
192_vproc_transaction_begin(void)
193{
ddbbfbc1
A
194 if (unlikely(vproc_shmem == NULL)) {
195 int po_r = pthread_once(&shmem_inited, vproc_client_init);
196 if (po_r != 0 || vproc_shmem == NULL) {
197 return;
198 }
199 }
200
dcace88f
A
201 /* We need to deal with the potential race condition of trying to open a
202 * transaction after launchd has marked the process for death. Consider if
203 * one thread closes the last transaction after marking the process for
204 * death. Then we call _exit(2). But exiting isn't instantaneous. So if some
205 * other threads come in very soon after and try to open transactions, we
206 * have to cut them off so that they can't begin their work. Because if they
207 * can manage to get to the point of, say, parking themselves in an
208 * uninterruptible wait, then the process won't exit.
209 *
210 * We loop here so that, if someone's calling vproc_transaction_end() at the
211 * same time, we can pick up the descent to -1 if launchd has marked us for
212 * death.
213 */
ddbbfbc1
A
214 typeof(vproc_shmem->vp_shmem_transaction_cnt) old = 0;
215 do {
216 old = vproc_shmem->vp_shmem_transaction_cnt;
217
218 if (unlikely(old < 0)) {
dcace88f
A
219 /* No transactions should be opened after this point, so make sure
220 * this thread can't proceed. We don't crash here because it could
221 * be a legitimate race, as described above.
222 */
ddbbfbc1
A
223 if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) {
224 _exit(0);
225 } else {
dcace88f 226 _vproc_set_crash_log_message("Unbalanced: vproc_transaction_begin()");
ddbbfbc1
A
227 }
228 abort();
dcace88f
A
229 } else if (old == 0 && vproc_gonenonzero) {
230 vproc_gonenonzero();
ddbbfbc1 231 }
dcace88f 232 } while (!__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, old, old + 1));
ddbbfbc1
A
233
234 runtime_ktrace(RTKT_VPROC_TRANSACTION_INCREMENT, old + 1, 0, 0);
ddbbfbc1
A
235}
236
237size_t
238_vproc_transaction_count(void)
239{
240 return likely(vproc_shmem) ? vproc_shmem->vp_shmem_transaction_cnt : INT32_MAX;
241}
242
243size_t
244_vproc_standby_count(void)
245{
246#ifdef VPROC_STANDBY_IMPLEMENTED
247 return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_cnt : INT32_MAX;
248#else
249 return 0;
250#endif
251}
252
253size_t
254_vproc_standby_timeout(void)
255{
256 return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_timeout : 0;
257}
258
259bool
260_vproc_pid_is_managed(pid_t p)
261{
262 boolean_t result = false;
263 vproc_mig_pid_is_managed(bootstrap_port, p, &result);
264
265 return result;
266}
267
268kern_return_t
269_vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned)
270{
271 boolean_t _condemned = false;
272 kern_return_t kr = vproc_mig_transaction_count_for_pid(bootstrap_port, p, count, &_condemned);
dcace88f 273 if (kr == KERN_SUCCESS && condemned) {
ddbbfbc1
A
274 *condemned = _condemned ? true : false;
275 }
276
277 return kr;
278}
279
280void
ddbbfbc1
A
281_vproc_transaction_try_exit(int status)
282{
dcace88f 283#if !TARGET_OS_EMBEDDED
ddbbfbc1
A
284 if (unlikely(vproc_shmem == NULL)) {
285 return;
286 }
287
288 if (__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, 0, -1)) {
289 vproc_shmem->vp_shmem_flags |= VPROC_SHMEM_EXITING;
290 _exit(status);
291 }
ddbbfbc1 292#else
ddbbfbc1 293
ddbbfbc1 294#endif
dcace88f 295}
ddbbfbc1
A
296
297void
298vproc_transaction_end(vproc_t vp __attribute__((unused)), vproc_transaction_t vpt)
299{
300 if (unlikely(vpt != (vproc_transaction_t)vproc_shmem_init)) {
dcace88f 301 _vproc_set_crash_log_message("Bogus transaction handle passed to vproc_transaction_end() ");
ddbbfbc1
A
302 abort();
303 }
304
ddbbfbc1 305 _vproc_transaction_end();
ddbbfbc1
A
306}
307
308void
309_vproc_transaction_end(void)
310{
ddbbfbc1
A
311 typeof(vproc_shmem->vp_shmem_transaction_cnt) newval;
312
313 if (unlikely(vproc_shmem == NULL)) {
314 return;
315 }
316
317 newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_transaction_cnt, 1);
318
319 runtime_ktrace(RTKT_VPROC_TRANSACTION_DECREMENT, newval, 0, 0);
320 if (unlikely(newval < 0)) {
321 if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) {
322 _exit(0);
323 } else {
dcace88f 324 _vproc_set_crash_log_message("Unbalanced: vproc_transaction_end()");
ddbbfbc1
A
325 }
326 abort();
dcace88f
A
327 } else if (newval == 0 && vproc_gone2zero) {
328 vproc_gone2zero();
ddbbfbc1 329 }
ddbbfbc1
A
330}
331
332vproc_standby_t
333vproc_standby_begin(vproc_t vp __attribute__((unused)))
334{
335#ifdef VPROC_STANDBY_IMPLEMENTED
336 vproc_standby_t vpsb = (vproc_standby_t)vproc_shmem_init; /* we need a "random" variable that is testable */
337
338 _vproc_standby_begin();
339
340 return vpsb;
341#else
342 return NULL;
343#endif
344}
345
346void
347_vproc_standby_begin(void)
348{
349#ifdef VPROC_STANDBY_IMPLEMENTED
350 typeof(vproc_shmem->vp_shmem_standby_cnt) newval;
351
352 if (unlikely(vproc_shmem == NULL)) {
353 int po_r = pthread_once(&shmem_inited, vproc_client_init);
354 if (po_r != 0 || vproc_shmem == NULL) {
355 return;
356 }
357 }
358
359 newval = __sync_add_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1);
360
361 if (unlikely(newval < 1)) {
dcace88f 362 _vproc_set_crash_log_message("Unbalanced: vproc_standby_begin()");
ddbbfbc1
A
363 abort();
364 }
365#else
366 return;
367#endif
368}
369
370void
371vproc_standby_end(vproc_t vp __attribute__((unused)), vproc_standby_t vpt __attribute__((unused)))
372{
373#ifdef VPROC_STANDBY_IMPLEMENTED
374 if (unlikely(vpt != (vproc_standby_t)vproc_shmem_init)) {
dcace88f 375 _vproc_set_crash_log_message("Bogus standby handle passed to vproc_standby_end() ");
ddbbfbc1
A
376 abort();
377 }
378
379 _vproc_standby_end();
380#else
381 return;
382#endif
383}
384
385void
386_vproc_standby_end(void)
387{
388#ifdef VPROC_STANDBY_IMPLEMENTED
389 typeof(vproc_shmem->vp_shmem_standby_cnt) newval;
390
dcace88f
A
391 if (unlikely(vproc_shmem == NULL)) {
392 _vproc_set_crash_log_message("Process called vproc_standby_end() when not enrolled in transaction model.");
ddbbfbc1
A
393 abort();
394 }
395
396 newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1);
397
398 if (unlikely(newval < 0)) {
dcace88f 399 _vproc_set_crash_log_message("Unbalanced: vproc_standby_end()");
ddbbfbc1
A
400 abort();
401 }
402#else
403 return;
404#endif
405}
5b0a4722 406
dcace88f
A
407int32_t *
408_vproc_transaction_ptr(void)
409{
410 return NULL;
411}
412
413void
414_vproc_transaction_set_callouts(_vproc_transaction_callout gone2zero, _vproc_transaction_callout gonenonzero)
415{
416 if (unlikely(vproc_shmem == NULL)) {
417 int po_r = pthread_once(&shmem_inited, vproc_client_init);
418 if (po_r != 0 || vproc_shmem == NULL) {
419 return;
420 }
421 }
422
423 static bool once = false;
424 if (once) {
425 _vproc_set_crash_log_message("This SPI may only be called once. It is only meant for libxpc.");
426 abort();
427 }
428
429 once = true;
430
431 vproc_gone2zero = gone2zero;
432 vproc_gonenonzero = gonenonzero;
433}
434
5b0a4722
A
435kern_return_t
436_vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, launch_data_t *outval,
437 mach_port_array_t *ports, mach_msg_type_number_t *portCnt)
438{
439 mach_msg_type_number_t outdata_cnt;
440 vm_offset_t outdata = 0;
441 size_t data_offset = 0;
442 launch_data_t out_obj;
443 kern_return_t kr;
444
445 if ((kr = vproc_mig_take_subset(bp, reqport, rcvright, &outdata, &outdata_cnt, ports, portCnt))) {
446 goto out;
447 }
448
449 if ((out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
450 *outval = launch_data_copy(out_obj);
451 } else {
452 kr = 1;
453 }
454
455out:
456 if (outdata) {
457 mig_deallocate(outdata, outdata_cnt);
458 }
459
460 return kr;
461}
462
463vproc_err_t
464_vproc_post_fork_ping(void)
465{
ddbbfbc1
A
466#if !TARGET_OS_EMBEDDED
467 au_asid_t s = AU_DEFAUDITSID;
468 do {
469 mach_port_t session = MACH_PORT_NULL;
470 kern_return_t kr = vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session);
dcace88f 471 if (kr != KERN_SUCCESS) {
ddbbfbc1
A
472 /* If this happens, our bootstrap port probably got hosed. */
473 _vproc_log(LOG_ERR, "Post-fork ping failed!");
474 break;
475 }
476
477 /* If we get back MACH_PORT_NULL, that means we just stick with the session
478 * we inherited across fork(2).
479 */
dcace88f 480 if (session == MACH_PORT_NULL) {
ddbbfbc1
A
481 s = ~AU_DEFAUDITSID;
482 break;
483 }
484
485 s = _audit_session_join(session);
dcace88f 486 if (s == 0) {
ddbbfbc1
A
487 _vproc_log_error(LOG_ERR, "Could not join security session!");
488 s = AU_DEFAUDITSID;
489 } else {
490 _vproc_log(LOG_DEBUG, "Joined session %d.", s);
491 }
dcace88f 492 } while (0);
ddbbfbc1
A
493
494 return s != AU_DEFAUDITSID ? NULL : _vproc_post_fork_ping;
495#else
496 mach_port_t session = MACH_PORT_NULL;
497 return vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session) ? _vproc_post_fork_ping : NULL;
498#endif
5b0a4722
A
499}
500
501vproc_err_t
502_vprocmgr_init(const char *session_type)
503{
ddbbfbc1 504 if (vproc_mig_init_session(bootstrap_port, (char *)session_type, _audit_session_self()) == 0) {
5b0a4722
A
505 return NULL;
506 }
507
508 return (vproc_err_t)_vprocmgr_init;
509}
510
511vproc_err_t
ddbbfbc1 512_vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags)
5b0a4722 513{
5b0a4722
A
514 kern_return_t kr = 0;
515 bool is_bkgd = (strcmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0);
516 int64_t ldpid, lduid;
517
518 if (vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, 0, &ldpid) != 0) {
519 return (vproc_err_t)_vprocmgr_move_subset_to_user;
520 }
521
522 if (vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, 0, &lduid) != 0) {
523 return (vproc_err_t)_vprocmgr_move_subset_to_user;
524 }
525
526 if (!is_bkgd && ldpid != 1) {
527 if (lduid == getuid()) {
528 return NULL;
529 }
530 /*
531 * Not all sessions can be moved.
532 * We should clean up this mess someday.
533 */
534 return (vproc_err_t)_vprocmgr_move_subset_to_user;
535 }
536
ddbbfbc1
A
537 mach_port_t puc = 0, rootbs = get_root_bootstrap_port();
538
539 if (vproc_mig_lookup_per_user_context(rootbs, target_user, &puc) != 0) {
540 return (vproc_err_t)_vprocmgr_move_subset_to_user;
541 }
542
dcace88f 543 if (is_bkgd) {
ddbbfbc1
A
544 task_set_bootstrap_port(mach_task_self(), puc);
545 mach_port_deallocate(mach_task_self(), bootstrap_port);
546 bootstrap_port = puc;
5b0a4722 547 } else {
ddbbfbc1
A
548 kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type, _audit_session_self(), flags);
549 mach_port_deallocate(mach_task_self(), puc);
5b0a4722 550 }
ddbbfbc1 551
5b0a4722
A
552 cached_pid = -1;
553
554 if (kr) {
555 return (vproc_err_t)_vprocmgr_move_subset_to_user;
556 }
557
5b0a4722
A
558 return _vproc_post_fork_ping();
559}
560
ddbbfbc1
A
561vproc_err_t
562_vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags __attribute__((unused)))
563{
564 mach_port_t new_bsport = MACH_PORT_NULL;
565 kern_return_t kr = KERN_FAILURE;
566
567 mach_port_t tnp = MACH_PORT_NULL;
568 task_name_for_pid(mach_task_self(), getpid(), &tnp);
dcace88f 569 if ((kr = vproc_mig_switch_to_session(bootstrap_port, tnp, (char *)target_session, _audit_session_self(), &new_bsport)) != KERN_SUCCESS) {
ddbbfbc1
A
570 _vproc_log(LOG_NOTICE, "_vprocmgr_switch_to_session(): kr = 0x%x", kr);
571 return (vproc_err_t)_vprocmgr_switch_to_session;
572 }
573
574 task_set_bootstrap_port(mach_task_self(), new_bsport);
575 mach_port_deallocate(mach_task_self(), bootstrap_port);
576 bootstrap_port = new_bsport;
577
578 return !issetugid() ? _vproc_post_fork_ping() : NULL;
579}
580
581vproc_err_t
582_vprocmgr_detach_from_console(vproc_flags_t flags __attribute__((unused)))
583{
584 return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND, 0);
585}
5b0a4722
A
586
587pid_t
588_spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version)
589{
590 size_t i, good_enough_size = 10*1024*1024;
591 mach_msg_type_number_t indata_cnt = 0;
592 vm_offset_t indata = 0;
593 mach_port_t obsvr_port = MACH_PORT_NULL;
594 launch_data_t tmp, tmp_array, in_obj;
595 const char *const *tmpp;
596 kern_return_t kr = 1;
597 void *buf = NULL;
598 pid_t p = -1;
599
600 if ((in_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)) == NULL) {
601 goto out;
602 }
603
604 if ((tmp = launch_data_new_string(label)) == NULL) {
605 goto out;
606 }
607
608 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_LABEL);
609
610 if ((tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY)) == NULL) {
611 goto out;
612 }
613
614 for (i = 0; *argv; i++, argv++) {
615 tmp = launch_data_new_string(*argv);
616 if (tmp == NULL) {
617 goto out;
618 }
619
620 launch_data_array_set_index(tmp_array, tmp, i);
621 }
622
623 launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
624
625 if (spawn_attrs) switch (struct_version) {
dcace88f 626 case 3:
5b0a4722 627 case 2:
f36da725 628#if HAVE_QUARANTINE
5b0a4722
A
629 if (spawn_attrs->spawn_quarantine) {
630 char qbuf[QTN_SERIALIZED_DATA_MAX];
631 size_t qbuf_sz = QTN_SERIALIZED_DATA_MAX;
632
633 if (qtn_proc_to_data(spawn_attrs->spawn_quarantine, qbuf, &qbuf_sz) == 0) {
634 tmp = launch_data_new_opaque(qbuf, qbuf_sz);
635 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_QUARANTINEDATA);
636 }
637 }
f36da725 638#endif
5b0a4722
A
639
640 if (spawn_attrs->spawn_seatbelt_profile) {
641 tmp = launch_data_new_string(spawn_attrs->spawn_seatbelt_profile);
642 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXPROFILE);
643 }
644
645 if (spawn_attrs->spawn_seatbelt_flags) {
646 tmp = launch_data_new_integer(*spawn_attrs->spawn_seatbelt_flags);
647 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXFLAGS);
648 }
649
650 /* fall through */
651 case 1:
652 if (spawn_attrs->spawn_binpref) {
653 tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY);
654 for (i = 0; i < spawn_attrs->spawn_binpref_cnt; i++) {
655 tmp = launch_data_new_integer(spawn_attrs->spawn_binpref[i]);
656 launch_data_array_set_index(tmp_array, tmp, i);
657 }
658 launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_BINARYORDERPREFERENCE);
659 }
660 /* fall through */
661 case 0:
662 if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_STOPPED) {
663 tmp = launch_data_new_bool(true);
664 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WAITFORDEBUGGER);
665 }
dcace88f
A
666 if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_TALAPP) {
667 tmp = launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP);
668 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_POSIXSPAWNTYPE);
669 }
670 if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_WIDGET) {
671 tmp = launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_WIDGET);
672 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_POSIXSPAWNTYPE);
673 }
674 if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_DISABLE_ASLR) {
675 tmp = launch_data_new_bool(true);
676 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_DISABLEASLR);
677 }
5b0a4722
A
678
679 if (spawn_attrs->spawn_env) {
680 launch_data_t tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
681
682 for (tmpp = spawn_attrs->spawn_env; *tmpp; tmpp++) {
683 char *eqoff, tmpstr[strlen(*tmpp) + 1];
684
685 strcpy(tmpstr, *tmpp);
686
687 eqoff = strchr(tmpstr, '=');
688
689 if (!eqoff) {
690 goto out;
691 }
692
693 *eqoff = '\0';
694
695 launch_data_dict_insert(tmp_dict, launch_data_new_string(eqoff + 1), tmpstr);
696 }
697
698 launch_data_dict_insert(in_obj, tmp_dict, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES);
699 }
700
701 if (spawn_attrs->spawn_path) {
702 tmp = launch_data_new_string(spawn_attrs->spawn_path);
703 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_PROGRAM);
704 }
705
706 if (spawn_attrs->spawn_chdir) {
707 tmp = launch_data_new_string(spawn_attrs->spawn_chdir);
708 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WORKINGDIRECTORY);
709 }
710
711 if (spawn_attrs->spawn_umask) {
712 tmp = launch_data_new_integer(*spawn_attrs->spawn_umask);
713 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_UMASK);
714 }
715
716 break;
717 default:
718 break;
719 }
720
721 if (!(buf = malloc(good_enough_size))) {
722 goto out;
723 }
724
725 if ((indata_cnt = launch_data_pack(in_obj, buf, good_enough_size, NULL, NULL)) == 0) {
726 goto out;
727 }
728
729 indata = (vm_offset_t)buf;
730
dcace88f
A
731 if (struct_version == 3) {
732 kr = vproc_mig_spawn2(bootstrap_port, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
733 } else {
734 _vproc_set_crash_log_message("Bogus version passed to _spawn_via_launchd(). For this release, the only valid version is 3.");
735 }
5b0a4722
A
736
737 if (kr == VPROC_ERR_TRY_PER_USER) {
738 mach_port_t puc;
739
740 if (vproc_mig_lookup_per_user_context(bootstrap_port, 0, &puc) == 0) {
dcace88f
A
741 if (struct_version == 3) {
742 kr = vproc_mig_spawn2(puc, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
743 }
5b0a4722
A
744 mach_port_deallocate(mach_task_self(), puc);
745 }
746 }
747
748out:
749 if (in_obj) {
750 launch_data_free(in_obj);
751 }
752
753 if (buf) {
754 free(buf);
755 }
756
757 switch (kr) {
758 case BOOTSTRAP_SUCCESS:
759 if (spawn_attrs && spawn_attrs->spawn_observer_port) {
760 *spawn_attrs->spawn_observer_port = obsvr_port;
761 } else {
dcace88f
A
762 if (struct_version == 3) {
763 mach_port_mod_refs(mach_task_self(), obsvr_port, MACH_PORT_RIGHT_RECEIVE, -1);
764 } else {
765 mach_port_deallocate(mach_task_self(), obsvr_port);
766 }
5b0a4722
A
767 }
768 return p;
769 case BOOTSTRAP_NOT_PRIVILEGED:
770 errno = EPERM; break;
771 case BOOTSTRAP_NO_MEMORY:
772 errno = ENOMEM; break;
773 case BOOTSTRAP_NAME_IN_USE:
774 errno = EEXIST; break;
775 case 1:
776 errno = EIO; break;
777 default:
778 errno = EINVAL; break;
779 }
780
781 return -1;
782}
783
784kern_return_t
dcace88f 785mpm_wait(mach_port_t ajob __attribute__((unused)), int *wstatus)
5b0a4722 786{
dcace88f
A
787 *wstatus = 0;
788 return 0;
5b0a4722
A
789}
790
791kern_return_t
dcace88f 792mpm_uncork_fork(mach_port_t ajob __attribute__((unused)))
5b0a4722 793{
dcace88f 794 return KERN_FAILURE;
5b0a4722
A
795}
796
797kern_return_t
798_vprocmgr_getsocket(name_t sockpath)
799{
800 return vproc_mig_getsocket(bootstrap_port, sockpath);
801}
802
803vproc_err_t
804_vproc_get_last_exit_status(int *wstatus)
805{
806 int64_t val;
807
808 if (vproc_swap_integer(NULL, VPROC_GSK_LAST_EXIT_STATUS, 0, &val) == 0) {
809 *wstatus = (int)val;
810 return NULL;
811 }
812
813 return (vproc_err_t)_vproc_get_last_exit_status;
814}
815
816vproc_err_t
817_vproc_send_signal_by_label(const char *label, int sig)
818{
819 if (vproc_mig_send_signal(bootstrap_port, (char *)label, sig) == 0) {
820 return NULL;
821 }
822
823 return _vproc_send_signal_by_label;
824}
825
826vproc_err_t
827_vprocmgr_log_forward(mach_port_t mp, void *data, size_t len)
828{
829 if (vproc_mig_log_forward(mp, (vm_offset_t)data, len) == 0) {
830 return NULL;
831 }
832
833 return _vprocmgr_log_forward;
834}
835
836vproc_err_t
837_vprocmgr_log_drain(vproc_t vp __attribute__((unused)), pthread_mutex_t *mutex, _vprocmgr_log_drain_callback_t func)
838{
839 mach_msg_type_number_t outdata_cnt, tmp_cnt;
840 vm_offset_t outdata = 0;
ddbbfbc1 841 struct timeval tv;
5b0a4722
A
842 struct logmsg_s *lm;
843
844 if (!func) {
845 return _vprocmgr_log_drain;
846 }
847
848 if (vproc_mig_log_drain(bootstrap_port, &outdata, &outdata_cnt) != 0) {
849 return _vprocmgr_log_drain;
850 }
851
852 tmp_cnt = outdata_cnt;
853
854 if (mutex) {
855 pthread_mutex_lock(mutex);
856 }
857
858 for (lm = (struct logmsg_s *)outdata; tmp_cnt > 0; lm = ((void *)lm + lm->obj_sz)) {
ddbbfbc1
A
859 lm->from_name = (char *)lm + lm->from_name_offset;
860 lm->about_name = (char *)lm + lm->about_name_offset;
861 lm->msg = (char *)lm + lm->msg_offset;
862 lm->session_name = (char *)lm + lm->session_name_offset;
863
864 tv.tv_sec = lm->when / USEC_PER_SEC;
865 tv.tv_usec = lm->when % USEC_PER_SEC;
5b0a4722 866
ddbbfbc1 867 func(&tv, lm->from_pid, lm->about_pid, lm->sender_uid, lm->sender_gid, lm->pri,
5b0a4722
A
868 lm->from_name, lm->about_name, lm->session_name, lm->msg);
869
870 tmp_cnt -= lm->obj_sz;
871 }
872
873 if (mutex) {
874 pthread_mutex_unlock(mutex);
875 }
876
877 if (outdata) {
878 mig_deallocate(outdata, outdata_cnt);
879 }
880
881 return NULL;
882}
883
884vproc_err_t
ddbbfbc1 885vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval)
5b0a4722
A
886{
887 static int64_t cached_is_managed = -1;
888 int64_t dummyval = 0;
889
890 switch (key) {
891 case VPROC_GSK_MGR_PID:
892 if (cached_pid != -1 && outval) {
893 *outval = cached_pid;
894 return NULL;
895 }
896 break;
897 case VPROC_GSK_IS_MANAGED:
898 if (cached_is_managed != -1 && outval) {
899 *outval = cached_is_managed;
900 return NULL;
901 }
902 break;
ddbbfbc1
A
903 case VPROC_GSK_TRANSACTIONS_ENABLED:
904 /* Shared memory region is required for transactions. */
dcace88f 905 if (unlikely(vproc_shmem == NULL)) {
ddbbfbc1 906 int po_r = pthread_once(&shmem_inited, vproc_client_init);
dcace88f
A
907 if (po_r != 0 || vproc_shmem == NULL) {
908 if (outval) {
ddbbfbc1
A
909 *outval = -1;
910 }
911 return (vproc_err_t)vproc_swap_integer;
912 }
913 }
914
dcace88f 915 if (s_cached_transactions_enabled && outval) {
ddbbfbc1
A
916 *outval = s_cached_transactions_enabled;
917 return NULL;
918 }
919 break;
5b0a4722
A
920 default:
921 break;
922 }
923
ddbbfbc1
A
924 kern_return_t kr = KERN_FAILURE;
925 mach_port_t mp = vp ? vp->j_port : bootstrap_port;
926 if ((kr = vproc_mig_swap_integer(mp, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval)) == 0) {
5b0a4722
A
927 switch (key) {
928 case VPROC_GSK_MGR_PID:
929 cached_pid = outval ? *outval : dummyval;
930 break;
931 case VPROC_GSK_IS_MANAGED:
932 cached_is_managed = outval ? *outval : dummyval;
933 break;
ddbbfbc1 934 case VPROC_GSK_TRANSACTIONS_ENABLED:
ddbbfbc1
A
935 s_cached_transactions_enabled = 1;
936 break;
dcace88f
A
937 case VPROC_GSK_PERUSER_SUSPEND:
938 if (dummyval) {
939 /* Wait for the per-user launchd to exit before returning. */
940 int kq = kqueue();
941 struct kevent kev;
942 EV_SET(&kev, dummyval, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0);
943 int r = kevent(kq, &kev, 1, &kev, 1, NULL);
944 (void)close(kq);
945 if (r != 1) {
946 return NULL;
947 }
948 break;
ddbbfbc1 949 }
5b0a4722
A
950 default:
951 break;
952 }
953 return NULL;
954 }
955
956 return (vproc_err_t)vproc_swap_integer;
957}
958
959mach_port_t
960get_root_bootstrap_port(void)
961{
962 mach_port_t parent_port = 0;
963 mach_port_t previous_port = 0;
964
965 do {
966 if (previous_port) {
967 if (previous_port != bootstrap_port) {
968 mach_port_deallocate(mach_task_self(), previous_port);
969 }
970 previous_port = parent_port;
971 } else {
972 previous_port = bootstrap_port;
973 }
974
975 if (bootstrap_parent(previous_port, &parent_port) != 0) {
976 return MACH_PORT_NULL;
977 }
978
979 } while (parent_port != previous_port);
980
981 return parent_port;
982}
983
984vproc_err_t
ddbbfbc1 985vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval)
5b0a4722
A
986{
987 size_t data_offset = 0, good_enough_size = 10*1024*1024;
988 mach_msg_type_number_t indata_cnt = 0, outdata_cnt;
989 vm_offset_t indata = 0, outdata = 0;
990 launch_data_t out_obj;
991 void *rval = vproc_swap_complex;
992 void *buf = NULL;
993
994 if (inval) {
995 if (!(buf = malloc(good_enough_size))) {
996 goto out;
997 }
998
999 if ((indata_cnt = launch_data_pack(inval, buf, good_enough_size, NULL, NULL)) == 0) {
1000 goto out;
1001 }
1002
1003 indata = (vm_offset_t)buf;
1004 }
1005
ddbbfbc1
A
1006 mach_port_t mp = vp ? vp->j_port : bootstrap_port;
1007 if (vproc_mig_swap_complex(mp, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) {
5b0a4722
A
1008 goto out;
1009 }
1010
1011 if (outval) {
1012 if (!(out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
1013 goto out;
1014 }
1015
1016 if (!(*outval = launch_data_copy(out_obj))) {
1017 goto out;
1018 }
1019 }
1020
1021 rval = NULL;
1022out:
1023 if (buf) {
1024 free(buf);
1025 }
1026
1027 if (outdata) {
1028 mig_deallocate(outdata, outdata_cnt);
1029 }
1030
1031 return rval;
1032}
1033
ddbbfbc1
A
1034vproc_err_t
1035vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr)
1036{
1037 launch_data_t instr_data = instr ? launch_data_new_string(instr) : NULL;
1038 launch_data_t outstr_data = NULL;
1039
1040 vproc_err_t verr = vproc_swap_complex(vp, key, instr_data, &outstr_data);
dcace88f
A
1041 if (!verr && outstr) {
1042 if (launch_data_get_type(outstr_data) == LAUNCH_DATA_STRING) {
ddbbfbc1
A
1043 *outstr = strdup(launch_data_get_string(outstr_data));
1044 } else {
1045 verr = (vproc_err_t)vproc_swap_string;
1046 }
1047 launch_data_free(outstr_data);
1048 }
dcace88f 1049 if (instr_data) {
ddbbfbc1
A
1050 launch_data_free(instr_data);
1051 }
1052
1053 return verr;
1054}
1055
5b0a4722
A
1056void *
1057reboot2(uint64_t flags)
1058{
1059 if (vproc_mig_reboot2(get_root_bootstrap_port(), flags) == 0) {
1060 return NULL;
1061 }
1062
1063 return reboot2;
1064}
1065
f36da725 1066vproc_err_t
dcace88f 1067_vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name __attribute__((unused)), mach_port_t *out_obsrvr_port __attribute__((unused)), vproc_flags_t flags)
f36da725 1068{
dcace88f
A
1069 /* Ignore the two port parameters. This SPI isn't long for this world, and
1070 * all the current clients just leak them anyway.
1071 */
1072 kern_return_t kr = vproc_mig_kickstart(bootstrap_port, (char *)label, out_pid, flags);
1073 if (kr == KERN_SUCCESS) {
f36da725
A
1074 return NULL;
1075 }
1076
1077 return (vproc_err_t)_vproc_kickstart_by_label;
1078}
1079
5b0a4722
A
1080vproc_err_t
1081_vproc_set_global_on_demand(bool state)
1082{
1083 int64_t val = state ? ~0 : 0;
1084
1085 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_ON_DEMAND, &val, NULL) == 0) {
1086 return NULL;
1087 }
1088
1089 return (vproc_err_t)_vproc_set_global_on_demand;
1090}
1091
1092void
1093_vproc_logv(int pri, int err, const char *msg, va_list ap)
1094{
1095 char flat_msg[3000];
1096
1097 vsnprintf(flat_msg, sizeof(flat_msg), msg, ap);
1098
1099 vproc_mig_log(bootstrap_port, pri, err, flat_msg);
1100}
1101
1102void
1103_vproc_log(int pri, const char *msg, ...)
1104{
1105 va_list ap;
1106
1107 va_start(ap, msg);
1108 _vproc_logv(pri, 0, msg, ap);
1109 va_end(ap);
1110}
1111
1112void
1113_vproc_log_error(int pri, const char *msg, ...)
1114{
1115 int saved_errno = errno;
1116 va_list ap;
1117
1118 va_start(ap, msg);
1119 _vproc_logv(pri, saved_errno, msg, ap);
1120 va_end(ap);
1121}
dcace88f
A
1122
1123bool
1124vprocmgr_helper_check_in(const char *name, mach_port_t rp, launch_data_t *events, uint64_t *tokens)
1125{
1126 vm_offset_t events_packed = 0;
1127 mach_msg_type_number_t sz = 0;
1128 size_t data_off = 0;
1129
1130 kern_return_t kr = vproc_mig_event_source_check_in(bootstrap_port, (char *)name, rp, &events_packed, &sz, tokens);
1131 if (kr == 0) {
1132 launch_data_t _events = launch_data_unpack((void *)events_packed, sz, NULL, 0, &data_off, 0);
1133 *events = launch_data_copy(_events);
1134 if (!*events) {
1135 kr = 1;
1136 }
1137
1138 mig_deallocate(events_packed, sz);
1139 }
1140
1141 return (kr == 0);
1142}
1143
1144bool
1145vprocmgr_helper_event_set_state(const char *sysname, uint64_t token, bool state)
1146{
1147 kern_return_t kr = vproc_mig_event_set_state(bootstrap_port, (char *)sysname, token, state);
1148 return (kr == 0);
1149}
1150
1151void
1152vprocmgr_helper_register(vproc_helper_recv_ping_t callout)
1153{
1154 vprocmgr_helper_callout = callout;
1155}
1156
1157/* The type naming convention is as follows:
1158 * For requests...
1159 * union __RequestUnion__<userprefix><subsystem>_subsystem
1160 * For replies...
1161 * union __ReplyUnion__<userprefix><subsystem>_subsystem
1162 */
1163union maxmsgsz {
1164 union __RequestUnion__helper_downcall_launchd_helper_subsystem req;
1165 union __ReplyUnion__helper_downcall_launchd_helper_subsystem rep;
1166};
1167
1168size_t vprocmgr_helper_maxmsgsz = sizeof(union maxmsgsz);
1169
1170kern_return_t
1171helper_recv_ping(mach_port_t p, audit_token_t autok)
1172{
1173 return vprocmgr_helper_callout(p, autok);
1174}
1175
1176boolean_t
1177vprocmgr_helper_server_routine_for_dispatch(mach_msg_header_t *message, mach_msg_header_t *reply)
1178{
1179 return launchd_helper_server(message, reply);
1180}
1181
1182kern_return_t
1183helper_recv_wait(mach_port_t p, int status)
1184{
1185 /* Total hack. */
1186 return (errno = mach_port_set_context(mach_task_self(), p, (mach_vm_address_t)status));
1187}
1188
1189int
1190launch_wait(mach_port_t port)
1191{
1192 int status = -1;
1193 errno = mach_msg_server_once(launchd_helper_server, vprocmgr_helper_maxmsgsz, port, 0);
1194 if (errno == MACH_MSG_SUCCESS) {
1195 mach_vm_address_t ctx = 0;
1196 if ((errno = mach_port_get_context(mach_task_self(), port, &ctx)) == KERN_SUCCESS) {
1197 status = ctx;
1198 }
1199 }
1200
1201 return status;
1202}