1226031Sstas/* 2226031Sstas * Copyright (c) 2009 Kungliga Tekniska H�gskolan 3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4226031Sstas * All rights reserved. 5226031Sstas * 6226031Sstas * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 7226031Sstas * 8226031Sstas * Redistribution and use in source and binary forms, with or without 9226031Sstas * modification, are permitted provided that the following conditions 10226031Sstas * are met: 11226031Sstas * 12226031Sstas * 1. Redistributions of source code must retain the above copyright 13226031Sstas * notice, this list of conditions and the following disclaimer. 14226031Sstas * 15226031Sstas * 2. Redistributions in binary form must reproduce the above copyright 16226031Sstas * notice, this list of conditions and the following disclaimer in the 17226031Sstas * documentation and/or other materials provided with the distribution. 18226031Sstas * 19226031Sstas * 3. Neither the name of the Institute nor the names of its contributors 20226031Sstas * may be used to endorse or promote products derived from this software 21226031Sstas * without specific prior written permission. 22226031Sstas * 23226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26226031Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33226031Sstas * SUCH DAMAGE. 34226031Sstas */ 35226031Sstas 36226031Sstas#include "hi_locl.h" 37226031Sstas 38226031Sstas#if defined(__APPLE__) && defined(HAVE_GCD) 39226031Sstas 40226031Sstas#include "heim_ipc.h" 41226031Sstas#include "heim_ipc_asyncServer.h" 42226031Sstas 43226031Sstas#include <dispatch/dispatch.h> 44226031Sstas#include <mach/mach.h> 45226031Sstas 46226031Sstasstatic dispatch_once_t jobqinited = 0; 47226031Sstasstatic dispatch_queue_t jobq = NULL; 48226031Sstasstatic dispatch_queue_t syncq; 49226031Sstas 50226031Sstasstruct mach_ctx { 51226031Sstas mach_port_t server; 52226031Sstas char *name; 53226031Sstas}; 54226031Sstas 55226031Sstasstatic int 56226031Sstasmach_release(void *ctx); 57226031Sstas 58226031Sstasstatic int 59226031Sstasmach_init(const char *service, void **ctx) 60226031Sstas{ 61226031Sstas struct mach_ctx *ipc; 62226031Sstas mach_port_t sport; 63226031Sstas int ret; 64226031Sstas 65226031Sstas dispatch_once(&jobqinited, ^{ 66226031Sstas jobq = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 67226031Sstas syncq = dispatch_queue_create("heim-ipc-syncq", NULL); 68226031Sstas }); 69226031Sstas 70226031Sstas ret = bootstrap_look_up(bootstrap_port, service, &sport); 71226031Sstas if (ret) 72226031Sstas return ret; 73226031Sstas 74226031Sstas ipc = malloc(sizeof(*ipc)); 75226031Sstas if (ipc == NULL) { 76226031Sstas mach_port_destroy(mach_task_self(), sport); 77226031Sstas return ENOMEM; 78226031Sstas } 79226031Sstas 80226031Sstas ipc->server = sport; 81226031Sstas ipc->name = strdup(service); 82226031Sstas if (ipc->name == NULL) { 83226031Sstas mach_release(ipc); 84226031Sstas return ENOMEM; 85226031Sstas } 86226031Sstas 87226031Sstas *ctx = ipc; 88226031Sstas 89226031Sstas return 0; 90226031Sstas} 91226031Sstas 92226031Sstasstatic int 93226031Sstasmach_ipc(void *ctx, 94226031Sstas const heim_idata *request, heim_idata *response, 95226031Sstas heim_icred *cred) 96226031Sstas{ 97226031Sstas struct mach_ctx *ipc = ctx; 98226031Sstas heim_ipc_message_inband_t requestin; 99226031Sstas mach_msg_type_number_t requestin_length = 0; 100226031Sstas heim_ipc_message_outband_t requestout = NULL; 101226031Sstas mach_msg_type_number_t requestout_length = 0; 102226031Sstas heim_ipc_message_inband_t replyin; 103226031Sstas mach_msg_type_number_t replyin_length; 104226031Sstas heim_ipc_message_outband_t replyout; 105226031Sstas mach_msg_type_number_t replyout_length; 106226031Sstas int ret, errorcode, retries = 0; 107226031Sstas 108226031Sstas memcpy(requestin, request->data, request->length); 109226031Sstas requestin_length = request->length; 110226031Sstas 111226031Sstas while (retries < 2) { 112226031Sstas __block mach_port_t sport; 113226031Sstas 114226031Sstas dispatch_sync(syncq, ^{ sport = ipc->server; }); 115226031Sstas 116226031Sstas ret = mheim_ipc_call(sport, 117226031Sstas requestin, requestin_length, 118226031Sstas requestout, requestout_length, 119226031Sstas &errorcode, 120226031Sstas replyin, &replyin_length, 121226031Sstas &replyout, &replyout_length); 122226031Sstas if (ret == MACH_SEND_INVALID_DEST) { 123226031Sstas mach_port_t nport; 124226031Sstas /* race other threads to get a new port */ 125226031Sstas ret = bootstrap_look_up(bootstrap_port, ipc->name, &nport); 126226031Sstas if (ret) 127226031Sstas return ret; 128226031Sstas dispatch_sync(syncq, ^{ 129226031Sstas /* check if we lost the race to lookup the port */ 130226031Sstas if (sport != ipc->server) { 131226031Sstas mach_port_deallocate(mach_task_self(), nport); 132226031Sstas } else { 133226031Sstas mach_port_deallocate(mach_task_self(), ipc->server); 134226031Sstas ipc->server = nport; 135226031Sstas } 136226031Sstas }); 137226031Sstas retries++; 138226031Sstas } else if (ret) { 139226031Sstas return ret; 140226031Sstas } else 141226031Sstas break; 142226031Sstas } 143226031Sstas if (retries >= 2) 144226031Sstas return EINVAL; 145226031Sstas 146226031Sstas if (errorcode) { 147226031Sstas if (replyout_length) 148226031Sstas vm_deallocate (mach_task_self (), (vm_address_t) replyout, 149226031Sstas replyout_length); 150226031Sstas return errorcode; 151226031Sstas } 152226031Sstas 153226031Sstas if (replyout_length) { 154226031Sstas response->data = malloc(replyout_length); 155226031Sstas if (response->data == NULL) { 156226031Sstas vm_deallocate (mach_task_self (), (vm_address_t) replyout, 157226031Sstas replyout_length); 158226031Sstas return ENOMEM; 159226031Sstas } 160226031Sstas memcpy(response->data, replyout, replyout_length); 161226031Sstas response->length = replyout_length; 162226031Sstas vm_deallocate (mach_task_self (), (vm_address_t) replyout, 163226031Sstas replyout_length); 164226031Sstas } else { 165226031Sstas response->data = malloc(replyin_length); 166226031Sstas if (response->data == NULL) 167226031Sstas return ENOMEM; 168226031Sstas memcpy(response->data, replyin, replyin_length); 169226031Sstas response->length = replyin_length; 170226031Sstas } 171226031Sstas 172226031Sstas return 0; 173226031Sstas} 174226031Sstas 175226031Sstasstruct async_client { 176226031Sstas mach_port_t mp; 177226031Sstas dispatch_source_t source; 178226031Sstas dispatch_queue_t queue; 179226031Sstas void (*func)(void *, int, heim_idata *, heim_icred); 180226031Sstas void *userctx; 181226031Sstas}; 182226031Sstas 183226031Sstaskern_return_t 184226031Sstasmheim_ado_acall_reply(mach_port_t server_port, 185226031Sstas audit_token_t client_creds, 186226031Sstas int returnvalue, 187226031Sstas heim_ipc_message_inband_t replyin, 188226031Sstas mach_msg_type_number_t replyinCnt, 189226031Sstas heim_ipc_message_outband_t replyout, 190226031Sstas mach_msg_type_number_t replyoutCnt) 191226031Sstas{ 192226031Sstas struct async_client *c = dispatch_get_context(dispatch_get_current_queue()); 193226031Sstas heim_idata response; 194226031Sstas 195226031Sstas if (returnvalue) { 196226031Sstas response.data = NULL; 197226031Sstas response.length = 0; 198226031Sstas } else if (replyoutCnt) { 199226031Sstas response.data = replyout; 200226031Sstas response.length = replyoutCnt; 201226031Sstas } else { 202226031Sstas response.data = replyin; 203226031Sstas response.length = replyinCnt; 204226031Sstas } 205226031Sstas 206226031Sstas (*c->func)(c->userctx, returnvalue, &response, NULL); 207226031Sstas 208226031Sstas if (replyoutCnt) 209226031Sstas vm_deallocate (mach_task_self (), (vm_address_t) replyout, replyoutCnt); 210226031Sstas 211226031Sstas dispatch_source_cancel(c->source); 212226031Sstas 213226031Sstas return 0; 214226031Sstas 215226031Sstas 216226031Sstas} 217226031Sstas 218226031Sstas 219226031Sstasstatic int 220226031Sstasmach_async(void *ctx, const heim_idata *request, void *userctx, 221226031Sstas void (*func)(void *, int, heim_idata *, heim_icred)) 222226031Sstas{ 223226031Sstas struct mach_ctx *ipc = ctx; 224226031Sstas heim_ipc_message_inband_t requestin; 225226031Sstas mach_msg_type_number_t requestin_length = 0; 226226031Sstas heim_ipc_message_outband_t requestout = NULL; 227226031Sstas mach_msg_type_number_t requestout_length = 0; 228226031Sstas int ret, retries = 0; 229226031Sstas kern_return_t kr; 230226031Sstas struct async_client *c; 231226031Sstas 232226031Sstas /* first create the service that will catch the reply from the server */ 233226031Sstas /* XXX these object should be cached and reused */ 234226031Sstas 235226031Sstas c = malloc(sizeof(*c)); 236226031Sstas if (c == NULL) 237226031Sstas return ENOMEM; 238226031Sstas 239226031Sstas kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &c->mp); 240226031Sstas if (kr != KERN_SUCCESS) 241226031Sstas return EINVAL; 242226031Sstas 243226031Sstas c->queue = dispatch_queue_create("heim-ipc-async-client", NULL); 244226031Sstas c->source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, c->mp, 0, c->queue); 245226031Sstas dispatch_set_context(c->queue, c); 246226031Sstas 247226031Sstas dispatch_source_set_event_handler(c->source, ^{ 248226031Sstas dispatch_mig_server(c->source, 249226031Sstas sizeof(union __RequestUnion__mheim_ado_mheim_aipc_subsystem), 250226031Sstas mheim_aipc_server); 251226031Sstas }); 252226031Sstas 253226031Sstas dispatch_source_set_cancel_handler(c->source, ^{ 254226031Sstas mach_port_mod_refs(mach_task_self(), c->mp, 255226031Sstas MACH_PORT_RIGHT_RECEIVE, -1); 256226031Sstas dispatch_release(c->queue); 257226031Sstas dispatch_release(c->source); 258226031Sstas free(c); 259226031Sstas }); 260226031Sstas 261226031Sstas c->func = func; 262226031Sstas c->userctx = userctx; 263226031Sstas 264226031Sstas dispatch_resume(c->source); 265226031Sstas 266226031Sstas /* ok, send the message */ 267226031Sstas 268226031Sstas memcpy(requestin, request->data, request->length); 269226031Sstas requestin_length = request->length; 270226031Sstas 271226031Sstas while (retries < 2) { 272226031Sstas __block mach_port_t sport; 273226031Sstas 274226031Sstas dispatch_sync(syncq, ^{ sport = ipc->server; }); 275226031Sstas 276226031Sstas ret = mheim_ipc_call_request(sport, c->mp, 277226031Sstas requestin, requestin_length, 278226031Sstas requestout, requestout_length); 279226031Sstas if (ret == MACH_SEND_INVALID_DEST) { 280226031Sstas ret = bootstrap_look_up(bootstrap_port, ipc->name, &sport); 281226031Sstas if (ret) { 282226031Sstas dispatch_source_cancel(c->source); 283226031Sstas return ret; 284226031Sstas } 285226031Sstas mach_port_deallocate(mach_task_self(), ipc->server); 286226031Sstas ipc->server = sport; 287226031Sstas retries++; 288226031Sstas } else if (ret) { 289226031Sstas dispatch_source_cancel(c->source); 290226031Sstas return ret; 291226031Sstas } else 292226031Sstas break; 293226031Sstas } 294226031Sstas if (retries >= 2) { 295226031Sstas dispatch_source_cancel(c->source); 296226031Sstas return EINVAL; 297226031Sstas } 298226031Sstas 299226031Sstas return 0; 300226031Sstas} 301226031Sstas 302226031Sstasstatic int 303226031Sstasmach_release(void *ctx) 304226031Sstas{ 305226031Sstas struct mach_ctx *ipc = ctx; 306226031Sstas if (ipc->server != MACH_PORT_NULL) 307226031Sstas mach_port_deallocate(mach_task_self(), ipc->server); 308226031Sstas free(ipc->name); 309226031Sstas free(ipc); 310226031Sstas return 0; 311226031Sstas} 312226031Sstas 313226031Sstas#endif 314226031Sstas 315226031Sstasstruct path_ctx { 316226031Sstas char *path; 317226031Sstas int fd; 318226031Sstas}; 319226031Sstas 320226031Sstasstatic int common_release(void *); 321226031Sstas 322226031Sstasstatic int 323226031Sstasconnect_unix(struct path_ctx *s) 324226031Sstas{ 325226031Sstas struct sockaddr_un addr; 326226031Sstas 327226031Sstas addr.sun_family = AF_UNIX; 328226031Sstas strlcpy(addr.sun_path, s->path, sizeof(addr.sun_path)); 329226031Sstas 330226031Sstas s->fd = socket(AF_UNIX, SOCK_STREAM, 0); 331226031Sstas if (s->fd < 0) 332226031Sstas return errno; 333226031Sstas rk_cloexec(s->fd); 334226031Sstas 335226031Sstas if (connect(s->fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { 336226031Sstas close(s->fd); 337226031Sstas return errno; 338226031Sstas } 339226031Sstas 340226031Sstas return 0; 341226031Sstas} 342226031Sstas 343226031Sstasstatic int 344226031Sstascommon_path_init(const char *service, 345226031Sstas const char *file, 346226031Sstas void **ctx) 347226031Sstas{ 348226031Sstas struct path_ctx *s; 349226031Sstas 350226031Sstas s = malloc(sizeof(*s)); 351226031Sstas if (s == NULL) 352226031Sstas return ENOMEM; 353226031Sstas s->fd = -1; 354226031Sstas 355226031Sstas asprintf(&s->path, "/var/run/.heim_%s-%s", service, file); 356226031Sstas 357226031Sstas *ctx = s; 358226031Sstas 359226031Sstas return 0; 360226031Sstas} 361226031Sstas 362226031Sstasstatic int 363226031Sstasunix_socket_init(const char *service, 364226031Sstas void **ctx) 365226031Sstas{ 366226031Sstas int ret; 367226031Sstas 368226031Sstas ret = common_path_init(service, "socket", ctx); 369226031Sstas if (ret) 370226031Sstas return ret; 371226031Sstas ret = connect_unix(*ctx); 372226031Sstas if (ret) 373226031Sstas common_release(*ctx); 374226031Sstas 375226031Sstas return ret; 376226031Sstas} 377226031Sstas 378226031Sstasstatic int 379226031Sstasunix_socket_ipc(void *ctx, 380226031Sstas const heim_idata *req, heim_idata *rep, 381226031Sstas heim_icred *cred) 382226031Sstas{ 383226031Sstas struct path_ctx *s = ctx; 384226031Sstas uint32_t len = htonl(req->length); 385226031Sstas uint32_t rv; 386226031Sstas int retval; 387226031Sstas 388226031Sstas if (cred) 389226031Sstas *cred = NULL; 390226031Sstas 391226031Sstas rep->data = NULL; 392226031Sstas rep->length = 0; 393226031Sstas 394226031Sstas if (net_write(s->fd, &len, sizeof(len)) != sizeof(len)) 395226031Sstas return -1; 396226031Sstas if (net_write(s->fd, req->data, req->length) != (ssize_t)req->length) 397226031Sstas return -1; 398226031Sstas 399226031Sstas if (net_read(s->fd, &len, sizeof(len)) != sizeof(len)) 400226031Sstas return -1; 401226031Sstas if (net_read(s->fd, &rv, sizeof(rv)) != sizeof(rv)) 402226031Sstas return -1; 403226031Sstas retval = ntohl(rv); 404226031Sstas 405226031Sstas rep->length = ntohl(len); 406226031Sstas if (rep->length > 0) { 407226031Sstas rep->data = malloc(rep->length); 408226031Sstas if (rep->data == NULL) 409226031Sstas return -1; 410226031Sstas if (net_read(s->fd, rep->data, rep->length) != (ssize_t)rep->length) 411226031Sstas return -1; 412226031Sstas } else 413226031Sstas rep->data = NULL; 414226031Sstas 415226031Sstas return retval; 416226031Sstas} 417226031Sstas 418226031Sstasint 419226031Sstascommon_release(void *ctx) 420226031Sstas{ 421226031Sstas struct path_ctx *s = ctx; 422226031Sstas if (s->fd >= 0) 423226031Sstas close(s->fd); 424226031Sstas free(s->path); 425226031Sstas free(s); 426226031Sstas return 0; 427226031Sstas} 428226031Sstas 429226031Sstas#ifdef HAVE_DOOR 430226031Sstas 431226031Sstasstatic int 432226031Sstasdoor_init(const char *service, 433226031Sstas void **ctx) 434226031Sstas{ 435226031Sstas ret = common_path_init(context, service, "door", ctx); 436226031Sstas if (ret) 437226031Sstas return ret; 438226031Sstas ret = connect_door(*ctx); 439226031Sstas if (ret) 440226031Sstas common_release(*ctx); 441226031Sstas return ret; 442226031Sstas} 443226031Sstas 444226031Sstasstatic int 445226031Sstasdoor_ipc(void *ctx, 446226031Sstas const heim_idata *request, heim_idata *response, 447226031Sstas heim_icred *cred) 448226031Sstas{ 449226031Sstas door_arg_t arg; 450226031Sstas int ret; 451226031Sstas 452226031Sstas arg.data_ptr = request->data; 453226031Sstas arg.data_size = request->length; 454226031Sstas arg.desc_ptr = NULL; 455226031Sstas arg.desc_num = 0; 456226031Sstas arg.rbuf = NULL; 457226031Sstas arg.rsize = 0; 458226031Sstas 459226031Sstas ret = door_call(fd, &arg); 460226031Sstas close(fd); 461226031Sstas if (ret != 0) 462226031Sstas return errno; 463226031Sstas 464226031Sstas response->data = malloc(arg.rsize); 465226031Sstas if (response->data == NULL) { 466226031Sstas munmap(arg.rbuf, arg.rsize); 467226031Sstas return ENOMEM; 468226031Sstas } 469226031Sstas memcpy(response->data, arg.rbuf, arg.rsize); 470226031Sstas response->length = arg.rsize; 471226031Sstas munmap(arg.rbuf, arg.rsize); 472226031Sstas 473226031Sstas return ret; 474226031Sstas} 475226031Sstas 476226031Sstas#endif 477226031Sstas 478226031Sstasstruct hipc_ops { 479226031Sstas const char *prefix; 480226031Sstas int (*init)(const char *, void **); 481226031Sstas int (*release)(void *); 482226031Sstas int (*ipc)(void *,const heim_idata *, heim_idata *, heim_icred *); 483226031Sstas int (*async)(void *, const heim_idata *, void *, 484226031Sstas void (*)(void *, int, heim_idata *, heim_icred)); 485226031Sstas}; 486226031Sstas 487226031Sstasstruct hipc_ops ipcs[] = { 488226031Sstas#if defined(__APPLE__) && defined(HAVE_GCD) 489226031Sstas { "MACH", mach_init, mach_release, mach_ipc, mach_async }, 490226031Sstas#endif 491226031Sstas#ifdef HAVE_DOOR 492226031Sstas { "DOOR", door_init, common_release, door_ipc, NULL } 493226031Sstas#endif 494226031Sstas { "UNIX", unix_socket_init, common_release, unix_socket_ipc, NULL } 495226031Sstas}; 496226031Sstas 497226031Sstasstruct heim_ipc { 498226031Sstas struct hipc_ops *ops; 499226031Sstas void *ctx; 500226031Sstas}; 501226031Sstas 502226031Sstas 503226031Sstasint 504226031Sstasheim_ipc_init_context(const char *name, heim_ipc *ctx) 505226031Sstas{ 506226031Sstas unsigned int i; 507226031Sstas int ret, any = 0; 508226031Sstas 509226031Sstas for(i = 0; i < sizeof(ipcs)/sizeof(ipcs[0]); i++) { 510226031Sstas size_t prefix_len = strlen(ipcs[i].prefix); 511226031Sstas heim_ipc c; 512226031Sstas if(strncmp(ipcs[i].prefix, name, prefix_len) == 0 513226031Sstas && name[prefix_len] == ':') { 514226031Sstas } else if (strncmp("ANY:", name, 4) == 0) { 515226031Sstas prefix_len = 3; 516226031Sstas any = 1; 517226031Sstas } else 518226031Sstas continue; 519226031Sstas 520226031Sstas c = calloc(1, sizeof(*c)); 521226031Sstas if (c == NULL) 522226031Sstas return ENOMEM; 523226031Sstas 524226031Sstas c->ops = &ipcs[i]; 525226031Sstas 526226031Sstas ret = (c->ops->init)(name + prefix_len + 1, &c->ctx); 527226031Sstas if (ret) { 528226031Sstas free(c); 529226031Sstas if (any) 530226031Sstas continue; 531226031Sstas return ret; 532226031Sstas } 533226031Sstas 534226031Sstas *ctx = c; 535226031Sstas return 0; 536226031Sstas } 537226031Sstas 538226031Sstas return ENOENT; 539226031Sstas} 540226031Sstas 541226031Sstasvoid 542226031Sstasheim_ipc_free_context(heim_ipc ctx) 543226031Sstas{ 544226031Sstas (ctx->ops->release)(ctx->ctx); 545226031Sstas free(ctx); 546226031Sstas} 547226031Sstas 548226031Sstasint 549226031Sstasheim_ipc_call(heim_ipc ctx, const heim_idata *snd, heim_idata *rcv, 550226031Sstas heim_icred *cred) 551226031Sstas{ 552226031Sstas if (cred) 553226031Sstas *cred = NULL; 554226031Sstas return (ctx->ops->ipc)(ctx->ctx, snd, rcv, cred); 555226031Sstas} 556226031Sstas 557226031Sstasint 558226031Sstasheim_ipc_async(heim_ipc ctx, const heim_idata *snd, void *userctx, 559226031Sstas void (*func)(void *, int, heim_idata *, heim_icred)) 560226031Sstas{ 561226031Sstas if (ctx->ops->async == NULL) { 562226031Sstas heim_idata rcv; 563226031Sstas heim_icred cred = NULL; 564226031Sstas int ret; 565226031Sstas 566226031Sstas ret = (ctx->ops->ipc)(ctx->ctx, snd, &rcv, &cred); 567226031Sstas (*func)(userctx, ret, &rcv, cred); 568226031Sstas heim_ipc_free_cred(cred); 569226031Sstas free(rcv.data); 570226031Sstas return ret; 571226031Sstas } else { 572226031Sstas return (ctx->ops->async)(ctx->ctx, snd, userctx, func); 573226031Sstas } 574226031Sstas} 575