Piping for Input/Output

Piping for input/output

Your primary problem is that you have the arguments to dup2() reversed. You need to use:

dup2(fd_p2c[0], 0);   // Duplicate read end of pipe to standard input
dup2(fd_pFc[1], 1); // Duplicate write end of pipe to standard output

I got suckered into misreading what you wrote as OK until I put error checking on the set-up code and got unexpected values from the dup2() calls, which told me what the trouble was. When something goes wrong, insert the error checks you skimped on before.

You also did not ensure null termination of the data read from the child; this code does.

Working code (with diagnostics), using cat as the simplest possible 'other command':

#include <unistd.h>
#include <string>
#include <iostream>
using namespace std;

int main()
{
int fd_p2c[2], fd_c2p[2], bytes_read;
pid_t childpid;
char readbuffer[80];
string program_name = "/bin/cat";
string gulp_command = "this is the command data sent to the child cat (kitten?)";
string receive_output = "";

if (pipe(fd_p2c) != 0 || pipe(fd_c2p) != 0)
{
cerr << "Failed to pipe\n";
exit(1);
}
childpid = fork();

if (childpid < 0)
{
cout << "Fork failed" << endl;
exit(-1);
}
else if (childpid == 0)
{
if (dup2(fd_p2c[0], 0) != 0 ||
close(fd_p2c[0]) != 0 ||
close(fd_p2c[1]) != 0)
{
cerr << "Child: failed to set up standard input\n";
exit(1);
}
if (dup2(fd_c2p[1], 1) != 1 ||
close(fd_c2p[1]) != 0 ||
close(fd_c2p[0]) != 0)
{
cerr << "Child: failed to set up standard output\n";
exit(1);
}

execl(program_name.c_str(), program_name.c_str(), (char *) 0);
cerr << "Failed to execute " << program_name << endl;
exit(1);
}
else
{
close(fd_p2c[0]);
close(fd_c2p[1]);

cout << "Writing to child: <<" << gulp_command << ">>" << endl;
int nbytes = gulp_command.length();
if (write(fd_p2c[1], gulp_command.c_str(), nbytes) != nbytes)
{
cerr << "Parent: short write to child\n";
exit(1);
}
close(fd_p2c[1]);

while (1)
{
bytes_read = read(fd_c2p[0], readbuffer, sizeof(readbuffer)-1);

if (bytes_read <= 0)
break;

readbuffer[bytes_read] = '\0';
receive_output += readbuffer;
}
close(fd_c2p[0]);
cout << "From child: <<" << receive_output << ">>" << endl;
}
return 0;
}

Sample output:

Writing to child: <<this is the command data sent to the child cat (kitten?)>>
From child: <<this is the command data sent to the child cat (kitten?)>>

Note that you will need to be careful to ensure you don't get deadlocked with your code. If you have a strictly synchronous protocol (so the parent writes a message and reads a response in lock-step), you should be fine, but if the parent is trying to write a message that's too big to fit in the pipe to the child while the child is trying to write a message that's too big to fit in the pipe back to the parent, then each will be blocked writing while waiting for the other to read.

How to use the output of 2 applications as the input of another (Windows/DOS batch files)?

The fc command expects command line arguments that specify files containing the input data to compare.

But fc does not read the console input (STDIN, hande 0; see Redirection), which is something completely different than that command line arguments, so you cannot use input redirection (<) or piping (|, with fc on the right side) to provide the input data.

So you will have to use temporary files, as you anyway already did.

Redirect Input\Output pipes from parent process to cmd child process C/C++ WinApi


The problem is that the cmd process closes right after the first
command is executed.

The child process exits after this line executed:

if (!CloseHandle(g_hChildStd_IN_Wr))

To solve this problem you can use separate threads. One thread for reading command from console input. Another thread for reading
result of executed command and print them out. Without closing
g_hChildStd_IN_Wr handle.

In main() method:

HANDLE rThread = CreateThread(NULL, 0, ReceiveCommand, NULL, 0, NULL);

HANDLE oThread = CreateThread(NULL, 0, OutputResult, NULL, 0, NULL);

WaitForSingleObject(rThread, INFINITE);

Split WriteToPipe function to two parts: WriteToPipe and ReadFromPipe.

void WriteToPipe(char * command)
{
DWORD dwWritten;
BOOL bSuccess = FALSE;
bSuccess = WriteFile(g_hChildStd_IN_Wr, (LPVOID)command, (DWORD)strlen(command), &dwWritten, NULL);
printf("Command: %s\n", command);
if (!bSuccess)
{
wprintf(L"\nWriteFile() - Failed to write to pipe for child\'s STDIN! Error %u\n", GetLastError());
//break;
}
else
wprintf(L"\nWriteFile() - writing to the pipe for the child\'s STDIN...\n");
}

void ReadFromPipe()
{
DWORD dwWritten;
DWORD dwRead;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
DWORD availableBytes;
DWORD bytesToRead;

//Read from pipe
bSuccess = FALSE;
bzero(chBuf, sizeof(chBuf));
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (!CloseHandle(g_hChildStd_OUT_Wr))
printf("Error closing STDOUT handle, %d\n", GetLastError());
for (;;)
{
PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, NULL, NULL, &availableBytes, NULL);

while (availableBytes > 0)
{
if (availableBytes <= BUFSIZE)
{
bytesToRead = availableBytes;
}
else
{
bytesToRead = BUFSIZE;
}

bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, bytesToRead, &dwRead, NULL);
if (!bSuccess || dwRead == 0)
{
wprintf(L"\nReadFile() from child's standard output failed! Error %u\n", GetLastError());
break;
}

availableBytes -= bytesToRead;

bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
if (!bSuccess)
{
wprintf(L"\nWriteFile() to parent's standard output failed! Error %u\n", GetLastError());
break;
}
}
}
}

Using GetConsoleScreenBufferInfo to monitor cursor position to wait the output from child process print completely. Before that don't start to receive new command.

DWORD WINAPI ReceiveCommand(LPVOID lpParameter)
{
char chr[1000];
CONSOLE_SCREEN_BUFFER_INFO cbsi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cbsi))
{
curPos = cbsi.dwCursorPosition;
}

for (;;)
{
while (TRUE)
{
Sleep(50);
if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cbsi))
{
// printf error message
return 0;
}

if ((curPos.X == cbsi.dwCursorPosition.X) && (curPos.Y == cbsi.dwCursorPosition.Y))
{
// All output of the last command executed have been printed completely.
break;
}

curPos = cbsi.dwCursorPosition;
}

bzero(chr, sizeof(chr));
printf("Enter a character: ");
scanf("%s", chr);
strcat(chr, "\n");
if (strncmp(chr, "exit", 4) == 0)
break;

WriteToPipe(chr);
}

return 0;
}

DWORD WINAPI OutputResult(LPVOID lpParameter)
{
ReadFromPipe();
return 0;
}


Related Topics



Leave a reply



Submit