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