Skip to content

Instantly share code, notes, and snippets.

@jalvarado91
Created April 23, 2017 03:43
Show Gist options
  • Save jalvarado91/cbaf6b5ddba90743bb36abe0635439d6 to your computer and use it in GitHub Desktop.
Save jalvarado91/cbaf6b5ddba90743bb36abe0635439d6 to your computer and use it in GitHub Desktop.
A shell son
#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