add heim_ipc_reply.defs
[heimdal.git] / lib / ipc / server.c
blobe2f771cf05f5126365c0b5229f8ce243f035820e
1 /*
2 * Copyright (c) 2009 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
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
10 * are met:
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
33 * SUCH DAMAGE.
36 #include "hi_locl.h"
37 #include <assert.h>
39 #define MAX_PACKET_SIZE (128 * 1024)
41 struct heim_sipc {
42 int (*release)(heim_sipc ctx);
43 heim_ipc_callback callback;
44 void *userctx;
45 void *mech;
48 #if defined(__APPLE__) && defined(HAVE_GCD)
50 #include "heim_ipcServer.h"
51 #include "heim_ipc_reply.h"
52 #include "heim_ipc_async.h"
54 static dispatch_source_t timer;
55 static dispatch_queue_t timerq;
56 static uint64_t timeoutvalue;
58 static dispatch_queue_t eventq;
60 static dispatch_queue_t workq;
62 static void
63 default_timer_ev(void)
65 exit(0);
68 static void (*timer_ev)(void) = default_timer_ev;
70 static void
71 set_timer(void)
73 dispatch_source_set_timer(timer,
74 dispatch_time(DISPATCH_TIME_NOW,
75 timeoutvalue * NSEC_PER_SEC),
76 timeoutvalue * NSEC_PER_SEC, 1000000);
79 static void
80 init_globals(void)
82 static dispatch_once_t once;
83 dispatch_once(&once, ^{
84 timerq = dispatch_queue_create("hiem-sipc-timer-q", NULL);
85 timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerq);
86 dispatch_source_set_event_handler(timer, ^{ timer_ev(); } );
88 workq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
89 eventq = dispatch_queue_create("heim-ipc.event-queue", NULL);
90 });
93 static void
94 suspend_timer(void)
96 dispatch_suspend(timer);
99 static void
100 restart_timer(void)
102 dispatch_sync(timerq, ^{ set_timer(); });
103 dispatch_resume(timer);
106 struct mach_service {
107 mach_port_t sport;
108 dispatch_source_t source;
109 dispatch_queue_t queue;
112 struct mach_call_ctx {
113 mach_port_t reply_port;
114 heim_icred cred;
115 heim_idata req;
119 static void
120 mach_complete_sync(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
122 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
123 heim_ipc_message_inband_t replyin;
124 mach_msg_type_number_t replyinCnt;
125 heim_ipc_message_outband_t replyout;
126 mach_msg_type_number_t replyoutCnt;
127 kern_return_t kr;
129 if (returnvalue) {
130 /* on error, no reply */
131 replyinCnt = 0;
132 replyout = 0; replyoutCnt = 0;
133 kr = KERN_SUCCESS;
134 } else if (reply->length < 2048) {
135 replyinCnt = reply->length;
136 memcpy(replyin, reply->data, replyinCnt);
137 replyout = 0; replyoutCnt = 0;
138 kr = KERN_SUCCESS;
139 } else {
140 replyinCnt = 0;
141 kr = vm_read(mach_task_self(),
142 (vm_address_t)reply->data, reply->length,
143 (vm_address_t *)&replyout, &replyoutCnt);
146 mheim_ripc_call_reply(s->reply_port, returnvalue,
147 replyin, replyinCnt,
148 replyout, replyoutCnt);
150 heim_ipc_free_cred(s->cred);
151 free(s->req.data);
152 free(s);
153 restart_timer();
156 static void
157 mach_complete_async(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
159 struct mach_call_ctx *s = (struct mach_call_ctx *)ctx;
160 heim_ipc_message_inband_t replyin;
161 mach_msg_type_number_t replyinCnt;
162 heim_ipc_message_outband_t replyout;
163 mach_msg_type_number_t replyoutCnt;
164 kern_return_t kr;
166 if (returnvalue) {
167 /* on error, no reply */
168 replyinCnt = 0;
169 replyout = 0; replyoutCnt = 0;
170 kr = KERN_SUCCESS;
171 } else if (reply->length < 2048) {
172 replyinCnt = reply->length;
173 memcpy(replyin, reply->data, replyinCnt);
174 replyout = 0; replyoutCnt = 0;
175 kr = KERN_SUCCESS;
176 } else {
177 replyinCnt = 0;
178 kr = vm_read(mach_task_self(),
179 (vm_address_t)reply->data, reply->length,
180 (vm_address_t *)&replyout, &replyoutCnt);
183 kr = mheim_aipc_acall_reply(s->reply_port, returnvalue,
184 replyin, replyinCnt,
185 replyout, replyoutCnt);
186 heim_ipc_free_cred(s->cred);
187 free(s->req.data);
188 free(s);
189 restart_timer();
193 kern_return_t
194 mheim_do_call(mach_port_t server_port,
195 audit_token_t client_creds,
196 mach_port_t reply_port,
197 heim_ipc_message_inband_t requestin,
198 mach_msg_type_number_t requestinCnt,
199 heim_ipc_message_outband_t requestout,
200 mach_msg_type_number_t requestoutCnt,
201 int *returnvalue,
202 heim_ipc_message_inband_t replyin,
203 mach_msg_type_number_t *replyinCnt,
204 heim_ipc_message_outband_t *replyout,
205 mach_msg_type_number_t *replyoutCnt)
207 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
208 struct mach_call_ctx *s;
209 kern_return_t kr;
210 uid_t uid;
211 gid_t gid;
212 pid_t pid;
213 au_asid_t session;
215 *replyout = NULL;
216 *replyoutCnt = 0;
217 *replyinCnt = 0;
219 s = malloc(sizeof(*s));
220 if (s == NULL)
221 return KERN_MEMORY_FAILURE; /* XXX */
223 s->reply_port = reply_port;
225 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
227 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
228 if (kr) {
229 free(s);
230 return kr;
233 suspend_timer();
235 if (requestinCnt) {
236 s->req.data = malloc(requestinCnt);
237 memcpy(s->req.data, requestin, requestinCnt);
238 s->req.length = requestinCnt;
239 } else {
240 s->req.data = malloc(requestoutCnt);
241 memcpy(s->req.data, requestout, requestoutCnt);
242 s->req.length = requestoutCnt;
245 dispatch_async(workq, ^{
246 (ctx->callback)(ctx->userctx, &s->req, s->cred,
247 mach_complete_sync, (heim_sipc_call)s);
250 return MIG_NO_REPLY;
253 kern_return_t
254 mheim_do_call_request(mach_port_t server_port,
255 audit_token_t client_creds,
256 mach_port_t reply_port,
257 heim_ipc_message_inband_t requestin,
258 mach_msg_type_number_t requestinCnt,
259 heim_ipc_message_outband_t requestout,
260 mach_msg_type_number_t requestoutCnt)
262 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
263 struct mach_call_ctx *s;
264 kern_return_t kr;
265 uid_t uid;
266 gid_t gid;
267 pid_t pid;
268 au_asid_t session;
270 s = malloc(sizeof(*s));
271 if (s == NULL)
272 return KERN_MEMORY_FAILURE; /* XXX */
274 s->reply_port = reply_port;
276 audit_token_to_au32(client_creds, NULL, &uid, &gid, NULL, NULL, &pid, &session, NULL);
278 kr = _heim_ipc_create_cred(uid, gid, pid, session, &s->cred);
279 if (kr) {
280 free(s);
281 return kr;
284 suspend_timer();
286 if (requestinCnt) {
287 s->req.data = malloc(requestinCnt);
288 memcpy(s->req.data, requestin, requestinCnt);
289 s->req.length = requestinCnt;
290 } else {
291 s->req.data = malloc(requestoutCnt);
292 memcpy(s->req.data, requestout, requestoutCnt);
293 s->req.length = requestoutCnt;
296 dispatch_async(workq, ^{
297 (ctx->callback)(ctx->userctx, &s->req, s->cred,
298 mach_complete_async, (heim_sipc_call)s);
301 return KERN_SUCCESS;
304 static int
305 mach_init(const char *service, mach_port_t sport, heim_sipc ctx)
307 struct mach_service *s;
308 char *name;
310 init_globals();
312 s = calloc(1, sizeof(*s));
313 if (s == NULL)
314 return ENOMEM;
316 asprintf(&name, "heim-ipc-mach-%s", service);
318 s->queue = dispatch_queue_create(name, NULL);
319 free(name);
320 s->sport = sport;
322 s->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV,
323 s->sport, 0, s->queue);
324 if (s->source == NULL) {
325 dispatch_release(s->queue);
326 free(s);
327 return ENOMEM;
329 ctx->mech = s;
331 dispatch_set_context(s->queue, ctx);
332 dispatch_set_context(s->source, s);
334 dispatch_source_set_event_handler(s->source, ^{
335 dispatch_mig_server(s->source, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem), mheim_ipc_server);
338 dispatch_source_set_cancel_handler(s->source, ^{
339 heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
340 struct mach_service *st = ctx->mech;
341 mach_port_mod_refs(mach_task_self(), st->sport,
342 MACH_PORT_RIGHT_RECEIVE, -1);
343 dispatch_release(st->queue);
344 dispatch_release(st->source);
345 free(st);
346 free(ctx);
349 dispatch_resume(s->source);
351 return 0;
354 static int
355 mach_release(heim_sipc ctx)
357 struct mach_service *s = ctx->mech;
358 dispatch_source_cancel(s->source);
359 dispatch_release(s->source);
360 return 0;
363 static mach_port_t
364 mach_checkin_or_register(const char *service)
366 mach_port_t mp;
367 kern_return_t kr;
369 kr = bootstrap_check_in(bootstrap_port, service, &mp);
370 if (kr == KERN_SUCCESS)
371 return mp;
373 /* Pre SnowLeopard version */
374 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp);
375 if (kr != KERN_SUCCESS)
376 return MACH_PORT_NULL;
378 kr = mach_port_insert_right(mach_task_self(), mp, mp,
379 MACH_MSG_TYPE_MAKE_SEND);
380 if (kr != KERN_SUCCESS) {
381 mach_port_destroy(mach_task_self(), mp);
382 return MACH_PORT_NULL;
385 kr = bootstrap_register(bootstrap_port, rk_UNCONST(service), mp);
386 if (kr != KERN_SUCCESS) {
387 mach_port_destroy(mach_task_self(), mp);
388 return MACH_PORT_NULL;
391 return mp;
395 #endif /* __APPLE__ && HAVE_GCD */
399 heim_sipc_launchd_mach_init(const char *service,
400 heim_ipc_callback callback,
401 void *user, heim_sipc *ctx)
403 #if defined(__APPLE__) && defined(HAVE_GCD)
404 mach_port_t sport = MACH_PORT_NULL;
405 heim_sipc c = NULL;
406 int ret;
408 *ctx = NULL;
410 sport = mach_checkin_or_register(service);
411 if (sport == MACH_PORT_NULL) {
412 ret = ENOENT;
413 goto error;
416 c = calloc(1, sizeof(*c));
417 if (c == NULL) {
418 ret = ENOMEM;
419 goto error;
421 c->release = mach_release;
422 c->userctx = user;
423 c->callback = callback;
425 ret = mach_init(service, sport, c);
426 if (ret)
427 goto error;
429 *ctx = c;
430 return 0;
431 error:
432 if (c)
433 free(c);
434 if (sport != MACH_PORT_NULL)
435 mach_port_mod_refs(mach_task_self(), sport,
436 MACH_PORT_RIGHT_RECEIVE, -1);
437 return ret;
438 #else /* !(__APPLE__ && HAVE_GCD) */
439 *ctx = NULL;
440 return EINVAL;
441 #endif /* __APPLE__ && HAVE_GCD */
444 struct client {
445 int fd;
446 heim_ipc_callback callback;
447 void *userctx;
448 int flags;
449 #define LISTEN_SOCKET 1
450 #define WAITING_READ 2
451 #define WAITING_WRITE 4
452 #define WAITING_CLOSE 8
454 #define HTTP_REPLY 16
456 #define INHERIT_MASK 0xffff0000
457 #define INCLUDE_ERROR_CODE (1 << 16)
458 #define ALLOW_HTTP (1<<17)
459 unsigned calls;
460 size_t ptr, len;
461 uint8_t *inmsg;
462 size_t olen;
463 uint8_t *outmsg;
464 #ifdef HAVE_GCD
465 dispatch_source_t in;
466 dispatch_source_t out;
467 #endif
470 #ifndef HAVE_GCD
471 static unsigned num_clients = 0;
472 static struct client **clients = NULL;
473 #endif
475 static void handle_read(struct client *);
476 static void handle_write(struct client *);
477 static int maybe_close(struct client *);
479 static struct client *
480 add_new_socket(int fd,
481 int flags,
482 heim_ipc_callback callback,
483 void *userctx)
485 struct client *c;
486 int fileflags;
488 c = calloc(1, sizeof(*c));
489 if (c == NULL)
490 return NULL;
492 if (flags & LISTEN_SOCKET) {
493 c->fd = fd;
494 } else {
495 c->fd = accept(fd, NULL, NULL);
496 if(c->fd < 0) {
497 free(c);
498 return NULL;
502 c->flags = flags;
503 c->callback = callback;
504 c->userctx = userctx;
506 fileflags = fcntl(c->fd, F_GETFL, 0);
507 fcntl(c->fd, F_SETFL, fileflags | O_NONBLOCK);
509 #ifdef HAVE_GCD
510 init_globals();
512 c->in = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
513 c->fd, 0, eventq);
514 c->out = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE,
515 c->fd, 0, eventq);
517 dispatch_source_set_event_handler(c->in, ^{
518 int rw = (c->flags & WAITING_WRITE);
519 handle_read(c);
520 if (rw == 0 && (c->flags & WAITING_WRITE))
521 dispatch_resume(c->out);
522 if ((c->flags & WAITING_READ) == 0)
523 dispatch_suspend(c->in);
524 maybe_close(c);
526 dispatch_source_set_event_handler(c->out, ^{
527 handle_write(c);
528 if ((c->flags & WAITING_WRITE) == 0) {
529 dispatch_suspend(c->out);
531 maybe_close(c);
534 dispatch_resume(c->in);
535 #else
536 clients = erealloc(clients, sizeof(clients[0]) * (num_clients + 1));
537 clients[num_clients] = c;
538 num_clients++;
539 #endif
541 return c;
544 static int
545 maybe_close(struct client *c)
547 if (c->calls != 0)
548 return 0;
549 if (c->flags & (WAITING_READ|WAITING_WRITE))
550 return 0;
552 #ifdef HAVE_GCD
553 dispatch_source_cancel(c->in);
554 if ((c->flags & WAITING_READ) == 0)
555 dispatch_resume(c->in);
556 dispatch_release(c->in);
558 dispatch_source_cancel(c->out);
559 if ((c->flags & WAITING_WRITE) == 0)
560 dispatch_resume(c->out);
561 dispatch_release(c->out);
562 #endif
563 close(c->fd); /* ref count fd close */
564 free(c);
565 return 1;
569 struct socket_call {
570 heim_idata in;
571 struct client *c;
574 static void
575 output_data(struct client *c, const void *data, size_t len)
577 if (c->olen + len < c->olen)
578 abort();
579 c->outmsg = erealloc(c->outmsg, c->olen + len);
580 memcpy(&c->outmsg[c->olen], data, len);
581 c->olen += len;
582 c->flags |= WAITING_WRITE;
585 static void
586 socket_complete(heim_sipc_call ctx, int returnvalue, heim_idata *reply)
588 struct socket_call *sc = (struct socket_call *)ctx;
589 struct client *c = sc->c;
591 /* double complete ? */
592 if (c == NULL)
593 abort();
595 if ((c->flags & WAITING_CLOSE) == 0) {
596 uint32_t u32;
598 /* length */
599 u32 = htonl(reply->length);
600 output_data(c, &u32, sizeof(u32));
602 /* return value */
603 if (c->flags & INCLUDE_ERROR_CODE) {
604 u32 = htonl(returnvalue);
605 output_data(c, &u32, sizeof(u32));
608 /* data */
609 output_data(c, reply->data, reply->length);
611 /* if HTTP, close connection */
612 if (c->flags & HTTP_REPLY) {
613 c->flags |= WAITING_CLOSE;
614 c->flags &= ~WAITING_READ;
618 c->calls--;
620 free(sc->in.data);
621 sc->c = NULL; /* so we can catch double complete */
622 free(sc);
624 maybe_close(c);
627 /* remove HTTP %-quoting from buf */
628 static int
629 de_http(char *buf)
631 unsigned char *p, *q;
632 for(p = q = (unsigned char *)buf; *p; p++, q++) {
633 if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
634 unsigned int x;
635 if(sscanf((char *)p + 1, "%2x", &x) != 1)
636 return -1;
637 *q = x;
638 p += 2;
639 } else
640 *q = *p;
642 *q = '\0';
643 return 0;
646 static struct socket_call *
647 handle_http_tcp(struct client *c)
649 struct socket_call *cs;
650 char *s, *p, *t;
651 void *data;
652 char *proto;
653 int len;
655 s = (char *)c->inmsg;
657 p = strstr(s, "\r\n");
658 if (p == NULL)
659 return NULL;
661 *p = 0;
663 p = NULL;
664 t = strtok_r(s, " \t", &p);
665 if (t == NULL)
666 return NULL;
668 t = strtok_r(NULL, " \t", &p);
669 if (t == NULL)
670 return NULL;
672 data = malloc(strlen(t));
673 if (data == NULL)
674 return NULL;
676 if(*t == '/')
677 t++;
678 if(de_http(t) != 0) {
679 free(data);
680 return NULL;
682 proto = strtok_r(NULL, " \t", &p);
683 if (proto == NULL) {
684 free(data);
685 return NULL;
687 len = base64_decode(t, data);
688 if(len <= 0){
689 const char *msg =
690 " 404 Not found\r\n"
691 "Server: Heimdal/" VERSION "\r\n"
692 "Cache-Control: no-cache\r\n"
693 "Pragma: no-cache\r\n"
694 "Content-type: text/html\r\n"
695 "Content-transfer-encoding: 8bit\r\n\r\n"
696 "<TITLE>404 Not found</TITLE>\r\n"
697 "<H1>404 Not found</H1>\r\n"
698 "That page doesn't exist, maybe you are looking for "
699 "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
700 free(data);
701 output_data(c, proto, strlen(proto));
702 output_data(c, msg, strlen(msg));
703 return NULL;
706 cs = emalloc(sizeof(*cs));
707 cs->c = c;
708 cs->in.data = data;
709 cs->in.length = len;
710 c->ptr = 0;
713 const char *msg =
714 " 200 OK\r\n"
715 "Server: Heimdal/" VERSION "\r\n"
716 "Cache-Control: no-cache\r\n"
717 "Pragma: no-cache\r\n"
718 "Content-type: application/octet-stream\r\n"
719 "Content-transfer-encoding: binary\r\n\r\n";
720 output_data(c, proto, strlen(proto));
721 output_data(c, msg, strlen(msg));
724 return cs;
728 static void
729 handle_read(struct client *c)
731 ssize_t len;
732 uint32_t dlen;
734 if (c->flags & LISTEN_SOCKET) {
735 add_new_socket(c->fd,
736 WAITING_READ | (c->flags & INHERIT_MASK),
737 c->callback,
738 c->userctx);
739 return;
742 if (c->ptr - c->len < 1024) {
743 c->inmsg = erealloc(c->inmsg,
744 c->len + 1024);
745 c->len += 1024;
748 len = read(c->fd, c->inmsg + c->ptr, c->len - c->ptr);
749 if (len <= 0) {
750 c->flags |= WAITING_CLOSE;
751 c->flags &= ~WAITING_READ;
752 return;
754 c->ptr += len;
755 if (c->ptr > c->len)
756 abort();
758 while (c->ptr >= sizeof(dlen)) {
759 struct socket_call *cs;
761 if((c->flags & ALLOW_HTTP) && c->ptr >= 4 &&
762 strncmp((char *)c->inmsg, "GET ", 4) == 0 &&
763 strncmp((char *)c->inmsg + c->ptr - 4, "\r\n\r\n", 4) == 0) {
765 /* remove the trailing \r\n\r\n so the string is NUL terminated */
766 c->inmsg[c->ptr - 4] = '\0';
768 c->flags |= HTTP_REPLY;
770 cs = handle_http_tcp(c);
771 if (cs == NULL) {
772 c->flags |= WAITING_CLOSE;
773 c->flags &= ~WAITING_READ;
774 break;
776 } else {
777 memcpy(&dlen, c->inmsg, sizeof(dlen));
778 dlen = ntohl(dlen);
780 if (dlen > MAX_PACKET_SIZE) {
781 c->flags |= WAITING_CLOSE;
782 c->flags &= ~WAITING_READ;
783 return;
785 if (dlen < c->ptr - sizeof(dlen)) {
786 break;
789 cs = emalloc(sizeof(*cs));
790 cs->c = c;
791 cs->in.data = emalloc(dlen);
792 memcpy(cs->in.data, c->inmsg + sizeof(dlen), dlen);
793 cs->in.length = dlen;
795 c->ptr -= sizeof(dlen) + dlen;
796 memmove(c->inmsg,
797 c->inmsg + sizeof(dlen) + dlen,
798 c->ptr);
801 c->calls++;
802 c->callback(c->userctx, &cs->in,
803 NULL, socket_complete,
804 (heim_sipc_call)cs);
808 static void
809 handle_write(struct client *c)
811 ssize_t len;
813 len = write(c->fd, c->outmsg, c->olen);
814 if (len <= 0) {
815 c->flags |= WAITING_CLOSE;
816 c->flags &= ~(WAITING_WRITE);
817 } else if (c->olen != len) {
818 memmove(&c->outmsg[0], &c->outmsg[len], c->olen - len);
819 c->olen -= len;
820 } else {
821 c->olen = 0;
822 free(c->outmsg);
823 c->outmsg = NULL;
824 c->flags &= ~(WAITING_WRITE);
829 #ifndef HAVE_GCD
831 static void
832 process_loop(void)
834 struct pollfd *fds;
835 unsigned n;
836 unsigned num_fds;
838 while(num_clients > 0) {
840 fds = malloc(num_clients * sizeof(fds[0]));
841 if(fds == NULL)
842 abort();
844 num_fds = num_clients;
846 for (n = 0 ; n < num_fds; n++) {
847 fds[n].fd = clients[n]->fd;
848 fds[n].events = 0;
849 if (clients[n]->flags & WAITING_READ)
850 fds[n].events |= POLLIN;
851 if (clients[n]->flags & WAITING_WRITE)
852 fds[n].events |= POLLOUT;
854 fds[n].revents = 0;
857 poll(fds, num_fds, -1);
859 for (n = 0 ; n < num_fds; n++) {
860 if (clients[n] == NULL)
861 continue;
862 if (fds[n].revents & POLLERR) {
863 clients[n]->flags |= WAITING_CLOSE;
864 continue;
867 if (fds[n].revents & POLLIN)
868 handle_read(clients[n]);
869 if (fds[n].revents & POLLOUT)
870 handle_write(clients[n]);
873 n = 0;
874 while (n < num_clients) {
875 struct client *c = clients[n];
876 if (maybe_close(c)) {
877 if (n < num_clients - 1)
878 clients[n] = clients[num_clients - 1];
879 num_clients--;
880 } else
881 n++;
884 free(fds);
888 #endif
890 static int
891 socket_release(heim_sipc ctx)
893 struct client *c = ctx->mech;
894 c->flags |= WAITING_CLOSE;
895 return 0;
899 heim_sipc_stream_listener(int fd, int type,
900 heim_ipc_callback callback,
901 void *user, heim_sipc *ctx)
903 heim_sipc ct = calloc(1, sizeof(*ct));
904 struct client *c;
906 if ((type & HEIM_SIPC_TYPE_IPC) && (type & (HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP)))
907 return EINVAL;
909 switch (type) {
910 case HEIM_SIPC_TYPE_IPC:
911 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|INCLUDE_ERROR_CODE, callback, user);
912 break;
913 case HEIM_SIPC_TYPE_UINT32:
914 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ, callback, user);
915 break;
916 case HEIM_SIPC_TYPE_HTTP:
917 case HEIM_SIPC_TYPE_UINT32|HEIM_SIPC_TYPE_HTTP:
918 c = add_new_socket(fd, LISTEN_SOCKET|WAITING_READ|ALLOW_HTTP, callback, user);
919 break;
920 default:
921 free(ct);
922 return EINVAL;
925 ct->mech = c;
926 ct->release = socket_release;
927 *ctx = ct;
928 return 0;
932 heim_sipc_service_unix(const char *service,
933 heim_ipc_callback callback,
934 void *user, heim_sipc *ctx)
936 struct sockaddr_un un;
937 int fd;
939 un.sun_family = AF_UNIX;
941 snprintf(un.sun_path, sizeof(un.sun_path),
942 "/var/run/.heim_%s-socket", service);
943 fd = socket(AF_UNIX, SOCK_STREAM, 0);
944 if (fd < 0)
945 return errno;
947 socket_set_reuseaddr(fd, 1);
948 #ifdef LOCAL_CREDS
950 int one = 1;
951 setsockopt(fd, 0, LOCAL_CREDS, (void *)&one, sizeof(one));
953 #endif
955 unlink(un.sun_path);
957 if (bind(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
958 close(fd);
959 return errno;
962 if (listen(fd, SOMAXCONN) < 0) {
963 close(fd);
964 return errno;
967 chmod(un.sun_path, 0666);
969 return heim_sipc_stream_listener(fd, HEIM_SIPC_TYPE_IPC,
970 callback, user, ctx);
974 * Set the idle timeout value
976 * The timeout event handler is triggered recurrently every idle
977 * period `t'. The default action is rather draconian and just calls
978 * exit(0), so you might want to change this to something more
979 * graceful using heim_sipc_set_timeout_handler().
982 void
983 heim_sipc_timeout(time_t t)
985 #ifdef HAVE_GCD
986 static dispatch_once_t timeoutonce;
987 init_globals();
988 dispatch_sync(timerq, ^{
989 timeoutvalue = t;
990 set_timer();
992 dispatch_once(&timeoutonce, ^{ dispatch_resume(timer); });
993 #else
994 abort();
995 #endif
999 * Set the timeout event handler
1001 * Replaces the default idle timeout action.
1004 void
1005 heim_sipc_set_timeout_handler(void (*func)(void))
1007 #ifdef HAVE_GCD
1008 init_globals();
1009 dispatch_sync(timerq, ^{ timer_ev = func; });
1010 #else
1011 abort();
1012 #endif
1016 void
1017 heim_sipc_free_context(heim_sipc ctx)
1019 (ctx->release)(ctx);
1022 void
1023 heim_ipc_main(void)
1025 #ifdef HAVE_GCD
1026 dispatch_main();
1027 #else
1028 process_loop();
1029 #endif