1/**
2 * \file
3 * \brief spawn service
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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <stdio.h>
16#include <string.h>
17#include <barrelfish/barrelfish.h>
18#include <spawndomain/spawndomain.h>
19#include <barrelfish/monitor_client.h>
20#include <barrelfish/nameservice_client.h>
21#include <barrelfish/cpu_arch.h>
22#include <vfs/vfs.h>
23#include <vfs/vfs_path.h>
24#include <dist/barrier.h>
25#include <if/spawn_defs.h>
26#include <if/monitor_defs.h>
27#include <if/monitor_blocking_defs.h>
28#include <barrelfish/dispatcher_arch.h>
29#include <barrelfish/invocations_arch.h>
30
31#include "internal.h"
32#include "ps.h"
33
34
35static errval_t spawn(struct capref domain_cap, const char *path,
36                      char *const argv[], const char *argbuf, size_t argbytes,
37                      char *const envp[], struct capref inheritcn_cap,
38                      struct capref argcn_cap, uint8_t flags,
39                      domainid_t *domainid)
40{
41    errval_t err, msgerr;
42
43    /* read file into memory */
44    vfs_handle_t fh;
45    err = vfs_open(path, &fh);
46    if (err_is_fail(err)) {
47        return err_push(err, SPAWN_ERR_LOAD);
48    }
49
50    struct vfs_fileinfo info;
51    err = vfs_stat(fh, &info);
52    if (err_is_fail(err)) {
53        vfs_close(fh);
54        return err_push(err, SPAWN_ERR_LOAD);
55    }
56
57    assert(info.type == VFS_FILE);
58    uint8_t *image = malloc(info.size);
59    if (image == NULL) {
60        vfs_close(fh);
61        return err_push(err, SPAWN_ERR_LOAD);
62    }
63
64    size_t pos = 0, readlen;
65    do {
66        err = vfs_read(fh, &image[pos], info.size - pos, &readlen);
67        if (err_is_fail(err)) {
68            vfs_close(fh);
69            free(image);
70            return err_push(err, SPAWN_ERR_LOAD);
71        } else if (readlen == 0) {
72            vfs_close(fh);
73            free(image);
74            return SPAWN_ERR_LOAD; // XXX
75        } else {
76            pos += readlen;
77        }
78    } while (err_is_ok(err) && readlen > 0 && pos < info.size);
79
80    err = vfs_close(fh);
81    if (err_is_fail(err)) {
82        DEBUG_ERR(err, "failed to close file %s", path);
83    }
84
85    // find short name (last part of path)
86    const char *name = strrchr(path, VFS_PATH_SEP);
87    if (name == NULL) {
88        name = path;
89    } else {
90        name++;
91    }
92
93    /* spawn the image */
94    struct spawninfo si;
95    si.flags = flags;
96    err = spawn_load_image(&si, (lvaddr_t)image, info.size, CURRENT_CPU_TYPE,
97                           name, my_core_id, argv, envp, inheritcn_cap,
98                           argcn_cap);
99    if (err_is_fail(err)) {
100        free(image);
101        return err;
102    }
103
104    free(image);
105
106    /* request connection from monitor */
107    struct monitor_blocking_binding *mrpc = get_monitor_blocking_binding();
108    struct capref monep;
109    err = slot_alloc(&monep);
110    if (err_is_fail(err)) {
111        return err_push(err, SPAWN_ERR_MONEP_SLOT_ALLOC);
112    }
113    err = mrpc->rpc_tx_vtbl.alloc_monitor_ep(mrpc, &msgerr, &monep);
114    if (err_is_fail(err)) {
115        return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
116    } else if (err_is_fail(msgerr)) {
117        return msgerr;
118    }
119
120    /* copy connection into the new domain */
121    struct capref destep = {
122        .cnode = si.taskcn,
123        .slot  = TASKCN_SLOT_MONITOREP,
124    };
125    err = cap_copy(destep, monep);
126    if (err_is_fail(err)) {
127        spawn_free(&si);
128        cap_destroy(monep);
129        return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
130    }
131
132    err = cap_destroy(monep);
133    if (err_is_fail(err)) {
134        return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
135    }
136
137    debug_printf("spawning %s on core %u\n", path, my_core_id);
138
139    /* give the perfmon capability */
140    struct capref dest, src;
141    dest.cnode = si.taskcn;
142    dest.slot = TASKCN_SLOT_PERF_MON;
143    src.cnode = cnode_task;
144    src.slot = TASKCN_SLOT_PERF_MON;
145    err = cap_copy(dest, src);
146    if (err_is_fail(err)) {
147        return err_push(err, SPAWN_ERR_COPY_PERF_MON);
148    }
149
150    if (!capref_is_null(domain_cap)) {
151        // Pass over the domain cap.
152        dest.cnode = si.taskcn;
153        dest.slot = TASKCN_SLOT_DOMAINID;
154        err = cap_copy(dest, domain_cap);
155        if (err_is_fail(err)) {
156            return err_push(err, SPAWN_ERR_COPY_DOMAIN_CAP);
157        }
158    }
159
160    /* run the domain */
161    err = spawn_run(&si);
162    if (err_is_fail(err)) {
163        spawn_free(&si);
164        return err_push(err, SPAWN_ERR_RUN);
165    }
166
167    // Allocate domain id
168    struct ps_entry *pe = malloc(sizeof(struct ps_entry));
169    assert(pe != NULL);
170    memset(pe, 0, sizeof(struct ps_entry));
171    memcpy(pe->argv, argv, MAX_CMDLINE_ARGS*sizeof(*argv));
172    pe->argbuf = memdup(argbuf, argbytes);
173    pe->argbytes = argbytes;
174    /*
175     * NB: It's important to keep a copy of the DCB *and* the root
176     * CNode around.  We need to revoke both (in the right order, see
177     * kill_domain() below), so that we ensure no one else is
178     * referring to the domain's CSpace anymore. Especially the loop
179     * created by placing rootcn into its own address space becomes a
180     * problem here.
181     */
182    err = slot_alloc(&pe->rootcn_cap);
183    assert(err_is_ok(err));
184    err = cap_copy(pe->rootcn_cap, si.rootcn_cap);
185    pe->rootcn = si.rootcn;
186    assert(err_is_ok(err));
187    err = slot_alloc(&pe->dcb);
188    assert(err_is_ok(err));
189    err = cap_copy(pe->dcb, si.dcb);
190    assert(err_is_ok(err));
191    pe->status = PS_STATUS_RUNNING;
192
193    if (!capref_is_null(domain_cap)) {
194        err = ps_hash_domain(pe, domain_cap);
195        if (err_is_fail(err)) {
196            free(pe);
197            spawn_free(&si);
198            return err_push(err, SPAWN_ERR_DOMAIN_CAP_HASH);
199        }
200    }
201
202    err = ps_allocate(pe, domainid);
203    if(err_is_fail(err)) {
204        free(pe);
205    }
206
207    // Store in target dispatcher frame
208    struct dispatcher_generic *dg = get_dispatcher_generic(si.handle);
209    dg->domain_id = *domainid;
210
211    /* cleanup */
212    err = spawn_free(&si);
213    if (err_is_fail(err)) {
214        return err_push(err, SPAWN_ERR_FREE);
215    }
216
217    return SYS_ERR_OK;
218}
219
220static void retry_use_local_memserv_response(void *a)
221{
222    errval_t err;
223
224    struct spawn_binding *b = (struct spawn_binding*)a;
225
226    err = b->tx_vtbl.use_local_memserv_response(b, NOP_CONT);
227
228    if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
229        // try again
230        err = b->register_send(b, get_default_waitset(),
231                               MKCONT(retry_use_local_memserv_response,a));
232    }
233    if (err_is_fail(err)) {
234        DEBUG_ERR(err, "error sending use_local_memserv reply\n");
235    }
236
237}
238
239
240static void use_local_memserv_handler(struct spawn_binding *b)
241{
242    ram_alloc_set(NULL);
243
244    errval_t err;
245    err = b->tx_vtbl.use_local_memserv_response(b, NOP_CONT);
246    if (err_is_fail(err)) {
247        DEBUG_ERR(err, "error sending use_local_memserv reply");
248        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
249            err = b->register_send(b, get_default_waitset(),
250                               MKCONT(retry_use_local_memserv_response, b));
251            if (err_is_fail(err)) {
252                // note that only one continuation may be registered at a time
253                DEBUG_ERR(err, "register_send failed!");
254            }
255        }
256    }
257}
258
259struct pending_spawn_response {
260    struct spawn_binding *b;
261    errval_t err;
262    domainid_t domainid;
263};
264
265static errval_t spawn_with_caps_common(struct capref domain_cap,
266                                       const char *path, const char *argbuf,
267                                       size_t argbytes, const char *envbuf,
268                                       size_t envbytes,
269                                       struct capref inheritcn_cap,
270                                       struct capref argcn_cap, uint8_t flags,
271                                       domainid_t *domainid)
272{
273    errval_t err;
274    assert(domainid);
275    *domainid = 0;
276
277    /* extract arguments from buffer */
278    char *argv[MAX_CMDLINE_ARGS + 1];
279    int i = 0;
280    size_t pos = 0;
281    while (pos < argbytes && i < MAX_CMDLINE_ARGS) {
282        argv[i++] = (CONST_CAST)argbuf + pos;
283        char *end = memchr(&argbuf[pos], '\0', argbytes - pos);
284        if (end == NULL) {
285            err = SPAWN_ERR_GET_CMDLINE_ARGS;
286            goto finish;
287        }
288        pos = end - argbuf + 1;
289    }
290    assert(i <= MAX_CMDLINE_ARGS);
291    argv[i] = NULL;
292
293    /* extract environment from buffer */
294    char *envp[MAX_CMDLINE_ARGS + 1];
295    i = 0;
296    pos = 0;
297    while (pos < envbytes && i < MAX_CMDLINE_ARGS) {
298        envp[i++] = (CONST_CAST)envbuf + pos;
299        char *end = memchr(&envbuf[pos], '\0', envbytes - pos);
300        if (end == NULL) {
301            err = SPAWN_ERR_GET_CMDLINE_ARGS;
302            goto finish;
303        }
304        pos = end - envbuf + 1;
305    }
306    assert(i <= MAX_CMDLINE_ARGS);
307    envp[i] = NULL;
308
309    char *npath;
310    npath = alloca(strlen(path));
311    strcpy(npath, path);
312    vfs_path_normalise(npath);
313
314    err = spawn(domain_cap, npath, argv, argbuf, argbytes, envp, inheritcn_cap,
315                argcn_cap, flags, domainid);
316    // XXX: do we really want to delete the inheritcn and the argcn here? iaw:
317    // do we copy these somewhere? -SG
318    if (!capref_is_null(inheritcn_cap)) {
319        errval_t err2;
320        err2 = cap_delete(inheritcn_cap);
321        assert(err_is_ok(err2));
322    }
323    if (!capref_is_null(argcn_cap)) {
324        errval_t err2;
325        err2 = cap_delete(argcn_cap);
326        assert(err_is_ok(err2));
327    }
328
329 finish:
330    if(err_is_fail(err)) {
331        DEBUG_ERR(err, "spawn");
332    }
333
334    return err;
335}
336
337static errval_t spawn_with_caps_handler(struct spawn_binding *b, const char *path,
338    const char *argvbuf, size_t argvbytes, const char *envbuf, size_t envbytes,
339    struct capref inheritcn_cap, struct capref argcn_cap, uint8_t flags,
340    errval_t *err, spawn_domainid_t *domain_id)
341{
342    *err = spawn_with_caps_common(NULL_CAP, path, argvbuf, argvbytes, envbuf,
343                                  envbytes, inheritcn_cap, argcn_cap, flags,
344                                  domain_id);
345    return SYS_ERR_OK;
346}
347
348static errval_t spawn_handler(struct spawn_binding *b, const char *path,
349    const char *argvbuf, size_t argvbytes, const char *envbuf, size_t envbytes,
350    uint8_t flags, errval_t *err, spawn_domainid_t *domain_id)
351{
352    *err = spawn_with_caps_common(NULL_CAP, path, argvbuf, argvbytes, envbuf,
353                                  envbytes, NULL_CAP, NULL_CAP, flags,
354                                  domain_id);
355    return SYS_ERR_OK;
356}
357
358static void spawn_with_caps_request_handler(struct spawn_binding *b,
359                                            struct capref procmng_cap,
360                                            struct capref domain_cap,
361                                            const char *path,
362                                            const char *argvbuf,
363                                            size_t argvbytes,
364                                            const char *envbuf,
365                                            size_t envbytes,
366                                            struct capref inheritcn_cap,
367                                            struct capref argcn_cap,
368                                            uint8_t flags)
369{
370    errval_t err, reply_err;
371    struct capability ret;
372    err = monitor_cap_identify_remote(procmng_cap, &ret);
373    if (err_is_fail(err)) {
374        err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
375        goto reply;
376    }
377
378    if (ret.type != ObjType_ProcessManager) {
379        err = SPAWN_ERR_NOT_PROC_MNGR;
380        goto reply;
381    }
382
383    spawn_domainid_t dummy_domain_id;
384    err = spawn_with_caps_common(domain_cap, path, argvbuf, argvbytes, envbuf,
385                                 envbytes, inheritcn_cap, argcn_cap, flags,
386                                 &dummy_domain_id);
387
388reply:
389    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
390    if (err_is_fail(reply_err)) {
391        DEBUG_ERR(err, "failed to send spawn_with_caps_reply");
392    }
393}
394
395static void spawn_request_handler(struct spawn_binding *b,
396                                  struct capref procmng_cap,
397                                  struct capref domain_cap, const char *path,
398                                  const char *argvbuf, size_t argvbytes,
399                                  const char *envbuf, size_t envbytes,
400                                  uint8_t flags)
401{
402    errval_t err, reply_err;
403    struct capability ret;
404    err = monitor_cap_identify_remote(procmng_cap, &ret);
405    if (err_is_fail(err)) {
406        err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
407        goto reply;
408    }
409
410    if (ret.type != ObjType_ProcessManager) {
411        err = SPAWN_ERR_NOT_PROC_MNGR;
412        goto reply;
413    }
414
415    spawn_domainid_t dummy_domain_id;
416    err = spawn_with_caps_common(domain_cap, path, argvbuf, argvbytes, envbuf,
417                                 envbytes, NULL_CAP, NULL_CAP, flags,
418                                 &dummy_domain_id);
419
420reply:
421    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
422    if (err_is_fail(reply_err)) {
423        DEBUG_ERR(err, "failed to send spawn_reply");
424    }
425}
426
427static void span_request_handler(struct spawn_binding *b,
428                                 struct capref procmng_cap,
429                                 struct capref domain_cap, struct capref vroot,
430                                 struct capref dispframe)
431{
432    errval_t err, mon_err, reply_err;
433    struct capability ret;
434    err = monitor_cap_identify_remote(procmng_cap, &ret);
435    if (err_is_fail(err)) {
436        err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
437        goto reply;
438    }
439
440    if (ret.type != ObjType_ProcessManager) {
441        err = SPAWN_ERR_NOT_PROC_MNGR;
442        goto reply;
443    }
444
445    struct spawninfo si;
446    memset(&si, 0, sizeof(si));
447
448    debug_printf("Spanning domain to core %d\n", disp_get_core_id());
449
450    // Span domain
451    err = spawn_span_domain(&si, vroot, dispframe);
452    if (err_is_fail(err)) {
453        err = err_push(err, SPAWN_ERR_SPAN);
454        goto reply;
455    }
456
457    // Set connection to monitor.
458    struct monitor_blocking_binding *mrpc = get_monitor_blocking_binding();
459    struct capref monep;
460    err = slot_alloc(&monep);
461    if (err_is_fail(err)) {
462        err = err_push(err, SPAWN_ERR_MONEP_SLOT_ALLOC);
463        goto reply;
464    }
465    err = mrpc->rpc_tx_vtbl.alloc_monitor_ep(mrpc, &mon_err, &monep);
466    if (err_is_ok(err)) {
467        err = mon_err;
468    }
469    if (err_is_fail(err)) {
470        err = err_push(err, SPAWN_ERR_MONITOR_CLIENT);
471        goto reply;
472    }
473
474    /* copy connection into the new domain */
475    struct capref destep = {
476        .cnode = si.taskcn,
477        .slot  = TASKCN_SLOT_MONITOREP,
478    };
479    err = cap_copy(destep, monep);
480    if (err_is_fail(err)) {
481        spawn_free(&si);
482        cap_destroy(monep);
483        err = err_push(err, SPAWN_ERR_MONITOR_CLIENT);
484        goto reply;
485    }
486
487    err = cap_destroy(monep);
488    if (err_is_fail(err)) {
489        err = err_push(err, SPAWN_ERR_MONITOR_CLIENT);
490        goto reply;
491    }
492
493    /* give the perfmon capability */
494    struct capref dest, src;
495    dest.cnode = si.taskcn;
496    dest.slot = TASKCN_SLOT_PERF_MON;
497    src.cnode = cnode_task;
498    src.slot = TASKCN_SLOT_PERF_MON;
499    err = cap_copy(dest, src);
500    if (err_is_fail(err)) {
501        err = err_push(err, SPAWN_ERR_COPY_PERF_MON);
502        goto reply;
503    }
504
505    // Pass over the domain cap.
506    dest.cnode = si.taskcn;
507    dest.slot = TASKCN_SLOT_DOMAINID;
508    err = cap_copy(dest, domain_cap);
509    if (err_is_fail(err)) {
510        err = err_push(err, SPAWN_ERR_COPY_DOMAIN_CAP);
511        goto reply;
512    }
513
514    // Make runnable
515    err = spawn_run(&si);
516    if (err_is_fail(err)) {
517        err = err_push(err, SPAWN_ERR_RUN);
518        goto reply;
519    }
520
521    // Allocate an id for this dispatcher.
522    struct ps_entry *pe = malloc(sizeof(struct ps_entry));
523    assert(pe != NULL);
524    memset(pe, 0, sizeof(struct ps_entry));
525    /*
526     * NB: It's important to keep a copy of the DCB *and* the root
527     * CNode around.  We need to revoke both (in the right order, see
528     * kill_domain() below), so that we ensure no one else is
529     * referring to the domain's CSpace anymore. Especially the loop
530     * created by placing rootcn into its own address space becomes a
531     * problem here.
532     */
533    // TODO(razvan): The following code is here to comply with spawn().
534    err = slot_alloc(&pe->rootcn_cap);
535    assert(err_is_ok(err));
536    err = cap_copy(pe->rootcn_cap, si.rootcn_cap);
537    pe->rootcn = si.rootcn;
538    assert(err_is_ok(err));
539    err = slot_alloc(&pe->dcb);
540    assert(err_is_ok(err));
541    err = cap_copy(pe->dcb, si.dcb);
542    assert(err_is_ok(err));
543    pe->status = PS_STATUS_RUNNING;
544
545    err = ps_hash_domain(pe, domain_cap);
546    if (err_is_fail(err)) {
547        free(pe);
548        spawn_free(&si);
549        err = err_push(err, SPAWN_ERR_DOMAIN_CAP_HASH);
550        goto reply;
551    }
552
553    domainid_t domainid;
554    err = ps_allocate(pe, &domainid);
555    if(err_is_fail(err)) {
556        free(pe);
557    }
558
559    // Cleanup
560    err = spawn_free(&si);
561    if (err_is_fail(err)) {
562        err = err_push(err, SPAWN_ERR_FREE);
563    }
564
565reply:
566    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
567    if (err_is_fail(reply_err)) {
568        DEBUG_ERR(err, "failed to send span_reply");
569    }
570}
571
572static void cleanup_cap(struct capref cap)
573{
574    errval_t err;
575
576    err = cap_revoke(cap);
577    if (err_is_fail(err)) {
578        DEBUG_ERR(err, "cap_revoke");
579    }
580    err = cap_destroy(cap);
581    if (err_is_fail(err)) {
582        DEBUG_ERR(err, "cap_destroy");
583    }
584}
585
586static void kill_request_handler(struct spawn_binding *b,
587                                 struct capref procmng_cap,
588                                 struct capref victim_domain_cap)
589{
590    errval_t err, reply_err;
591    struct capability ret;
592    err = monitor_cap_identify_remote(procmng_cap, &ret);
593    if (err_is_fail(err)) {
594        err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
595        goto reply;
596    }
597
598    if (ret.type != ObjType_ProcessManager) {
599        err = SPAWN_ERR_NOT_PROC_MNGR;
600        goto reply;
601    }
602
603    struct ps_entry *pe;
604    err = ps_get_domain(victim_domain_cap, &pe, NULL);
605    if (err_is_fail(err)) {
606        err = err_push(err, SPAWN_ERR_DOMAIN_NOTFOUND);
607        goto reply;
608    }
609
610    cleanup_cap(pe->dcb);
611
612reply:
613    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
614    if (err_is_fail(reply_err)) {
615        DEBUG_ERR(err, "failed to send kill_reply");
616    }
617}
618
619static void cleanup_request_handler(struct spawn_binding *b,
620                                    struct capref procmng_cap,
621                                    struct capref domain_cap)
622{
623    errval_t err, reply_err;
624    struct capability ret;
625    err = monitor_cap_identify_remote(procmng_cap, &ret);
626    if (err_is_fail(err)) {
627        err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
628        goto reply;
629    }
630
631    if (ret.type != ObjType_ProcessManager) {
632        err = SPAWN_ERR_NOT_PROC_MNGR;
633        goto reply;
634    }
635
636    struct ps_entry *pe;
637    err = ps_release_domain(domain_cap, &pe);
638    if (err_is_fail(err)) {
639        err = err_push(err, SPAWN_ERR_DOMAIN_NOTFOUND);
640        goto reply;
641    }
642
643    cleanup_cap(pe->rootcn_cap);
644
645    // Cleanup struct ps_entry. Note that waiters will be handled by the process
646    // manager, as opposed to the old protocol of handling them here.
647    free(pe->argbuf);
648    ps_remove(pe->domain_id);
649    free(pe);
650
651reply:
652    reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, err);
653    if (err_is_fail(reply_err)) {
654        DEBUG_ERR(err, "failed to send cleanup_reply");
655    }
656}
657
658/**
659 * \brief Removes a zombie domain.
660 */
661static void cleanup_domain(domainid_t domainid)
662{
663    errval_t err;
664    struct ps_entry *ps = ps_get(domainid);
665    assert(ps != NULL);
666
667    // Tell all waiters of exit and free list as we go
668    for(struct ps_waiter *w = ps->waiters; w != NULL;) {
669        err = w->binding->tx_vtbl.wait_response
670            (w->binding, NOP_CONT, ps->exitcode, SYS_ERR_OK);
671        if(err_is_fail(err)) {
672            DEBUG_ERR(err, "wait_response");
673        }
674
675        struct ps_waiter *oldw = w;
676        w = w->next;
677        free(oldw);
678    }
679    ps->waiters = NULL;
680
681    // Cleanup rest of ps entry
682    free(ps->argbuf);
683
684    ps_remove(domainid);
685}
686
687static errval_t kill_domain(domainid_t domainid, uint8_t exitcode)
688{
689    struct ps_entry *ps = ps_get(domainid);
690
691    if(ps == NULL) {
692        return SPAWN_ERR_DOMAIN_NOTFOUND;
693    }
694
695    ps->status = PS_STATUS_ZOMBIE;
696    ps->exitcode = exitcode;
697
698    // Garbage collect victim's capabilities
699    cleanup_cap(ps->dcb);       // Deschedule dispatcher (do this first!)
700    cleanup_cap(ps->rootcn_cap);
701
702    // XXX: why only when waiters exist? -SG
703    if(ps->waiters != NULL) {
704        // Cleanup local data structures and inform waiters
705        cleanup_domain(domainid);
706    }
707
708    return SYS_ERR_OK;
709}
710
711static void kill_handler(struct spawn_binding *b, domainid_t domainid)
712{
713    errval_t err = kill_domain(domainid, 0);
714
715    err = b->tx_vtbl.kill_response(b, NOP_CONT, err);
716    if(err_is_fail(err)) {
717        DEBUG_ERR(err, "kill_response");
718    }
719}
720
721static void exit_handler(struct spawn_binding *b, domainid_t domainid,
722                         uint8_t exitcode)
723{
724    errval_t err = kill_domain(domainid, exitcode);
725    struct ps_entry *ps = ps_get(domainid);
726
727    if(err_is_fail(err)) {
728        DEBUG_ERR(err, "kill_domain");
729    }
730
731    if(ps == NULL) {
732        // XXX: Can't do nothing
733        return;
734    }
735
736    // May never return anything to client
737}
738
739static void wait_handler(struct spawn_binding *b, domainid_t domainid,
740                         bool nohang)
741{
742    errval_t err;
743    struct ps_entry *ps = ps_get(domainid);
744
745    if(ps == NULL) {
746        err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_NOTFOUND);
747        if(err_is_fail(err)) {
748            DEBUG_ERR(err, "wait_response");
749        }
750    } else {
751        if(!nohang || ps->status == PS_STATUS_ZOMBIE) {
752            // Enqueue the waiter
753            struct ps_waiter *waiter = malloc(sizeof(struct ps_waiter));
754            assert(waiter != NULL);
755            waiter->next = ps->waiters;
756            waiter->binding = b;
757            ps->waiters = waiter;
758        } else {
759            // nohang and no zombie, return error
760            err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_RUNNING);
761            if(err_is_fail(err)) {
762                DEBUG_ERR(err, "wait_response");
763            }
764        }
765
766        // Cleanup if zombie (will send the reply)
767        if(ps->status == PS_STATUS_ZOMBIE) {
768            cleanup_domain(domainid);
769        }
770    }
771}
772
773static void get_domainlist_sent(void *arg)
774{
775    free(arg);
776}
777
778static void get_domainlist_handler(struct spawn_binding *b)
779{
780    errval_t err;
781    size_t len = 0;
782    uint8_t *domains = calloc(sizeof(uint8_t), MAX_DOMAINS);
783
784    // XXX: Very inefficient
785    for(domainid_t i = 0; i < MAX_DOMAINS; i++) {
786        if(ps_exists(i)) {
787            domains[len++] = i;
788        }
789    }
790
791    err = b->tx_vtbl.get_domainlist_response
792        (b, MKCLOSURE(get_domainlist_sent, domains), domains, len);
793    if(err_is_fail(err)) {
794        DEBUG_ERR(err, "get_domainlist_response");
795        free(domains);
796    }
797}
798
799static void status_handler(struct spawn_binding *b, domainid_t domainid)
800{
801    errval_t err;
802    struct ps_entry *ps = ps_get(domainid);
803    spawn_ps_entry_t pse;
804
805    memset(&pse, 0, sizeof(pse));
806
807    if(ps == NULL) {
808        err = b->tx_vtbl.status_response(b, NOP_CONT, pse, NULL, 0,
809                                         SPAWN_ERR_DOMAIN_NOTFOUND);
810        if(err_is_fail(err)) {
811            DEBUG_ERR(err, "status_response");
812        }
813    }
814
815    pse.status = ps->status;
816
817    err = b->tx_vtbl.status_response(b, NOP_CONT, pse, ps->argbuf, ps->argbytes,
818                                     SYS_ERR_OK);
819    if(err_is_fail(err)) {
820        DEBUG_ERR(err, "status_response");
821    }
822}
823
824
825static errval_t dump_capabilities(domainid_t domainid) {
826    struct ps_entry *ps = ps_get(domainid);
827
828    if(ps == NULL) {
829        return SPAWN_ERR_DOMAIN_NOTFOUND;
830    }
831
832    return invoke_dispatcher_dump_capabilities(ps->dcb);
833}
834
835static void dump_capabilities_handler(struct spawn_binding *b, domainid_t domainid) {
836    errval_t err = dump_capabilities(domainid);
837
838    err = b->tx_vtbl.dump_capabilities_response(b, NOP_CONT, err);
839    if(err_is_fail(err)) {
840        DEBUG_ERR(err, "debug_print_capabilities_response");
841    }
842}
843
844static struct spawn_rx_vtbl rx_vtbl = {
845    // .spawn_domain_call = spawn_handler,
846    // .spawn_domain_with_caps_call = spawn_with_caps_handler,
847
848    // Async messages for the process manager.
849    .spawn_request           = spawn_request_handler,
850    .spawn_with_caps_request = spawn_with_caps_request_handler,
851    .span_request            = span_request_handler,
852    .kill_request            = kill_request_handler,
853    .cleanup_request         = cleanup_request_handler,
854
855    .use_local_memserv_call = use_local_memserv_handler,
856    .kill_call = kill_handler,
857    .exit_call = exit_handler,
858    .wait_call = wait_handler,
859    .get_domainlist_call = get_domainlist_handler,
860    .status_call = status_handler,
861    .dump_capabilities_call = dump_capabilities_handler
862};
863
864static struct spawn_rpc_rx_vtbl rpc_rx_vtbl = {
865    .spawn_domain_call = spawn_handler,
866    .spawn_domain_with_caps_call = spawn_with_caps_handler,
867    // .use_local_memserv_call = use_local_memserv_handler,
868    // .kill_call = kill_handler,
869    // .exit_call = exit_handler,
870    // .wait_call = wait_handler,
871    // .get_domainlist_call = get_domainlist_handler,
872    // .status_call = status_handler,
873    // .dump_capabilities_call = dump_capabilities_handler
874};
875
876static void export_cb(void *st, errval_t err, iref_t iref)
877{
878    if (err_is_fail(err)) {
879        USER_PANIC_ERR(err, "export failed");
880    }
881
882    // Send iref back to monitor, which will forward it to the process manager.
883    struct monitor_binding *mb = get_monitor_binding();
884    err = mb->tx_vtbl.set_spawn_iref_request(mb, NOP_CONT, iref);
885    if (err_is_fail(err)) {
886        USER_PANIC_ERR(err, "failed to send set_spawn_iref_request to "
887                "monitor");
888    }
889
890    // construct name
891    char namebuf[32];
892    size_t len = snprintf(namebuf, sizeof(namebuf), "%s.%d", SERVICE_BASENAME,
893                          my_core_id);
894    assert(len < sizeof(namebuf));
895    namebuf[sizeof(namebuf) - 1] = '\0';
896
897    // register this iref with the name service
898    err = nameservice_register(namebuf, iref);
899    if (err_is_fail(err)) {
900        USER_PANIC_ERR(err, "nameservice_register failed");
901    }
902}
903
904
905static errval_t connect_cb(void *st, struct spawn_binding *b)
906{
907    // copy my message receive handler vtable to the binding
908    b->rx_vtbl = rx_vtbl;
909    b->rpc_rx_vtbl = rpc_rx_vtbl;
910    return SYS_ERR_OK;
911}
912
913errval_t start_service(void)
914{
915    return spawn_export(NULL, export_cb, connect_cb, get_default_waitset(),
916                         IDC_EXPORT_FLAGS_DEFAULT);
917}
918