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