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