2 * Copyright (c) 2009 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #define MAX_PACKET_SIZE (128 * 1024)
43 int (*release)(heim_sipc ctx);
44 heim_ipc_callback callback;
49 #if defined(__APPLE__) && defined(HAVE_GCD)
51 #include "heim_ipcServer.h"
52 #include "heim_ipc_reply.h"
53 #include "heim_ipc_async.h"
55 static dispatch_source_t timer;
56 static dispatch_queue_t timerq;
57 static uint64_t timeoutvalue;
59 static dispatch_queue_t eventq;
61 static dispatch_queue_t workq;
64 default_timer_ev(void)
69 static void (*timer_ev)(void) = default_timer_ev;
74 dispatch_source_set_timer(timer,
75 dispatch_time(DISPATCH_TIME_NOW,
76 timeoutvalue * NSEC_PER_SEC),
77 timeoutvalue * NSEC_PER_SEC, 1000000);
83 static dispatch_once_t once;
84 dispatch_once(&once, ^{
85 timerq = dispatch_queue_create("hiem-sipc-timer-q", NULL);
86 timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerq);
87 dispatch_source_set_event_handler(timer, ^{ timer_ev(); } );
89 workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
90 eventq = dispatch_queue_create("heim-ipc.event-queue", NULL);
97 dispatch_suspend(timer);
103 dispatch_sync(timerq, ^{ set_timer(); });
104 dispatch_resume(timer);
107 struct mach_service {
109 dispatch_source_t source;
110 dispatch_queue_t queue;
113 struct mach_call_ctx {
114 mach_port_t reply_port;
121 mach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
123 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
124 heim_ipc_message_inband_t replyin;
125 mach_msg_type_number_t replyinCnt = 0;
126 heim_ipc_message_outband_t replyout = 0;
127 mach_msg_type_number_t replyoutCnt = 0;
130 /* on error, no reply */
131 } else if (reply->length < 2048) {
132 replyinCnt = reply->length;
133 memcpy(replyin, reply->data, replyinCnt);
135 vm_read(mach_task_self(),
136 (vm_address_t)reply->data, reply->length,
137 (vm_address_t *)&replyout, &replyoutCnt);
140 mheim_ripc_call_reply(s->reply_port, returnvalue,
142 replyout, replyoutCnt);
144 heim_ipc_free_cred(s->cred);
151 mach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
153 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
154 heim_ipc_message_inband_t replyin;
155 mach_msg_type_number_t replyinCnt = 0;
156 heim_ipc_message_outband_t replyout = 0;
157 mach_msg_type_number_t replyoutCnt = 0;
160 /* on error, no reply */
161 } else if (reply->length < 2048) {
162 replyinCnt = reply->length;
163 memcpy(replyin, reply->data, replyinCnt);
165 vm_read(mach_task_self(),
166 (vm_address_t)reply->data, reply->length,
167 (vm_address_t *)&replyout, &replyoutCnt);
170 mheim_aipc_acall_reply(s->reply_port, returnvalue,
172 replyout, replyoutCnt);
173 heim_ipc_free_cred(s->cred);
181 mheim_do_call(mach_port_t server_port,
182 audit_token_t client_creds,
183 mach_port_t reply_port,
184 heim_ipc_message_inband_t requestin,
185 mach_msg_type_number_t requestinCnt,
186 heim_ipc_message_outband_t requestout,
187 mach_msg_type_number_t requestoutCnt,
189 heim_ipc_message_inband_t replyin,
190 mach_msg_type_number_t *replyinCnt,
191 heim_ipc_message_outband_t *replyout,
192 mach_msg_type_number_t *replyoutCnt)
194 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
195 struct mach_call_ctx *s;
206 s = malloc(sizeof(*s));
208 return KERN_MEMORY_FAILURE; /* XXX */
210 s->reply_port = reply_port;
212 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
214 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
223 s->req.data = malloc(requestinCnt);
224 memcpy(s->req.data, requestin, requestinCnt);
225 s->req.length = requestinCnt;
227 s->req.data = malloc(requestoutCnt);
228 memcpy(s->req.data, requestout, requestoutCnt);
229 s->req.length = requestoutCnt;
232 dispatch_async(workq, ^{
233 (ctx->callback)(ctx->userctx, &s->req, s->cred,
234 mach_complete_sync, (heim_sipc_call)s);
241 mheim_do_call_request(mach_port_t server_port,
242 audit_token_t client_creds,
243 mach_port_t reply_port,
244 heim_ipc_message_inband_t requestin,
245 mach_msg_type_number_t requestinCnt,
246 heim_ipc_message_outband_t requestout,
247 mach_msg_type_number_t requestoutCnt)
249 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
250 struct mach_call_ctx *s;
257 s = malloc(sizeof(*s));
259 return KERN_MEMORY_FAILURE; /* XXX */
261 s->reply_port = reply_port;
263 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
265 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
274 s->req.data = malloc(requestinCnt);
275 memcpy(s->req.data, requestin, requestinCnt);
276 s->req.length = requestinCnt;
278 s->req.data = malloc(requestoutCnt);
279 memcpy(s->req.data, requestout, requestoutCnt);
280 s->req.length = requestoutCnt;
283 dispatch_async(workq, ^{
284 (ctx->callback)(ctx->userctx, &s->req, s->cred,
285 mach_complete_async, (heim_sipc_call)s);
292 mach_init(const char *service, mach_port_t sport, heim_sipc ctx)
294 struct mach_service *s;
299 s = calloc(1, sizeof(*s));
303 asprintf(&name, "heim-ipc-mach-%s", service);
305 s->queue = dispatch_queue_create(name, NULL);
309 s->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
310 s->sport, 0, s->queue);
311 if (s->source == NULL) {
312 dispatch_release(s->queue);
318 dispatch_set_context(s->queue, ctx);
319 dispatch_set_context(s->source, s);
321 dispatch_source_set_event_handler(s->source, ^{
322 dispatch_mig_server(s->source, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem), mheim_ipc_server);
325 dispatch_source_set_cancel_handler(s->source, ^{
326 heim_sipc sctx = dispatch_get_context(dispatch_get_current_queue());
327 struct mach_service *st = sctx->mech;
328 mach_port_mod_refs(mach_task_self(), st->sport,
329 MACH_PORT_RIGHT_RECEIVE, -1);
330 dispatch_release(st->queue);
331 dispatch_release(st->source);
336 dispatch_resume(s->source);
342 mach_release(heim_sipc ctx)
344 struct mach_service *s = ctx->mech;
345 dispatch_source_cancel(s->source);
346 dispatch_release(s->source);
351 mach_checkin_or_register(const char *service)
356 kr = bootstrap_check_in(bootstrap_port, service, &mp);
357 if (kr == KERN_SUCCESS)
360 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
361 /* Pre SnowLeopard version */
362 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
363 if (kr != KERN_SUCCESS)
364 return MACH_PORT_NULL;
366 kr = mach_port_insert_right(mach_task_self(), mp, mp,
367 MACH_MSG_TYPE_MAKE_SEND);
368 if (kr != KERN_SUCCESS) {
369 mach_port_destroy(mach_task_self(), mp);
370 return MACH_PORT_NULL;
373 kr = bootstrap_register(bootstrap_port, rk_UNCONST(service), mp);
374 if (kr != KERN_SUCCESS) {
375 mach_port_destroy(mach_task_self(), mp);
376 return MACH_PORT_NULL;
381 return MACH_PORT_NULL;
386 #endif /* __APPLE__ && HAVE_GCD */
390 heim_sipc_launchd_mach_init(const char *service,
391 heim_ipc_callback callback,
392 void *user, heim_sipc *ctx)
394 #if defined(__APPLE__) && defined(HAVE_GCD)
395 mach_port_t sport = MACH_PORT_NULL;
401 sport = mach_checkin_or_register(service);
402 if (sport == MACH_PORT_NULL) {
407 c = calloc(1, sizeof(*c));
412 c->release = mach_release;
414 c->callback = callback;
416 ret = mach_init(service, sport, c);
425 if (sport != MACH_PORT_NULL)
426 mach_port_mod_refs(mach_task_self(), sport,
427 MACH_PORT_RIGHT_RECEIVE, -1);
429 #else /* !(__APPLE__ && HAVE_GCD) */
432 #endif /* __APPLE__ && HAVE_GCD */
437 heim_ipc_callback callback;
440 #define LISTEN_SOCKET 1
441 #define WAITING_READ 2
442 #define WAITING_WRITE 4
443 #define WAITING_CLOSE 8
445 #define HTTP_REPLY 16
448 #define INHERIT_MASK 0xffff0000
449 #define INCLUDE_ERROR_CODE (1 << 16)
450 #define ALLOW_HTTP (1<<17)
451 #define UNIX_SOCKET (1<<18)
458 dispatch_source_t in;
459 dispatch_source_t out;
469 static unsigned num_clients = 0;
470 static struct client **clients = NULL;
473 static void handle_read(struct client *);
474 static void handle_write(struct client *);
475 static int maybe_close(struct client *);
478 * Update peer credentials from socket.
480 * SCM_CREDS can only be updated the first time there is read data to
481 * read from the filedescriptor, so if we read do it before this
482 * point, the cred data might not be is not there yet.
486 update_client_creds(struct client *c)
488 #ifdef HAVE_GETPEERUCRED
491 ucred_t *peercred = NULL;
493 if (getpeerucred(c->fd, &peercred) == 0) {
494 c->unixrights.uid = ucred_geteuid(peercred);
495 c->unixrights.gid = ucred_getegid(peercred);
496 c->unixrights.pid = 0;
497 ucred_free(peercred);
502 #ifdef HAVE_GETPEEREID
503 /* FreeBSD, OpenBSD */
508 if (getpeereid(c->fd, &uid, &gid) == 0) {
509 c->unixrights.uid = uid;
510 c->unixrights.gid = gid;
511 c->unixrights.pid = 0;
516 #if defined(SO_PEERCRED) && defined(__linux__)
520 socklen_t pclen = sizeof(pc);
522 if (getsockopt(c->fd, SOL_SOCKET, SO_PEERCRED, (void *)&pc, &pclen) == 0) {
523 c->unixrights.uid = pc.uid;
524 c->unixrights.gid = pc.gid;
525 c->unixrights.pid = pc.pid;
530 #if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
532 struct xucred peercred;
533 socklen_t peercredlen = sizeof(peercred);
535 if (getsockopt(c->fd, LOCAL_PEERCRED, 1,
536 (void *)&peercred, &peercredlen) == 0
537 && peercred.cr_version == XUCRED_VERSION)
539 c->unixrights.uid = peercred.cr_uid;
540 c->unixrights.gid = peercred.cr_gid;
541 c->unixrights.pid = 0;
546 #if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
548 if (c->unixrights.uid == (uid_t)-1) {
555 memset(&msg, 0, sizeof(msg));
556 crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
560 crmsg = malloc(crmsgsize);
562 goto failed_scm_creds;
564 memset(crmsg, 0, crmsgsize);
566 msg.msg_control = crmsg;
567 msg.msg_controllen = crmsgsize;
569 if (recvmsg(c->fd, &msg, 0) < 0) {
571 goto failed_scm_creds;
574 if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
576 goto failed_scm_creds;
579 cmp = CMSG_FIRSTHDR(&msg);
580 if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
582 goto failed_scm_creds;
585 sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
587 c->unixrights.uid = sc->sc_euid;
588 c->unixrights.gid = sc->sc_egid;
589 c->unixrights.pid = 0;
594 /* we already got the cred, just return it */
602 static struct client *
603 add_new_socket(int fd,
605 heim_ipc_callback callback,
610 c = calloc(1, sizeof(*c));
614 if (flags & LISTEN_SOCKET) {
616 } else if (flags & DOOR_FD) {
617 c->fd = -1; /* cannot poll a door descriptor */
619 c->fd = accept(fd, NULL, NULL);
627 c->callback = callback;
628 c->userctx = userctx;
630 socket_set_nonblocking(fd, 1);
635 c->in = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
637 c->out = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE,
640 dispatch_source_set_event_handler(c->in, ^{
641 int rw = (c->flags & WAITING_WRITE);
643 if (rw == 0 && (c->flags & WAITING_WRITE))
644 dispatch_resume(c->out);
645 if ((c->flags & WAITING_READ) == 0)
646 dispatch_suspend(c->in);
649 dispatch_source_set_event_handler(c->out, ^{
651 if ((c->flags & WAITING_WRITE) == 0) {
652 dispatch_suspend(c->out);
657 dispatch_resume(c->in);
659 clients = erealloc(clients, sizeof(clients[0]) * (num_clients + 1));
660 clients[num_clients] = c;
668 maybe_close(struct client *c)
672 if (c->flags & (WAITING_READ|WAITING_WRITE))
676 dispatch_source_cancel(c->in);
677 if ((c->flags & WAITING_READ) == 0)
678 dispatch_resume(c->in);
679 dispatch_release(c->in);
681 dispatch_source_cancel(c->out);
682 if ((c->flags & WAITING_WRITE) == 0)
683 dispatch_resume(c->out);
684 dispatch_release(c->out);
686 close(c->fd); /* ref count fd close */
700 output_data(struct client *c, const void *data, size_t len)
702 if (c->olen + len < c->olen)
704 c->outmsg = erealloc(c->outmsg, c->olen + len);
705 memcpy(&c->outmsg[c->olen], data, len);
707 c->flags |= WAITING_WRITE;
711 socket_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
713 struct socket_call *sc = (struct socket_call *)ctx;
714 struct client *c = sc->c;
716 /* double complete ? */
720 if ((c->flags & WAITING_CLOSE) == 0) {
724 u32 = htonl(reply->length);
725 output_data(c, &u32, sizeof(u32));
728 if (c->flags & INCLUDE_ERROR_CODE) {
729 u32 = htonl(returnvalue);
730 output_data(c, &u32, sizeof(u32));
734 output_data(c, reply->data, reply->length);
736 /* if HTTP, close connection */
737 if (c->flags & HTTP_REPLY) {
738 c->flags |= WAITING_CLOSE;
739 c->flags &= ~WAITING_READ;
745 heim_ipc_free_cred(sc->cred);
747 sc->c = NULL; /* so we can catch double complete */
753 /* remove HTTP %-quoting from buf */
757 unsigned char *p, *q;
758 for(p = q = (unsigned char *)buf; *p; p++, q++) {
759 if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
761 if(sscanf((char *)p + 1, "%2x", &x) != 1)
772 static struct socket_call *
773 handle_http_tcp(struct client *c)
775 struct socket_call *cs;
781 s = (char *)c->inmsg;
783 p = strstr(s, "\r\n");
790 t = strtok_r(s, " \t", &p);
794 t = strtok_r(NULL, " \t", &p);
798 data = malloc(strlen(t));
804 if(de_http(t) != 0) {
808 proto = strtok_r(NULL, " \t", &p);
813 len = rk_base64_decode(t, data);
817 "Server: Heimdal/" VERSION "\r\n"
818 "Cache-Control: no-cache\r\n"
819 "Pragma: no-cache\r\n"
820 "Content-type: text/html\r\n"
821 "Content-transfer-encoding: 8bit\r\n\r\n"
822 "<TITLE>404 Not found</TITLE>\r\n"
823 "<H1>404 Not found</H1>\r\n"
824 "That page doesn't exist, maybe you are looking for "
825 "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
827 output_data(c, proto, strlen(proto));
828 output_data(c, msg, strlen(msg));
832 cs = emalloc(sizeof(*cs));
841 "Server: Heimdal/" VERSION "\r\n"
842 "Cache-Control: no-cache\r\n"
843 "Pragma: no-cache\r\n"
844 "Content-type: application/octet-stream\r\n"
845 "Content-transfer-encoding: binary\r\n\r\n";
846 output_data(c, proto, strlen(proto));
847 output_data(c, msg, strlen(msg));
855 handle_read(struct client *c)
860 assert((c->flags & DOOR_FD) == 0);
862 if (c->flags & LISTEN_SOCKET) {
863 add_new_socket(c->fd,
864 WAITING_READ | (c->flags & INHERIT_MASK),
870 if (c->ptr - c->len < 1024) {
871 c->inmsg = erealloc(c->inmsg,
876 len = read(c->fd, c->inmsg + c->ptr, c->len - c->ptr);
878 c->flags |= WAITING_CLOSE;
879 c->flags &= ~WAITING_READ;
886 while (c->ptr >= sizeof(dlen)) {
887 struct socket_call *cs;
889 if((c->flags & ALLOW_HTTP) && c->ptr >= 4 &&
890 strncmp((char *)c->inmsg, "GET ", 4) == 0 &&
891 strncmp((char *)c->inmsg + c->ptr - 4, "\r\n\r\n", 4) == 0) {
893 /* remove the trailing \r\n\r\n so the string is NUL terminated */
894 c->inmsg[c->ptr - 4] = '\0';
896 c->flags |= HTTP_REPLY;
898 cs = handle_http_tcp(c);
900 c->flags |= WAITING_CLOSE;
901 c->flags &= ~WAITING_READ;
905 memcpy(&dlen, c->inmsg, sizeof(dlen));
908 if (dlen > MAX_PACKET_SIZE) {
909 c->flags |= WAITING_CLOSE;
910 c->flags &= ~WAITING_READ;
913 if (dlen > c->ptr - sizeof(dlen)) {
917 cs = emalloc(sizeof(*cs));
919 cs->in.data = emalloc(dlen);
920 memcpy(cs->in.data, c->inmsg + sizeof(dlen), dlen);
921 cs->in.length = dlen;
924 c->ptr -= sizeof(dlen) + dlen;
926 c->inmsg + sizeof(dlen) + dlen,
932 if ((c->flags & UNIX_SOCKET) != 0) {
933 if (update_client_creds(c))
934 _heim_ipc_create_cred(c->unixrights.uid, c->unixrights.gid,
935 c->unixrights.pid, -1, &cs->cred);
938 c->callback(c->userctx, &cs->in,
939 cs->cred, socket_complete,
945 handle_write(struct client *c)
949 len = write(c->fd, c->outmsg, c->olen);
951 c->flags |= WAITING_CLOSE;
952 c->flags &= ~(WAITING_WRITE);
953 } else if (c->olen != (size_t)len) {
954 memmove(&c->outmsg[0], &c->outmsg[len], c->olen - len);
960 c->flags &= ~(WAITING_WRITE);
974 while (num_clients > 0) {
976 fds = malloc(num_clients * sizeof(fds[0]));
980 num_fds = num_clients;
982 for (n = 0 ; n < num_fds; n++) {
983 fds[n].fd = clients[n]->fd;
985 if (clients[n]->flags & WAITING_READ)
986 fds[n].events |= POLLIN;
987 if (clients[n]->flags & WAITING_WRITE)
988 fds[n].events |= POLLOUT;
993 while (poll(fds, num_fds, -1) == -1) {
994 if (errno == EINTR || errno == EAGAIN)
996 err(1, "poll(2) failed");
999 for (n = 0 ; n < num_fds; n++) {
1000 if (clients[n] == NULL)
1002 if (fds[n].revents & POLLIN)
1003 handle_read(clients[n]);
1004 if (fds[n].revents & POLLOUT)
1005 handle_write(clients[n]);
1006 if (fds[n].revents & POLLERR)
1007 clients[n]->flags |= WAITING_CLOSE;
1011 while (n < num_clients) {
1012 struct client *c = clients[n];
1013 if (maybe_close(c)) {
1014 if (n < num_clients - 1)
1015 clients[n] = clients[num_clients - 1];
1028 socket_release(heim_sipc ctx)
1030 struct client *c = ctx->mech;
1031 c->flags |= WAITING_CLOSE;
1036 heim_sipc_stream_listener(int fd, int type,
1037 heim_ipc_callback callback,
1038 void *user, heim_sipc *ctx)
1043 if ((type & HEIM_SIPC_TYPE_IPC) && (type & (HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP)))
1046 ct = calloc(1, sizeof(*ct));
1051 case HEIM_SIPC_TYPE_IPC:
1052 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|INCLUDE_ERROR_CODE, callback, user);
1054 case HEIM_SIPC_TYPE_UINT32:
1055 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ, callback, user);
1057 case HEIM_SIPC_TYPE_HTTP:
1058 case HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP:
1059 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|ALLOW_HTTP, callback, user);
1067 ct->release = socket_release;
1069 c->unixrights.uid = (uid_t) -1;
1070 c->unixrights.gid = (gid_t) -1;
1071 c->unixrights.pid = (pid_t) 0;
1078 heim_sipc_service_unix(const char *service,
1079 heim_ipc_callback callback,
1080 void *user, heim_sipc *ctx)
1082 struct sockaddr_un un;
1083 const char *d = secure_getenv("HEIM_IPC_DIR");
1086 if (strncasecmp(service, "UNIX:", sizeof("UNIX:") - 1) == 0)
1087 service += sizeof("UNIX:") - 1;
1089 un.sun_family = AF_UNIX;
1091 if (snprintf(un.sun_path, sizeof(un.sun_path),
1092 "%s/.heim_%s-socket", d ? d : _PATH_VARRUN,
1093 service) > sizeof(un.sun_path) + sizeof("-s") - 1)
1094 return ENAMETOOLONG;
1095 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1099 socket_set_reuseaddr(fd, 1);
1103 (void) setsockopt(fd, 0, LOCAL_CREDS, (void *)&one, sizeof(one));
1107 unlink(un.sun_path);
1109 if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
1114 if (listen(fd, SOMAXCONN) < 0) {
1119 (void) chmod(un.sun_path, 0666);
1121 ret = heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC,
1122 callback, user, ctx);
1124 struct client *c = (*ctx)->mech;
1125 c->flags |= UNIX_SOCKET;
1131 #ifdef HAVE_DOOR_CREATE
1134 #ifdef HAVE_SYS_MMAN_H
1135 #include <sys/mman.h>
1138 #include "heim_threads.h"
1140 static HEIMDAL_thread_key door_key;
1151 unsigned char data[1];
1155 door_release(heim_sipc ctx)
1157 struct client *c = ctx->mech;
1162 door_reply_destroy(void *data)
1168 door_key_create(void *key)
1172 HEIMDAL_key_create((HEIMDAL_thread_key *)key, door_reply_destroy, ret);
1176 door_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
1178 static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
1179 struct door_call *cs = (struct door_call *)ctx;
1181 struct door_reply *r = NULL;
1183 struct door_reply reply;
1184 char buffer[offsetof(struct door_reply, data) + BUFSIZ];
1187 heim_base_once_f(&once, &door_key, door_key_create);
1189 /* door_return() doesn't return; don't leak cred */
1190 heim_ipc_free_cred(cs->cred);
1193 rlen = offsetof(struct door_reply, data);
1194 if (returnvalue == 0)
1195 rlen += reply->length;
1197 /* long replies (> BUFSIZ) are allocated from the heap */
1198 if (rlen > BUFSIZ) {
1201 /* door_return() doesn't return, so stash reply buffer in TLS */
1202 r = realloc(HEIMDAL_getspecific(door_key), rlen);
1204 returnvalue = EAGAIN; /* don't leak ENOMEM to caller */
1208 HEIMDAL_setspecific(door_key, r, ret);
1210 r = &replyBuf.reply;
1213 r->returnvalue = returnvalue;
1214 if (r->returnvalue == 0) {
1215 r->length = reply->length;
1216 memcpy(r->data, reply->data, reply->length);
1221 door_return((char *)r, rlen, NULL, 0);
1225 door_callback(void *cookie,
1231 heim_sipc c = (heim_sipc)cookie;
1232 struct door_call cs = { 0 };
1233 ucred_t *peercred = NULL;
1235 if (door_ucred(&peercred) < 0)
1238 _heim_ipc_create_cred(ucred_geteuid(peercred),
1239 ucred_getegid(peercred),
1240 ucred_getpid(peercred),
1243 ucred_free(peercred);
1247 cs.in.length = arg_size;
1249 c->callback(c->userctx, &cs.in, cs.cred, door_complete, (heim_sipc_call)&cs);
1253 heim_sipc_service_door(const char *service,
1254 heim_ipc_callback callback,
1255 void *user, heim_sipc *ctx)
1257 char path[PATH_MAX];
1258 int fd = -1, dfd = -1, ret;
1259 heim_sipc ct = NULL;
1260 struct client *c = NULL;
1262 ct = calloc(1, sizeof(*ct));
1267 ct->release = door_release;
1269 ct->callback = callback;
1271 if (snprintf(path, sizeof(path), "/var/run/.heim_%s-door",
1272 service) >= sizeof(path) + sizeof("-d") - 1) {
1276 fd = door_create(door_callback, ct, DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
1283 dfd = open(path, O_RDWR | O_CREAT, 0666);
1288 (void) fchmod(dfd, 0666); /* XXX */
1290 if (fattach(fd, path) < 0) {
1295 c = add_new_socket(fd, DOOR_FD, callback, user);
1313 #endif /* HAVE_DOOR_CREATE */
1316 * Set the idle timeout value
1318 * The timeout event handler is triggered recurrently every idle
1319 * period `t'. The default action is rather draconian and just calls
1320 * exit(0), so you might want to change this to something more
1321 * graceful using heim_sipc_set_timeout_handler().
1325 heim_sipc_timeout(time_t t)
1328 static dispatch_once_t timeoutonce;
1330 dispatch_sync(timerq, ^{
1334 dispatch_once(&timeoutonce, ^{ dispatch_resume(timer); });
1341 * Set the timeout event handler
1343 * Replaces the default idle timeout action.
1347 heim_sipc_set_timeout_handler(void (*func)(void))
1351 dispatch_sync(timerq, ^{ timer_ev = func; });
1359 heim_sipc_free_context(heim_sipc ctx)
1361 (ctx->release)(ctx);