libbarrelfish: started merging proc_mgmt_client and spawn_client
[barrelfish] / lib / barrelfish / spawn_client.c
1 /**
2  * \file
3  * \brief Client for interacting with the spawn daemon on each core
4  */
5
6 /*
7  * Copyright (c) 2010, 2011, 2012, 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, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
13  * Attn: Systems Group.
14  */
15
16 #include <string.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <barrelfish/barrelfish.h>
20 #include <barrelfish/nameservice_client.h>
21 #include <barrelfish/spawn_client.h>
22 #include <barrelfish/cpu_arch.h>
23 #include <barrelfish/proc_mgmt_client.h>
24 #include <if/spawn_defs.h>
25 #include <if/arrakis_defs.h>
26 #include <if/monitor_defs.h>
27 #include <if/octopus_defs.h>
28 #include <vfs/vfs_path.h>
29
30 // For spawn_program_on_all_cores
31 #include <octopus/getset.h> // for oct_read TODO
32 #include <octopus/trigger.h> // for NOP_TRIGGER
33
34
35
36 extern char **environ;
37
38 struct spawn_bind_retst {
39     errval_t err;
40     struct spawn_binding *b;
41     bool present;
42 };
43
44 struct arrakis_bind_retst {
45     errval_t err;
46     struct arrakis_binding *b;
47     bool present;
48 };
49
50 static void spawn_bind_cont(void *st, errval_t err, struct spawn_binding *b)
51 {
52     struct spawn_bind_retst *retst = st;
53     assert(retst != NULL);
54     assert(!retst->present);
55     retst->err = err;
56     retst->b = b;
57     retst->present = true;
58 }
59
60 static void arrakis_bind_cont(void *st, errval_t err, struct arrakis_binding *b)
61 {
62     struct arrakis_bind_retst *retst = st;
63     assert(retst != NULL);
64     assert(!retst->present);
65     retst->err = err;
66     retst->b = b;
67     retst->present = true;
68 }
69
70 static struct spawn_binding *spawn_b = NULL;
71
72 static errval_t bind_client(coreid_t coreid)
73 {
74     struct spawn_binding *cl;
75     errval_t err = SYS_ERR_OK;
76
77     // do we have a spawn client connection for this core?
78     assert(coreid < MAX_CPUS);
79     cl = get_spawn_binding(coreid);
80     if (cl == NULL) {
81         char namebuf[16];
82         snprintf(namebuf, sizeof(namebuf), "spawn.%u", coreid);
83         namebuf[sizeof(namebuf) - 1] = '\0';
84
85         iref_t iref;
86         err = nameservice_blocking_lookup(namebuf, &iref);
87         if (err_is_fail(err)) {
88             //DEBUG_ERR(err, "spawn daemon on core %u not found\n", coreid);
89             return err;
90         }
91
92         // initiate bind
93         struct spawn_bind_retst bindst = { .present = false };
94         err = spawn_bind(iref, spawn_bind_cont, &bindst, get_default_waitset(),
95                          IDC_BIND_FLAGS_DEFAULT);
96         if (err_is_fail(err)) {
97             DEBUG_ERR(err, "spawn_bind failed");
98             return err;
99         }
100
101         // XXX: block for bind completion
102         while (!bindst.present) {
103             messages_wait_and_handle_next();
104         }
105
106         if (err_is_fail(bindst.err)) {
107             return bindst.err;
108         }
109
110         spawn_b = bindst.b;
111
112         spawn_rpc_client_init(bindst.b);
113         set_spawn_binding(coreid, bindst.b);
114     }
115
116     return err;
117 }
118
119 errval_t spawn_bind_iref(iref_t iref, struct spawn_binding **ret_client)
120 {
121     assert(ret_client != NULL);
122
123     struct spawn_bind_retst bindst = { .present = false };
124     errval_t err = spawn_bind(iref, spawn_bind_cont, &bindst,
125                               get_default_waitset(), IDC_BIND_FLAGS_DEFAULT);
126     if (err_is_fail(err)) {
127         DEBUG_ERR(err, "spawn_bind failed");
128         return err;
129     }
130
131     // XXX: block for bind completion
132     while (!bindst.present) {
133         messages_wait_and_handle_next();
134     }
135
136     if (err_is_fail(bindst.err)) {
137         return bindst.err;
138     }
139
140     spawn_rpc_client_init(bindst.b);
141     *ret_client = bindst.b;
142     // set_spawn_binding(coreid, bindst.b);
143
144     return err;
145 }
146
147 /**
148  * \brief Request the spawn daemon on a specific core to spawn a program
149  *
150  * \param coreid        Core ID on which to spawn the program
151  * \param path          Absolute path in the file system to an executable image
152  *                      suitable for the given core
153  * \param argv          Command-line arguments, NULL-terminated
154  * \param envp          Optional environment, NULL-terminated
155  *                      (pass NULL to inherit)
156  * \param inheritcn_cap Cap to a CNode containing capabilities to be inherited
157  * \param argcn_cap     Cap to a CNode containing capabilities passed as
158  *                      arguments
159  * \param flags         Flags to spawn
160  * \param domain_Cap  If non-NULL, filled in with domain cap of program
161  *
162  * \bug flags are currently ignored
163  */
164 errval_t spawn_program_with_caps(coreid_t coreid, const char *path,
165                                  char *const argv[], char *const envp[],
166                                  struct capref inheritcn_cap,
167                                  struct capref argcn_cap, spawn_flags_t flags,
168                                  struct capref *domain_cap)
169 {
170     return proc_mgmt_spawn_program_with_caps(coreid, path, argv, envp, inheritcn_cap, 
171                                              argcn_cap, flags, domain_cap);
172 }
173
174 errval_t spawn_arrakis_program(coreid_t coreid, const char *path,
175                                char *const argv[], char *const envp[],
176                                struct capref inheritcn_cap,
177                                struct capref argcn_cap, spawn_flags_t flags,
178                                domainid_t *ret_domainid)
179 {
180     struct arrakis_binding *cl;
181     errval_t err, msgerr;
182
183     // default to copying our environment
184     if (envp == NULL) {
185         envp = environ;
186     }
187
188     // do we have a arrakis client connection for this core?
189     assert(coreid < MAX_CPUS);
190     cl = get_arrakis_binding(coreid);
191     if (cl == NULL) {
192         char namebuf[16];
193         snprintf(namebuf, sizeof(namebuf), "arrakis.%u", coreid);
194         namebuf[sizeof(namebuf) - 1] = '\0';
195
196         iref_t iref;
197         err = nameservice_blocking_lookup(namebuf, &iref);
198         if (err_is_fail(err)) {
199             //DEBUG_ERR(err, "arrakis daemon on core %u not found\n", coreid);
200             return err;
201         }
202
203         // initiate bind
204         struct arrakis_bind_retst bindst = { .present = false };
205         err = arrakis_bind(iref, arrakis_bind_cont, &bindst, get_default_waitset(),
206                            IDC_BIND_FLAGS_DEFAULT);
207         if (err_is_fail(err)) {
208             USER_PANIC_ERR(err, "arrakis_bind failed");
209         }
210
211         // XXX: block for bind completion
212         while (!bindst.present) {
213             messages_wait_and_handle_next();
214         }
215
216         if(err_is_fail(bindst.err)) {
217             USER_PANIC_ERR(bindst.err, "asynchronous error during arrakis_bind");
218         }
219         assert(bindst.b != NULL);
220
221         arrakis_rpc_client_init(bindst.b);
222         set_arrakis_binding(coreid, bindst.b);
223     }
224
225     // construct argument "string"
226     // \0-separated strings in contiguous character buffer
227     // this is needed, as flounder can't send variable-length arrays of strings
228     size_t argstrlen = 0;
229     for (int i = 0; argv[i] != NULL; i++) {
230         argstrlen += strlen(argv[i]) + 1;
231     }
232
233     char argstr[argstrlen];
234     size_t argstrpos = 0;
235     for (int i = 0; argv[i] != NULL; i++) {
236         strcpy(&argstr[argstrpos], argv[i]);
237         argstrpos += strlen(argv[i]);
238         argstr[argstrpos++] = '\0';
239     }
240     assert(argstrpos == argstrlen);
241
242     // repeat for environment
243     size_t envstrlen = 0;
244     for (int i = 0; envp[i] != NULL; i++) {
245         envstrlen += strlen(envp[i]) + 1;
246     }
247
248     char envstr[envstrlen];
249     size_t envstrpos = 0;
250     for (int i = 0; envp[i] != NULL; i++) {
251         strcpy(&envstr[envstrpos], envp[i]);
252         envstrpos += strlen(envp[i]);
253         envstr[envstrpos++] = '\0';
254     }
255     assert(envstrpos == envstrlen);
256
257
258     domainid_t domain_id;
259
260     // make an unqualified path absolute using the $PATH variable
261     // TODO: implement search (currently assumes PATH is a single directory)
262     char *searchpath = getenv("PATH");
263     if (searchpath == NULL) {
264         searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root
265     }
266     size_t buflen = strlen(path) + strlen(searchpath) + 2;
267     char pathbuf[buflen];
268     if (path[0] != VFS_PATH_SEP) {
269         snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path);
270         pathbuf[buflen - 1] = '\0';
271         //vfs_path_normalise(pathbuf);
272         path = pathbuf;
273     }
274
275     err = cl->rpc_tx_vtbl.spawn_arrakis_domain(cl, path, argstr, argstrlen,
276                                         envstr, envstrlen, &msgerr, &domain_id);
277     if (err_is_fail(err)) {
278         USER_PANIC_ERR(err, "error sending arrakis request");
279     } else if (err_is_fail(msgerr)) {
280         return msgerr;
281     }
282
283     if (ret_domainid != NULL) {
284         *ret_domainid = domain_id;
285     }
286
287     return msgerr;
288 }
289
290
291 /**
292  * \brief Request the spawn daemon on a specific core to spawn a program
293  *
294  * \param coreid Core ID on which to spawn the program
295  * \param path   Absolute path in the file system to an executable image
296  *                        suitable for the given core
297  * \param argv   Command-line arguments, NULL-terminated
298  * \param envp   Optional environment, NULL-terminated (pass NULL to inherit)
299  * \param flags  Flags to spawn
300  * \param ret_domain_cap If non-NULL, filled in with domain cap of program
301  *
302  * \bug flags are currently ignored
303  */
304 errval_t spawn_program(coreid_t coreid, const char *path,
305                        char *const argv[], char *const envp[],
306                        spawn_flags_t flags, struct capref* ret_domain_cap)
307 {
308     return spawn_program_with_caps(coreid, path, argv, envp, NULL_CAP,
309                                    NULL_CAP, flags, ret_domain_cap);
310 }
311
312
313
314 /**
315  * \brief Request a program be spawned on all cores in the system
316  *
317  * \param same_core Iff false, don't spawn on the same core as the caller
318  * \param path   Absolute path in the file system to an executable image
319  *                        suitable for the given core
320  * \param argv   Command-line arguments, NULL-terminated
321  * \param envp   Optional environment, NULL-terminated (pass NULL to inherit)
322  * \param flags  Flags to spawn
323  * \param ret_domainid If non-NULL, filled in with domain ID of program
324  * \param count How much programs it spawned
325  *
326  * \note This function is for legacy compatibility with existing benchmark/test
327  *    code, and SHOULD NOT BE USED IN NEW CODE UNLESS YOU HAVE A GOOD REASON!
328  *    It doesn't make much sense from a scalability perspective, and is
329  *    probably useless on a heterogeneous system.
330  */
331 errval_t spawn_program_on_all_cores(bool same_core, const char *path,
332                                     char *const argv[], char *const envp[],
333                                     spawn_flags_t flags, struct capref *ret_domain_cap,
334                                     coreid_t* spawn_count)
335 {
336     // TODO: handle flags, domain ID
337     errval_t err = SYS_ERR_OK;
338
339     struct octopus_binding *r = get_octopus_binding();
340     if (r == NULL) {
341         return LIB_ERR_NAMESERVICE_NOT_BOUND;
342     }
343
344     // FIXME: world's most (kinda less now) broken implementation...
345     char** names = NULL;
346     size_t count = 0;
347
348     static char* spawnds = "r'spawn.[0-9]+' { iref: _ }";
349     struct octopus_get_names_response__rx_args reply;
350     err = r->rpc_tx_vtbl.get_names(r, spawnds, NOP_TRIGGER, reply.output, &reply.tid,
351                             &reply.error_code);
352     if (err_is_fail(err) || err_is_fail(reply.error_code)) {
353         err = err_push(err, SPAWN_ERR_FIND_SPAWNDS);
354         goto out;
355     }
356
357     err = oct_parse_names(reply.output, &names, &count);
358     if (err_is_fail(err)) {
359         goto out;
360     }
361
362     for (size_t c = 0; c < count; c++) {
363         coreid_t coreid;
364         int ret = sscanf(names[c], "spawn.%hhu", &coreid);
365         if (ret != 1) {
366             err = SPAWN_ERR_MALFORMED_SPAWND_RECORD;
367             goto out;
368         }
369
370         if (!same_core && coreid == disp_get_core_id()) {
371             continue;
372         }
373
374         err = proc_mgmt_spawn_program(c, path, argv, envp, flags, NULL);
375         if (err_is_ok(err) && spawn_count != NULL) {
376             *spawn_count += 1;
377         }
378
379         if (err_is_fail(err)) {
380             DEBUG_ERR(err, "error spawning %s on core %u\n", path, c);
381             goto out;
382         }
383     }
384
385 out:
386     oct_free_names(names, count);
387     return err;
388 }
389
390 errval_t spawn_binding(coreid_t coreid, struct spawn_binding **ret_client)
391 {
392     errval_t err = bind_client(coreid);
393     if (err_is_fail(err)) {
394         return err;
395     }
396
397     *ret_client = get_spawn_binding(coreid);
398     return SYS_ERR_OK;
399 }
400
401 /**
402  * \brief Kill a domain.
403  */
404 errval_t spawn_kill(struct capref domain_cap)
405 {
406     return proc_mgmt_kill(domain_cap);
407 }
408
409 errval_t spawn_wait_compat(uint8_t domainid,
410                            uint8_t *exitcode, bool nohang)
411  {
412     errval_t err, reterr;
413
414     err = bind_client(disp_get_core_id());
415     if (err_is_fail(err)) {
416         return err;
417     }
418     struct spawn_binding *cl = get_spawn_binding(disp_get_core_id());
419     assert(cl != NULL);
420
421     err = cl->rpc_tx_vtbl.wait(cl, domainid, nohang, exitcode, &reterr);
422     if (err_is_fail(err)) {
423         return err;
424     }
425
426     return reterr;
427  }
428
429 /**
430  * \brief Exit this domain.
431  */
432 errval_t spawn_exit(uint8_t exitcode)
433 {
434     return proc_mgmt_exit(exitcode);
435 }
436
437 /**
438  * \brief Wait for spawned proccess to exit on core.
439  */
440 errval_t spawn_wait_coreid(coreid_t coreid, struct capref domain_cap,
441                            uint8_t *exitcode, bool nohang)
442 {
443     return spawn_wait_core(disp_get_core_id(), domain_cap, exitcode, nohang);
444 }
445
446 /**
447  * \brief Wait for the termination of a domain on a remote core.
448  */
449 errval_t spawn_wait_core(coreid_t coreid, struct capref domain_cap,
450                          uint8_t *exitcode, bool nohang)
451 {
452     return proc_mgmt_wait(domain_cap, exitcode);
453 }
454
455 /**
456  * \brief Wait for spawned proccess to exit on current core.
457  */
458 errval_t spawn_wait(struct capref domain_cap, uint8_t *exitcode, bool nohang)
459 {
460     return spawn_wait_coreid(disp_get_core_id(), domain_cap, exitcode, nohang);
461 }
462
463 /**
464  * \brief Get the list of domains for ps like implementation
465  */
466 errval_t spawn_get_domain_list(uint8_t **domains, size_t *len)
467 {
468     errval_t err;
469
470     struct spawn_binding *cl;
471     err = spawn_binding(disp_get_core_id(), &cl);
472     if (err_is_fail(err)) {
473         USER_PANIC_ERR(err, "spawn_binding");
474     }
475     assert(cl != NULL);
476
477     struct spawn_get_domainlist_response__rx_args reply;
478     err = cl->rpc_tx_vtbl.get_domainlist(cl, reply.domains, len);
479     if (err_is_fail(err)) {
480         USER_PANIC_ERR(err, "get_domainlist");
481     }
482
483     *domains = memdup(reply.domains, *len);
484     return SYS_ERR_OK;
485 }
486
487 /**
488  * \brief Get the status of a domain for ps like implementation
489  */
490 errval_t spawn_get_status(uint8_t domain, struct spawn_ps_entry *pse,
491                           char **argbuf, size_t *arglen, errval_t *reterr)
492 {
493     errval_t err;
494
495     struct spawn_binding *cl;
496     err = spawn_binding(disp_get_core_id(), &cl);
497     if (err_is_fail(err)) {
498         USER_PANIC_ERR(err, "spawn_binding");
499     }
500     assert(cl != NULL);
501
502     struct spawn_status_response__rx_args reply;
503     err = cl->rpc_tx_vtbl.status(cl, domain, (spawn_ps_entry_t *)pse, reply.argv,
504                           arglen, reterr);
505     if (err_is_fail(err)) {
506         USER_PANIC_ERR(err, "status");
507     }
508
509     *argbuf = memdup(reply.argv, *arglen);
510     return SYS_ERR_OK;
511 }
512
513 /**
514  * \brief Dump capabilities for a given domain
515  */
516 errval_t spawn_dump_capabilities_compat(domainid_t domainid)
517 {
518     errval_t err, reterr;
519
520     err = bind_client(disp_get_core_id());
521     if (err_is_fail(err)) {
522         return err;
523     }
524     struct spawn_binding *cl = get_spawn_binding(disp_get_core_id());
525     assert(cl != NULL);
526
527     err = cl->rpc_tx_vtbl.dump_capabilities(cl, domainid, &reterr);
528     if (err_is_fail(err)) {
529         return err;
530     }
531
532     return reterr;
533 }
534
535 /**
536  * \brief Utility function to create an inherit cnode
537  * and copy caps into it.
538  *
539  * \param inheritcn_capp Pointer to capref, filled-in with location of inheritcn
540  *                       capability.
541  * \param fdcap          fdcap to copy into inherit cnode.
542  * \param sidcap         sidcap to copy into inherit cnode.
543  * \param kernelcap      kernelcap to copy into inherit cnode.
544  *
545  * \retval SYS_ERR_OK inherticn_capp is allocated and contains copies of the
546  * provided caps.
547  */
548 errval_t alloc_inheritcn_with_caps(struct capref *inheritcn_capp,
549                                    struct capref fdcap,
550                                    struct capref sidcap,
551                                    struct capref kernelcap)
552 {
553     errval_t err;
554
555     // construct inherit CNode
556     struct cnoderef inheritcn;
557     err = cnode_create_l2(inheritcn_capp, &inheritcn);
558     if (err_is_fail(err)) {
559         return err;
560     }
561
562     if (!capref_is_null(fdcap)) {
563         // copy fdcap to inherit Cnode
564         struct capref dest = {
565             .cnode = inheritcn,
566             .slot  = INHERITCN_SLOT_FDSPAGE
567         };
568         err = cap_copy(dest, fdcap);
569         if (err_is_fail(err)) {
570             return err;
571         }
572     }
573
574     if (!capref_is_null(sidcap)) {
575         // copy fdcap to inherit Cnode
576         struct capref dest = {
577             .cnode = inheritcn,
578             .slot  = INHERITCN_SLOT_SESSIONID
579         };
580         err = cap_copy(dest, sidcap);
581         if (err_is_fail(err)) {
582             return err;
583         }
584     }
585
586     if (!capref_is_null(kernelcap)) {
587         // copy fdcap to inherit Cnode
588         struct capref dest = {
589             .cnode = inheritcn,
590             .slot  = INHERITCN_SLOT_KERNELCAP
591         };
592         err = cap_copy(dest, kernelcap);
593         if (err_is_fail(err)) {
594             return err;
595         }
596     }
597
598     return SYS_ERR_OK;
599 }