#include #include #include #include #include #include #include #include "yeeshell.h" /* record cmdline history */ char **history = NULL; int cmdline_amount = 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, sizeof(char)); if (!(history = (char **)calloc(CMDLINE_HISTORY_MAX_QUANTITY, CMDLINE_MAX_SIZE * sizeof(char *)))) { printf("yeeshell: allocation error!\n"); exit(EXIT_FAILURE); } /* execute the shell's read, parse and execution loop */ do { if (!getcwd(pwd, PATH_MAX)) { printf("yeeshell: The current path cannot be obtained!\n"); exit(0); } printf("[yeeshell %s]# ", pwd); cmdline = readline(); history[cmdline_amount++] = cmdline; status = execute(cmdline, args); free(cmdline); } while (status); free(history); 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 execute(char *cmdline, char **args) { int bg = 0, i = 0; pid_t pid; sigset_t mask_all, mask_prev; sigprocmask(SIG_BLOCK, NULL, &mask_all); sigaddset(&mask_all, SIGCHLD); bg = parseline(cmdline, 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 */ { /* 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 (execve(args[0], args, environ) <= 0) { printf("%s: Command not found\n", args[0]); exit(0); } } else { do { } while (!args[i++]); 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); } } } 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], "ls")) { return builtin_ls(args); } else if (!strcmp(args[0], "jobs")) { return builtin_jobs(args); } else { return -1; } } int builtin_cd(char **args) { if (args[1] == NULL) { return 1; } else { if (chdir(args[1]) != 0) { perror("yeeshell"); } return 1; } } int builtin_ls(char **args) { DIR *dp; struct dirent *dirp; char *path = NULL; if (args[1] == NULL) { path = (char *)calloc(PATH_MAX, sizeof(char)); getcwd(path, PATH_MAX) } else if (args[1]) if ((dp = opendir(args[1])) == NULL) { perror("yeeshell"); } while ((dirp = readdir(dp)) != NULL) { printf("%s\n", dirp->d_name); } 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++) { printf("%d\t%s\n", i, 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) { } /************************ * manipulate job list ************************/ 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); 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; } 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++) { 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); } } } 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; } 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; } int hide(const char *path) { if (*path == '.') { return 1; } else { return 0; } }