flounder: making const pointers in receiving handlers, using CONST_CAST as a temporar...
[barrelfish] / usr / arrakismon / vmkitmon.c
1 /**
2  * \file
3  */
4
5 /*
6  * Copyright (c) 2009, 2010, 2011, ETH Zurich.
7  * All rights reserved.
8  *
9  * This file is distributed under the terms in the attached LICENSE file.
10  * If you do not find this file, copies can be found by writing to:
11  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
12  */
13
14 #include "vmkitmon.h"
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <barrelfish/barrelfish.h>
20 #include <barrelfish/nameservice_client.h>
21 #include <barrelfish/cpu_arch.h>
22 /* #include <barrelfish/terminal.h> */
23 #include <vfs/vfs.h>
24 #include <vfs/vfs_path.h>
25 #include <spawndomain/spawndomain.h>
26 #include <if/arrakis_defs.h>
27 #include <if/monitor_blocking_rpcclient_defs.h>
28 /* #include <timer/timer.h> */
29 #include "ps.h"
30
31 #define SERVICE_BASENAME    "arrakis" // the core ID is appended to this
32
33 static errval_t spawn_arrakis(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                               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     err = spawn_load_image(&si, (lvaddr_t)image, info.size, CURRENT_CPU_TYPE,
93                            name, disp_get_core_id(), argv, envp, inheritcn_cap,
94                            argcn_cap);
95     if (err_is_fail(err)) {
96         free(image);
97         return err;
98     }
99
100     free(image);
101
102     /* request connection from monitor */
103     struct monitor_blocking_rpc_client *mrpc = get_monitor_blocking_rpc_client();
104     struct capref monep;
105     err = mrpc->vtbl.alloc_monitor_ep(mrpc, &msgerr, &monep);
106     if (err_is_fail(err)) {
107         return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
108     } else if (err_is_fail(msgerr)) {
109         return msgerr;
110     }
111
112     /* copy connection into the new domain */
113     struct capref destep = {
114         .cnode = si.taskcn,
115         .slot  = TASKCN_SLOT_MONITOREP,
116     };
117     err = cap_copy(destep, monep);
118     if (err_is_fail(err)) {
119         spawn_free(&si);
120         cap_destroy(monep);
121         return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
122     }
123
124     err = cap_destroy(monep);
125     if (err_is_fail(err)) {
126         return err_push(err, SPAWN_ERR_MONITOR_CLIENT);
127     }
128
129     debug_printf("spawning %s on core %u\n", path, disp_get_core_id());
130
131     /* give the perfmon capability */
132     struct capref dest, src;
133     dest.cnode = si.taskcn;
134     dest.slot = TASKCN_SLOT_PERF_MON;
135     src.cnode = cnode_task;
136     src.slot = TASKCN_SLOT_PERF_MON;
137     err = cap_copy(dest, src);
138     if (err_is_fail(err)) {
139         return err_push(err, INIT_ERR_COPY_PERF_MON);
140     }
141
142     // Create a new guest control structure
143     struct guest *g = guest_create();
144     assert(g != NULL);
145
146     // run the domain
147     spawn_guest_domain(g, &si);
148
149     err = guest_make_runnable(g, true);
150     assert_err(err, "guest_make_runnable");
151
152     // Allocate domain id
153     struct ps_entry *pe = malloc(sizeof(struct ps_entry));
154     assert(pe != NULL);
155     memset(pe, 0, sizeof(struct ps_entry));
156     memcpy(pe->argv, (CONST_CAST)argv, MAX_CMDLINE_ARGS*sizeof(*argv));
157     pe->argbuf = argbuf;
158     pe->argbytes = argbytes;
159     /*
160      * NB: It's important to keep a copy of the DCB *and* the root
161      * CNode around.  We need to revoke both (in the right order, see
162      * kill_domain() below), so that we ensure no one else is
163      * referring to the domain's CSpace anymore. Especially the loop
164      * created by placing rootcn into its own address space becomes a
165      * problem here.
166      */
167     err = slot_alloc(&pe->rootcn_cap);
168     assert(err_is_ok(err));
169     err = cap_copy(pe->rootcn_cap, si.rootcn_cap);
170     pe->rootcn = si.rootcn;
171     assert(err_is_ok(err));
172     err = slot_alloc(&pe->dcb);
173     assert(err_is_ok(err));
174     err = cap_copy(pe->dcb, si.dcb);
175     assert(err_is_ok(err));
176     pe->status = PS_STATUS_RUNNING;
177     err = ps_allocate(pe, domainid);
178     if(err_is_fail(err)) {
179         free(pe);
180     }
181
182     // Store in target dispatcher frame
183     struct dispatcher_generic *dg = get_dispatcher_generic(si.handle);
184     dg->domain_id = *domainid;
185
186     /* cleanup */
187     err = spawn_free(&si);
188     if (err_is_fail(err)) {
189         return err_push(err, SPAWN_ERR_FREE);
190     }
191
192     return SYS_ERR_OK;
193 }
194
195 struct pending_spawn_response {
196     struct arrakis_binding *b;
197     errval_t err;
198     domainid_t domainid;
199 };
200
201 static void retry_spawn_domain_response(void *a)
202 {
203     errval_t err;
204
205     struct pending_spawn_response *r = (struct pending_spawn_response*)a;
206     struct arrakis_binding *b = r->b;
207
208     err = b->tx_vtbl.spawn_arrakis_domain_response(b, NOP_CONT, r->err, r->domainid);
209
210     if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
211         // try again
212         err = b->register_send(b, get_default_waitset(),
213                                MKCONT(retry_spawn_domain_response,a));
214     }
215     if (err_is_fail(err)) {
216         DEBUG_ERR(err, "error sending spawn_domain reply\n");
217     }
218
219     free(a);
220 }
221
222
223 static errval_t spawn_reply(struct arrakis_binding *b, errval_t rerr,
224                             domainid_t domainid)
225 {
226     errval_t err;
227  
228     err = b->tx_vtbl.spawn_arrakis_domain_response(b, NOP_CONT, rerr, domainid);
229
230     if (err_is_fail(err)) {
231         DEBUG_ERR(err, "error sending spawn_domain reply\n");
232
233         if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
234             // this will be freed in the retry handler
235             struct pending_spawn_response *sr =
236                 malloc(sizeof(struct pending_spawn_response));
237             if (sr == NULL) {
238                 return LIB_ERR_MALLOC_FAIL;
239             }
240             sr->b = b;
241             sr->err = rerr;
242             sr->domainid = domainid;
243             err = b->register_send(b, get_default_waitset(),
244                                    MKCONT(retry_spawn_domain_response, sr));
245             if (err_is_fail(err)) {
246                 // note that only one continuation may be registered at a time
247                 free(sr);
248                 DEBUG_ERR(err, "register_send failed!");
249                 return err;
250             }
251         }
252     }
253
254     return SYS_ERR_OK;
255 }
256
257
258 static void spawn_with_caps_handler(struct arrakis_binding *b, const char *path,
259                                     const char *argbuf, size_t argbytes,
260                                     const char *envbuf, size_t envbytes,
261                                     struct capref inheritcn_cap,
262                                     struct capref argcn_cap)
263 {
264     errval_t err;
265     domainid_t domainid = 0;
266
267     /* printf("arrakismon: spawning '%s'\n", path); */
268
269     /* extract arguments from buffer */
270     char * argv[MAX_CMDLINE_ARGS + 1];
271     int i = 0;
272     size_t pos = 0;
273     while (pos < argbytes && i < MAX_CMDLINE_ARGS) {
274         argv[i++] = (CONST_CAST)argbuf + pos;
275         char *end = memchr(&argbuf[pos], '\0', argbytes - pos);
276         if (end == NULL) {
277             err = SPAWN_ERR_GET_CMDLINE_ARGS;
278             goto finish;
279         }
280         pos = end - argbuf + 1;
281     }
282     assert(i <= MAX_CMDLINE_ARGS);
283     argv[i] = NULL;
284
285     /* extract environment from buffer */
286     char *envp[MAX_CMDLINE_ARGS + 1];
287     i = 0;
288     pos = 0;
289     while (pos < envbytes && i < MAX_CMDLINE_ARGS) {
290         envp[i++] = (CONST_CAST)envbuf + pos;
291         char *end = memchr(&envbuf[pos], '\0', envbytes - pos);
292         if (end == NULL) {
293             err = SPAWN_ERR_GET_CMDLINE_ARGS;
294             goto finish;
295         }
296         pos = end - envbuf + 1;
297     }
298     assert(i <= MAX_CMDLINE_ARGS);
299     envp[i] = NULL;
300
301     char *npath = alloca(strlen(path));
302     strcpy(npath, path);
303     vfs_path_normalise(npath);
304
305     err = spawn_arrakis(npath, argv, argbuf, argbytes, envp, inheritcn_cap,
306                         argcn_cap, &domainid);
307     if (!capref_is_null(inheritcn_cap)) {
308         errval_t err2;
309         err2 = cap_delete(inheritcn_cap);
310         assert(err_is_ok(err2));
311     }
312     if (!capref_is_null(argcn_cap)) {
313         errval_t err2;
314         err2 = cap_delete(argcn_cap);
315         assert(err_is_ok(err2));
316     }
317
318  finish:
319     if(err_is_fail(err)) {
320         DEBUG_ERR(err, "spawn");
321     }
322
323     err = spawn_reply(b, err, domainid);
324
325     if (err_is_fail(err)) {
326         // not much we can do about this
327         DEBUG_ERR(err, "while sending reply in spawn_handler");
328     }
329 }
330
331
332 static void spawn_handler(struct arrakis_binding *b, const char *path, const char *argbuf,
333                           size_t argbytes, const char *envbuf, size_t envbytes)
334 {
335     spawn_with_caps_handler(b, path, argbuf, argbytes, envbuf, envbytes,
336                             NULL_CAP, NULL_CAP);
337 }
338
339 static struct arrakis_rx_vtbl rx_vtbl = {
340     .spawn_arrakis_domain_call = spawn_handler,
341 };
342
343 static void export_cb(void *st, errval_t err, iref_t iref)
344 {
345     if (err_is_fail(err)) {
346         USER_PANIC_ERR(err, "export failed");
347     }
348
349     // construct name
350     char namebuf[32];
351     size_t len = snprintf(namebuf, sizeof(namebuf), "%s.%d", SERVICE_BASENAME,
352                           disp_get_core_id());
353     assert(len < sizeof(namebuf));
354     namebuf[sizeof(namebuf) - 1] = '\0';
355
356     // register this iref with the name service
357     err = nameservice_register(namebuf, iref);
358     if (err_is_fail(err)) {
359         USER_PANIC_ERR(err, "nameservice_register failed");
360     }
361 }
362
363 static errval_t connect_cb(void *st, struct arrakis_binding *b)
364 {
365     // copy my message receive handler vtable to the binding
366     b->rx_vtbl = rx_vtbl;
367     return SYS_ERR_OK;
368 }
369
370 static errval_t start_service(void)
371 {
372     return arrakis_export(NULL, export_cb, connect_cb, get_default_waitset(),
373                           IDC_EXPORT_FLAGS_DEFAULT);
374 }
375
376 int main (int argc, char *argv[])
377 {
378     vfs_init();
379
380     /* err = timer_init(); */
381     /* if (err_is_fail(err)) { */
382     /*     USER_PANIC_ERR(err, "error initialising timer client library\n"); */
383     /* } */
384
385 #if 0
386     /* Initialization */
387     err = realmode_init();
388     assert_err(err, "realmode_init");
389
390     // aquire the standard input
391     err = terminal_want_stdin(TERMINAL_SOURCE_SERIAL);
392     assert_err(err, "terminal_want_stdin");
393 #endif
394
395     // Start arrakis service
396     start_service();
397
398     // Spawn test arrakis application
399     /* err = spawn_arrakis("/x86_64/sbin/hellotest", ); */
400     /* assert_err(err, "spawn_arrakis"); */
401
402 #if 0
403     guest = guest_create ();
404     assert(guest != NULL);
405     err = guest_make_runnable(guest, true);
406     assert_err(err, "guest_make_runnable");
407
408     printf("arrakismon: main loop\n");
409 #endif
410
411     messages_handler_loop();
412 }