]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/liblaunch.c
cd36506a9bb78ebef0e97493fab3d9fd0c022589
[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 "liblaunch_public.h"
23 #include "liblaunch_private.h"
24 #include "liblaunch_internal.h"
25
26 #include <mach/mach.h>
27 #include <libkern/OSByteOrder.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/fcntl.h>
31 #include <sys/un.h>
32 #include <sys/uio.h>
33 #include <sys/stat.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <pthread.h>
38 #include <unistd.h>
39 #include <errno.h>
40 #include <pwd.h>
41 #include <assert.h>
42
43 #include "libbootstrap_public.h"
44 #include "libvproc_public.h"
45 #include "libvproc_private.h"
46 #include "libvproc_internal.h"
47
48 /* __OSBogusByteSwap__() must not really exist in the symbol namespace
49 * in order for the following to generate an error at build time.
50 */
51 extern void __OSBogusByteSwap__(void);
52
53 #define host2big(x) \
54 ({ typeof (x) _X, _x = (x); \
55 switch (sizeof(_x)) { \
56 case 8: \
57 _X = OSSwapHostToBigInt64(_x); \
58 break; \
59 case 4: \
60 _X = OSSwapHostToBigInt32(_x); \
61 break; \
62 case 2: \
63 _X = OSSwapHostToBigInt16(_x); \
64 break; \
65 case 1: \
66 _X = _x; \
67 break; \
68 default: \
69 __OSBogusByteSwap__(); \
70 break; \
71 } \
72 _X; \
73 })
74
75
76 #define big2host(x) \
77 ({ typeof (x) _X, _x = (x); \
78 switch (sizeof(_x)) { \
79 case 8: \
80 _X = OSSwapBigToHostInt64(_x); \
81 break; \
82 case 4: \
83 _X = OSSwapBigToHostInt32(_x); \
84 break; \
85 case 2: \
86 _X = OSSwapBigToHostInt16(_x); \
87 break; \
88 case 1: \
89 _X = _x; \
90 break; \
91 default: \
92 __OSBogusByteSwap__(); \
93 break; \
94 } \
95 _X; \
96 })
97
98
99 struct launch_msg_header {
100 uint64_t magic;
101 uint64_t len;
102 };
103
104 #define LAUNCH_MSG_HEADER_MAGIC 0xD2FEA02366B39A41ull
105
106 struct _launch_data {
107 uint64_t type;
108 union {
109 struct {
110 union {
111 launch_data_t *_array;
112 char *string;
113 void *opaque;
114 int64_t __junk;
115 };
116 union {
117 uint64_t _array_cnt;
118 uint64_t string_len;
119 uint64_t opaque_size;
120 };
121 };
122 int fd;
123 mach_port_t mp;
124 int err;
125 long long number;
126 uint32_t boolean; /* We'd use 'bool' but this struct needs to be used under Rosetta, and sizeof(bool) is different between PowerPC and Intel */
127 double float_num;
128 };
129 };
130
131 struct _launch {
132 void *sendbuf;
133 int *sendfds;
134 void *recvbuf;
135 int *recvfds;
136 size_t sendlen;
137 size_t sendfdcnt;
138 size_t recvlen;
139 size_t recvfdcnt;
140 int fd;
141 };
142
143 static launch_data_t launch_data_array_pop_first(launch_data_t where);
144 static int _fd(int fd);
145 static void launch_client_init(void);
146 static void launch_msg_getmsgs(launch_data_t m, void *context);
147 static launch_data_t launch_msg_internal(launch_data_t d);
148 static void launch_mach_checkin_service(launch_data_t obj, const char *key, void *context);
149
150 static pthread_once_t _lc_once = PTHREAD_ONCE_INIT;
151
152 static struct _launch_client {
153 pthread_mutex_t mtx;
154 launch_t l;
155 launch_data_t async_resp;
156 } *_lc = NULL;
157
158 void
159 launch_client_init(void)
160 {
161 struct sockaddr_un sun;
162 char *where = getenv(LAUNCHD_SOCKET_ENV);
163 char *_launchd_fd = getenv(LAUNCHD_TRUSTED_FD_ENV);
164 int dfd, lfd = -1;
165 name_t spath;
166
167 _lc = calloc(1, sizeof(struct _launch_client));
168
169 if (!_lc)
170 return;
171
172 pthread_mutex_init(&_lc->mtx, NULL);
173
174 if (_launchd_fd) {
175 lfd = strtol(_launchd_fd, NULL, 10);
176 if ((dfd = dup(lfd)) >= 0) {
177 close(dfd);
178 _fd(lfd);
179 } else {
180 lfd = -1;
181 }
182 unsetenv(LAUNCHD_TRUSTED_FD_ENV);
183 }
184 if (lfd == -1) {
185 memset(&sun, 0, sizeof(sun));
186 sun.sun_family = AF_UNIX;
187
188 if (where && where[0] != '\0') {
189 strncpy(sun.sun_path, where, sizeof(sun.sun_path));
190 } else if (!getenv("SUDO_COMMAND") && _vprocmgr_getsocket(spath) == 0) {
191 size_t min_len;
192
193 min_len = sizeof(sun.sun_path) < sizeof(spath) ? sizeof(sun.sun_path) : sizeof(spath);
194
195 strncpy(sun.sun_path, spath, min_len);
196 } else {
197 strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path));
198 }
199
200 if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1)
201 goto out_bad;
202 if (-1 == connect(lfd, (struct sockaddr *)&sun, sizeof(sun)))
203 goto out_bad;
204 }
205 if (!(_lc->l = launchd_fdopen(lfd)))
206 goto out_bad;
207 if (!(_lc->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY)))
208 goto out_bad;
209
210 return;
211 out_bad:
212 if (_lc->l)
213 launchd_close(_lc->l, close);
214 else if (lfd != -1)
215 close(lfd);
216 if (_lc)
217 free(_lc);
218 _lc = NULL;
219 }
220
221 launch_data_t
222 launch_data_alloc(launch_data_type_t t)
223 {
224 launch_data_t d = calloc(1, sizeof(struct _launch));
225
226 if (d) {
227 d->type = t;
228 switch (t) {
229 case LAUNCH_DATA_DICTIONARY:
230 case LAUNCH_DATA_ARRAY:
231 d->_array = malloc(0);
232 break;
233 default:
234 break;
235 }
236 }
237
238 return d;
239 }
240
241 launch_data_type_t
242 launch_data_get_type(launch_data_t d)
243 {
244 return d->type;
245 }
246
247 void
248 launch_data_free(launch_data_t d)
249 {
250 size_t i;
251
252 switch (d->type) {
253 case LAUNCH_DATA_DICTIONARY:
254 case LAUNCH_DATA_ARRAY:
255 for (i = 0; i < d->_array_cnt; i++)
256 launch_data_free(d->_array[i]);
257 free(d->_array);
258 break;
259 case LAUNCH_DATA_STRING:
260 if (d->string)
261 free(d->string);
262 break;
263 case LAUNCH_DATA_OPAQUE:
264 if (d->opaque)
265 free(d->opaque);
266 break;
267 default:
268 break;
269 }
270 free(d);
271 }
272
273 size_t
274 launch_data_dict_get_count(launch_data_t dict)
275 {
276 return dict->_array_cnt / 2;
277 }
278
279
280 bool
281 launch_data_dict_insert(launch_data_t dict, launch_data_t what, const char *key)
282 {
283 size_t i;
284 launch_data_t thekey = launch_data_alloc(LAUNCH_DATA_STRING);
285
286 launch_data_set_string(thekey, key);
287
288 for (i = 0; i < dict->_array_cnt; i += 2) {
289 if (!strcasecmp(key, dict->_array[i]->string)) {
290 launch_data_array_set_index(dict, thekey, i);
291 launch_data_array_set_index(dict, what, i + 1);
292 return true;
293 }
294 }
295 launch_data_array_set_index(dict, thekey, i);
296 launch_data_array_set_index(dict, what, i + 1);
297 return true;
298 }
299
300 launch_data_t
301 launch_data_dict_lookup(launch_data_t dict, const char *key)
302 {
303 size_t i;
304
305 if (LAUNCH_DATA_DICTIONARY != dict->type)
306 return NULL;
307
308 for (i = 0; i < dict->_array_cnt; i += 2) {
309 if (!strcasecmp(key, dict->_array[i]->string))
310 return dict->_array[i + 1];
311 }
312
313 return NULL;
314 }
315
316 bool
317 launch_data_dict_remove(launch_data_t dict, const char *key)
318 {
319 size_t i;
320
321 for (i = 0; i < dict->_array_cnt; i += 2) {
322 if (!strcasecmp(key, dict->_array[i]->string))
323 break;
324 }
325 if (i == dict->_array_cnt)
326 return false;
327 launch_data_free(dict->_array[i]);
328 launch_data_free(dict->_array[i + 1]);
329 memmove(dict->_array + i, dict->_array + i + 2, (dict->_array_cnt - (i + 2)) * sizeof(launch_data_t));
330 dict->_array_cnt -= 2;
331 return true;
332 }
333
334 void
335 launch_data_dict_iterate(launch_data_t dict, void (*cb)(launch_data_t, const char *, void *), void *context)
336 {
337 size_t i;
338
339 if (LAUNCH_DATA_DICTIONARY != dict->type)
340 return;
341
342 for (i = 0; i < dict->_array_cnt; i += 2)
343 cb(dict->_array[i + 1], dict->_array[i]->string, context);
344 }
345
346 bool
347 launch_data_array_set_index(launch_data_t where, launch_data_t what, size_t ind)
348 {
349 if ((ind + 1) >= where->_array_cnt) {
350 where->_array = reallocf(where->_array, (ind + 1) * sizeof(launch_data_t));
351 memset(where->_array + where->_array_cnt, 0, (ind + 1 - where->_array_cnt) * sizeof(launch_data_t));
352 where->_array_cnt = ind + 1;
353 }
354
355 if (where->_array[ind])
356 launch_data_free(where->_array[ind]);
357 where->_array[ind] = what;
358 return true;
359 }
360
361 launch_data_t
362 launch_data_array_get_index(launch_data_t where, size_t ind)
363 {
364 if (LAUNCH_DATA_ARRAY != where->type)
365 return NULL;
366 if (ind < where->_array_cnt)
367 return where->_array[ind];
368 return NULL;
369 }
370
371 launch_data_t
372 launch_data_array_pop_first(launch_data_t where)
373 {
374 launch_data_t r = NULL;
375
376 if (where->_array_cnt > 0) {
377 r = where->_array[0];
378 memmove(where->_array, where->_array + 1, (where->_array_cnt - 1) * sizeof(launch_data_t));
379 where->_array_cnt--;
380 }
381 return r;
382 }
383
384 size_t
385 launch_data_array_get_count(launch_data_t where)
386 {
387 if (LAUNCH_DATA_ARRAY != where->type)
388 return 0;
389 return where->_array_cnt;
390 }
391
392 bool
393 launch_data_set_errno(launch_data_t d, int e)
394 {
395 d->err = e;
396 return true;
397 }
398
399 bool
400 launch_data_set_fd(launch_data_t d, int fd)
401 {
402 d->fd = fd;
403 return true;
404 }
405
406 bool
407 launch_data_set_machport(launch_data_t d, mach_port_t p)
408 {
409 d->mp = p;
410 return true;
411 }
412
413 bool
414 launch_data_set_integer(launch_data_t d, long long n)
415 {
416 d->number = n;
417 return true;
418 }
419
420 bool
421 launch_data_set_bool(launch_data_t d, bool b)
422 {
423 d->boolean = b;
424 return true;
425 }
426
427 bool
428 launch_data_set_real(launch_data_t d, double n)
429 {
430 d->float_num = n;
431 return true;
432 }
433
434 bool
435 launch_data_set_string(launch_data_t d, const char *s)
436 {
437 if (d->string)
438 free(d->string);
439 d->string = strdup(s);
440 if (d->string) {
441 d->string_len = strlen(d->string);
442 return true;
443 }
444 return false;
445 }
446
447 bool
448 launch_data_set_opaque(launch_data_t d, const void *o, size_t os)
449 {
450 d->opaque_size = os;
451 if (d->opaque)
452 free(d->opaque);
453 d->opaque = malloc(os);
454 if (d->opaque) {
455 memcpy(d->opaque, o, os);
456 return true;
457 }
458 return false;
459 }
460
461 int
462 launch_data_get_errno(launch_data_t d)
463 {
464 return d->err;
465 }
466
467 int
468 launch_data_get_fd(launch_data_t d)
469 {
470 return d->fd;
471 }
472
473 mach_port_t
474 launch_data_get_machport(launch_data_t d)
475 {
476 return d->mp;
477 }
478
479 long long
480 launch_data_get_integer(launch_data_t d)
481 {
482 return d->number;
483 }
484
485 bool
486 launch_data_get_bool(launch_data_t d)
487 {
488 return d->boolean;
489 }
490
491 double
492 launch_data_get_real(launch_data_t d)
493 {
494 return d->float_num;
495 }
496
497 const char *
498 launch_data_get_string(launch_data_t d)
499 {
500 if (LAUNCH_DATA_STRING != d->type)
501 return NULL;
502 return d->string;
503 }
504
505 void *
506 launch_data_get_opaque(launch_data_t d)
507 {
508 if (LAUNCH_DATA_OPAQUE != d->type)
509 return NULL;
510 return d->opaque;
511 }
512
513 size_t
514 launch_data_get_opaque_size(launch_data_t d)
515 {
516 return d->opaque_size;
517 }
518
519 int
520 launchd_getfd(launch_t l)
521 {
522 return l->fd;
523 }
524
525 launch_t
526 launchd_fdopen(int fd)
527 {
528 launch_t c;
529
530 c = calloc(1, sizeof(struct _launch));
531 if (!c)
532 return NULL;
533
534 c->fd = fd;
535
536 fcntl(fd, F_SETFL, O_NONBLOCK);
537
538 if ((c->sendbuf = malloc(0)) == NULL)
539 goto out_bad;
540 if ((c->sendfds = malloc(0)) == NULL)
541 goto out_bad;
542 if ((c->recvbuf = malloc(0)) == NULL)
543 goto out_bad;
544 if ((c->recvfds = malloc(0)) == NULL)
545 goto out_bad;
546
547 return c;
548
549 out_bad:
550 if (c->sendbuf)
551 free(c->sendbuf);
552 if (c->sendfds)
553 free(c->sendfds);
554 if (c->recvbuf)
555 free(c->recvbuf);
556 if (c->recvfds)
557 free(c->recvfds);
558 free(c);
559 return NULL;
560 }
561
562 void
563 launchd_close(launch_t lh, typeof(close) closefunc)
564 {
565 if (lh->sendbuf)
566 free(lh->sendbuf);
567 if (lh->sendfds)
568 free(lh->sendfds);
569 if (lh->recvbuf)
570 free(lh->recvbuf);
571 if (lh->recvfds)
572 free(lh->recvfds);
573 closefunc(lh->fd);
574 free(lh);
575 }
576
577 #define ROUND_TO_64BIT_WORD_SIZE(x) ((x + 7) & ~7)
578
579 size_t
580 launch_data_pack(launch_data_t d, void *where, size_t len, int *fd_where, size_t *fd_cnt)
581 {
582 launch_data_t o_in_w = where;
583 size_t i, rsz, total_data_len = sizeof(struct _launch_data);
584
585 if (total_data_len > len) {
586 return 0;
587 }
588
589 where += total_data_len;
590
591 o_in_w->type = host2big(d->type);
592
593 switch (d->type) {
594 case LAUNCH_DATA_INTEGER:
595 o_in_w->number = host2big(d->number);
596 break;
597 case LAUNCH_DATA_REAL:
598 o_in_w->float_num = host2big(d->float_num);
599 break;
600 case LAUNCH_DATA_BOOL:
601 o_in_w->boolean = host2big(d->boolean);
602 break;
603 case LAUNCH_DATA_ERRNO:
604 o_in_w->err = host2big(d->err);
605 break;
606 case LAUNCH_DATA_FD:
607 o_in_w->fd = host2big(d->fd);
608 if (fd_where && d->fd != -1) {
609 fd_where[*fd_cnt] = d->fd;
610 (*fd_cnt)++;
611 }
612 break;
613 case LAUNCH_DATA_STRING:
614 o_in_w->string_len = host2big(d->string_len);
615 total_data_len += ROUND_TO_64BIT_WORD_SIZE(strlen(d->string) + 1);
616 if (total_data_len > len) {
617 return 0;
618 }
619 memcpy(where, d->string, strlen(d->string) + 1);
620 break;
621 case LAUNCH_DATA_OPAQUE:
622 o_in_w->opaque_size = host2big(d->opaque_size);
623 total_data_len += ROUND_TO_64BIT_WORD_SIZE(d->opaque_size);
624 if (total_data_len > len) {
625 return 0;
626 }
627 memcpy(where, d->opaque, d->opaque_size);
628 break;
629 case LAUNCH_DATA_DICTIONARY:
630 case LAUNCH_DATA_ARRAY:
631 o_in_w->_array_cnt = host2big(d->_array_cnt);
632 total_data_len += d->_array_cnt * sizeof(uint64_t);
633 if (total_data_len > len) {
634 return 0;
635 }
636
637 where += d->_array_cnt * sizeof(uint64_t);
638
639 for (i = 0; i < d->_array_cnt; i++) {
640 rsz = launch_data_pack(d->_array[i], where, len - total_data_len, fd_where, fd_cnt);
641 if (rsz == 0) {
642 return 0;
643 }
644 where += rsz;
645 total_data_len += rsz;
646 }
647 break;
648 default:
649 break;
650 }
651
652 return total_data_len;
653 }
654
655 launch_data_t
656 launch_data_unpack(void *data, size_t data_size, int *fds, size_t fd_cnt, size_t *data_offset, size_t *fdoffset)
657 {
658 launch_data_t r = data + *data_offset;
659 size_t i, tmpcnt;
660
661 if ((data_size - *data_offset) < sizeof(struct _launch_data))
662 return NULL;
663 *data_offset += sizeof(struct _launch_data);
664
665 switch (big2host(r->type)) {
666 case LAUNCH_DATA_DICTIONARY:
667 case LAUNCH_DATA_ARRAY:
668 tmpcnt = big2host(r->_array_cnt);
669 if ((data_size - *data_offset) < (tmpcnt * sizeof(uint64_t))) {
670 errno = EAGAIN;
671 return NULL;
672 }
673 r->_array = data + *data_offset;
674 *data_offset += tmpcnt * sizeof(uint64_t);
675 for (i = 0; i < tmpcnt; i++) {
676 r->_array[i] = launch_data_unpack(data, data_size, fds, fd_cnt, data_offset, fdoffset);
677 if (r->_array[i] == NULL)
678 return NULL;
679 }
680 r->_array_cnt = tmpcnt;
681 break;
682 case LAUNCH_DATA_STRING:
683 tmpcnt = big2host(r->string_len);
684 if ((data_size - *data_offset) < (tmpcnt + 1)) {
685 errno = EAGAIN;
686 return NULL;
687 }
688 r->string = data + *data_offset;
689 r->string_len = tmpcnt;
690 *data_offset += ROUND_TO_64BIT_WORD_SIZE(tmpcnt + 1);
691 break;
692 case LAUNCH_DATA_OPAQUE:
693 tmpcnt = big2host(r->opaque_size);
694 if ((data_size - *data_offset) < tmpcnt) {
695 errno = EAGAIN;
696 return NULL;
697 }
698 r->opaque = data + *data_offset;
699 r->opaque_size = tmpcnt;
700 *data_offset += ROUND_TO_64BIT_WORD_SIZE(tmpcnt);
701 break;
702 case LAUNCH_DATA_FD:
703 if (r->fd != -1 && fd_cnt > *fdoffset) {
704 r->fd = _fd(fds[*fdoffset]);
705 *fdoffset += 1;
706 }
707 break;
708 case LAUNCH_DATA_INTEGER:
709 r->number = big2host(r->number);
710 break;
711 case LAUNCH_DATA_REAL:
712 r->float_num = big2host(r->float_num);
713 break;
714 case LAUNCH_DATA_BOOL:
715 r->boolean = big2host(r->boolean);
716 break;
717 case LAUNCH_DATA_ERRNO:
718 r->err = big2host(r->err);
719 case LAUNCH_DATA_MACHPORT:
720 break;
721 default:
722 errno = EINVAL;
723 return NULL;
724 break;
725 }
726
727 r->type = big2host(r->type);
728
729 return r;
730 }
731
732 int launchd_msg_send(launch_t lh, launch_data_t d)
733 {
734 struct launch_msg_header lmh;
735 struct cmsghdr *cm = NULL;
736 struct msghdr mh;
737 struct iovec iov[2];
738 size_t sentctrllen = 0;
739 int r;
740
741 memset(&mh, 0, sizeof(mh));
742
743 /* confirm that the next hack works */
744 assert((d && lh->sendlen == 0) || (!d && lh->sendlen));
745
746 if (d) {
747 size_t fd_slots_used = 0;
748 size_t good_enough_size = 10 * 1024 * 1024;
749 uint64_t msglen;
750
751 /* hack, see the above assert to verify "correctness" */
752 free(lh->sendbuf);
753 lh->sendbuf = malloc(good_enough_size);
754 free(lh->sendfds);
755 lh->sendfds = malloc(4 * 1024);
756
757 lh->sendlen = launch_data_pack(d, lh->sendbuf, good_enough_size, lh->sendfds, &fd_slots_used);
758
759 if (lh->sendlen == 0) {
760 errno = ENOMEM;
761 return -1;
762 }
763
764 lh->sendfdcnt = fd_slots_used;
765
766 msglen = lh->sendlen + sizeof(struct launch_msg_header); /* type promotion to make the host2big() macro work right */
767 lmh.len = host2big(msglen);
768 lmh.magic = host2big(LAUNCH_MSG_HEADER_MAGIC);
769
770 iov[0].iov_base = &lmh;
771 iov[0].iov_len = sizeof(lmh);
772 mh.msg_iov = iov;
773 mh.msg_iovlen = 2;
774 } else {
775 mh.msg_iov = iov + 1;
776 mh.msg_iovlen = 1;
777 }
778
779 iov[1].iov_base = lh->sendbuf;
780 iov[1].iov_len = lh->sendlen;
781
782
783 if (lh->sendfdcnt > 0) {
784 sentctrllen = mh.msg_controllen = CMSG_SPACE(lh->sendfdcnt * sizeof(int));
785 cm = alloca(mh.msg_controllen);
786 mh.msg_control = cm;
787
788 memset(cm, 0, mh.msg_controllen);
789
790 cm->cmsg_len = CMSG_LEN(lh->sendfdcnt * sizeof(int));
791 cm->cmsg_level = SOL_SOCKET;
792 cm->cmsg_type = SCM_RIGHTS;
793
794 memcpy(CMSG_DATA(cm), lh->sendfds, lh->sendfdcnt * sizeof(int));
795 }
796
797 if ((r = sendmsg(lh->fd, &mh, 0)) == -1) {
798 return -1;
799 } else if (r == 0) {
800 errno = ECONNRESET;
801 return -1;
802 } else if (sentctrllen != mh.msg_controllen) {
803 errno = ECONNRESET;
804 return -1;
805 }
806
807 if (d) {
808 r -= sizeof(struct launch_msg_header);
809 }
810
811 lh->sendlen -= r;
812 if (lh->sendlen > 0) {
813 memmove(lh->sendbuf, lh->sendbuf + r, lh->sendlen);
814 } else {
815 free(lh->sendbuf);
816 lh->sendbuf = malloc(0);
817 }
818
819 lh->sendfdcnt = 0;
820 free(lh->sendfds);
821 lh->sendfds = malloc(0);
822
823 if (lh->sendlen > 0) {
824 errno = EAGAIN;
825 return -1;
826 }
827
828 return 0;
829 }
830
831
832 int
833 launch_get_fd(void)
834 {
835 pthread_once(&_lc_once, launch_client_init);
836
837 if (!_lc) {
838 errno = ENOTCONN;
839 return -1;
840 }
841
842 return _lc->l->fd;
843 }
844
845 void
846 launch_msg_getmsgs(launch_data_t m, void *context)
847 {
848 launch_data_t async_resp, *sync_resp = context;
849
850 if ((LAUNCH_DATA_DICTIONARY == launch_data_get_type(m)) && (async_resp = launch_data_dict_lookup(m, LAUNCHD_ASYNC_MSG_KEY))) {
851 launch_data_array_set_index(_lc->async_resp, launch_data_copy(async_resp), launch_data_array_get_count(_lc->async_resp));
852 } else {
853 *sync_resp = launch_data_copy(m);
854 }
855 }
856
857 void
858 launch_mach_checkin_service(launch_data_t obj, const char *key, void *context __attribute__((unused)))
859 {
860 kern_return_t result;
861 mach_port_t p;
862 name_t srvnm;
863
864 strlcpy(srvnm, key, sizeof(srvnm));
865
866 result = bootstrap_check_in(bootstrap_port, srvnm, &p);
867
868 if (result == BOOTSTRAP_SUCCESS)
869 launch_data_set_machport(obj, p);
870 }
871
872 launch_data_t
873 launch_msg(launch_data_t d)
874 {
875 launch_data_t mps, r = launch_msg_internal(d);
876
877 if (launch_data_get_type(d) == LAUNCH_DATA_STRING) {
878 if (strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) != 0)
879 return r;
880 if (r == NULL)
881 return r;
882 if (launch_data_get_type(r) != LAUNCH_DATA_DICTIONARY)
883 return r;
884 mps = launch_data_dict_lookup(r, LAUNCH_JOBKEY_MACHSERVICES);
885 if (mps == NULL)
886 return r;
887 launch_data_dict_iterate(mps, launch_mach_checkin_service, NULL);
888 }
889
890 return r;
891 }
892
893 launch_data_t
894 launch_msg_internal(launch_data_t d)
895 {
896 launch_data_t resp = NULL;
897
898 if (d && (launch_data_get_type(d) == LAUNCH_DATA_STRING)
899 && (strcmp(launch_data_get_string(d), LAUNCH_KEY_GETJOBS) == 0)
900 && vproc_swap_complex(NULL, VPROC_GSK_ALLJOBS, NULL, &resp) == NULL) {
901 return resp;
902 }
903
904 pthread_once(&_lc_once, launch_client_init);
905
906 if (!_lc) {
907 errno = ENOTCONN;
908 return NULL;
909 }
910
911 pthread_mutex_lock(&_lc->mtx);
912
913 if (d && launchd_msg_send(_lc->l, d) == -1) {
914 do {
915 if (errno != EAGAIN)
916 goto out;
917 } while (launchd_msg_send(_lc->l, NULL) == -1);
918 }
919
920 while (resp == NULL) {
921 if (d == NULL && launch_data_array_get_count(_lc->async_resp) > 0) {
922 resp = launch_data_array_pop_first(_lc->async_resp);
923 goto out;
924 }
925 if (launchd_msg_recv(_lc->l, launch_msg_getmsgs, &resp) == -1) {
926 if (errno != EAGAIN) {
927 goto out;
928 } else if (d == NULL) {
929 errno = 0;
930 goto out;
931 } else {
932 fd_set rfds;
933
934 FD_ZERO(&rfds);
935 FD_SET(_lc->l->fd, &rfds);
936
937 select(_lc->l->fd + 1, &rfds, NULL, NULL, NULL);
938 }
939 }
940 }
941
942 out:
943 pthread_mutex_unlock(&_lc->mtx);
944
945 return resp;
946 }
947
948 int launchd_msg_recv(launch_t lh, void (*cb)(launch_data_t, void *), void *context)
949 {
950 struct cmsghdr *cm = alloca(4096);
951 launch_data_t rmsg = NULL;
952 size_t data_offset, fd_offset;
953 struct msghdr mh;
954 struct iovec iov;
955 int r;
956
957 memset(&mh, 0, sizeof(mh));
958 mh.msg_iov = &iov;
959 mh.msg_iovlen = 1;
960
961 lh->recvbuf = reallocf(lh->recvbuf, lh->recvlen + 8*1024);
962
963 iov.iov_base = lh->recvbuf + lh->recvlen;
964 iov.iov_len = 8*1024;
965 mh.msg_control = cm;
966 mh.msg_controllen = 4096;
967
968 if ((r = recvmsg(lh->fd, &mh, 0)) == -1)
969 return -1;
970 if (r == 0) {
971 errno = ECONNRESET;
972 return -1;
973 }
974 if (mh.msg_flags & MSG_CTRUNC) {
975 errno = ECONNABORTED;
976 return -1;
977 }
978 lh->recvlen += r;
979 if (mh.msg_controllen > 0) {
980 lh->recvfds = reallocf(lh->recvfds, lh->recvfdcnt * sizeof(int) + mh.msg_controllen - sizeof(struct cmsghdr));
981 memcpy(lh->recvfds + lh->recvfdcnt, CMSG_DATA(cm), mh.msg_controllen - sizeof(struct cmsghdr));
982 lh->recvfdcnt += (mh.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
983 }
984
985 r = 0;
986
987 while (lh->recvlen > 0) {
988 struct launch_msg_header *lmhp = lh->recvbuf;
989 uint64_t tmplen;
990 data_offset = sizeof(struct launch_msg_header);
991 fd_offset = 0;
992
993 if (lh->recvlen < sizeof(struct launch_msg_header))
994 goto need_more_data;
995
996 tmplen = big2host(lmhp->len);
997
998 if (big2host(lmhp->magic) != LAUNCH_MSG_HEADER_MAGIC || tmplen <= sizeof(struct launch_msg_header)) {
999 errno = EBADRPC;
1000 goto out_bad;
1001 }
1002
1003 if (lh->recvlen < tmplen) {
1004 goto need_more_data;
1005 }
1006
1007 if ((rmsg = launch_data_unpack(lh->recvbuf, lh->recvlen, lh->recvfds, lh->recvfdcnt, &data_offset, &fd_offset)) == NULL) {
1008 errno = EBADRPC;
1009 goto out_bad;
1010 }
1011
1012 cb(rmsg, context);
1013
1014 lh->recvlen -= data_offset;
1015 if (lh->recvlen > 0) {
1016 memmove(lh->recvbuf, lh->recvbuf + data_offset, lh->recvlen);
1017 } else {
1018 free(lh->recvbuf);
1019 lh->recvbuf = malloc(0);
1020 }
1021
1022 lh->recvfdcnt -= fd_offset;
1023 if (lh->recvfdcnt > 0) {
1024 memmove(lh->recvfds, lh->recvfds + fd_offset, lh->recvfdcnt * sizeof(int));
1025 } else {
1026 free(lh->recvfds);
1027 lh->recvfds = malloc(0);
1028 }
1029 }
1030
1031 return r;
1032
1033 need_more_data:
1034 errno = EAGAIN;
1035 out_bad:
1036 return -1;
1037 }
1038
1039 launch_data_t launch_data_copy(launch_data_t o)
1040 {
1041 launch_data_t r = launch_data_alloc(o->type);
1042 size_t i;
1043
1044 free(r->_array);
1045 memcpy(r, o, sizeof(struct _launch_data));
1046
1047 switch (o->type) {
1048 case LAUNCH_DATA_DICTIONARY:
1049 case LAUNCH_DATA_ARRAY:
1050 r->_array = calloc(1, o->_array_cnt * sizeof(launch_data_t));
1051 for (i = 0; i < o->_array_cnt; i++) {
1052 if (o->_array[i])
1053 r->_array[i] = launch_data_copy(o->_array[i]);
1054 }
1055 break;
1056 case LAUNCH_DATA_STRING:
1057 r->string = strdup(o->string);
1058 break;
1059 case LAUNCH_DATA_OPAQUE:
1060 r->opaque = malloc(o->opaque_size);
1061 memcpy(r->opaque, o->opaque, o->opaque_size);
1062 break;
1063 default:
1064 break;
1065 }
1066
1067 return r;
1068 }
1069
1070 void
1071 launchd_batch_enable(bool b)
1072 {
1073 int64_t val = b;
1074
1075 vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_ON_DEMAND, &val, NULL);
1076 }
1077
1078 bool
1079 launchd_batch_query(void)
1080 {
1081 int64_t val;
1082
1083 if (vproc_swap_integer(NULL, VPROC_GSK_GLOBAL_ON_DEMAND, NULL, &val) == NULL) {
1084 return (bool)val;
1085 }
1086
1087 return false;
1088 }
1089
1090 static int _fd(int fd)
1091 {
1092 if (fd >= 0)
1093 fcntl(fd, F_SETFD, 1);
1094 return fd;
1095 }
1096
1097 launch_data_t launch_data_new_errno(int e)
1098 {
1099 launch_data_t r = launch_data_alloc(LAUNCH_DATA_ERRNO);
1100
1101 if (r)
1102 launch_data_set_errno(r, e);
1103
1104 return r;
1105 }
1106
1107 launch_data_t launch_data_new_fd(int fd)
1108 {
1109 launch_data_t r = launch_data_alloc(LAUNCH_DATA_FD);
1110
1111 if (r)
1112 launch_data_set_fd(r, fd);
1113
1114 return r;
1115 }
1116
1117 launch_data_t launch_data_new_machport(mach_port_t p)
1118 {
1119 launch_data_t r = launch_data_alloc(LAUNCH_DATA_MACHPORT);
1120
1121 if (r)
1122 launch_data_set_machport(r, p);
1123
1124 return r;
1125 }
1126
1127 launch_data_t launch_data_new_integer(long long n)
1128 {
1129 launch_data_t r = launch_data_alloc(LAUNCH_DATA_INTEGER);
1130
1131 if (r)
1132 launch_data_set_integer(r, n);
1133
1134 return r;
1135 }
1136
1137 launch_data_t launch_data_new_bool(bool b)
1138 {
1139 launch_data_t r = launch_data_alloc(LAUNCH_DATA_BOOL);
1140
1141 if (r)
1142 launch_data_set_bool(r, b);
1143
1144 return r;
1145 }
1146
1147 launch_data_t launch_data_new_real(double d)
1148 {
1149 launch_data_t r = launch_data_alloc(LAUNCH_DATA_REAL);
1150
1151 if (r)
1152 launch_data_set_real(r, d);
1153
1154 return r;
1155 }
1156
1157 launch_data_t launch_data_new_string(const char *s)
1158 {
1159 launch_data_t r = launch_data_alloc(LAUNCH_DATA_STRING);
1160
1161 if (r == NULL)
1162 return NULL;
1163
1164 if (!launch_data_set_string(r, s)) {
1165 launch_data_free(r);
1166 return NULL;
1167 }
1168
1169 return r;
1170 }
1171
1172 launch_data_t launch_data_new_opaque(const void *o, size_t os)
1173 {
1174 launch_data_t r = launch_data_alloc(LAUNCH_DATA_OPAQUE);
1175
1176 if (r == NULL)
1177 return NULL;
1178
1179 if (!launch_data_set_opaque(r, o, os)) {
1180 launch_data_free(r);
1181 return NULL;
1182 }
1183
1184 return r;
1185 }
1186
1187 void
1188 load_launchd_jobs_at_loginwindow_prompt(int flags __attribute__((unused)), ...)
1189 {
1190 _vprocmgr_init("LoginWindow");
1191 }
1192
1193 pid_t
1194 create_and_switch_to_per_session_launchd(const char *login __attribute__((unused)), int flags __attribute__((unused)), ...)
1195 {
1196 mach_port_t bezel_ui_server;
1197 struct stat sb;
1198 uid_t target_user = geteuid() ? geteuid() : getuid();
1199
1200 if (_vprocmgr_move_subset_to_user(target_user, "Aqua")) {
1201 return -1;
1202 }
1203
1204 #define BEZEL_UI_PATH "/System/Library/LoginPlugins/BezelServices.loginPlugin/Contents/Resources/BezelUI/BezelUIServer"
1205 #define BEZEL_UI_PLIST "/System/Library/LaunchAgents/com.apple.BezelUIServer.plist"
1206 #define BEZEL_UI_SERVICE "BezelUI"
1207
1208 if (!(stat(BEZEL_UI_PLIST, &sb) == 0 && S_ISREG(sb.st_mode))) {
1209 if (bootstrap_create_server(bootstrap_port, BEZEL_UI_PATH, target_user, true, &bezel_ui_server) == BOOTSTRAP_SUCCESS) {
1210 mach_port_t srv;
1211
1212 if (bootstrap_create_service(bezel_ui_server, BEZEL_UI_SERVICE, &srv) == BOOTSTRAP_SUCCESS) {
1213 mach_port_deallocate(mach_task_self(), srv);
1214 }
1215
1216 mach_port_deallocate(mach_task_self(), bezel_ui_server);
1217 }
1218 }
1219
1220 return 1;
1221 }