]> git.saurik.com Git - apple/launchd.git/blob - liblaunch/liblaunch.c
launchd-842.92.1.tar.gz
[apple/launchd.git] / liblaunch / liblaunch.c
1 /*
2 * Copyright (c) 2005-2012 Apple 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 "launch.h"
23 #include "launch_priv.h"
24 #include "launch_internal.h"
25 #include "ktrace.h"
26
27 #include <mach/mach.h>
28 #include <libkern/OSByteOrder.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/fcntl.h>
32 #include <sys/un.h>
33 #include <sys/uio.h>
34 #include <sys/stat.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <pthread.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <pwd.h>
42 #include <assert.h>
43 #include <uuid/uuid.h>
44 #include <sys/syscall.h>
45 #include <dlfcn.h>
46
47 #ifdef __LP64__
48 /* workaround: 5723161 */
49 #ifndef __DARWIN_ALIGN32
50 #define __DARWIN_ALIGN32(x) (((size_t)(x) + 3) & ~3)
51 #endif
52 #undef CMSG_DATA
53 #define CMSG_DATA(cmsg) \
54 ((uint8_t *)(cmsg) + __DARWIN_ALIGN32(sizeof(struct cmsghdr)))
55 #undef CMSG_SPACE
56 #define CMSG_SPACE(l) \
57 (__DARWIN_ALIGN32(sizeof(struct cmsghdr)) + __DARWIN_ALIGN32(l))
58 #undef CMSG_LEN
59 #define CMSG_LEN(l) \
60 (__DARWIN_ALIGN32(sizeof(struct cmsghdr)) + (l))
61 #endif
62
63 struct _launch_data {
64 uint64_t type;
65 union {
66 struct {
67 union {
68 launch_data_t *_array;
69 char *string;
70 void *opaque;
71 int64_t __junk;
72 };
73 union {
74 uint64_t _array_cnt;
75 uint64_t string_len;
76 uint64_t opaque_size;
77 };
78 };
79 int64_t fd;
80 uint64_t mp;
81 uint64_t err;
82 int64_t number;
83 uint64_t boolean; /* We'd use 'bool' but this struct needs to be used under Rosetta, and sizeof(bool) is different between PowerPC and Intel */
84 double float_num;
85 };
86 };
87
88 #include "bootstrap.h"
89 #include "vproc.h"
90 #include "vproc_priv.h"
91 #include "vproc_internal.h"
92
93 /* __OSBogusByteSwap__() must not really exist in the symbol namespace
94 * in order for the following to generate an error at build time.
95 */
96 extern void __OSBogusByteSwap__(void);
97
98 #define host2wire(x) \
99 ({ typeof (x) _X, _x = (x); \
100 switch (sizeof(_x)) { \
101 case 8: \
102 _X = OSSwapHostToLittleInt64(_x); \
103 break; \
104 case 4: \
105 _X = OSSwapHostToLittleInt32(_x); \
106 break; \
107 case 2: \
108 _X = OSSwapHostToLittleInt16(_x); \
109 break; \
110 case 1: \
111 _X = _x; \
112 break; \
113 default: \
114 __OSBogusByteSwap__(); \
115 break; \
116 } \
117 _X; \
118 })
119
120
121 #define big2wire(x) \
122 ({ typeof (x) _X, _x = (x); \
123 switch (sizeof(_x)) { \
124 case 8: \
125 _X = OSSwapLittleToHostInt64(_x); \
126 break; \
127 case 4: \
128 _X = OSSwapLittleToHostInt32(_x); \
129 break; \
130 case 2: \
131 _X = OSSwapLittleToHostInt16(_x); \
132 break; \
133 case 1: \
134 _X = _x; \
135 break; \
136 default: \
137 __OSBogusByteSwap__(); \
138 break; \
139 } \
140 _X; \
141 })
142
143 union _launch_double_u {
144 uint64_t iv;
145 double dv;
146 };
147
148 #define host2wire_f(x) ({ \
149 typeof(x) _F, _f = (x); \
150 union _launch_double_u s; \
151 s.dv = _f; \
152 s.iv = host2wire(s.iv); \
153 _F = s.dv; \
154 _F; \
155 })
156
157 #define big2wire_f(x) ({ \
158 typeof(x) _F, _f = (x); \
159 union _launch_double_u s; \
160 s.dv = _f; \
161 s.iv = big2wire(s.iv); \
162 _F = s.dv; \
163 _F; \
164 })
165
166
167 struct launch_msg_header {
168 uint64_t magic;
169 uint64_t len;
170 };
171
172 #define LAUNCH_MSG_HEADER_MAGIC 0xD2FEA02366B39A41ull
173
174 enum {
175 LAUNCHD_USE_CHECKIN_FD,
176 LAUNCHD_USE_OTHER_FD,
177 };
178 struct _launch {
179 void *sendbuf;
180 int *sendfds;
181 void *recvbuf;
182 int *recvfds;
183 size_t sendlen;
184 size_t sendfdcnt;
185 size_t recvlen;
186 size_t recvfdcnt;
187 int which;
188 int cifd;
189 int fd;
190 };
191
192 static launch_data_t launch_data_array_pop_first(launch_data_t where);
193 static int _fd(int fd);
194 static void launch_client_init(void);
195 static void launch_msg_getmsgs(launch_data_t m, void *context);
196 static launch_data_t launch_msg_internal(launch_data_t d);
197 static void launch_mach_checkin_service(launch_data_t obj, const char *key, void *context);
198
199 void
200 _launch_init_globals(launch_globals_t globals)
201 {
202 pthread_once_t once = PTHREAD_ONCE_INIT;
203 globals->lc_once = once;
204 pthread_mutex_init(&globals->lc_mtx, NULL);
205 }
206
207 #if !_LIBLAUNCH_HAS_ALLOC_ONCE
208 launch_globals_t __launch_globals;
209
210 void
211 _launch_globals_init(void)
212 {
213 __launch_globals = calloc(1, sizeof(struct launch_globals_s));
214 _launch_init_globals(__launch_globals);
215 }
216
217 launch_globals_t
218 _launch_globals_impl(void)
219 {
220 static pthread_once_t once = PTHREAD_ONCE_INIT;
221 pthread_once(&once, &_launch_globals_init);
222 return __launch_globals;
223 }
224 #endif
225
226 void
227 launch_client_init(void)
228 {
229 struct sockaddr_un sun;
230 char *where = getenv(LAUNCHD_SOCKET_ENV);
231 char *_launchd_fd = getenv(LAUNCHD_TRUSTED_FD_ENV);
232 int dfd, lfd = -1, cifd = -1;
233 name_t spath;
234
235 if (_launchd_fd) {
236 cifd = strtol(_launchd_fd, NULL, 10);
237 if ((dfd = dup(cifd)) >= 0) {
238 close(dfd);
239 _fd(cifd);
240 } else {
241 cifd = -1;
242 }
243 unsetenv(LAUNCHD_TRUSTED_FD_ENV);
244 }
245
246 memset(&sun, 0, sizeof(sun));
247 sun.sun_family = AF_UNIX;
248
249 /* The rules are as follows.
250 * - All users (including root) talk to their per-user launchd's by default.
251 * - If we have been invoked under sudo, talk to the system launchd.
252 * - If we're the root user and the __USE_SYSTEM_LAUNCHD environment variable is set, then
253 * talk to the system launchd.
254 */
255 if (where && where[0] != '\0') {
256 strncpy(sun.sun_path, where, sizeof(sun.sun_path));
257 } else {
258 if (_vprocmgr_getsocket(spath) == 0) {
259 if ((getenv("SUDO_COMMAND") || getenv("__USE_SYSTEM_LAUNCHD")) && geteuid() == 0) {
260 /* Talk to the system launchd. */
261 strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path));
262 } else {
263 /* Talk to our per-user launchd. */
264 size_t min_len;
265
266 min_len = sizeof(sun.sun_path) < sizeof(spath) ? sizeof(sun.sun_path) : sizeof(spath);
267
268 strncpy(sun.sun_path, spath, min_len);
269 }
270 }
271 }
272
273 launch_globals_t globals = _launch_globals();
274 if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
275 goto out_bad;
276 }
277
278 #if TARGET_OS_EMBEDDED
279 (void)vproc_swap_integer(NULL, VPROC_GSK_EMBEDDEDROOTEQUIVALENT, NULL, &globals->s_am_embedded_god);
280 #endif
281 if (-1 == connect(lfd, (struct sockaddr *)&sun, sizeof(sun))) {
282 if (cifd != -1 || globals->s_am_embedded_god) {
283 /* There is NO security enforced by this check. This is just a hint to our
284 * library that we shouldn't error out due to failing to open this socket. If
285 * we inherited a trusted file descriptor, we shouldn't fail. This should be
286 * adequate for clients' expectations.
287 */
288 close(lfd);
289 lfd = -1;
290 } else {
291 goto out_bad;
292 }
293 }
294
295 if (!(globals->l = launchd_fdopen(lfd, cifd))) {
296 goto out_bad;
297 }
298
299 if (!(globals->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY))) {
300 goto out_bad;
301 }
302
303 return;
304 out_bad:
305 if (globals->l) {
306 launchd_close(globals->l, close);
307 globals->l = NULL;
308 } else if (lfd != -1) {
309 close(lfd);
310 }
311 if (cifd != -1) {
312 close(cifd);
313 }
314 }
315
316 launch_data_t
317 launch_data_alloc(launch_data_type_t t)
318 {
319 launch_data_t d = calloc(1, sizeof(struct _launch_data));
320
321 if (d) {
322 d->type = t;
323 switch (t) {
324 case LAUNCH_DATA_DICTIONARY:
325 case LAUNCH_DATA_ARRAY:
326 d->_array = malloc(0);
327 break;
328 case LAUNCH_DATA_OPAQUE:
329 d->opaque = malloc(0);
330 default:
331 break;
332 }
333 }
334
335 return d;
336 }
337
338 launch_data_type_t
339 launch_data_get_type(launch_data_t d)
340 {
341 return d->type;
342 }
343
344 void
345 launch_data_free(launch_data_t d)
346 {
347 size_t i;
348
349 switch (d->type) {
350 case LAUNCH_DATA_DICTIONARY:
351 case LAUNCH_DATA_ARRAY:
352 for (i = 0; i < d->_array_cnt; i++) {
353 if (d->_array[i]) {
354 launch_data_free(d->_array[i]);
355 }
356 }
357 free(d->_array);
358 break;
359 case LAUNCH_DATA_STRING:
360 if (d->string)
361 free(d->string);
362 break;
363 case LAUNCH_DATA_OPAQUE:
364 if (d->opaque)
365 free(d->opaque);
366 break;
367 default:
368 break;
369 }
370 free(d);
371 }
372
373 size_t
374 launch_data_dict_get_count(launch_data_t dict)
375 {
376 return dict->_array_cnt / 2;
377 }
378
379 bool
380 launch_data_dict_insert(launch_data_t dict, launch_data_t what, const char *key)
381 {
382 size_t i;
383 launch_data_t thekey = launch_data_alloc(LAUNCH_DATA_STRING);
384
385 launch_data_set_string(thekey, key);
386
387 for (i = 0; i < dict->_array_cnt; i += 2) {
388 if (!strcasecmp(key, dict->_array[i]->string)) {
389 launch_data_array_set_index(dict, thekey, i);
390 launch_data_array_set_index(dict, what, i + 1);
391 return true;
392 }
393 }
394 launch_data_array_set_index(dict, thekey, i);
395 launch_data_array_set_index(dict, what, i + 1);
396 return true;
397 }
398
399 launch_data_t
400 launch_data_dict_lookup(launch_data_t dict, const char *key)
401 {
402 size_t i;
403
404 if (LAUNCH_DATA_DICTIONARY != dict->type)
405 return NULL;
406
407 for (i = 0; i < dict->_array_cnt; i += 2) {
408 if (!strcasecmp(key, dict->_array[i]->string))
409 return dict->_array[i + 1];
410 }
411
412 return NULL;
413 }
414
415 bool
416 launch_data_dict_remove(launch_data_t dict, const char *key)
417 {
418 size_t i;
419
420 for (i = 0; i < dict->_array_cnt; i += 2) {
421 if (!strcasecmp(key, dict->_array[i]->string))
422 break;
423 }
424 if (i == dict->_array_cnt)
425 return false;
426 launch_data_free(dict->_array[i]);
427 launch_data_free(dict->_array[i + 1]);
428 memmove(dict->_array + i, dict->_array + i + 2, (dict->_array_cnt - (i + 2)) * sizeof(launch_data_t));
429 dict->_array_cnt -= 2;
430 return true;
431 }
432
433 void
434 launch_data_dict_iterate(launch_data_t dict, void (*cb)(launch_data_t, const char *, void *), void *context)
435 {
436 size_t i;
437
438 if (LAUNCH_DATA_DICTIONARY != dict->type) {
439 return;
440 }
441
442 for (i = 0; i < dict->_array_cnt; i += 2) {
443 cb(dict->_array[i + 1], dict->_array[i]->string, context);
444 }
445 }
446
447 bool
448 launch_data_array_set_index(launch_data_t where, launch_data_t what, size_t ind)
449 {
450 if ((ind + 1) >= where->_array_cnt) {
451 where->_array = reallocf(where->_array, (ind + 1) * sizeof(launch_data_t));
452 memset(where->_array + where->_array_cnt, 0, (ind + 1 - where->_array_cnt) * sizeof(launch_data_t));
453 where->_array_cnt = ind + 1;
454 }
455
456 if (where->_array[ind]) {
457 launch_data_free(where->_array[ind]);
458 }
459
460 where->_array[ind] = what;
461 return true;
462 }
463
464 launch_data_t
465 launch_data_array_get_index(launch_data_t where, size_t ind)
466 {
467 if (LAUNCH_DATA_ARRAY != where->type || ind >= where->_array_cnt) {
468 return NULL;
469 } else {
470 return where->_array[ind];
471 }
472 }
473
474 launch_data_t
475 launch_data_array_pop_first(launch_data_t where)
476 {
477 launch_data_t r = NULL;
478
479 if (where->_array_cnt > 0) {
480 r = where->_array[0];
481 memmove(where->_array, where->_array + 1, (where->_array_cnt - 1) * sizeof(launch_data_t));
482 where->_array_cnt--;
483 }
484 return r;
485 }
486
487 size_t
488 launch_data_array_get_count(launch_data_t where)
489 {
490 if (LAUNCH_DATA_ARRAY != where->type)
491 return 0;
492 return where->_array_cnt;
493 }
494
495 bool
496 launch_data_set_errno(launch_data_t d, int e)
497 {
498 d->err = e;
499 return true;
500 }
501
502 bool
503 launch_data_set_fd(launch_data_t d, int fd)
504 {
505 d->fd = fd;
506 return true;
507 }
508
509 bool
510 launch_data_set_machport(launch_data_t d, mach_port_t p)
511 {
512 d->mp = p;
513 return true;
514 }
515
516 bool
517 launch_data_set_integer(launch_data_t d, long long n)
518 {
519 d->number = n;
520 return true;
521 }
522
523 bool
524 launch_data_set_bool(launch_data_t d, bool b)
525 {
526 d->boolean = b;
527 return true;
528 }
529
530 bool
531 launch_data_set_real(launch_data_t d, double n)
532 {
533 d->float_num = n;
534 return true;
535 }
536
537 bool
538 launch_data_set_string(launch_data_t d, const char *s)
539 {
540 if (d->string)
541 free(d->string);
542 d->string = strdup(s);
543 if (d->string) {
544 d->string_len = strlen(d->string);
545 return true;
546 }
547 return false;
548 }
549
550 bool
551 launch_data_set_opaque(launch_data_t d, const void *o, size_t os)
552 {
553 d->opaque_size = os;
554 if (d->opaque)
555 free(d->opaque);
556 d->opaque = malloc(os);
557 if (d->opaque) {
558 memcpy(d->opaque, o, os);
559 return true;
560 }
561 return false;
562 }
563
564 int
565 launch_data_get_errno(launch_data_t d)
566 {
567 return d->err;
568 }
569
570 int
571 launch_data_get_fd(launch_data_t d)
572 {
573 return d->fd;
574 }
575
576 mach_port_t
577 launch_data_get_machport(launch_data_t d)
578 {
579 return d->mp;
580 }
581
582 long long
583 launch_data_get_integer(launch_data_t d)
584 {
585 return d->number;
586 }
587
588 bool
589 launch_data_get_bool(launch_data_t d)
590 {
591 return d->boolean;
592 }
593
594 double
595 launch_data_get_real(launch_data_t d)
596 {
597 return d->float_num;
598 }
599
600 const char *
601 launch_data_get_string(launch_data_t d)
602 {
603 if (LAUNCH_DATA_STRING != d->type)
604 return NULL;
605 return d->string;
606 }
607
608 void *
609 launch_data_get_opaque(launch_data_t d)
610 {
611 if (LAUNCH_DATA_OPAQUE != d->type)
612 return NULL;
613 return d->opaque;
614 }
615
616 size_t
617 launch_data_get_opaque_size(launch_data_t d)
618 {
619 return d->opaque_size;
620 }
621
622 int
623 launchd_getfd(launch_t l)
624 {
625 return (l->which == LAUNCHD_USE_CHECKIN_FD) ? l->cifd : l->fd;
626 }
627
628 launch_t
629 launchd_fdopen(int fd, int cifd)
630 {
631 launch_t c;
632
633 c = calloc(1, sizeof(struct _launch));
634 if (!c)
635 return NULL;
636
637 c->fd = fd;
638 c->cifd = cifd;
639
640 if (c->fd == -1 || (c->fd != -1 && c->cifd != -1)) {
641 c->which = LAUNCHD_USE_CHECKIN_FD;
642 } else if (c->cifd == -1) {
643 c->which = LAUNCHD_USE_OTHER_FD;
644 }
645
646 fcntl(fd, F_SETFL, O_NONBLOCK);
647 fcntl(cifd, F_SETFL, O_NONBLOCK);
648
649 if ((c->sendbuf = malloc(0)) == NULL)
650 goto out_bad;
651 if ((c->sendfds = malloc(0)) == NULL)
652 goto out_bad;
653 if ((c->recvbuf = malloc(0)) == NULL)
654 goto out_bad;
655 if ((c->recvfds = malloc(0)) == NULL)
656 goto out_bad;
657
658 return c;
659
660 out_bad:
661 if (c->sendbuf)
662 free(c->sendbuf);
663 if (c->sendfds)
664 free(c->sendfds);
665 if (c->recvbuf)
666 free(c->recvbuf);
667 if (c->recvfds)
668 free(c->recvfds);
669 free(c);
670 return NULL;
671 }
672
673 void
674 launchd_close(launch_t lh, typeof(close) closefunc)
675 {
676 launch_globals_t globals = _launch_globals();
677
678 if (globals->in_flight_msg_recv_client == lh) {
679 globals->in_flight_msg_recv_client = NULL;
680 }
681
682 if (lh->sendbuf)
683 free(lh->sendbuf);
684 if (lh->sendfds)
685 free(lh->sendfds);
686 if (lh->recvbuf)
687 free(lh->recvbuf);
688 if (lh->recvfds)
689 free(lh->recvfds);
690 closefunc(lh->fd);
691 closefunc(lh->cifd);
692 free(lh);
693 }
694
695 #define ROUND_TO_64BIT_WORD_SIZE(x) ((x + 7) & ~7)
696
697 size_t
698 launch_data_pack(launch_data_t d, void *where, size_t len, int *fd_where, size_t *fd_cnt)
699 {
700 launch_data_t o_in_w = where;
701 size_t i, rsz, node_data_len = sizeof(struct _launch_data);
702
703 if (node_data_len > len) {
704 return 0;
705 }
706
707 where += node_data_len;
708
709 o_in_w->type = host2wire(d->type);
710
711 size_t pad_len = 0;
712 switch (d->type) {
713 case LAUNCH_DATA_INTEGER:
714 o_in_w->number = host2wire(d->number);
715 break;
716 case LAUNCH_DATA_REAL:
717 o_in_w->float_num = host2wire_f(d->float_num);
718 break;
719 case LAUNCH_DATA_BOOL:
720 o_in_w->boolean = host2wire(d->boolean);
721 break;
722 case LAUNCH_DATA_ERRNO:
723 o_in_w->err = host2wire(d->err);
724 break;
725 case LAUNCH_DATA_FD:
726 o_in_w->fd = host2wire(d->fd);
727 if (fd_where && d->fd != -1) {
728 fd_where[*fd_cnt] = d->fd;
729 (*fd_cnt)++;
730 }
731 break;
732 case LAUNCH_DATA_STRING:
733 o_in_w->string_len = host2wire(d->string_len);
734 node_data_len += ROUND_TO_64BIT_WORD_SIZE(d->string_len + 1);
735
736 if (node_data_len > len) {
737 return 0;
738 }
739 memcpy(where, d->string, d->string_len + 1);
740
741 /* Zero padded data. */
742 pad_len = ROUND_TO_64BIT_WORD_SIZE(d->string_len + 1) - (d->string_len + 1);
743 bzero(where + d->string_len + 1, pad_len);
744
745 break;
746 case LAUNCH_DATA_OPAQUE:
747 o_in_w->opaque_size = host2wire(d->opaque_size);
748 node_data_len += ROUND_TO_64BIT_WORD_SIZE(d->opaque_size);
749 if (node_data_len > len) {
750 return 0;
751 }
752 memcpy(where, d->opaque, d->opaque_size);
753
754 /* Zero padded data. */
755 pad_len = ROUND_TO_64BIT_WORD_SIZE(d->opaque_size) - d->opaque_size;
756 bzero(where + d->opaque_size, pad_len);
757
758 break;
759 case LAUNCH_DATA_DICTIONARY:
760 case LAUNCH_DATA_ARRAY:
761 o_in_w->_array_cnt = host2wire(d->_array_cnt);
762 node_data_len += d->_array_cnt * sizeof(uint64_t);
763 if (node_data_len > len) {
764 return 0;
765 }
766
767 where += d->_array_cnt * sizeof(uint64_t);
768
769 for (i = 0; i < d->_array_cnt; i++) {
770 rsz = launch_data_pack(d->_array[i], where, len - node_data_len, fd_where, fd_cnt);
771 if (rsz == 0) {
772 return 0;
773 }
774 where += rsz;
775 node_data_len += rsz;
776 }
777 break;
778 default:
779 break;
780 }
781
782 return node_data_len;
783 }
784
785 launch_data_t
786 launch_data_unpack(void *data, size_t data_size, int *fds, size_t fd_cnt, size_t *data_offset, size_t *fdoffset)
787 {
788 launch_data_t r = data + *data_offset;
789 size_t i, tmpcnt;
790
791 //Check for integer underflow
792 if (data_size < *data_offset)
793 return NULL;
794
795 if ((data_size - *data_offset) < sizeof(struct _launch_data))
796 return NULL;
797 *data_offset += sizeof(struct _launch_data);
798
799 switch (big2wire(r->type)) {
800 case LAUNCH_DATA_DICTIONARY:
801 case LAUNCH_DATA_ARRAY:
802 tmpcnt = big2wire(r->_array_cnt);
803
804 //Check for integer overflows
805 if (tmpcnt > SIZE_MAX / sizeof(uint64_t)) {
806 errno = EAGAIN;
807 return NULL;
808 }
809
810 if ((data_size - *data_offset) < (tmpcnt * sizeof(uint64_t))) {
811 errno = EAGAIN;
812 return NULL;
813 }
814 r->_array = data + *data_offset;
815 *data_offset += tmpcnt * sizeof(uint64_t);
816 for (i = 0; i < tmpcnt; i++) {
817 r->_array[i] = launch_data_unpack(data, data_size, fds, fd_cnt, data_offset, fdoffset);
818 if (r->_array[i] == NULL)
819 return NULL;
820 }
821 r->_array_cnt = tmpcnt;
822 break;
823 case LAUNCH_DATA_STRING:
824 tmpcnt = big2wire(r->string_len);
825 if ((data_size - *data_offset) < (tmpcnt + 1)) {
826 errno = EAGAIN;
827 return NULL;
828 }
829 r->string = data + *data_offset;
830 r->string_len = tmpcnt;
831 *data_offset += ROUND_TO_64BIT_WORD_SIZE(tmpcnt + 1);
832 break;
833 case LAUNCH_DATA_OPAQUE:
834 tmpcnt = big2wire(r->opaque_size);
835 if ((data_size - *data_offset) < tmpcnt) {
836 errno = EAGAIN;
837 return NULL;
838 }
839 r->opaque = data + *data_offset;
840 r->opaque_size = tmpcnt;
841 *data_offset += ROUND_TO_64BIT_WORD_SIZE(tmpcnt);
842 break;
843 case LAUNCH_DATA_FD:
844 if (r->fd != -1 && fd_cnt > *fdoffset) {
845 r->fd = _fd(fds[*fdoffset]);
846 *fdoffset += 1;
847 }
848 break;
849 case LAUNCH_DATA_INTEGER:
850 r->number = big2wire(r->number);
851 break;
852 case LAUNCH_DATA_REAL:
853 r->float_num = big2wire_f(r->float_num);
854 break;
855 case LAUNCH_DATA_BOOL:
856 r->boolean = big2wire(r->boolean);
857 break;
858 case LAUNCH_DATA_ERRNO:
859 r->err = big2wire(r->err);
860 case LAUNCH_DATA_MACHPORT:
861 break;
862 default:
863 errno = EINVAL;
864 return NULL;
865 break;
866 }
867
868 r->type = big2wire(r->type);
869
870 return r;
871 }
872
873 int
874 launchd_msg_send(launch_t lh, launch_data_t d)
875 {
876 struct launch_msg_header lmh;
877 struct cmsghdr *cm = NULL;
878 struct msghdr mh;
879 struct iovec iov[2];
880 size_t sentctrllen = 0;
881 int r;
882
883 int fd2use = launchd_getfd(lh);
884 if (fd2use == -1) {
885 errno = EPERM;
886 return -1;
887 }
888
889 memset(&mh, 0, sizeof(mh));
890
891 /* confirm that the next hack works */
892 assert((d && lh->sendlen == 0) || (!d && lh->sendlen));
893
894 if (d) {
895 size_t fd_slots_used = 0;
896 size_t good_enough_size = 10 * 1024 * 1024;
897 uint64_t msglen;
898
899 /* hack, see the above assert to verify "correctness" */
900 free(lh->sendbuf);
901 lh->sendbuf = malloc(good_enough_size);
902 if (!lh->sendbuf) {
903 errno = ENOMEM;
904 return -1;
905 }
906
907 free(lh->sendfds);
908 lh->sendfds = malloc(4 * 1024);
909 if (!lh->sendfds) {
910 free(lh->sendbuf);
911 lh->sendbuf = NULL;
912 errno = ENOMEM;
913 return -1;
914 }
915
916 lh->sendlen = launch_data_pack(d, lh->sendbuf, good_enough_size, lh->sendfds, &fd_slots_used);
917
918 if (lh->sendlen == 0) {
919 errno = ENOMEM;
920 return -1;
921 }
922
923 lh->sendfdcnt = fd_slots_used;
924
925 msglen = lh->sendlen + sizeof(struct launch_msg_header); /* type promotion to make the host2wire() macro work right */
926 lmh.len = host2wire(msglen);
927 lmh.magic = host2wire(LAUNCH_MSG_HEADER_MAGIC);
928
929 iov[0].iov_base = &lmh;
930 iov[0].iov_len = sizeof(lmh);
931 mh.msg_iov = iov;
932 mh.msg_iovlen = 2;
933 } else {
934 mh.msg_iov = iov + 1;
935 mh.msg_iovlen = 1;
936 }
937
938 iov[1].iov_base = lh->sendbuf;
939 iov[1].iov_len = lh->sendlen;
940
941
942 if (lh->sendfdcnt > 0) {
943 sentctrllen = mh.msg_controllen = CMSG_SPACE(lh->sendfdcnt * sizeof(int));
944 cm = alloca(mh.msg_controllen);
945 mh.msg_control = cm;
946
947 memset(cm, 0, mh.msg_controllen);
948
949 cm->cmsg_len = CMSG_LEN(lh->sendfdcnt * sizeof(int));
950 cm->cmsg_level = SOL_SOCKET;
951 cm->cmsg_type = SCM_RIGHTS;
952
953 memcpy(CMSG_DATA(cm), lh->sendfds, lh->sendfdcnt * sizeof(int));
954 }
955
956 if ((r = sendmsg(fd2use, &mh, 0)) == -1) {
957 return -1;
958 } else if (r == 0) {
959 errno = ECONNRESET;
960 return -1;
961 } else if (sentctrllen != mh.msg_controllen) {
962 errno = ECONNRESET;
963 return -1;
964 }
965
966 if (d) {
967 r -= sizeof(struct launch_msg_header);
968 }
969
970 lh->sendlen -= r;
971 if (lh->sendlen > 0) {
972 memmove(lh->sendbuf, lh->sendbuf + r, lh->sendlen);
973 } else {
974 free(lh->sendbuf);
975 lh->sendbuf = malloc(0);
976 }
977
978 lh->sendfdcnt = 0;
979 free(lh->sendfds);
980 lh->sendfds = malloc(0);
981
982 if (lh->sendlen > 0) {
983 errno = EAGAIN;
984 return -1;
985 }
986
987 return 0;
988 }
989
990 int
991 launch_get_fd(void)
992 {
993 launch_globals_t globals = _launch_globals();
994 pthread_once(&globals->lc_once, launch_client_init);
995
996 if (!globals->l) {
997 errno = ENOTCONN;
998 return -1;
999 }
1000
1001 return globals->l->fd;
1002 }
1003
1004 void
1005 launch_msg_getmsgs(launch_data_t m, void *context)
1006 {
1007 launch_data_t async_resp, *sync_resp = context;
1008
1009 launch_globals_t globals = _launch_globals();
1010
1011 if ((LAUNCH_DATA_DICTIONARY == launch_data_get_type(m)) && (async_resp = launch_data_dict_lookup(m, LAUNCHD_ASYNC_MSG_KEY))) {
1012 launch_data_array_set_index(globals->async_resp, launch_data_copy(async_resp), launch_data_array_get_count(globals->async_resp));
1013 } else {
1014 *sync_resp = launch_data_copy(m);
1015 }
1016 }
1017
1018 void
1019 launch_mach_checkin_service(launch_data_t obj, const char *key, void *context __attribute__((unused)))
1020 {
1021 kern_return_t result;
1022 mach_port_t p;
1023 name_t srvnm;
1024
1025 strlcpy(srvnm, key, sizeof(srvnm));
1026
1027 result = bootstrap_check_in(bootstrap_port, srvnm, &p);
1028
1029 if (result == BOOTSTRAP_SUCCESS)
1030 launch_data_set_machport(obj, p);
1031 }
1032
1033 launch_data_t
1034 launch_msg(launch_data_t d)
1035 {
1036 launch_data_t mps, r = launch_msg_internal(d);
1037
1038 if (launch_data_get_type(d) == LAUNCH_DATA_STRING) {
1039 if (strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) != 0)
1040 return r;
1041 if (r == NULL)
1042 return r;
1043 if (launch_data_get_type(r) != LAUNCH_DATA_DICTIONARY)
1044 return r;
1045 mps = launch_data_dict_lookup(r, LAUNCH_JOBKEY_MACHSERVICES);
1046 if (mps == NULL)
1047 return r;
1048 launch_data_dict_iterate(mps, launch_mach_checkin_service, NULL);
1049 }
1050
1051 return r;
1052 }
1053
1054 extern kern_return_t vproc_mig_set_security_session(mach_port_t, uuid_t, mach_port_t);
1055
1056 static inline bool
1057 uuid_data_is_null(launch_data_t d)
1058 {
1059 bool result = false;
1060 if (launch_data_get_type(d) == LAUNCH_DATA_OPAQUE && launch_data_get_opaque_size(d) == sizeof(uuid_t)) {
1061 uuid_t existing_uuid;
1062 memcpy(existing_uuid, launch_data_get_opaque(d), sizeof(uuid_t));
1063
1064 /* A NULL UUID tells us to keep the session inherited from the parent. */
1065 result = (bool)uuid_is_null(existing_uuid);
1066 }
1067
1068 return result;
1069 }
1070
1071 launch_data_t
1072 launch_msg_internal(launch_data_t d)
1073 {
1074 launch_data_t resp = NULL;
1075
1076 if (d && (launch_data_get_type(d) == LAUNCH_DATA_STRING)
1077 && (strcmp(launch_data_get_string(d), LAUNCH_KEY_GETJOBS) == 0)
1078 && vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) {
1079 return resp;
1080 }
1081
1082 launch_globals_t globals = _launch_globals();
1083 pthread_once(&globals->lc_once, launch_client_init);
1084 if (!globals->l) {
1085 errno = ENOTCONN;
1086 return NULL;
1087 }
1088
1089 int fd2use = -1;
1090 if ((launch_data_get_type(d) == LAUNCH_DATA_STRING && strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) == 0) || globals->s_am_embedded_god) {
1091 globals->l->which = LAUNCHD_USE_CHECKIN_FD;
1092 } else {
1093 globals->l->which = LAUNCHD_USE_OTHER_FD;
1094 }
1095
1096 fd2use = launchd_getfd(globals->l);
1097
1098 if (fd2use == -1) {
1099 errno = EPERM;
1100 return NULL;
1101 }
1102
1103 #if !TARGET_OS_EMBEDDED
1104 uuid_t uuid;
1105 launch_data_t uuid_d = NULL;
1106 size_t jobs_that_need_sessions = 0;
1107 if (d && launch_data_get_type(d) == LAUNCH_DATA_DICTIONARY) {
1108 launch_data_t v = launch_data_dict_lookup(d, LAUNCH_KEY_SUBMITJOB);
1109
1110 if (v && launch_data_get_type(v) == LAUNCH_DATA_ARRAY) {
1111 size_t cnt = launch_data_array_get_count(v);
1112 size_t i = 0;
1113
1114 uuid_generate(uuid);
1115 for (i = 0; i < cnt; i++) {
1116 launch_data_t ji = launch_data_array_get_index(v, i);
1117 if (launch_data_get_type(ji) == LAUNCH_DATA_DICTIONARY) {
1118 launch_data_t existing_v = launch_data_dict_lookup(ji, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1119 if (!existing_v) {
1120 /* I really wish these were reference-counted. Sigh... */
1121 uuid_d = launch_data_new_opaque(uuid, sizeof(uuid));
1122 launch_data_dict_insert(ji, uuid_d, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1123 jobs_that_need_sessions++;
1124 } else if (launch_data_get_type(existing_v) == LAUNCH_DATA_OPAQUE) {
1125 jobs_that_need_sessions += uuid_data_is_null(existing_v) ? 0 : 1;
1126 }
1127 }
1128 }
1129 } else if (v && launch_data_get_type(v) == LAUNCH_DATA_DICTIONARY) {
1130 launch_data_t existing_v = launch_data_dict_lookup(v, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1131 if (!existing_v) {
1132 uuid_generate(uuid);
1133 uuid_d = launch_data_new_opaque(uuid, sizeof(uuid));
1134 launch_data_dict_insert(v, uuid_d, LAUNCH_JOBKEY_SECURITYSESSIONUUID);
1135 jobs_that_need_sessions++;
1136 } else {
1137 jobs_that_need_sessions += uuid_data_is_null(existing_v) ? 0 : 1;
1138 }
1139 }
1140 }
1141 #endif
1142
1143 pthread_mutex_lock(&globals->lc_mtx);
1144
1145 if (d && launchd_msg_send(globals->l, d) == -1) {
1146 do {
1147 if (errno != EAGAIN)
1148 goto out;
1149 } while (launchd_msg_send(globals->l, NULL) == -1);
1150 }
1151
1152 while (resp == NULL) {
1153 if (d == NULL && launch_data_array_get_count(globals->async_resp) > 0) {
1154 resp = launch_data_array_pop_first(globals->async_resp);
1155 goto out;
1156 }
1157 if (launchd_msg_recv(globals->l, launch_msg_getmsgs, &resp) == -1) {
1158 if (errno != EAGAIN) {
1159 goto out;
1160 } else if (d == NULL) {
1161 errno = 0;
1162 goto out;
1163 } else {
1164 fd_set rfds;
1165
1166 FD_ZERO(&rfds);
1167 FD_SET(fd2use, &rfds);
1168
1169 select(fd2use + 1, &rfds, NULL, NULL, NULL);
1170 }
1171 }
1172 }
1173
1174 out:
1175 #if !TARGET_OS_EMBEDDED
1176 if (!uuid_is_null(uuid) && resp && jobs_that_need_sessions > 0) {
1177 mach_port_t session_port = _audit_session_self();
1178 launch_data_type_t resp_type = launch_data_get_type(resp);
1179
1180 bool set_session = false;
1181 if (resp_type == LAUNCH_DATA_ERRNO) {
1182 set_session = (launch_data_get_errno(resp) == ENEEDAUTH);
1183 } else if (resp_type == LAUNCH_DATA_ARRAY) {
1184 set_session = true;
1185 }
1186
1187 kern_return_t kr = KERN_FAILURE;
1188 if (set_session) {
1189 kr = vproc_mig_set_security_session(bootstrap_port, uuid, session_port);
1190 }
1191
1192 if (kr == KERN_SUCCESS) {
1193 if (resp_type == LAUNCH_DATA_ERRNO) {
1194 launch_data_set_errno(resp, 0);
1195 } else {
1196 size_t i = 0;
1197 for (i = 0; i < launch_data_array_get_count(resp); i++) {
1198 launch_data_t ri = launch_data_array_get_index(resp, i);
1199
1200 int recvd_err = 0;
1201 if (launch_data_get_type(ri) == LAUNCH_DATA_ERRNO && (recvd_err = launch_data_get_errno(ri))) {
1202 launch_data_set_errno(ri, recvd_err == ENEEDAUTH ? 0 : recvd_err);
1203 }
1204 }
1205 }
1206 }
1207
1208 mach_port_deallocate(mach_task_self(), session_port);
1209 }
1210 #endif
1211
1212 pthread_mutex_unlock(&globals->lc_mtx);
1213
1214 return resp;
1215 }
1216
1217 int
1218 launchd_msg_recv(launch_t lh, void (*cb)(launch_data_t, void *), void *context)
1219 {
1220 struct cmsghdr *cm = alloca(4096);
1221 launch_data_t rmsg = NULL;
1222 size_t data_offset, fd_offset;
1223 struct msghdr mh;
1224 struct iovec iov;
1225 int r;
1226
1227 int fd2use = launchd_getfd(lh);
1228 if (fd2use == -1) {
1229 errno = EPERM;
1230 return -1;
1231 }
1232
1233 memset(&mh, 0, sizeof(mh));
1234 mh.msg_iov = &iov;
1235 mh.msg_iovlen = 1;
1236
1237 lh->recvbuf = reallocf(lh->recvbuf, lh->recvlen + 8*1024);
1238
1239 iov.iov_base = lh->recvbuf + lh->recvlen;
1240 iov.iov_len = 8*1024;
1241 mh.msg_control = cm;
1242 mh.msg_controllen = 4096;
1243
1244 if ((r = recvmsg(fd2use, &mh, 0)) == -1)
1245 return -1;
1246 if (r == 0) {
1247 errno = ECONNRESET;
1248 return -1;
1249 }
1250 if (mh.msg_flags & MSG_CTRUNC) {
1251 errno = ECONNABORTED;
1252 return -1;
1253 }
1254 lh->recvlen += r;
1255 if (mh.msg_controllen > 0) {
1256 lh->recvfds = reallocf(lh->recvfds, lh->recvfdcnt * sizeof(int) + mh.msg_controllen - sizeof(struct cmsghdr));
1257 memcpy(lh->recvfds + lh->recvfdcnt, CMSG_DATA(cm), mh.msg_controllen - sizeof(struct cmsghdr));
1258 lh->recvfdcnt += (mh.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
1259 }
1260
1261 r = 0;
1262
1263 while (lh->recvlen > 0) {
1264 struct launch_msg_header *lmhp = lh->recvbuf;
1265 uint64_t tmplen;
1266 data_offset = sizeof(struct launch_msg_header);
1267 fd_offset = 0;
1268
1269 if (lh->recvlen < sizeof(struct launch_msg_header))
1270 goto need_more_data;
1271
1272 tmplen = big2wire(lmhp->len);
1273
1274 if (big2wire(lmhp->magic) != LAUNCH_MSG_HEADER_MAGIC || tmplen <= sizeof(struct launch_msg_header)) {
1275 errno = EBADRPC;
1276 goto out_bad;
1277 }
1278
1279 if (lh->recvlen < tmplen) {
1280 goto need_more_data;
1281 }
1282
1283 if ((rmsg = launch_data_unpack(lh->recvbuf, lh->recvlen, lh->recvfds, lh->recvfdcnt, &data_offset, &fd_offset)) == NULL) {
1284 errno = EBADRPC;
1285 goto out_bad;
1286 }
1287
1288 launch_globals_t globals = _launch_globals();
1289
1290 globals->in_flight_msg_recv_client = lh;
1291
1292 cb(rmsg, context);
1293
1294 /* launchd and only launchd can call launchd_close() as a part of the callback */
1295 if (globals->in_flight_msg_recv_client == NULL) {
1296 r = 0;
1297 break;
1298 }
1299
1300 lh->recvlen -= data_offset;
1301 if (lh->recvlen > 0) {
1302 memmove(lh->recvbuf, lh->recvbuf + data_offset, lh->recvlen);
1303 } else {
1304 free(lh->recvbuf);
1305 lh->recvbuf = malloc(0);
1306 }
1307
1308 lh->recvfdcnt -= fd_offset;
1309 if (lh->recvfdcnt > 0) {
1310 memmove(lh->recvfds, lh->recvfds + fd_offset, lh->recvfdcnt * sizeof(int));
1311 } else {
1312 free(lh->recvfds);
1313 lh->recvfds = malloc(0);
1314 }
1315 }
1316
1317 return r;
1318
1319 need_more_data:
1320 errno = EAGAIN;
1321 out_bad:
1322 return -1;
1323 }
1324
1325 launch_data_t
1326 launch_data_copy(launch_data_t o)
1327 {
1328 launch_data_t r = launch_data_alloc(o->type);
1329 size_t i;
1330
1331 free(r->_array);
1332 memcpy(r, o, sizeof(struct _launch_data));
1333
1334 switch (o->type) {
1335 case LAUNCH_DATA_DICTIONARY:
1336 case LAUNCH_DATA_ARRAY:
1337 r->_array = calloc(1, o->_array_cnt * sizeof(launch_data_t));
1338 for (i = 0; i < o->_array_cnt; i++) {
1339 if (o->_array[i])
1340 r->_array[i] = launch_data_copy(o->_array[i]);
1341 }
1342 break;
1343 case LAUNCH_DATA_STRING:
1344 r->string = strdup(o->string);
1345 break;
1346 case LAUNCH_DATA_OPAQUE:
1347 r->opaque = malloc(o->opaque_size);
1348 memcpy(r->opaque, o->opaque, o->opaque_size);
1349 break;
1350 default:
1351 break;
1352 }
1353
1354 return r;
1355 }
1356
1357 int
1358 _fd(int fd)
1359 {
1360 if (fd >= 0)
1361 fcntl(fd, F_SETFD, 1);
1362 return fd;
1363 }
1364
1365 launch_data_t
1366 launch_data_new_errno(int e)
1367 {
1368 launch_data_t r = launch_data_alloc(LAUNCH_DATA_ERRNO);
1369
1370 if (r)
1371 launch_data_set_errno(r, e);
1372
1373 return r;
1374 }
1375
1376 launch_data_t
1377 launch_data_new_fd(int fd)
1378 {
1379 launch_data_t r = launch_data_alloc(LAUNCH_DATA_FD);
1380
1381 if (r)
1382 launch_data_set_fd(r, fd);
1383
1384 return r;
1385 }
1386
1387 launch_data_t
1388 launch_data_new_machport(mach_port_t p)
1389 {
1390 launch_data_t r = launch_data_alloc(LAUNCH_DATA_MACHPORT);
1391
1392 if (r)
1393 launch_data_set_machport(r, p);
1394
1395 return r;
1396 }
1397
1398 launch_data_t
1399 launch_data_new_integer(long long n)
1400 {
1401 launch_data_t r = launch_data_alloc(LAUNCH_DATA_INTEGER);
1402
1403 if (r)
1404 launch_data_set_integer(r, n);
1405
1406 return r;
1407 }
1408
1409 launch_data_t
1410 launch_data_new_bool(bool b)
1411 {
1412 launch_data_t r = launch_data_alloc(LAUNCH_DATA_BOOL);
1413
1414 if (r)
1415 launch_data_set_bool(r, b);
1416
1417 return r;
1418 }
1419
1420 launch_data_t
1421 launch_data_new_real(double d)
1422 {
1423 launch_data_t r = launch_data_alloc(LAUNCH_DATA_REAL);
1424
1425 if (r)
1426 launch_data_set_real(r, d);
1427
1428 return r;
1429 }
1430
1431 launch_data_t
1432 launch_data_new_string(const char *s)
1433 {
1434 launch_data_t r = launch_data_alloc(LAUNCH_DATA_STRING);
1435
1436 if (r == NULL)
1437 return NULL;
1438
1439 if (!launch_data_set_string(r, s)) {
1440 launch_data_free(r);
1441 return NULL;
1442 }
1443
1444 return r;
1445 }
1446
1447 launch_data_t
1448 launch_data_new_opaque(const void *o, size_t os)
1449 {
1450 launch_data_t r = launch_data_alloc(LAUNCH_DATA_OPAQUE);
1451
1452 if (r == NULL)
1453 return NULL;
1454
1455 if (!launch_data_set_opaque(r, o, os)) {
1456 launch_data_free(r);
1457 return NULL;
1458 }
1459
1460 return r;
1461 }
1462
1463 void
1464 load_launchd_jobs_at_loginwindow_prompt(int flags __attribute__((unused)), ...)
1465 {
1466 _vprocmgr_init(VPROCMGR_SESSION_LOGINWINDOW);
1467 }
1468
1469 pid_t
1470 create_and_switch_to_per_session_launchd(const char *login __attribute__((unused)), int flags, ...)
1471 {
1472 uid_t target_user = geteuid() ? geteuid() : getuid();
1473 if (_vprocmgr_move_subset_to_user(target_user, VPROCMGR_SESSION_AQUA, flags)) {
1474 return -1;
1475 }
1476
1477 return 1;
1478 }