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