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