execve grep process never exit - c

The following code simulate the pipe and grep operation by forking process and using execve system call. The output seems fine, however, the grep process seems never exit (still running in the back) until the whole process ends. What's the problem? It is abnormal since using grep in shell always exit.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(int argc, char *argv[], char *env[]) {
char ch[32];
while (1) {
scanf("%s", ch);
int pd[2];
if (pipe(pd) == -1) {
perror("Pipe failed");
exit(1);
}
int childPid, pid;
if ((childPid = fork()) < 0) {
perror("fork failed\n");
exit(1);
} else {
// parent process
if (childPid) {
int status;
wait(&status);
// print exit code of child process
printf("Exit code %d\n", status);
}
// child process, execute command
else {
// fork a child
if ((pid = fork()) < 0) {
perror("fork failed\n");
exit(1);
}
if (pid) { // parent as pipe WRITER
close(pd[0]);
close(1);
// replace input with pipe
dup(pd[1]);
char* cmds[] = { "/bin/cat", "aa", 0 };
execve(cmds[0], cmds, env);
exit(0);
} else { // child as pipe READER
close(pd[1]);
close(0); // close its READ end
dup(pd[0]);
char* cmds[] = { "/bin/grep", "rw", 0 };
execve(cmds[0], cmds, env);
exit(0); // never been here
}
}
}
}
return 0;
}
Here is the output I monitor the process before and after running this program once.
hengstar#ubuntu:~$ ps -ef | grep "grep"
hengstar 58073 58038 0 01:43 pts/26 00:00:00 grep --color=auto grep
hengstar#ubuntu:~$ ps -ef | grep "grep"
hengstar 58075 1886 0 01:43 pts/11 00:00:00 /bin/grep drw
hengstar 58077 58038 0 01:43 pts/26 00:00:00 grep --color=auto grep

The exec family of functions never returns. Their purpose is to load a new program to replace the current program running in the process.
If an exec function returns, it means there was an error.

I laughted so hard I myself when I found this! Did you noticed that while(1) above? Well, everything is okay, except for that! Without it, your program works as you describe it to.
BTW: It happens that if a process's parent dies, the child becomes adopted by init, a.k.a (pid_t)1. That was happening with your code with while(1).

Related

using fork() , execlp() and dup2()

I am writing a C program which will run Linux commands, like:
ls -al | grep "main" | sort
I used just the children to do that. (it work when I use the parent to execute the last one but I want to use just the children )
I have a problem that is the last fork execute before the second fork.
and that will run this command ls -al | sort | grep "main"
in this example the result is the same but with others is wrong.
how can I wait the second fork in the last fork ?.
code :
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#define READ 0
#define WRITE 1
int main(int argc, char *argv[])
{
pid_t pid;
int fd[2],fd1[2];
pipe(fd);pipe(fd1);
pid = fork();
if (pid == 0)
{
close(fd1[READ]);close(fd1[WRITE]);close(fd[READ]);
printf("i'm the child used for ls \n");
dup2(fd[WRITE], STDOUT_FILENO);
close(fd[WRITE]);
execlp("ls", "ls", "-al", NULL);
}
else
{
pid = fork();
if (pid == 0)
{
close(fd1[READ]);close(fd[WRITE]);
printf("i'm in the second child, which will be used to run grep\n");
dup2(fd[READ], STDIN_FILENO);
close(fd[READ]);
close(fd1[READ]);close(fd[WRITE]);
dup2(fd1[WRITE], STDOUT_FILENO);
close(fd1[WRITE]);
execlp("grep", "grep", "main", NULL);
}
else
{
pid = fork();
if (pid == 0)
{
/* code */
close(fd[READ]);close(fd[WRITE]);close(fd1[WRITE]);
printf("i'm in the third child, which will be used to run (sort)\n");
dup2(fd1[READ], STDIN_FILENO);
close(fd1[READ]);
execlp("sort", "sort", NULL);
}
}
}
return 0;
}

redirection using dup2 command

I know this is a simple exercise but I'm having troubles with it.
ps | sed 1d | wc -l
I am trying to generate an output of the above terminal command using c code for my school exercise. I have written a code that is redirecting all the inputs and outputs to the required destination but I am not getting any output on the console as I haven't redirected the final output in any file so it must be displayed on console.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/file.h>
#define READ 0
#define WRITE 1
int main()
{
//fd fd1
//ps | sed 1d | wc -l
pid_t pid;
pid_t pid1;
pid_t pid2;
int fd[2];
int fd1[2];
pipe(fd);
pipe(fd1);
pid=fork();
if(pid==0)
{ //redirecting into sed 1d
close(fd[READ]);
dup2(fd[WRITE],1);
close(fd[WRITE]);
execlp("ps","ps",NULL);
}
else
{
pid1=fork();
if(pid1==0)
{ //redirecting into wc -l
close(fd[WRITE]);
dup2(fd[READ],0);
close(fd[READ]);
close(fd1[READ]);
dup2(fd1[WRITE],1);
close(fd1[WRITE]);
execlp("sed","sed","1d",NULL);
}
else
{
pid2=fork();
if(pid2==0)
{ //must display on console
close(fd1[WRITE]);
dup2(fd1[READ],0);
close(fd1[READ]);
execlp("wc","wc","-l",NULL);
}
else
{
close(fd[READ]);
close(fd[WRITE]);
close(fd1[READ]);
close(fd1[WRITE]);
wait(NULL);
}
}
}
}
What can be the problem?
The ps process has both ends of the fd1 pipe open, which it shouldn't. Also, the wc -l process has both ends of the fd pipe open, which it shouldn't. Fix both of those things and your program will stop hanging. Also, wait(NULL) only waits for one child process to terminate, not all of them, so you're risking the terminal prompt reappearing before your program is actually done.

How to terminate child process when another child process finishes?

I have a code segment that looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
pid_t first, last;
last = fork();
if (last == 0) {
char *args[] = { "./last", NULL };
char *env[] = { NULL };
execve("./last", args, env);
_exit(1);
} else if (last == -1) {
fprintf(stderr, "Error: failed to fork last.\n");
exit(1);
}
first = fork();
if (first == -1) {
fprintf(stderr, "Error: failed to fork first.\n");
exit(1);
} else if (first > 0) {
int status;
waitpid(first, &status, 0);
} else {
char *args[] = { "./first", NULL };
char *env[] = { NULL };
execve("./first", args, env);
_exit(1);
}
return 0;
}
This works fine in a sense that I can see processes being invoked and running. However, my issue is that process last has an infinity loop, and when process first terminates, it still stays running. Is there a way in C to force process last here to terminate also when process first finishes?
kill() should be of interest. From the man page of kill():
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
Since fork() returns the child PID in the parent, you can use that to call kill(). Here is the modified code and test run.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h> // Modification 1
int main(int argc, char *argv[]) {
pid_t first, last;
last = fork();
if (last == 0) {
char *args[] = { "./last", NULL };
char *env[] = { NULL };
execve("./last", args, env);
_exit(1);
} else if (last == -1) {
fprintf(stderr, "Error: failed to fork last.\n");
exit(1);
}
first = fork();
if (first == -1) {
fprintf(stderr, "Error: failed to fork first.\n");
exit(1);
} else if (first > 0) {
int status;
waitpid(first, &status, 0);
kill(last, SIGINT); // Modification 2
} else {
char *args[] = { "./first", NULL };
char *env[] = { NULL };
execve("./first", args, env);
_exit(1);
}
return 0;
}
Terminal Session (before):
$ ls test first last
first last test
$ ./test
$ ps aux | grep last
root 165130 0.0 0.0 2136 752 pts/3 S 16:58 0:00 ./last
root 165135 0.0 0.0 6136 892 pts/3 S+ 16:58 0:00 grep last
Terminal Session (after):
$ ls test first last
first last test
$ ./test
$ ps aux | grep last
root 165240 0.0 0.0 6136 836 pts/3 S+ 17:01 0:00 grep last
Regarding which signal to be passed: anyone whose default action is termination. You can find more from the signal man page. Since I don't know what is exactly in the last executable, I assume that there is no signal handler registered for SIGINT and hence when last gets SIGINT, the program is terminated by default.
One way to do that is by sending a SIGTERM or SIGKILL signal to the first process, using the function kill()
An example:
kill(first, SIGTERM);
When you send that signal, if you want, you can make some "clean up" on that process. To do that you need to capture and hadle the signal. In that case i would recommend the use of SIGTERM.
For signal handling, take a look at this.

forks and pipes, what am I doing wrong?

I'm trying to learn about pipes and redirections. For this purpose I'm doing some little programs to get used to the related system calls. On this one, I'm trying to launch cat on a file, pipe4.c, and pipe its output to grep that I launch after. It doesn't work and I don't understand the results,
I though that the logic was good but I'm clearly missing something with fork. What am I doing wrong?
Here's the code:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFF_SIZE 4092
//redirecting the output of a program ('cat' here)
//to the stdin of another program ('grep' here)
void err_handler(char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
//creating a pipe to communicate between the child processes
int p[2];
if (pipe(p) < 0)
err_handler("pipe error: ");
/*
** forking for cat
*/
pid_t cat_pid;
if ((cat_pid = fork()) < 0)
err_handler("fork error: ");
if (cat_pid)
close(p[1]);
if (!cat_pid) {
printf("===CAT===\n");
dup2(p[1], STDOUT_FILENO);
close(p[0]);
close(p[1]);
execl("/bin/cat", "cat", "pipe4.c", NULL);
exit(EXIT_SUCCESS);
}
/*
** forking for grep
*/
pid_t grep_pid;
if ((grep_pid = fork()) < 0)
err_handler("fork_error: ");
if (grep_pid)
wait(&grep_pid);
if (!grep_pid) {
printf("===GREP===\n");
dup2(p[0], STDIN_FILENO);
close(p[0]);
close(p[1]);
execl("/bin/grep", "grep", "\"err_handler\"", NULL);
exit(EXIT_SUCCESS);
}
return 0;
}
I only get this on my terminal:
> pom#parrot ~/dev/19/syscall> sudo ./a.out
> ===GREP===
> ===CAT===
> ===GREP===
The order in which those lines are printed changes each time I execute it.
What I expect is obviously all the lines containing "err_handler" in my pipe4.c file, like if I did it directly in the shell:
> pom#parrot ~/dev/19/syscall> cat pipe4.c | grep "err_handler"
> void err_handler(char *msg) err_handler("pipe error: ");
> err_handler("fork error: "); err_handler("creat error: ");
> err_handler("read error: ");
> err_handler("write error:");
There are (I think!) 3 major issues.
1) you are not using wait correctly. I would advise using waitpid instead.
2) at least on my system, /bin/grep does not exist. It is /usr/bin/grep. But since you are returning EXIT_SUCCESS when exec fails, you aren't seeing an error. You should replace execl (...); exit(EXIT_SUCCESS), with execl(...); perror("execl"); exit(EXIT_FAILURE);
3) You are searching for literal quotes. It is as if you ran:
grep '"err_handler"'
That is, the argument to execl when you spawn grep should be "err_handler", not "\"err_handler\""

Create child using fork() inside for loop to run execlp()

I am writing a C program which will run Linux commands, like:
$ cat /etc/passwd | cut -f1 -d: | sort
The idea is to create child process using fork() to run the commands using execlp(). I planned to use two pipes for the communication and direct the input-output using dup().
The output is wrong:
ls -l | wc -c on command returns 1746
the program returns 1761
The code(edited to reflect suggestions):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
int main()
{
int i,fd1[2],status,listpid[2];
pid_t child;
pipe(fd1);
for(i=0; i< 2; i++)
{
printf("\ncreating child\n");
if((child = fork()) == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(child == 0)
{
if(i == 0)
{
close(1); dup(fd1[1]);
close(fd1[0]);
close(fd1[1]);
printf("\nrunning ls -l\n");
fflush(stdout);
execlp("ls","ls","-l", (char *)NULL);
exit(EXIT_SUCCESS);
}
else if(i == 1)
{
close(0); dup(fd1[0]);
close(fd1[1]);
close(fd1[0]);
printf("\nrunning wc -c\n");
fflush(stdout);
execlp("wc","wc","-c", (char *)NULL);
exit(EXIT_SUCCESS);
}
}
else
{
listpid[i]=child;
}
}
close(fd1[0]);
close(fd1[1]);
for(i = 0; i < 2; i++)
{
waitpid(listpid[i], &status, 0);
if(WIFEXITED(status))
{
printf("\n[%d] TERMINATED (Status: %d)\n",listpid[i], WEXITSTATUS(status));
}
}
exit(EXIT_SUCCESS);
}
First you can't waitpid in the loop -- if the output of ls is big enough it ill fill the pipe, and so it won't finish until someone reads it: you must wait for both children after the for loop.
Second -- the wc will go on for as long as the other end of the pipe is open, ie you will have to close the pipe in the parent as well.
After your update, the two child processes are behaving correctly. However, you still need to add:
close(fd1[0]);
close(fd1[1]);
between the for loop that launches the children and the for loop that collects the exit statuses.
Because the write end of the pipe is still open, wc does not receive EOF, so it doesn't terminate, so your process is waiting indefinitely.

Resources