3 * \brief Client for interacting with the spawn daemon on each core
7 * Copyright (c) 2010, 2011, 2012, ETH Zurich.
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, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
13 * Attn: Systems Group.
19 #include <barrelfish/barrelfish.h>
20 #include <barrelfish/nameservice_client.h>
21 #include <barrelfish/spawn_client.h>
22 #include <barrelfish/cpu_arch.h>
23 #include <if/spawn_defs.h>
24 #include <if/arrakis_defs.h>
25 #include <if/monitor_defs.h>
26 #include <if/octopus_defs.h>
27 #include <vfs/vfs_path.h>
29 // For spawn_program_on_all_cores
30 #include <octopus/getset.h> // for oct_read TODO
31 #include <octopus/trigger.h> // for NOP_TRIGGER
35 extern char **environ;
37 struct spawn_bind_retst {
39 struct spawn_binding *b;
43 struct arrakis_bind_retst {
45 struct arrakis_binding *b;
49 static void spawn_bind_cont(void *st, errval_t err, struct spawn_binding *b)
51 struct spawn_bind_retst *retst = st;
52 assert(retst != NULL);
53 assert(!retst->present);
56 retst->present = true;
59 static void arrakis_bind_cont(void *st, errval_t err, struct arrakis_binding *b)
61 struct arrakis_bind_retst *retst = st;
62 assert(retst != NULL);
63 assert(!retst->present);
66 retst->present = true;
69 static struct spawn_binding *spawn_b = NULL;
71 static errval_t bind_client(coreid_t coreid)
73 struct spawn_binding *cl;
74 errval_t err = SYS_ERR_OK;
76 // do we have a spawn client connection for this core?
77 assert(coreid < MAX_CPUS);
78 cl = get_spawn_binding(coreid);
81 snprintf(namebuf, sizeof(namebuf), "spawn.%u", coreid);
82 namebuf[sizeof(namebuf) - 1] = '\0';
85 err = nameservice_blocking_lookup(namebuf, &iref);
86 if (err_is_fail(err)) {
87 //DEBUG_ERR(err, "spawn daemon on core %u not found\n", coreid);
92 struct spawn_bind_retst bindst = { .present = false };
93 err = spawn_bind(iref, spawn_bind_cont, &bindst, get_default_waitset(),
94 IDC_BIND_FLAGS_DEFAULT);
95 if (err_is_fail(err)) {
96 DEBUG_ERR(err, "spawn_bind failed");
100 // XXX: block for bind completion
101 while (!bindst.present) {
102 messages_wait_and_handle_next();
105 if (err_is_fail(bindst.err)) {
111 spawn_rpc_client_init(bindst.b);
112 set_spawn_binding(coreid, bindst.b);
118 errval_t spawn_bind_iref(iref_t iref, struct spawn_binding **ret_client)
120 assert(ret_client != NULL);
122 struct spawn_bind_retst bindst = { .present = false };
123 errval_t err = spawn_bind(iref, spawn_bind_cont, &bindst,
124 get_default_waitset(), IDC_BIND_FLAGS_DEFAULT);
125 if (err_is_fail(err)) {
126 DEBUG_ERR(err, "spawn_bind failed");
130 // XXX: block for bind completion
131 while (!bindst.present) {
132 messages_wait_and_handle_next();
135 if (err_is_fail(bindst.err)) {
139 spawn_rpc_client_init(bindst.b);
140 *ret_client = bindst.b;
141 // set_spawn_binding(coreid, bindst.b);
147 * \brief Request the spawn daemon on a specific core to spawn a program
149 * \param coreid Core ID on which to spawn the program
150 * \param path Absolute path in the file system to an executable image
151 * suitable for the given core
152 * \param argv Command-line arguments, NULL-terminated
153 * \param envp Optional environment, NULL-terminated
154 * (pass NULL to inherit)
155 * \param inheritcn_cap Cap to a CNode containing capabilities to be inherited
156 * \param argcn_cap Cap to a CNode containing capabilities passed as
158 * \param flags Flags to spawn
159 * \param ret_domainid If non-NULL, filled in with domain ID of program
161 * \bug flags are currently ignored
163 errval_t spawn_program_with_caps(coreid_t coreid, const char *path,
164 char *const argv[], char *const envp[],
165 struct capref inheritcn_cap,
166 struct capref argcn_cap, spawn_flags_t flags,
167 domainid_t *ret_domainid)
169 errval_t err, msgerr;
171 // default to copying our environment
176 err = bind_client(coreid);
177 if (err_is_fail(err)) {
181 struct spawn_binding *cl = get_spawn_binding(coreid);
184 // construct argument "string"
185 // \0-separated strings in contiguous character buffer
186 // this is needed, as flounder can't send variable-length arrays of strings
187 size_t argstrlen = 0;
188 for (int i = 0; argv[i] != NULL; i++) {
189 argstrlen += strlen(argv[i]) + 1;
192 char argstr[argstrlen];
193 size_t argstrpos = 0;
194 for (int i = 0; argv[i] != NULL; i++) {
195 strcpy(&argstr[argstrpos], argv[i]);
196 argstrpos += strlen(argv[i]);
197 argstr[argstrpos++] = '\0';
199 assert(argstrpos == argstrlen);
201 // repeat for environment
202 size_t envstrlen = 0;
203 for (int i = 0; envp[i] != NULL; i++) {
204 envstrlen += strlen(envp[i]) + 1;
207 char envstr[envstrlen];
208 size_t envstrpos = 0;
209 for (int i = 0; envp[i] != NULL; i++) {
210 strcpy(&envstr[envstrpos], envp[i]);
211 envstrpos += strlen(envp[i]);
212 envstr[envstrpos++] = '\0';
214 assert(envstrpos == envstrlen);
217 domainid_t domain_id;
219 // make an unqualified path absolute using the $PATH variable
220 // TODO: implement search (currently assumes PATH is a single directory)
221 char *searchpath = getenv("PATH");
222 if (searchpath == NULL) {
223 searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root
225 size_t buflen = strlen(path) + strlen(searchpath) + 2;
226 char pathbuf[buflen];
227 if (path[0] != VFS_PATH_SEP) {
228 snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path);
229 pathbuf[buflen - 1] = '\0';
230 //vfs_path_normalise(pathbuf);
234 if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) {
235 err = cl->rpc_tx_vtbl.spawn_domain(cl, path, argstr, argstrlen,
236 envstr, envstrlen, flags,
237 &msgerr, &domain_id);
239 err = cl->rpc_tx_vtbl.spawn_domain_with_caps(cl, path, argstr, argstrlen,
240 envstr, envstrlen, inheritcn_cap,
241 argcn_cap, flags, &msgerr, &domain_id);
243 if (err_is_fail(err)) {
244 USER_PANIC_ERR(err, "error sending spawn request");
245 } else if (err_is_fail(msgerr)) {
249 if (ret_domainid != NULL) {
250 *ret_domainid = domain_id;
257 errval_t spawn_arrakis_program(coreid_t coreid, const char *path,
258 char *const argv[], char *const envp[],
259 struct capref inheritcn_cap,
260 struct capref argcn_cap, spawn_flags_t flags,
261 domainid_t *ret_domainid)
263 struct arrakis_binding *cl;
264 errval_t err, msgerr;
266 // default to copying our environment
271 // do we have a arrakis client connection for this core?
272 assert(coreid < MAX_CPUS);
273 cl = get_arrakis_binding(coreid);
276 snprintf(namebuf, sizeof(namebuf), "arrakis.%u", coreid);
277 namebuf[sizeof(namebuf) - 1] = '\0';
280 err = nameservice_blocking_lookup(namebuf, &iref);
281 if (err_is_fail(err)) {
282 //DEBUG_ERR(err, "arrakis daemon on core %u not found\n", coreid);
287 struct arrakis_bind_retst bindst = { .present = false };
288 err = arrakis_bind(iref, arrakis_bind_cont, &bindst, get_default_waitset(),
289 IDC_BIND_FLAGS_DEFAULT);
290 if (err_is_fail(err)) {
291 USER_PANIC_ERR(err, "arrakis_bind failed");
294 // XXX: block for bind completion
295 while (!bindst.present) {
296 messages_wait_and_handle_next();
299 if(err_is_fail(bindst.err)) {
300 USER_PANIC_ERR(bindst.err, "asynchronous error during arrakis_bind");
302 assert(bindst.b != NULL);
304 arrakis_rpc_client_init(bindst.b);
305 set_arrakis_binding(coreid, bindst.b);
308 // construct argument "string"
309 // \0-separated strings in contiguous character buffer
310 // this is needed, as flounder can't send variable-length arrays of strings
311 size_t argstrlen = 0;
312 for (int i = 0; argv[i] != NULL; i++) {
313 argstrlen += strlen(argv[i]) + 1;
316 char argstr[argstrlen];
317 size_t argstrpos = 0;
318 for (int i = 0; argv[i] != NULL; i++) {
319 strcpy(&argstr[argstrpos], argv[i]);
320 argstrpos += strlen(argv[i]);
321 argstr[argstrpos++] = '\0';
323 assert(argstrpos == argstrlen);
325 // repeat for environment
326 size_t envstrlen = 0;
327 for (int i = 0; envp[i] != NULL; i++) {
328 envstrlen += strlen(envp[i]) + 1;
331 char envstr[envstrlen];
332 size_t envstrpos = 0;
333 for (int i = 0; envp[i] != NULL; i++) {
334 strcpy(&envstr[envstrpos], envp[i]);
335 envstrpos += strlen(envp[i]);
336 envstr[envstrpos++] = '\0';
338 assert(envstrpos == envstrlen);
341 domainid_t domain_id;
343 // make an unqualified path absolute using the $PATH variable
344 // TODO: implement search (currently assumes PATH is a single directory)
345 char *searchpath = getenv("PATH");
346 if (searchpath == NULL) {
347 searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root
349 size_t buflen = strlen(path) + strlen(searchpath) + 2;
350 char pathbuf[buflen];
351 if (path[0] != VFS_PATH_SEP) {
352 snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path);
353 pathbuf[buflen - 1] = '\0';
354 //vfs_path_normalise(pathbuf);
358 err = cl->rpc_tx_vtbl.spawn_arrakis_domain(cl, path, argstr, argstrlen,
359 envstr, envstrlen, &msgerr, &domain_id);
360 if (err_is_fail(err)) {
361 USER_PANIC_ERR(err, "error sending arrakis request");
362 } else if (err_is_fail(msgerr)) {
366 if (ret_domainid != NULL) {
367 *ret_domainid = domain_id;
375 * \brief Request the spawn daemon on a specific core to spawn a program
377 * \param coreid Core ID on which to spawn the program
378 * \param path Absolute path in the file system to an executable image
379 * suitable for the given core
380 * \param argv Command-line arguments, NULL-terminated
381 * \param envp Optional environment, NULL-terminated (pass NULL to inherit)
382 * \param flags Flags to spawn
383 * \param ret_domainid If non-NULL, filled in with domain ID of program
385 * \bug flags are currently ignored
387 errval_t spawn_program(coreid_t coreid, const char *path,
388 char *const argv[], char *const envp[],
389 spawn_flags_t flags, domainid_t *ret_domainid)
391 return spawn_program_with_caps(coreid, path, argv, envp, NULL_CAP,
392 NULL_CAP, flags, ret_domainid);
398 * \brief Request a program be spawned on all cores in the system
400 * \param same_core Iff false, don't spawn on the same core as the caller
401 * \param path Absolute path in the file system to an executable image
402 * suitable for the given core
403 * \param argv Command-line arguments, NULL-terminated
404 * \param envp Optional environment, NULL-terminated (pass NULL to inherit)
405 * \param flags Flags to spawn
406 * \param ret_domainid If non-NULL, filled in with domain ID of program
407 * \param count How much programs it spawned
409 * \note This function is for legacy compatibility with existing benchmark/test
410 * code, and SHOULD NOT BE USED IN NEW CODE UNLESS YOU HAVE A GOOD REASON!
411 * It doesn't make much sense from a scalability perspective, and is
412 * probably useless on a heterogeneous system.
414 errval_t spawn_program_on_all_cores(bool same_core, const char *path,
415 char *const argv[], char *const envp[],
416 spawn_flags_t flags, domainid_t *ret_domainid,
417 coreid_t* spawn_count)
419 // TODO: handle flags, domain ID
420 errval_t err = SYS_ERR_OK;
422 struct octopus_binding *r = get_octopus_binding();
424 return LIB_ERR_NAMESERVICE_NOT_BOUND;
427 // FIXME: world's most (kinda less now) broken implementation...
431 static char* spawnds = "r'spawn.[0-9]+' { iref: _ }";
432 struct octopus_get_names_response__rx_args reply;
433 err = r->rpc_tx_vtbl.get_names(r, spawnds, NOP_TRIGGER, reply.output, &reply.tid,
435 if (err_is_fail(err) || err_is_fail(reply.error_code)) {
436 err = err_push(err, SPAWN_ERR_FIND_SPAWNDS);
440 err = oct_parse_names(reply.output, &names, &count);
441 if (err_is_fail(err)) {
445 for (size_t c = 0; c < count; c++) {
447 int ret = sscanf(names[c], "spawn.%hhu", &coreid);
449 err = SPAWN_ERR_MALFORMED_SPAWND_RECORD;
453 if (!same_core && coreid == disp_get_core_id()) {
457 err = spawn_program(c, path, argv, envp, flags, NULL);
458 if (err_is_ok(err) && spawn_count != NULL) {
461 if (err_is_fail(err)) {
462 DEBUG_ERR(err, "error spawning %s on core %u\n", path, c);
468 oct_free_names(names, count);
472 errval_t spawn_binding(coreid_t coreid, struct spawn_binding **ret_client)
474 errval_t err = bind_client(coreid);
475 if (err_is_fail(err)) {
479 *ret_client = get_spawn_binding(coreid);
484 * \brief Kill a domain.
486 errval_t spawn_kill(domainid_t domainid)
488 errval_t err, reterr;
490 err = bind_client(disp_get_core_id());
491 if (err_is_fail(err)) {
494 struct spawn_binding *cl = get_spawn_binding(disp_get_core_id());
497 err = cl->rpc_tx_vtbl.kill(cl, domainid, &reterr);
498 if (err_is_fail(err)) {
506 * \brief Exit this domain.
508 errval_t spawn_exit(uint8_t exitcode)
512 err = bind_client(disp_get_core_id());
513 if (err_is_fail(err)) {
516 struct spawn_binding *cl = get_spawn_binding(disp_get_core_id());
519 err = cl->rpc_tx_vtbl.exit(cl, disp_get_domain_id(), exitcode);
520 if (err_is_fail(err)) {
528 * \brief Wait for spawned proccess to exit on core.
530 errval_t spawn_wait_coreid(coreid_t coreid, domainid_t domainid,
531 uint8_t *exitcode, bool nohang)
533 return spawn_wait_core(disp_get_core_id(), domainid, exitcode, nohang);
537 * \brief Wait for the termination of a domain on a remote core.
539 errval_t spawn_wait_core(coreid_t coreid, domainid_t domainid,
540 uint8_t *exitcode, bool nohang)
542 errval_t err, reterr;
544 err = bind_client(coreid);
545 if (err_is_fail(err)) {
548 struct spawn_binding *cl = get_spawn_binding(coreid);
551 err = cl->rpc_tx_vtbl.wait(cl, domainid, nohang, exitcode, &reterr);
552 if (err_is_fail(err)) {
560 * \brief Wait for spawned proccess to exit on current core.
562 errval_t spawn_wait(domainid_t domainid, uint8_t *exitcode, bool nohang)
564 return spawn_wait_coreid(disp_get_core_id(), domainid, exitcode, nohang);
568 * \brief Get the list of domains for ps like implementation
570 errval_t spawn_get_domain_list(uint8_t **domains, size_t *len)
574 struct spawn_binding *cl;
575 err = spawn_binding(disp_get_core_id(), &cl);
576 if (err_is_fail(err)) {
577 USER_PANIC_ERR(err, "spawn_binding");
581 struct spawn_get_domainlist_response__rx_args reply;
582 err = cl->rpc_tx_vtbl.get_domainlist(cl, reply.domains, len);
583 if (err_is_fail(err)) {
584 USER_PANIC_ERR(err, "get_domainlist");
587 *domains = memdup(reply.domains, *len);
592 * \brief Get the status of a domain for ps like implementation
594 errval_t spawn_get_status(uint8_t domain, struct spawn_ps_entry *pse,
595 char **argbuf, size_t *arglen, errval_t *reterr)
599 struct spawn_binding *cl;
600 err = spawn_binding(disp_get_core_id(), &cl);
601 if (err_is_fail(err)) {
602 USER_PANIC_ERR(err, "spawn_binding");
606 struct spawn_status_response__rx_args reply;
607 err = cl->rpc_tx_vtbl.status(cl, domain, (spawn_ps_entry_t *)pse, reply.argv,
609 if (err_is_fail(err)) {
610 USER_PANIC_ERR(err, "status");
613 *argbuf = memdup(reply.argv, *arglen);
618 * \brief Dump capabilities for a given domain
620 errval_t spawn_dump_capabilities(domainid_t domainid)
622 errval_t err, reterr;
624 err = bind_client(disp_get_core_id());
625 if (err_is_fail(err)) {
628 struct spawn_binding *cl = get_spawn_binding(disp_get_core_id());
631 err = cl->rpc_tx_vtbl.dump_capabilities(cl, domainid, &reterr);
632 if (err_is_fail(err)) {
640 * \brief Utility function to create an inherit cnode
641 * and copy caps into it.
643 * \param inheritcn_capp Pointer to capref, filled-in with location of inheritcn
645 * \param fdcap fdcap to copy into inherit cnode.
646 * \param sidcap sidcap to copy into inherit cnode.
647 * \param kernelcap kernelcap to copy into inherit cnode.
649 * \retval SYS_ERR_OK inherticn_capp is allocated and contains copies of the
652 errval_t alloc_inheritcn_with_caps(struct capref *inheritcn_capp,
654 struct capref sidcap,
655 struct capref kernelcap)
659 // construct inherit CNode
660 struct cnoderef inheritcn;
661 err = cnode_create_l2(inheritcn_capp, &inheritcn);
662 if (err_is_fail(err)) {
666 if (!capref_is_null(fdcap)) {
667 // copy fdcap to inherit Cnode
668 struct capref dest = {
670 .slot = INHERITCN_SLOT_FDSPAGE
672 err = cap_copy(dest, fdcap);
673 if (err_is_fail(err)) {
678 if (!capref_is_null(sidcap)) {
679 // copy fdcap to inherit Cnode
680 struct capref dest = {
682 .slot = INHERITCN_SLOT_SESSIONID
684 err = cap_copy(dest, sidcap);
685 if (err_is_fail(err)) {
690 if (!capref_is_null(kernelcap)) {
691 // copy fdcap to inherit Cnode
692 struct capref dest = {
694 .slot = INHERITCN_SLOT_KERNELCAP
696 err = cap_copy(dest, kernelcap);
697 if (err_is_fail(err)) {