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"
28 static void add_spawnd_handler(struct proc_mgmt_binding *b, coreid_t core_id,
31 if (spawnd_state_exists(core_id)) {
32 DEBUG_ERR(PROC_MGMT_ERR_SPAWND_EXISTS, "spawnd_state_exists");
36 // Bind with the spawnd.
37 struct spawn_binding *spawnb;
38 errval_t err = spawn_bind_iref(iref, &spawnb);
39 if (err_is_fail(err)) {
40 DEBUG_ERR(err, "spawn_bind_iref");
44 err = spawnd_state_alloc(core_id, spawnb);
45 if (err_is_fail(err)) {
46 DEBUG_ERR(err, "spawnd_state_alloc");
49 debug_printf("Process manager bound with spawnd.%u on iref %u\n", core_id,
53 static void add_spawnd_handler_non_monitor(struct proc_mgmt_binding *b,
54 coreid_t core_id, iref_t iref)
56 // debug_printf("Ignoring add_spawnd call: %s\n",
57 // err_getstring(PROC_MGMT_ERR_NOT_MONITOR));
60 static bool cleanup_request_sender(struct msg_queue_elem *m);
62 static void spawn_reply_handler(struct spawn_binding *b, errval_t spawn_err)
64 struct pending_client *cl =
65 (struct pending_client*) spawnd_state_dequeue_recv(b->st);
67 struct pending_spawn *spawn = NULL;
68 struct pending_span *span = NULL;
69 struct pending_kill_cleanup *kc = NULL;
71 struct domain_entry *entry;
73 errval_t err, resp_err;
76 case ClientType_Spawn:
77 case ClientType_SpawnWithCaps:
78 spawn = (struct pending_spawn*) cl->st;
80 if (err_is_ok(spawn_err)) {
81 err = domain_spawn(spawn->domain_cap, spawn->core_id);
82 if (cl->type == ClientType_Spawn) {
83 resp_err = cl->b->tx_vtbl.spawn_response(cl->b, NOP_CONT,
87 resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b,
98 span = (struct pending_span*) cl->st;
100 if (entry->status == DOMAIN_STATUS_RUNNING) {
101 resp_err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT,
108 case ClientType_Cleanup:
109 kc = (struct pending_kill_cleanup*) cl->st;
112 assert(entry->num_spawnds_resources > 0);
113 assert(entry->status != DOMAIN_STATUS_CLEANED);
115 --entry->num_spawnds_resources;
116 if (entry->num_spawnds_resources == 0) {
117 entry->status = DOMAIN_STATUS_CLEANED;
119 // At this point, the domain exists in state CLEANED for history
120 // reasons. For instance, if some other domain issues a wait
121 // call for this one, the process manager can return the exit
122 // status directly. At some point, however, we might want to
123 // just clean up the domain entry and recycle the domain cap.
129 case ClientType_Kill:
130 case ClientType_Exit:
131 kc = (struct pending_kill_cleanup*) cl->st;
134 assert(entry->num_spawnds_running > 0);
135 assert(entry->status != DOMAIN_STATUS_STOPPED);
137 --entry->num_spawnds_running;
139 if (entry->num_spawnds_running == 0) {
140 entry->status = DOMAIN_STATUS_STOPPED;
142 if (cl->type == ClientType_Kill) {
143 entry->exit_status = EXIT_STATUS_KILLED;
144 resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT,
148 struct domain_waiter *waiter = entry->waiters;
149 while (waiter != NULL) {
150 waiter->b->tx_vtbl.wait_response(waiter->b, NOP_CONT,
153 struct domain_waiter *tmp = waiter;
154 waiter = waiter->next;
158 for (coreid_t i = 0; i < MAX_COREID; ++i) {
159 if (entry->spawnds[i] == NULL) {
163 struct spawn_binding *spb = entry->spawnds[i]->b;
165 struct pending_kill_cleanup *cleanup =
166 (struct pending_kill_cleanup*) malloc(
167 sizeof(struct pending_kill_cleanup));
169 cleanup->domain_cap = kc->domain_cap;
170 cleanup->entry = entry;
172 struct pending_client *cleanup_cl =
173 (struct pending_client*) malloc(
174 sizeof(struct pending_client));
175 cleanup_cl->b = cl->b;
176 cleanup_cl->type = ClientType_Cleanup;
177 cleanup_cl->st = cleanup;
179 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
180 sizeof(struct msg_queue_elem));
181 msg->st = cleanup_cl;
182 msg->cont = cleanup_request_sender;
184 err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
186 if (err_is_fail(err)) {
187 DEBUG_ERR(err, "enqueuing cleanup request");
199 USER_PANIC("Unknown client type in spawn_reply_handler: %u\n",
206 static bool spawn_request_sender(struct msg_queue_elem *m)
208 struct pending_client *cl = (struct pending_client*) m->st;
209 struct pending_spawn *spawn = (struct pending_spawn*) cl->st;
210 spawn->b->rx_vtbl.spawn_reply = spawn_reply_handler;
213 bool with_caps = !(capref_is_null(spawn->inheritcn_cap) &&
214 capref_is_null(spawn->argcn_cap));
216 err = spawn->b->tx_vtbl.spawn_with_caps_request(spawn->b, NOP_CONT,
224 spawn->inheritcn_cap,
228 err = spawn->b->tx_vtbl.spawn_request(spawn->b, NOP_CONT, cap_procmng,
229 spawn->domain_cap, spawn->path,
230 spawn->argvbuf, spawn->argvbytes,
231 spawn->envbuf, spawn->envbytes,
235 if (err_is_fail(err)) {
236 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
239 USER_PANIC_ERR(err, "sending spawn request");
248 static bool span_request_sender(struct msg_queue_elem *m)
250 struct pending_client *cl = (struct pending_client*) m->st;
251 struct pending_span *span = (struct pending_span*) cl->st;
254 span->b->rx_vtbl.spawn_reply = spawn_reply_handler;
255 err = span->b->tx_vtbl.span_request(span->b, NOP_CONT, cap_procmng,
256 span->domain_cap, span->vroot,
259 if (err_is_fail(err)) {
260 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
263 USER_PANIC_ERR(err, "sending span request");
272 static bool kill_request_sender(struct msg_queue_elem *m)
274 struct pending_client *cl = (struct pending_client*) m->st;
275 struct pending_kill_cleanup *kill = (struct pending_kill_cleanup*) cl->st;
278 kill->b->rx_vtbl.spawn_reply = spawn_reply_handler;
279 err = kill->b->tx_vtbl.kill_request(kill->b, NOP_CONT, cap_procmng,
282 if (err_is_fail(err)) {
283 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
286 USER_PANIC_ERR(err, "sending kill request");
295 static bool cleanup_request_sender(struct msg_queue_elem *m)
297 struct pending_client *cl = (struct pending_client*) m->st;
298 struct pending_kill_cleanup *cleanup = (struct pending_kill_cleanup*) cl->st;
301 cleanup->b->rx_vtbl.spawn_reply = spawn_reply_handler;
302 err = cleanup->b->tx_vtbl.cleanup_request(cleanup->b, NOP_CONT,
304 cleanup->domain_cap);
306 if (err_is_fail(err)) {
307 if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
310 USER_PANIC_ERR(err, "sending cleanup request");
319 static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
320 enum ClientType type,
321 coreid_t core_id, const char *path,
322 const char *argvbuf, size_t argvbytes,
323 const char *envbuf, size_t envbytes,
324 struct capref inheritcn_cap,
325 struct capref argcn_cap, uint8_t flags)
327 if (!spawnd_state_exists(core_id)) {
328 return PROC_MGMT_ERR_INVALID_SPAWND;
331 struct spawnd_state *spawnd = spawnd_state_get(core_id);
332 assert(spawnd != NULL);
333 struct spawn_binding *cl = spawnd->b;
336 struct capref domain_cap;
337 errval_t err = slot_alloc(&domain_cap);
338 if (err_is_fail(err)) {
339 DEBUG_ERR(err, "slot_alloc domain_cap");
340 return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
342 err = cap_retype(domain_cap, cap_procmng, 0, ObjType_Domain, 0, 1);
343 if (err_is_fail(err)) {
344 DEBUG_ERR(err, "cap_retype domain_cap");
345 return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
348 struct pending_spawn *spawn = (struct pending_spawn*) malloc(
349 sizeof(struct pending_spawn));
350 spawn->domain_cap = domain_cap;
352 spawn->core_id = core_id;
354 spawn->argvbuf = argvbuf;
355 spawn->argvbytes = argvbytes;
356 spawn->envbuf = envbuf;
357 spawn->envbytes = envbytes;
358 spawn->inheritcn_cap = inheritcn_cap;
359 spawn->argcn_cap = argcn_cap;
360 spawn->flags = flags;
362 struct pending_client *spawn_cl = (struct pending_client*) malloc(
363 sizeof(struct pending_client));
365 spawn_cl->type = type;
366 spawn_cl->st = spawn;
368 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
369 sizeof(struct msg_queue_elem));
371 msg->cont = spawn_request_sender;
373 err = spawnd_state_enqueue_send(spawnd, msg);
375 if (err_is_fail(err)) {
376 DEBUG_ERR(err, "enqueuing spawn request");
385 static void spawn_handler(struct proc_mgmt_binding *b, coreid_t core_id,
386 const char *path, const char *argvbuf,
387 size_t argvbytes, const char *envbuf, size_t envbytes,
390 errval_t err, resp_err;
391 err = spawn_handler_common(b, ClientType_Spawn, core_id, path, argvbuf,
392 argvbytes, envbuf, envbytes, NULL_CAP, NULL_CAP,
394 if (err_is_ok(err)) {
395 // Will respond to client when we get the reply from spawnd.
399 resp_err = b->tx_vtbl.spawn_response(b, NOP_CONT, err, NULL_CAP);
400 if (err_is_fail(resp_err)) {
401 DEBUG_ERR(resp_err, "failed to send spawn_response");
405 static void spawn_with_caps_handler(struct proc_mgmt_binding *b,
406 coreid_t core_id, const char *path,
407 const char *argvbuf, size_t argvbytes,
408 const char *envbuf, size_t envbytes,
409 struct capref inheritcn_cap,
410 struct capref argcn_cap, uint8_t flags)
412 errval_t err, resp_err;
413 err = spawn_handler_common(b, ClientType_SpawnWithCaps, core_id, path,
414 argvbuf, argvbytes, envbuf, envbytes,
415 inheritcn_cap, argcn_cap, flags);
416 if (err_is_ok(err)) {
417 // Will respond to client when we get the reply from spawnd.
421 resp_err = b->tx_vtbl.spawn_with_caps_response(b, NOP_CONT, err,
423 if (err_is_fail(resp_err)) {
424 DEBUG_ERR(resp_err, "failed to send spawn_with_caps_response");
428 static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
429 coreid_t core_id, struct capref vroot,
430 struct capref dispframe)
432 errval_t err, resp_err;
433 struct domain_entry *entry = NULL;
434 err = domain_get_by_cap(domain_cap, &entry);
435 if (err_is_fail(err)) {
436 goto respond_with_err;
439 assert(entry != NULL);
440 if (entry->status != DOMAIN_STATUS_RUNNING) {
441 err = PROC_MGMT_ERR_DOMAIN_NOT_RUNNING;
442 goto respond_with_err;
445 if (entry->spawnds[core_id] != NULL) {
446 // TODO(razvan): Maybe we want to allow the same domain to span multiple
447 // dispatchers onto the same core?
448 err = PROC_MGMT_ERR_ALREADY_SPANNED;
449 goto respond_with_err;
452 if (!spawnd_state_exists(core_id)) {
453 err = PROC_MGMT_ERR_INVALID_SPAWND;
454 goto respond_with_err;
457 struct spawnd_state *spawnd = spawnd_state_get(core_id);
458 assert(spawnd != NULL);
459 struct spawn_binding *cl = spawnd->b;
462 struct pending_span *span = (struct pending_span*) malloc(
463 sizeof(struct pending_span));
464 span->domain_cap = domain_cap;
467 span->core_id = core_id;
469 span->dispframe = dispframe;
471 struct pending_client *span_cl = (struct pending_client*) malloc(
472 sizeof(struct pending_client));
474 span_cl->type = ClientType_Span;
477 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
478 sizeof(struct msg_queue_elem));
480 msg->cont = span_request_sender;
482 err = spawnd_state_enqueue_send(spawnd, msg);
484 if (err_is_fail(err)) {
485 DEBUG_ERR(err, "enqueuing span request");
492 resp_err = b->tx_vtbl.span_response(b, NOP_CONT, err);
493 if (err_is_fail(resp_err)) {
494 DEBUG_ERR(resp_err, "failed to send span_response");
498 static errval_t kill_handler_common(struct proc_mgmt_binding *b,
499 struct capref domain_cap,
500 enum ClientType type,
503 struct domain_entry *entry;
504 errval_t err = domain_get_by_cap(domain_cap, &entry);
505 if (err_is_fail(err)) {
509 entry->exit_status = exit_status;
510 domain_stop_pending(entry);
512 for (coreid_t i = 0; i < MAX_COREID; ++i) {
513 if (entry->spawnds[i] == NULL) {
517 struct spawn_binding *spb = entry->spawnds[i]->b;
519 struct pending_kill_cleanup *cmd = (struct pending_kill_cleanup*) malloc(
520 sizeof(struct pending_kill_cleanup));
521 cmd->domain_cap = domain_cap;
525 struct pending_client *cl = (struct pending_client*) malloc(
526 sizeof(struct pending_client));
531 struct msg_queue_elem *msg = (struct msg_queue_elem*) malloc(
532 sizeof(struct msg_queue_elem));
534 msg->cont = kill_request_sender;
536 err = spawnd_state_enqueue_send(entry->spawnds[i], msg);
537 if (err_is_fail(err)) {
538 DEBUG_ERR(err, "enqueuing kill request");
548 static void kill_handler(struct proc_mgmt_binding *b,
549 struct capref victim_domain_cap)
551 errval_t err = kill_handler_common(b, victim_domain_cap, ClientType_Kill,
553 if (err_is_fail(err)) {
554 errval_t resp_err = b->tx_vtbl.kill_response(b, NOP_CONT, err);
555 if (err_is_fail(resp_err)) {
556 DEBUG_ERR(resp_err, "failed to send kill_response");
561 static void exit_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
564 errval_t err = kill_handler_common(b, domain_cap, ClientType_Exit,
566 if (err_is_fail(err)) {
567 DEBUG_ERR(err, "processing exit_handler for requesting domain, exit "
568 "code %u", exit_status);
570 // Error or not, there's no client to respond to anymore.
573 static void wait_handler(struct proc_mgmt_binding *b, struct capref domain_cap)
575 errval_t err, resp_err;
576 struct domain_entry *entry;
577 err = domain_get_by_cap(domain_cap, &entry);
578 if (err_is_fail(err)) {
582 if (entry->status == DOMAIN_STATUS_STOPPED) {
583 // Domain has already been stopped, so just reply with exit status.
587 struct domain_waiter *waiter = (struct domain_waiter*) malloc(
588 sizeof(struct domain_waiter));
590 waiter->next = entry->waiters;
591 entry->waiters = waiter;
592 // Will respond when domain is stopped.
596 resp_err = b->tx_vtbl.wait_response(b, NOP_CONT, err, entry->exit_status);
597 if (err_is_fail(resp_err)) {
598 DEBUG_ERR(resp_err, "failed to send wait_response");
602 static struct proc_mgmt_rx_vtbl monitor_vtbl = {
603 .add_spawnd = add_spawnd_handler,
604 .spawn_call = spawn_handler,
605 .spawn_with_caps_call = spawn_with_caps_handler,
606 .span_call = span_handler,
607 .kill_call = kill_handler,
608 .exit_call = exit_handler,
609 .wait_call = wait_handler
612 static struct proc_mgmt_rx_vtbl non_monitor_vtbl = {
613 .add_spawnd = add_spawnd_handler_non_monitor,
614 .spawn_call = spawn_handler,
615 .spawn_with_caps_call = spawn_with_caps_handler,
616 .span_call = span_handler,
617 .kill_call = kill_handler,
618 .exit_call = exit_handler,
619 .wait_call = wait_handler
622 static errval_t alloc_ep_for_monitor(struct capref *ep)
624 struct proc_mgmt_lmp_binding *lmpb =
625 malloc(sizeof(struct proc_mgmt_lmp_binding));
626 assert(lmpb != NULL);
628 // setup our end of the binding
629 errval_t err = proc_mgmt_client_lmp_accept(lmpb, get_default_waitset(),
630 DEFAULT_LMP_BUF_WORDS);
631 if (err_is_fail(err)) {
633 return err_push(err, LIB_ERR_PROC_MGMT_CLIENT_ACCEPT);
636 *ep = lmpb->chan.local_cap;
637 lmpb->b.rx_vtbl = monitor_vtbl;
642 static void export_cb(void *st, errval_t err, iref_t iref)
644 if (err_is_fail(err)) {
645 USER_PANIC_ERR(err, "export failed");
648 // Allocate an endpoint for the local monitor, who will use it to inform
649 // us about new spawnd irefs on behalf of other monitors.
651 err = alloc_ep_for_monitor(&ep);
652 if (err_is_fail(err)) {
653 USER_PANIC_ERR(err, "failed to allocate LMP EP for local monitor");
656 // Send the endpoint to the monitor, so it can finish the handshake.
657 struct monitor_binding *mb = get_monitor_binding();
658 err = mb->tx_vtbl.set_proc_mgmt_ep_request(mb, NOP_CONT, ep);
659 if (err_is_fail(err)) {
660 USER_PANIC_ERR(err, "failed to send set_proc_mgmt_ep_request to "
664 // Also register this iref with the name service, for arbitrary client
665 // domains to use for spawn-related ops.
666 err = nameservice_register(SERVICE_BASENAME, iref);
667 if (err_is_fail(err)) {
668 USER_PANIC_ERR(err, "nameservice_register failed");
672 static errval_t connect_cb(void *st, struct proc_mgmt_binding *b)
674 b->rx_vtbl = non_monitor_vtbl;
678 errval_t start_service(void)
680 return proc_mgmt_export(NULL, export_cb, connect_cb, get_default_waitset(),
681 IDC_EXPORT_FLAGS_DEFAULT);