b2600e753f3b1e67b8df0fb1b30221e6dc77d521
[barrelfish] / lib / barrelfish / proc_mgmt_client.c
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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
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>
21
22 struct proc_mgmt_bind_retst {
23     errval_t err;
24     struct proc_mgmt_binding *b;
25     bool present;
26 };
27
28 extern char **environ;
29
30 static void error_handler(struct proc_mgmt_binding *b, errval_t err)
31 {
32 #if defined(__x86_64__) || defined(__i386__)
33     debug_printf("%p \n",  __builtin_return_address(0));
34 #endif
35     debug_err(__FILE__, __func__, __LINE__, err,
36               "asynchronous error in proc_mgmt binding");
37     abort();
38 }
39
40 static void proc_mgmt_bind_cont(void *st, errval_t err,
41         struct proc_mgmt_binding *b)
42 {
43     struct proc_mgmt_bind_retst *retst = (struct proc_mgmt_bind_retst*) st;
44     assert(retst != NULL);
45     assert(!retst->present);
46     retst->err = err;
47     retst->b = b;
48     retst->present = true;
49     b->st = retst;
50 }
51
52 static void proc_mgmt_accept_recv_handler(void *arg)
53 {
54     struct proc_mgmt_lmp_binding *b = arg;
55     struct lmp_recv_msg msg = LMP_RECV_MSG_INIT;
56     struct capref cap;
57     errval_t err;
58
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,
66                 .arg = b,
67             };
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));
70         } else {
71             // real error, report to user
72             b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_CHAN_RECV));
73         }
74         return;
75     }
76
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;
82
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));
88     }
89
90     /* Run the RX handler; has a side-effect of registering for receive events */
91     proc_mgmt_lmp_rx_handler(b);
92 }
93
94 static errval_t init_lmp_binding(struct proc_mgmt_lmp_binding *lmpb,
95                                  struct waitset *ws,
96                                  size_t buflen_words)
97 {
98     errval_t err;
99
100     proc_mgmt_lmp_init(lmpb, ws);
101
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);
106     }
107
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);
114     }
115
116     /* allocate an initial receive slot */
117     err = lmp_chan_alloc_recv_slot(&lmpb->chan);
118     if (err_is_fail(err)) {
119         return err;
120     }
121
122     /* setup error handler */
123     lmpb->b.error_handler = error_handler;
124
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;
128
129     // connect handlers
130     lmpb->b.change_waitset(&lmpb->b, lmpb->b.waitset);
131     return SYS_ERR_OK;
132 }
133
134 /**
135  * \brief Accept a new LMP binding to a proc mgmt client.
136  *
137  * Should only be used in the process manager.
138  *
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
142  */
143 errval_t proc_mgmt_client_lmp_accept(struct proc_mgmt_lmp_binding *lmpb,
144                                      struct waitset *ws,
145                                      size_t lmp_buflen_words)
146 {
147     errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
148     if (err_is_fail(err)) {
149         return err;
150     }
151
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
154
155     /* Register for receive notification on our special handler */
156     struct event_closure receive_handler = {
157         .handler = proc_mgmt_accept_recv_handler,
158         .arg = lmpb,
159     };
160     err = lmp_chan_register_recv(&lmpb->chan, ws, receive_handler);
161     if (err_is_fail(err)) {
162         return err;  // TODO(razvan): cleanup?
163     }
164
165     return SYS_ERR_OK;
166 }
167
168
169 /**
170  * \brief Initiate a new LMP binding to the process manager
171  *
172  * To be used by the monitor for setting up the privileged channel used for
173  * spawnd discovery.
174  * Requires an explicit remote endpoint cap allocated by the process manager.
175  *
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
182  */
183 errval_t proc_mgmt_client_lmp_bind(struct proc_mgmt_lmp_binding *lmpb,
184                                    struct capref ep,
185                                    proc_mgmt_bind_continuation_fn *cont,
186                                    void *st,
187                                    struct waitset *ws,
188                                    size_t lmp_buflen_words)
189 {
190     errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
191     if (err_is_fail(err)) {
192         return err;
193     }
194
195     lmpb->chan.remote_cap = ep;
196
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));
206         return err;
207     }
208
209     /* Run the RX handler; has a side-effect of registering for receive events */
210     proc_mgmt_lmp_rx_handler(lmpb);
211
212     /* Run the continuation */
213     cont(st, SYS_ERR_OK, &lmpb->b);
214
215     return SYS_ERR_OK;
216 }
217
218 errval_t proc_mgmt_bind_client(void)
219 {
220     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
221     if (b != NULL) {
222         return SYS_ERR_OK;
223     }
224
225     errval_t err;
226     iref_t iref;
227     // Try using nameserver to retrievew the proc mgmt iref.
228     err = nameservice_blocking_lookup("proc_mgmt", &iref);
229     if (err_is_fail(err)) {
230         return err;
231     }
232     
233     // Initiate bind.
234     struct proc_mgmt_bind_retst bindst = {
235         .present = false
236     };
237
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");
242     }
243
244     // Wait for bind completion.
245     while (!bindst.present) {
246         messages_wait_and_handle_next();
247     }
248
249     if (err_is_fail(bindst.err)) {
250         return bindst.err;
251     }
252
253     proc_mgmt_rpc_client_init(bindst.b);
254
255     set_proc_mgmt_binding(bindst.b);
256
257     return SYS_ERR_OK;
258 }
259
260 errval_t proc_mgmt_add_spawnd(iref_t iref, coreid_t core_id)
261 {
262     errval_t err = proc_mgmt_bind_client();
263     if (err_is_fail(err)) {
264         DEBUG_ERR(err, "proc_mgmt_bind_client");
265         return err;
266     }
267
268     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
269     assert(b != NULL);
270
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");
274     }
275
276     return err;
277 }
278
279 /**
280  * \brief Request the process manager to spawn a program on a specific core
281  *
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
290  *                        arguments
291  * \param flags           Flags to spawn
292  * \param ret_domain_cap  If non-NULL, filled in with domain cap of new domain
293  *
294  * \bug flags are currently ignored
295  */
296 errval_t proc_mgmt_spawn_program_with_caps(coreid_t core_id, const char *path,
297                                            char *const argv[],
298                                            char *const envp[],
299                                            struct capref inheritcn_cap,
300                                            struct capref argcn_cap,
301                                            uint8_t flags,
302                                            struct capref *ret_domain_cap)
303 {
304     errval_t err, msgerr;
305
306     // default to copying our environment
307     if (envp == NULL) {
308         envp = environ;
309     }
310
311     err = proc_mgmt_bind_client();
312     if (err_is_fail(err)) {
313         USER_PANIC_ERR(err, "proc_mgmt_bind_client");
314     }
315
316     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
317     assert(b != NULL);
318
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;
323
324     for (int i = 0; argv[i] != NULL; i++) {
325         argstrlen += strlen(argv[i]) + 1;
326     }
327
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';
334     }
335     assert(argstrpos == argstrlen);
336
337     // repeat for environment
338     size_t envstrlen = 0;
339     for (int i = 0; envp[i] != NULL; i++) {
340         envstrlen += strlen(envp[i]) + 1;
341     }
342
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';
349     }
350     assert(envstrpos == envstrlen);
351
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
357     }
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);
364         path = pathbuf;
365     }
366
367     struct capref domain_cap;
368     err = slot_alloc(&domain_cap);
369     if (err_is_fail(err)) {
370         return err;
371     }
372
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);
376     } else {
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);
381     }
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)) {
385         goto out;
386     }
387
388     if (ret_domain_cap != NULL) {
389         *ret_domain_cap = domain_cap;
390     }
391
392 out:
393     return msgerr;
394     
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  */
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)
417 {
418     return proc_mgmt_spawn_program_with_caps(core_id, path, argv, envp,
419                                              NULL_CAP, NULL_CAP, flags,
420                                              ret_domain_cap);
421 }
422
423 /**
424  * \brief Request the process manager to span onto a new core.
425  *
426  * \param core_id ID of core to span onto.
427  *
428  * Blocks until the new dispatcher has established an interdispatcher connection
429  * to the current one.
430  */
431 errval_t proc_mgmt_span(coreid_t core_id)
432 {
433     coreid_t my_core_id = disp_get_core_id();
434     assert (core_id != my_core_id);
435
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");
440     }
441     
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");
446     }
447
448     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
449     assert(b != NULL);
450
451     err = b->rpc_tx_vtbl.span(b, cap_domainid, core_id, st->vroot, st->frame,
452                               &msgerr);
453     if (err_is_fail(err)) {
454         USER_PANIC_ERR(err, "error sending span request to process manager");
455     }
456
457     if (err_is_fail(msgerr)) {
458         return msgerr;
459     }
460
461     while(!st->initialized) {
462         event_dispatch(get_default_waitset());
463     }
464     free(st);
465
466     return SYS_ERR_OK;
467 }
468
469 /**
470  * \brief Request the process manager to kill a domain
471  *
472  * \param domain_cap Domain ID cap for the victim
473  */
474 errval_t proc_mgmt_kill(struct capref domain_cap)
475 {
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");
480     }
481
482     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
483     assert(b != NULL);
484
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");
488     }
489
490     return msgerr;
491 }
492
493 /**
494  * \brief Inform the process manager about exiting execution.
495  */
496 errval_t proc_mgmt_exit(uint8_t status)
497 {
498     errval_t err = proc_mgmt_bind_client();
499     if (err_is_fail(err)) {
500         return err;
501     }
502
503     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
504     assert(b != NULL);
505
506     err = b->rpc_tx_vtbl.exit(b, cap_domainid, status);
507     if (err_is_fail(err)) {
508         return err;
509     }
510
511     return SYS_ERR_OK;
512 }
513
514 errval_t proc_mgmt_wait(struct capref domain_cap, uint8_t *status)
515 {
516     errval_t err, msgerr;
517     err = proc_mgmt_bind_client();
518     if (err_is_fail(err)) {
519         return err;
520     }
521
522     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
523     assert(b != NULL);
524
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");
528     }
529
530     return msgerr;
531 }