Implement kill_request_handler in spawnd, using domain caps.
[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, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
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>
22
23 #include "domain.h"
24 #include "internal.h"
25 #include "pending_clients.h"
26 #include "spawnd_state.h"
27
28 static void add_spawnd_handler(struct proc_mgmt_binding *b, coreid_t core_id,
29                                iref_t iref)
30 {
31     if (spawnd_state_exists(core_id)) {
32         DEBUG_ERR(PROC_MGMT_ERR_SPAWND_EXISTS, "spawnd_state_exists");
33         return;
34     }
35
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");
41         return;
42     }
43
44     err = spawnd_state_alloc(core_id, spawnb);
45     if (err_is_fail(err)) {
46         DEBUG_ERR(err, "spawnd_state_alloc");
47     }
48
49     debug_printf("Process manager bound with spawnd.%u on iref %u\n", core_id,
50             iref);
51 }
52
53 static void add_spawnd_handler_non_monitor(struct proc_mgmt_binding *b,
54                                            coreid_t core_id, iref_t iref)
55 {
56     debug_printf("Ignoring add_spawnd call: %s\n",
57                  err_getstring(PROC_MGMT_ERR_NOT_MONITOR));
58 }
59
60 static void spawn_reply_handler(struct spawn_binding *b,
61                                 struct capref domain_cap, errval_t spawn_err)
62 {
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
69         // released.
70         debug_printf("Unable to retrieve pending client based on domain cap "
71                      "returned by spawnd");
72         return;
73     }
74
75     errval_t resp_err;
76     switch (cl->type) {
77         case ClientType_Spawn:
78             err = spawn_err;
79             if (err_is_ok(spawn_err)) {
80                 err = domain_spawn(domain_cap, cl->core_id);
81             }
82             resp_err = cl->b->tx_vtbl.spawn_response(cl->b, NOP_CONT, err,
83                                                      domain_cap);
84             break;
85
86         case ClientType_SpawnWithCaps:
87             err = spawn_err;
88             if (err_is_ok(spawn_err)) {
89                 err = domain_spawn(domain_cap, cl->core_id);
90             }
91             resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b, NOP_CONT,
92                                                                err, domain_cap);
93             break;
94
95         case ClientType_Span:
96             err = spawn_err;
97             if (err_is_ok(spawn_err)) {
98                 err = domain_span(domain_cap, cl->core_id);
99             }
100             resp_err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT, err);
101             break;
102
103         default:
104             // TODO(razvan): Handle the other cases, e.g. kill.
105             debug_printf("Unknown client type %u\n", cl->type);
106             return;
107     }
108
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;
120             assert(spb != NULL);
121
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 "
127                           "dispatcher");
128             }
129         }
130     }
131
132     free(cl);
133
134     if (err_is_fail(resp_err)) {
135         DEBUG_ERR(resp_err, "failed to send response to client");
136     }
137 }
138
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)
147 {
148     assert(ret_domain_cap != NULL);
149
150     if (!spawnd_state_exists(core_id)) {
151         return PROC_MGMT_ERR_INVALID_SPAWND;
152     }
153
154     struct spawnd_state *state = spawnd_state_get(core_id);
155     assert(state != NULL);
156     struct spawn_binding *cl = state->b;
157     assert(cl != NULL);
158
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);
164     }
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);
169     }
170
171     err = pending_clients_add(domain_cap, b, type, core_id);
172     if (err_is_fail(err)) {
173         DEBUG_ERR(err, "pending_clients_add");
174         return err;
175     }
176
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);
181     } else {
182         err = cl->tx_vtbl.spawn_with_caps_request(cl, NOP_CONT, domain_cap,
183                                                   path, argvbuf, argvbytes,
184                                                   envbuf, envbytes,
185                                                   inheritcn_cap, argcn_cap,
186                                                   flags);
187     }
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);
192     }
193
194     return SYS_ERR_OK;
195 }
196
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,
200                           uint8_t flags)
201 {
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,
206                                flags, &domain_cap);
207     if (err_is_ok(err)) {
208         // Will respond to client when we get the reply from spawnd.
209         return;
210     }
211
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");
215     }
216 }
217
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)
224 {
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.
232         return;
233     }
234
235     resp_err = b->tx_vtbl.spawn_with_caps_response(b, NOP_CONT, err,
236                                                             NULL_CAP);
237     if (err_is_fail(resp_err)) {
238         DEBUG_ERR(resp_err, "failed to send spawn_with_caps_response");
239     }
240 }
241
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)
245 {
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;
250     }
251
252     if (!spawnd_state_exists(core_id)) {
253         err = PROC_MGMT_ERR_INVALID_SPAWND;
254         goto respond_with_err;
255     }
256
257     struct spawnd_state *state = spawnd_state_get(core_id);
258     assert(state != NULL);
259     struct spawn_binding *cl = state->b;
260     assert(cl != NULL);
261
262     err = pending_clients_add(domain_cap, b, ClientType_Span, core_id);
263     if (err_is_fail(err)) {
264         goto respond_with_err;
265     }
266
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.
271         return;
272     } else {
273         DEBUG_ERR(err, "sending span request");
274         pending_clients_release(domain_cap, NULL);
275         err = err_push(err, PROC_MGMT_ERR_SPAWND_REQUEST);
276     }
277
278 respond_with_err:
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");
282     }
283 }
284
285 static void kill_handler(struct proc_mgmt_binding *b, struct capref domain_cap)
286 {
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);
291     }
292 }
293
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
300 };
301
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
308 };
309
310 static errval_t alloc_ep_for_monitor(struct capref *ep)
311 {
312     struct proc_mgmt_lmp_binding *lmpb =
313         malloc(sizeof(struct proc_mgmt_lmp_binding));
314     assert(lmpb != NULL);
315
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)) {
320         free(lmpb);
321         return err_push(err, LIB_ERR_PROC_MGMT_CLIENT_ACCEPT);
322     }
323
324     *ep = lmpb->chan.local_cap;
325     lmpb->b.rx_vtbl = monitor_vtbl;
326
327     return SYS_ERR_OK;
328 }
329
330 static void export_cb(void *st, errval_t err, iref_t iref)
331 {
332     if (err_is_fail(err)) {
333         USER_PANIC_ERR(err, "export failed");
334     }
335
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.
338     struct capref ep;
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");
342     }
343
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 "
349                        "monitor");
350     }
351
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");
357     }
358 }
359
360 static errval_t connect_cb(void *st, struct proc_mgmt_binding *b)
361 {
362     b->rx_vtbl = non_monitor_vtbl;
363     return SYS_ERR_OK;
364 }
365
366 errval_t start_service(void)
367 {
368     return proc_mgmt_export(NULL, export_cb, connect_cb, get_default_waitset(),
369             IDC_EXPORT_FLAGS_DEFAULT);
370 }