I have been making simple shell which performs pipe.
Here is some code for operating pipe syntax.
int fd[2];
int stdin_copy;
int stdout_copy;
int status;
char * msg;
if (pipe(fd) == -1) {
perror("pipe");
exit(1);
}
// fd[0] : process read from fd[0]
// fd[1] : process write to fd[1]
if (execok(pr_words) == 0) { /* is it executable? */
status = fork(); /* yes; create a new process */
if (status == -1) { /* verify fork succeeded */
perror("fork");
exit(1);
} else if (status == 0) { /* in the child process... */
stdout_copy = dup(1);
close(1); // close standard output
dup(fd[1]);
close(fd[0]);
close(fd[1]); // close and fd[1] will be stdout
pr_words[l_nwds] = NULL; /* mark end of argument array */
status = execve(path, pr_words, environ); /* try to execute it */
perror("execve"); /* we only get here if */
exit(0); /* execve failed... */
}
/*------------------------------------------------*/
/* The parent process (the shell) continues here. */
/*------------------------------------------------*/
else if (status > 0) { // in the parent process....
wait( & status); /* wait for process to end */
if (execok(af_words) == 0) {
if (pipe(fd2) == -1) {
perror("pipe");
exit(1);
}
status = fork();
if (status == -1) {
perror("fork");
exit(1);
} else if (status == 0) { // in the child process...
stdin_copy = dup(0);
close(0);
dup(fd[0]);
close(fd[1]);
close(fd[0]);
read(fd[0], readbuffer, sizeof(readbuffer));
af_words[r_nwds] = NULL; /* mark end of argument array */
status = execve(path, af_words, environ); /* try to execute it */
} else if (status > 0) {
wait( & status);
msg = "over";
write(2, msg, strlen(msg));
close(fd[0]);
close(fd[1]);
dup2(stdin_copy, 0);
dup2(stdout_copy, 1);
close(stdin_copy);
close(stdout_copy);
printf("%s", "hi");
}
} else {
/*----------------------------------------------------------*/
/* Command cannot be executed. Display appropriate message. */
/*----------------------------------------------------------*/
msg = "*** ERROR: '";
write(2, msg, strlen(msg));
write(2, af_words[0], strlen(af_words[0]));
msg = "' cannot be executed.\n";
write(2, msg, strlen(msg));
}
}
} else {
/*----------------------------------------------------------*/
/* Command cannot be executed. Display appropriate message. */
/*----------------------------------------------------------*/
msg = "*** ERROR: '";
write(2, msg, strlen(msg));
write(2, pr_words[0], strlen(pr_words[0]));
msg = "' cannot be executed.\n";
write(2, msg, strlen(msg));
}
pr_words and af_words is two-dimensional pointer containing command, right-side and left-side of pipe. (ex. ls | cat -> pr_words = "ls\0" , af_words = "cat\0")
And, first I make child process using fork() and register fd[1] for standard output. (and also save stdin file descriptor before closing stdin) And after execute left side of command, make other child process for handling right side of command.
Similarly, I saved stdout file descriptor before close stdout and made fd[0] standard input. By using input from first outcome of execve function, I thought every outcome would be saved in fd[1]. (Because this was currently registered as std output).
And, finally, restore pipe input and output to standard output. (I don't want to use dup2 but I have no choice because of my lack of knowledge )
However, in execution of this code, after I enter the 'ls | cat', there is no output. Furthermore, I set every entry of terminal will print '#'. (which means that '# ls' or '# cat' ...) But, after enter above pipe command, that program even does not print '#'.
I guess input and output stream of this program are completely twisted after dealing with pipe command.
How can I fix it? I mean, I want save outcome of first execve into fd[1] and after using this fd[1] for performing second execve, make final outcome will be printed through stdout file description.
I see a few issues with your code at least:
First off, you shouldn't wait() on the first process before starting the second one. A pipe only has a few KB of buffer in it, after which your shell will hang if the first child process tries to continue to write there. You need to start both children before wait()ing for each of them. Just move the first wait(&status) call down next to the other one. You'll probably want to use waitpid or something later so you know which one finished first and which status goes to which, but you can address that once you get the basics working.
Secondly, all variables and file descriptor mappings in your program are copied when you fork(). Therefore, you don't need to save stdin or stdout in either child process, because none of the changes you make in the child processes will affect the parent. Furthermore, because you only initialize stdin_copy and stdout_copy in the child processes, the versions of those variables you use in the parent process after the second fork() are uninitialized. This is what's causing the parent shell's I/O to be messed up after executing this code. You don't actually need to do anything in the parent after forking the second time to maintain the original stdin and stdout there -- you never change them in that process before that point. You probably want to remove all of this from the post-fork parent code:
close(fd[0]);
close(fd[1]);
dup2(stdin_copy, 0);
dup2(stdout_copy, 1);
close(stdin_copy);
close(stdout_copy);
Thirdly, why are you reading from the pipe before calling execve() in the second child? That's just going to strip data out of the pipe that your exec'd child will never see. That's probably what's causing the pipe itself to appear not to work. You probably want to remove this:
read(fd[0], readbuffer, sizeof(readbuffer));
Lastly, this line probably needs to go before the execok() call (and similarly for the other similar one):
pr_words[l_nwds] = NULL; /* mark end of argument array */
The skeleton of the code should look about like this, leaving off error handling and execok checks, and demonstrating the use of waitpid() if you want to know which status code is for which child:
int child_pid[2];
child_pid[0] = fork();
if (child_pid[0] == 0) {
// first child, close stdout and replace with pipe, then exec
} else {
child_pid[1] = fork();
if (child_pid[1] == 0) {
// second child, close stdin and replace with pipe, then exec
} else {
// parent, and now we have the pids of the children
waitpid(child_pid[0], &status, 0); // wait for first child
waitpid(child_pid[1], &status, 0); // wait for second child
// *do not* mess with stdin/stdout, they are okay here
}
}
Related
I've been searching and reading manuals, but still can't get the pipe mechanism. I'm making a program, which should do the following:
Parent process creates pipe, two child proccesses and waits.
First child generates pairs of random numbers and passes them with space between to the second process via pipe. It goes on till
gets signal from parent.
Second child redirects it input so it is the output of the first child and redirects output to the out.txt file. Then it executes
already compiled program, calculating GCD of numbers from (1);
Parent closes pipe and kills child.
So I got this C code (I reduced it so the post can fit the rules):
const int PIPE_READEND=0;
const int PIPE_WRITEEND=1;
(...)
if (child1 == 0) {
//Child1 code here
close(fd[1]);
struct sigaction sa;
sa.sa_handler = sigHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (sigaction(SIGUSR1,&sa,NULL) == -1){ //Handling SIGUSR1 signal
perror("Signal handling unexpected error");
exit(errno);
}
int a,b;
srand(time(&t));
if (dup2(fd[PIPE_READEND],1) < 0){ //Redirecting stdout to the pipe fd.
perror("In Child1 Redirecting stdout to pipe error");
exit(errno);
}
close(fd[0]);
while(1){
a = rand();
b = rand();
printf("%d %d\n", a, b);
sleep(1);
}
(...)
if ((child2 = fork()) < 0){
perror("Fork error in Child2 process");
exit(errno);
} else if (child2 == 0){
//Child2 code here
close(fd[PIPE_READEND]);
FILE *outfile = fopen("out.txt","w");
dup2(fd[PIPE_WRITEEND],0);
dup2(outfile,1);
close(fd[PIPE_WRITEEND]);
execl("c1/main","main",(char *)NULL);
The problem is, after executing it, out.txt stays empty. I'm disappointed about the pipe array indices, which one is used for what.
You write and read from wrong indexes of pipe. You need to change them:
This redirects stdout to pipe input.
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
This redirects pipe output to stdin.
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
Also dup2 takes integer, not pointer, so you should do:
f = fopen("out.txt", "w");
dup2(fileno(f), STDOUT_FILENO);
FILE *outfile = fopen("out.txt","w");
dup2(fd[PIPE_WRITEEND],0);
dup2(outfile,1);
This makes no sense. The dup2 function doesn't take a FILE * as a parameter. Use open, not fopen.
I've been trying to write a program that will send and receive commands to a bash shell (/bin/sh). Like a wrapper program around a bash shell. So, I could write to stdin "cd ~/Desktop", then write again "ls" and I will receive a listing of the files on the desktop. I can't get it working though. On the second write command in this code, it will echo back whatever I wrote to stdin. I've also tried using popen() but that only provides output, not allowing me to write to stdin. Could someone please help solve this problem? Thanks
void main()
{
// Create a pipe and fork
//
int fd[2];
int p = pipe(fd);
pid_t pid = fork();
if (pid > 0)
{
// Read from the pipe and output the result
//
//close(fd[1]);
char buf[1024] = { 0 };
read(fd[0], buf, sizeof(buf));
printf("1 - %s\n", buf);
write (fd[1], "ifconfig", strlen ("ifconfig") );
// problem is here, read is returning echo'd bytes from write()
read(fd[0], buf, sizeof(buf));
printf("2 - %s\n", buf);
// Wait for child to terminate
int status;
wait(&status);
}
else if (pid == 0)
{
// Redirect stdout and stderr to the pipe and execute the shell
// command
//
dup2(fd[0], STDIN_FILENO);
dup2(fd[1], STDOUT_FILENO);
dup2(fd[1], STDERR_FILENO);
//close(fd[0]);
execl("/bin/sh", "exec sh", "-c", "ls", (char*) NULL );
}
}
EDIT - Updated code per 1st answer, now there is no output from the 2nd read() call
void main()
{
// Create a pipe and fork
//
int fd[2];
int ChildToParent[2], ParentToChild[2];
pipe (ParentToChild);
pipe (ChildToParent);
pid_t pid = fork();
if (pid > 0)
{
// In parent process
// Read the output of the child from child_to_parent[0]
// We don't need child_to_parent[1] so close it
close(ChildToParent[1]);
// Write output to the child using parent_to_child[1]
// We don't need parent_to_child[0] so close it
close(ParentToChild[0]);
// Read from and write to the child process...
char buf[1024] = { 0 };
read(ChildToParent[0], buf, sizeof(buf));
printf("1 - %s\n", buf);
write(ParentToChild[1], "whoami", strlen ("whoami") );
memset (buf, 0, 1024);
// this call to read returns nothing
read(ChildToParent[0], buf, sizeof(buf));
printf("2 - %s\n", buf);
}
else if (pid == 0)
{
// Redirect stdout and stderr to the pipe and execute the shell
// command
//
// child_to_parent[1] is were we write output, it's the
// new standard output, child_to_parent[0] can be closed
dup2 (ChildToParent[1], STDOUT_FILENO);
close(ChildToParent[0]);
// parent_to_child[0] is where we read input from, it's the
// new standard input, parent_to_child[1] can be closed
dup2 (ParentToChild[0], STDIN_FILENO);
close(ParentToChild[1]);
//close(fd[0]);
execl("/bin/sh", "exec sh", "-c", "ls", (char*) NULL );
}
}
Remember that pipes are a one-way communication stream. You can't use it for two-way communication between two processes. For that you need two pipes, one in each direction.
Perhaps something like this simple example:
// Pipe for the child process to write to the parent process
int child_to_parent[2];
// Pipe for the parent process to write to the child process
int parent_to_child[2];
// Create the TWO pipes
pipe(child_to_parent);
pipe(parent_to_child);
pid_t pid = fork();
if (pid > 0)
{
// In parent process
// Read the output of the child from child_to_parent[0]
// We don't need child_to_parent[1] so close it
close(child_to_parent[1]);
// Write output to the child using parent_to_child[1]
// We don't need parent_to_child[0] so close it
close(parent_to_child[0]);
// Read from and write to the child process...
}
else if (pid == 0)
{
// In child process
// child_to_parent[1] is were we write output, it's the
// new standard output, child_to_parent[0] can be closed
dup2(child_to_parent[1], STDOUT_FILENO);
close(child_to_parent[0]);
// parent_to_child[0] is where we read input from, it's the
// new standard input, parent_to_child[1] can be closed
dup2(parent_to_child[0], STDIN_FILENO);
close(parent_to_child[1]);
// Do whatever the child is supposed to do
}
This is a homework assignment that has me stumped. I make two pipes, then two child processes to handle both sides of the pipe. The first child handles the first command and writes it to the first pipe, the second child handles the second command and writes it to the second pipe. However, when all is said and done, I read the contents from the second pipe and put it into a buffer and simply printf(buffer). Its at this step that my code is failing. I cannot read from the buffer. I have tested all my method calls such as getWordsBeforePipe() and I know they work. Do you guys see anything I am missing?
// Create the first pipe
pipeStatus = pipe(pfd1);
if (pipeStatus == -1) {
perror("pipe");
exit(1);
}
// create the first child
pid = fork();
if (pid == -1) {
printf("Bad first fork()...\n");
exit(1);
}
// Here we will run the first command inside of the first child.
if (pid == 0) {
printf("Im in the first child...\n");
getWordsBeforePipe(pipeLoc); // get the words before the pipe
close(pfd1[0]); // close read end because we arent reading anything
dup2(pfd1[1], 1); // copy to write-end of pfd instead of stdout
close(pfd1[1]); // close the write end
firstCommand = execve(pathFirst, beforePipeWords, environ);
perror("execve"); // we only get here if execve died
_exit(1);
}
// create the second pipe
pipeStatus = pipe(pfd2);
if (pipeStatus == -1) {
perror("pipe");
exit(1);
}
// create the second child
pid = fork();
if (pid == -1) {
printf("Bad second fork()...\n");
exit(1);
}
// Here we will run the second command and put its
// output into the second pipe
// first command business
if (pid == 0) {
printf("Im in the second child...\n");
getWordsAfterPipe(pipeLoc);
close(pfd1[1]); // close first child write end
dup2(pfd1[0], 0); // read from the pfd read end instead of stdin
close(pfd1[0]); // close the read end
// second command business
close(pfd2[0]); // close read end because we arent reading anything
dup2(pfd2[1], 1); // copy to write end of pfd instead of stdout
close(pfd2[1]);
secondCommand = execve(pathSecond, afterPipeWords, environ);
perror("execve"); // we only get here if execve died
_exit(1);
}
close(pfd1[0]);
close(pfd2[0]);
close(pfd2[1]);
// read from the second pipe and output the final value
readSuccess = read(pfd2[0], buffer, 256);
if (readSuccess < 0) {
printf("Failure reading the buffer...\n"); // I keep getting this error
exit(1);
}
if (readSuccess == 0) {
printf("Empty buffer...\n");
exit(1);
}
buffer[readSuccess] = '\0';
printf("%s", buffer);
The parent process is doing this:
close(pfd2[0]);
Followed by this:
readSuccess = read(pfd2[0], buffer, 256);
You can't read from a file descriptor after it's been closed.
You properly closed both ends of the pfd1 pair, since the two children read/write from them. The second child writes to pfd2[1], so the parent should be closing that instead of pfd2[0].
Check that the command specified by pathFirst writes to stdout, and that the command specified by pathSecond both reads from stdin and writes to stdout.
I am trying to run ls|wc using execvp. So I create a pipe and then fork to create a child. I close the appropriate(read./write) end in parent/child and then map the other end to stdout/stdin. Then I run the ls in parent using execvp and wc in child. When I run the program it says
wc:standard input:bad file descriptor.
0 0 0
wc: -:Bad file descriptor
Here is my code:
int main()
{
//int nbBytes = 0; //stream length
int pfd_1[2]; //file descriptor
//char buffer[MAX_FILE_LENGTH];
char* arg[MAX_FILE_LENGTH];
pid_t processPid;
//Create a pipe
if(pipe(pfd_1) == -1)
{
printf("Error in creating pipe");
return 0;
}
//Create a child
processPid = fork();
if(processPid == -1)
{
printf("Erro in fork");
exit(1);
}
else if(processPid == 0) //Child
{
//redirect read end file descriptor to standard input
dup2(pfd_1[0],0);
//Close the write end
if(close(pfd_1[1] == -1))
{
printf("Error in closing the write end file descriptor");
exit(1);
}
arg[0] = "wc";
//arg[1] = "-l";
arg[1] = '\0';
if(execvp(arg[0],arg) == -1)
{
printf("Error in executing ls");
}
}
else //Parent
{
//redirect standard output to the file descriptor
dup2(pfd_1[1],1);
//Close the read end
if(close(pfd_1[0] == -1))
{
printf("Error in closing the read end from parent");
exit(1);
}
//Command
arg[0] = "ls";
arg[1] = "/proc/1/status";
arg[2] = '\0';
if(execvp(arg[0],arg) == -1)
{
printf("Error in executing ls");
}
}
}
Any idea what might be wrong? Why would it consider standard input as bad file descriptor? My understanding was since the stdin and read end file descriptor are aliases so the wc -l would read whatever the output is from the parent process. Do I need to do scanf to read from the stdin?
The problem is in this line:
if(close(pfd_1[1] == -1))
You are closing the result of pfd_1[1] == -1, which is by necessity equal to 0 (as they will never be equal). The correct line would probably be:
if (close(pfd_1[1]) == -1)
Note that you do this again later in attempting to close the read end in the parent process.
If you're going to fork children, you have to call wait() in the parent process in order to avoid "zombie" child processes. So you don't want to overlay the parent process that did the original process forking with another executable via exec.
One quick way to setup a series of pipes in the way you want would be to fork a child for each executable you want to run, and read that data back into a buffer in the parent. Then feed that data from the first child into a new child process that the parent forks off. So each child is fed data from the parent, processes the data, and writes the data back to the parent process, which stores the transformed data in a buffer. That buffer is then fed to the next child, etc., etc. The final results of the data in the buffer are the final output of the pipe.
Here's a little pseudo-code:
//allocate buffer
unsigned char buffer[SIZE];
for (each executable to run in pipeline)
{
pipes[2];
pipe(pipes);
pid_t pid = fork();
if (pid == 0)
{
//setup the pipe in the child process
//call exec
}
else
{
//setup the pipe in the parent process
if (child executable is not the first in the pipeline)
{
//write contents of buffer to child process
}
//read from the pipe until the child exits
//store the results in buffer
//call wait, and maybe also check the return value to make sure the
//child returned successfully
wait(NULL);
//clean up the pipe
}
}
What I am implementing is a (simpler) version of bash. One of the tasks is to accept the command :
$ bg <command> <arguments>
This will then fork a new process and then run execvp() in the new process using <command> and <arguments> as parameters. The problem is that when I capture the output from the child process, I use pipe(), and after getting the output from the child process and outputting it when I want, I can't seem to switch back to STDIN for my parent (shell) process, which results in a segfault the next time I need to accept input.
Here is part of my "bg" function.
ChildPID = fork();
if (ChildPID < 0) {
/* There is an error */
printf("Error forking the process.\n");
exit(1);
}
if (ChildPID >= 0) {
if (ChildPID == 0) { /* Child Process */
close(m_pipefd[0]);
dup2(m_pipefd[1], STDOUT_FILENO);
close(m_pipefd[1]);
//sleep(5);
err = execvp(optionsPTR[0], optionsPTR);
switch (errno) {
case ENOENT:
printf("RSI: %s: command not found\n", optionsPTR[0]);
break;
case EACCES:
printf("RSI: %s: Permission denied\n", optionsPTR[0]);
break;
}
exit(1);
}
else { /* Parent Process */
WaitErr = waitpid(ChildPID, &status, WNOHANG | WUNTRACED);
return(0); /* to main */
}
}
return 0;
And the code for when I get the output from the pipe after the process finishes.
close(m_pipefd[1]);
dup2(m_pipefd[0], STDIN_FILENO);
while(fgets(buffer, sizeof(buffer), stdin)) {
buf = buffer;
printf("%s\n", buf);
}
close(m_pipefd[0]);
So the tl;dr version is that I need to reset back to stdin for the parent process after capturing the child processes output.
Thanks,
Braden
There is usually no need to mess with stdin and stdout in your parent. After you connect your pipes in the child to stdin and stdout, the other ends of the pipes should be able to send data or get data from the child.
Just read from m_pipefd[1] in your parent.