3 * \brief Client for interacting with the process management server.
7 * Copyright (c) 2017, 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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
15 #include <barrelfish/barrelfish.h>
16 #include <barrelfish/nameservice_client.h>
17 #include <barrelfish/proc_mgmt_client.h>
18 #include <if/octopus_defs.h>
19 #include <if/proc_mgmt_defs.h>
20 #include <vfs/vfs_path.h>
22 struct proc_mgmt_bind_retst {
24 struct proc_mgmt_binding *b;
28 extern char **environ;
30 static void error_handler(struct proc_mgmt_binding *b, errval_t err)
32 #if defined(__x86_64__) || defined(__i386__)
33 debug_printf("%p %p %p %p\n",
34 __builtin_return_address(0),
35 __builtin_return_address(1),
36 __builtin_return_address(2),
37 __builtin_return_address(3));
39 debug_err(__FILE__, __func__, __LINE__, err,
40 "asynchronous error in proc_mgmt binding");
44 static void proc_mgmt_bind_cont(void *st, errval_t err,
45 struct proc_mgmt_binding *b)
47 struct proc_mgmt_bind_retst *retst = (struct proc_mgmt_bind_retst*) st;
48 assert(retst != NULL);
49 assert(!retst->present);
52 retst->present = true;
55 static void proc_mgmt_accept_recv_handler(void *arg)
57 struct proc_mgmt_lmp_binding *b = arg;
58 struct lmp_recv_msg msg = LMP_RECV_MSG_INIT;
62 // try to retrieve a message from the channel
63 err = lmp_chan_recv(&b->chan, &msg, &cap);
64 if (err_is_fail(err)) {
65 if (err_no(err) == LIB_ERR_NO_LMP_MSG) {
66 // nothing there, re-register
67 struct event_closure recv_handler = {
68 .handler = proc_mgmt_accept_recv_handler,
71 err = lmp_chan_register_recv(&b->chan, b->b.waitset, recv_handler);
72 b->b.error_handler(&b->b, err_push(err, LIB_ERR_CHAN_REGISTER_RECV));
74 // real error, report to user
75 b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_CHAN_RECV));
80 // TODO(razvan): LMP_PROC_MGMT_ACCEPT ?
81 assert(b->chan.connstate == LMP_MONITOR_ACCEPT);
82 assert(!capref_is_null(cap));
83 b->chan.remote_cap = cap;
84 b->chan.connstate = LMP_CONNECTED;
86 /* allocate a new receive slot */
87 err = lmp_chan_alloc_recv_slot(&b->chan);
88 if (err_is_fail(err)) {
89 // XXX: report the error, but continue
90 b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_ALLOC_RECV_SLOT));
93 /* Run the RX handler; has a side-effect of registering for receive events */
94 proc_mgmt_lmp_rx_handler(b);
97 static errval_t init_lmp_binding(struct proc_mgmt_lmp_binding *lmpb,
103 proc_mgmt_lmp_init(lmpb, ws);
105 /* allocate a cap slot for the new endpoint cap */
106 err = slot_alloc(&lmpb->chan.local_cap);
107 if (err_is_fail(err)) {
108 return err_push(err, LIB_ERR_SLOT_ALLOC);
111 /* allocate a local endpoint */
112 err = lmp_endpoint_create_in_slot(buflen_words, lmpb->chan.local_cap,
113 &lmpb->chan.endpoint);
114 if (err_is_fail(err)) {
115 // TODO(razvan): Free cap slot.
116 return err_push(err, LIB_ERR_ENDPOINT_CREATE);
119 /* allocate an initial receive slot */
120 err = lmp_chan_alloc_recv_slot(&lmpb->chan);
121 if (err_is_fail(err)) {
125 /* setup error handler */
126 lmpb->b.error_handler = error_handler;
128 /* setup initial receive handlers */
129 // TODO(razvan): Don't think this is needed, but dunno for sure yet.
130 // lmpb->b.rx_vtbl = monitor_rx_vtbl;
133 lmpb->b.change_waitset(&lmpb->b, lmpb->b.waitset);
138 * \brief Accept a new LMP binding to a proc mgmt client.
140 * Should only be used in the process manager.
142 * \param lmpb Storage for binding state
143 * \param ws Waitset for handling incoming messages
144 * \param buflen_words Size of incoming buffer, in number of words
146 errval_t proc_mgmt_client_lmp_accept(struct proc_mgmt_lmp_binding *lmpb,
148 size_t lmp_buflen_words)
150 errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
151 if (err_is_fail(err)) {
155 lmpb->chan.connstate = LMP_MONITOR_ACCEPT; // TODO(razvan): LMP_PROC_MGMT_ACCEPT?
156 lmpb->chan.remote_cap = NULL_CAP; // will be sent to us by the client
158 /* Register for receive notification on our special handler */
159 struct event_closure receive_handler = {
160 .handler = proc_mgmt_accept_recv_handler,
163 err = lmp_chan_register_recv(&lmpb->chan, ws, receive_handler);
164 if (err_is_fail(err)) {
165 return err; // TODO(razvan): cleanup?
173 * \brief Initiate a new LMP binding to the process manager
175 * To be used by the monitor for setting up the privileged channel used for
177 * Requires an explicit remote endpoint cap allocated by the process manager.
179 * \param lmpb Storage for binding state
180 * \param ep Remote endpoint of the process manager
181 * \param ws Waitset for handling incoming messages
182 * \param cont Continuation for when binding completes or fails
183 * \param st State passed to continuation function
184 * \param buflen_words Size of incoming buffer, in number of words
186 errval_t proc_mgmt_client_lmp_bind(struct proc_mgmt_lmp_binding *lmpb,
188 proc_mgmt_bind_continuation_fn *cont,
191 size_t lmp_buflen_words)
193 errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
194 if (err_is_fail(err)) {
198 lmpb->chan.remote_cap = ep;
200 // Send the local endpoint cap to the process manager.
201 lmpb->chan.connstate = LMP_CONNECTED; /* pre-established */
202 err = lmp_chan_send0(&lmpb->chan, 0, lmpb->chan.local_cap);
203 if (err_is_fail(err)) {
204 // TODO(razvan): This, below.
205 /* XXX: I'm lazily assuming this can never fail with a transient error,
206 * since we only do it once at dispatcher startup. If not, we need to
207 * register and retry here */
208 assert(!lmp_err_is_transient(err));
212 /* Run the RX handler; has a side-effect of registering for receive events */
213 proc_mgmt_lmp_rx_handler(lmpb);
215 /* Run the continuation */
216 cont(st, SYS_ERR_OK, &lmpb->b);
221 errval_t proc_mgmt_bind_client(void)
223 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
230 // Try using nameserver to retrievew the proc mgmt iref.
231 err = nameservice_blocking_lookup("proc_mgmt", &iref);
232 if (err_is_fail(err)) {
237 struct proc_mgmt_bind_retst bindst = {
241 err = proc_mgmt_bind(iref, proc_mgmt_bind_cont, &bindst,
242 get_default_waitset(), /*IDC_BIND_FLAG_RPC_CAP_TRANSFER*/IDC_BIND_FLAGS_DEFAULT);
243 if (err_is_fail(err)) {
244 USER_PANIC_ERR(err, "proc_mgmt_bind");
247 // Wait for bind completion.
248 while (!bindst.present) {
249 messages_wait_and_handle_next();
252 if (err_is_fail(bindst.err)) {
256 proc_mgmt_rpc_client_init(bindst.b);
258 set_proc_mgmt_binding(bindst.b);
263 errval_t proc_mgmt_add_spawnd(iref_t iref, coreid_t core_id)
265 errval_t err = proc_mgmt_bind_client();
266 if (err_is_fail(err)) {
267 DEBUG_ERR(err, "proc_mgmt_bind_client");
271 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
274 err = b->tx_vtbl.add_spawnd(b, NOP_CONT, core_id, iref);
275 if (err_is_fail(err)) {
276 DEBUG_ERR(err, "add_spawnd");
283 * \brief Request the process manager to spawn a program on a specific core
285 * \param coreid Core ID on which to spawn the program
286 * \param path Absolute path in the file system to an executable
287 * image suitable for the given core
288 * \param argv Command-line arguments, NULL-terminated
289 * \param envp Optional environment, NULL-terminated
290 * (pass NULL to inherit)
291 * \param inheritcn_cap Cap to a CNode containing capabilities to be inherited
292 * \param argcn_cap Cap to a CNode containing capabilities passed as
294 * \param flags Flags to spawn
295 * \param ret_domain_cap If non-NULL, filled in with domain cap of new domain
297 * \bug flags are currently ignored
299 errval_t proc_mgmt_spawn_program_with_caps(coreid_t core_id, const char *path,
302 struct capref inheritcn_cap,
303 struct capref argcn_cap,
305 struct capref *ret_domain_cap)
307 errval_t err, msgerr;
309 // default to copying our environment
314 err = proc_mgmt_bind_client();
315 if (err_is_fail(err)) {
316 USER_PANIC_ERR(err, "proc_mgmt_bind_client");
319 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
322 // construct argument "string"
323 // \0-separated strings in contiguous character buffer
324 // this is needed, as flounder can't send variable-length arrays of strings
325 size_t argstrlen = 0;
326 for (int i = 0; argv[i] != NULL; i++) {
327 argstrlen += strlen(argv[i]) + 1;
330 char argstr[argstrlen];
331 size_t argstrpos = 0;
332 for (int i = 0; argv[i] != NULL; i++) {
333 strcpy(&argstr[argstrpos], argv[i]);
334 argstrpos += strlen(argv[i]);
335 argstr[argstrpos++] = '\0';
337 assert(argstrpos == argstrlen);
339 // repeat for environment
340 size_t envstrlen = 0;
341 for (int i = 0; envp[i] != NULL; i++) {
342 envstrlen += strlen(envp[i]) + 1;
345 char envstr[envstrlen];
346 size_t envstrpos = 0;
347 for (int i = 0; envp[i] != NULL; i++) {
348 strcpy(&envstr[envstrpos], envp[i]);
349 envstrpos += strlen(envp[i]);
350 envstr[envstrpos++] = '\0';
352 assert(envstrpos == envstrlen);
354 // make an unqualified path absolute using the $PATH variable
355 // TODO: implement search (currently assumes PATH is a single directory)
356 char *searchpath = getenv("PATH");
357 if (searchpath == NULL) {
358 searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root
360 size_t buflen = strlen(path) + strlen(searchpath) + 2;
361 char pathbuf[buflen];
362 if (path[0] != VFS_PATH_SEP) {
363 snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path);
364 pathbuf[buflen - 1] = '\0';
365 //vfs_path_normalise(pathbuf);
369 struct capref domain_cap;
371 if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) {
372 err = b->rpc_tx_vtbl.spawn(b, core_id, path, argstr, argstrlen, envstr,
373 envstrlen, flags, &msgerr, &domain_cap);
375 err = b->rpc_tx_vtbl.spawn_with_caps(b, core_id, path, argstr,
376 argstrlen, envstr, envstrlen,
377 inheritcn_cap, argcn_cap, flags,
378 &msgerr, &domain_cap);
380 if (err_is_fail(err)) {
381 USER_PANIC_ERR(err, "error sending spawn request to process manager");
382 } else if (err_is_fail(msgerr)) {
386 if (ret_domain_cap != NULL) {
387 *ret_domain_cap = domain_cap;
396 * \brief Request the process manager to spawn a program on a specific core
398 * \param coreid Core ID on which to spawn the program
399 * \param path Absolute path in the file system to an executable
400 * image suitable for the given core
401 * \param argv Command-line arguments, NULL-terminated
402 * \param envp Optional environment, NULL-terminated
403 * (pass NULL to inherit)
404 * \param inheritcn_cap Cap to a CNode containing capabilities to be inherited
405 * \param argcn_cap Cap to a CNode containing capabilities passed as
407 * \param flags Flags to spawn
408 * \param ret_domain_cap If non-NULL, filled in with domain cap of new domain
410 * \bug flags are currently ignored
412 errval_t proc_mgmt_spawn_program(coreid_t core_id, const char *path,
413 char *const argv[], char *const envp[],
414 uint8_t flags, struct capref *ret_domain_cap)
416 return proc_mgmt_spawn_program_with_caps(core_id, path, argv, envp,
417 NULL_CAP, NULL_CAP, flags,
422 * \brief Request the process manager to span onto a new core.
424 * \param core_id ID of core to span onto.
426 * Blocks until the new dispatcher has established an interdispatcher connection
427 * to the current one.
429 errval_t proc_mgmt_span(coreid_t core_id)
431 coreid_t my_core_id = disp_get_core_id();
432 assert (core_id != my_core_id);
434 errval_t err, msgerr;
435 err = proc_mgmt_bind_client();
436 if (err_is_fail(err)) {
437 USER_PANIC_ERR(err, "proc_mgmt_bind_client");
440 struct span_domain_state *st;
441 err = domain_new_dispatcher_setup_only(core_id, &st);
442 if (err_is_fail(err)) {
443 USER_PANIC_ERR(err, "failed to setup new dispatcher");
446 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
449 err = b->rpc_tx_vtbl.span(b, cap_domainid, core_id, st->vroot, st->frame,
451 if (err_is_fail(err)) {
452 USER_PANIC_ERR(err, "error sending span request to process manager");
455 if (err_is_fail(msgerr)) {
459 while(!st->initialized) {
460 event_dispatch(get_default_waitset());
468 * \brief Request the process manager to kill a domain
470 * \param domain_cap Domain ID cap for the victim
472 errval_t proc_mgmt_kill(struct capref domain_cap)
474 errval_t err, msgerr;
475 err = proc_mgmt_bind_client();
476 if (err_is_fail(err)) {
477 USER_PANIC_ERR(err, "proc_mgmt_bind_client");
480 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
483 err = b->rpc_tx_vtbl.kill(b, domain_cap, &msgerr);
484 if (err_is_fail(err)) {
485 USER_PANIC_ERR(err, "error sending kill request to process manager");
492 * \brief Inform the process manager about exiting execution.
494 errval_t proc_mgmt_exit(uint8_t status )
496 errval_t err = proc_mgmt_bind_client();
497 if (err_is_fail(err)) {
501 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
504 err = b->tx_vtbl.exit(b, NOP_CONT, cap_domainid, status);
505 if (err_is_fail(err)) {
512 errval_t proc_mgmt_wait(struct capref domain_cap, uint8_t *status)
514 errval_t err, msgerr;
515 err = proc_mgmt_bind_client();
516 if (err_is_fail(err)) {
520 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
523 err = b->rpc_tx_vtbl.wait(b, domain_cap, &msgerr, status);
524 if (err_is_fail(err)) {
525 USER_PANIC_ERR(err, "error sending wait request to process manager");