1/** 2 * \file 3 * \brief Client for interacting with the process management server. 4 */ 5 6/* 7 * Copyright (c) 2017, 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 <barrelfish/barrelfish.h> 16#include <barrelfish/nameservice_client.h> 17#include <barrelfish/spawn_client.h> 18#include <if/octopus_defs.h> 19#include <if/proc_mgmt_defs.h> 20#include <if/arrakis_defs.h> 21#include <if/monitor_defs.h> 22#include <if/spawn_defs.h> 23#include <vfs/vfs_path.h> 24 25// For spawn_program_on_all_cores 26#include <octopus/getset.h> // for oct_read TODO 27#include <octopus/trigger.h> // for NOP_TRIGGER 28 29 30struct proc_mgmt_bind_retst { 31 errval_t err; 32 struct proc_mgmt_binding *b; 33 bool present; 34}; 35 36struct spawn_bind_retst { 37 errval_t err; 38 struct spawn_binding *b; 39 bool present; 40}; 41 42struct arrakis_bind_retst { 43 errval_t err; 44 struct arrakis_binding *b; 45 bool present; 46}; 47 48extern char **environ; 49 50static void spawn_bind_cont(void *st, errval_t err, struct spawn_binding *b) 51{ 52 struct spawn_bind_retst *retst = st; 53 assert(retst != NULL); 54 assert(!retst->present); 55 retst->err = err; 56 retst->b = b; 57 retst->present = true; 58} 59 60static void arrakis_bind_cont(void *st, errval_t err, struct arrakis_binding *b) 61{ 62 struct arrakis_bind_retst *retst = st; 63 assert(retst != NULL); 64 assert(!retst->present); 65 retst->err = err; 66 retst->b = b; 67 retst->present = true; 68} 69 70static struct spawn_binding *spawn_b = NULL; 71 72static errval_t bind_client(coreid_t coreid) 73{ 74 struct spawn_binding *cl; 75 errval_t err = SYS_ERR_OK; 76 77 // do we have a spawn client connection for this core? 78 assert(coreid < MAX_CPUS); 79 cl = get_spawn_binding(coreid); 80 if (cl == NULL) { 81 char namebuf[16]; 82 snprintf(namebuf, sizeof(namebuf), "spawn.%u", coreid); 83 namebuf[sizeof(namebuf) - 1] = '\0'; 84 85 iref_t iref; 86 err = nameservice_blocking_lookup(namebuf, &iref); 87 if (err_is_fail(err)) { 88 //DEBUG_ERR(err, "spawn daemon on core %u not found\n", coreid); 89 return err; 90 } 91 92 // initiate bind 93 struct spawn_bind_retst bindst = { .present = false }; 94 err = spawn_bind(iref, spawn_bind_cont, &bindst, get_default_waitset(), 95 IDC_BIND_FLAGS_DEFAULT); 96 if (err_is_fail(err)) { 97 DEBUG_ERR(err, "spawn_bind failed"); 98 return err; 99 } 100 101 // XXX: block for bind completion 102 while (!bindst.present) { 103 messages_wait_and_handle_next(); 104 } 105 106 if (err_is_fail(bindst.err)) { 107 return bindst.err; 108 } 109 110 spawn_b = bindst.b; 111 112 spawn_rpc_client_init(bindst.b); 113 set_spawn_binding(coreid, bindst.b); 114 } 115 116 return err; 117} 118 119errval_t spawn_bind_iref(iref_t iref, struct spawn_binding **ret_client) 120{ 121 assert(ret_client != NULL); 122 123 struct spawn_bind_retst bindst = { .present = false }; 124 errval_t err = spawn_bind(iref, spawn_bind_cont, &bindst, 125 get_default_waitset(), IDC_BIND_FLAGS_DEFAULT); 126 if (err_is_fail(err)) { 127 DEBUG_ERR(err, "spawn_bind failed"); 128 return err; 129 } 130 131 // XXX: block for bind completion 132 while (!bindst.present) { 133 messages_wait_and_handle_next(); 134 } 135 136 if (err_is_fail(bindst.err)) { 137 return bindst.err; 138 } 139 140 spawn_rpc_client_init(bindst.b); 141 *ret_client = bindst.b; 142 // set_spawn_binding(coreid, bindst.b); 143 144 return err; 145} 146 147 148static void error_handler(struct proc_mgmt_binding *b, errval_t err) 149{ 150#if defined(__x86_64__) || defined(__i386__) 151 debug_printf("%p \n", __builtin_return_address(0)); 152#endif 153 debug_err(__FILE__, __func__, __LINE__, err, 154 "asynchronous error in proc_mgmt binding"); 155 abort(); 156} 157 158static void proc_mgmt_bind_cont(void *st, errval_t err, 159 struct proc_mgmt_binding *b) 160{ 161 struct proc_mgmt_bind_retst *retst = (struct proc_mgmt_bind_retst*) st; 162 assert(retst != NULL); 163 assert(!retst->present); 164 retst->err = err; 165 retst->b = b; 166 retst->present = true; 167 b->st = retst; 168} 169 170static void proc_mgmt_accept_recv_handler(void *arg) 171{ 172 struct proc_mgmt_lmp_binding *b = arg; 173 struct lmp_recv_msg msg = LMP_RECV_MSG_INIT; 174 struct capref cap; 175 errval_t err; 176 177 // try to retrieve a message from the channel 178 err = lmp_chan_recv(&b->chan, &msg, &cap); 179 if (err_is_fail(err)) { 180 if (err_no(err) == LIB_ERR_NO_LMP_MSG) { 181 // nothing there, re-register 182 struct event_closure recv_handler = { 183 .handler = proc_mgmt_accept_recv_handler, 184 .arg = b, 185 }; 186 err = lmp_chan_register_recv(&b->chan, b->b.waitset, recv_handler); 187 b->b.error_handler(&b->b, err_push(err, LIB_ERR_CHAN_REGISTER_RECV)); 188 } else { 189 // real error, report to user 190 b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_CHAN_RECV)); 191 } 192 return; 193 } 194 195 // TODO(razvan): LMP_PROC_MGMT_ACCEPT ? 196 assert(b->chan.connstate == LMP_MONITOR_ACCEPT); 197 assert(!capref_is_null(cap)); 198 b->chan.remote_cap = cap; 199 b->chan.connstate = LMP_CONNECTED; 200 201 /* allocate a new receive slot */ 202 err = lmp_chan_alloc_recv_slot(&b->chan); 203 if (err_is_fail(err)) { 204 // XXX: report the error, but continue 205 b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_ALLOC_RECV_SLOT)); 206 } 207 208 /* Run the RX handler; has a side-effect of registering for receive events */ 209 proc_mgmt_lmp_rx_handler(b); 210} 211 212static errval_t init_lmp_binding(struct proc_mgmt_lmp_binding *lmpb, 213 struct waitset *ws, 214 size_t buflen_words) 215{ 216 errval_t err; 217 218 proc_mgmt_lmp_init(lmpb, ws); 219 220 /* allocate a cap slot for the new endpoint cap */ 221 err = slot_alloc(&lmpb->chan.local_cap); 222 if (err_is_fail(err)) { 223 return err_push(err, LIB_ERR_SLOT_ALLOC); 224 } 225 226 /* allocate a local endpoint */ 227 err = lmp_endpoint_create_in_slot(buflen_words, lmpb->chan.local_cap, 228 &lmpb->chan.endpoint); 229 if (err_is_fail(err)) { 230 // TODO(razvan): Free cap slot. 231 return err_push(err, LIB_ERR_ENDPOINT_CREATE); 232 } 233 234 /* allocate an initial receive slot */ 235 err = lmp_chan_alloc_recv_slot(&lmpb->chan); 236 if (err_is_fail(err)) { 237 return err; 238 } 239 240 /* setup error handler */ 241 lmpb->b.error_handler = error_handler; 242 243 /* setup initial receive handlers */ 244 // TODO(razvan): Don't think this is needed, but dunno for sure yet. 245 // lmpb->b.rx_vtbl = monitor_rx_vtbl; 246 247 // connect handlers 248 lmpb->b.change_waitset(&lmpb->b, lmpb->b.waitset); 249 return SYS_ERR_OK; 250} 251 252/** 253 * \brief Accept a new LMP binding to a proc mgmt client. 254 * 255 * Should only be used in the process manager. 256 * 257 * \param lmpb Storage for binding state 258 * \param ws Waitset for handling incoming messages 259 * \param buflen_words Size of incoming buffer, in number of words 260 */ 261errval_t proc_mgmt_client_lmp_accept(struct proc_mgmt_lmp_binding *lmpb, 262 struct waitset *ws, 263 size_t lmp_buflen_words) 264{ 265 errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words); 266 if (err_is_fail(err)) { 267 return err; 268 } 269 270 lmpb->chan.connstate = LMP_MONITOR_ACCEPT; // TODO(razvan): LMP_PROC_MGMT_ACCEPT? 271 lmpb->chan.remote_cap = NULL_CAP; // will be sent to us by the client 272 273 /* Register for receive notification on our special handler */ 274 struct event_closure receive_handler = { 275 .handler = proc_mgmt_accept_recv_handler, 276 .arg = lmpb, 277 }; 278 err = lmp_chan_register_recv(&lmpb->chan, ws, receive_handler); 279 if (err_is_fail(err)) { 280 return err; // TODO(razvan): cleanup? 281 } 282 283 return SYS_ERR_OK; 284} 285 286 287/** 288 * \brief Initiate a new LMP binding to the process manager 289 * 290 * To be used by the monitor for setting up the privileged channel used for 291 * spawnd discovery. 292 * Requires an explicit remote endpoint cap allocated by the process manager. 293 * 294 * \param lmpb Storage for binding state 295 * \param ep Remote endpoint of the process manager 296 * \param ws Waitset for handling incoming messages 297 * \param cont Continuation for when binding completes or fails 298 * \param st State passed to continuation function 299 * \param buflen_words Size of incoming buffer, in number of words 300 */ 301errval_t proc_mgmt_client_lmp_bind(struct proc_mgmt_lmp_binding *lmpb, 302 struct capref ep, 303 proc_mgmt_bind_continuation_fn *cont, 304 void *st, 305 struct waitset *ws, 306 size_t lmp_buflen_words) 307{ 308 errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words); 309 if (err_is_fail(err)) { 310 return err; 311 } 312 313 lmpb->chan.remote_cap = ep; 314 315 // Send the local endpoint cap to the process manager. 316 lmpb->chan.connstate = LMP_CONNECTED; /* pre-established */ 317 err = lmp_chan_send0(&lmpb->chan, 0, lmpb->chan.local_cap); 318 if (err_is_fail(err)) { 319 // TODO(razvan): This, below. 320 /* XXX: I'm lazily assuming this can never fail with a transient error, 321 * since we only do it once at dispatcher startup. If not, we need to 322 * register and retry here */ 323 assert(!lmp_err_is_transient(err)); 324 return err; 325 } 326 327 /* Run the RX handler; has a side-effect of registering for receive events */ 328 proc_mgmt_lmp_rx_handler(lmpb); 329 330 /* Run the continuation */ 331 cont(st, SYS_ERR_OK, &lmpb->b); 332 333 return SYS_ERR_OK; 334} 335 336errval_t proc_mgmt_bind_client(void) 337{ 338 struct proc_mgmt_binding *b = get_proc_mgmt_binding(); 339 if (b != NULL) { 340 return SYS_ERR_OK; 341 } 342 343 errval_t err; 344 iref_t iref; 345 // Try using nameserver to retrievew the proc mgmt iref. 346 err = nameservice_blocking_lookup("proc_mgmt", &iref); 347 if (err_is_fail(err)) { 348 return err; 349 } 350 351 // Initiate bind. 352 struct proc_mgmt_bind_retst bindst = { 353 .present = false 354 }; 355 356 err = proc_mgmt_bind(iref, proc_mgmt_bind_cont, &bindst, 357 get_default_waitset(), /*IDC_BIND_FLAG_RPC_CAP_TRANSFER*/IDC_BIND_FLAGS_DEFAULT); 358 if (err_is_fail(err)) { 359 USER_PANIC_ERR(err, "proc_mgmt_bind"); 360 } 361 362 // Wait for bind completion. 363 while (!bindst.present) { 364 messages_wait_and_handle_next(); 365 } 366 367 if (err_is_fail(bindst.err)) { 368 return bindst.err; 369 } 370 371 proc_mgmt_rpc_client_init(bindst.b); 372 373 set_proc_mgmt_binding(bindst.b); 374 375 return SYS_ERR_OK; 376} 377 378errval_t proc_mgmt_add_spawnd(iref_t iref, coreid_t core_id) 379{ 380 errval_t err = proc_mgmt_bind_client(); 381 if (err_is_fail(err)) { 382 DEBUG_ERR(err, "proc_mgmt_bind_client"); 383 return err; 384 } 385 386 struct proc_mgmt_binding *b = get_proc_mgmt_binding(); 387 assert(b != NULL); 388 389 err = b->tx_vtbl.add_spawnd(b, NOP_CONT, core_id, iref); 390 if (err_is_fail(err)) { 391 DEBUG_ERR(err, "add_spawnd"); 392 } 393 394 return err; 395} 396 397/** 398 * \brief Request the process manager to spawn a program on a specific core 399 * 400 * \param coreid Core ID on which to spawn the program 401 * \param path Absolute path in the file system to an executable 402 * image suitable for the given core 403 * \param argv Command-line arguments, NULL-terminated 404 * \param envp Optional environment, NULL-terminated 405 * (pass NULL to inherit) 406 * \param inheritcn_cap Cap to a CNode containing capabilities to be inherited 407 * \param argcn_cap Cap to a CNode containing capabilities passed as 408 * arguments 409 * \param flags Flags to spawn 410 * \param ret_domain_cap If non-NULL, filled in with domain cap of new domain 411 * 412 * \bug flags are currently ignored 413 */ 414errval_t spawn_program_with_caps(coreid_t core_id, const char *path, 415 char *const argv[], 416 char *const envp[], 417 struct capref inheritcn_cap, 418 struct capref argcn_cap, 419 uint8_t flags, 420 struct capref *ret_domain_cap) 421{ 422 errval_t err, msgerr; 423 424 // default to copying our environment 425 if (envp == NULL) { 426 envp = environ; 427 } 428 429 err = proc_mgmt_bind_client(); 430 if (err_is_fail(err)) { 431 USER_PANIC_ERR(err, "proc_mgmt_bind_client"); 432 } 433 434 struct proc_mgmt_binding *b = get_proc_mgmt_binding(); 435 assert(b != NULL); 436 437 // construct argument "string" 438 // \0-separated strings in contiguous character buffer 439 // this is needed, as flounder can't send variable-length arrays of strings 440 size_t argstrlen = 0; 441 442 for (int i = 0; argv[i] != NULL; i++) { 443 argstrlen += strlen(argv[i]) + 1; 444 } 445 446 char argstr[argstrlen]; 447 size_t argstrpos = 0; 448 for (int i = 0; argv[i] != NULL; i++) { 449 strcpy(&argstr[argstrpos], argv[i]); 450 argstrpos += strlen(argv[i]); 451 argstr[argstrpos++] = '\0'; 452 } 453 assert(argstrpos == argstrlen); 454 455 // repeat for environment 456 size_t envstrlen = 0; 457 for (int i = 0; envp[i] != NULL; i++) { 458 envstrlen += strlen(envp[i]) + 1; 459 } 460 461 char envstr[envstrlen]; 462 size_t envstrpos = 0; 463 for (int i = 0; envp[i] != NULL; i++) { 464 strcpy(&envstr[envstrpos], envp[i]); 465 envstrpos += strlen(envp[i]); 466 envstr[envstrpos++] = '\0'; 467 } 468 assert(envstrpos == envstrlen); 469 470 // make an unqualified path absolute using the $PATH variable 471 // TODO: implement search (currently assumes PATH is a single directory) 472 char *searchpath = getenv("PATH"); 473 if (searchpath == NULL) { 474 searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root 475 } 476 size_t buflen = strlen(path) + strlen(searchpath) + 2; 477 char pathbuf[buflen]; 478 if (path[0] != VFS_PATH_SEP) { 479 snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path); 480 pathbuf[buflen - 1] = '\0'; 481 //vfs_path_normalise(pathbuf); 482 path = pathbuf; 483 } 484 485 struct capref domain_cap; 486 err = slot_alloc(&domain_cap); 487 if (err_is_fail(err)) { 488 return err; 489 } 490 491 if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) { 492 err = b->rpc_tx_vtbl.spawn(b, core_id, path, argstr, argstrlen, envstr, 493 envstrlen, flags, &msgerr, &domain_cap); 494 } else { 495 err = b->rpc_tx_vtbl.spawn_with_caps(b, core_id, path, argstr, 496 argstrlen, envstr, envstrlen, 497 inheritcn_cap, argcn_cap, flags, 498 &msgerr, &domain_cap); 499 } 500 if (err_is_fail(err)) { 501 USER_PANIC_ERR(err, "error sending spawn request to process manager"); 502 } else if (err_is_fail(msgerr)) { 503 goto out; 504 } 505 506 if (ret_domain_cap != NULL) { 507 *ret_domain_cap = domain_cap; 508 } 509 510out: 511 return msgerr; 512 513} 514 515/** 516 * \brief Request the process manager to spawn a program on a specific core 517 * 518 * \param coreid Core ID on which to spawn the program 519 * \param path Absolute path in the file system to an executable 520 * image suitable for the given core 521 * \param argv Command-line arguments, NULL-terminated 522 * \param envp Optional environment, NULL-terminated 523 * (pass NULL to inherit) 524 * \param inheritcn_cap Cap to a CNode containing capabilities to be inherited 525 * \param argcn_cap Cap to a CNode containing capabilities passed as 526 * arguments 527 * \param flags Flags to spawn 528 * \param ret_domain_cap If non-NULL, filled in with domain cap of new domain 529 * 530 * \bug flags are currently ignored 531 */ 532errval_t spawn_program(coreid_t core_id, const char *path, 533 char *const argv[], char *const envp[], 534 uint8_t flags, struct capref *ret_domain_cap) 535{ 536 return spawn_program_with_caps(core_id, path, argv, envp, 537 NULL_CAP, NULL_CAP, flags, 538 ret_domain_cap); 539} 540 541errval_t spawn_arrakis_program(coreid_t coreid, const char *path, 542 char *const argv[], char *const envp[], 543 struct capref inheritcn_cap, 544 struct capref argcn_cap, spawn_flags_t flags, 545 domainid_t *ret_domainid) 546{ 547 struct arrakis_binding *cl; 548 errval_t err, msgerr; 549 550 // default to copying our environment 551 if (envp == NULL) { 552 envp = environ; 553 } 554 555 // do we have a arrakis client connection for this core? 556 assert(coreid < MAX_CPUS); 557 cl = get_arrakis_binding(coreid); 558 if (cl == NULL) { 559 char namebuf[16]; 560 snprintf(namebuf, sizeof(namebuf), "arrakis.%u", coreid); 561 namebuf[sizeof(namebuf) - 1] = '\0'; 562 563 iref_t iref; 564 err = nameservice_blocking_lookup(namebuf, &iref); 565 if (err_is_fail(err)) { 566 //DEBUG_ERR(err, "arrakis daemon on core %u not found\n", coreid); 567 return err; 568 } 569 570 // initiate bind 571 struct arrakis_bind_retst bindst = { .present = false }; 572 err = arrakis_bind(iref, arrakis_bind_cont, &bindst, get_default_waitset(), 573 IDC_BIND_FLAGS_DEFAULT); 574 if (err_is_fail(err)) { 575 USER_PANIC_ERR(err, "arrakis_bind failed"); 576 } 577 578 // XXX: block for bind completion 579 while (!bindst.present) { 580 messages_wait_and_handle_next(); 581 } 582 583 if(err_is_fail(bindst.err)) { 584 USER_PANIC_ERR(bindst.err, "asynchronous error during arrakis_bind"); 585 } 586 assert(bindst.b != NULL); 587 588 arrakis_rpc_client_init(bindst.b); 589 set_arrakis_binding(coreid, bindst.b); 590 } 591 592 // construct argument "string" 593 // \0-separated strings in contiguous character buffer 594 // this is needed, as flounder can't send variable-length arrays of strings 595 size_t argstrlen = 0; 596 for (int i = 0; argv[i] != NULL; i++) { 597 argstrlen += strlen(argv[i]) + 1; 598 } 599 600 char argstr[argstrlen]; 601 size_t argstrpos = 0; 602 for (int i = 0; argv[i] != NULL; i++) { 603 strcpy(&argstr[argstrpos], argv[i]); 604 argstrpos += strlen(argv[i]); 605 argstr[argstrpos++] = '\0'; 606 } 607 assert(argstrpos == argstrlen); 608 609 // repeat for environment 610 size_t envstrlen = 0; 611 for (int i = 0; envp[i] != NULL; i++) { 612 envstrlen += strlen(envp[i]) + 1; 613 } 614 615 char envstr[envstrlen]; 616 size_t envstrpos = 0; 617 for (int i = 0; envp[i] != NULL; i++) { 618 strcpy(&envstr[envstrpos], envp[i]); 619 envstrpos += strlen(envp[i]); 620 envstr[envstrpos++] = '\0'; 621 } 622 assert(envstrpos == envstrlen); 623 624 625 domainid_t domain_id; 626 627 // make an unqualified path absolute using the $PATH variable 628 // TODO: implement search (currently assumes PATH is a single directory) 629 char *searchpath = getenv("PATH"); 630 if (searchpath == NULL) { 631 searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root 632 } 633 size_t buflen = strlen(path) + strlen(searchpath) + 2; 634 char pathbuf[buflen]; 635 if (path[0] != VFS_PATH_SEP) { 636 snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path); 637 pathbuf[buflen - 1] = '\0'; 638 //vfs_path_normalise(pathbuf); 639 path = pathbuf; 640 } 641 642 err = cl->rpc_tx_vtbl.spawn_arrakis_domain(cl, path, argstr, argstrlen, 643 envstr, envstrlen, &msgerr, &domain_id); 644 if (err_is_fail(err)) { 645 USER_PANIC_ERR(err, "error sending arrakis request"); 646 } else if (err_is_fail(msgerr)) { 647 return msgerr; 648 } 649 650 if (ret_domainid != NULL) { 651 *ret_domainid = domain_id; 652 } 653 654 return msgerr; 655} 656 657/** 658 * \brief Request a program be spawned on all cores in the system 659 * 660 * \param same_core Iff false, don't spawn on the same core as the caller 661 * \param path Absolute path in the file system to an executable image 662 * suitable for the given core 663 * \param argv Command-line arguments, NULL-terminated 664 * \param envp Optional environment, NULL-terminated (pass NULL to inherit) 665 * \param flags Flags to spawn 666 * \param ret_domainid If non-NULL, filled in with domain ID of program 667 * \param count How much programs it spawned 668 * 669 * \note This function is for legacy compatibility with existing benchmark/test 670 * code, and SHOULD NOT BE USED IN NEW CODE UNLESS YOU HAVE A GOOD REASON! 671 * It doesn't make much sense from a scalability perspective, and is 672 * probably useless on a heterogeneous system. 673 */ 674errval_t spawn_program_on_all_cores(bool same_core, const char *path, 675 char *const argv[], char *const envp[], 676 spawn_flags_t flags, struct capref *ret_domain_cap, 677 coreid_t* spawn_count) 678{ 679 // TODO: handle flags, domain ID 680 errval_t err = SYS_ERR_OK; 681 682 struct octopus_binding *r = get_octopus_binding(); 683 if (r == NULL) { 684 return LIB_ERR_NAMESERVICE_NOT_BOUND; 685 } 686 687 // FIXME: world's most (kinda less now) broken implementation... 688 char** names = NULL; 689 size_t count = 0; 690 691 static char* spawnds = "r'spawn.[0-9]+' { iref: _ }"; 692 struct octopus_get_names_response__rx_args reply; 693 err = r->rpc_tx_vtbl.get_names(r, spawnds, NOP_TRIGGER, reply.output, &reply.tid, 694 &reply.error_code); 695 if (err_is_fail(err) || err_is_fail(reply.error_code)) { 696 err = err_push(err, SPAWN_ERR_FIND_SPAWNDS); 697 goto out; 698 } 699 700 err = oct_parse_names(reply.output, &names, &count); 701 if (err_is_fail(err)) { 702 goto out; 703 } 704 705 for (size_t c = 0; c < count; c++) { 706 coreid_t coreid; 707 int ret = sscanf(names[c], "spawn.%hhu", &coreid); 708 if (ret != 1) { 709 err = SPAWN_ERR_MALFORMED_SPAWND_RECORD; 710 goto out; 711 } 712 713 if (!same_core && coreid == disp_get_core_id()) { 714 continue; 715 } 716 717 err = spawn_program(c, path, argv, envp, flags, NULL); 718 if (err_is_ok(err) && spawn_count != NULL) { 719 *spawn_count += 1; 720 } 721 722 if (err_is_fail(err)) { 723 DEBUG_ERR(err, "error spawning %s on core %u\n", path, c); 724 goto out; 725 } 726 } 727 728out: 729 oct_free_names(names, count); 730 return err; 731} 732 733errval_t spawn_binding(coreid_t coreid, struct spawn_binding **ret_client) 734{ 735 errval_t err = bind_client(coreid); 736 if (err_is_fail(err)) { 737 return err; 738 } 739 740 *ret_client = get_spawn_binding(coreid); 741 return SYS_ERR_OK; 742} 743 744/** 745 * \brief Request the process manager to span onto a new core. 746 * 747 * \param core_id ID of core to span onto. 748 * 749 * Blocks until the new dispatcher has established an interdispatcher connection 750 * to the current one. 751 */ 752errval_t spawn_span(coreid_t core_id) 753{ 754 coreid_t my_core_id = disp_get_core_id(); 755 assert (core_id != my_core_id); 756 757 errval_t err, msgerr; 758 err = proc_mgmt_bind_client(); 759 if (err_is_fail(err)) { 760 USER_PANIC_ERR(err, "proc_mgmt_bind_client"); 761 } 762 763 struct span_domain_state *st; 764 err = domain_new_dispatcher_setup_only(core_id, &st); 765 if (err_is_fail(err)) { 766 USER_PANIC_ERR(err, "failed to setup new dispatcher"); 767 } 768 769 struct proc_mgmt_binding *b = get_proc_mgmt_binding(); 770 assert(b != NULL); 771 772 err = b->rpc_tx_vtbl.span(b, cap_domainid, core_id, st->vroot, st->frame, 773 &msgerr); 774 if (err_is_fail(err)) { 775 USER_PANIC_ERR(err, "error sending span request to process manager"); 776 } 777 778 if (err_is_fail(msgerr)) { 779 return msgerr; 780 } 781 782 while(!st->initialized) { 783 event_dispatch(get_default_waitset()); 784 } 785 free(st); 786 787 return SYS_ERR_OK; 788} 789 790/** 791 * \brief Request the process manager to kill a domain 792 * 793 * \param domain_cap Domain ID cap for the victim 794 */ 795errval_t spawn_kill(struct capref domain_cap) 796{ 797 errval_t err, msgerr; 798 err = proc_mgmt_bind_client(); 799 if (err_is_fail(err)) { 800 USER_PANIC_ERR(err, "proc_mgmt_bind_client"); 801 } 802 803 struct proc_mgmt_binding *b = get_proc_mgmt_binding(); 804 assert(b != NULL); 805 806 err = b->rpc_tx_vtbl.kill(b, domain_cap, &msgerr); 807 if (err_is_fail(err)) { 808 USER_PANIC_ERR(err, "error sending kill request to process manager"); 809 } 810 811 return msgerr; 812} 813 814/** 815 * \brief Inform the process manager about exiting execution. 816 */ 817errval_t spawn_exit(uint8_t status) 818{ 819 errval_t err = proc_mgmt_bind_client(); 820 if (err_is_fail(err)) { 821 return err; 822 } 823 824 struct proc_mgmt_binding *b = get_proc_mgmt_binding(); 825 assert(b != NULL); 826 827 err = b->rpc_tx_vtbl.exit(b, cap_domainid, status); 828 if (err_is_fail(err)) { 829 return err; 830 } 831 832 return SYS_ERR_OK; 833} 834 835/** 836 * \brief Wait for spawned proccess to exit on core. 837 */ 838errval_t spawn_wait_coreid(coreid_t coreid, struct capref domain_cap, 839 uint8_t *exitcode, bool nohang) 840{ 841 return spawn_wait_core(disp_get_core_id(), domain_cap, exitcode, nohang); 842} 843 844 845/** 846 * \brief Wait for the termination of a domain on a remote core. 847 */ 848errval_t spawn_wait_core(coreid_t coreid, struct capref domain_cap, 849 uint8_t *exitcode, bool nohang) 850{ 851 return spawn_wait(domain_cap, exitcode, nohang); 852} 853 854 855/** 856 * \brief Wait for spawned proccess to exit on current core. 857 */ 858errval_t spawn_wait(struct capref domain_cap, uint8_t *status, bool nohang) 859{ 860 errval_t err, msgerr; 861 err = proc_mgmt_bind_client(); 862 if (err_is_fail(err)) { 863 return err; 864 } 865 866 struct proc_mgmt_binding *b = get_proc_mgmt_binding(); 867 assert(b != NULL); 868 869 err = b->rpc_tx_vtbl.wait(b, domain_cap, nohang, &msgerr, status); 870 if (err_is_fail(err)) { 871 USER_PANIC_ERR(err, "error sending wait request to process manager"); 872 } 873 874 return msgerr; 875} 876 877errval_t spawn_wait_compat(uint8_t domainid, 878 uint8_t *exitcode, bool nohang) 879{ 880 errval_t err, reterr; 881 882 err = bind_client(disp_get_core_id()); 883 if (err_is_fail(err)) { 884 return err; 885 } 886 struct spawn_binding *cl = get_spawn_binding(disp_get_core_id()); 887 assert(cl != NULL); 888 889 err = cl->rpc_tx_vtbl.wait(cl, domainid, nohang, exitcode, &reterr); 890 if (err_is_fail(err)) { 891 return err; 892 } 893 894 return reterr; 895} 896 897static int compare_domainid(const void *a, const void *b) 898{ 899 const domainid_t *da = (const domainid_t *) a; 900 const domainid_t *db = (const domainid_t *) b; 901 902 return (*da > *db) - (*da < *db); 903} 904 905/** 906 * \brief Get the list of domains for ps like implementation 907 */ 908errval_t spawn_get_domain_list(bool sorted, domainid_t **domains, size_t *len) 909{ 910 errval_t err; 911 err = proc_mgmt_bind_client(); 912 if (err_is_fail(err)) { 913 return err; 914 } 915 916 struct proc_mgmt_binding *b = get_proc_mgmt_binding(); 917 assert(b != NULL); 918 919 struct proc_mgmt_get_domainlist_response__rx_args reply; 920 size_t length; 921 err = b->rpc_tx_vtbl.get_domainlist(b, reply.domains, &length); 922 if (err_is_fail(err)) { 923 return err; 924 } 925 926 // length is in bytes 927 *len = length/sizeof(domainid_t); 928 if (sorted) { 929 qsort(reply.domains, *len, sizeof(domainid_t), compare_domainid); 930 } 931 932 *domains = memdup(reply.domains, length); 933 934 return SYS_ERR_OK; 935} 936 937/** 938 * \brief Get the status of a domain for ps like implementation 939 */ 940errval_t spawn_get_status(domainid_t domain_id, struct spawn_ps_entry *pse, 941 char **argbuf, size_t *arglen, errval_t *reterr) 942{ 943 944 errval_t err; 945 err = proc_mgmt_bind_client(); 946 if (err_is_fail(err)) { 947 return err; 948 } 949 950 struct proc_mgmt_binding *b = get_proc_mgmt_binding(); 951 assert(b != NULL); 952 953 struct proc_mgmt_get_status_response__rx_args reply; 954 err = b->rpc_tx_vtbl.get_status(b, domain_id, (proc_mgmt_ps_entry_t*) pse, 955 reply.argv, arglen, reterr); 956 if (err_is_fail(err)) { 957 return err; 958 } 959 960 *argbuf = memdup(reply.argv, *arglen); 961 962 return SYS_ERR_OK; 963} 964 965/** 966 * \brief Dump capabilities for a given domain 967 */ 968errval_t spawn_dump_capabilities_compat(domainid_t domainid) 969{ 970 errval_t err, reterr; 971 972 err = bind_client(disp_get_core_id()); 973 if (err_is_fail(err)) { 974 return err; 975 } 976 struct spawn_binding *cl = get_spawn_binding(disp_get_core_id()); 977 assert(cl != NULL); 978 979 err = cl->rpc_tx_vtbl.dump_capabilities(cl, domainid, &reterr); 980 if (err_is_fail(err)) { 981 return err; 982 } 983 984 return reterr; 985} 986 987/** 988 * \brief Utility function to create an inherit cnode 989 * and copy caps into it. 990 * 991 * \param inheritcn_capp Pointer to capref, filled-in with location of inheritcn 992 * capability. 993 * \param fdcap fdcap to copy into inherit cnode. 994 * \param sidcap sidcap to copy into inherit cnode. 995 * \param kernelcap kernelcap to copy into inherit cnode. 996 * 997 * \retval SYS_ERR_OK inherticn_capp is allocated and contains copies of the 998 * provided caps. 999 */ 1000errval_t alloc_inheritcn_with_caps(struct capref *inheritcn_capp, 1001 struct capref fdcap, 1002 struct capref sidcap, 1003 struct capref kernelcap) 1004{ 1005 errval_t err; 1006 1007 // construct inherit CNode 1008 struct cnoderef inheritcn; 1009 err = cnode_create_l2(inheritcn_capp, &inheritcn); 1010 if (err_is_fail(err)) { 1011 return err; 1012 } 1013 1014 if (!capref_is_null(fdcap)) { 1015 // copy fdcap to inherit Cnode 1016 struct capref dest = { 1017 .cnode = inheritcn, 1018 .slot = INHERITCN_SLOT_FDSPAGE 1019 }; 1020 err = cap_copy(dest, fdcap); 1021 if (err_is_fail(err)) { 1022 return err; 1023 } 1024 } 1025 1026 if (!capref_is_null(sidcap)) { 1027 // copy fdcap to inherit Cnode 1028 struct capref dest = { 1029 .cnode = inheritcn, 1030 .slot = INHERITCN_SLOT_SESSIONID 1031 }; 1032 err = cap_copy(dest, sidcap); 1033 if (err_is_fail(err)) { 1034 return err; 1035 } 1036 } 1037 1038 if (!capref_is_null(kernelcap)) { 1039 // copy fdcap to inherit Cnode 1040 struct capref dest = { 1041 .cnode = inheritcn, 1042 .slot = INHERITCN_SLOT_KERNELCAP 1043 }; 1044 err = cap_copy(dest, kernelcap); 1045 if (err_is_fail(err)) { 1046 return err; 1047 } 1048 } 1049 1050 return SYS_ERR_OK; 1051} 1052