summaryrefslogblamecommitdiffstats
path: root/02_exercise/process.c
blob: c5be8c355325ed5f0a7da78183310e4a665b6c75 (plain) (tree)
1
2
3
4
5
6
7
8
9



                   
                 



                    
 
                                                                                 
                                                                  







                                                                                        





                                
                  
                  

                                    
 

                                          
                            

         
 


                              
                               


                                      

                                        




                          
                                                             

                                                        




                                                       
 


                              
                                                                                                   
                                              
 



                                      
 






                                                               
 
                                         
                                             
 
                                                                                     
                                            
                   
                             


                                            

                                           
     

                                                 
                                                       
                                    
                                    
                                      




                                             
 


                    
                             

               
                                

                             
                           
         


                                         
 

                              
                            
         
 
                                  
                                                                  



                       
                    


                                     
 


                       

                        

                        
 


                                
                                                                


                                               


                               

                                   
                                      
                                        
                                       

                                         
                                  

                                    





                             
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>

#include "array.h"
#include "process.h"


// given a substring of a line tries to parse out as much information as possible
int parse_command(char const *line, char const *end, process *p) {
    char *part;
    char **local_parts;
    if (arrayInit(part) != 0 || arrayInit(local_parts) != 0) {
        fprintf(stderr, "Failed to prepare new part / parts array whilst parsing line");
        return -1;
    }

    for (size_t i = 0; line + i < end; ++i) {
        char c;
        switch (c = line[i]) {
        default:
            arrayPush(part) = c;
            break;
        case ' ':
        case '\t':
        case '\0':
            if (arrayLen(part) == 0)
                continue;

            arrayPush(part) = '\0';
            arrayPush(local_parts) = part;
            arrayInit(part);
        }
    }

    if (arrayLen(part) == 0) {
        arrayRelease(part);
    } else {
        arrayPush(part) = '\0';
        arrayPush(local_parts) = part;
    }

    arrayPush(local_parts) = NULL;
    p->argc = arrayLen(local_parts) - 1;
    p->argv = local_parts;

    return 0;
}

int parse_line(char const *const line, process **processes) {
    // splits the line at | and then parses the commands
    int ret_code;

    if (arrayInit(*processes) != 0) {
        perror("Failed to initialize processes array");
        return -1;
    }

    bool done = false;
    char const *cursor = line;
    while (!done) {
        process p = {.argv = NULL, .argc = 0, .in_fd = 0, .out_fd = 0, .pid = 0, .blocking = true};
        char const *end = strchr(cursor, '|');

        if (end == NULL) {
            done = true;
            end = line + strlen(line);
        }

        if ((ret_code = parse_command(cursor, end, &p)) != 0) {
            return ret_code;
        }

        arrayPush(*processes) = p;
        cursor = end + 1;
    }

    size_t  p_len = arrayLen(*processes);
    process *last = *processes + (p_len - 1);

    // linking up all processes as we currently only have pipes for multiple commands
    for (size_t i = 0; i < p_len - 1; ++i) {
        int fds[2];
        if (pipe(fds) != 0) {
            perror("Failed to create pipe");
            return -1;
        }
        (*processes)[i].out_fd = fds[1];
        (*processes)[i + 1].in_fd = fds[0];
    }

    // setting all processes to non blocking when
    if (strcmp(last->argv[last->argc - 1], "&") == 0) {
        arrayPop(last->argv) = NULL;
        last->argc = last->argc - 1;
        last->argv[last->argc] = NULL;

        for (size_t i = 0; i < p_len; ++i) {
            (*processes)[i].blocking = false;
        }
    }

    return ret_code;
}

int exec_command(process p) {
    int status;

    if ((p.pid = fork()) == 0) {
        if (p.in_fd != 0) {
            dup2(p.in_fd, 0);
            close(p.in_fd);
        }
        if(p.in_fd == 0 && !p.blocking) {
            close(0);
        }

        if (p.out_fd != 0) {
            dup2(p.out_fd, 1);
            close(p.out_fd);
        }

        execvp(p.argv[0], p.argv);
        fprintf(stderr, "command not found: \"%s\"\n", p.argv[0]);
        fflush(stderr);
        exit(-1);
    }

    if (p.pid < 0) {
        fprintf(stderr, "no fork\n");
        exit(-2);
    }

    if (p.in_fd != 0) {
        close(p.in_fd);
    }

    if (p.out_fd != 0) {
        close(p.out_fd);
    }

    if (!p.blocking)
        printf("[%i]\n", p.pid);

    if (waitpid(p.pid, &status, p.blocking ? 0 : WNOHANG) < 0) {
        fprintf(stderr, "wait error'ed out\n");
    }

    return WEXITSTATUS(status);
}

void free_processes(process **pr) {
    process *processes = *pr;
    while (!arrayIsEmpty(processes)) {
        process p = arrayPop(processes);
        while (!arrayIsEmpty(p.argv)) {
            char *tmp = arrayTop(p.argv);
            if (tmp) {
                arrayRelease(tmp);
            }
            arrayPop(p.argv) = NULL;
        }
        arrayRelease(p.argv);
    }
    arrayRelease(processes);
    *pr = NULL;
}