summaryrefslogblamecommitdiffstats
path: root/03_exercise/srv/shell.c
blob: aea1a688f9d4669c24cbad7ead083591131c7829 (plain) (tree)
1
2
3
4
5



                   
                  








                         
                     


                   





















                                                                                             
                 
 





                                                                           

         
                          
     


                                                   

 










                                                          





































                                            



                                     

                               









                                                                                                  
                                                                 
                                                                   


                      
                                 
                         
 
                       



                                                         





                                                                 
                                                     
 



                                


                                   






















                                                              
                
                                                              












                                                                                                  


                                                         
             




                                   

                               
                                 


             
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>
#include <wait.h>
#include <errno.h>

#include "array.h"
#include "process.h"
#include "prompt_utils.h"
#include "builtins.h"

#define BUF_SIZE 2048

process *processes;

int write_all(int sock, char *buffer, int size) {
    int nwrite, sent = 0;

    while (sent < size) {
        if ((nwrite = write(sock, buffer + sent, size - sent)) < 0) {
            perror("Write");
            exit(1);
        }

        sent += nwrite;
    }

    return sent;
}

void receive_file(int sock, char *path, int size) {
    char buffer[512];
    int  file_fd;
    int  nread, received;

    if ((file_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) { perror("Open"); }

    received = 0;

    /* Read all data */
    while (received < size) {
        if ((nread = read(sock, buffer + received, size - received)) < 0) {
            perror("Read");
            //pthread_exit(NULL);
            exit(1);
        }

        received += nread;
    }

    write_all(file_fd, buffer, strlen(buffer) + 1);
    close(file_fd);
}

void signal_handler(int signal) {
    if (signal == SIGINT) {
        for (size_t i = 0; i < arrayLen(processes); ++i) {
            pid_t pid = processes[i].pid;
            if (pid != 0) {
                kill(pid, SIGINT);
            }
        }
    }
}

bool check_put(char *buffer, int sock) {
    // "<<!bytecount!name"
    char cmd[4];
    char rest[BUF_SIZE - 3];
    size_t file_size;

    memcpy(cmd, buffer, 3);
    cmd[3] = 0;

    if (strcmp(cmd, "<<!") == 0) {
        int offset = 3;
        int i = offset;
        for (; i < BUF_SIZE; ++i) {
            if (buffer[i] == '!')
                break;
            rest[i - offset] = buffer[i];
        }
        rest[i - offset] = 0;
        file_size = atoi(rest);

        offset = ++i;
        for (; i < BUF_SIZE; ++i) {
            if (buffer[i] == '!')
                break;
            rest[i - offset] = buffer[i];
        }
        rest[i - offset] = 0;
        char path[strlen(rest) + 1];
        memcpy(path, rest, strlen(rest));
        path[strlen(rest)] = 0;

        receive_file(sock, path, file_size);

        return true;
    }
    return false;
}

int shell(int in_fd) {
    setvbuf(stderr, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    dup2(in_fd, STDOUT_FILENO);
    dup2(in_fd, STDERR_FILENO);

    printf("Welcome! Available built-ins are:\n"
           "cd: `cd <path>` - if no path is given, return to the current dir\n"
           "wait: `wait pid1 ... pidN` - wait on the processes and report their exit conditions\n"
           "fg: `fg pid` - pulls a process from the background back in the foreground\n"
           "\n"
           "You can put processes in the background using `&`\n"
           "And with `|` you can pipe the output from one process to the input of another\n"
    );

    char const *const original_wd = ".";//get_current_dir_name();
    //char const *prompt = relative_path(original_wd, original_wd);

    bool done = false;
    while (!done) {
        char      line[BUF_SIZE];
        __ssize_t length;

        fflush(stdout);
        if ((length = read(in_fd, line, BUF_SIZE)) < 0) {
            fprintf(stderr, "Failed to read from STDIN");
            fflush(stderr);
            exit(-1);
        }

        if (strspn(line, " \n\t") == strlen(line)) {
            // skip empty lines - empty being just spaces or tabs
            continue;
        }
        line[length - 1] = '\0'; // cut the line feed

        if (strlen(line) == 0) {
            continue;
        }

        if (check_put(line, in_fd))
            continue;

        processes = NULL;
        parse_line(line, &processes);

        if (strcmp(processes[0].argv[0], "cd") == 0) {
            if (arrayLen(processes) != 1) {
                perror("Can't chain cd with other processes");
            }
            int ret;
            switch (arrayLen(processes[0].argv)) {
                case 3:
                    ret = chdir(processes[0].argv[1]);
                    break;
                case 2:
                    ret = chdir(original_wd);
                    break;
                default:
                    fprintf(stderr, "usage: cd <path>\n");
                    fflush(stderr);
                    ret = -1;
            }

            if (ret)
                printf("[%i] ", ret);
            else
                printf("%s\n", "."/*get_current_dir_name()*/);

        } else if (strcmp(processes[0].argv[0], "exit") == 0) {
            done = true;
        } else if (strcmp(processes[0].argv[0], "wait") == 0) {
            builtin_wait(processes[0], false);
        } else if (strcmp(processes[0].argv[0], "fg") == 0) {
            // same behaviour as wait, just bind to shell again (i.e. terminate process on ctrl-c)
            builtin_wait(processes[0], true);
        } else {
            if (arrayLen(processes) != 0 && processes[arrayLen(processes) - 1].blocking) {
                signal(SIGINT, signal_handler);
            }
            for (size_t i = 0; i < arrayLen(processes); ++i) {
                /*int ret = */exec_command(processes[i]);
                /*if (ret)
                    printf("[%i] ", ret);*/
            }
        }

        free_processes(&processes);
    }

    printf("Disconnecting...");

    //free((void *) original_wd);

    return 0;
}