#define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include #define ARG_SEP " " /* Argument separator for commands */ char** create_argv(char* command); int get_argc(char* command); int get_argc_argv(char * argv[]); char* prompt(); int cd(char* path); int _kill(int argc, char * argv[]); mode_t _umask = 022; char * prompt_str; #define PROMPT prompt_str int stat_loc; /* Used by waitpid; global because used in _kill() */ int main() { char** argv; char* user_input; pid_t child_pid; prompt_str = getenv("PS1"); prompt_str = prompt_str == NULL ? "$ " : prompt_str; signal(SIGINT, SIG_IGN); while(1) { user_input = prompt(); argv = create_argv(user_input); if(strcmp(argv[0], "cd") == 0) { if(cd(argv[1]) < 0) { perror(argv[1]); } /* causes the fork to get skipped */ continue; } else if(strcmp(argv[0], "kill") == 0) { _kill(get_argc_argv(argv), argv); continue; } else if(strcmp(argv[0], "exit") == 0 || strcmp(argv[0], "quit") == 0) { exit(0); } else if(strcmp(argv[0], "umask") == 0) { if(strcmp(argv[1], "-S") == 0) { /* TODO: Print symbolic string. */ } else if(argv[1] != NULL) { /* TODO: Implement symbolic support. */ _umask = atoi(argv[1]); umask(_umask); } else { printf("%o\n", _umask); } continue; } child_pid = fork(); if(child_pid < 0) { /* Could not start child process. Check why and error out. */ if(errno == EAGAIN) { perror("Could not execute command: insufficient resources"); exit(1); } else if(errno == ENOMEM) { /* Is this out of RAM or out of storage? */ perror("Could not execute command: insufficient storage space"); exit(1); } else { perror("Could not execute command: unknown failure"); exit(1); } } else if(child_pid == 0) { /* We are the child, so allow ^C */ signal(SIGINT, SIG_DFL); if(execvp(argv[0], argv) < 0) { perror(argv[0]); exit(1); } } else { /* Wait for the child to finish */ waitpid(child_pid, &stat_loc, WUNTRACED); } free(user_input); free(argv); } return 0; } char** create_argv(char* command) { int argc = get_argc(command) + 1; char* temp; int i = 0; char** argv = calloc(argc, sizeof(*argv)); if(argv == NULL) { perror("Unable to parse command: could not allocate memory"); exit(1); } for(temp = strtok(command, ARG_SEP); temp != NULL; temp = strtok(NULL, ARG_SEP)) { if(strcmp(temp, "$?") == 0) { argv[i] = calloc(4, sizeof(**argv)); sprintf(argv[i], "%d", WEXITSTATUS(stat_loc)); } else { argv[i] = temp; } i++; } /* Set last item in argv to NULL, since execvp requires it */ argv[i] = NULL; return argv; } char* prompt() { char cwd[PATH_MAX]; getcwd(cwd, sizeof(cwd)); char* bname = basename(cwd); char prompt[PATH_MAX + 5] = ""; strcat(prompt, bname); strcat(prompt, PROMPT); char* input = calloc(LINE_MAX, sizeof(*input)); if(input == NULL) { perror("Unable to read command: could not allocate memory"); exit(1); } printf("%s", prompt); fgets(input, LINE_MAX, stdin); input[strcspn(input, "\r\n")] = '\0'; return input; } int get_argc(char* command) { int argc = 0, word = 0; char* command_iter = command; do { switch(*command_iter) { case '\0': case ' ': case '\t': if(word) { word = 0; argc++; } default: word = 1; } } while(*(command_iter++)); return argc; } int get_argc_argv(char * argv[]) { int argc = 0; while(argv[argc] != NULL) { argc++; } return argc; } int cd(char* path) { return chdir(path); } int get_signum(char * signame) { if(strcmp(signame, "SIGHUP") == 0) { return 1; } else if(strcmp(signame, "SIGINT") == 0) { return 2; } else if(strcmp(signame, "SIGQUIT") == 0) { return 3; } else if(strcmp(signame, "SIGABRT") == 0) { return 6; } else if(strcmp(signame, "SIGKILL") == 0) { return 9; } else if(strcmp(signame, "SIGALRM") == 0) { return 14; } else if(strcmp(signame, "SIGTERM") == 0) { return 15; } } int _kill(int argc, char * argv[]) { int signal = 0; pid_t pid; char c; int write_signame = 0, error = 0; while((c = getopt(argc, argv, "s:l")) != -1) { switch(c) { case 's': signal = get_signum(optarg); break; case 'l': write_signame = 1; break; } } if(signal == 0 && argv[1][0] == '-' && write_signame == 0) { if(argv[1][1] >= '0' && argv[1][1] <= '9') { signal = atoi((argv[1])+1); } else { signal = get_signum((argv[1])+1); } } if(write_signame) { if(argc > 2 && atoi(argv[3]) == WEXITSTATUS(stat_loc) && WIFSIGNALED(stat_loc)) { switch(WTERMSIG(stat_loc)) { case 0: printf("0\n"); break; case 1: printf("SIGHUP\n"); break; case 2: printf("SIGINT\n"); break; case 3: printf("SIGQUIT\n"); break; case 6: printf("SIGABRT\n"); break; case 9: printf("SIGKILL\n"); break; case 14: printf("SIGALRM\n"); break; case 15: printf("SIGTERM\n"); break; } return 0; } else if(argc > 2) { switch(atoi(argv[3])) { case 0: printf("0\n"); break; case 1: printf("HUP\n"); break; case 2: printf("INT\n"); break; case 3: printf("QUIT\n"); break; case 6: printf("ABRT\n"); break; case 9: printf("KILL\n"); break; case 14: printf("ALRM\n"); break; case 15: printf("TERM\n"); break; } } else { printf("SIGHUP\n"); printf("SIGINT\n"); printf("SIGQUIT\n"); printf("SIGABRT\n"); printf("SIGKILL\n"); printf("SIGALRM\n"); printf("SIGTERM\n"); return 0; } } else { for(int i = optind; i < argc && argv[i] != NULL; i++) { pid = atoi(argv[optind]); if(kill(pid, signal) != 0) { error = 1; } } } }