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);
}
Related
I am new to pipes but how do I redirect the output from child_1 to the input for child_2?
I am trying to pass the value from the parent to child_1, adds 1 to the value, print the value, then use that output and pass it into child_2, add 1 again, and finally print the value.
The code below has the right output value for child_1, but not for child_2, how do I redirect the output from child_1 to the input for child_2?
Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char * argv[]) {
int fd[2];
int PID;
pipe(fd); //fd1[0] = read | fd1[1] = write
PID = fork(); // spawn child_1
if (PID < 0){ // failed to fork
perror("Unable to fork child");
exit(1);
}
if (PID > 0) { // parent
int value = 100;
// since parent is only writing, close the reading end of pipe
close(fd[0]);
// write the data to the write end of the pipe
write(fd[1], &value, sizeof(int));
// then close the writing end of the pipe (parent)
close(fd[1]);
/**********************************************************/
} else { // child 1
int val = 0;
// read from the parent pipe
read(fd[0], &val, sizeof(int));
val += 1;
// is this how to redirect from one pipe to another?
dup2(fd[0], fd[1]);
// this prints the right value for val (val [101] = value [100] + 1)
printf("Child [%d] read value %d\n", getpid(), val);
// close the reading end of the pipe for child_1
close(fd[0]);
int PID2 = fork(); // make child 2
if(PID2 == 0) { // child 2
int val2 = 0;
close(0); // close stdin since we are trying to take the value from child_1
// read input from child_1 pipe (NOT WORKING?)
read(fd[0], &val2, sizeof(int));
val2 += 1;
printf("Child [%d] out %d\n", getpid(), val2);
close(fd[0]);
}
}
return EXIT_SUCCESS;
}
The way you have things set up, there's no need to use dup2() or any other I/O redirection.
Add #include <unistd.h> to the list of include files (and remove #include <string.h> — it seems to be unused)
Delete: dup2(fd[0], fd[1]);
Delete: close(fd[0]);
Delete: close(0);
Before the second fork(), add write(fd[1], &val, sizeof(val));
When you have close(fd[0]) in the first child, you effectively close fd[0] for the second child too.
You should check the status of the read and write operations before using the results.
Those changes lead to:
/* SO 7383-1815 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
int fd[2];
int PID;
pipe(fd);
PID = fork();
if (PID < 0)
{
perror("Unable to fork child");
exit(EXIT_FAILURE);
}
if (PID > 0)
{
int value = 100;
close(fd[0]);
write(fd[1], &value, sizeof(int));
close(fd[1]);
}
else
{
int val = 0;
if (read(fd[0], &val, sizeof(val)) != sizeof(val))
{
perror("read() failed in child 1");
exit(EXIT_FAILURE);
}
val += 1;
printf("Child [%d] read value %d\n", getpid(), val);
if (write(fd[1], &val, sizeof(val)) != sizeof(val))
{
perror("write() failed in child 1");
exit(EXIT_FAILURE);
}
int PID2 = fork();
if (PID2 == 0)
{
int val2 = 0;
if (read(fd[0], &val2, sizeof(val2)) != sizeof(val2))
{
perror("read() failed in child 2");
exit(EXIT_FAILURE);
}
val2 += 1;
printf("Child [%d] out %d\n", getpid(), val2);
close(fd[0]);
close(fd[1]);
}
}
return EXIT_SUCCESS;
}
When compiled (cleanly with options set fussy), it produces output such as:
Child [34520] read value 101
Child [34521] out 102
I believe this is what was wanted.
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
int main() {
int p[2];
pipe(p);
if (fork() == 0) {
// child
/*#0*/ close(p[1]);
int received = -1;
while (read(p[0], &received, 4) != 0) {
printf("receive integer: %d\n", received);
received = -1;
}
printf("child exit\n");
exit(0);
} else {
// parent
/*#1*/ close(p[0]);
int sent = 42;
write(p[1], &sent, 4);
/*#2*/ close(p[1]);
printf("wait for child\n");
wait(0);
}
printf("finished\n");
}
I'm trying to understand fork and pipe in C. This program fork a child process, which receive an integer from parent process then exit when pipe closed. When executing, it prints
wait for child
receive integer: 42
child exit
finished
Yet the while loop got stuck after close(p[1]); at position #0 removed: that read would infinitely wait for an incoming variable from the pipe and never detect the pipe closed.
Can someone explain to me why p[1] has to be closed by both parent (position #2) and child (position #0) process?
Here is the code (from Linux manual page) with comments at the bottom of the code.
https://man7.org/linux/man-pages/man2/pipe.2.html
At /#2/ close(pipefd[1]), the comment states that "Reader will see EOF". It means there is nothing to read into child process anymore and then the statement "read(p[0], &received, 4)" will return 0. In the Linux manaul page https://man7.org/linux/man-pages/man2/read.2.html
states that "On success, the number of bytes read is returned (zero indicates end of file)"
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else {/* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
/*#2*/ close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
I'm trying to write a c program that is the equivalent of the linux command ps -aux | sort -r -n -k 5 but I'm not getting any output
Here's my code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char ** argv){
int pipes[2];
int r;
r = pipe(pipes);
if (r < 0) {
fprintf(stderr, "pipe failed\n\n"); // stderr is a FILE* variable for the standard error file (terminal)
exit(2);
}
int saved_stdout = dup(1);
int pid = fork();
if(pid > 0){
// Parent
pid = fork();
if(pid > 0){
// Parent
wait(NULL);
}else if (pid == 0){
// Child 1
printf("Child 1\n");
dup2(pipes[1], 1);
close(pipes[0]);
close(pipes[1]);
execlp("/bin/ps", "ps", "-aux", (char*) NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
}else if (pid == 0){
// Child 2
printf("Child 2\n");
dup2(pipes[0], 0);
close(pipes[0]);
close(pipes[1]);
dup2(saved_stdout, 1);
close(saved_stdout);
execlp("/bin/sort", "sort", "-r", "-n", "-k", "5", (char*)NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
wait(NULL);
printf("Exiting parent\n");
}
The output I get is this
Child 1
Child 2
Exiting parent
I doesn't actually print the execlp command, I've tried saving stdout to variable saved_stdout which is the solution I found in another answer, but that doesn't seem to work.
How can I redirect stdout back to the terminal?
Strange my output with your code is:
Child 1
Child 2
and the program don't stop. Or you sure that your output is valid ?
Whatever, your problem is that you don't close your pipe in your parents. Just add:
close(pipes[0]);
close(pipes[1]);
In your both parents (before your two call to wait()).
Plus saved_stdout is useless in your case, because you only change stdout in your child1. saved_stdout and 1 describe the same file in your child2.
This is my code.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
int file,parentID,childID;
pid_t pid;
int main(int argc, char *argv[])
{
if( argc != 2 )
{
printf("ERROR ! You have not write an argument\n");
printf("ERROR ! You give more than one argument");
return 1;
}
file = open(argv[1], O_RDONLY); //open file
if(file<0) //test the file
{
printf("Error open file\n");
printf("ERROR : %s\n", strerror(errno));
return 1;
}
// ---------------------------------------------------------------------
pid = fork();
if( pid == -1) //error fork
{
printf("Error fork\n");
return 1;
}
if(pid == 0) // child process
{
childID = getpid();
printf("Child process %d\n",childID);
// if(childID %2 == 1)
// {
// parentID = getppid();
// printf("Process of father of this child= %d\n",parentID);
// }
}
if( pid == 1)
{
parentID = getppid();
printf("ParentProcess %d\n",parentID);
}
}
I have to write a program to create a child process.Depending on the parity of the child process , the parent should transmit to child a message through a file , the message being taken over and showed by the child process( if the child process is a number that is divizible with 2 it will say -"Good morning!" else "Good night!" ).The parent should wait for the final execution of the child to terminate.
I'm trying really hard to do this exercise and i can't find anywere to explain me how or what function/structure object should i use to do this.Above i tried but i failed , and i understand somehow how fork does but... please help me with this code , or suggest me were should i go to read to make this exercise .Sorry for my bad english spelling.
What documentation are you using for the system calls?
There are a number of ways to do this, but what you probably want to do is create a pipe, and then fork the process. Since a fork copies everything, and child processes inherit the environment, each process has a copy of the file descriptors for the pipe. You can then read/write based on the return value of fork().
int main(int argc, char *argv[])
{
int fd[2];
char in[128], out[128];
if( argc != 2 )
{
printf("ERROR ! You have not write an argument\n");
printf("ERROR ! You give more than one argument");
return 1;
}
if (pipe(fd) == -1)
return 1;
// ---------------------------------------------------------------------
pid = fork();
if (!pid)
read(fd[0], in, 128);
else
write(fd[1], out, strlen(out) + 1);
pipe(2)
note, you usually want to close the file descriptor you're not using for one way communication
I think this is the code.
#include <stdio.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void sighandler (int sig)
{
if (sig == SIGUSR1)
{
FILE *f;
char line[100];
f = fopen("file","r");
fgets(line, 100, f);
printf ("Procesul copil cu pid %d a primit mesajul %s", getpid(), line);
fclose(f);
}
}
int main ()
{
pid_t pid;
FILE * f;
pid = fork();
if (pid < 0)
{
perror ("Eroare la fork()");
return (1);
}
else
if (pid == 0)
{
signal (SIGUSR1, sighandler);
pause();
return 0;
}
else
{
if (pid % 2 == 0)
{
printf ("Notificam procesul fiu cu pid %d", pid);
f = fopen ("file","w");
fprintf (f,"Good morning!");
fclose(f);
kill (pid, SIGUSR1);
}
else
{
printf ("Notificam procesul fiu cu pid %d", pid);
f = fopen ("file","w");
fprintf (f,"Good night!");
fclose(f);
kill (pid, SIGUSR1);
}
}
wait(NULL);
return 0;
}
Hi i'm trying to build a shell on linux and i'm stuck with the pipelining part.First i take the inputs from the user like "ls | sort" then when i try to run the program it lookls like the commands ls and sort doesnt work
It looks like i've done everything right but it still cant seem to work. can you help please. thanks in advance
include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <fcntl.h>
#include <sys/stat.h>
#define CREATE_FLAGS (O_WRONLY | O_CREAT | O_APPEND)
#define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int setup();
int main(int argc, const char * argv[])
{
while(1)
{
printf("333sh: ");
if(setup())
break;
}
return 0;
}
int setup(){
char input [128];
char *arg[32];
int i = 1;
while(fgets(input,128,stdin)!=NULL)
{
arg[0] = strtok(input," \n");
while((arg[i]=strtok(NULL," \n")) != NULL){
i++;
}
if (arg[1]!=NULL && strcmp(arg[1],"|")==0 && arg[2]!=NULL ){
pid_t pid;
int fd[3];
pipe(fd);
pid=fork();
if(pid<0){
printf("fork");
}
else if(pid==0){
pid_t cpid;
cpid=fork();
if(cpid==0){
dup2(fd[2], 1); // Replace stdin with the read end of the pipe
close(fd[0]); // Don't need another copy of the pipe read end hanging about
close(fd[2]);
execvp(arg[0],arg);
}
else if(pid>0){
dup2(fd[0], 0); // Replace stdout with the write end of the pipe
close(fd[0]); //close read from pipe, in parent
close(fd[2]); // Don't need another copy of the pipe write end hanging about
execvp(arg[2], arg);
}
}
else if(pid>0){
waitpid(pid, NULL,0);
}
}
}
}
Your biggest problem is that your argument lists for your commands are malformed (after you've resolved the index 2 vs index 1 issue with the pipe file descriptors diagnosed by Ben Jackson in his answer).
I added a function:
static void dump_args(int pid, char **argv)
{
int i = 0;
fprintf(stderr, "args for %d:\n", pid);
while (*argv != 0)
fprintf(stderr, "%d: [%s]\n", i++, *argv++);
}
and called it just before the calls to execvp(), and the output I got was:
$ ./ns
333sh: ls | sort
args for 29780:
0: [ls]
1: [|]
2: [sort]
ls: sort: No such file or directory
ls: |: No such file or directory
^C
$
The control-C was me interrupting the program. The arguments for each command must be 'the command name' (conventionally, the name of the executable), followed by the remaining arguments and a null pointer.
Your tokenization code is not providing two correct commands.
You also have a problem with which PID you're looking at:
cpid = fork();
if (cpid == 0)
{
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), arg);
execvp(arg[0], arg);
fprintf(stderr, "Failed to exec %s\n", arg[0]);
exit(1);
}
else if (pid > 0) // should be cpid!
{
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
dump_args(pid, arg);
execvp(arg[1], arg);
fprintf(stderr, "Failed to exec %s\n", arg[1]);
exit(1);
}
You also need to close the pipe file descriptors in the parent process before waiting.
This code compiles and 'works' for simple x | y command sequences such as ls | sort or ls | sort -r. However, it is far from being a general solution; you'll need to fix your argument parsing code quite a lot before you reach a general solution.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int setup(void);
int main(void)
{
while (1)
{
printf("333sh: ");
if (setup())
break;
}
return 0;
}
static void dump_args(int pid, char **argv)
{
int i = 0;
fprintf(stderr, "args for %d:\n", pid);
while (*argv != 0)
fprintf(stderr, "%d: [%s]\n", i++, *argv++);
}
int setup(void)
{
char input[128];
char *arg[32];
int i = 1;
while (fgets(input, sizeof(input), stdin) != NULL)
{
arg[0] = strtok(input, " \n");
while ((arg[i] = strtok(NULL, " \n")) != NULL)
{
i++;
}
if (arg[1] != NULL && strcmp(arg[1], "|") == 0 && arg[2] != NULL)
{
pid_t pid;
int fd[2];
arg[1] = NULL;
pipe(fd);
pid = fork();
if (pid < 0)
{
fprintf(stderr, "fork failed\n");
return 1;
}
else if (pid == 0)
{
pid_t cpid = fork();
if (cpid < 0)
{
fprintf(stderr, "fork failed\n");
return 1;
}
else if (cpid == 0)
{
printf("Writer: [%s]\n", arg[0]);
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), arg);
execvp(arg[0], arg);
fprintf(stderr, "Failed to exec %s\n", arg[0]);
exit(1);
}
else
{
printf("Reader: [%s]\n", arg[2]);
assert(cpid > 0);
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), &arg[2]);
execvp(arg[2], &arg[2]);
fprintf(stderr, "Failed to exec %s\n", arg[2]);
exit(1);
}
}
else
{
close(fd[0]);
close(fd[1]);
assert(pid > 0);
while (waitpid(pid, NULL, 0) != -1)
;
}
}
}
return 1;
}
You're using fd[0] and fd[2] but pipe(fd) only sets fd[0] and fd[1].
Couple of immediate problems:
setup() has no return value, but you expect an int
The definition of fgets is:
char * fgets ( char * str, int num, FILE * stream );
Get string from stream
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.
fgets() returns NULL on an error; otherwise it returns a pointer to str. So this seems like a very unsound test condition in your while loop.