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