Continuously write to subprocess using popen in C++ - c++

I need to open a subprocess using popen, the process will continuously ask for user input... The main process need to send that data over the pipe.
This is my first attempt:
FILE *in;
char buff[1024];
if(!(in = popen("cd FIX/fix2/src; java -cp .:./* com.fix.bot", "w"))){
return 1;
}
while(1){
char buffer[] = { 'x' };
fwrite(buffer, sizeof(char), sizeof(buffer), in);
cout << "Wrote!" << endl;
usleep(1000000);
}
However the data is not sent! I need to close the pipe with pclose() so that the data is written to the process. How can I make sure to write the data without having to close the pipe everytime?

You'll want to call fflush(in) to make sure that the buffered data is actually written to the stream.
Also check that java -cp .:./* in the command isn't expanding to an invalid classpath. I think that'll end up expanding to several arguments if there's more than one file in the current directory, and not actual classpath entries.

This works for me:
#include <stdio.h>
#include <unistd.h>
#include <iostream>
int main(int argc, char ** argv) {
FILE *in;
char buff[1024];
if(!(in = popen("./2.sh", "w"))){
return 1;
}
while(1) {
char buffer[] = "xx\n";
fwrite(buffer, sizeof(char), sizeof(buffer), in);
fflush(in);
std::cout << "Wrote!" << std::endl;
usleep(1000000);
}
}
where 2.sh is:
#!/bin/sh
read INP
echo 'read: ' $INP
So I am really suspecting that the problem is missing \n.

Related

Why is there a built-in delay when reading with popen()?

I'm executing a long-running (and often blocked) command via popen() : "ls -R /"
Problem: popen() reads into a buffer that you supply, and it seemingly attempts to populate the ENTIRE buffer before returning. This causes it to block quite often (if your buffer is large).
The solution would seem to be to make the underling fd non-blocking. When I do this, popen() still blocks, usually about 1 second each time. Why is this happening?
Here is my code. Make sure to compile with -std=c++11:
#include <cstdio>
#include <iostream>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
static constexpr size_t SIZE = 65536;
struct Time
{
friend std::ostream &operator<<(std::ostream &os, Time const &t)
{
(void)t;
timeval tv;
gettimeofday(&tv, nullptr);
os << tv.tv_sec << "." << std::fixed << tv.tv_usec << " ";
return os;
}
};
int main()
{
FILE *file;
file = popen("ls -R /", "r");
if(!file)
{
std::cerr << "Could not open app: " << errno;
return -1;
}
// Make it non-blocking
int fd = fileno(file);
fcntl(fd, F_SETFL, O_NONBLOCK);
char buffer[SIZE];
Time t;
while(true)
{
int rc = fread(buffer, 1, SIZE, file);
if(rc <= 0)
{
if(EAGAIN == errno)
{
usleep(10);
continue;
}
std::cerr << t << "Error reading: " << errno << std::endl;
break;
}
std::cerr << t << "Read " << rc << std::endl;
}
pclose(file);
return 0;
}
Output (notice that they are about 1 second apart, even though the fd is nonblocking and I only have a 10mS pause in the loop):
1429625100.983786 Read 4096
1429625101.745369 Read 4096
1429625102.426967 Read 4096
1429625103.185273 Read 4096
1429625103.834241 Read 4096
1429625104.512131 Read 4096
1429625105.188010 Read 4096
1429625105.942257 Read 4096
1429625106.642877 Read 4096
First, you should use read rather than fread. The stdio functions have their own layer of buffering beyond the OS's, so they can block even on non-blocking file descriptors. Use read to avoid this.
Second, you need to stop ls from buffering its output. The default behavior for programs that link to glibc is to use line buffering when stdout is connecting to a TTY, and full buffering when it is connected to a pipe or redirected to a file. Full buffering means the output is only flushed when the 4KB buffer fills up, rather than flushing every time a newline is output.
You can use stdbuf to override this behavior. Note that it will only work for programs that use C streams and link to glibc. That is most programs, but not all.
popen("stdbuf -oL ls -R /", "r");

stderr output when using popen in C++ linux

when I use this simple program to get the standard output of the process, I also somehow get the standard error of the process, although the man page for popen says that only the standard output is redirected. Is it possible that this is related to the shell that is used when forking a process with popen? errEr is a simple program outputting to stderr (cerr << "hello";). I'm using RHEL 6.4. Thanks!
#include <iostream>
#include <cstdio>
using namespace std;
int main ()
{
FILE *fd = popen("errWr", "r");
char str [1024];
while (fgets(str, 1024, fd))
{
cout<<str<<endl;
}
return 0;
}
You're not getting the output from stderr in your program using popen.
Following simple example shows, that error stream from started application is printed in terminal, without being read in your program. popen redirects only stdout stream and stderr stream is not affected, so it's just printed to terminal.
#include <iostream>
#include <cstdio>
using namespace std;
int main ()
{
FILE *fd = popen("errWr", "r");
char str [1024];
while (fgets(str, 1024, fd))
{
cout << "=>" << str << "<=" << endl;
}
return 0;
}

How to execute extreme long command with C++?

I am trying to execute quite long external command (curl with long parameters) in my C++ program.
Works fine on Linux, but Windows (under Cygwin) always shows "System cannot execute specified program" if "cmd" is longer than 127 chars.
Linux accepts with same code almost unlimited size.
Tried changing size of buffer, no change.
Used this code:
#include <string>
#include <iostream>
#include <stdio.h>
std::string exec(char* cmd) {
FILE* pipe = _popen(cmd, "r");
if (!pipe)
return "ERROR";
char buffer[128];
std::string result = "";
while(!feof(pipe)) {
if(fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
_pclose(pipe);
return result;
}
Is that Windows specific limitation, or is possible somehow to extend the cmd buffer?
Added how is created char* which I am trying to send:
string message="This is message to send.";
std::string send="";
std::string curl_header="curl -s -F \"token=aE71iieVt8G1RWdJb59qmAa3hbxxxx\" -F \"user=uPKYQyNgGURbH1g56nBnjn9jNsxxxxx\" -F \"message=";
send.append(curl_header);
send.append(message);
send.append("\" https://api.pushover.net/1/messages.json");
cout << "\n";
cout << "FULL STRING WITH PARAMS TO EXECUTE:";
std::cout << send << '\n';
char *cstr = new char[send.length() + 1];
strcpy(cstr, send.c_str());
cout << "FULL CHAR ARRAY WITH PARAMS TO EXECUTE:";
std::cout << cstr << '\n';//this returns correct string, even longer than 127 chars
cout << exec(cstr);// this returns error, same with system() if cstr is longer than 127 chars
delete [] cstr;
Have you tried using system?
#include <iostream>
using namespace std;
int main(int argc,char** args){
if(argc<2){
system("run run run run run run run run run run runr urn runrunrunru unr unrunr urn run runr unr urn run runrun runr unr urnrun runr urnurnrunrunrunrunrunrurn urnurnunrunrunrunrunrunrn u unru ru runr ur urn ru run rururnunrunrunr");
}else{
printf("argc: %d",argc);
}
system("pause");
}
This seems to work fine to me. The program is named run, so it runs itself with that huge argument and then prints out the number of arguments it sees. Is this what you are trying to do?

Creating (or passing) a string from a pipe

Alright, I'm working on a "simple" project of using forks and piping to search a file for the number of occurrences of a given word. The program is to fork the process in a manner where the parent sends a word at a time to the child for the child to then search through a file and sum up the occurrences of the passed word. My issue is that I'm not familiar with C++ and therefore am having a hard time figuring out how to get the string out of the pipe. The program is currently passing the words through the pipe, but they are only coming out as a long line of characters at run time. Can anyone provide some examples or tips for retrieving as a string and not individual characters? Here is my code as of now:
int main (int argc, char * argv[]) {
fstream fileWell ("well.txt");
fstream fileWords ("words.txt");
int pipefd[2];
int counter = 0;
pid_t cpid;
char buf;
const char * sentWord;
string toCheck;
string toSend;
pipe(pipefd);
cpid = fork();
if (cpid == -1) {
cout << "ERROR" << endl;
exit(1);
}
if (cpid == 0) {
close(pipefd[1]);
while (read(pipefd[0], &buf, 1) > 0)
write(1, &buf, 1);
cout << endl;
write(1, "\n", 1);
}
else {
while (getline(fileWords, toSend)) {
close(pipefd[0]);
sentWord = toSend.c_str();
write(pipefd[1], sentWord, strlen(sentWord));
}
close(pipefd[0]);
toSend = "-1";
sentWord = toSend.c_str();
write(pipefd[1], sentWord, 3);
close(pipefd[1]);
wait(NULL);
exit(EXIT_SUCCESS);
}
return 0;
}
I feel like I know what to do once I've gotten the string, but I can't really move forward without that part. Thank you for any suggestions or help.
Pulling out the unused data and concentrating on just the purpose of your functional code, I'm fairly certain the following was what you were at least trying to accomplish. Some things that were addressed.
Input file stream not opened unless on parent process only.
Fixed multiple closures on pipe handles.
Use std:string members for data pointers and length calculation
Ensure the string terminator was sent as part of the data package.
Treat a terminator on the child as a signal to finish the string.
What you do with the words you receive is up to you. I adapted this to simply send them to standard out before resetting. I feel you likely have alternate plans, but that is somewhat unrelated to the question.
See below. Adapt as needed:
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
using namespace std;
#define READ_FD 0
#define WRITE_FD 1
int main (int argc, char * argv[])
{
int fd[2];
pid_t cpid;
pipe(fd);
if ((cpid = fork()) == -1)
{
cout << "ERROR" << endl;
exit(1);
}
// child process
if (cpid == 0)
{
// don't need the write-side of this
close(fd[WRITE_FD]);
std::string s;
char ch;
while (read(fd[READ_FD], &ch, 1) > 0)
{
if (ch != 0)
s.push_back(ch);
else
{
std::cout << s << '\n';
s.clear();
}
}
// finished with read-side
close(fd[READ_FD]);
}
// parent process
else
{
// don't need the read-side of this
close(fd[READ_FD]);
fstream fileWords ("words.txt");
string toSend;
while (fileWords >> toSend)
{
// send word including terminator
write(fd[WRITE_FD], toSend.c_str(), toSend.length()+1);
}
// finished with write-side
close(fd[WRITE_FD]);
wait(NULL);
}
return EXIT_SUCCESS;
}
Test
The word file I sent through this was Billy Shakespeare's monologue from As You Like It, Act II, Scene VII. The beginning and ending of the output appear below:
All
the
worlds
a
stage
And
all
the
men
and
women
merely
players
....
oblivion
Sans
teeth
sans
eyes
sans
taste
sans
everything
Alternative: A Custom Stream Buffer
An alternative (and perhaps what you're really looking for) is to adapt a stream buffer that can be married to a std::istream to use on the client side similarly to regular stream io. The simplest example I can muster, a one-char-buffer streambuf, appears below:
// adapt a single char streambuf for an input stream
class ifd_streambuf : public std::streambuf
{
protected:
int d_fd;
char d_buffer[1];
public:
ifd_streambuf(int fd) : d_fd(fd)
{
setg(d_buffer, d_buffer + 1, d_buffer + 1);
};
private:
int underflow()
{
if (read(d_fd, d_buffer, 1) <= 0)
return EOF;
setg(d_buffer, d_buffer, d_buffer + 1);
return *gptr();
}
};
Utilizing this, the client process code segment from the previous source listing can be adapted to simply following:
ifd_streambuf fdbuf(fd[READ_FD]);
std::istream inf(&fdbuf);
std::string s;
while (inf >> s)
std::cout << s << '\n';
which is considerably more the style of C++ you're likely accustom to. The server side would need to change as well, appending any whitespace as the word-separator:
while (fileWords >> toSend)
{
write(fd[WRITE_FD], toSend.c_str(), toSend.length());
write(fd[WRITE_FD], " ", 1);
}
It would take extra work to adapt this streambuf to buffer more than a single character (some extra housekeeping stuff), but I leave that for you to discover.

Manipulating result from "system" call in C++ program

Say that I execute some command to run in the terminal within a C++ program. For instance:
int main(){
std::system("./myprog");
return 0;
}
Assume that myprog produces some output that is printed to the console. Can I make use of this output within my C++ program? For example:
int main(){
some_var = std::system("./myprog");
if (some_var == "something")
// Do something.
return 0;
}
Any help would be very much appreciated. Thanks again.
You need to use the popen function:
FILE *fp = popen("./myprog", "r");
char buffer[128];
while (fgets(buffer, sizeof(buffer), fp))
{
std::cout << "Output from program: " << buffer << '\n';
}
pclose(fp);