C++ piping issue - c++

I am trying to to fork my c++ program and direct the parents output into the childs input, I am using pipe() and fork(). In the directory of the program there is a file called input.txt. Unfortunately the only output I get is "wc: stdin: read: Bad file descriptor". Does anyone know why this is? If so what am I doing wrong? Thanks
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
int main(int argc, char *argv[]){
int pipes[2],pid,stdIn,stdOut;
stdIn = dup(0);
stdOut = dup(1);
pipe(pipes);
pid = fork();
if(pid == 0){
dup2(pipes[1],0);
close(pipes[1]);
execlp("wc","wc",NULL);
}
else{
dup2(pipes[0],1);
close(pipes[0]);
std::cout<<"input.txt"<<std::endl;
dup2(stdOut,0);
std::cout<<"parent done\n";
wait(NULL);
}
std::cout<<"after"<<std::endl;
return 0;
}

There are several things that should be fixed in your program:
Use STDIN_FILENO and STDOUT_FILENO instead of 0 and 1. This values may change on different platforms and you have also made a mistake which could probably be avoided if you've used names instead of value, e.g. dup2(stdOut,0); duplicated stdin and you need to duplicate stdout here.
You should close write end of the pipe in both child and parent.
By making wc read from stdin, you are then passing "input.txt" string to it - it will return stats for that string, not for the file. You could either fix it be opening a file descriptor for that file or using exec* with cat.
None of your calls the functions like pipe() or execlp() checks for failure. You should do it like that:
if (pipe(pipes) == -1) {
perror("pipe");
exit(1);
}
You don't need stdIn variable.
You will find fixed code below (it does not implement what I've described in the (5) though):
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int pipes[2], pid, stdOut;
stdOut = dup(STDOUT_FILENO);
pipe(pipes);
pid = fork();
if (pid == 0) {
dup2(pipes[0], STDIN_FILENO);
/* You need to close write end of the pipe here */
close(pipes[1]);
execlp("wc", "wc", NULL);
} else {
std::cout << "Parent setup" << std::endl;
dup2(pipes[1], STDOUT_FILENO);
/* You need to close write end of the pipe here as well */
close(pipes[1]);
/* This will only send the string "input.txt" through the pipe, to the
* wc command */
std::cout << "input.txt" << std::endl;
dup2(stdOut, STDOUT_FILENO);
std::cout << "Parent done" << std::endl;
wait(NULL);
}
std::cout << "Program finished" << std::endl;
return 0;
}
EDIT: As suggested in the comment to the other answer, you could simple use xargs wc to read stdint as file argument:
execlp("xargs", "xargs","wc",NULL);

You have the pipe backwards, you have connected the write end of the pipe to the standard input of wc. You will need to close the write end of the pipe in both processes before wc will detect an end of file condition and terminate normally.
You also incorrectly restore the original standard output to the standard input of the parent.
Furthermore wc will by default not interpret standard input as a list filenames and will therefore not read input.txt.

Related

Non-blocking way to check if there is data on a iofstream

I need to have a way to check if there is data to read on a file (fifo) in a non-blocking way.
I have tried using peek; but it is blocking, I have tried to get and then unget a character in order to check the file without altering the contents; but once again get is blocking...
The only non-blocking solution I have found is to use std::getline(file, line_str) and check if the string is empty; however this does not suit my needs as it alters the data on the file. (The data is a serialized object I will read once I detect there is something to read).
Note: I need this to be non-blocking: I have multiple file streams and need to check all of them regularly to see if there is an object to read/deserialize.
Here is a simple example of what I am trying to achieve:
Sender.cpp:
#include <fstream>
#include <iostream>
#include <string>
extern "C"{
#include <sys/stat.h> // S_IRUSR, S_IWUSR, mkfifo
}
#include <cerrno> // errno
int main(int, char** argv) {
std::string pipe = "foobar";
if(mkfifo(pipe.c_str(), S_IRUSR | S_IWUSR) < 0){
if (errno != EEXIST){
std::cerr << errno;
}
}
std::ofstream file{pipe.c_str()};
file.write("boop", 4); // Simulated object serialization
}
Reader.cpp:
#include <fstream>
#include <iostream>
#include <string>
extern "C"{
#include <sys/stat.h> // S_IRUSR, S_IWUSR, mkfifo
}
#include <cerrno> // errno
int main(int, char** argv) {
std::string pipe = "foobar";
if(mkfifo(pipe.c_str(), S_IRUSR | S_IWUSR) < 0){
if (errno != EEXIST){
std::cerr << errno;
}
}
std::ifstream file{pipe.c_str()};
// ...
/* Do check for data and read/deserialize if any data */
// This is in some sort of loop that goes over the different
// filestreams and checks to see if they have data to treat
}
Any help is really appreciated...
EDIT:
Following Zoso's answer I tried using the file size to determine if the file had been changed; however attempeting to get the size of a fifo named pipe is not possible : filesystem error: cannot get file size: Operation not supported [myFilePath]
I'm not sure if this would work for your particular use case but you could use the filesystem APIs. A simple example is
#include <iostream>
#include <fstream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
while (true) {
auto path = fs::current_path().append("test");
std::cout <<"Press enter to know file size of "<<path.c_str() <<'\n';
char c= getchar();
try {
std::cout<<"Size of "<<path.c_str()<<"is "<<fs::file_size(path)<<'\n';
} catch(fs::filesystem_error& e) {
std::cout << e.what() << '\n';
}
}
}
As and when the file gets data, that can be kept track of based on the increasing size and the data to be processed can be tracked as and when that data is consumed.

cin.tellg returns -1 when receiving input from a pipe

I have a case where I need pipe the output of a child process to an ifstream.
I am trying both creating an ifstream from a file descriptor using the method here: How to construct a c++ fstream from a POSIX file descriptor?
and I am also trying to just use a pipe from the child stderr to my own stdin and using cin as my stream.
In both cases I am getting -1 when I call tellg.
Here is my code with the pipe from child stderr to parent stdin:
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
using namespace std;
int
main()
{
int mypipe[2];
pipe(mypipe);
dup2(mypipe[0], STDIN_FILENO);
dup2(mypipe[1], STDERR_FILENO);
__pid_t pid = fork();
if(pid == 0)
{
// this is a process that outputs stuff into std::err
char* argv[] = {"copy-feats", nullptr};
int ret = execvp(argv[0], argv);
exit(ret);
}
int status;
waitpid(pid, &status, WNOHANG);
cin.clear(); // attempting to clear the error state. Not working.
long size = cin.tellg();
cout << size << endl;
}
so as I said the output of tellg is -1.
thought if I try to use getline(cin, some_string) I'll actually be able to see the output of the child program.
I tried creating a stream from a pipe, but it still gives me -1.
Here's the code I used:
#include <iostream>
#include <fstream>
#include <ext/stdio_filebuf.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#define READ_FD 0
#define WRITE_FD 1
using namespace std;
using FilebufType = __gnu_cxx::stdio_filebuf<std::ifstream::char_type>;
int
main()
{
int mypipe[2];
pipe(mypipe);
__pid_t pid = fork();
if(pid == 0)
{
dup2(mypipe[WRITE_FD], STDERR_FILENO);
char* argv[] = {"copy-feats", nullptr};
int ret = execvp(argv[0], argv);
exit(ret);
}
int status;
waitpid(pid, &status, WNOHANG);
FilebufType filebuf(mypipe[READ_FD], std::ios::in);
istream is(&filebuf);
is.clear();
auto size = is.tellg();
cout << size << endl;
}
Thanks in advance.
In practice tellg() returns an actual file position only when the input stream is a real file. That is, when the input stream is a std::ifstream, and then only when the underlying file is a plain file.
Pipes, and other non-plain files don't have a concept of a file position.
On Linux, which you are using, tellg() is typically implemented (indirectly, but that's not relevant here) by using lseek(2), and lseek(2)'s documentation explicitly specifies that it returns an ESPIPE error if the file descriptor is a pipe. And an error return, eventually, translates to tellg() returning -1.

IPC using unnamed Pipes

First off, Hello and thanks for your help!
I'm trying to get an understanding of IPC with unnamed pipes. Specifically, I'm going to be communicating with Maxima to expand an input that was grabbed from stdin and sent to the input Maxima and then that output is sent to stdout. So simply read input from stdin send it to the child and then write the output to stdout. Currently, I've gotten it to output:
Input ">(x+2)^2"
(%o2) x^2+4x+4
which is correct, but there is a newline between the input and output which shouldn't be there and the (%o2) comes from the Maxima formatted output, so that also should not be there.
I guess my question now comes to two things:
1) How do I fix my output so that it is formatted without the trailing newline and the output indicator?
2) What about the following code can I fix? What can I make better? and Why? (My code is not yet near completion because I have another segment I wish to write)
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <fcntl.h>
#include <errno.h>
#include <iostream> // cin, cout
#include <signal.h>
using namespace std;
int main(int argc, char* argv[]){
pid_t pid;
int status;
int count;
int fpipe[2];
string start = "display2d:false$expand("; string end = ");"; string inp, sent;
string quit = "quit();";
string buffer;
if(pipe(fpipe)){cerr<<"Pipe Failure" << endl; exit(1);}
if((pid = fork()) < 0){ cerr<<"Fork Failure"<<endl; exit(2);}
if(pid == 0){ // child process
close(0); // close stdin
dup(fpipe[0]); // copy stdin
close(fpipe[1]);
execlp("maxima", "maxima", "-q", (char*)0);
read(fpipe[0], (void*)buffer.c_str(), buffer.length());
cout << buffer << " 1" << endl;
exit(EXIT_FAILURE);
}
else{
if(argc == 1){ // parent process
//close(fpipe[0]);
close(1); // close stdout
//dup(fpipe[1]); // redirect stdout
while(1){
cout << ">";
cin >> buffer;
if(buffer == "quit"){
break;
}
buffer = start+buffer+end+'\n';
int dp = write(fpipe[1], buffer.c_str(), buffer.length());
//cout << buffer << endl;
waitpid(getpid(), &status, 0);
}
}
else if(argc > 1){ // just do it for # of argc
}
}
return 0;}
Sample input and output
$./expand
> (x+2)^2
x^2+4*x+4
Current output
(%o#) x^2+4*x+4

read and write with the same process using two different pipes c++

I am trying to create a child that calls some program or process. The parent write and read some data from child through a two pipes. My code compiles and runs, but there is no text on input. What am I doing wrong? Am I not closing the pipes correctly, writing the pipes or outputting the data correctly?
#include <iostream>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
int main(){
int pipedes1[2],pipedes2[2];
char buff[256];
string text = "Hello";
pid_t pid;
pipe(pipedes1);
pipe(pipedes2);
pid = fork();
if(pid > 0){
close(pipedes1[1]);
close(pipedes2[0]);
dup2(pipedes2[1], STDOUT_FILENO);
dup2(pipedes1[0], STDIN_FILENO);
execve("/home/pi/Test", NULL, NULL);
} else {
close(pipedes1[1]);
close(pipedes2[1]);
write(pipedes1[0], text.c_str(), text.length());
while((len = read(pipedes2[0], buff, 256)) != 0){
cout << buff << endl;
}
close(pipedes2[0]);
close(pipedes1[0]);
}
return 0;
}
And there is my "chield" program:
int main(){
string str;
cin >> str;
str = "echo " + str + " >> /home/pi/1";
cout << str << endl;
return 0;
}
Output of prog:
echo << /home/pi/1
Im found a problem write() returns -1.
But i dont know why?
write(pipedes1[0], text.c_str(), text.length());
You are writing to the reading end of the pipe.
Except for this, your application is endangered by deadlock. What if you are attempting to write so much that the pipe buffer fills up, and the child produces so much data that its pipe buffer fills up as well? Then both processes are waiting for the other to drain the buffer, but they are each blocked in write!

cout vs printf when doing fork()

I am trying to understand fork() using some test program. And I find different behaviors between cout and printf() :
program 1:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int main()
{
printf("Hi , %d\n" , getpid());
fork();
return 0;
}
I get:
Hi , 9375
Hi , 9375
program 2:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int main()
{
cout << "Hi , " <<getpid() << endl;
fork();
return 0;
}
I get:
Hi , 7277
The only difference between two program is the first use printf() to print the output while second use cout
Can anyone explain it?
Thanks
When you use stdio, stdout is fully-buffered unless it's writing to a terminal; when writing to a terminal it's line-buffered.
So if you run Program 1 with output redirected to a file or pipe, printf writes the line to the output buffer, but doesn't flush the buffer. When the process forks, the buffer is duplicated in both processes. When they exit, they each flush their copy of the buffer, which prints the line.
You would get the same result in Program 2 if you'd written:
cout << "Hi , " <<getpid() << "\n";
But endl, in addition to outputting the newline character, also flushes the buffer. The equivalent in Program 1 would be:
printf("Hi , %d\n" , getpid());
fflush(stdout);