Implement kill() and exit() in the process manager.
[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_ERR(err, "failed to retrieve pending client based on domain cap "
71                   "returned by spawnd");
72         return;
73     }
74
75     errval_t resp_err = SYS_ERR_OK;
76     struct domain_entry *entry;
77     switch (cl->type) {
78         case ClientType_Spawn:
79             err = spawn_err;
80             if (err_is_ok(spawn_err)) {
81                 err = domain_spawn(domain_cap, cl->core_id);
82             }
83             resp_err = cl->b->tx_vtbl.spawn_response(cl->b, NOP_CONT, err,
84                                                      domain_cap);
85             break;
86
87         case ClientType_SpawnWithCaps:
88             err = spawn_err;
89             if (err_is_ok(spawn_err)) {
90                 err = domain_spawn(domain_cap, cl->core_id);
91             }
92             resp_err = cl->b->tx_vtbl.spawn_with_caps_response(cl->b, NOP_CONT,
93                                                                err, domain_cap);
94             break;
95
96         case ClientType_Span:
97             err = spawn_err;
98             if (err_is_ok(spawn_err)) {
99                 err = domain_span(domain_cap, cl->core_id);
100             }
101             resp_err = cl->b->tx_vtbl.span_response(cl->b, NOP_CONT, err);
102             break;
103
104         case ClientType_Kill:
105                 if (err_is_fail(spawn_err)) {
106                     // Looks like some spawnd was unable to successfully kill
107                     // its dispatcher for this domain. Not much the process
108                     // manager can do about it; return the error to the client.
109                     resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT,
110                                                             err);
111                     break;
112                 }
113
114                 err = domain_get_by_cap(domain_cap, &entry);
115                 if (err_is_fail(err)) {
116                     DEBUG_ERR(err, "failed to retrieve domain by domain_cap "
117                               "returned by spawnd after kill");
118                     break;
119                 }
120
121                 assert(entry->num_spawnds_running > 0);
122                 assert(entry->status != DOMAIN_STATUS_STOPPED);
123
124                 --entry->num_spawnds_running;
125                 if (entry->num_spawnds_running == 0) {
126                     entry->status = DOMAIN_STATUS_STOPPED;
127                     entry->exit_status = EXIT_STATUS_KILLED;  // TODO(razvan): Is this desirable?
128
129                     resp_err = cl->b->tx_vtbl.kill_response(cl->b, NOP_CONT,
130                                                             err);
131
132                     // At this point, the domain exists in state STOPPED for
133                     // history reasons.
134                     // TODO(razvan): This is where we will inform waiters.
135                     break;
136                 }
137
138             break;
139
140         case ClientType_Exit:
141             if (err_is_fail(spawn_err)) {
142                 // Looks like some spawnd was unable to successfully kill
143                 // its dispatcher for this domain. Not much the process
144                 // manager can do about it. Furthermore, this was an exit call,
145                 // so there's no client to reply back to.
146                 break;
147             }
148
149             err = domain_get_by_cap(domain_cap, &entry);
150             if (err_is_fail(err)) {
151                 DEBUG_ERR(err, "failed to retrieve domain by domain_cap "
152                           "returned by spawnd after kill");
153                 break;
154             }
155
156             assert(entry->num_spawnds_running > 0);
157             assert(entry->status != DOMAIN_STATUS_STOPPED);
158
159             --entry->num_spawnds_running;
160             if (entry->num_spawnds_running == 0) {
161                 entry->status = DOMAIN_STATUS_STOPPED;
162
163                 // At this point, the domain exists in state STOPPED for
164                 // history reasons.
165                 // TODO(razvan): This is where we will inform waiters.
166                 break;
167             }
168
169         break;
170
171         default:
172             // TODO(razvan): Handle the other cases, e.g. wait.
173             debug_printf("Unknown client type %u\n", cl->type);
174             return;
175     }
176
177     if (err_is_ok(spawn_err) && err_is_fail(err)) {
178         // Spawnd has successfully completed its end of the operation, but
179         // there's been an error in the process manager's book-keeping
180         // of domains. Therefore, if the request was a spawn or span one, spawnd
181         // needs to be asked to stop the dispatcher which it has just enqueued.
182         if (cl->type == ClientType_Spawn ||
183             cl->type == ClientType_SpawnWithCaps ||
184             cl->type == ClientType_Span) {
185             struct spawnd_state *state = spawnd_state_get(cl->core_id);
186             assert(state != NULL);
187             struct spawn_binding *spb = state->b;
188             assert(spb != NULL);
189
190             err = spb->tx_vtbl.kill_request(spb, NOP_CONT, domain_cap);
191             if (err_is_fail(err)) {
192                 // XXX: How severe is this? Maybe we want something more
193                 // assertive than logging an error message.
194                 DEBUG_ERR(err, "failed to send kill request for dangling "
195                           "dispatcher");
196             }
197         }
198     }
199
200     free(cl);
201
202     if (err_is_fail(resp_err)) {
203         DEBUG_ERR(resp_err, "failed to send response to client");
204     }
205 }
206
207 static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
208                                      enum ClientType type,
209                                      coreid_t core_id, const char *path,
210                                      const char *argvbuf, size_t argvbytes,
211                                      const char *envbuf, size_t envbytes,
212                                      struct capref inheritcn_cap,
213                                      struct capref argcn_cap, uint8_t flags,
214                                      struct capref *ret_domain_cap)
215 {
216     assert(ret_domain_cap != NULL);
217
218     if (!spawnd_state_exists(core_id)) {
219         return PROC_MGMT_ERR_INVALID_SPAWND;
220     }
221
222     struct spawnd_state *state = spawnd_state_get(core_id);
223     assert(state != NULL);
224     struct spawn_binding *cl = state->b;
225     assert(cl != NULL);
226
227     struct capref domain_cap;
228     errval_t err = slot_alloc(&domain_cap);
229     if (err_is_fail(err)) {
230         DEBUG_ERR(err, "slot_alloc domain_cap");
231         return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
232     }
233     err = cap_retype(domain_cap, cap_procmng, 0, ObjType_Domain, 0, 1);
234     if (err_is_fail(err)) {
235         DEBUG_ERR(err, "cap_retype domain_cap");
236         return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
237     }
238
239     err = pending_clients_add(domain_cap, b, type, core_id);
240     if (err_is_fail(err)) {
241         DEBUG_ERR(err, "pending_clients_add");
242         return err;
243     }
244
245     cl->rx_vtbl.spawn_reply = spawn_reply_handler;
246     if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) {
247         err = cl->tx_vtbl.spawn_request(cl, NOP_CONT, domain_cap, path, argvbuf,
248                                         argvbytes, envbuf, envbytes, flags);
249     } else {
250         err = cl->tx_vtbl.spawn_with_caps_request(cl, NOP_CONT, domain_cap,
251                                                   path, argvbuf, argvbytes,
252                                                   envbuf, envbytes,
253                                                   inheritcn_cap, argcn_cap,
254                                                   flags);
255     }
256     if (err_is_fail(err)) {
257         DEBUG_ERR(err, "sending spawn request");
258         pending_clients_release(domain_cap, NULL);
259         return err_push(err, PROC_MGMT_ERR_SPAWND_REQUEST);
260     }
261
262     return SYS_ERR_OK;
263 }
264
265 static void spawn_handler(struct proc_mgmt_binding *b, coreid_t core_id,
266                           const char *path, const char *argvbuf,
267                           size_t argvbytes, const char *envbuf, size_t envbytes,
268                           uint8_t flags)
269 {
270     errval_t err, resp_err;
271     struct capref domain_cap;
272     err = spawn_handler_common(b, ClientType_Spawn, core_id, path, argvbuf,
273                                argvbytes, envbuf, envbytes, NULL_CAP, NULL_CAP,
274                                flags, &domain_cap);
275     if (err_is_ok(err)) {
276         // Will respond to client when we get the reply from spawnd.
277         return;
278     }
279
280     resp_err = b->tx_vtbl.spawn_response(b, NOP_CONT, err, NULL_CAP);
281     if (err_is_fail(resp_err)) {
282         DEBUG_ERR(resp_err, "failed to send spawn_response");
283     }
284 }
285
286 static void spawn_with_caps_handler(struct proc_mgmt_binding *b,
287                                     coreid_t core_id, const char *path,
288                                     const char *argvbuf, size_t argvbytes,
289                                     const char *envbuf, size_t envbytes,
290                                     struct capref inheritcn_cap,
291                                     struct capref argcn_cap, uint8_t flags)
292 {
293     errval_t err, resp_err;
294     struct capref domain_cap;
295     err = spawn_handler_common(b, ClientType_SpawnWithCaps, core_id, path,
296                                argvbuf, argvbytes, envbuf, envbytes,
297                                inheritcn_cap, argcn_cap, flags, &domain_cap);
298     if (err_is_ok(err)) {
299         // Will respond to client when we get the reply from spawnd.
300         return;
301     }
302
303     resp_err = b->tx_vtbl.spawn_with_caps_response(b, NOP_CONT, err,
304                                                             NULL_CAP);
305     if (err_is_fail(resp_err)) {
306         DEBUG_ERR(resp_err, "failed to send spawn_with_caps_response");
307     }
308 }
309
310 static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
311                          coreid_t core_id, struct capref vroot,
312                          struct capref dispframe)
313 {
314     errval_t err, resp_err;
315     err = domain_can_span(domain_cap, core_id);
316     if (err_is_fail(err)) {
317         goto respond_with_err;
318     }
319
320     if (!spawnd_state_exists(core_id)) {
321         err = PROC_MGMT_ERR_INVALID_SPAWND;
322         goto respond_with_err;
323     }
324
325     struct spawnd_state *state = spawnd_state_get(core_id);
326     assert(state != NULL);
327     struct spawn_binding *cl = state->b;
328     assert(cl != NULL);
329
330     err = pending_clients_add(domain_cap, b, ClientType_Span, core_id);
331     if (err_is_fail(err)) {
332         goto respond_with_err;
333     }
334
335     cl->rx_vtbl.spawn_reply = spawn_reply_handler;
336     err = cl->tx_vtbl.span_request(cl, NOP_CONT, domain_cap, vroot, dispframe);
337     if (err_is_ok(err)) {
338         // Will respond to client when we get the reply from spawnd.
339         return;
340     } else {
341         DEBUG_ERR(err, "sending span request");
342         pending_clients_release(domain_cap, NULL);
343         err = err_push(err, PROC_MGMT_ERR_SPAWND_REQUEST);
344     }
345
346 respond_with_err:
347     resp_err = b->tx_vtbl.span_response(b, NOP_CONT, err);
348     if (err_is_fail(resp_err)) {
349         DEBUG_ERR(resp_err, "failed to send span_response");
350     }
351 }
352
353 static errval_t kill_handler_common(struct proc_mgmt_binding *b,
354                                     struct capref domain_cap,
355                                     enum ClientType type,
356                                     uint8_t exit_status)
357 {
358     errval_t err = pending_clients_add(domain_cap, b, type, MAX_COREID);
359     if (err_is_fail(err)) {
360         return err;
361     }
362
363     struct domain_entry *entry;
364     err = domain_get_by_cap(domain_cap, &entry);
365     if (err_is_fail(err)) {
366         return err;
367     }
368
369     entry->exit_status = exit_status;
370     domain_stop_pending(entry);
371
372     for (coreid_t i = 0; i < MAX_COREID; ++i) {
373         if (entry->spawnds[i] == NULL) {
374             continue;
375         }
376
377         struct spawn_binding *spb = entry->spawnds[i]->b;
378         errval_t req_err = spb->tx_vtbl.kill_request(spb, NOP_CONT, domain_cap);
379         if (err_is_fail(req_err)) {
380             DEBUG_ERR(req_err, "failed to send kill_request to spawnd %u\n", i);
381         }
382     }
383
384     return SYS_ERR_OK;
385 }
386
387 static void kill_handler(struct proc_mgmt_binding *b, struct capref domain_cap)
388 {
389     errval_t err = kill_handler_common(b, domain_cap, ClientType_Kill,
390                                        EXIT_STATUS_KILLED);
391     if (err_is_fail(err)) {
392         errval_t resp_err = b->tx_vtbl.kill_response(b, NOP_CONT, err);
393         if (err_is_fail(resp_err)) {
394             DEBUG_ERR(resp_err, "failed to send kill_response");
395         }
396     }
397 }
398
399 static void exit_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
400                          uint8_t exit_status)
401 {
402     errval_t err = kill_handler_common(b, domain_cap, ClientType_Exit,
403                                        exit_status);
404     if (err_is_fail(err)) {
405         DEBUG_ERR(err, "processing exit_handler for requesting domain, exit "
406                   "code %u", exit_status);
407     }
408     // Error or not, there's no client to reply to anymore.
409 }
410
411 static struct proc_mgmt_rx_vtbl monitor_vtbl = {
412     .add_spawnd           = add_spawnd_handler,
413     .spawn_call           = spawn_handler,
414     .spawn_with_caps_call = spawn_with_caps_handler,
415     .span_call            = span_handler,
416     .kill_call            = kill_handler,
417     .exit                 = exit_handler
418 };
419
420 static struct proc_mgmt_rx_vtbl non_monitor_vtbl = {
421     .add_spawnd           = add_spawnd_handler_non_monitor,
422     .spawn_call           = spawn_handler,
423     .spawn_with_caps_call = spawn_with_caps_handler,
424     .span_call            = span_handler,
425     .kill_call            = kill_handler,
426     .exit                 = exit_handler
427 };
428
429 static errval_t alloc_ep_for_monitor(struct capref *ep)
430 {
431     struct proc_mgmt_lmp_binding *lmpb =
432         malloc(sizeof(struct proc_mgmt_lmp_binding));
433     assert(lmpb != NULL);
434
435     // setup our end of the binding
436     errval_t err = proc_mgmt_client_lmp_accept(lmpb, get_default_waitset(),
437                                                DEFAULT_LMP_BUF_WORDS);
438     if (err_is_fail(err)) {
439         free(lmpb);
440         return err_push(err, LIB_ERR_PROC_MGMT_CLIENT_ACCEPT);
441     }
442
443     *ep = lmpb->chan.local_cap;
444     lmpb->b.rx_vtbl = monitor_vtbl;
445
446     return SYS_ERR_OK;
447 }
448
449 static void export_cb(void *st, errval_t err, iref_t iref)
450 {
451     if (err_is_fail(err)) {
452         USER_PANIC_ERR(err, "export failed");
453     }
454
455     // Allocate an endpoint for the local monitor, who will use it to inform
456     // us about new spawnd irefs on behalf of other monitors.
457     struct capref ep;
458     err = alloc_ep_for_monitor(&ep);
459     if (err_is_fail(err)) {
460         USER_PANIC_ERR(err, "failed to allocate LMP EP for local monitor");
461     }
462
463     // Send the endpoint to the monitor, so it can finish the handshake.
464     struct monitor_binding *mb = get_monitor_binding();
465     err = mb->tx_vtbl.set_proc_mgmt_ep_request(mb, NOP_CONT, ep);
466     if (err_is_fail(err)) {
467         USER_PANIC_ERR(err, "failed to send set_proc_mgmt_ep_request to "
468                        "monitor");
469     }
470
471     // Also register this iref with the name service, for arbitrary client
472     // domains to use for spawn-related ops.
473     err = nameservice_register(SERVICE_BASENAME, iref);
474     if (err_is_fail(err)) {
475         USER_PANIC_ERR(err, "nameservice_register failed");
476     }
477 }
478
479 static errval_t connect_cb(void *st, struct proc_mgmt_binding *b)
480 {
481     b->rx_vtbl = non_monitor_vtbl;
482     return SYS_ERR_OK;
483 }
484
485 errval_t start_service(void)
486 {
487     return proc_mgmt_export(NULL, export_cb, connect_cb, get_default_waitset(),
488             IDC_EXPORT_FLAGS_DEFAULT);
489 }