Add per-spawnd message queues to the process manager.
[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 %p %p %p\n",
34                  __builtin_return_address(0),
35                  __builtin_return_address(1),
36                  __builtin_return_address(2),
37                  __builtin_return_address(3));
38 #endif
39     debug_err(__FILE__, __func__, __LINE__, err,
40               "asynchronous error in proc_mgmt binding");
41     abort();
42 }
43
44 static void proc_mgmt_bind_cont(void *st, errval_t err,
45         struct proc_mgmt_binding *b)
46 {
47     struct proc_mgmt_bind_retst *retst = (struct proc_mgmt_bind_retst*) st;
48     assert(retst != NULL);
49     assert(!retst->present);
50     retst->err = err;
51     retst->b = b;
52     retst->present = true;
53 }
54
55 static void proc_mgmt_accept_recv_handler(void *arg)
56 {
57     struct proc_mgmt_lmp_binding *b = arg;
58     struct lmp_recv_msg msg = LMP_RECV_MSG_INIT;
59     struct capref cap;
60     errval_t err;
61
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,
69                 .arg = b,
70             };
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));
73         } else {
74             // real error, report to user
75             b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_CHAN_RECV));
76         }
77         return;
78     }
79
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;
85
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));
91     }
92
93     /* Run the RX handler; has a side-effect of registering for receive events */
94     proc_mgmt_lmp_rx_handler(b);
95 }
96
97 static errval_t init_lmp_binding(struct proc_mgmt_lmp_binding *lmpb,
98                                  struct waitset *ws,
99                                  size_t buflen_words)
100 {
101     errval_t err;
102
103     proc_mgmt_lmp_init(lmpb, ws);
104
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);
109     }
110
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);
117     }
118
119     /* allocate an initial receive slot */
120     err = lmp_chan_alloc_recv_slot(&lmpb->chan);
121     if (err_is_fail(err)) {
122         return err;
123     }
124
125     /* setup error handler */
126     lmpb->b.error_handler = error_handler;
127
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;
131
132     // connect handlers
133     lmpb->b.change_waitset(&lmpb->b, lmpb->b.waitset);
134     return SYS_ERR_OK;
135 }
136
137 /**
138  * \brief Accept a new LMP binding to a proc mgmt client.
139  *
140  * Should only be used in the process manager.
141  *
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
145  */
146 errval_t proc_mgmt_client_lmp_accept(struct proc_mgmt_lmp_binding *lmpb,
147                                      struct waitset *ws,
148                                      size_t lmp_buflen_words)
149 {
150     errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
151     if (err_is_fail(err)) {
152         return err;
153     }
154
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
157
158     /* Register for receive notification on our special handler */
159     struct event_closure receive_handler = {
160         .handler = proc_mgmt_accept_recv_handler,
161         .arg = lmpb,
162     };
163     err = lmp_chan_register_recv(&lmpb->chan, ws, receive_handler);
164     if (err_is_fail(err)) {
165         return err;  // TODO(razvan): cleanup?
166     }
167
168     return SYS_ERR_OK;
169 }
170
171
172 /**
173  * \brief Initiate a new LMP binding to the process manager
174  *
175  * To be used by the monitor for setting up the privileged channel used for
176  * spawnd discovery.
177  * Requires an explicit remote endpoint cap allocated by the process manager.
178  *
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
185  */
186 errval_t proc_mgmt_client_lmp_bind(struct proc_mgmt_lmp_binding *lmpb,
187                                    struct capref ep,
188                                    proc_mgmt_bind_continuation_fn *cont,
189                                    void *st,
190                                    struct waitset *ws,
191                                    size_t lmp_buflen_words)
192 {
193     errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
194     if (err_is_fail(err)) {
195         return err;
196     }
197
198     lmpb->chan.remote_cap = ep;
199
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));
209         return err;
210     }
211
212     /* Run the RX handler; has a side-effect of registering for receive events */
213     proc_mgmt_lmp_rx_handler(lmpb);
214
215     /* Run the continuation */
216     cont(st, SYS_ERR_OK, &lmpb->b);
217
218     return SYS_ERR_OK;
219 }
220
221 errval_t proc_mgmt_bind_client(void)
222 {
223     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
224     if (b != NULL) {
225         return SYS_ERR_OK;
226     }
227
228     errval_t err;
229     iref_t iref;
230     // Try using nameserver to retrievew the proc mgmt iref.
231     err = nameservice_blocking_lookup("proc_mgmt", &iref);
232     if (err_is_fail(err)) {
233         return err;
234     }
235     
236     // Initiate bind.
237     struct proc_mgmt_bind_retst bindst = {
238         .present = false
239     };
240
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");
245     }
246
247     // Wait for bind completion.
248     while (!bindst.present) {
249         messages_wait_and_handle_next();
250     }
251
252     if (err_is_fail(bindst.err)) {
253         return bindst.err;
254     }
255
256     proc_mgmt_rpc_client_init(bindst.b);
257
258     set_proc_mgmt_binding(bindst.b);
259
260     return SYS_ERR_OK;
261 }
262
263 errval_t proc_mgmt_add_spawnd(iref_t iref, coreid_t core_id)
264 {
265     errval_t err = proc_mgmt_bind_client();
266     if (err_is_fail(err)) {
267         DEBUG_ERR(err, "proc_mgmt_bind_client");
268         return err;
269     }
270
271     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
272     assert(b != NULL);
273
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");
277     }
278
279     return err;
280 }
281
282 /**
283  * \brief Request the process manager to spawn a program on a specific core
284  *
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
293  *                        arguments
294  * \param flags           Flags to spawn
295  * \param ret_domain_cap  If non-NULL, filled in with domain cap of new domain
296  *
297  * \bug flags are currently ignored
298  */
299 errval_t proc_mgmt_spawn_program_with_caps(coreid_t core_id, const char *path,
300                                            char *const argv[],
301                                            char *const envp[],
302                                            struct capref inheritcn_cap,
303                                            struct capref argcn_cap,
304                                            uint8_t flags,
305                                            struct capref *ret_domain_cap)
306 {
307     errval_t err, msgerr;
308
309     // default to copying our environment
310     if (envp == NULL) {
311         envp = environ;
312     }
313
314     err = proc_mgmt_bind_client();
315     if (err_is_fail(err)) {
316         USER_PANIC_ERR(err, "proc_mgmt_bind_client");
317     }
318
319     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
320     assert(b != NULL);
321
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;
328     }
329
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';
336     }
337     assert(argstrpos == argstrlen);
338
339     // repeat for environment
340     size_t envstrlen = 0;
341     for (int i = 0; envp[i] != NULL; i++) {
342         envstrlen += strlen(envp[i]) + 1;
343     }
344
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';
351     }
352     assert(envstrpos == envstrlen);
353
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
359     }
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);
366         path = pathbuf;
367     }
368
369     struct capref domain_cap;
370
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);
374     } else {
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);
379     }
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)) {
383         goto out;
384     }
385
386     if (ret_domain_cap != NULL) {
387         *ret_domain_cap = domain_cap;
388     }
389
390 out:
391     return msgerr;
392     
393 }
394
395 /**
396  * \brief Request the process manager to spawn a program on a specific core
397  *
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
406  *                        arguments
407  * \param flags           Flags to spawn
408  * \param ret_domain_cap  If non-NULL, filled in with domain cap of new domain
409  *
410  * \bug flags are currently ignored
411  */
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)
415 {
416     return proc_mgmt_spawn_program_with_caps(core_id, path, argv, envp,
417                                              NULL_CAP, NULL_CAP, flags,
418                                              ret_domain_cap);
419 }
420
421 /**
422  * \brief Request the process manager to span onto a new core.
423  *
424  * \param core_id ID of core to span onto.
425  *
426  * Blocks until the new dispatcher has established an interdispatcher connection
427  * to the current one.
428  */
429 errval_t proc_mgmt_span(coreid_t core_id)
430 {
431     coreid_t my_core_id = disp_get_core_id();
432     assert (core_id != my_core_id);
433
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");
438     }
439     
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");
444     }
445
446     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
447     assert(b != NULL);
448
449     err = b->rpc_tx_vtbl.span(b, cap_domainid, core_id, st->vroot, st->frame,
450                               &msgerr);
451     if (err_is_fail(err)) {
452         USER_PANIC_ERR(err, "error sending span request to process manager");
453     }
454
455     if (err_is_fail(msgerr)) {
456         return msgerr;
457     }
458
459     while(!st->initialized) {
460         event_dispatch(get_default_waitset());
461     }
462     free(st);
463
464     return SYS_ERR_OK;
465 }
466
467 /**
468  * \brief Request the process manager to kill a domain
469  *
470  * \param domain_cap Domain ID cap for the victim
471  */
472 errval_t proc_mgmt_kill(struct capref domain_cap)
473 {
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");
478     }
479
480     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
481     assert(b != NULL);
482
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");
486     }
487
488     return msgerr;
489 }
490
491 /**
492  * \brief Inform the process manager about exiting execution.
493  */
494 errval_t proc_mgmt_exit(uint8_t status)
495 {
496     errval_t err = proc_mgmt_bind_client();
497     if (err_is_fail(err)) {
498         return err;
499     }
500
501     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
502     assert(b != NULL);
503
504     err = b->rpc_tx_vtbl.exit(b, cap_domainid, status);
505     if (err_is_fail(err)) {
506         return err;
507     }
508
509     return SYS_ERR_OK;
510 }
511
512 errval_t proc_mgmt_wait(struct capref domain_cap, uint8_t *status)
513 {
514     errval_t err, msgerr;
515     err = proc_mgmt_bind_client();
516     if (err_is_fail(err)) {
517         return err;
518     }
519
520     struct proc_mgmt_binding *b = get_proc_mgmt_binding();
521     assert(b != NULL);
522
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");
526     }
527
528     return msgerr;
529 }