Cleaning up the fish mess.
[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_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 static int cat2(int argc, char *argv[])
508 {
509     errval_t err;
510     char *path;
511     int ret;
512
513     if(argc < 3) {
514         printf("Usage: %s [input-files...] output-file\n", argv[0]);
515         return EXIT_FAILURE;
516     }
517
518     /* Open output file creating it if it does not exist */
519     path = vfs_path_mkabsolute(cwd, argv[argc - 1]);
520     vfs_handle_t output_vh;
521     err = vfs_create(path, &output_vh);
522     free(path);
523     if (err_is_fail(err)) {
524         DEBUG_ERR(err, "error opening output file");
525         return EXIT_FAILURE;
526     }
527
528     /* Open input files, read buffer and write to output file */
529     for (int i = 1; i < argc - 1; i++) {
530         uint8_t buf[1024];
531         size_t size;
532         vfs_handle_t input_vh;
533         path = vfs_path_mkabsolute(cwd, argv[i]);
534         err = vfs_open(path, &input_vh);
535         free(path);
536         if (err_is_fail(err)) {
537             printf("%s: file not found\n", argv[i]);
538             ret = EXIT_FAILURE;
539             continue;
540         }
541
542         do {
543             err = vfs_read(input_vh, buf, sizeof(buf), &size);
544             if (err_is_fail(err)) {
545                 // XXX: Close any files that might be open
546                 DEBUG_ERR(err, "error reading file");
547                 return EXIT_FAILURE;
548             }
549
550             size_t output_size;
551             err = vfs_write(output_vh, buf, size, &output_size);
552             if (err_is_fail(err)) {
553                 // XXX: Close any files that might be open
554                 DEBUG_ERR(err, "error writing to output file");
555                 return EXIT_FAILURE;
556             }
557             if (output_size != size) {
558                 printf("Wanted to write %zu but only wrote %zu, aborting\n",
559                        size, output_size);
560                 // XXX: Close any files that might be open
561                 return EXIT_FAILURE;
562             }
563         } while(size > 0);
564
565         err = vfs_close(input_vh);
566         if (err_is_fail(err)) {
567             DEBUG_ERR(err, "in vfs_close");
568         }
569     }
570
571     err = vfs_close(output_vh);
572     if (err_is_fail(err)) {
573         DEBUG_ERR(err, "in vfs_close");
574     }
575     return ret;
576 }
577
578 static int cp(int argc, char *argv[])
579 {
580     if (argc != 3) {
581         printf("Usage: %s src dest\n", argv[0]);
582         return EXIT_FAILURE;
583     }
584
585     static uint8_t buf[32768];
586     size_t rsize, wsize;
587     vfs_handle_t src = NULL, dst = NULL;
588     errval_t err;
589     int ret = EXIT_SUCCESS;
590
591     char *path = vfs_path_mkabsolute(cwd, argv[1]);
592     err = vfs_open(path, &src);
593     free(path);
594     if (err_is_fail(err)) {
595         printf("%s: %s\n", argv[1], err_getstring(err));
596         return EXIT_FAILURE;
597     }
598
599     path = vfs_path_mkabsolute(cwd, argv[2]);
600     err = vfs_create(path, &dst);
601     free(path);
602     if (err_is_fail(err)) {
603         printf("%s: %s\n", argv[2], err_getstring(err));
604         ret = EXIT_FAILURE;
605         goto out;
606     }
607
608     err = vfs_truncate(dst, 0);
609     if (err_is_fail(err)) {
610         printf("truncate %s: %s\n", argv[2], err_getstring(err));
611         ret = EXIT_FAILURE;
612         goto out;
613     }
614
615     do {
616         err = vfs_read(src, buf, sizeof(buf), &rsize);
617         if (err_is_fail(err)) {
618             DEBUG_ERR(err, "error reading file");
619             ret = EXIT_FAILURE;
620             goto out;
621         }
622
623         size_t wpos = 0;
624         while (wpos < rsize) {
625             err = vfs_write(dst, &buf[wpos], rsize - wpos, &wsize);
626             if (err_is_fail(err) || wsize == 0) {
627                 DEBUG_ERR(err, "error writing file");
628                 ret = EXIT_FAILURE;
629                 goto out;
630             }
631             wpos += wsize;
632         }
633     } while(rsize > 0);
634
635 out:
636     if (src != NULL) {
637         err = vfs_close(src);
638         if (err_is_fail(err)) {
639             DEBUG_ERR(err, "in vfs_close");
640         }
641     }
642
643     if (dst != NULL) {
644         err = vfs_close(dst);
645         if (err_is_fail(err)) {
646             DEBUG_ERR(err, "in vfs_close");
647         }
648     }
649
650     return ret;
651 }
652
653 static int dd(int argc, char *argv[])
654 {
655     // parse options
656     char *source = NULL;
657     char *target = NULL;
658
659     vfs_handle_t source_vh = NULL;
660     vfs_handle_t target_vh = NULL;
661     
662     size_t blocksize = 512;
663     size_t count = 0;
664     size_t skip = 0;
665     size_t seek = 0;
666
667     size_t rsize = 0;
668     size_t wsize = 0;
669     size_t blocks_written = 0;
670
671     size_t total_bytes_read = 0;
672     size_t total_bytes_written = 0;
673
674     size_t progress = 0;
675
676     errval_t err;
677     int ret = EXIT_SUCCESS;
678
679     for (int i = 1; i < argc; i++)
680     {
681         if (!strncmp(argv[i], "bs=", 3))
682             blocksize = atoi(argv[i] + 3);
683
684         else if (!strncmp(argv[i], "count=", 6))
685             count = atoi(argv[i] + 6);
686
687         else if (!strncmp(argv[i], "skip=", 5))
688             skip = atoi(argv[i] + 5);
689
690         else if (!strncmp(argv[i], "seek=", 5))
691             seek = atoi(argv[i] + 5);
692
693         else if (!strncmp(argv[i], "if=", 3))
694             source = (argv[i] + 3);
695
696         else if (!strncmp(argv[i], "of=", 3))
697             target = (argv[i] + 3);
698         else if (!strncmp(argv[i], "progress", 8))
699             progress = 1;
700     }
701
702     size_t one_per_cent = (blocksize * count) / 100;
703
704     printf("from: %s to: %s bs=%zd count=%zd seek=%zd skip=%zd\n", source, target, blocksize, count, seek, skip);
705
706     if (source != NULL)
707     {
708         char *path = vfs_path_mkabsolute(cwd, source);
709         err = vfs_open(path, &source_vh);
710         free(path);
711         if (err_is_fail(err)) {
712             printf("%s: %s\n", source, err_getstring(err));
713             return EXIT_FAILURE;
714         }
715
716         if (skip != 0)
717         {
718             // TODO: skip
719         }
720     }
721
722     if (target != NULL)
723     {
724         char *path = vfs_path_mkabsolute(cwd, target);
725         err = vfs_create(path, &target_vh);
726         free(path);
727         if (err_is_fail(err)) {
728             // close source handle
729             if (source_vh != NULL)
730                 vfs_close(source_vh);
731             printf("%s: %s\n", target, err_getstring(err));
732             return EXIT_FAILURE;
733         }
734
735         if (seek != 0)
736         {
737             // TODO: seek
738         }
739     }
740
741     uint8_t * buffer = malloc(blocksize);
742
743 #if defined(__x86_64__) || defined(__i386__)
744     uint64_t tscperms;
745     err = sys_debug_get_tsc_per_ms(&tscperms);
746     assert(err_is_ok(err));
747
748     //printf("ticks per millisec: %" PRIu64 "\n", tscperms);
749     uint64_t start = rdtsc();
750 #endif
751
752     if (buffer == NULL)
753     {
754         ret = EXIT_FAILURE;
755         printf("failed to allocate buffer of size %zd\n", blocksize);
756         goto out;
757     }
758
759     do
760     {
761         //printf("copying block\n");
762         size_t read_bytes = 0;
763         do {
764             err = vfs_read(source_vh, buffer, blocksize, &rsize);
765             if (err_is_fail(err)) {
766                 DEBUG_ERR(err, "error reading file");
767                 ret = EXIT_FAILURE;
768                 goto out;
769             }
770
771             total_bytes_read += rsize;
772             read_bytes += rsize;
773
774             size_t wpos = 0;
775             while (wpos < rsize) {
776                 if (wpos > 0)
777                     printf("was unable to write the whole chunk of size %zd. Now at pos: %zd of buffer\n", rsize, wpos);
778
779                 err = vfs_write(target_vh, &buffer[wpos], rsize - wpos, &wsize);
780                 if (err_is_fail(err) || wsize == 0) {
781                     DEBUG_ERR(err, "error writing file");
782                     ret = EXIT_FAILURE;
783                     goto out;
784                 }
785                 wpos += wsize;
786                 total_bytes_written += wsize;
787             }
788         } while(read_bytes < blocksize);
789
790         blocks_written++;
791
792         if (progress && one_per_cent && total_bytes_written % one_per_cent == 0) {
793             printf(".");
794         }
795
796         //printf("block successfully copied. read: %zd. blocks written: %zd\n", rsize, blocks_written);
797     } while (rsize > 0 && !(count > 0 && blocks_written >= count));
798
799     if (progress) printf("\n");
800
801 out:
802     if (buffer != NULL)
803         free(buffer);
804     
805     if (source_vh != NULL) {
806         err = vfs_close(source_vh);
807         if (err_is_fail(err)) {
808             DEBUG_ERR(err, "in vfs_close");
809         }
810     }
811
812     if (target_vh != NULL) {
813         err = vfs_close(target_vh);
814         if (err_is_fail(err)) {
815             DEBUG_ERR(err, "in vfs_close");
816         }
817     }
818
819 #if defined(__x86_64__) || defined(__i386__)
820     uint64_t stop = rdtsc();
821     uint64_t elapsed_msecs = ((stop - start) / tscperms);
822     double elapsed_secs = (double)elapsed_msecs/1000.0;
823
824     printf("start: %" PRIu64 " stop: %" PRIu64 "\n", start, stop);
825
826     double kbps = ((double)total_bytes_written / 1024.0) / elapsed_secs;
827
828     printf("%zd bytes read. %zd bytes written. %f s, %f kB/s\n", total_bytes_read, total_bytes_written, elapsed_secs, kbps); 
829 #else
830     printf("%zd bytes read. %zd bytes written.\n", total_bytes_read, total_bytes_written); 
831 #endif
832
833     return ret;
834 }
835
836 static int touch(int argc, char *argv[])
837 {
838     if(argc < 2) {
839         printf("Usage: %s [file...]\n", argv[0]);
840         return EXIT_FAILURE;
841     }
842
843     vfs_handle_t vh;
844     errval_t err;
845     int ret = EXIT_SUCCESS;
846
847     for (int i = 1; i < argc; i++) {
848         char *path = vfs_path_mkabsolute(cwd, argv[i]);
849         err = vfs_create(path, &vh);
850         free(path);
851         if (err_is_fail(err)) {
852             printf("%s: %s\n", argv[i], err_getstring(err));
853             DEBUG_ERR(err, "vfs_create failed");
854             ret = EXIT_FAILURE;
855             continue;
856         }
857
858         err = vfs_close(vh);
859         if (err_is_fail(err)) {
860             DEBUG_ERR(err, "in vfs_close");
861         }
862     }
863
864     return ret;
865 }
866
867 static char vfs_type_char(enum vfs_filetype type)
868 {
869     switch(type) {
870     case VFS_FILE:
871         return '-';
872     case VFS_DIRECTORY:
873         return 'd';
874     default:
875         return '?';
876     }
877 }
878
879 static int ls(int argc, char *argv[])
880 {
881     errval_t err;
882     int ret = EXIT_SUCCESS;
883
884     // XXX: cheat and assume we have some extra space wherever argv lives
885     if (argc <= 1) {
886         argv[1] = cwd;
887         argc = 2;
888     }
889
890     for (int i = 1; i < argc; i++) {
891         vfs_handle_t vh;
892         char *path = vfs_path_mkabsolute(cwd, argv[i]);
893         err = vfs_opendir(path, &vh);
894         free(path);
895         if (err_is_fail(err)) {
896             DEBUG_ERR(err, "in vfs_opendir %s", argv[i]);
897             printf("%s: not found\n", argv[i]);
898             ret = EXIT_FAILURE;
899             continue;
900         }
901
902         if (i > 1) {
903             printf("\n");
904         }
905         printf("%s:\n", argv[i]);
906
907         do {
908             struct vfs_fileinfo info;
909             char *name;
910             err = vfs_dir_read_next(vh, &name, &info);
911             if (err_is_ok(err)) {
912                 printf("%8zu %c %s\n", info.size, vfs_type_char(info.type),
913                        name);
914                 free(name);
915             }
916         } while(err_is_ok(err));
917
918         err = vfs_closedir(vh);
919         if (err_is_fail(err)) {
920             DEBUG_ERR(err, "in vfs_closedir");
921         }
922     }
923
924     return ret;
925 }
926
927 static int mkdir(int argc, char *argv[])
928 {
929     if(argc != 2) {
930         printf("Usage: %s dir\n", argv[0]);
931         return EXIT_FAILURE;
932     }
933
934     char *path = vfs_path_mkabsolute(cwd, argv[1]);
935     errval_t err = vfs_mkdir(path);
936     free(path);
937     if (err_is_fail(err)) {
938         printf("%s\n", err_getstring(err));
939         return EXIT_FAILURE;
940     } else {
941         return EXIT_SUCCESS;
942     }
943 }
944
945 static int rmdir(int argc, char *argv[])
946 {
947     if(argc != 2) {
948         printf("Usage: %s dir\n", argv[0]);
949         return EXIT_FAILURE;
950     }
951
952     char *path = vfs_path_mkabsolute(cwd, argv[1]);
953     errval_t err = vfs_rmdir(path);
954     free(path);
955     if (err_is_fail(err)) {
956         printf("%s\n", err_getstring(err));
957         return EXIT_FAILURE;
958     } else {
959         return EXIT_SUCCESS;
960     }
961 }
962
963 static int rm(int argc, char *argv[])
964 {
965     if(argc < 2) {
966         printf("Usage: %s file...\n", argv[0]);
967         return EXIT_FAILURE;
968     }
969
970     int ret = EXIT_SUCCESS;
971
972     for (int i = 1; i < argc; i++) {
973         char *path = vfs_path_mkabsolute(cwd, argv[i]);
974         errval_t err = vfs_remove(path);
975         free(path);
976         if (err_is_fail(err)) {
977             printf("%s: %s\n", argv[i], err_getstring(err));
978             ret = EXIT_FAILURE;
979         }
980     }
981
982     return ret;
983 }
984
985 static int cd(int argc, char *argv[])
986 {
987     errval_t err;
988
989     if (argc != 2) {
990         printf("Usage: %s DIR\n", argv[0]);
991         return EXIT_FAILURE;
992     }
993
994     char *newcwd = vfs_path_mkabsolute(cwd, argv[1]);
995
996     // ensure directory exists, by attempting to open it
997     vfs_handle_t dh;
998     err = vfs_opendir(newcwd, &dh);
999     if (err_is_fail(err)) {
1000         printf("cd to %s (-> %s) failed: %s\n",
1001                argv[1], newcwd, err_getstring(err));
1002         free(newcwd);
1003         return EXIT_FAILURE;
1004     }
1005     vfs_closedir(dh);
1006
1007     // ok!
1008     free(cwd);
1009     cwd = newcwd;
1010
1011     return EXIT_SUCCESS;
1012 }
1013
1014 static int pwd(int argc, char *argv[])
1015 {
1016     printf("%s\n", cwd);
1017     return EXIT_SUCCESS;
1018 }
1019
1020 static int mnfs(int argc, char *argv[])
1021 {
1022     char *args1[2] = { "mkdir", "/nfs" };
1023     mkdir(2, args1);
1024     char *args2[3] = { "mount", "/nfs", "nfs://10.110.4.4/local/nfs" };
1025     return mount(3, args2);
1026 }
1027
1028 /// Open file(s) with a list of commands and execute them
1029 static int src(int argc, char *argv[])
1030 {
1031     if (argc < 2) {
1032         printf("Usage: %s file...\n", argv[0]);
1033     }
1034
1035     int ret = EXIT_SUCCESS;
1036     for (int i = 1; i < argc; i++) {
1037         char *path = vfs_path_mkabsolute(cwd, argv[i]);
1038         FILE *f = fopen(path, "r");
1039         if (!f) {
1040             printf("File %s not found\n", path);
1041             ret = EXIT_FAILURE;
1042             continue;
1043         }
1044         printf("Executing file %s\n", path);
1045
1046         // Read one line at a time, make args out of it and execute it
1047         while (true) {
1048             char buf[1024] = "\0";
1049             char *p = fgets(buf, 1024, f);
1050             if (!p) {
1051                 break;
1052             }
1053
1054             char *q = strrchr(p, '\n');
1055             *q = '\0';
1056             char *cmdstr = strdup(p);
1057             char *cmd_argv[64];
1058             int cmd_argc = makeargs(cmdstr, cmd_argv);
1059             if (cmd_argc == 0) {
1060                 continue;
1061             }
1062
1063             struct cmd *cmd = find_command(cmd_argv[0]);
1064             if (!cmd) {
1065             } else {
1066                 printf("running command: %s\n", p);
1067                 int r = cmd->cmd(cmd_argc, cmd_argv);
1068                 if (r != 0) {
1069                     printf("running command %s failed errorcode %d\n", p, r);
1070                 }
1071             }
1072             free(cmdstr);
1073         }
1074         free(path);
1075     }
1076     return ret;
1077 }
1078
1079 static int freecmd(int argc, char *argv[])
1080 {
1081     struct mem_rpc_client *mc = get_mem_client();
1082     assert(mc != NULL);
1083     errval_t err;
1084     genpaddr_t available, total;
1085
1086     err = ram_available(&available, &total);
1087     if(err_is_fail(err)) {
1088         DEBUG_ERR(err, "available");
1089         return EXIT_FAILURE;
1090     }
1091
1092     printf("Free memory: %" PRIuGENPADDR " bytes\n", available);
1093     printf("Total memory: %" PRIuGENPADDR " bytes\n", total);
1094
1095     return EXIT_SUCCESS;
1096 }
1097
1098 static int nproc(int argc, char* argv[]) {
1099     errval_t err, error_code;
1100     octopus_trigger_id_t tid;
1101     size_t count = 0;
1102     char** names = NULL;
1103     char* buffer;
1104
1105     static char* spawnds = "r'spawn.[0-9]+' { iref: _ }";
1106     oct_init();
1107
1108     struct octopus_rpc_client *r = get_octopus_rpc_client();
1109     err = r->vtbl.get_names(r, spawnds, NOP_TRIGGER, &buffer, &tid, &error_code);
1110     if (err_is_fail(err) || err_is_fail(error_code)) {
1111         DEBUG_ERR(err, "get_names failed");
1112         goto out;
1113     }
1114
1115     err = oct_parse_names(buffer, &names, &count);
1116     if (err_is_fail(err)) {
1117         DEBUG_ERR(err, "parse_names failed.");
1118         goto out;
1119     }
1120
1121 out:
1122     free(buffer);
1123     oct_free_names(names, count);
1124
1125     printf("%zx\n", count);
1126     return EXIT_SUCCESS;
1127 }
1128
1129
1130 static struct cmd commands[] = {
1131     {"help", help, "Output usage information about given shell command"},
1132     {"print_cspace", print_cspace, "Debug print-out of my cspace"},
1133     {"quit", quit, "Quit the shell"},
1134     {"nproc", nproc, "Get amount of cores in system."},
1135     {"ps", ps, "List running processes"},
1136     {"demo", demo, "Run barrelfish demo"},
1137     {"pixels", spawnpixels, "Spawn pixels on all cores"},
1138     {"mnfs", mnfs, "Mount script for NFS on emmentaler"},
1139     {"oncore", oncore, "Start program on specified core"},
1140     {"reset", reset, "Reset machine"},
1141     {"poweroff", poweroff, "Power down machine"},
1142     {"skb", skb, "Send command to system knowledge base"},
1143     {"mount", mount, "Mount file system"},
1144     {"ls", ls, "List directory contents"},
1145     {"cd", cd, "Change working directory"},
1146     {"pwd", pwd, "Print current working directory"},
1147     {"touch", touch, "Create an empty file"},
1148     {"cat", cat, "Print the contents of file(s)"},
1149     {"cat2", cat2, "Print the contents of file(s) into another file"},
1150     {"dd", dd, "copy stuff"},
1151     {"cp", cp, "Copy files"},
1152     {"rm", rm, "Remove files"},
1153     {"mkdir", mkdir, "Create a new directory"},
1154     {"rmdir", rmdir, "Remove an existing directory"},
1155     {"setenv", setenvcmd, "Set environment variables"},
1156     {"src", src, "Execute the list of commands in a file"},
1157     {"printenv", printenv, "Display environment variables"},
1158     {"free", freecmd, "Display amount of free memory in the system"},
1159 };
1160
1161 static void getline(char *input, size_t size)
1162 {
1163     int i = 0, in;
1164
1165     do {
1166         in = getchar();
1167         if (in == '\b' || in == 0x7f /* DEL */) {
1168             if (i > 0) {
1169                 i--;
1170                 putchar('\b'); // FIXME: this kinda works on my terminal
1171                 putchar(' ');
1172                 putchar('\b');
1173                 //fputs("\033[1X", stdout); // XXX: suitable for xterm
1174             }
1175         } else if (in != '\n' && i < size - 1) {
1176             input[i++] = in;
1177         }
1178         fflush(stdout);
1179     } while (in != '\n');
1180     assert(i < size);
1181     input[i] = '\0';
1182     putchar('\n');
1183     fflush(stdout);
1184 }
1185
1186 static struct cmd *find_command(const char *name)
1187 {
1188     for(int i = 0; i < ENTRIES(commands); i++) {
1189         struct cmd *cmd = &commands[i];
1190
1191         if(strcmp(name, cmd->name) == 0) {
1192             return cmd;
1193         }
1194     }
1195
1196     return NULL;
1197 }
1198
1199 static int help(int argc, char *argv[])
1200 {
1201     struct cmd *cmd;
1202
1203     if (argc == 1) {
1204         printf("available commands:\n");
1205         for (int i=0; i < ENTRIES(commands); i++) {
1206             printf("%-15s", commands[i].name);
1207             if (((i + 1) % 5) == 0) {
1208                 printf("\n");
1209             }
1210         }
1211         printf("\n");
1212         return EXIT_SUCCESS;
1213     }
1214
1215     if ((cmd = find_command(argv[1])) != NULL) {
1216         printf("%s: %s\n", argv[1], cmd->usage);
1217         return EXIT_SUCCESS;
1218     } else {
1219         printf("%s: %s: command not found\n", argv[0], argv[1]);
1220         return EXIT_FAILURE;
1221     }
1222 }
1223
1224 static int makeargs(char *cmdline, char *argv[])
1225 {
1226     char *p = cmdline;
1227     bool inquote = false;
1228     int argc = 0;
1229
1230     if (p == NULL) {
1231         return 0;
1232     }
1233
1234     while (*p == ' ') {
1235         p++;
1236     }
1237
1238     if (*p == '\0') {
1239         return 0;
1240     }
1241
1242     for(argv[argc++] = p; *p != '\0'; p++) {
1243         if (*p == '"') {
1244             inquote = !inquote;
1245             *p = ' '; // mega-kludge!
1246         } else if (*p == ' ' && !inquote) {
1247             *p++ = '\0';
1248             // Skip any redundant whitespace
1249             while(*p == ' ') {
1250                 p++;
1251             }
1252             if (*p != '\0') {
1253                 argv[argc++] = p;
1254                 if (*p == '"') {
1255                     inquote = true;
1256                     *p = ' '; // mega-kludge
1257                 }
1258             }
1259         }
1260     }
1261
1262     return argc;
1263 }
1264
1265 static uint8_t wait_domain_id(domainid_t domainid)
1266 {
1267     uint8_t exitcode;
1268     errval_t err = spawn_wait(domainid, &exitcode, false);
1269     if (err_is_fail(err)) {
1270         USER_PANIC_ERR(err, "spawn_wait");
1271     }
1272     return exitcode;
1273 }
1274
1275 static void runbootscript(void)
1276 {
1277     char cmdstr[1024];
1278     snprintf(cmdstr, 1024,"sh %s", BOOTSCRIPT_NAME);
1279     char *cmd_argv[64];
1280     int cmd_argc = makeargs(cmdstr, cmd_argv);
1281     int ret = src(cmd_argc, cmd_argv);
1282     if (ret != 0) {
1283         snprintf(cmdstr, 1024, "help");
1284         cmd_argc = makeargs(cmdstr, cmd_argv);
1285         help(cmd_argc, cmd_argv);
1286     }
1287 }
1288
1289
1290 int main(int argc, const char *argv[])
1291 {
1292     char        input[MAX_LINE];
1293     int         exitcode = 0;
1294     bool        is_bootscript = true;
1295     coreid_t my_core_id = disp_get_core_id();
1296
1297     vfs_init();
1298
1299     for (int i = 1; i < argc; i++) {
1300         if (strcmp(argv[i], "nobootscript") == 0) {
1301             is_bootscript = false;
1302         }
1303     }
1304
1305     cwd = strdup("/");
1306
1307     printf("fish v0.2 -- pleased to meet you!\n");
1308
1309     // run canned pre-boot commands
1310     if (is_bootscript) {
1311         runbootscript();
1312     }
1313
1314     for (;;) {
1315         int             cmd_argc;
1316         char            *cmd_argv[64];      // Support a max of 64 cmd args
1317         struct cmd      *cmd;
1318
1319         printf("> ");
1320         fflush(stdout);
1321         getline(input, MAX_LINE);
1322         cmd_argc = makeargs(input, cmd_argv);
1323
1324         /* check for trailing '&' (== run in background) */
1325         bool wait = true;
1326         if (cmd_argc > 0) {
1327             size_t len = strlen(cmd_argv[cmd_argc - 1]);
1328             if (len > 0 && cmd_argv[cmd_argc - 1][len - 1] == '&') {
1329                 wait = false;
1330                 // remove '&' character from args
1331                 if (len == 1) {
1332                     cmd_argc--;
1333                 } else {
1334                     cmd_argv[cmd_argc - 1][len - 1] = '\0';
1335                 }
1336             }
1337         }
1338
1339         if (cmd_argc == 0) {
1340             continue;
1341         } else if ((cmd = find_command(cmd_argv[0])) != NULL) {
1342             exitcode = cmd->cmd(cmd_argc, cmd_argv);
1343         } else {
1344             // Try loading a program off disk if VFS is initialized
1345             domainid_t domain_id;
1346             exitcode = execute_program(my_core_id, cmd_argc, cmd_argv, &domain_id);
1347
1348             // wait if it succeeds
1349             if (exitcode == 0 && wait) {
1350                 exitcode = wait_domain_id(domain_id);
1351                 char exitstr[128];
1352                 snprintf(exitstr, 128, "%u", exitcode);
1353                 int r = setenv("EXITCODE", exitstr, 1);
1354                 assert(r == 0);
1355             }
1356         }
1357     }
1358 }