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