1 /* $NetBSD: client.c,v 1.1.1.2 2014/04/24 12:45:48 pettai Exp $ */
4 * Copyright (c) 2009 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
8 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the Institute nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #if defined(__APPLE__) && defined(HAVE_GCD)
43 #include "heim_ipc_asyncServer.h"
45 #include <dispatch/dispatch.h>
46 #include <mach/mach.h>
48 static dispatch_once_t jobqinited
= 0;
49 static dispatch_queue_t jobq
= NULL
;
50 static dispatch_queue_t syncq
;
58 mach_release(void *ctx
);
61 mach_init(const char *service
, void **ctx
)
67 dispatch_once(&jobqinited
, ^{
68 jobq
= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0);
69 syncq
= dispatch_queue_create("heim-ipc-syncq", NULL
);
72 ret
= bootstrap_look_up(bootstrap_port
, service
, &sport
);
76 ipc
= malloc(sizeof(*ipc
));
78 mach_port_destroy(mach_task_self(), sport
);
83 ipc
->name
= strdup(service
);
84 if (ipc
->name
== NULL
) {
96 const heim_idata
*request
, heim_idata
*response
,
99 struct mach_ctx
*ipc
= ctx
;
100 heim_ipc_message_inband_t requestin
;
101 mach_msg_type_number_t requestin_length
= 0;
102 heim_ipc_message_outband_t requestout
= NULL
;
103 mach_msg_type_number_t requestout_length
= 0;
104 heim_ipc_message_inband_t replyin
;
105 mach_msg_type_number_t replyin_length
;
106 heim_ipc_message_outband_t replyout
;
107 mach_msg_type_number_t replyout_length
;
108 int ret
, errorcode
, retries
= 0;
110 memcpy(requestin
, request
->data
, request
->length
);
111 requestin_length
= request
->length
;
113 while (retries
< 2) {
114 __block mach_port_t sport
;
116 dispatch_sync(syncq
, ^{ sport
= ipc
->server
; });
118 ret
= mheim_ipc_call(sport
,
119 requestin
, requestin_length
,
120 requestout
, requestout_length
,
122 replyin
, &replyin_length
,
123 &replyout
, &replyout_length
);
124 if (ret
== MACH_SEND_INVALID_DEST
) {
126 /* race other threads to get a new port */
127 ret
= bootstrap_look_up(bootstrap_port
, ipc
->name
, &nport
);
130 dispatch_sync(syncq
, ^{
131 /* check if we lost the race to lookup the port */
132 if (sport
!= ipc
->server
) {
133 mach_port_deallocate(mach_task_self(), nport
);
135 mach_port_deallocate(mach_task_self(), ipc
->server
);
150 vm_deallocate (mach_task_self (), (vm_address_t
) replyout
,
155 if (replyout_length
) {
156 response
->data
= malloc(replyout_length
);
157 if (response
->data
== NULL
) {
158 vm_deallocate (mach_task_self (), (vm_address_t
) replyout
,
162 memcpy(response
->data
, replyout
, replyout_length
);
163 response
->length
= replyout_length
;
164 vm_deallocate (mach_task_self (), (vm_address_t
) replyout
,
167 response
->data
= malloc(replyin_length
);
168 if (response
->data
== NULL
)
170 memcpy(response
->data
, replyin
, replyin_length
);
171 response
->length
= replyin_length
;
177 struct async_client
{
179 dispatch_source_t source
;
180 dispatch_queue_t queue
;
181 void (*func
)(void *, int, heim_idata
*, heim_icred
);
186 mheim_ado_acall_reply(mach_port_t server_port
,
187 audit_token_t client_creds
,
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 struct async_client
*c
= dispatch_get_context(dispatch_get_current_queue());
198 response
.data
= NULL
;
200 } else if (replyoutCnt
) {
201 response
.data
= replyout
;
202 response
.length
= replyoutCnt
;
204 response
.data
= replyin
;
205 response
.length
= replyinCnt
;
208 (*c
->func
)(c
->userctx
, returnvalue
, &response
, NULL
);
211 vm_deallocate (mach_task_self (), (vm_address_t
) replyout
, replyoutCnt
);
213 dispatch_source_cancel(c
->source
);
222 mach_async(void *ctx
, const heim_idata
*request
, void *userctx
,
223 void (*func
)(void *, int, heim_idata
*, heim_icred
))
225 struct mach_ctx
*ipc
= ctx
;
226 heim_ipc_message_inband_t requestin
;
227 mach_msg_type_number_t requestin_length
= 0;
228 heim_ipc_message_outband_t requestout
= NULL
;
229 mach_msg_type_number_t requestout_length
= 0;
230 int ret
, retries
= 0;
232 struct async_client
*c
;
234 /* first create the service that will catch the reply from the server */
235 /* XXX these object should be cached and reused */
237 c
= malloc(sizeof(*c
));
241 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &c
->mp
);
242 if (kr
!= KERN_SUCCESS
)
245 c
->queue
= dispatch_queue_create("heim-ipc-async-client", NULL
);
246 c
->source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, c
->mp
, 0, c
->queue
);
247 dispatch_set_context(c
->queue
, c
);
249 dispatch_source_set_event_handler(c
->source
, ^{
250 dispatch_mig_server(c
->source
,
251 sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem
),
255 dispatch_source_set_cancel_handler(c
->source
, ^{
256 mach_port_mod_refs(mach_task_self(), c
->mp
,
257 MACH_PORT_RIGHT_RECEIVE
, -1);
258 dispatch_release(c
->queue
);
259 dispatch_release(c
->source
);
264 c
->userctx
= userctx
;
266 dispatch_resume(c
->source
);
268 /* ok, send the message */
270 memcpy(requestin
, request
->data
, request
->length
);
271 requestin_length
= request
->length
;
273 while (retries
< 2) {
274 __block mach_port_t sport
;
276 dispatch_sync(syncq
, ^{ sport
= ipc
->server
; });
278 ret
= mheim_ipc_call_request(sport
, c
->mp
,
279 requestin
, requestin_length
,
280 requestout
, requestout_length
);
281 if (ret
== MACH_SEND_INVALID_DEST
) {
282 ret
= bootstrap_look_up(bootstrap_port
, ipc
->name
, &sport
);
284 dispatch_source_cancel(c
->source
);
287 mach_port_deallocate(mach_task_self(), ipc
->server
);
291 dispatch_source_cancel(c
->source
);
297 dispatch_source_cancel(c
->source
);
305 mach_release(void *ctx
)
307 struct mach_ctx
*ipc
= ctx
;
308 if (ipc
->server
!= MACH_PORT_NULL
)
309 mach_port_deallocate(mach_task_self(), ipc
->server
);
322 static int common_release(void *);
325 connect_unix(struct path_ctx
*s
)
327 struct sockaddr_un addr
;
329 addr
.sun_family
= AF_UNIX
;
330 strlcpy(addr
.sun_path
, s
->path
, sizeof(addr
.sun_path
));
332 s
->fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
337 if (connect(s
->fd
, (struct sockaddr
*)&addr
, sizeof(addr
)) != 0) {
346 common_path_init(const char *service
,
352 s
= malloc(sizeof(*s
));
357 asprintf(&s
->path
, "/var/run/.heim_%s-%s", service
, file
);
365 unix_socket_init(const char *service
,
370 ret
= common_path_init(service
, "socket", ctx
);
373 ret
= connect_unix(*ctx
);
375 common_release(*ctx
);
381 unix_socket_ipc(void *ctx
,
382 const heim_idata
*req
, heim_idata
*rep
,
385 struct path_ctx
*s
= ctx
;
386 uint32_t len
= htonl(req
->length
);
396 if (net_write(s
->fd
, &len
, sizeof(len
)) != sizeof(len
))
398 if (net_write(s
->fd
, req
->data
, req
->length
) != (ssize_t
)req
->length
)
401 if (net_read(s
->fd
, &len
, sizeof(len
)) != sizeof(len
))
403 if (net_read(s
->fd
, &rv
, sizeof(rv
)) != sizeof(rv
))
407 rep
->length
= ntohl(len
);
408 if (rep
->length
> 0) {
409 rep
->data
= malloc(rep
->length
);
410 if (rep
->data
== NULL
)
412 if (net_read(s
->fd
, rep
->data
, rep
->length
) != (ssize_t
)rep
->length
)
421 common_release(void *ctx
)
423 struct path_ctx
*s
= ctx
;
434 door_init(const char *service
,
437 ret
= common_path_init(context
, service
, "door", ctx
);
440 ret
= connect_door(*ctx
);
442 common_release(*ctx
);
448 const heim_idata
*request
, heim_idata
*response
,
454 arg
.data_ptr
= request
->data
;
455 arg
.data_size
= request
->length
;
461 ret
= door_call(fd
, &arg
);
466 response
->data
= malloc(arg
.rsize
);
467 if (response
->data
== NULL
) {
468 munmap(arg
.rbuf
, arg
.rsize
);
471 memcpy(response
->data
, arg
.rbuf
, arg
.rsize
);
472 response
->length
= arg
.rsize
;
473 munmap(arg
.rbuf
, arg
.rsize
);
482 int (*init
)(const char *, void **);
483 int (*release
)(void *);
484 int (*ipc
)(void *,const heim_idata
*, heim_idata
*, heim_icred
*);
485 int (*async
)(void *, const heim_idata
*, void *,
486 void (*)(void *, int, heim_idata
*, heim_icred
));
489 struct hipc_ops ipcs
[] = {
490 #if defined(__APPLE__) && defined(HAVE_GCD)
491 { "MACH", mach_init
, mach_release
, mach_ipc
, mach_async
},
494 { "DOOR", door_init
, common_release
, door_ipc
, NULL
}
496 { "UNIX", unix_socket_init
, common_release
, unix_socket_ipc
, NULL
}
500 struct hipc_ops
*ops
;
506 heim_ipc_init_context(const char *name
, heim_ipc
*ctx
)
511 for(i
= 0; i
< sizeof(ipcs
)/sizeof(ipcs
[0]); i
++) {
512 size_t prefix_len
= strlen(ipcs
[i
].prefix
);
514 if(strncmp(ipcs
[i
].prefix
, name
, prefix_len
) == 0
515 && name
[prefix_len
] == ':') {
516 } else if (strncmp("ANY:", name
, 4) == 0) {
522 c
= calloc(1, sizeof(*c
));
528 ret
= (c
->ops
->init
)(name
+ prefix_len
+ 1, &c
->ctx
);
544 heim_ipc_free_context(heim_ipc ctx
)
546 (ctx
->ops
->release
)(ctx
->ctx
);
551 heim_ipc_call(heim_ipc ctx
, const heim_idata
*snd
, heim_idata
*rcv
,
556 return (ctx
->ops
->ipc
)(ctx
->ctx
, snd
, rcv
, cred
);
560 heim_ipc_async(heim_ipc ctx
, const heim_idata
*snd
, void *userctx
,
561 void (*func
)(void *, int, heim_idata
*, heim_icred
))
563 if (ctx
->ops
->async
== NULL
) {
565 heim_icred cred
= NULL
;
568 ret
= (ctx
->ops
->ipc
)(ctx
->ctx
, snd
, &rcv
, &cred
);
569 (*func
)(userctx
, ret
, &rcv
, cred
);
570 heim_ipc_free_cred(cred
);
574 return (ctx
->ops
->async
)(ctx
->ctx
, snd
, userctx
, func
);