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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <barrelfish/barrelfish.h>
16#include <barrelfish/nameservice_client.h>
17#include <barrelfish/spawn_client.h>
18#include <if/octopus_defs.h>
19#include <if/proc_mgmt_defs.h>
20#include <if/arrakis_defs.h>
21#include <if/monitor_defs.h>
22#include <if/spawn_defs.h>
23#include <vfs/vfs_path.h>
24
25// For spawn_program_on_all_cores
26#include <octopus/getset.h> // for oct_read TODO
27#include <octopus/trigger.h> // for NOP_TRIGGER
28
29
30struct proc_mgmt_bind_retst {
31    errval_t err;
32    struct proc_mgmt_binding *b;
33    bool present;
34};
35
36struct spawn_bind_retst {
37    errval_t err;
38    struct spawn_binding *b;
39    bool present;
40};
41
42struct arrakis_bind_retst {
43    errval_t err;
44    struct arrakis_binding *b;
45    bool present;
46};
47
48extern char **environ;
49
50static 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
60static 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
70static struct spawn_binding *spawn_b = NULL;
71
72static 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
119errval_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
148static void error_handler(struct proc_mgmt_binding *b, errval_t err)
149{
150#if defined(__x86_64__) || defined(__i386__)
151    debug_printf("%p \n",  __builtin_return_address(0));
152#endif
153    debug_err(__FILE__, __func__, __LINE__, err,
154              "asynchronous error in proc_mgmt binding");
155    abort();
156}
157
158static void proc_mgmt_bind_cont(void *st, errval_t err,
159        struct proc_mgmt_binding *b)
160{
161    struct proc_mgmt_bind_retst *retst = (struct proc_mgmt_bind_retst*) st;
162    assert(retst != NULL);
163    assert(!retst->present);
164    retst->err = err;
165    retst->b = b;
166    retst->present = true;
167    b->st = retst;
168}
169
170static void proc_mgmt_accept_recv_handler(void *arg)
171{
172    struct proc_mgmt_lmp_binding *b = arg;
173    struct lmp_recv_msg msg = LMP_RECV_MSG_INIT;
174    struct capref cap;
175    errval_t err;
176
177    // try to retrieve a message from the channel
178    err = lmp_chan_recv(&b->chan, &msg, &cap);
179    if (err_is_fail(err)) {
180        if (err_no(err) == LIB_ERR_NO_LMP_MSG) {
181            // nothing there, re-register
182            struct event_closure recv_handler = {
183                .handler = proc_mgmt_accept_recv_handler,
184                .arg = b,
185            };
186            err = lmp_chan_register_recv(&b->chan, b->b.waitset, recv_handler);
187            b->b.error_handler(&b->b, err_push(err, LIB_ERR_CHAN_REGISTER_RECV));
188        } else {
189            // real error, report to user
190            b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_CHAN_RECV));
191        }
192        return;
193    }
194
195    // TODO(razvan): LMP_PROC_MGMT_ACCEPT ?
196    assert(b->chan.connstate == LMP_MONITOR_ACCEPT);
197    assert(!capref_is_null(cap));
198    b->chan.remote_cap = cap;
199    b->chan.connstate = LMP_CONNECTED;
200
201    /* allocate a new receive slot */
202    err = lmp_chan_alloc_recv_slot(&b->chan);
203    if (err_is_fail(err)) {
204        // XXX: report the error, but continue
205        b->b.error_handler(&b->b, err_push(err, LIB_ERR_LMP_ALLOC_RECV_SLOT));
206    }
207
208    /* Run the RX handler; has a side-effect of registering for receive events */
209    proc_mgmt_lmp_rx_handler(b);
210}
211
212static errval_t init_lmp_binding(struct proc_mgmt_lmp_binding *lmpb,
213                                 struct waitset *ws,
214                                 size_t buflen_words)
215{
216    errval_t err;
217
218    proc_mgmt_lmp_init(lmpb, ws);
219
220    /* allocate a cap slot for the new endpoint cap */
221    err = slot_alloc(&lmpb->chan.local_cap);
222    if (err_is_fail(err)) {
223        return err_push(err, LIB_ERR_SLOT_ALLOC);
224    }
225
226    /* allocate a local endpoint */
227    err = lmp_endpoint_create_in_slot(buflen_words, lmpb->chan.local_cap,
228                                      &lmpb->chan.endpoint);
229    if (err_is_fail(err)) {
230        // TODO(razvan): Free cap slot.
231        return err_push(err, LIB_ERR_ENDPOINT_CREATE);
232    }
233
234    /* allocate an initial receive slot */
235    err = lmp_chan_alloc_recv_slot(&lmpb->chan);
236    if (err_is_fail(err)) {
237        return err;
238    }
239
240    /* setup error handler */
241    lmpb->b.error_handler = error_handler;
242
243    /* setup initial receive handlers */
244    // TODO(razvan): Don't think this is needed, but dunno for sure yet.
245    // lmpb->b.rx_vtbl = monitor_rx_vtbl;
246
247    // connect handlers
248    lmpb->b.change_waitset(&lmpb->b, lmpb->b.waitset);
249    return SYS_ERR_OK;
250}
251
252/**
253 * \brief Accept a new LMP binding to a proc mgmt client.
254 *
255 * Should only be used in the process manager.
256 *
257 * \param lmpb         Storage for binding state
258 * \param ws           Waitset for handling incoming messages
259 * \param buflen_words Size of incoming buffer, in number of words
260 */
261errval_t proc_mgmt_client_lmp_accept(struct proc_mgmt_lmp_binding *lmpb,
262                                     struct waitset *ws,
263                                     size_t lmp_buflen_words)
264{
265    errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
266    if (err_is_fail(err)) {
267        return err;
268    }
269
270    lmpb->chan.connstate = LMP_MONITOR_ACCEPT;  // TODO(razvan): LMP_PROC_MGMT_ACCEPT?
271    lmpb->chan.remote_cap = NULL_CAP; // will be sent to us by the client
272
273    /* Register for receive notification on our special handler */
274    struct event_closure receive_handler = {
275        .handler = proc_mgmt_accept_recv_handler,
276        .arg = lmpb,
277    };
278    err = lmp_chan_register_recv(&lmpb->chan, ws, receive_handler);
279    if (err_is_fail(err)) {
280        return err;  // TODO(razvan): cleanup?
281    }
282
283    return SYS_ERR_OK;
284}
285
286
287/**
288 * \brief Initiate a new LMP binding to the process manager
289 *
290 * To be used by the monitor for setting up the privileged channel used for
291 * spawnd discovery.
292 * Requires an explicit remote endpoint cap allocated by the process manager.
293 *
294 * \param lmpb         Storage for binding state
295 * \param ep           Remote endpoint of the process manager
296 * \param ws           Waitset for handling incoming messages
297 * \param cont         Continuation for when binding completes or fails
298 * \param st           State passed to continuation function
299 * \param buflen_words Size of incoming buffer, in number of words
300 */
301errval_t proc_mgmt_client_lmp_bind(struct proc_mgmt_lmp_binding *lmpb,
302                                   struct capref ep,
303                                   proc_mgmt_bind_continuation_fn *cont,
304                                   void *st,
305                                   struct waitset *ws,
306                                   size_t lmp_buflen_words)
307{
308    errval_t err = init_lmp_binding(lmpb, ws, lmp_buflen_words);
309    if (err_is_fail(err)) {
310        return err;
311    }
312
313    lmpb->chan.remote_cap = ep;
314
315    // Send the local endpoint cap to the process manager.
316    lmpb->chan.connstate = LMP_CONNECTED; /* pre-established */
317    err = lmp_chan_send0(&lmpb->chan, 0, lmpb->chan.local_cap);
318    if (err_is_fail(err)) {
319        // TODO(razvan): This, below.
320        /* XXX: I'm lazily assuming this can never fail with a transient error,
321         * since we only do it once at dispatcher startup. If not, we need to
322         * register and retry here */
323        assert(!lmp_err_is_transient(err));
324        return err;
325    }
326
327    /* Run the RX handler; has a side-effect of registering for receive events */
328    proc_mgmt_lmp_rx_handler(lmpb);
329
330    /* Run the continuation */
331    cont(st, SYS_ERR_OK, &lmpb->b);
332
333    return SYS_ERR_OK;
334}
335
336errval_t proc_mgmt_bind_client(void)
337{
338    struct proc_mgmt_binding *b = get_proc_mgmt_binding();
339    if (b != NULL) {
340        return SYS_ERR_OK;
341    }
342
343    errval_t err;
344    iref_t iref;
345    // Try using nameserver to retrievew the proc mgmt iref.
346    err = nameservice_blocking_lookup("proc_mgmt", &iref);
347    if (err_is_fail(err)) {
348        return err;
349    }
350
351    // Initiate bind.
352    struct proc_mgmt_bind_retst bindst = {
353        .present = false
354    };
355
356    err = proc_mgmt_bind(iref, proc_mgmt_bind_cont, &bindst,
357            get_default_waitset(), /*IDC_BIND_FLAG_RPC_CAP_TRANSFER*/IDC_BIND_FLAGS_DEFAULT);
358    if (err_is_fail(err)) {
359        USER_PANIC_ERR(err, "proc_mgmt_bind");
360    }
361
362    // Wait for bind completion.
363    while (!bindst.present) {
364        messages_wait_and_handle_next();
365    }
366
367    if (err_is_fail(bindst.err)) {
368        return bindst.err;
369    }
370
371    proc_mgmt_rpc_client_init(bindst.b);
372
373    set_proc_mgmt_binding(bindst.b);
374
375    return SYS_ERR_OK;
376}
377
378errval_t proc_mgmt_add_spawnd(iref_t iref, coreid_t core_id)
379{
380    errval_t err = proc_mgmt_bind_client();
381    if (err_is_fail(err)) {
382        DEBUG_ERR(err, "proc_mgmt_bind_client");
383        return err;
384    }
385
386    struct proc_mgmt_binding *b = get_proc_mgmt_binding();
387    assert(b != NULL);
388
389    err = b->tx_vtbl.add_spawnd(b, NOP_CONT, core_id, iref);
390    if (err_is_fail(err)) {
391        DEBUG_ERR(err, "add_spawnd");
392    }
393
394    return err;
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 */
414errval_t spawn_program_with_caps(coreid_t core_id, const char *path,
415                                 char *const argv[],
416                                 char *const envp[],
417                                 struct capref inheritcn_cap,
418                                 struct capref argcn_cap,
419                                 uint8_t flags,
420                                 struct capref *ret_domain_cap)
421{
422    errval_t err, msgerr;
423
424    // default to copying our environment
425    if (envp == NULL) {
426        envp = environ;
427    }
428
429    err = proc_mgmt_bind_client();
430    if (err_is_fail(err)) {
431        USER_PANIC_ERR(err, "proc_mgmt_bind_client");
432    }
433
434    struct proc_mgmt_binding *b = get_proc_mgmt_binding();
435    assert(b != NULL);
436
437    // construct argument "string"
438    // \0-separated strings in contiguous character buffer
439    // this is needed, as flounder can't send variable-length arrays of strings
440    size_t argstrlen = 0;
441
442    for (int i = 0; argv[i] != NULL; i++) {
443        argstrlen += strlen(argv[i]) + 1;
444    }
445
446    char argstr[argstrlen];
447    size_t argstrpos = 0;
448    for (int i = 0; argv[i] != NULL; i++) {
449        strcpy(&argstr[argstrpos], argv[i]);
450        argstrpos += strlen(argv[i]);
451        argstr[argstrpos++] = '\0';
452    }
453    assert(argstrpos == argstrlen);
454
455    // repeat for environment
456    size_t envstrlen = 0;
457    for (int i = 0; envp[i] != NULL; i++) {
458        envstrlen += strlen(envp[i]) + 1;
459    }
460
461    char envstr[envstrlen];
462    size_t envstrpos = 0;
463    for (int i = 0; envp[i] != NULL; i++) {
464        strcpy(&envstr[envstrpos], envp[i]);
465        envstrpos += strlen(envp[i]);
466        envstr[envstrpos++] = '\0';
467    }
468    assert(envstrpos == envstrlen);
469
470    // make an unqualified path absolute using the $PATH variable
471    // TODO: implement search (currently assumes PATH is a single directory)
472    char *searchpath = getenv("PATH");
473    if (searchpath == NULL) {
474        searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root
475    }
476    size_t buflen = strlen(path) + strlen(searchpath) + 2;
477    char pathbuf[buflen];
478    if (path[0] != VFS_PATH_SEP) {
479        snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path);
480        pathbuf[buflen - 1] = '\0';
481        //vfs_path_normalise(pathbuf);
482        path = pathbuf;
483    }
484
485    struct capref domain_cap;
486    err = slot_alloc(&domain_cap);
487    if (err_is_fail(err)) {
488        return err;
489    }
490
491    if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) {
492        err = b->rpc_tx_vtbl.spawn(b, core_id, path, argstr, argstrlen, envstr,
493                                   envstrlen, flags, &msgerr, &domain_cap);
494    } else {
495        err = b->rpc_tx_vtbl.spawn_with_caps(b, core_id, path, argstr,
496                                             argstrlen, envstr, envstrlen,
497                                             inheritcn_cap, argcn_cap, flags,
498                                             &msgerr, &domain_cap);
499    }
500    if (err_is_fail(err)) {
501        USER_PANIC_ERR(err, "error sending spawn request to process manager");
502    } else if (err_is_fail(msgerr)) {
503        goto out;
504    }
505
506    if (ret_domain_cap != NULL) {
507        *ret_domain_cap = domain_cap;
508    }
509
510out:
511    return msgerr;
512
513}
514
515/**
516 * \brief Request the process manager to spawn a program on a specific core
517 *
518 * \param coreid          Core ID on which to spawn the program
519 * \param path            Absolute path in the file system to an executable
520 *                        image suitable for the given core
521 * \param argv            Command-line arguments, NULL-terminated
522 * \param envp            Optional environment, NULL-terminated
523 *                        (pass NULL to inherit)
524 * \param inheritcn_cap   Cap to a CNode containing capabilities to be inherited
525 * \param argcn_cap       Cap to a CNode containing capabilities passed as
526 *                        arguments
527 * \param flags           Flags to spawn
528 * \param ret_domain_cap  If non-NULL, filled in with domain cap of new domain
529 *
530 * \bug flags are currently ignored
531 */
532errval_t spawn_program(coreid_t core_id, const char *path,
533                       char *const argv[], char *const envp[],
534                       uint8_t flags, struct capref *ret_domain_cap)
535{
536    return spawn_program_with_caps(core_id, path, argv, envp,
537                                   NULL_CAP, NULL_CAP, flags,
538                                   ret_domain_cap);
539}
540
541errval_t spawn_arrakis_program(coreid_t coreid, const char *path,
542                               char *const argv[], char *const envp[],
543                               struct capref inheritcn_cap,
544                               struct capref argcn_cap, spawn_flags_t flags,
545                               domainid_t *ret_domainid)
546{
547    struct arrakis_binding *cl;
548    errval_t err, msgerr;
549
550    // default to copying our environment
551    if (envp == NULL) {
552        envp = environ;
553    }
554
555    // do we have a arrakis client connection for this core?
556    assert(coreid < MAX_CPUS);
557    cl = get_arrakis_binding(coreid);
558    if (cl == NULL) {
559        char namebuf[16];
560        snprintf(namebuf, sizeof(namebuf), "arrakis.%u", coreid);
561        namebuf[sizeof(namebuf) - 1] = '\0';
562
563        iref_t iref;
564        err = nameservice_blocking_lookup(namebuf, &iref);
565        if (err_is_fail(err)) {
566            //DEBUG_ERR(err, "arrakis daemon on core %u not found\n", coreid);
567            return err;
568        }
569
570        // initiate bind
571        struct arrakis_bind_retst bindst = { .present = false };
572        err = arrakis_bind(iref, arrakis_bind_cont, &bindst, get_default_waitset(),
573                           IDC_BIND_FLAGS_DEFAULT);
574        if (err_is_fail(err)) {
575            USER_PANIC_ERR(err, "arrakis_bind failed");
576        }
577
578        // XXX: block for bind completion
579        while (!bindst.present) {
580            messages_wait_and_handle_next();
581        }
582
583        if(err_is_fail(bindst.err)) {
584            USER_PANIC_ERR(bindst.err, "asynchronous error during arrakis_bind");
585        }
586        assert(bindst.b != NULL);
587
588        arrakis_rpc_client_init(bindst.b);
589        set_arrakis_binding(coreid, bindst.b);
590    }
591
592    // construct argument "string"
593    // \0-separated strings in contiguous character buffer
594    // this is needed, as flounder can't send variable-length arrays of strings
595    size_t argstrlen = 0;
596    for (int i = 0; argv[i] != NULL; i++) {
597        argstrlen += strlen(argv[i]) + 1;
598    }
599
600    char argstr[argstrlen];
601    size_t argstrpos = 0;
602    for (int i = 0; argv[i] != NULL; i++) {
603        strcpy(&argstr[argstrpos], argv[i]);
604        argstrpos += strlen(argv[i]);
605        argstr[argstrpos++] = '\0';
606    }
607    assert(argstrpos == argstrlen);
608
609    // repeat for environment
610    size_t envstrlen = 0;
611    for (int i = 0; envp[i] != NULL; i++) {
612        envstrlen += strlen(envp[i]) + 1;
613    }
614
615    char envstr[envstrlen];
616    size_t envstrpos = 0;
617    for (int i = 0; envp[i] != NULL; i++) {
618        strcpy(&envstr[envstrpos], envp[i]);
619        envstrpos += strlen(envp[i]);
620        envstr[envstrpos++] = '\0';
621    }
622    assert(envstrpos == envstrlen);
623
624
625    domainid_t domain_id;
626
627    // make an unqualified path absolute using the $PATH variable
628    // TODO: implement search (currently assumes PATH is a single directory)
629    char *searchpath = getenv("PATH");
630    if (searchpath == NULL) {
631        searchpath = VFS_PATH_SEP_STR; // XXX: just put it in the root
632    }
633    size_t buflen = strlen(path) + strlen(searchpath) + 2;
634    char pathbuf[buflen];
635    if (path[0] != VFS_PATH_SEP) {
636        snprintf(pathbuf, buflen, "%s%c%s", searchpath, VFS_PATH_SEP, path);
637        pathbuf[buflen - 1] = '\0';
638        //vfs_path_normalise(pathbuf);
639        path = pathbuf;
640    }
641
642    err = cl->rpc_tx_vtbl.spawn_arrakis_domain(cl, path, argstr, argstrlen,
643                                        envstr, envstrlen, &msgerr, &domain_id);
644    if (err_is_fail(err)) {
645        USER_PANIC_ERR(err, "error sending arrakis request");
646    } else if (err_is_fail(msgerr)) {
647        return msgerr;
648    }
649
650    if (ret_domainid != NULL) {
651        *ret_domainid = domain_id;
652    }
653
654    return msgerr;
655}
656
657/**
658 * \brief Request a program be spawned on all cores in the system
659 *
660 * \param same_core Iff false, don't spawn on the same core as the caller
661 * \param path   Absolute path in the file system to an executable image
662 *                        suitable for the given core
663 * \param argv   Command-line arguments, NULL-terminated
664 * \param envp   Optional environment, NULL-terminated (pass NULL to inherit)
665 * \param flags  Flags to spawn
666 * \param ret_domainid If non-NULL, filled in with domain ID of program
667 * \param count How much programs it spawned
668 *
669 * \note This function is for legacy compatibility with existing benchmark/test
670 *    code, and SHOULD NOT BE USED IN NEW CODE UNLESS YOU HAVE A GOOD REASON!
671 *    It doesn't make much sense from a scalability perspective, and is
672 *    probably useless on a heterogeneous system.
673 */
674errval_t spawn_program_on_all_cores(bool same_core, const char *path,
675                                    char *const argv[], char *const envp[],
676                                    spawn_flags_t flags, struct capref *ret_domain_cap,
677                                    coreid_t* spawn_count)
678{
679    // TODO: handle flags, domain ID
680    errval_t err = SYS_ERR_OK;
681
682    struct octopus_binding *r = get_octopus_binding();
683    if (r == NULL) {
684        return LIB_ERR_NAMESERVICE_NOT_BOUND;
685    }
686
687    // FIXME: world's most (kinda less now) broken implementation...
688    char** names = NULL;
689    size_t count = 0;
690
691    static char* spawnds = "r'spawn.[0-9]+' { iref: _ }";
692    struct octopus_get_names_response__rx_args reply;
693    err = r->rpc_tx_vtbl.get_names(r, spawnds, NOP_TRIGGER, reply.output, &reply.tid,
694                            &reply.error_code);
695    if (err_is_fail(err) || err_is_fail(reply.error_code)) {
696        err = err_push(err, SPAWN_ERR_FIND_SPAWNDS);
697        goto out;
698    }
699
700    err = oct_parse_names(reply.output, &names, &count);
701    if (err_is_fail(err)) {
702        goto out;
703    }
704
705    for (size_t c = 0; c < count; c++) {
706        coreid_t coreid;
707        int ret = sscanf(names[c], "spawn.%hhu", &coreid);
708        if (ret != 1) {
709            err = SPAWN_ERR_MALFORMED_SPAWND_RECORD;
710            goto out;
711        }
712
713        if (!same_core && coreid == disp_get_core_id()) {
714            continue;
715        }
716
717        err = spawn_program(c, path, argv, envp, flags, NULL);
718        if (err_is_ok(err) && spawn_count != NULL) {
719            *spawn_count += 1;
720        }
721
722        if (err_is_fail(err)) {
723            DEBUG_ERR(err, "error spawning %s on core %u\n", path, c);
724            goto out;
725        }
726    }
727
728out:
729    oct_free_names(names, count);
730    return err;
731}
732
733errval_t spawn_binding(coreid_t coreid, struct spawn_binding **ret_client)
734{
735    errval_t err = bind_client(coreid);
736    if (err_is_fail(err)) {
737        return err;
738    }
739
740    *ret_client = get_spawn_binding(coreid);
741    return SYS_ERR_OK;
742}
743
744/**
745 * \brief Request the process manager to span onto a new core.
746 *
747 * \param core_id ID of core to span onto.
748 *
749 * Blocks until the new dispatcher has established an interdispatcher connection
750 * to the current one.
751 */
752errval_t spawn_span(coreid_t core_id)
753{
754    coreid_t my_core_id = disp_get_core_id();
755    assert (core_id != my_core_id);
756
757    errval_t err, msgerr;
758    err = proc_mgmt_bind_client();
759    if (err_is_fail(err)) {
760        USER_PANIC_ERR(err, "proc_mgmt_bind_client");
761    }
762
763    struct span_domain_state *st;
764    err = domain_new_dispatcher_setup_only(core_id, &st);
765    if (err_is_fail(err)) {
766        USER_PANIC_ERR(err, "failed to setup new dispatcher");
767    }
768
769    struct proc_mgmt_binding *b = get_proc_mgmt_binding();
770    assert(b != NULL);
771
772    err = b->rpc_tx_vtbl.span(b, cap_domainid, core_id, st->vroot, st->frame,
773                              &msgerr);
774    if (err_is_fail(err)) {
775        USER_PANIC_ERR(err, "error sending span request to process manager");
776    }
777
778    if (err_is_fail(msgerr)) {
779        return msgerr;
780    }
781
782    while(!st->initialized) {
783        event_dispatch(get_default_waitset());
784    }
785    free(st);
786
787    return SYS_ERR_OK;
788}
789
790/**
791 * \brief Request the process manager to kill a domain
792 *
793 * \param domain_cap Domain ID cap for the victim
794 */
795errval_t spawn_kill(struct capref domain_cap)
796{
797    errval_t err, msgerr;
798    err = proc_mgmt_bind_client();
799    if (err_is_fail(err)) {
800        USER_PANIC_ERR(err, "proc_mgmt_bind_client");
801    }
802
803    struct proc_mgmt_binding *b = get_proc_mgmt_binding();
804    assert(b != NULL);
805
806    err = b->rpc_tx_vtbl.kill(b, domain_cap, &msgerr);
807    if (err_is_fail(err)) {
808        USER_PANIC_ERR(err, "error sending kill request to process manager");
809    }
810
811    return msgerr;
812}
813
814/**
815 * \brief Inform the process manager about exiting execution.
816 */
817errval_t spawn_exit(uint8_t status)
818{
819    errval_t err = proc_mgmt_bind_client();
820    if (err_is_fail(err)) {
821        return err;
822    }
823
824    struct proc_mgmt_binding *b = get_proc_mgmt_binding();
825    assert(b != NULL);
826
827    err = b->rpc_tx_vtbl.exit(b, cap_domainid, status);
828    if (err_is_fail(err)) {
829        return err;
830    }
831
832    return SYS_ERR_OK;
833}
834
835/**
836 * \brief Wait for spawned proccess to exit on core.
837 */
838errval_t spawn_wait_coreid(coreid_t coreid, struct capref domain_cap,
839                           uint8_t *exitcode, bool nohang)
840{
841    return spawn_wait_core(disp_get_core_id(), domain_cap, exitcode, nohang);
842}
843
844
845/**
846 * \brief Wait for the termination of a domain on a remote core.
847 */
848errval_t spawn_wait_core(coreid_t coreid, struct capref domain_cap,
849                         uint8_t *exitcode, bool nohang)
850{
851    return spawn_wait(domain_cap, exitcode, nohang);
852}
853
854
855/**
856 * \brief Wait for spawned proccess to exit on current core.
857 */
858errval_t spawn_wait(struct capref domain_cap, uint8_t *status, bool nohang)
859{
860    errval_t err, msgerr;
861    err = proc_mgmt_bind_client();
862    if (err_is_fail(err)) {
863        return err;
864    }
865
866    struct proc_mgmt_binding *b = get_proc_mgmt_binding();
867    assert(b != NULL);
868
869    err = b->rpc_tx_vtbl.wait(b, domain_cap, nohang, &msgerr, status);
870    if (err_is_fail(err)) {
871        USER_PANIC_ERR(err, "error sending wait request to process manager");
872    }
873
874    return msgerr;
875}
876
877errval_t spawn_wait_compat(uint8_t domainid,
878                           uint8_t *exitcode, bool nohang)
879{
880    errval_t err, reterr;
881
882    err = bind_client(disp_get_core_id());
883    if (err_is_fail(err)) {
884        return err;
885    }
886    struct spawn_binding *cl = get_spawn_binding(disp_get_core_id());
887    assert(cl != NULL);
888
889    err = cl->rpc_tx_vtbl.wait(cl, domainid, nohang, exitcode, &reterr);
890    if (err_is_fail(err)) {
891        return err;
892    }
893
894    return reterr;
895}
896
897static int compare_domainid(const void *a, const void *b)
898{
899  const domainid_t *da = (const domainid_t *) a;
900  const domainid_t *db = (const domainid_t *) b;
901
902  return (*da > *db) - (*da < *db);
903}
904
905/**
906 * \brief Get the list of domains for ps like implementation
907 */
908errval_t spawn_get_domain_list(bool sorted, domainid_t **domains, size_t *len)
909{
910    errval_t err;
911    err = proc_mgmt_bind_client();
912    if (err_is_fail(err)) {
913        return err;
914    }
915
916    struct proc_mgmt_binding *b = get_proc_mgmt_binding();
917    assert(b != NULL);
918
919    struct proc_mgmt_get_domainlist_response__rx_args reply;
920    size_t length;
921    err = b->rpc_tx_vtbl.get_domainlist(b, reply.domains, &length);
922    if (err_is_fail(err)) {
923        return err;
924    }
925
926    // length is in bytes
927    *len = length/sizeof(domainid_t);
928    if (sorted) {
929        qsort(reply.domains, *len, sizeof(domainid_t), compare_domainid);
930    }
931
932    *domains = memdup(reply.domains, length);
933
934    return SYS_ERR_OK;
935}
936
937/**
938 * \brief Get the status of a domain for ps like implementation
939 */
940errval_t spawn_get_status(domainid_t domain_id, struct spawn_ps_entry *pse,
941                          char **argbuf, size_t *arglen, errval_t *reterr)
942{
943
944    errval_t err;
945    err = proc_mgmt_bind_client();
946    if (err_is_fail(err)) {
947        return err;
948    }
949
950    struct proc_mgmt_binding *b = get_proc_mgmt_binding();
951    assert(b != NULL);
952
953    struct proc_mgmt_get_status_response__rx_args reply;
954    err = b->rpc_tx_vtbl.get_status(b, domain_id, (proc_mgmt_ps_entry_t*) pse,
955                                    reply.argv, arglen, reterr);
956    if (err_is_fail(err)) {
957        return err;
958    }
959
960    *argbuf = memdup(reply.argv, *arglen);
961
962    return SYS_ERR_OK;
963}
964
965/**
966 * \brief Dump capabilities for a given domain
967 */
968errval_t spawn_dump_capabilities_compat(domainid_t domainid)
969{
970    errval_t err, reterr;
971
972    err = bind_client(disp_get_core_id());
973    if (err_is_fail(err)) {
974        return err;
975    }
976    struct spawn_binding *cl = get_spawn_binding(disp_get_core_id());
977    assert(cl != NULL);
978
979    err = cl->rpc_tx_vtbl.dump_capabilities(cl, domainid, &reterr);
980    if (err_is_fail(err)) {
981        return err;
982    }
983
984    return reterr;
985}
986
987/**
988 * \brief Utility function to create an inherit cnode
989 * and copy caps into it.
990 *
991 * \param inheritcn_capp Pointer to capref, filled-in with location of inheritcn
992 *                       capability.
993 * \param fdcap          fdcap to copy into inherit cnode.
994 * \param sidcap         sidcap to copy into inherit cnode.
995 * \param kernelcap      kernelcap to copy into inherit cnode.
996 *
997 * \retval SYS_ERR_OK inherticn_capp is allocated and contains copies of the
998 * provided caps.
999 */
1000errval_t alloc_inheritcn_with_caps(struct capref *inheritcn_capp,
1001                                   struct capref fdcap,
1002                                   struct capref sidcap,
1003                                   struct capref kernelcap)
1004{
1005    errval_t err;
1006
1007    // construct inherit CNode
1008    struct cnoderef inheritcn;
1009    err = cnode_create_l2(inheritcn_capp, &inheritcn);
1010    if (err_is_fail(err)) {
1011        return err;
1012    }
1013
1014    if (!capref_is_null(fdcap)) {
1015        // copy fdcap to inherit Cnode
1016        struct capref dest = {
1017            .cnode = inheritcn,
1018            .slot  = INHERITCN_SLOT_FDSPAGE
1019        };
1020        err = cap_copy(dest, fdcap);
1021        if (err_is_fail(err)) {
1022            return err;
1023        }
1024    }
1025
1026    if (!capref_is_null(sidcap)) {
1027        // copy fdcap to inherit Cnode
1028        struct capref dest = {
1029            .cnode = inheritcn,
1030            .slot  = INHERITCN_SLOT_SESSIONID
1031        };
1032        err = cap_copy(dest, sidcap);
1033        if (err_is_fail(err)) {
1034            return err;
1035        }
1036    }
1037
1038    if (!capref_is_null(kernelcap)) {
1039        // copy fdcap to inherit Cnode
1040        struct capref dest = {
1041            .cnode = inheritcn,
1042            .slot  = INHERITCN_SLOT_KERNELCAP
1043        };
1044        err = cap_copy(dest, kernelcap);
1045        if (err_is_fail(err)) {
1046            return err;
1047        }
1048    }
1049
1050    return SYS_ERR_OK;
1051}
1052