how to implement pipe command from linux shell in c++? - c++

i am working in a project to implement a mini linux shell ,
i want to implement a pipe command where it basically works like this :
command1 | command2: using the pipe character “|” will produce a pipe, redirects command1 stdout to its write channel and command2 stdin to its read channel.
or:
command1 |& command2: using the pipe character “|&” will produce a pipe, redirects command1 stderr to the pipe’s write channel and command2 stdin to the pipe’s read channel.
now command 1 can be either an external command from linux that i run using execv or a built in command that i wrote , and command2 is always an external command
my code is not working correctly and i don't know where is the problem exactly , because i implemented many commands and they all worked perfect for example (cp, redirection ... ) , so the base is good in my code , but the pipe is just wrong ! for example if the command is : showpid | ./parser.exe 1
where parser.exe is a giving file that does parsing on the command , for example here if showpid prints : shell process pid is 12311 , then calling this command showpid | ./parser.exe 1 the output should be "shell" , but in my code the output is shell process pid is 12311
this is my pipe command implementation :
this is the class of the pipe command :
class PipeCommand : public Command {
private:
int pipeNum;
int split;
string cmd1;
string cmd2;
public:
PipeCommand(const char* cmd_line);
virtual ~PipeCommand() {}
void execute() override;
};
// the pipe constructor , here i want to extract each command from the right and left side of the pipe from the cmd_line , which is the command line that i get
// fro example : " showpid | grep 1 "
PipeCommand::PipeCommand(const char* cmd_line):Command(cmd_line) {
pipeNum = -1;
isBackground = _isBackgroundComamnd(cmd_line);
string cmd1 = "", cmd2 = "";
int split = -1;
for (int i = 0; i < this->num_args; i++) {
if (strcmp(args[i], "|") == 0) {
split = i;
pipeNum = 1;
break;
}
if (strcmp(args[i], "|&") == 0) {
split = i;
pipeNum = 2;
break;
}
}
for (int i = 0; i < split; i++) {
cmd1 = cmd1 + args[i] + " ";
}
for (int i = split + 1; i < num_args; i++) {
cmd2 = cmd2 + args[i] + " ";
}
// the implementation of the pipe command
void PipeCommand::execute() {
int pipeFd[2];
int pid;
pipe(pipeFd);
pid = fork();
if (pid == 0) { // child process .
close(pipeFd[1]);
dup2(pipeFd[1], pipeNum);
if (isBuiltInCMD(args[0])) { // if the command is built in which means i wrote it i run it like this ( this works fine i checked it)
Command *newCmd = CreateBuiltInCommand(const_cast<char *>(cmd1.c_str()));
newCmd->execute();
exit(0);
} else { // if the command is external than use execv
const char **argv = new const char *[4];
argv[0] = "/bin/bash";
argv[1] = "-c";
argv[2] = cmd1.c_str();
argv[3] = nullptr;
execv(argv[0], const_cast<char **>(argv));
perror("execvp failed");
}
} else { // the parent process , basically runs the command2 , which it can be only an external command
pid = fork(); // we fork again in the parent process
if (pid == 0) { // the child process executes the secomd command using execv
dup2(pipeFd[0], STDIN_FILENO);
close(pipeFd[0]);
dup2(pipeFd[0], pipeNum);
// execute
const char **argv = new const char *[4];
argv[0] = "/bin/bash";
argv[1] = "-c";
argv[2] = cmd2.c_str();
argv[3] = nullptr;
execv(argv[0], const_cast<char **>(argv));
perror("execvp failed");
} else { // the parent process waits
waitpid(pid,NULL,0);
close(pipeFd[1]);
close(pipeFd[0]);
}
}
}

I think you should look at the order that you are closing / duping file descriptors. Specifically:
The first command needs to use existing stdin (fd 0). Don't close it.
But you should close existing stdout (fd 1) and THEN do the fd dup so it becomes 1.
The second command does it the other way.
I would test with a MUCH simpler example. Get the piping thing to work and THEN do the exec thing.
This is edited information added later.
In a C/C++ world, you have 3 standard files when the program starts:
FD 0 is stdin -- Used for input
FD 1 is stdout -- Used for normal output
FD 2 is stderr -- Used for error output
When you do this:
grep foo < file.txt | grep bar
What the shell does is:
-Does the pipe call to get the input and output files
-On the first grep for foo, close fd 0 (stdin) and open file.txt for input. It will land on 0, and thus is stdin to the grep command.
-Close stdout and assign it to the out part of the pipe
On the second grep:
-Close 1 (stdin)
-And move the pipe input portion to 1 so stdin is set.
Thus, in the end:
part 1 fd 0 (stdin) is the file
part 1 fd 1 (stdout) is the output portion of the pipe
part 2 fd 0 (stdin) is the input portion of the pipe

Related

Do input redirection and capture command output (Custom shell-like program)

I'm writing a custom shell where I try to add support for input, output redirections and pipes just like standard shell. I stuck at point where I cannot do input redirection, but output redirection is perfectly working. My implementation is something like this (only related part), you can assume that (string) input is non-empty
void execute() {
... // stuff before execution and initialization of variables
int *fds;
std::string content;
std::string input = readFromAFile(in_file); // for input redirection
for (int i = 0; i < commands.size(); i++) {
fds = subprocess(commands[i]);
dprintf(fds[1], "%s", input.data()); // write to write-end of pipe
close(fds[1]);
content += readFromFD(fds[0]); // read from read-end of pipe
close(fds[0]);
}
... // stuff after execution
}
int *subprocess(std::string &cmd) {
std::string s;
int *fds = new int[2];
pipe(fds);
pid_t pid = fork();
if (pid == -1) {
std::cerr << "Fork failed.";
}
if (pid == 0) {
dup2(fds[1], STDOUT_FILENO);
dup2(fds[0], STDIN_FILENO);
close(fds[1]);
close(fds[0]);
system(cmd.data());
exit(0); // child terminates
}
return fds;
}
My thought is subprocess returns a pipe (fd_in, fd_out) and parent can write to write-end and read-from read-end afterwards. However when I try an input redirection something like sort < in.txt, the program just hangs. I think there is a deadlock because one waiting other to write, and other one to read, however, after parent writes to write-end it closes, and then read from read-end. How should I consider this case ?
When I did a bit of searching, I saw this answer, which my original thinking was similar except that in the answer it mentions creating two pipes. I did not quite understand this part. Why do we need two separate pipes ?

How would one go about 'chain' piping more than 2 pipes in C++? Currently I am getting the Bad file descriptor error from Bash on the second command

To expand on this, by chain piping I am referring to when I have 3 separate processes:
process 1 writes to process 2,
process 2 reads from process 1 and writes to process 3,
process 3 reads from process 2 and then finishes.
I am specifically trying to handle complex commands in a C++ written bash shell. So I would be using this to execute a set of commands like this that all communicate with each other:
ls | sort | grep "exit"
where process 1 is executing ls and its stdout is written to process 2 through a pipe, etc.
I already am writing code to solve this for a project and was just wondering if my approach is correct, as right now when just doing a 2 command call of ls | grep "exit" I am getting the bash error "grep: (standard input): Bad file descriptor"
//Block for when the userInput is a complex command
else{
if (debug)
printf("Complex command: %s\n", userInput.c_str());
vector<char*> commandsVect = splitCString(const_cast<char*>(userInput.c_str()), const_cast<char*>( delimVertPipe.c_str()));
if (debug)
printVect(commandsVect);
if (pipe(fileDescriptor) == -1){
fprintf(stderr, "Pipe failed for command %s\n", userInput.c_str());
return 1;
}
for (int i = 0; i < commandsVect.size(); ++i) {
vector<char*> tokens = splitCString(const_cast<char*>(commandsVect[i]), const_cast<char*>( delimSpace.c_str()));
printf("Commands vect size is %ld\n", commandsVect.size());
printf("Parsing command \'%s\'\n", commandsVect[i]);
if (debug) {
printVect(tokens);
}
procID = fork();
//Block for the first command
if (i == 0){
if (procID < 0){
fprintf(stderr, "Fork number %d in the complex command \'%s\' failed\n", i+1, userInput.c_str());
return 1;
}
//Child process
else if (procID == 0){
//close(fileDescriptor[READ_END]);
close(STDOUT_FILENO);
//Links the write end of the pipe to the STDOUT
dup2(fileDescriptor[WRITE_END], 1);
close(fileDescriptor[READ_END]);
close(fileDescriptor[WRITE_END]);
tokens.push_back(nullptr); //execvp() arg array needs a NULL pointer at the end
if ( execvp(tokens[0], tokens.data()) < 0 ) {
fprintf( stderr, "execvp() call failed for the command \'%s\' inside the input string \'%s\'\n", commandsVect[i], userInput.c_str() );
return 1;
}
exit(1);
}
//Parent process
else{
close(fileDescriptor[READ_END]);
close(fileDescriptor[WRITE_END]);
wait(NULL);
}
}
//Block for the very last command, which will pipe input from the previous
else if (i == commandsVect.size() - 1){
if (procID < 0){
fprintf(stderr, "Fork number %d in the complex command \'%s\' failed\n", i+1, userInput.c_str());
return 1;
}
//Child process
else if (procID == 0){
//close(fileDescriptor[WRITE_END]);
close(STDIN_FILENO);
//Links the read end of the pipe to the STDIN
dup2(fileDescriptor[READ_END], 0);
close(fileDescriptor[WRITE_END]);
close(fileDescriptor[READ_END]);
tokens.push_back(nullptr); //execvp() arg array needs a NULL pointer at the end
if ( execvp(tokens[0], tokens.data()) < 0 ) {
fprintf( stderr, "execvp() call failed for the command \'%s\' inside the input string \'%s\'\n", commandsVect[i], userInput.c_str() );
return 1;
}
exit(1);
}
//Parent process
else{
close(fileDescriptor[READ_END]);
close(fileDescriptor[WRITE_END]);
wait(NULL);
}
}
//To note for StackOverflow, this block of code is never executed since I am only ever calling a 2 chained command like ls|grep "exit"
//Block for the middle commands. (Will pipe input from previous, and output to the next)
else{
printf("GOING THROUGH BAD CODE");
continue;
if (procID < 0){
fprintf(stderr, "Fork number %d in the complex command \'%s\' failed\n", i+1, userInput.c_str());
return 1;
}
//Child process
else if (procID == 0){
exit(1);
}
//Parent process
else{
wait(NULL);
}
}
}
close(fileDescriptor[READ_END]);
close(fileDescriptor[WRITE_END]);
}
This might not be possible with your larger application, but you could simplify things by letting the shell manage the pipes. Write P1 (process one), P2, and P3 as three separate executables. In stead of doing IO on pipes, each program could read from stdin and write to stdout. Simple. To execute - let bash or whatever shell you use glue the three together by calling them as...
$P1 | P2 | P3;
Under the hood, your shell is doing pretty much what you're doing in C++ (only successfully 😉). It creates a pipe for P1, which it passes to exec as stdin to launch P1 after forking. It creates an input and output pipe for P2, and binds it stdin and stdout as appropriate in the same way - passed into exec when launching P2 after the fork. P3 gets only a stdin pipe and its stdout stream goes right to the console as normal. It's not quite as sexy as doing it all in C++, but it's very robust - pretty much guaranteed to work.

shell command inside the linux daemon

I have written the daemon in C/C++ in linux.
Now I want to get the out put of ls -l (list directory) command inside daemon and write output of command to the file.
I know how to write to the file from my daemon, but,
I don't know how to execute ls -l command and get the output in buffer.
Here is the code...
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failures here */
ofs << "set sid : fail";
ofs.close();
exit(EXIT_FAILURE);
}
ofs << "\nchdir :" << chdir(filePath) << "\n";
/* Change the current working directory */
if ((chdir(filePath)) < 0) {
/* Log any failures here */
ofs << "chdir : fail";
ofs.close();
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
while(1){
//here I want to execute the ls -l and get output of the command
}
You can use popen that executes a shell command and return the output as a pipe:
#include <stdio.h>
FILE* pipe = popen("ls -l", "r");
if (!pipe) return "ERROR";
You can also use system to execute any shell command:
#include <stdlib.h>
int system(const char *command);
To get the output of ls -l, forward it to a file ls -l >> myls.log than read that file.
system("ls -l >> myls.log");

Asyncronous Bidirectional IO Redirection for a child process

I am trying to figure out a generalized way for Asynchronous Bidirectional IO Redirection of a child process. Basically, I would like to spawn an interactive child process that waits for input and any output should be read back. I tried to experiment with python.subprocess by spawning a new python process. A base simplistic example tried to achieve is as follows
process = subprocess.Popen(['/usr/bin/python'],shell=False,stdin=subprocess.PIPE, stdout=subprocess.PIPE)
while True:
output = process.stdout.readline()
print output
input = sys.stdin.readline()
process.stdin.write(input)
and executing the above code snippet simply hangs without any output. I tried running with /usr/bash and /usr/bin/irb but the result is all the same. My guess is, buffered IO is simply not gelling well with IO redirection.
So my question is, is it feasible to read the output of a child process without flushing the buffer or quitting the subprocess?
The following post mentions IPC sockets but for that I would have to change the child process which may not be feasible. Is there any other way to achieve it?
Note*** My ultimate goal is to create a server REPL process which can interact with a remote web client. Though the example given is of Python, my ultimate goal is to wrap all available REPL by a generalized wrapper.
With the help of some of the suggestion in the answers I came up with the following
#!/usr/bin/python
import subprocess, os, select
proc = subprocess.Popen(['/usr/bin/python'],shell=False,stdin=subprocess.PIPE, stdout=subprocess.PIPE,stderr=subprocess.PIPE)
for i in xrange(0,5):
inputready, outputready, exceptready = select.select([proc.stdout, proc.stderr],[proc.stdout, proc.stderr],[proc.stdout, proc.stderr],0)
if not inputready: print "No Data",
print inputready, outputready, exceptready
for s in inputready: print s.fileno(),s.readline()
proc.terminate()
print "After Terminating"
for i in xrange(0,5):
inputready, outputready, exceptready = select.select([proc.stdout, proc.stderr],[proc.stdout, proc.stderr],[proc.stdout, proc.stderr],0)
if not inputready: print "No Data",
print inputready, outputready, exceptready
for s in inputready: print s.fileno(),s.readline()
now, though the programs is not in deadlock but unfortunately there is no output. Running the above code I get
No Data [] [] []
No Data [] [] []
No Data [] [] []
No Data [] [] []
No Data [] [] []
After Terminating
No Data [] [] []
No Data [] [] []
No Data [] [] []
No Data [] [] []
No Data [] [] []
Just FYI,
running python as
/usr/bin/python 2>&1|tee test.out
seems to be working just fine.
I also came up with a 'C' code. But the result is not different.
int kbhit() {
struct timeval tv;
fd_set fds;
tv.tv_sec = tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
return FD_ISSET(STDIN_FILENO, &fds);
}
void receive(char *str) {
char ch;
fprintf(stderr,"IN1\n");
if(!kbhit()) return;
fprintf(stderr,"IN2\n");
fprintf(stderr,"%d\n",kbhit());
for(;kbhit() && (ch=fgetc(stdin))!=EOF;) {
fprintf(stderr,"%c,%d",ch,kbhit());
}
fprintf(stderr,"Done\n");
}
int main(){
pid_t pid;
int rv, pipeP2C[2],pipeC2P[2];
pipe(pipeP2C);
pipe(pipeC2P);
pid=fork();
if(pid){
dup2(pipeP2C[1],1); /* Replace stdout with out side of the pipe */
close(pipeP2C[0]); /* Close unused side of pipe (in side) */
dup2(pipeC2P[0],0); /* Replace stdin with in side of the pipe */
close(pipeC2P[1]); /* Close unused side of pipe (out side) */
setvbuf(stdout,(char*)NULL,_IONBF,0); /* Set non-buffered output on stdout */
sleep(2);
receive("quit()\n");
wait(&rv); /* Wait for child process to end */
fprintf(stderr,"Child exited with a %d value\n",rv);
}
else{
dup2(pipeP2C[0],0); /* Replace stdin with the in side of the pipe */
close(pipeP2C[1]); /* Close unused side of pipe (out side) */
dup2(pipeC2P[1],1); /* Replace stdout with the out side of the pipe */
close(pipeC2P[0]); /* Close unused side of pipe (out side) */
setvbuf(stdout,(char*)NULL,_IONBF,0); /* Set non-buffered output on stdout */
close(2), dup2(1,2); /*Redirect stderr to stdout */
if(execl("/usr/bin/python","/usr/bin/python",NULL) == -1){
fprintf(stderr,"execl Error!");
exit(1);
}
}
return 0;
}
In the Python code you posted, you're not using the right streams:
inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], # read list
[proc.stdout, proc.stderr], # write list
[proc.stdout, proc.stderr], # error list.
0) # time out.
I haven't tried fixing it, but I bet reading and writing to the same set of streams is incorrect.
There are multiple things going wrong in your sample. The first is that the python executable that you launch as as a child process produces no output. The second is that there is a race condition since you can invoke select() 5 times in a row before the child process produces output, in which case you will kill the process before reading anything.
I fixed the three problems mentioned above (write list, starting a process that produces output and race condition). Try out this sample and see if it works for you:
#!/usr/bin/python
import subprocess, os, select, time
path = "/usr/bin/python"
proc = subprocess.Popen([path, "foo.py"], shell=False,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
for i in xrange(0,5):
time.sleep(1)
inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], [proc.stdin,],
[proc.stdout, proc.stderr, proc.stdin], 0)
if not inputready:
print "No Data",
print inputready, outputready, exceptready
for s in inputready:
print s.fileno(),s.readline()
proc.terminate()
print "After Terminating"
for i in xrange(0,5):
inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], [proc.stdin,],
[proc.stdout, proc.stderr, proc.stdin], 0)
if not inputready:
print "No Data",
print inputready, outputready, exceptready
for s in inputready:
print s.fileno(),s.readline()
The foo.py file I used contained this:
#!/usr/bin/python
print "Hello, world!"
The following version (mostly removed redundant output to make results easier to read):
#!/usr/bin/python
import subprocess, os, select, time
path = "/usr/bin/python"
proc = subprocess.Popen([path, "foo.py"], shell=False,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
for i in xrange(0,5):
time.sleep(1)
inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], [proc.stdin,],
[proc.stdout, proc.stderr, proc.stdin], 0)
for s in inputready:
line = s.readline()
if line:
print s.fileno(), line
proc.terminate()
print "After Terminating"
for i in xrange(0,5):
time.sleep(1)
inputready, outputready, exceptready = select.select(
[proc.stdout, proc.stderr], [proc.stdin,],
[proc.stdout, proc.stderr, proc.stdin], 0)
for s in inputready:
line = s.readline()
if line:
print s.fileno(), line
Gives the following output:
5 Hello, world!
After Terminating
Note that for some reason, using the timeout parameter in select.select() did not produce the expected results on my system, and I resorted to using time.sleep() instead.
Just FYI, running python as
/usr/bin/python 2>&1|tee test.out
seems to be working just fine.
You cannot get this effect because this example still gives the python interpreter a controlling tty. Without the controlling tty, the python interpreter does not print the Python version and does not display the >>> prompt.
A close example would be something like the following. You can replace the /dev/null with a file containing commands to send to the interpreter.
/usr/bin/python </dev/null 2>&1|tee test.out
If you redirect anything other than the controlling tty (keyboard) as the standard input to the process, you will get no output from the python interpreter. This is why your code appears not to work.
There are different way to do this.
You can, for example:
use SysV message queues and poll with timeout on the queue for message to arrive
create a pipe() for the child and a pipe() for the father both using the O_NONBLOCK flag and then select() on the file descriptors for data to arrive (to can even handle timeouts if no data arrives)
use socket() AF_UNIX or AF_INET, set it non blocking and select() or epoll() for data to arrive
mmap() MAP_SHARED memory segments and signal the other process when data is arrived, pay attention to the shared segment with a locking mechanism.
I wrote a sample in C with double pipes:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <signal.h>
#define BUFLEN (6*1024)
#define EXECFILE "/usr/bin/python"
char *itoa(int n, char *s, int b) {
static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
int i=0, sign;
if ((sign = n) < 0)
n = -n;
do {
s[i++] = digits[n % b];
} while ((n /= b) > 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
return s;
}
/*
int set_nonblock(int sockfd) { // set socket to non blocking
int arg,i;
if ((arg=fcntl(sockfd, F_GETFL, NULL)) < 0) {
printf("error getting socket flag for fd %i: fcntl(..., F_GETFL): %i\n", sockfd, errno);
return -1;
}
// set O_NONBLOCK flag
arg |= O_NONBLOCK;
if ((i=fcntl(sockfd, F_SETFL, arg)) < 0) {
printf("error setting socket flag for fd %i: fcntl(..., F_SETFL): %i\n", sockfd, errno);
return -1;
}
return i;
}
int set_block(int sockfd) { // set socket to blocking
int arg,i;
if ((arg=fcntl(sockfd, F_GETFL, NULL)) < 0) {
printf("error getting socket flag for fd %i: fcntl(..., F_GETFL): %i\n", sockfd, errno);
return -1;
}
// clean O_NONBLOCK flag
arg &= (~O_NONBLOCK);
if ((i=fcntl(sockfd, F_SETFL, arg)) < 0) {
printf("error setting socket flag for fd %i: fcntl(..., F_SETFL): %i\n", sockfd, errno);
return -1;
}
return i;
}
*/
int main() {
FILE *input;
char slice[BUFLEN];
int status = 0;
pid_t pid;
int err;
int newfd;
// if you want you can pass arguments to the program to execute
// char *const arguments[] = {EXECFILE, "-v", NULL};
char *const arguments[] = {EXECFILE, NULL};
int father2child_pipefd[2];
int child2father_pipefd[2];
char *read_data = NULL;
FILE *retclam;
fd_set myset;
int x=1;
signal(SIGPIPE, SIG_IGN);
newfd = dup(0);
input = fdopen(newfd, "r");
pipe(father2child_pipefd); // Father speaking to child
pipe(child2father_pipefd); // Child speaking to father
pid = fork();
if (pid > 0) { // Father
close(father2child_pipefd[0]);
close(child2father_pipefd[1]);
// Write to the pipe reading from stdin
retclam = fdopen(child2father_pipefd[0], "r");
// set the two fd non blocking
//set_nonblock(0);
//set_nonblock(child2father_pipefd[0]);
//set_nonblock(fileno(retclam));
while(x==1) {
// clear the file descriptor set
FD_ZERO(&myset);
// add the stdin to the set
FD_SET(fileno(input), &myset);
// add the child pipe to the set
FD_SET(fileno(retclam), &myset);
// here we wait for data to arrive from stdin or from the child pipe. The last argument is a timeout, if you like
err = select(fileno(retclam)+1, &myset, NULL, NULL, NULL);
switch(err) {
case -1:
// Problem with select(). The errno variable knows why
//exit(1);
x=0;
break;
case 0:
// timeout on select(). Data did not arrived in time, only valid if the last attribute of select() was specified
break;
default:
// data is ready to be read
bzero(slice, BUFLEN);
if (FD_ISSET(fileno(retclam), &myset)) { // data ready on the child
//set_block(fileno(retclam));
read_data = fgets(slice, BUFLEN, retclam); // read a line from the child (max BUFLEN bytes)
//set_nonblock(fileno(retclam));
if (read_data == NULL) {
//exit(0);
x=0;
break;
}
// write data back to stdout
write (1, slice, strlen(slice));
if(feof(retclam)) {
//exit(0);
x=0;
break;
}
break;
}
bzero(slice, BUFLEN);
if (FD_ISSET(fileno(input), &myset)) { // data ready on stdin
//printf("father\n");
//set_block(fileno(input));
read_data = fgets(slice, BUFLEN, input); // read a line from stdin (max BUFLEN bytes)
//set_nonblock(fileno(input));
if (read_data == NULL) {
//exit (0);
close(father2child_pipefd[1]);
waitpid(pid, &status, 0);
//fclose(input);
break;
}
// write data to the child
write (father2child_pipefd[1], slice, strlen(slice));
/*
if(feof(input)) {
exit(0);
}*/
break;
}
}
}
close(father2child_pipefd[1]);
fclose(input);
fsync(1);
waitpid(pid, &status, 0);
// child process terminated
fclose (retclam);
// Parse output data from child
// write (1, "you can append somethind else on stdout if you like");
if (WEXITSTATUS(status) == 0) {
exit (0); // child process exited successfully
}
}
if (pid == 0) { // Child
close (0); // stdin is not needed
close (1); // stdout is not needed
// Close the write side of this pipe
close(father2child_pipefd[1]);
// Close the read side of this pipe
close(child2father_pipefd[0]);
// Let's read on stdin, but this stdin is associated to the read pipe
dup2(father2child_pipefd[0], 0);
// Let's speak on stdout, but this stdout is associated to the write pipe
dup2(child2father_pipefd[1], 1);
// if you like you can put something back to the father before execve
//write (child2father_pipefd[1], "something", 9);
//fsync(child2father_pipefd[1]);
err = execve(EXECFILE, arguments, NULL);
// we'll never be here again after execve succeeded!! So we get here only if the execve() failed
//fprintf(stderr, "Problem executing file %s: %i: %s\n", EXECFILE, err, strerror(errno));
exit (1);
}
if (pid < 0) { // Error
exit (1);
}
fclose(input);
return 0;
}
I use 2-way io in bash like this:
mkfifo hotleg
mkfifo coldleg
program <coldleg |tee hotleg &
while read LINE; do
case $LINE in
*)call_a_function $LINE;;
esac
done <hotleg |tee coldleg &
(note that you can just ">" instead of tee, but you may want to see the output at first)
Your guess that buffered I/O is to blame is most likely correct. The way you wrote your loop, the read will block until it fills the required buffer, and you won't be able to process any input until it returns. This can easily cause a deadlock.
Popen.communicate deals with this by making a thread to work with each pipe, and by making sure it has all the data to be written to stdin, so that the actual write cannot be delayed while the file object waits for a buffer to fill or for the file object to be flushed/closed. I think you could make a solution involving threads work if you needed to, but that's not really asynchronous and probably not the easiest solution.
You can get around python's buffering by not using the file objects provided by Popen to access the pipes, and instead grabbing their fd's using the fileno() method. You can then use the fd's with os.read, os.write, and select.select. The os.read and os.write functions will do no buffering, but they will block until at least one byte can be read/written. You need to make sure the pipe is readable/writeable before calling them. The simplest way to do this is to use select.select() to wait for all the pipes you want to read/write, and make a single read or write call to every pipe that's ready when select() returns. You should be able to find examples of select loops if you search (they'll probably be using sockets instead of pipes, but the principle is the same). (Also, never do a read or write without checking first that it won't block, or you can end up with cases where you cause a deadlock with the child process. You have to be ready to read data even when you haven't yet written everything you want.)
If you need to control a Python interpreter session, you're probably better off with
embedding Python into your program (plain evals if it's in Python itself), or
using RPC facilities like rpyc like PyScripter does.
Btw in the latter case, the server can be run anywhere and PyScripter already has a working server module (client module is in Pascal, will need to translate).

Qt GUI app unexpectedly ending

Hi I am working on Linux and I am trying to create a GUI app to go with my executable I have made.
For some reason it unexpectedly ends. There is no error message, it just says in the Qt console window it unexpectedly ended with exit code 0.
Can someone please have a look at it for me. I am working on Linux.
I will also paste the code here.
void MainWindow::on_pushButton_clicked()
{
QString stringURL = ui->lineEdit->text();
ui->labelError->clear();
if(stringURL.isEmpty() || stringURL.isNull()) {
ui->labelError->setText("You have not entered a URL.");
stringURL.clear();
return;
}
std::string cppString = stringURL.toStdString();
const char* cString = cppString.c_str();
char* output;
//These arrays will hold the file id of each end of two pipes
int fidOut[2];
int fidIn[2];
//Create two uni-directional pipes
int p1 = pipe(fidOut); //populates the array fidOut with read/write fid
int p2 = pipe(fidIn); //populates the array fidIn with read/write fid
if ((p1 == -1) || (p2 == -1)) {
printf("Error\n");
return;
}
//To make this more readable - I'm going to copy each fileid
//into a semantically more meaningful name
int parentRead = fidIn[0];
int parentWrite = fidOut[1];
int childRead = fidOut[0];
int childWrite = fidIn[1];
//////////////////////////
//Fork into two processes/
//////////////////////////
pid_t processId = fork();
//Which process am I?
if (processId == 0) {
/////////////////////////////////////////////////
//CHILD PROCESS - inherits file id's from parent/
/////////////////////////////////////////////////
::close(parentRead); //Don't need these
::close(parentWrite); //
//Map stdin and stdout to pipes
dup2(childRead, STDIN_FILENO);
dup2(childWrite, STDOUT_FILENO);
//Exec - turn child into sort (and inherit file id's)
execlp("htmlstrip", "htmlstrip", "-n", NULL);
} else {
/////////////////
//PARENT PROCESS/
/////////////////
::close(childRead); //Don't need this
::close(childWrite); //
//Write data to child process
//char strMessage[] = cString;
write(parentWrite, cString, strlen(cString));
::close(parentWrite); //this will send an EOF and prompt sort to run
//Read data back from child
char charIn;
while ( read(parentRead, &charIn, 1) > 0 ) {
output = output + (charIn);
printf("%s", output);
}
::close(parentRead); //This will prompt the child process to quit
}
return;
}
EDIT:: DEBUGGING RESULTS
I ran the debugger and this is the error I received:
The inferior stopped because it received a signal from the Operating System.
Signal name : SIGSEGV
Signal meaning : Segmentation fault
You haven't initialized the "output" variable. On the last lines of your code, you do this:
while ( read(parentRead, &charIn, 1) > 0 ) {
output = output + (charIn);
printf("%s", output);
}
Which will do nasty things, since you are adding a byte read from your child process, to the output variable, which is a pointer that contains garbage, and then printing the contents of the "output" variable's address as a string. You probably want "output" to be a std::string, that way your code could make sense:
std::string output;
/* ... */
while ( read(parentRead, &charIn, 1) > 0 ) {
output += (charIn);
}
std::cout << output;
Once you have read all the data your child process has generated, you can write it to stdout.
EDIT: since you want to set the contents of "output" to a QPlainTextEdit, you can use QPlainTextEdit::setPlainText:
while ( read(parentRead, &charIn, 1) > 0 ) {
output += (charIn);
}
plainTextEdit.setPlainText(output.c_str());