Goto sanos source index

//
// cmds.c
//
// Shell commands
//
// Copyright (C) 2012 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
// SUCH DAMAGE.
// 

#include <os.h>
#include <time.h>

#include "sh.h"

#define BUFSIZE 4096

static void display_buffer(FILE *f, char *buffer, int size) {
  char *p;
  char *q;
  char *end;

  p = buffer;
  end = buffer + size;
  while (p < end) {
    q = p;
    while (q < end && *q != '\n') q++;

    if (p != q) fwrite(p, q - p, 1, f);
    if (q < end) {
      fwrite("\r\n", 2, 1, f);
      q++;
    }
    p = q;
  }
}

static int read_line(int f, char *buf) {
  char c;
  int rc;
  int count;

  count = 0;
  while ((rc = read(f, &c, 1)) > 0) {
    if (c == '\r') continue;
    if (c == '\n') {
      *buf++ = 0;
      return count;
    }
    *buf++ = c;
    count++;
  }

  *buf = 0;
  return rc;
}

static int httpget(char *server, char *path, char *filename) {
  struct hostent *hp;
  struct sockaddr_in sin;
  struct utsname sys;
  int s;
  int rc;
  int n;
  int count;
  int f;
  char *buf;

  memset(&sys, 0, sizeof(struct utsname));
  uname(&sys);
  
  hp = gethostbyname(server);
  if (!hp) return -errno;

  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s < 0) return s;

  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  memcpy(&sin.sin_addr, hp->h_addr_list[0], hp->h_length);
  sin.sin_port = htons(80);

  rc = connect(s, (struct sockaddr *) &sin, sizeof(sin));
  if (rc  < 0) {
    close(s);
    return rc;
  }

  buf = malloc(8 * 1024);
  if (!buf) return -ENOMEM;

  sprintf(buf, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: httpget/1.0 %s/%s (Build %s; CPU %s)\r\n\r\n", 
          path, server, sys.sysname, sys.release, sys.version, sys.machine);
  rc = send(s, buf, strlen(buf), 0);
  if (rc < 0) {
    close(s);
    return rc;
  }

  while ((rc = read_line(s, buf)) > 0) {
    //printf("hdr %s\n", buf);
  }

  if (rc < 0) return rc;

  f = open(filename, O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
  if (f < 0) {
    close(s);
    free(buf);
    return f;
  }

  count = 0;
  while ((n = recv(s, buf, 8 * 1024, 0)) > 0) {
    write(f, buf, n);
    count += n;
    //printf("len %d\n", n);
  }

  if (rc < 0) {
    close(s);
    close(f);
    free(buf);
    return n;
  }

  close(s);
  close(f);
  free(buf);

  return count;
}

shellcmd(basename) {
  if (argc != 2) {
    printf("usage: basename FILE\n");
    return -EINVAL;
  }
  printf("%s\n", basename(argv[1]));
  return 0;
}

shellcmd(beep) {
  return ioctl(1, IOCTL_BEEP, NULL, 0);
}

shellcmd(bye) {
  exitos(EXITOS_POWEROFF);
  return 0;
}

shellcmd(cat) {
  int count;
  int fd;
  int i;
  char *filename;
  char buffer[BUFSIZE];

  if (argc == 1) {
    while ((count = read(fdin, buffer, sizeof buffer)) > 0) {
      write(fdout, buffer, count);
    }
  } else {
    for (i = 1; i < argc; i++) {
      filename = argv[i];
      if ((fd = open(filename, O_BINARY)) < 0) {
        fprintf(stderr, "%s: %s\n", filename, strerror(fd));
        return 1;
      }
      while ((count = read(fd, buffer, sizeof buffer)) > 0) {
        fwrite(buffer, 1, count, stdout);
      }
      close(fd);
    }
  }
  return 0;
}

shellcmd(cls) {
  printf("\033c\f");
  return 0;
}

shellcmd(dirname) {
  if (argc != 2) {
    printf("usage: dirname FILE\n");
    return -EINVAL;
  }
  printf("%s\n", dirname(argv[1]));
  return 0;
}

shellcmd(glob) {
  glob_t globbuf;
  int i;
  int rc;

  if (argc > 1) {
    for (i = 1; i < argc; i++) {
      rc = glob(argv[i], i > 1 ? GLOB_APPEND : 0, NULL, &globbuf);
      if (rc < 0) printf("glob(%s) returned %d\n", argv[i], rc);
    }

    for (i = 0; i < (int) globbuf.gl_pathc; i++) {
      printf("%s\n", globbuf.gl_pathv[i]);
    }
    globfree(&globbuf);
  }

  return 0;
}

shellcmd(ln) {
  char *target;
  char *linkname;

  if (argc != 3) {
    printf("usage: ln <target> <link name>\n");
    return -EINVAL;
  }
  target = argv[1];
  linkname = argv[2];

  if (link(target, linkname) < 0) {
    perror(target);
    return -1;
  }

  return 0;
}

shellcmd(crypt) {
  char *pw;
  char *salt;
  char saltc[3];

  if (argc != 2 && argc != 3) {
    printf("usage: crypt <pw> [<salt>]\n");
    return -EINVAL;
  }

  pw = argv[1];
  if (argc == 3) {
    salt = argv[2];
  } else {
    int i;
    salt = saltc;
    for (i = 0; i < 2; i++) {
      saltc[i] = (char) (random() & 077) + '.';
      if (saltc[i] > '9') saltc[i] += 7;
      if (saltc[i] > 'Z') saltc[i] += 6;
    }
    saltc[2] = 0;
  }

  printf("%s\n", crypt(pw, salt));
  return 0;
}

shellcmd(date) {
  time_t t;
  struct tm tm;

  t = time(NULL);
  gmtime_r(&t, &tm);

  if (argc > 1) {
    char buffer[256];

    strftime(buffer, 256, argv[1], &tm);
    printf("%s\n", buffer);
  } else {
    printf("Time is %04d/%02d/%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
  }
  return 0;
}

shellcmd(debug) {
  struct peb *peb = gettib()->peb;

  if (argc > 1) {
    if (strcmp(argv[1], "on") == 0) {
      peb->debug = 1;
    } else if (strcmp(argv[1], "off") == 0) {
      peb->debug = 0;
    } else {
      printf("usage: debug [on|off]\n");
      return -EINVAL;
    }
  }

  printf("debug %s\n", peb->debug ? "on" : "off");
  return 0;
}

shellcmd(df) {
  int count;
  int rc;
  int n;
  struct statfs *buf;
  struct statfs *b;

  count = getfsstat(NULL, 0);
  if (count < 0) {
    printf("df: %s\n", strerror(errno));
    return -1;
  }

  buf = (struct statfs *) malloc(count * sizeof(struct statfs));
  rc = getfsstat(buf, count * sizeof(struct statfs));
  if (rc < 0) {
    printf("df: %s\n", strerror(errno));
    return -1;
  }

  printf("type   mounted on mounted from          cache    total     used    avail files\n");
  printf("------ ---------- -------------------- ------ -------- -------- -------- -----\n");

  for (n = 0; n < count; n++) {
    b = buf + n;

    printf("%-7s%-11s%-20s", b->fstype, b->mntto, b->mntfrom);
    if (b->blocks != -1) {
      printf("%6dK", b->cachesize / 1024);
      if (b->blocks * (b->bsize / 512) / 2 > 10000) {
        printf("%8dM", b->blocks * (b->bsize / 512) / 2000);
      } else {
        printf("%8dK", b->blocks * (b->bsize / 512) / 2);
      }

      if ((b->blocks - b->bfree) * (b->bsize / 512) / 2 > 10000) {
        printf("%8dM", (b->blocks - b->bfree) * (b->bsize / 512) / 2000);
      } else {
        printf("%8dK", (b->blocks - b->bfree) * (b->bsize / 512) / 2);
      }

      if (b->bfree * (b->bsize / 512) / 2 > 10000) {
         printf("%8dM", b->bfree * (b->bsize / 512) / 2000);
      } else {
        printf("%8dK", b->bfree * (b->bsize / 512) / 2);
      }

      if (b->files != -1 && b->ffree != -1) printf(" %d/%d", b->files - b->ffree, b->files);
    }

    printf("\n");
  }

  free(buf);
  return 0;
}

shellcmd(dump) {
  char *filename;
  int fd;
  unsigned char buf[16];
  int pos;
  int count;
  int line;
  int i;
  int ch;

  if (argc != 2) {
    printf("usage: dump <file>\n");
    return -EINVAL;
  }
  filename = argv[1];

  if ((fd = open(filename, O_BINARY)) < 0) {
    printf("%s: %s\n", filename, strerror(errno));
    return -EINVAL;
  }

  line = 0;
  pos = 0;
  while ((count = read(fd, buf, 16)) > 0) {
    printf("%08X ", pos);
    for (i = 0; i < count; i++) printf("%02X ", buf[i]);
    for (i = count; i < 16; i++) printf("   ");
    for (i = 0; i < count; i++) printf("%c", buf[i] < 0x20 ? '.' : buf[i]);
    printf("\n");

    if (line == 23) {
      fflush(stdout);
      ch = getchar();
      if (ch == 'x') {
        close(fd);
        return 0;
      }
      line = 0;
    } else {
      line++;
    }

    pos += count;
  }

  close(fd);
  return 0;
}

shellcmd(echo) {
  int i;

  for (i = 1; i < argc; i++) {
    if (i > 1) fputc(' ', stdout);
    fputs(argv[i], stdout);
  }
  fputs("\n", stdout);

  return 0;
}

shellcmd(id) {
  gid_t groups[NGROUPS_MAX];
  int ngroups;
  struct passwd *pwd = getpwuid(getuid());
  struct group *grp = getgrgid(getgid());

  if (!pwd || !grp) {
    perror("id");
    return -1;
  }

  printf("uid=%d(%s) gid=%d(%s)", pwd->pw_uid, pwd->pw_name, grp->gr_gid, grp->gr_name);

  ngroups = getgroups(NGROUPS_MAX, groups);
  if (ngroups > 0) {
    int i;

    printf(" groups");
    for (i = 0; i < ngroups; i++) {
      grp = getgrgid(groups[i]);
      printf("%c%d(%s)", (i == 0 ? '=' : ','), groups[i], grp->gr_name);
    }
  }

  printf("\n");
  return 0;


  return 0;
}

shellcmd(ifconfig) {
  struct peb *peb = gettib()->peb;
  int sock;
  struct ifcfg iflist[16];
  int n, i;

  if (*peb->hostname) {
    printf("Host Name ............... : %s\n", peb->hostname);
  }

  if (*peb->default_domain) {
    printf("Domain Name ............. : %s\n", peb->default_domain);
  }

  if (peb->primary_dns.s_addr != INADDR_ANY) {
    printf("Primary DNS Server ...... : %s\n", inet_ntoa(peb->primary_dns));
  }

  if (peb->secondary_dns.s_addr != INADDR_ANY) {
    printf("Secondary DNS Server .... : %s\n", inet_ntoa(peb->secondary_dns));
  }

  if (peb->ntp_server1.s_addr != INADDR_ANY) {
    printf("Primary NTP Server ...... : %s\n", inet_ntoa(peb->ntp_server1));
  }

  if (peb->ntp_server2.s_addr != INADDR_ANY) {
    printf("Secondary NTP Server .... : %s\n", inet_ntoa(peb->ntp_server2));
  }

  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock < 0) return -1;

  n = ioctl(sock, SIOIFLIST, iflist, sizeof iflist);
  if (n < 0) {
    close(sock);
    return -1;
  }

  for (i = 0; i < n / (int) sizeof(struct ifcfg); i++) {
    struct sockaddr_in *addr = ((struct sockaddr_in *) &iflist[i].addr);
    struct sockaddr_in *gw = ((struct sockaddr_in *) &iflist[i].gw);
    struct sockaddr_in *mask = ((struct sockaddr_in *) &iflist[i].netmask);
    struct sockaddr_in *bcast = ((struct sockaddr_in *) &iflist[i].broadcast);
    unsigned char *hwaddr = (unsigned char *) iflist[i].hwaddr;

    printf("\n");
    printf("Network interface %s:\n", iflist[i].name);
    printf("  IP Address ......... : %s\n", inet_ntoa(addr->sin_addr));
    printf("  Subnet Mask ........ : %s\n", inet_ntoa(mask->sin_addr));
    printf("  Default Gateway .... : %s\n", inet_ntoa(gw->sin_addr));
    printf("  Broadcast Address .. : %s\n", inet_ntoa(bcast->sin_addr));

    printf("  Physical Address ... : %02x:%02x:%02x:%02x:%02x:%02x\n",
      hwaddr[0], hwaddr[1], hwaddr[2], 
      hwaddr[3], hwaddr[4], hwaddr[5]);

    printf("  Flags .............. :");
    if (iflist[i].flags & IFCFG_UP) printf(" UP");
    if (iflist[i].flags & IFCFG_DHCP) printf(" DHCP");
    if (iflist[i].flags & IFCFG_DEFAULT) printf(" DEFAULT");
    if (iflist[i].flags & IFCFG_LOOPBACK) printf(" LOOPBACK");
    printf("\n");
  }

  close(sock);
  return 0;
}

shellcmd(keyb) {
  char *keybname;
  int keymap;
  int rc;

  if (argc != 2) {
    printf("usage: keyb [us|uk|dk|fr]\n");
    return -EINVAL;
  }
  keybname = argv[1];

  if (strcmp(keybname, "us") == 0) {
    keymap = 0;
  } else if (strcmp(keybname, "dk") == 0) {
    keymap = 1;
  } else if (strcmp(keybname, "uk") == 0) {
    keymap = 2;
  } else if (strcmp(keybname, "fr") == 0) {
    keymap = 3;
  } else {
    printf("keyb: unknown keymap '%s'\n", keybname);
    return -EINVAL;
  }

  rc = ioctl(0, IOCTL_SET_KEYMAP, &keymap, sizeof(int));
  return rc;
}

shellcmd(heapstat) {
  struct mallinfo m;

  m = mallinfo();

  printf("Non-mmapped space allocated from system .. : %12d\n", m.arena);
  printf("Number of free chunks .................... : %12d\n", m.ordblks);
  printf("Number of fastbin blocks ................. : %12d\n", m.smblks);
  printf("Number of mmapped regions ................ : %12d\n", m.hblks);
  printf("Space in mmapped regions ................. : %12d\n", m.hblkhd);
  printf("Maximum total allocated space ............ : %12d\n", m.usmblks);
  printf("Space available in freed fastbin blocks .. : %12d\n", m.fsmblks);
  printf("Total allocated space .................... : %12d\n", m.uordblks);
  printf("Total free space ......................... : %12d\n", m.fordblks);
  printf("Top-most, releasable space ............... : %12d\n", m.keepcost);

  return 0;
}

shellcmd(httpget) {
  char *server;
  char *path;
  char *filename;
  int rc;
  clock_t t;

  server = "localhost";
  path = "/";
  filename = "/dev/null";

  if (argc == 1) {
    printf("usage: httpget <server> [<url> [<filename>]]\n");
    return -EINVAL;
  }

  if (argc >= 2) server = argv[1];
  if (argc >= 3) path = argv[2];
  if (argc >= 4) filename = argv[3];
  
  t = clock();
  rc = httpget(server, path, filename);
  if (rc < 0) {
    printf("Error %d '%s' retrieving %s from %s\n", errno, strerror(errno), path, server);
    return -1;
  }
   
  t = clock() - t;
  if (t == 0) t = 1;
  printf("Received %d bytes in %d ms (%d KB/s)\n", rc, t, rc / t);

  return 0;
}

shellcmd(kbd) {
  int escs;
  int ch;

  printf("(press esc three times to exit)\n");

  escs = 0;
  while (1) {
    fflush(stdout);
    ch = getchar();

    if (ch == 0x1B) {
      escs++;
    } else {
      escs = 0;
    }

    if (escs == 3) break;

    printf("[0x%02X]", ch);
  }
  printf("\n");
  return 0;
}

shellcmd(kill) {
  int pid = getpid();
  int signum = SIGINT;
  int rc;
  
  if (argc > 1) pid = atoi(argv[1]);
  if (argc > 2) signum = atoi(argv[2]);

  rc = kill(pid, signum);
  if (rc < 0) perror("kill");

  return 0;
}

shellcmd(klog) {
  int enabled;
  int klog;
  int rc;

  if (argc != 2) {
    printf("usage: klog [on|off]\n");
    return -EINVAL;
  }

  enabled = strcmp(argv[1], "on") == 0;

  klog = open("/dev/klog", 0);
  rc = ioctl(klog, IOCTL_KPRINT_ENABLED, &enabled, 4);
  close(klog);

  return rc;
}

shellcmd(load) {
  char *pgm;
  hmodule_t hmod;

  if (argc != 2) {
    printf("usage: load <module>\n");
    return -EINVAL;
  }
  pgm = argv[1];

  hmod = dlopen(pgm, 0);
  if (hmod == NULL) {
    printf("%s: unable to load module\n", pgm);
    return -1;
  }

  return 0;
}

shellcmd(loglevel) {
  int level;
  int ll = 0;

  if (argc != 2) {
    printf("usage: logevel [<loglevel>]\n");
    return -EINVAL;
  }

  level = atoi(argv[1]);
  setlogmask((1 << (level + 1)) - 1);
  return 0;
}

shellcmd(mkfs) {
  char *devname;
  char *type = "dfs";
  char *opts = NULL;
  int rc;

  if (argc < 2) {
    printf("usage: mkfs <device> [<type> [<options>]]\n");
    return -EINVAL;
  }

  devname = argv[1];
  if (argc > 2) type = argv[2];
  if (argc > 3) opts = argv[3];

  rc = mkfs(devname, type, opts); 
  if (rc < 0) {
    printf("%s: %s\n", devname, strerror(errno));
    return -1;
  }

  return 0;
}

shellcmd(more) {
  char *filename;
  int count;
  int fd;
  int line;
  char *start;
  char *end;
  char ch;
  char buffer[BUFSIZE];
  struct term *term;

  if (argc == 1) {
    fd = dup(fdin);
  } else if (argc == 2) {
    filename = argv[1];
    if ((fd = open(filename, O_BINARY)) < 0) {
      printf("%s: %s\n", filename, strerror(errno));
      return fd;
    }
  } else {
    printf("usage: more <file>\n");
    return -EINVAL;
  }

  term = gettib()->proc->term;
  line = 0;
  while ((count = read(fd, buffer, sizeof buffer)) > 0) {
    start = end = buffer;
    while (end < buffer + count) {
      if (line == 24) {
        display_buffer(stdout, start, end - start);
        fflush(stdout);
        if (read(term->ttyin, &ch, 1) < 0) break;
        if (ch == 'q') {
          close(fd);
          return 0;
        }
        start = end;
        line = 0;
      }
      if (*end++ == '\n') line++;
    }
    display_buffer(stdout, start, end - start);
  }

  close(fd);
  return 0;
}

shellcmd(mount) {
  char *devname;
  char *path;
  char *type = "dfs";
  char *opts = NULL;
  int rc;

  if (argc < 3) {
    printf("usage: mount <device> <path> [<type> [<options>]]\n");
    return -EINVAL;
  }

  devname = argv[1];
  path = argv[2];
  if (argc > 3) type = argv[3];
  if (argc > 4) opts = argv[4];

  rc = mount(type, path, devname, opts); 
  if (rc < 0) {
    printf("%s: %s\n", devname, strerror(errno));
    return -1;
  }

  return 0;
}

shellcmd(nslookup) {
  char *name;
  struct hostent *hp;
  int i;

  if (argc != 2) {
    printf("usage: nslookup [<hostname>|<ipaddress>]\n");
    return -EINVAL;
  }
  name = argv[1];

  if (!*name) return 0;

  if (*name >= '0' && *name <= '9') {
    struct in_addr addr;
    addr.s_addr = inet_addr(name);
    if (addr.s_addr == INADDR_NONE) {
      printf("%s: %s\n", name, strerror(errno));
      return -1;
    }

    hp = gethostbyaddr((char *) &addr, sizeof(struct in_addr), AF_INET);
  } else {
    hp = gethostbyname(name);
  }

  if (!hp) {
    printf("%s: %s\n", name, strerror(errno));
    return -errno;
  }

  if (hp->h_addrtype != AF_INET) {
    printf("unknown address type %d\n", hp->h_addrtype);
    return 0;
  }

  if (hp->h_length != sizeof(struct in_addr)) {
    printf("unknown address length %d\n", hp->h_length);
    return 0;
  }

  printf("name: %s\n", hp->h_name);
  for (i = 0; hp->h_aliases[i] != NULL; i++) printf("alias: %s\n", hp->h_aliases[i]);
  for (i = 0; hp->h_addr_list[i] != NULL; i++) {
    struct in_addr *addr = (struct in_addr *) (hp->h_addr_list[i]);
    printf("address: %s\n", inet_ntoa(*addr));
  }

  return 0;
}

shellcmd(pwd) {
  char cwd[FILENAME_MAX];
  if (getcwd(cwd, FILENAME_MAX) == NULL) {
    perror("cwd");
    return 1;
  }
  printf("%s\n", cwd);
  return 0;
}

shellcmd(ps) {
  struct peb *peb = gettib()->peb;
  struct process *proc = peb->firstproc;

  printf("  id parent name         hmod     threads in  out err command line\n");
  printf("---- ------ ------------ -------- ------- --- --- --- -------------------------\n");

  while (proc) {
    printf("%4d %6d %-13s%08x%8d%4d%4d%4d %s\n", proc->id, proc->parent ? proc->parent->id : 0, proc->ident, proc->hmod, proc->threadcnt, proc->iob[0], proc->iob[1], proc->iob[2], proc->cmdline ? proc->cmdline : "");
    proc = proc->nextproc;
  }

  return 0;
}

shellcmd(read) {
  char *filename;
  int count;
  int fd;
  char *data;
  clock_t start;
  clock_t time;
  int bytes;

  if (argc != 2) {
    printf("usage: read <file>\n");
    return -EINVAL;
  }
  filename = argv[1];

  if ((fd = open(filename, 0)) < 0) {
    printf("%s: %s\n", filename, strerror(errno));
    return fd;
  }

  data = malloc(64 * 1024);

  bytes = 0;
  start = clock();
  while ((count = read(fd, data, 64 * 1024)) > 0) {
    bytes += count;
  }
  time = clock() - start;
  if (time == 0) time = 1;

  printf("%s: read %dKB in %d ms, %dKB/s\n", filename, bytes / 1024, time, bytes / time);
  
  free(data);

  if (count < 0) printf("%s: %s\n", filename, strerror(errno));

  close(fd);
  return 0;
}

shellcmd(reboot) {
  exitos(EXITOS_REBOOT);
  return 0;
}

shellcmd(rmdir) {
  char *path;
  int rc;

  if (argc != 2) {
    printf("usage: rmdir <path>\n");
    return -EINVAL;
  }
  path = argv[1];

  rc = rmdir(path); 
  if (rc < 0) {
    printf("%s: %s\n", path, strerror(errno));
    return -1;
  }

  return 0;
}

shellcmd(sleep) {
  int ms;

  if (argc != 2) {
    printf("usage: sleep <millis>\n");
    return -EINVAL;
  }
  ms = atoi(argv[1]);

  msleep(ms);
  return 0;
}

shellcmd(sound) {
  int freq;

  if (argc != 2) {
    printf("usage: sound <freq> (0=silence)\n");
    return -EINVAL;
  }
  freq = atoi(argv[1]);

  return ioctl(1, IOCTL_SOUND, &freq, 4);
}

shellcmd(shutdown) {
  exitos(EXITOS_POWEROFF);
  return 0;
}

shellcmd(sysinfo) {
  int rc;
  struct cpuinfo cpu;
  struct meminfo mem;
  struct loadinfo load;

  rc = sysinfo(SYSINFO_CPU, &cpu, sizeof(cpu));
  if (rc < 0) return rc;

  rc = sysinfo(SYSINFO_MEM, &mem, sizeof(mem));
  if (rc < 0) return rc;

  rc = sysinfo(SYSINFO_LOAD, &load, sizeof(load));
  if (rc < 0) return rc;

  printf("cpu vendor: %d family: %d model: %d stepping: %d mhz: %d feat: %08X pagesize: %d vendorid: %s modelid: %s\n", 
         cpu.cpu_vendor, cpu.cpu_family, cpu.cpu_model, cpu.cpu_stepping, cpu.cpu_mhz, cpu.cpu_features, 
         cpu.pagesize, cpu.vendorid, cpu.modelid);

  printf("mem phys total: %d K avail: %d K virt total: %d K avail: %d K pagesize: %d\n", 
         mem.physmem_total / 1024, mem.physmem_avail / 1024, mem.virtmem_total / 1024, mem.virtmem_avail / 1024, mem.pagesize);

  printf("load uptime: %d s user: %d%% sys: %d%% intr: %d%% idle: %d%%\n", 
         load.uptime, load.load_user, load.load_system, load.load_intr, load.load_idle);

  return 0;
}

shellcmd(time) {
  clock_t start, end;
  struct tms tms;
  handle_t h;
  int rc;
  
  // Spawn program
  h = spawnve(P_NOWAIT | P_SUSPEND, NULL, argv + 1, NULL, NULL);
  if (h < 0) return -1;

  // Start program and wait for termination
  memset(&tms, 0, sizeof(struct tms));
  start = clock();
  rc = resume(h);
  if (rc >= 0) {
    while (1) {
      rc = waitone(h, INFINITE);
      if (rc >= 0 || errno != EINTR) break;
    }
    threadtimes(h, &tms);
    close(h);
  }
  end = clock();

  // Print CPU usage
  fprintf(stderr, "real %f\nuser %f\nsys %f\n",
          (end - start) / 1000.0,
          (tms.tms_utime + tms.tms_cutime) / 1000.0,
          (tms.tms_stime + tms.tms_cstime) / 1000.0);

  return rc;
}

shellcmd(uname) {
  struct utsname buf;
  
  if (uname(&buf) < 0) {
    perror("uname");
    return -1;
  }

  printf("%s %s %s %s %s\n", buf.sysname, buf.nodename, buf.release, buf.version, buf.machine);
  return 0;
}

shellcmd(umount) {
  char *path;
  int rc;

  if (argc != 2) {
    printf("usage: umount <path>\n");
    return -EINVAL;
  }
  path = argv[1];

  rc = umount(path);
  if (rc < 0) {
    printf("%s: %s\n", path, strerror(errno));
    return -1;
  }

  return 0;
}

shellcmd(whoami) {
  struct passwd *pwd;

  pwd = getpwuid(getuid());
  if (pwd) {
    printf("%s\n", pwd->pw_name);
  } else {
    printf("uid%d\n", getuid);
  }

  return 0;
}

shellcmd(write) {
  char *filename;
  int size;
  int count;
  int fd;
  char *data;
  clock_t start;
  clock_t time;
  int bytes;

  if (argc != 3) {
    printf("usage: write <file> <size (in kb)>\n");
    return -EINVAL;
  }
  filename = argv[1];
  size = atoi(argv[2]) * 1024;

  if ((fd = open(filename, O_CREAT | O_BINARY, S_IREAD | S_IWRITE)) < 0) {
    printf("%s: %s\n", filename, strerror(errno));
    return -1;
  }

  data = malloc(64 * 1024);

  bytes = 0;
  start = clock();
  while (bytes < size) {
    if ((count = write(fd, data, 64 * 1024)) <= 0) {
      printf("%s: error writing file\n", filename);
      break;
    }

    bytes += count;
  }
  time = clock() - start;
  if (time == 0) time = 1;

  printf("%s: wrote %dKB in %d ms, %dKB/s\n", filename, bytes / 1024, time, bytes / time);
  
  free(data);

  close(fd);
  return 0;
}