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
;
126 heim_ipc_message_outband_t replyout
;
127 mach_msg_type_number_t replyoutCnt
;
131 /* on error, no reply */
133 replyout
= 0; replyoutCnt
= 0;
135 } else if (reply
->length
< 2048) {
136 replyinCnt
= reply
->length
;
137 memcpy(replyin
, reply
->data
, replyinCnt
);
138 replyout
= 0; replyoutCnt
= 0;
142 kr
= vm_read(mach_task_self(),
143 (vm_address_t
)reply
->data
, reply
->length
,
144 (vm_address_t
*)&replyout
, &replyoutCnt
);
147 mheim_ripc_call_reply(s
->reply_port
, returnvalue
,
149 replyout
, replyoutCnt
);
151 heim_ipc_free_cred(s
->cred
);
158 mach_complete_async(heim_sipc_call ctx
, int returnvalue
, heim_idata
*reply
)
160 struct mach_call_ctx
*s
= (struct mach_call_ctx
*)ctx
;
161 heim_ipc_message_inband_t replyin
;
162 mach_msg_type_number_t replyinCnt
;
163 heim_ipc_message_outband_t replyout
;
164 mach_msg_type_number_t replyoutCnt
;
168 /* on error, no reply */
170 replyout
= 0; replyoutCnt
= 0;
172 } else if (reply
->length
< 2048) {
173 replyinCnt
= reply
->length
;
174 memcpy(replyin
, reply
->data
, replyinCnt
);
175 replyout
= 0; replyoutCnt
= 0;
179 kr
= vm_read(mach_task_self(),
180 (vm_address_t
)reply
->data
, reply
->length
,
181 (vm_address_t
*)&replyout
, &replyoutCnt
);
184 kr
= mheim_aipc_acall_reply(s
->reply_port
, returnvalue
,
186 replyout
, replyoutCnt
);
187 heim_ipc_free_cred(s
->cred
);
195 mheim_do_call(mach_port_t server_port
,
196 audit_token_t client_creds
,
197 mach_port_t reply_port
,
198 heim_ipc_message_inband_t requestin
,
199 mach_msg_type_number_t requestinCnt
,
200 heim_ipc_message_outband_t requestout
,
201 mach_msg_type_number_t requestoutCnt
,
203 heim_ipc_message_inband_t replyin
,
204 mach_msg_type_number_t
*replyinCnt
,
205 heim_ipc_message_outband_t
*replyout
,
206 mach_msg_type_number_t
*replyoutCnt
)
208 heim_sipc ctx
= dispatch_get_context(dispatch_get_current_queue());
209 struct mach_call_ctx
*s
;
220 s
= malloc(sizeof(*s
));
222 return KERN_MEMORY_FAILURE
; /* XXX */
224 s
->reply_port
= reply_port
;
226 audit_token_to_au32(client_creds
, NULL
, &uid
, &gid
, NULL
, NULL
, &pid
, &session
, NULL
);
228 kr
= _heim_ipc_create_cred(uid
, gid
, pid
, session
, &s
->cred
);
237 s
->req
.data
= malloc(requestinCnt
);
238 memcpy(s
->req
.data
, requestin
, requestinCnt
);
239 s
->req
.length
= requestinCnt
;
241 s
->req
.data
= malloc(requestoutCnt
);
242 memcpy(s
->req
.data
, requestout
, requestoutCnt
);
243 s
->req
.length
= requestoutCnt
;
246 dispatch_async(workq
, ^{
247 (ctx
->callback
)(ctx
->userctx
, &s
->req
, s
->cred
,
248 mach_complete_sync
, (heim_sipc_call
)s
);
255 mheim_do_call_request(mach_port_t server_port
,
256 audit_token_t client_creds
,
257 mach_port_t reply_port
,
258 heim_ipc_message_inband_t requestin
,
259 mach_msg_type_number_t requestinCnt
,
260 heim_ipc_message_outband_t requestout
,
261 mach_msg_type_number_t requestoutCnt
)
263 heim_sipc ctx
= dispatch_get_context(dispatch_get_current_queue());
264 struct mach_call_ctx
*s
;
271 s
= malloc(sizeof(*s
));
273 return KERN_MEMORY_FAILURE
; /* XXX */
275 s
->reply_port
= reply_port
;
277 audit_token_to_au32(client_creds
, NULL
, &uid
, &gid
, NULL
, NULL
, &pid
, &session
, NULL
);
279 kr
= _heim_ipc_create_cred(uid
, gid
, pid
, session
, &s
->cred
);
288 s
->req
.data
= malloc(requestinCnt
);
289 memcpy(s
->req
.data
, requestin
, requestinCnt
);
290 s
->req
.length
= requestinCnt
;
292 s
->req
.data
= malloc(requestoutCnt
);
293 memcpy(s
->req
.data
, requestout
, requestoutCnt
);
294 s
->req
.length
= requestoutCnt
;
297 dispatch_async(workq
, ^{
298 (ctx
->callback
)(ctx
->userctx
, &s
->req
, s
->cred
,
299 mach_complete_async
, (heim_sipc_call
)s
);
306 mach_init(const char *service
, mach_port_t sport
, heim_sipc ctx
)
308 struct mach_service
*s
;
313 s
= calloc(1, sizeof(*s
));
317 asprintf(&name
, "heim-ipc-mach-%s", service
);
319 s
->queue
= dispatch_queue_create(name
, NULL
);
323 s
->source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
,
324 s
->sport
, 0, s
->queue
);
325 if (s
->source
== NULL
) {
326 dispatch_release(s
->queue
);
332 dispatch_set_context(s
->queue
, ctx
);
333 dispatch_set_context(s
->source
, s
);
335 dispatch_source_set_event_handler(s
->source
, ^{
336 dispatch_mig_server(s
->source
, sizeof(union __RequestUnion__mheim_do_mheim_ipc_subsystem
), mheim_ipc_server
);
339 dispatch_source_set_cancel_handler(s
->source
, ^{
340 heim_sipc sctx
= dispatch_get_context(dispatch_get_current_queue());
341 struct mach_service
*st
= sctx
->mech
;
342 mach_port_mod_refs(mach_task_self(), st
->sport
,
343 MACH_PORT_RIGHT_RECEIVE
, -1);
344 dispatch_release(st
->queue
);
345 dispatch_release(st
->source
);
350 dispatch_resume(s
->source
);
356 mach_release(heim_sipc ctx
)
358 struct mach_service
*s
= ctx
->mech
;
359 dispatch_source_cancel(s
->source
);
360 dispatch_release(s
->source
);
365 mach_checkin_or_register(const char *service
)
370 kr
= bootstrap_check_in(bootstrap_port
, service
, &mp
);
371 if (kr
== KERN_SUCCESS
)
374 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
375 /* Pre SnowLeopard version */
376 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &mp
);
377 if (kr
!= KERN_SUCCESS
)
378 return MACH_PORT_NULL
;
380 kr
= mach_port_insert_right(mach_task_self(), mp
, mp
,
381 MACH_MSG_TYPE_MAKE_SEND
);
382 if (kr
!= KERN_SUCCESS
) {
383 mach_port_destroy(mach_task_self(), mp
);
384 return MACH_PORT_NULL
;
387 kr
= bootstrap_register(bootstrap_port
, rk_UNCONST(service
), mp
);
388 if (kr
!= KERN_SUCCESS
) {
389 mach_port_destroy(mach_task_self(), mp
);
390 return MACH_PORT_NULL
;
395 return MACH_PORT_NULL
;
400 #endif /* __APPLE__ && HAVE_GCD */
404 heim_sipc_launchd_mach_init(const char *service
,
405 heim_ipc_callback callback
,
406 void *user
, heim_sipc
*ctx
)
408 #if defined(__APPLE__) && defined(HAVE_GCD)
409 mach_port_t sport
= MACH_PORT_NULL
;
415 sport
= mach_checkin_or_register(service
);
416 if (sport
== MACH_PORT_NULL
) {
421 c
= calloc(1, sizeof(*c
));
426 c
->release
= mach_release
;
428 c
->callback
= callback
;
430 ret
= mach_init(service
, sport
, c
);
439 if (sport
!= MACH_PORT_NULL
)
440 mach_port_mod_refs(mach_task_self(), sport
,
441 MACH_PORT_RIGHT_RECEIVE
, -1);
443 #else /* !(__APPLE__ && HAVE_GCD) */
446 #endif /* __APPLE__ && HAVE_GCD */
451 heim_ipc_callback callback
;
454 #define LISTEN_SOCKET 1
455 #define WAITING_READ 2
456 #define WAITING_WRITE 4
457 #define WAITING_CLOSE 8
459 #define HTTP_REPLY 16
462 #define INHERIT_MASK 0xffff0000
463 #define INCLUDE_ERROR_CODE (1 << 16)
464 #define ALLOW_HTTP (1<<17)
465 #define UNIX_SOCKET (1<<18)
472 dispatch_source_t in
;
473 dispatch_source_t out
;
483 static unsigned num_clients
= 0;
484 static struct client
**clients
= NULL
;
487 static void handle_read(struct client
*);
488 static void handle_write(struct client
*);
489 static int maybe_close(struct client
*);
492 * Update peer credentials from socket.
494 * SCM_CREDS can only be updated the first time there is read data to
495 * read from the filedescriptor, so if we read do it before this
496 * point, the cred data might not be is not there yet.
500 update_client_creds(struct client
*c
)
502 #ifdef HAVE_GETPEERUCRED
505 ucred_t
*peercred
= NULL
;
507 if (getpeerucred(c
->fd
, &peercred
) == 0) {
508 c
->unixrights
.uid
= ucred_geteuid(peercred
);
509 c
->unixrights
.gid
= ucred_getegid(peercred
);
510 c
->unixrights
.pid
= 0;
511 ucred_free(peercred
);
516 #ifdef HAVE_GETPEEREID
517 /* FreeBSD, OpenBSD */
522 if (getpeereid(c
->fd
, &uid
, &gid
) == 0) {
523 c
->unixrights
.uid
= uid
;
524 c
->unixrights
.gid
= gid
;
525 c
->unixrights
.pid
= 0;
530 #if defined(SO_PEERCRED) && defined(__linux__)
534 socklen_t pclen
= sizeof(pc
);
536 if (getsockopt(c
->fd
, SOL_SOCKET
, SO_PEERCRED
, (void *)&pc
, &pclen
) == 0) {
537 c
->unixrights
.uid
= pc
.uid
;
538 c
->unixrights
.gid
= pc
.gid
;
539 c
->unixrights
.pid
= pc
.pid
;
544 #if defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
546 struct xucred peercred
;
547 socklen_t peercredlen
= sizeof(peercred
);
549 if (getsockopt(c
->fd
, LOCAL_PEERCRED
, 1,
550 (void *)&peercred
, &peercredlen
) == 0
551 && peercred
.cr_version
== XUCRED_VERSION
)
553 c
->unixrights
.uid
= peercred
.cr_uid
;
554 c
->unixrights
.gid
= peercred
.cr_gid
;
555 c
->unixrights
.pid
= 0;
560 #if defined(SOCKCREDSIZE) && defined(SCM_CREDS)
562 if (c
->unixrights
.uid
== (uid_t
)-1) {
569 memset(&msg
, 0, sizeof(msg
));
570 crmsgsize
= CMSG_SPACE(SOCKCREDSIZE(NGROUPS
));
574 crmsg
= malloc(crmsgsize
);
576 goto failed_scm_creds
;
578 memset(crmsg
, 0, crmsgsize
);
580 msg
.msg_control
= crmsg
;
581 msg
.msg_controllen
= crmsgsize
;
583 if (recvmsg(c
->fd
, &msg
, 0) < 0) {
585 goto failed_scm_creds
;
588 if (msg
.msg_controllen
== 0 || (msg
.msg_flags
& MSG_CTRUNC
) != 0) {
590 goto failed_scm_creds
;
593 cmp
= CMSG_FIRSTHDR(&msg
);
594 if (cmp
->cmsg_level
!= SOL_SOCKET
|| cmp
->cmsg_type
!= SCM_CREDS
) {
596 goto failed_scm_creds
;
599 sc
= (struct sockcred
*)(void *)CMSG_DATA(cmp
);
601 c
->unixrights
.uid
= sc
->sc_euid
;
602 c
->unixrights
.gid
= sc
->sc_egid
;
603 c
->unixrights
.pid
= 0;
608 /* we already got the cred, just return it */
616 static struct client
*
617 add_new_socket(int fd
,
619 heim_ipc_callback callback
,
625 c
= calloc(1, sizeof(*c
));
629 if (flags
& LISTEN_SOCKET
) {
631 } else if (flags
& DOOR_FD
) {
632 c
->fd
= -1; /* cannot poll a door descriptor */
634 c
->fd
= accept(fd
, NULL
, NULL
);
642 c
->callback
= callback
;
643 c
->userctx
= userctx
;
645 fileflags
= fcntl(c
->fd
, F_GETFL
, 0);
646 fcntl(c
->fd
, F_SETFL
, fileflags
| O_NONBLOCK
);
651 c
->in
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
,
653 c
->out
= dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE
,
656 dispatch_source_set_event_handler(c
->in
, ^{
657 int rw
= (c
->flags
& WAITING_WRITE
);
659 if (rw
== 0 && (c
->flags
& WAITING_WRITE
))
660 dispatch_resume(c
->out
);
661 if ((c
->flags
& WAITING_READ
) == 0)
662 dispatch_suspend(c
->in
);
665 dispatch_source_set_event_handler(c
->out
, ^{
667 if ((c
->flags
& WAITING_WRITE
) == 0) {
668 dispatch_suspend(c
->out
);
673 dispatch_resume(c
->in
);
675 clients
= erealloc(clients
, sizeof(clients
[0]) * (num_clients
+ 1));
676 clients
[num_clients
] = c
;
684 maybe_close(struct client
*c
)
688 if (c
->flags
& (WAITING_READ
|WAITING_WRITE
))
692 dispatch_source_cancel(c
->in
);
693 if ((c
->flags
& WAITING_READ
) == 0)
694 dispatch_resume(c
->in
);
695 dispatch_release(c
->in
);
697 dispatch_source_cancel(c
->out
);
698 if ((c
->flags
& WAITING_WRITE
) == 0)
699 dispatch_resume(c
->out
);
700 dispatch_release(c
->out
);
702 close(c
->fd
); /* ref count fd close */
715 output_data(struct client
*c
, const void *data
, size_t len
)
717 if (c
->olen
+ len
< c
->olen
)
719 c
->outmsg
= erealloc(c
->outmsg
, c
->olen
+ len
);
720 memcpy(&c
->outmsg
[c
->olen
], data
, len
);
722 c
->flags
|= WAITING_WRITE
;
726 socket_complete(heim_sipc_call ctx
, int returnvalue
, heim_idata
*reply
)
728 struct socket_call
*sc
= (struct socket_call
*)ctx
;
729 struct client
*c
= sc
->c
;
731 /* double complete ? */
735 if ((c
->flags
& WAITING_CLOSE
) == 0) {
739 u32
= htonl(reply
->length
);
740 output_data(c
, &u32
, sizeof(u32
));
743 if (c
->flags
& INCLUDE_ERROR_CODE
) {
744 u32
= htonl(returnvalue
);
745 output_data(c
, &u32
, sizeof(u32
));
749 output_data(c
, reply
->data
, reply
->length
);
751 /* if HTTP, close connection */
752 if (c
->flags
& HTTP_REPLY
) {
753 c
->flags
|= WAITING_CLOSE
;
754 c
->flags
&= ~WAITING_READ
;
760 heim_ipc_free_cred(sc
->cred
);
762 sc
->c
= NULL
; /* so we can catch double complete */
768 /* remove HTTP %-quoting from buf */
772 unsigned char *p
, *q
;
773 for(p
= q
= (unsigned char *)buf
; *p
; p
++, q
++) {
774 if(*p
== '%' && isxdigit(p
[1]) && isxdigit(p
[2])) {
776 if(sscanf((char *)p
+ 1, "%2x", &x
) != 1)
787 static struct socket_call
*
788 handle_http_tcp(struct client
*c
)
790 struct socket_call
*cs
;
796 s
= (char *)c
->inmsg
;
798 p
= strstr(s
, "\r\n");
805 t
= strtok_r(s
, " \t", &p
);
809 t
= strtok_r(NULL
, " \t", &p
);
813 data
= malloc(strlen(t
));
819 if(de_http(t
) != 0) {
823 proto
= strtok_r(NULL
, " \t", &p
);
828 len
= rk_base64_decode(t
, data
);
832 "Server: Heimdal/" VERSION
"\r\n"
833 "Cache-Control: no-cache\r\n"
834 "Pragma: no-cache\r\n"
835 "Content-type: text/html\r\n"
836 "Content-transfer-encoding: 8bit\r\n\r\n"
837 "<TITLE>404 Not found</TITLE>\r\n"
838 "<H1>404 Not found</H1>\r\n"
839 "That page doesn't exist, maybe you are looking for "
840 "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
842 output_data(c
, proto
, strlen(proto
));
843 output_data(c
, msg
, strlen(msg
));
847 cs
= emalloc(sizeof(*cs
));
856 "Server: Heimdal/" VERSION
"\r\n"
857 "Cache-Control: no-cache\r\n"
858 "Pragma: no-cache\r\n"
859 "Content-type: application/octet-stream\r\n"
860 "Content-transfer-encoding: binary\r\n\r\n";
861 output_data(c
, proto
, strlen(proto
));
862 output_data(c
, msg
, strlen(msg
));
870 handle_read(struct client
*c
)
875 assert((c
->flags
& DOOR_FD
) == 0);
877 if (c
->flags
& LISTEN_SOCKET
) {
878 add_new_socket(c
->fd
,
879 WAITING_READ
| (c
->flags
& INHERIT_MASK
),
885 if (c
->ptr
- c
->len
< 1024) {
886 c
->inmsg
= erealloc(c
->inmsg
,
891 len
= read(c
->fd
, c
->inmsg
+ c
->ptr
, c
->len
- c
->ptr
);
893 c
->flags
|= WAITING_CLOSE
;
894 c
->flags
&= ~WAITING_READ
;
901 while (c
->ptr
>= sizeof(dlen
)) {
902 struct socket_call
*cs
;
904 if((c
->flags
& ALLOW_HTTP
) && c
->ptr
>= 4 &&
905 strncmp((char *)c
->inmsg
, "GET ", 4) == 0 &&
906 strncmp((char *)c
->inmsg
+ c
->ptr
- 4, "\r\n\r\n", 4) == 0) {
908 /* remove the trailing \r\n\r\n so the string is NUL terminated */
909 c
->inmsg
[c
->ptr
- 4] = '\0';
911 c
->flags
|= HTTP_REPLY
;
913 cs
= handle_http_tcp(c
);
915 c
->flags
|= WAITING_CLOSE
;
916 c
->flags
&= ~WAITING_READ
;
920 memcpy(&dlen
, c
->inmsg
, sizeof(dlen
));
923 if (dlen
> MAX_PACKET_SIZE
) {
924 c
->flags
|= WAITING_CLOSE
;
925 c
->flags
&= ~WAITING_READ
;
928 if (dlen
> c
->ptr
- sizeof(dlen
)) {
932 cs
= emalloc(sizeof(*cs
));
934 cs
->in
.data
= emalloc(dlen
);
935 memcpy(cs
->in
.data
, c
->inmsg
+ sizeof(dlen
), dlen
);
936 cs
->in
.length
= dlen
;
939 c
->ptr
-= sizeof(dlen
) + dlen
;
941 c
->inmsg
+ sizeof(dlen
) + dlen
,
947 if ((c
->flags
& UNIX_SOCKET
) != 0) {
948 if (update_client_creds(c
))
949 _heim_ipc_create_cred(c
->unixrights
.uid
, c
->unixrights
.gid
,
950 c
->unixrights
.pid
, -1, &cs
->cred
);
953 c
->callback(c
->userctx
, &cs
->in
,
954 cs
->cred
, socket_complete
,
960 handle_write(struct client
*c
)
964 len
= write(c
->fd
, c
->outmsg
, c
->olen
);
966 c
->flags
|= WAITING_CLOSE
;
967 c
->flags
&= ~(WAITING_WRITE
);
968 } else if (c
->olen
!= (size_t)len
) {
969 memmove(&c
->outmsg
[0], &c
->outmsg
[len
], c
->olen
- len
);
975 c
->flags
&= ~(WAITING_WRITE
);
989 while (num_clients
> 0) {
991 fds
= malloc(num_clients
* sizeof(fds
[0]));
995 num_fds
= num_clients
;
997 for (n
= 0 ; n
< num_fds
; n
++) {
998 fds
[n
].fd
= clients
[n
]->fd
;
1000 if (clients
[n
]->flags
& WAITING_READ
)
1001 fds
[n
].events
|= POLLIN
;
1002 if (clients
[n
]->flags
& WAITING_WRITE
)
1003 fds
[n
].events
|= POLLOUT
;
1008 while (poll(fds
, num_fds
, -1) == -1) {
1009 if (errno
== EINTR
|| errno
== EAGAIN
)
1011 err(1, "poll(2) failed");
1014 for (n
= 0 ; n
< num_fds
; n
++) {
1015 if (clients
[n
] == NULL
)
1017 if (fds
[n
].revents
& POLLERR
) {
1018 clients
[n
]->flags
|= WAITING_CLOSE
;
1022 if (fds
[n
].revents
& POLLIN
)
1023 handle_read(clients
[n
]);
1024 if (fds
[n
].revents
& POLLOUT
)
1025 handle_write(clients
[n
]);
1029 while (n
< num_clients
) {
1030 struct client
*c
= clients
[n
];
1031 if (maybe_close(c
)) {
1032 if (n
< num_clients
- 1)
1033 clients
[n
] = clients
[num_clients
- 1];
1046 socket_release(heim_sipc ctx
)
1048 struct client
*c
= ctx
->mech
;
1049 c
->flags
|= WAITING_CLOSE
;
1054 heim_sipc_stream_listener(int fd
, int type
,
1055 heim_ipc_callback callback
,
1056 void *user
, heim_sipc
*ctx
)
1061 if ((type
& HEIM_SIPC_TYPE_IPC
) && (type
& (HEIM_SIPC_TYPE_UINT32
|HEIM_SIPC_TYPE_HTTP
)))
1064 ct
= calloc(1, sizeof(*ct
));
1069 case HEIM_SIPC_TYPE_IPC
:
1070 c
= add_new_socket(fd
, LISTEN_SOCKET
|WAITING_READ
|INCLUDE_ERROR_CODE
, callback
, user
);
1072 case HEIM_SIPC_TYPE_UINT32
:
1073 c
= add_new_socket(fd
, LISTEN_SOCKET
|WAITING_READ
, callback
, user
);
1075 case HEIM_SIPC_TYPE_HTTP
:
1076 case HEIM_SIPC_TYPE_UINT32
|HEIM_SIPC_TYPE_HTTP
:
1077 c
= add_new_socket(fd
, LISTEN_SOCKET
|WAITING_READ
|ALLOW_HTTP
, callback
, user
);
1085 ct
->release
= socket_release
;
1087 c
->unixrights
.uid
= (uid_t
) -1;
1088 c
->unixrights
.gid
= (gid_t
) -1;
1089 c
->unixrights
.pid
= (pid_t
) 0;
1096 heim_sipc_service_unix(const char *service
,
1097 heim_ipc_callback callback
,
1098 void *user
, heim_sipc
*ctx
)
1100 struct sockaddr_un un
;
1101 const char *d
= secure_getenv("HEIM_IPC_DIR");
1104 un
.sun_family
= AF_UNIX
;
1106 if (snprintf(un
.sun_path
, sizeof(un
.sun_path
),
1107 "%s/.heim_%s-socket", d
? d
: _PATH_VARRUN
,
1108 service
) > sizeof(un
.sun_path
) + sizeof("-s") - 1)
1109 return ENAMETOOLONG
;
1110 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
1114 socket_set_reuseaddr(fd
, 1);
1118 setsockopt(fd
, 0, LOCAL_CREDS
, (void *)&one
, sizeof(one
));
1122 unlink(un
.sun_path
);
1124 if (bind(fd
, (struct sockaddr
*)&un
, sizeof(un
)) < 0) {
1129 if (listen(fd
, SOMAXCONN
) < 0) {
1134 chmod(un
.sun_path
, 0666);
1136 ret
= heim_sipc_stream_listener(fd
, HEIM_SIPC_TYPE_IPC
,
1137 callback
, user
, ctx
);
1139 struct client
*c
= (*ctx
)->mech
;
1140 c
->flags
|= UNIX_SOCKET
;
1146 #ifdef HAVE_DOOR_CREATE
1149 #ifdef HAVE_SYS_MMAN_H
1150 #include <sys/mman.h>
1153 #include "heim_threads.h"
1155 static HEIMDAL_thread_key door_key
;
1166 unsigned char data
[1];
1170 door_release(heim_sipc ctx
)
1172 struct client
*c
= ctx
->mech
;
1177 door_reply_destroy(void *data
)
1183 door_key_create(void *key
)
1187 HEIMDAL_key_create((HEIMDAL_thread_key
*)key
, door_reply_destroy
, ret
);
1191 door_complete(heim_sipc_call ctx
, int returnvalue
, heim_idata
*reply
)
1193 static heim_base_once_t once
= HEIM_BASE_ONCE_INIT
;
1194 struct door_call
*cs
= (struct door_call
*)ctx
;
1196 struct door_reply
*r
= NULL
;
1198 struct door_reply reply
;
1199 char buffer
[offsetof(struct door_reply
, data
) + BUFSIZ
];
1202 heim_base_once_f(&once
, &door_key
, door_key_create
);
1204 /* door_return() doesn't return; don't leak cred */
1205 heim_ipc_free_cred(cs
->cred
);
1208 rlen
= offsetof(struct door_reply
, data
);
1209 if (returnvalue
== 0)
1210 rlen
+= reply
->length
;
1212 /* long replies (> BUFSIZ) are allocated from the heap */
1213 if (rlen
> BUFSIZ
) {
1216 /* door_return() doesn't return, so stash reply buffer in TLS */
1217 r
= realloc(HEIMDAL_getspecific(door_key
), rlen
);
1219 returnvalue
= EAGAIN
; /* don't leak ENOMEM to caller */
1223 HEIMDAL_setspecific(door_key
, r
, ret
);
1225 r
= &replyBuf
.reply
;
1228 r
->returnvalue
= returnvalue
;
1229 if (r
->returnvalue
== 0) {
1230 r
->length
= reply
->length
;
1231 memcpy(r
->data
, reply
->data
, reply
->length
);
1236 door_return((char *)r
, rlen
, NULL
, 0);
1240 door_callback(void *cookie
,
1246 heim_sipc c
= (heim_sipc
)cookie
;
1247 struct door_call cs
= { 0 };
1248 ucred_t
*peercred
= NULL
;
1250 if (door_ucred(&peercred
) < 0)
1253 _heim_ipc_create_cred(ucred_geteuid(peercred
),
1254 ucred_getegid(peercred
),
1255 ucred_getpid(peercred
),
1258 ucred_free(peercred
);
1262 cs
.in
.length
= arg_size
;
1264 c
->callback(c
->userctx
, &cs
.in
, cs
.cred
, door_complete
, (heim_sipc_call
)&cs
);
1268 heim_sipc_service_door(const char *service
,
1269 heim_ipc_callback callback
,
1270 void *user
, heim_sipc
*ctx
)
1272 char path
[PATH_MAX
];
1273 int fd
= -1, dfd
= -1, ret
;
1274 heim_sipc ct
= NULL
;
1275 struct client
*c
= NULL
;
1277 ct
= calloc(1, sizeof(*ct
));
1282 ct
->release
= door_release
;
1284 ct
->callback
= callback
;
1286 if (snprintf(path
, sizeof(path
), "/var/run/.heim_%s-door",
1287 service
) >= sizeof(path
) + sizeof("-d") - 1) {
1291 fd
= door_create(door_callback
, ct
, DOOR_REFUSE_DESC
| DOOR_NO_CANCEL
);
1298 dfd
= open(path
, O_RDWR
| O_CREAT
, 0666);
1303 fchmod(dfd
, 0666); /* XXX */
1305 if (fattach(fd
, path
) < 0) {
1310 c
= add_new_socket(fd
, DOOR_FD
, callback
, user
);
1328 #endif /* HAVE_DOOR_CREATE */
1331 * Set the idle timeout value
1333 * The timeout event handler is triggered recurrently every idle
1334 * period `t'. The default action is rather draconian and just calls
1335 * exit(0), so you might want to change this to something more
1336 * graceful using heim_sipc_set_timeout_handler().
1340 heim_sipc_timeout(time_t t
)
1343 static dispatch_once_t timeoutonce
;
1345 dispatch_sync(timerq
, ^{
1349 dispatch_once(&timeoutonce
, ^{ dispatch_resume(timer
); });
1356 * Set the timeout event handler
1358 * Replaces the default idle timeout action.
1362 heim_sipc_set_timeout_handler(void (*func
)(void))
1366 dispatch_sync(timerq
, ^{ timer_ev
= func
; });
1374 heim_sipc_free_context(heim_sipc ctx
)
1376 (ctx
->release
)(ctx
);