0

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

  • `using namespace std;` `string` -- Your code is C++, not C. Returning a `std::string` is something only C++ knows anything about. Having said that, a `std::string` is not a character buffer, if the goal of your code is to return a character buffer. – PaulMcKenzie Jul 15 '23 at 14:03
  • 1
    Note that your use of [`execvp`](https://man7.org/linux/man-pages/man3/exec.3.html#RETURN_VALUE) is, with all due respect, completely wrong -- if it returns at all then an error has occurred so there's no point in trying to set up a pipe etc., etc. Have a look at [this answer](https://stackoverflow.com/questions/7292642/grabbing-output-from-exec/7292659#7292659) to a previous post. – G.M. Jul 15 '23 at 14:25
  • @G.M. Thanks. Great link. Correct me if I'm wrong. I send the client command to the server. Server process will create a child process, redirect its STDOUT to a pipe in between and then using ```exec```, run the command. Whatever the OS return, can be read from this pipe and buffered to be shipped to the client. – Iman Abdollahzadeh Jul 15 '23 at 14:57

1 Answers1

0

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());
}