3 * \brief Process management service.
7 * Copyright (c) 2017, ETH Zurich.
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.
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>
22 #include <dist/barrier.h>
26 #include "pending_clients.h"
27 #include "spawnd_state.h"
30 * \brief Handler for message add_spawnd, for the local monitor binding.
32 static void add_spawnd_handler(struct proc_mgmt_binding *b, coreid_t core_id,
35 if (spawnd_state_exists(core_id)) {
36 DEBUG_ERR(PROC_MGMT_ERR_SPAWND_EXISTS, "spawnd_state_exists");
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");
48 err = spawnd_state_alloc(core_id, spawnb);
49 if (err_is_fail(err)) {
50 DEBUG_ERR(err, "spawnd_state_alloc");
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");
61 * \brief Handler for message add_spawnd, for non-monitor bindings.
63 static void add_spawnd_handler_non_monitor(struct proc_mgmt_binding *b,
64 coreid_t core_id, iref_t iref)
66 // debug_printf("Ignoring add_spawnd call: %s\n",
67 // err_getstring(PROC_MGMT_ERR_NOT_MONITOR));
70 static bool cleanup_request_sender(struct msg_queue_elem *m);
73 * General-purpose handler for replies from spawnd.
75 static void spawn_reply_handler(struct spawn_binding *b, errval_t spawn_err)
77 struct pending_client *cl =
78 (struct pending_client*) spawnd_state_dequeue_recv(b->st);
80 /* there's no pending client, return */
85 struct pending_spawn *spawn = NULL;
86 struct pending_span *span = NULL;
87 struct pending_kill_cleanup *kc = NULL;
89 struct domain_entry *entry;
91 errval_t err, resp_err;
94 case ClientType_Spawn:
95 case ClientType_SpawnWithCaps:
96 spawn = (struct pending_spawn*) cl->st;
98 if (err_is_ok(spawn_err)) {
99 err = domain_spawn(spawn->cap_node, spawn->core_id, spawn->argvbuf,
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);
106 resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b,
107 NOP_CONT, err, spawn->cap_node->domain_cap);
113 case ClientType_Span:
114 span = (struct pending_span*) cl->st;
116 if (entry->status == DOMAIN_STATUS_RUNNING) {
117 resp_err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT,
124 case ClientType_Cleanup:
125 kc = (struct pending_kill_cleanup*) cl->st;
128 assert(entry->num_spawnds_resources > 0);
129 assert(entry->status != DOMAIN_STATUS_CLEANED);
131 --entry->num_spawnds_resources;
132 if (entry->num_spawnds_resources == 0) {
133 entry->status = DOMAIN_STATUS_CLEANED;
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.
145 case ClientType_Kill:
146 case ClientType_Exit:
147 kc = (struct pending_kill_cleanup*) cl->st;
150 assert(entry->num_spawnds_running > 0);
151 assert(entry->status != DOMAIN_STATUS_STOPPED);
153 --entry->num_spawnds_running;
155 if (entry->num_spawnds_running == 0) {
156 entry->status = DOMAIN_STATUS_STOPPED;
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,
164 struct domain_waiter *waiter = entry->waiters;
165 while (waiter != NULL) {
166 waiter->b->tx_vtbl.wait_response(waiter->b, NOP_CONT,
169 struct domain_waiter *tmp = waiter;
170 waiter = waiter->next;
174 for (coreid_t i = 0; i < MAX_COREID; ++i) {
175 if (entry->spawnds[i] == NULL) {
179 struct spawn_binding *spb = entry->spawnds[i]->b;
181 struct pending_kill_cleanup *cleanup =
182 (struct pending_kill_cleanup*) malloc(
183 sizeof(struct pending_kill_cleanup));
185 cleanup->domain_cap = kc->domain_cap;
186 cleanup->entry = entry;
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;
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;
200 err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
202 if (err_is_fail(err)) {
203 DEBUG_ERR(err, "enqueuing cleanup request");
215 USER_PANIC("Unknown client type in spawn_reply_handler: %u\n",
223 * \brief Handler for sending spawn requests.
225 static bool spawn_request_sender(struct msg_queue_elem *m)
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;
232 bool with_caps = !(capref_is_null(spawn->inheritcn_cap) &&
233 capref_is_null(spawn->argcn_cap));
235 err = spawn->b->tx_vtbl.spawn_with_caps_request(spawn->b, NOP_CONT,
237 spawn->cap_node->domain_cap,
243 spawn->inheritcn_cap,
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);
254 if (err_is_fail(err)) {
255 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
258 USER_PANIC_ERR(err, "sending spawn request");
268 * \brief Handler for sending span requests.
270 static bool span_request_sender(struct msg_queue_elem *m)
272 struct pending_client *cl = (struct pending_client*) m->st;
273 struct pending_span *span = (struct pending_span*) cl->st;
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,
281 if (err_is_fail(err)) {
282 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
285 USER_PANIC_ERR(err, "sending span request");
295 * \brief Handler for sending kill requests.
297 static bool kill_request_sender(struct msg_queue_elem *m)
299 struct pending_client *cl = (struct pending_client*) m->st;
300 struct pending_kill_cleanup *kill = (struct pending_kill_cleanup*) cl->st;
303 kill->b->rx_vtbl.spawn_reply = spawn_reply_handler;
304 err = kill->b->tx_vtbl.kill_request(kill->b, NOP_CONT, cap_procmng,
307 if (err_is_fail(err)) {
308 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
311 USER_PANIC_ERR(err, "sending kill request");
321 * \brief Handler for sending cleanup requests.
323 static bool cleanup_request_sender(struct msg_queue_elem *m)
325 struct pending_client *cl = (struct pending_client*) m->st;
326 struct pending_kill_cleanup *cleanup = (struct pending_kill_cleanup*) cl->st;
329 cleanup->b->rx_vtbl.spawn_reply = spawn_reply_handler;
330 err = cleanup->b->tx_vtbl.cleanup_request(cleanup->b, NOP_CONT,
332 cleanup->domain_cap);
334 if (err_is_fail(err)) {
335 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
338 USER_PANIC_ERR(err, "sending cleanup request");
348 * \brief Common bits of the spawn and spawn_with_caps handlers.
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)
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
363 return PROC_MGMT_ERR_INVALID_SPAWND;
366 struct spawnd_state *spawnd = spawnd_state_get(core_id);
367 assert(spawnd != NULL);
368 struct spawn_binding *cl = spawnd->b;
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);
379 struct domain_cap_node *cap_node = next_cap_node();
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;
386 spawn->core_id = core_id;
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;
396 struct pending_client *spawn_cl = (struct pending_client*) malloc(
397 sizeof(struct pending_client));
399 spawn_cl->type = type;
400 spawn_cl->st = spawn;
402 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
403 sizeof(struct msg_queue_elem));
405 msg->cont = spawn_request_sender;
407 err = spawnd_state_enqueue_send(spawnd, msg);
408 if (err_is_fail(err)) {
409 DEBUG_ERR(err, "enqueuing spawn request");
419 * \brief Handler for rpc spawn.
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,
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,
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");
440 * \brief Handler for rpc spawn_with_caps.
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)
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.
458 resp_err = b->tx_vtbl.spawn_with_caps_response(b, NOP_CONT, err,
460 if (err_is_fail(resp_err)) {
461 DEBUG_ERR(resp_err, "failed to send spawn_with_caps_response");
466 * \brief Handler for rpc span.
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)
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;
479 assert(entry != NULL);
480 if (entry->status != DOMAIN_STATUS_RUNNING) {
481 err = PROC_MGMT_ERR_DOMAIN_NOT_RUNNING;
482 goto respond_with_err;
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;
492 if (!spawnd_state_exists(core_id)) {
493 err = PROC_MGMT_ERR_INVALID_SPAWND;
494 goto respond_with_err;
497 struct spawnd_state *spawnd = spawnd_state_get(core_id);
498 assert(spawnd != NULL);
499 struct spawn_binding *cl = spawnd->b;
502 struct pending_span *span = (struct pending_span*) malloc(
503 sizeof(struct pending_span));
504 span->domain_cap = domain_cap;
507 span->core_id = core_id;
509 span->dispframe = dispframe;
511 struct pending_client *span_cl = (struct pending_client*) malloc(
512 sizeof(struct pending_client));
514 span_cl->type = ClientType_Span;
517 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
518 sizeof(struct msg_queue_elem));
520 msg->cont = span_request_sender;
522 err = spawnd_state_enqueue_send(spawnd, msg);
524 if (err_is_fail(err)) {
525 DEBUG_ERR(err, "enqueuing span request");
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");
539 * \brief Common bits of the kill and exit handlers.
541 static errval_t kill_handler_common(struct proc_mgmt_binding *b,
542 struct capref domain_cap,
543 enum ClientType type,
546 struct domain_entry *entry;
547 errval_t err = domain_get_by_cap(domain_cap, &entry);
548 if (err_is_fail(err)) {
552 entry->exit_status = exit_status;
553 domain_stop_pending(entry);
555 for (coreid_t i = 0; i < MAX_COREID; ++i) {
556 if (entry->spawnds[i] == NULL) {
560 struct spawn_binding *spb = entry->spawnds[i]->b;
562 struct pending_kill_cleanup *cmd = (struct pending_kill_cleanup*) malloc(
563 sizeof(struct pending_kill_cleanup));
564 cmd->domain_cap = domain_cap;
568 struct pending_client *cl = (struct pending_client*) malloc(
569 sizeof(struct pending_client));
574 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
575 sizeof(struct msg_queue_elem));
577 msg->cont = kill_request_sender;
579 err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
580 if (err_is_fail(err)) {
581 DEBUG_ERR(err, "enqueuing kill request");
592 * \brief Handler for rpc kill.
594 static void kill_handler(struct proc_mgmt_binding *b,
595 struct capref victim_domain_cap)
597 errval_t err = kill_handler_common(b, victim_domain_cap, ClientType_Kill,
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");
608 * \brief Handler for message exit.
610 static void exit_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
613 errval_t err = kill_handler_common(b, domain_cap, ClientType_Exit,
615 if (err_is_fail(err)) {
616 DEBUG_ERR(err, "processing exit_handler for requesting domain, exit "
617 "code %u", exit_status);
619 // Error or not, there's no client to respond to anymore.
623 * \brief Handler for rpc wait.
625 static void wait_handler(struct proc_mgmt_binding *b, struct capref domain_cap, bool nohang)
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)) {
634 if (entry->status == DOMAIN_STATUS_STOPPED) {
635 // Domain has already been stopped, so just reply with exit status.
640 entry->exit_status = -1;
644 struct domain_waiter *waiter = (struct domain_waiter*) malloc(
645 sizeof(struct domain_waiter));
647 waiter->next = entry->waiters;
648 entry->waiters = waiter;
649 // Will respond when domain is stopped.
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");
660 * \brief Handler for rpc get_domainlist.
662 static void get_domainlist_handler(struct proc_mgmt_binding *b)
668 domain_get_all_ids(&domains, &len);
670 // 4096 hardcoded limit in flounder interface
671 assert(sizeof(domainid_t)/sizeof(uint8_t)*len < 4096);
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");
681 * \brief Handler for rpc get_status.
683 static void get_status_handler(struct proc_mgmt_binding *b, domainid_t domain)
686 struct domain_entry* entry;
687 proc_mgmt_ps_entry_t pse;
688 memset(&pse, 0, sizeof(pse));
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,
694 if(err_is_fail(err)) {
695 DEBUG_ERR(err, "status_response");
699 pse.status = entry->status;
701 err = b->tx_vtbl.get_status_response(b, NOP_CONT, pse, entry->argbuf, entry->argbytes,
703 if(err_is_fail(err)) {
704 DEBUG_ERR(err, "status_response");
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
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
731 * \brief Allocates a special LMP endpoint for authenticating with the monitor.
733 static errval_t alloc_ep_for_monitor(struct capref *ep)
735 struct proc_mgmt_lmp_binding *lmpb =
736 malloc(sizeof(struct proc_mgmt_lmp_binding));
737 assert(lmpb != NULL);
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)) {
744 return err_push(err, LIB_ERR_PROC_MGMT_CLIENT_ACCEPT);
747 *ep = lmpb->chan.local_cap;
748 lmpb->b.rx_vtbl = monitor_vtbl;
753 static void export_cb(void *st, errval_t err, iref_t iref)
755 if (err_is_fail(err)) {
756 USER_PANIC_ERR(err, "export failed");
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.
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");
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 "
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");
783 static errval_t connect_cb(void *st, struct proc_mgmt_binding *b)
785 b->rx_vtbl = non_monitor_vtbl;
789 errval_t start_service(void)
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");
797 return proc_mgmt_export(NULL, export_cb, connect_cb, get_default_waitset(),
798 IDC_EXPORT_FLAGS_DEFAULT);