LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-29-2009, 11:53 AM   #1
default5
LQ Newbie
 
Registered: Feb 2008
Distribution: Fedora
Posts: 18

Rep: Reputation: 0
Piping stdin/out/err from a subprocess


Hey guys, I know there are a lot of posts around the internet dealing with this, but unfortunately I haven't found one that answers my question. I'm trying to use fork(), dup2(), and exec() to create a subprocess (more specifically a bash shell) with all its i/o piped through the parent process. First, I just piped stdin, and that worked great. But trying stdout/err gives me some weird errors. Here is the code:
Code:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

char *data = "echo \"hi\" && sleep 3\nexit\n";

int main()
{
        /*in[2] is the stdin pipe and out[2] is the stdout/err pipe*/
	int in[2], out[2], n, pid, pstatus;
	char buf[255];
	
	pipe(in);
	pipe(out);
	if((pid = fork()) == 0)	
	{
		close(out[0]);
		close(in[1]);
		dup2(in[0], STDIN_FILENO);
		//dup2(out[1], STDOUT_FILENO);
		//dup2(out[1], STDERR_FILENO);
		close(in[0]);
		close(out[1]);
		execlp("bash", "bash", "-i", (char *)NULL);
		perror("exec");
		exit(1);
	}
	close(in[0]);
	close(out[1]);
	
	write(in[1], data, strlen(data));
	close(in[1]);
	
	n = read(out[0],buf,50);
	buf[n] = '\0';
	printf("->%s\n",buf);
	waitpid(pid,&pstatus, 0);
	exit(0);
}
Okay, so I compile and run with both stdout/err dup2()'s commented out:
Code:
[Dan@localhost c]$ gcc -Wall exec.c -o exec
[Dan@localhost c]$ ./exec
->
[Dan@localhost c]$ hi
[Dan@localhost c]$ exit
Again with just the stderr dup2() commented out:

Code:
[Dan@localhost c]$ gcc -Wall exec.c -o exec
[Dan@localhost c]$ ./exec
->]0;Dan@localhost:~/prog/c
[Dan@localhost c]$ [Dan@localhost c]$ exit
With stdout dup2() commented out:
Code:
[Dan@localhost c]$ gcc -Wall exec.c -o exec
[Dan@localhost c]$ ./exec
->bash: no job control in this shell

hi
And with nothing commented out:

Code:
[Dan@localhost c]$ gcc -Wall exec.c -o exec
[Dan@localhost c]$ ./exec
->bash: no job control in this shell
It's worth it to note that the pause from the sleep 3 call appears in all outputs. Thanks in advance.
 
Old 03-29-2009, 12:46 PM   #2
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
For one, you don't write a terminating null when sending the string. Don't use write to send text; fdopen the descriptor and use fprintf. Also, you probably don't need the -i option with bash. The first thing you should do when you run into errors is make sure you're checking the return status of e.g. dup2 and printing the error if the call fails.
Kevin Barry
 
Old 03-29-2009, 01:34 PM   #3
default5
LQ Newbie
 
Registered: Feb 2008
Distribution: Fedora
Posts: 18

Original Poster
Rep: Reputation: 0
Okay, I added error checking, and the same results occur.

Code:
char *data = "echo \"hi\" && sleep 3\nexit\n";

int main()
{
	FILE *writestream;
	int in[2], out[2], n, pid, pstatus;
	char buf[255];
	
	if(pipe(in) == -1) perror("stdin pipe");
	if(pipe(out) == -1) perror("stdout pipe");
	if((pid = fork()) == 0)	
	{
		if(close(out[0]) == -1) perror("close");
		if(close(in[1]) == -1) perror("close");
		if(dup2(in[0], STDIN_FILENO) == -1) perror("stdin dup2");
		//if(dup2(out[1], STDOUT_FILENO) == -1) perror("stdout dup2");
		//if(dup2(out[1], STDERR_FILENO) == -1) perror("stderr dup2");
		if(close(in[0]) == -1) perror("close");
		if(close(out[1]) == -1) perror("close");
		execlp("bash", "bash", "-i", (char *)NULL);
		perror("exec");
		exit(1);
	}
	else if(pid < 0) perror("fork");
	
	if(close(in[0]) == -1) perror("close");
	if(close(out[1]) == -1) perror("close");
	
	if((writestream = fdopen(in[1],"w")) == NULL) perror("fdopen");
	
	fprintf(writestream, data);
	if(fclose(writestream) == -1) perror("fclose");
	
	n = read(out[0],buf,50);
	buf[n] = '\0';
	printf("->%s\n",buf);
	waitpid(pid,&pstatus, 0);
	exit(0);
}
As far as the execlp() call, my man pages say this:
Quote:
The const char *arg and subsequent ellipses in the execl(), execlp(),
and execle() functions can be thought of as arg0, arg1, ..., argn.
Together they describe a list of one or more pointers to null-termi-
nated strings that represent the argument list available to the exe-
cuted program. The first argument, by convention, should point to the
filename associated with the file being executed. The list of argu-
ments must be terminated by a NULL pointer, and, since these are vari-
adic functions, this pointer must be cast (char *) NULL.
 
Old 03-29-2009, 05:33 PM   #4
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
Change this:
Code:
	write(in[1], data, strlen(data));
	close(in[1]);
to this:
Code:
	FILE *output = fdopen(in[1], "w");
	fprintf(output, "%s\n", data);
	fclose(output);
That will give you both the 0x00 and the newline required for bash to interpret the data as a command line (error checking omitted to make it readable.)
Kevin Barry

PS You might want to add setlinebuf(output); just after fdopen to make sure data is sent right after the newline is written to the stream.

Last edited by ta0kira; 03-29-2009 at 06:12 PM.
 
Old 03-29-2009, 08:40 PM   #5
default5
LQ Newbie
 
Registered: Feb 2008
Distribution: Fedora
Posts: 18

Original Poster
Rep: Reputation: 0
I did make that change when I added the error checking (except the setlinebuf() call). However, as I said in my first post, writing to of bash works fine. It prints the expected output on the terminal. the problem is when I try to redirect its output through my program and then print it out. I would expect the same output, but I get some weird text.
 
Old 03-29-2009, 09:01 PM   #6
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
Sorry, I missed that part. I just saw that you were pointing out the execlp and though you thought that was the NULL I was talking about. Why didn't you uncomment dup2(out[1], STDOUT_FILENO);? It seems to work (except for the fact that you have no includes.) Again, I suggest you remove the -i bash option because the prompts are misleading.
Kevin Barry
 
Old 03-29-2009, 10:08 PM   #7
default5
LQ Newbie
 
Registered: Feb 2008
Distribution: Fedora
Posts: 18

Original Poster
Rep: Reputation: 0
I just added the comments to show different output when different pipes were created. For instance, with the two lines commented out as they were originally, only stdin was piped. My first post shows the outputs for those all combinations of the two lines piping stdin and stderr being uncommented and commented. The reason I have the -i switch is that I want the prompts. I don't want to just get the output of commands, I want the whole shell. It appears to me from my tests that the "hi" appears on the stdout pipe, while the prompts and the result of echo appear on the stderr pipe (which are actually the same if both lines are uncommented). However, I'm confused why the bash job control error shows up on the stderr pipe with the prompts, but is not output when I don't pipe stderr, even though the prompts are. Sorry if that explanation was a bit confusing, I'll clarify anything if you didn't understand me.
 
Old 03-30-2009, 10:54 AM   #8
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
I see what you mean now. However, this doesn't happen if you remove -i. That means it's a bash issue rather than something you're doing in the program (as in you're not meeting the needs of bash; not that the program is inherently flawed.)
Kevin Barry
 
Old 03-30-2009, 11:46 AM   #9
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
Here's your problem. You change standard error to something that isn't a terminal; therefore, bash refuses interactive mode because it uses the terminal to show info about background processes, etc.
Kevin Barry
 
Old 03-31-2009, 06:03 AM   #10
default5
LQ Newbie
 
Registered: Feb 2008
Distribution: Fedora
Posts: 18

Original Poster
Rep: Reputation: 0
How can I get its output without causing it to leave interactive mode?
 
Old 03-31-2009, 08:35 AM   #11
ta0kira
Senior Member
 
Registered: Sep 2004
Distribution: FreeBSD 9.1, Kubuntu 12.10
Posts: 3,078

Rep: Reputation: Disabled
More importantly, how do you expect to use interactive mode when all three standard file descriptors are pipes? Without a terminal, bash will either need to print the prompts to standard error or not at all and it has no way of receiving e.g. [Ctrl]+Z. Do you want to read the prompts from the pipe and print them, or do you want them to show up on the screen? I suggest you change your command line to this and leave standard error untouched:
Code:
char *data = "{ echo \"hi\" && sleep 3; exit; } 2>&1";
Kevin Barry

PS If that doesn't work, you probably need to look into pseudo-terminals.

Last edited by ta0kira; 03-31-2009 at 07:50 PM.
 
Old 03-31-2009, 07:59 PM   #12
default5
LQ Newbie
 
Registered: Feb 2008
Distribution: Fedora
Posts: 18

Original Poster
Rep: Reputation: 0
I would like to read from the prompts and print them, and to read input from the user and send it to bash.

EDIT: I missed the comment about pty's on the first read, and I've looked into them briefly. They seem to be exactly what I'm looking for. I've been wondering how xterm worked because it does exactly what I wanted to do. Thanks.

Last edited by default5; 03-31-2009 at 09:06 PM. Reason: Missed part of previous post
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
dpkg -i goes wrong because of a piping error to/from subprocess wilsonsamm Linux - General 1 06-09-2008 10:51 PM
Dual system boot err: rootnoverify(hd1,0),but the hd0 err msg existed befor shut down lilipeng24 Linux - General 3 06-25-2005 12:10 PM
piping variables from stdin ziox Programming 6 02-13-2005 06:56 PM
Telnet user redirectng/piping stdin/out? MikHud Linux - General 2 02-11-2002 04:18 AM
Telnet user redirectng/piping stdin/out? MikHud Linux - Networking 0 01-30-2002 10:55 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 02:05 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration