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 <barrelfish/proc_mgmt_client.h>
24 #include <if/spawn_defs.h>
25 #include <if/arrakis_defs.h>
26 #include <if/monitor_defs.h>
27 #include <if/octopus_defs.h>
28 #include <vfs/vfs_path.h>
30 // For spawn_program_on_all_cores
31 #include <octopus/getset.h> // for oct_read TODO
32 #include <octopus/trigger.h> // for NOP_TRIGGER
36 extern char **environ;
38 struct spawn_bind_retst {
40 struct spawn_binding *b;
44 struct arrakis_bind_retst {
46 struct arrakis_binding *b;
50 static void spawn_bind_cont(void *st, errval_t err, struct spawn_binding *b)
52 struct spawn_bind_retst *retst = st;
53 assert(retst != NULL);
54 assert(!retst->present);
57 retst->present = true;
60 static void arrakis_bind_cont(void *st, errval_t err, struct arrakis_binding *b)
62 struct arrakis_bind_retst *retst = st;
63 assert(retst != NULL);
64 assert(!retst->present);
67 retst->present = true;
70 static struct spawn_binding *spawn_b = NULL;
72 static errval_t bind_client(coreid_t coreid)
74 struct spawn_binding *cl;
75 errval_t err = SYS_ERR_OK;
77 // do we have a spawn client connection for this core?
78 assert(coreid < MAX_CPUS);
79 cl = get_spawn_binding(coreid);
82 snprintf(namebuf, sizeof(namebuf), "spawn.%u", coreid);
83 namebuf[sizeof(namebuf) - 1] = '\0';
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);
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");
101 // XXX: block for bind completion
102 while (!bindst.present) {
103 messages_wait_and_handle_next();
106 if (err_is_fail(bindst.err)) {
112 spawn_rpc_client_init(bindst.b);
113 set_spawn_binding(coreid, bindst.b);
119 errval_t spawn_bind_iref(iref_t iref, struct spawn_binding **ret_client)
121 assert(ret_client != NULL);
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");
131 // XXX: block for bind completion
132 while (!bindst.present) {
133 messages_wait_and_handle_next();
136 if (err_is_fail(bindst.err)) {
140 spawn_rpc_client_init(bindst.b);
141 *ret_client = bindst.b;
142 // set_spawn_binding(coreid, bindst.b);
148 * \brief Request the spawn daemon on a specific core to spawn a program
150 * \param coreid Core ID on which to spawn the program
151 * \param path Absolute path in the file system to an executable image
152 * suitable for the given core
153 * \param argv Command-line arguments, NULL-terminated
154 * \param envp Optional environment, NULL-terminated
155 * (pass NULL to inherit)
156 * \param inheritcn_cap Cap to a CNode containing capabilities to be inherited
157 * \param argcn_cap Cap to a CNode containing capabilities passed as
159 * \param flags Flags to spawn
160 * \param domain_Cap If non-NULL, filled in with domain cap of program
162 * \bug flags are currently ignored
164 errval_t spawn_program_with_caps(coreid_t coreid, const char *path,
165 char *const argv[], char *const envp[],
166 struct capref inheritcn_cap,
167 struct capref argcn_cap, spawn_flags_t flags,
168 struct capref *domain_cap)
170 return proc_mgmt_spawn_program_with_caps(coreid, path, argv, envp, inheritcn_cap,
171 argcn_cap, flags, domain_cap);
174 errval_t spawn_arrakis_program(coreid_t coreid, const char *path,
175 char *const argv[], char *const envp[],
176 struct capref inheritcn_cap,
177 struct capref argcn_cap, spawn_flags_t flags,
178 domainid_t *ret_domainid)
180 struct arrakis_binding *cl;
181 errval_t err, msgerr;
183 // default to copying our environment
188 // do we have a arrakis client connection for this core?
189 assert(coreid < MAX_CPUS);
190 cl = get_arrakis_binding(coreid);
193 snprintf(namebuf, sizeof(namebuf), "arrakis.%u", coreid);
194 namebuf[sizeof(namebuf) - 1] = '\0';
197 err = nameservice_blocking_lookup(namebuf, &iref);
198 if (err_is_fail(err)) {
199 //DEBUG_ERR(err, "arrakis daemon on core %u not found\n", coreid);
204 struct arrakis_bind_retst bindst = { .present = false };
205 err = arrakis_bind(iref, arrakis_bind_cont, &bindst, get_default_waitset(),
206 IDC_BIND_FLAGS_DEFAULT);
207 if (err_is_fail(err)) {
208 USER_PANIC_ERR(err, "arrakis_bind failed");
211 // XXX: block for bind completion
212 while (!bindst.present) {
213 messages_wait_and_handle_next();
216 if(err_is_fail(bindst.err)) {
217 USER_PANIC_ERR(bindst.err, "asynchronous error during arrakis_bind");
219 assert(bindst.b != NULL);
221 arrakis_rpc_client_init(bindst.b);
222 set_arrakis_binding(coreid, bindst.b);
225 // construct argument "string"
226 // \0-separated strings in contiguous character buffer
227 // this is needed, as flounder can't send variable-length arrays of strings
228 size_t argstrlen = 0;
229 for (int i = 0; argv[i] != NULL; i++) {
230 argstrlen += strlen(argv[i]) + 1;
233 char argstr[argstrlen];
234 size_t argstrpos = 0;
235 for (int i = 0; argv[i] != NULL; i++) {
236 strcpy(&argstr[argstrpos], argv[i]);
237 argstrpos += strlen(argv[i]);
238 argstr[argstrpos++] = '\0';
240 assert(argstrpos == argstrlen);
242 // repeat for environment
243 size_t envstrlen = 0;
244 for (int i = 0; envp[i] != NULL; i++) {
245 envstrlen += strlen(envp[i]) + 1;
248 char envstr[envstrlen];
249 size_t envstrpos = 0;
250 for (int i = 0; envp[i] != NULL; i++) {
251 strcpy(&envstr[envstrpos], envp[i]);
252 envstrpos += strlen(envp[i]);
253 envstr[envstrpos++] = '\0';
255 assert(envstrpos == envstrlen);
258 domainid_t domain_id;
260 // make an unqualified path absolute using the $PATH variable
261 // TODO: implement search (currently assumes PATH is a single directory)
262 char *searchpath = getenv("PATH");
263 if (searchpath == NULL) {
264 searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root
266 size_t buflen = strlen(path) + strlen(searchpath) + 2;
267 char pathbuf[buflen];
268 if (path[0] != VFS_PATH_SEP) {
269 snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path);
270 pathbuf[buflen - 1] = '\0';
271 //vfs_path_normalise(pathbuf);
275 err = cl->rpc_tx_vtbl.spawn_arrakis_domain(cl, path, argstr, argstrlen,
276 envstr, envstrlen, &msgerr, &domain_id);
277 if (err_is_fail(err)) {
278 USER_PANIC_ERR(err, "error sending arrakis request");
279 } else if (err_is_fail(msgerr)) {
283 if (ret_domainid != NULL) {
284 *ret_domainid = domain_id;
292 * \brief Request the spawn daemon on a specific core to spawn a program
294 * \param coreid Core ID on which to spawn the program
295 * \param path Absolute path in the file system to an executable image
296 * suitable for the given core
297 * \param argv Command-line arguments, NULL-terminated
298 * \param envp Optional environment, NULL-terminated (pass NULL to inherit)
299 * \param flags Flags to spawn
300 * \param ret_domain_cap If non-NULL, filled in with domain cap of program
302 * \bug flags are currently ignored
304 errval_t spawn_program(coreid_t coreid, const char *path,
305 char *const argv[], char *const envp[],
306 spawn_flags_t flags, struct capref* ret_domain_cap)
308 return spawn_program_with_caps(coreid, path, argv, envp, NULL_CAP,
309 NULL_CAP, flags, ret_domain_cap);
315 * \brief Request a program be spawned on all cores in the system
317 * \param same_core Iff false, don't spawn on the same core as the caller
318 * \param path Absolute path in the file system to an executable image
319 * suitable for the given core
320 * \param argv Command-line arguments, NULL-terminated
321 * \param envp Optional environment, NULL-terminated (pass NULL to inherit)
322 * \param flags Flags to spawn
323 * \param ret_domainid If non-NULL, filled in with domain ID of program
324 * \param count How much programs it spawned
326 * \note This function is for legacy compatibility with existing benchmark/test
327 * code, and SHOULD NOT BE USED IN NEW CODE UNLESS YOU HAVE A GOOD REASON!
328 * It doesn't make much sense from a scalability perspective, and is
329 * probably useless on a heterogeneous system.
331 errval_t spawn_program_on_all_cores(bool same_core, const char *path,
332 char *const argv[], char *const envp[],
333 spawn_flags_t flags, struct capref *ret_domain_cap,
334 coreid_t* spawn_count)
336 // TODO: handle flags, domain ID
337 errval_t err = SYS_ERR_OK;
339 struct octopus_binding *r = get_octopus_binding();
341 return LIB_ERR_NAMESERVICE_NOT_BOUND;
344 // FIXME: world's most (kinda less now) broken implementation...
348 static char* spawnds = "r'spawn.[0-9]+' { iref: _ }";
349 struct octopus_get_names_response__rx_args reply;
350 err = r->rpc_tx_vtbl.get_names(r, spawnds, NOP_TRIGGER, reply.output, &reply.tid,
352 if (err_is_fail(err) || err_is_fail(reply.error_code)) {
353 err = err_push(err, SPAWN_ERR_FIND_SPAWNDS);
357 err = oct_parse_names(reply.output, &names, &count);
358 if (err_is_fail(err)) {
362 for (size_t c = 0; c < count; c++) {
364 int ret = sscanf(names[c], "spawn.%hhu", &coreid);
366 err = SPAWN_ERR_MALFORMED_SPAWND_RECORD;
370 if (!same_core && coreid == disp_get_core_id()) {
374 err = proc_mgmt_spawn_program(c, path, argv, envp, flags, NULL);
375 if (err_is_ok(err) && spawn_count != NULL) {
379 if (err_is_fail(err)) {
380 DEBUG_ERR(err, "error spawning %s on core %u\n", path, c);
386 oct_free_names(names, count);
390 errval_t spawn_binding(coreid_t coreid, struct spawn_binding **ret_client)
392 errval_t err = bind_client(coreid);
393 if (err_is_fail(err)) {
397 *ret_client = get_spawn_binding(coreid);
402 * \brief Kill a domain.
404 errval_t spawn_kill(struct capref domain_cap)
406 return proc_mgmt_kill(domain_cap);
409 errval_t spawn_wait_compat(uint8_t domainid,
410 uint8_t *exitcode, bool nohang)
412 errval_t err, reterr;
414 err = bind_client(disp_get_core_id());
415 if (err_is_fail(err)) {
418 struct spawn_binding *cl = get_spawn_binding(disp_get_core_id());
421 err = cl->rpc_tx_vtbl.wait(cl, domainid, nohang, exitcode, &reterr);
422 if (err_is_fail(err)) {
430 * \brief Exit this domain.
432 errval_t spawn_exit(uint8_t exitcode)
434 return proc_mgmt_exit(exitcode);
438 * \brief Wait for spawned proccess to exit on core.
440 errval_t spawn_wait_coreid(coreid_t coreid, struct capref domain_cap,
441 uint8_t *exitcode, bool nohang)
443 return spawn_wait_core(disp_get_core_id(), domain_cap, exitcode, nohang);
447 * \brief Wait for the termination of a domain on a remote core.
449 errval_t spawn_wait_core(coreid_t coreid, struct capref domain_cap,
450 uint8_t *exitcode, bool nohang)
452 return proc_mgmt_wait(domain_cap, exitcode);
456 * \brief Wait for spawned proccess to exit on current core.
458 errval_t spawn_wait(struct capref domain_cap, uint8_t *exitcode, bool nohang)
460 return spawn_wait_coreid(disp_get_core_id(), domain_cap, exitcode, nohang);
464 * \brief Get the list of domains for ps like implementation
466 errval_t spawn_get_domain_list(uint8_t **domains, size_t *len)
470 struct spawn_binding *cl;
471 err = spawn_binding(disp_get_core_id(), &cl);
472 if (err_is_fail(err)) {
473 USER_PANIC_ERR(err, "spawn_binding");
477 struct spawn_get_domainlist_response__rx_args reply;
478 err = cl->rpc_tx_vtbl.get_domainlist(cl, reply.domains, len);
479 if (err_is_fail(err)) {
480 USER_PANIC_ERR(err, "get_domainlist");
483 *domains = memdup(reply.domains, *len);
488 * \brief Get the status of a domain for ps like implementation
490 errval_t spawn_get_status(uint8_t domain, struct spawn_ps_entry *pse,
491 char **argbuf, size_t *arglen, errval_t *reterr)
495 struct spawn_binding *cl;
496 err = spawn_binding(disp_get_core_id(), &cl);
497 if (err_is_fail(err)) {
498 USER_PANIC_ERR(err, "spawn_binding");
502 struct spawn_status_response__rx_args reply;
503 err = cl->rpc_tx_vtbl.status(cl, domain, (spawn_ps_entry_t *)pse, reply.argv,
505 if (err_is_fail(err)) {
506 USER_PANIC_ERR(err, "status");
509 *argbuf = memdup(reply.argv, *arglen);
514 * \brief Dump capabilities for a given domain
516 errval_t spawn_dump_capabilities_compat(domainid_t domainid)
518 errval_t err, reterr;
520 err = bind_client(disp_get_core_id());
521 if (err_is_fail(err)) {
524 struct spawn_binding *cl = get_spawn_binding(disp_get_core_id());
527 err = cl->rpc_tx_vtbl.dump_capabilities(cl, domainid, &reterr);
528 if (err_is_fail(err)) {
536 * \brief Utility function to create an inherit cnode
537 * and copy caps into it.
539 * \param inheritcn_capp Pointer to capref, filled-in with location of inheritcn
541 * \param fdcap fdcap to copy into inherit cnode.
542 * \param sidcap sidcap to copy into inherit cnode.
543 * \param kernelcap kernelcap to copy into inherit cnode.
545 * \retval SYS_ERR_OK inherticn_capp is allocated and contains copies of the
548 errval_t alloc_inheritcn_with_caps(struct capref *inheritcn_capp,
550 struct capref sidcap,
551 struct capref kernelcap)
555 // construct inherit CNode
556 struct cnoderef inheritcn;
557 err = cnode_create_l2(inheritcn_capp, &inheritcn);
558 if (err_is_fail(err)) {
562 if (!capref_is_null(fdcap)) {
563 // copy fdcap to inherit Cnode
564 struct capref dest = {
566 .slot = INHERITCN_SLOT_FDSPAGE
568 err = cap_copy(dest, fdcap);
569 if (err_is_fail(err)) {
574 if (!capref_is_null(sidcap)) {
575 // copy fdcap to inherit Cnode
576 struct capref dest = {
578 .slot = INHERITCN_SLOT_SESSIONID
580 err = cap_copy(dest, sidcap);
581 if (err_is_fail(err)) {
586 if (!capref_is_null(kernelcap)) {
587 // copy fdcap to inherit Cnode
588 struct capref dest = {
590 .slot = INHERITCN_SLOT_KERNELCAP
592 err = cap_copy(dest, kernelcap);
593 if (err_is_fail(err)) {