libbarrelfish: started merging proc_mgmt_client and spawn_client
[barrelfish] / usr / fish / fish_common.c
1 /**
2  * \file
3  * \brief fish - Shell commands
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 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, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
13  * Attn: Systems Group.
14  */
15
16 #define _USE_XOPEN
17
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <inttypes.h>
22 #include <barrelfish/barrelfish.h>
23 #include <barrelfish/dispatch.h>
24 #include <barrelfish_kpi/init.h>
25 #include <barrelfish/debug.h>
26 #include <barrelfish/monitor_client.h>
27 #include <barrelfish/nameservice_client.h>
28 #include <barrelfish/spawn_client.h>
29 #include <barrelfish/terminal.h>
30 #include <term/client/client_blocking.h>
31 #include <trace/trace.h>
32 #include <trace_definitions/trace_defs.h>
33 #include <skb/skb.h>
34 #include <vfs/vfs.h>
35 #include <vfs/vfs_path.h>
36 #include <if/pixels_defs.h>
37
38 #include <if/octopus_defs.h>
39 #include <octopus/getset.h> // for oct_read TODO
40 #include <octopus/trigger.h> // for NOP_TRIGGER
41 #include <octopus/init.h> // oct_init
42
43 #include <linenoise/linenoise.h>
44
45 #include "fish.h"
46
47 #define MAX_LINE        512
48 #define BOOTSCRIPT_NAME "/init.fish"
49
50 #define ENTRIES(array)  (sizeof(array) / sizeof(array[0]))
51
52 extern char **environ;
53 static struct cmd *find_command(const char *name);
54 static int makeargs(char *cmdline, char *argv[]);
55
56 typedef int (*Command)(int argc, char *argv[]);
57
58 static int spawnpixels(int argc, char *argv[]);
59
60 struct cmd {
61     const char  *name;
62     Command     cmd;
63     const char  *usage;
64 };
65
66 static char *cwd;
67
68 static struct capref inheritcn_cap;
69
70 static int help(int argc, char *argv[]);
71
72 static int execute_program(coreid_t coreid, int argc, char *argv[],
73                            struct capref *ret_domain_cap)
74 {
75     vfs_handle_t vh;
76     errval_t err;
77
78     // if the name contains a directory separator, assume it is relative to PWD
79     char *prog = argv[0];
80     if (strchr(argv[0], VFS_PATH_SEP) != NULL) {
81         prog = vfs_path_mkabsolute(cwd, argv[0]);
82
83         // check it exists
84         err = vfs_open(prog, &vh);
85         if (err_is_fail(err)) {
86             printf("%s: file not found: %s\n", prog, err_getstring(err));
87             free(prog);
88             return EXIT_FAILURE;
89         }
90         vfs_close(vh);
91     }
92
93     assert(ret_domain_cap != NULL);
94
95     argv[argc] = NULL;
96     err = spawn_program_with_caps(coreid, prog, argv, NULL, inheritcn_cap,
97                                   NULL_CAP, SPAWN_FLAGS_NEW_DOMAIN, ret_domain_cap);
98
99     if (prog != argv[0]) {
100         free(prog);
101     }
102
103     if (err_is_fail(err)) {
104         printf("%s: error spawning: %s\n", argv[0], err_getstring(err));
105         DEBUG_ERR(err, "Spawning Error\n");
106         return EXIT_FAILURE;
107     }
108
109     return EXIT_SUCCESS;
110 }
111
112 static int quit(int argc, char *argv[])
113 {
114     exit(EXIT_SUCCESS);
115     assert(!"exit() returned");
116     return 255;
117 }
118
119 static int print_cspace(int argc, char *argv[])
120 {
121     debug_my_cspace();
122     return EXIT_SUCCESS;
123 }
124
125 static int dump_caps(int argc, char *argv[]) {
126     errval_t err;
127     if (argc > 1) {
128         domainid_t domain = strtol(argv[1], NULL, 10);
129         err = spawn_dump_capabilities_compat(domain);
130     } else {
131         dispatcher_handle_t handle = curdispatcher();
132         struct capref dcb = get_dispatcher_generic(handle)->dcb_cap;
133         err = invoke_dispatcher_dump_capabilities(dcb);
134     }
135     if (err_is_fail(err)) {
136         printf("%s: error dumping capabilities: %s\n", argv[0], err_getstring(err));
137         DEBUG_ERR(err, "Error\n");
138         return EXIT_FAILURE;
139     }
140
141
142     return EXIT_SUCCESS;
143 }
144
145 static int setenvcmd(int argc, char *argv[])
146 {
147     if (argc <= 1) {
148         printf("Usage: %s [name=value]...\n", argv[0]);
149         return EXIT_FAILURE;
150     }
151
152     for (int i=1; i < argc; i++) {
153         char *sep = strchr(argv[i], '=');
154         char *value = "";
155         if (sep != NULL) {
156             *sep = '\0'; // XXX: modify arg inplace
157             value = sep + 1;
158         }
159         int r = setenv(argv[i], value, 1);
160         if (r != 0) {
161             fprintf(stderr, "Error: setenv(%s, %s) failed\n", argv[i], value);
162             return r;
163         }
164     }
165
166     return EXIT_SUCCESS;
167 }
168
169 static int printenv(int argc, char *argv[])
170 {
171     if (argc <= 1) {
172         for (int i=0; environ[i] != NULL; i++) {
173             printf("%s\n", environ[i]);
174         }
175     } else {
176         for (int i = 1; i < argc; i++) {
177             char *val = getenv(argv[i]);
178             if (val) {
179                 printf("%s\n", val);
180             }
181         }
182     }
183
184     return EXIT_SUCCESS;
185 }
186
187 static bool pixels_started = false;
188 static bool pixels_inited = false;
189 static int pixels_connected = 0;
190 #define NUM_PIXELS 16
191 static struct pixels_binding my_pixels_bindings[NUM_PIXELS];
192
193 static int acks = 0;
194
195 static void pixels_ack(struct pixels_binding *cl)
196 {
197     acks--;
198 }
199
200 static struct pixels_rx_vtbl pixels_vtbl = {
201     .ack = pixels_ack
202 };
203
204 static void my_pixels_bind_cb(void *st, errval_t err, struct pixels_binding *b)
205 {
206     struct pixels_binding *pb = (struct pixels_binding *)st;
207
208     if (err_is_fail(err)) {
209         USER_PANIC_ERR(err, "bind failed");
210     }
211
212     pb->rx_vtbl = pixels_vtbl;
213     pixels_connected++;
214 }
215
216 static void pixels_init(void)
217 {
218     // ensure pixels is up
219     if (!pixels_started) {
220         printf("Starting pixels...\n");
221         spawnpixels(0, NULL);
222     }
223
224     pixels_connected = 0;
225
226     for (int core = 0; core < NUM_PIXELS; core ++) {
227         char name[16];
228         iref_t serv_iref;
229         errval_t err;
230
231         sprintf(name, "pixels.%d", core);
232
233         /* Connect to the server */
234         err = nameservice_blocking_lookup(name, &serv_iref);
235         if (err_is_fail(err)) {
236             DEBUG_ERR(err, "failed to lookup server");
237             exit(EXIT_FAILURE);
238         }
239
240         if (serv_iref == 0) {
241             DEBUG_ERR(err, "failed to get a valid iref back from lookup");
242             exit(EXIT_FAILURE);
243         }
244       
245         err = pixels_bind(serv_iref, 
246                   my_pixels_bind_cb, 
247                   &my_pixels_bindings[core],
248                   get_default_waitset(),
249                   IDC_BIND_FLAGS_DEFAULT);
250         if (err_is_fail(err)) {
251             DEBUG_ERR(err, "bind request to pixels server failed immediately");
252             exit(EXIT_FAILURE);
253         }
254     }
255
256     while (pixels_connected < NUM_PIXELS) 
257         messages_wait_and_handle_next();
258     
259     printf("connected to pixels server\n");
260     pixels_inited = true;
261 }
262
263 static const char *scroller = "Barrelfish posse in full effect!!!   ";
264
265 static char c64map(char c) {
266     if ('A' <= c && c <= 'Z') {
267         return 65 + c-'A';
268     } else if ('a' <= c && c <= 'z') {
269         return 1 + c-'a';
270     } else if (c == ' ') {
271         return 32;
272     } else if (c == '!') {
273         return 33;
274     }
275     else {return 32;}
276 }
277
278 extern const char font[];
279
280 #define RENDER_WIDTH 48
281 #define PIXEL_WIDTH 100000
282 #define FRAMES 10
283
284 static int demo(int argc, char *argv[])
285 {
286     int core;
287     int pixwidth = PIXEL_WIDTH;
288     int frames = FRAMES;
289
290     if (!pixels_inited) pixels_init();
291
292     if (argc == 3) {
293         pixwidth = atoi(argv[1]);
294         frames = atoi(argv[2]);
295     }
296     int width = 8 * strlen(scroller);
297
298     for (int x = 0; x < width - RENDER_WIDTH; x++) {
299
300         // Repeat each frame a few times to slow down scrolling!
301         for (int f = 0; f < frames; f++) {
302         trace_event(TRACE_SUBSYS_BENCH, TRACE_EVENT_BENCH_PCBENCH, 1);
303         for(int i = 0; i < RENDER_WIDTH; i++) {
304
305             int xpos = (x + i)%width;
306             char ascii = scroller[xpos >> 3];
307             char c64char = c64map(ascii);
308             int xsub = xpos & 7;
309
310             acks = 0;
311             for (core = 0 ;core < 8; core++) {
312                 unsigned char bits = font[c64char*8 + (7-core)];
313
314                 if (bits & (1<<(7-xsub)) ) {
315
316                     my_pixels_bindings[core+2].tx_vtbl.display(&my_pixels_bindings[core+2], NOP_CONT, pixwidth);
317                     acks++;
318                 }
319             }
320
321             uint64_t now = rdtsc();
322
323             while (acks) {
324                 messages_wait_and_handle_next();
325             }
326             while (rdtsc() - now < pixwidth) ;
327         }
328
329         trace_event(TRACE_SUBSYS_BENCH, TRACE_EVENT_BENCH_PCBENCH, 0);
330         }
331     }
332     return EXIT_SUCCESS;
333 }
334
335 static int oncore(int argc, char *argv[])
336 {
337     if(argc < 3) {
338         printf("Usage: %s <core id> <program> [args]\n", argv[0]);
339         return EXIT_FAILURE;
340     }
341
342     int core = atoi(argv[1]);
343
344     argc -= 2;
345     argv += 2;
346
347     struct capref domain_cap;
348     int ret = execute_program(core, argc, argv, &domain_cap);
349
350     // TODO: do something with domain_id
351
352     return ret;
353 }
354
355 static int spawnpixels(int argc, char *argv[])
356 {
357     errval_t err;
358
359     /* Spawn on all cores */
360     char *spawnargv[] = {"pixels", NULL};
361     err = spawn_program_on_all_cores(true, spawnargv[0], spawnargv, NULL,
362                                      SPAWN_FLAGS_DEFAULT, NULL, NULL);
363     if (err_is_fail(err)) {
364         USER_PANIC_ERR(err, "error spawning other core");
365     }
366     pixels_started = true;
367     printf("Done\n");
368
369     return EXIT_SUCCESS;
370 }
371
372 static int ps(int argc, char *argv[])
373 {
374     uint8_t *domains;
375     size_t len;
376     errval_t err;
377
378     err = spawn_get_domain_list(&domains, &len);
379     if (err_is_fail(err)) {
380         DEBUG_ERR(err, "spawn_get_domain_list");
381         return EXIT_FAILURE;
382     }
383
384     printf("DOMAINID\tSTAT\tCOMMAND\n");
385     for(size_t i = 0; i < len; i++) {
386         struct spawn_ps_entry pse;
387         char *argbuf, status;
388         size_t arglen;
389         errval_t reterr;
390
391         err = spawn_get_status(domains[i], &pse, &argbuf, &arglen, &reterr);
392         if (err_is_fail(err)) {
393             DEBUG_ERR(err, "spawn_get_status");
394             return EXIT_FAILURE;
395         }
396         if(err_is_fail(reterr)) {
397             if(err_no(reterr) == SPAWN_ERR_DOMAIN_NOTFOUND) {
398                 return reterr;
399             }
400             DEBUG_ERR(err, "status");
401             return EXIT_FAILURE;
402         }
403
404         switch(pse.status) {
405         case 0:
406             status = 'R';
407             break;
408
409         case 1:
410             status = 'Z';
411             break;
412
413         default:
414             status = '?';
415             break;
416         }
417
418         printf("%-8u\t%c\t", domains[i], status);
419         size_t pos = 0;
420         for(int p = 0; pos < arglen && p < MAX_CMDLINE_ARGS;) {
421             printf("%s ", &argbuf[pos]);
422             char *end = memchr(&argbuf[pos], '\0', arglen - pos);
423             assert(end != NULL);
424             pos = end - argbuf + 1;
425         }
426         printf("\n");
427
428         free(argbuf);
429     }
430
431     free(domains);
432     return EXIT_SUCCESS;
433 }
434
435 static int skb(int argc, char *argv[])
436 {
437     static bool init = false;
438
439     if(argc < 2) {
440         printf("Usage: %s <program>\n", argv[0]);
441         return EXIT_FAILURE;
442     }
443
444     if(!init) {
445         skb_client_connect();
446         init = true;
447     }
448
449     char *result = NULL, *str_err = NULL;
450     int32_t int_err;
451
452     skb_evaluate(argv[1], &result, &str_err, &int_err);
453
454     if (int_err != 0 || (str_err != NULL && str_err[0] != '\0')) {
455         printf("SKB error returned: %"PRIu32" %s\n", int_err, str_err);
456     } else {
457         printf("SKB returned: %s\n", result);
458     }
459
460     free(result);
461     free(str_err);
462
463     return EXIT_SUCCESS;
464 }
465
466 static int mount(int argc, char *argv[])
467 {
468     if (argc != 3) {
469         printf("Usage: %s MOUNTPOINT URI\n", argv[0]);
470         return EXIT_FAILURE;
471     }
472
473     char *path = vfs_path_mkabsolute(cwd, argv[1]);
474     errval_t err = vfs_mount(path, argv[2]);
475     free(path);
476     if (err_is_fail(err)) {
477         DEBUG_ERR(err, "in vfs_mount %s %s", argv[1], argv[2]);
478         return EXIT_FAILURE;
479     }
480     return EXIT_SUCCESS;
481 }
482
483 static int cat(int argc, char *argv[])
484 {
485     if(argc < 2) {
486         printf("Usage: %s [file...]\n", argv[0]);
487         return EXIT_FAILURE;
488     }
489
490     uint8_t buf[1024];
491     size_t size;
492     vfs_handle_t vh;
493     errval_t err;
494     int ret = EXIT_SUCCESS;
495
496     for (int i = 1; i < argc; i++) {
497         char *path = vfs_path_mkabsolute(cwd, argv[i]);
498         err = vfs_open(path, &vh);
499         free(path);
500         if (err_is_fail(err)) {
501             printf("%s: file not found\n", argv[i]);
502             ret = EXIT_FAILURE;
503             continue;
504         }
505
506         do {
507             err = vfs_read(vh, buf, sizeof(buf), &size);
508             if (err_is_fail(err)) {
509                 // XXX: Close any files that might be open
510                 DEBUG_ERR(err, "error reading file");
511                 return EXIT_FAILURE;
512             }
513
514             fwrite(buf, 1, size, stdout);
515         } while(size > 0);
516
517         err = vfs_close(vh);
518         if (err_is_fail(err)) {
519             DEBUG_ERR(err, "in vfs_close");
520         }
521     }
522
523     return ret;
524 }
525
526 #define LINE_SIZE 16
527 static int hd(int argc, char *argv[])
528 {
529     if(argc < 2) {
530         printf("Usage: %s [file...]\n", argv[0]);
531         return EXIT_FAILURE;
532     }
533
534     uint8_t buf[1024];
535     size_t size;
536     vfs_handle_t vh;
537     errval_t err;
538     int ret = EXIT_SUCCESS;
539
540     for (int i = 1; i < argc; i++) {
541         char *path = vfs_path_mkabsolute(cwd, argv[i]);
542         err = vfs_open(path, &vh);
543         free(path);
544         if (err_is_fail(err)) {
545             printf("%s: file not found\n", argv[i]);
546             ret = EXIT_FAILURE;
547             continue;
548         }
549
550         printf("Contents of %s\n", argv[i]);
551         int k=0;
552         do {
553             err = vfs_read(vh, buf, sizeof(buf), &size);
554             if (err_is_fail(err)) {
555                 // XXX: Close any files that might be open
556                 DEBUG_ERR(err, "error reading file");
557                 return EXIT_FAILURE;
558             }
559
560             for (int j = k%LINE_SIZE; j < size; j++) {
561                 if (j % LINE_SIZE == 0) {
562                     printf("%08X: ", k+j);
563                 }
564                 printf("%02x%s", buf[j], (j+1)%LINE_SIZE == 0 ? "\n" : " ");
565             }
566             k+=size;
567         } while(size > 0);
568         if (k%LINE_SIZE) {
569             printf("\n");
570         }
571
572         err = vfs_close(vh);
573         if (err_is_fail(err)) {
574             DEBUG_ERR(err, "in vfs_close");
575         }
576     }
577
578     return ret;
579 }
580
581 static int cat2(int argc, char *argv[])
582 {
583     errval_t err;
584     char *path;
585     int ret;
586
587     if(argc < 3) {
588         printf("Usage: %s [input-files...] output-file\n", argv[0]);
589         return EXIT_FAILURE;
590     }
591
592     /* Open output file creating it if it does not exist */
593     path = vfs_path_mkabsolute(cwd, argv[argc - 1]);
594     vfs_handle_t output_vh;
595     err = vfs_create(path, &output_vh);
596     free(path);
597     if (err_is_fail(err)) {
598         DEBUG_ERR(err, "error opening output file");
599         return EXIT_FAILURE;
600     }
601
602     /* Open input files, read buffer and write to output file */
603     for (int i = 1; i < argc - 1; i++) {
604         uint8_t buf[1024];
605         size_t size;
606         vfs_handle_t input_vh;
607         path = vfs_path_mkabsolute(cwd, argv[i]);
608         err = vfs_open(path, &input_vh);
609         free(path);
610         if (err_is_fail(err)) {
611             printf("%s: file not found\n", argv[i]);
612             ret = EXIT_FAILURE;
613             continue;
614         }
615
616         do {
617             err = vfs_read(input_vh, buf, sizeof(buf), &size);
618             if (err_is_fail(err)) {
619                 // XXX: Close any files that might be open
620                 DEBUG_ERR(err, "error reading file");
621                 return EXIT_FAILURE;
622             }
623
624             size_t output_size;
625             err = vfs_write(output_vh, buf, size, &output_size);
626             if (err_is_fail(err)) {
627                 // XXX: Close any files that might be open
628                 DEBUG_ERR(err, "error writing to output file");
629                 return EXIT_FAILURE;
630             }
631             if (output_size != size) {
632                 printf("Wanted to write %zu but only wrote %zu, aborting\n",
633                        size, output_size);
634                 // XXX: Close any files that might be open
635                 return EXIT_FAILURE;
636             }
637         } while(size > 0);
638
639         err = vfs_close(input_vh);
640         if (err_is_fail(err)) {
641             DEBUG_ERR(err, "in vfs_close");
642         }
643     }
644
645     err = vfs_close(output_vh);
646     if (err_is_fail(err)) {
647         DEBUG_ERR(err, "in vfs_close");
648     }
649     return ret;
650 }
651
652 static int cp(int argc, char *argv[])
653 {
654     if (argc != 3) {
655         printf("Usage: %s src dest\n", argv[0]);
656         return EXIT_FAILURE;
657     }
658
659     static uint8_t buf[32768];
660     size_t rsize, wsize;
661     vfs_handle_t src = NULL, dst = NULL;
662     errval_t err;
663     int ret = EXIT_SUCCESS;
664
665     char *path = vfs_path_mkabsolute(cwd, argv[1]);
666     err = vfs_open(path, &src);
667     free(path);
668     if (err_is_fail(err)) {
669         printf("%s: %s\n", argv[1], err_getstring(err));
670         return EXIT_FAILURE;
671     }
672
673     path = vfs_path_mkabsolute(cwd, argv[2]);
674     err = vfs_create(path, &dst);
675     free(path);
676     if (err_is_fail(err)) {
677         printf("%s: %s\n", argv[2], err_getstring(err));
678         ret = EXIT_FAILURE;
679         goto out;
680     }
681
682     err = vfs_truncate(dst, 0);
683     if (err_is_fail(err)) {
684         printf("truncate %s: %s\n", argv[2], err_getstring(err));
685         ret = EXIT_FAILURE;
686         goto out;
687     }
688
689     do {
690         err = vfs_read(src, buf, sizeof(buf), &rsize);
691         if (err_is_fail(err)) {
692             DEBUG_ERR(err, "error reading file");
693             ret = EXIT_FAILURE;
694             goto out;
695         }
696
697         size_t wpos = 0;
698         while (wpos < rsize) {
699             err = vfs_write(dst, &buf[wpos], rsize - wpos, &wsize);
700             if (err_is_fail(err) || wsize == 0) {
701                 DEBUG_ERR(err, "error writing file");
702                 ret = EXIT_FAILURE;
703                 goto out;
704             }
705             wpos += wsize;
706         }
707     } while(rsize > 0);
708
709 out:
710     if (src != NULL) {
711         err = vfs_close(src);
712         if (err_is_fail(err)) {
713             DEBUG_ERR(err, "in vfs_close");
714         }
715     }
716
717     if (dst != NULL) {
718         err = vfs_close(dst);
719         if (err_is_fail(err)) {
720             DEBUG_ERR(err, "in vfs_close");
721         }
722     }
723
724     return ret;
725 }
726
727 static int dd(int argc, char *argv[])
728 {
729     // parse options
730     char *source = NULL;
731     char *target = NULL;
732
733     vfs_handle_t source_vh = NULL;
734     vfs_handle_t target_vh = NULL;
735
736     size_t blocksize = 512;
737     size_t count = 0;
738     size_t skip = 0;
739     size_t seek = 0;
740
741     size_t rsize = 0;
742     size_t wsize = 0;
743     size_t blocks_written = 0;
744
745     size_t total_bytes_read = 0;
746     size_t total_bytes_written = 0;
747
748     size_t progress = 0;
749
750     errval_t err;
751     int ret = EXIT_SUCCESS;
752
753     for (int i = 1; i < argc; i++)
754     {
755         if (!strncmp(argv[i], "bs=", 3))
756             blocksize = atoi(argv[i] + 3);
757
758         else if (!strncmp(argv[i], "count=", 6))
759             count = atoi(argv[i] + 6);
760
761         else if (!strncmp(argv[i], "skip=", 5))
762             skip = atoi(argv[i] + 5);
763
764         else if (!strncmp(argv[i], "seek=", 5))
765             seek = atoi(argv[i] + 5);
766
767         else if (!strncmp(argv[i], "if=", 3))
768             source = (argv[i] + 3);
769
770         else if (!strncmp(argv[i], "of=", 3))
771             target = (argv[i] + 3);
772         else if (!strncmp(argv[i], "progress", 8))
773             progress = 1;
774     }
775
776     size_t one_per_cent = (blocksize * count) / 100;
777
778     printf("from: %s to: %s bs=%zd count=%zd seek=%zd skip=%zd\n", source, target, blocksize, count, seek, skip);
779
780     if (source != NULL)
781     {
782         char *path = vfs_path_mkabsolute(cwd, source);
783         err = vfs_open(path, &source_vh);
784         free(path);
785         if (err_is_fail(err)) {
786             printf("%s: %s\n", source, err_getstring(err));
787             return EXIT_FAILURE;
788         }
789
790         if (skip != 0)
791         {
792             // TODO: skip
793         }
794     }
795
796     if (target != NULL)
797     {
798         char *path = vfs_path_mkabsolute(cwd, target);
799         err = vfs_create(path, &target_vh);
800         free(path);
801         if (err_is_fail(err)) {
802             // close source handle
803             if (source_vh != NULL)
804                 vfs_close(source_vh);
805             printf("%s: %s\n", target, err_getstring(err));
806             return EXIT_FAILURE;
807         }
808
809         if (seek != 0)
810         {
811             // TODO: seek
812         }
813     }
814
815     uint8_t * buffer = malloc(blocksize);
816
817 #if defined(__x86_64__) || defined(__i386__)
818     uint64_t tscperms;
819     err = sys_debug_get_tsc_per_ms(&tscperms);
820     assert(err_is_ok(err));
821
822     //printf("ticks per millisec: %" PRIu64 "\n", tscperms);
823     uint64_t start = rdtsc();
824 #endif
825
826     if (buffer == NULL)
827     {
828         ret = EXIT_FAILURE;
829         printf("failed to allocate buffer of size %zd\n", blocksize);
830         goto out;
831     }
832
833     do
834     {
835         //printf("copying block\n");
836         size_t read_bytes = 0;
837         do {
838             err = vfs_read(source_vh, buffer, blocksize, &rsize);
839             if (err_is_fail(err)) {
840                 DEBUG_ERR(err, "error reading file");
841                 ret = EXIT_FAILURE;
842                 goto out;
843             }
844
845             total_bytes_read += rsize;
846             read_bytes += rsize;
847
848             size_t wpos = 0;
849             while (wpos < rsize) {
850                 if (wpos > 0)
851                     printf("was unable to write the whole chunk of size %zd. Now at pos: %zd of buffer\n", rsize, wpos);
852
853                 err = vfs_write(target_vh, &buffer[wpos], rsize - wpos, &wsize);
854                 if (err_is_fail(err) || wsize == 0) {
855                     DEBUG_ERR(err, "error writing file");
856                     ret = EXIT_FAILURE;
857                     goto out;
858                 }
859                 wpos += wsize;
860                 total_bytes_written += wsize;
861             }
862         } while(read_bytes < blocksize);
863
864         blocks_written++;
865
866         if (progress && one_per_cent && total_bytes_written % one_per_cent == 0) {
867             printf(".");
868         }
869
870         //printf("block successfully copied. read: %zd. blocks written: %zd\n", rsize, blocks_written);
871     } while (rsize > 0 && !(count > 0 && blocks_written >= count));
872
873     if (progress) printf("\n");
874
875 out:
876     if (buffer != NULL)
877         free(buffer);
878
879     if (source_vh != NULL) {
880         err = vfs_close(source_vh);
881         if (err_is_fail(err)) {
882             DEBUG_ERR(err, "in vfs_close");
883         }
884     }
885
886     if (target_vh != NULL) {
887         err = vfs_close(target_vh);
888         if (err_is_fail(err)) {
889             DEBUG_ERR(err, "in vfs_close");
890         }
891     }
892
893 #if defined(__x86_64__) || defined(__i386__)
894     uint64_t stop = rdtsc();
895     uint64_t elapsed_msecs = ((stop - start) / tscperms);
896     double elapsed_secs = (double)elapsed_msecs/1000.0;
897
898     printf("start: %" PRIu64 " stop: %" PRIu64 "\n", start, stop);
899
900     double kbps = ((double)total_bytes_written / 1024.0) / elapsed_secs;
901
902     printf("%zd bytes read. %zd bytes written. %f s, %f kB/s\n", total_bytes_read, total_bytes_written, elapsed_secs, kbps);
903 #else
904     printf("%zd bytes read. %zd bytes written.\n", total_bytes_read, total_bytes_written);
905 #endif
906
907     return ret;
908 }
909
910 static int touch(int argc, char *argv[])
911 {
912     if(argc < 2) {
913         printf("Usage: %s [file...]\n", argv[0]);
914         return EXIT_FAILURE;
915     }
916
917     vfs_handle_t vh;
918     errval_t err;
919     int ret = EXIT_SUCCESS;
920
921     for (int i = 1; i < argc; i++) {
922         char *path = vfs_path_mkabsolute(cwd, argv[i]);
923         err = vfs_create(path, &vh);
924         free(path);
925         if (err_is_fail(err)) {
926             printf("%s: %s\n", argv[i], err_getstring(err));
927             DEBUG_ERR(err, "vfs_create failed");
928             ret = EXIT_FAILURE;
929             continue;
930         }
931
932         err = vfs_close(vh);
933         if (err_is_fail(err)) {
934             DEBUG_ERR(err, "in vfs_close");
935         }
936     }
937
938     return ret;
939 }
940
941 static char vfs_type_char(enum vfs_filetype type)
942 {
943     switch(type) {
944     case VFS_FILE:
945         return '-';
946     case VFS_DIRECTORY:
947         return 'd';
948     default:
949         return '?';
950     }
951 }
952
953 static int ls(int argc, char *argv[])
954 {
955     errval_t err;
956     int ret = EXIT_SUCCESS;
957
958     // XXX: cheat and assume we have some extra space wherever argv lives
959     if (argc <= 1) {
960         argv[1] = cwd;
961         argc = 2;
962     }
963
964     for (int i = 1; i < argc; i++) {
965         vfs_handle_t vh;
966         char *path = vfs_path_mkabsolute(cwd, argv[i]);
967         err = vfs_opendir(path, &vh);
968         free(path);
969         if (err_is_fail(err)) {
970             DEBUG_ERR(err, "in vfs_opendir %s", argv[i]);
971             printf("%s: not found\n", argv[i]);
972             ret = EXIT_FAILURE;
973             continue;
974         }
975
976         if (i > 1) {
977             printf("\n");
978         }
979         printf("%s:\n", argv[i]);
980
981         do {
982             struct vfs_fileinfo info;
983             char *name;
984             err = vfs_dir_read_next(vh, &name, &info);
985             if (err_is_ok(err)) {
986                 printf("%8zu %c %s\n", info.size, vfs_type_char(info.type),
987                        name);
988                 free(name);
989             }
990         } while(err_is_ok(err));
991
992         err = vfs_closedir(vh);
993         if (err_is_fail(err)) {
994             DEBUG_ERR(err, "in vfs_closedir");
995         }
996     }
997
998     return ret;
999 }
1000
1001 static int mkdir(int argc, char *argv[])
1002 {
1003     if(argc != 2) {
1004         printf("Usage: %s dir\n", argv[0]);
1005         return EXIT_FAILURE;
1006     }
1007
1008     char *path = vfs_path_mkabsolute(cwd, argv[1]);
1009     errval_t err = vfs_mkdir(path);
1010     free(path);
1011     if (err_is_fail(err)) {
1012         printf("%s\n", err_getstring(err));
1013         return EXIT_FAILURE;
1014     } else {
1015         return EXIT_SUCCESS;
1016     }
1017 }
1018
1019 static int rmdir(int argc, char *argv[])
1020 {
1021     if(argc != 2) {
1022         printf("Usage: %s dir\n", argv[0]);
1023         return EXIT_FAILURE;
1024     }
1025
1026     char *path = vfs_path_mkabsolute(cwd, argv[1]);
1027     errval_t err = vfs_rmdir(path);
1028     free(path);
1029     if (err_is_fail(err)) {
1030         printf("%s\n", err_getstring(err));
1031         return EXIT_FAILURE;
1032     } else {
1033         return EXIT_SUCCESS;
1034     }
1035 }
1036
1037 static int rm(int argc, char *argv[])
1038 {
1039     if(argc < 2) {
1040         printf("Usage: %s file...\n", argv[0]);
1041         return EXIT_FAILURE;
1042     }
1043
1044     int ret = EXIT_SUCCESS;
1045
1046     for (int i = 1; i < argc; i++) {
1047         char *path = vfs_path_mkabsolute(cwd, argv[i]);
1048         errval_t err = vfs_remove(path);
1049         free(path);
1050         if (err_is_fail(err)) {
1051             printf("%s: %s\n", argv[i], err_getstring(err));
1052             ret = EXIT_FAILURE;
1053         }
1054     }
1055
1056     return ret;
1057 }
1058
1059 static int cd(int argc, char *argv[])
1060 {
1061     errval_t err;
1062
1063     if (argc != 2) {
1064         printf("Usage: %s DIR\n", argv[0]);
1065         return EXIT_FAILURE;
1066     }
1067
1068     char *newcwd = vfs_path_mkabsolute(cwd, argv[1]);
1069
1070     // ensure directory exists, by attempting to open it
1071     vfs_handle_t dh;
1072     err = vfs_opendir(newcwd, &dh);
1073     if (err_is_fail(err)) {
1074         printf("cd to %s (-> %s) failed: %s\n",
1075                argv[1], newcwd, err_getstring(err));
1076         free(newcwd);
1077         return EXIT_FAILURE;
1078     }
1079     vfs_closedir(dh);
1080
1081     // ok!
1082     free(cwd);
1083     cwd = newcwd;
1084
1085     return EXIT_SUCCESS;
1086 }
1087
1088 static int pwd(int argc, char *argv[])
1089 {
1090     printf("%s\n", cwd);
1091     return EXIT_SUCCESS;
1092 }
1093
1094 static int mnfs(int argc, char *argv[])
1095 {
1096     char *args1[2] = { "mkdir", "/nfs" };
1097     mkdir(2, args1);
1098     char *args2[3] = { "mount", "/nfs", "nfs://10.110.4.4/local/nfs" };
1099     return mount(3, args2);
1100 }
1101
1102 /// Open file(s) with a list of commands and execute them
1103 static int src(int argc, char *argv[])
1104 {
1105     if (argc < 2) {
1106         printf("Usage: %s file...\n", argv[0]);
1107     }
1108
1109     int ret = EXIT_SUCCESS;
1110     for (int i = 1; i < argc; i++) {
1111         char *path = vfs_path_mkabsolute(cwd, argv[i]);
1112         FILE *f = fopen(path, "r");
1113         if (!f) {
1114             printf("File %s not found\n", path);
1115             ret = EXIT_FAILURE;
1116             continue;
1117         }
1118         printf("Executing file %s\n", path);
1119
1120         // Read one line at a time, make args out of it and execute it
1121         while (true) {
1122             char buf[1024] = "\0";
1123             char *p = fgets(buf, 1024, f);
1124             if (!p) {
1125                 break;
1126             }
1127
1128             char *q = strrchr(p, '\n');
1129             *q = '\0';
1130             char *cmdstr = strdup(p);
1131             char *cmd_argv[64];
1132             int cmd_argc = makeargs(cmdstr, cmd_argv);
1133             if (cmd_argc == 0) {
1134                 continue;
1135             }
1136
1137             struct cmd *cmd = find_command(cmd_argv[0]);
1138             if (!cmd) {
1139             } else {
1140                 printf("running command: %s\n", p);
1141                 int r = cmd->cmd(cmd_argc, cmd_argv);
1142                 if (r != 0) {
1143                     printf("running command %s failed errorcode %d\n", p, r);
1144                 }
1145             }
1146             free(cmdstr);
1147         }
1148         free(path);
1149     }
1150     return ret;
1151 }
1152
1153 static int freecmd(int argc, char *argv[])
1154 {
1155     struct mem_binding *mc = get_mem_client();
1156     assert(mc != NULL);
1157     errval_t err;
1158     genpaddr_t available, total;
1159
1160     err = ram_available(&available, &total);
1161     if(err_is_fail(err)) {
1162         DEBUG_ERR(err, "available");
1163         return EXIT_FAILURE;
1164     }
1165
1166     printf("Free memory: %" PRIuGENPADDR " bytes\n", available);
1167     printf("Total memory: %" PRIuGENPADDR " bytes\n", total);
1168
1169     return EXIT_SUCCESS;
1170 }
1171
1172 static int nproc(int argc, char* argv[]) {
1173     errval_t err;
1174     size_t count = 0;
1175     char** names = NULL;
1176
1177     static char* spawnds = "r'spawn.[0-9]+' { iref: _ }";
1178     oct_init();
1179
1180     struct octopus_get_names_response__rx_args reply;
1181     struct octopus_binding *r = get_octopus_binding();
1182     err = r->rpc_tx_vtbl.get_names(r, spawnds, NOP_TRIGGER, reply.output,
1183                             &reply.tid, &reply.error_code);
1184     if (err_is_fail(err) || err_is_fail(reply.error_code)) {
1185         DEBUG_ERR(err, "get_names failed");
1186         goto out;
1187     }
1188
1189     err = oct_parse_names(reply.output, &names, &count);
1190     if (err_is_fail(err)) {
1191         DEBUG_ERR(err, "parse_names failed.");
1192         goto out;
1193     }
1194
1195 out:
1196     oct_free_names(names, count);
1197
1198     printf("%zx\n", count);
1199     return EXIT_SUCCESS;
1200 }
1201
1202
1203 static struct cmd commands[] = {
1204     {"help", help, "Output usage information about given shell command"},
1205     {"print_cspace", print_cspace, "Debug print-out of my cspace"},
1206     {"quit", quit, "Quit the shell"},
1207     {"nproc", nproc, "Get amount of cores in system."},
1208     {"ps", ps, "List running processes"},
1209     {"demo", demo, "Run barrelfish demo"},
1210     {"pixels", spawnpixels, "Spawn pixels on all cores"},
1211     {"mnfs", mnfs, "Mount script for NFS on emmentaler"},
1212     {"oncore", oncore, "Start program on specified core"},
1213     {"reset", reset, "Reset machine"},
1214     {"poweroff", poweroff, "Power down machine"},
1215     {"skb", skb, "Send command to system knowledge base"},
1216     {"mount", mount, "Mount file system"},
1217     {"ls", ls, "List directory contents"},
1218     {"cd", cd, "Change working directory"},
1219     {"pwd", pwd, "Print current working directory"},
1220     {"touch", touch, "Create an empty file"},
1221     {"cat", cat, "Print the contents of file(s)"},
1222     {"hd", hd, "Print the contents of file(s) as hexdump"},
1223     {"cat2", cat2, "Print the contents of file(s) into another file"},
1224     {"dd", dd, "copy stuff"},
1225     {"cp", cp, "Copy files"},
1226     {"rm", rm, "Remove files"},
1227     {"mkdir", mkdir, "Create a new directory"},
1228     {"rmdir", rmdir, "Remove an existing directory"},
1229     {"setenv", setenvcmd, "Set environment variables"},
1230     {"src", src, "Execute the list of commands in a file"},
1231     {"printenv", printenv, "Display environment variables"},
1232     {"free", freecmd, "Display amount of free memory in the system"},
1233     {"dump_caps", dump_caps, "Display cspace debug information"},
1234 };
1235
1236 static struct cmd *find_command(const char *name)
1237 {
1238     for(int i = 0; i < ENTRIES(commands); i++) {
1239         struct cmd *cmd = &commands[i];
1240
1241         if(strcmp(name, cmd->name) == 0) {
1242             return cmd;
1243         }
1244     }
1245
1246     return NULL;
1247 }
1248
1249 static int help(int argc, char *argv[])
1250 {
1251     struct cmd *cmd;
1252
1253     if (argc == 1) {
1254         printf("available commands:\n");
1255         for (int i=0; i < ENTRIES(commands); i++) {
1256             printf("%-15s", commands[i].name);
1257             if (((i + 1) % 5) == 0) {
1258                 printf("\n");
1259             }
1260         }
1261         printf("\n");
1262         return EXIT_SUCCESS;
1263     }
1264
1265     if ((cmd = find_command(argv[1])) != NULL) {
1266         printf("%s: %s\n", argv[1], cmd->usage);
1267         return EXIT_SUCCESS;
1268     } else {
1269         printf("%s: %s: command not found\n", argv[0], argv[1]);
1270         return EXIT_FAILURE;
1271     }
1272 }
1273
1274 static int makeargs(char *cmdline, char *argv[])
1275 {
1276     char *p = cmdline;
1277     bool inquote = false;
1278     int argc = 0;
1279
1280     if (p == NULL) {
1281         return 0;
1282     }
1283
1284     while (*p == ' ') {
1285         p++;
1286     }
1287
1288     if (*p == '\0') {
1289         return 0;
1290     }
1291
1292     for(argv[argc++] = p; *p != '\0'; p++) {
1293         if (*p == '"') {
1294             inquote = !inquote;
1295             *p = ' '; // mega-kludge!
1296         } else if (*p == ' ' && !inquote) {
1297             *p++ = '\0';
1298             // Skip any redundant whitespace
1299             while(*p == ' ') {
1300                 p++;
1301             }
1302             if (*p != '\0') {
1303                 argv[argc++] = p;
1304                 if (*p == '"') {
1305                     inquote = true;
1306                     *p = ' '; // mega-kludge
1307                 }
1308             }
1309         }
1310     }
1311
1312     return argc;
1313 }
1314
1315 static uint8_t wait_domain_id(struct capref domainid)
1316 {
1317     uint8_t exitcode;
1318     errval_t err = spawn_wait(domainid, &exitcode, false);
1319     if (err_is_fail(err)) {
1320         USER_PANIC_ERR(err, "spawn_wait");
1321     }
1322     return exitcode;
1323 }
1324
1325 static void runbootscript(void)
1326 {
1327     char cmdstr[1024];
1328     snprintf(cmdstr, 1024,"sh %s", BOOTSCRIPT_NAME);
1329     char *cmd_argv[64];
1330     int cmd_argc = makeargs(cmdstr, cmd_argv);
1331     int ret = src(cmd_argc, cmd_argv);
1332     if (ret != 0) {
1333         snprintf(cmdstr, 1024, "help");
1334         cmd_argc = makeargs(cmdstr, cmd_argv);
1335         help(cmd_argc, cmd_argv);
1336     }
1337 }
1338
1339
1340 int main(int argc, const char *argv[])
1341 {
1342     int         exitcode = 0;
1343     bool        is_bootscript = true;
1344     coreid_t my_core_id = disp_get_core_id();
1345
1346     vfs_init();
1347
1348     for (int i = 1; i < argc; i++) {
1349         if (strcmp(argv[i], "nobootscript") == 0) {
1350             is_bootscript = false;
1351         }
1352     }
1353
1354     cwd = strdup("/");
1355
1356     printf("fish v0.2 -- pleased to meet you!\n");
1357
1358     // run canned pre-boot commands
1359     if (is_bootscript) {
1360         runbootscript();
1361     }
1362
1363     struct terminal_state *ts = get_terminal_state();
1364     term_client_blocking_config(&ts->client, TerminalConfig_CTRLC, false);
1365     linenoiseHistorySetMaxLen(1024);
1366
1367     // Create inherit CNode to pass session cap to programs spawned from fish
1368     errval_t err;
1369     err = alloc_inheritcn_with_caps(&inheritcn_cap, NULL_CAP, cap_sessionid, NULL_CAP);
1370     if (err_is_fail(err)) {
1371         USER_PANIC_ERR(err, "Error allocating inherit CNode with session cap.");
1372     }
1373
1374     for (;;) {
1375         char* input = NULL;
1376         int cmd_argc;
1377         char *cmd_argv[64]; // Support a max of 64 cmd args
1378         struct cmd *cmd;
1379
1380         input = linenoise("> ");
1381         if (input == NULL || input[0] == '\0') {
1382             continue;
1383         }
1384
1385         linenoiseHistoryAdd(input); /* Add to the history. */
1386         linenoiseHistorySave("history.txt"); /* Save the history on disk. */
1387         cmd_argc = makeargs(input, cmd_argv);
1388
1389         /* check for trailing '&' (== run in background) */
1390         bool wait = true;
1391         if (cmd_argc > 0) {
1392             size_t len = strlen(cmd_argv[cmd_argc - 1]);
1393             if (len > 0 && cmd_argv[cmd_argc - 1][len - 1] == '&') {
1394                 wait = false;
1395                 // remove '&' character from args
1396                 if (len == 1) {
1397                     cmd_argc--;
1398                 } else {
1399                     cmd_argv[cmd_argc - 1][len - 1] = '\0';
1400                 }
1401             }
1402         }
1403
1404         if (cmd_argc == 0) {
1405             continue;
1406         } else if ((cmd = find_command(cmd_argv[0])) != NULL) {
1407             exitcode = cmd->cmd(cmd_argc, cmd_argv);
1408         } else {
1409             // Try loading a program off disk if VFS is initialized
1410             struct capref domain_cap;
1411             exitcode = execute_program(my_core_id, cmd_argc, cmd_argv, &domain_cap);
1412
1413             // wait if it succeeds
1414             if (exitcode == 0 && wait) {
1415                 exitcode = wait_domain_id(domain_cap);
1416                 char exitstr[128];
1417                 snprintf(exitstr, 128, "%u", exitcode);
1418                 int r = setenv("EXITCODE", exitstr, 1);
1419                 assert(r == 0);
1420             }
1421         }
1422
1423         free(input);
1424     }
1425 }