Search code examples
cforkdup2

bad file descriptor in c program with forks


this program is supposed to simulate a posix shell in regards to commands with pipes. The example I've tried to simulate and wanna make work is "ls | nl", but it doesn't and I can't figure out why. I've debugged this code for many hours with no success.

I get the error: "nl: input error: Bad file descriptor", and when I've tried not closing any of the file descriptors or closing only some (or in only one of the forks, or only the parent, etc...), and the errors change, or it works but then nl keeps waiting for input. Anyways, I'm pretty sure the errors are in fork_cmd or fork_cmds and has to do with close.

I've included all the code. I know there's nothing wrong with parser.h. I know this is pretty shitty code but it should still work I think.

I'm probably blind, but I would really appreciate it if someone could help me figure it out. Hopefully it's something that I and maybe others can learn something from.

#include "parser.h"

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>

#define READ  0
#define WRITE 1


void fork_error() {
    perror("fork() failed)");
    exit(EXIT_FAILURE);
}

void close_error() {
    perror("Couldn't close file descriptor");
    exit(EXIT_FAILURE);
}


void fork_cmd(char* argv[], int n, int read_pipe[2], int write_pipe[2], int (*all_fds)[2]) {
    pid_t pid;

    switch (pid = fork()) {
    case -1:
        fork_error();
    case 0:
        if (read_pipe != NULL) {
            if (dup2(read_pipe[READ], STDIN_FILENO) < 0) {
                perror("Failed to redirect STDIN to pipe");
                exit(EXIT_FAILURE);
            }
        }

        if (write_pipe != NULL) {
            if (dup2(write_pipe[WRITE], STDOUT_FILENO) < 0) {
                perror("Failed to redirect STDOUT to pipe");
                exit(EXIT_FAILURE);
            }
        }

        for (int i = 0; i < n - 1; i++) {
            if (close(all_fds[i][READ]) == -1 || close(all_fds[i][WRITE] == -1)) {
                close_error();
            }
        }

        execvp(argv[0], argv);
        perror("execvp");
        exit(EXIT_FAILURE);

    default:
        printf("Pid of %s: %d\n", argv[0], pid);
        break;
    }
}

void fork_cmds(char* argvs[MAX_COMMANDS][MAX_ARGV], int n, int (*fds)[2]) {
    for (int i = 0; i < n; i++) {
        if (n == 1) {
            fork_cmd(argvs[i], n, NULL, NULL, fds);
        }
        // n > 1
        else if (i == 0) {
            fork_cmd(argvs[i], n, NULL, fds[i], fds);
        }
        else if (i == n - 1) {
            fork_cmd(argvs[i], n, fds[i - 1], NULL, fds);
        }
        else {
            fork_cmd(argvs[i], n, fds[i - 1], fds[i], fds);
        }
    }

    for (int i = 0; i < n - 1; i++) {
        if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1)) {
            close_error();
        }
    }
}

void get_line(char* buffer, size_t size) {
    getline(&buffer, &size, stdin);
    buffer[strlen(buffer)-1] = '\0';
}

void wait_for_all_cmds(int n) {
    // Not implemented yet!

    for (int i = 0; i < n; i++) {
        int status;
        int pid;
        if ((pid = wait(&status)) == -1) {
            printf("Wait error");
        } else {
            printf("PARENT <%ld>: Child with PID = %ld and exit status = %d terminated.\n",
                   (long) getpid(), (long) pid, WEXITSTATUS(status));
        }
    }
}

int main() {
    int n;
    char* argvs[MAX_COMMANDS][MAX_ARGV];
    size_t size = 128;
    char line[size];

    printf(" >> ");

    get_line(line, size);

    n = parse(line, argvs);

    // Debug printouts.
    printf("%d commands parsed.\n", n);
    print_argvs(argvs);

    int (*fds)[2] = malloc(sizeof(int) * 2 * (n - 1)); // should be pointer to arrays of size 2

    for (int i = 0; i < n - 1; i++) {
        if (pipe(fds[i]) == -1) {
            perror("Creating pipe error"); // Creating pipe error: ...
            exit(EXIT_FAILURE);
        }
        printf("pipe %d: read: %d, write: %d\n", i, fds[i][READ], fds[i][WRITE]);
    }

    fork_cmds(argvs, n, fds);
    wait_for_all_cmds(n);

    exit(EXIT_SUCCESS);
}

Solution

  • The problem was that one of the parenthesis was at the wrong place in both fork_cmd and fork_cmds, it should be like this of course: close(fds[i][WRITE]). This was the original code:

    for (int i = 0; i < n - 1; i++) {
        if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1))<--
        {
                close_error();
        }
    }