36232fb3081e4912a4d56cf6f87cddfe1ba281bb
[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/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, domain_cap, err);
390     if (err_is_fail(reply_err)) {
391         DEBUG_ERR(err, "failed to send spawn_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
403     errval_t err, reply_err;
404     struct capability ret;
405     err = monitor_cap_identify_remote(procmng_cap, &ret);
406     if (err_is_fail(err)) {
407         err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
408         goto reply;
409     }
410
411     if (ret.type != ObjType_ProcessManager) {
412         err = SPAWN_ERR_NOT_PROC_MNGR;
413         goto reply;
414     }
415     
416     spawn_domainid_t dummy_domain_id;
417     err = spawn_with_caps_common(domain_cap, path, argvbuf, argvbytes, envbuf,
418                                  envbytes, NULL_CAP, NULL_CAP, flags,
419                                  &dummy_domain_id);
420
421 reply:
422     reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, domain_cap, err);
423     if (err_is_fail(reply_err)) {
424         DEBUG_ERR(err, "failed to send spawn_reply");
425     }
426 }
427
428 static void span_request_handler(struct spawn_binding *b,
429                                  struct capref procmng_cap,
430                                  struct capref domain_cap, struct capref vroot,
431                                  struct capref dispframe)
432 {
433     errval_t err, mon_err, reply_err;
434     struct capability ret;
435     err = monitor_cap_identify_remote(procmng_cap, &ret);
436     if (err_is_fail(err)) {
437         err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
438         goto reply;
439     }
440
441     if (ret.type != ObjType_ProcessManager) {
442         err = SPAWN_ERR_NOT_PROC_MNGR;
443         goto reply;
444     }
445
446     struct spawninfo si;
447     memset(&si, 0, sizeof(si));
448
449     debug_printf("Spanning domain to core %d\n", disp_get_core_id());
450
451     // Span domain
452     err = spawn_span_domain(&si, vroot, dispframe);
453     if (err_is_fail(err)) {
454         err = err_push(err, SPAWN_ERR_SPAN);
455         goto reply;
456     }
457
458     // Set connection to monitor.
459     struct monitor_blocking_binding *mrpc = get_monitor_blocking_binding();
460     struct capref monep;
461     err = slot_alloc(&monep);
462     if (err_is_fail(err)) {
463         err = err_push(err, SPAWN_ERR_MONEP_SLOT_ALLOC);
464         goto reply;
465     }
466     err = mrpc->rpc_tx_vtbl.alloc_monitor_ep(mrpc, &mon_err, &monep);
467     if (err_is_ok(err)) {
468         err = mon_err;
469     }
470     if (err_is_fail(err)) {
471         err = err_push(err, SPAWN_ERR_MONITOR_CLIENT);
472         goto reply;
473     }
474
475     /* copy connection into the new domain */
476     struct capref destep = {
477         .cnode = si.taskcn,
478         .slot  = TASKCN_SLOT_MONITOREP,
479     };
480     err = cap_copy(destep, monep);
481     if (err_is_fail(err)) {
482         spawn_free(&si);
483         cap_destroy(monep);
484         err = err_push(err, SPAWN_ERR_MONITOR_CLIENT);
485         goto reply;
486     }
487
488     err = cap_destroy(monep);
489     if (err_is_fail(err)) {
490         err = err_push(err, SPAWN_ERR_MONITOR_CLIENT);
491         goto reply;
492     }
493
494     /* give the perfmon capability */
495     struct capref dest, src;
496     dest.cnode = si.taskcn;
497     dest.slot = TASKCN_SLOT_PERF_MON;
498     src.cnode = cnode_task;
499     src.slot = TASKCN_SLOT_PERF_MON;
500     err = cap_copy(dest, src);
501     if (err_is_fail(err)) {
502         err = err_push(err, SPAWN_ERR_COPY_PERF_MON);
503         goto reply;
504     }
505
506     // Pass over the domain cap.
507     dest.cnode = si.taskcn;
508     dest.slot = TASKCN_SLOT_DOMAINID;
509     err = cap_copy(dest, domain_cap);
510     if (err_is_fail(err)) {
511         err = err_push(err, SPAWN_ERR_COPY_DOMAIN_CAP);
512         goto reply;
513     }
514
515     // Make runnable
516     err = spawn_run(&si);
517     if (err_is_fail(err)) {
518         err = err_push(err, SPAWN_ERR_RUN);
519         goto reply;
520     }
521
522     // Allocate an id for this dispatcher.
523     struct ps_entry *pe = malloc(sizeof(struct ps_entry));
524     assert(pe != NULL);
525     memset(pe, 0, sizeof(struct ps_entry));
526     /*
527      * NB: It's important to keep a copy of the DCB *and* the root
528      * CNode around.  We need to revoke both (in the right order, see
529      * kill_domain() below), so that we ensure no one else is
530      * referring to the domain's CSpace anymore. Especially the loop
531      * created by placing rootcn into its own address space becomes a
532      * problem here.
533      */
534     // TODO(razvan): The following code is here to comply with spawn().
535     err = slot_alloc(&pe->rootcn_cap);
536     assert(err_is_ok(err));
537     err = cap_copy(pe->rootcn_cap, si.rootcn_cap);
538     pe->rootcn = si.rootcn;
539     assert(err_is_ok(err));
540     err = slot_alloc(&pe->dcb);
541     assert(err_is_ok(err));
542     err = cap_copy(pe->dcb, si.dcb);
543     assert(err_is_ok(err));
544     pe->status = PS_STATUS_RUNNING;
545
546     err = ps_hash_domain(pe, domain_cap);
547     if (err_is_fail(err)) {
548         free(pe);
549         spawn_free(&si);
550         err = err_push(err, SPAWN_ERR_DOMAIN_CAP_HASH);
551         goto reply;
552     }
553
554     domainid_t domainid;
555     err = ps_allocate(pe, &domainid);
556     if(err_is_fail(err)) {
557         free(pe);
558     }
559
560     // Cleanup
561     err = spawn_free(&si);
562     if (err_is_fail(err)) {
563         err = err_push(err, SPAWN_ERR_FREE);
564     }
565
566 reply:
567     reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, domain_cap, err);
568     if (err_is_fail(reply_err)) {
569         DEBUG_ERR(err, "failed to send spawn_reply");
570     }
571 }
572
573 static void cleanup_cap(struct capref cap)
574 {
575     errval_t err;
576
577     err = cap_revoke(cap);
578     if (err_is_fail(err)) {
579         DEBUG_ERR(err, "cap_revoke");
580     }
581     err = cap_destroy(cap);
582     if (err_is_fail(err)) {
583         DEBUG_ERR(err, "cap_destroy");
584     }
585 }
586
587 static void kill_request_handler(struct spawn_binding *b,
588                                  struct capref procmng_cap,
589                                  struct capref domain_cap)
590 {
591     errval_t err, reply_err;
592     struct capability ret;
593     err = monitor_cap_identify_remote(procmng_cap, &ret);
594     if (err_is_fail(err)) {
595         err = err_push(err, SPAWN_ERR_IDENTIFY_PROC_MNGR_CAP);
596         goto reply;
597     }
598
599     if (ret.type != ObjType_ProcessManager) {
600         err = SPAWN_ERR_NOT_PROC_MNGR;
601         goto reply;
602     }
603
604     struct ps_entry *pe;
605     err = ps_release_domain(domain_cap, &pe);
606     if (err_is_fail(err)) {
607         err = err_push(err, SPAWN_ERR_DOMAIN_NOTFOUND);
608         goto reply;
609     }
610
611     // Garbage collect victim's capabilities
612     cleanup_cap(pe->dcb);       // Deschedule dispatcher (do this first!)
613     cleanup_cap(pe->rootcn_cap);
614
615     // Cleanup struct ps_entry. Note that waiters will be handled by the process
616     // manager, as opposed to the old protocol of handling them here.
617     free(pe->argbuf);
618     ps_remove(pe->domain_id);
619     free(pe);
620
621 reply:
622     reply_err = b->tx_vtbl.spawn_reply(b, NOP_CONT, domain_cap, err);
623     if (err_is_fail(reply_err)) {
624         DEBUG_ERR(err, "failed to send spawn_reply");
625     }
626 }
627
628 /**
629  * \brief Removes a zombie domain.
630  */
631 static void cleanup_domain(domainid_t domainid)
632 {
633     errval_t err;
634     struct ps_entry *ps = ps_get(domainid);
635     assert(ps != NULL);
636
637     // Tell all waiters of exit and free list as we go
638     for(struct ps_waiter *w = ps->waiters; w != NULL;) {
639         err = w->binding->tx_vtbl.wait_response
640             (w->binding, NOP_CONT, ps->exitcode, SYS_ERR_OK);
641         if(err_is_fail(err)) {
642             DEBUG_ERR(err, "wait_response");
643         }
644
645         struct ps_waiter *oldw = w;
646         w = w->next;
647         free(oldw);
648     }
649     ps->waiters = NULL;
650
651     // Cleanup rest of ps entry
652     free(ps->argbuf);
653
654     ps_remove(domainid);
655 }
656
657 static errval_t kill_domain(domainid_t domainid, uint8_t exitcode)
658 {
659     struct ps_entry *ps = ps_get(domainid);
660
661     if(ps == NULL) {
662         return SPAWN_ERR_DOMAIN_NOTFOUND;
663     }
664
665     ps->status = PS_STATUS_ZOMBIE;
666     ps->exitcode = exitcode;
667
668     // Garbage collect victim's capabilities
669     cleanup_cap(ps->dcb);       // Deschedule dispatcher (do this first!)
670     cleanup_cap(ps->rootcn_cap);
671
672     // XXX: why only when waiters exist? -SG
673     if(ps->waiters != NULL) {
674         // Cleanup local data structures and inform waiters
675         cleanup_domain(domainid);
676     }
677
678     return SYS_ERR_OK;
679 }
680
681 static void kill_handler(struct spawn_binding *b, domainid_t domainid)
682 {
683     errval_t err = kill_domain(domainid, 0);
684
685     err = b->tx_vtbl.kill_response(b, NOP_CONT, err);
686     if(err_is_fail(err)) {
687         DEBUG_ERR(err, "kill_response");
688     }
689 }
690
691 static void exit_handler(struct spawn_binding *b, domainid_t domainid,
692                          uint8_t exitcode)
693 {
694     errval_t err = kill_domain(domainid, exitcode);
695     struct ps_entry *ps = ps_get(domainid);
696
697     if(err_is_fail(err)) {
698         DEBUG_ERR(err, "kill_domain");
699     }
700
701     if(ps == NULL) {
702         // XXX: Can't do nothing
703         return;
704     }
705
706     // May never return anything to client
707 }
708
709 static void wait_handler(struct spawn_binding *b, domainid_t domainid,
710                          bool nohang)
711 {
712     errval_t err;
713     struct ps_entry *ps = ps_get(domainid);
714
715     if(ps == NULL) {
716         err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_NOTFOUND);
717         if(err_is_fail(err)) {
718             DEBUG_ERR(err, "wait_response");
719         }
720     } else {
721         if(!nohang || ps->status == PS_STATUS_ZOMBIE) {
722             // Enqueue the waiter
723             struct ps_waiter *waiter = malloc(sizeof(struct ps_waiter));
724             assert(waiter != NULL);
725             waiter->next = ps->waiters;
726             waiter->binding = b;
727             ps->waiters = waiter;
728         } else {
729             // nohang and no zombie, return error
730             err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_RUNNING);
731             if(err_is_fail(err)) {
732                 DEBUG_ERR(err, "wait_response");
733             }
734         }
735
736         // Cleanup if zombie (will send the reply)
737         if(ps->status == PS_STATUS_ZOMBIE) {
738             cleanup_domain(domainid);
739         }
740     }
741 }
742
743 static void get_domainlist_sent(void *arg)
744 {
745     free(arg);
746 }
747
748 static void get_domainlist_handler(struct spawn_binding *b)
749 {
750     errval_t err;
751     size_t len = 0;
752     uint8_t *domains = calloc(sizeof(uint8_t), MAX_DOMAINS);
753
754     // XXX: Very inefficient
755     for(domainid_t i = 0; i < MAX_DOMAINS; i++) {
756         if(ps_exists(i)) {
757             domains[len++] = i;
758         }
759     }
760
761     err = b->tx_vtbl.get_domainlist_response
762         (b, MKCLOSURE(get_domainlist_sent, domains), domains, len);
763     if(err_is_fail(err)) {
764         DEBUG_ERR(err, "get_domainlist_response");
765         free(domains);
766     }
767 }
768
769 static void status_handler(struct spawn_binding *b, domainid_t domainid)
770 {
771     errval_t err;
772     struct ps_entry *ps = ps_get(domainid);
773     spawn_ps_entry_t pse;
774
775     memset(&pse, 0, sizeof(pse));
776
777     if(ps == NULL) {
778         err = b->tx_vtbl.status_response(b, NOP_CONT, pse, NULL, 0,
779                                          SPAWN_ERR_DOMAIN_NOTFOUND);
780         if(err_is_fail(err)) {
781             DEBUG_ERR(err, "status_response");
782         }
783     }
784
785     pse.status = ps->status;
786
787     err = b->tx_vtbl.status_response(b, NOP_CONT, pse, ps->argbuf, ps->argbytes,
788                                      SYS_ERR_OK);
789     if(err_is_fail(err)) {
790         DEBUG_ERR(err, "status_response");
791     }
792 }
793
794
795 static errval_t dump_capabilities(domainid_t domainid) {
796     struct ps_entry *ps = ps_get(domainid);
797
798     if(ps == NULL) {
799         return SPAWN_ERR_DOMAIN_NOTFOUND;
800     }
801
802     return invoke_dispatcher_dump_capabilities(ps->dcb);
803 }
804
805 static void dump_capabilities_handler(struct spawn_binding *b, domainid_t domainid) {
806     errval_t err = dump_capabilities(domainid);
807
808     err = b->tx_vtbl.dump_capabilities_response(b, NOP_CONT, err);
809     if(err_is_fail(err)) {
810         DEBUG_ERR(err, "debug_print_capabilities_response");
811     }
812 }
813
814 static struct spawn_rx_vtbl rx_vtbl = {
815     // .spawn_domain_call = spawn_handler,
816     // .spawn_domain_with_caps_call = spawn_with_caps_handler,
817
818     // Async messages for the process manager.
819     .spawn_request           = spawn_request_handler,
820     .spawn_with_caps_request = spawn_with_caps_request_handler,
821     .span_request            = span_request_handler,
822     .kill_request            = kill_request_handler,
823
824     .use_local_memserv_call = use_local_memserv_handler,
825     .kill_call = kill_handler,
826     .exit_call = exit_handler,
827     .wait_call = wait_handler,
828     .get_domainlist_call = get_domainlist_handler,
829     .status_call = status_handler,
830     .dump_capabilities_call = dump_capabilities_handler
831 };
832
833 static struct spawn_rpc_rx_vtbl rpc_rx_vtbl = {
834     .spawn_domain_call = spawn_handler,
835     .spawn_domain_with_caps_call = spawn_with_caps_handler,
836     // .use_local_memserv_call = use_local_memserv_handler,
837     // .kill_call = kill_handler,
838     // .exit_call = exit_handler,
839     // .wait_call = wait_handler,
840     // .get_domainlist_call = get_domainlist_handler,
841     // .status_call = status_handler,
842     // .dump_capabilities_call = dump_capabilities_handler
843 };
844
845 static void export_cb(void *st, errval_t err, iref_t iref)
846 {
847     if (err_is_fail(err)) {
848         USER_PANIC_ERR(err, "export failed");
849     }
850
851     // Send iref back to monitor, which will forward it to the process manager.
852     struct monitor_binding *mb = get_monitor_binding();
853     err = mb->tx_vtbl.set_spawn_iref_request(mb, NOP_CONT, iref);
854     if (err_is_fail(err)) {
855         USER_PANIC_ERR(err, "failed to send set_spawn_iref_request to "
856                 "monitor");
857     }
858
859     // construct name
860     char namebuf[32];
861     size_t len = snprintf(namebuf, sizeof(namebuf), "%s.%d", SERVICE_BASENAME,
862                           my_core_id);
863     assert(len < sizeof(namebuf));
864     namebuf[sizeof(namebuf) - 1] = '\0';
865
866     // register this iref with the name service
867     err = nameservice_register(namebuf, iref);
868     if (err_is_fail(err)) {
869         USER_PANIC_ERR(err, "nameservice_register failed");
870     }
871 }
872
873
874 static errval_t connect_cb(void *st, struct spawn_binding *b)
875 {
876     // copy my message receive handler vtable to the binding
877     b->rx_vtbl = rx_vtbl;
878     b->rpc_rx_vtbl = rpc_rx_vtbl;
879     return SYS_ERR_OK;
880 }
881
882 errval_t start_service(void)
883 {
884     return spawn_export(NULL, export_cb, connect_cb, get_default_waitset(),
885                          IDC_EXPORT_FLAGS_DEFAULT);
886 }