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 \n", __builtin_return_address(0));
35 debug_err(__FILE__, __func__, __LINE__, err,
36 "asynchronous error in proc_mgmt binding");
40 static void proc_mgmt_bind_cont(void *st, errval_t err,
41 struct proc_mgmt_binding *b)
43 struct proc_mgmt_bind_retst *retst = (struct proc_mgmt_bind_retst*) st;
44 assert(retst != NULL);
45 assert(!retst->present);
48 retst->present = true;
52 static void proc_mgmt_accept_recv_handler(void *arg)
54 struct proc_mgmt_lmp_binding *b = arg;
55 struct lmp_recv_msg msg = LMP_RECV_MSG_INIT;
59 // try to retrieve a message from the channel
60 err = lmp_chan_recv(&b->chan, &msg, &cap);
61 if (err_is_fail(err)) {
62 if (err_no(err) == LIB_ERR_NO_LMP_MSG) {
63 // nothing there, re-register
64 struct event_closure recv_handler = {
65 .handler = proc_mgmt_accept_recv_handler,
68 err = lmp_chan_register_recv(&b->chan, b->b.waitset, recv_handler);
69 b->b.error_handler(&b->b, err_push(err, LIB_ERR_CHAN_REGISTER_RECV));
71 // real error, report to user
72 b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_CHAN_RECV));
77 // TODO(razvan): LMP_PROC_MGMT_ACCEPT ?
78 assert(b->chan.connstate == LMP_MONITOR_ACCEPT);
79 assert(!capref_is_null(cap));
80 b->chan.remote_cap = cap;
81 b->chan.connstate = LMP_CONNECTED;
83 /* allocate a new receive slot */
84 err = lmp_chan_alloc_recv_slot(&b->chan);
85 if (err_is_fail(err)) {
86 // XXX: report the error, but continue
87 b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_ALLOC_RECV_SLOT));
90 /* Run the RX handler; has a side-effect of registering for receive events */
91 proc_mgmt_lmp_rx_handler(b);
94 static errval_t init_lmp_binding(struct proc_mgmt_lmp_binding *lmpb,
100 proc_mgmt_lmp_init(lmpb, ws);
102 /* allocate a cap slot for the new endpoint cap */
103 err = slot_alloc(&lmpb->chan.local_cap);
104 if (err_is_fail(err)) {
105 return err_push(err, LIB_ERR_SLOT_ALLOC);
108 /* allocate a local endpoint */
109 err = lmp_endpoint_create_in_slot(buflen_words, lmpb->chan.local_cap,
110 &lmpb->chan.endpoint);
111 if (err_is_fail(err)) {
112 // TODO(razvan): Free cap slot.
113 return err_push(err, LIB_ERR_ENDPOINT_CREATE);
116 /* allocate an initial receive slot */
117 err = lmp_chan_alloc_recv_slot(&lmpb->chan);
118 if (err_is_fail(err)) {
122 /* setup error handler */
123 lmpb->b.error_handler = error_handler;
125 /* setup initial receive handlers */
126 // TODO(razvan): Don't think this is needed, but dunno for sure yet.
127 // lmpb->b.rx_vtbl = monitor_rx_vtbl;
130 lmpb->b.change_waitset(&lmpb->b, lmpb->b.waitset);
135 * \brief Accept a new LMP binding to a proc mgmt client.
137 * Should only be used in the process manager.
139 * \param lmpb Storage for binding state
140 * \param ws Waitset for handling incoming messages
141 * \param buflen_words Size of incoming buffer, in number of words
143 errval_t proc_mgmt_client_lmp_accept(struct proc_mgmt_lmp_binding *lmpb,
145 size_t lmp_buflen_words)
147 errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
148 if (err_is_fail(err)) {
152 lmpb->chan.connstate = LMP_MONITOR_ACCEPT; // TODO(razvan): LMP_PROC_MGMT_ACCEPT?
153 lmpb->chan.remote_cap = NULL_CAP; // will be sent to us by the client
155 /* Register for receive notification on our special handler */
156 struct event_closure receive_handler = {
157 .handler = proc_mgmt_accept_recv_handler,
160 err = lmp_chan_register_recv(&lmpb->chan, ws, receive_handler);
161 if (err_is_fail(err)) {
162 return err; // TODO(razvan): cleanup?
170 * \brief Initiate a new LMP binding to the process manager
172 * To be used by the monitor for setting up the privileged channel used for
174 * Requires an explicit remote endpoint cap allocated by the process manager.
176 * \param lmpb Storage for binding state
177 * \param ep Remote endpoint of the process manager
178 * \param ws Waitset for handling incoming messages
179 * \param cont Continuation for when binding completes or fails
180 * \param st State passed to continuation function
181 * \param buflen_words Size of incoming buffer, in number of words
183 errval_t proc_mgmt_client_lmp_bind(struct proc_mgmt_lmp_binding *lmpb,
185 proc_mgmt_bind_continuation_fn *cont,
188 size_t lmp_buflen_words)
190 errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
191 if (err_is_fail(err)) {
195 lmpb->chan.remote_cap = ep;
197 // Send the local endpoint cap to the process manager.
198 lmpb->chan.connstate = LMP_CONNECTED; /* pre-established */
199 err = lmp_chan_send0(&lmpb->chan, 0, lmpb->chan.local_cap);
200 if (err_is_fail(err)) {
201 // TODO(razvan): This, below.
202 /* XXX: I'm lazily assuming this can never fail with a transient error,
203 * since we only do it once at dispatcher startup. If not, we need to
204 * register and retry here */
205 assert(!lmp_err_is_transient(err));
209 /* Run the RX handler; has a side-effect of registering for receive events */
210 proc_mgmt_lmp_rx_handler(lmpb);
212 /* Run the continuation */
213 cont(st, SYS_ERR_OK, &lmpb->b);
218 errval_t proc_mgmt_bind_client(void)
220 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
227 // Try using nameserver to retrievew the proc mgmt iref.
228 err = nameservice_blocking_lookup("proc_mgmt", &iref);
229 if (err_is_fail(err)) {
234 struct proc_mgmt_bind_retst bindst = {
238 err = proc_mgmt_bind(iref, proc_mgmt_bind_cont, &bindst,
239 get_default_waitset(), /*IDC_BIND_FLAG_RPC_CAP_TRANSFER*/IDC_BIND_FLAGS_DEFAULT);
240 if (err_is_fail(err)) {
241 USER_PANIC_ERR(err, "proc_mgmt_bind");
244 // Wait for bind completion.
245 while (!bindst.present) {
246 messages_wait_and_handle_next();
249 if (err_is_fail(bindst.err)) {
253 proc_mgmt_rpc_client_init(bindst.b);
255 set_proc_mgmt_binding(bindst.b);
260 errval_t proc_mgmt_add_spawnd(iref_t iref, coreid_t core_id)
262 errval_t err = proc_mgmt_bind_client();
263 if (err_is_fail(err)) {
264 DEBUG_ERR(err, "proc_mgmt_bind_client");
268 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
271 err = b->tx_vtbl.add_spawnd(b, NOP_CONT, core_id, iref);
272 if (err_is_fail(err)) {
273 DEBUG_ERR(err, "add_spawnd");
280 * \brief Request the process manager to spawn a program on a specific core
282 * \param coreid Core ID on which to spawn the program
283 * \param path Absolute path in the file system to an executable
284 * image suitable for the given core
285 * \param argv Command-line arguments, NULL-terminated
286 * \param envp Optional environment, NULL-terminated
287 * (pass NULL to inherit)
288 * \param inheritcn_cap Cap to a CNode containing capabilities to be inherited
289 * \param argcn_cap Cap to a CNode containing capabilities passed as
291 * \param flags Flags to spawn
292 * \param ret_domain_cap If non-NULL, filled in with domain cap of new domain
294 * \bug flags are currently ignored
296 errval_t proc_mgmt_spawn_program_with_caps(coreid_t core_id, const char *path,
299 struct capref inheritcn_cap,
300 struct capref argcn_cap,
302 struct capref *ret_domain_cap)
304 errval_t err, msgerr;
306 // default to copying our environment
311 err = proc_mgmt_bind_client();
312 if (err_is_fail(err)) {
313 USER_PANIC_ERR(err, "proc_mgmt_bind_client");
316 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
319 // construct argument "string"
320 // \0-separated strings in contiguous character buffer
321 // this is needed, as flounder can't send variable-length arrays of strings
322 size_t argstrlen = 0;
324 for (int i = 0; argv[i] != NULL; i++) {
325 argstrlen += strlen(argv[i]) + 1;
328 char argstr[argstrlen];
329 size_t argstrpos = 0;
330 for (int i = 0; argv[i] != NULL; i++) {
331 strcpy(&argstr[argstrpos], argv[i]);
332 argstrpos += strlen(argv[i]);
333 argstr[argstrpos++] = '\0';
335 assert(argstrpos == argstrlen);
337 // repeat for environment
338 size_t envstrlen = 0;
339 for (int i = 0; envp[i] != NULL; i++) {
340 envstrlen += strlen(envp[i]) + 1;
343 char envstr[envstrlen];
344 size_t envstrpos = 0;
345 for (int i = 0; envp[i] != NULL; i++) {
346 strcpy(&envstr[envstrpos], envp[i]);
347 envstrpos += strlen(envp[i]);
348 envstr[envstrpos++] = '\0';
350 assert(envstrpos == envstrlen);
352 // make an unqualified path absolute using the $PATH variable
353 // TODO: implement search (currently assumes PATH is a single directory)
354 char *searchpath = getenv("PATH");
355 if (searchpath == NULL) {
356 searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root
358 size_t buflen = strlen(path) + strlen(searchpath) + 2;
359 char pathbuf[buflen];
360 if (path[0] != VFS_PATH_SEP) {
361 snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path);
362 pathbuf[buflen - 1] = '\0';
363 //vfs_path_normalise(pathbuf);
367 struct capref domain_cap;
368 err = slot_alloc(&domain_cap);
369 if (err_is_fail(err)) {
373 if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) {
374 err = b->rpc_tx_vtbl.spawn(b, core_id, path, argstr, argstrlen, envstr,
375 envstrlen, flags, &msgerr, &domain_cap);
377 err = b->rpc_tx_vtbl.spawn_with_caps(b, core_id, path, argstr,
378 argstrlen, envstr, envstrlen,
379 inheritcn_cap, argcn_cap, flags,
380 &msgerr, &domain_cap);
382 if (err_is_fail(err)) {
383 USER_PANIC_ERR(err, "error sending spawn request to process manager");
384 } else if (err_is_fail(msgerr)) {
388 if (ret_domain_cap != NULL) {
389 *ret_domain_cap = domain_cap;
398 * \brief Request the process manager to spawn a program on a specific core
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
409 * \param flags Flags to spawn
410 * \param ret_domain_cap If non-NULL, filled in with domain cap of new domain
412 * \bug flags are currently ignored
414 errval_t proc_mgmt_spawn_program(coreid_t core_id, const char *path,
415 char *const argv[], char *const envp[],
416 uint8_t flags, struct capref *ret_domain_cap)
418 return proc_mgmt_spawn_program_with_caps(core_id, path, argv, envp,
419 NULL_CAP, NULL_CAP, flags,
424 * \brief Request the process manager to span onto a new core.
426 * \param core_id ID of core to span onto.
428 * Blocks until the new dispatcher has established an interdispatcher connection
429 * to the current one.
431 errval_t proc_mgmt_span(coreid_t core_id)
433 coreid_t my_core_id = disp_get_core_id();
434 assert (core_id != my_core_id);
436 errval_t err, msgerr;
437 err = proc_mgmt_bind_client();
438 if (err_is_fail(err)) {
439 USER_PANIC_ERR(err, "proc_mgmt_bind_client");
442 struct span_domain_state *st;
443 err = domain_new_dispatcher_setup_only(core_id, &st);
444 if (err_is_fail(err)) {
445 USER_PANIC_ERR(err, "failed to setup new dispatcher");
448 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
451 err = b->rpc_tx_vtbl.span(b, cap_domainid, core_id, st->vroot, st->frame,
453 if (err_is_fail(err)) {
454 USER_PANIC_ERR(err, "error sending span request to process manager");
457 if (err_is_fail(msgerr)) {
461 while(!st->initialized) {
462 event_dispatch(get_default_waitset());
470 * \brief Request the process manager to kill a domain
472 * \param domain_cap Domain ID cap for the victim
474 errval_t proc_mgmt_kill(struct capref domain_cap)
476 errval_t err, msgerr;
477 err = proc_mgmt_bind_client();
478 if (err_is_fail(err)) {
479 USER_PANIC_ERR(err, "proc_mgmt_bind_client");
482 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
485 err = b->rpc_tx_vtbl.kill(b, domain_cap, &msgerr);
486 if (err_is_fail(err)) {
487 USER_PANIC_ERR(err, "error sending kill request to process manager");
494 * \brief Inform the process manager about exiting execution.
496 errval_t proc_mgmt_exit(uint8_t status)
498 errval_t err = proc_mgmt_bind_client();
499 if (err_is_fail(err)) {
503 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
506 err = b->rpc_tx_vtbl.exit(b, cap_domainid, status);
507 if (err_is_fail(err)) {
514 errval_t proc_mgmt_wait(struct capref domain_cap, uint8_t *status)
516 errval_t err, msgerr;
517 err = proc_mgmt_bind_client();
518 if (err_is_fail(err)) {
522 struct proc_mgmt_binding *b = get_proc_mgmt_binding();
525 err = b->rpc_tx_vtbl.wait(b, domain_cap, &msgerr, status);
526 if (err_is_fail(err)) {
527 USER_PANIC_ERR(err, "error sending wait request to process manager");