proc_mgmt: make sure that exit message is sent before things are
[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     bool kill_sent;
27 };
28
29 extern char **environ;
30
31 static void error_handler(struct proc_mgmt_binding *b, errval_t err)
32 {
33 #if defined(__x86_64__) || defined(__i386__)
34     debug_printf("%p %p %p %p\n",
35                  __builtin_return_address(0),
36                  __builtin_return_address(1),
37                  __builtin_return_address(2),
38                  __builtin_return_address(3));
39 #endif
40     debug_err(__FILE__, __func__, __LINE__, err,
41               "asynchronous error in proc_mgmt binding");
42     abort();
43 }
44
45 static void proc_mgmt_bind_cont(void *st, errval_t err,
46         struct proc_mgmt_binding *b)
47 {
48     struct proc_mgmt_bind_retst *retst = (struct proc_mgmt_bind_retst*) st;
49     assert(retst != NULL);
50     assert(!retst->present);
51     retst->err = err;
52     retst->b = b;
53     retst->present = true;
54     b->st = retst;
55 }
56
57 static void proc_mgmt_accept_recv_handler(void *arg)
58 {
59     struct proc_mgmt_lmp_binding *b = arg;
60     struct lmp_recv_msg msg = LMP_RECV_MSG_INIT;
61     struct capref cap;
62     errval_t err;
63
64     // try to retrieve a message from the channel
65     err = lmp_chan_recv(&b->chan, &msg, &cap);
66     if (err_is_fail(err)) {
67         if (err_no(err) == LIB_ERR_NO_LMP_MSG) {
68             // nothing there, re-register
69             struct event_closure recv_handler = {
70                 .handler = proc_mgmt_accept_recv_handler,
71                 .arg = b,
72             };
73             err = lmp_chan_register_recv(&b->chan, b->b.waitset, recv_handler);
74             b->b.error_handler(&b->b, err_push(err, LIB_ERR_CHAN_REGISTER_RECV));
75         } else {
76             // real error, report to user
77             b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_CHAN_RECV));
78         }
79         return;
80     }
81
82     // TODO(razvan): LMP_PROC_MGMT_ACCEPT ?
83     assert(b->chan.connstate == LMP_MONITOR_ACCEPT);
84     assert(!capref_is_null(cap));
85     b->chan.remote_cap = cap;
86     b->chan.connstate = LMP_CONNECTED;
87
88     /* allocate a new receive slot */
89     err = lmp_chan_alloc_recv_slot(&b->chan);
90     if (err_is_fail(err)) {
91         // XXX: report the error, but continue
92         b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_ALLOC_RECV_SLOT));
93     }
94
95     /* Run the RX handler; has a side-effect of registering for receive events */
96     proc_mgmt_lmp_rx_handler(b);
97 }
98
99 static errval_t init_lmp_binding(struct proc_mgmt_lmp_binding *lmpb,
100                                  struct waitset *ws,
101                                  size_t buflen_words)
102 {
103     errval_t err;
104
105     proc_mgmt_lmp_init(lmpb, ws);
106
107     /* allocate a cap slot for the new endpoint cap */
108     err = slot_alloc(&lmpb->chan.local_cap);
109     if (err_is_fail(err)) {
110         return err_push(err, LIB_ERR_SLOT_ALLOC);
111     }
112
113     /* allocate a local endpoint */
114     err = lmp_endpoint_create_in_slot(buflen_words, lmpb->chan.local_cap,
115                                       &lmpb->chan.endpoint);
116     if (err_is_fail(err)) {
117         // TODO(razvan): Free cap slot.
118         return err_push(err, LIB_ERR_ENDPOINT_CREATE);
119     }
120
121     /* allocate an initial receive slot */
122     err = lmp_chan_alloc_recv_slot(&lmpb->chan);
123     if (err_is_fail(err)) {
124         return err;
125     }
126
127     /* setup error handler */
128     lmpb->b.error_handler = error_handler;
129
130     /* setup initial receive handlers */
131     // TODO(razvan): Don't think this is needed, but dunno for sure yet.
132     // lmpb->b.rx_vtbl = monitor_rx_vtbl;
133
134     // connect handlers
135     lmpb->b.change_waitset(&lmpb->b, lmpb->b.waitset);
136     return SYS_ERR_OK;
137 }
138
139 /**
140  * \brief Accept a new LMP binding to a proc mgmt client.
141  *
142  * Should only be used in the process manager.
143  *
144  * \param lmpb         Storage for binding state
145  * \param ws           Waitset for handling incoming messages
146  * \param buflen_words Size of incoming buffer, in number of words
147  */
148 errval_t proc_mgmt_client_lmp_accept(struct proc_mgmt_lmp_binding *lmpb,
149                                      struct waitset *ws,
150                                      size_t lmp_buflen_words)
151 {
152     errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
153     if (err_is_fail(err)) {
154         return err;
155     }
156
157     lmpb->chan.connstate = LMP_MONITOR_ACCEPT;  // TODO(razvan): LMP_PROC_MGMT_ACCEPT?
158     lmpb->chan.remote_cap = NULL_CAP; // will be sent to us by the client
159
160     /* Register for receive notification on our special handler */
161     struct event_closure receive_handler = {
162         .handler = proc_mgmt_accept_recv_handler,
163         .arg = lmpb,
164     };
165     err = lmp_chan_register_recv(&lmpb->chan, ws, receive_handler);
166     if (err_is_fail(err)) {
167         return err;  // TODO(razvan): cleanup?
168     }
169
170     return SYS_ERR_OK;
171 }
172
173
174 /**
175  * \brief Initiate a new LMP binding to the process manager
176  *
177  * To be used by the monitor for setting up the privileged channel used for
178  * spawnd discovery.
179  * Requires an explicit remote endpoint cap allocated by the process manager.
180  *
181  * \param lmpb         Storage for binding state
182  * \param ep           Remote endpoint of the process manager
183  * \param ws           Waitset for handling incoming messages
184  * \param cont         Continuation for when binding completes or fails
185  * \param st           State passed to continuation function
186  * \param buflen_words Size of incoming buffer, in number of words
187  */
188 errval_t proc_mgmt_client_lmp_bind(struct proc_mgmt_lmp_binding *lmpb,
189                                    struct capref ep,
190                                    proc_mgmt_bind_continuation_fn *cont,
191                                    void *st,
192                                    struct waitset *ws,
193                                    size_t lmp_buflen_words)
194 {
195     errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
196     if (err_is_fail(err)) {
197         return err;
198     }
199
200     lmpb->chan.remote_cap = ep;
201
202     // Send the local endpoint cap to the process manager.
203     lmpb->chan.connstate = LMP_CONNECTED; /* pre-established */
204     err = lmp_chan_send0(&lmpb->chan, 0, lmpb->chan.local_cap);
205     if (err_is_fail(err)) {
206         // TODO(razvan): This, below.
207         /* XXX: I'm lazily assuming this can never fail with a transient error,
208          * since we only do it once at dispatcher startup. If not, we need to
209          * register and retry here */
210         assert(!lmp_err_is_transient(err));
211         return err;
212     }
213
214     /* Run the RX handler; has a side-effect of registering for receive events */
215     proc_mgmt_lmp_rx_handler(lmpb);
216
217     /* Run the continuation */
218     cont(st, SYS_ERR_OK, &lmpb->b);
219
220     return SYS_ERR_OK;
221 }
222
223 errval_t proc_mgmt_bind_client(void)
224 {
225     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
226     if (b != NULL) {
227         return SYS_ERR_OK;
228     }
229
230     errval_t err;
231     iref_t iref;
232     // Try using nameserver to retrievew the proc mgmt iref.
233     err = nameservice_blocking_lookup("proc_mgmt", &iref);
234     if (err_is_fail(err)) {
235         return err;
236     }
237     
238     // Initiate bind.
239     struct proc_mgmt_bind_retst bindst = {
240         .present = false
241     };
242
243     err = proc_mgmt_bind(iref, proc_mgmt_bind_cont, &bindst,
244             get_default_waitset(), /*IDC_BIND_FLAG_RPC_CAP_TRANSFER*/IDC_BIND_FLAGS_DEFAULT);
245     if (err_is_fail(err)) {
246         USER_PANIC_ERR(err, "proc_mgmt_bind");
247     }
248
249     // Wait for bind completion.
250     while (!bindst.present) {
251         messages_wait_and_handle_next();
252     }
253
254     if (err_is_fail(bindst.err)) {
255         return bindst.err;
256     }
257
258     proc_mgmt_rpc_client_init(bindst.b);
259
260     set_proc_mgmt_binding(bindst.b);
261
262     return SYS_ERR_OK;
263 }
264
265 errval_t proc_mgmt_add_spawnd(iref_t iref, coreid_t core_id)
266 {
267     errval_t err = proc_mgmt_bind_client();
268     if (err_is_fail(err)) {
269         DEBUG_ERR(err, "proc_mgmt_bind_client");
270         return err;
271     }
272
273     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
274     assert(b != NULL);
275
276     err = b->tx_vtbl.add_spawnd(b, NOP_CONT, core_id, iref);
277     if (err_is_fail(err)) {
278         DEBUG_ERR(err, "add_spawnd");
279     }
280
281     return err;
282 }
283
284 /**
285  * \brief Request the process manager to spawn a program on a specific core
286  *
287  * \param coreid          Core ID on which to spawn the program
288  * \param path            Absolute path in the file system to an executable
289  *                        image suitable for the given core
290  * \param argv            Command-line arguments, NULL-terminated
291  * \param envp            Optional environment, NULL-terminated
292  *                        (pass NULL to inherit)
293  * \param inheritcn_cap   Cap to a CNode containing capabilities to be inherited
294  * \param argcn_cap       Cap to a CNode containing capabilities passed as
295  *                        arguments
296  * \param flags           Flags to spawn
297  * \param ret_domain_cap  If non-NULL, filled in with domain cap of new domain
298  *
299  * \bug flags are currently ignored
300  */
301 errval_t proc_mgmt_spawn_program_with_caps(coreid_t core_id, const char *path,
302                                            char *const argv[],
303                                            char *const envp[],
304                                            struct capref inheritcn_cap,
305                                            struct capref argcn_cap,
306                                            uint8_t flags,
307                                            struct capref *ret_domain_cap)
308 {
309     errval_t err, msgerr;
310
311     // default to copying our environment
312     if (envp == NULL) {
313         envp = environ;
314     }
315
316     err = proc_mgmt_bind_client();
317     if (err_is_fail(err)) {
318         USER_PANIC_ERR(err, "proc_mgmt_bind_client");
319     }
320
321     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
322     assert(b != NULL);
323
324     // construct argument "string"
325     // \0-separated strings in contiguous character buffer
326     // this is needed, as flounder can't send variable-length arrays of strings
327     size_t argstrlen = 0;
328
329     for (int i = 0; argv[i] != NULL; i++) {
330         argstrlen += strlen(argv[i]) + 1;
331     }
332
333     char argstr[argstrlen];
334     size_t argstrpos = 0;
335     for (int i = 0; argv[i] != NULL; i++) {
336         strcpy(&argstr[argstrpos], argv[i]);
337         argstrpos += strlen(argv[i]);
338         argstr[argstrpos++] = '\0';
339     }
340     assert(argstrpos == argstrlen);
341
342     // repeat for environment
343     size_t envstrlen = 0;
344     for (int i = 0; envp[i] != NULL; i++) {
345         envstrlen += strlen(envp[i]) + 1;
346     }
347
348     char envstr[envstrlen];
349     size_t envstrpos = 0;
350     for (int i = 0; envp[i] != NULL; i++) {
351         strcpy(&envstr[envstrpos], envp[i]);
352         envstrpos += strlen(envp[i]);
353         envstr[envstrpos++] = '\0';
354     }
355     assert(envstrpos == envstrlen);
356
357     // make an unqualified path absolute using the $PATH variable
358     // TODO: implement search (currently assumes PATH is a single directory)
359     char *searchpath = getenv("PATH");
360     if (searchpath == NULL) {
361         searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root
362     }
363     size_t buflen = strlen(path) + strlen(searchpath) + 2;
364     char pathbuf[buflen];
365     if (path[0] != VFS_PATH_SEP) {
366         snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path);
367         pathbuf[buflen - 1] = '\0';
368         //vfs_path_normalise(pathbuf);
369         path = pathbuf;
370     }
371
372     struct capref domain_cap;
373
374     if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) {
375         err = b->rpc_tx_vtbl.spawn(b, core_id, path, argstr, argstrlen, envstr,
376                                    envstrlen, flags, &msgerr, &domain_cap);
377     } else {
378         err = b->rpc_tx_vtbl.spawn_with_caps(b, core_id, path, argstr,
379                                              argstrlen, envstr, envstrlen,
380                                              inheritcn_cap, argcn_cap, flags,
381                                              &msgerr, &domain_cap);
382     }
383     if (err_is_fail(err)) {
384         USER_PANIC_ERR(err, "error sending spawn request to process manager");
385     } else if (err_is_fail(msgerr)) {
386         goto out;
387     }
388
389     if (ret_domain_cap != NULL) {
390         *ret_domain_cap = domain_cap;
391     }
392
393 out:
394     return msgerr;
395     
396 }
397
398 /**
399  * \brief Request the process manager to spawn a program on a specific core
400  *
401  * \param coreid          Core ID on which to spawn the program
402  * \param path            Absolute path in the file system to an executable
403  *                        image suitable for the given core
404  * \param argv            Command-line arguments, NULL-terminated
405  * \param envp            Optional environment, NULL-terminated
406  *                        (pass NULL to inherit)
407  * \param inheritcn_cap   Cap to a CNode containing capabilities to be inherited
408  * \param argcn_cap       Cap to a CNode containing capabilities passed as
409  *                        arguments
410  * \param flags           Flags to spawn
411  * \param ret_domain_cap  If non-NULL, filled in with domain cap of new domain
412  *
413  * \bug flags are currently ignored
414  */
415 errval_t proc_mgmt_spawn_program(coreid_t core_id, const char *path,
416                                  char *const argv[], char *const envp[],
417                                  uint8_t flags, struct capref *ret_domain_cap)
418 {
419     return proc_mgmt_spawn_program_with_caps(core_id, path, argv, envp,
420                                              NULL_CAP, NULL_CAP, flags,
421                                              ret_domain_cap);
422 }
423
424 /**
425  * \brief Request the process manager to span onto a new core.
426  *
427  * \param core_id ID of core to span onto.
428  *
429  * Blocks until the new dispatcher has established an interdispatcher connection
430  * to the current one.
431  */
432 errval_t proc_mgmt_span(coreid_t core_id)
433 {
434     coreid_t my_core_id = disp_get_core_id();
435     assert (core_id != my_core_id);
436
437     errval_t err, msgerr;
438     err = proc_mgmt_bind_client();
439     if (err_is_fail(err)) {
440         USER_PANIC_ERR(err, "proc_mgmt_bind_client");
441     }
442     
443     struct span_domain_state *st;
444     err = domain_new_dispatcher_setup_only(core_id, &st);
445     if (err_is_fail(err)) {
446         USER_PANIC_ERR(err, "failed to setup new dispatcher");
447     }
448
449     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
450     assert(b != NULL);
451
452     err = b->rpc_tx_vtbl.span(b, cap_domainid, core_id, st->vroot, st->frame,
453                               &msgerr);
454     if (err_is_fail(err)) {
455         USER_PANIC_ERR(err, "error sending span request to process manager");
456     }
457
458     if (err_is_fail(msgerr)) {
459         return msgerr;
460     }
461
462     while(!st->initialized) {
463         event_dispatch(get_default_waitset());
464     }
465     free(st);
466
467     return SYS_ERR_OK;
468 }
469
470 /**
471  * \brief Request the process manager to kill a domain
472  *
473  * \param domain_cap Domain ID cap for the victim
474  */
475 errval_t proc_mgmt_kill(struct capref domain_cap)
476 {
477     errval_t err, msgerr;
478     err = proc_mgmt_bind_client();
479     if (err_is_fail(err)) {
480         USER_PANIC_ERR(err, "proc_mgmt_bind_client");
481     }
482
483     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
484     assert(b != NULL);
485
486     err = b->rpc_tx_vtbl.kill(b, domain_cap, &msgerr);
487     if (err_is_fail(err)) {
488         USER_PANIC_ERR(err, "error sending kill request to process manager");
489     }
490
491     return msgerr;
492 }
493
494 static void cont(void* arg) 
495 {
496     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
497     struct proc_mgmt_bind_retst *st = (struct proc_mgmt_bind_retst*) b->st;
498     st->kill_sent = true;
499 }
500
501 /**
502  * \brief Inform the process manager about exiting execution.
503  */
504 errval_t proc_mgmt_exit(uint8_t status)
505 {
506     errval_t err = proc_mgmt_bind_client();
507     if (err_is_fail(err)) {
508         return err;
509     }
510
511     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
512     assert(b != NULL);
513
514
515     struct proc_mgmt_bind_retst *st = (struct proc_mgmt_bind_retst*) b->st;
516     st->kill_sent = false;
517     err = b->tx_vtbl.exit(b, MKCLOSURE(cont, 0), cap_domainid, status);
518     if (err_is_fail(err)) {
519         return err;
520     }
521     
522     /* We have to make sure that the kill message is sent, otherwise
523      * we might cleanup the channel BEFORE the messages is actually sent 
524      * to the process manager
525      */
526     while(!st->kill_sent) {
527         event_dispatch(get_default_waitset());
528     }
529     
530     return SYS_ERR_OK;
531 }
532
533 errval_t proc_mgmt_wait(struct capref domain_cap, uint8_t *status)
534 {
535     errval_t err, msgerr;
536     err = proc_mgmt_bind_client();
537     if (err_is_fail(err)) {
538         return err;
539     }
540
541     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
542     assert(b != NULL);
543
544     err = b->rpc_tx_vtbl.wait(b, domain_cap, &msgerr, status);
545     if (err_is_fail(err)) {
546         USER_PANIC_ERR(err, "error sending wait request to process manager");
547     }
548
549     return msgerr;
550 }