1/** 2 * \file 3 * \brief spawn service 4 */ 5 6/* 7 * Copyright (c) 2010, 2011, 2012, ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <stdio.h> 16#include <string.h> 17#include <barrelfish/barrelfish.h> 18#include <spawndomain/spawndomain.h> 19#include <barrelfish/monitor_client.h> 20#include <barrelfish/nameservice_client.h> 21#include <barrelfish/cpu_arch.h> 22#include <vfs/vfs.h> 23#include <vfs/vfs_path.h> 24#include <dist/barrier.h> 25#include <if/spawn_defs.h> 26#include <if/monitor_defs.h> 27#include <if/monitor_blocking_defs.h> 28#include <barrelfish/dispatcher_arch.h> 29#include <barrelfish/invocations_arch.h> 30 31#include "internal.h" 32#include "ps.h" 33 34 35static errval_t spawn(struct capref domain_cap, const char *path, 36 char *const argv[], const char *argbuf, size_t argbytes, 37 char *const envp[], struct capref inheritcn_cap, 38 struct capref argcn_cap, uint8_t flags, 39 domainid_t *domainid) 40{ 41 errval_t err, msgerr; 42 43 /* read file into memory */ 44 vfs_handle_t fh; 45 err = vfs_open(path, &fh); 46 if (err_is_fail(err)) { 47 return err_push(err, SPAWN_ERR_LOAD); 48 } 49 50 struct vfs_fileinfo info; 51 err = vfs_stat(fh, &info); 52 if (err_is_fail(err)) { 53 vfs_close(fh); 54 return err_push(err, SPAWN_ERR_LOAD); 55 } 56 57 assert(info.type == VFS_FILE); 58 uint8_t *image = malloc(info.size); 59 if (image == NULL) { 60 vfs_close(fh); 61 return err_push(err, SPAWN_ERR_LOAD); 62 } 63 64 size_t pos = 0, readlen; 65 do { 66 err = vfs_read(fh, &image[pos], info.size - pos, &readlen); 67 if (err_is_fail(err)) { 68 vfs_close(fh); 69 free(image); 70 return err_push(err, SPAWN_ERR_LOAD); 71 } else if (readlen == 0) { 72 vfs_close(fh); 73 free(image); 74 return SPAWN_ERR_LOAD; // XXX 75 } else { 76 pos += readlen; 77 } 78 } while (err_is_ok(err) && readlen > 0 && pos < info.size); 79 80 err = vfs_close(fh); 81 if (err_is_fail(err)) { 82 DEBUG_ERR(err, "failed to close file %s", path); 83 } 84 85 // find short name (last part of path) 86 const char *name = strrchr(path, VFS_PATH_SEP); 87 if (name == NULL) { 88 name = path; 89 } else { 90 name++; 91 } 92 93 /* spawn the image */ 94 struct spawninfo si; 95 si.flags = flags; 96 err = spawn_load_image(&si, (lvaddr_t)image, info.size, CURRENT_CPU_TYPE, 97 name, my_core_id, argv, envp, inheritcn_cap, 98 argcn_cap); 99 if (err_is_fail(err)) { 100 free(image); 101 return err; 102 } 103 104 free(image); 105 106 /* request connection from monitor */ 107 struct monitor_blocking_binding *mrpc = get_monitor_blocking_binding(); 108 struct capref monep; 109 err = slot_alloc(&monep); 110 if (err_is_fail(err)) { 111 return err_push(err, SPAWN_ERR_MONEP_SLOT_ALLOC); 112 } 113 err = mrpc->rpc_tx_vtbl.alloc_monitor_ep(mrpc, &msgerr, &monep); 114 if (err_is_fail(err)) { 115 return err_push(err, SPAWN_ERR_MONITOR_CLIENT); 116 } else if (err_is_fail(msgerr)) { 117 return msgerr; 118 } 119 120 /* copy connection into the new domain */ 121 struct capref destep = { 122 .cnode = si.taskcn, 123 .slot = TASKCN_SLOT_MONITOREP, 124 }; 125 err = cap_copy(destep, monep); 126 if (err_is_fail(err)) { 127 spawn_free(&si); 128 cap_destroy(monep); 129 return err_push(err, SPAWN_ERR_MONITOR_CLIENT); 130 } 131 132 err = cap_destroy(monep); 133 if (err_is_fail(err)) { 134 return err_push(err, SPAWN_ERR_MONITOR_CLIENT); 135 } 136 137 debug_printf("spawning %s on core %u\n", path, my_core_id); 138 139 /* give the perfmon capability */ 140 struct capref dest, src; 141 dest.cnode = si.taskcn; 142 dest.slot = TASKCN_SLOT_PERF_MON; 143 src.cnode = cnode_task; 144 src.slot = TASKCN_SLOT_PERF_MON; 145 err = cap_copy(dest, src); 146 if (err_is_fail(err)) { 147 return err_push(err, SPAWN_ERR_COPY_PERF_MON); 148 } 149 150 if (!capref_is_null(domain_cap)) { 151 // Pass over the domain cap. 152 dest.cnode = si.taskcn; 153 dest.slot = TASKCN_SLOT_DOMAINID; 154 err = cap_copy(dest, domain_cap); 155 if (err_is_fail(err)) { 156 return err_push(err, SPAWN_ERR_COPY_DOMAIN_CAP); 157 } 158 } 159 160 /* run the domain */ 161 err = spawn_run(&si); 162 if (err_is_fail(err)) { 163 spawn_free(&si); 164 return err_push(err, SPAWN_ERR_RUN); 165 } 166 167 // Allocate domain id 168 struct ps_entry *pe = malloc(sizeof(struct ps_entry)); 169 assert(pe != NULL); 170 memset(pe, 0, sizeof(struct ps_entry)); 171 memcpy(pe->argv, argv, MAX_CMDLINE_ARGS*sizeof(*argv)); 172 pe->argbuf = memdup(argbuf, argbytes); 173 pe->argbytes = argbytes; 174 /* 175 * NB: It's important to keep a copy of the DCB *and* the root 176 * CNode around. We need to revoke both (in the right order, see 177 * kill_domain() below), so that we ensure no one else is 178 * referring to the domain's CSpace anymore. Especially the loop 179 * created by placing rootcn into its own address space becomes a 180 * problem here. 181 */ 182 err = slot_alloc(&pe->rootcn_cap); 183 assert(err_is_ok(err)); 184 err = cap_copy(pe->rootcn_cap, si.rootcn_cap); 185 pe->rootcn = si.rootcn; 186 assert(err_is_ok(err)); 187 err = slot_alloc(&pe->dcb); 188 assert(err_is_ok(err)); 189 err = cap_copy(pe->dcb, si.dcb); 190 assert(err_is_ok(err)); 191 pe->status = PS_STATUS_RUNNING; 192 193 if (!capref_is_null(domain_cap)) { 194 err = ps_hash_domain(pe, domain_cap); 195 if (err_is_fail(err)) { 196 free(pe); 197 spawn_free(&si); 198 return err_push(err, SPAWN_ERR_DOMAIN_CAP_HASH); 199 } 200 } 201 202 err = ps_allocate(pe, domainid); 203 if(err_is_fail(err)) { 204 free(pe); 205 } 206 207 // Store in target dispatcher frame 208 struct dispatcher_generic *dg = get_dispatcher_generic(si.handle); 209 dg->domain_id = *domainid; 210 211 /* cleanup */ 212 err = spawn_free(&si); 213 if (err_is_fail(err)) { 214 return err_push(err, SPAWN_ERR_FREE); 215 } 216 217 return SYS_ERR_OK; 218} 219 220static void retry_use_local_memserv_response(void *a) 221{ 222 errval_t err; 223 224 struct spawn_binding *b = (struct spawn_binding*)a; 225 226 err = b->tx_vtbl.use_local_memserv_response(b, NOP_CONT); 227 228 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) { 229 // try again 230 err = b->register_send(b, get_default_waitset(), 231 MKCONT(retry_use_local_memserv_response,a)); 232 } 233 if (err_is_fail(err)) { 234 DEBUG_ERR(err, "error sending use_local_memserv reply\n"); 235 } 236 237} 238 239 240static void use_local_memserv_handler(struct spawn_binding *b) 241{ 242 ram_alloc_set(NULL); 243 244 errval_t err; 245 err = b->tx_vtbl.use_local_memserv_response(b, NOP_CONT); 246 if (err_is_fail(err)) { 247 DEBUG_ERR(err, "error sending use_local_memserv reply"); 248 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) { 249 err = b->register_send(b, get_default_waitset(), 250 MKCONT(retry_use_local_memserv_response, b)); 251 if (err_is_fail(err)) { 252 // note that only one continuation may be registered at a time 253 DEBUG_ERR(err, "register_send failed!"); 254 } 255 } 256 } 257} 258 259struct pending_spawn_response { 260 struct spawn_binding *b; 261 errval_t err; 262 domainid_t domainid; 263}; 264 265static errval_t spawn_with_caps_common(struct capref domain_cap, 266 const char *path, const char *argbuf, 267 size_t argbytes, const char *envbuf, 268 size_t envbytes, 269 struct capref inheritcn_cap, 270 struct capref argcn_cap, uint8_t flags, 271 domainid_t *domainid) 272{ 273 errval_t err; 274 assert(domainid); 275 *domainid = 0; 276 277 /* extract arguments from buffer */ 278 char *argv[MAX_CMDLINE_ARGS + 1]; 279 int i = 0; 280 size_t pos = 0; 281 while (pos < argbytes && i < MAX_CMDLINE_ARGS) { 282 argv[i++] = (CONST_CAST)argbuf + pos; 283 char *end = memchr(&argbuf[pos], '\0', argbytes - pos); 284 if (end == NULL) { 285 err = SPAWN_ERR_GET_CMDLINE_ARGS; 286 goto finish; 287 } 288 pos = end - argbuf + 1; 289 } 290 assert(i <= MAX_CMDLINE_ARGS); 291 argv[i] = NULL; 292 293 /* extract environment from buffer */ 294 char *envp[MAX_CMDLINE_ARGS + 1]; 295 i = 0; 296 pos = 0; 297 while (pos < envbytes && i < MAX_CMDLINE_ARGS) { 298 envp[i++] = (CONST_CAST)envbuf + pos; 299 char *end = memchr(&envbuf[pos], '\0', envbytes - pos); 300 if (end == NULL) { 301 err = SPAWN_ERR_GET_CMDLINE_ARGS; 302 goto finish; 303 } 304 pos = end - envbuf + 1; 305 } 306 assert(i <= MAX_CMDLINE_ARGS); 307 envp[i] = NULL; 308 309 char *npath; 310 npath = alloca(strlen(path)); 311 strcpy(npath, path); 312 vfs_path_normalise(npath); 313 314 err = spawn(domain_cap, npath, argv, argbuf, argbytes, envp, inheritcn_cap, 315 argcn_cap, flags, domainid); 316 // XXX: do we really want to delete the inheritcn and the argcn here? iaw: 317 // do we copy these somewhere? -SG 318 if (!capref_is_null(inheritcn_cap)) { 319 errval_t err2; 320 err2 = cap_delete(inheritcn_cap); 321 assert(err_is_ok(err2)); 322 } 323 if (!capref_is_null(argcn_cap)) { 324 errval_t err2; 325 err2 = cap_delete(argcn_cap); 326 assert(err_is_ok(err2)); 327 } 328 329 finish: 330 if(err_is_fail(err)) { 331 DEBUG_ERR(err, "spawn"); 332 } 333 334 return err; 335} 336 337static errval_t spawn_with_caps_handler(struct spawn_binding *b, const char *path, 338 const char *argvbuf, size_t argvbytes, const char *envbuf, size_t envbytes, 339 struct capref inheritcn_cap, struct capref argcn_cap, uint8_t flags, 340 errval_t *err, spawn_domainid_t *domain_id) 341{ 342 *err = spawn_with_caps_common(NULL_CAP, path, argvbuf, argvbytes, envbuf, 343 envbytes, inheritcn_cap, argcn_cap, flags, 344 domain_id); 345 return SYS_ERR_OK; 346} 347 348static errval_t spawn_handler(struct spawn_binding *b, const char *path, 349 const char *argvbuf, size_t argvbytes, const char *envbuf, size_t envbytes, 350 uint8_t flags, errval_t *err, spawn_domainid_t *domain_id) 351{ 352 *err = spawn_with_caps_common(NULL_CAP, path, argvbuf, argvbytes, envbuf, 353 envbytes, NULL_CAP, NULL_CAP, flags, 354 domain_id); 355 return SYS_ERR_OK; 356} 357 358static void spawn_with_caps_request_handler(struct spawn_binding *b, 359 struct capref procmng_cap, 360 struct capref domain_cap, 361 const char *path, 362 const char *argvbuf, 363 size_t argvbytes, 364 const char *envbuf, 365 size_t envbytes, 366 struct capref inheritcn_cap, 367 struct capref argcn_cap, 368 uint8_t flags) 369{ 370 errval_t err, reply_err; 371 struct capability ret; 372 err = monitor_cap_identify_remote(procmng_cap, &ret); 373 if (err_is_fail(err)) { 374 err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP); 375 goto reply; 376 } 377 378 if (ret.type != ObjType_ProcessManager) { 379 err = SPAWN_ERR_NOT_PROC_MNGR; 380 goto reply; 381 } 382 383 spawn_domainid_t dummy_domain_id; 384 err = spawn_with_caps_common(domain_cap, path, argvbuf, argvbytes, envbuf, 385 envbytes, inheritcn_cap, argcn_cap, flags, 386 &dummy_domain_id); 387 388reply: 389 reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err); 390 if (err_is_fail(reply_err)) { 391 DEBUG_ERR(err, "failed to send spawn_with_caps_reply"); 392 } 393} 394 395static void spawn_request_handler(struct spawn_binding *b, 396 struct capref procmng_cap, 397 struct capref domain_cap, const char *path, 398 const char *argvbuf, size_t argvbytes, 399 const char *envbuf, size_t envbytes, 400 uint8_t flags) 401{ 402 errval_t err, reply_err; 403 struct capability ret; 404 err = monitor_cap_identify_remote(procmng_cap, &ret); 405 if (err_is_fail(err)) { 406 err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP); 407 goto reply; 408 } 409 410 if (ret.type != ObjType_ProcessManager) { 411 err = SPAWN_ERR_NOT_PROC_MNGR; 412 goto reply; 413 } 414 415 spawn_domainid_t dummy_domain_id; 416 err = spawn_with_caps_common(domain_cap, path, argvbuf, argvbytes, envbuf, 417 envbytes, NULL_CAP, NULL_CAP, flags, 418 &dummy_domain_id); 419 420reply: 421 reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err); 422 if (err_is_fail(reply_err)) { 423 DEBUG_ERR(err, "failed to send spawn_reply"); 424 } 425} 426 427static void span_request_handler(struct spawn_binding *b, 428 struct capref procmng_cap, 429 struct capref domain_cap, struct capref vroot, 430 struct capref dispframe) 431{ 432 errval_t err, mon_err, reply_err; 433 struct capability ret; 434 err = monitor_cap_identify_remote(procmng_cap, &ret); 435 if (err_is_fail(err)) { 436 err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP); 437 goto reply; 438 } 439 440 if (ret.type != ObjType_ProcessManager) { 441 err = SPAWN_ERR_NOT_PROC_MNGR; 442 goto reply; 443 } 444 445 struct spawninfo si; 446 memset(&si, 0, sizeof(si)); 447 448 debug_printf("Spanning domain to core %d\n", disp_get_core_id()); 449 450 // Span domain 451 err = spawn_span_domain(&si, vroot, dispframe); 452 if (err_is_fail(err)) { 453 err = err_push(err, SPAWN_ERR_SPAN); 454 goto reply; 455 } 456 457 // Set connection to monitor. 458 struct monitor_blocking_binding *mrpc = get_monitor_blocking_binding(); 459 struct capref monep; 460 err = slot_alloc(&monep); 461 if (err_is_fail(err)) { 462 err = err_push(err, SPAWN_ERR_MONEP_SLOT_ALLOC); 463 goto reply; 464 } 465 err = mrpc->rpc_tx_vtbl.alloc_monitor_ep(mrpc, &mon_err, &monep); 466 if (err_is_ok(err)) { 467 err = mon_err; 468 } 469 if (err_is_fail(err)) { 470 err = err_push(err, SPAWN_ERR_MONITOR_CLIENT); 471 goto reply; 472 } 473 474 /* copy connection into the new domain */ 475 struct capref destep = { 476 .cnode = si.taskcn, 477 .slot = TASKCN_SLOT_MONITOREP, 478 }; 479 err = cap_copy(destep, monep); 480 if (err_is_fail(err)) { 481 spawn_free(&si); 482 cap_destroy(monep); 483 err = err_push(err, SPAWN_ERR_MONITOR_CLIENT); 484 goto reply; 485 } 486 487 err = cap_destroy(monep); 488 if (err_is_fail(err)) { 489 err = err_push(err, SPAWN_ERR_MONITOR_CLIENT); 490 goto reply; 491 } 492 493 /* give the perfmon capability */ 494 struct capref dest, src; 495 dest.cnode = si.taskcn; 496 dest.slot = TASKCN_SLOT_PERF_MON; 497 src.cnode = cnode_task; 498 src.slot = TASKCN_SLOT_PERF_MON; 499 err = cap_copy(dest, src); 500 if (err_is_fail(err)) { 501 err = err_push(err, SPAWN_ERR_COPY_PERF_MON); 502 goto reply; 503 } 504 505 // Pass over the domain cap. 506 dest.cnode = si.taskcn; 507 dest.slot = TASKCN_SLOT_DOMAINID; 508 err = cap_copy(dest, domain_cap); 509 if (err_is_fail(err)) { 510 err = err_push(err, SPAWN_ERR_COPY_DOMAIN_CAP); 511 goto reply; 512 } 513 514 // Make runnable 515 err = spawn_run(&si); 516 if (err_is_fail(err)) { 517 err = err_push(err, SPAWN_ERR_RUN); 518 goto reply; 519 } 520 521 // Allocate an id for this dispatcher. 522 struct ps_entry *pe = malloc(sizeof(struct ps_entry)); 523 assert(pe != NULL); 524 memset(pe, 0, sizeof(struct ps_entry)); 525 /* 526 * NB: It's important to keep a copy of the DCB *and* the root 527 * CNode around. We need to revoke both (in the right order, see 528 * kill_domain() below), so that we ensure no one else is 529 * referring to the domain's CSpace anymore. Especially the loop 530 * created by placing rootcn into its own address space becomes a 531 * problem here. 532 */ 533 // TODO(razvan): The following code is here to comply with spawn(). 534 err = slot_alloc(&pe->rootcn_cap); 535 assert(err_is_ok(err)); 536 err = cap_copy(pe->rootcn_cap, si.rootcn_cap); 537 pe->rootcn = si.rootcn; 538 assert(err_is_ok(err)); 539 err = slot_alloc(&pe->dcb); 540 assert(err_is_ok(err)); 541 err = cap_copy(pe->dcb, si.dcb); 542 assert(err_is_ok(err)); 543 pe->status = PS_STATUS_RUNNING; 544 545 err = ps_hash_domain(pe, domain_cap); 546 if (err_is_fail(err)) { 547 free(pe); 548 spawn_free(&si); 549 err = err_push(err, SPAWN_ERR_DOMAIN_CAP_HASH); 550 goto reply; 551 } 552 553 domainid_t domainid; 554 err = ps_allocate(pe, &domainid); 555 if(err_is_fail(err)) { 556 free(pe); 557 } 558 559 // Cleanup 560 err = spawn_free(&si); 561 if (err_is_fail(err)) { 562 err = err_push(err, SPAWN_ERR_FREE); 563 } 564 565reply: 566 reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err); 567 if (err_is_fail(reply_err)) { 568 DEBUG_ERR(err, "failed to send span_reply"); 569 } 570} 571 572static void cleanup_cap(struct capref cap) 573{ 574 errval_t err; 575 576 err = cap_revoke(cap); 577 if (err_is_fail(err)) { 578 DEBUG_ERR(err, "cap_revoke"); 579 } 580 err = cap_destroy(cap); 581 if (err_is_fail(err)) { 582 DEBUG_ERR(err, "cap_destroy"); 583 } 584} 585 586static void kill_request_handler(struct spawn_binding *b, 587 struct capref procmng_cap, 588 struct capref victim_domain_cap) 589{ 590 errval_t err, reply_err; 591 struct capability ret; 592 err = monitor_cap_identify_remote(procmng_cap, &ret); 593 if (err_is_fail(err)) { 594 err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP); 595 goto reply; 596 } 597 598 if (ret.type != ObjType_ProcessManager) { 599 err = SPAWN_ERR_NOT_PROC_MNGR; 600 goto reply; 601 } 602 603 struct ps_entry *pe; 604 err = ps_get_domain(victim_domain_cap, &pe, NULL); 605 if (err_is_fail(err)) { 606 err = err_push(err, SPAWN_ERR_DOMAIN_NOTFOUND); 607 goto reply; 608 } 609 610 cleanup_cap(pe->dcb); 611 612reply: 613 reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err); 614 if (err_is_fail(reply_err)) { 615 DEBUG_ERR(err, "failed to send kill_reply"); 616 } 617} 618 619static void cleanup_request_handler(struct spawn_binding *b, 620 struct capref procmng_cap, 621 struct capref domain_cap) 622{ 623 errval_t err, reply_err; 624 struct capability ret; 625 err = monitor_cap_identify_remote(procmng_cap, &ret); 626 if (err_is_fail(err)) { 627 err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP); 628 goto reply; 629 } 630 631 if (ret.type != ObjType_ProcessManager) { 632 err = SPAWN_ERR_NOT_PROC_MNGR; 633 goto reply; 634 } 635 636 struct ps_entry *pe; 637 err = ps_release_domain(domain_cap, &pe); 638 if (err_is_fail(err)) { 639 err = err_push(err, SPAWN_ERR_DOMAIN_NOTFOUND); 640 goto reply; 641 } 642 643 cleanup_cap(pe->rootcn_cap); 644 645 // Cleanup struct ps_entry. Note that waiters will be handled by the process 646 // manager, as opposed to the old protocol of handling them here. 647 free(pe->argbuf); 648 ps_remove(pe->domain_id); 649 free(pe); 650 651reply: 652 reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err); 653 if (err_is_fail(reply_err)) { 654 DEBUG_ERR(err, "failed to send cleanup_reply"); 655 } 656} 657 658/** 659 * \brief Removes a zombie domain. 660 */ 661static void cleanup_domain(domainid_t domainid) 662{ 663 errval_t err; 664 struct ps_entry *ps = ps_get(domainid); 665 assert(ps != NULL); 666 667 // Tell all waiters of exit and free list as we go 668 for(struct ps_waiter *w = ps->waiters; w != NULL;) { 669 err = w->binding->tx_vtbl.wait_response 670 (w->binding, NOP_CONT, ps->exitcode, SYS_ERR_OK); 671 if(err_is_fail(err)) { 672 DEBUG_ERR(err, "wait_response"); 673 } 674 675 struct ps_waiter *oldw = w; 676 w = w->next; 677 free(oldw); 678 } 679 ps->waiters = NULL; 680 681 // Cleanup rest of ps entry 682 free(ps->argbuf); 683 684 ps_remove(domainid); 685} 686 687static errval_t kill_domain(domainid_t domainid, uint8_t exitcode) 688{ 689 struct ps_entry *ps = ps_get(domainid); 690 691 if(ps == NULL) { 692 return SPAWN_ERR_DOMAIN_NOTFOUND; 693 } 694 695 ps->status = PS_STATUS_ZOMBIE; 696 ps->exitcode = exitcode; 697 698 // Garbage collect victim's capabilities 699 cleanup_cap(ps->dcb); // Deschedule dispatcher (do this first!) 700 cleanup_cap(ps->rootcn_cap); 701 702 // XXX: why only when waiters exist? -SG 703 if(ps->waiters != NULL) { 704 // Cleanup local data structures and inform waiters 705 cleanup_domain(domainid); 706 } 707 708 return SYS_ERR_OK; 709} 710 711static void kill_handler(struct spawn_binding *b, domainid_t domainid) 712{ 713 errval_t err = kill_domain(domainid, 0); 714 715 err = b->tx_vtbl.kill_response(b, NOP_CONT, err); 716 if(err_is_fail(err)) { 717 DEBUG_ERR(err, "kill_response"); 718 } 719} 720 721static void exit_handler(struct spawn_binding *b, domainid_t domainid, 722 uint8_t exitcode) 723{ 724 errval_t err = kill_domain(domainid, exitcode); 725 struct ps_entry *ps = ps_get(domainid); 726 727 if(err_is_fail(err)) { 728 DEBUG_ERR(err, "kill_domain"); 729 } 730 731 if(ps == NULL) { 732 // XXX: Can't do nothing 733 return; 734 } 735 736 // May never return anything to client 737} 738 739static void wait_handler(struct spawn_binding *b, domainid_t domainid, 740 bool nohang) 741{ 742 errval_t err; 743 struct ps_entry *ps = ps_get(domainid); 744 745 if(ps == NULL) { 746 err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_NOTFOUND); 747 if(err_is_fail(err)) { 748 DEBUG_ERR(err, "wait_response"); 749 } 750 } else { 751 if(!nohang || ps->status == PS_STATUS_ZOMBIE) { 752 // Enqueue the waiter 753 struct ps_waiter *waiter = malloc(sizeof(struct ps_waiter)); 754 assert(waiter != NULL); 755 waiter->next = ps->waiters; 756 waiter->binding = b; 757 ps->waiters = waiter; 758 } else { 759 // nohang and no zombie, return error 760 err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_RUNNING); 761 if(err_is_fail(err)) { 762 DEBUG_ERR(err, "wait_response"); 763 } 764 } 765 766 // Cleanup if zombie (will send the reply) 767 if(ps->status == PS_STATUS_ZOMBIE) { 768 cleanup_domain(domainid); 769 } 770 } 771} 772 773static void get_domainlist_sent(void *arg) 774{ 775 free(arg); 776} 777 778static void get_domainlist_handler(struct spawn_binding *b) 779{ 780 errval_t err; 781 size_t len = 0; 782 uint8_t *domains = calloc(sizeof(uint8_t), MAX_DOMAINS); 783 784 // XXX: Very inefficient 785 for(domainid_t i = 0; i < MAX_DOMAINS; i++) { 786 if(ps_exists(i)) { 787 domains[len++] = i; 788 } 789 } 790 791 err = b->tx_vtbl.get_domainlist_response 792 (b, MKCLOSURE(get_domainlist_sent, domains), domains, len); 793 if(err_is_fail(err)) { 794 DEBUG_ERR(err, "get_domainlist_response"); 795 free(domains); 796 } 797} 798 799static void status_handler(struct spawn_binding *b, domainid_t domainid) 800{ 801 errval_t err; 802 struct ps_entry *ps = ps_get(domainid); 803 spawn_ps_entry_t pse; 804 805 memset(&pse, 0, sizeof(pse)); 806 807 if(ps == NULL) { 808 err = b->tx_vtbl.status_response(b, NOP_CONT, pse, NULL, 0, 809 SPAWN_ERR_DOMAIN_NOTFOUND); 810 if(err_is_fail(err)) { 811 DEBUG_ERR(err, "status_response"); 812 } 813 } 814 815 pse.status = ps->status; 816 817 err = b->tx_vtbl.status_response(b, NOP_CONT, pse, ps->argbuf, ps->argbytes, 818 SYS_ERR_OK); 819 if(err_is_fail(err)) { 820 DEBUG_ERR(err, "status_response"); 821 } 822} 823 824 825static errval_t dump_capabilities(domainid_t domainid) { 826 struct ps_entry *ps = ps_get(domainid); 827 828 if(ps == NULL) { 829 return SPAWN_ERR_DOMAIN_NOTFOUND; 830 } 831 832 return invoke_dispatcher_dump_capabilities(ps->dcb); 833} 834 835static void dump_capabilities_handler(struct spawn_binding *b, domainid_t domainid) { 836 errval_t err = dump_capabilities(domainid); 837 838 err = b->tx_vtbl.dump_capabilities_response(b, NOP_CONT, err); 839 if(err_is_fail(err)) { 840 DEBUG_ERR(err, "debug_print_capabilities_response"); 841 } 842} 843 844static struct spawn_rx_vtbl rx_vtbl = { 845 // .spawn_domain_call = spawn_handler, 846 // .spawn_domain_with_caps_call = spawn_with_caps_handler, 847 848 // Async messages for the process manager. 849 .spawn_request = spawn_request_handler, 850 .spawn_with_caps_request = spawn_with_caps_request_handler, 851 .span_request = span_request_handler, 852 .kill_request = kill_request_handler, 853 .cleanup_request = cleanup_request_handler, 854 855 .use_local_memserv_call = use_local_memserv_handler, 856 .kill_call = kill_handler, 857 .exit_call = exit_handler, 858 .wait_call = wait_handler, 859 .get_domainlist_call = get_domainlist_handler, 860 .status_call = status_handler, 861 .dump_capabilities_call = dump_capabilities_handler 862}; 863 864static struct spawn_rpc_rx_vtbl rpc_rx_vtbl = { 865 .spawn_domain_call = spawn_handler, 866 .spawn_domain_with_caps_call = spawn_with_caps_handler, 867 // .use_local_memserv_call = use_local_memserv_handler, 868 // .kill_call = kill_handler, 869 // .exit_call = exit_handler, 870 // .wait_call = wait_handler, 871 // .get_domainlist_call = get_domainlist_handler, 872 // .status_call = status_handler, 873 // .dump_capabilities_call = dump_capabilities_handler 874}; 875 876static void export_cb(void *st, errval_t err, iref_t iref) 877{ 878 if (err_is_fail(err)) { 879 USER_PANIC_ERR(err, "export failed"); 880 } 881 882 // Send iref back to monitor, which will forward it to the process manager. 883 struct monitor_binding *mb = get_monitor_binding(); 884 err = mb->tx_vtbl.set_spawn_iref_request(mb, NOP_CONT, iref); 885 if (err_is_fail(err)) { 886 USER_PANIC_ERR(err, "failed to send set_spawn_iref_request to " 887 "monitor"); 888 } 889 890 // construct name 891 char namebuf[32]; 892 size_t len = snprintf(namebuf, sizeof(namebuf), "%s.%d", SERVICE_BASENAME, 893 my_core_id); 894 assert(len < sizeof(namebuf)); 895 namebuf[sizeof(namebuf) - 1] = '\0'; 896 897 // register this iref with the name service 898 err = nameservice_register(namebuf, iref); 899 if (err_is_fail(err)) { 900 USER_PANIC_ERR(err, "nameservice_register failed"); 901 } 902} 903 904 905static errval_t connect_cb(void *st, struct spawn_binding *b) 906{ 907 // copy my message receive handler vtable to the binding 908 b->rx_vtbl = rx_vtbl; 909 b->rpc_rx_vtbl = rpc_rx_vtbl; 910 return SYS_ERR_OK; 911} 912 913errval_t start_service(void) 914{ 915 return spawn_export(NULL, export_cb, connect_cb, get_default_waitset(), 916 IDC_EXPORT_FLAGS_DEFAULT); 917} 918