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