563658ef26a045606f39a600e20d34ad8275d185
[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. For instance, if some other domain
134                 // issues a wait call for this one, the process manager can
135                 // return the exit status directly.
136                 // At some point, however, we might want to just clean up
137                 // the domain entry and recycle the domain cap.
138                 struct domain_waiter *waiter = entry->waiters;
139                 while (waiter != NULL) {
140                     waiter->b->tx_vtbl.wait_response(waiter->b,
141                                                      NOP_CONT,
142                                                      SYS_ERR_OK,
143                                                      entry->exit_status);
144                     struct domain_waiter *aux = waiter;
145                     waiter = waiter->next;
146                     free(aux);
147                 }
148                 break;
149             } else {
150                 // Expecting to receive further kill replies from other spawnds
151                 // for the same domain cap, hence re-add the pending client.
152                 err = pending_clients_add(domain_cap, cl->b, ClientType_Kill,
153                                           MAX_COREID);
154                 if (err_is_fail(err)) {
155                 DEBUG_ERR(err, "pending_clients_add in kill reply handler");
156                 }
157             }
158
159             break;
160
161         case ClientType_Exit:
162             if (err_is_fail(spawn_err)) {
163                 // Looks like some spawnd was unable to successfully kill
164                 // its dispatcher for this domain. Not much the process
165                 // manager can do about it. Furthermore, this was an exit call,
166                 // so there's no client to reply back to.
167                 break;
168             }
169
170             err = domain_get_by_cap(domain_cap, &entry);
171             if (err_is_fail(err)) {
172                 DEBUG_ERR(err, "failed to retrieve domain by domain_cap "
173                           "returned by spawnd after kill");
174                 break;
175             }
176
177             assert(entry->num_spawnds_running > 0);
178             assert(entry->status != DOMAIN_STATUS_STOPPED);
179
180             --entry->num_spawnds_running;
181             if (entry->num_spawnds_running == 0) {
182                 entry->status = DOMAIN_STATUS_STOPPED;
183
184                 // At this point, the domain exists in state STOPPED for
185                 // history reasons. For instance, if some other domain
186                 // issues a wait call for this one, the process manager can
187                 // return the exit status directly.
188                 // At some point, however, we might want to just clean up
189                 // the domain entry and recycle the domain cap.
190                 struct domain_waiter *waiter = entry->waiters;
191                 while (waiter != NULL) {
192                     waiter->b->tx_vtbl.wait_response(waiter->b,
193                                                      NOP_CONT,
194                                                      SYS_ERR_OK,
195                                                      entry->exit_status);
196                     struct domain_waiter *aux = waiter;
197                     waiter = waiter->next;
198                     free(aux);
199                 }
200                 break;
201             } else {
202                 // Expecting to receive further kill replies from other spawnds
203                 // for the same domain cap, hence re-add the pending client.
204                 err = pending_clients_add(domain_cap, cl->b, ClientType_Exit,
205                                           MAX_COREID);
206                 if (err_is_fail(err)) {
207                 DEBUG_ERR(err, "pending_clients_add in exit reply handler");
208                 }
209             }
210
211         break;
212
213         default:
214             // TODO(razvan): Handle the other cases, e.g. wait.
215             debug_printf("Unknown client type %u\n", cl->type);
216             return;
217     }
218
219     if (err_is_ok(spawn_err) && err_is_fail(err)) {
220         // Spawnd has successfully completed its end of the operation, but
221         // there's been an error in the process manager's book-keeping
222         // of domains. Therefore, if the request was a spawn or span one, spawnd
223         // needs to be asked to stop the dispatcher which it has just enqueued.
224         if (cl->type == ClientType_Spawn ||
225             cl->type == ClientType_SpawnWithCaps ||
226             cl->type == ClientType_Span) {
227             struct spawnd_state *state = spawnd_state_get(cl->core_id);
228             assert(state != NULL);
229             struct spawn_binding *spb = state->b;
230             assert(spb != NULL);
231
232             err = spb->tx_vtbl.kill_request(spb, NOP_CONT, cap_procmng, 
233                                             domain_cap);
234             if (err_is_fail(err)) {
235                 // XXX: How severe is this? Maybe we want something more
236                 // assertive than logging an error message.
237                 DEBUG_ERR(err, "failed to send kill request for dangling "
238                           "dispatcher");
239             }
240         }
241     }
242
243     free(cl);
244
245     if (err_is_fail(resp_err)) {
246         DEBUG_ERR(resp_err, "failed to send response to client");
247     }
248 }
249
250 static errval_t spawn_handler_common(struct proc_mgmt_binding *b,
251                                      enum ClientType type,
252                                      coreid_t core_id, const char *path,
253                                      const char *argvbuf, size_t argvbytes,
254                                      const char *envbuf, size_t envbytes,
255                                      struct capref inheritcn_cap,
256                                      struct capref argcn_cap, uint8_t flags,
257                                      struct capref *ret_domain_cap)
258 {
259     assert(ret_domain_cap != NULL);
260
261     if (!spawnd_state_exists(core_id)) {
262         return PROC_MGMT_ERR_INVALID_SPAWND;
263     }
264
265     struct spawnd_state *state = spawnd_state_get(core_id);
266     assert(state != NULL);
267     struct spawn_binding *cl = state->b;
268     assert(cl != NULL);
269
270     struct capref domain_cap;
271     errval_t err = slot_alloc(&domain_cap);
272     if (err_is_fail(err)) {
273         DEBUG_ERR(err, "slot_alloc domain_cap");
274         return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
275     }
276     err = cap_retype(domain_cap, cap_procmng, 0, ObjType_Domain, 0, 1);
277     if (err_is_fail(err)) {
278         DEBUG_ERR(err, "cap_retype domain_cap");
279         return err_push(err, PROC_MGMT_ERR_CREATE_DOMAIN_CAP);
280     }
281
282     err = pending_clients_add(domain_cap, b, type, core_id);
283     if (err_is_fail(err)) {
284         DEBUG_ERR(err, "pending_clients_add");
285         return err;
286     }
287
288     cl->rx_vtbl.spawn_reply = spawn_reply_handler;
289     if (capref_is_null(inheritcn_cap) && capref_is_null(argcn_cap)) {
290         err = cl->tx_vtbl.spawn_request(cl, NOP_CONT, cap_procmng, domain_cap,
291                                         path, argvbuf, argvbytes, envbuf,
292                                         envbytes, flags);
293     } else {
294         err = cl->tx_vtbl.spawn_with_caps_request(cl, NOP_CONT, cap_procmng,
295                                                   domain_cap, path, argvbuf,
296                                                   argvbytes, envbuf, envbytes,
297                                                   inheritcn_cap, argcn_cap,
298                                                   flags);
299     }
300     if (err_is_fail(err)) {
301         DEBUG_ERR(err, "sending spawn request");
302         pending_clients_release(domain_cap, NULL);
303         return err_push(err, PROC_MGMT_ERR_SPAWND_REQUEST);
304     }
305
306     return SYS_ERR_OK;
307 }
308
309 static void spawn_handler(struct proc_mgmt_binding *b, coreid_t core_id,
310                           const char *path, const char *argvbuf,
311                           size_t argvbytes, const char *envbuf, size_t envbytes,
312                           uint8_t flags)
313 {
314     errval_t err, resp_err;
315     struct capref domain_cap;
316     err = spawn_handler_common(b, ClientType_Spawn, core_id, path, argvbuf,
317                                argvbytes, envbuf, envbytes, NULL_CAP, NULL_CAP,
318                                flags, &domain_cap);
319     if (err_is_ok(err)) {
320         // Will respond to client when we get the reply from spawnd.
321         return;
322     }
323
324     resp_err = b->tx_vtbl.spawn_response(b, NOP_CONT, err, NULL_CAP);
325     if (err_is_fail(resp_err)) {
326         DEBUG_ERR(resp_err, "failed to send spawn_response");
327     }
328 }
329
330 static void spawn_with_caps_handler(struct proc_mgmt_binding *b,
331                                     coreid_t core_id, const char *path,
332                                     const char *argvbuf, size_t argvbytes,
333                                     const char *envbuf, size_t envbytes,
334                                     struct capref inheritcn_cap,
335                                     struct capref argcn_cap, uint8_t flags)
336 {
337     errval_t err, resp_err;
338     struct capref domain_cap;
339     err = spawn_handler_common(b, ClientType_SpawnWithCaps, core_id, path,
340                                argvbuf, argvbytes, envbuf, envbytes,
341                                inheritcn_cap, argcn_cap, flags, &domain_cap);
342     if (err_is_ok(err)) {
343         // Will respond to client when we get the reply from spawnd.
344         return;
345     }
346
347     resp_err = b->tx_vtbl.spawn_with_caps_response(b, NOP_CONT, err,
348                                                             NULL_CAP);
349     if (err_is_fail(resp_err)) {
350         DEBUG_ERR(resp_err, "failed to send spawn_with_caps_response");
351     }
352 }
353
354 static void span_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
355                          coreid_t core_id, struct capref vroot,
356                          struct capref dispframe)
357 {
358     errval_t err, resp_err;
359     err = domain_can_span(domain_cap, core_id);
360     if (err_is_fail(err)) {
361         goto respond_with_err;
362     }
363
364     if (!spawnd_state_exists(core_id)) {
365         err = PROC_MGMT_ERR_INVALID_SPAWND;
366         goto respond_with_err;
367     }
368
369     struct spawnd_state *state = spawnd_state_get(core_id);
370     assert(state != NULL);
371     struct spawn_binding *cl = state->b;
372     assert(cl != NULL);
373
374     err = pending_clients_add(domain_cap, b, ClientType_Span, core_id);
375     if (err_is_fail(err)) {
376         goto respond_with_err;
377     }
378
379     cl->rx_vtbl.spawn_reply = spawn_reply_handler;
380     err = cl->tx_vtbl.span_request(cl, NOP_CONT, cap_procmng, domain_cap, vroot,
381                                    dispframe);
382     if (err_is_ok(err)) {
383         // Will respond to client when we get the reply from spawnd.
384         return;
385     } else {
386         DEBUG_ERR(err, "sending span request");
387         pending_clients_release(domain_cap, NULL);
388         err = err_push(err, PROC_MGMT_ERR_SPAWND_REQUEST);
389     }
390
391 respond_with_err:
392     resp_err = b->tx_vtbl.span_response(b, NOP_CONT, err);
393     if (err_is_fail(resp_err)) {
394         DEBUG_ERR(resp_err, "failed to send span_response");
395     }
396 }
397
398 static errval_t kill_handler_common(struct proc_mgmt_binding *b,
399                                     struct capref domain_cap,
400                                     enum ClientType type,
401                                     uint8_t exit_status)
402 {
403     errval_t err = pending_clients_add(domain_cap, b, type, MAX_COREID);
404     if (err_is_fail(err)) {
405         return err;
406     }
407
408     struct domain_entry *entry;
409     err = domain_get_by_cap(domain_cap, &entry);
410     if (err_is_fail(err)) {
411         return err;
412     }
413
414     entry->exit_status = exit_status;
415     domain_stop_pending(entry);
416
417     for (coreid_t i = 0; i < MAX_COREID; ++i) {
418         if (entry->spawnds[i] == NULL) {
419             continue;
420         }
421
422         struct spawn_binding *spb = entry->spawnds[i]->b;
423         spb->rx_vtbl.spawn_reply = spawn_reply_handler;
424         errval_t req_err = spb->tx_vtbl.kill_request(spb, NOP_CONT, cap_procmng,
425                                                      domain_cap);
426         if (err_is_fail(req_err)) {
427             DEBUG_ERR(req_err, "failed to send kill_request to spawnd %u\n", i);
428         }
429     }
430
431     return SYS_ERR_OK;
432 }
433
434 static void kill_handler(struct proc_mgmt_binding *b, struct capref domain_cap)
435 {
436     errval_t err = kill_handler_common(b, domain_cap, ClientType_Kill,
437                                        EXIT_STATUS_KILLED);
438     if (err_is_fail(err)) {
439         errval_t resp_err = b->tx_vtbl.kill_response(b, NOP_CONT, err);
440         if (err_is_fail(resp_err)) {
441             DEBUG_ERR(resp_err, "failed to send kill_response");
442         }
443     }
444 }
445
446 static void exit_handler(struct proc_mgmt_binding *b, struct capref domain_cap,
447                          uint8_t exit_status)
448 {
449     errval_t err = kill_handler_common(b, domain_cap, ClientType_Exit,
450                                        exit_status);
451     if (err_is_fail(err)) {
452         DEBUG_ERR(err, "processing exit_handler for requesting domain, exit "
453                   "code %u", exit_status);
454     }
455     // Error or not, there's no client to reply to anymore.
456 }
457
458 static void wait_handler(struct proc_mgmt_binding *b, struct capref domain_cap)
459 {
460     errval_t err, resp_err;
461     struct domain_entry *entry;
462     err = domain_get_by_cap(domain_cap, &entry);
463     if (err_is_fail(err)) {
464         goto respond;
465     }
466
467     if (entry->status == DOMAIN_STATUS_STOPPED) {
468         // Domain has already been stopped, so just reply with exit status.
469         goto respond;
470     }
471
472     struct domain_waiter *waiter = (struct domain_waiter*) malloc(
473             sizeof(struct domain_waiter));
474     waiter->b = b;
475     waiter->next = entry->waiters;
476     entry->waiters = waiter;
477     // Will respond when domain is stopped.
478     return;
479
480 respond:
481     resp_err = b->tx_vtbl.wait_response(b, NOP_CONT, err, entry->exit_status);
482     if (err_is_fail(resp_err)) {
483         DEBUG_ERR(resp_err, "failed to send wait_response");
484     }
485 }
486
487 static struct proc_mgmt_rx_vtbl monitor_vtbl = {
488     .add_spawnd           = add_spawnd_handler,
489     .spawn_call           = spawn_handler,
490     .spawn_with_caps_call = spawn_with_caps_handler,
491     .span_call            = span_handler,
492     .kill_call            = kill_handler,
493     .exit                 = exit_handler,
494     .wait_call            = wait_handler
495 };
496
497 static struct proc_mgmt_rx_vtbl non_monitor_vtbl = {
498     .add_spawnd           = add_spawnd_handler_non_monitor,
499     .spawn_call           = spawn_handler,
500     .spawn_with_caps_call = spawn_with_caps_handler,
501     .span_call            = span_handler,
502     .kill_call            = kill_handler,
503     .exit                 = exit_handler,
504     .wait_call            = wait_handler
505 };
506
507 static errval_t alloc_ep_for_monitor(struct capref *ep)
508 {
509     struct proc_mgmt_lmp_binding *lmpb =
510         malloc(sizeof(struct proc_mgmt_lmp_binding));
511     assert(lmpb != NULL);
512
513     // setup our end of the binding
514     errval_t err = proc_mgmt_client_lmp_accept(lmpb, get_default_waitset(),
515                                                DEFAULT_LMP_BUF_WORDS);
516     if (err_is_fail(err)) {
517         free(lmpb);
518         return err_push(err, LIB_ERR_PROC_MGMT_CLIENT_ACCEPT);
519     }
520
521     *ep = lmpb->chan.local_cap;
522     lmpb->b.rx_vtbl = monitor_vtbl;
523
524     return SYS_ERR_OK;
525 }
526
527 static void export_cb(void *st, errval_t err, iref_t iref)
528 {
529     if (err_is_fail(err)) {
530         USER_PANIC_ERR(err, "export failed");
531     }
532
533     // Allocate an endpoint for the local monitor, who will use it to inform
534     // us about new spawnd irefs on behalf of other monitors.
535     struct capref ep;
536     err = alloc_ep_for_monitor(&ep);
537     if (err_is_fail(err)) {
538         USER_PANIC_ERR(err, "failed to allocate LMP EP for local monitor");
539     }
540
541     // Send the endpoint to the monitor, so it can finish the handshake.
542     struct monitor_binding *mb = get_monitor_binding();
543     err = mb->tx_vtbl.set_proc_mgmt_ep_request(mb, NOP_CONT, ep);
544     if (err_is_fail(err)) {
545         USER_PANIC_ERR(err, "failed to send set_proc_mgmt_ep_request to "
546                        "monitor");
547     }
548
549     // Also register this iref with the name service, for arbitrary client
550     // domains to use for spawn-related ops.
551     err = nameservice_register(SERVICE_BASENAME, iref);
552     if (err_is_fail(err)) {
553         USER_PANIC_ERR(err, "nameservice_register failed");
554     }
555 }
556
557 static errval_t connect_cb(void *st, struct proc_mgmt_binding *b)
558 {
559     b->rx_vtbl = non_monitor_vtbl;
560     return SYS_ERR_OK;
561 }
562
563 errval_t start_service(void)
564 {
565     return proc_mgmt_export(NULL, export_cb, connect_cb, get_default_waitset(),
566             IDC_EXPORT_FLAGS_DEFAULT);
567 }