611 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			611 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2009 Kungliga Tekniska Högskolan
 | 
						|
 * (Royal Institute of Technology, Stockholm, Sweden).
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 *
 | 
						|
 * 1. Redistributions of source code must retain the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer.
 | 
						|
 *
 | 
						|
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer in the
 | 
						|
 *    documentation and/or other materials provided with the distribution.
 | 
						|
 *
 | 
						|
 * 3. Neither the name of the Institute nor the names of its contributors
 | 
						|
 *    may be used to endorse or promote products derived from this software
 | 
						|
 *    without specific prior written permission.
 | 
						|
 *
 | 
						|
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 | 
						|
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
						|
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
						|
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 | 
						|
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
						|
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
						|
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
						|
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
						|
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
						|
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
						|
 * SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
#include "hi_locl.h"
 | 
						|
 | 
						|
#if defined(__APPLE__) && defined(HAVE_GCD)
 | 
						|
 | 
						|
#include "heim_ipc.h"
 | 
						|
#include "heim_ipc_asyncServer.h"
 | 
						|
 | 
						|
#include <dispatch/dispatch.h>
 | 
						|
#include <mach/mach.h>
 | 
						|
 | 
						|
static dispatch_once_t jobqinited = 0;
 | 
						|
static dispatch_queue_t jobq = NULL;
 | 
						|
static dispatch_queue_t syncq;
 | 
						|
 | 
						|
struct mach_ctx {
 | 
						|
    mach_port_t server;
 | 
						|
    char *name;
 | 
						|
};
 | 
						|
 | 
						|
static int
 | 
						|
mach_release(void *ctx);
 | 
						|
 | 
						|
static int
 | 
						|
mach_init(const char *service, void **ctx)
 | 
						|
{
 | 
						|
    struct mach_ctx *ipc;
 | 
						|
    mach_port_t sport;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    dispatch_once(&jobqinited, ^{
 | 
						|
	    jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 | 
						|
	    syncq = dispatch_queue_create("heim-ipc-syncq", NULL);
 | 
						|
	});
 | 
						|
 | 
						|
    ret = bootstrap_look_up(bootstrap_port, service, &sport);
 | 
						|
    if (ret)
 | 
						|
	return ret;
 | 
						|
 | 
						|
    ipc = malloc(sizeof(*ipc));
 | 
						|
    if (ipc == NULL) {
 | 
						|
	mach_port_destroy(mach_task_self(), sport);
 | 
						|
	return ENOMEM;
 | 
						|
    }
 | 
						|
 | 
						|
    ipc->server = sport;
 | 
						|
    ipc->name = strdup(service);
 | 
						|
    if (ipc->name == NULL) {
 | 
						|
	mach_release(ipc);
 | 
						|
	return ENOMEM;
 | 
						|
    }
 | 
						|
 | 
						|
    *ctx = ipc;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
mach_ipc(void *ctx,
 | 
						|
	 const heim_idata *request, heim_idata *response,
 | 
						|
	 heim_icred *cred)
 | 
						|
{
 | 
						|
    struct mach_ctx *ipc = ctx;
 | 
						|
    heim_ipc_message_inband_t requestin;
 | 
						|
    mach_msg_type_number_t requestin_length = 0;
 | 
						|
    heim_ipc_message_outband_t requestout = NULL;
 | 
						|
    mach_msg_type_number_t requestout_length = 0;
 | 
						|
    heim_ipc_message_inband_t replyin;
 | 
						|
    mach_msg_type_number_t replyin_length;
 | 
						|
    heim_ipc_message_outband_t replyout;
 | 
						|
    mach_msg_type_number_t replyout_length;
 | 
						|
    int ret, errorcode, retries = 0;
 | 
						|
 | 
						|
    memcpy(requestin, request->data, request->length);
 | 
						|
    requestin_length = request->length;
 | 
						|
 | 
						|
    while (retries < 2) {
 | 
						|
	__block mach_port_t sport;
 | 
						|
 | 
						|
	dispatch_sync(syncq, ^{ sport = ipc->server; });
 | 
						|
 | 
						|
	ret = mheim_ipc_call(sport,
 | 
						|
			     requestin, requestin_length,
 | 
						|
			     requestout, requestout_length,
 | 
						|
			     &errorcode,
 | 
						|
			     replyin, &replyin_length,
 | 
						|
			     &replyout, &replyout_length);
 | 
						|
	if (ret == MACH_SEND_INVALID_DEST) {
 | 
						|
	    mach_port_t nport;
 | 
						|
	    /* race other threads to get a new port */
 | 
						|
	    ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport);
 | 
						|
	    if (ret)
 | 
						|
		return ret;
 | 
						|
	    dispatch_sync(syncq, ^{
 | 
						|
		    /* check if we lost the race to lookup the port */
 | 
						|
		    if (sport != ipc->server) {
 | 
						|
			mach_port_deallocate(mach_task_self(), nport);
 | 
						|
		    } else {
 | 
						|
			mach_port_deallocate(mach_task_self(), ipc->server);
 | 
						|
			ipc->server = nport;
 | 
						|
		    }
 | 
						|
		});
 | 
						|
	    retries++;
 | 
						|
	} else if (ret) {
 | 
						|
	    return ret;
 | 
						|
	} else
 | 
						|
	    break;
 | 
						|
    }
 | 
						|
    if (retries >= 2)
 | 
						|
	return EINVAL;
 | 
						|
 | 
						|
    if (errorcode) {
 | 
						|
	if (replyout_length)
 | 
						|
	    vm_deallocate (mach_task_self (), (vm_address_t) replyout,
 | 
						|
			   replyout_length);
 | 
						|
	return errorcode;
 | 
						|
    }
 | 
						|
 | 
						|
    if (replyout_length) {
 | 
						|
	response->data = malloc(replyout_length);
 | 
						|
	if (response->data == NULL) {
 | 
						|
	    vm_deallocate (mach_task_self (), (vm_address_t) replyout,
 | 
						|
			   replyout_length);
 | 
						|
	    return ENOMEM;
 | 
						|
	}
 | 
						|
	memcpy(response->data, replyout, replyout_length);
 | 
						|
	response->length = replyout_length;
 | 
						|
	vm_deallocate (mach_task_self (), (vm_address_t) replyout,
 | 
						|
		       replyout_length);
 | 
						|
    } else {
 | 
						|
	response->data = malloc(replyin_length);
 | 
						|
	if (response->data == NULL)
 | 
						|
	    return ENOMEM;
 | 
						|
	memcpy(response->data, replyin, replyin_length);
 | 
						|
	response->length = replyin_length;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
struct async_client {
 | 
						|
    mach_port_t mp;
 | 
						|
    dispatch_source_t source;
 | 
						|
    dispatch_queue_t queue;
 | 
						|
    void (*func)(void *, int, heim_idata *, heim_icred);
 | 
						|
    void *userctx;
 | 
						|
};
 | 
						|
 | 
						|
kern_return_t
 | 
						|
mheim_ado_acall_reply(mach_port_t server_port,
 | 
						|
		      audit_token_t client_creds,
 | 
						|
		      int returnvalue,
 | 
						|
		      heim_ipc_message_inband_t replyin,
 | 
						|
		      mach_msg_type_number_t replyinCnt,
 | 
						|
		      heim_ipc_message_outband_t replyout,
 | 
						|
		      mach_msg_type_number_t replyoutCnt)
 | 
						|
{
 | 
						|
    struct async_client *c = dispatch_get_context(dispatch_get_current_queue());
 | 
						|
    heim_idata response;
 | 
						|
 | 
						|
    if (returnvalue) {
 | 
						|
	response.data = NULL;
 | 
						|
	response.length = 0;
 | 
						|
    } else if (replyoutCnt) {
 | 
						|
	response.data = replyout;
 | 
						|
	response.length = replyoutCnt;
 | 
						|
    } else {
 | 
						|
	response.data = replyin;
 | 
						|
	response.length = replyinCnt;
 | 
						|
    }
 | 
						|
 | 
						|
    (*c->func)(c->userctx, returnvalue, &response, NULL);
 | 
						|
 | 
						|
    if (replyoutCnt)
 | 
						|
	vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt);
 | 
						|
 | 
						|
    dispatch_source_cancel(c->source);
 | 
						|
 | 
						|
    return 0;
 | 
						|
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
mach_async(void *ctx, const heim_idata *request, void *userctx,
 | 
						|
	   void (*func)(void *, int, heim_idata *, heim_icred))
 | 
						|
{
 | 
						|
    struct mach_ctx *ipc = ctx;
 | 
						|
    heim_ipc_message_inband_t requestin;
 | 
						|
    mach_msg_type_number_t requestin_length = 0;
 | 
						|
    heim_ipc_message_outband_t requestout = NULL;
 | 
						|
    mach_msg_type_number_t requestout_length = 0;
 | 
						|
    int ret, retries = 0;
 | 
						|
    kern_return_t kr;
 | 
						|
    struct async_client *c;
 | 
						|
 | 
						|
    /* first create the service that will catch the reply from the server */
 | 
						|
    /* XXX these object should be cached and reused */
 | 
						|
 | 
						|
    c = malloc(sizeof(*c));
 | 
						|
    if (c == NULL)
 | 
						|
	return ENOMEM;
 | 
						|
 | 
						|
    kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp);
 | 
						|
    if (kr != KERN_SUCCESS)
 | 
						|
	return EINVAL;
 | 
						|
 | 
						|
    c->queue = dispatch_queue_create("heim-ipc-async-client", NULL);
 | 
						|
    c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue);
 | 
						|
    dispatch_set_context(c->queue, c);
 | 
						|
 | 
						|
    dispatch_source_set_event_handler(c->source, ^{
 | 
						|
	    dispatch_mig_server(c->source,
 | 
						|
				sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem),
 | 
						|
				mheim_aipc_server);
 | 
						|
	});
 | 
						|
 | 
						|
    dispatch_source_set_cancel_handler(c->source, ^{
 | 
						|
	    mach_port_mod_refs(mach_task_self(), c->mp,
 | 
						|
			       MACH_PORT_RIGHT_RECEIVE, -1);
 | 
						|
	    dispatch_release(c->queue);
 | 
						|
	    dispatch_release(c->source);
 | 
						|
	    free(c);
 | 
						|
	});
 | 
						|
 | 
						|
    c->func = func;
 | 
						|
    c->userctx = userctx;
 | 
						|
 | 
						|
    dispatch_resume(c->source);
 | 
						|
 | 
						|
    /* ok, send the message */
 | 
						|
 | 
						|
    memcpy(requestin, request->data, request->length);
 | 
						|
    requestin_length = request->length;
 | 
						|
 | 
						|
    while (retries < 2) {
 | 
						|
	__block mach_port_t sport;
 | 
						|
 | 
						|
	dispatch_sync(syncq, ^{ sport = ipc->server; });
 | 
						|
 | 
						|
	ret = mheim_ipc_call_request(sport, c->mp,
 | 
						|
				     requestin, requestin_length,
 | 
						|
				     requestout, requestout_length);
 | 
						|
	if (ret == MACH_SEND_INVALID_DEST) {
 | 
						|
	    ret = bootstrap_look_up(bootstrap_port, ipc->name, &sport);
 | 
						|
	    if (ret) {
 | 
						|
		dispatch_source_cancel(c->source);
 | 
						|
		return ret;
 | 
						|
	    }
 | 
						|
	    mach_port_deallocate(mach_task_self(), ipc->server);
 | 
						|
	    ipc->server = sport;
 | 
						|
	    retries++;
 | 
						|
	} else if (ret) {
 | 
						|
	    dispatch_source_cancel(c->source);
 | 
						|
	    return ret;
 | 
						|
	} else
 | 
						|
	    break;
 | 
						|
    }
 | 
						|
    if (retries >= 2) {
 | 
						|
	dispatch_source_cancel(c->source);
 | 
						|
	return EINVAL;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
mach_release(void *ctx)
 | 
						|
{
 | 
						|
    struct mach_ctx *ipc = ctx;
 | 
						|
    if (ipc->server != MACH_PORT_NULL)
 | 
						|
	mach_port_deallocate(mach_task_self(), ipc->server);
 | 
						|
    free(ipc->name);
 | 
						|
    free(ipc);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
struct path_ctx {
 | 
						|
    char *path;
 | 
						|
    int fd;
 | 
						|
};
 | 
						|
 | 
						|
static int common_release(void *);
 | 
						|
 | 
						|
static int
 | 
						|
connect_unix(struct path_ctx *s)
 | 
						|
{
 | 
						|
    struct sockaddr_un addr;
 | 
						|
 | 
						|
    addr.sun_family = AF_UNIX;
 | 
						|
    strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path));
 | 
						|
 | 
						|
    s->fd = socket(AF_UNIX, SOCK_STREAM, 0);
 | 
						|
    if (s->fd < 0)
 | 
						|
	return errno;
 | 
						|
    rk_cloexec(s->fd);
 | 
						|
 | 
						|
    if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
 | 
						|
	return errno;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
common_path_init(const char *base,
 | 
						|
                 const char *service,
 | 
						|
		 const char *file,
 | 
						|
		 void **ctx)
 | 
						|
{
 | 
						|
    struct path_ctx *s;
 | 
						|
 | 
						|
    s = malloc(sizeof(*s));
 | 
						|
    if (s == NULL)
 | 
						|
	return ENOMEM;
 | 
						|
    s->fd = -1;
 | 
						|
 | 
						|
    if (asprintf(&s->path, "%s/.heim_%s-%s", base, service, file) == -1) {
 | 
						|
	free(s);
 | 
						|
	return ENOMEM;
 | 
						|
    }
 | 
						|
 | 
						|
    *ctx = s;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
unix_socket_init(const char *service,
 | 
						|
		 void **ctx)
 | 
						|
{
 | 
						|
    const char *base = secure_getenv("HEIM_IPC_DIR");
 | 
						|
    int ret;
 | 
						|
 | 
						|
    ret = common_path_init(base ? base : _PATH_VARRUN, service, "socket", ctx);
 | 
						|
    if (ret)
 | 
						|
	return ret;
 | 
						|
    ret = connect_unix(*ctx);
 | 
						|
    if (ret)
 | 
						|
	common_release(*ctx);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
unix_socket_ipc(void *ctx,
 | 
						|
		const heim_idata *req, heim_idata *rep,
 | 
						|
		heim_icred *cred)
 | 
						|
{
 | 
						|
    struct path_ctx *s = ctx;
 | 
						|
    uint32_t len = htonl(req->length);
 | 
						|
    uint32_t rv;
 | 
						|
    int retval;
 | 
						|
 | 
						|
    if (cred)
 | 
						|
	*cred = NULL;
 | 
						|
 | 
						|
    rep->data = NULL;
 | 
						|
    rep->length = 0;
 | 
						|
 | 
						|
    if (net_write(s->fd, &len, sizeof(len)) != sizeof(len))
 | 
						|
	return -1;
 | 
						|
    if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length)
 | 
						|
	return -1;
 | 
						|
 | 
						|
    if (net_read(s->fd, &len, sizeof(len)) != sizeof(len))
 | 
						|
	return -1;
 | 
						|
    if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv))
 | 
						|
	return -1;
 | 
						|
    retval = ntohl(rv);
 | 
						|
 | 
						|
    rep->length = ntohl(len);
 | 
						|
    if (rep->length > 0) {
 | 
						|
	rep->data = malloc(rep->length);
 | 
						|
	if (rep->data == NULL)
 | 
						|
	    return -1;
 | 
						|
	if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length)
 | 
						|
	    return -1;
 | 
						|
    } else
 | 
						|
	rep->data = NULL;
 | 
						|
 | 
						|
    return retval;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
common_release(void *ctx)
 | 
						|
{
 | 
						|
    struct path_ctx *s = ctx;
 | 
						|
    if (s->fd >= 0)
 | 
						|
	close(s->fd);
 | 
						|
    free(s->path);
 | 
						|
    free(s);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef HAVE_DOOR_CREATE
 | 
						|
 | 
						|
#include <door.h>
 | 
						|
 | 
						|
#ifdef HAVE_SYS_MMAN_H
 | 
						|
#include <sys/mman.h>
 | 
						|
#endif
 | 
						|
 | 
						|
static int
 | 
						|
door_init(const char *service,
 | 
						|
	  void **ctx)
 | 
						|
{
 | 
						|
    const char *base = secure_getenv("HEIM_IPC_DIR");
 | 
						|
    int ret;
 | 
						|
    struct path_ctx *d;
 | 
						|
 | 
						|
    ret = common_path_init(base ? base : _PATH_VARRUN, service, "door", ctx);
 | 
						|
    if (ret)
 | 
						|
	return ret;
 | 
						|
 | 
						|
    d = (struct path_ctx *)*ctx;
 | 
						|
    d->fd = open(d->path, O_RDWR);
 | 
						|
    if (d->fd < 0) {
 | 
						|
        ret = errno;
 | 
						|
	common_release(*ctx);
 | 
						|
        return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
struct door_reply {
 | 
						|
    int returnvalue;
 | 
						|
    size_t length;
 | 
						|
    unsigned char data[1];
 | 
						|
};
 | 
						|
 | 
						|
static int
 | 
						|
door_ipc(void *ctx,
 | 
						|
	 const heim_idata *request, heim_idata *response,
 | 
						|
	 heim_icred *cred)
 | 
						|
{
 | 
						|
    struct path_ctx *d = (struct path_ctx *)ctx;
 | 
						|
    door_arg_t arg;
 | 
						|
    int ret;
 | 
						|
    struct door_reply *r;
 | 
						|
 | 
						|
    arg.data_ptr = request->data;
 | 
						|
    arg.data_size = request->length;
 | 
						|
    arg.desc_ptr = NULL;
 | 
						|
    arg.desc_num = 0;
 | 
						|
    arg.rbuf = NULL;
 | 
						|
    arg.rsize = 0;
 | 
						|
 | 
						|
    ret = door_call(d->fd, &arg);
 | 
						|
    if (ret != 0)
 | 
						|
	return errno;
 | 
						|
 | 
						|
    if (arg.rsize < offsetof(struct door_reply, data))
 | 
						|
        return EINVAL;
 | 
						|
 | 
						|
    r = (struct door_reply *)arg.rbuf;
 | 
						|
    if (r->returnvalue != 0)
 | 
						|
        return r->returnvalue;
 | 
						|
 | 
						|
    if (arg.rsize < offsetof(struct door_reply, data) + r->length)
 | 
						|
        return ERANGE;
 | 
						|
 | 
						|
    response->data = malloc(r->length);
 | 
						|
    if (response->data == NULL) {
 | 
						|
	munmap(arg.rbuf, arg.rsize);
 | 
						|
	return ENOMEM;
 | 
						|
    }
 | 
						|
 | 
						|
    memcpy(response->data, r->data, r->length);
 | 
						|
    response->length = r->length;
 | 
						|
    munmap(arg.rbuf, arg.rsize);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* HAVE_DOOR_CREATE */
 | 
						|
 | 
						|
struct hipc_ops {
 | 
						|
    const char *prefix;
 | 
						|
    int (*init)(const char *, void **);
 | 
						|
    int (*release)(void *);
 | 
						|
    int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *);
 | 
						|
    int (*async)(void *, const heim_idata *, void *,
 | 
						|
		 void (*)(void *, int, heim_idata *, heim_icred));
 | 
						|
};
 | 
						|
 | 
						|
static const struct hipc_ops ipcs[] = {
 | 
						|
#if defined(__APPLE__) && defined(HAVE_GCD)
 | 
						|
    { "MACH", mach_init, mach_release, mach_ipc, mach_async },
 | 
						|
#endif
 | 
						|
#ifdef HAVE_DOOR_CREATE
 | 
						|
    { "DOOR", door_init, common_release, door_ipc, NULL },
 | 
						|
#endif
 | 
						|
    { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL }
 | 
						|
};
 | 
						|
 | 
						|
struct heim_ipc {
 | 
						|
    const struct hipc_ops *ops;
 | 
						|
    void *ctx;
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
heim_ipc_init_context(const char *name, heim_ipc *ctx)
 | 
						|
{
 | 
						|
    unsigned int i;
 | 
						|
    int ret, any = 0;
 | 
						|
 | 
						|
    for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) {
 | 
						|
	size_t prefix_len = strlen(ipcs[i].prefix);
 | 
						|
	heim_ipc c;
 | 
						|
	if(strncmp(ipcs[i].prefix, name, prefix_len) == 0
 | 
						|
	   && name[prefix_len] == ':')  {
 | 
						|
	} else if (strncmp("ANY:", name, 4) == 0) {
 | 
						|
	    prefix_len = 3;
 | 
						|
	    any = 1;
 | 
						|
	} else
 | 
						|
	    continue;
 | 
						|
 | 
						|
	c = calloc(1, sizeof(*c));
 | 
						|
	if (c == NULL)
 | 
						|
	    return ENOMEM;
 | 
						|
 | 
						|
	c->ops = &ipcs[i];
 | 
						|
 | 
						|
	ret = (c->ops->init)(name + prefix_len + 1, &c->ctx);
 | 
						|
	if (ret) {
 | 
						|
	    free(c);
 | 
						|
	    if (any)
 | 
						|
		continue;
 | 
						|
	    return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	*ctx = c;
 | 
						|
	return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    return ENOENT;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
heim_ipc_free_context(heim_ipc ctx)
 | 
						|
{
 | 
						|
    (ctx->ops->release)(ctx->ctx);
 | 
						|
    free(ctx);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
heim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv,
 | 
						|
	      heim_icred *cred)
 | 
						|
{
 | 
						|
    if (cred)
 | 
						|
	*cred = NULL;
 | 
						|
    return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
heim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx,
 | 
						|
	       void (*func)(void *, int, heim_idata *, heim_icred))
 | 
						|
{
 | 
						|
    if (ctx->ops->async == NULL) {
 | 
						|
	heim_idata rcv;
 | 
						|
	heim_icred cred = NULL;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred);
 | 
						|
	(*func)(userctx, ret, &rcv, cred);
 | 
						|
	heim_ipc_free_cred(cred);
 | 
						|
	free(rcv.data);
 | 
						|
	return ret;
 | 
						|
    } else {
 | 
						|
	return (ctx->ops->async)(ctx->ctx, snd, userctx, func);
 | 
						|
    }
 | 
						|
}
 |