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