This project focuses on building a custom shell implementation in C, addressing complex challenges like signal handling, job control, and pipe-based inter-process communication. The implementation demonstrates accomplishments in processing user input to handle commands, redirection, and piping. Unique highlights include a dynamic system for managing background jobs with suspended state handling and an elegant mechanism for orderly job reactivation. Through modularized functions like execute_command, this project tackles intricate challenges such as robust memory management, error handling, and multi-process coordination, showcasing significant achievements in system-level programming.
//This program was created as part of the Operating Systems coursework at New York University with project specifications provided by Professor Yang Tang. //Developed by Travis Perry #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> #include <string.h> #include <signal.h> #include <errno.h> #include <pwd.h> #include <fcntl.h> #include <sys/types.h> typedef struct { pid_t pid; char commandName[256]; int status; int index; } jobs; jobs* jobList[100]; int any_suspended_jobs(jobs *jobList[]){ int i=0; while (jobList[i]!=NULL){ if(jobList[i]->status==1){ return 1; } i++; } return 0; } void sigtstp_handler(int signo) { (void) signo; } void execute_command(char *cmd, char *inputFile, char *outputFile, int appendTF, int input_fd, int output_fd) { char *argList[100]; char *args = strtok(cmd, " "); int j = 0; while (args != NULL) { argList[j++] = args; args = strtok(NULL, " "); } argList[j] = NULL; if (inputFile) { int fd = open(inputFile, O_RDONLY); if (fd < 0) { fprintf(stderr,"Error: could not open file"); exit(1); } dup2(fd, STDIN_FILENO); close(fd); } else if (input_fd != -1) { dup2(input_fd, STDIN_FILENO); } if (outputFile) { int fd; if (appendTF) { fd = open(outputFile, O_WRONLY | O_CREAT | O_APPEND, 0644); } else { fd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); } if (fd < 0) { fprintf(stderr,"Error: could not open file"); exit(1); } dup2(fd, STDOUT_FILENO); close(fd); } else if (output_fd != -1) { dup2(output_fd, STDOUT_FILENO); } execvp(argList[0], argList); fprintf(stderr, "Error: exec failed"); exit(1); } int main(void) { signal(SIGTSTP, sigtstp_handler); int numJobs=0; while (1) { // Milestone 1 char cwd[256]; char prompt[256]; getcwd(cwd, sizeof(cwd)); size_t i = strlen(cwd) - 1; if (i == 0) { prompt[0] = '/'; } else { while (i > 0) { if (cwd[i] == '/') { break; } i--; } } i++; int arrIndex = 0; for (; i < strlen(cwd); i++) { prompt[arrIndex] = cwd[i]; arrIndex++; } prompt[arrIndex] = '\0'; if (strcmp(cwd, "/") == 0) { printf("[nyush /]$ "); } else { printf("[nyush %s]$ ", prompt); } fflush(stdout); // Milestone 2:Get user input signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); char *buffer; size_t maxSize = 1000; buffer = malloc(maxSize * sizeof(char)); ssize_t command = getline(&buffer, &maxSize, stdin); if (command == -1) { free(buffer); exit(0); } char storedBuffer[1000]; strcpy(storedBuffer,buffer); buffer[strlen(buffer) - 1] = '\0'; int numPipes=0; int numArgs=1; for (size_t i=0; i") == 0) { args = strtok(NULL, " "); if (args != NULL) { outputFile = args; } } else if (strcmp(args, "<") == 0) { args = strtok(NULL, " "); if (args != NULL) { inputFile = args; } } else { argList[j++] = args; } args = strtok(NULL, " "); } argList[j] = NULL; jobList[numJobs]=malloc(sizeof(jobs)+1); jobList[numJobs]->pid=getpid(); strcpy(jobList[numJobs]->commandName,storedBuffer); jobList[numJobs]->status=0; jobList[numJobs]->index=0; numJobs++; if (strlen(buffer)==0){ continue; } else if (strcmp(argList[0],"cd")==0){ if (numArgs != 2) { fprintf(stderr,"Error: invalid command\n"); } else { if (strcmp(argList[1],prompt)==0){ fprintf(stderr,"Error: invalid directory\n"); } else { chdir(argList[1]); } } } else if (strcmp(buffer,"exit")==0){ if (numArgs>1){ fprintf(stderr,"Error: invalid command\n"); } else{ //Milestone 10 if(any_suspended_jobs(jobList)){ fprintf(stderr,"Error: there are suspended jobs\n"); fflush(stderr); } else { exit(0); } } } else if (strcmp(argList[0],"jobs")==0){ if (numArgs>1){ fprintf(stderr,"Error: invalid command\n"); continue; } if (!any_suspended_jobs(jobList)){ printf("\n"); continue; } jobs *suspendedJobs[100]; // Assuming max 100 suspended jobs int numSuspended = 0; int i=0; while(jobList[i]!=NULL){ if (jobList[i]->status==1){ suspendedJobs[numSuspended] = jobList[i]; numSuspended++; } i++; } for (int j = 0; j < numSuspended - 1; j++) { for (int k = j + 1; k < numSuspended; k++) { if (suspendedJobs[j]->index > suspendedJobs[k]->index) { jobs *temp = suspendedJobs[j]; suspendedJobs[j] = suspendedJobs[k]; suspendedJobs[k] = temp; } } } for (int j = 0; j < numSuspended; j++) { printf("[%d] %s", suspendedJobs[j]->index, suspendedJobs[j]->commandName); } } else if (strcmp(argList[0], "fg") == 0) { if (numArgs != 2) { fprintf(stderr, "Error: invalid command\n"); continue; } else if (!any_suspended_jobs(jobList)){ fprintf(stderr,"Error: invalid job\n"); continue; } int i=0; int jobIndex=0; while(jobList[i]!=NULL){ if (jobList[i]->index==atoi(argList[1])){ jobIndex=i; break; } i++; } pid_t pid = jobList[jobIndex]->pid; kill(pid, SIGCONT); jobList[jobIndex]->status = 0; i=0; int k=0; while(jobList[k]!=NULL){ if (jobList[k]->index > jobList[jobIndex]->index) { jobList[k]->index--; } k++; } jobList[jobIndex]->index = 0; int status; waitpid(pid, &status, WUNTRACED); if (WIFSTOPPED(status)) { jobList[jobIndex]->status=1; fflush(stdout); int maxIndex=0; int i=0; while (jobList[i]!=NULL){ if(jobList[i]->index>maxIndex){ maxIndex=jobList[i]->index; } i++; } jobList[jobIndex]->index=maxIndex+1; } } else { int pid = fork(); if (pid < 0) { fprintf(stderr, "Error: Child process creation failure\n"); } //Milestones 6&7 else if (pid == 0) { signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTSTP, SIG_DFL); if (outputFile) { int fd; if (appendTF) { fd = open(outputFile, O_WRONLY | O_CREAT | O_APPEND, 0644); } else { fd = open(outputFile, O_WRONLY | O_CREAT | O_TRUNC, 0644); } if (fd < 0) { fprintf(stderr,"Error: invalid file\n"); exit(1); } if (dup2(fd, STDOUT_FILENO) < 0) { fprintf(stderr,"Error: invalid file\n"); close(fd); exit(1); } close(fd); } if (inputFile){ int fd = open(inputFile, O_RDONLY); if (fd < 0) { fprintf(stderr,"Error: invalid file\n"); exit(1); } dup2(fd, STDIN_FILENO); close(fd); } //Milestone 8 if (strcmp(argList[0],"fsck")==0){ fprintf(stderr,"Error: invalid program\n"); exit(1); } execvp(argList[0], argList); fprintf(stderr,"Error: invalid program\n"); fflush(stderr); exit(1); } else { jobList[numJobs]=malloc(sizeof(jobs)); jobList[numJobs]->pid=pid; strcpy(jobList[numJobs]->commandName,storedBuffer); numJobs++; int status; waitpid(pid, &status, WUNTRACED); if (WIFSTOPPED(status)) { int maxIndex=0; int i=0; int suspendedJob=0; while (jobList[i]!=NULL){ if (pid==jobList[i]->pid){ jobList[i]->status=1; suspendedJob=i; } if(jobList[i]->index>maxIndex){ maxIndex=jobList[i]->index; } i++; } jobList[suspendedJob]->index=maxIndex+1; } } } } else { char *inputFile = NULL, *outputFile = NULL; int appendTF = 0; char *commands[100]; int numPipes = 0; char *command = strtok(buffer, "|"); while (command != NULL) { commands[numPipes++] = command; command = strtok(NULL, "|"); } int pipes[numPipes - 1][2]; for (int i = 0; i < numPipes - 1; i++) { if(pipe(pipes[i])==-1){ fprintf(stderr,"Error: invalid command\n"); exit(1); }; } for (int i = 0; i < numPipes; i++) { pid_t pid = fork(); if (pid < 0) { fprintf(stderr, "Error: fork failed"); exit(1); } if (pid == 0) { char *redir_cmd = strtok(commands[i], ">"); if (redir_cmd != NULL && i == numPipes - 1) { char *output_part = strtok(NULL, " "); if (output_part != NULL) { outputFile = output_part; } } redir_cmd = strtok(commands[i], "<"); if (redir_cmd != NULL && i == 0) { char *input_part = strtok(NULL, " "); if (input_part != NULL) { inputFile = input_part; } } if (i > 0) { dup2(pipes[i - 1][0], STDIN_FILENO); // Read from previous pipe } if (i < numPipes - 1) { dup2(pipes[i][1], STDOUT_FILENO); // Write to next pipe } for (int j = 0; j < numPipes - 1; j++) { close(pipes[j][0]); close(pipes[j][1]); } execute_command(commands[i], inputFile, outputFile, appendTF, -1, -1); } } for (int i = 0; i < numPipes - 1; i++) { close(pipes[i][0]); close(pipes[i][1]); } for (int i = 0; i < numPipes; i++) { wait(NULL); } } } return 1; }