0
votes

I used pipe and dup2 to redirection the file descriptors but after the first command, the second execvp stuck, I used the for loop to circle multiple commands.I'm pretty sure the tokenize and buffer stuff works, If I run the first command, it works, but for the second command, it just stuck after the first command.

#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>

using namespace std;

char buffer[10][500];
char *args[50];
int fd[20];

void tokenFnc(int counter);

//int counter = 0;
int main(int argc, char *argv[])
{

    string input;
    cout << "myshell$";
    getline(cin, input);
    int k = 0;
    int counter = 0;
    int status_code;

    for (unsigned i = 0; i <= input.size(); i++)
    {
        if (i > 0 && input[i] == ' ' && input[i - 1] == '|')
        {
            continue;
        }
        if (input[i] == '|')
        {
            counter++;
            k = 0;
        }
        else
        {
            buffer[counter][k] = input[i];
            k++;
        }
    }

    //  int fd[(counter-1)*2];
    for (int i = 0; i < counter; i++)
    {
        pipe(fd + i * 2);
    }

    counter += 1; //how many commands

    for (int i = 0; i < counter; i++)
    {
        if (pipe(fd) == -1)
        {
            perror("pipe failed");
            return 1;
        }
        tokenFnc(i);

        int rc = fork();
        if (rc < 0)
        { // fork creation fail
            perror("fork failed\n");
            return 1;
        }
        if (rc == 0)
        { //child process
            if (i == 0 && counter > 1)
                dup2(fd[1], 1);

            if (i > 0 && i != counter - 1)
            {
                dup2(fd[(i - 1) * 2], 0);
                dup2(fd[(2 * i) + 1], 1);
            }
            if (i == counter - 1 && counter > 1)
                dup2(fd[(i - 1) * 2], 0);

            for (int k = 0; k < (counter - 1) * 2; k++)
            {
                close(fd[k]);
            }

            status_code = execvp(args[0], (char *const *)args);
            if (status_code == -1)
            { //error handling
                perror("Child process terminated fail");
                return 1;
            }
        }
    }
    int status;
    for (int i = 0; i < counter; i++)
    {
        pid_t pid = wait(&status);
        if (pid == -1)
        {
            perror("wait exective fail \n");
            return 1;
        }
        printf("process %i exits with %i\n", pid, status);
    }
    return 0;
}

void tokenFnc(int counter)
{
    int tokenCount = 0;
    char delim[] = " ";
    // cout << counter << endl;

    // cout << "The token are: " << endl;
    char *token = strtok(buffer[counter], delim);
    while (token)
    {
        //   cout << token << " ";
        token = strtok(NULL, delim);
        tokenCount++;
    }
    // cout << endl;

    int pos = 0;
    for (int i = 0; i < tokenCount; i++)
    {
        args[i] = (char *)&buffer[counter][pos];
        pos += strlen(args[i]) + 1;
        // cout << args[i] << ", ";
    }
    args[tokenCount] = (char *)nullptr;
    // cout << endl;
}
1
At the risk of appearing self-gratifying, start with something simpler and work your way up from there once you full understand the concepts. - WhozCraig

1 Answers

0
votes
        for (int k = 0; k < (counter - 1) * 2; k++)
        {
            close(fd[k]);
        }

The shown code closes all the pre-piped file descriptors in each child process, here.

But it fails to do the same in the parent process, after starting all the child processes. The opened file descriptors in the parent process prevent the child processes from properly detecting an end-of-file condition on the pipe that's connected to their standard input. All the pipe descriptors are still open in the parent process.

Overall, the shown handling of pipe file descriptors seems to be overly complicated. It should not be necessary to pre-pipe everything, but create one pipe at a time, when starting each child process. Connect the new process's standard input it to the file descriptor from the previous iteration's pipe (or standard input for the first child process); set up a pipe for the child process standard output (or to the parent process's standard output, for the last process in the pipeline); and then just close a pair of file descriptors in the parent. Mission accomplished.