Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ UPROGS=\
$U/_usertests\
$U/_grind\
$U/_wc\
$U/_watch\
$U/_zombie\

fs.img: mkfs/mkfs README.md $(UPROGS)
Expand Down
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <interval>: Set interval in seconds between command executions.
-h: Display a timestamped header before each execution.
-o <file>: 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.
5 changes: 5 additions & 0 deletions kernel/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion kernel/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*);

Expand Down
2 changes: 2 additions & 0 deletions kernel/memlayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
23 changes: 23 additions & 0 deletions kernel/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
2 changes: 2 additions & 0 deletions kernel/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions kernel/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
#define SYS_gettime 22
8 changes: 8 additions & 0 deletions kernel/sysproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions kernel/trap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions user/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -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*);
Expand Down
95 changes: 95 additions & 0 deletions user/watch.c
Original file line number Diff line number Diff line change
@@ -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] <command>\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);
}