fork () and execlp () , printf before execlp() does not get executed - c

I m learning interprocess communication ...this is the code that bugs me
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int pfds[2];
pipe(pfds);
if (!fork())
{
printf("I m the child process\n");
close(1); /* close normal stdout */
dup(pfds[1]); /* make stdout same as pfds[1] */
close(pfds[0]); /* we don't need this */
execlp("ls", "ls", NULL);
}
else
{
printf("I m the parent process\n");
close(0); /* close normal stdin */
dup(pfds[0]); /* make stdin same as pfds[0] */
close(pfds[1]); /* we don't need this */
execlp("wc", "wc", "-l", NULL);
}
return 0;
}
These are some of the questions :-
1) I agree that after the execlp() , nothing gets executed , but my printf() statements are before the execlp() , then still why they does not get executed ??
2)The program acts as a pipe command in linux
hence it executes like "ls | wc -l" , but how does the system knows to execute the program like "ls | wc -l" and not "wc -l | ls" .. ??
3) I think the 2) question is because i have closed the stdout and used it as my pfds[1] and closed the stdin and used as my pads[0]..but what if the one threads gets exited before the other .. ??
4) (i m using Xcode as well as gcc) , when run the above program in gcc , it works well , but when run in Xcode it shows a "SIGTRAP" and returns "1" in the console
PLZ HELP ...
PS : It would be better if someone tell me how to see execution of the seperate threads in any general problem !!
thank you !!

Your printf is being executed. However, it only gets flushed after you redirect stdout, so its output is not shown on the terminal. Try fprintf(stderr, ...) if you want to see it on stderr, or fflush(stdout) before you start messing with file descriptors.
You attach the writing end of the pipe to the stdout of the ls process, and the reading end to stdin on wc, so there's no confusion.
I'm not sure what your concern is here. The ls process exits once it finishes listing the directory, and wc exits once it runs out of input and prints the number of lines. The pipe gets cleaned up automatically once both processes which hold an endpoint have closed it (by exiting).
Hard to say. What does the debugger show? The Xcode debugging environment is primarily aimed at developing and debugging desktop/mobile applications, though, so it's probably just a fluke of some sort.

Related

C program cannot use pipe to execute the "more" command in "execlp" in order to view program's output

thanks in advance for any help.
I am trying to replicate the behavior of the shell command ls -1 /usr/include | more using a C program.
I wrote this code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
int page[2]; // page is the name of my pipe
pipe(page);
switch (fork()){
case -1:
exit(1);
break;
case 0:;
close(page[1]);
dup2(page[0], 0);
close(page[0]);
execlp("more", "more", NULL);
default:
close(page[0]);
dup2(page[1], 1);
close(page[1]);
execlp("ls", "ls", "-1", "/usr/include", NULL);
break;
}
}
But it only prints one page (as more would do) and causes some weird behavior that blocks my terminal (forcing me to use reset to set it back to normal).
Your original code creates a kind of a race condition between the parent and the child processes, and the shell which started your program. The ls process ends before more can read all the data from the pipe, and since in your program the parent process is replaced by the ls process, when that ls process ends (after writing all its output into the pipe buffer) it exits, and in doing so closes the pipe and gives control back to the shell which will immediately get ready to read another command.
So, initially both more and the shell may be reading from the same TTY device (it's reading from its STDERR descriptor, still attached to your TTY), and then once more eventually gets some input it will try to read from the pipe again (its STDIN) and it will get an end-of-file (the pipe has been closed on the write end by the exit of ls), and so more will now also exit (without printing any more output). There's also a possible race between the more process and the shell as to which (re)sets the TTY driver modes and when.
An alternative implementation of your program is for the original parent process to start two child processes, one for more and one for ls, and then to wait for both processes to terminate, but this will of course require more system resources.
I just realized the roles of the parent and the child process were being mixed up. The one to run the more command should be the parent. Since more is an interactive command, the terminal will respond better to it as the parent (I'm guessing).
So to solve my issue, I switched the roles of the parent and the child.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
int page[2]; // page is the name of my pipe
pipe(page);
switch (fork()){
case -1:
exit(1);
break;
case 0:;
close(page[0]);
dup2(page[1], 1);
close(page[1]);
execlp("ls", "ls", "-1", "/usr/include", NULL);
break;
default:
close(page[1]);
dup2(page[0], 0);
close(page[0]);
execlp("more", "more", NULL);
}
}
WHY DOES THIS SOLVES THE PROBLEM? (I still do not clearly understand why it worked!)

Pipes in c linux to connect 3 processes to execute a command

My task is to write a C program that executes the command "ls -l /bin/?? | grep rwxr-xr-x | sort". There are 3 child processes where each of them executes one of the commands separately and sends the result through a pipe to the next child process. I'm using a Swedish modified verision of debian so the error message is in Swedish, but i'll translate the error i get, it's something along the lines of: sort: failed to status -: unknown fileidentifier.
Maybe it's my pipes that do not work as intended, I'm not too sure about the close() commands. I'm pretty sure the error comes from the pipes. Would be grateful if someone could run the program and get the english error message.
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
int main()
{
int ret;
int fds1[2], fds2[2], fds3[2];
char buf[20];
pid_t pid;
///initiating pipes
ret=pipe(fds1);
if(ret == -1){
perror("could not pipe");
exit(1);
}
ret=pipe(fds2);
if( ret == -1){
perror("could not pipe");
exit(1);
}
ret=pipe(fds3);
if (ret == -1){
perror("could not pipe");
exit(1);
}
pid=fork();
if(pid==-1){
fprintf(stderr,"fork failed");
exit(0);
}
if(pid==0){
///CHILD 1
close(1);
dup(fds1[1]);
close(fds1[0]);
close(fds1[1]);
close(0);
execlp("/bin/sh","bin/sh", "ls-l /bin/??", (char *)NULL);
}
else{
wait(0);
}
pid=fork();
if(pid==-1){
fprintf(stderr,"fork failed");
exit(0);
}
if(pid==0){
close(0);
dup(fds1[0]);
close(fds1[0]);
close(fds1[1]);
close(1);
dup(fds2[1]);
close(fds2[0]);
close(fds2[1]);
execlp("/usr/share/grep/", "grep", "rwxr-xr-x", NULL);
}
else{
wait(0);
}
close(fds1[0]);
close(fds1[1]);
pid=fork();
if(pid==-1){
fprintf(stderr,"fork failed");
exit(0);
}
if(pid==0){
close(0);
dup(fds2[0]);
close(fds2[0]);
close(fds2[1]);
execlp("sort", "sort", NULL);
}
else{
wait(0);
}
close(fds2[0]);
close(fds2[1]);
}
Your code has several problems, but before I discuss them, let me introduce you to a flavor of one of my favorite preprocessor macros:
#define DO_OR_DIE(x, s) do { \
if ((x) < 0) { \
perror(s); \
exit(1); \
} \
} while (0)
Using this macro where it is applicable can clarify your code by replacing all the boilerplate error checking. For example, this:
ret=pipe(fds1);
if(ret == -1){
perror("could not pipe");
exit(1);
}
becomes just
DO_OR_DIE(pipe(fds1), "pipe");
That makes it a lot easier to see and focus on the key parts of the code, and it's easier to type, too. As a result, it also reduces the temptation to skip error checks, such as those for your calls to dup().
Now, as to your code. For me, it exhibits not just the one misbehavior you now describe in your question, but three:
It emits an error message "bin/sh: ls-l /bin/??: No such file or directory".
It emits the error message you describe, "sort: stat failed: -: Bad file descriptor".
It does not terminate.
The first error message pertains to multiple problems in the arguments to your first execlp() call. If you want to launch a shell and specify a command for it to run, as opposed to a file from which to read commands, then you must pass the -c option to it. Additionally, you've omitted mandatory whitespace between the ls and its arguments. It looks like you want this:
execlp("/bin/sh","sh", "-c", "ls -l /bin/??", (char *)NULL);
Setting aside the second problem for the moment, let's turn to the failure to terminate. You have several problems in this area, falling into these categories:
Holding pipe ends open where you should ensure them closed
Calling wait() at the wrong points
When you set up a pipe between two processes, you generally want to make sure that there are no open file descriptors on either end of the pipe other than one on the write end held by one process, and one on the read end held by the other process. Each end should be open exactly once, in exactly one process. Since the processes being connected invariably inherit these file descriptors from their parent, it is essential that the parent close its copies (except that the parent will want to keep one open in the event that it itself is one of the communicating processes).
The process on the read end of a pipe will not see EOF on that pipe until all open file descriptors on the write end are closed. Child processes running programs such as grep and sort that read their input to its end will hang indefinitely if the write end of the pipe is not completely closed.
That can be a particularly perverse problem when the child reading the pipe also has a copy of the write end of that pipe, unused, or if one of its siblings does.
Additionally, the whole point of a pipeline is that the processes involved run concurrently. If you wait() after starting one before starting the next, then at minimum you prevent such concurrency. Worse, however, that can also cause your program to hang, because a pipe has finite buffer capacity. If the child is writing output to a pipe, but no one is reading it, then the pipe's buffer can fill to capacity, at which point the child blocks. If the parent is waiting for the child to finish before launching the process that will drain the pipe, then you have a deadlock. Therefore, you should start all the processes in the pipeline first, then wait for them all.
Having fixed such problems in your code, I find that the program emits a different error for me:
execlp: No such file or directory
(The specifics of this message derive from the nature of my fixes.) This should be especially concerning, because if execlp() fails then it returns in the process in which it was called. In your cases, control will then fall right out of your if statement, into the code intended only for the parent to execute. For this reason, it is essential to handle errors from execlp(). At minimum, add a call to exit() or _Exit() immediately after.
But what's failing? Well, it's the grep this time. Note that you specify the command to execute as "/usr/share/grep/" -- that trailing / is erroneous, and the path itself is suspect. On my system, the correct path is /usr/bin/grep, but since we're using execlp, which resolves the executable in the path, we might as well omit the path altogether:
execlp("grep", "grep", "rwxr-xr-x", (char *) NULL);
Et voilĂ ! After making that correction as well, your program runs for me.
Additional advice: do not use dup() when you care what file descriptor number you want the duplicate to have, such as when you're trying to dup onto one of the standard streams. Use dup2() for that, which has the additional advantage that you don't need to close the specified file descriptor first.

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).

Using processes to run commands with pipes

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);

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.

Resources