|
|
- #include <stdio.h>
- #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 <signal.h>
- #include <errno.h>
- #include <fcntl.h>
- #include "yeeshell.h"
-
- /* record cmdline history */
- 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;
-
- /* environment variable */
- extern char **environ;
-
- /* The job list */
- struct job_t jobs[JOBS_MAX_QUANTITY];
-
- int main()
- {
- char *cmdline = NULL, *pwd = NULL;
- char *args[ARGS_MAX_QUANTITY];
- int status = 1;
- pwd = (char *)calloc(PATH_MAX_SIZE, sizeof(char));
- for (int i = 0; i < CMDLINE_HISTORY_MAX_QUANTITY; i++)
- {
- history[i] = (char *)calloc(CMDLINE_MAX_SIZE, sizeof(char));
- }
-
- /* Install the signal handlers */
- Signal(SIGINT, sigint_handler); /* ctrl-c */
- Signal(SIGTSTP, sigtstp_handler); /* ctrl-z */
- Signal(SIGCHLD, sigchld_handler); /* Terminated or stopped child */
- Signal(SIGQUIT, sigquit_handler);
-
- /* initiate job list */
- initjobs(jobs);
-
- /* execute the shell's read, parse and execution loop */
- do
- {
- if (!getcwd(pwd, PATH_MAX_SIZE))
- {
- printf("yeeshell: The current path cannot be obtained!\n");
- exit(0);
- }
- printf("[root@yeeshell %s]# ", pwd);
- cmdline = readline();
- strcpy(history[cmdline_amount++], cmdline);
- status = execute(cmdline, args);
- free(cmdline);
- } while (status);
- for (int i = 0; i < CMDLINE_HISTORY_MAX_QUANTITY; i++)
- {
- free(history[i]);
- }
- exit(EXIT_SUCCESS);
- }
-
- char *readline()
- {
- char *cmdline = NULL;
- ssize_t bufsize = 0;
- getline(&cmdline, &bufsize, stdin);
- return cmdline;
- }
-
- int parseline(char *cmdline, char **args)
- {
- static char array[CMDLINE_MAX_SIZE]; /* holds local copy of command line */
- char *buf = array; /* ptr that traverses command line */
- char *delim; /* points to first space delimiter */
- int argc; /* number of args */
- int bg; /* background job? */
-
- strcpy(buf, cmdline);
- buf[strlen(buf) - 1] = ' '; /* replace trailing '\n' with space */
- while (*buf && (*buf == ' ')) /* ignore leading spaces */
- {
- buf++;
- }
-
- /* Build the argv list */
- argc = 0;
- if (*buf == '\'')
- {
- buf++;
- delim = strchr(buf, '\'');
- }
- else
- {
- delim = strchr(buf, ' ');
- }
-
- while (delim)
- {
- args[argc++] = buf;
- *delim = '\0';
- buf = delim + 1;
- while (*buf && (*buf == ' ')) /* ignore spaces */
- {
- buf++;
- }
-
- if (*buf == '\'')
- {
- buf++;
- delim = strchr(buf, '\'');
- }
- else
- {
- delim = strchr(buf, ' ');
- }
- }
- args[argc] = NULL;
-
- if (argc == 0) /* ignore blank line */
- {
- return 1;
- }
-
- /* should the job run in the background? */
- if ((bg = (*args[argc - 1] == '&')) != 0)
- {
- args[--argc] = NULL;
- }
- return bg;
- }
-
- int check_redirect(char **args, char *redirect_filename, char **redirect_args)
- {
- int i = 0, j = 0, redirect_flag = REDIRECT_NO;
- while (args[i] != NULL)
- {
- if (!strcmp(args[i], ">"))
- {
- redirect_flag = REDIRECT_OUT;
- break;
- }
- else if (!strcmp(args[i], "<"))
- {
- redirect_flag = REDIRECT_IN;
- break;
- }
- i++;
- }
-
- if (redirect_flag == 1) /* redirect output */
- {
- strcpy(redirect_filename, args[i + 1]);
- for (j = 0; j < i; j++)
- {
- redirect_args[j] = args[j];
- }
- }
- else if (redirect_flag == 2) /* redirect input */
- {
- redirect_filename = args[0];
- i++;
- while (args[i] != NULL)
- {
- redirect_args[j++] = args[i++];
- }
- }
- return redirect_flag;
- }
-
- int execute(char *cmdline, char **args)
- {
- int bg = 0, i = 0, redirect_flag = 0, fd = 1;
- pid_t pid;
- char *redirect_filename = NULL;
- redirect_filename = (char *)calloc(32, sizeof(char));
- char *redirect_args[ARGS_MAX_QUANTITY];
- sigset_t mask_all, mask_prev;
- sigprocmask(SIG_BLOCK, NULL, &mask_all);
- sigaddset(&mask_all, SIGCHLD);
- bg = parseline(cmdline, args);
- redirect_flag = check_redirect(args, redirect_filename, redirect_args);
-
- if (args[0] == NULL)
- {
- return 1;
- }
- if (!built_in(args))
- {
- /* Prevent child processes from ending between parent processes, that is, between addJob and deleteJob. */
- sigprocmask(SIG_BLOCK, &mask_all, &mask_prev); /* Shield SIGCHLD */
-
- if ((pid = fork()) == 0) /* Child process */
- {
- if (redirect_flag == 1)
- {
- fd = open(redirect_filename, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
- close(1);
- dup2(1, fd);
- }
- else if (redirect_flag == 2)
- {
- fd = open(redirect_filename, O_RDONLY, S_IRUSR);
- close(0);
- dup2(0, fd);
- }
-
- /* The child process inherits the parent's signal mask and will inherit it after exec,
- so it needs to restore the signal mask before executing. */
- sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* Child process, unblock SIGCHLD */
-
- /* 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 (execvp(args[0], args) <= 0)
- {
- printf("%s: Command not found\n", args[0]);
- free(redirect_filename);
- free(redirect_args);
- exit(0);
- }
- }
- else
- {
- if (bg) /* bg task */
- {
- addjob(jobs, pid, BG, cmdline);
- }
- else /* fg task */
- {
- addjob(jobs, pid, FG, cmdline);
- }
-
- sigprocmask(SIG_SETMASK, &mask_prev, NULL);
-
- if (bg) /* Don't wait for background tasks to finish */
- {
- printf("[%d](%d)%s", pid2jid(pid), pid, cmdline);
- }
- else /* Wait for foreground tasks to finish */
- {
- waitfg(pid);
- }
- }
- }
- close(fd);
- free(redirect_filename);
- free(redirect_args);
- return 1;
- }
-
- int built_in(char **args)
- {
- if (!strcmp(args[0], "exit"))
- {
- exit(0);
- }
- else if (!strcmp(args[0], "cd"))
- {
- return builtin_cd(args);
- }
- else if (!strcmp(args[0], "history"))
- {
- return builtin_history(args);
- }
- else if (!strcmp(args[0], "mytop"))
- {
- return builtin_mytop(args);
- }
- else if (!strcmp(args[0], "jobs"))
- {
- return builtin_jobs(args);
- }
- else
- {
- return 0;
- }
- }
-
- int builtin_cd(char **args)
- {
- if (args[1] == NULL)
- {
- return 1;
- }
- else
- {
- if (chdir(args[1]) != 0)
- {
- perror("yeeshell");
- }
- return 1;
- }
- }
-
- int builtin_history(char **args)
- {
- int n = 0;
- if (args[1] == NULL)
- {
- n = cmdline_amount;
- }
- else
- {
- n = atoi(args[1]) < cmdline_amount ? atoi(args[1]) : cmdline_amount;
- }
- printf("ID\tCommandline\n");
- for (int i = 0; i < n; i++)
- {
- printf("%d\t%s\n", i + 1, history[i]);
- }
- return 1;
- }
-
- int builtin_jobs(char **args)
- {
- /* To prevent interruptions, block all signals. */
- sigset_t mask_all, mask_prev;
- sigfillset(&mask_all);
- sigprocmask(SIG_SETMASK, &mask_all, &mask_prev);
-
- for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
- {
- if (jobs[i].pid != 0)
- {
- printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
- switch (jobs[i].state)
- {
- case BG:
- printf("Running ");
- break;
- case FG:
- printf("Foreground ");
- break;
- case ST:
- printf("Stopped ");
- break;
- default:
- printf("listjobs: Internal error: job[%d].state=%d ",
- i, jobs[i].state);
- }
- printf("%s", jobs[i].cmdline);
- }
- }
-
- sigprocmask(SIG_SETMASK, &mask_prev, NULL); /* unclock */
- return 1;
- }
-
- 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]);
- }
-
- int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline)
- {
- int i;
-
- if (pid < 1)
- return 0;
-
- for (i = 0; i < JOBS_MAX_QUANTITY; i++)
- {
- if (jobs[i].pid == 0)
- {
- jobs[i].pid = pid;
- jobs[i].state = state;
- jobs[i].jid = nextjid++;
- if (nextjid > JOBS_MAX_QUANTITY)
- nextjid = 1;
- strcpy(jobs[i].cmdline, cmdline);
-
- if (verbose)
- {
- printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
- }
- return 1;
- }
- }
- printf("Tried to create too many jobs\n");
- return 0;
- }
-
- void listjobs(struct job_t *jobs)
- {
- for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
- {
- if (jobs[i].pid != 0)
- {
- printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
- switch (jobs[i].state)
- {
- case BG:
- printf("Running ");
- break;
- case FG:
- printf("Foreground ");
- break;
- case ST:
- printf("Stopped ");
- break;
- default:
- printf("listjobs: Internal error: job[%d].state=%d ", i, jobs[i].state);
- }
- printf("%s", jobs[i].cmdline);
- }
- }
- }
-
- int deletejob(struct job_t *jobs, pid_t pid)
- {
- int i;
-
- if (pid < 1)
- return 0;
-
- for (i = 0; i < JOBS_MAX_QUANTITY; i++)
- {
- if (jobs[i].pid == pid)
- {
- clearjob(&jobs[i]);
- nextjid = maxjid(jobs) + 1;
- return 1;
- }
- }
- 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)
- {
- for (int i = 0; i < JOBS_MAX_QUANTITY; i++)
- if (jobs[i].state == FG)
- return jobs[i].pid;
- return 0;
- }
-
- struct job_t *getjobpid(struct job_t *jobs, pid_t pid)
- {
- 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;
- }
-
- int maxjid(struct job_t *jobs)
- {
- int i, max = 0;
-
- for (i = 0; i < JOBS_MAX_QUANTITY; i++)
- {
- if (jobs[i].jid > max)
- {
- max = jobs[i].jid;
- }
- }
- return max;
- }
-
- 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)
- {
- 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);
- }
- }
- return;
- }
-
- void sigint_handler(int signal)
- {
- pid_t pid = fgpid(jobs);
- if (pid != 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);
- }
|