diff --git a/6-RShell/bats/assignment_tests.sh b/6-RShell/bats/assignment_tests.sh
new file mode 100644
index 0000000000000000000000000000000000000000..5246ee441c5e049315e4d3191569dd6314c40bb9
--- /dev/null
+++ b/6-RShell/bats/assignment_tests.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bats
+
+############################ DO NOT EDIT THIS FILE #####################################
+# File: assignement_tests.sh
+# 
+# DO NOT EDIT THIS FILE
+#
+# Add/Edit Student tests in student_tests.sh
+# 
+# All tests in this file must pass - it is used as part of grading!
+########################################################################################
+
+@test "Pipes" {
+    run "./dsh" <<EOF                
+ls | grep dshlib.c
+EOF
+
+    # Strip all whitespace (spaces, tabs, newlines) from the output
+    stripped_output=$(echo "$output" | tr -d '[:space:]')
+
+    # Expected output with all whitespace removed for easier matching
+    expected_output="dshlib.clocalmodedsh4>dsh4>cmdloopreturned0"
+
+    # These echo commands will help with debugging and will only print
+    #if the test fails
+    echo "Captured stdout:" 
+    echo "Output: $output"
+    echo "Exit Status: $status"
+    echo "${stripped_output} -> ${expected_output}"
+
+    # Check exact match
+    [ "$stripped_output" = "$expected_output" ]
+
+    # Assertions
+    [ "$status" -eq 0 ]
+}
diff --git a/6-RShell/bats/student_tests.sh b/6-RShell/bats/student_tests.sh
new file mode 100644
index 0000000000000000000000000000000000000000..638bc341446f7580a80c2aff52971b8023407ea8
--- /dev/null
+++ b/6-RShell/bats/student_tests.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bats
+
+# File: student_tests.sh
+# 
+# Create your unit tests suit in this file
+
+@test "Example: check ls runs without errors" {
+    run ./dsh <<EOF                
+ls
+EOF
+
+    # Assertions
+    [ "$status" -eq 0 ]
+}
diff --git a/6-RShell/dragon.txt b/6-RShell/dragon.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a9177fd41bb14d2fa0fd439742e74d3f61f60d2d
--- /dev/null
+++ b/6-RShell/dragon.txt
@@ -0,0 +1,38 @@
+                                                                        @%%%%                       
+                                                                     %%%%%%                         
+                                                                    %%%%%%                          
+                                                                 % %%%%%%%           @              
+                                                                %%%%%%%%%%        %%%%%%%           
+                                       %%%%%%%  %%%%@         %%%%%%%%%%%%@    %%%%%%  @%%%%        
+                                  %%%%%%%%%%%%%%%%%%%%%%      %%%%%%%%%%%%%%%%%%%%%%%%%%%%          
+                                %%%%%%%%%%%%%%%%%%%%%%%%%%   %%%%%%%%%%%% %%%%%%%%%%%%%%%           
+                               %%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%     %%%            
+                             %%%%%%%%%%%%%%%%%%%%%%%%%%%%@ @%%%%%%%%%%%%%%%%%%        %%            
+                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%                
+                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              
+                            %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@%%%%%%@              
+      %%%%%%%%@           %%%%%%%%%%%%%%%%        %%%%%%%%%%%%%%%%%%%%%%%%%%      %%                
+    %%%%%%%%%%%%%         %%@%%%%%%%%%%%%           %%%%%%%%%%% %%%%%%%%%%%%      @%                
+  %%%%%%%%%%   %%%        %%%%%%%%%%%%%%            %%%%%%%%%%%%%%%%%%%%%%%%                        
+ %%%%%%%%%       %         %%%%%%%%%%%%%             %%%%%%%%%%%%@%%%%%%%%%%%                       
+%%%%%%%%%@                % %%%%%%%%%%%%%            @%%%%%%%%%%%%%%%%%%%%%%%%%                     
+%%%%%%%%@                 %%@%%%%%%%%%%%%            @%%%%%%%%%%%%%%%%%%%%%%%%%%%%                  
+%%%%%%%@                   %%%%%%%%%%%%%%%           %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              
+%%%%%%%%%%                  %%%%%%%%%%%%%%%          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%      %%%%  
+%%%%%%%%%@                   @%%%%%%%%%%%%%%         %%%%%%%%%%%%@ %%%% %%%%%%%%%%%%%%%%%   %%%%%%%%
+%%%%%%%%%%                  %%%%%%%%%%%%%%%%%        %%%%%%%%%%%%%      %%%%%%%%%%%%%%%%%% %%%%%%%%%
+%%%%%%%%%@%%@                %%%%%%%%%%%%%%%%@       %%%%%%%%%%%%%%     %%%%%%%%%%%%%%%%%%%%%%%%  %%
+ %%%%%%%%%%                  % %%%%%%%%%%%%%%@        %%%%%%%%%%%%%%   %%%%%%%%%%%%%%%%%%%%%%%%%% %%
+  %%%%%%%%%%%%  @           %%%%%%%%%%%%%%%%%%        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  %%% 
+   %%%%%%%%%%%%% %%  %  %@ %%%%%%%%%%%%%%%%%%          %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    %%% 
+    %%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%           @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    %%%%%%% 
+     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%              %%%%%%%%%%%%%%%%%%%%%%%%%%%%        %%%   
+      @%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                  %%%%%%%%%%%%%%%%%%%%%%%%%               
+        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                      %%%%%%%%%%%%%%%%%%%  %%%%%%%          
+           %%%%%%%%%%%%%%%%%%%%%%%%%%                           %%%%%%%%%%%%%%%  @%%%%%%%%%         
+              %%%%%%%%%%%%%%%%%%%%           @%@%                  @%%%%%%%%%%%%%%%%%%   %%%        
+                  %%%%%%%%%%%%%%%        %%%%%%%%%%                    %%%%%%%%%%%%%%%    %         
+                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                      %%%%%%%%%%%%%%            
+                %%%%%%%%%%%%%%%%%%%%%%%%%%  %%%% %%%                      %%%%%%%%%%  %%%@          
+                     %%%%%%%%%%%%%%%%%%% %%%%%% %%                          %%%%%%%%%%%%%@          
+                                                                                 %%%%%%%@       
diff --git a/6-RShell/dsh b/6-RShell/dsh
new file mode 100755
index 0000000000000000000000000000000000000000..4a114e1042aaab4e10a5c2019c72fe1a06536bff
Binary files /dev/null and b/6-RShell/dsh differ
diff --git a/6-RShell/dsh_cli.c b/6-RShell/dsh_cli.c
new file mode 100644
index 0000000000000000000000000000000000000000..3cf5a244c9f7e66b80557be2e6b283220c85d0d7
--- /dev/null
+++ b/6-RShell/dsh_cli.c
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <getopt.h>
+
+#include "dshlib.h"
+#include "rshlib.h"
+
+
+/*
+ * Used to pass startup parameters back to main
+ */
+#define MODE_LCLI   0       //Local client
+#define MODE_SCLI   1       //Socket client
+#define MODE_SSVR   2       //Socket server
+
+typedef struct cmd_args{
+  int   mode;
+  char  ip[16];   //e.g., 192.168.100.101\0
+  int   port;
+  int   threaded_server;
+}cmd_args_t;
+
+
+
+//You dont really need to understand this but the C runtime library provides
+//an getopt() service to simplify handling command line arguments.  This
+//code will help setup dsh to handle triggering client or server mode along
+//with passing optional connection parameters. 
+
+void print_usage(const char *progname) {
+  printf("Usage: %s [-c | -s] [-i IP] [-p PORT] [-x] [-h]\n", progname);
+  printf("  Default is to run %s in local mode\n", progname);
+  printf("  -c            Run as client\n");
+  printf("  -s            Run as server\n");
+  printf("  -i IP         Set IP/Interface address (only valid with -c or -s)\n");
+  printf("  -p PORT       Set port number (only valid with -c or -s)\n");
+  printf("  -x            Enable threaded mode (only valid with -s)\n");
+  printf("  -h            Show this help message\n");
+  exit(0);
+}
+
+void parse_args(int argc, char *argv[], cmd_args_t *cargs) {
+  int opt;
+  memset(cargs, 0, sizeof(cmd_args_t));
+
+  //defaults
+  cargs->mode = MODE_LCLI;
+  cargs->port = RDSH_DEF_PORT;
+
+  while ((opt = getopt(argc, argv, "csi:p:xh")) != -1) {
+      switch (opt) {
+          case 'c':
+              if (cargs->mode != MODE_LCLI) {
+                  fprintf(stderr, "Error: Cannot use both -c and -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              cargs->mode = MODE_SCLI;
+              strncpy(cargs->ip, RDSH_DEF_CLI_CONNECT, sizeof(cargs->ip) - 1);
+              break;
+          case 's':
+              if (cargs->mode != MODE_LCLI) {
+                  fprintf(stderr, "Error: Cannot use both -c and -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              cargs->mode = MODE_SSVR;
+              strncpy(cargs->ip, RDSH_DEF_SVR_INTFACE, sizeof(cargs->ip) - 1);
+              break;
+          case 'i':
+              if (cargs->mode == MODE_LCLI) {
+                  fprintf(stderr, "Error: -i can only be used with -c or -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              strncpy(cargs->ip, optarg, sizeof(cargs->ip) - 1);
+              cargs->ip[sizeof(cargs->ip) - 1] = '\0';  // Ensure null termination
+              break;
+          case 'p':
+              if (cargs->mode == MODE_LCLI) {
+                  fprintf(stderr, "Error: -p can only be used with -c or -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              cargs->port = atoi(optarg);
+              if (cargs->port <= 0) {
+                  fprintf(stderr, "Error: Invalid port number\n");
+                  exit(EXIT_FAILURE);
+              }
+              break;
+          case 'x':
+              if (cargs->mode != MODE_SSVR) {
+                  fprintf(stderr, "Error: -x can only be used with -s\n");
+                  exit(EXIT_FAILURE);
+              }
+              cargs->threaded_server = 1;
+              break;
+          case 'h':
+              print_usage(argv[0]);
+              break;
+          default:
+              print_usage(argv[0]);
+      }
+  }
+
+  if (cargs->threaded_server && cargs->mode != MODE_SSVR) {
+      fprintf(stderr, "Error: -x can only be used with -s\n");
+      exit(EXIT_FAILURE);
+  }
+}
+
+
+
+/* DO NOT EDIT
+ * main() logic fully implemented to:
+ *    1. run locally (no parameters)
+ *    2. start the server with the -s option
+ *    3. start the client with the -c option
+*/
+int main(int argc, char *argv[]){
+  cmd_args_t cargs;
+  int rc;
+
+  memset(&cargs, 0, sizeof(cmd_args_t));
+  parse_args(argc, argv, &cargs);
+
+  switch(cargs.mode){
+    case MODE_LCLI:
+      printf("local mode\n");
+      rc = exec_local_cmd_loop();
+      break;
+    case MODE_SCLI:
+      printf("socket client mode:  addr:%s:%d\n", cargs.ip, cargs.port);
+      rc = exec_remote_cmd_loop(cargs.ip, cargs.port);
+      break;
+    case MODE_SSVR:
+      printf("socket server mode:  addr:%s:%d\n", cargs.ip, cargs.port);
+      if (cargs.threaded_server){
+        printf("-> Multi-Threaded Mode\n");
+      } else {
+        printf("-> Single-Threaded Mode\n");
+      }
+      rc = start_server(cargs.ip, cargs.port, cargs.threaded_server);
+      break;
+    default:
+      printf("error unknown mode\n");
+      exit(EXIT_FAILURE);
+  }
+
+  printf("cmd loop returned %d\n", rc);
+}
diff --git a/6-RShell/dshlib.c b/6-RShell/dshlib.c
new file mode 100644
index 0000000000000000000000000000000000000000..5cc3320f37818d0f8f8e343f52a40180be76b0e1
--- /dev/null
+++ b/6-RShell/dshlib.c
@@ -0,0 +1,448 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#include "dshlib.h"
+
+/**** 
+ **** FOR REMOTE SHELL USE YOUR SOLUTION FROM SHELL PART 3 HERE
+ **** THE MAIN FUNCTION CALLS THIS ONE AS ITS ENTRY POINT TO
+ **** EXECUTE THE SHELL LOCALLY
+ ****
+ */
+
+/*
+ * Implement your exec_local_cmd_loop function by building a loop that prompts the 
+ * user for input.  Use the SH_PROMPT constant from dshlib.h and then
+ * use fgets to accept user input.
+ * 
+ *      while(1){
+ *        printf("%s", SH_PROMPT);
+ *        if (fgets(cmd_buff, ARG_MAX, stdin) == NULL){
+ *           printf("\n");
+ *           break;
+ *        }
+ *        //remove the trailing \n from cmd_buff
+ *        cmd_buff[strcspn(cmd_buff,"\n")] = '\0';
+ * 
+ *        //IMPLEMENT THE REST OF THE REQUIREMENTS
+ *      }
+ * 
+ *   Also, use the constants in the dshlib.h in this code.  
+ *      SH_CMD_MAX              maximum buffer size for user input
+ *      EXIT_CMD                constant that terminates the dsh program
+ *      SH_PROMPT               the shell prompt
+ *      OK                      the command was parsed properly
+ *      WARN_NO_CMDS            the user command was empty
+ *      ERR_TOO_MANY_COMMANDS   too many pipes used
+ *      ERR_MEMORY              dynamic memory management failure
+ * 
+ *   errors returned
+ *      OK                     No error
+ *      ERR_MEMORY             Dynamic memory management failure
+ *      WARN_NO_CMDS           No commands parsed
+ *      ERR_TOO_MANY_COMMANDS  too many pipes used
+ *   
+ *   console messages
+ *      CMD_WARN_NO_CMD        print on WARN_NO_CMDS
+ *      CMD_ERR_PIPE_LIMIT     print on ERR_TOO_MANY_COMMANDS
+ *      CMD_ERR_EXECUTE        print on execution failure of external command
+ * 
+ *  Standard Library Functions You Might Want To Consider Using (assignment 1+)
+ *      malloc(), free(), strlen(), fgets(), strcspn(), printf()
+ * 
+ *  Standard Library Functions You Might Want To Consider Using (assignment 2+)
+ *      fork(), execvp(), exit(), chdir()
+ */
+int numInstanceOf(char *str, const char c) {
+    int count = 0;
+    while (*str != '\0') {
+        if (*str == c) count++;
+        str++;
+    }
+    return count;
+}
+
+void print_dragon(){
+    FILE *dragon = fopen("dragon.txt", "r");
+    if (dragon == NULL) {
+            return;
+    }
+
+    char *s = NULL;
+    size_t nbyte;
+    ssize_t nchar;
+
+    while (1) {
+            nchar = getline(&s, &nbyte, dragon);
+            if (nchar == -1) {
+            break;
+    }
+    if (nchar == 0) {
+        continue;
+    }
+    if (s == NULL) {
+        exit(1);
+    }
+    if (s[nchar - 1] == '\n') {
+        s[nchar - 1] = '\0';
+        nchar--;
+    }
+
+    printf("%s\n", s);
+    }
+    free(s);
+    fclose(dragon);
+}
+
+char* trim_whitespace(char *str) {
+    int start = 0;
+    while (isspace((unsigned char)str[start])) {
+        start++;
+    }
+
+    int end = strlen(str) - 1;
+    while (end > start && isspace((unsigned char)str[end])) {
+        end--;
+    }
+
+    int j = 0;
+    for (int i = start; i <= end; i++) {
+        str[j++] = str[i];
+    }
+    str[j] = '\0';
+
+    return str;
+}
+
+int alloc_cmd_buff(cmd_buff_t *cmd_buff) {
+    if (cmd_buff == NULL) return ERR_MEMORY;
+
+    cmd_buff->argc = 0;
+
+    cmd_buff->argv = (char**)malloc(CMD_ARGV_MAX * sizeof(char *));
+    if (cmd_buff->argv == NULL) return ERR_MEMORY;
+
+    for (int i = 0; i < CMD_ARGV_MAX; i++) {
+        cmd_buff->argv[i] = (char *)malloc(ARG_MAX * sizeof(char));
+        if (cmd_buff->argv[i] == NULL) {
+            for (int j = 0; j < i; j++) free(cmd_buff->argv[j]);
+            free(cmd_buff->argv);
+            return ERR_MEMORY;
+        }
+    }
+
+    cmd_buff->_cmd_buffer = (char *)malloc(SH_CMD_MAX * sizeof(char));
+    if (cmd_buff->_cmd_buffer == NULL) {
+        for (int i = 0; i < CMD_ARGV_MAX; i++) free(cmd_buff->argv[i]);
+        free(cmd_buff->argv);
+        return ERR_MEMORY;
+    }
+
+    return OK;
+}
+
+void free_cmd_buff(cmd_buff_t *cmd_buff) {
+    if (cmd_buff != NULL) {
+                if (cmd_buff->_cmd_buffer != NULL) {
+                free(cmd_buff->_cmd_buffer);
+                cmd_buff->_cmd_buffer = NULL;
+                }
+
+                if (cmd_buff->argv != NULL) {
+                for (int i = 0; i < CMD_ARGV_MAX; i++) {
+                        if (cmd_buff->argv[i] != NULL) {
+                                free(cmd_buff->argv[i]);
+                                cmd_buff->argv[i] = NULL;
+                        }
+                }
+                free(cmd_buff->argv);
+                cmd_buff->argv = NULL;
+        }
+        memset(cmd_buff, 0, sizeof(cmd_buff_t));
+    }
+}
+
+void clear_cmd_buff(cmd_buff_t *cmd_buff) {
+    if (cmd_buff != NULL) {
+        cmd_buff->argc = 0;
+        if (cmd_buff->_cmd_buffer) memset(cmd_buff->_cmd_buffer, 0, strlen(cmd_buff->_cmd_buffer));
+        for (int i = 0; i < CMD_ARGV_MAX; i++) cmd_buff->argv[i] = NULL;
+    }
+}
+
+int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff) {
+    if ((int)strlen(cmd_line) > SH_CMD_MAX) return ERR_CMD_OR_ARGS_TOO_BIG;
+
+    if ((int)strlen(cmd_line) == 0) return WARN_NO_CMDS;
+
+        if (cmd_buff->_cmd_buffer != NULL) {
+                free(cmd_buff->_cmd_buffer);
+                cmd_buff->_cmd_buffer = strdup(trim_whitespace(cmd_line));
+        } else return ERR_MEMORY;
+
+    char *token = cmd_buff->_cmd_buffer;
+    bool quotes = false;
+    char *p = NULL;
+
+    while (*token) {
+        if (*token == DOUBLE_QUOTE_CHAR) {
+            quotes = !quotes;
+            if (quotes) p = token + 1;
+            else *token = '\0';
+        } else if (!quotes && (*token == SPACE_CHAR || *token == '\t')) {
+            *token = '\0';
+
+            if (p != NULL) {
+                cmd_buff->argv[cmd_buff->argc++] = p;
+                p = NULL;
+            }
+        } else if (p == NULL) {
+            p = token;
+        }
+        token++;
+    }
+
+        if (p != NULL) {
+                if (cmd_buff->argc >= CMD_ARGV_MAX - 1) return ERR_CMD_OR_ARGS_TOO_BIG;
+        cmd_buff->argv[cmd_buff->argc++] = p;
+        }
+
+    cmd_buff->argv[cmd_buff->argc] = NULL;
+    return OK;
+}
+
+int alloc_cmd_list(command_list_t *clist, int rc) {
+        if (clist == NULL) return ERR_MEMORY;
+        clist->num = 0;
+        clist->commands = (cmd_buff_t **)calloc(CMD_MAX, sizeof(cmd_buff_t *));
+        if (clist->commands == NULL) return ERR_MEMORY;
+
+        for (int i = 0; i < CMD_MAX; i++) {
+                cmd_buff_t *cmd = (cmd_buff_t *)malloc(sizeof(cmd_buff_t));
+                if (cmd == NULL) {
+                        rc = ERR_MEMORY;
+                        break;
+                }
+
+                memset(cmd, 0, sizeof(cmd_buff_t));
+
+                if ((rc = alloc_cmd_buff(cmd)) != OK_EXIT) {
+                        free(cmd);
+                        break;
+        }
+        clist->commands[i] = cmd;
+    }
+
+    return rc;
+}
+
+int build_cmd_list(char *cmd_line, command_list_t *clist, int rc) {
+    if (numInstanceOf(cmd_line, PIPE_CHAR) > CMD_MAX-1) return ERR_TOO_MANY_COMMANDS;
+
+    if ((int)strlen(cmd_line) > SH_CMD_MAX) return ERR_CMD_OR_ARGS_TOO_BIG;
+
+    char *outer_saveptr = NULL;
+
+    char *outer_token = strtok_r(cmd_line, PIPE_STRING, &outer_saveptr);
+
+    while (outer_token != NULL) {
+        if (clist->num > CMD_MAX) return ERR_TOO_MANY_COMMANDS;
+
+                cmd_buff_t *cmd = malloc(sizeof(cmd_buff_t));
+
+                if ((rc = alloc_cmd_buff(cmd)) != OK) {
+                        free(cmd);
+                        return rc;
+                }
+
+        if ((rc = build_cmd_buff(outer_token, cmd)) != OK) {
+                free(cmd);
+                return rc;
+        }
+
+        clist->commands[clist->num] = cmd;
+        clist->num++;
+
+        outer_token = strtok_r(NULL, PIPE_STRING, &outer_saveptr);
+    }
+
+    return OK;
+}
+
+void free_cmd_list(command_list_t *clist) {
+        if (clist != NULL) {
+                if (clist->commands != NULL) {
+                        for (int i = 0; i < CMD_MAX; i++) {
+                                if (clist->commands[i] != NULL) {
+                                        free_cmd_buff(clist->commands[i]);
+                                        free(clist->commands[i]);
+                                        clist->commands[i] = NULL;
+                                }
+                        }
+                        free(clist->commands);
+                        clist->commands = NULL;
+                }
+        }
+}
+
+void clear_cmd_list(command_list_t *clist) {
+        if (clist != NULL) {
+                clist->num = 0;
+                for (int i = 0; i < CMD_MAX; i++) clear_cmd_buff(clist->commands[i]);
+        }
+}
+
+int execute_pipeline(command_list_t *clist) {
+        int pipes[clist->num - 1][2];
+    pid_t pids[clist->num];
+
+    for (int i = 0; i < clist->num - 1; i++) {
+        if (pipe(pipes[i]) == -1) {
+            perror("pipe");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+        for (int i = 0; i < clist->num; i++) {
+        pids[i] = fork();
+        if (pids[i] == -1) {
+            perror("fork");
+            exit(EXIT_FAILURE);
+        }
+
+        if (pids[i] == 0) {
+            if (i > 0) dup2(pipes[i-1][0], STDIN_FILENO);
+
+            if (i < clist->num - 1) dup2(pipes[i][1], STDOUT_FILENO);
+
+            for (int j = 0; j < clist->num - 1; j++) {
+                close(pipes[j][0]);
+                close(pipes[j][1]);
+            }
+
+            execvp(clist->commands[i]->argv[0], clist->commands[i]->argv);
+            perror("execvp");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    for (int i = 0; i < clist->num - 1; i++) {
+        close(pipes[i][0]);
+        close(pipes[i][1]);
+    }
+
+    for (int i = 0; i < clist->num; i++) waitpid(pids[i], NULL, 0);
+
+        return OK;
+}
+
+int exec_one_cmd(command_list_t *clist, int rc) {
+        cmd_buff_t *cmd = clist->commands[0];
+        if (strcmp(cmd->argv[0], "dragon") == 0) {
+                print_dragon();
+                return OK;
+        }
+
+        if (strcmp(cmd->argv[0], "rc") == 0) {
+                printf("%d\n", rc);
+                return OK;
+        }
+
+        if (strcmp(cmd->argv[0], EXIT_CMD) == 0) {
+                printf("exiting...\n");
+                return(OK_EXIT);
+        }
+
+        cmd->argv[cmd->argc] = 0;
+
+        if (strcmp(cmd->argv[0], "cd") == 0) chdir(cmd->argv[1]);
+        else {
+                int f_result, c_result;
+                f_result = fork();
+
+                if (f_result < 0) perror("fork failed");
+                else if (f_result == 0) {
+                        rc = execvp(cmd->argv[0], cmd->argv);
+                        perror("execvp failed");
+                        exit(EXIT_FAILURE);
+                } else {
+                        wait(&c_result);
+                }
+        }
+        return OK;
+}
+
+int exec_cmd(command_list_t *clist, int rc) {
+        if (clist == NULL) return ERR_MEMORY;
+
+        if (rc == ERR_TOO_MANY_COMMANDS) {
+        printf(CMD_ERR_PIPE_LIMIT, CMD_MAX);
+        return rc;
+    }
+
+        char *cmd = clist->commands[0]->argv[0];
+    if (!cmd) return ERR_MEMORY;
+
+        if (clist->num == 1) {
+                if ((rc = exec_one_cmd(clist, rc)) != OK) {
+                        if (rc == OK_EXIT) {
+                                clear_cmd_list(clist);
+                                return rc;
+                        } else return ERR_EXEC_CMD;
+                }
+        }
+
+        if (clist->num > 1) {
+                if ((rc = execute_pipeline(clist)) != OK) return ERR_EXEC_CMD;
+        }
+
+        return OK;
+}
+
+int exec_local_cmd_loop()
+{
+        char *cmd_line = (char *)malloc(ARG_MAX * sizeof(char));
+        int rc = OK;
+        command_list_t *clist = (command_list_t *)malloc(sizeof(command_list_t));
+
+        if ((rc = alloc_cmd_list(clist, rc)) != OK) {
+                free(cmd_line);
+                return rc;
+        }
+
+        while(1){
+                printf("%s", SH_PROMPT);
+        if (fgets(cmd_line, ARG_MAX, stdin) == NULL){
+                printf("\n");
+            break;
+        }
+        //remove the trailing \n from cmd_buff
+        cmd_line[strcspn(cmd_line,"\n")] = '\0';
+
+        //IMPLEMENT THE REST OF THE REQUIREMENTS
+                if (strlen(cmd_line) == 0) {
+                        printf(CMD_WARN_NO_CMD);
+                        continue;
+                }
+
+                if ((rc = build_cmd_list(cmd_line, clist, rc)) != OK) break;
+
+                if ((rc = exec_cmd(clist, rc)) != OK) {
+                        if (rc == OK_EXIT) rc = OK;
+                        break;
+                }
+
+        clear_cmd_list(clist);
+    }
+
+    free(cmd_line);
+    free_cmd_list(clist);
+    free(clist);
+    return rc;
+}
diff --git a/6-RShell/dshlib.h b/6-RShell/dshlib.h
new file mode 100644
index 0000000000000000000000000000000000000000..06ef5b931788100ee5f8dee8b3b369eb4bac3489
--- /dev/null
+++ b/6-RShell/dshlib.h
@@ -0,0 +1,100 @@
+#ifndef __DSHLIB_H__
+    #define __DSHLIB_H__
+
+
+//Constants for command structure sizes
+#define EXE_MAX 64
+#define ARG_MAX 256
+#define CMD_MAX 8
+#define CMD_ARGV_MAX (CMD_MAX + 1)
+// Longest command that can be read from the shell
+#define SH_CMD_MAX EXE_MAX + ARG_MAX
+
+typedef struct command
+{
+    char exe[EXE_MAX];
+    char args[ARG_MAX];
+} command_t;
+
+#include <stdbool.h>
+
+typedef struct cmd_buff
+{
+    int  argc;
+    char **argv; //[CMD_ARGV_MAX]
+    char *_cmd_buffer;
+    char *input_file;  // extra credit, stores input redirection file (for `<`)
+    char *output_file; // extra credit, stores output redirection file (for `>`)
+    bool append_mode; // extra credit, sets append mode fomr output_file
+} cmd_buff_t;
+
+typedef struct command_list{
+    int num;
+    cmd_buff_t **commands; // [CMD_MAX]
+}command_list_t;
+
+//Special character #defines
+#define SPACE_CHAR  ' '
+#define PIPE_CHAR   '|'
+#define PIPE_STRING "|"
+#define DOUBLE_QUOTE_CHAR '"'
+
+#define SH_PROMPT       "dsh4> "
+#define EXIT_CMD        "exit"
+#define RC_SC           99
+#define EXIT_SC         100
+
+//Standard Return Codes
+#define OK                       0
+#define WARN_NO_CMDS            -1
+#define ERR_TOO_MANY_COMMANDS   -2
+#define ERR_CMD_OR_ARGS_TOO_BIG -3
+#define ERR_CMD_ARGS_BAD        -4      //for extra credit
+#define ERR_MEMORY              -5
+#define ERR_EXEC_CMD            -6
+#define OK_EXIT                 -7
+
+
+//helpers
+int numInstanceOf(char *str, const char c);
+void print_dragon();
+char* trim_whitesapce(char *str);
+
+//prototypes
+int alloc_cmd_buff(cmd_buff_t *cmd_buff);
+void free_cmd_buff(cmd_buff_t *cmd_buff);
+void clear_cmd_buff(cmd_buff_t *cmd_buff);
+int build_cmd_buff(char *cmd_line, cmd_buff_t *cmd_buff);
+//int close_cmd_buff(cmd_buff_t *cmd_buff);
+int alloc_cmd_list(command_list_t *clist, int rc);
+int build_cmd_list(char *cmd_line, command_list_t *clist, int rc);
+void free_cmd_list(command_list_t *cmd_lst);
+void clear_cmd_list(command_list_t *clist);
+
+//built in command stuff
+typedef enum {
+    BI_CMD_EXIT,
+    BI_CMD_DRAGON,
+    BI_CMD_CD,
+    BI_CMD_RC,              //extra credit command
+    BI_CMD_STOP_SVR,        //new command "stop-server"
+    BI_NOT_BI,
+    BI_EXECUTED,
+} Built_In_Cmds;
+Built_In_Cmds match_command(const char *input); 
+Built_In_Cmds exec_built_in_cmd(cmd_buff_t *cmd);
+
+//main execution context
+int exec_local_cmd_loop();
+int exec_one_cmd(command_list_t *clist, int rc);
+int exec_cmd(command_list_t *clist, int rc);
+int execute_pipeline(command_list_t *clist);
+
+
+//output constants
+#define CMD_OK_HEADER       "PARSED COMMAND LINE - TOTAL COMMANDS %d\n"
+#define CMD_WARN_NO_CMD     "warning: no commands provided\n"
+#define CMD_ERR_PIPE_LIMIT  "error: piping limited to %d commands\n"
+#define BI_NOT_IMPLEMENTED "not implemented"
+
+#endif
diff --git a/6-RShell/makefile b/6-RShell/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..a079ef4946ce8578836606ceab3f553b305b1875
--- /dev/null
+++ b/6-RShell/makefile
@@ -0,0 +1,31 @@
+# Compiler settings
+CC = gcc
+CFLAGS = -Wall -Wextra -g
+
+# Target executable name
+TARGET = dsh
+
+# Find all source and header files
+SRCS = $(wildcard *.c)
+HDRS = $(wildcard *.h)
+
+# Default target
+all: $(TARGET)
+
+# Compile source to executable
+$(TARGET): $(SRCS) $(HDRS)
+	$(CC) $(CFLAGS) -o $(TARGET) $(SRCS)
+
+# Clean up build files
+clean:
+	rm -f $(TARGET)
+
+test:
+	bats $(wildcard ./bats/*.sh)
+
+valgrind:
+	echo "pwd\nexit" | valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./$(TARGET) 
+	echo "pwd\nexit" | valgrind --tool=helgrind --error-exitcode=1 ./$(TARGET) 
+
+# Phony targets
+.PHONY: all clean test
diff --git a/6-RShell/rsh_cli.c b/6-RShell/rsh_cli.c
new file mode 100644
index 0000000000000000000000000000000000000000..d1dea1326836cfc46c987d18c57e74792c589edc
--- /dev/null
+++ b/6-RShell/rsh_cli.c
@@ -0,0 +1,238 @@
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <fcntl.h>
+
+#include "dshlib.h"
+#include "rshlib.h"
+
+
+
+
+/*
+ * exec_remote_cmd_loop(server_ip, port)
+ *      server_ip:  a string in ip address format, indicating the servers IP
+ *                  address.  Note 127.0.0.1 is the default meaning the server
+ *                  is running on the same machine as the client
+ *              
+ *      port:   The port the server will use.  Note the constant 
+ *              RDSH_DEF_PORT which is 1234 in rshlib.h.  If you are using
+ *              tux you may need to change this to your own default, or even
+ *              better use the command line override -c implemented in dsh_cli.c
+ *              For example ./dsh -c 10.50.241.18:5678 where 5678 is the new port
+ *              number and the server address is 10.50.241.18    
+ * 
+ *      This function basically implements the network version of 
+ *      exec_local_cmd_loop() from the last assignemnt.  It will:
+ *  
+ *          1. Allocate buffers for sending and receiving data over the 
+ *             network
+ *          2. Create a network connection to the server, getting an active
+ *             socket by calling the start_client(server_ip, port) function.
+ *          2. Go into an infinite while(1) loop prompting the user for
+ *             input commands. 
+ * 
+ *             a. Accept a command from the user via fgets()
+ *             b. Send that command to the server using send() - it should
+ *                be a null terminated string
+ *             c. Go into a loop and receive client requests.  Note each
+ *                receive might not be a C string so you need to print it
+ *                out using:
+ *                     printf("%.*s", (int)bytes_received, rsp_buff);
+ *                this version of printf() uses the "%.*s" flag that indicates
+ *                that the rsp_buff might be a null terminated string, or
+ *                it might not be, if its not, print exactly bytes_received
+ *                bytes. 
+ *             d. In the recv() loop described above. Each time you receive
+ *                data from the server, see if the last byte received is the
+ *                EOF character. This indicates the server is done and you can
+ *                send another command by going to the top of the loop.  The
+ *                best way to do this is as follows assuming you are receiving
+ *                data into a buffer called recv_buff, and you received
+ *                recv_bytes in the call to recv:
+ * 
+ *                  recv_bytes = recv(sock, recv_buff, recv_buff_sz, 0)
+ *                  
+ *                if recv_bytes:
+ *                  <negative_number>: communication error
+ *                    0:    Didn't receive anything, likely server down
+ *                  > 0:    Got some data. Check if the last byte is EOF
+ *                          is_eof = (recv_buff[recv_bytes-1] == RDSH_EOF_CHAR) ? 1 : 0;
+ *                    if is_eof is true, this is the last part of the transmission
+ *                    from the server and you can break out of the recv() loop. 
+ * 
+ *   returns:
+ *          OK:      The client executed all of its commands and is exiting
+ *                   either by the `exit` command that terminates the client
+ *                   or the `stop-server` command that terminates both the
+ *                   client and the server. 
+ *          ERR_MEMORY:             If this function cannot allocate memory via
+ *                                  malloc for the send and receive buffers
+ *          ERR_RDSH_CLIENT:        If the client cannot connect to the server. 
+ *                                  AKA the call to start_client() fails.
+ *          ERR_RDSH_COMMUNICATION: If there is a communication error, AKA
+ *                                  any failures from send() or recv().
+ * 
+ *   NOTE:  Since there are several exit points and each exit point must
+ *          call free() on the buffers allocated, close the socket, and
+ *          return an appropriate error code.  Its suggested you use the
+ *          helper function client_cleanup() for these purposes.  For example:
+ * 
+ *   return client_cleanup(cli_socket, request_buff, resp_buff, ERR_RDSH_COMMUNICATION);
+ *   return client_cleanup(cli_socket, request_buff, resp_buff, OK);
+ *
+ *   The above will return ERR_RDSH_COMMUNICATION and OK respectively to the main()
+ *   function after cleaning things up.  See the documentation for client_cleanup()
+ *      
+ */
+int exec_remote_cmd_loop(char *address, int port)
+{
+    char *cmd_buff;
+    char *rsp_buff;
+    int cli_socket;
+    ssize_t io_size;
+    int is_eof;
+
+    // TODO set up cmd and response buffs
+	cmd_buff = (char *)malloc(RDSH_COMM_BUFF_SZ);
+    rsp_buff = (char *)malloc(RDSH_COMM_BUFF_SZ);
+	if (!cmd_buff || !rsp_buff) {
+		perror("malloc failed");
+		return ERR_MEMORY; 
+	}
+
+    cli_socket = start_client(address,port);
+    if (cli_socket < 0){
+        perror("start client");
+        return client_cleanup(cli_socket, cmd_buff, rsp_buff, ERR_RDSH_CLIENT);
+    }
+
+    while (1) 
+    {
+        // TODO print prompt
+		printf("%s", SH_PROMPT);
+        // TODO fgets input
+		if (fgets(cmd_buff, ARG_MAX, stdin) == NULL) {
+			printf("\n");
+			break;
+		}
+		cmd_buff[strcspn(cmd_buff,"\n")] = '\0';
+        
+        // TODO send() over cli_socket
+		int send_len = strlen(cmd_buff) + 1;
+		int bytes_sent = send(cli_socket, cmd_buff, send_len, 0);
+		if (bytes_sent < 0) {
+			perror("send failed");
+			return client_cleanup(cli_socket, cmd_buff, rsp_buff, ERR_RDSH_COMMUNICATION);
+		}
+
+        // TODO recv all the results
+		char eof = RDSH_EOF_CHAR;
+		while ((io_size = recv(cli_socket, rsp_buff, RDSH_COMM_BUFF_SZ, 0)) > 0) {
+	 		if (io_size < 0) {
+				perror("recv failed");
+				return client_cleanup(cli_socket, cmd_buff, rsp_buff, ERR_RDSH_COMMUNICATION);
+			}
+			if (io_size == 0) break;
+			is_eof = ((char)rsp_buff[io_size - 1] == eof) ? 1 : 0;
+			if (is_eof) rsp_buff[io_size - 1] = '\0';
+			printf("%.*s", (int)io_size, rsp_buff);
+
+			if (is_eof) break;
+		}
+
+        // TODO break on exit command
+    	if (strcmp(cmd_buff, EXIT_CMD) == 0 || strcmp(cmd_buff, "stop-server") == 0 ) break;
+    }
+
+    return client_cleanup(cli_socket, cmd_buff, rsp_buff, OK);
+}
+
+/*
+ * start_client(server_ip, port)
+ *      server_ip:  a string in ip address format, indicating the servers IP
+ *                  address.  Note 127.0.0.1 is the default meaning the server
+ *                  is running on the same machine as the client
+ *              
+ *      port:   The port the server will use.  Note the constant 
+ *              RDSH_DEF_PORT which is 1234 in rshlib.h.  If you are using
+ *              tux you may need to change this to your own default, or even
+ *              better use the command line override -c implemented in dsh_cli.c
+ *              For example ./dsh -c 10.50.241.18:5678 where 5678 is the new port
+ *              number and the server address is 10.50.241.18    
+ * 
+ *      This function basically runs the client by: 
+ *          1. Creating the client socket via socket()
+ *          2. Calling connect()
+ *          3. Returning the client socket after connecting to the server
+ * 
+ *   returns:
+ *          client_socket:      The file descriptor fd of the client socket
+ *          ERR_RDSH_CLIENT:    If socket() or connect() fail
+ * 
+ */
+int start_client(char *server_ip, int port){
+    struct sockaddr_in addr;
+    int cli_socket = socket(AF_INET, SOCK_STREAM, 0);
+    int ret;
+
+    // TODO set up cli_socket
+	if (cli_socket == -1) {
+		perror("socket");
+		return ERR_RDSH_CLIENT; 
+	}
+
+	memset(&addr, 0, sizeof(struct sockaddr_in)); 
+	addr.sin_family = AF_INET; 
+	addr.sin_addr.s_addr = inet_addr(server_ip); 
+	addr.sin_port = htons(port); 
+
+	ret = connect(cli_socket, (const struct sockaddr *) &addr, sizeof(struct sockaddr_in)); 
+	if (ret == -1) {
+		perror("connect");
+		return ERR_RDSH_CLIENT;
+	}
+
+    return cli_socket;
+}
+
+/*
+ * client_cleanup(int cli_socket, char *cmd_buff, char *rsp_buff, int rc)
+ *      cli_socket:   The client socket
+ *      cmd_buff:     The buffer that will hold commands to send to server
+ *      rsp_buff:     The buffer that will hld server responses
+ * 
+ *   This function does the following: 
+ *      1. If cli_socket > 0 it calls close(cli_socket) to close the socket
+ *      2. It calls free() on cmd_buff and rsp_buff
+ *      3. It returns the value passed as rc
+ *  
+ *   Note this function is intended to be helper to manage exit conditions
+ *   from the exec_remote_cmd_loop() function given there are several
+ *   cleanup steps.  We provide it to you fully implemented as a helper.
+ *   You do not have to use it if you want to develop an alternative
+ *   strategy for cleaning things up in your exec_remote_cmd_loop()
+ *   implementation. 
+ * 
+ *   returns:
+ *          rc:   This function just returns the value passed as the 
+ *                rc parameter back to the caller.  This way the caller
+ *                can just write return client_cleanup(...)
+ *      
+ */
+int client_cleanup(int cli_socket, char *cmd_buff, char *rsp_buff, int rc){
+    //If a valid socket number close it.
+    if(cli_socket > 0) close(cli_socket);
+
+    //Free up the buffers 
+    free(cmd_buff);
+    free(rsp_buff);
+
+    //Echo the return value that was passed as a parameter
+    return rc;
+}
diff --git a/6-RShell/rsh_server.c b/6-RShell/rsh_server.c
new file mode 100644
index 0000000000000000000000000000000000000000..c49edcebc85f41b8cc725aa29dca1f69a68dabe4
--- /dev/null
+++ b/6-RShell/rsh_server.c
@@ -0,0 +1,620 @@
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <fcntl.h>
+
+//INCLUDES for extra credit
+//#include <signal.h>
+//#include <pthread.h>
+//-------------------------
+
+#include "dshlib.h"
+#include "rshlib.h"
+
+
+/*
+ * start_server(ifaces, port, is_threaded)
+ *      ifaces:  a string in ip address format, indicating the interface
+ *              where the server will bind.  In almost all cases it will
+ *              be the default "0.0.0.0" which binds to all interfaces.
+ *              note the constant RDSH_DEF_SVR_INTFACE in rshlib.h
+ * 
+ *      port:   The port the server will use.  Note the constant 
+ *              RDSH_DEF_PORT which is 1234 in rshlib.h.  If you are using
+ *              tux you may need to change this to your own default, or even
+ *              better use the command line override -s implemented in dsh_cli.c
+ *              For example ./dsh -s 0.0.0.0:5678 where 5678 is the new port  
+ * 
+ *      is_threded:  Used for extra credit to indicate the server should implement
+ *                   per thread connections for clients  
+ * 
+ *      This function basically runs the server by: 
+ *          1. Booting up the server
+ *          2. Processing client requests until the client requests the
+ *             server to stop by running the `stop-server` command
+ *          3. Stopping the server. 
+ * 
+ *      This function is fully implemented for you and should not require
+ *      any changes for basic functionality.  
+ * 
+ *      IF YOU IMPLEMENT THE MULTI-THREADED SERVER FOR EXTRA CREDIT YOU NEED
+ *      TO DO SOMETHING WITH THE is_threaded ARGUMENT HOWEVER.  
+ */
+int start_server(char *ifaces, int port, int is_threaded){
+    int svr_socket;
+    int rc;
+
+    //
+    //TODO:  If you are implementing the extra credit, please add logic
+    //       to keep track of is_threaded to handle this feature
+    //
+	
+    svr_socket = boot_server(ifaces, port);
+    if (svr_socket < 0){
+        int err_code = svr_socket;  //server socket will carry error code
+        return err_code;
+    }
+
+    rc = process_cli_requests(svr_socket);
+
+    stop_server(svr_socket);
+
+
+    return rc;
+}
+
+/*
+ * stop_server(svr_socket)
+ *      svr_socket: The socket that was created in the boot_server()
+ *                  function. 
+ * 
+ *      This function simply returns the value of close() when closing
+ *      the socket.  
+ */
+int stop_server(int svr_socket){
+    return close(svr_socket);
+}
+
+/*
+ * boot_server(ifaces, port)
+ *      ifaces & port:  see start_server for description.  They are passed
+ *                      as is to this function.   
+ * 
+ *      This function "boots" the rsh server.  It is responsible for all
+ *      socket operations prior to accepting client connections.  Specifically: 
+ * 
+ *      1. Create the server socket using the socket() function. 
+ *      2. Calling bind to "bind" the server to the interface and port
+ *      3. Calling listen to get the server ready to listen for connections.
+ * 
+ *      after creating the socket and prior to calling bind you might want to 
+ *      include the following code:
+ * 
+ *      int enable=1;
+ *      setsockopt(svr_socket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
+ * 
+ *      when doing development you often run into issues where you hold onto
+ *      the port and then need to wait for linux to detect this issue and free
+ *      the port up.  The code above tells linux to force allowing this process
+ *      to use the specified port making your life a lot easier.
+ * 
+ *  Returns:
+ * 
+ *      server_socket:  Sockets are just file descriptors, if this function is
+ *                      successful, it returns the server socket descriptor, 
+ *                      which is just an integer.
+ * 
+ *      ERR_RDSH_COMMUNICATION:  This error code is returned if the socket(),
+ *                               bind(), or listen() call fails. 
+ * 
+ */
+int boot_server(char *ifaces, int port){
+    int svr_socket = socket(AF_INET, SOCK_STREAM, 0);
+	int ret;
+    
+    struct sockaddr_in addr;
+
+    // TODO set up the socket - this is very similar to the demo code
+
+    /*
+     * Prepare for accepting connections. The backlog size is set
+     * to 20. So while one request is being processed other requests
+     * can be waiting.
+     */
+	if (svr_socket == -1) {
+		perror("socket");
+		return ERR_RDSH_COMMUNICATION;
+	}
+
+	int enable=1;
+	setsockopt(svr_socket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
+	
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = inet_addr(ifaces);
+    addr.sin_port = htons(port);
+
+    ret = bind(svr_socket, (const struct sockaddr *) &addr, sizeof(struct sockaddr_in));
+	
+	if (ret == -1) {
+		perror("bind");
+		return ERR_RDSH_COMMUNICATION;
+	}
+
+    ret = listen(svr_socket, 20);
+    if (ret == -1) {
+        perror("listen");
+        return ERR_RDSH_COMMUNICATION;
+    }
+
+    return svr_socket;
+}
+
+/*
+ * process_cli_requests(svr_socket)
+ *      svr_socket:  The server socket that was obtained from boot_server()
+ *   
+ *  This function handles managing client connections.  It does this using
+ *  the following logic
+ * 
+ *      1.  Starts a while(1) loop:
+ *  
+ *          a. Calls accept() to wait for a client connection. Recall that 
+ *             the accept() function returns another socket specifically
+ *             bound to a client connection. 
+ *          b. Calls exec_client_requests() to handle executing commands
+ *             sent by the client. It will use the socket returned from
+ *             accept().
+ *          c. Loops back to the top (step 2) to accept connecting another
+ *             client.  
+ * 
+ *          note that the exec_client_requests() return code should be
+ *          negative if the client requested the server to stop by sending
+ *          the `stop-server` command.  If this is the case step 2b breaks
+ *          out of the while(1) loop. 
+ * 
+ *      2.  After we exit the loop, we need to cleanup.  Dont forget to 
+ *          free the buffer you allocated in step #1.  Then call stop_server()
+ *          to close the server socket. 
+ * 
+ *  Returns:
+ * 
+ *      OK_EXIT:  When the client sends the `stop-server` command this function
+ *                should return OK_EXIT. 
+ * 
+ *      ERR_RDSH_COMMUNICATION:  This error code terminates the loop and is
+ *                returned from this function in the case of the accept() 
+ *                function failing. 
+ * 
+ *      OTHERS:   See exec_client_requests() for return codes.  Note that positive
+ *                values will keep the loop running to accept additional client
+ *                connections, and negative values terminate the server. 
+ * 
+ */
+int process_cli_requests(int svr_socket){
+    int cli_socket;
+    int rc = OK;    
+
+    while(1){
+        // TODO use the accept syscall to create cli_socket 
+        cli_socket  = accept(svr_socket, NULL, NULL);
+        if (cli_socket == -1) {
+			perror("accept");
+			return ERR_RDSH_COMMUNICATION; 
+        }
+
+        // and then exec_client_requests(cli_socket)
+		rc = exec_client_requests(cli_socket); 
+    	if (rc < 0) break;
+    	
+    }
+
+    stop_server(svr_socket);
+    return rc;
+}
+
+/*
+ * exec_client_requests(cli_socket)
+ *      cli_socket:  The server-side socket that is connected to the client
+ *   
+ *  This function handles accepting remote client commands. The function will
+ *  loop and continue to accept and execute client commands.  There are 2 ways
+ *  that this ongoing loop accepting client commands ends:
+ * 
+ *      1.  When the client executes the `exit` command, this function returns
+ *          to process_cli_requests() so that we can accept another client
+ *          connection. 
+ *      2.  When the client executes the `stop-server` command this function
+ *          returns to process_cli_requests() with a return code of OK_EXIT
+ *          indicating that the server should stop. 
+ * 
+ *  Note that this function largely follows the implementation of the
+ *  exec_local_cmd_loop() function that you implemented in the last 
+ *  shell program deliverable. The main difference is that the command will
+ *  arrive over the recv() socket call rather than reading a string from the
+ *  keyboard. 
+ * 
+ *  This function also must send the EOF character after a command is
+ *  successfully executed to let the client know that the output from the
+ *  command it sent is finished.  Use the send_message_eof() to accomplish 
+ *  this. 
+ * 
+ *  Of final note, this function must allocate a buffer for storage to 
+ *  store the data received by the client. For example:
+ *     io_buff = malloc(RDSH_COMM_BUFF_SZ);
+ *  And since it is allocating storage, it must also properly clean it up
+ *  prior to exiting.
+ * 
+ *  Returns:
+ * 
+ *      OK:       The client sent the `exit` command.  Get ready to connect
+ *                another client. 
+ *      OK_EXIT:  The client sent `stop-server` command to terminate the server
+ * 
+ *      ERR_RDSH_COMMUNICATION:  A catch all for any socket() related send
+ *                or receive errors. 
+ */
+int exec_client_requests(int cli_socket) {
+    int io_size;
+    command_list_t* cmd_list = malloc(sizeof(command_list_t));
+    int rc;
+    int cmd_rc;
+    //int last_rc;
+    char *io_buff;
+
+    io_buff = malloc(RDSH_COMM_BUFF_SZ);
+    if (io_buff == NULL){
+        return ERR_RDSH_SERVER;
+    }
+
+    while(1) {
+        // TODO use recv() syscall to get input
+		io_size = recv(cli_socket, io_buff, RDSH_COMM_BUFF_SZ, 0);
+		if (io_size == -1) {
+			perror("read error");
+			return ERR_RDSH_COMMUNICATION;
+		}
+        // TODO build up a cmd_list
+		cmd_list->num = 0;
+		
+		rc = alloc_cmd_list(cmd_list, rc);
+
+		rc = build_cmd_list(io_buff, cmd_list, rc); 
+		if (rc == OK) {
+        	// TODO rsh_execute_pipeline to run your cmd_list
+			cmd_rc = rsh_execute_pipeline(cli_socket, cmd_list);
+		}
+		
+		if (strcmp(io_buff, EXIT_CMD) == 0) {
+            free(io_buff);
+    		free_cmd_list(cmd_list);
+            rc = OK;
+            return OK;
+        } else if (strcmp(io_buff, "stop-server") == 0) {
+            free(io_buff);
+    		free_cmd_list(cmd_list);
+            rc = OK;
+            return OK_EXIT;
+        }
+		
+        // TODO send appropriate respones with send_message_string
+        // - error constants for failures
+        // - buffer contents from execute commands
+        //  - etc.
+		if (cmd_rc == 0) send_message_string(cli_socket, io_buff);
+		else send_message_string(cli_socket, CMD_ERR_RDSH_EXEC);
+
+        // TODO send_message_eof when done
+    	send_message_eof(cli_socket);
+    }
+
+    return WARN_RDSH_NOT_IMPL;
+}
+
+/*
+ * send_message_eof(cli_socket)
+ *      cli_socket:  The server-side socket that is connected to the client
+
+ *  Sends the EOF character to the client to indicate that the server is
+ *  finished executing the command that it sent. 
+ * 
+ *  Returns:
+ * 
+ *      OK:  The EOF character was sent successfully. 
+ * 
+ *      ERR_RDSH_COMMUNICATION:  The send() socket call returned an error or if
+ *           we were unable to send the EOF character. 
+ */
+int send_message_eof(int cli_socket){
+    int send_len = (int)sizeof(RDSH_EOF_CHAR);
+    int sent_len = send(cli_socket, &RDSH_EOF_CHAR, send_len, 0);
+
+    if (sent_len != send_len) return ERR_RDSH_COMMUNICATION;
+    return OK;
+}
+
+
+/*
+ * send_message_string(cli_socket, char *buff)
+ *      cli_socket:  The server-side socket that is connected to the client
+ *      buff:        A C string (aka null terminated) of a message we want
+ *                   to send to the client. 
+ *   
+ *  Sends a message to the client.  Note this command executes both a send()
+ *  to send the message and a send_message_eof() to send the EOF character to
+ *  the client to indicate command execution terminated. 
+ * 
+ *  Returns:
+ * 
+ *      OK:  The message in buff followed by the EOF character was 
+ *           sent successfully. 
+ * 
+ *      ERR_RDSH_COMMUNICATION:  The send() socket call returned an error or if
+ *           we were unable to send the message followed by the EOF character. 
+ */
+int send_message_string(int cli_socket, char *buff){
+    //TODO implement writing to cli_socket with send()
+    int send_len = strlen(buff) + 1; 
+    int bytes_sent; 
+
+    bytes_sent = send(cli_socket, buff, send_len, 0); 
+    if (bytes_sent == -1) {
+		perror("send failed");
+		return ERR_RDSH_COMMUNICATION; 
+    }
+	
+	int result = send_message_eof(cli_socket); 
+	if (result == OK) return OK; 
+	else {
+		perror("eof send failed");
+		return ERR_RDSH_COMMUNICATION;
+	}
+
+    return WARN_RDSH_NOT_IMPL;
+}
+
+
+/*
+ * rsh_execute_pipeline(int cli_sock, command_list_t *clist)
+ *      cli_sock:    The server-side socket that is connected to the client
+ *      clist:       The command_list_t structure that we implemented in
+ *                   the last shell. 
+ *   
+ *  This function executes the command pipeline.  It should basically be a
+ *  replica of the execute_pipeline() function from the last deliverable. 
+ *  The only thing different is that you will be using the cli_sock as the
+ *  main file descriptor on the first executable in the pipeline for STDIN,
+ *  and the cli_sock for the file descriptor for STDOUT, and STDERR for the
+ *  last executable in the pipeline.  See picture below:  
+ * 
+ *      
+ *┌───────────┐                                                    ┌───────────┐
+ *│ cli_sock  │                                                    │ cli_sock  │
+ *└─────┬─────┘                                                    └────▲──▲───┘
+ *      │   ┌──────────────┐     ┌──────────────┐     ┌──────────────┐  │  │    
+ *      │   │   Process 1  │     │   Process 2  │     │   Process N  │  │  │    
+ *      │   │              │     │              │     │              │  │  │    
+ *      └───▶stdin   stdout├─┬──▶│stdin   stdout├─┬──▶│stdin   stdout├──┘  │    
+ *          │              │ │   │              │ │   │              │     │    
+ *          │        stderr├─┘   │        stderr├─┘   │        stderr├─────┘    
+ *          └──────────────┘     └──────────────┘     └──────────────┘   
+ *                                                      WEXITSTATUS()
+ *                                                      of this last
+ *                                                      process to get
+ *                                                      the return code
+ *                                                      for this function       
+ * 
+ *  Returns:
+ * 
+ *      EXIT_CODE:  This function returns the exit code of the last command
+ *                  executed in the pipeline.  If only one command is executed
+ *                  that value is returned.  Remember, use the WEXITSTATUS()
+ *                  macro that we discussed during our fork/exec lecture to
+ *                  get this value. 
+ */
+int rsh_execute_pipeline(int cli_sock, command_list_t *clist) {
+    int pipes[clist->num - 1][2];  // Array of pipes
+    pid_t pids[clist->num];
+    int  pids_st[clist->num];         // Array to store process IDs
+    //Built_In_Cmds bi_cmd;
+    int exit_code;
+
+    // Create all necessary pipes
+    for (int i = 0; i < clist->num - 1; i++) {
+        if (pipe(pipes[i]) == -1) {
+            perror("pipe");
+            exit(EXIT_FAILURE);
+        }
+    }
+
+    for (int i = 0; i < clist->num; i++) {
+        // TODO this is basically the same as the piped fork/exec assignment, except for where you connect the begin and end of the pipeline (hint: cli_sock)
+        // TODO HINT you can dup2(cli_sock with STDIN_FILENO, STDOUT_FILENO, etc.
+		pids[i] = fork(); 
+		if (pids[i] == -1) {
+            perror("fork");
+            exit(EXIT_FAILURE);
+        }
+	
+		if (pids[i] == 0) {
+		if (pids[i] == 0) { //child process
+            cmd_buff_t *cmd = clist->commands[i];
+            //set up input pipe for all except first process
+            if (i == 0) {
+				dup2(cli_sock, STDIN_FILENO);
+            }
+			if (i == clist->num - 1) {
+				dup2(cli_sock, STDOUT_FILENO);
+                dup2(cli_sock, STDERR_FILENO);
+			}
+
+            if (i > 0) {
+                dup2(pipes[i-1][0], STDIN_FILENO);
+            }
+            //set up output pipes for all except last process
+            if (i < clist->num - 1) {
+                dup2(pipes[i][1], STDOUT_FILENO);
+            }
+            //close all pipe ends in child
+            for (int j = 0; j < clist->num - 1; j++) {
+                close(pipes[j][0]);
+                close(pipes[j][1]);
+            }
+            //execute command
+            execvp(cmd->argv[0], cmd->argv);
+            perror("execvp");
+            exit(EXIT_FAILURE);
+        }	cmd_buff_t *cmd = clist->commands[i];
+        	if (i == 0) dup2(cli_sock, STDIN_FILENO);
+		
+			if (i == clist->num - 1) {
+				dup2(cli_sock, STDOUT_FILENO);
+            	dup2(cli_sock, STDERR_FILENO);
+			}
+
+        	if (i > 0) dup2(pipes[i-1][0], STDIN_FILENO);
+            
+        	if (i < clist->num - 1) dup2(pipes[i][1], STDOUT_FILENO);
+            
+        	for (int j = 0; j < clist->num - 1; j++) {
+            	close(pipes[j][0]);
+            	close(pipes[j][1]);
+        	}
+            
+        	execvp(cmd->argv[0], cmd->argv);
+        	perror("execvp");
+        	exit(EXIT_FAILURE);
+    	}
+    }
+
+    // Parent process: close all pipe ends
+    for (int i = 0; i < clist->num - 1; i++) {
+        close(pipes[i][0]);
+        close(pipes[i][1]);
+    }
+
+    // Wait for all children
+    for (int i = 0; i < clist->num; i++) {
+        waitpid(pids[i], &pids_st[i], 0);
+    }
+
+    //by default get exit code of last process
+    //use this as the return value
+    exit_code = WEXITSTATUS(pids_st[clist->num - 1]);
+    for (int i = 0; i < clist->num; i++) {
+        //if any commands in the pipeline are EXIT_SC
+        //return that to enable the caller to react
+        if (WEXITSTATUS(pids_st[i]) == EXIT_SC)
+            exit_code = EXIT_SC;
+    }
+    return exit_code;
+}
+
+/**************   OPTIONAL STUFF  ***************/
+/****
+ **** NOTE THAT THE FUNCTIONS BELOW ALIGN TO HOW WE CRAFTED THE SOLUTION
+ **** TO SEE IF A COMMAND WAS BUILT IN OR NOT.  YOU CAN USE A DIFFERENT
+ **** STRATEGY IF YOU WANT.  IF YOU CHOOSE TO DO SO PLEASE REMOVE THESE
+ **** FUNCTIONS AND THE PROTOTYPES FROM rshlib.h
+ **** 
+ */
+
+/*
+ * rsh_match_command(const char *input)
+ *      cli_socket:  The string command for a built-in command, e.g., dragon,
+ *                   cd, exit-server
+ *   
+ *  This optional function accepts a command string as input and returns
+ *  one of the enumerated values from the BuiltInCmds enum as output. For
+ *  example:
+ * 
+ *      Input             Output
+ *      exit              BI_CMD_EXIT
+ *      dragon            BI_CMD_DRAGON
+ * 
+ *  This function is entirely optional to implement if you want to handle
+ *  processing built-in commands differently in your implementation. 
+ * 
+ *  Returns:
+ * 
+ *      BI_CMD_*:   If the command is built-in returns one of the enumeration
+ *                  options, for example "cd" returns BI_CMD_CD
+ * 
+ *      BI_NOT_BI:  If the command is not "built-in" the BI_NOT_BI value is
+ *                  returned. 
+ */
+Built_In_Cmds rsh_match_command(const char *input)
+{
+    if (strcmp(input, "exit") == 0)
+        return BI_CMD_EXIT;
+    if (strcmp(input, "dragon") == 0)
+        return BI_CMD_DRAGON;
+    if (strcmp(input, "cd") == 0)
+        return BI_CMD_CD;
+    if (strcmp(input, "stop-server") == 0)
+        return BI_CMD_STOP_SVR;
+    if (strcmp(input, "rc") == 0)
+        return BI_CMD_RC;
+    return BI_NOT_BI;
+}
+
+/*
+ * rsh_built_in_cmd(cmd_buff_t *cmd)
+ *      cmd:  The cmd_buff_t of the command, remember, this is the 
+ *            parsed version fo the command
+ *   
+ *  This optional function accepts a parsed cmd and then checks to see if
+ *  the cmd is built in or not.  It calls rsh_match_command to see if the 
+ *  cmd is built in or not.  Note that rsh_match_command returns BI_NOT_BI
+ *  if the command is not built in. If the command is built in this function
+ *  uses a switch statement to handle execution if appropriate.   
+ * 
+ *  Again, using this function is entirely optional if you are using a different
+ *  strategy to handle built-in commands.  
+ * 
+ *  Returns:
+ * 
+ *      BI_NOT_BI:   Indicates that the cmd provided as input is not built
+ *                   in so it should be sent to your fork/exec logic
+ *      BI_EXECUTED: Indicates that this function handled the direct execution
+ *                   of the command and there is nothing else to do, consider
+ *                   it executed.  For example the cmd of "cd" gets the value of
+ *                   BI_CMD_CD from rsh_match_command().  It then makes the libc
+ *                   call to chdir(cmd->argv[1]); and finally returns BI_EXECUTED
+ *      BI_CMD_*     Indicates that a built-in command was matched and the caller
+ *                   is responsible for executing it.  For example if this function
+ *                   returns BI_CMD_STOP_SVR the caller of this function is
+ *                   responsible for stopping the server.  If BI_CMD_EXIT is returned
+ *                   the caller is responsible for closing the client connection.
+ * 
+ *   AGAIN - THIS IS TOTALLY OPTIONAL IF YOU HAVE OR WANT TO HANDLE BUILT-IN
+ *   COMMANDS DIFFERENTLY. 
+ */
+Built_In_Cmds rsh_built_in_cmd(cmd_buff_t *cmd)
+{
+    Built_In_Cmds ctype = BI_NOT_BI;
+    ctype = rsh_match_command(cmd->argv[0]);
+
+    switch (ctype)
+    {
+    // case BI_CMD_DRAGON:
+    //     print_dragon();
+    //     return BI_EXECUTED;
+    case BI_CMD_EXIT:
+        return BI_CMD_EXIT;
+    case BI_CMD_STOP_SVR:
+        return BI_CMD_STOP_SVR;
+    case BI_CMD_RC:
+        return BI_CMD_RC;
+    case BI_CMD_CD:
+        chdir(cmd->argv[1]);
+        return BI_EXECUTED;
+    default:
+        return BI_NOT_BI;
+    }
+}
diff --git a/6-RShell/rshlib.h b/6-RShell/rshlib.h
new file mode 100644
index 0000000000000000000000000000000000000000..a104cf14358dae73e2d328a1c79c0c6fd9c8ff2c
--- /dev/null
+++ b/6-RShell/rshlib.h
@@ -0,0 +1,78 @@
+#ifndef __RSH_LIB_H__
+    #define __RSH_LIB_H__
+
+#include "dshlib.h"
+
+//common remote shell client and server constants and definitions
+
+
+//Constants for communication
+//Note that these should work fine in a local VM but you will likely have
+//to change the port number if you are working on tux.
+#define RDSH_DEF_PORT           1234        //Default port #
+#define RDSH_DEF_SVR_INTFACE    "0.0.0.0"   //Default start all interfaces
+#define RDSH_DEF_CLI_CONNECT    "127.0.0.1" //Default server is running on
+                                            //localhost 127.0.0.1
+
+//constants for buffer sizes
+#define RDSH_COMM_BUFF_SZ       (1024*64)   //64K
+#define STOP_SERVER_SC          200         //returned from pipeline excution
+                                            //if the command is to stop the
+                                            //server.  See documentation for 
+                                            //exec_client_requests() for more info
+
+//end of message delimiter.  This is super important.  TCP is a stream, therefore
+//the protocol designer is responsible for managing where messages begin and end
+//there are many common techniques for this, but one of the simplest ways is to
+//use an end of stream marker.  Since rsh is a "shell" program we will be using
+//ascii code 0x04, which is commonly used as the end-of-file (EOF) character in
+//linux based systems. 
+static const char RDSH_EOF_CHAR = 0x04;    
+
+//rdsh specific error codes for functions
+#define ERR_RDSH_COMMUNICATION  -50     //Used for communication errors
+#define ERR_RDSH_SERVER         -51     //General server errors
+#define ERR_RDSH_CLIENT         -52     //General client errors
+#define ERR_RDSH_CMD_EXEC       -53     //RSH command execution errors
+#define WARN_RDSH_NOT_IMPL      -99     //Not Implemented yet warning
+
+//Output message constants for server
+#define CMD_ERR_RDSH_COMM   "rdsh-error: communications error\n"
+#define CMD_ERR_RDSH_EXEC   "rdsh-error: command execution error\n"
+#define CMD_ERR_RDSH_ITRNL  "rdsh-error: internal server error - %d\n"
+#define CMD_ERR_RDSH_SEND   "rdsh-error: partial send.  Sent %d, expected to send %d\n"
+#define RCMD_SERVER_EXITED  "server appeared to terminate - exiting\n"
+
+//Output message constants for client
+#define RCMD_MSG_CLIENT_EXITED  "client exited: getting next connection...\n"
+#define RCMD_MSG_SVR_STOP_REQ   "client requested server to stop, stopping...\n"
+#define RCMD_MSG_SVR_EXEC_REQ   "rdsh-exec:  %s\n"
+#define RCMD_MSG_SVR_RC_CMD     "rdsh-exec:  rc = %d\n"
+
+//client prototypes for rsh_cli.c - - see documentation for each function to
+//see what they do
+int start_client(char *address, int port);
+int client_cleanup(int cli_socket, char *cmd_buff, char *rsp_buff, int rc);
+int exec_remote_cmd_loop(char *address, int port);
+    
+
+//server prototypes for rsh_server.c - see documentation for each function to
+//see what they do
+int start_server(char *ifaces, int port, int is_threaded);
+int boot_server(char *ifaces, int port);
+int stop_server(int svr_socket);
+int send_message_eof(int cli_socket);
+int send_message_string(int cli_socket, char *buff);
+int process_cli_requests(int svr_socket);
+int exec_client_requests(int cli_socket);
+int rsh_execute_pipeline(int socket_fd, command_list_t *clist);
+
+Built_In_Cmds rsh_match_command(const char *input);
+Built_In_Cmds rsh_built_in_cmd(cmd_buff_t *cmd);
+
+//eliminate from template, for extra credit
+void set_threaded_server(int val);
+int exec_client_thread(int main_socket, int cli_socket);
+void *handle_client(void *arg);
+
+#endif