readme: add NXP iMX8X to supported platforms
[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, 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
35 static 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
220 static 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
240 static 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
259 struct pending_spawn_response {
260     struct spawn_binding *b;
261     errval_t err;
262     domainid_t domainid;
263 };
264
265 static 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
337 static 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
348 static 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
358 static 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
388 reply:
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
395 static 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
420 reply:
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
427 static 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
565 reply:
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
572 static 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
586 static 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
612 reply:
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
619 static 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
651 reply:
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  */
661 static 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
687 static 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
711 static 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
721 static 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
739 static 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
773 static void get_domainlist_sent(void *arg)
774 {
775     free(arg);
776 }
777
778 static 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
799 static 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
825 static 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
835 static 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
844 static 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
864 static 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
876 static 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
905 static 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
913 errval_t start_service(void)
914 {
915     return spawn_export(NULL, export_cb, connect_cb, get_default_waitset(),
916                          IDC_EXPORT_FLAGS_DEFAULT);
917 }