2020-12-24 04:32:35 +00:00
|
|
|
#define _XOPEN_SOURCE 700
|
2020-12-13 20:15:04 +00:00
|
|
|
|
2020-02-12 05:30:50 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/wait.h>
|
2020-12-24 05:08:54 +00:00
|
|
|
#include <sys/stat.h>
|
2020-02-12 05:30:50 +00:00
|
|
|
#include <signal.h>
|
|
|
|
#include <libgen.h>
|
2020-12-02 02:33:33 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
#define ARG_SEP " " /* Argument separator for commands */
|
2020-02-12 05:30:50 +00:00
|
|
|
|
|
|
|
char** create_argv(char* command);
|
|
|
|
int get_argc(char* command);
|
2020-12-24 04:32:35 +00:00
|
|
|
int get_argc_argv(char * argv[]);
|
2020-02-12 05:30:50 +00:00
|
|
|
char* prompt();
|
|
|
|
int cd(char* path);
|
2020-12-24 04:32:35 +00:00
|
|
|
int _kill(int argc, char * argv[]);
|
2020-02-12 05:30:50 +00:00
|
|
|
|
2020-12-24 05:08:54 +00:00
|
|
|
mode_t _umask = 022;
|
|
|
|
char * prompt_str;
|
|
|
|
#define PROMPT prompt_str
|
2020-12-24 23:31:19 +00:00
|
|
|
int stat_loc; /* Used by waitpid; global because used in _kill() */
|
2020-12-24 05:08:54 +00:00
|
|
|
|
2020-02-12 05:30:50 +00:00
|
|
|
int main() {
|
2020-12-13 20:15:04 +00:00
|
|
|
char** argv;
|
2020-02-12 05:30:50 +00:00
|
|
|
char* user_input;
|
|
|
|
pid_t child_pid;
|
2020-12-24 05:08:54 +00:00
|
|
|
prompt_str = getenv("PS1");
|
|
|
|
prompt_str = prompt_str == NULL ? "$ " : prompt_str;
|
2020-02-12 05:30:50 +00:00
|
|
|
|
2020-12-13 20:15:04 +00:00
|
|
|
signal(SIGINT, SIG_IGN);
|
2020-02-12 05:30:50 +00:00
|
|
|
|
|
|
|
while(1) {
|
|
|
|
user_input = prompt();
|
|
|
|
argv = create_argv(user_input);
|
|
|
|
|
|
|
|
if(strcmp(argv[0], "cd") == 0) {
|
2020-12-13 20:15:04 +00:00
|
|
|
if(cd(argv[1]) < 0) {
|
|
|
|
perror(argv[1]);
|
2020-02-12 05:30:50 +00:00
|
|
|
}
|
2020-12-02 02:33:33 +00:00
|
|
|
/* causes the fork to get skipped */
|
2020-02-12 05:30:50 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-12-24 04:32:35 +00:00
|
|
|
else if(strcmp(argv[0], "kill") == 0) {
|
|
|
|
_kill(get_argc_argv(argv), argv);
|
|
|
|
continue;
|
|
|
|
}
|
2020-02-12 05:30:50 +00:00
|
|
|
else if(strcmp(argv[0], "exit") == 0 ||
|
2020-12-13 20:15:04 +00:00
|
|
|
strcmp(argv[0], "quit") == 0) {
|
2020-02-12 05:30:50 +00:00
|
|
|
exit(0);
|
|
|
|
}
|
2020-12-24 05:08:54 +00:00
|
|
|
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;
|
|
|
|
}
|
2020-02-12 05:30:50 +00:00
|
|
|
|
|
|
|
child_pid = fork();
|
|
|
|
if(child_pid < 0) {
|
2020-12-02 02:33:33 +00:00
|
|
|
/* Could not start child process. Check why and error out. */
|
|
|
|
if(errno == EAGAIN) {
|
2020-12-13 20:15:04 +00:00
|
|
|
perror("Could not execute command: insufficient resources");
|
|
|
|
exit(1);
|
2020-12-02 02:33:33 +00:00
|
|
|
}
|
|
|
|
else if(errno == ENOMEM) {
|
2020-12-13 20:15:04 +00:00
|
|
|
/* Is this out of RAM or out of storage? */
|
|
|
|
perror("Could not execute command: insufficient storage space");
|
|
|
|
exit(1);
|
2020-12-02 02:33:33 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-12-13 20:15:04 +00:00
|
|
|
perror("Could not execute command: unknown failure");
|
|
|
|
exit(1);
|
2020-12-02 02:33:33 +00:00
|
|
|
}
|
2020-02-12 05:30:50 +00:00
|
|
|
}
|
|
|
|
else if(child_pid == 0) {
|
2020-12-02 02:33:33 +00:00
|
|
|
/* We are the child, so allow ^C */
|
2020-02-12 05:30:50 +00:00
|
|
|
signal(SIGINT, SIG_DFL);
|
|
|
|
if(execvp(argv[0], argv) < 0) {
|
2020-12-13 20:15:04 +00:00
|
|
|
perror(argv[0]);
|
|
|
|
exit(1);
|
2020-02-12 05:30:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-12-02 02:33:33 +00:00
|
|
|
/* Wait for the child to finish */
|
2020-02-12 05:30:50 +00:00
|
|
|
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) {
|
2020-12-02 02:33:33 +00:00
|
|
|
perror("Unable to parse command: could not allocate memory");
|
2020-02-12 05:30:50 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(temp = strtok(command, ARG_SEP); temp != NULL;
|
|
|
|
temp = strtok(NULL, ARG_SEP)) {
|
2020-12-24 23:31:19 +00:00
|
|
|
if(strcmp(temp, "$?") == 0) {
|
|
|
|
argv[i] = calloc(4, sizeof(**argv));
|
|
|
|
sprintf(argv[i], "%d", WEXITSTATUS(stat_loc));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
argv[i] = temp;
|
|
|
|
}
|
2020-02-12 05:30:50 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
2020-12-02 02:33:33 +00:00
|
|
|
/* Set last item in argv to NULL, since execvp requires it */
|
2020-02-12 05:30:50 +00:00
|
|
|
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);
|
2020-12-02 02:33:33 +00:00
|
|
|
char* input = calloc(LINE_MAX, sizeof(*input));
|
2020-02-12 05:30:50 +00:00
|
|
|
if(input == NULL) {
|
2020-12-02 02:33:33 +00:00
|
|
|
perror("Unable to read command: could not allocate memory");
|
2020-02-12 05:30:50 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
printf("%s", prompt);
|
2020-12-02 02:33:33 +00:00
|
|
|
fgets(input, LINE_MAX, stdin);
|
2020-02-12 05:30:50 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-12-24 04:32:35 +00:00
|
|
|
int get_argc_argv(char * argv[]) {
|
|
|
|
int argc = 0;
|
|
|
|
while(argv[argc] != NULL) {
|
|
|
|
argc++;
|
|
|
|
}
|
|
|
|
return argc;
|
|
|
|
}
|
|
|
|
|
2020-02-12 05:30:50 +00:00
|
|
|
int cd(char* path) {
|
|
|
|
return chdir(path);
|
|
|
|
}
|
2020-12-24 04:32:35 +00:00
|
|
|
|
|
|
|
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) {
|
2020-12-24 23:31:19 +00:00
|
|
|
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;
|
|
|
|
}
|
2020-12-24 04:32:35 +00:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|