ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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:
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
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.
That will give you both the 0x00and 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.
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.
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 -ibash option because the prompts are misleading.
Kevin Barry
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.
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
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
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:
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
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.