Can't pipe to less(1) - c

I'm trying to write a program that should works as " printenv | sort | less ". However the last pipe to less does not work. I can pipe to cat with the expected result (environment variables are displayed) but when i pipe to less nothing is displayed. The program runs less but nothing happens. If I go with more instead the program cats the output until the bottom of the window and after that nothing happens. I'm not in a pager.
Any ideas?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#define WRITE 1
#define READ 0
#define DEBUG 1
int p1[2], p2[2], p3[2];
void err(char* msg){
perror(msg);
exit(EXIT_FAILURE);
}
void closePipe(int pipeEnd[2]){
if (close(pipeEnd[READ]) == -1)
err("error when closing pipe, read");
if (close(pipeEnd[WRITE]) == -1)
err("error when closing pipe, write");
}
int main(int argc, char** argv, char** envp){
int pid1, pid2, pid3;
/*Creating pipes*/
if (pipe(p1) == -1){
err("pipe1");
}
if (pipe(p2) == -1){
err("pipe2");
}
/*
if (pipe(p3) == -1){
err("pipe3");
}
*/
/*First fork*/
pid1 = fork();
if (pid1 == -1){
err("fork1");
}
/**FIRST CHILD*************PRINTENV****************/
else if (pid1 == 0){
if (dup2(p1[WRITE],STDOUT_FILENO)== -1){
err("dup2 miss");
}
closePipe(p2);
closePipe(p1);
if (execlp("printenv", "printenv", NULL) == -1){
err("execlp printenv does not like you!");
}
}
/**PARENT*******************************************/
else {
pid2 = fork();
if (pid2 == -1){
err("fork2");
}
/**SECOND*CHILD***********SORT*******************/
else if (pid2 == 0){
if (dup2(p1[READ],STDIN_FILENO) == -1){
err( "dup2 p1 read" );
}
if (dup2(p2[WRITE], STDOUT_FILENO) == -1){
err("dup2 p2 write");
}
closePipe(p2);
closePipe(p1);
if (execlp("sort", "sort", NULL) == -1){
err("execlp sort does not like you!");
}
}
/**PARENT*****************************************/
else {
pid3=fork();
if (pid3 == -1){
err("fork3");
}
/**THIRD CILD***************LESS****************/
else if (pid3 == 0){
if (dup2(p2[READ], STDIN_FILENO) == -1){
err("err dup2 read");
}
closePipe(p2);
closePipe(p1);
char *args4exec[] = {"less", NULL};
if (execvp(args4exec[0], args4exec) == -1){
err("err in exelp pager");
}
}
}
}
return 0;
}

Two things. First off, in the parent you also need to close the pipes. Second, your parent process should wait for the less process to finish before exiting itself. Try adding this to your code:
/* ... */
closePipe(p1);
closePipe(p2);
waitpid(pid3, NULL, 0);
return 0;
}
The first issue causes the child never to be able to properly receive an EOF, and the 2nd will cause less to exit right away because the parent goes away.

Related

pipe in C stuck when calling `wc` or `grep`

Problem - when calling ls -l | grep etc, stuck on grep (grep child process does not exit)
trying to run "ls | grep r" with "execvp()" suggests that
need to close file descriptors
wait outside of the forking loop
IMO I have performed both of above but the problem still exists.
Any opinion is welcome, thanks!
Note that below is a hard-coded version for 2 pipes only
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i = 0;
int pfd[2];
if (pipe(pfd) != 0)
{
printf("Error creating pipe\n");
exit(errno);
}
char **ptr = get_pipes(); // pipes as array of strings
char *command = *ptr;
while (command != NULL)
{
if (i == 2)
break; // hard code to ignore all commands after 2nd pipe
char **args = parse_cmd(command); // this parses a space-separated command as arguments
pid_t pid = fork();
if (pid == 0 && i == 0) // 1st pipe, 1st child
{
close(pfd[0]); // close pipe read end
dup2(pfd[1], 1); // set pipe write end to stdout
if (execvp(args[0], args) == -1)
{
fprintf(stderr, "%s: %s\n", args[0], strerror(errno));
exit(EXIT_FAILURE);
}
}
else if (pid == 0 && i == 1) // 2nd pipe, 2nd child
{
close(pfd[1]); // close pipe write end
dup2(pfd[0], 0); // set pipe read end to stdin
if (execvp(args[0], args) == -1)
{
fprintf(stderr, "'%s': %s\n", args[0], strerror(errno));
exit(EXIT_FAILURE);
}
// exit(EXIT_SUCCESS);
}
else if (pid > 0) // parent
{
printf("Parent pid: %d and child's pid is %d\n", (int)getpid(), (int)pid);
}
command = *++ptr;
i++;
}
pid_t zombie_pid;
int status;
do
{
zombie_pid = waitpid(-1, &status, 0);
printf("Child PID %d died with status %d\n", (int)zombie_pid, WEXITSTATUS(status));
} while (zombie_pid > 0);
}

Two pipes in c program and the second one does not work as expected

I am currently making some tests with the pipes and forks in C.
I am trying to copy the behavior of this shell command in my program:
cat < test | wc
The test file contains only a little text.
Here is my program's code:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <stdlib.h>
// cat < test | wc
int main(int ac, char **av)
{
int p1[2];
int pid1;
printf("\nBegin of the test...\n");
if (pipe(p1) == -1)
{
perror("Pipe1");
exit(1);
}
else
{
pid1 = fork();
if (pid1 > 0)
{
int p2[2];
int pid2;
close(p1[0]);
if (pipe(p2) == -1)
{
perror("Pipe1");
exit(1);
}
else
{
pid2 = fork();
if (pid2 > 0)
{
int pid3;
close(p1[1]);
pid3 = fork();
if (pid3 > 0)
{
close(p2[1]);
wait(0);
printf("All is done !\n");
}
else if (pid3 == 0)
{
close(p2[1]);
dup2(p2[0], 0);
execve("/usr/bin/wc", av, NULL);
}
wait(0);
}
else if (pid2 == 0)
{
int fd;
char buffer[20];
int ret;
close(p2[0]);
dup2(p2[1], 1);
fd = open("test", O_RDONLY);
if (fd >= 0)
{
while ((ret = read(fd, buffer, 20)) > 0)
write(p1[1], buffer, ret);
close(fd);
close(p1[1]);
}
else
{
perror("Open file error");
exit(1);
}
close(p1[1]);
exit(1);
}
}
wait(0);
}
else if (pid1 == 0)
{
close(p1[1]);
dup2(p1[0], 0);
execve("/bin/cat", av, NULL);
}
}
printf("\nEnd of the program.\n");
return (1);
}
The output is:
Begin of the test...
All is done !
Content of the test file
0 0 0
End of the program.
Can someone explain to me why the cat output is not redirected to the wc through the pipe 2?

Recursive piping in Unix again

I know this topic came up already several times, but I'm still stuck at one point.
I need to write a program that emulates cmd1 | cmd2 | cmd3 ... piping.
My code is here: http://ideone.com/fedrB8
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void pipeline( char * ar[], int pos, int in_fd);
void error_exit(const char*);
static int child = 0; /* whether it is a child process relative to main() */
int main(int argc, char * argv[]) {
if(argc < 2){
printf("Usage: %s option (option) ...\n", argv[0]);
exit(1);
}
pipeline(argv, 1, STDIN_FILENO);
return 0;
}
void error_exit(const char *kom){
perror(kom);
(child ? _exit : exit)(EXIT_FAILURE);
}
void pipeline(char *ar[], int pos, int in_fd){
if(ar[pos+1] == NULL){ /*last command */
if(in_fd != STDIN_FILENO){
if(dup2(in_fd, STDIN_FILENO) != -1)
close(in_fd); /*successfully redirected*/
else error_exit("dup2");
}
execlp(ar[pos], ar[pos], NULL);
error_exit("execlp last");
}
else{
int fd[2];
pid_t childpid;
if ((pipe(fd) == -1) || ((childpid = fork()) == -1)) {
error_exit("Failed to setup pipeline");
}
if (childpid == 0){ /* child executes current command */
child = 1;
close(fd[0]);
if (dup2(in_fd, STDIN_FILENO) == -1) /*read from in_fd */
perror("Failed to redirect stdin");
if (dup2(fd[1], STDOUT_FILENO) == -1) /*write to fd[1]*/
perror("Failed to redirect stdout");
else if ((close(fd[1]) == -1) || (close(in_fd) == - 1))
perror("Failed to close extra pipe descriptors");
else {
execlp(ar[pos], ar[pos], NULL);
error_exit("Failed to execlp");
}
}
close(fd[1]); /* parent executes the rest of commands */
close(in_fd);
pipeline(ar, pos+1, fd[0]);
}
}
It works completely fine for up to 3 commands, but when it comes to 4 and more it doesnt any more and after hours of analysing, I still cant get where the problem is.
Example:
./prog ls uniq sort head
gives:
sort: stat failed: -: Bad file descriptor
Not an expert, but it seems the following line is the problem:
((close(fd[1]) == -1) || (close(in_fd) == - 1))
Try not to close in_fd there.
I think, the parent is trying to close the same fd that is closed by child.
When you use dup2() you do not need to close the files as dup2() already closes the file.

Why read() wont return zero when reading from pipe

I am having a problem with my assignment I have due for class. I have to create a read/write program that will read a text file into it and write the contents to a new text file. The thing is, I have to use parent/child processes and piping. I have to pass the contents into the pipe with one child, and use another child to read the data from the pipe and write it to a new file.
I have three files: parent.c, read.c and write.c. The program works fine for the most part! It even transfers the data from one file to the other perfectly. The problem I am having is that the write.c process will never complete. I think it may have something to do with the reading from pipe(won't return 0 or EOF). Here is my source code:
parent.c
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFF_SIZE 255
int main(int ac, char* av[])
{
if(ac <3)
{
printf("Please enter all required arguments!\n");
exit(0);
}
int pfd[2];
int pipeCreated;
char readFile[50];
char writePipe[20];
pid_t child_pid_read;
pid_t child_pid_write;
pipeCreated = pipe(pfd);
if(pipeCreated == -1)
{
printf("An error occurred when trying to create a pipe\n");
exit(0);
}
strcpy(readFile, av[1]);
sprintf(writePipe,"%d", pfd[1]);
child_pid_read = fork();
char writeFile[50];
char readPipe[20];
//Handling the read()
switch(child_pid_read)
{
//Error in case forfk() failed
case -1:
perror("fork failed");
return 1;
//Handle child processes
case 0:
if(close(pfd[0]) == -1)
{
printf("An error occurred while closing the pipe\n");
exit(0);
}
if(execle("./read.out", "./read.out", readFile, writePipe, (char*)0, NULL) == -1)
{
printf("Child: Error creating read.\n");
exit(0);
}
default:
wait(&child_pid_read);
strcpy(writeFile, av[2]);
sprintf(readPipe,"%d", pfd[0]);
child_pid_write = fork();
break;
}
//Handling the write
switch(child_pid_write)
{
//Error in case fork() failed
case -1:
perror("fork failed");
return 1;
//Handle child processes
case 0:
if(close(pfd[1]) == -1)
{
printf("An error occurred while closing the pipe\n");
exit(0);
}
if(execle("./write.out", "./write.out", writeFile, readPipe, (char*)0, NULL) == -1)
{
printf("Child: Error creating read.\n");
exit(-1);
}
break;
default:
wait(&child_pid_write);
break;
}
printf("Write completed!");
return 0;
}
read.c:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define BUFF_SIZE 16
int main(int ac, char* av[])
{
char buffer[BUFF_SIZE];
int fd;
int pid;
if(ac > 1)
{
fd = open(av[1], O_RDONLY);
if(fd == -1)
{
printf("error: Could Not Open File\n");
exit(0);
}
pid = atoi(av[2]);
}
int num_read = 1;
while(1)
{
num_read = read(fd, buffer, BUFF_SIZE);
if(num_read == -1)
{
printf("Error reading file\n");
exit(0);
}
if(num_read == 0)
{
break;
}
if(write(pid, buffer, num_read) != num_read)
{
printf("Error writing to pipe\n");
break;
}
}
close(fd);
return 1;
}
write.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define BUFF_SIZE 1
int main(int ac, char* av[])
{
char buffer[BUFF_SIZE];
int fd = open(av[1], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
int pid = atoi(av[2]);
int num_read = 1;
while(1)
{
num_read = read(pid, buffer, BUFF_SIZE);
printf("num_read: %d\n", num_read);
if(num_read == -1)
{
printf("Error reading pipe\n");
break;
}
if(write(fd, buffer, num_read) != num_read)
{
printf("Error writing to file\n");
break;
}
if(num_read == EOF)
{
break;
}
}
close(fd);
return 1;
}
Please look over my code and suggest corrections. I am passing in the names of the text files through the terminal (./parent.out, oldFile.txt, newFile.txt).
Two problems:
You're not forking the write process until after wait() for the read process returns. If the read process tries to write more data than will fit in the pipe buffer, it will block and never exit. You need to allow both processes to run concurrently to avoid this deadlock. It will work with a small file, but if the file is bigger than 4KB it will hang.
After forking the write process, the parent process has to close pfd[0]. The reader of a pipe doesn't get EOF until all processes that have the write end open close it. It should be:
default:
if(close(pfd[0]) == -1)
{
printf("An error occurred while closing the pipe\n");
exit(0);
}
wait(&child_pid_write);
break;
Your child want to read data,why you close the fd[0], return from pipe indicating that fd[0] for reading and fd[1] for writing.As i can't add a comment, i have to post the comment here....

I'm building a small shell. How do I set the standard in- and output of two processes to a pipe, so they can communicate?

I'm trying to implement a very small shell of my own. I have to be able to handle pipes, like
ls -l | wc -l
but only for two programs at a time. Right now, I have this:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFER_SIZE 256
#define NO_PARAMS 32
void split_string(char **params, char *string){
char *arg;
int i;
arg = strtok(string, " ");
params[0] = arg;
i = 1;
while(arg != NULL){
arg = strtok(NULL, " ");
params[i] = arg;
i++;
}
}
int main(int argc, char **argv){
char string[BUFFER_SIZE];
char *prog1, *prog2;
int i, err;
int fd[2];
pid_t pid1, pid2;
size_t buffer = BUFFER_SIZE;
char *params1[NO_PARAMS], *params2[NO_PARAMS];
int pipe_exists = 0;
memset(string,0,buffer);
while(1){
/*Read command*/
fgets(string, BUFFER_SIZE-1, stdin);
if(string == NULL){
perror("Error reading input:\n");
exit(1);
}
/*replace linefeed character with end of line character*/
for(i=0;i<BUFFER_SIZE;i++){
if(string[i] == 10){
string[i] = 0;
}
}
/*check if command is "exit"*/
if(strcmp(string,"exit") == 0){
return 0;
}
/*split command into different program calls*/
prog1 = strtok(string, "|");
prog2 = strtok(NULL,"\0");
if(prog2 != NULL){
pipe_exists = 1;
printf("PIPE!\n");
err = pipe(fd);
if(err<0){
perror("Error creating pipe:\n");
exit(1);
}
}
/*split string into arguments*/
split_string(params1, prog1);
if(pipe_exists){
split_string(params2, prog2);
}
/*fork child process*/
pid1 = fork();
if(pid1==0){ /*child 1*/
if(pipe_exists){
close(fd[0]); /*close read-end*/
err = dup2(fd[1], 1);
if(err<0){
perror("Error with dup in child 1!\n");
exit(1);
}
}
execvp(params1[0],params1);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent*/
if(pipe_exists){
pid2 = fork();
if(pid2==0){ /*child 2*/
close(fd[1]); /*close pipe write-end*/
err = dup2(fd[0], 0);
if(err<0){
perror("Error with dup in child 2!\n");
exit(1);
}
execvp(params2[0],params2);
perror("Error calling exec()!\n");
exit(1);
}else{ /*parent with 2 children*/
waitpid(pid1,0,0);
waitpid(pid2,0,0);
}
}else{ /*parent with 1 child*/
waitpid(pid1,0,0);
}
}
}
}
Right now, it'll handle single commands fine, but when I input something like the command above, nothing happens!
Thanks!
Oh! I've already figured it out. I had to close the pipe in the parent program as well :)
To start with, you should loop as long as you find the pipe character. Then you need to create a pipe for each "piping".
Real shells usually forks and exec itself for each command in the pipeline. This is so it should be able to handle internal commands.
There are 3 main parts in a command with pipes.
The begining, that takes stdin and pipes its output something |
The middle, optionnal or repeated at will with two pipes | something |
The end, that outputs to stdout | something
Then use three functions, one for each of those:
#define PIPE_INPUT 0
#define PIPE_OUTPUT 1
execute_pipe_start(t_cmdlist *commands)
{
int pid;
int fd[2];
if (!commands)
return;
if (commands->next)
{
if (pipe(fd) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd[PIPE_INPUT]);
if (dup2(fd[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
{
waitpid(...); //what you put here is a bit tricky because
//some shells like tcsh will execute all
//commands at the same time (try cat | cat | cat | cat)
}
if (commands->next->next != null) //If you have 2 commands in line there is a middle
execute_pipe_middle(commands->next, fd);
else // no middle
execute_pipe_end(commands->next, fd);
}
else
parse_and_exec_cmd(commands->cmd);
}
execute_pipe_middle(t_cmdlist *commands, int fd_before[2])
{
int pid;
int fd_after[2];
if (pipe(fd_after) < 0)
{
perror("pipe failed");
exit(1);
}
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
close(fd_after[PIPE_INPUT]);
if (dup2(fd_after[PIPE_OUTPUT, 1) < 0)
{
perror("dup2 failed");
exit(1);
}
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
if (commands->next->next != null) //More than two following commands : a middle again
execute_pipe_middle(commands->next, fd_after);
else // No more repetition
execute_pipe_end(commands->next, fd_after);
}
execute_pipe_end(t_cmdlist *commands, int fd_before[2])
{
int pid;
if (!commands)
return;
if (commands->next)
{
pid = fork();
if (!pid)
{
close(fd_before[PIPE_OUTPUT]);
if (dup2(fd_before[PIPE_INPUT, 0) < 0)
{
perror("dup2 failed");
exit(1);
}
parse_and_exec_cmd(commands->cmd);
}
else
waitpid(...);
}
}

Resources