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
38 #if defined(__APPLE__) && defined(HAVE_GCD)
41 #include "heim_ipc_asyncServer.h"
43 #include <dispatch/dispatch.h>
44 #include <mach/mach.h>
46 static dispatch_once_t jobqinited
= 0;
47 static dispatch_queue_t jobq
= NULL
;
48 static dispatch_queue_t syncq
;
56 mach_release(void *ctx
);
59 mach_init(const char *service
, void **ctx
)
65 dispatch_once(&jobqinited
, ^{
66 jobq
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
67 syncq
= dispatch_queue_create("heim-ipc-syncq", NULL
);
70 ret
= bootstrap_look_up(bootstrap_port
, service
, &sport
);
74 ipc
= malloc(sizeof(*ipc
));
76 mach_port_destroy(mach_task_self(), sport
);
81 ipc
->name
= strdup(service
);
82 if (ipc
->name
== NULL
) {
94 const heim_idata
*request
, heim_idata
*response
,
97 struct mach_ctx
*ipc
= ctx
;
98 heim_ipc_message_inband_t requestin
;
99 mach_msg_type_number_t requestin_length
= 0;
100 heim_ipc_message_outband_t requestout
= NULL
;
101 mach_msg_type_number_t requestout_length
= 0;
102 heim_ipc_message_inband_t replyin
;
103 mach_msg_type_number_t replyin_length
;
104 heim_ipc_message_outband_t replyout
;
105 mach_msg_type_number_t replyout_length
;
106 int ret
, errorcode
, retries
= 0;
108 memcpy(requestin
, request
->data
, request
->length
);
109 requestin_length
= request
->length
;
111 while (retries
< 2) {
112 __block mach_port_t sport
;
114 dispatch_sync(syncq
, ^{ sport
= ipc
->server
; });
116 ret
= mheim_ipc_call(sport
,
117 requestin
, requestin_length
,
118 requestout
, requestout_length
,
120 replyin
, &replyin_length
,
121 &replyout
, &replyout_length
);
122 if (ret
== MACH_SEND_INVALID_DEST
) {
124 /* race other threads to get a new port */
125 ret
= bootstrap_look_up(bootstrap_port
, ipc
->name
, &nport
);
128 dispatch_sync(syncq
, ^{
129 /* check if we lost the race to lookup the port */
130 if (sport
!= ipc
->server
) {
131 mach_port_deallocate(mach_task_self(), nport
);
133 mach_port_deallocate(mach_task_self(), ipc
->server
);
148 vm_deallocate (mach_task_self (), (vm_address_t
) replyout
,
153 if (replyout_length
) {
154 response
->data
= malloc(replyout_length
);
155 if (response
->data
== NULL
) {
156 vm_deallocate (mach_task_self (), (vm_address_t
) replyout
,
160 memcpy(response
->data
, replyout
, replyout_length
);
161 response
->length
= replyout_length
;
162 vm_deallocate (mach_task_self (), (vm_address_t
) replyout
,
165 response
->data
= malloc(replyin_length
);
166 if (response
->data
== NULL
)
168 memcpy(response
->data
, replyin
, replyin_length
);
169 response
->length
= replyin_length
;
175 struct async_client
{
177 dispatch_source_t source
;
178 dispatch_queue_t queue
;
179 void (*func
)(void *, int, heim_idata
*, heim_icred
);
184 mheim_ado_acall_reply(mach_port_t server_port
,
185 audit_token_t client_creds
,
187 heim_ipc_message_inband_t replyin
,
188 mach_msg_type_number_t replyinCnt
,
189 heim_ipc_message_outband_t replyout
,
190 mach_msg_type_number_t replyoutCnt
)
192 struct async_client
*c
= dispatch_get_context(dispatch_get_current_queue());
196 response
.data
= NULL
;
198 } else if (replyoutCnt
) {
199 response
.data
= replyout
;
200 response
.length
= replyoutCnt
;
202 response
.data
= replyin
;
203 response
.length
= replyinCnt
;
206 (*c
->func
)(c
->userctx
, returnvalue
, &response
, NULL
);
209 vm_deallocate (mach_task_self (), (vm_address_t
) replyout
, replyoutCnt
);
211 dispatch_source_cancel(c
->source
);
220 mach_async(void *ctx
, const heim_idata
*request
, void *userctx
,
221 void (*func
)(void *, int, heim_idata
*, heim_icred
))
223 struct mach_ctx
*ipc
= ctx
;
224 heim_ipc_message_inband_t requestin
;
225 mach_msg_type_number_t requestin_length
= 0;
226 heim_ipc_message_outband_t requestout
= NULL
;
227 mach_msg_type_number_t requestout_length
= 0;
228 int ret
, retries
= 0;
230 struct async_client
*c
;
232 /* first create the service that will catch the reply from the server */
233 /* XXX these object should be cached and reused */
235 c
= malloc(sizeof(*c
));
239 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &c
->mp
);
240 if (kr
!= KERN_SUCCESS
)
243 c
->queue
= dispatch_queue_create("heim-ipc-async-client", NULL
);
244 c
->source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, c
->mp
, 0, c
->queue
);
245 dispatch_set_context(c
->queue
, c
);
247 dispatch_source_set_event_handler(c
->source
, ^{
248 dispatch_mig_server(c
->source
,
249 sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem
),
253 dispatch_source_set_cancel_handler(c
->source
, ^{
254 mach_port_mod_refs(mach_task_self(), c
->mp
,
255 MACH_PORT_RIGHT_RECEIVE
, -1);
256 dispatch_release(c
->queue
);
257 dispatch_release(c
->source
);
262 c
->userctx
= userctx
;
264 dispatch_resume(c
->source
);
266 /* ok, send the message */
268 memcpy(requestin
, request
->data
, request
->length
);
269 requestin_length
= request
->length
;
271 while (retries
< 2) {
272 __block mach_port_t sport
;
274 dispatch_sync(syncq
, ^{ sport
= ipc
->server
; });
276 ret
= mheim_ipc_call_request(sport
, c
->mp
,
277 requestin
, requestin_length
,
278 requestout
, requestout_length
);
279 if (ret
== MACH_SEND_INVALID_DEST
) {
280 ret
= bootstrap_look_up(bootstrap_port
, ipc
->name
, &sport
);
282 dispatch_source_cancel(c
->source
);
285 mach_port_deallocate(mach_task_self(), ipc
->server
);
289 dispatch_source_cancel(c
->source
);
295 dispatch_source_cancel(c
->source
);
303 mach_release(void *ctx
)
305 struct mach_ctx
*ipc
= ctx
;
306 if (ipc
->server
!= MACH_PORT_NULL
)
307 mach_port_deallocate(mach_task_self(), ipc
->server
);
320 static int common_release(void *);
323 connect_unix(struct path_ctx
*s
)
325 struct sockaddr_un addr
;
327 addr
.sun_family
= AF_UNIX
;
328 strlcpy(addr
.sun_path
, s
->path
, sizeof(addr
.sun_path
));
330 s
->fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
335 if (connect(s
->fd
, (struct sockaddr
*)&addr
, sizeof(addr
)) != 0) {
344 common_path_init(const char *service
,
350 s
= malloc(sizeof(*s
));
355 asprintf(&s
->path
, "/var/run/.heim_%s-%s", service
, file
);
363 unix_socket_init(const char *service
,
368 ret
= common_path_init(service
, "socket", ctx
);
371 ret
= connect_unix(*ctx
);
373 common_release(*ctx
);
379 unix_socket_ipc(void *ctx
,
380 const heim_idata
*req
, heim_idata
*rep
,
383 struct path_ctx
*s
= ctx
;
384 uint32_t len
= htonl(req
->length
);
394 if (net_write(s
->fd
, &len
, sizeof(len
)) != sizeof(len
))
396 if (net_write(s
->fd
, req
->data
, req
->length
) != req
->length
)
399 if (net_read(s
->fd
, &len
, sizeof(len
)) != sizeof(len
))
401 if (net_read(s
->fd
, &rv
, sizeof(rv
)) != sizeof(rv
))
405 rep
->length
= ntohl(len
);
406 if (rep
->length
> 0) {
407 rep
->data
= malloc(rep
->length
);
408 if (rep
->data
== NULL
)
410 if (net_read(s
->fd
, rep
->data
, rep
->length
) != rep
->length
)
419 common_release(void *ctx
)
421 struct path_ctx
*s
= ctx
;
432 door_init(const char *service
,
435 ret
= common_path_init(context
, service
, "door", ctx
);
438 ret
= connect_door(*ctx
);
440 common_release(*ctx
);
446 const heim_idata
*request
, heim_idata
*response
,
452 arg
.data_ptr
= request
->data
;
453 arg
.data_size
= request
->length
;
459 ret
= door_call(fd
, &arg
);
464 response
->data
= malloc(arg
.rsize
);
465 if (response
->data
== NULL
) {
466 munmap(arg
.rbuf
, arg
.rsize
);
469 memcpy(response
->data
, arg
.rbuf
, arg
.rsize
);
470 response
->length
= arg
.rsize
;
471 munmap(arg
.rbuf
, arg
.rsize
);
480 int (*init
)(const char *, void **);
481 int (*release
)(void *);
482 int (*ipc
)(void *,const heim_idata
*, heim_idata
*, heim_icred
*);
483 int (*async
)(void *, const heim_idata
*, void *,
484 void (*)(void *, int, heim_idata
*, heim_icred
));
487 struct hipc_ops ipcs
[] = {
488 #if defined(__APPLE__) && defined(HAVE_GCD)
489 { "MACH", mach_init
, mach_release
, mach_ipc
, mach_async
},
492 { "DOOR", door_init
, common_release
, door_ipc
}
494 { "UNIX", unix_socket_init
, common_release
, unix_socket_ipc
}
498 struct hipc_ops
*ops
;
504 heim_ipc_init_context(const char *name
, heim_ipc
*ctx
)
509 for(i
= 0; i
< sizeof(ipcs
)/sizeof(ipcs
[0]); i
++) {
510 size_t prefix_len
= strlen(ipcs
[i
].prefix
);
512 if(strncmp(ipcs
[i
].prefix
, name
, prefix_len
) == 0
513 && name
[prefix_len
] == ':') {
514 } else if (strncmp("ANY:", name
, 4) == 0) {
520 c
= calloc(1, sizeof(*c
));
526 ret
= (c
->ops
->init
)(name
+ prefix_len
+ 1, &c
->ctx
);
542 heim_ipc_free_context(heim_ipc ctx
)
544 (ctx
->ops
->release
)(ctx
->ctx
);
549 heim_ipc_call(heim_ipc ctx
, const heim_idata
*send
, heim_idata
*recv
,
554 return (ctx
->ops
->ipc
)(ctx
->ctx
, send
, recv
, cred
);
558 heim_ipc_async(heim_ipc ctx
, const heim_idata
*send
, void *userctx
,
559 void (*func
)(void *, int, heim_idata
*, heim_icred
))
561 if (ctx
->ops
->async
== NULL
) {
563 heim_icred cred
= NULL
;
566 ret
= (ctx
->ops
->ipc
)(ctx
->ctx
, send
, &recv
, &cred
);
567 (*func
)(userctx
, ret
, &recv
, cred
);
568 heim_ipc_free_cred(cred
);
572 return (ctx
->ops
->async
)(ctx
->ctx
, send
, userctx
, func
);