]> git.saurik.com Git - apple/launchd.git/blame - liblaunch/libvproc.c
launchd-842.92.1.tar.gz
[apple/launchd.git] / liblaunch / libvproc.c
CommitLineData
5b0a4722 1/*
95379394 2 * Copyright (c) 1999-2012 Apple Inc. All rights reserved.
5b0a4722
A
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 25
eabd1701
A
26#include <dispatch/dispatch.h>
27#include <libproc.h>
5b0a4722
A
28#include <mach/mach.h>
29#include <mach/vm_map.h>
30#include <sys/param.h>
31#include <stdlib.h>
32#include <stdio.h>
33#include <errno.h>
34#include <unistd.h>
35#include <syslog.h>
36#include <pthread.h>
ddbbfbc1
A
37#include <signal.h>
38#include <assert.h>
39#include <libkern/OSAtomic.h>
40#include <sys/syscall.h>
dcace88f 41#include <sys/event.h>
eabd1701 42#include <System/sys/fileport.h>
95379394 43#include <os/assumes.h>
ddbbfbc1 44
f36da725
A
45#if HAVE_QUARANTINE
46#include <quarantine.h>
47#endif
5b0a4722 48
ef398931
A
49#include "launch.h"
50#include "launch_priv.h"
51#include "launch_internal.h"
eabd1701 52#include "ktrace.h"
5b0a4722 53
eabd1701 54#include "job.h"
5b0a4722 55
eabd1701
A
56#include "helper.h"
57#include "helperServer.h"
dcace88f 58
5b0a4722
A
59#include "reboot2.h"
60
eabd1701
A
61#define likely(x) __builtin_expect((bool)(x), true)
62#define unlikely(x) __builtin_expect((bool)(x), false)
ddbbfbc1 63
eabd1701 64#define _vproc_set_crash_log_message(x)
5b0a4722 65
eabd1701
A
66void _vproc_transactions_enable_internal(void *arg);
67void _vproc_transaction_begin_internal(void *arg __unused);
68void _vproc_transaction_end_internal(void *arg __unused);
ddbbfbc1 69
eabd1701 70#pragma mark vproc Object
ddbbfbc1
A
71struct vproc_s {
72 int32_t refcount;
73 mach_port_t j_port;
74};
75
eabd1701
A
76vproc_t
77vprocmgr_lookup_vproc(const char *label)
ddbbfbc1
A
78{
79 struct vproc_s *vp = NULL;
eabd1701 80
ddbbfbc1
A
81 mach_port_t mp = MACH_PORT_NULL;
82 kern_return_t kr = vproc_mig_port_for_label(bootstrap_port, (char *)label, &mp);
dcace88f 83 if (kr == BOOTSTRAP_SUCCESS) {
ddbbfbc1 84 vp = (struct vproc_s *)calloc(1, sizeof(struct vproc_s));
dcace88f 85 if (vp) {
ddbbfbc1
A
86 vp->refcount = 1;
87 mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1);
88 vp->j_port = mp;
89 }
dcace88f 90 (void)mach_port_deallocate(mach_task_self(), mp);
ddbbfbc1 91 }
eabd1701 92
ddbbfbc1
A
93 return vp;
94}
95
eabd1701
A
96vproc_t
97vproc_retain(vproc_t vp)
ddbbfbc1
A
98{
99 int32_t orig = OSAtomicAdd32(1, &vp->refcount) - 1;
dcace88f 100 if (orig <= 0) {
dcace88f 101 _vproc_set_crash_log_message("Under-retain / over-release of vproc_t.");
ddbbfbc1
A
102 abort();
103 }
eabd1701 104
ddbbfbc1
A
105 return vp;
106}
107
eabd1701
A
108void
109vproc_release(vproc_t vp)
ddbbfbc1
A
110{
111 int32_t newval = OSAtomicAdd32(-1, &vp->refcount);
dcace88f 112 if (newval < 0) {
dcace88f 113 _vproc_set_crash_log_message("Over-release of vproc_t.");
ddbbfbc1 114 abort();
dcace88f 115 } else if (newval == 0) {
ddbbfbc1
A
116 mach_port_deallocate(mach_task_self(), vp->j_port);
117 free(vp);
118 }
119}
120
eabd1701 121#pragma mark Transactions
ddbbfbc1 122static void
eabd1701 123_vproc_transaction_init_once(void *arg __unused)
ddbbfbc1 124{
95379394
A
125 launch_globals_t globals = _launch_globals();
126
eabd1701
A
127 int64_t enable_transactions = 0;
128 (void)vproc_swap_integer(NULL, VPROC_GSK_TRANSACTIONS_ENABLED, 0, &enable_transactions);
129 if (enable_transactions != 0) {
95379394
A
130 (void)os_assumes_zero(proc_track_dirty(getpid(), PROC_DIRTY_TRACK));
131 globals->_vproc_transaction_enabled = 1;
eabd1701 132 }
95379394 133 globals->_vproc_transaction_queue = dispatch_queue_create("com.apple.idle-exit-queue", NULL);
eabd1701 134}
ddbbfbc1 135
eabd1701
A
136void
137_vproc_transactions_enable_internal(void *arg __unused)
138{
95379394
A
139 launch_globals_t globals = _launch_globals();
140
141 if (!globals->_vproc_transaction_enabled) {
142 (void)os_assumes_zero(proc_track_dirty(getpid(), PROC_DIRTY_TRACK));
143 globals->_vproc_transaction_enabled = 1;
144 }
ddbbfbc1 145
95379394
A
146 if (globals->_vproc_transaction_cnt > 0) {
147 (void)os_assumes_zero(proc_set_dirty(getpid(), true));
ddbbfbc1 148 }
eabd1701 149}
ddbbfbc1 150
eabd1701
A
151void
152_vproc_transactions_enable(void)
153{
95379394
A
154 launch_globals_t globals = _launch_globals();
155
156 dispatch_once_f(&globals->_vproc_transaction_once, NULL, _vproc_transaction_init_once);
157 dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transactions_enable_internal);
ddbbfbc1
A
158}
159
eabd1701
A
160void
161_vproc_transaction_begin_internal(void *ctx __unused)
ddbbfbc1 162{
95379394
A
163 launch_globals_t globals = _launch_globals();
164
165 int64_t new = ++globals->_vproc_transaction_cnt;
166 if (!globals->_vproc_transaction_enabled || new > 1) {
167 return;
168 }
169
170 if (new < 1) {
171 _vproc_set_crash_log_message("Underflow of transaction count.");
172 abort();
ddbbfbc1 173 }
95379394
A
174
175 (void)os_assumes_zero(proc_set_dirty(getpid(), true));
eabd1701
A
176}
177
178void
179_vproc_transaction_begin(void)
180{
95379394
A
181 launch_globals_t globals = _launch_globals();
182
183 dispatch_once_f(&globals->_vproc_transaction_once, NULL, _vproc_transaction_init_once);
184 dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transaction_begin_internal);
ddbbfbc1
A
185}
186
187vproc_transaction_t
eabd1701 188vproc_transaction_begin(vproc_t vp __unused)
ddbbfbc1 189{
ddbbfbc1 190 _vproc_transaction_begin();
ddbbfbc1 191
eabd1701
A
192 /* Return non-NULL on success. Originally, there were dreams of returning
193 * an object or something, but those never panned out.
194 */
95379394 195 return (vproc_transaction_t)vproc_transaction_begin;
ddbbfbc1
A
196}
197
95379394
A
198void _vproc_transaction_end_flush(void);
199
ddbbfbc1 200void
95379394 201_vproc_transaction_end_internal2(void *ctx)
ddbbfbc1 202{
95379394
A
203 launch_globals_t globals = _launch_globals();
204
205 globals->_vproc_gone2zero_callout(ctx);
206 _vproc_transaction_end_flush();
207}
208
209void
210_vproc_transaction_end_internal(void *arg)
211{
212 launch_globals_t globals = _launch_globals();
213
214 int64_t new = --globals->_vproc_transaction_cnt;
215 if (!globals->_vproc_transaction_enabled || new > 0) {
216 return;
217 }
218
219 if (new < 0) {
eabd1701 220 _vproc_set_crash_log_message("Underflow of transaction count.");
95379394 221 abort();
ddbbfbc1 222 }
95379394
A
223
224 if (globals->_vproc_gone2zero_callout && !arg) {
225 globals->_vproc_transaction_cnt = 1;
226 dispatch_async_f(globals->_vproc_gone2zero_queue, globals->_vproc_gone2zero_ctx, _vproc_transaction_end_internal2);
227 } else {
228 (void)os_assumes_zero(proc_set_dirty(getpid(), false));
229 }
230}
231
232void
233_vproc_transaction_end_flush2(void *ctx __unused)
234{
235 _vproc_transaction_end_internal((void *)1);
236}
237
238void
239_vproc_transaction_end_flush(void)
240{
241 launch_globals_t globals = _launch_globals();
242
243 dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transaction_end_flush2);
eabd1701 244}
ddbbfbc1 245
eabd1701
A
246void
247_vproc_transaction_end(void)
248{
95379394
A
249 launch_globals_t globals = _launch_globals();
250
251 dispatch_once_f(&globals->_vproc_transaction_once, NULL, _vproc_transaction_init_once);
252 dispatch_sync_f(globals->_vproc_transaction_queue, NULL, _vproc_transaction_end_internal);
eabd1701
A
253}
254
255void
256vproc_transaction_end(vproc_t vp __unused, vproc_transaction_t vpt __unused)
257{
258 _vproc_transaction_end();
ddbbfbc1
A
259}
260
261size_t
262_vproc_transaction_count(void)
263{
95379394
A
264 launch_globals_t globals = _launch_globals();
265
266 return globals->_vproc_transaction_cnt;
ddbbfbc1
A
267}
268
269size_t
270_vproc_standby_count(void)
271{
ddbbfbc1 272 return 0;
eabd1701 273}
ddbbfbc1
A
274
275size_t
276_vproc_standby_timeout(void)
277{
eabd1701 278 return 0;
ddbbfbc1
A
279}
280
281bool
282_vproc_pid_is_managed(pid_t p)
283{
284 boolean_t result = false;
285 vproc_mig_pid_is_managed(bootstrap_port, p, &result);
eabd1701 286
ddbbfbc1
A
287 return result;
288}
289
290kern_return_t
291_vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned)
292{
eabd1701
A
293 /* Activity Monitor relies on us returning this error code when the process
294 * is not opted into Instant Off.
295 */
296 kern_return_t error = BOOTSTRAP_NO_MEMORY;
ddbbfbc1 297
eabd1701
A
298 if (condemned) {
299 *condemned = false;
ddbbfbc1
A
300 }
301
eabd1701
A
302 if (count) {
303 uint32_t flags;
304 int ret = proc_get_dirty(p, &flags);
305 if (ret == 0) {
306 if (flags & PROC_DIRTY_TRACKED) {
307 *count = (flags & PROC_DIRTY_IS_DIRTY) ? 1 : 0;
308 error = BOOTSTRAP_SUCCESS;
309 } else {
310 error = BOOTSTRAP_NO_MEMORY;
311 }
312 } else if (ret == ENOTSUP) {
313 error = BOOTSTRAP_NO_MEMORY;
314 } else if (ret == ESRCH) {
315 error = BOOTSTRAP_UNKNOWN_SERVICE;
316 } else if (ret == EPERM) {
317 error = BOOTSTRAP_NOT_PRIVILEGED;
318 } else {
319 error = ret;
320 }
ddbbfbc1 321 }
eabd1701 322 return error;
dcace88f 323}
ddbbfbc1 324void
eabd1701 325_vproc_transaction_try_exit(int status)
ddbbfbc1 326{
eabd1701 327#if !TARGET_OS_EMBEDDED
95379394
A
328 launch_globals_t globals = _launch_globals();
329 if (globals->_vproc_transaction_cnt == 0) {
eabd1701 330 _exit(status);
ddbbfbc1 331 }
ddbbfbc1 332#else
eabd1701 333 _exit(status);
ddbbfbc1
A
334#endif
335}
336
337void
338_vproc_standby_begin(void)
339{
ddbbfbc1 340
ddbbfbc1
A
341}
342
eabd1701
A
343vproc_standby_t
344vproc_standby_begin(vproc_t vp __unused)
ddbbfbc1 345{
eabd1701 346 return (vproc_standby_t)vproc_standby_begin;
ddbbfbc1
A
347}
348
349void
350_vproc_standby_end(void)
351{
ddbbfbc1 352
ddbbfbc1 353}
5b0a4722 354
dcace88f 355void
95379394 356_vproc_transaction_set_clean_callback(dispatch_queue_t targetq, void *ctx, dispatch_function_t func)
dcace88f 357{
95379394 358 launch_globals_t globals = _launch_globals();
dcace88f 359
95379394
A
360 globals->_vproc_gone2zero_queue = targetq;
361 dispatch_retain(targetq);
362
363 globals->_vproc_gone2zero_callout = func;
364 globals->_vproc_gone2zero_ctx = ctx;
365}
dcace88f 366
eabd1701
A
367void
368vproc_standby_end(vproc_t vp __unused, vproc_standby_t vpt __unused)
369{
dcace88f 370
dcace88f
A
371}
372
eabd1701 373#pragma mark Miscellaneous SPI
5b0a4722 374kern_return_t
eabd1701
A
375_vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright,
376 launch_data_t *outval, mach_port_array_t *ports,
377 mach_msg_type_number_t *portCnt)
5b0a4722
A
378{
379 mach_msg_type_number_t outdata_cnt;
380 vm_offset_t outdata = 0;
381 size_t data_offset = 0;
382 launch_data_t out_obj;
383 kern_return_t kr;
384
385 if ((kr = vproc_mig_take_subset(bp, reqport, rcvright, &outdata, &outdata_cnt, ports, portCnt))) {
386 goto out;
387 }
388
389 if ((out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
390 *outval = launch_data_copy(out_obj);
391 } else {
392 kr = 1;
393 }
394
395out:
396 if (outdata) {
397 mig_deallocate(outdata, outdata_cnt);
398 }
399
400 return kr;
401}
402
5b0a4722 403vproc_err_t
ddbbfbc1 404_vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags)
5b0a4722 405{
5b0a4722
A
406 kern_return_t kr = 0;
407 bool is_bkgd = (strcmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0);
408 int64_t ldpid, lduid;
409
410 if (vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, 0, &ldpid) != 0) {
411 return (vproc_err_t)_vprocmgr_move_subset_to_user;
412 }
413
414 if (vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, 0, &lduid) != 0) {
415 return (vproc_err_t)_vprocmgr_move_subset_to_user;
416 }
417
418 if (!is_bkgd && ldpid != 1) {
419 if (lduid == getuid()) {
420 return NULL;
421 }
422 /*
423 * Not all sessions can be moved.
424 * We should clean up this mess someday.
425 */
426 return (vproc_err_t)_vprocmgr_move_subset_to_user;
427 }
428
eabd1701
A
429 mach_port_t puc = 0;
430 mach_port_t rootbs = MACH_PORT_NULL;
431 (void)bootstrap_get_root(bootstrap_port, &rootbs);
432
ddbbfbc1
A
433 if (vproc_mig_lookup_per_user_context(rootbs, target_user, &puc) != 0) {
434 return (vproc_err_t)_vprocmgr_move_subset_to_user;
435 }
eabd1701 436
dcace88f 437 if (is_bkgd) {
ddbbfbc1
A
438 task_set_bootstrap_port(mach_task_self(), puc);
439 mach_port_deallocate(mach_task_self(), bootstrap_port);
440 bootstrap_port = puc;
5b0a4722 441 } else {
ddbbfbc1
A
442 kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type, _audit_session_self(), flags);
443 mach_port_deallocate(mach_task_self(), puc);
5b0a4722 444 }
5b0a4722
A
445
446 if (kr) {
447 return (vproc_err_t)_vprocmgr_move_subset_to_user;
448 }
449
5b0a4722
A
450 return _vproc_post_fork_ping();
451}
452
ddbbfbc1
A
453vproc_err_t
454_vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags __attribute__((unused)))
455{
456 mach_port_t new_bsport = MACH_PORT_NULL;
457 kern_return_t kr = KERN_FAILURE;
458
459 mach_port_t tnp = MACH_PORT_NULL;
460 task_name_for_pid(mach_task_self(), getpid(), &tnp);
dcace88f 461 if ((kr = vproc_mig_switch_to_session(bootstrap_port, tnp, (char *)target_session, _audit_session_self(), &new_bsport)) != KERN_SUCCESS) {
ddbbfbc1
A
462 _vproc_log(LOG_NOTICE, "_vprocmgr_switch_to_session(): kr = 0x%x", kr);
463 return (vproc_err_t)_vprocmgr_switch_to_session;
464 }
eabd1701 465
ddbbfbc1
A
466 task_set_bootstrap_port(mach_task_self(), new_bsport);
467 mach_port_deallocate(mach_task_self(), bootstrap_port);
468 bootstrap_port = new_bsport;
eabd1701 469
ddbbfbc1
A
470 return !issetugid() ? _vproc_post_fork_ping() : NULL;
471}
472
473vproc_err_t
474_vprocmgr_detach_from_console(vproc_flags_t flags __attribute__((unused)))
475{
476 return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND, 0);
477}
5b0a4722 478
eabd1701
A
479vproc_err_t
480_vproc_post_fork_ping(void)
481{
95379394
A
482 mach_port_t session = MACH_PORT_NULL;
483 kern_return_t kr = vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session);
484 if (kr) {
485 return _vproc_post_fork_ping;
486 }
eabd1701 487
95379394
A
488 if (session) {
489 (void)_audit_session_join(session);
490 (void)mach_port_deallocate(mach_task_self(), session);
491 }
eabd1701 492
95379394 493 return NULL;
eabd1701
A
494}
495
496vproc_err_t
497_vprocmgr_init(const char *session_type)
498{
499 if (vproc_mig_init_session(bootstrap_port, (char *)session_type, _audit_session_self()) == 0) {
500 return NULL;
501 }
502
503 return (vproc_err_t)_vprocmgr_init;
504}
505
5b0a4722
A
506pid_t
507_spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version)
508{
509 size_t i, good_enough_size = 10*1024*1024;
510 mach_msg_type_number_t indata_cnt = 0;
511 vm_offset_t indata = 0;
512 mach_port_t obsvr_port = MACH_PORT_NULL;
513 launch_data_t tmp, tmp_array, in_obj;
514 const char *const *tmpp;
515 kern_return_t kr = 1;
516 void *buf = NULL;
517 pid_t p = -1;
518
519 if ((in_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)) == NULL) {
520 goto out;
521 }
522
523 if ((tmp = launch_data_new_string(label)) == NULL) {
524 goto out;
525 }
526
527 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_LABEL);
528
529 if ((tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY)) == NULL) {
530 goto out;
531 }
532
533 for (i = 0; *argv; i++, argv++) {
534 tmp = launch_data_new_string(*argv);
535 if (tmp == NULL) {
536 goto out;
537 }
538
539 launch_data_array_set_index(tmp_array, tmp, i);
540 }
541
542 launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
543
544 if (spawn_attrs) switch (struct_version) {
dcace88f 545 case 3:
5b0a4722 546 case 2:
f36da725 547#if HAVE_QUARANTINE
5b0a4722
A
548 if (spawn_attrs->spawn_quarantine) {
549 char qbuf[QTN_SERIALIZED_DATA_MAX];
550 size_t qbuf_sz = QTN_SERIALIZED_DATA_MAX;
551
552 if (qtn_proc_to_data(spawn_attrs->spawn_quarantine, qbuf, &qbuf_sz) == 0) {
553 tmp = launch_data_new_opaque(qbuf, qbuf_sz);
554 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_QUARANTINEDATA);
555 }
556 }
f36da725 557#endif
5b0a4722
A
558
559 if (spawn_attrs->spawn_seatbelt_profile) {
560 tmp = launch_data_new_string(spawn_attrs->spawn_seatbelt_profile);
561 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXPROFILE);
562 }
563
564 if (spawn_attrs->spawn_seatbelt_flags) {
565 tmp = launch_data_new_integer(*spawn_attrs->spawn_seatbelt_flags);
566 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXFLAGS);
567 }
568
569 /* fall through */
570 case 1:
571 if (spawn_attrs->spawn_binpref) {
572 tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY);
573 for (i = 0; i < spawn_attrs->spawn_binpref_cnt; i++) {
574 tmp = launch_data_new_integer(spawn_attrs->spawn_binpref[i]);
575 launch_data_array_set_index(tmp_array, tmp, i);
576 }
577 launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_BINARYORDERPREFERENCE);
578 }
579 /* fall through */
580 case 0:
581 if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_STOPPED) {
582 tmp = launch_data_new_bool(true);
583 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WAITFORDEBUGGER);
584 }
dcace88f
A
585 if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_TALAPP) {
586 tmp = launch_data_new_string(LAUNCH_KEY_POSIXSPAWNTYPE_TALAPP);
587 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_POSIXSPAWNTYPE);
588 }
dcace88f
A
589 if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_DISABLE_ASLR) {
590 tmp = launch_data_new_bool(true);
591 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_DISABLEASLR);
592 }
5b0a4722
A
593
594 if (spawn_attrs->spawn_env) {
595 launch_data_t tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
596
597 for (tmpp = spawn_attrs->spawn_env; *tmpp; tmpp++) {
598 char *eqoff, tmpstr[strlen(*tmpp) + 1];
599
600 strcpy(tmpstr, *tmpp);
601
602 eqoff = strchr(tmpstr, '=');
603
604 if (!eqoff) {
605 goto out;
606 }
eabd1701 607
5b0a4722 608 *eqoff = '\0';
eabd1701 609
5b0a4722
A
610 launch_data_dict_insert(tmp_dict, launch_data_new_string(eqoff + 1), tmpstr);
611 }
612
613 launch_data_dict_insert(in_obj, tmp_dict, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES);
614 }
615
616 if (spawn_attrs->spawn_path) {
617 tmp = launch_data_new_string(spawn_attrs->spawn_path);
618 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_PROGRAM);
619 }
620
621 if (spawn_attrs->spawn_chdir) {
622 tmp = launch_data_new_string(spawn_attrs->spawn_chdir);
623 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WORKINGDIRECTORY);
624 }
625
626 if (spawn_attrs->spawn_umask) {
627 tmp = launch_data_new_integer(*spawn_attrs->spawn_umask);
628 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_UMASK);
629 }
630
631 break;
632 default:
633 break;
634 }
635
636 if (!(buf = malloc(good_enough_size))) {
637 goto out;
638 }
639
640 if ((indata_cnt = launch_data_pack(in_obj, buf, good_enough_size, NULL, NULL)) == 0) {
641 goto out;
642 }
643
644 indata = (vm_offset_t)buf;
645
dcace88f
A
646 if (struct_version == 3) {
647 kr = vproc_mig_spawn2(bootstrap_port, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
648 } else {
649 _vproc_set_crash_log_message("Bogus version passed to _spawn_via_launchd(). For this release, the only valid version is 3.");
650 }
5b0a4722
A
651
652 if (kr == VPROC_ERR_TRY_PER_USER) {
653 mach_port_t puc;
654
655 if (vproc_mig_lookup_per_user_context(bootstrap_port, 0, &puc) == 0) {
dcace88f
A
656 if (struct_version == 3) {
657 kr = vproc_mig_spawn2(puc, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port);
658 }
5b0a4722
A
659 mach_port_deallocate(mach_task_self(), puc);
660 }
661 }
662
663out:
664 if (in_obj) {
665 launch_data_free(in_obj);
666 }
667
668 if (buf) {
669 free(buf);
670 }
671
672 switch (kr) {
673 case BOOTSTRAP_SUCCESS:
674 if (spawn_attrs && spawn_attrs->spawn_observer_port) {
675 *spawn_attrs->spawn_observer_port = obsvr_port;
676 } else {
dcace88f
A
677 if (struct_version == 3) {
678 mach_port_mod_refs(mach_task_self(), obsvr_port, MACH_PORT_RIGHT_RECEIVE, -1);
679 } else {
680 mach_port_deallocate(mach_task_self(), obsvr_port);
681 }
5b0a4722
A
682 }
683 return p;
684 case BOOTSTRAP_NOT_PRIVILEGED:
685 errno = EPERM; break;
686 case BOOTSTRAP_NO_MEMORY:
687 errno = ENOMEM; break;
688 case BOOTSTRAP_NAME_IN_USE:
689 errno = EEXIST; break;
690 case 1:
691 errno = EIO; break;
692 default:
693 errno = EINVAL; break;
694 }
695
696 return -1;
697}
698
699kern_return_t
dcace88f 700mpm_wait(mach_port_t ajob __attribute__((unused)), int *wstatus)
5b0a4722 701{
dcace88f
A
702 *wstatus = 0;
703 return 0;
5b0a4722
A
704}
705
706kern_return_t
dcace88f 707mpm_uncork_fork(mach_port_t ajob __attribute__((unused)))
5b0a4722 708{
dcace88f 709 return KERN_FAILURE;
5b0a4722
A
710}
711
712kern_return_t
713_vprocmgr_getsocket(name_t sockpath)
714{
715 return vproc_mig_getsocket(bootstrap_port, sockpath);
716}
717
718vproc_err_t
719_vproc_get_last_exit_status(int *wstatus)
720{
721 int64_t val;
722
723 if (vproc_swap_integer(NULL, VPROC_GSK_LAST_EXIT_STATUS, 0, &val) == 0) {
724 *wstatus = (int)val;
725 return NULL;
726 }
727
728 return (vproc_err_t)_vproc_get_last_exit_status;
729}
730
731vproc_err_t
732_vproc_send_signal_by_label(const char *label, int sig)
733{
734 if (vproc_mig_send_signal(bootstrap_port, (char *)label, sig) == 0) {
735 return NULL;
736 }
737
738 return _vproc_send_signal_by_label;
739}
740
741vproc_err_t
742_vprocmgr_log_forward(mach_port_t mp, void *data, size_t len)
743{
744 if (vproc_mig_log_forward(mp, (vm_offset_t)data, len) == 0) {
745 return NULL;
746 }
747
748 return _vprocmgr_log_forward;
749}
750
751vproc_err_t
752_vprocmgr_log_drain(vproc_t vp __attribute__((unused)), pthread_mutex_t *mutex, _vprocmgr_log_drain_callback_t func)
753{
754 mach_msg_type_number_t outdata_cnt, tmp_cnt;
755 vm_offset_t outdata = 0;
ddbbfbc1 756 struct timeval tv;
5b0a4722
A
757 struct logmsg_s *lm;
758
759 if (!func) {
760 return _vprocmgr_log_drain;
761 }
762
763 if (vproc_mig_log_drain(bootstrap_port, &outdata, &outdata_cnt) != 0) {
764 return _vprocmgr_log_drain;
765 }
766
767 tmp_cnt = outdata_cnt;
768
769 if (mutex) {
770 pthread_mutex_lock(mutex);
771 }
772
773 for (lm = (struct logmsg_s *)outdata; tmp_cnt > 0; lm = ((void *)lm + lm->obj_sz)) {
ddbbfbc1
A
774 lm->from_name = (char *)lm + lm->from_name_offset;
775 lm->about_name = (char *)lm + lm->about_name_offset;
776 lm->msg = (char *)lm + lm->msg_offset;
777 lm->session_name = (char *)lm + lm->session_name_offset;
778
779 tv.tv_sec = lm->when / USEC_PER_SEC;
780 tv.tv_usec = lm->when % USEC_PER_SEC;
5b0a4722 781
ddbbfbc1 782 func(&tv, lm->from_pid, lm->about_pid, lm->sender_uid, lm->sender_gid, lm->pri,
5b0a4722
A
783 lm->from_name, lm->about_name, lm->session_name, lm->msg);
784
785 tmp_cnt -= lm->obj_sz;
786 }
787
788 if (mutex) {
789 pthread_mutex_unlock(mutex);
790 }
791
792 if (outdata) {
793 mig_deallocate(outdata, outdata_cnt);
794 }
795
796 return NULL;
797}
798
799vproc_err_t
ddbbfbc1 800vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval)
5b0a4722 801{
ddbbfbc1 802 kern_return_t kr = KERN_FAILURE;
eabd1701 803 int64_t dummyval = 0;
ddbbfbc1
A
804 mach_port_t mp = vp ? vp->j_port : bootstrap_port;
805 if ((kr = vproc_mig_swap_integer(mp, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval)) == 0) {
5b0a4722 806 switch (key) {
dcace88f
A
807 case VPROC_GSK_PERUSER_SUSPEND:
808 if (dummyval) {
809 /* Wait for the per-user launchd to exit before returning. */
810 int kq = kqueue();
811 struct kevent kev;
812 EV_SET(&kev, dummyval, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, 0);
813 int r = kevent(kq, &kev, 1, &kev, 1, NULL);
814 (void)close(kq);
815 if (r != 1) {
816 return NULL;
817 }
818 break;
ddbbfbc1 819 }
5b0a4722
A
820 default:
821 break;
822 }
823 return NULL;
824 }
825
826 return (vproc_err_t)vproc_swap_integer;
827}
828
5b0a4722 829vproc_err_t
ddbbfbc1 830vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval)
5b0a4722
A
831{
832 size_t data_offset = 0, good_enough_size = 10*1024*1024;
833 mach_msg_type_number_t indata_cnt = 0, outdata_cnt;
834 vm_offset_t indata = 0, outdata = 0;
835 launch_data_t out_obj;
836 void *rval = vproc_swap_complex;
837 void *buf = NULL;
838
839 if (inval) {
840 if (!(buf = malloc(good_enough_size))) {
841 goto out;
842 }
843
844 if ((indata_cnt = launch_data_pack(inval, buf, good_enough_size, NULL, NULL)) == 0) {
845 goto out;
846 }
847
848 indata = (vm_offset_t)buf;
849 }
850
ddbbfbc1
A
851 mach_port_t mp = vp ? vp->j_port : bootstrap_port;
852 if (vproc_mig_swap_complex(mp, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) {
5b0a4722
A
853 goto out;
854 }
855
856 if (outval) {
857 if (!(out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
858 goto out;
859 }
860
861 if (!(*outval = launch_data_copy(out_obj))) {
862 goto out;
863 }
864 }
865
866 rval = NULL;
867out:
868 if (buf) {
869 free(buf);
870 }
871
872 if (outdata) {
873 mig_deallocate(outdata, outdata_cnt);
874 }
875
876 return rval;
877}
878
ddbbfbc1
A
879vproc_err_t
880vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr)
881{
882 launch_data_t instr_data = instr ? launch_data_new_string(instr) : NULL;
883 launch_data_t outstr_data = NULL;
eabd1701 884
ddbbfbc1 885 vproc_err_t verr = vproc_swap_complex(vp, key, instr_data, &outstr_data);
dcace88f
A
886 if (!verr && outstr) {
887 if (launch_data_get_type(outstr_data) == LAUNCH_DATA_STRING) {
ddbbfbc1
A
888 *outstr = strdup(launch_data_get_string(outstr_data));
889 } else {
890 verr = (vproc_err_t)vproc_swap_string;
891 }
892 launch_data_free(outstr_data);
893 }
dcace88f 894 if (instr_data) {
ddbbfbc1
A
895 launch_data_free(instr_data);
896 }
eabd1701 897
ddbbfbc1
A
898 return verr;
899}
900
5b0a4722
A
901void *
902reboot2(uint64_t flags)
903{
eabd1701
A
904 mach_port_t rootbs = MACH_PORT_NULL;
905 (void)bootstrap_get_root(bootstrap_port, &rootbs);
906 if (vproc_mig_reboot2(rootbs, flags) == 0) {
907 (void)mach_port_deallocate(mach_task_self(), rootbs);
5b0a4722
A
908 return NULL;
909 }
910
911 return reboot2;
912}
913
f36da725 914vproc_err_t
eabd1701
A
915_vproc_kickstart_by_label(const char *label, pid_t *out_pid,
916 mach_port_t *out_port_name __unused, mach_port_t *out_obsrvr_port __unused,
917 vproc_flags_t flags)
f36da725 918{
dcace88f
A
919 /* Ignore the two port parameters. This SPI isn't long for this world, and
920 * all the current clients just leak them anyway.
921 */
922 kern_return_t kr = vproc_mig_kickstart(bootstrap_port, (char *)label, out_pid, flags);
923 if (kr == KERN_SUCCESS) {
f36da725
A
924 return NULL;
925 }
926
927 return (vproc_err_t)_vproc_kickstart_by_label;
928}
929
5b0a4722
A
930vproc_err_t
931_vproc_set_global_on_demand(bool state)
932{
933 int64_t val = state ? ~0 : 0;
934
935 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_ON_DEMAND, &val, NULL) == 0) {
936 return NULL;
937 }
938
939 return (vproc_err_t)_vproc_set_global_on_demand;
940}
941
942void
943_vproc_logv(int pri, int err, const char *msg, va_list ap)
944{
945 char flat_msg[3000];
946
947 vsnprintf(flat_msg, sizeof(flat_msg), msg, ap);
948
949 vproc_mig_log(bootstrap_port, pri, err, flat_msg);
950}
951
952void
953_vproc_log(int pri, const char *msg, ...)
954{
955 va_list ap;
956
957 va_start(ap, msg);
958 _vproc_logv(pri, 0, msg, ap);
959 va_end(ap);
960}
961
962void
963_vproc_log_error(int pri, const char *msg, ...)
964{
965 int saved_errno = errno;
966 va_list ap;
967
968 va_start(ap, msg);
969 _vproc_logv(pri, saved_errno, msg, ap);
970 va_end(ap);
971}
dcace88f 972
dcace88f
A
973/* The type naming convention is as follows:
974 * For requests...
975 * union __RequestUnion__<userprefix><subsystem>_subsystem
976 * For replies...
977 * union __ReplyUnion__<userprefix><subsystem>_subsystem
978 */
979union maxmsgsz {
980 union __RequestUnion__helper_downcall_launchd_helper_subsystem req;
981 union __ReplyUnion__helper_downcall_launchd_helper_subsystem rep;
982};
983
95379394 984const size_t vprocmgr_helper_maxmsgsz = sizeof(union maxmsgsz);
dcace88f 985
dcace88f
A
986kern_return_t
987helper_recv_wait(mach_port_t p, int status)
988{
eabd1701
A
989#if __LAUNCH_MACH_PORT_CONTEXT_T_DEFINED__
990 mach_port_context_t ctx = status;
991#else
992 mach_vm_address_t ctx = status;
993#endif
994
995 return (errno = mach_port_set_context(mach_task_self(), p, ctx));
dcace88f
A
996}
997
998int
999launch_wait(mach_port_t port)
1000{
1001 int status = -1;
1002 errno = mach_msg_server_once(launchd_helper_server, vprocmgr_helper_maxmsgsz, port, 0);
1003 if (errno == MACH_MSG_SUCCESS) {
eabd1701
A
1004#if __LAUNCH_MACH_PORT_CONTEXT_T_DEFINED__
1005 mach_port_context_t ctx = 0;
1006#else
dcace88f 1007 mach_vm_address_t ctx = 0;
eabd1701 1008#endif
dcace88f
A
1009 if ((errno = mach_port_get_context(mach_task_self(), port, &ctx)) == KERN_SUCCESS) {
1010 status = ctx;
1011 }
1012 }
1013
1014 return status;
1015}
eabd1701
A
1016
1017launch_data_t
1018launch_socket_service_check_in(void)
1019{
1020 launch_data_t reply = NULL;
1021
1022 size_t big_enough = 10 * 1024;
1023 void *buff = malloc(big_enough);
1024 if (buff) {
1025 launch_data_t req = launch_data_new_string(LAUNCH_KEY_CHECKIN);
1026 if (req) {
1027 size_t sz = launch_data_pack(req, buff, big_enough, NULL, NULL);
1028 if (sz) {
1029 vm_address_t sreply = 0;
1030 mach_msg_size_t sreplyCnt = 0;
1031 mach_port_array_t fdps = NULL;
1032 mach_msg_size_t fdpsCnt = 0;
1033 kern_return_t kr = vproc_mig_legacy_ipc_request(bootstrap_port, (vm_address_t)buff, sz, NULL, 0, &sreply, &sreplyCnt, &fdps, &fdpsCnt, _audit_session_self());
1034 if (kr == BOOTSTRAP_SUCCESS) {
1035 int fds[128];
1036
1037 size_t i = 0;
1038 size_t nfds = fdpsCnt / sizeof(fdps[0]);
1039 for (i = 0; i < nfds; i++) {
1040 fds[i] = fileport_makefd(fdps[i]);
1041 (void)mach_port_deallocate(mach_task_self(), fdps[i]);
1042 }
1043
1044 size_t dataoff = 0;
1045 size_t fdoff = 0;
1046 reply = launch_data_unpack((void *)sreply, sreplyCnt, fds, nfds, &dataoff, &fdoff);
1047 reply = launch_data_copy(reply);
1048
1049 mig_deallocate(sreply, sreplyCnt);
1050 mig_deallocate((vm_address_t)fdps, fdpsCnt);
1051 }
1052 }
1053
1054 launch_data_free(req);
1055 }
1056
1057 free(buff);
1058 }
1059
1060 return reply;
1061}