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