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