Search code examples
c++linuxtcppipe

redirect STDOUT of the server to be visible to the client of a TCP connection in C++


In a TCP client-server implementation, I set the client to send a shell command to be ran by server and then server send back the response of the OS to the client.

using namespace std;

string run_shell()
{
    // return buffer
    char buffer[1024];
    memset(buffer, 0, 1024);

    //fork child
    int pid = fork();

    //Child
    if(!pid)
    {
        char* args[] = {"ls", "-l", NULL};
        int status = execvp("ls", args);
        if(status < 0)
        {
            cout << "Error: executing shell command failed!" << endl;
            return "";
        }

        int out_pipe[2];

        // save stdout
        int saved_stdout = dup(STDOUT_FILENO);

        pipe(out_pipe);

        // redirect stdout to the pipe
        dup2(out_pipe[1], STDOUT_FILENO);
        close(out_pipe[1]);

        // read from pipe into buffer
        read(out_pipe[0], buffer, 1024);
    }

    //Parent
    else
    {
    }

    return buffer;
}

output:

in server's terminal:

Server received a message
ls -l
-rw-r--r-- 1 XXXX XXXX  4865 Jul 15 12:27 Client.cpp
-rw-r--r-- 1 XXXX XXXX   281 Jul 15 16:40 client_main.cpp
-rw-r--r-- 1 XXXX XXXX 12008 Jul 15 17:06 client_main.o
Server sent a message

in client's terminal:

Client sent a message
ServerMessage: -> `�7��

I would want to get the STDOUT of the executed command by server available as a string to the client. From what I have done, I always get garbage in client side.

Can someone help me address that? There should be some part of redirection in parent branch, but i cannot code it properly.

*** these links have been visited: 1, 2


Solution

  • Solution:

    string run_shell()
    {
        // create pipe lines between parent and child processes
        int pipe_lines[2];
    
        // create the return buffer
        vector<char> return_buffer;
    
        // define the pipes
        if(pipe(pipe_lines) == -1)
        {
            cout << "Error: creating pipe_line in server failed!" << endl;
            return "";
        }
    
        //fork child
        int pid = fork();
    
        //Child
        if(!pid)
        {
            // command
            char* args[] = {"ls", "-l", NULL};
    
            // copy STDOUT and STDERR of child process to be copied into write pipe
            dup2 (pipe_lines[1], STDOUT_FILENO);
            dup2 (pipe_lines[1], STDERR_FILENO);
    
            // chile does not read
            close(pipe_lines[0]);
    
            // child does not write
            close(pipe_lines[1]);
    
            int status = execvp("ls", args);
            if(!status)
            {
                cout << "Error: executing shell command failed!" << endl;
                return "";
            }
    
            // send the 'exec' output into the pipe
            fprintf(stderr, "%s\n", execvp);
    
            // exit child process
            exit(EXIT_FAILURE);
        }
    
        //Parent
        else
        {
            // parent does not write
            close(pipe_lines[1]);
    
            // how many bytes have been read
            int read_bytes;
    
            // a temp char buffer
            char tmp_ch = 0;
    
            // read until there is no more character left in read pipe
            while((read_bytes = read(pipe_lines[0], &tmp_ch, 1)) == 1)
            {
                return_buffer.push_back(tmp_ch);
                tmp_ch = 0;
            }
    
            // block the parent process until any of its children has finished
            wait(NULL);
        }
    
        return string(return_buffer.data());
    }