Created
April 23, 2017 03:43
-
-
Save jalvarado91/cbaf6b5ddba90743bb36abe0635439d6 to your computer and use it in GitHub Desktop.
A shell son
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <sys/errno.h> | |
#include <sys/types.h> | |
#include <fcntl.h> | |
#define MAX_ARGS 20 | |
#define BUFSIZ 1024 | |
#define SHELL_NAME "COP4338" | |
#define PIPE_READ_END 0 | |
#define PIPE_WRITE_END 1 | |
//// TYPE DEFS //// | |
typedef struct command_type { | |
char **argv; | |
int argc; | |
char *infile; | |
char *outfile; | |
int append; | |
} command; | |
pid_t pid[MAX_ARGS]; | |
//// SIGNATURE DEFS //// | |
void execute(char* cmdline); | |
int get_args(char* cmdline, char* args[]); | |
int get_commands(char* args[], int numargs, command commands[]); | |
void run_commands(int number_of_commands, command *comm, int is_async); | |
int do_dup_pipe(int the_pipe[2], int end, int destfd); | |
int do_redirect(char *filename, int is_reading, int should_append, int destfd); | |
void debug_commands(int number_of_commands, command *the_commands); | |
int main (int argc, char* argv []) | |
{ | |
char cmdline[BUFSIZ]; | |
for(;;) { | |
printf("COP4338$ "); | |
if(fgets(cmdline, BUFSIZ, stdin) == NULL) { | |
perror("fgets failed"); | |
exit(1); | |
} | |
execute(cmdline) ; | |
} | |
return 0; | |
} | |
////// FUNCTION DEFS ////// | |
void execute(char* cmdline) | |
{ | |
memset(pid, -1, sizeof pid); | |
int async; | |
char* args[MAX_ARGS]; | |
int number_of_commands; | |
command the_commands[MAX_ARGS]; | |
int nargs = get_args(cmdline, args); | |
if(nargs <= 0) return; | |
if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) { | |
exit(0); | |
} | |
/* check if async call */ | |
if(!strcmp(args[nargs-1], "&")) { async = 1; args[--nargs] = 0; } | |
else async = 0; | |
// Init Command Structs | |
number_of_commands = get_commands(args, nargs, the_commands); | |
// Run Commands | |
run_commands(number_of_commands, the_commands, async); | |
} | |
int get_args(char* cmdline, char* args[]) | |
{ | |
int i = 0; | |
/* if no args */ | |
if((args[0] = strtok(cmdline, "\n\t ")) == NULL) | |
return 0; | |
while((args[++i] = strtok(NULL, "\n\t ")) != NULL) { | |
if(i >= MAX_ARGS) { | |
printf("Too many arguments!\n"); | |
exit(1); | |
} | |
} | |
/* the last one is always NULL */ | |
return i; | |
} | |
int get_commands(char* args[], int numargs, command commands[]) { | |
// Make sure we don't have garbage | |
int i; | |
for (i = 0; i < numargs; i++) { | |
commands[i].argv = NULL; | |
commands[i].argc = 0; | |
commands[i].infile = NULL; | |
commands[i].outfile = NULL; | |
commands[i].append = 0; | |
} | |
// Make Commands | |
int arg_idx; | |
int command_count = 0; | |
for (arg_idx = 0; arg_idx < numargs; arg_idx++) { | |
if (commands[command_count].argc == 0) { | |
commands[command_count].argv = args + arg_idx; | |
commands[command_count].argc++; | |
} | |
else if ((!strcmp(args[arg_idx], "<")) && (arg_idx+1 < numargs)) { | |
if(strchr("|<>", *args[arg_idx+1])) { | |
fprintf(stderr, "Missing redirect source\n"); | |
return 0; | |
} | |
else { | |
args[arg_idx] = NULL; | |
commands[command_count].infile = args[++arg_idx]; | |
} | |
} | |
else if ( (!strcmp(args[arg_idx], ">") || | |
(!strcmp(args[arg_idx], ">>")) ) && (arg_idx+1 < numargs)) { | |
if(strchr("|<>", *args[arg_idx+1])) { | |
fprintf(stderr, "Missing redirect destination\n"); | |
return 0; | |
} | |
else { | |
if (!strcmp(args[arg_idx], ">>")) | |
commands[command_count].append = 1; | |
args[arg_idx] = NULL; | |
commands[command_count].outfile = args[++arg_idx]; | |
} | |
} | |
else if (!strcmp(args[arg_idx], "|")) { | |
if ( (arg_idx+1 < numargs) && strchr("|<>", *args[arg_idx+1]) ) { | |
fprintf(stderr, "Missing pipe destination\n"); | |
return 0; | |
} | |
else { | |
args[arg_idx] = NULL; | |
command_count++; | |
} | |
} | |
else if ((!strcmp(args[arg_idx], "<") || | |
!strcmp(args[arg_idx], ">") || !strcmp(args[arg_idx], ">>") ) && | |
(arg_idx == numargs - 1) ) { | |
fprintf(stderr, "Missing redirect destination\n"); | |
return 0; | |
} | |
else { | |
if (commands[command_count].infile || commands[command_count].outfile) { | |
fprintf(stderr, "Extra characters after command: %s\n", args[arg_idx]); | |
return 0; | |
} | |
commands[command_count].argc++; | |
} | |
if ( arg_idx == numargs - 1) | |
command_count++; | |
} | |
if (command_count > 0 && commands[command_count-1].argv == NULL) { | |
fprintf(stderr, "Invalid null command\n"); | |
command_count = 0; | |
} | |
return command_count; | |
} | |
/** | |
* Pipe -> Fork | |
* if child: dup when needed and redirect when needed, execute | |
* if parent: close pipes, wait for child | |
*/ | |
void run_commands(int number_of_commands, command *comm, int is_async) { | |
int pipefds[2][2]; | |
int i; | |
for (i = 0; i < number_of_commands; i++) { | |
int is_first = 0; | |
int is_last = 0; | |
if (i == 0) is_first = 1; | |
if (i == number_of_commands - 1) is_last = 1; | |
// Only redirect first or last commands | |
if ( (!is_first && comm[i].infile) || (!is_last && comm[i].outfile) ) { | |
fprintf(stderr, "%s: Invalid syntax \n", SHELL_NAME); | |
return; | |
} | |
if (!is_last && pipe(pipefds[i%2]) < 0) { | |
fprintf(stderr, "%s: %s\n", SHELL_NAME, strerror(errno)); | |
} | |
if ((pid[i] = fork()) < 0) { // Error | |
fprintf(stderr, "%s: %s\n", SHELL_NAME, strerror(errno)); | |
} | |
else if (pid[i] == 0) { // Child Process | |
if (number_of_commands > 1) { | |
if (!is_last) { | |
if (do_dup_pipe(pipefds[i%2], PIPE_WRITE_END, STDOUT_FILENO) < 0) { | |
fprintf(stderr, "%s: %s\n", SHELL_NAME, strerror(errno)); | |
exit(-1); | |
} | |
} | |
if (!is_first) { | |
if (do_dup_pipe(pipefds[(i+1)%2], PIPE_READ_END, STDIN_FILENO) < 0) { | |
fprintf(stderr, "%s: %s\n", SHELL_NAME, strerror(errno)); | |
exit(-1); | |
} | |
} | |
} | |
if (is_first && comm[i].infile) { | |
// READING | |
if (do_redirect(comm[i].infile, 1, comm[i].append, STDIN_FILENO) < 0) { | |
fprintf(stderr, "%s: %s\n", SHELL_NAME, strerror(errno)); | |
exit(-1); | |
} | |
} | |
if (is_last && comm[i].outfile) { | |
// WRITING | |
if (do_redirect(comm[i].outfile, 0, comm[i].append, STDOUT_FILENO) < 0) { | |
fprintf(stderr, "%s: %s\n", SHELL_NAME, strerror(errno)); | |
exit(-1); | |
} | |
} | |
if (execvp(comm[i].argv[0], comm[i].argv) < 0) { | |
fprintf(stderr, "%s: %s\n", SHELL_NAME, strerror(errno)); | |
exit(-1); | |
} | |
} | |
else { // Parent Process | |
if (is_first) { | |
continue; | |
} | |
if (close(pipefds[(i+1)%2][PIPE_WRITE_END]) < 0 || | |
close(pipefds[(i+1)%2][PIPE_READ_END]) < 0) { | |
fprintf(stderr, "%s: %s\n", SHELL_NAME, strerror(errno)); | |
} | |
} | |
} | |
for (i = 0; i < number_of_commands; i ++) { | |
int status; | |
if (!is_async) { | |
do { | |
waitpid(pid[i], &status, 0); | |
} while (!WIFEXITED(status) && !WIFSIGNALED(status)); | |
pid[i] = -1; | |
} | |
else { | |
printf("This is an async call\n"); | |
} | |
} | |
} | |
int do_dup_pipe(int the_pipe[2], int end, int destfd) { | |
if (dup2(the_pipe[end], destfd) < 0) return -1; | |
if (close(the_pipe[PIPE_WRITE_END]) < 0) return -1; | |
if (close(the_pipe[PIPE_READ_END]) < 0) return -1; | |
return destfd; | |
} | |
int do_redirect(char *filename, int is_reading, int should_append, int destfd) { | |
int fd; | |
int oflags; | |
if (!is_reading) { // writing | |
if (!should_append) { | |
oflags = O_WRONLY | O_TRUNC | O_CREAT; | |
} | |
else { | |
oflags = O_WRONLY | O_CREAT | O_APPEND; | |
} | |
} | |
else { // reading | |
oflags = O_RDONLY; | |
} | |
if ((fd = open(filename, oflags, S_IRUSR | S_IWUSR)) < 0) return -1; | |
if (dup2(fd, destfd) < 0) return -1; | |
if (close(fd) < 0) return -1; | |
return destfd; | |
} | |
void debug_commands(int number_of_commands, command *the_commands) { | |
int allcmds = 0; | |
printf("NUM COMMANDS: %d\n", number_of_commands); | |
for (; allcmds < number_of_commands; allcmds++) { | |
printf("=============================================\n"); | |
printf("A COMMAND's argv: %s\n", (*the_commands[allcmds].argv)); | |
printf("A COMMAND's argc: %d\n", the_commands[allcmds].argc); | |
printf("A COMMAND's infile: %s\n", the_commands[allcmds].infile); | |
printf("A COMMAND's outfile: %s\n", the_commands[allcmds].outfile); | |
printf("A COMMAND's append: %d\n", the_commands[allcmds].append); | |
printf("=============================================\n"); | |
printf("\n"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment