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