Using processes to run commands with pipes - c

I'm still new to processes,pipes and dup2, therefore I'd like someone to help me figure out what's wrong with a program I've created. This program is supposed to run ls | wc. So far the output I get is :
wc : standard input : Bad file descriptor
0 0 0
ls : write error : Bad file descriptor
After I get this output, the terminal still accepts inputs. It's like wc is still running, although if I put commands like ls first(without any other input before) it runs them and shuts down. I tried running ps before/after and while the program was still running and it didn't show any process being open aside from bash and ps. (I'm running this program in Linux terminal)
Here's my code :
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
#include<errno.h>
int main(int argc, char* argv[]){
pid_t pid;
int fd[2];
char com1[1024] = ("ls");
char com2[1024] = ("wc");
pipe(fd);
pid = fork();
if(pid == 0){
open(fd[1]);
dup2(fd[0],STDOUT_FILENO);
close(fd[0]);
execlp(com1, com1, NULL);
}
else {
pid = fork();
if (pid == 0){
open(fd[0]);
dup2(fd[1],STDIN_FILENO);
close(fd[1]);
execlp(com2, com2, NULL);
}
}
return 0;
}
Bear in mind that I know some if commands for checking are required(like if(pid<0)exit(0);) but I tried to simplify my code as much as possible in order to see if there's any mistake due to carelessness.
Thank you in advance!

According to the pipe manual page:
pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe.
Now take this line from the first child, the process that calls the ls command:
dup2(fd[0],STDOUT_FILENO);
Here you duplicate the read end of the pipe to STDOUT_FILENO, i.e. where output is written. If you stop and think a little about it, how would you write to a read-only file-descriptor like fd[0]?
Same with the other child process, where you make the write end of the pipe standard input.
The solution is simple: Swap places of the descriptors you duplicate. Use fd[1] for the first child process, and fd[0] for the second child process.
In the first process where you call the ls command:
dup2(fd[1],STDOUT_FILENO);
close(fd[1]);
execlp(com1, com1, NULL);
And in the second child process where you call the wc command:
dup2(fd[0],STDIN_FILENO);
close(fd[0]);
execlp(com2, com2, NULL);

Related

execve /bin/bash as child process. interacting with bash via parent process using pipes

I want to execve a bash as a child process in a c program. The bash should essentially be controlled by the parent process: the parent process reads from stdin, stores the read input into a buffer and writes the content of the buffer to the bash through a pipe. The output of the bash is supposed to be passed through another pipe back to the parent process's stdout. For instance: the parent process reads "ls" and gives it to the bash through a pipe and receives the output of the bash through another pipe. I know this program doesn't make sense, because there are better ways to execute ls (or some other program) on behalf of the parent process. I'm actually just trying to understand how piping works and this is the first program that came into my mind. And i can't make this program work. That's what i have so far:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
int main() {
int pc[2];//"parent to child"-pipe
int cp[2];//"child to parent"-pipe
int status;
char buffer[256];
char eof = EOF;
if (pipe(pc) < 0 || pipe(cp) < 0) {
printf("ERROR: Pipes could not be created\n");
return -1;
}
pid_t child_pid = fork();
if (child_pid == 0) { //child has pid 0, child enters here
close(pc[1]);//close write end of pc
close(cp[0]);//close read end of cp
//redirecting file descriptors to stdin/stdout
dup2(cp[1], STDOUT_FILENO);
dup2(pc[0], STDIN_FILENO);
execve("/bin/bash",NULL,NULL);
} else {//parent enters here
close(cp[1]);//close write end of cp
close(pc[0]);//close read end of pc
//redirecting file descriptors to stdin/stdout
dup2(cp[0], STDOUT_FILENO);
while(1) {
read(STDIN_FILENO, buffer, 3);
write(pc[1], buffer, 3);
}
waitpid(child_pid, &status, 0);
}
return 0;
}
On execution: I type in ls, hit enter, nothing happens, hit enter again, output.
$ ./pipe
ls
bash: line 3: s: command not found
Why is only the character 's' delivered to the bash?

error in executing system function with STDOUT_FILENO closed

I have an strange issue. I am not very good in C language. I am trying to create a daemon to execute my bash script based service in Linux. To make it easy I have made the code simple. Below is my code.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
int main(int argc, char* argv[])
{
pid_t process_id = 0;
pid_t sid = 0;
process_id = fork();
if (process_id < 0)
{
printf("fork failed!\n");
exit(1);
}
if (process_id > 0)
{
printf("daemon creatd with process id %d \n", process_id);
exit(0);
}
umask(0);
sid = setsid();
if(sid < 0)
{
fprintf(stderr, "Return error\n");
exit(1);
}
chdir("/");
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
int status = system("ls");
openlog("slog", LOG_PID|LOG_CONS, LOG_USER);
syslog(LOG_INFO, "Returned status is %d", status);
closelog();
return (0);
}
As you can see, the above program will execute the system function to execute the ls command and output the exit code to system log.
After I compile the program and run, in the logs I can see the status code is 512. But If I comment out the following line,
close(STDOUT_FILENO);
then it works perfect and I can see in the log the status code is 0,
What I might be doing wrong?
UPDATE
My program is pretty big and I am not using ls in real environment. i made the program simple to reproduce the issue what I am facing. Also, to see the status of program, I am not looking at the output but the status code in the syslog.
In case it is not clear from other comments and answers, ls writes its output to stdout. If it can not write to stdout it terminates and (apparently) sets an status code of 512 (non-zero in any case).
The child process will inherit stdout from the parent, so if you do not close stdout in the parent, the child will have a stdout to write to and the command will succeed. If you close stdout, then the child has nowhere to write its output and the error occurs.
So, although ls is just an example for this question, your actual child process is writing to stdout which results in the same problem.
The simplest way around this, and assuming that you do not want the child's output, is to redirect the child's stdout on the command line:
int status = system("ls >/dev/null 2>&1");
This will redirect stdout and stderr to /dev/null, effectively throwing away the child's output while still giving it somewhere to write to.
Your daemon creation looks fine. But daemon processes by convention do not have a controlling terminal which you accomplish by closing all the standard file descriptors and call setsid() to create a new session to make the daemon a session leader. So, you can't make the daemon produce any output on the stdout stream. It obviously works if you don't close the stdout stream.
So, what you are doing is trying to write something to a tty from a terminal. So, you either don't need a daemon for this purpose or you need to write to a different file (instead of stdout).

Linux Fork Process termination

//Executing shell command ls -l | sort
int *pipeIN, *pipeOUT;
int runLS(){
char* parmListLS[] = { "ls", "-l", NULL };
int pid = fork();
if(pid==0){
close(*pipeIN);
dup2(*pipeOUT, STDOUT_FILENO);
execvp(parmListLS[0], parmListLS);
}else return pid;
}
int runSORT(){
char* parmListSORT[] = { "sort", NULL };
int pid = fork();
if(pid==0){
close(*pipeOUT);
dup2(*pipeIN, STDIN_FILENO);
execvp(parmListSORT[0], parmListSORT);
}else return pid;
}
int main(void){
int pidLS, pidSort, pipeId[2];
pipeIN = &pipeId[0], pipeOUT = &pipeId[1];
pipe(pipeId); //open pipes
pidLS = runLS();
pidSort = runSORT();
printf("PIDS: LS -> %d, Sort -> %d\n", pidLS, pidSort);
printf("Terminated: %d\n", wait(NULL)); //return pid of 1st exited proc
printf("Terminated: %d\n", wait(NULL)); //return pid of 2nd exited proc
printf("Terminated Main Proccess!\n");
}
Hello! I'm having trouble making some simple pipes work on linux.
I'm trying to emulate the shell command ls - l | sort.
As you can see, I'm initializing two child proccesses,
one for ls -l, and the other for sort.
Those proccesses run independently, and I'm trying to redirect the output
of ls - l, as input for sort via pipes. The concept is that the main program
should wait for both of them to terminate. However, wait seems to work
fine for ls -l, but it hangs - never terminates for sort. So the whole program
hangs. If I remove the two wait code lines from the program, the ls - l -> sort
pipe works perfectly, and of course main terminates before them.
I suspect this is due to the fact that sort keeps waiting for input,
even after ls - l has terminated. I don't understand how the termination of
the parent affects the termination of both children processes.
Would anyone please be so kind as to explain to me what actualy happens?
Thank you very much. ps. I ignored most error checking for clarity and less code.
The sort can't terminate because the parent process still has the write end (and the read end) of the pipe open, so the sort hasn't reached EOF on the pipe, so it hasn't got all the data, so it can't write any output.
Add:
close(pipeId[0]);
close(pipeID[1]);
after the calls to runLS() and runSORT() and things work as expected.
Ideally, the children should close both ends of the pipe after they've just duplicated. If you duplicate a pipe file descriptor to a standard file stream, you should almost always close both ends of the pipe. In this code, you get away without doing it; that won't always be the case.
Personally, I don't think the two global variables help much. The pipe labelled pipeIn is the input to sort but the output from ls, for example.

ps command linux vs unix different behavior in c program

I have a simple c program that executes 'ps' and pipes it to 'grep', basically 'ps | grep x'.
the code goes more or less something like this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
int pipefd[2];
int pid;
pipe(pipefd);
pid=fork();
if (pid == 0){
close(pipefd[1]);
dup2(pipefd[0], 0);
close(pipefd[0]);
execlp("grep", "grep", "b", (char *) 0);
}
else{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ps", "ps", (char *) 0);
}
exit(0);
}
The problem that i have is that when i run this on unix (Solaris) is works perfect, but when i run this on (Debian) it executes properly but gives me an error message.
error message:
Signal 17 (CHLD) caught by ps (procps-ng version 3.3.3).
ps:display.c:59: please report this bug
I have try the same program running different commands like 'ls' and 'grep' with no problem on either os. What makes 'ps' different?
EDIT:
added the included libraries to the code.
When your program calls fork, it creates a parent process and a child process. In the child process fork returns 0 and in the parent it returns 1. Whenever a child process terminates, a SIGCHLD signal is sent to the parent process.
Now, in your case you call execlp in both the parent and child process, which replaces the running process image but does not change the relationship. This means that ps is your parent process and grep is your child process. Normally this would not matter, as programs ignore SIGCHLD by default, but ps catches all unknown signals and quits with the message you see there. You can see the relevant function in the source code for ps (or rather procps).

Unix pipe - reading data from stdin in the child descriptor

I'm trying to implement unix piping in c (i.e. execute ls | wc). I have found a related solution to my problem (C Unix Pipes Example) however, I am not sure why a specific portion of the solved code snippet works.
Here's the code:
/* Run WC. */
int filedes[2];
pipe(filedes);
/* Run LS. */
pid_t pid = fork();
if (pid == 0) {
/* Set stdout to the input side of the pipe, and run 'ls'. */
dup2(filedes[1], 1);
char *argv[] = {"ls", NULL};
execv("/bin/ls", argv);
} else {
/* Close the input side of the pipe, to prevent it staying open. */
close(filedes[1]);
}
/* Run WC. */
pid = fork();
if (pid == 0) {
dup2(filedes[0], 0);
char *argv[] = {"wc", NULL};
execv("/usr/bin/wc", argv);
}
In the child process that executes the wc command, though it attaches stndin to a file descriptor, it seems that we are not explicitly reading the output produced by ls in the first child process. Thus, to me it seems that ls is run independently and wc is running independently as we not explicitly using the output of ls when executing wc. How then does this code work (i.e. it executes ls | wc)?
The code shown just about works (it cuts a number of corners, but it works) because the forked children ensure that the the file descriptor that the executed process will write to (in the case of ls) and read from (in the case of wc) is the appropriate end of the pipe. You don't have to do any more; standard input is file descriptor 0, so wc with no (filename) arguments reads from standard input. ls always writes to standard output, file descriptor 1, unless it is writing an error message.
There are three processes in the code snippet; the parent process and two children, one from each fork().
The parent process should be closing both its ends of the pipe too; it only closes one.
In general, after you do a dup() or dup2() call on a pipe file descriptor, you should close both ends of the pipe. You get away with it here because ls generates data and terminates; you wouldn't in all circumstances.
The comment:
/* Set stdout to the input side of the pipe, and run 'ls'. */
is inaccurate; you're setting stdout to the output side of the pipe, not the input side.
You should have an error exit after the execv() calls; if they fail, they return, and the process can wreak havoc (for example, if the ls fails, you end up with two copies of wc running.
An SSCCE
Note the careful closing of both ends of the pipe in each of the processes. The parent process has no use for the pipe once it has launched both children. I left the code which closes filedes[1] early in place (but removed it from an explicit else block since the following code was also only executed if the else was executed). I might well have kept pairs of closes() in each of the three code paths where files need to be closed.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
int filedes[2];
int corpse;
int status;
pipe(filedes);
/* Run LS. */
pid_t pid = fork();
if (pid == 0)
{
/* Set stdout to the output side of the pipe, and run 'ls'. */
dup2(filedes[1], 1);
close(filedes[1]);
close(filedes[0]);
char *argv[] = {"ls", NULL};
execv("/bin/ls", argv);
fprintf(stderr, "Failed to execute /bin/ls\n");
exit(1);
}
/* Close the input side of the pipe, to prevent it staying open. */
close(filedes[1]);
/* Run WC. */
pid = fork();
if (pid == 0)
{
/* Set stdin to the input side of the pipe, and run 'wc'. */
dup2(filedes[0], 0);
close(filedes[0]);
char *argv[] = {"wc", NULL};
execv("/usr/bin/wc", argv);
fprintf(stderr, "Failed to execute /usr/bin/wc\n");
exit(1);
}
close(filedes[0]);
while ((corpse = waitpid(-1, &status, 0)) > 0)
printf("PID %d died 0x%.4X\n", corpse, status);
return(0);
}
Example output:
$ ./pipes-14312939
32 32 389
PID 75954 died 0x0000
PID 75955 died 0x0000
$

Resources