2 * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
22 #include "libvproc_public.h"
23 #include "libvproc_private.h"
24 #include "libvproc_internal.h"
26 #include <mach/mach.h>
27 #include <mach/vm_map.h>
28 #include <sys/param.h>
36 #include <quarantine.h>
39 #include "liblaunch_public.h"
40 #include "liblaunch_private.h"
41 #include "liblaunch_internal.h"
43 #include "protocol_vproc.h"
47 static mach_port_t
get_root_bootstrap_port(void);
49 static int64_t cached_pid
= -1;
52 _vproc_grab_subset(mach_port_t bp
, mach_port_t
*reqport
, mach_port_t
*rcvright
, launch_data_t
*outval
,
53 mach_port_array_t
*ports
, mach_msg_type_number_t
*portCnt
)
55 mach_msg_type_number_t outdata_cnt
;
56 vm_offset_t outdata
= 0;
57 size_t data_offset
= 0;
58 launch_data_t out_obj
;
61 if ((kr
= vproc_mig_take_subset(bp
, reqport
, rcvright
, &outdata
, &outdata_cnt
, ports
, portCnt
))) {
65 if ((out_obj
= launch_data_unpack((void *)outdata
, outdata_cnt
, NULL
, 0, &data_offset
, NULL
))) {
66 *outval
= launch_data_copy(out_obj
);
73 mig_deallocate(outdata
, outdata_cnt
);
80 _vproc_post_fork_ping(void)
82 return vproc_mig_post_fork_ping(bootstrap_port
, mach_task_self()) == 0 ? NULL
: _vproc_post_fork_ping
;
86 setup_env_hack(const launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
88 setenv(key
, launch_data_get_string(obj
), 1);
92 _vprocmgr_init(const char *session_type
)
94 if (vproc_mig_move_subset(bootstrap_port
, MACH_PORT_NULL
, (char *)session_type
) == 0) {
98 return (vproc_err_t
)_vprocmgr_init
;
102 _vprocmgr_move_subset_to_user(uid_t target_user
, const char *session_type
)
104 launch_data_t output_obj
;
105 kern_return_t kr
= 0;
106 bool is_bkgd
= (strcmp(session_type
, VPROCMGR_SESSION_BACKGROUND
) == 0);
107 int64_t ldpid
, lduid
;
109 if (vproc_swap_integer(NULL
, VPROC_GSK_MGR_PID
, 0, &ldpid
) != 0) {
110 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
113 if (vproc_swap_integer(NULL
, VPROC_GSK_MGR_UID
, 0, &lduid
) != 0) {
114 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
117 if (!is_bkgd
&& ldpid
!= 1) {
118 if (lduid
== getuid()) {
122 * Not all sessions can be moved.
123 * We should clean up this mess someday.
125 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
128 if (is_bkgd
|| target_user
) {
129 mach_port_t puc
= 0, rootbs
= get_root_bootstrap_port();
131 if (vproc_mig_lookup_per_user_context(rootbs
, target_user
, &puc
) != 0) {
132 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
136 task_set_bootstrap_port(mach_task_self(), puc
);
137 mach_port_deallocate(mach_task_self(), bootstrap_port
);
138 bootstrap_port
= puc
;
140 kr
= vproc_mig_move_subset(puc
, bootstrap_port
, (char *)session_type
);
141 mach_port_deallocate(mach_task_self(), puc
);
144 kr
= _vprocmgr_init(session_type
) ? 1 : 0;
150 return (vproc_err_t
)_vprocmgr_move_subset_to_user
;
153 /* XXX We need to give 'nohup' a better API after Leopard ships */
154 if (getprogname() && strcmp(getprogname(), "nohup") != 0) {
155 if (vproc_swap_complex(NULL
, VPROC_GSK_ENVIRONMENT
, NULL
, &output_obj
) == NULL
) {
156 if (launch_data_get_type(output_obj
) == LAUNCH_DATA_DICTIONARY
) {
157 launch_data_dict_iterate(output_obj
, setup_env_hack
, NULL
);
158 launch_data_free(output_obj
);
163 return _vproc_post_fork_ping();
168 _spawn_via_launchd(const char *label
, const char *const *argv
, const struct spawn_via_launchd_attr
*spawn_attrs
, int struct_version
)
170 size_t i
, good_enough_size
= 10*1024*1024;
171 mach_msg_type_number_t indata_cnt
= 0;
172 vm_offset_t indata
= 0;
173 mach_port_t obsvr_port
= MACH_PORT_NULL
;
174 launch_data_t tmp
, tmp_array
, in_obj
;
175 const char *const *tmpp
;
176 kern_return_t kr
= 1;
180 if ((in_obj
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
)) == NULL
) {
184 if ((tmp
= launch_data_new_string(label
)) == NULL
) {
188 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_LABEL
);
190 if ((tmp_array
= launch_data_alloc(LAUNCH_DATA_ARRAY
)) == NULL
) {
194 for (i
= 0; *argv
; i
++, argv
++) {
195 tmp
= launch_data_new_string(*argv
);
200 launch_data_array_set_index(tmp_array
, tmp
, i
);
203 launch_data_dict_insert(in_obj
, tmp_array
, LAUNCH_JOBKEY_PROGRAMARGUMENTS
);
205 if (spawn_attrs
) switch (struct_version
) {
208 if (spawn_attrs
->spawn_quarantine
) {
209 char qbuf
[QTN_SERIALIZED_DATA_MAX
];
210 size_t qbuf_sz
= QTN_SERIALIZED_DATA_MAX
;
212 if (qtn_proc_to_data(spawn_attrs
->spawn_quarantine
, qbuf
, &qbuf_sz
) == 0) {
213 tmp
= launch_data_new_opaque(qbuf
, qbuf_sz
);
214 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_QUARANTINEDATA
);
219 if (spawn_attrs
->spawn_seatbelt_profile
) {
220 tmp
= launch_data_new_string(spawn_attrs
->spawn_seatbelt_profile
);
221 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_SANDBOXPROFILE
);
224 if (spawn_attrs
->spawn_seatbelt_flags
) {
225 tmp
= launch_data_new_integer(*spawn_attrs
->spawn_seatbelt_flags
);
226 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_SANDBOXFLAGS
);
231 if (spawn_attrs
->spawn_binpref
) {
232 tmp_array
= launch_data_alloc(LAUNCH_DATA_ARRAY
);
233 for (i
= 0; i
< spawn_attrs
->spawn_binpref_cnt
; i
++) {
234 tmp
= launch_data_new_integer(spawn_attrs
->spawn_binpref
[i
]);
235 launch_data_array_set_index(tmp_array
, tmp
, i
);
237 launch_data_dict_insert(in_obj
, tmp_array
, LAUNCH_JOBKEY_BINARYORDERPREFERENCE
);
241 if (spawn_attrs
->spawn_flags
& SPAWN_VIA_LAUNCHD_STOPPED
) {
242 tmp
= launch_data_new_bool(true);
243 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_WAITFORDEBUGGER
);
246 if (spawn_attrs
->spawn_env
) {
247 launch_data_t tmp_dict
= launch_data_alloc(LAUNCH_DATA_DICTIONARY
);
249 for (tmpp
= spawn_attrs
->spawn_env
; *tmpp
; tmpp
++) {
250 char *eqoff
, tmpstr
[strlen(*tmpp
) + 1];
252 strcpy(tmpstr
, *tmpp
);
254 eqoff
= strchr(tmpstr
, '=');
262 launch_data_dict_insert(tmp_dict
, launch_data_new_string(eqoff
+ 1), tmpstr
);
265 launch_data_dict_insert(in_obj
, tmp_dict
, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES
);
268 if (spawn_attrs
->spawn_path
) {
269 tmp
= launch_data_new_string(spawn_attrs
->spawn_path
);
270 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_PROGRAM
);
273 if (spawn_attrs
->spawn_chdir
) {
274 tmp
= launch_data_new_string(spawn_attrs
->spawn_chdir
);
275 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_WORKINGDIRECTORY
);
278 if (spawn_attrs
->spawn_umask
) {
279 tmp
= launch_data_new_integer(*spawn_attrs
->spawn_umask
);
280 launch_data_dict_insert(in_obj
, tmp
, LAUNCH_JOBKEY_UMASK
);
288 if (!(buf
= malloc(good_enough_size
))) {
292 if ((indata_cnt
= launch_data_pack(in_obj
, buf
, good_enough_size
, NULL
, NULL
)) == 0) {
296 indata
= (vm_offset_t
)buf
;
298 kr
= vproc_mig_spawn(bootstrap_port
, indata
, indata_cnt
, &p
, &obsvr_port
);
300 if (kr
== VPROC_ERR_TRY_PER_USER
) {
303 if (vproc_mig_lookup_per_user_context(bootstrap_port
, 0, &puc
) == 0) {
304 kr
= vproc_mig_spawn(puc
, indata
, indata_cnt
, &p
, &obsvr_port
);
305 mach_port_deallocate(mach_task_self(), puc
);
311 launch_data_free(in_obj
);
319 case BOOTSTRAP_SUCCESS
:
320 if (spawn_attrs
&& spawn_attrs
->spawn_observer_port
) {
321 *spawn_attrs
->spawn_observer_port
= obsvr_port
;
323 mach_port_deallocate(mach_task_self(), obsvr_port
);
326 case BOOTSTRAP_NOT_PRIVILEGED
:
327 errno
= EPERM
; break;
328 case BOOTSTRAP_NO_MEMORY
:
329 errno
= ENOMEM
; break;
330 case BOOTSTRAP_NAME_IN_USE
:
331 errno
= EEXIST
; break;
335 errno
= EINVAL
; break;
342 mpm_wait(mach_port_t ajob
__attribute__((unused
)), int *wstatus
)
344 return vproc_mig_wait(ajob
, wstatus
);
348 mpm_uncork_fork(mach_port_t ajob
)
350 return vproc_mig_uncork_fork(ajob
);
354 _vprocmgr_getsocket(name_t sockpath
)
356 return vproc_mig_getsocket(bootstrap_port
, sockpath
);
360 _vproc_get_last_exit_status(int *wstatus
)
364 if (vproc_swap_integer(NULL
, VPROC_GSK_LAST_EXIT_STATUS
, 0, &val
) == 0) {
369 return (vproc_err_t
)_vproc_get_last_exit_status
;
373 _vproc_send_signal_by_label(const char *label
, int sig
)
375 if (vproc_mig_send_signal(bootstrap_port
, (char *)label
, sig
) == 0) {
379 return _vproc_send_signal_by_label
;
383 _vprocmgr_log_forward(mach_port_t mp
, void *data
, size_t len
)
385 if (vproc_mig_log_forward(mp
, (vm_offset_t
)data
, len
) == 0) {
389 return _vprocmgr_log_forward
;
393 _vprocmgr_log_drain(vproc_t vp
__attribute__((unused
)), pthread_mutex_t
*mutex
, _vprocmgr_log_drain_callback_t func
)
395 mach_msg_type_number_t outdata_cnt
, tmp_cnt
;
396 vm_offset_t outdata
= 0;
400 return _vprocmgr_log_drain
;
403 if (vproc_mig_log_drain(bootstrap_port
, &outdata
, &outdata_cnt
) != 0) {
404 return _vprocmgr_log_drain
;
407 tmp_cnt
= outdata_cnt
;
410 pthread_mutex_lock(mutex
);
413 for (lm
= (struct logmsg_s
*)outdata
; tmp_cnt
> 0; lm
= ((void *)lm
+ lm
->obj_sz
)) {
414 lm
->from_name
+= (size_t)lm
;
415 lm
->about_name
+= (size_t)lm
;
416 lm
->msg
+= (size_t)lm
;
417 lm
->session_name
+= (size_t)lm
;
419 func(&lm
->when
, lm
->from_pid
, lm
->about_pid
, lm
->sender_uid
, lm
->sender_gid
, lm
->pri
,
420 lm
->from_name
, lm
->about_name
, lm
->session_name
, lm
->msg
);
422 tmp_cnt
-= lm
->obj_sz
;
426 pthread_mutex_unlock(mutex
);
430 mig_deallocate(outdata
, outdata_cnt
);
437 vproc_swap_integer(vproc_t vp
__attribute__((unused
)), vproc_gsk_t key
, int64_t *inval
, int64_t *outval
)
439 static int64_t cached_is_managed
= -1;
440 int64_t dummyval
= 0;
443 case VPROC_GSK_MGR_PID
:
444 if (cached_pid
!= -1 && outval
) {
445 *outval
= cached_pid
;
449 case VPROC_GSK_IS_MANAGED
:
450 if (cached_is_managed
!= -1 && outval
) {
451 *outval
= cached_is_managed
;
459 if (vproc_mig_swap_integer(bootstrap_port
, inval
? key
: 0, outval
? key
: 0, inval
? *inval
: 0, outval
? outval
: &dummyval
) == 0) {
461 case VPROC_GSK_MGR_PID
:
462 cached_pid
= outval
? *outval
: dummyval
;
464 case VPROC_GSK_IS_MANAGED
:
465 cached_is_managed
= outval
? *outval
: dummyval
;
473 return (vproc_err_t
)vproc_swap_integer
;
477 get_root_bootstrap_port(void)
479 mach_port_t parent_port
= 0;
480 mach_port_t previous_port
= 0;
484 if (previous_port
!= bootstrap_port
) {
485 mach_port_deallocate(mach_task_self(), previous_port
);
487 previous_port
= parent_port
;
489 previous_port
= bootstrap_port
;
492 if (bootstrap_parent(previous_port
, &parent_port
) != 0) {
493 return MACH_PORT_NULL
;
496 } while (parent_port
!= previous_port
);
502 vproc_swap_complex(vproc_t vp
__attribute__((unused
)), vproc_gsk_t key
, launch_data_t inval
, launch_data_t
*outval
)
504 size_t data_offset
= 0, good_enough_size
= 10*1024*1024;
505 mach_msg_type_number_t indata_cnt
= 0, outdata_cnt
;
506 vm_offset_t indata
= 0, outdata
= 0;
507 launch_data_t out_obj
;
508 void *rval
= vproc_swap_complex
;
512 if (!(buf
= malloc(good_enough_size
))) {
516 if ((indata_cnt
= launch_data_pack(inval
, buf
, good_enough_size
, NULL
, NULL
)) == 0) {
520 indata
= (vm_offset_t
)buf
;
523 if (vproc_mig_swap_complex(bootstrap_port
, inval
? key
: 0, outval
? key
: 0, indata
, indata_cnt
, &outdata
, &outdata_cnt
) != 0) {
528 if (!(out_obj
= launch_data_unpack((void *)outdata
, outdata_cnt
, NULL
, 0, &data_offset
, NULL
))) {
532 if (!(*outval
= launch_data_copy(out_obj
))) {
544 mig_deallocate(outdata
, outdata_cnt
);
551 reboot2(uint64_t flags
)
553 if (vproc_mig_reboot2(get_root_bootstrap_port(), flags
) == 0) {
561 _vproc_kickstart_by_label(const char *label
, pid_t
*out_pid
, mach_port_t
*out_port_name
)
563 if (vproc_mig_embedded_kickstart(bootstrap_port
, (char *)label
, out_pid
, out_port_name
) == 0) {
567 return (vproc_err_t
)_vproc_kickstart_by_label
;
571 _vproc_wait_by_label(const char *label
, int *out_wstatus
)
573 if (vproc_mig_embedded_wait(bootstrap_port
, (char *)label
, out_wstatus
) == 0) {
577 return (vproc_err_t
)_vproc_wait_by_label
;
581 _vproc_set_global_on_demand(bool state
)
583 int64_t val
= state
? ~0 : 0;
585 if (vproc_swap_integer(NULL
, VPROC_GSK_GLOBAL_ON_DEMAND
, &val
, NULL
) == 0) {
589 return (vproc_err_t
)_vproc_set_global_on_demand
;
593 _vproc_logv(int pri
, int err
, const char *msg
, va_list ap
)
597 vsnprintf(flat_msg
, sizeof(flat_msg
), msg
, ap
);
599 vproc_mig_log(bootstrap_port
, pri
, err
, flat_msg
);
603 _vproc_log(int pri
, const char *msg
, ...)
608 _vproc_logv(pri
, 0, msg
, ap
);
613 _vproc_log_error(int pri
, const char *msg
, ...)
615 int saved_errno
= errno
;
619 _vproc_logv(pri
, saved_errno
, msg
, ap
);