etc/services - sync with NetBSD-8
[minix.git] / crypto / external / bsd / heimdal / dist / lib / ipc / client.c
blobf09722e51c308bec1cb2ecb8faf6efdeddf5ecd0
1 /* $NetBSD: client.c,v 1.1.1.2 2014/04/24 12:45:48 pettai Exp $ */
3 /*
4 * Copyright (c) 2009 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
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
12 * are met:
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
35 * SUCH DAMAGE.
38 #include "hi_locl.h"
40 #if defined(__APPLE__) && defined(HAVE_GCD)
42 #include "heim_ipc.h"
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;
52 struct mach_ctx {
53 mach_port_t server;
54 char *name;
57 static int
58 mach_release(void *ctx);
60 static int
61 mach_init(const char *service, void **ctx)
63 struct mach_ctx *ipc;
64 mach_port_t sport;
65 int ret;
67 dispatch_once(&jobqinited, ^{
68 jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
69 syncq = dispatch_queue_create("heim-ipc-syncq", NULL);
70 });
72 ret = bootstrap_look_up(bootstrap_port, service, &sport);
73 if (ret)
74 return ret;
76 ipc = malloc(sizeof(*ipc));
77 if (ipc == NULL) {
78 mach_port_destroy(mach_task_self(), sport);
79 return ENOMEM;
82 ipc->server = sport;
83 ipc->name = strdup(service);
84 if (ipc->name == NULL) {
85 mach_release(ipc);
86 return ENOMEM;
89 *ctx = ipc;
91 return 0;
94 static int
95 mach_ipc(void *ctx,
96 const heim_idata *request, heim_idata *response,
97 heim_icred *cred)
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,
121 &errorcode,
122 replyin, &replyin_length,
123 &replyout, &replyout_length);
124 if (ret == MACH_SEND_INVALID_DEST) {
125 mach_port_t nport;
126 /* race other threads to get a new port */
127 ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport);
128 if (ret)
129 return ret;
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);
134 } else {
135 mach_port_deallocate(mach_task_self(), ipc->server);
136 ipc->server = nport;
139 retries++;
140 } else if (ret) {
141 return ret;
142 } else
143 break;
145 if (retries >= 2)
146 return EINVAL;
148 if (errorcode) {
149 if (replyout_length)
150 vm_deallocate (mach_task_self (), (vm_address_t) replyout,
151 replyout_length);
152 return errorcode;
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,
159 replyout_length);
160 return ENOMEM;
162 memcpy(response->data, replyout, replyout_length);
163 response->length = replyout_length;
164 vm_deallocate (mach_task_self (), (vm_address_t) replyout,
165 replyout_length);
166 } else {
167 response->data = malloc(replyin_length);
168 if (response->data == NULL)
169 return ENOMEM;
170 memcpy(response->data, replyin, replyin_length);
171 response->length = replyin_length;
174 return 0;
177 struct async_client {
178 mach_port_t mp;
179 dispatch_source_t source;
180 dispatch_queue_t queue;
181 void (*func)(void *, int, heim_idata *, heim_icred);
182 void *userctx;
185 kern_return_t
186 mheim_ado_acall_reply(mach_port_t server_port,
187 audit_token_t client_creds,
188 int returnvalue,
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());
195 heim_idata response;
197 if (returnvalue) {
198 response.data = NULL;
199 response.length = 0;
200 } else if (replyoutCnt) {
201 response.data = replyout;
202 response.length = replyoutCnt;
203 } else {
204 response.data = replyin;
205 response.length = replyinCnt;
208 (*c->func)(c->userctx, returnvalue, &response, NULL);
210 if (replyoutCnt)
211 vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt);
213 dispatch_source_cancel(c->source);
215 return 0;
221 static int
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;
231 kern_return_t kr;
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));
238 if (c == NULL)
239 return ENOMEM;
241 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp);
242 if (kr != KERN_SUCCESS)
243 return EINVAL;
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),
252 mheim_aipc_server);
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);
260 free(c);
263 c->func = func;
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);
283 if (ret) {
284 dispatch_source_cancel(c->source);
285 return ret;
287 mach_port_deallocate(mach_task_self(), ipc->server);
288 ipc->server = sport;
289 retries++;
290 } else if (ret) {
291 dispatch_source_cancel(c->source);
292 return ret;
293 } else
294 break;
296 if (retries >= 2) {
297 dispatch_source_cancel(c->source);
298 return EINVAL;
301 return 0;
304 static int
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);
310 free(ipc->name);
311 free(ipc);
312 return 0;
315 #endif
317 struct path_ctx {
318 char *path;
319 int fd;
322 static int common_release(void *);
324 static int
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);
333 if (s->fd < 0)
334 return errno;
335 rk_cloexec(s->fd);
337 if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
338 close(s->fd);
339 return errno;
342 return 0;
345 static int
346 common_path_init(const char *service,
347 const char *file,
348 void **ctx)
350 struct path_ctx *s;
352 s = malloc(sizeof(*s));
353 if (s == NULL)
354 return ENOMEM;
355 s->fd = -1;
357 asprintf(&s->path, "/var/run/.heim_%s-%s", service, file);
359 *ctx = s;
361 return 0;
364 static int
365 unix_socket_init(const char *service,
366 void **ctx)
368 int ret;
370 ret = common_path_init(service, "socket", ctx);
371 if (ret)
372 return ret;
373 ret = connect_unix(*ctx);
374 if (ret)
375 common_release(*ctx);
377 return ret;
380 static int
381 unix_socket_ipc(void *ctx,
382 const heim_idata *req, heim_idata *rep,
383 heim_icred *cred)
385 struct path_ctx *s = ctx;
386 uint32_t len = htonl(req->length);
387 uint32_t rv;
388 int retval;
390 if (cred)
391 *cred = NULL;
393 rep->data = NULL;
394 rep->length = 0;
396 if (net_write(s->fd, &len, sizeof(len)) != sizeof(len))
397 return -1;
398 if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length)
399 return -1;
401 if (net_read(s->fd, &len, sizeof(len)) != sizeof(len))
402 return -1;
403 if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv))
404 return -1;
405 retval = ntohl(rv);
407 rep->length = ntohl(len);
408 if (rep->length > 0) {
409 rep->data = malloc(rep->length);
410 if (rep->data == NULL)
411 return -1;
412 if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length)
413 return -1;
414 } else
415 rep->data = NULL;
417 return retval;
421 common_release(void *ctx)
423 struct path_ctx *s = ctx;
424 if (s->fd >= 0)
425 close(s->fd);
426 free(s->path);
427 free(s);
428 return 0;
431 #ifdef HAVE_DOOR
433 static int
434 door_init(const char *service,
435 void **ctx)
437 ret = common_path_init(context, service, "door", ctx);
438 if (ret)
439 return ret;
440 ret = connect_door(*ctx);
441 if (ret)
442 common_release(*ctx);
443 return ret;
446 static int
447 door_ipc(void *ctx,
448 const heim_idata *request, heim_idata *response,
449 heim_icred *cred)
451 door_arg_t arg;
452 int ret;
454 arg.data_ptr = request->data;
455 arg.data_size = request->length;
456 arg.desc_ptr = NULL;
457 arg.desc_num = 0;
458 arg.rbuf = NULL;
459 arg.rsize = 0;
461 ret = door_call(fd, &arg);
462 close(fd);
463 if (ret != 0)
464 return errno;
466 response->data = malloc(arg.rsize);
467 if (response->data == NULL) {
468 munmap(arg.rbuf, arg.rsize);
469 return ENOMEM;
471 memcpy(response->data, arg.rbuf, arg.rsize);
472 response->length = arg.rsize;
473 munmap(arg.rbuf, arg.rsize);
475 return ret;
478 #endif
480 struct hipc_ops {
481 const char *prefix;
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 },
492 #endif
493 #ifdef HAVE_DOOR
494 { "DOOR", door_init, common_release, door_ipc, NULL }
495 #endif
496 { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL }
499 struct heim_ipc {
500 struct hipc_ops *ops;
501 void *ctx;
506 heim_ipc_init_context(const char *name, heim_ipc *ctx)
508 unsigned int i;
509 int ret, any = 0;
511 for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) {
512 size_t prefix_len = strlen(ipcs[i].prefix);
513 heim_ipc c;
514 if(strncmp(ipcs[i].prefix, name, prefix_len) == 0
515 && name[prefix_len] == ':') {
516 } else if (strncmp("ANY:", name, 4) == 0) {
517 prefix_len = 3;
518 any = 1;
519 } else
520 continue;
522 c = calloc(1, sizeof(*c));
523 if (c == NULL)
524 return ENOMEM;
526 c->ops = &ipcs[i];
528 ret = (c->ops->init)(name + prefix_len + 1, &c->ctx);
529 if (ret) {
530 free(c);
531 if (any)
532 continue;
533 return ret;
536 *ctx = c;
537 return 0;
540 return ENOENT;
543 void
544 heim_ipc_free_context(heim_ipc ctx)
546 (ctx->ops->release)(ctx->ctx);
547 free(ctx);
551 heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv,
552 heim_icred *cred)
554 if (cred)
555 *cred = NULL;
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) {
564 heim_idata rcv;
565 heim_icred cred = NULL;
566 int ret;
568 ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred);
569 (*func)(userctx, ret, &rcv, cred);
570 heim_ipc_free_cred(cred);
571 free(rcv.data);
572 return ret;
573 } else {
574 return (ctx->ops->async)(ctx->ctx, snd, userctx, func);