Search code examples
cforkexec

Having issues trying to redirect file to stdin using dup2() and open() and execv()


Edit: The issue is that echo does not read from stdin apparently. Thanks @Jonathan Leffler

I have a programming exercise, and my code is below:

int processProgram(char **tokens, int requireWait) {
    char *cmd = tokens[0];
    if (access(cmd, R_OK) == -1) {
        printf("%s not found\n", cmd);
        return -1;
    }

    int index = 1;
    char *iterator = tokens[index];
    int inputTokenIndex = 0;
    // Look for a < to figure out where the redirection is
    while (iterator != NULL) {
        if (strcmp(iterator, "<") == 0) {                      // <---- THIS IS WHERE I ATTEMPT TO FIND THE INDEX OF "<"
            if (access(tokens[index + 1], R_OK) == -1) {
                printf("%s does not exist\n", tokens[index + 1]);
                return -1;
            }
            // set null at <
            tokens[index] = NULL;
            inputTokenIndex = index + 1;
            break;
        }
        index++;
        iterator = tokens[index];
    }
    // basically create a struct to store pids and stuff....
    processList *process = addNewProcess();
    int childPid = fork();
    if (childPid == 0) {
        // is the child
        if (inputTokenIndex > 0) {
            // if previously strcmp "<" is true
            // set redirection for input....
            int fd = open(tokens[inputTokenIndex], O_RDONLY);   // <----- THIS IS WHERE I DO THE REDIRECTING FOR STDIN!
            dup2(fd, STDIN_FILENO);
            close(fd);
        }
        // start from where inputTokenIndex could have left off
        // since > is always at least after < in our test cases
        index = inputTokenIndex + 1;
        iterator = tokens[index];
        while (iterator != NULL) {                            // <--- LOOKING FOR ">" and "2>", CURRENTLY THIS WORKS FINE
            // look for output redirection FROM previous while loop onwards
            int out = 0;
            if (strcmp(iterator, ">") == 0) {
                // stdout
                out = STDOUT_FILENO;
            } else if (strcmp(iterator, "2>") == 0) {
                // stderr
                out = STDERR_FILENO;
            }
            if (out != 0) {
                int fd = open(tokens[index + 1], O_WRONLY | O_TRUNC | O_CREAT, 0777);
                dup2(fd, out);
                tokens[index] = NULL;
                close(fd);
            }
            index++;
            iterator = tokens[index];
        }
        
        int value = execv(cmd, tokens);                  // <----- EXEC IS HERE!!
        if (value == -1) {
            exit(errno);
        }
        exit(0);
    } else {
        int status;
        if (requireWait == 1) {
            // wait for process to finish
            process->pid = childPid;
            wait(&status);
            process->running = 0;
            process->status = status;
            if (status != 0) {
                printf("%s failed", cmd);
            }
        } else {
            process->pid = childPid;
            printf("Child[%d] in background\n", childPid);
            // no return should be expected
            status = 0;
        }
        return status;
    }
}

The idea is to simulate linux redirection using < and >

tokens is the array of strings provided into the shell.

Example call to write hello to "a.txt": /bin/echo hello > ./a.txt

The problem

It does not work for redirecting in, i.e "/bin/echo < ./a.txt" would echo as if there was no arguments passed in, which is an empty line. "/bin/sleep < ./a.txt" would say no arguments passed in when the file is not empty.

The file a.txt is created using this program. So full example would be

/bin/echo hellooooo > ./a.txt

/bin/echo < ./a.txt

in which the first command successfully creates and writes a.txt, but the second command produces only an empty line

How can you help

Assume that tokens is correct, and that the filename exists, why doesn't the execv() call take in input from the filename given that I redirected it????

Thank you for reading this!!


Solution

  • As I noted in a comment, the echo command does not read standard input, so you won't be able to tell whether your input redirection was successful with echo (or with sleep).

    Use a command which does read standard input — cat is the easiest example, but tr and sed could be sensible alternatives.