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 void spawn_reply_handler(struct spawn_binding *b,
61 struct capref domain_cap, errval_t spawn_err)
63 struct pending_client *cl;
64 errval_t err = pending_clients_release(domain_cap, &cl);
65 if (err_is_fail(err)) {
66 // This might be a kill request issued after a successful spawn/span
67 // followed by a local error in the process manager (see below). If that
68 // is the case, then we won't have a client, as it has already been
70 debug_printf("Unable to retrieve pending client based on domain cap "
71 "returned by spawnd");
77 case ClientType_Spawn:
79 if (err_is_ok(spawn_err)) {
80 err = domain_spawn(domain_cap, cl->core_id);
82 resp_err = cl->b->tx_vtbl.spawn_response(cl->b, NOP_CONT, err,
86 case ClientType_SpawnWithCaps:
88 if (err_is_ok(spawn_err)) {
89 err = domain_spawn(domain_cap, cl->core_id);
91 resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b, NOP_CONT,
97 if (err_is_ok(spawn_err)) {
98 err = domain_span(domain_cap, cl->core_id);
100 resp_err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT, err);
104 // TODO(razvan): Handle the other cases, e.g. kill.
105 debug_printf("Unknown client type %u\n", cl->type);
109 if (err_is_ok(spawn_err) && err_is_fail(err)) {
110 // Spawnd has successfully completed its end of the operation, but
111 // there's been an error in the process manager's book-keeping
112 // of domains. Therefore, if the request was a spawn or span one, spawnd
113 // needs to be asked to stop the dispatcher which it has just enqueued.
114 if (cl->type == ClientType_Spawn ||
115 cl->type == ClientType_SpawnWithCaps ||
116 cl->type == ClientType_Span) {
117 struct spawnd_state *state = spawnd_state_get(cl->core_id);
118 assert(state != NULL);
119 struct spawn_binding *spb = state->b;
122 err = spb->tx_vtbl.kill_request(spb, NOP_CONT, domain_cap);
123 if (err_is_fail(err)) {
124 // XXX: How severe is this? Maybe we want something more
125 // assertive than logging an error message.
126 DEBUG_ERR(err, "failed to send kill request for dangling "
134 if (err_is_fail(resp_err)) {
135 DEBUG_ERR(resp_err, "failed to send response to client");
139 static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
140 enum ClientType type,
141 coreid_t core_id, const char *path,
142 const char *argvbuf, size_t argvbytes,
143 const char *envbuf, size_t envbytes,
144 struct capref inheritcn_cap,
145 struct capref argcn_cap, uint8_t flags,
146 struct capref *ret_domain_cap)
148 assert(ret_domain_cap != NULL);
150 if (!spawnd_state_exists(core_id)) {
151 return PROC_MGMT_ERR_INVALID_SPAWND;
154 struct spawnd_state *state = spawnd_state_get(core_id);
155 assert(state != NULL);
156 struct spawn_binding *cl = state->b;
159 struct capref domain_cap;
160 errval_t err = slot_alloc(&domain_cap);
161 if (err_is_fail(err)) {
162 DEBUG_ERR(err, "slot_alloc domain_cap");
163 return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
165 err = cap_retype(domain_cap, cap_procmng, 0, ObjType_Domain, 0, 1);
166 if (err_is_fail(err)) {
167 DEBUG_ERR(err, "cap_retype domain_cap");
168 return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
171 err = pending_clients_add(domain_cap, b, type, core_id);
172 if (err_is_fail(err)) {
173 DEBUG_ERR(err, "pending_clients_add");
177 cl->rx_vtbl.spawn_reply = spawn_reply_handler;
178 if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) {
179 err = cl->tx_vtbl.spawn_request(cl, NOP_CONT, domain_cap, path, argvbuf,
180 argvbytes, envbuf, envbytes, flags);
182 err = cl->tx_vtbl.spawn_with_caps_request(cl, NOP_CONT, domain_cap,
183 path, argvbuf, argvbytes,
185 inheritcn_cap, argcn_cap,
188 if (err_is_fail(err)) {
189 DEBUG_ERR(err, "sending spawn request");
190 pending_clients_release(domain_cap, NULL);
191 return err_push(err, PROC_MGMT_ERR_SPAWND_REQUEST);
197 static void spawn_handler(struct proc_mgmt_binding *b, coreid_t core_id,
198 const char *path, const char *argvbuf,
199 size_t argvbytes, const char *envbuf, size_t envbytes,
202 errval_t err, resp_err;
203 struct capref domain_cap;
204 err = spawn_handler_common(b, ClientType_Spawn, core_id, path, argvbuf,
205 argvbytes, envbuf, envbytes, NULL_CAP, NULL_CAP,
207 if (err_is_ok(err)) {
208 // Will respond to client when we get the reply from spawnd.
212 resp_err = b->tx_vtbl.spawn_response(b, NOP_CONT, err, NULL_CAP);
213 if (err_is_fail(resp_err)) {
214 DEBUG_ERR(resp_err, "failed to send spawn_response");
218 static void spawn_with_caps_handler(struct proc_mgmt_binding *b,
219 coreid_t core_id, const char *path,
220 const char *argvbuf, size_t argvbytes,
221 const char *envbuf, size_t envbytes,
222 struct capref inheritcn_cap,
223 struct capref argcn_cap, uint8_t flags)
225 errval_t err, resp_err;
226 struct capref domain_cap;
227 err = spawn_handler_common(b, ClientType_SpawnWithCaps, core_id, path,
228 argvbuf, argvbytes, envbuf, envbytes,
229 inheritcn_cap, argcn_cap, flags, &domain_cap);
230 if (err_is_ok(err)) {
231 // Will respond to client when we get the reply from spawnd.
235 resp_err = b->tx_vtbl.spawn_with_caps_response(b, NOP_CONT, err,
237 if (err_is_fail(resp_err)) {
238 DEBUG_ERR(resp_err, "failed to send spawn_with_caps_response");
242 static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
243 coreid_t core_id, struct capref vroot,
244 struct capref dispframe)
246 errval_t err, resp_err;
247 err = domain_can_span(domain_cap, core_id);
248 if (err_is_fail(err)) {
249 goto respond_with_err;
252 if (!spawnd_state_exists(core_id)) {
253 err = PROC_MGMT_ERR_INVALID_SPAWND;
254 goto respond_with_err;
257 struct spawnd_state *state = spawnd_state_get(core_id);
258 assert(state != NULL);
259 struct spawn_binding *cl = state->b;
262 err = pending_clients_add(domain_cap, b, ClientType_Span, core_id);
263 if (err_is_fail(err)) {
264 goto respond_with_err;
267 cl->rx_vtbl.spawn_reply = spawn_reply_handler;
268 err = cl->tx_vtbl.span_request(cl, NOP_CONT, domain_cap, vroot, dispframe);
269 if (err_is_ok(err)) {
270 // Will respond to client when we get the reply from spawnd.
273 DEBUG_ERR(err, "sending span request");
274 pending_clients_release(domain_cap, NULL);
275 err = err_push(err, PROC_MGMT_ERR_SPAWND_REQUEST);
279 resp_err = b->tx_vtbl.span_response(b, NOP_CONT, err);
280 if (err_is_fail(resp_err)) {
281 DEBUG_ERR(resp_err, "failed to send span_response");
285 static void kill_handler(struct proc_mgmt_binding *b, struct capref domain_cap)
287 struct domain_entry *entry;
288 errval_t err = domain_get_by_cap(domain_cap, &entry);
289 if (err_is_ok(err)) {
290 domain_send_stop(entry);
294 static struct proc_mgmt_rx_vtbl monitor_vtbl = {
295 .add_spawnd = add_spawnd_handler,
296 .spawn_call = spawn_handler,
297 .spawn_with_caps_call = spawn_with_caps_handler,
298 .span_call = span_handler,
299 .kill_call = kill_handler
302 static struct proc_mgmt_rx_vtbl non_monitor_vtbl = {
303 .add_spawnd = add_spawnd_handler_non_monitor,
304 .spawn_call = spawn_handler,
305 .spawn_with_caps_call = spawn_with_caps_handler,
306 .span_call = span_handler,
307 .kill_call = kill_handler
310 static errval_t alloc_ep_for_monitor(struct capref *ep)
312 struct proc_mgmt_lmp_binding *lmpb =
313 malloc(sizeof(struct proc_mgmt_lmp_binding));
314 assert(lmpb != NULL);
316 // setup our end of the binding
317 errval_t err = proc_mgmt_client_lmp_accept(lmpb, get_default_waitset(),
318 DEFAULT_LMP_BUF_WORDS);
319 if (err_is_fail(err)) {
321 return err_push(err, LIB_ERR_PROC_MGMT_CLIENT_ACCEPT);
324 *ep = lmpb->chan.local_cap;
325 lmpb->b.rx_vtbl = monitor_vtbl;
330 static void export_cb(void *st, errval_t err, iref_t iref)
332 if (err_is_fail(err)) {
333 USER_PANIC_ERR(err, "export failed");
336 // Allocate an endpoint for the local monitor, who will use it to inform
337 // us about new spawnd irefs on behalf of other monitors.
339 err = alloc_ep_for_monitor(&ep);
340 if (err_is_fail(err)) {
341 USER_PANIC_ERR(err, "failed to allocate LMP EP for local monitor");
344 // Send the endpoint to the monitor, so it can finish the handshake.
345 struct monitor_binding *mb = get_monitor_binding();
346 err = mb->tx_vtbl.set_proc_mgmt_ep_request(mb, NOP_CONT, ep);
347 if (err_is_fail(err)) {
348 USER_PANIC_ERR(err, "failed to send set_proc_mgmt_ep_request to "
352 // Also register this iref with the name service, for arbitrary client
353 // domains to use for spawn-related ops.
354 err = nameservice_register(SERVICE_BASENAME, iref);
355 if (err_is_fail(err)) {
356 USER_PANIC_ERR(err, "nameservice_register failed");
360 static errval_t connect_cb(void *st, struct proc_mgmt_binding *b)
362 b->rx_vtbl = non_monitor_vtbl;
366 errval_t start_service(void)
368 return proc_mgmt_export(NULL, export_cb, connect_cb, get_default_waitset(),
369 IDC_EXPORT_FLAGS_DEFAULT);