Add spawn, spawn_with_caps and span calls to the Process Manager API.
[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     pe->domain_cap = domain_cap;
192     err = ps_allocate(pe, domainid);
193     if(err_is_fail(err)) {
194         free(pe);
195     }
196
197     // Store in target dispatcher frame
198     struct dispatcher_generic *dg = get_dispatcher_generic(si.handle);
199     dg->domain_id = *domainid;
200
201     /* cleanup */
202     err = spawn_free(&si);
203     if (err_is_fail(err)) {
204         return err_push(err, SPAWN_ERR_FREE);
205     }
206
207     return SYS_ERR_OK;
208 }
209
210 static void retry_use_local_memserv_response(void *a)
211 {
212     errval_t err;
213
214     struct spawn_binding *b = (struct spawn_binding*)a;
215
216     err = b->tx_vtbl.use_local_memserv_response(b, NOP_CONT);
217
218     if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
219         // try again
220         err = b->register_send(b, get_default_waitset(),
221                                MKCONT(retry_use_local_memserv_response,a));
222     }
223     if (err_is_fail(err)) {
224         DEBUG_ERR(err, "error sending use_local_memserv reply\n");
225     }
226
227 }
228
229
230 static void use_local_memserv_handler(struct spawn_binding *b)
231 {
232     ram_alloc_set(NULL);
233
234     errval_t err;
235     err = b->tx_vtbl.use_local_memserv_response(b, NOP_CONT);
236     if (err_is_fail(err)) {
237         DEBUG_ERR(err, "error sending use_local_memserv reply");
238         if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
239             err = b->register_send(b, get_default_waitset(),
240                                MKCONT(retry_use_local_memserv_response, b));
241             if (err_is_fail(err)) {
242                 // note that only one continuation may be registered at a time
243                 DEBUG_ERR(err, "register_send failed!");
244             }
245         }
246     }
247 }
248
249 struct pending_spawn_response {
250     struct spawn_binding *b;
251     errval_t err;
252     domainid_t domainid;
253 };
254
255 static errval_t spawn_with_caps_common(struct capref domain_cap,
256                                        const char *path, const char *argbuf,
257                                        size_t argbytes, const char *envbuf,
258                                        size_t envbytes,
259                                        struct capref inheritcn_cap,
260                                        struct capref argcn_cap, uint8_t flags,
261                                        domainid_t *domainid)
262 {
263     errval_t err;
264     assert(domainid);
265     *domainid = 0;
266
267     /* extract arguments from buffer */
268     char *argv[MAX_CMDLINE_ARGS + 1];
269     int i = 0;
270     size_t pos = 0;
271     while (pos < argbytes && i < MAX_CMDLINE_ARGS) {
272         argv[i++] = (CONST_CAST)argbuf + pos;
273         char *end = memchr(&argbuf[pos], '\0', argbytes - pos);
274         if (end == NULL) {
275             err = SPAWN_ERR_GET_CMDLINE_ARGS;
276             goto finish;
277         }
278         pos = end - argbuf + 1;
279     }
280     assert(i <= MAX_CMDLINE_ARGS);
281     argv[i] = NULL;
282
283     /* extract environment from buffer */
284     char *envp[MAX_CMDLINE_ARGS + 1];
285     i = 0;
286     pos = 0;
287     while (pos < envbytes && i < MAX_CMDLINE_ARGS) {
288         envp[i++] = (CONST_CAST)envbuf + pos;
289         char *end = memchr(&envbuf[pos], '\0', envbytes - pos);
290         if (end == NULL) {
291             err = SPAWN_ERR_GET_CMDLINE_ARGS;
292             goto finish;
293         }
294         pos = end - envbuf + 1;
295     }
296     assert(i <= MAX_CMDLINE_ARGS);
297     envp[i] = NULL;
298
299     char *npath;
300     npath = alloca(strlen(path));
301     strcpy(npath, path);
302     vfs_path_normalise(npath);
303
304     err = spawn(domain_cap, npath, argv, argbuf, argbytes, envp, inheritcn_cap,
305                 argcn_cap, flags, domainid);
306     // XXX: do we really want to delete the inheritcn and the argcn here? iaw:
307     // do we copy these somewhere? -SG
308     if (!capref_is_null(inheritcn_cap)) {
309         errval_t err2;
310         err2 = cap_delete(inheritcn_cap);
311         assert(err_is_ok(err2));
312     }
313     if (!capref_is_null(argcn_cap)) {
314         errval_t err2;
315         err2 = cap_delete(argcn_cap);
316         assert(err_is_ok(err2));
317     }
318
319  finish:
320     if(err_is_fail(err)) {
321         DEBUG_ERR(err, "spawn");
322     }
323
324     return err;
325 }
326
327 static errval_t spawn_with_caps_handler(struct spawn_binding *b, const char *path,
328     const char *argvbuf, size_t argvbytes, const char *envbuf, size_t envbytes,
329     struct capref inheritcn_cap, struct capref argcn_cap, uint8_t flags,
330     errval_t *err, spawn_domainid_t *domain_id)
331 {
332     *err = spawn_with_caps_common(NULL_CAP, path, argvbuf, argvbytes, envbuf,
333                                   envbytes, inheritcn_cap, argcn_cap, flags,
334                                   domain_id);
335     return SYS_ERR_OK;
336 }
337
338 static errval_t spawn_handler(struct spawn_binding *b, const char *path,
339     const char *argvbuf, size_t argvbytes, const char *envbuf, size_t envbytes,
340     uint8_t flags, errval_t *err, spawn_domainid_t *domain_id)
341 {
342     *err = spawn_with_caps_common(NULL_CAP, path, argvbuf, argvbytes, envbuf,
343                                   envbytes, NULL_CAP, NULL_CAP, flags,
344                                   domain_id);
345     return SYS_ERR_OK;
346 }
347
348 static void spawn_with_caps_request_handler(struct spawn_binding *b,
349                                             struct capref domain_cap,
350                                             const char *path,
351                                             const char *argvbuf,
352                                             size_t argvbytes,
353                                             const char *envbuf,
354                                             size_t envbytes,
355                                             struct capref inheritcn_cap,
356                                             struct capref argcn_cap,
357                                             uint8_t flags)
358 {
359     spawn_domainid_t dummy_domain_id;
360     errval_t err = spawn_with_caps_common(domain_cap, path, argvbuf, argvbytes,
361                                           envbuf, envbytes, inheritcn_cap,
362                                           argcn_cap, flags, &dummy_domain_id);
363     errval_t reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, domain_cap, err);
364     if (err_is_fail(reply_err)) {
365         DEBUG_ERR(err, "failed to send spawn_reply");
366     }
367 }
368
369 static void spawn_request_handler(struct spawn_binding *b,
370                                   struct capref domain_cap, const char *path,
371                                   const char *argvbuf, size_t argvbytes,
372                                   const char *envbuf, size_t envbytes,
373                                   uint8_t flags)
374 {
375     spawn_domainid_t dummy_domain_id;
376     errval_t err = spawn_with_caps_common(domain_cap, path, argvbuf, argvbytes,
377                                           envbuf, envbytes, NULL_CAP, NULL_CAP,
378                                           flags, &dummy_domain_id);
379     errval_t reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, domain_cap, err);
380     if (err_is_fail(reply_err)) {
381         DEBUG_ERR(err, "failed to send spawn_reply");
382     }
383 }
384
385 static void span_request_handler(struct spawn_binding *b,
386                                  struct capref domain_cap, struct capref vroot,
387                                  struct capref dispframe)
388 {
389     struct spawninfo si;
390     errval_t err, mon_err, reply_err;
391
392     memset(&si, 0, sizeof(si));
393
394     debug_printf("Spanning domain to core %d\n", disp_get_core_id());
395
396     // Span domain
397     err = spawn_span_domain(&si, vroot, dispframe);
398     if (err_is_fail(err)) {
399         err = err_push(err, SPAWN_ERR_SPAN);
400         goto reply;
401     }
402
403     // Set connection to monitor.
404     struct monitor_blocking_binding *mrpc = get_monitor_blocking_binding();
405     struct capref monep;
406     err = slot_alloc(&monep);
407     if (err_is_fail(err)) {
408         err = err_push(err, SPAWN_ERR_MONEP_SLOT_ALLOC);
409         goto reply;
410     }
411     err = mrpc->rpc_tx_vtbl.alloc_monitor_ep(mrpc, &mon_err, &monep);
412     if (err_is_ok(err)) {
413         err = mon_err;
414     }
415     if (err_is_fail(err)) {
416         err = err_push(err, SPAWN_ERR_MONITOR_CLIENT);
417         goto reply;
418     }
419
420     /* copy connection into the new domain */
421     struct capref destep = {
422         .cnode = si.taskcn,
423         .slot  = TASKCN_SLOT_MONITOREP,
424     };
425     err = cap_copy(destep, monep);
426     if (err_is_fail(err)) {
427         spawn_free(&si);
428         cap_destroy(monep);
429         err = err_push(err, SPAWN_ERR_MONITOR_CLIENT);
430         goto reply;
431     }
432
433     err = cap_destroy(monep);
434     if (err_is_fail(err)) {
435         err = err_push(err, SPAWN_ERR_MONITOR_CLIENT);
436         goto reply;
437     }
438
439     /* give the perfmon capability */
440     struct capref dest, src;
441     dest.cnode = si.taskcn;
442     dest.slot = TASKCN_SLOT_PERF_MON;
443     src.cnode = cnode_task;
444     src.slot = TASKCN_SLOT_PERF_MON;
445     err = cap_copy(dest, src);
446     if (err_is_fail(err)) {
447         err = err_push(err, SPAWN_ERR_COPY_PERF_MON);
448         goto reply;
449     }
450
451     // Pass over the domain cap.
452     dest.cnode = si.taskcn;
453     dest.slot = TASKCN_SLOT_DOMAINID;
454     err = cap_copy(dest, domain_cap);
455     if (err_is_fail(err)) {
456         err = err_push(err, SPAWN_ERR_COPY_DOMAIN_CAP);
457         goto reply;
458     }
459
460     // Make runnable
461     err = spawn_run(&si);
462     if (err_is_fail(err)) {
463         err = err_push(err, SPAWN_ERR_RUN);
464         goto reply;
465     }
466
467     // Allocate an id for this dispatcher.
468     struct ps_entry *pe = malloc(sizeof(struct ps_entry));
469     assert(pe != NULL);
470     memset(pe, 0, sizeof(struct ps_entry));
471     /*
472      * NB: It's important to keep a copy of the DCB *and* the root
473      * CNode around.  We need to revoke both (in the right order, see
474      * kill_domain() below), so that we ensure no one else is
475      * referring to the domain's CSpace anymore. Especially the loop
476      * created by placing rootcn into its own address space becomes a
477      * problem here.
478      */
479     // TODO(razvan): The following code is here to comply with spawn().
480     err = slot_alloc(&pe->rootcn_cap);
481     assert(err_is_ok(err));
482     err = cap_copy(pe->rootcn_cap, si.rootcn_cap);
483     pe->rootcn = si.rootcn;
484     assert(err_is_ok(err));
485     err = slot_alloc(&pe->dcb);
486     assert(err_is_ok(err));
487     err = cap_copy(pe->dcb, si.dcb);
488     assert(err_is_ok(err));
489     pe->status = PS_STATUS_RUNNING;
490     pe->domain_cap = domain_cap;
491     domainid_t domainid;
492     err = ps_allocate(pe, &domainid);
493     if(err_is_fail(err)) {
494         free(pe);
495     }
496
497     // Cleanup
498     err = spawn_free(&si);
499     if (err_is_fail(err)) {
500         err = err_push(err, SPAWN_ERR_FREE);
501     }
502
503 reply:
504     reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, domain_cap, err);
505     if (err_is_fail(reply_err)) {
506         DEBUG_ERR(err, "failed to send spawn_reply");
507     }
508 }
509
510 static void kill_request_handler(struct spawn_binding *b,
511                                  struct capref domain_cap)
512 {
513     debug_printf("kill_request_handler: NYI\n");
514 }
515
516 /**
517  * \brief Removes a zombie domain.
518  */
519 static void cleanup_domain(domainid_t domainid)
520 {
521     errval_t err;
522     struct ps_entry *ps = ps_get(domainid);
523     assert(ps != NULL);
524
525     // Tell all waiters of exit and free list as we go
526     for(struct ps_waiter *w = ps->waiters; w != NULL;) {
527         err = w->binding->tx_vtbl.wait_response
528             (w->binding, NOP_CONT, ps->exitcode, SYS_ERR_OK);
529         if(err_is_fail(err)) {
530             DEBUG_ERR(err, "wait_response");
531         }
532
533         struct ps_waiter *oldw = w;
534         w = w->next;
535         free(oldw);
536     }
537     ps->waiters = NULL;
538
539     // Cleanup rest of ps entry
540     free(ps->argbuf);
541
542     ps_remove(domainid);
543 }
544
545 static void cleanup_cap(struct capref cap)
546 {
547     errval_t err;
548
549     err = cap_revoke(cap);
550     if (err_is_fail(err)) {
551         DEBUG_ERR(err, "cap_revoke");
552     }
553     err = cap_destroy(cap);
554     if (err_is_fail(err)) {
555         DEBUG_ERR(err, "cap_destroy");
556     }
557 }
558
559 static errval_t kill_domain(domainid_t domainid, uint8_t exitcode)
560 {
561     struct ps_entry *ps = ps_get(domainid);
562
563     if(ps == NULL) {
564         return SPAWN_ERR_DOMAIN_NOTFOUND;
565     }
566
567     ps->status = PS_STATUS_ZOMBIE;
568     ps->exitcode = exitcode;
569
570     // Garbage collect victim's capabilities
571     cleanup_cap(ps->dcb);       // Deschedule dispatcher (do this first!)
572     cleanup_cap(ps->rootcn_cap);
573
574     // XXX: why only when waiters exist? -SG
575     if(ps->waiters != NULL) {
576         // Cleanup local data structures and inform waiters
577         cleanup_domain(domainid);
578     }
579
580     return SYS_ERR_OK;
581 }
582
583 static void kill_handler(struct spawn_binding *b, domainid_t domainid)
584 {
585     errval_t err = kill_domain(domainid, 0);
586
587     err = b->tx_vtbl.kill_response(b, NOP_CONT, err);
588     if(err_is_fail(err)) {
589         DEBUG_ERR(err, "kill_response");
590     }
591 }
592
593 static void exit_handler(struct spawn_binding *b, domainid_t domainid,
594                          uint8_t exitcode)
595 {
596     errval_t err = kill_domain(domainid, exitcode);
597     struct ps_entry *ps = ps_get(domainid);
598
599     if(err_is_fail(err)) {
600         DEBUG_ERR(err, "kill_domain");
601     }
602
603     if(ps == NULL) {
604         // XXX: Can't do nothing
605         return;
606     }
607
608     // May never return anything to client
609 }
610
611 static void wait_handler(struct spawn_binding *b, domainid_t domainid,
612                          bool nohang)
613 {
614     errval_t err;
615     struct ps_entry *ps = ps_get(domainid);
616
617     if(ps == NULL) {
618         err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_NOTFOUND);
619         if(err_is_fail(err)) {
620             DEBUG_ERR(err, "wait_response");
621         }
622     } else {
623         if(!nohang || ps->status == PS_STATUS_ZOMBIE) {
624             // Enqueue the waiter
625             struct ps_waiter *waiter = malloc(sizeof(struct ps_waiter));
626             assert(waiter != NULL);
627             waiter->next = ps->waiters;
628             waiter->binding = b;
629             ps->waiters = waiter;
630         } else {
631             // nohang and no zombie, return error
632             err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_RUNNING);
633             if(err_is_fail(err)) {
634                 DEBUG_ERR(err, "wait_response");
635             }
636         }
637
638         // Cleanup if zombie (will send the reply)
639         if(ps->status == PS_STATUS_ZOMBIE) {
640             cleanup_domain(domainid);
641         }
642     }
643 }
644
645 static void get_domainlist_sent(void *arg)
646 {
647     free(arg);
648 }
649
650 static void get_domainlist_handler(struct spawn_binding *b)
651 {
652     errval_t err;
653     size_t len = 0;
654     uint8_t *domains = calloc(sizeof(uint8_t), MAX_DOMAINS);
655
656     // XXX: Very inefficient
657     for(domainid_t i = 0; i < MAX_DOMAINS; i++) {
658         if(ps_exists(i)) {
659             domains[len++] = i;
660         }
661     }
662
663     err = b->tx_vtbl.get_domainlist_response
664         (b, MKCLOSURE(get_domainlist_sent, domains), domains, len);
665     if(err_is_fail(err)) {
666         DEBUG_ERR(err, "get_domainlist_response");
667         free(domains);
668     }
669 }
670
671 static void status_handler(struct spawn_binding *b, domainid_t domainid)
672 {
673     errval_t err;
674     struct ps_entry *ps = ps_get(domainid);
675     spawn_ps_entry_t pse;
676
677     memset(&pse, 0, sizeof(pse));
678
679     if(ps == NULL) {
680         err = b->tx_vtbl.status_response(b, NOP_CONT, pse, NULL, 0,
681                                          SPAWN_ERR_DOMAIN_NOTFOUND);
682         if(err_is_fail(err)) {
683             DEBUG_ERR(err, "status_response");
684         }
685     }
686
687     pse.status = ps->status;
688
689     err = b->tx_vtbl.status_response(b, NOP_CONT, pse, ps->argbuf, ps->argbytes,
690                                      SYS_ERR_OK);
691     if(err_is_fail(err)) {
692         DEBUG_ERR(err, "status_response");
693     }
694 }
695
696
697 static errval_t dump_capabilities(domainid_t domainid) {
698     struct ps_entry *ps = ps_get(domainid);
699
700     if(ps == NULL) {
701         return SPAWN_ERR_DOMAIN_NOTFOUND;
702     }
703
704     return invoke_dispatcher_dump_capabilities(ps->dcb);
705 }
706
707 static void dump_capabilities_handler(struct spawn_binding *b, domainid_t domainid) {
708     errval_t err = dump_capabilities(domainid);
709
710     err = b->tx_vtbl.dump_capabilities_response(b, NOP_CONT, err);
711     if(err_is_fail(err)) {
712         DEBUG_ERR(err, "debug_print_capabilities_response");
713     }
714 }
715
716 static struct spawn_rx_vtbl rx_vtbl = {
717     // .spawn_domain_call = spawn_handler,
718     // .spawn_domain_with_caps_call = spawn_with_caps_handler,
719
720     // Async messages for the process manager.
721     .spawn_request           = spawn_request_handler,
722     .spawn_with_caps_request = spawn_with_caps_request_handler,
723     .span_request            = span_request_handler,
724     .kill_request            = kill_request_handler,
725
726     .use_local_memserv_call = use_local_memserv_handler,
727     .kill_call = kill_handler,
728     .exit_call = exit_handler,
729     .wait_call = wait_handler,
730     .get_domainlist_call = get_domainlist_handler,
731     .status_call = status_handler,
732     .dump_capabilities_call = dump_capabilities_handler
733 };
734
735 static struct spawn_rpc_rx_vtbl rpc_rx_vtbl = {
736     .spawn_domain_call = spawn_handler,
737     .spawn_domain_with_caps_call = spawn_with_caps_handler,
738     // .use_local_memserv_call = use_local_memserv_handler,
739     // .kill_call = kill_handler,
740     // .exit_call = exit_handler,
741     // .wait_call = wait_handler,
742     // .get_domainlist_call = get_domainlist_handler,
743     // .status_call = status_handler,
744     // .dump_capabilities_call = dump_capabilities_handler
745 };
746
747 static void export_cb(void *st, errval_t err, iref_t iref)
748 {
749     if (err_is_fail(err)) {
750         USER_PANIC_ERR(err, "export failed");
751     }
752
753     // Send iref back to monitor, which will forward it to the process manager.
754     struct monitor_binding *mb = get_monitor_binding();
755     err = mb->tx_vtbl.set_spawn_iref_request(mb, NOP_CONT, iref);
756     if (err_is_fail(err)) {
757         USER_PANIC_ERR(err, "failed to send set_spawn_iref_request to "
758                 "monitor");
759     }
760
761     // construct name
762     char namebuf[32];
763     size_t len = snprintf(namebuf, sizeof(namebuf), "%s.%d", SERVICE_BASENAME,
764                           my_core_id);
765     assert(len < sizeof(namebuf));
766     namebuf[sizeof(namebuf) - 1] = '\0';
767
768     // register this iref with the name service
769     err = nameservice_register(namebuf, iref);
770     if (err_is_fail(err)) {
771         USER_PANIC_ERR(err, "nameservice_register failed");
772     }
773 }
774
775
776 static errval_t connect_cb(void *st, struct spawn_binding *b)
777 {
778     // copy my message receive handler vtable to the binding
779     b->rx_vtbl = rx_vtbl;
780     b->rpc_rx_vtbl = rpc_rx_vtbl;
781     return SYS_ERR_OK;
782 }
783
784 errval_t start_service(void)
785 {
786     return spawn_export(NULL, export_cb, connect_cb, get_default_waitset(),
787                          IDC_EXPORT_FLAGS_DEFAULT);
788 }