]>
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> | |
39 | ||
f36da725 A |
40 | #if HAVE_QUARANTINE |
41 | #include <quarantine.h> | |
42 | #endif | |
5b0a4722 | 43 | |
ef398931 A |
44 | #include "launch.h" |
45 | #include "launch_priv.h" | |
46 | #include "launch_internal.h" | |
ddbbfbc1 | 47 | #include "launchd_ktrace.h" |
5b0a4722 A |
48 | |
49 | #include "protocol_vproc.h" | |
50 | ||
51 | #include "reboot2.h" | |
52 | ||
ddbbfbc1 A |
53 | #define likely(x) __builtin_expect((bool)(x), true) |
54 | #define unlikely(x) __builtin_expect((bool)(x), false) | |
55 | ||
5b0a4722 A |
56 | static mach_port_t get_root_bootstrap_port(void); |
57 | ||
ddbbfbc1 A |
58 | const char *__crashreporter_info__; /* this should get merged with other versions of the symbol */ |
59 | ||
5b0a4722 | 60 | static int64_t cached_pid = -1; |
ddbbfbc1 A |
61 | static struct vproc_shmem_s *vproc_shmem; |
62 | static pthread_once_t shmem_inited = PTHREAD_ONCE_INIT; | |
63 | static uint64_t s_cached_transactions_enabled = 0; | |
64 | ||
65 | struct vproc_s { | |
66 | int32_t refcount; | |
67 | mach_port_t j_port; | |
68 | }; | |
69 | ||
70 | vproc_t vprocmgr_lookup_vproc(const char *label) | |
71 | { | |
72 | struct vproc_s *vp = NULL; | |
73 | ||
74 | mach_port_t mp = MACH_PORT_NULL; | |
75 | kern_return_t kr = vproc_mig_port_for_label(bootstrap_port, (char *)label, &mp); | |
76 | if( kr == BOOTSTRAP_SUCCESS ) { | |
77 | vp = (struct vproc_s *)calloc(1, sizeof(struct vproc_s)); | |
78 | if( vp ) { | |
79 | vp->refcount = 1; | |
80 | mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, 1); | |
81 | vp->j_port = mp; | |
82 | } | |
83 | mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_SEND, -1); | |
84 | } | |
85 | ||
86 | return vp; | |
87 | } | |
88 | ||
89 | vproc_t vproc_retain(vproc_t vp) | |
90 | { | |
91 | int32_t orig = OSAtomicAdd32(1, &vp->refcount) - 1; | |
92 | if( orig <= 0 ) { | |
93 | /* We've gone from 0 to 1, which means that this object was due to be freed. */ | |
94 | __crashreporter_info__ = "Under-retain / over-release of vproc_t."; | |
95 | abort(); | |
96 | } | |
97 | ||
98 | return vp; | |
99 | } | |
100 | ||
101 | void vproc_release(vproc_t vp) | |
102 | { | |
103 | int32_t newval = OSAtomicAdd32(-1, &vp->refcount); | |
104 | if( newval < 0 ) { | |
105 | /* We're in negative numbers, which is bad. */ | |
106 | __crashreporter_info__ = "Over-release of vproc_t."; | |
107 | abort(); | |
108 | } else if( newval == 0 ) { | |
109 | mach_port_deallocate(mach_task_self(), vp->j_port); | |
110 | free(vp); | |
111 | } | |
112 | } | |
113 | ||
114 | static void | |
115 | vproc_shmem_init(void) | |
116 | { | |
117 | vm_address_t vm_addr = 0; | |
118 | mach_port_t shmem_port; | |
119 | kern_return_t kr; | |
120 | ||
121 | kr = vproc_mig_setup_shmem(bootstrap_port, &shmem_port); | |
122 | ||
123 | //assert(kr == 0); | |
124 | if (kr) { | |
125 | /* rdar://problem/6416724 | |
126 | * If we fail to set up a shared memory page, just allocate a local chunk | |
127 | * of memory. This way, processes can still introspect their own transaction | |
128 | * counts if they're being run under a debugger. Moral of the story: Debug | |
129 | * from the environment you intend to run in. | |
130 | */ | |
131 | void *_vm_addr = calloc(1, sizeof(struct vproc_shmem_s)); | |
132 | if( !_vm_addr ) { | |
133 | return; | |
134 | } | |
135 | ||
136 | vm_addr = (vm_address_t)_vm_addr; | |
137 | } else { | |
138 | kr = vm_map(mach_task_self(), &vm_addr, getpagesize(), 0, true, shmem_port, 0, false, | |
139 | VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE); | |
140 | ||
141 | //assert(kr == 0); | |
142 | if (kr) return; | |
143 | ||
144 | kr = mach_port_deallocate(mach_task_self(), shmem_port); | |
145 | ||
146 | //assert(kr == 0); | |
147 | } | |
148 | ||
149 | vproc_shmem = (struct vproc_shmem_s *)vm_addr; | |
150 | } | |
151 | ||
152 | static void | |
153 | vproc_client_init(void) | |
154 | { | |
155 | char *val = getenv(LAUNCHD_DO_APPLE_INTERNAL_LOGGING); | |
156 | if( val ) { | |
157 | if( strncmp(val, "true", sizeof("true") - 1) == 0 ) { | |
158 | do_apple_internal_logging = true; | |
159 | } | |
160 | } | |
161 | ||
162 | vproc_shmem_init(); | |
163 | } | |
164 | ||
165 | vproc_transaction_t | |
166 | vproc_transaction_begin(vproc_t vp __attribute__((unused))) | |
167 | { | |
168 | vproc_transaction_t vpt = (vproc_transaction_t)vproc_shmem_init; /* we need a "random" variable that is testable */ | |
169 | #if !TARGET_OS_EMBEDDED | |
170 | _vproc_transaction_begin(); | |
171 | #endif | |
172 | ||
173 | return vpt; | |
174 | } | |
175 | ||
176 | void | |
177 | _vproc_transaction_begin(void) | |
178 | { | |
179 | #if !TARGET_OS_EMBEDDED | |
180 | if (unlikely(vproc_shmem == NULL)) { | |
181 | int po_r = pthread_once(&shmem_inited, vproc_client_init); | |
182 | if (po_r != 0 || vproc_shmem == NULL) { | |
183 | return; | |
184 | } | |
185 | } | |
186 | ||
187 | typeof(vproc_shmem->vp_shmem_transaction_cnt) old = 0; | |
188 | do { | |
189 | old = vproc_shmem->vp_shmem_transaction_cnt; | |
190 | ||
191 | if (unlikely(old < 0)) { | |
192 | if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) { | |
193 | _exit(0); | |
194 | } else { | |
195 | __crashreporter_info__ = "Unbalanced: vproc_transaction_begin()"; | |
196 | } | |
197 | abort(); | |
198 | } | |
199 | } while( !__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, old, old + 1) ); | |
200 | ||
201 | runtime_ktrace(RTKT_VPROC_TRANSACTION_INCREMENT, old + 1, 0, 0); | |
202 | #endif | |
203 | } | |
204 | ||
205 | size_t | |
206 | _vproc_transaction_count(void) | |
207 | { | |
208 | return likely(vproc_shmem) ? vproc_shmem->vp_shmem_transaction_cnt : INT32_MAX; | |
209 | } | |
210 | ||
211 | size_t | |
212 | _vproc_standby_count(void) | |
213 | { | |
214 | #ifdef VPROC_STANDBY_IMPLEMENTED | |
215 | return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_cnt : INT32_MAX; | |
216 | #else | |
217 | return 0; | |
218 | #endif | |
219 | } | |
220 | ||
221 | size_t | |
222 | _vproc_standby_timeout(void) | |
223 | { | |
224 | return likely(vproc_shmem) ? vproc_shmem->vp_shmem_standby_timeout : 0; | |
225 | } | |
226 | ||
227 | bool | |
228 | _vproc_pid_is_managed(pid_t p) | |
229 | { | |
230 | boolean_t result = false; | |
231 | vproc_mig_pid_is_managed(bootstrap_port, p, &result); | |
232 | ||
233 | return result; | |
234 | } | |
235 | ||
236 | kern_return_t | |
237 | _vproc_transaction_count_for_pid(pid_t p, int32_t *count, bool *condemned) | |
238 | { | |
239 | boolean_t _condemned = false; | |
240 | kern_return_t kr = vproc_mig_transaction_count_for_pid(bootstrap_port, p, count, &_condemned); | |
241 | if( kr == KERN_SUCCESS && condemned ) { | |
242 | *condemned = _condemned ? true : false; | |
243 | } | |
244 | ||
245 | return kr; | |
246 | } | |
247 | ||
248 | void | |
249 | #if !TARGET_OS_EMBEDDED | |
250 | _vproc_transaction_try_exit(int status) | |
251 | { | |
252 | if (unlikely(vproc_shmem == NULL)) { | |
253 | return; | |
254 | } | |
255 | ||
256 | if (__sync_bool_compare_and_swap(&vproc_shmem->vp_shmem_transaction_cnt, 0, -1)) { | |
257 | vproc_shmem->vp_shmem_flags |= VPROC_SHMEM_EXITING; | |
258 | _exit(status); | |
259 | } | |
260 | } | |
261 | #else | |
262 | _vproc_transaction_try_exit(int status __attribute__((unused))) | |
263 | { | |
264 | ||
265 | } | |
266 | #endif | |
267 | ||
268 | void | |
269 | vproc_transaction_end(vproc_t vp __attribute__((unused)), vproc_transaction_t vpt) | |
270 | { | |
271 | if (unlikely(vpt != (vproc_transaction_t)vproc_shmem_init)) { | |
272 | __crashreporter_info__ = "Bogus transaction handle passed to vproc_transaction_end() "; | |
273 | abort(); | |
274 | } | |
275 | ||
276 | #if !TARGET_OS_EMBEDDED | |
277 | _vproc_transaction_end(); | |
278 | #endif | |
279 | } | |
280 | ||
281 | void | |
282 | _vproc_transaction_end(void) | |
283 | { | |
284 | #if !TARGET_OS_EMBEDDED | |
285 | typeof(vproc_shmem->vp_shmem_transaction_cnt) newval; | |
286 | ||
287 | if (unlikely(vproc_shmem == NULL)) { | |
288 | return; | |
289 | } | |
290 | ||
291 | newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_transaction_cnt, 1); | |
292 | ||
293 | runtime_ktrace(RTKT_VPROC_TRANSACTION_DECREMENT, newval, 0, 0); | |
294 | if (unlikely(newval < 0)) { | |
295 | if (vproc_shmem->vp_shmem_flags & VPROC_SHMEM_EXITING) { | |
296 | _exit(0); | |
297 | } else { | |
298 | __crashreporter_info__ = "Unbalanced: vproc_transaction_end()"; | |
299 | } | |
300 | abort(); | |
301 | } | |
302 | #endif | |
303 | } | |
304 | ||
305 | vproc_standby_t | |
306 | vproc_standby_begin(vproc_t vp __attribute__((unused))) | |
307 | { | |
308 | #ifdef VPROC_STANDBY_IMPLEMENTED | |
309 | vproc_standby_t vpsb = (vproc_standby_t)vproc_shmem_init; /* we need a "random" variable that is testable */ | |
310 | ||
311 | _vproc_standby_begin(); | |
312 | ||
313 | return vpsb; | |
314 | #else | |
315 | return NULL; | |
316 | #endif | |
317 | } | |
318 | ||
319 | void | |
320 | _vproc_standby_begin(void) | |
321 | { | |
322 | #ifdef VPROC_STANDBY_IMPLEMENTED | |
323 | typeof(vproc_shmem->vp_shmem_standby_cnt) newval; | |
324 | ||
325 | if (unlikely(vproc_shmem == NULL)) { | |
326 | int po_r = pthread_once(&shmem_inited, vproc_client_init); | |
327 | if (po_r != 0 || vproc_shmem == NULL) { | |
328 | return; | |
329 | } | |
330 | } | |
331 | ||
332 | newval = __sync_add_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1); | |
333 | ||
334 | if (unlikely(newval < 1)) { | |
335 | __crashreporter_info__ = "Unbalanced: vproc_standby_begin()"; | |
336 | abort(); | |
337 | } | |
338 | #else | |
339 | return; | |
340 | #endif | |
341 | } | |
342 | ||
343 | void | |
344 | vproc_standby_end(vproc_t vp __attribute__((unused)), vproc_standby_t vpt __attribute__((unused))) | |
345 | { | |
346 | #ifdef VPROC_STANDBY_IMPLEMENTED | |
347 | if (unlikely(vpt != (vproc_standby_t)vproc_shmem_init)) { | |
348 | __crashreporter_info__ = "Bogus standby handle passed to vproc_standby_end() "; | |
349 | abort(); | |
350 | } | |
351 | ||
352 | _vproc_standby_end(); | |
353 | #else | |
354 | return; | |
355 | #endif | |
356 | } | |
357 | ||
358 | void | |
359 | _vproc_standby_end(void) | |
360 | { | |
361 | #ifdef VPROC_STANDBY_IMPLEMENTED | |
362 | typeof(vproc_shmem->vp_shmem_standby_cnt) newval; | |
363 | ||
364 | if( unlikely(vproc_shmem == NULL) ) { | |
365 | __crashreporter_info__ = "Process called vproc_standby_end() when not enrolled in transaction model."; | |
366 | abort(); | |
367 | } | |
368 | ||
369 | newval = __sync_sub_and_fetch(&vproc_shmem->vp_shmem_standby_cnt, 1); | |
370 | ||
371 | if (unlikely(newval < 0)) { | |
372 | __crashreporter_info__ = "Unbalanced: vproc_standby_end()"; | |
373 | abort(); | |
374 | } | |
375 | #else | |
376 | return; | |
377 | #endif | |
378 | } | |
5b0a4722 A |
379 | |
380 | kern_return_t | |
381 | _vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, launch_data_t *outval, | |
382 | mach_port_array_t *ports, mach_msg_type_number_t *portCnt) | |
383 | { | |
384 | mach_msg_type_number_t outdata_cnt; | |
385 | vm_offset_t outdata = 0; | |
386 | size_t data_offset = 0; | |
387 | launch_data_t out_obj; | |
388 | kern_return_t kr; | |
389 | ||
390 | if ((kr = vproc_mig_take_subset(bp, reqport, rcvright, &outdata, &outdata_cnt, ports, portCnt))) { | |
391 | goto out; | |
392 | } | |
393 | ||
394 | if ((out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) { | |
395 | *outval = launch_data_copy(out_obj); | |
396 | } else { | |
397 | kr = 1; | |
398 | } | |
399 | ||
400 | out: | |
401 | if (outdata) { | |
402 | mig_deallocate(outdata, outdata_cnt); | |
403 | } | |
404 | ||
405 | return kr; | |
406 | } | |
407 | ||
408 | vproc_err_t | |
409 | _vproc_post_fork_ping(void) | |
410 | { | |
ddbbfbc1 A |
411 | #if !TARGET_OS_EMBEDDED |
412 | au_asid_t s = AU_DEFAUDITSID; | |
413 | do { | |
414 | mach_port_t session = MACH_PORT_NULL; | |
415 | kern_return_t kr = vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session); | |
416 | if( kr != KERN_SUCCESS ) { | |
417 | /* If this happens, our bootstrap port probably got hosed. */ | |
418 | _vproc_log(LOG_ERR, "Post-fork ping failed!"); | |
419 | break; | |
420 | } | |
421 | ||
422 | /* If we get back MACH_PORT_NULL, that means we just stick with the session | |
423 | * we inherited across fork(2). | |
424 | */ | |
425 | if( session == MACH_PORT_NULL ) { | |
426 | s = ~AU_DEFAUDITSID; | |
427 | break; | |
428 | } | |
429 | ||
430 | s = _audit_session_join(session); | |
431 | if( s == 0 ) { | |
432 | _vproc_log_error(LOG_ERR, "Could not join security session!"); | |
433 | s = AU_DEFAUDITSID; | |
434 | } else { | |
435 | _vproc_log(LOG_DEBUG, "Joined session %d.", s); | |
436 | } | |
437 | } while( 0 ); | |
438 | ||
439 | return s != AU_DEFAUDITSID ? NULL : _vproc_post_fork_ping; | |
440 | #else | |
441 | mach_port_t session = MACH_PORT_NULL; | |
442 | return vproc_mig_post_fork_ping(bootstrap_port, mach_task_self(), &session) ? _vproc_post_fork_ping : NULL; | |
443 | #endif | |
5b0a4722 A |
444 | } |
445 | ||
446 | vproc_err_t | |
447 | _vprocmgr_init(const char *session_type) | |
448 | { | |
ddbbfbc1 | 449 | if (vproc_mig_init_session(bootstrap_port, (char *)session_type, _audit_session_self()) == 0) { |
5b0a4722 A |
450 | return NULL; |
451 | } | |
452 | ||
453 | return (vproc_err_t)_vprocmgr_init; | |
454 | } | |
455 | ||
456 | vproc_err_t | |
ddbbfbc1 | 457 | _vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type, uint64_t flags) |
5b0a4722 | 458 | { |
5b0a4722 A |
459 | kern_return_t kr = 0; |
460 | bool is_bkgd = (strcmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0); | |
461 | int64_t ldpid, lduid; | |
462 | ||
463 | if (vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, 0, &ldpid) != 0) { | |
464 | return (vproc_err_t)_vprocmgr_move_subset_to_user; | |
465 | } | |
466 | ||
467 | if (vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, 0, &lduid) != 0) { | |
468 | return (vproc_err_t)_vprocmgr_move_subset_to_user; | |
469 | } | |
470 | ||
471 | if (!is_bkgd && ldpid != 1) { | |
472 | if (lduid == getuid()) { | |
473 | return NULL; | |
474 | } | |
475 | /* | |
476 | * Not all sessions can be moved. | |
477 | * We should clean up this mess someday. | |
478 | */ | |
479 | return (vproc_err_t)_vprocmgr_move_subset_to_user; | |
480 | } | |
481 | ||
ddbbfbc1 A |
482 | mach_port_t puc = 0, rootbs = get_root_bootstrap_port(); |
483 | ||
484 | if (vproc_mig_lookup_per_user_context(rootbs, target_user, &puc) != 0) { | |
485 | return (vproc_err_t)_vprocmgr_move_subset_to_user; | |
486 | } | |
487 | ||
488 | if( is_bkgd ) { | |
489 | task_set_bootstrap_port(mach_task_self(), puc); | |
490 | mach_port_deallocate(mach_task_self(), bootstrap_port); | |
491 | bootstrap_port = puc; | |
5b0a4722 | 492 | } else { |
ddbbfbc1 A |
493 | kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type, _audit_session_self(), flags); |
494 | mach_port_deallocate(mach_task_self(), puc); | |
5b0a4722 | 495 | } |
ddbbfbc1 | 496 | |
5b0a4722 A |
497 | cached_pid = -1; |
498 | ||
499 | if (kr) { | |
500 | return (vproc_err_t)_vprocmgr_move_subset_to_user; | |
501 | } | |
502 | ||
5b0a4722 A |
503 | return _vproc_post_fork_ping(); |
504 | } | |
505 | ||
ddbbfbc1 A |
506 | vproc_err_t |
507 | _vprocmgr_switch_to_session(const char *target_session, vproc_flags_t flags __attribute__((unused))) | |
508 | { | |
509 | mach_port_t new_bsport = MACH_PORT_NULL; | |
510 | kern_return_t kr = KERN_FAILURE; | |
511 | ||
512 | mach_port_t tnp = MACH_PORT_NULL; | |
513 | task_name_for_pid(mach_task_self(), getpid(), &tnp); | |
514 | if( (kr = vproc_mig_switch_to_session(bootstrap_port, tnp, (char *)target_session, _audit_session_self(), &new_bsport)) != KERN_SUCCESS ) { | |
515 | _vproc_log(LOG_NOTICE, "_vprocmgr_switch_to_session(): kr = 0x%x", kr); | |
516 | return (vproc_err_t)_vprocmgr_switch_to_session; | |
517 | } | |
518 | ||
519 | task_set_bootstrap_port(mach_task_self(), new_bsport); | |
520 | mach_port_deallocate(mach_task_self(), bootstrap_port); | |
521 | bootstrap_port = new_bsport; | |
522 | ||
523 | return !issetugid() ? _vproc_post_fork_ping() : NULL; | |
524 | } | |
525 | ||
526 | vproc_err_t | |
527 | _vprocmgr_detach_from_console(vproc_flags_t flags __attribute__((unused))) | |
528 | { | |
529 | return _vprocmgr_switch_to_session(VPROCMGR_SESSION_BACKGROUND, 0); | |
530 | } | |
5b0a4722 A |
531 | |
532 | pid_t | |
533 | _spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version) | |
534 | { | |
535 | size_t i, good_enough_size = 10*1024*1024; | |
536 | mach_msg_type_number_t indata_cnt = 0; | |
537 | vm_offset_t indata = 0; | |
538 | mach_port_t obsvr_port = MACH_PORT_NULL; | |
539 | launch_data_t tmp, tmp_array, in_obj; | |
540 | const char *const *tmpp; | |
541 | kern_return_t kr = 1; | |
542 | void *buf = NULL; | |
543 | pid_t p = -1; | |
544 | ||
545 | if ((in_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)) == NULL) { | |
546 | goto out; | |
547 | } | |
548 | ||
549 | if ((tmp = launch_data_new_string(label)) == NULL) { | |
550 | goto out; | |
551 | } | |
552 | ||
553 | launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_LABEL); | |
554 | ||
555 | if ((tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY)) == NULL) { | |
556 | goto out; | |
557 | } | |
558 | ||
559 | for (i = 0; *argv; i++, argv++) { | |
560 | tmp = launch_data_new_string(*argv); | |
561 | if (tmp == NULL) { | |
562 | goto out; | |
563 | } | |
564 | ||
565 | launch_data_array_set_index(tmp_array, tmp, i); | |
566 | } | |
567 | ||
568 | launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_PROGRAMARGUMENTS); | |
569 | ||
570 | if (spawn_attrs) switch (struct_version) { | |
571 | case 2: | |
f36da725 | 572 | #if HAVE_QUARANTINE |
5b0a4722 A |
573 | if (spawn_attrs->spawn_quarantine) { |
574 | char qbuf[QTN_SERIALIZED_DATA_MAX]; | |
575 | size_t qbuf_sz = QTN_SERIALIZED_DATA_MAX; | |
576 | ||
577 | if (qtn_proc_to_data(spawn_attrs->spawn_quarantine, qbuf, &qbuf_sz) == 0) { | |
578 | tmp = launch_data_new_opaque(qbuf, qbuf_sz); | |
579 | launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_QUARANTINEDATA); | |
580 | } | |
581 | } | |
f36da725 | 582 | #endif |
5b0a4722 A |
583 | |
584 | if (spawn_attrs->spawn_seatbelt_profile) { | |
585 | tmp = launch_data_new_string(spawn_attrs->spawn_seatbelt_profile); | |
586 | launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXPROFILE); | |
587 | } | |
588 | ||
589 | if (spawn_attrs->spawn_seatbelt_flags) { | |
590 | tmp = launch_data_new_integer(*spawn_attrs->spawn_seatbelt_flags); | |
591 | launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXFLAGS); | |
592 | } | |
593 | ||
594 | /* fall through */ | |
595 | case 1: | |
596 | if (spawn_attrs->spawn_binpref) { | |
597 | tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY); | |
598 | for (i = 0; i < spawn_attrs->spawn_binpref_cnt; i++) { | |
599 | tmp = launch_data_new_integer(spawn_attrs->spawn_binpref[i]); | |
600 | launch_data_array_set_index(tmp_array, tmp, i); | |
601 | } | |
602 | launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_BINARYORDERPREFERENCE); | |
603 | } | |
604 | /* fall through */ | |
605 | case 0: | |
606 | if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_STOPPED) { | |
607 | tmp = launch_data_new_bool(true); | |
608 | launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WAITFORDEBUGGER); | |
609 | } | |
610 | ||
611 | if (spawn_attrs->spawn_env) { | |
612 | launch_data_t tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY); | |
613 | ||
614 | for (tmpp = spawn_attrs->spawn_env; *tmpp; tmpp++) { | |
615 | char *eqoff, tmpstr[strlen(*tmpp) + 1]; | |
616 | ||
617 | strcpy(tmpstr, *tmpp); | |
618 | ||
619 | eqoff = strchr(tmpstr, '='); | |
620 | ||
621 | if (!eqoff) { | |
622 | goto out; | |
623 | } | |
624 | ||
625 | *eqoff = '\0'; | |
626 | ||
627 | launch_data_dict_insert(tmp_dict, launch_data_new_string(eqoff + 1), tmpstr); | |
628 | } | |
629 | ||
630 | launch_data_dict_insert(in_obj, tmp_dict, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES); | |
631 | } | |
632 | ||
633 | if (spawn_attrs->spawn_path) { | |
634 | tmp = launch_data_new_string(spawn_attrs->spawn_path); | |
635 | launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_PROGRAM); | |
636 | } | |
637 | ||
638 | if (spawn_attrs->spawn_chdir) { | |
639 | tmp = launch_data_new_string(spawn_attrs->spawn_chdir); | |
640 | launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WORKINGDIRECTORY); | |
641 | } | |
642 | ||
643 | if (spawn_attrs->spawn_umask) { | |
644 | tmp = launch_data_new_integer(*spawn_attrs->spawn_umask); | |
645 | launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_UMASK); | |
646 | } | |
647 | ||
648 | break; | |
649 | default: | |
650 | break; | |
651 | } | |
652 | ||
653 | if (!(buf = malloc(good_enough_size))) { | |
654 | goto out; | |
655 | } | |
656 | ||
657 | if ((indata_cnt = launch_data_pack(in_obj, buf, good_enough_size, NULL, NULL)) == 0) { | |
658 | goto out; | |
659 | } | |
660 | ||
661 | indata = (vm_offset_t)buf; | |
662 | ||
ddbbfbc1 | 663 | kr = vproc_mig_spawn(bootstrap_port, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port); |
5b0a4722 A |
664 | |
665 | if (kr == VPROC_ERR_TRY_PER_USER) { | |
666 | mach_port_t puc; | |
667 | ||
668 | if (vproc_mig_lookup_per_user_context(bootstrap_port, 0, &puc) == 0) { | |
ddbbfbc1 | 669 | kr = vproc_mig_spawn(puc, indata, indata_cnt, _audit_session_self(), &p, &obsvr_port); |
5b0a4722 A |
670 | mach_port_deallocate(mach_task_self(), puc); |
671 | } | |
672 | } | |
673 | ||
674 | out: | |
675 | if (in_obj) { | |
676 | launch_data_free(in_obj); | |
677 | } | |
678 | ||
679 | if (buf) { | |
680 | free(buf); | |
681 | } | |
682 | ||
683 | switch (kr) { | |
684 | case BOOTSTRAP_SUCCESS: | |
685 | if (spawn_attrs && spawn_attrs->spawn_observer_port) { | |
686 | *spawn_attrs->spawn_observer_port = obsvr_port; | |
687 | } else { | |
688 | mach_port_deallocate(mach_task_self(), obsvr_port); | |
689 | } | |
690 | return p; | |
691 | case BOOTSTRAP_NOT_PRIVILEGED: | |
692 | errno = EPERM; break; | |
693 | case BOOTSTRAP_NO_MEMORY: | |
694 | errno = ENOMEM; break; | |
695 | case BOOTSTRAP_NAME_IN_USE: | |
696 | errno = EEXIST; break; | |
697 | case 1: | |
698 | errno = EIO; break; | |
699 | default: | |
700 | errno = EINVAL; break; | |
701 | } | |
702 | ||
703 | return -1; | |
704 | } | |
705 | ||
706 | kern_return_t | |
ddbbfbc1 | 707 | mpm_wait(mach_port_t ajob, int *wstatus) |
5b0a4722 A |
708 | { |
709 | return vproc_mig_wait(ajob, wstatus); | |
710 | } | |
711 | ||
712 | kern_return_t | |
713 | mpm_uncork_fork(mach_port_t ajob) | |
714 | { | |
715 | return vproc_mig_uncork_fork(ajob); | |
716 | } | |
717 | ||
718 | kern_return_t | |
719 | _vprocmgr_getsocket(name_t sockpath) | |
720 | { | |
721 | return vproc_mig_getsocket(bootstrap_port, sockpath); | |
722 | } | |
723 | ||
724 | vproc_err_t | |
725 | _vproc_get_last_exit_status(int *wstatus) | |
726 | { | |
727 | int64_t val; | |
728 | ||
729 | if (vproc_swap_integer(NULL, VPROC_GSK_LAST_EXIT_STATUS, 0, &val) == 0) { | |
730 | *wstatus = (int)val; | |
731 | return NULL; | |
732 | } | |
733 | ||
734 | return (vproc_err_t)_vproc_get_last_exit_status; | |
735 | } | |
736 | ||
737 | vproc_err_t | |
738 | _vproc_send_signal_by_label(const char *label, int sig) | |
739 | { | |
740 | if (vproc_mig_send_signal(bootstrap_port, (char *)label, sig) == 0) { | |
741 | return NULL; | |
742 | } | |
743 | ||
744 | return _vproc_send_signal_by_label; | |
745 | } | |
746 | ||
747 | vproc_err_t | |
748 | _vprocmgr_log_forward(mach_port_t mp, void *data, size_t len) | |
749 | { | |
750 | if (vproc_mig_log_forward(mp, (vm_offset_t)data, len) == 0) { | |
751 | return NULL; | |
752 | } | |
753 | ||
754 | return _vprocmgr_log_forward; | |
755 | } | |
756 | ||
757 | vproc_err_t | |
758 | _vprocmgr_log_drain(vproc_t vp __attribute__((unused)), pthread_mutex_t *mutex, _vprocmgr_log_drain_callback_t func) | |
759 | { | |
760 | mach_msg_type_number_t outdata_cnt, tmp_cnt; | |
761 | vm_offset_t outdata = 0; | |
ddbbfbc1 | 762 | struct timeval tv; |
5b0a4722 A |
763 | struct logmsg_s *lm; |
764 | ||
765 | if (!func) { | |
766 | return _vprocmgr_log_drain; | |
767 | } | |
768 | ||
769 | if (vproc_mig_log_drain(bootstrap_port, &outdata, &outdata_cnt) != 0) { | |
770 | return _vprocmgr_log_drain; | |
771 | } | |
772 | ||
773 | tmp_cnt = outdata_cnt; | |
774 | ||
775 | if (mutex) { | |
776 | pthread_mutex_lock(mutex); | |
777 | } | |
778 | ||
779 | for (lm = (struct logmsg_s *)outdata; tmp_cnt > 0; lm = ((void *)lm + lm->obj_sz)) { | |
ddbbfbc1 A |
780 | lm->from_name = (char *)lm + lm->from_name_offset; |
781 | lm->about_name = (char *)lm + lm->about_name_offset; | |
782 | lm->msg = (char *)lm + lm->msg_offset; | |
783 | lm->session_name = (char *)lm + lm->session_name_offset; | |
784 | ||
785 | tv.tv_sec = lm->when / USEC_PER_SEC; | |
786 | tv.tv_usec = lm->when % USEC_PER_SEC; | |
5b0a4722 | 787 | |
ddbbfbc1 | 788 | func(&tv, lm->from_pid, lm->about_pid, lm->sender_uid, lm->sender_gid, lm->pri, |
5b0a4722 A |
789 | lm->from_name, lm->about_name, lm->session_name, lm->msg); |
790 | ||
791 | tmp_cnt -= lm->obj_sz; | |
792 | } | |
793 | ||
794 | if (mutex) { | |
795 | pthread_mutex_unlock(mutex); | |
796 | } | |
797 | ||
798 | if (outdata) { | |
799 | mig_deallocate(outdata, outdata_cnt); | |
800 | } | |
801 | ||
802 | return NULL; | |
803 | } | |
804 | ||
805 | vproc_err_t | |
ddbbfbc1 | 806 | vproc_swap_integer(vproc_t vp, vproc_gsk_t key, int64_t *inval, int64_t *outval) |
5b0a4722 A |
807 | { |
808 | static int64_t cached_is_managed = -1; | |
809 | int64_t dummyval = 0; | |
810 | ||
811 | switch (key) { | |
812 | case VPROC_GSK_MGR_PID: | |
813 | if (cached_pid != -1 && outval) { | |
814 | *outval = cached_pid; | |
815 | return NULL; | |
816 | } | |
817 | break; | |
818 | case VPROC_GSK_IS_MANAGED: | |
819 | if (cached_is_managed != -1 && outval) { | |
820 | *outval = cached_is_managed; | |
821 | return NULL; | |
822 | } | |
823 | break; | |
ddbbfbc1 A |
824 | case VPROC_GSK_TRANSACTIONS_ENABLED: |
825 | /* Shared memory region is required for transactions. */ | |
826 | if( unlikely(vproc_shmem == NULL) ) { | |
827 | int po_r = pthread_once(&shmem_inited, vproc_client_init); | |
828 | if( po_r != 0 || vproc_shmem == NULL ) { | |
829 | if( outval ) { | |
830 | *outval = -1; | |
831 | } | |
832 | return (vproc_err_t)vproc_swap_integer; | |
833 | } | |
834 | } | |
835 | ||
836 | if( s_cached_transactions_enabled && outval ) { | |
837 | *outval = s_cached_transactions_enabled; | |
838 | return NULL; | |
839 | } | |
840 | break; | |
5b0a4722 A |
841 | default: |
842 | break; | |
843 | } | |
844 | ||
ddbbfbc1 A |
845 | kern_return_t kr = KERN_FAILURE; |
846 | mach_port_t mp = vp ? vp->j_port : bootstrap_port; | |
847 | if ((kr = vproc_mig_swap_integer(mp, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval)) == 0) { | |
5b0a4722 A |
848 | switch (key) { |
849 | case VPROC_GSK_MGR_PID: | |
850 | cached_pid = outval ? *outval : dummyval; | |
851 | break; | |
852 | case VPROC_GSK_IS_MANAGED: | |
853 | cached_is_managed = outval ? *outval : dummyval; | |
854 | break; | |
ddbbfbc1 A |
855 | case VPROC_GSK_TRANSACTIONS_ENABLED: |
856 | /* Once you're in the transaction model, you're in for good. Like the Mafia. */ | |
857 | s_cached_transactions_enabled = 1; | |
858 | break; | |
859 | case VPROC_GSK_PERUSER_SUSPEND: { | |
860 | char peruser_label[NAME_MAX]; | |
861 | snprintf(peruser_label, NAME_MAX - 1, "com.apple.launchd.peruser.%u", (uid_t)*inval); | |
862 | ||
863 | vproc_t pu_vp = vprocmgr_lookup_vproc(peruser_label); | |
864 | if( pu_vp ) { | |
865 | int status = 0; | |
866 | kr = vproc_mig_wait2(bootstrap_port, pu_vp->j_port, &status, false); | |
867 | vproc_release(pu_vp); | |
868 | } | |
869 | break; | |
870 | } | |
5b0a4722 A |
871 | default: |
872 | break; | |
873 | } | |
874 | return NULL; | |
875 | } | |
876 | ||
877 | return (vproc_err_t)vproc_swap_integer; | |
878 | } | |
879 | ||
880 | mach_port_t | |
881 | get_root_bootstrap_port(void) | |
882 | { | |
883 | mach_port_t parent_port = 0; | |
884 | mach_port_t previous_port = 0; | |
885 | ||
886 | do { | |
887 | if (previous_port) { | |
888 | if (previous_port != bootstrap_port) { | |
889 | mach_port_deallocate(mach_task_self(), previous_port); | |
890 | } | |
891 | previous_port = parent_port; | |
892 | } else { | |
893 | previous_port = bootstrap_port; | |
894 | } | |
895 | ||
896 | if (bootstrap_parent(previous_port, &parent_port) != 0) { | |
897 | return MACH_PORT_NULL; | |
898 | } | |
899 | ||
900 | } while (parent_port != previous_port); | |
901 | ||
902 | return parent_port; | |
903 | } | |
904 | ||
905 | vproc_err_t | |
ddbbfbc1 | 906 | vproc_swap_complex(vproc_t vp, vproc_gsk_t key, launch_data_t inval, launch_data_t *outval) |
5b0a4722 A |
907 | { |
908 | size_t data_offset = 0, good_enough_size = 10*1024*1024; | |
909 | mach_msg_type_number_t indata_cnt = 0, outdata_cnt; | |
910 | vm_offset_t indata = 0, outdata = 0; | |
911 | launch_data_t out_obj; | |
912 | void *rval = vproc_swap_complex; | |
913 | void *buf = NULL; | |
914 | ||
915 | if (inval) { | |
916 | if (!(buf = malloc(good_enough_size))) { | |
917 | goto out; | |
918 | } | |
919 | ||
920 | if ((indata_cnt = launch_data_pack(inval, buf, good_enough_size, NULL, NULL)) == 0) { | |
921 | goto out; | |
922 | } | |
923 | ||
924 | indata = (vm_offset_t)buf; | |
925 | } | |
926 | ||
ddbbfbc1 A |
927 | mach_port_t mp = vp ? vp->j_port : bootstrap_port; |
928 | if (vproc_mig_swap_complex(mp, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) { | |
5b0a4722 A |
929 | goto out; |
930 | } | |
931 | ||
932 | if (outval) { | |
933 | if (!(out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) { | |
934 | goto out; | |
935 | } | |
936 | ||
937 | if (!(*outval = launch_data_copy(out_obj))) { | |
938 | goto out; | |
939 | } | |
940 | } | |
941 | ||
942 | rval = NULL; | |
943 | out: | |
944 | if (buf) { | |
945 | free(buf); | |
946 | } | |
947 | ||
948 | if (outdata) { | |
949 | mig_deallocate(outdata, outdata_cnt); | |
950 | } | |
951 | ||
952 | return rval; | |
953 | } | |
954 | ||
ddbbfbc1 A |
955 | vproc_err_t |
956 | vproc_swap_string(vproc_t vp, vproc_gsk_t key, const char *instr, char **outstr) | |
957 | { | |
958 | launch_data_t instr_data = instr ? launch_data_new_string(instr) : NULL; | |
959 | launch_data_t outstr_data = NULL; | |
960 | ||
961 | vproc_err_t verr = vproc_swap_complex(vp, key, instr_data, &outstr_data); | |
962 | if( !verr && outstr ) { | |
963 | if( launch_data_get_type(outstr_data) == LAUNCH_DATA_STRING ) { | |
964 | *outstr = strdup(launch_data_get_string(outstr_data)); | |
965 | } else { | |
966 | verr = (vproc_err_t)vproc_swap_string; | |
967 | } | |
968 | launch_data_free(outstr_data); | |
969 | } | |
970 | if( instr_data ) { | |
971 | launch_data_free(instr_data); | |
972 | } | |
973 | ||
974 | return verr; | |
975 | } | |
976 | ||
5b0a4722 A |
977 | void * |
978 | reboot2(uint64_t flags) | |
979 | { | |
980 | if (vproc_mig_reboot2(get_root_bootstrap_port(), flags) == 0) { | |
981 | return NULL; | |
982 | } | |
983 | ||
984 | return reboot2; | |
985 | } | |
986 | ||
f36da725 | 987 | vproc_err_t |
ddbbfbc1 | 988 | _vproc_kickstart_by_label(const char *label, pid_t *out_pid, mach_port_t *out_port_name, mach_port_t *out_obsrvr_port, vproc_flags_t flags) |
f36da725 | 989 | { |
ddbbfbc1 A |
990 | mach_port_t junk = MACH_PORT_NULL; |
991 | mach_port_t junk2 = MACH_PORT_NULL; | |
992 | ||
993 | kern_return_t kr = vproc_mig_kickstart(bootstrap_port, (char *)label, out_pid, out_port_name ?: &junk, out_obsrvr_port ?: &junk2, flags); | |
994 | if( kr == KERN_SUCCESS ) { | |
995 | if( !out_port_name ) { | |
996 | mach_port_mod_refs(mach_task_self(), junk, MACH_PORT_RIGHT_SEND, -1); | |
997 | } | |
998 | ||
999 | if( !out_obsrvr_port ) { | |
1000 | mach_port_mod_refs(mach_task_self(), junk2, MACH_PORT_RIGHT_SEND, -1); | |
1001 | } | |
1002 | ||
f36da725 A |
1003 | return NULL; |
1004 | } | |
1005 | ||
1006 | return (vproc_err_t)_vproc_kickstart_by_label; | |
1007 | } | |
1008 | ||
1009 | vproc_err_t | |
1010 | _vproc_wait_by_label(const char *label, int *out_wstatus) | |
1011 | { | |
1012 | if (vproc_mig_embedded_wait(bootstrap_port, (char *)label, out_wstatus) == 0) { | |
1013 | return NULL; | |
1014 | } | |
1015 | ||
1016 | return (vproc_err_t)_vproc_wait_by_label; | |
1017 | } | |
1018 | ||
5b0a4722 A |
1019 | vproc_err_t |
1020 | _vproc_set_global_on_demand(bool state) | |
1021 | { | |
1022 | int64_t val = state ? ~0 : 0; | |
1023 | ||
1024 | if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_ON_DEMAND, &val, NULL) == 0) { | |
1025 | return NULL; | |
1026 | } | |
1027 | ||
1028 | return (vproc_err_t)_vproc_set_global_on_demand; | |
1029 | } | |
1030 | ||
1031 | void | |
1032 | _vproc_logv(int pri, int err, const char *msg, va_list ap) | |
1033 | { | |
1034 | char flat_msg[3000]; | |
1035 | ||
1036 | vsnprintf(flat_msg, sizeof(flat_msg), msg, ap); | |
1037 | ||
1038 | vproc_mig_log(bootstrap_port, pri, err, flat_msg); | |
1039 | } | |
1040 | ||
1041 | void | |
1042 | _vproc_log(int pri, const char *msg, ...) | |
1043 | { | |
1044 | va_list ap; | |
1045 | ||
1046 | va_start(ap, msg); | |
1047 | _vproc_logv(pri, 0, msg, ap); | |
1048 | va_end(ap); | |
1049 | } | |
1050 | ||
1051 | void | |
1052 | _vproc_log_error(int pri, const char *msg, ...) | |
1053 | { | |
1054 | int saved_errno = errno; | |
1055 | va_list ap; | |
1056 | ||
1057 | va_start(ap, msg); | |
1058 | _vproc_logv(pri, saved_errno, msg, ap); | |
1059 | va_end(ap); | |
1060 | } |