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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
15 #include <barrelfish/barrelfish.h>
16 #include <barrelfish/nameservice_client.h>
17 #include <barrelfish/proc_mgmt_client.h>
18 #include <barrelfish/spawn_client.h>
19 #include <if/monitor_defs.h>
20 #include <if/proc_mgmt_defs.h>
21 #include <if/spawn_defs.h>
25 #include "pending_clients.h"
26 #include "spawnd_state.h"
29 * \brief Handler for message add_spawnd, for the local monitor binding.
31 static void add_spawnd_handler(struct proc_mgmt_binding *b, coreid_t core_id,
34 if (spawnd_state_exists(core_id)) {
35 DEBUG_ERR(PROC_MGMT_ERR_SPAWND_EXISTS, "spawnd_state_exists");
39 // Bind with the spawnd.
40 struct spawn_binding *spawnb;
41 errval_t err = spawn_bind_iref(iref, &spawnb);
42 if (err_is_fail(err)) {
43 DEBUG_ERR(err, "spawn_bind_iref");
47 err = spawnd_state_alloc(core_id, spawnb);
48 if (err_is_fail(err)) {
49 DEBUG_ERR(err, "spawnd_state_alloc");
52 debug_printf("Process manager bound with spawnd.%u on iref %u\n", core_id,
57 * \brief Handler for message add_spawnd, for non-monitor bindings.
59 static void add_spawnd_handler_non_monitor(struct proc_mgmt_binding *b,
60 coreid_t core_id, iref_t iref)
62 // debug_printf("Ignoring add_spawnd call: %s\n",
63 // err_getstring(PROC_MGMT_ERR_NOT_MONITOR));
66 static bool cleanup_request_sender(struct msg_queue_elem *m);
69 * General-purpose handler for replies from spawnd.
71 static void spawn_reply_handler(struct spawn_binding *b, errval_t spawn_err)
73 struct pending_client *cl =
74 (struct pending_client*) spawnd_state_dequeue_recv(b->st);
76 struct pending_spawn *spawn = NULL;
77 struct pending_span *span = NULL;
78 struct pending_kill_cleanup *kc = NULL;
80 struct domain_entry *entry;
82 errval_t err, resp_err;
85 case ClientType_Spawn:
86 case ClientType_SpawnWithCaps:
87 spawn = (struct pending_spawn*) cl->st;
89 if (err_is_ok(spawn_err)) {
90 err = domain_spawn(spawn->cap_node, spawn->core_id);
91 if (cl->type == ClientType_Spawn) {
92 resp_err = cl->b->tx_vtbl.spawn_response(cl->b, NOP_CONT,
93 err, spawn->cap_node->domain_cap);
95 resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b,
96 NOP_CONT, err, spawn->cap_node->domain_cap);
103 case ClientType_Span:
104 span = (struct pending_span*) cl->st;
106 if (entry->status == DOMAIN_STATUS_RUNNING) {
107 resp_err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT,
114 case ClientType_Cleanup:
115 kc = (struct pending_kill_cleanup*) cl->st;
118 assert(entry->num_spawnds_resources > 0);
119 assert(entry->status != DOMAIN_STATUS_CLEANED);
121 --entry->num_spawnds_resources;
122 if (entry->num_spawnds_resources == 0) {
123 entry->status = DOMAIN_STATUS_CLEANED;
125 // At this point, the domain exists in state CLEANED for history
126 // reasons. For instance, if some other domain issues a wait
127 // call for this one, the process manager can return the exit
128 // status directly. At some point, however, we might want to
129 // just clean up the domain entry and recycle the domain cap.
135 case ClientType_Kill:
136 case ClientType_Exit:
137 kc = (struct pending_kill_cleanup*) cl->st;
140 assert(entry->num_spawnds_running > 0);
141 assert(entry->status != DOMAIN_STATUS_STOPPED);
143 --entry->num_spawnds_running;
145 if (entry->num_spawnds_running == 0) {
146 entry->status = DOMAIN_STATUS_STOPPED;
148 if (cl->type == ClientType_Kill) {
149 entry->exit_status = EXIT_STATUS_KILLED;
150 resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT,
154 struct domain_waiter *waiter = entry->waiters;
155 while (waiter != NULL) {
156 waiter->b->tx_vtbl.wait_response(waiter->b, NOP_CONT,
159 struct domain_waiter *tmp = waiter;
160 waiter = waiter->next;
164 for (coreid_t i = 0; i < MAX_COREID; ++i) {
165 if (entry->spawnds[i] == NULL) {
169 struct spawn_binding *spb = entry->spawnds[i]->b;
171 struct pending_kill_cleanup *cleanup =
172 (struct pending_kill_cleanup*) malloc(
173 sizeof(struct pending_kill_cleanup));
175 cleanup->domain_cap = kc->domain_cap;
176 cleanup->entry = entry;
178 struct pending_client *cleanup_cl =
179 (struct pending_client*) malloc(
180 sizeof(struct pending_client));
181 cleanup_cl->b = cl->b;
182 cleanup_cl->type = ClientType_Cleanup;
183 cleanup_cl->st = cleanup;
185 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
186 sizeof(struct msg_queue_elem));
187 msg->st = cleanup_cl;
188 msg->cont = cleanup_request_sender;
190 err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
192 if (err_is_fail(err)) {
193 DEBUG_ERR(err, "enqueuing cleanup request");
205 USER_PANIC("Unknown client type in spawn_reply_handler: %u\n",
213 * \brief Handler for sending spawn requests.
215 static bool spawn_request_sender(struct msg_queue_elem *m)
217 struct pending_client *cl = (struct pending_client*) m->st;
218 struct pending_spawn *spawn = (struct pending_spawn*) cl->st;
219 spawn->b->rx_vtbl.spawn_reply = spawn_reply_handler;
222 bool with_caps = !(capref_is_null(spawn->inheritcn_cap) &&
223 capref_is_null(spawn->argcn_cap));
225 err = spawn->b->tx_vtbl.spawn_with_caps_request(spawn->b, NOP_CONT,
227 spawn->cap_node->domain_cap,
233 spawn->inheritcn_cap,
237 err = spawn->b->tx_vtbl.spawn_request(spawn->b, NOP_CONT, cap_procmng,
238 spawn->cap_node->domain_cap,
239 spawn->path, spawn->argvbuf,
240 spawn->argvbytes, spawn->envbuf,
241 spawn->envbytes, spawn->flags);
244 if (err_is_fail(err)) {
245 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
248 USER_PANIC_ERR(err, "sending spawn request");
258 * \brief Handler for sending span requests.
260 static bool span_request_sender(struct msg_queue_elem *m)
262 struct pending_client *cl = (struct pending_client*) m->st;
263 struct pending_span *span = (struct pending_span*) cl->st;
266 span->b->rx_vtbl.spawn_reply = spawn_reply_handler;
267 err = span->b->tx_vtbl.span_request(span->b, NOP_CONT, cap_procmng,
268 span->domain_cap, span->vroot,
271 if (err_is_fail(err)) {
272 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
275 USER_PANIC_ERR(err, "sending span request");
285 * \brief Handler for sending kill requests.
287 static bool kill_request_sender(struct msg_queue_elem *m)
289 struct pending_client *cl = (struct pending_client*) m->st;
290 struct pending_kill_cleanup *kill = (struct pending_kill_cleanup*) cl->st;
293 kill->b->rx_vtbl.spawn_reply = spawn_reply_handler;
294 err = kill->b->tx_vtbl.kill_request(kill->b, NOP_CONT, cap_procmng,
297 if (err_is_fail(err)) {
298 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
301 USER_PANIC_ERR(err, "sending kill request");
311 * \brief Handler for sending cleanup requests.
313 static bool cleanup_request_sender(struct msg_queue_elem *m)
315 struct pending_client *cl = (struct pending_client*) m->st;
316 struct pending_kill_cleanup *cleanup = (struct pending_kill_cleanup*) cl->st;
319 cleanup->b->rx_vtbl.spawn_reply = spawn_reply_handler;
320 err = cleanup->b->tx_vtbl.cleanup_request(cleanup->b, NOP_CONT,
322 cleanup->domain_cap);
324 if (err_is_fail(err)) {
325 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
328 USER_PANIC_ERR(err, "sending cleanup request");
338 * \brief Common bits of the spawn and spawn_with_caps handlers.
340 static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
341 enum ClientType type,
342 coreid_t core_id, const char *path,
343 const char *argvbuf, size_t argvbytes,
344 const char *envbuf, size_t envbytes,
345 struct capref inheritcn_cap,
346 struct capref argcn_cap, uint8_t flags)
348 if (!spawnd_state_exists(core_id)) {
349 return PROC_MGMT_ERR_INVALID_SPAWND;
352 struct spawnd_state *spawnd = spawnd_state_get(core_id);
353 assert(spawnd != NULL);
354 struct spawn_binding *cl = spawnd->b;
358 if (domain_should_refill_caps()) {
359 err = domain_prealloc_caps();
360 if (err_is_fail(err)) {
361 return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
365 struct domain_cap_node *cap_node = next_cap_node();
367 struct pending_spawn *spawn = (struct pending_spawn*) malloc(
368 sizeof(struct pending_spawn));
369 spawn->cap_node = cap_node;
370 // spawn->domain_cap = domain_cap;
372 spawn->core_id = core_id;
374 spawn->argvbuf = argvbuf;
375 spawn->argvbytes = argvbytes;
376 spawn->envbuf = envbuf;
377 spawn->envbytes = envbytes;
378 spawn->inheritcn_cap = inheritcn_cap;
379 spawn->argcn_cap = argcn_cap;
380 spawn->flags = flags;
382 struct pending_client *spawn_cl = (struct pending_client*) malloc(
383 sizeof(struct pending_client));
385 spawn_cl->type = type;
386 spawn_cl->st = spawn;
388 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
389 sizeof(struct msg_queue_elem));
391 msg->cont = spawn_request_sender;
393 err = spawnd_state_enqueue_send(spawnd, msg);
394 if (err_is_fail(err)) {
395 DEBUG_ERR(err, "enqueuing spawn request");
405 * \brief Handler for rpc spawn.
407 static void spawn_handler(struct proc_mgmt_binding *b, coreid_t core_id,
408 const char *path, const char *argvbuf,
409 size_t argvbytes, const char *envbuf, size_t envbytes,
412 errval_t err, resp_err;
413 err = spawn_handler_common(b, ClientType_Spawn, core_id, path, argvbuf,
414 argvbytes, envbuf, envbytes, NULL_CAP, NULL_CAP,
417 if (err_is_fail(err)) {
418 resp_err = b->tx_vtbl.spawn_response(b, NOP_CONT, err, NULL_CAP);
419 if (err_is_fail(resp_err)) {
420 DEBUG_ERR(resp_err, "failed to send spawn_response");
426 * \brief Handler for rpc spawn_with_caps.
428 static void spawn_with_caps_handler(struct proc_mgmt_binding *b,
429 coreid_t core_id, const char *path,
430 const char *argvbuf, size_t argvbytes,
431 const char *envbuf, size_t envbytes,
432 struct capref inheritcn_cap,
433 struct capref argcn_cap, uint8_t flags)
435 errval_t err, resp_err;
436 err = spawn_handler_common(b, ClientType_SpawnWithCaps, core_id, path,
437 argvbuf, argvbytes, envbuf, envbytes,
438 inheritcn_cap, argcn_cap, flags);
439 if (err_is_ok(err)) {
440 // Will respond to client when we get the reply from spawnd.
444 resp_err = b->tx_vtbl.spawn_with_caps_response(b, NOP_CONT, err,
446 if (err_is_fail(resp_err)) {
447 DEBUG_ERR(resp_err, "failed to send spawn_with_caps_response");
452 * \brief Handler for rpc span.
454 static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
455 coreid_t core_id, struct capref vroot,
456 struct capref dispframe)
458 errval_t err, resp_err;
459 struct domain_entry *entry = NULL;
460 err = domain_get_by_cap(domain_cap, &entry);
461 if (err_is_fail(err)) {
462 goto respond_with_err;
465 assert(entry != NULL);
466 if (entry->status != DOMAIN_STATUS_RUNNING) {
467 err = PROC_MGMT_ERR_DOMAIN_NOT_RUNNING;
468 goto respond_with_err;
471 if (entry->spawnds[core_id] != NULL) {
472 // TODO(razvan): Maybe we want to allow the same domain to span multiple
473 // dispatchers onto the same core?
474 err = PROC_MGMT_ERR_ALREADY_SPANNED;
475 goto respond_with_err;
478 if (!spawnd_state_exists(core_id)) {
479 err = PROC_MGMT_ERR_INVALID_SPAWND;
480 goto respond_with_err;
483 struct spawnd_state *spawnd = spawnd_state_get(core_id);
484 assert(spawnd != NULL);
485 struct spawn_binding *cl = spawnd->b;
488 struct pending_span *span = (struct pending_span*) malloc(
489 sizeof(struct pending_span));
490 span->domain_cap = domain_cap;
493 span->core_id = core_id;
495 span->dispframe = dispframe;
497 struct pending_client *span_cl = (struct pending_client*) malloc(
498 sizeof(struct pending_client));
500 span_cl->type = ClientType_Span;
503 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
504 sizeof(struct msg_queue_elem));
506 msg->cont = span_request_sender;
508 err = spawnd_state_enqueue_send(spawnd, msg);
510 if (err_is_fail(err)) {
511 DEBUG_ERR(err, "enqueuing span request");
518 resp_err = b->tx_vtbl.span_response(b, NOP_CONT, err);
519 if (err_is_fail(resp_err)) {
520 DEBUG_ERR(resp_err, "failed to send span_response");
525 * \brief Common bits of the kill and exit handlers.
527 static errval_t kill_handler_common(struct proc_mgmt_binding *b,
528 struct capref domain_cap,
529 enum ClientType type,
532 struct domain_entry *entry;
533 errval_t err = domain_get_by_cap(domain_cap, &entry);
534 if (err_is_fail(err)) {
538 entry->exit_status = exit_status;
539 domain_stop_pending(entry);
541 for (coreid_t i = 0; i < MAX_COREID; ++i) {
542 if (entry->spawnds[i] == NULL) {
546 struct spawn_binding *spb = entry->spawnds[i]->b;
548 struct pending_kill_cleanup *cmd = (struct pending_kill_cleanup*) malloc(
549 sizeof(struct pending_kill_cleanup));
550 cmd->domain_cap = domain_cap;
554 struct pending_client *cl = (struct pending_client*) malloc(
555 sizeof(struct pending_client));
560 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
561 sizeof(struct msg_queue_elem));
563 msg->cont = kill_request_sender;
565 err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
566 if (err_is_fail(err)) {
567 DEBUG_ERR(err, "enqueuing kill request");
578 * \brief Handler for rpc kill.
580 static void kill_handler(struct proc_mgmt_binding *b,
581 struct capref victim_domain_cap)
583 errval_t err = kill_handler_common(b, victim_domain_cap, ClientType_Kill,
585 if (err_is_fail(err)) {
586 errval_t resp_err = b->tx_vtbl.kill_response(b, NOP_CONT, err);
587 if (err_is_fail(resp_err)) {
588 DEBUG_ERR(resp_err, "failed to send kill_response");
594 * \brief Handler for message exit.
596 static void exit_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
599 errval_t err = kill_handler_common(b, domain_cap, ClientType_Exit,
601 if (err_is_fail(err)) {
602 DEBUG_ERR(err, "processing exit_handler for requesting domain, exit "
603 "code %u", exit_status);
605 // Error or not, there's no client to respond to anymore.
609 * \brief Handler for rpc wait.
611 static void wait_handler(struct proc_mgmt_binding *b, struct capref domain_cap)
613 errval_t err, resp_err;
614 struct domain_entry *entry;
615 err = domain_get_by_cap(domain_cap, &entry);
616 if (err_is_fail(err)) {
620 if (entry->status == DOMAIN_STATUS_STOPPED) {
621 // Domain has already been stopped, so just reply with exit status.
625 struct domain_waiter *waiter = (struct domain_waiter*) malloc(
626 sizeof(struct domain_waiter));
628 waiter->next = entry->waiters;
629 entry->waiters = waiter;
630 // Will respond when domain is stopped.
634 resp_err = b->tx_vtbl.wait_response(b, NOP_CONT, err, entry->exit_status);
635 if (err_is_fail(resp_err)) {
636 DEBUG_ERR(resp_err, "failed to send wait_response");
640 static struct proc_mgmt_rx_vtbl monitor_vtbl = {
641 .add_spawnd = add_spawnd_handler,
642 .spawn_call = spawn_handler,
643 .spawn_with_caps_call = spawn_with_caps_handler,
644 .span_call = span_handler,
645 .kill_call = kill_handler,
646 .exit_call = exit_handler,
647 .wait_call = wait_handler
650 static struct proc_mgmt_rx_vtbl non_monitor_vtbl = {
651 .add_spawnd = add_spawnd_handler_non_monitor,
652 .spawn_call = spawn_handler,
653 .spawn_with_caps_call = spawn_with_caps_handler,
654 .span_call = span_handler,
655 .kill_call = kill_handler,
656 .exit_call = exit_handler,
657 .wait_call = wait_handler
661 * \brief Allocates a special LMP endpoint for authenticating with the monitor.
663 static errval_t alloc_ep_for_monitor(struct capref *ep)
665 struct proc_mgmt_lmp_binding *lmpb =
666 malloc(sizeof(struct proc_mgmt_lmp_binding));
667 assert(lmpb != NULL);
669 // setup our end of the binding
670 errval_t err = proc_mgmt_client_lmp_accept(lmpb, get_default_waitset(),
671 DEFAULT_LMP_BUF_WORDS);
672 if (err_is_fail(err)) {
674 return err_push(err, LIB_ERR_PROC_MGMT_CLIENT_ACCEPT);
677 *ep = lmpb->chan.local_cap;
678 lmpb->b.rx_vtbl = monitor_vtbl;
683 static void export_cb(void *st, errval_t err, iref_t iref)
685 if (err_is_fail(err)) {
686 USER_PANIC_ERR(err, "export failed");
689 // Allocate an endpoint for the local monitor, who will use it to inform
690 // us about new spawnd irefs on behalf of other monitors.
692 err = alloc_ep_for_monitor(&ep);
693 if (err_is_fail(err)) {
694 USER_PANIC_ERR(err, "failed to allocate LMP EP for local monitor");
697 // Send the endpoint to the monitor, so it can finish the handshake.
698 struct monitor_binding *mb = get_monitor_binding();
699 err = mb->tx_vtbl.set_proc_mgmt_ep_request(mb, NOP_CONT, ep);
700 if (err_is_fail(err)) {
701 USER_PANIC_ERR(err, "failed to send set_proc_mgmt_ep_request to "
705 // Also register this iref with the name service, for arbitrary client
706 // domains to use for spawn-related ops.
707 err = nameservice_register(SERVICE_BASENAME, iref);
708 if (err_is_fail(err)) {
709 USER_PANIC_ERR(err, "nameservice_register failed");
713 static errval_t connect_cb(void *st, struct proc_mgmt_binding *b)
715 b->rx_vtbl = non_monitor_vtbl;
719 errval_t start_service(void)
721 errval_t err = domain_prealloc_caps();
722 if (err_is_fail(err)) {
723 USER_PANIC_ERR(err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP),
724 "domain_prealloc_caps in start_service");
727 return proc_mgmt_export(NULL, export_cb, connect_cb, get_default_waitset(),
728 IDC_EXPORT_FLAGS_DEFAULT);