launch command with input and output

Hello, i have a project that im working on for some time now and i encountered a problem. Simply said, i have the main process and i want to launch multiple cmd commands (in threads) (i mean a command that takes an unknown amount of time, gives output time to time and can take input anytime) but, so far, i can get the output of it and also send input, but only one at a time. The problem is that i need to do both at the same time.

What i got so far with pipes:
Getting the output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
std::string theCommand = "a long time taking command";

std::array<char, 128> theBuffer;
std::unique_ptr<FILE, decltype(&pclose)> thePipe(popen(theCommand.c_str(), "r"), pclose);

if(!thePipe) {
    return 0;
}

while (fgets(theBuffer.data(), theBuffer.size(), thePipe.get()) != nullptr || closeThread == false) { //behaves perfectly!
    std::cout<<theBuffer.data();

    if(closeThread)
       break;
}


Sending input:
1
2
std::unique_ptr<FILE, decltype(&pclose)> thePipe(popen(theCommand.c_str(), "w"), pclose);
fputs(command.c_str(), thePipe.get());

Last edited on
Those are processes, not threads.

What exactly is the problem? A small complete program that demonstrates the problem would clarify things.

However, a standard pipe cannot (portably) be opened for both reading and writing. In particular, the popen command says you can open for reading or writing, but not both.

And if you are trying to open the same command with two different popen invocations, well, that's not going to work since it's two separate commands.

If you handle your own pipes you can do it.
Last edited on
Partial example of handling your own pipes (and forking and exec'ing your own process).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <unistd.h>

int main() {
    int inp[2], outp[2];
    pipe(inp);
    pipe(outp);

    switch (fork()) {
    case -1:
        perror("fork");
        exit(EXIT_FAILURE);

    case 0:
        close( inp[0]); close(1); dup( inp[1]); close( inp[1]);
        close(outp[1]); close(0); dup(outp[0]); close(outp[0]);

        execlp("program", "program", (const char*)NULL);

        perror("exec");
        exit(EXIT_FAILURE);
    }
        
    // parent ------------------------------------------------
    
    // write to outp[1]; read from inp[0]
    FILE* fin  = fdopen(inp[0], "r");
    FILE* fout = fdopen(outp[1], "w");

    // ...
}
Last edited on
I'm going to illustrate something that comes from a memory perhaps 30 years old, and I'm going to limit this to the scope of a suggestion, it will be up to you, @CosmimPerRam, to flesh this out.

I've referenced a few doc pages to be sure I'm heading in a reasonable direction. The last time I did this was in the very early 90's, when text based applications on UNIX, using actual TTY devices (AKA dumb terminals via RS232), was still a thing.

The objective, back then, was to execute another program, sending data to it as if that program received stdin keystrokes, and reading data back from that program's stdout.

The basic idea is to fashion a short, generic program which opens named pipes. The type of pipe you're using here are, as I recall, anonymous pipes. Named pipes appear as files in the file system (which is common to *NIX operation), but are not files themselves - the type of the entry is not a typical block storage (applicable to files), but of a pipe.

Your program launches this generic pipe substitution utility (which you write). It substitutes stdin and stdout (and perhaps stderr) to 2 (or 3 ) named pipes. Then, it uses execvp (or one from the family of functions) to run the program of interest, but now the stdin and stdout of that process has been tied to the named pipes, leaving the pipe substitutions intact.

Your application opens these 2 (or 3) named pipes to gain the kind of access you want.

First, look up the mkfifo function. An example is here: https://www.geeksforgeeks.org/named-pipe-fifo-example-c-program/

mkfifo creates a named pipe.

1
2
mkfifo( "PipeStdIn", 0666 );
mkfifo( "PipeStdOut", 0666 );


These are now two named pipes in a directory (the path can specify where).

Then, open the pipes with open....

1
2
int pipe_stdin = open( "PipeStdIn", O_WRONLY );
int pipe_stdout = open( "PipeStdOut", O_WRONLY );


This pipe can then be substituted for stdin or stdout using dup2 in the proposed generic utility

1
2
3
4
5
dup2( pipe_stdin, 0); // stdin is zero

or

dup2( pipe_stdout, 1); // stdout is 1 - stderr is 2 



This proposed utility executes the program of interest (probably execvp), whatever that is, but that process's stdin and stdout have been tied to these named pipes.

Your application opens these two pipes using open, writing to one ( the pipe stdin), reading from the other (pipe stdout).

Good luck. I had this working, back in '91/'92, and it was used for at least 15 years when the *NIX box was, finally, replaced in that company.

I remember some tinkering was required, but alas I'm not building a working example to see what that was.



Last edited on
I'm sorry for the very late reply, i was gone some days and i was working on other stuff the other days.

@dutch (0)
Those are processes, not threads.
I've meant that im going to run, read and write in a thread in my program.

The simplest example of a program from that i can get the output and send the output is:
1
2
3
4
5
6
7
8
9
10
11
12
start

thread
 while 1
  read

while 1
 output "aa"

//other stuff

end //i didnt had the time at the moment to write the C++ code equivalent 


And if you are trying to open the same command with two different popen invocations, well, that's not going to work since it's two separate commands.
That's right.

If you handle your own pipes you can do it.
Yeah but i dont know how to handle them, especially the ones from C.

@dutch (1)
After implementing it in a example and in my project, i still cannot send input (to outp[1]) but i can very easily read the output (inp[0]). I'll try again soon to check if i've done something wrong.

@Niccolo
Thanks for taking your time to write the details, i'm still looking forward to try the solution you suggested but it requires a little bit more research, i'll come back with an answer soon.
Topic archived. No new replies allowed.