From 8b58e81077abf4e843873f16c03077e2fafce52d Mon Sep 17 00:00:00 2001 From: kaashoek Date: Wed, 23 Aug 2006 01:09:24 +0000 Subject: [PATCH] i/o redirection in sh better parsing of sh commands (copied from jos sh) cat: read from 1 if no args sbrk system call, but untested getpid system call moved locks in keyboard intr, but why do we get intr w. null characters from keyboard? --- Notes | 8 ++ cat.c | 37 ++++--- console.c | 17 ++- defs.h | 1 + echo.c | 2 + lapic.c | 2 +- ls.c | 57 ++++++---- proc.c | 18 ++++ sh.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++++------ syscall.c | 29 +++++ syscall.h | 2 + ulib.c | 17 +++ user.h | 3 + userfs.c | 4 + usys.S | 2 + 15 files changed, 433 insertions(+), 74 deletions(-) diff --git a/Notes b/Notes index dd81f1b..4c9855d 100644 --- a/Notes +++ b/Notes @@ -131,3 +131,11 @@ maybe get rid of per-proc gdt and ts one per cpu refresh it when needed setupsegs(proc *) + +why do we get 0 characters from keyboard? +are the locks in the right place in keyboardintr? + +sh: support pipes? leave it for the class? +sh: dynamic memory allocation? +sh: should sh support ; () & --- need malloc +sh: stop stdin on ctrl-d (for cat > y) \ No newline at end of file diff --git a/cat.c b/cat.c index 8154ae2..631bd09 100644 --- a/cat.c +++ b/cat.c @@ -1,17 +1,32 @@ +#include "types.h" +#include "stat.h" #include "user.h" char buf[513]; +void +rfile(int fd) +{ + int cc; + + while((cc = read(fd, buf, sizeof(buf) - 1)) > 0){ + buf[cc] = '\0'; + puts(buf); + } + if(cc < 0){ + puts("cat: read error\n"); + exit(); + } +} + int main(int argc, char *argv[]) { - int fd, i, cc; - - if(argc < 2){ - puts("Usage: cat files...\n"); - exit(); - } + int fd, i; + if (argc <= 1) { + rfile(0); + } else { for(i = 1; i < argc; i++){ fd = open(argv[i], 0); if(fd < 0){ @@ -20,16 +35,10 @@ main(int argc, char *argv[]) puts("\n"); exit(); } - while((cc = read(fd, buf, sizeof(buf) - 1)) > 0){ - buf[cc] = '\0'; - puts(buf); - } - if(cc < 0){ - puts("cat: read error\n"); - exit(); - } + rfile(fd); close(fd); } + } exit(); } diff --git a/console.c b/console.c index 3d19a8e..d1741dd 100644 --- a/console.c +++ b/console.c @@ -304,26 +304,31 @@ char kbd_buf[KBD_BUF]; int kbd_r; int kbd_w; struct spinlock kbd_lock; +static uint shift; void kbd_intr() { uint st, data, c; - static uint shift; + + acquire(&kbd_lock); st = inb(KBSTATP); if ((st & KBS_DIB) == 0){ + release(&kbd_lock); return; } data = inb(KBDATAP); if (data == 0xE0) { shift |= E0ESC; + release(&kbd_lock); return; } else if (data & 0x80) { // Key released data = (shift & E0ESC ? data : data & 0x7F); shift &= ~(shiftcode[data] | E0ESC); + release(&kbd_lock); return; } else if (shift & E0ESC) { // Last character was an E0 escape; or with 0x80 @@ -341,8 +346,12 @@ kbd_intr() else if ('A' <= c && c <= 'Z') c += 'a' - 'A'; } - - acquire(&kbd_lock); + + // xxx hack + if (c == 0x0) { + release(&kbd_lock); + return; + } if(((kbd_w + 1) % KBD_BUF) != kbd_r){ kbd_buf[kbd_w++] = c; @@ -367,7 +376,7 @@ console_read(int minor, char *dst, int n) sleep(&kbd_r, &kbd_lock); while(n > 0 && kbd_w != kbd_r){ - *dst = kbd_buf[kbd_r]; + *dst = (kbd_buf[kbd_r]) & 0xff; cons_putc(*dst & 0xff); dst++; --n; diff --git a/defs.h b/defs.h index 2928428..b8b7b84 100644 --- a/defs.h +++ b/defs.h @@ -16,6 +16,7 @@ struct jmpbuf; void setupsegs(struct proc *); struct proc * copyproc(struct proc*); struct spinlock; +int growproc(int); void sleep(void *, struct spinlock *); void wakeup(void *); void scheduler(void); diff --git a/echo.c b/echo.c index 5d0c5a4..7bf3a45 100644 --- a/echo.c +++ b/echo.c @@ -1,3 +1,5 @@ +#include "types.h" +#include "stat.h" #include "user.h" int diff --git a/lapic.c b/lapic.c index 161d0a5..0d389f1 100644 --- a/lapic.c +++ b/lapic.c @@ -120,7 +120,7 @@ lapic_timerinit(void) void lapic_timerintr(void) { - cprintf("cpu%d: timer interrupt!\n", cpu()); + // cprintf("cpu%d: timer interrupt!\n", cpu()); lapic_write (LAPIC_EOI, 0); } diff --git a/ls.c b/ls.c index 3441eba..34fac2a 100644 --- a/ls.c +++ b/ls.c @@ -7,13 +7,24 @@ char buf[512]; struct stat st; struct dirent dirent; +void +pname(char *n) +{ + int i; + + for (i = 0; (i < DIRSIZ) && (n[i] != '\0') ; i++) { + printf(1, "%c", n[i]); + } + for (; i < DIRSIZ; i++) + printf(1, " "); +} + int main(int argc, char *argv[]) { int fd; uint off; uint sz; - int i; if(argc > 2){ puts("Usage: ls [dir]\n"); @@ -23,7 +34,7 @@ main(int argc, char *argv[]) if (argc == 2) { fd = open(argv[1], 0); if(fd < 0){ - printf(2, "ls: cannot open dir %s\n", argv[1]); + printf(2, "ls: cannot open %s\n", argv[1]); exit(); } } else { @@ -38,31 +49,31 @@ main(int argc, char *argv[]) printf(2, "ls: cannot stat dir\n"); exit(); } - if (st.st_type != T_DIR) { - printf(2, "ls: dir is not a directory\n"); - } - sz = st.st_size; - for(off = 0; off < sz; off += sizeof(struct dirent)) { - if (read(fd, &dirent, sizeof(struct dirent)) != sizeof(struct dirent)) { - printf(1, "ls: read error\n"); - break; - } - if (dirent.inum != 0) { - // xxx prepend to name the pathname supplied to ls (e.g. .. in ls ..) - if (stat (dirent.name, &st) < 0) { - printf(1, "stat: failed %s\n", dirent.name); - continue; + + switch (st.st_type) { + case T_FILE: + pname(argv[1]); + printf(1, "%d %d %d\n", st.st_type, st.st_ino, st.st_size); + break; + case T_DIR: + sz = st.st_size; + for(off = 0; off < sz; off += sizeof(struct dirent)) { + if (read(fd, &dirent, sizeof(struct dirent)) != sizeof(struct dirent)) { + printf(1, "ls: read error\n"); + break; } - for (i = 0; i < DIRSIZ; i++) { - if (dirent.name[i] != '\0') - printf(1, "%c", dirent.name[i]); - else - printf(1, " "); + if (dirent.inum != 0) { + // xxx prepend to name the pathname supplied to ls (e.g. .. in ls ..) + if (stat (dirent.name, &st) < 0) { + printf(1, "stat: failed %s\n", dirent.name); + continue; + } + pname(dirent.name); + printf(1, "%d %d %d\n", st.st_type, dirent.inum, st.st_size); } - printf(1, "%d %d %d\n", st.st_type, dirent.inum, st.st_size); } + break; } close(fd); - exit(); } diff --git a/proc.c b/proc.c index 9f7064f..7382add 100644 --- a/proc.c +++ b/proc.c @@ -138,6 +138,24 @@ copyproc(struct proc* p) return np; } +int +growproc(int n) +{ + struct proc *cp = curproc[cpu()]; + char *newmem, *oldmem; + + newmem = kalloc(cp->sz + n); + if(newmem == 0) return -1; + memmove(newmem, cp->mem, cp->sz); + memset(newmem + cp->sz, 0, n); + oldmem = cp->mem; + cp->mem = newmem; + kfree(oldmem, cp->sz); + cp->sz += n; + cprintf("growproc: added %d bytes\n", n); + return 0; +} + // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. // Scheduler never returns. It loops, doing: diff --git a/sh.c b/sh.c index 9e0cb3f..2f2d297 100644 --- a/sh.c +++ b/sh.c @@ -4,52 +4,296 @@ #include "fs.h" #include "fcntl.h" -char *args[100]; -void parse(char buf[]); +#define BUFSIZ 512 +#define MAXARGS 10 +#define MAXNODE 2 + +// only allocate nodes for i/o redir; at some point we may have to build a +// a real parse tree. +struct node { + int token; + char *s; +}; +struct node list[MAXNODE]; +int nextnode; + +char buf[BUFSIZ]; +char *argv[MAXARGS]; +char argv0buf[BUFSIZ]; +int argc; + +int debug = 1; + +int parse(char *s); +void runcmd(void); +int ioredirection(void); +int gettoken(char *s, char **token); +int _gettoken(char *s, char **p1, char **p2); +void addnode(int token, char *s); int main(void) { - char buf[128]; - int pid; - while(1){ puts("$ "); memset (buf, '\0', sizeof(buf)); gets(buf, sizeof(buf)); - if(buf[0] == '\0') + if (parse(buf) < 0) continue; - pid = fork(); - if(pid == 0){ - parse(buf); - if (buf[0] == 'c' && buf[1] == 'd' && buf[2] == '\0') { // cd - chdir(&buf[3]); - } else { - exec(buf, args); - printf(1, "%s: not found\n", buf); - exit(); - } - } - if(pid > 0) - wait(); + runcmd(); } } -void -parse(char buf[]) +int +parse(char *s) { - int j = 1; - int i; - args[0] = buf; - for (i = 0; buf[i] != '\0'; i++) { - if (buf[i] == ' ') { - buf[i] = '\0'; - args[j++] = buf + i + 1; - if (j >= 100) { - printf(2, "too many args\n"); - exit(); + char *t; + int c; + + gettoken(s, 0); + + argc = 0; + nextnode = 0; + while (1) { + switch ((c = gettoken(0, &t))) { + + case 'w': // Add an argument + if (argc == MAXARGS) { + printf(2, "too many arguments\n"); + return -1; } + argv[argc++] = t; + break; + + case '<': // Input redirection + // Grab the filename from the argument list + if (gettoken(0, &t) != 'w') { + printf(2, "syntax error: < not followed by word\n"); + return -1; + } + addnode('<', t); + break; + + case '>': // Output redirection + // Grab the filename from the argument list + if (gettoken(0, &t) != 'w') { + printf(2, "syntax error: > not followed by word\n"); + return -1; + } + addnode('>', t); + break; + + case 0: // String is complete + return 0; + + default: + printf(2, "syntax error: bad return %d from gettoken", c); + return -1; + } } - args[j] = '\0'; } + + +void +runcmd(void) +{ + int i, r, pid; + + // Return immediately if command line was empty. + if(argc == 0) { + if (debug) + printf(2, "EMPTY COMMAND\n"); + return; + } + + // Clean up command line. + // Read all commands from the filesystem: add an initial '/' to + // the command name. + // This essentially acts like 'PATH=/'. + if (argv[0][0] != '/') { + argv0buf[0] = '/'; + strcpy(argv0buf + 1, argv[0]); + argv[0] = argv0buf; + } + argv[argc] = 0; + + // Print the command. + if (debug) { + printf(2, "[%d] SPAWN:", getpid()); + for (i = 0; argv[i]; i++) + printf(2, " %s", argv[i]); + for (i = 0; i < nextnode; i++) { + printf(2, "%c %s", list[i].token, list[i].s); + } + printf(2, "\n"); + } + + if (strcmp(argv[0], "/cd") == 0) { + if (debug) printf (2, "/cd %s is build in\n", argv[1]); + chdir(argv[1]); + return; + } + + pid = fork(); + if (pid == 0) { + if (ioredirection() < 0) + exit(); + if ((r = exec(argv0buf, (char**) argv)) < 0) { + printf(2, "exec %s: %d\n", argv[0], r); + exit(); + } + } + + if (pid > 0) { + if (debug) + printf(2, "[%d] WAIT %s\n", getpid(), argv[0]); + wait(); + if (debug) + printf(2, "[%d] wait finished\n", getpid()); + } +} + +int +ioredirection(void) +{ + int i, fd, dfd; + + for (i = 0; i < nextnode; i++) { + switch (list[i].token) { + case '<': + if ((fd = open(list[i].s, O_RDONLY)) < 0) { + printf(2, "failed to open %s for read: %d", list[i].s, fd); + return -1; + } + + if (debug) + printf(2, "redirect 0 from %s\n", list[i].s); + + close(0); + if ((dfd = dup(fd)) < 0) + printf(2, "dup failed\n"); + if (debug) + printf(2, "dup returns %d\n", dfd); + close(fd); + break; + case '>': + if ((fd = open(list[i].s, O_WRONLY|O_CREATE)) < 0) { + printf(2, "failed to open %s for write: %d", list[i].s, fd); + exit(); + } + + if (debug) + printf(2, "redirect 1 to %s\n", list[i].s); + + if (close(1) < 0) + printf(2, "close 1 failed\n"); + if ((dfd = dup(fd)) < 0) + printf(2, "dup failed\n"); + if (debug) + printf(2, "dup returns %d\n", dfd); + close(fd); + break; + } + } + return 0; +} + +void +addnode(int token, char *s) +{ + if (nextnode >= MAXNODE) { + printf(2, "addnode: ran out of nodes\n"); + return; + } + + list[nextnode].token = token; + list[nextnode].s = s; + nextnode++; +} + + +// gettoken(s, 0) prepares gettoken for subsequent calls and returns 0. +// gettoken(0, token) parses a shell token from the previously set string, +// null-terminates that token, stores the token pointer in '*token', +// and returns a token ID (0, '<', '>', '|', or 'w'). +// Subsequent calls to 'gettoken(0, token)' will return subsequent +// tokens from the string. + +int +gettoken(char *s, char **p1) +{ + static int c, nc; + static char* np1, *np2; + + if (s) { + nc = _gettoken(s, &np1, &np2); + return 0; + } + c = nc; + *p1 = np1; + nc = _gettoken(np2, &np1, &np2); + return c; +} + + +// Get the next token from string s. +// Set *p1 to the beginning of the token and *p2 just past the token. +// Returns +// 0 for end-of-string; +// < for <; +// > for >; +// | for |; +// w for a word. +// +// Eventually (once we parse the space where the \0 will go), +// words get nul-terminated. +#define WHITESPACE " \t\r\n" +#define SYMBOLS "<|>&;()" + +int +_gettoken(char *s, char **p1, char **p2) +{ + int t; + + if (s == 0) { + if (debug > 1) + printf(2, "GETTOKEN NULL\n"); + return 0; + } + + if (debug > 1) + printf(2, "GETTOKEN: %s\n", s); + + *p1 = 0; + *p2 = 0; + + while (strchr(WHITESPACE, *s)) + *s++ = 0; + if (*s == 0) { + if (debug > 1) + printf(2, "EOL\n"); + return 0; + } + if (strchr(SYMBOLS, *s)) { + t = *s; + *p1 = s; + *s++ = 0; + *p2 = s; + if (debug > 1) + printf(2, "TOK %c\n", t); + return t; + } + *p1 = s; + while (*s && !strchr(WHITESPACE SYMBOLS, *s)) + s++; + *p2 = s; + if (debug > 1) { + t = **p2; + **p2 = 0; + printf(2, "WORD: %s\n", *p1); + **p2 = t; + } + return 'w'; +} + diff --git a/syscall.c b/syscall.c index 80d5d8e..39c6821 100644 --- a/syscall.c +++ b/syscall.c @@ -140,6 +140,7 @@ sys_write(void) return -1; if(addr + n > p->sz) return -1; + ret = fd_write(p->fds[fd], p->mem + addr, n); return ret; } @@ -421,6 +422,7 @@ sys_dup(void) fd_close(fd1); return -1; } + cp->fds[ufd1] = fd1; fd1->type = cp->fds[fd]->type; fd1->readable = cp->fds[fd]->readable; fd1->writeable = cp->fds[fd]->writeable; @@ -449,6 +451,27 @@ sys_link(void) return r; } +int +sys_getpid(void) +{ + struct proc *cp = curproc[cpu()]; + return cp->pid; +} + + +int +sys_sbrk(void) +{ + int r, n; + struct proc *cp = curproc[cpu()]; + + if(fetcharg(0, &n) < 0) + return -1; + r = growproc(n); + setupsegs(cp); + return r; +} + int sys_exec(void) { @@ -638,6 +661,12 @@ syscall(void) case SYS_dup: ret = sys_dup(); break; + case SYS_getpid: + ret = sys_getpid(); + break; + case SYS_sbrk: + ret = sys_sbrk(); + break; default: cprintf("unknown sys call %d\n", num); // XXX fault diff --git a/syscall.h b/syscall.h index c89d5a3..4efd255 100644 --- a/syscall.h +++ b/syscall.h @@ -15,4 +15,6 @@ #define SYS_mkdir 16 #define SYS_chdir 17 #define SYS_dup 18 +#define SYS_getpid 19 +#define SYS_sbrk 20 diff --git a/ulib.c b/ulib.c index 004b934..c6c7f19 100644 --- a/ulib.c +++ b/ulib.c @@ -20,6 +20,14 @@ strcpy(char *s, char *t) return os; } +int +strcmp(const char *p, const char *q) +{ + while (*p && *p == *q) + p++, q++; + return (int) ((unsigned char) *p - (unsigned char) *q); +} + unsigned int strlen(char *s) { @@ -40,6 +48,15 @@ memset(void *dst, int c, unsigned int n) return dst; } +char * +strchr(const char *s, char c) +{ + for (; *s; s++) + if (*s == c) + return (char *) s; + return 0; +} + char * gets(char *buf, int max) { diff --git a/user.h b/user.h index 26d984c..cc82ccf 100644 --- a/user.h +++ b/user.h @@ -18,10 +18,13 @@ int link(char *, char *); int mkdir(char *); int chdir(char *); int dup(int); +int getpid(); int stat(char *, struct stat *stat); int puts(char*); char* strcpy(char*, char*); +char *strchr(const char *s, char c); +int strcmp(const char *p, const char *q); void printf(int fd, char *fmt, ...); char *gets(char *, int max); unsigned int strlen(char *); diff --git a/userfs.c b/userfs.c index 25dfaf5..d1cd49e 100644 --- a/userfs.c +++ b/userfs.c @@ -20,6 +20,10 @@ main(void) printf(stdout, "userfs is running\n"); + if (sbrk(4096) < 0) { + printf(stdout, "sbrk failed\n"); + } + fd = open("echo", 0); if(fd >= 0){ printf(stdout, "open echo ok\n"); diff --git a/usys.S b/usys.S index c7a162c..617fdc5 100644 --- a/usys.S +++ b/usys.S @@ -25,3 +25,5 @@ STUB(link) STUB(mkdir) STUB(chdir) STUB(dup) +STUB(getpid) +STUB(sbrk)