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