readme: add NXP iMX8X to supported platforms
[barrelfish] / usr / proc_mgmt / service.c
1 /**
2  * \file
3  * \brief Process management service.
4  */
5
6 /*
7  * Copyright (c) 2017, 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 <barrelfish/barrelfish.h>
16 #include <barrelfish/nameservice_client.h>
17 #include <barrelfish/spawn_client.h>
18 #include <if/monitor_defs.h>
19 #include <if/proc_mgmt_defs.h>
20 #include <if/spawn_defs.h>
21
22 #include <dist/barrier.h>
23
24 #include "domain.h"
25 #include "internal.h"
26 #include "pending_clients.h"
27 #include "spawnd_state.h"
28
29 /**
30  * \brief Handler for message add_spawnd, for the local monitor binding.
31  */
32 static void add_spawnd_handler(struct proc_mgmt_binding *b, coreid_t core_id,
33                                iref_t iref)
34 {
35     if (spawnd_state_exists(core_id)) {
36         DEBUG_ERR(PROC_MGMT_ERR_SPAWND_EXISTS, "spawnd_state_exists");
37         return;
38     }
39
40     // Bind with the spawnd.
41     struct spawn_binding *spawnb;
42     errval_t err = spawn_bind_iref(iref, &spawnb);
43     if (err_is_fail(err)) {
44         DEBUG_ERR(err, "spawn_bind_iref");
45         return;
46     }
47
48     err = spawnd_state_alloc(core_id, spawnb);
49     if (err_is_fail(err)) {
50         DEBUG_ERR(err, "spawnd_state_alloc");
51     }
52
53     // Tell name service that we're ready on the core spawnd just came up
54     err = nsb_register_n(core_id, SERVICE_BASENAME);
55     if (err_is_fail(err)) {
56         USER_PANIC_ERR(err, "nsb_register_n failed");
57     }
58 }
59
60 /**
61  * \brief Handler for message add_spawnd, for non-monitor bindings.
62  */
63 static void add_spawnd_handler_non_monitor(struct proc_mgmt_binding *b,
64                                            coreid_t core_id, iref_t iref)
65 {
66     // debug_printf("Ignoring add_spawnd call: %s\n",
67     //              err_getstring(PROC_MGMT_ERR_NOT_MONITOR));
68 }
69
70 static bool cleanup_request_sender(struct msg_queue_elem *m);
71
72 /**
73  * General-purpose handler for replies from spawnd.
74  */
75 static void spawn_reply_handler(struct spawn_binding *b, errval_t spawn_err)
76 {
77     struct pending_client *cl =
78             (struct pending_client*) spawnd_state_dequeue_recv(b->st);
79
80     /* there's no pending client, return */
81     if (cl == NULL) {
82         return;    
83     }
84
85     struct pending_spawn *spawn = NULL;
86     struct pending_span *span = NULL;
87     struct pending_kill_cleanup *kc = NULL;
88
89     struct domain_entry *entry;
90     
91     errval_t err, resp_err;
92
93     switch (cl->type) {
94         case ClientType_Spawn:
95         case ClientType_SpawnWithCaps:
96             spawn = (struct pending_spawn*) cl->st;
97             err = spawn_err;
98             if (err_is_ok(spawn_err)) {
99                 err = domain_spawn(spawn->cap_node, spawn->core_id, spawn->argvbuf,
100                                    spawn->argvbytes);
101             }
102             if (cl->type == ClientType_Spawn) {
103                 resp_err = cl->b->tx_vtbl.spawn_response(cl->b, NOP_CONT,
104                         err, spawn->cap_node->domain_cap);
105             } else {
106                 resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b,
107                         NOP_CONT, err, spawn->cap_node->domain_cap);
108             }
109
110             free(spawn);
111             break;
112
113         case ClientType_Span:
114             span = (struct pending_span*) cl->st;
115             entry = span->entry;
116             if (entry->status == DOMAIN_STATUS_RUNNING) {
117                 resp_err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT,
118                                                         spawn_err);
119             }
120
121             free(span);
122             break;
123
124         case ClientType_Cleanup:
125             kc = (struct pending_kill_cleanup*) cl->st;
126             entry = kc->entry;
127
128             assert(entry->num_spawnds_resources > 0);
129             assert(entry->status != DOMAIN_STATUS_CLEANED);
130
131             --entry->num_spawnds_resources;
132             if (entry->num_spawnds_resources == 0) {
133                 entry->status = DOMAIN_STATUS_CLEANED;
134
135                 // At this point, the domain exists in state CLEANED for history
136                 // reasons. For instance, if some other domain issues a wait
137                 // call for this one, the process manager can return the exit
138                 // status directly. At some point, however, we might want to
139                 // just clean up the domain entry and recycle the domain cap.
140             }
141
142             free(kc);
143             break;
144
145         case ClientType_Kill:
146         case ClientType_Exit:
147             kc = (struct pending_kill_cleanup*) cl->st;
148             entry = kc->entry;
149
150             assert(entry->num_spawnds_running > 0);
151             assert(entry->status != DOMAIN_STATUS_STOPPED);
152
153             --entry->num_spawnds_running;
154
155             if (entry->num_spawnds_running == 0) {
156                 entry->status = DOMAIN_STATUS_STOPPED;
157
158                 if (cl->type == ClientType_Kill) {
159                     entry->exit_status = EXIT_STATUS_KILLED;
160                     resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT,
161                                                             spawn_err);
162                 }
163
164                 struct domain_waiter *waiter = entry->waiters;
165                 while (waiter != NULL) {
166                     waiter->b->tx_vtbl.wait_response(waiter->b, NOP_CONT,
167                                                      SYS_ERR_OK,
168                                                      entry->exit_status);
169                     struct domain_waiter *tmp = waiter;
170                     waiter = waiter->next;
171                     free(tmp);
172                 }
173
174                 for (coreid_t i = 0; i < MAX_COREID; ++i) {
175                     if (entry->spawnds[i] == NULL) {
176                         continue;
177                     }
178
179                     struct spawn_binding *spb = entry->spawnds[i]->b;
180
181                     struct pending_kill_cleanup *cleanup =
182                             (struct pending_kill_cleanup*) malloc(
183                                     sizeof(struct pending_kill_cleanup));
184                     cleanup->b = spb;
185                     cleanup->domain_cap = kc->domain_cap;
186                     cleanup->entry = entry;
187
188                     struct pending_client *cleanup_cl =
189                             (struct pending_client*) malloc(
190                                     sizeof(struct pending_client));
191                     cleanup_cl->b = cl->b;
192                     cleanup_cl->type = ClientType_Cleanup;
193                     cleanup_cl->st = cleanup;
194
195                     struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
196                             sizeof(struct msg_queue_elem));
197                     msg->st = cleanup_cl;
198                     msg->cont = cleanup_request_sender;
199
200                     err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
201
202                     if (err_is_fail(err)) {
203                         DEBUG_ERR(err, "enqueuing cleanup request");
204                         free(cleanup);
205                         free(cleanup_cl);
206                         free(msg);
207                     }
208                 }
209             }
210
211             free(kc);
212             break;
213
214         default:
215             USER_PANIC("Unknown client type in spawn_reply_handler: %u\n",
216                        cl->type);
217     }
218
219     free(cl);
220 }
221
222 /**
223  * \brief Handler for sending spawn requests.
224  */
225 static bool spawn_request_sender(struct msg_queue_elem *m)
226 {
227     struct pending_client *cl = (struct pending_client*) m->st;
228     struct pending_spawn *spawn = (struct pending_spawn*) cl->st;
229     spawn->b->rx_vtbl.spawn_reply = spawn_reply_handler;
230
231     errval_t err;
232     bool with_caps = !(capref_is_null(spawn->inheritcn_cap) &&
233                        capref_is_null(spawn->argcn_cap));
234     if (with_caps) {
235         err = spawn->b->tx_vtbl.spawn_with_caps_request(spawn->b, NOP_CONT,
236                                                         cap_procmng,
237                                                         spawn->cap_node->domain_cap,
238                                                         spawn->path,
239                                                         spawn->argvbuf,
240                                                         spawn->argvbytes,
241                                                         spawn->envbuf,
242                                                         spawn->envbytes,
243                                                         spawn->inheritcn_cap,
244                                                         spawn->argcn_cap,
245                                                         spawn->flags);
246     } else {
247         err = spawn->b->tx_vtbl.spawn_request(spawn->b, NOP_CONT, cap_procmng,
248                                               spawn->cap_node->domain_cap,
249                                               spawn->path, spawn->argvbuf,
250                                               spawn->argvbytes, spawn->envbuf,
251                                               spawn->envbytes, spawn->flags);
252     }
253
254     if (err_is_fail(err)) {
255         if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
256             return false;
257         } else {
258             USER_PANIC_ERR(err, "sending spawn request");
259         }
260     }
261
262     free(m);
263
264     return true;
265 }
266
267 /**
268  * \brief Handler for sending span requests.
269  */
270 static bool span_request_sender(struct msg_queue_elem *m)
271 {
272     struct pending_client *cl = (struct pending_client*) m->st;
273     struct pending_span *span = (struct pending_span*) cl->st;
274
275     errval_t err;
276     span->b->rx_vtbl.spawn_reply = spawn_reply_handler;
277     err = span->b->tx_vtbl.span_request(span->b, NOP_CONT, cap_procmng,
278                                         span->domain_cap, span->vroot,
279                                         span->dispframe);
280
281     if (err_is_fail(err)) {
282         if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
283             return false;
284         } else {
285             USER_PANIC_ERR(err, "sending span request");
286         }
287     }
288
289     free(m);
290
291     return true;
292 }
293
294 /**
295  * \brief Handler for sending kill requests.
296  */
297 static bool kill_request_sender(struct msg_queue_elem *m)
298 {
299     struct pending_client *cl = (struct pending_client*) m->st;
300     struct pending_kill_cleanup *kill = (struct pending_kill_cleanup*) cl->st;
301
302     errval_t err;
303     kill->b->rx_vtbl.spawn_reply = spawn_reply_handler;
304     err = kill->b->tx_vtbl.kill_request(kill->b, NOP_CONT, cap_procmng,
305                                         kill->domain_cap);
306
307     if (err_is_fail(err)) {
308         if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
309             return false;
310         } else {
311             USER_PANIC_ERR(err, "sending kill request");
312         }
313     }
314
315     free(m);
316
317     return true;
318 }
319
320 /**
321  * \brief Handler for sending cleanup requests.
322  */
323 static bool cleanup_request_sender(struct msg_queue_elem *m)
324 {
325     struct pending_client *cl = (struct pending_client*) m->st;
326     struct pending_kill_cleanup *cleanup = (struct pending_kill_cleanup*) cl->st;
327
328     errval_t err;
329     cleanup->b->rx_vtbl.spawn_reply = spawn_reply_handler;
330     err = cleanup->b->tx_vtbl.cleanup_request(cleanup->b, NOP_CONT,
331                                               cap_procmng,
332                                               cleanup->domain_cap);
333
334     if (err_is_fail(err)) {
335         if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
336             return false;
337         } else {
338             USER_PANIC_ERR(err, "sending cleanup request");
339         }
340     }
341
342     free(m);
343
344     return true;
345 }
346
347 /**
348  * \brief Common bits of the spawn and spawn_with_caps handlers.
349  */
350 static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
351                                      enum ClientType type,
352                                      coreid_t core_id, const char *path,
353                                      const char *argvbuf, size_t argvbytes,
354                                      const char *envbuf, size_t envbytes,
355                                      struct capref inheritcn_cap,
356                                      struct capref argcn_cap, uint8_t flags)
357 {
358     if (!spawnd_state_exists(core_id)) {
359         /* Spawnd on the requested core is not up
360          * Clients can do nsb_wait_n(core_id, "proc_mgmt") to avoid
361          * this situation 
362          */
363         return PROC_MGMT_ERR_INVALID_SPAWND;
364     }
365
366     struct spawnd_state *spawnd = spawnd_state_get(core_id);
367     assert(spawnd != NULL);
368     struct spawn_binding *cl = spawnd->b;
369     assert(cl != NULL);
370
371     errval_t err;
372     if (domain_should_refill_caps()) {
373         err = domain_prealloc_caps();
374         if (err_is_fail(err)) {
375             return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
376         }
377     }
378
379     struct domain_cap_node *cap_node = next_cap_node();
380
381     struct pending_spawn *spawn = (struct pending_spawn*) malloc(
382             sizeof(struct pending_spawn));
383     spawn->cap_node = cap_node;
384     // spawn->domain_cap = domain_cap;
385     spawn->b = cl;
386     spawn->core_id = core_id;
387     spawn->path = path;
388     spawn->argvbuf = argvbuf;
389     spawn->argvbytes = argvbytes;
390     spawn->envbuf = envbuf;
391     spawn->envbytes = envbytes;
392     spawn->inheritcn_cap = inheritcn_cap;
393     spawn->argcn_cap = argcn_cap;
394     spawn->flags = flags;
395
396     struct pending_client *spawn_cl = (struct pending_client*) malloc(
397             sizeof(struct pending_client));
398     spawn_cl->b = b;
399     spawn_cl->type = type;
400     spawn_cl->st = spawn;
401
402     struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
403             sizeof(struct msg_queue_elem));
404     msg->st = spawn_cl;
405     msg->cont = spawn_request_sender;
406
407     err = spawnd_state_enqueue_send(spawnd, msg);
408     if (err_is_fail(err)) {
409         DEBUG_ERR(err, "enqueuing spawn request");
410         free(spawn);
411         free(spawn_cl);
412         free(msg);
413     }
414
415     return SYS_ERR_OK;
416 }
417
418 /**
419  * \brief Handler for rpc spawn.
420  */
421 static void spawn_handler(struct proc_mgmt_binding *b, coreid_t core_id,
422                           const char *path, const char *argvbuf,
423                           size_t argvbytes, const char *envbuf, size_t envbytes,
424                           uint8_t flags)
425 {
426     errval_t err, resp_err;
427     err = spawn_handler_common(b, ClientType_Spawn, core_id, path, argvbuf,
428                                argvbytes, envbuf, envbytes, NULL_CAP, NULL_CAP,
429                                flags);
430
431     if (err_is_fail(err)) {
432         resp_err = b->tx_vtbl.spawn_response(b, NOP_CONT, err, NULL_CAP);
433         if (err_is_fail(resp_err)) {
434             DEBUG_ERR(resp_err, "failed to send spawn_response");
435         }
436     }
437 }
438
439 /**
440  * \brief Handler for rpc spawn_with_caps.
441  */
442 static void spawn_with_caps_handler(struct proc_mgmt_binding *b,
443                                     coreid_t core_id, const char *path,
444                                     const char *argvbuf, size_t argvbytes,
445                                     const char *envbuf, size_t envbytes,
446                                     struct capref inheritcn_cap,
447                                     struct capref argcn_cap, uint8_t flags)
448 {
449     errval_t err, resp_err;
450     err = spawn_handler_common(b, ClientType_SpawnWithCaps, core_id, path,
451                                argvbuf, argvbytes, envbuf, envbytes,
452                                inheritcn_cap, argcn_cap, flags);
453     if (err_is_ok(err)) {
454         // Will respond to client when we get the reply from spawnd.
455         return;
456     }
457
458     resp_err = b->tx_vtbl.spawn_with_caps_response(b, NOP_CONT, err,
459                                                             NULL_CAP);
460     if (err_is_fail(resp_err)) {
461         DEBUG_ERR(resp_err, "failed to send spawn_with_caps_response");
462     }
463 }
464
465 /**
466  * \brief Handler for rpc span.
467  */
468 static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
469                          coreid_t core_id, struct capref vroot,
470                          struct capref dispframe)
471 {
472     errval_t err, resp_err;
473     struct domain_entry *entry = NULL;
474     err = domain_get_by_cap(domain_cap, &entry);
475     if (err_is_fail(err)) {
476         goto respond_with_err;
477     }
478
479     assert(entry != NULL);
480     if (entry->status != DOMAIN_STATUS_RUNNING) {
481         err = PROC_MGMT_ERR_DOMAIN_NOT_RUNNING;
482         goto respond_with_err;
483     }
484
485     if (entry->spawnds[core_id] != NULL) {
486         // TODO(razvan): Maybe we want to allow the same domain to span multiple
487         // dispatchers onto the same core?
488         err = PROC_MGMT_ERR_ALREADY_SPANNED;
489         goto respond_with_err;
490     }
491
492     if (!spawnd_state_exists(core_id)) {
493         err = PROC_MGMT_ERR_INVALID_SPAWND;
494         goto respond_with_err;
495     }
496
497     struct spawnd_state *spawnd = spawnd_state_get(core_id);
498     assert(spawnd != NULL);
499     struct spawn_binding *cl = spawnd->b;
500     assert(cl != NULL);
501
502     struct pending_span *span = (struct pending_span*) malloc(
503             sizeof(struct pending_span));
504     span->domain_cap = domain_cap;
505     span->entry = entry;
506     span->b = cl;
507     span->core_id = core_id;
508     span->vroot = vroot;
509     span->dispframe = dispframe;
510
511     struct pending_client *span_cl = (struct pending_client*) malloc(
512             sizeof(struct pending_client));
513     span_cl->b = b;
514     span_cl->type = ClientType_Span;
515     span_cl->st = span;
516
517     struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
518             sizeof(struct msg_queue_elem));
519     msg->st = span_cl;
520     msg->cont = span_request_sender;
521
522     err = spawnd_state_enqueue_send(spawnd, msg);
523
524     if (err_is_fail(err)) {
525         DEBUG_ERR(err, "enqueuing span request");
526         free(span);
527         free(span_cl);
528         free(msg);
529     }
530
531 respond_with_err:
532     resp_err = b->tx_vtbl.span_response(b, NOP_CONT, err);
533     if (err_is_fail(resp_err)) {
534         DEBUG_ERR(resp_err, "failed to send span_response");
535     }
536 }
537
538 /**
539  * \brief Common bits of the kill and exit handlers.
540  */
541 static errval_t kill_handler_common(struct proc_mgmt_binding *b,
542                                     struct capref domain_cap,
543                                     enum ClientType type,
544                                     uint8_t exit_status)
545 {
546     struct domain_entry *entry;
547     errval_t err = domain_get_by_cap(domain_cap, &entry);
548     if (err_is_fail(err)) {
549         return err;
550     }
551
552     entry->exit_status = exit_status;
553     domain_stop_pending(entry);
554
555     for (coreid_t i = 0; i < MAX_COREID; ++i) {
556         if (entry->spawnds[i] == NULL) {
557             continue;
558         }
559
560         struct spawn_binding *spb = entry->spawnds[i]->b;
561
562         struct pending_kill_cleanup *cmd = (struct pending_kill_cleanup*) malloc(
563                 sizeof(struct pending_kill_cleanup));
564         cmd->domain_cap = domain_cap;
565         cmd->entry = entry;
566         cmd->b = spb;
567
568         struct pending_client *cl = (struct pending_client*) malloc(
569                 sizeof(struct pending_client));
570         cl->b = b;
571         cl->type = type;
572         cl->st = cmd;
573
574         struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
575                 sizeof(struct msg_queue_elem));
576         msg->st = cl;
577         msg->cont = kill_request_sender;
578
579         err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
580         if (err_is_fail(err)) {
581             DEBUG_ERR(err, "enqueuing kill request");
582             free(cmd);
583             free(cl);
584             free(msg);
585         }
586     }
587
588     return SYS_ERR_OK;
589 }
590
591 /**
592  * \brief Handler for rpc kill.
593  */
594 static void kill_handler(struct proc_mgmt_binding *b,
595                          struct capref victim_domain_cap)
596 {
597     errval_t err = kill_handler_common(b, victim_domain_cap, ClientType_Kill,
598                                        EXIT_STATUS_KILLED);
599     if (err_is_fail(err)) {
600         errval_t resp_err = b->tx_vtbl.kill_response(b, NOP_CONT, err);
601         if (err_is_fail(resp_err)) {
602             DEBUG_ERR(resp_err, "failed to send kill_response");
603         }
604     }
605 }
606
607 /**
608  * \brief Handler for message exit.
609  */
610 static void exit_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
611                          uint8_t exit_status)
612 {
613     errval_t err = kill_handler_common(b, domain_cap, ClientType_Exit,
614                                        exit_status);
615     if (err_is_fail(err)) {
616         DEBUG_ERR(err, "processing exit_handler for requesting domain, exit "
617                   "code %u", exit_status);
618     }
619     // Error or not, there's no client to respond to anymore.
620 }
621
622 /**
623  * \brief Handler for rpc wait.
624  */
625 static void wait_handler(struct proc_mgmt_binding *b, struct capref domain_cap, bool nohang)
626 {
627     errval_t err, resp_err;
628     struct domain_entry *entry;
629     err = domain_get_by_cap(domain_cap, &entry);
630     if (err_is_fail(err)) {
631         goto respond;
632     }
633
634     if (entry->status == DOMAIN_STATUS_STOPPED) {
635         // Domain has already been stopped, so just reply with exit status.
636         goto respond;
637     }
638
639     if (nohang) {
640         entry->exit_status = -1;
641         goto respond;   
642     } 
643
644     struct domain_waiter *waiter = (struct domain_waiter*) malloc(
645             sizeof(struct domain_waiter));
646     waiter->b = b;
647     waiter->next = entry->waiters;
648     entry->waiters = waiter;
649     // Will respond when domain is stopped.
650     return;
651
652 respond:
653     resp_err = b->tx_vtbl.wait_response(b, NOP_CONT, err, entry->exit_status);
654     if (err_is_fail(resp_err)) {
655         DEBUG_ERR(resp_err, "failed to send wait_response");
656     }
657 }
658
659 /**
660  * \brief Handler for rpc get_domainlist.
661  */
662 static void get_domainlist_handler(struct proc_mgmt_binding *b)
663 {
664     errval_t resp_err;
665     size_t len;
666     domainid_t* domains;
667
668     domain_get_all_ids(&domains, &len);
669
670     // 4096 hardcoded limit in flounder interface
671     assert(sizeof(domainid_t)/sizeof(uint8_t)*len < 4096);
672
673     resp_err = b->tx_vtbl.get_domainlist_response(b, NOP_CONT, (uint8_t*) domains, 
674                                                   sizeof(domainid_t)/sizeof(uint8_t)*len);
675     if (err_is_fail(resp_err)) {
676         DEBUG_ERR(resp_err, "failed to send wait_response");
677     }
678 }
679
680 /**
681  * \brief Handler for rpc get_status.
682  */
683 static void get_status_handler(struct proc_mgmt_binding *b, domainid_t domain)
684 {
685     errval_t err;
686     struct domain_entry* entry;
687     proc_mgmt_ps_entry_t pse;
688     memset(&pse, 0, sizeof(pse));
689
690     err = domain_get_by_id(domain, &entry);
691     if (err_is_fail(err)) {
692         err = b->tx_vtbl.get_status_response(b, NOP_CONT, pse, NULL, 0,
693                                              err);
694         if(err_is_fail(err)) {
695             DEBUG_ERR(err, "status_response");
696         }
697     }
698
699     pse.status = entry->status;
700
701     err = b->tx_vtbl.get_status_response(b, NOP_CONT, pse, entry->argbuf, entry->argbytes,
702                                          SYS_ERR_OK);
703     if(err_is_fail(err)) {
704         DEBUG_ERR(err, "status_response");
705     }
706 }
707
708 static struct proc_mgmt_rx_vtbl monitor_vtbl = {
709     .add_spawnd           = add_spawnd_handler,
710     .spawn_call           = spawn_handler,
711     .spawn_with_caps_call = spawn_with_caps_handler,
712     .span_call            = span_handler,
713     .kill_call            = kill_handler,
714     .exit_call            = exit_handler,
715     .wait_call            = wait_handler
716 };
717
718 static struct proc_mgmt_rx_vtbl non_monitor_vtbl = {
719     .add_spawnd           = add_spawnd_handler_non_monitor,
720     .spawn_call           = spawn_handler,
721     .spawn_with_caps_call = spawn_with_caps_handler,
722     .span_call            = span_handler,
723     .kill_call            = kill_handler,
724     .exit_call            = exit_handler,
725     .wait_call            = wait_handler,
726     .get_domainlist_call  = get_domainlist_handler,
727     .get_status_call      = get_status_handler
728 };
729
730 /**
731  * \brief Allocates a special LMP endpoint for authenticating with the monitor.
732  */
733 static errval_t alloc_ep_for_monitor(struct capref *ep)
734 {
735     struct proc_mgmt_lmp_binding *lmpb =
736         malloc(sizeof(struct proc_mgmt_lmp_binding));
737     assert(lmpb != NULL);
738
739     // setup our end of the binding
740     errval_t err = proc_mgmt_client_lmp_accept(lmpb, get_default_waitset(),
741                                                DEFAULT_LMP_BUF_WORDS);
742     if (err_is_fail(err)) {
743         free(lmpb);
744         return err_push(err, LIB_ERR_PROC_MGMT_CLIENT_ACCEPT);
745     }
746
747     *ep = lmpb->chan.local_cap;
748     lmpb->b.rx_vtbl = monitor_vtbl;
749
750     return SYS_ERR_OK;
751 }
752
753 static void export_cb(void *st, errval_t err, iref_t iref)
754 {
755     if (err_is_fail(err)) {
756         USER_PANIC_ERR(err, "export failed");
757     }
758
759     // Allocate an endpoint for the local monitor, who will use it to inform
760     // us about new spawnd irefs on behalf of other monitors.
761     struct capref ep;
762     err = alloc_ep_for_monitor(&ep);
763     if (err_is_fail(err)) {
764         USER_PANIC_ERR(err, "failed to allocate LMP EP for local monitor");
765     }
766
767     // Send the endpoint to the monitor, so it can finish the handshake.
768     struct monitor_binding *mb = get_monitor_binding();
769     err = mb->tx_vtbl.set_proc_mgmt_ep_request(mb, NOP_CONT, ep);
770     if (err_is_fail(err)) {
771         USER_PANIC_ERR(err, "failed to send set_proc_mgmt_ep_request to "
772                        "monitor");
773     }
774
775     // Also register this iref with the name service, for arbitrary client
776     // domains to use for spawn-related ops.
777     err = nameservice_register(SERVICE_BASENAME, iref);
778     if (err_is_fail(err)) {
779         USER_PANIC_ERR(err, "nameservice_register failed");
780     }
781 }
782
783 static errval_t connect_cb(void *st, struct proc_mgmt_binding *b)
784 {
785     b->rx_vtbl = non_monitor_vtbl;
786     return SYS_ERR_OK;
787 }
788
789 errval_t start_service(void)
790 {
791     errval_t err = domain_prealloc_caps();
792     if (err_is_fail(err)) {
793         USER_PANIC_ERR(err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP),
794                        "domain_prealloc_caps in start_service");
795     }
796
797     return proc_mgmt_export(NULL, export_cb, connect_cb, get_default_waitset(),
798             IDC_EXPORT_FLAGS_DEFAULT);
799 }