diff --git a/yeeshell b/yeeshell index cbf51a2..ba5008e 100755 Binary files a/yeeshell and b/yeeshell differ diff --git a/yeeshell.c b/yeeshell.c index 4338544..ae5ff24 100644 --- a/yeeshell.c +++ b/yeeshell.c @@ -2,15 +2,20 @@ #include #include #include +#include #include #include #include +#include #include "yeeshell.h" /* record cmdline history */ -char **history = NULL; +char *history[CMDLINE_HISTORY_MAX_QUANTITY]; int cmdline_amount = 0; +/* if true, print additional output */ +int verbose = 0; + /* next job ID to allocate */ int nextjid = 1; @@ -26,12 +31,21 @@ int main() char *args[ARGS_MAX_QUANTITY]; int status = 1; pwd = (char *)calloc(PATH_MAX, sizeof(char)); - if (!(history = (char **)calloc(CMDLINE_HISTORY_MAX_QUANTITY, CMDLINE_MAX_SIZE * sizeof(char *)))) + for (int i = 0; i < CMDLINE_HISTORY_MAX_QUANTITY; i++) { - printf("yeeshell: allocation error!\n"); - exit(EXIT_FAILURE); + history[i] = (char *)calloc(CMDLINE_MAX_SIZE, sizeof(char)); } + /* Install the signal handlers */ + + /* These are the ones you will need to implement */ + Signal(SIGINT, sigint_handler); /* ctrl-c */ + Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */ + Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */ + + /* This one provides a clean way to kill the shell */ + Signal(SIGQUIT, sigquit_handler); + initjobs(jobs); /* execute the shell's read, parse and execution loop */ do { @@ -40,13 +54,16 @@ int main() printf("yeeshell: The current path cannot be obtained!\n"); exit(0); } - printf("[yeeshell %s]# ", pwd); + printf("[root@yeeshell %s]# ", pwd); cmdline = readline(); - history[cmdline_amount++] = cmdline; + strcpy(history[cmdline_amount++], cmdline); status = execute(cmdline, args); free(cmdline); } while (status); - free(history); + for (int i = 0; i < CMDLINE_HISTORY_MAX_QUANTITY; i++) + { + free(history[i]); + } exit(EXIT_SUCCESS); } @@ -147,7 +164,7 @@ int execute(char *cmdline, char **args) /* Set the pid of the current process to the group number of the process group it belongs to. */ /* avoid being grouped with tsh */ setpgid(0, 0); - if (execve(args[0], args, environ) <= 0) + if (execvp(args[0], args) <= 0) { printf("%s: Command not found\n", args[0]); exit(0); @@ -201,17 +218,13 @@ int built_in(char **args) { return builtin_mytop(args); } - else if (!strcmp(args[0], "ls")) - { - return builtin_ls(args); - } else if (!strcmp(args[0], "jobs")) { return builtin_jobs(args); } else { - return -1; + return 0; } } @@ -231,39 +244,21 @@ int builtin_cd(char **args) } } -int builtin_ls(char **args) +int builtin_history(char **args) { - DIR *dp; - struct dirent *dirp; - char *path = NULL; - + int n = 0; if (args[1] == NULL) { - path = (char *)calloc(PATH_MAX, sizeof(char)); - getcwd(path, PATH_MAX) + n = cmdline_amount; } - else if (args[1]) - - if ((dp = opendir(args[1])) == NULL) - { - perror("yeeshell"); - } - while ((dirp = readdir(dp)) != NULL) + else { - printf("%s\n", dirp->d_name); + n = atoi(args[1]) < cmdline_amount ? atoi(args[1]) : cmdline_amount; } - - closedir(dp); - return 0; -} - -int builtin_history(char **args) -{ - int n = atoi(args[1]); printf("ID\tCommandline\n"); - for (int i = 0; i < ((n < cmdline_amount) ? n : cmdline_amount); i++) + for (int i = 0; i < n; i++) { - printf("%d\t%s\n", i, history[i]); + printf("%d\t%s\n", i + 1, history[i]); } return 1; } @@ -309,11 +304,98 @@ int builtin_mytop(char **args) int do_bgfg(char **args) { + /* initialize variables */ + struct job_t *currentJob; + int jid; + pid_t pid; + sigset_t mask_all, mask_prev; + sigfillset(&mask_all); + + sigprocmask(SIG_SETMASK, &mask_all, &mask_prev); + + /* bg or fg has the argument? */ + if (args[1] == NULL) + { + printf("%s command requires PID or %%jobid argument\n", args[0]); + return 1; + } + /* if process by jid, gets the corresponding Job structure*/ + else if (args[1][0] == '%') + { + jid = atoi(args[1][1]); + currentJob = getjobjid(jobs, jid); + if (currentJob == NULL) + { + printf("%%%d: No such job\n", jid); + return 1; + } + pid = currentJob->pid; + } + /* if process by pid, gets the corresponding Job structure */ + else + { + pid = atoi(args[1]); + currentJob = getjobpid(jobs, pid); + if (pid <= 0) + { + printf("%s: argument must be a PID or %%jobid\n", args[0]); + return 1; + } + currentJob = getjobpid(jobs, pid); + if (currentJob == NULL) + { + printf("(%d): No such process\n", pid); + return 1; + } + } + + /* bg or fg? */ + if (!strcmp(args[0], "bg")) /* if bg */ + { + currentJob->state = BG; + printf("[%d] (%d) %s", currentJob->jid, pid, currentJob->cmdline); + sigprocmask(SIG_SETMASK, &mask_prev, NULL); + kill(-pid, SIGCONT); /* send the SIGCONT to the pid */ + return 1; + } + else if (!strcmp(args[0], "fg")) /* if fg */ + { + currentJob->state = FG; + sigprocmask(SIG_SETMASK, &mask_prev, NULL); + kill(-pid, SIGCONT); /* send the SIGCONT to the pid */ + waitfg(currentJob->pid); /* Child process switched to FG, so wait for it to finish */ + return 1; + } + else if (!strcmp(args[0], "kill")) + { + sigprocmask(SIG_SETMASK, &mask_prev, NULL); + kill(-pid, SIGQUIT); + return 1; + } + return 1; +} + +void waitfg(pid_t pid) +{ + sigset_t m; + sigemptyset(&m); + while (pid == fgpid(jobs)) + { + /* Wake up when there is a signal to check whether the foreground process PID change, */ + /* change means that the foreground process is over. */ + sigsuspend(&m); + } + return; +} + +void initjobs(struct job_t *jobs) +{ + int i; + + for (i = 0; i < JOBS_MAX_QUANTITY; i++) + clearjob(&jobs[i]); } -/************************ - * manipulate job list - ************************/ int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) { int i; @@ -332,8 +414,10 @@ int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) nextjid = 1; strcpy(jobs[i].cmdline, cmdline); - printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline); - + if (verbose) + { + printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline); + } return 1; } } @@ -341,20 +425,6 @@ int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) return 0; } -int pid2jid(pid_t pid) -{ - int i; - - if (pid < 1) - return 0; - for (i = 0; i < JOBS_MAX_QUANTITY; i++) - if (jobs[i].pid == pid) - { - return jobs[i].jid; - } - return 0; -} - void listjobs(struct job_t *jobs) { for (int i = 0; i < JOBS_MAX_QUANTITY; i++) @@ -381,17 +451,45 @@ void listjobs(struct job_t *jobs) } } -void waitfg(pid_t pid) +int deletejob(struct job_t *jobs, pid_t pid) { - sigset_t m; - sigemptyset(&m); - while (pid == fgpid(jobs)) + int i; + + if (pid < 1) + return 0; + + for (i = 0; i < JOBS_MAX_QUANTITY; i++) { - /* Wake up when there is a signal to check whether the foreground process PID change, */ - /* change means that the foreground process is over. */ - sigsuspend(&m); + if (jobs[i].pid == pid) + { + clearjob(&jobs[i]); + nextjid = maxjid(jobs) + 1; + return 1; + } } - return; + return 0; +} + +void clearjob(struct job_t *job) +{ + job->pid = 0; + job->jid = 0; + job->state = UNDF; + job->cmdline[0] = '\0'; +} + +int pid2jid(pid_t pid) +{ + int i; + + if (pid < 1) + return 0; + for (i = 0; i < JOBS_MAX_QUANTITY; i++) + if (jobs[i].pid == pid) + { + return jobs[i].jid; + } + return 0; } pid_t fgpid(struct job_t *jobs) @@ -402,14 +500,92 @@ pid_t fgpid(struct job_t *jobs) return 0; } -int hide(const char *path) +struct job_t *getjobpid(struct job_t *jobs, pid_t pid) { - if (*path == '.') + int i; + + if (pid < 1) + return NULL; + for (i = 0; i < JOBS_MAX_QUANTITY; i++) + if (jobs[i].pid == pid) + return &jobs[i]; + return NULL; +} + +struct job_t *getjobjid(struct job_t *jobs, int jid) +{ + int i; + + if (jid < 1) + return NULL; + for (i = 0; i < JOBS_MAX_QUANTITY; i++) + if (jobs[i].jid == jid) + return &jobs[i]; + return NULL; +} + +handler_t *Signal(int signum, handler_t *handler) +{ + struct sigaction action, old_action; + + action.sa_handler = handler; + sigemptyset(&action.sa_mask); /* block sigs of type being handled */ + action.sa_flags = SA_RESTART; /* restart syscalls if possible */ + + if (sigaction(signum, &action, &old_action) < 0) + fprintf(stdout, "%s: %s\n", 'Signal Error', strerror(errno)); + return (old_action.sa_handler); +} + +void sigchld_handler(int signal) +{ + pid_t pid; + int status; + while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { - return 1; + if (WIFEXITED(status)) /* Normal termination */ + { + deletejob(jobs, pid); + } + if (WIFSTOPPED(status)) /* Task suspension */ + { + struct job_t *job = getjobpid(jobs, pid); + int jid = pid2jid(pid); + printf("Job [%d] (%d) stopped by signal %d\n", jid, pid, WSTOPSIG(status)); + job->state = ST; + } + if (WIFSIGNALED(status)) /* Task terminated */ + { + int jid = pid2jid(pid); + printf("Job [%d] (%d) terminated by signal %d\n", jid, pid, WTERMSIG(status)); + deletejob(jobs, pid); + } } - else + return; +} + +void sigint_handler(int signal) +{ + pid_t pid = fgpid(jobs); + if (pid != 0) { - return 0; + kill(-pid, signal); } + return; +} + +void sigtstp_handler(int signal) +{ + pid_t pid = fgpid(jobs); + if (pid > 0) + { + kill(-pid, signal); + } + return; +} + +void sigquit_handler(int signal) +{ + printf("Terminating after receipt of SIGQUIT signal\n"); + exit(1); } \ No newline at end of file diff --git a/yeeshell.h b/yeeshell.h index a3ef706..b1c2152 100644 --- a/yeeshell.h +++ b/yeeshell.h @@ -7,10 +7,12 @@ #define CMDLINE_HISTORY_MAX_QUANTITY 256 #define JOBS_MAX_QUANTITY 16 #define PATH_MAX_SIZE 256 +#define LS_BUF_SIZE 1024 -#define FG 1 /* running in foreground */ -#define BG 2 /* running in background */ -#define ST 3 /* stopped */ +#define UNDF 0 /* undefined */ +#define FG 1 /* running in foreground */ +#define BG 2 /* running in background */ +#define ST 3 /* stopped */ /* job_t - The job struct */ struct job_t @@ -33,7 +35,6 @@ int execute(char *cmdline, char **args); /* builtin functions - Handle built-in command */ int built_in(char **args); int builtin_cd(char **args); -int builtin_ls(char **args); int builtin_history(char **args); int builtin_jobs(char **args); int builtin_mytop(char **args); @@ -41,19 +42,66 @@ int builtin_mytop(char **args); /* do_bgfg - Execute background/foregroung tasks */ int do_bgfg(char **args); +/* waitfg - Wait foreground jobs to finish */ +void waitfg(pid_t pid); + +/* initjobs - Initialize the job list */ +void initjobs(struct job_t *jobs); + /* addjob - Add jobs to joblist */ int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline); +/* listjobs - Print the job list */ +void listjobs(struct job_t *jobs); + +/* deletejob - Delete a job whose PID=pid from the job list */ +int deletejob(struct job_t *jobs, pid_t pid); + +/* clearjob - Clear the entries in a job struct */ +void clearjob(struct job_t *job); + /* pide2jid - Map process ID to job ID*/ int pid2jid(pid_t pid); -/* waitfg - Wait foreground jobs to finish */ -void waitfg(pid_t pid); - /* fgpid - Return PID of current foreground job, 0 if no such job */ pid_t fgpid(struct job_t *jobs); -/* determine if a file is hidden */ -int hide(const char *path); +/* getjobpid - Find a job (by PID) on the job list */ +struct job_t *getjobpid(struct job_t *jobs, pid_t pid); + +/* getjobjid - Find a job (by JID) on the job list */ +struct job_t *getjobjid(struct job_t *jobs, int jid); + +/* Signal - wrapper for the sigaction function */ +typedef void handler_t(int); +handler_t *Signal(int signum, handler_t *handler); + +/* + * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever + * a child job terminates (becomes a zombie), or stops because it + * received a SIGSTOP or SIGTSTP signal. The handler reaps all + * available zombie children, but doesn't wait for any other + * currently running children to terminate. + */ +void sigchld_handler(int signal); + +/* + * sigint_handler - The kernel sends a SIGINT to the shell whenver the + * user types ctrl-c at the keyboard. Catch it and send it along + * to the foreground job. + */ +void sigint_handler(int signal); + +/* + * sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever + * the user types ctrl-z at the keyboard. Catch it and suspend the + * foreground job by sending it a SIGTSTP. + */ + +void sigtstp_handler(int signal); -/* */ \ No newline at end of file +/* + * sigquit_handler - The driver program can gracefully terminate the + * child shell by sending it a SIGQUIT signal. + */ +void sigquit_handler(int signal); \ No newline at end of file