]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/libvproc.c
c6b2199cd536f0da97975e04f20cd20974809888
[apple/launchd.git] / launchd / src / libvproc.c
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"
22 #include "libvproc_public.h"
23 #include "libvproc_private.h"
24 #include "libvproc_internal.h"
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>
35
36 #include "liblaunch_public.h"
37 #include "liblaunch_private.h"
38 #include "liblaunch_internal.h"
39
40 #include "protocol_vproc.h"
41
42 #include "reboot2.h"
43
44 static mach_port_t get_root_bootstrap_port(void);
45
46 static int64_t cached_pid = -1;
47
48 kern_return_t
49 _vproc_grab_subset(mach_port_t bp, mach_port_t *reqport, mach_port_t *rcvright, launch_data_t *outval,
50 mach_port_array_t *ports, mach_msg_type_number_t *portCnt)
51 {
52 mach_msg_type_number_t outdata_cnt;
53 vm_offset_t outdata = 0;
54 size_t data_offset = 0;
55 launch_data_t out_obj;
56 kern_return_t kr;
57
58 if ((kr = vproc_mig_take_subset(bp, reqport, rcvright, &outdata, &outdata_cnt, ports, portCnt))) {
59 goto out;
60 }
61
62 if ((out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
63 *outval = launch_data_copy(out_obj);
64 } else {
65 kr = 1;
66 }
67
68 out:
69 if (outdata) {
70 mig_deallocate(outdata, outdata_cnt);
71 }
72
73 return kr;
74 }
75
76 vproc_err_t
77 _vproc_post_fork_ping(void)
78 {
79 return vproc_mig_post_fork_ping(bootstrap_port, mach_task_self()) == 0 ? NULL : _vproc_post_fork_ping;
80 }
81
82 static void
83 setup_env_hack(const launch_data_t obj, const char *key, void *context __attribute__((unused)))
84 {
85 setenv(key, launch_data_get_string(obj), 1);
86 }
87
88 vproc_err_t
89 _vprocmgr_init(const char *session_type)
90 {
91 if (vproc_mig_move_subset(bootstrap_port, MACH_PORT_NULL, (char *)session_type) == 0) {
92 return NULL;
93 }
94
95 return (vproc_err_t)_vprocmgr_init;
96 }
97
98 vproc_err_t
99 _vprocmgr_move_subset_to_user(uid_t target_user, const char *session_type)
100 {
101 launch_data_t output_obj;
102 kern_return_t kr = 0;
103 bool is_bkgd = (strcmp(session_type, VPROCMGR_SESSION_BACKGROUND) == 0);
104 int64_t ldpid, lduid;
105
106 if (vproc_swap_integer(NULL, VPROC_GSK_MGR_PID, 0, &ldpid) != 0) {
107 return (vproc_err_t)_vprocmgr_move_subset_to_user;
108 }
109
110 if (vproc_swap_integer(NULL, VPROC_GSK_MGR_UID, 0, &lduid) != 0) {
111 return (vproc_err_t)_vprocmgr_move_subset_to_user;
112 }
113
114 if (!is_bkgd && ldpid != 1) {
115 if (lduid == getuid()) {
116 return NULL;
117 }
118 /*
119 * Not all sessions can be moved.
120 * We should clean up this mess someday.
121 */
122 return (vproc_err_t)_vprocmgr_move_subset_to_user;
123 }
124
125 if (is_bkgd || target_user) {
126 mach_port_t puc = 0, rootbs = get_root_bootstrap_port();
127
128 if (vproc_mig_lookup_per_user_context(rootbs, target_user, &puc) != 0) {
129 return (vproc_err_t)_vprocmgr_move_subset_to_user;
130 }
131
132 if (is_bkgd) {
133 task_set_bootstrap_port(mach_task_self(), puc);
134 mach_port_deallocate(mach_task_self(), bootstrap_port);
135 bootstrap_port = puc;
136 } else {
137 kr = vproc_mig_move_subset(puc, bootstrap_port, (char *)session_type);
138 mach_port_deallocate(mach_task_self(), puc);
139 }
140 } else {
141 kr = _vprocmgr_init(session_type) ? 1 : 0;
142 }
143
144 cached_pid = -1;
145
146 if (kr) {
147 return (vproc_err_t)_vprocmgr_move_subset_to_user;
148 }
149
150 /* XXX We need to give 'nohup' a better API after Leopard ships */
151 if (getprogname() && strcmp(getprogname(), "nohup") != 0) {
152 if (vproc_swap_complex(NULL, VPROC_GSK_ENVIRONMENT, NULL, &output_obj) == NULL) {
153 if (launch_data_get_type(output_obj) == LAUNCH_DATA_DICTIONARY) {
154 launch_data_dict_iterate(output_obj, setup_env_hack, NULL);
155 launch_data_free(output_obj);
156 }
157 }
158 }
159
160 return _vproc_post_fork_ping();
161 }
162
163
164 pid_t
165 _spawn_via_launchd(const char *label, const char *const *argv, const struct spawn_via_launchd_attr *spawn_attrs, int struct_version)
166 {
167 size_t i, good_enough_size = 10*1024*1024;
168 mach_msg_type_number_t indata_cnt = 0;
169 vm_offset_t indata = 0;
170 mach_port_t obsvr_port = MACH_PORT_NULL;
171 launch_data_t tmp, tmp_array, in_obj;
172 const char *const *tmpp;
173 kern_return_t kr = 1;
174 void *buf = NULL;
175 pid_t p = -1;
176
177 if ((in_obj = launch_data_alloc(LAUNCH_DATA_DICTIONARY)) == NULL) {
178 goto out;
179 }
180
181 if ((tmp = launch_data_new_string(label)) == NULL) {
182 goto out;
183 }
184
185 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_LABEL);
186
187 if ((tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY)) == NULL) {
188 goto out;
189 }
190
191 for (i = 0; *argv; i++, argv++) {
192 tmp = launch_data_new_string(*argv);
193 if (tmp == NULL) {
194 goto out;
195 }
196
197 launch_data_array_set_index(tmp_array, tmp, i);
198 }
199
200 launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_PROGRAMARGUMENTS);
201
202 if (spawn_attrs) switch (struct_version) {
203 case 2:
204 if (spawn_attrs->spawn_quarantine) {
205 char qbuf[QTN_SERIALIZED_DATA_MAX];
206 size_t qbuf_sz = QTN_SERIALIZED_DATA_MAX;
207
208 if (qtn_proc_to_data(spawn_attrs->spawn_quarantine, qbuf, &qbuf_sz) == 0) {
209 tmp = launch_data_new_opaque(qbuf, qbuf_sz);
210 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_QUARANTINEDATA);
211 }
212 }
213
214 if (spawn_attrs->spawn_seatbelt_profile) {
215 tmp = launch_data_new_string(spawn_attrs->spawn_seatbelt_profile);
216 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXPROFILE);
217 }
218
219 if (spawn_attrs->spawn_seatbelt_flags) {
220 tmp = launch_data_new_integer(*spawn_attrs->spawn_seatbelt_flags);
221 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_SANDBOXFLAGS);
222 }
223
224 /* fall through */
225 case 1:
226 if (spawn_attrs->spawn_binpref) {
227 tmp_array = launch_data_alloc(LAUNCH_DATA_ARRAY);
228 for (i = 0; i < spawn_attrs->spawn_binpref_cnt; i++) {
229 tmp = launch_data_new_integer(spawn_attrs->spawn_binpref[i]);
230 launch_data_array_set_index(tmp_array, tmp, i);
231 }
232 launch_data_dict_insert(in_obj, tmp_array, LAUNCH_JOBKEY_BINARYORDERPREFERENCE);
233 }
234 /* fall through */
235 case 0:
236 if (spawn_attrs->spawn_flags & SPAWN_VIA_LAUNCHD_STOPPED) {
237 tmp = launch_data_new_bool(true);
238 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WAITFORDEBUGGER);
239 }
240
241 if (spawn_attrs->spawn_env) {
242 launch_data_t tmp_dict = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
243
244 for (tmpp = spawn_attrs->spawn_env; *tmpp; tmpp++) {
245 char *eqoff, tmpstr[strlen(*tmpp) + 1];
246
247 strcpy(tmpstr, *tmpp);
248
249 eqoff = strchr(tmpstr, '=');
250
251 if (!eqoff) {
252 goto out;
253 }
254
255 *eqoff = '\0';
256
257 launch_data_dict_insert(tmp_dict, launch_data_new_string(eqoff + 1), tmpstr);
258 }
259
260 launch_data_dict_insert(in_obj, tmp_dict, LAUNCH_JOBKEY_ENVIRONMENTVARIABLES);
261 }
262
263 if (spawn_attrs->spawn_path) {
264 tmp = launch_data_new_string(spawn_attrs->spawn_path);
265 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_PROGRAM);
266 }
267
268 if (spawn_attrs->spawn_chdir) {
269 tmp = launch_data_new_string(spawn_attrs->spawn_chdir);
270 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_WORKINGDIRECTORY);
271 }
272
273 if (spawn_attrs->spawn_umask) {
274 tmp = launch_data_new_integer(*spawn_attrs->spawn_umask);
275 launch_data_dict_insert(in_obj, tmp, LAUNCH_JOBKEY_UMASK);
276 }
277
278 break;
279 default:
280 break;
281 }
282
283 if (!(buf = malloc(good_enough_size))) {
284 goto out;
285 }
286
287 if ((indata_cnt = launch_data_pack(in_obj, buf, good_enough_size, NULL, NULL)) == 0) {
288 goto out;
289 }
290
291 indata = (vm_offset_t)buf;
292
293 kr = vproc_mig_spawn(bootstrap_port, indata, indata_cnt, &p, &obsvr_port);
294
295 if (kr == VPROC_ERR_TRY_PER_USER) {
296 mach_port_t puc;
297
298 if (vproc_mig_lookup_per_user_context(bootstrap_port, 0, &puc) == 0) {
299 kr = vproc_mig_spawn(puc, indata, indata_cnt, &p, &obsvr_port);
300 mach_port_deallocate(mach_task_self(), puc);
301 }
302 }
303
304 out:
305 if (in_obj) {
306 launch_data_free(in_obj);
307 }
308
309 if (buf) {
310 free(buf);
311 }
312
313 switch (kr) {
314 case BOOTSTRAP_SUCCESS:
315 if (spawn_attrs && spawn_attrs->spawn_observer_port) {
316 *spawn_attrs->spawn_observer_port = obsvr_port;
317 } else {
318 mach_port_deallocate(mach_task_self(), obsvr_port);
319 }
320 return p;
321 case BOOTSTRAP_NOT_PRIVILEGED:
322 errno = EPERM; break;
323 case BOOTSTRAP_NO_MEMORY:
324 errno = ENOMEM; break;
325 case BOOTSTRAP_NAME_IN_USE:
326 errno = EEXIST; break;
327 case 1:
328 errno = EIO; break;
329 default:
330 errno = EINVAL; break;
331 }
332
333 return -1;
334 }
335
336 kern_return_t
337 mpm_wait(mach_port_t ajob __attribute__((unused)), int *wstatus)
338 {
339 return vproc_mig_wait(ajob, wstatus);
340 }
341
342 kern_return_t
343 mpm_uncork_fork(mach_port_t ajob)
344 {
345 return vproc_mig_uncork_fork(ajob);
346 }
347
348 kern_return_t
349 _vprocmgr_getsocket(name_t sockpath)
350 {
351 return vproc_mig_getsocket(bootstrap_port, sockpath);
352 }
353
354 vproc_err_t
355 _vproc_get_last_exit_status(int *wstatus)
356 {
357 int64_t val;
358
359 if (vproc_swap_integer(NULL, VPROC_GSK_LAST_EXIT_STATUS, 0, &val) == 0) {
360 *wstatus = (int)val;
361 return NULL;
362 }
363
364 return (vproc_err_t)_vproc_get_last_exit_status;
365 }
366
367 vproc_err_t
368 _vproc_send_signal_by_label(const char *label, int sig)
369 {
370 if (vproc_mig_send_signal(bootstrap_port, (char *)label, sig) == 0) {
371 return NULL;
372 }
373
374 return _vproc_send_signal_by_label;
375 }
376
377 vproc_err_t
378 _vprocmgr_log_forward(mach_port_t mp, void *data, size_t len)
379 {
380 if (vproc_mig_log_forward(mp, (vm_offset_t)data, len) == 0) {
381 return NULL;
382 }
383
384 return _vprocmgr_log_forward;
385 }
386
387 vproc_err_t
388 _vprocmgr_log_drain(vproc_t vp __attribute__((unused)), pthread_mutex_t *mutex, _vprocmgr_log_drain_callback_t func)
389 {
390 mach_msg_type_number_t outdata_cnt, tmp_cnt;
391 vm_offset_t outdata = 0;
392 struct logmsg_s *lm;
393
394 if (!func) {
395 return _vprocmgr_log_drain;
396 }
397
398 if (vproc_mig_log_drain(bootstrap_port, &outdata, &outdata_cnt) != 0) {
399 return _vprocmgr_log_drain;
400 }
401
402 tmp_cnt = outdata_cnt;
403
404 if (mutex) {
405 pthread_mutex_lock(mutex);
406 }
407
408 for (lm = (struct logmsg_s *)outdata; tmp_cnt > 0; lm = ((void *)lm + lm->obj_sz)) {
409 lm->from_name += (size_t)lm;
410 lm->about_name += (size_t)lm;
411 lm->msg += (size_t)lm;
412 lm->session_name += (size_t)lm;
413
414 func(&lm->when, lm->from_pid, lm->about_pid, lm->sender_uid, lm->sender_gid, lm->pri,
415 lm->from_name, lm->about_name, lm->session_name, lm->msg);
416
417 tmp_cnt -= lm->obj_sz;
418 }
419
420 if (mutex) {
421 pthread_mutex_unlock(mutex);
422 }
423
424 if (outdata) {
425 mig_deallocate(outdata, outdata_cnt);
426 }
427
428 return NULL;
429 }
430
431 vproc_err_t
432 vproc_swap_integer(vproc_t vp __attribute__((unused)), vproc_gsk_t key, int64_t *inval, int64_t *outval)
433 {
434 static int64_t cached_is_managed = -1;
435 int64_t dummyval = 0;
436
437 switch (key) {
438 case VPROC_GSK_MGR_PID:
439 if (cached_pid != -1 && outval) {
440 *outval = cached_pid;
441 return NULL;
442 }
443 break;
444 case VPROC_GSK_IS_MANAGED:
445 if (cached_is_managed != -1 && outval) {
446 *outval = cached_is_managed;
447 return NULL;
448 }
449 break;
450 default:
451 break;
452 }
453
454 if (vproc_mig_swap_integer(bootstrap_port, inval ? key : 0, outval ? key : 0, inval ? *inval : 0, outval ? outval : &dummyval) == 0) {
455 switch (key) {
456 case VPROC_GSK_MGR_PID:
457 cached_pid = outval ? *outval : dummyval;
458 break;
459 case VPROC_GSK_IS_MANAGED:
460 cached_is_managed = outval ? *outval : dummyval;
461 break;
462 default:
463 break;
464 }
465 return NULL;
466 }
467
468 return (vproc_err_t)vproc_swap_integer;
469 }
470
471 mach_port_t
472 get_root_bootstrap_port(void)
473 {
474 mach_port_t parent_port = 0;
475 mach_port_t previous_port = 0;
476
477 do {
478 if (previous_port) {
479 if (previous_port != bootstrap_port) {
480 mach_port_deallocate(mach_task_self(), previous_port);
481 }
482 previous_port = parent_port;
483 } else {
484 previous_port = bootstrap_port;
485 }
486
487 if (bootstrap_parent(previous_port, &parent_port) != 0) {
488 return MACH_PORT_NULL;
489 }
490
491 } while (parent_port != previous_port);
492
493 return parent_port;
494 }
495
496 vproc_err_t
497 vproc_swap_complex(vproc_t vp __attribute__((unused)), vproc_gsk_t key, launch_data_t inval, launch_data_t *outval)
498 {
499 size_t data_offset = 0, good_enough_size = 10*1024*1024;
500 mach_msg_type_number_t indata_cnt = 0, outdata_cnt;
501 vm_offset_t indata = 0, outdata = 0;
502 launch_data_t out_obj;
503 void *rval = vproc_swap_complex;
504 void *buf = NULL;
505
506 if (inval) {
507 if (!(buf = malloc(good_enough_size))) {
508 goto out;
509 }
510
511 if ((indata_cnt = launch_data_pack(inval, buf, good_enough_size, NULL, NULL)) == 0) {
512 goto out;
513 }
514
515 indata = (vm_offset_t)buf;
516 }
517
518 if (vproc_mig_swap_complex(bootstrap_port, inval ? key : 0, outval ? key : 0, indata, indata_cnt, &outdata, &outdata_cnt) != 0) {
519 goto out;
520 }
521
522 if (outval) {
523 if (!(out_obj = launch_data_unpack((void *)outdata, outdata_cnt, NULL, 0, &data_offset, NULL))) {
524 goto out;
525 }
526
527 if (!(*outval = launch_data_copy(out_obj))) {
528 goto out;
529 }
530 }
531
532 rval = NULL;
533 out:
534 if (buf) {
535 free(buf);
536 }
537
538 if (outdata) {
539 mig_deallocate(outdata, outdata_cnt);
540 }
541
542 return rval;
543 }
544
545 void *
546 reboot2(uint64_t flags)
547 {
548 if (vproc_mig_reboot2(get_root_bootstrap_port(), flags) == 0) {
549 return NULL;
550 }
551
552 return reboot2;
553 }
554
555 vproc_err_t
556 _vproc_set_global_on_demand(bool state)
557 {
558 int64_t val = state ? ~0 : 0;
559
560 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_ON_DEMAND, &val, NULL) == 0) {
561 return NULL;
562 }
563
564 return (vproc_err_t)_vproc_set_global_on_demand;
565 }
566
567 void
568 _vproc_logv(int pri, int err, const char *msg, va_list ap)
569 {
570 char flat_msg[3000];
571
572 vsnprintf(flat_msg, sizeof(flat_msg), msg, ap);
573
574 vproc_mig_log(bootstrap_port, pri, err, flat_msg);
575 }
576
577 void
578 _vproc_log(int pri, const char *msg, ...)
579 {
580 va_list ap;
581
582 va_start(ap, msg);
583 _vproc_logv(pri, 0, msg, ap);
584 va_end(ap);
585 }
586
587 void
588 _vproc_log_error(int pri, const char *msg, ...)
589 {
590 int saved_errno = errno;
591 va_list ap;
592
593 va_start(ap, msg);
594 _vproc_logv(pri, saved_errno, msg, ap);
595 va_end(ap);
596 }