|
|
@ -2,15 +2,20 @@ |
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#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); |
|
|
|
} |