]> git.saurik.com Git - apple/launchd.git/blob - launchd/src/liblaunch.c
87de3cca8bc1e889881f865aa55041b26494bfbd
[apple/launchd.git] / launchd / src / liblaunch.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 #include <libkern/OSByteOrder.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/fcntl.h>
27 #include <sys/un.h>
28 #include <sys/uio.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <pthread.h>
33 #include <unistd.h>
34 #include <errno.h>
35
36 #include "launch.h"
37 #include "launch_priv.h"
38
39 /* __OSBogusByteSwap__() must not really exist in the symbol namespace
40 * in order for the following to generate an error at build time.
41 */
42 extern void __OSBogusByteSwap__(void);
43
44 #define host2big(x) \
45 ({ typeof (x) _X, _x = (x); \
46 switch (sizeof(_x)) { \
47 case 8: \
48 _X = OSSwapHostToBigInt64(_x); \
49 break; \
50 case 4: \
51 _X = OSSwapHostToBigInt32(_x); \
52 break; \
53 case 2: \
54 _X = OSSwapHostToBigInt16(_x); \
55 break; \
56 case 1: \
57 _X = _x; \
58 break; \
59 default: \
60 __OSBogusByteSwap__(); \
61 break; \
62 } \
63 _X; \
64 })
65
66
67 #define big2host(x) \
68 ({ typeof (x) _X, _x = (x); \
69 switch (sizeof(_x)) { \
70 case 8: \
71 _X = OSSwapBigToHostInt64(_x); \
72 break; \
73 case 4: \
74 _X = OSSwapBigToHostInt32(_x); \
75 break; \
76 case 2: \
77 _X = OSSwapBigToHostInt16(_x); \
78 break; \
79 case 1: \
80 _X = _x; \
81 break; \
82 default: \
83 __OSBogusByteSwap__(); \
84 break; \
85 } \
86 _X; \
87 })
88
89
90 struct launch_msg_header {
91 uint64_t magic;
92 uint64_t len;
93 };
94
95 #define LAUNCH_MSG_HEADER_MAGIC 0xD2FEA02366B39A41ull
96
97 struct _launch_data {
98 int type;
99 union {
100 struct {
101 launch_data_t *_array;
102 size_t _array_cnt;
103 };
104 struct {
105 char *string;
106 size_t string_len;
107 };
108 struct {
109 void *opaque;
110 size_t opaque_size;
111 };
112 int fd;
113 int err;
114 long long number;
115 bool boolean;
116 double float_num;
117 };
118 };
119
120 struct _launch {
121 void *sendbuf;
122 int *sendfds;
123 void *recvbuf;
124 int *recvfds;
125 size_t sendlen;
126 size_t sendfdcnt;
127 size_t recvlen;
128 size_t recvfdcnt;
129 int fd;
130 };
131
132 static void make_msg_and_cmsg(launch_data_t, void **, size_t *, int **, size_t *);
133 static launch_data_t make_data(launch_t, size_t *, size_t *);
134 static int _fd(int fd);
135
136 static pthread_once_t _lc_once = PTHREAD_ONCE_INIT;
137
138 static struct _launch_client {
139 pthread_mutex_t mtx;
140 launch_t l;
141 launch_data_t async_resp;
142 } *_lc = NULL;
143
144 static void launch_client_init(void)
145 {
146 struct sockaddr_un sun;
147 char *where = getenv(LAUNCHD_SOCKET_ENV);
148 char *_launchd_fd = getenv(LAUNCHD_TRUSTED_FD_ENV);
149 int r, dfd, lfd = -1, tries;
150
151 _lc = calloc(1, sizeof(struct _launch_client));
152
153 if (!_lc)
154 return;
155
156 pthread_mutex_init(&_lc->mtx, NULL);
157
158 if (_launchd_fd) {
159 lfd = strtol(_launchd_fd, NULL, 10);
160 if ((dfd = dup(lfd)) >= 0) {
161 close(dfd);
162 _fd(lfd);
163 } else {
164 lfd = -1;
165 }
166 unsetenv(LAUNCHD_TRUSTED_FD_ENV);
167 }
168 if (lfd == -1) {
169 memset(&sun, 0, sizeof(sun));
170 sun.sun_family = AF_UNIX;
171
172 if (where)
173 strncpy(sun.sun_path, where, sizeof(sun.sun_path));
174 else
175 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%u/sock", LAUNCHD_SOCK_PREFIX, getuid());
176
177 if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1)
178 goto out_bad;
179
180 for (tries = 0; tries < 10; tries++) {
181 r = connect(lfd, (struct sockaddr *)&sun, sizeof(sun));
182 if (r == -1) {
183 if (getuid() != 0 && fork() == 0)
184 execl("/sbin/launchd", "/sbin/launchd", NULL);
185 sleep(1);
186 } else {
187 break;
188 }
189 }
190 if (r == -1) {
191 close(lfd);
192 goto out_bad;
193 }
194 }
195 if (!(_lc->l = launchd_fdopen(lfd))) {
196 close(lfd);
197 goto out_bad;
198 }
199 if (!(_lc->async_resp = launch_data_alloc(LAUNCH_DATA_ARRAY)))
200 goto out_bad;
201
202 return;
203 out_bad:
204 if (_lc->l)
205 launchd_close(_lc->l);
206 if (_lc)
207 free(_lc);
208 _lc = NULL;
209 }
210
211 launch_data_t launch_data_alloc(launch_data_type_t t)
212 {
213 launch_data_t d = calloc(1, sizeof(struct _launch));
214
215 if (d) {
216 d->type = t;
217 switch (t) {
218 case LAUNCH_DATA_DICTIONARY:
219 case LAUNCH_DATA_ARRAY:
220 d->_array = malloc(0);
221 break;
222 default:
223 break;
224 }
225 }
226
227 return d;
228 }
229
230 launch_data_type_t launch_data_get_type(launch_data_t d)
231 {
232 return d->type;
233 }
234
235 void launch_data_free(launch_data_t d)
236 {
237 size_t i;
238
239 switch (d->type) {
240 case LAUNCH_DATA_DICTIONARY:
241 case LAUNCH_DATA_ARRAY:
242 for (i = 0; i < d->_array_cnt; i++)
243 launch_data_free(d->_array[i]);
244 free(d->_array);
245 break;
246 case LAUNCH_DATA_STRING:
247 if (d->string)
248 free(d->string);
249 break;
250 case LAUNCH_DATA_OPAQUE:
251 if (d->opaque)
252 free(d->opaque);
253 break;
254 default:
255 break;
256 }
257 free(d);
258 }
259
260 size_t launch_data_dict_get_count(launch_data_t dict)
261 {
262 return dict->_array_cnt / 2;
263 }
264
265
266 bool launch_data_dict_insert(launch_data_t dict, launch_data_t what, const char *key)
267 {
268 size_t i;
269 launch_data_t thekey = launch_data_alloc(LAUNCH_DATA_STRING);
270
271 launch_data_set_string(thekey, key);
272
273 for (i = 0; i < dict->_array_cnt; i += 2) {
274 if (!strcasecmp(key, dict->_array[i]->string)) {
275 launch_data_array_set_index(dict, thekey, i);
276 launch_data_array_set_index(dict, what, i + 1);
277 return true;
278 }
279 }
280 launch_data_array_set_index(dict, thekey, i);
281 launch_data_array_set_index(dict, what, i + 1);
282 return true;
283 }
284
285 launch_data_t launch_data_dict_lookup(launch_data_t dict, const char *key)
286 {
287 size_t i;
288
289 if (LAUNCH_DATA_DICTIONARY != dict->type)
290 return NULL;
291
292 for (i = 0; i < dict->_array_cnt; i += 2) {
293 if (!strcasecmp(key, dict->_array[i]->string))
294 return dict->_array[i + 1];
295 }
296
297 return NULL;
298 }
299
300 bool launch_data_dict_remove(launch_data_t dict, const char *key)
301 {
302 size_t i;
303
304 for (i = 0; i < dict->_array_cnt; i += 2) {
305 if (!strcasecmp(key, dict->_array[i]->string))
306 break;
307 }
308 if (i == dict->_array_cnt)
309 return false;
310 launch_data_free(dict->_array[i]);
311 launch_data_free(dict->_array[i + 1]);
312 memmove(dict->_array + i, dict->_array + i + 2, (dict->_array_cnt - (i + 2)) * sizeof(launch_data_t));
313 dict->_array_cnt -= 2;
314 return true;
315 }
316
317 void launch_data_dict_iterate(launch_data_t dict, void (*cb)(launch_data_t, const char *, void *), void *context)
318 {
319 size_t i;
320
321 if (LAUNCH_DATA_DICTIONARY != dict->type)
322 return;
323
324 for (i = 0; i < dict->_array_cnt; i += 2)
325 cb(dict->_array[i + 1], dict->_array[i]->string, context);
326 }
327
328 bool launch_data_array_set_index(launch_data_t where, launch_data_t what, size_t ind)
329 {
330 if ((ind + 1) >= where->_array_cnt) {
331 where->_array = realloc(where->_array, (ind + 1) * sizeof(launch_data_t));
332 memset(where->_array + where->_array_cnt, 0, (ind + 1 - where->_array_cnt) * sizeof(launch_data_t));
333 where->_array_cnt = ind + 1;
334 }
335
336 if (where->_array[ind])
337 launch_data_free(where->_array[ind]);
338 where->_array[ind] = what;
339 return true;
340 }
341
342 launch_data_t launch_data_array_get_index(launch_data_t where, size_t ind)
343 {
344 if (LAUNCH_DATA_ARRAY != where->type)
345 return NULL;
346 if (ind < where->_array_cnt)
347 return where->_array[ind];
348 return NULL;
349 }
350
351 launch_data_t launch_data_array_pop_first(launch_data_t where)
352 {
353 launch_data_t r = NULL;
354
355 if (where->_array_cnt > 0) {
356 r = where->_array[0];
357 memmove(where->_array, where->_array + 1, (where->_array_cnt - 1) * sizeof(launch_data_t));
358 where->_array_cnt--;
359 }
360 return r;
361 }
362
363 size_t launch_data_array_get_count(launch_data_t where)
364 {
365 if (LAUNCH_DATA_ARRAY != where->type)
366 return 0;
367 return where->_array_cnt;
368 }
369
370 bool launch_data_set_errno(launch_data_t d, int e)
371 {
372 d->err = e;
373 return true;
374 }
375
376 bool launch_data_set_fd(launch_data_t d, int fd)
377 {
378 d->fd = fd;
379 return true;
380 }
381
382 bool launch_data_set_integer(launch_data_t d, long long n)
383 {
384 d->number = n;
385 return true;
386 }
387
388 bool launch_data_set_bool(launch_data_t d, bool b)
389 {
390 d->boolean = b;
391 return true;
392 }
393
394 bool launch_data_set_real(launch_data_t d, double n)
395 {
396 d->float_num = n;
397 return true;
398 }
399
400 bool launch_data_set_string(launch_data_t d, const char *s)
401 {
402 if (d->string)
403 free(d->string);
404 d->string = strdup(s);
405 if (d->string) {
406 d->string_len = strlen(d->string);
407 return true;
408 }
409 return false;
410 }
411
412 bool launch_data_set_opaque(launch_data_t d, const void *o, size_t os)
413 {
414 d->opaque_size = os;
415 if (d->opaque)
416 free(d->opaque);
417 d->opaque = malloc(os);
418 if (d->opaque) {
419 memcpy(d->opaque, o, os);
420 return true;
421 }
422 return false;
423 }
424
425 int launch_data_get_errno(launch_data_t d)
426 {
427 return d->err;
428 }
429
430 int launch_data_get_fd(launch_data_t d)
431 {
432 return d->fd;
433 }
434
435 long long launch_data_get_integer(launch_data_t d)
436 {
437 return d->number;
438 }
439
440 bool launch_data_get_bool(launch_data_t d)
441 {
442 return d->boolean;
443 }
444
445 double launch_data_get_real(launch_data_t d)
446 {
447 return d->float_num;
448 }
449
450 const char *launch_data_get_string(launch_data_t d)
451 {
452 if (LAUNCH_DATA_STRING != d->type)
453 return NULL;
454 return d->string;
455 }
456
457 void *launch_data_get_opaque(launch_data_t d)
458 {
459 if (LAUNCH_DATA_OPAQUE != d->type)
460 return NULL;
461 return d->opaque;
462 }
463
464 size_t launch_data_get_opaque_size(launch_data_t d)
465 {
466 return d->opaque_size;
467 }
468
469 int launchd_getfd(launch_t l)
470 {
471 return l->fd;
472 }
473
474 launch_t launchd_fdopen(int fd)
475 {
476 launch_t c;
477
478 c = calloc(1, sizeof(struct _launch));
479 if (!c)
480 return NULL;
481
482 c->fd = fd;
483
484 fcntl(fd, F_SETFL, O_NONBLOCK);
485
486 if ((c->sendbuf = malloc(0)) == NULL)
487 goto out_bad;
488 if ((c->sendfds = malloc(0)) == NULL)
489 goto out_bad;
490 if ((c->recvbuf = malloc(0)) == NULL)
491 goto out_bad;
492 if ((c->recvfds = malloc(0)) == NULL)
493 goto out_bad;
494
495 return c;
496
497 out_bad:
498 if (c->sendbuf)
499 free(c->sendbuf);
500 if (c->sendfds)
501 free(c->sendfds);
502 if (c->recvbuf)
503 free(c->recvbuf);
504 if (c->recvfds)
505 free(c->recvfds);
506 free(c);
507 return NULL;
508 }
509
510 void launchd_close(launch_t lh)
511 {
512 if (lh->sendbuf)
513 free(lh->sendbuf);
514 if (lh->sendfds)
515 free(lh->sendfds);
516 if (lh->recvbuf)
517 free(lh->recvbuf);
518 if (lh->recvfds)
519 free(lh->recvfds);
520 close(lh->fd);
521 free(lh);
522 }
523
524 static void make_msg_and_cmsg(launch_data_t d, void **where, size_t *len, int **fd_where, size_t *fdcnt)
525 {
526 launch_data_t o_in_w;
527 size_t i;
528
529 *where = realloc(*where, *len + sizeof(struct _launch_data));
530
531 o_in_w = *where + *len;
532 memset(o_in_w, 0, sizeof(struct _launch_data));
533 *len += sizeof(struct _launch_data);
534
535 o_in_w->type = host2big(d->type);
536
537 switch (d->type) {
538 case LAUNCH_DATA_INTEGER:
539 o_in_w->number = host2big(d->number);
540 break;
541 case LAUNCH_DATA_REAL:
542 o_in_w->float_num = host2big(d->float_num);
543 break;
544 case LAUNCH_DATA_BOOL:
545 o_in_w->boolean = host2big(d->boolean);
546 break;
547 case LAUNCH_DATA_ERRNO:
548 o_in_w->err = host2big(d->err);
549 break;
550 case LAUNCH_DATA_FD:
551 o_in_w->fd = host2big(d->fd);
552 if (d->fd != -1) {
553 *fd_where = realloc(*fd_where, (*fdcnt + 1) * sizeof(int));
554 (*fd_where)[*fdcnt] = d->fd;
555 (*fdcnt)++;
556 }
557 break;
558 case LAUNCH_DATA_STRING:
559 o_in_w->string_len = host2big(d->string_len);
560 *where = realloc(*where, *len + strlen(d->string) + 1);
561 memcpy(*where + *len, d->string, strlen(d->string) + 1);
562 *len += strlen(d->string) + 1;
563 break;
564 case LAUNCH_DATA_OPAQUE:
565 o_in_w->opaque_size = host2big(d->opaque_size);
566 *where = realloc(*where, *len + d->opaque_size);
567 memcpy(*where + *len, d->opaque, d->opaque_size);
568 *len += d->opaque_size;
569 break;
570 case LAUNCH_DATA_DICTIONARY:
571 case LAUNCH_DATA_ARRAY:
572 o_in_w->_array_cnt = host2big(d->_array_cnt);
573 *where = realloc(*where, *len + (d->_array_cnt * sizeof(launch_data_t)));
574 memcpy(*where + *len, d->_array, d->_array_cnt * sizeof(launch_data_t));
575 *len += d->_array_cnt * sizeof(launch_data_t);
576
577 for (i = 0; i < d->_array_cnt; i++)
578 make_msg_and_cmsg(d->_array[i], where, len, fd_where, fdcnt);
579 break;
580 default:
581 break;
582 }
583 }
584
585 static launch_data_t make_data(launch_t conn, size_t *data_offset, size_t *fdoffset)
586 {
587 launch_data_t r = conn->recvbuf + *data_offset;
588 size_t i, tmpcnt;
589
590 if ((conn->recvlen - *data_offset) < sizeof(struct _launch_data))
591 return NULL;
592 *data_offset += sizeof(struct _launch_data);
593
594 switch (big2host(r->type)) {
595 case LAUNCH_DATA_DICTIONARY:
596 case LAUNCH_DATA_ARRAY:
597 tmpcnt = big2host(r->_array_cnt);
598 if ((conn->recvlen - *data_offset) < (tmpcnt * sizeof(launch_data_t))) {
599 errno = EAGAIN;
600 return NULL;
601 }
602 r->_array = conn->recvbuf + *data_offset;
603 *data_offset += tmpcnt * sizeof(launch_data_t);
604 for (i = 0; i < tmpcnt; i++) {
605 r->_array[i] = make_data(conn, data_offset, fdoffset);
606 if (r->_array[i] == NULL)
607 return NULL;
608 }
609 r->_array_cnt = tmpcnt;
610 break;
611 case LAUNCH_DATA_STRING:
612 tmpcnt = big2host(r->string_len);
613 if ((conn->recvlen - *data_offset) < (tmpcnt + 1)) {
614 errno = EAGAIN;
615 return NULL;
616 }
617 r->string = conn->recvbuf + *data_offset;
618 r->string_len = tmpcnt;
619 *data_offset += tmpcnt + 1;
620 break;
621 case LAUNCH_DATA_OPAQUE:
622 tmpcnt = big2host(r->opaque_size);
623 if ((conn->recvlen - *data_offset) < tmpcnt) {
624 errno = EAGAIN;
625 return NULL;
626 }
627 r->opaque = conn->recvbuf + *data_offset;
628 r->opaque_size = tmpcnt;
629 *data_offset += tmpcnt;
630 break;
631 case LAUNCH_DATA_FD:
632 if (r->fd != -1) {
633 r->fd = _fd(conn->recvfds[*fdoffset]);
634 *fdoffset += 1;
635 }
636 break;
637 case LAUNCH_DATA_INTEGER:
638 r->number = big2host(r->number);
639 break;
640 case LAUNCH_DATA_REAL:
641 r->float_num = big2host(r->float_num);
642 break;
643 case LAUNCH_DATA_BOOL:
644 r->boolean = big2host(r->boolean);
645 break;
646 case LAUNCH_DATA_ERRNO:
647 r->err = big2host(r->err);
648 break;
649 default:
650 errno = EINVAL;
651 return NULL;
652 break;
653 }
654
655 r->type = big2host(r->type);
656
657 return r;
658 }
659
660 int launchd_msg_send(launch_t lh, launch_data_t d)
661 {
662 struct launch_msg_header lmh;
663 struct cmsghdr *cm = NULL;
664 struct msghdr mh;
665 struct iovec iov[2];
666 size_t sentctrllen = 0;
667 int r;
668
669 memset(&mh, 0, sizeof(mh));
670
671 if (d) {
672 uint64_t msglen = lh->sendlen;
673
674 make_msg_and_cmsg(d, &lh->sendbuf, &lh->sendlen, &lh->sendfds, &lh->sendfdcnt);
675
676 msglen = (lh->sendlen - msglen) + sizeof(struct launch_msg_header);
677 lmh.len = host2big(msglen);
678 lmh.magic = host2big(LAUNCH_MSG_HEADER_MAGIC);
679
680 iov[0].iov_base = &lmh;
681 iov[0].iov_len = sizeof(lmh);
682 mh.msg_iov = iov;
683 mh.msg_iovlen = 2;
684 } else {
685 mh.msg_iov = iov + 1;
686 mh.msg_iovlen = 1;
687 }
688
689 iov[1].iov_base = lh->sendbuf;
690 iov[1].iov_len = lh->sendlen;
691
692
693 if (lh->sendfdcnt > 0) {
694 sentctrllen = mh.msg_controllen = CMSG_SPACE(lh->sendfdcnt * sizeof(int));
695 cm = alloca(mh.msg_controllen);
696 mh.msg_control = cm;
697
698 memset(cm, 0, mh.msg_controllen);
699
700 cm->cmsg_len = CMSG_LEN(lh->sendfdcnt * sizeof(int));
701 cm->cmsg_level = SOL_SOCKET;
702 cm->cmsg_type = SCM_RIGHTS;
703
704 memcpy(CMSG_DATA(cm), lh->sendfds, lh->sendfdcnt * sizeof(int));
705 }
706
707 if ((r = sendmsg(lh->fd, &mh, 0)) == -1) {
708 return -1;
709 } else if (r == 0) {
710 errno = ECONNRESET;
711 return -1;
712 } else if (sentctrllen != mh.msg_controllen) {
713 errno = ECONNRESET;
714 return -1;
715 }
716
717 if (d) {
718 r -= sizeof(struct launch_msg_header);
719 }
720
721 lh->sendlen -= r;
722 if (lh->sendlen > 0) {
723 memmove(lh->sendbuf, lh->sendbuf + r, lh->sendlen);
724 } else {
725 free(lh->sendbuf);
726 lh->sendbuf = malloc(0);
727 }
728
729 lh->sendfdcnt = 0;
730 free(lh->sendfds);
731 lh->sendfds = malloc(0);
732
733 if (lh->sendlen > 0) {
734 errno = EAGAIN;
735 return -1;
736 }
737
738 return 0;
739 }
740
741
742 int launch_get_fd(void)
743 {
744 pthread_once(&_lc_once, launch_client_init);
745
746 if (!_lc) {
747 errno = ENOTCONN;
748 return -1;
749 }
750
751 return _lc->l->fd;
752 }
753
754 static void launch_msg_getmsgs(launch_data_t m, void *context)
755 {
756 launch_data_t async_resp, *sync_resp = context;
757
758 if ((LAUNCH_DATA_DICTIONARY == launch_data_get_type(m)) && (async_resp = launch_data_dict_lookup(m, LAUNCHD_ASYNC_MSG_KEY))) {
759 launch_data_array_set_index(_lc->async_resp, launch_data_copy(async_resp), launch_data_array_get_count(_lc->async_resp));
760 } else {
761 *sync_resp = launch_data_copy(m);
762 }
763 }
764
765 launch_data_t launch_msg(launch_data_t d)
766 {
767 launch_data_t resp = NULL;
768
769 pthread_once(&_lc_once, launch_client_init);
770
771 if (!_lc) {
772 errno = ENOTCONN;
773 return NULL;
774 }
775
776 pthread_mutex_lock(&_lc->mtx);
777
778 if (d && launchd_msg_send(_lc->l, d) == -1) {
779 do {
780 if (errno != EAGAIN)
781 goto out;
782 } while (launchd_msg_send(_lc->l, NULL) == -1);
783 }
784
785 while (resp == NULL) {
786 if (d == NULL && launch_data_array_get_count(_lc->async_resp) > 0) {
787 resp = launch_data_array_pop_first(_lc->async_resp);
788 goto out;
789 }
790 if (launchd_msg_recv(_lc->l, launch_msg_getmsgs, &resp) == -1) {
791 if (errno != EAGAIN) {
792 goto out;
793 } else if (d == NULL) {
794 errno = 0;
795 goto out;
796 } else {
797 fd_set rfds;
798
799 FD_ZERO(&rfds);
800 FD_SET(_lc->l->fd, &rfds);
801
802 select(_lc->l->fd + 1, &rfds, NULL, NULL, NULL);
803 }
804 }
805 }
806
807 out:
808 pthread_mutex_unlock(&_lc->mtx);
809
810 return resp;
811 }
812
813 int launchd_msg_recv(launch_t lh, void (*cb)(launch_data_t, void *), void *context)
814 {
815 struct cmsghdr *cm = alloca(4096);
816 launch_data_t rmsg = NULL;
817 size_t data_offset, fd_offset;
818 struct msghdr mh;
819 struct iovec iov;
820 int r;
821
822 memset(&mh, 0, sizeof(mh));
823 mh.msg_iov = &iov;
824 mh.msg_iovlen = 1;
825
826 lh->recvbuf = realloc(lh->recvbuf, lh->recvlen + 8*1024);
827
828 iov.iov_base = lh->recvbuf + lh->recvlen;
829 iov.iov_len = 8*1024;
830 mh.msg_control = cm;
831 mh.msg_controllen = 4096;
832
833 if ((r = recvmsg(lh->fd, &mh, 0)) == -1)
834 return -1;
835 if (r == 0) {
836 errno = ECONNRESET;
837 return -1;
838 }
839 if (mh.msg_flags & MSG_CTRUNC) {
840 errno = ECONNABORTED;
841 return -1;
842 }
843 lh->recvlen += r;
844 if (mh.msg_controllen > 0) {
845 lh->recvfds = realloc(lh->recvfds, lh->recvfdcnt * sizeof(int) + mh.msg_controllen - sizeof(struct cmsghdr));
846 memcpy(lh->recvfds + lh->recvfdcnt, CMSG_DATA(cm), mh.msg_controllen - sizeof(struct cmsghdr));
847 lh->recvfdcnt += (mh.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
848 }
849
850 r = 0;
851
852 while (lh->recvlen > 0) {
853 struct launch_msg_header *lmhp = lh->recvbuf;
854 uint64_t tmplen;
855 data_offset = sizeof(struct launch_msg_header);
856 fd_offset = 0;
857
858 if (lh->recvlen < sizeof(struct launch_msg_header))
859 goto need_more_data;
860
861 tmplen = big2host(lmhp->len);
862
863 if (big2host(lmhp->magic) != LAUNCH_MSG_HEADER_MAGIC || tmplen <= sizeof(struct launch_msg_header)) {
864 errno = EBADRPC;
865 goto out_bad;
866 }
867
868 if (lh->recvlen < tmplen) {
869 goto need_more_data;
870 }
871
872 if ((rmsg = make_data(lh, &data_offset, &fd_offset)) == NULL) {
873 errno = EBADRPC;
874 goto out_bad;
875 }
876
877 cb(rmsg, context);
878
879 lh->recvlen -= data_offset;
880 if (lh->recvlen > 0) {
881 memmove(lh->recvbuf, lh->recvbuf + data_offset, lh->recvlen);
882 } else {
883 free(lh->recvbuf);
884 lh->recvbuf = malloc(0);
885 }
886
887 lh->recvfdcnt -= fd_offset;
888 if (lh->recvfdcnt > 0) {
889 memmove(lh->recvfds, lh->recvfds + fd_offset, lh->recvfdcnt * sizeof(int));
890 } else {
891 free(lh->recvfds);
892 lh->recvfds = malloc(0);
893 }
894 }
895
896 return r;
897
898 need_more_data:
899 errno = EAGAIN;
900 out_bad:
901 return -1;
902 }
903
904 launch_data_t launch_data_copy(launch_data_t o)
905 {
906 launch_data_t r = launch_data_alloc(o->type);
907 size_t i;
908
909 free(r->_array);
910 memcpy(r, o, sizeof(struct _launch_data));
911
912 switch (o->type) {
913 case LAUNCH_DATA_DICTIONARY:
914 case LAUNCH_DATA_ARRAY:
915 r->_array = calloc(1, o->_array_cnt * sizeof(launch_data_t));
916 for (i = 0; i < o->_array_cnt; i++) {
917 if (o->_array[i])
918 r->_array[i] = launch_data_copy(o->_array[i]);
919 }
920 break;
921 case LAUNCH_DATA_STRING:
922 r->string = strdup(o->string);
923 break;
924 case LAUNCH_DATA_OPAQUE:
925 r->opaque = malloc(o->opaque_size);
926 memcpy(r->opaque, o->opaque, o->opaque_size);
927 break;
928 default:
929 break;
930 }
931
932 return r;
933 }
934
935 void launchd_batch_enable(bool val)
936 {
937 launch_data_t resp, tmp, msg;
938
939 tmp = launch_data_alloc(LAUNCH_DATA_BOOL);
940 launch_data_set_bool(tmp, val);
941
942 msg = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
943 launch_data_dict_insert(msg, tmp, LAUNCH_KEY_BATCHCONTROL);
944
945 resp = launch_msg(msg);
946
947 launch_data_free(msg);
948
949 if (resp)
950 launch_data_free(resp);
951 }
952
953 bool launchd_batch_query(void)
954 {
955 launch_data_t resp, msg = launch_data_alloc(LAUNCH_DATA_STRING);
956 bool rval = true;
957
958 launch_data_set_string(msg, LAUNCH_KEY_BATCHQUERY);
959
960 resp = launch_msg(msg);
961
962 launch_data_free(msg);
963
964 if (resp) {
965 if (launch_data_get_type(resp) == LAUNCH_DATA_BOOL)
966 rval = launch_data_get_bool(resp);
967 launch_data_free(resp);
968 }
969 return rval;
970 }
971
972 static int _fd(int fd)
973 {
974 if (fd >= 0)
975 fcntl(fd, F_SETFD, 1);
976 return fd;
977 }
978
979 launch_data_t launch_data_new_errno(int e)
980 {
981 launch_data_t r = launch_data_alloc(LAUNCH_DATA_ERRNO);
982
983 if (r)
984 launch_data_set_errno(r, e);
985
986 return r;
987 }
988
989 launch_data_t launch_data_new_fd(int fd)
990 {
991 launch_data_t r = launch_data_alloc(LAUNCH_DATA_FD);
992
993 if (r)
994 launch_data_set_fd(r, fd);
995
996 return r;
997 }
998
999 launch_data_t launch_data_new_integer(long long n)
1000 {
1001 launch_data_t r = launch_data_alloc(LAUNCH_DATA_INTEGER);
1002
1003 if (r)
1004 launch_data_set_integer(r, n);
1005
1006 return r;
1007 }
1008
1009 launch_data_t launch_data_new_bool(bool b)
1010 {
1011 launch_data_t r = launch_data_alloc(LAUNCH_DATA_BOOL);
1012
1013 if (r)
1014 launch_data_set_bool(r, b);
1015
1016 return r;
1017 }
1018
1019 launch_data_t launch_data_new_real(double d)
1020 {
1021 launch_data_t r = launch_data_alloc(LAUNCH_DATA_REAL);
1022
1023 if (r)
1024 launch_data_set_real(r, d);
1025
1026 return r;
1027 }
1028
1029 launch_data_t launch_data_new_string(const char *s)
1030 {
1031 launch_data_t r = launch_data_alloc(LAUNCH_DATA_STRING);
1032
1033 if (r == NULL)
1034 return NULL;
1035
1036 if (!launch_data_set_string(r, s)) {
1037 launch_data_free(r);
1038 return NULL;
1039 }
1040
1041 return r;
1042 }
1043
1044 launch_data_t launch_data_new_opaque(const void *o, size_t os)
1045 {
1046 launch_data_t r = launch_data_alloc(LAUNCH_DATA_OPAQUE);
1047
1048 if (r == NULL)
1049 return NULL;
1050
1051 if (!launch_data_set_opaque(r, o, os)) {
1052 launch_data_free(r);
1053 return NULL;
1054 }
1055
1056 return r;
1057 }