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