Create "ProcessManager" and "Domain" capabilities.
[barrelfish] / usr / spawnd / service.c
1 /**
2  * \file
3  * \brief spawn service
4  */
5
6 /*
7  * Copyright (c) 2010, 2011, 2012, 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 <stdio.h>
16 #include <string.h>
17 #include <barrelfish/barrelfish.h>
18 #include <spawndomain/spawndomain.h>
19 #include <barrelfish/nameservice_client.h>
20 #include <barrelfish/cpu_arch.h>
21 #include <vfs/vfs.h>
22 #include <vfs/vfs_path.h>
23 #include <dist/barrier.h>
24 #include <if/spawn_defs.h>
25 #include <if/monitor_defs.h>
26 #include <if/monitor_blocking_defs.h>
27 #include <barrelfish/dispatcher_arch.h>
28 #include <barrelfish/invocations_arch.h>
29
30 #include "internal.h"
31 #include "ps.h"
32
33
34 static errval_t spawn(const char *path, char *const argv[], const char *argbuf,
35                       size_t argbytes, char *const envp[],
36                       struct capref inheritcn_cap, struct capref argcn_cap,
37                       uint8_t flags, domainid_t *domainid)
38 {
39     errval_t err, msgerr;
40
41     /* read file into memory */
42     vfs_handle_t fh;
43     err = vfs_open(path, &fh);
44     if (err_is_fail(err)) {
45         return err_push(err, SPAWN_ERR_LOAD);
46     }
47
48     struct vfs_fileinfo info;
49     err = vfs_stat(fh, &info);
50     if (err_is_fail(err)) {
51         vfs_close(fh);
52         return err_push(err, SPAWN_ERR_LOAD);
53     }
54
55     assert(info.type == VFS_FILE);
56     uint8_t *image = malloc(info.size);
57     if (image == NULL) {
58         vfs_close(fh);
59         return err_push(err, SPAWN_ERR_LOAD);
60     }
61
62     size_t pos = 0, readlen;
63     do {
64         err = vfs_read(fh, &image[pos], info.size - pos, &readlen);
65         if (err_is_fail(err)) {
66             vfs_close(fh);
67             free(image);
68             return err_push(err, SPAWN_ERR_LOAD);
69         } else if (readlen == 0) {
70             vfs_close(fh);
71             free(image);
72             return SPAWN_ERR_LOAD; // XXX
73         } else {
74             pos += readlen;
75         }
76     } while (err_is_ok(err) && readlen > 0 && pos < info.size);
77
78     err = vfs_close(fh);
79     if (err_is_fail(err)) {
80         DEBUG_ERR(err, "failed to close file %s", path);
81     }
82
83     // find short name (last part of path)
84     const char *name = strrchr(path, VFS_PATH_SEP);
85     if (name == NULL) {
86         name = path;
87     } else {
88         name++;
89     }
90
91     /* spawn the image */
92     struct spawninfo si;
93     si.flags = flags;
94     err = spawn_load_image(&si, (lvaddr_t)image, info.size, CURRENT_CPU_TYPE,
95                            name, my_core_id, argv, envp, inheritcn_cap,
96                            argcn_cap);
97     if (err_is_fail(err)) {
98         free(image);
99         return err;
100     }
101
102     free(image);
103
104     /* request connection from monitor */
105     struct monitor_blocking_binding *mrpc = get_monitor_blocking_binding();
106     struct capref monep;
107     err = slot_alloc(&monep);
108     if (err_is_fail(err)) {
109         return err_push(err, SPAWN_ERR_MONEP_SLOT_ALLOC);
110     }
111     err = mrpc->rpc_tx_vtbl.alloc_monitor_ep(mrpc, &msgerr, &monep);
112     if (err_is_fail(err)) {
113         return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
114     } else if (err_is_fail(msgerr)) {
115         return msgerr;
116     }
117
118     /* copy connection into the new domain */
119     struct capref destep = {
120         .cnode = si.taskcn,
121         .slot  = TASKCN_SLOT_MONITOREP,
122     };
123     err = cap_copy(destep, monep);
124     if (err_is_fail(err)) {
125         spawn_free(&si);
126         cap_destroy(monep);
127         return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
128     }
129
130     err = cap_destroy(monep);
131     if (err_is_fail(err)) {
132         return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
133     }
134
135     debug_printf("spawning %s on core %u\n", path, my_core_id);
136
137     /* give the perfmon capability */
138     struct capref dest, src;
139     dest.cnode = si.taskcn;
140     dest.slot = TASKCN_SLOT_PERF_MON;
141     src.cnode = cnode_task;
142     src.slot = TASKCN_SLOT_PERF_MON;
143     err = cap_copy(dest, src);
144     if (err_is_fail(err)) {
145         return err_push(err, INIT_ERR_COPY_PERF_MON);
146     }
147
148     /* run the domain */
149     err = spawn_run(&si);
150     if (err_is_fail(err)) {
151         spawn_free(&si);
152         return err_push(err, SPAWN_ERR_RUN);
153     }
154
155     // Allocate domain id
156     struct ps_entry *pe = malloc(sizeof(struct ps_entry));
157     assert(pe != NULL);
158     memset(pe, 0, sizeof(struct ps_entry));
159     memcpy(pe->argv, argv, MAX_CMDLINE_ARGS*sizeof(*argv));
160     pe->argbuf = memdup(argbuf, argbytes);
161     pe->argbytes = argbytes;
162     /*
163      * NB: It's important to keep a copy of the DCB *and* the root
164      * CNode around.  We need to revoke both (in the right order, see
165      * kill_domain() below), so that we ensure no one else is
166      * referring to the domain's CSpace anymore. Especially the loop
167      * created by placing rootcn into its own address space becomes a
168      * problem here.
169      */
170     err = slot_alloc(&pe->rootcn_cap);
171     assert(err_is_ok(err));
172     err = cap_copy(pe->rootcn_cap, si.rootcn_cap);
173     pe->rootcn = si.rootcn;
174     assert(err_is_ok(err));
175     err = slot_alloc(&pe->dcb);
176     assert(err_is_ok(err));
177     err = cap_copy(pe->dcb, si.dcb);
178     assert(err_is_ok(err));
179     pe->status = PS_STATUS_RUNNING;
180     err = ps_allocate(pe, domainid);
181     if(err_is_fail(err)) {
182         free(pe);
183     }
184
185     // Store in target dispatcher frame
186     struct dispatcher_generic *dg = get_dispatcher_generic(si.handle);
187     dg->domain_id = *domainid;
188
189     /* cleanup */
190     err = spawn_free(&si);
191     if (err_is_fail(err)) {
192         return err_push(err, SPAWN_ERR_FREE);
193     }
194
195     return SYS_ERR_OK;
196 }
197
198 static void retry_use_local_memserv_response(void *a)
199 {
200     errval_t err;
201
202     struct spawn_binding *b = (struct spawn_binding*)a;
203
204     err = b->tx_vtbl.use_local_memserv_response(b, NOP_CONT);
205
206     if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
207         // try again
208         err = b->register_send(b, get_default_waitset(),
209                                MKCONT(retry_use_local_memserv_response,a));
210     }
211     if (err_is_fail(err)) {
212         DEBUG_ERR(err, "error sending use_local_memserv reply\n");
213     }
214
215 }
216
217
218 static void use_local_memserv_handler(struct spawn_binding *b)
219 {
220     ram_alloc_set(NULL);
221
222     errval_t err;
223     err = b->tx_vtbl.use_local_memserv_response(b, NOP_CONT);
224     if (err_is_fail(err)) {
225         DEBUG_ERR(err, "error sending use_local_memserv reply");
226         if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
227             err = b->register_send(b, get_default_waitset(),
228                                MKCONT(retry_use_local_memserv_response, b));
229             if (err_is_fail(err)) {
230                 // note that only one continuation may be registered at a time
231                 DEBUG_ERR(err, "register_send failed!");
232             }
233         }
234     }
235 }
236
237 struct pending_spawn_response {
238     struct spawn_binding *b;
239     errval_t err;
240     domainid_t domainid;
241 };
242
243 static errval_t spawn_with_caps_common(const char *path, const char *argbuf,
244                                        size_t argbytes, const char *envbuf,
245                                        size_t envbytes,
246                                        struct capref inheritcn_cap,
247                                        struct capref argcn_cap, uint8_t flags,
248                                        domainid_t *domainid)
249 {
250     errval_t err;
251     assert(domainid);
252     *domainid = 0;
253
254     /* extract arguments from buffer */
255     char *argv[MAX_CMDLINE_ARGS + 1];
256     int i = 0;
257     size_t pos = 0;
258     while (pos < argbytes && i < MAX_CMDLINE_ARGS) {
259         argv[i++] = (CONST_CAST)argbuf + pos;
260         char *end = memchr(&argbuf[pos], '\0', argbytes - pos);
261         if (end == NULL) {
262             err = SPAWN_ERR_GET_CMDLINE_ARGS;
263             goto finish;
264         }
265         pos = end - argbuf + 1;
266     }
267     assert(i <= MAX_CMDLINE_ARGS);
268     argv[i] = NULL;
269
270     /* extract environment from buffer */
271     char *envp[MAX_CMDLINE_ARGS + 1];
272     i = 0;
273     pos = 0;
274     while (pos < envbytes && i < MAX_CMDLINE_ARGS) {
275         envp[i++] = (CONST_CAST)envbuf + pos;
276         char *end = memchr(&envbuf[pos], '\0', envbytes - pos);
277         if (end == NULL) {
278             err = SPAWN_ERR_GET_CMDLINE_ARGS;
279             goto finish;
280         }
281         pos = end - envbuf + 1;
282     }
283     assert(i <= MAX_CMDLINE_ARGS);
284     envp[i] = NULL;
285
286     char *npath;
287     npath = alloca(strlen(path));
288     strcpy(npath, path);
289     vfs_path_normalise(npath);
290
291     err = spawn(npath, argv, argbuf, argbytes, envp, inheritcn_cap, argcn_cap,
292                 flags, domainid);
293     // XXX: do we really want to delete the inheritcn and the argcn here? iaw:
294     // do we copy these somewhere? -SG
295     if (!capref_is_null(inheritcn_cap)) {
296         errval_t err2;
297         err2 = cap_delete(inheritcn_cap);
298         assert(err_is_ok(err2));
299     }
300     if (!capref_is_null(argcn_cap)) {
301         errval_t err2;
302         err2 = cap_delete(argcn_cap);
303         assert(err_is_ok(err2));
304     }
305
306  finish:
307     if(err_is_fail(err)) {
308         DEBUG_ERR(err, "spawn");
309     }
310
311     return err;
312 }
313
314 static errval_t spawn_with_caps_handler(struct spawn_binding *b, const char *path,
315     const char *argvbuf, size_t argvbytes, const char *envbuf, size_t envbytes,
316     struct capref inheritcn_cap, struct capref argcn_cap, uint8_t flags,
317     errval_t *err, spawn_domainid_t *domain_id)
318 {
319     *err = spawn_with_caps_common(path, argvbuf, argvbytes, envbuf, envbytes,
320                                  inheritcn_cap, argcn_cap, flags, domain_id);
321     return SYS_ERR_OK;
322 }
323
324 static errval_t spawn_handler(struct spawn_binding *b, const char *path,
325     const char *argvbuf, size_t argvbytes, const char *envbuf, size_t envbytes,
326     uint8_t flags, errval_t *err, spawn_domainid_t *domain_id)
327 {
328     *err = spawn_with_caps_common(path, argvbuf, argvbytes, envbuf, envbytes,
329                                  NULL_CAP, NULL_CAP, flags, domain_id);
330     return SYS_ERR_OK;
331 }
332
333 /**
334  * \brief Removes a zombie domain.
335  */
336 static void cleanup_domain(domainid_t domainid)
337 {
338     errval_t err;
339     struct ps_entry *ps = ps_get(domainid);
340     assert(ps != NULL);
341
342     // Tell all waiters of exit and free list as we go
343     for(struct ps_waiter *w = ps->waiters; w != NULL;) {
344         err = w->binding->tx_vtbl.wait_response
345             (w->binding, NOP_CONT, ps->exitcode, SYS_ERR_OK);
346         if(err_is_fail(err)) {
347             DEBUG_ERR(err, "wait_response");
348         }
349
350         struct ps_waiter *oldw = w;
351         w = w->next;
352         free(oldw);
353     }
354     ps->waiters = NULL;
355
356     // Cleanup rest of ps entry
357     free(ps->argbuf);
358
359     ps_remove(domainid);
360 }
361
362 static void cleanup_cap(struct capref cap)
363 {
364     errval_t err;
365
366     err = cap_revoke(cap);
367     if (err_is_fail(err)) {
368         DEBUG_ERR(err, "cap_revoke");
369     }
370     err = cap_destroy(cap);
371     if (err_is_fail(err)) {
372         DEBUG_ERR(err, "cap_destroy");
373     }
374 }
375
376 static errval_t kill_domain(domainid_t domainid, uint8_t exitcode)
377 {
378     struct ps_entry *ps = ps_get(domainid);
379
380     if(ps == NULL) {
381         return SPAWN_ERR_DOMAIN_NOTFOUND;
382     }
383
384     ps->status = PS_STATUS_ZOMBIE;
385     ps->exitcode = exitcode;
386
387     // Garbage collect victim's capabilities
388     cleanup_cap(ps->dcb);       // Deschedule dispatcher (do this first!)
389     cleanup_cap(ps->rootcn_cap);
390
391     // XXX: why only when waiters exist? -SG
392     if(ps->waiters != NULL) {
393         // Cleanup local data structures and inform waiters
394         cleanup_domain(domainid);
395     }
396
397     return SYS_ERR_OK;
398 }
399
400 static void kill_handler(struct spawn_binding *b, domainid_t domainid)
401 {
402     errval_t err = kill_domain(domainid, 0);
403
404     err = b->tx_vtbl.kill_response(b, NOP_CONT, err);
405     if(err_is_fail(err)) {
406         DEBUG_ERR(err, "kill_response");
407     }
408 }
409
410 static void exit_handler(struct spawn_binding *b, domainid_t domainid,
411                          uint8_t exitcode)
412 {
413     errval_t err = kill_domain(domainid, exitcode);
414     struct ps_entry *ps = ps_get(domainid);
415
416     if(err_is_fail(err)) {
417         DEBUG_ERR(err, "kill_domain");
418     }
419
420     if(ps == NULL) {
421         // XXX: Can't do nothing
422         return;
423     }
424
425     // May never return anything to client
426 }
427
428 static void wait_handler(struct spawn_binding *b, domainid_t domainid,
429                          bool nohang)
430 {
431     errval_t err;
432     struct ps_entry *ps = ps_get(domainid);
433
434     if(ps == NULL) {
435         err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_NOTFOUND);
436         if(err_is_fail(err)) {
437             DEBUG_ERR(err, "wait_response");
438         }
439     } else {
440         if(!nohang || ps->status == PS_STATUS_ZOMBIE) {
441             // Enqueue the waiter
442             struct ps_waiter *waiter = malloc(sizeof(struct ps_waiter));
443             assert(waiter != NULL);
444             waiter->next = ps->waiters;
445             waiter->binding = b;
446             ps->waiters = waiter;
447         } else {
448             // nohang and no zombie, return error
449             err = b->tx_vtbl.wait_response(b, NOP_CONT, 0, SPAWN_ERR_DOMAIN_RUNNING);
450             if(err_is_fail(err)) {
451                 DEBUG_ERR(err, "wait_response");
452             }
453         }
454
455         // Cleanup if zombie (will send the reply)
456         if(ps->status == PS_STATUS_ZOMBIE) {
457             cleanup_domain(domainid);
458         }
459     }
460 }
461
462 static void get_domainlist_sent(void *arg)
463 {
464     free(arg);
465 }
466
467 static void get_domainlist_handler(struct spawn_binding *b)
468 {
469     errval_t err;
470     size_t len = 0;
471     uint8_t *domains = calloc(sizeof(uint8_t), MAX_DOMAINS);
472
473     // XXX: Very inefficient
474     for(domainid_t i = 0; i < MAX_DOMAINS; i++) {
475         if(ps_exists(i)) {
476             domains[len++] = i;
477         }
478     }
479
480     err = b->tx_vtbl.get_domainlist_response
481         (b, MKCLOSURE(get_domainlist_sent, domains), domains, len);
482     if(err_is_fail(err)) {
483         DEBUG_ERR(err, "get_domainlist_response");
484         free(domains);
485     }
486 }
487
488 static void status_handler(struct spawn_binding *b, domainid_t domainid)
489 {
490     errval_t err;
491     struct ps_entry *ps = ps_get(domainid);
492     spawn_ps_entry_t pse;
493
494     memset(&pse, 0, sizeof(pse));
495
496     if(ps == NULL) {
497         err = b->tx_vtbl.status_response(b, NOP_CONT, pse, NULL, 0,
498                                          SPAWN_ERR_DOMAIN_NOTFOUND);
499         if(err_is_fail(err)) {
500             DEBUG_ERR(err, "status_response");
501         }
502     }
503
504     pse.status = ps->status;
505
506     err = b->tx_vtbl.status_response(b, NOP_CONT, pse, ps->argbuf, ps->argbytes,
507                                      SYS_ERR_OK);
508     if(err_is_fail(err)) {
509         DEBUG_ERR(err, "status_response");
510     }
511 }
512
513
514 static errval_t dump_capabilities(domainid_t domainid) {
515     struct ps_entry *ps = ps_get(domainid);
516
517     if(ps == NULL) {
518         return SPAWN_ERR_DOMAIN_NOTFOUND;
519     }
520
521     return invoke_dispatcher_dump_capabilities(ps->dcb);
522 }
523
524 static void dump_capabilities_handler(struct spawn_binding *b, domainid_t domainid) {
525     errval_t err = dump_capabilities(domainid);
526
527     err = b->tx_vtbl.dump_capabilities_response(b, NOP_CONT, err);
528     if(err_is_fail(err)) {
529         DEBUG_ERR(err, "debug_print_capabilities_response");
530     }
531 }
532
533 // TODO(razvan): This is just used to test that the process manager can connect
534 // to arbitrary spawnds; maybe remove it soon.
535 static errval_t echo_handler(struct spawn_binding *b, const char *sender_name,
536                              coreid_t sender_core)
537 {
538     debug_printf("spawnd ECHO from: %s.%u\n", sender_name, sender_core);
539     return SYS_ERR_OK;
540 }
541
542 static struct spawn_rx_vtbl rx_vtbl = {
543     // .spawn_domain_call = spawn_handler,
544     // .spawn_domain_with_caps_call = spawn_with_caps_handler,
545     .use_local_memserv_call = use_local_memserv_handler,
546     .kill_call = kill_handler,
547     .exit_call = exit_handler,
548     .wait_call = wait_handler,
549     .get_domainlist_call = get_domainlist_handler,
550     .status_call = status_handler,
551     .dump_capabilities_call = dump_capabilities_handler
552 };
553
554 static struct spawn_rpc_rx_vtbl rpc_rx_vtbl = {
555     .spawn_domain_call = spawn_handler,
556     .spawn_domain_with_caps_call = spawn_with_caps_handler,
557     .echo_call = echo_handler
558     // .use_local_memserv_call = use_local_memserv_handler,
559     // .kill_call = kill_handler,
560     // .exit_call = exit_handler,
561     // .wait_call = wait_handler,
562     // .get_domainlist_call = get_domainlist_handler,
563     // .status_call = status_handler,
564     // .dump_capabilities_call = dump_capabilities_handler
565 };
566
567 static void export_cb(void *st, errval_t err, iref_t iref)
568 {
569     if (err_is_fail(err)) {
570         USER_PANIC_ERR(err, "export failed");
571     }
572
573     // Send iref back to monitor, which will forward it to the process manager.
574     struct monitor_binding *mb = get_monitor_binding();
575     err = mb->tx_vtbl.set_spawn_iref_request(mb, NOP_CONT, iref);
576     if (err_is_fail(err)) {
577         USER_PANIC_ERR(err, "failed to send set_spawn_iref_request to "
578                 "monitor");
579     }
580
581     // construct name
582     char namebuf[32];
583     size_t len = snprintf(namebuf, sizeof(namebuf), "%s.%d", SERVICE_BASENAME,
584                           my_core_id);
585     assert(len < sizeof(namebuf));
586     namebuf[sizeof(namebuf) - 1] = '\0';
587
588     // register this iref with the name service
589     err = nameservice_register(namebuf, iref);
590     if (err_is_fail(err)) {
591         USER_PANIC_ERR(err, "nameservice_register failed");
592     }
593 }
594
595
596 static errval_t connect_cb(void *st, struct spawn_binding *b)
597 {
598     // copy my message receive handler vtable to the binding
599     b->rx_vtbl = rx_vtbl;
600     b->rpc_rx_vtbl = rpc_rx_vtbl;
601     return SYS_ERR_OK;
602 }
603
604 errval_t start_service(void)
605 {
606     return spawn_export(NULL, export_cb, connect_cb, get_default_waitset(),
607                          IDC_EXPORT_FLAGS_DEFAULT);
608 }