diff --git a/Makefile b/Makefile index 2584e4a..a016c8f 100644 --- a/Makefile +++ b/Makefile @@ -131,6 +131,7 @@ UPROGS=\ $U/_usertests\ $U/_grind\ $U/_wc\ + $U/_watch\ $U/_zombie\ fs.img: mkfs/mkfs README.md $(UPROGS) diff --git a/README.md b/README.md index 8524b54..4c7a5d5 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,59 @@ ![FogOS](docs/fogos.gif) +# Watch Command Documentation + +@author zguo18 +@author yhatnagar + +## Overview + +The custom `watch` command implemented in OS4 allows you to execute a specified command repeatedly at set intervals. The command comes with optional features for logging the output, displaying a header, and controlling the execution through pause and quit options. + +## Features + +1. Customizable Interval (`-n`): Specify the interval in seconds between command executions. The default interval is 2 seconds. + +2. Display Header (`-h`): Option to display the current timestamp in ticks before each command execution. + +3. Log Output (`-o`): Write the output of the command to a specified file instead of displaying it in the console. + +Pause and Quit (Ctrl+C): Press Ctrl+C to both pause and quit the command. When Ctrl+C is pressed, the system interrupts and stops the execution entirely. + +## Command Options implemented + +-n : Set interval in seconds between command executions. +-h: Display a timestamped header before each execution. +-o : Write output to a file instead of the console. + + +## Command Examples + +Here are some examples of how to use the custom `watch` command: + +- watch ls + +This runs the `ls` command every 2 seconds and prints the output to the console. + +- watch -n 5 ls + +This runs the `ls` command every 5 seconds instead of the default 2 seconds. + + +- watch -h -n 3 echo "Hello" + + This runs the `echo "Hello"` command every 3 seconds and displays a timestamp header before each execution. + + +- watch -o log.txt ls + +This runs the `ls` command every 2 seconds and writes the output to `log.txt` instead of printing it to the console. + +- watch -n 5 -h -o date_log.txt date + +This runs the `date` command every 5 seconds, displays a timestamped header, and logs the output to `date_log.txt`. + + +The watch command was implemented in C in an OS environment. It uses system calls such as fork(), exec(), and uptime() to execute the commands at set intervals, manage processes, and retrieve system time in ticks. + +The command also supports logging output to files by redirecting standard output and error streams to the specified file. For the pause and quit functionality, user input handling is employed to capture keypresses, though some functionality may depend on the specific OS shell configuration. diff --git a/kernel/console.c b/kernel/console.c index 05dc526..968c474 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -155,6 +155,11 @@ consoleintr(int c) consputc(BACKSPACE); } break; + case C('C'): + killwithname("watch"); + break; + break; + default: if(c != 0 && cons.e-cons.r < INPUT_BUF_SIZE){ c = (c == '\r') ? '\n' : c; diff --git a/kernel/defs.h b/kernel/defs.h index a3c962b..1ac072b 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -106,7 +106,7 @@ void yield(void); int either_copyout(int user_dst, uint64 dst, void *src, uint64 len); int either_copyin(void *dst, int user_src, uint64 src, uint64 len); void procdump(void); - +void killwithname(char *name); // swtch.S void swtch(struct context*, struct context*); diff --git a/kernel/memlayout.h b/kernel/memlayout.h index 776f98c..0bc72c2 100644 --- a/kernel/memlayout.h +++ b/kernel/memlayout.h @@ -65,3 +65,5 @@ // TRAPFRAME (p->trapframe, used by the trampoline) // TRAMPOLINE (the same page as in the kernel) #define TRAPFRAME (TRAMPOLINE - PGSIZE) +//define goldfish mem add +#define GOLDFISH_RTC 0x101000 diff --git a/kernel/proc.c b/kernel/proc.c index 031eeb5..5f483ac 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -688,3 +688,26 @@ procdump(void) printf("\n"); } } + + +static int +strcmp(const char *p, const char *q) +{ + while (*p && *p == *q){ + p++, q++; + }return (uchar)*p - (uchar)*q; +} + +void killwithname(char *name) +{ + struct proc *p; + for(p = proc; p < &proc[NPROC]; p++){ + if(p->state == UNUSED){ + continue; + } + if(p->state >= 0 && strcmp((const char*) name, (const char*)(p->name))==0){ + kill(p->pid); + break; + } + } +} diff --git a/kernel/syscall.c b/kernel/syscall.c index ed65409..993c1d2 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -101,6 +101,7 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_gettime(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +127,7 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_gettime] sys_gettime, }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index bc5f356..6d6c224 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,4 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_gettime 22 diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 1de184e..02e43e2 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -89,3 +89,11 @@ sys_uptime(void) release(&tickslock); return xticks; } + +//get time stamp +uint64 +sys_gettime(void){ + volatile uint64 *test_dev = (uint64 *)GOLDFISH_RTC; + uint64 time = *test_dev; + return time; +} diff --git a/kernel/trap.c b/kernel/trap.c index 512c850..d12beb2 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -6,6 +6,8 @@ #include "proc.h" #include "defs.h" +#define CTRL_C_KEY 0x3 //define ctrl+c in ascii value + struct spinlock tickslock; uint ticks; diff --git a/user/user.h b/user/user.h index 2e6fc55..0e88def 100644 --- a/user/user.h +++ b/user/user.h @@ -22,6 +22,7 @@ int getpid(void); char* sbrk(int); int sleep(int); int uptime(void); +uint64 gettime(void); // ulib.c int stat(const char*, struct stat*); diff --git a/user/watch.c b/user/watch.c new file mode 100644 index 0000000..8867451 --- /dev/null +++ b/user/watch.c @@ -0,0 +1,95 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fcntl.h" +#include "kernel/fs.h" + +void print_header(char *command, int fd, uint64 start_time) { + fprintf(fd,"\n---%d ms:%s ---\n",(uptime()-start_time)*100,command); +} + + +int main(int argc, char *argv[]) { + if (argc < 2) { + printf("Usage: watch [-n seconds] [-h] [-o output_file] \n"); + exit(1); + } + + int interval = 2; // Default interval + int command_start = 1; + int show_header = 0; + char *output_file = 0; + uint64 start_time=uptime(); + + // Parse options + while (command_start < argc && argv[command_start][0] == '-') { + if (strcmp(argv[command_start], "-n") == 0 && command_start + 1 < argc) { + interval = atoi(argv[command_start + 1]); + if (interval <= 0) { + printf("Invalid interval. Using default (2 seconds).\n"); + interval = 2; + } + command_start += 2; + } else if (strcmp(argv[command_start], "-h") == 0) { + show_header = 1; + command_start++; + } else if (strcmp(argv[command_start], "-o") == 0 && command_start + 1 < argc) { + output_file = argv[command_start + 1]; + command_start += 2; + } else { + printf("Unknown option: %s\n", argv[command_start]); + exit(1); + } + } + + if (command_start >= argc) { + printf("No command specified.\n"); + exit(1); + } + + int log_fd = 1; // Default to stdout + if (output_file) { + log_fd = open(output_file, O_WRONLY | O_CREATE | O_TRUNC); + if (log_fd < 0) { + printf("Failed to open output file: %s\n", output_file); + exit(1); + } + } + + // int paused = 0; + printf("Watch started. Press 'Ctrl-c' to pause and quit.\n"); + while (1) { + if (show_header) { + print_header(argv[command_start], log_fd, start_time); + } + + int pid = fork(); + if (pid < 0) { + printf("Failed to fork\n"); + exit(1); + } + + if (pid == 0) { + // Child process + if (output_file) { + // Redirect stdout and stderr to the log file + close(1); + close(2); + dup(log_fd); + dup(log_fd); + } + exec(argv[command_start], &argv[command_start]); + printf("Failed to execute command: %s\n", argv[command_start]); + exit(1); + } else { + // Parent process + wait(0); + } + sleep(interval * 10); // sleep takes ticks, 1 second = 10 ticks + } + + if (output_file) { + close(log_fd); + } + exit(0); +}