I'm trying to exec()a child process from a parent process. In this child process, I ask the user to enter a message so it can be printed out by the parent process but I can't find a way to do it...
So far, my code is :
parent.c
int main(int argc, char **argv) {
int fd[2];
char line[80];
pipe(fd);
pid_t pid = (pid_t)fork();
if(pid > 0) {
waitpid(pid, NULL, 0);
close(fd[0]);
int size = read(fd[1], line, 79);
close(fd[1]);
line[size] = '\0';
printf("[parent] Received \"%s\", size = %d\n", line, size);
}
else {
close(fd[1]);
close(stdin);
dup2(fd[0], stdin);
close(fd[0]);
exec("./child", 0, NULL);
}
return 0;
}
child.c
int main(int argc, char **argv) {
char line[80];
printf("[child] Enter message: ");
gets(line, 80);
printf("[child] line = %s\n", line);
return 0;
}
When I launch the parent process, it displays [child] Enter message: but when I try to type something, nothing appears, even if I hit the return key.
Do you know how can I make it work ?
Thank you for your help.
Besides the problems mentioned in my comments, the problem you are experiencing is a deadlock. You get that because the parent process waits for the child process to exit. But the child process is waiting for input that never arrives.
That's because in the child process you say that the input should come from the pipe.
Furthermore in the parent process you attempt to read from the write-end of the pipe.
Finally, your program will never work as long as the child process is wanting to read user-input, because all user-input will go to the parent process.
To make it all work, you need to rethink your design, and to make the parent process the one that reads input from the user, and writes to the pipe. And the child process should read from the pipe and print to (the non-piped) standard output. Or you close the (normal, non-piped) standard input in the parent, and in the child you write to the pipe (as standard output).
Your code has several problems:
you mix integer (low level) file descriptors, and (high level) FILE * constants.
you invert stdin and stdout as well as the order of the descriptors obtained through pipe
you write a message on stdout when you intend to redirect it (should use stderr)
Here is a fixed version (I changed your gets by fgets, as well as your exec with execl to have everything to compile):
parent.c:
int main(int argc, char **argv) {
int fd[2];
char line[80];
pipe(fd);
pid_t pid = (pid_t)fork();
if(pid > 0) {
waitpid(pid, NULL, 0);
close(fd[1]);
int size = read(fd[0], line, 79);
close(fd[0]);
line[size] = '\0';
printf("[parent] Received \"%s\", size = %d\n", line, size);
}
else {
close(fd[0]);
close(1);
dup2(fd[1], 1);
close(fd[1]);
execl("./child", NULL);
}
return 0;
}
child.c
int main(int argc, char **argv) {
char line[80];
fprintf(stderr, "[child] Enter message: ");
fgets(line, 80, stdin);
printf("[child] line = %s\n", line);
return 0;
}
I've found a workaround. My actual goal was to pass information between parent and child. My error was to dup2 on stdin. By doing this, I couldn't enter anything when the program asked. So I passed the value of the file descriptors in the argv and my problem was solved.
Thanks again for your help !
Related
I am trying to create a simple pipe/fork function, so that child process modifies the value of text, then it is printed by the parent.
I have checked a similar question on Modify variable in child process, but I am unable to print the text variable in the parent.
int main()
{
pid_t childp;
char text[100];
pipe(text);
childp = fork();
if (childp ==0){
strncpy(text, "Hello world", 100); // child running
}
else{
printf("%s\n", text); // parent prints "Hello world"
}
return 1;
}
Any help is appreciated (I am very new to C language)
look into this website :
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
int fd[2], nbytes;
pid_t childpid;
char string[] = "Hello, world!\n";
char readbuffer[80];
pipe(fd);
if((childpid = fork()) == -1)
{
perror("fork");
exit(1);
}
if(childpid == 0)
{
/* Child process closes up input side of pipe */
close(fd[0]);
/* Send "string" through the output side of pipe */
write(fd[1], string, (strlen(string)+1));
exit(0);
}
else
{
/* Parent process closes up output side of pipe */
close(fd[1]);
/* Read in a string from the pipe */
nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
printf("Received string: %s", readbuffer);
}
return(0);
}
It explain how to properly use C pipes. Take a look to this too.
Add this for error handling :
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe Failed");
return 1;
}
The closest to your code working example I can come up with.
int main()
{
pid_t childp;
char text[100];
int fd[2];
pipe(fd);
childp = fork();
if (childp ==0){
FILE *f=fdopen(fd[1], "w"); // Write into this "file" what you want the other end to read.
fprintf(f, "Hello world\n");
}
else{
FILE *f=fdopen(fd[0], "r"); // We can read from this "file"
fgets(text, 100, f); // Read one line of text from the "file" up to 100 bytes
printf("read <%s> from by new child\n", text)
}
return 1;
}
Note again that this is not a shared buffer. So you need both end to agree on a "protocol". Because everything that is written by one end must be read by the other (otherwise the "write" instruction will be blocked), and everything that is read by one end, must be writter by the other (otherwise the "read" instruction will be blocked).
So, either you use a fixed size message, for example. If you choose 100, you need to write 100 bytes exactly at one end (fill with 0 if needed), and read 100 bytes exactly at the other.
Or you find some protocol so that the reading end knows exactly when to stop reading.
I choose the latter (because it is the closest to your code). By using fgets to read, that stop to read at each newline, and fprintf a message ended by a newline at the writing end.
I am learning something about PIPE in Linux, but I met something I can't figure out. I was reading rozmichelle's blog http://www.rozmichelle.com/pipes-forks-dups/#pipelines. The code below is to sort three words that parent process passes on to child process by PIPE.
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2]; // an array that will hold two file descriptors
pipe(fds); // populates fds with two file descriptors
pid_t pid = fork(); // create child process that is a clone of the parent
if (pid == 0) { // if pid == 0, then this is the child process
dup2(fds[0], STDIN_FILENO); // fds[0] (the read end of pipe) donates its data to file descriptor 0
close(fds[0]); // file descriptor no longer needed in child since stdin is a copy
close(fds[1]); // file descriptor unused in child
char *argv[] = {(char *)"sort", NULL}; // create argument vector
if (execvp(argv[0], argv) < 0) exit(0); // run sort command (exit if something went wrong)
}
// if we reach here, we are in parent process
close(fds[0]); // file descriptor unused in parent
const char *words[] = {"pear", "peach", "apple"};
// write input to the writable file descriptor so it can be read in from child:
size_t numwords = sizeof(words)/sizeof(words[0]);
for (size_t i = 0; i < numwords; i++) {
dprintf(fds[1], "%s\n", words[i]);
}
// send EOF so child can continue (child blocks until all input has been processed):
close(fds[1]);
int status;
pid_t wpid = waitpid(pid, &status, 0); // wait for child to finish before exiting
return wpid == pid && WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}
In the code above, the parent process uses dprintf, but I wonder if we can redirect parent process' standard out to PIPE's in. So I tried to write the code below.
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int fds[2];
pipe(fds);
pid_t pid = fork();
if (pid == 0) {
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
close(fds[1]);
char *argv[] = {(char *)"sort", NULL};
if (execvp(argv[0], argv) < 0) exit(0);
}
// if we reach here, we are in parent process
close(fds[0]);
const char *words[] = {"pear", "peach", "apple"};
// write input to the writable file descriptor so it can be read in from child:
size_t numwords = sizeof(words)/sizeof(words[0]);
dup2(fds[1],STDOUT_FILENO);//redirect stdout
close(fds[1]); //fds[1] is not used anymore
for (size_t i = 0; i < numwords; i++) {
printf("%s\n", words[i]);
}
close(STDOUT_FILENO);
int status;
pid_t wpid = waitpid(pid, &status, 0);
return wpid == pid && WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}
After redrecting, I used printf, which in my understanding will output to STDOUT. However, this code print nothing, while the first code print as below:
apple
peach
pear
I can't figure out why this happen, is there something I understand mistakely?
According to man pages, dprintf is a POSIX extension, not a standard library function, so it is not equivalent in terms of portability.
As far as their implementation in GLIBC is concerned, both printf and dprintf call __vfprintf_internal, but note that dprintf does also this (done != EOF && _IO_do_flush (&tmpfil.file) == EOF) which suggests flushing the buffer after the write.
printf, on the other hand, does not.
I'd try fiddling with buffering, i.e. setbuf, fflush or similar on the stdout and see if that helps.
Here is the code, where parent process writes a string input in pipe and children processes read this from pipe. If child process reads from pipe the word "end", then i want to terminate all the processes and then terminate itself, and if reads the word "finish" i want to raise a signal to father for killing all the processes and then exit. I run the code and i had segmentation fault. Why it is wrong?
#define _POSIX_SOURCE
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
void measure_time(int sig)
{
printf("child [%d] received signal %d\n", getpid(), sig);
}
int main(int argc, char *argv[])
{
int n_task = 4;
pid_t pid;
pid_t pid_array[n_task];
int fd[2];
for (int i = 0; i < n_task; i++)
{
pid = fork();
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
if (pid < 0)
{
perror("fork");
exit(1);
}
if (pid == 0) //child
{
char *buf;
close(fd[1]);
read(fd[0], buf, 10);
printf("I read: %s", buf);
if (strcmp(buf, "end") == 0)
{
for (int i = 0; i < n_task; i++)
kill(pid_array[i], SIGUSR1);
}else if(strcmp(buf,"finish") == 0){
/*Here i want father to kill all children and then exit.*/
}
exit(0);
}
close(fd[0]);
char *buf;
printf("Give the input string: \n");
scanf("%s", buf);
write(fd[1], buf, strlen(buf));
close(fd[1]);
pid_array[i] = pid;
}
sleep(1);
for (int i = 0; i < n_task; i++)
wait(NULL);
return (0);
}
Besides the issue of uninitialized buf identified by #G. Sliepen, the pipe() need be called before fork() as file descriptors are kept open when forking child process(s). This is also how pipe works.
You can try to change your code snippet to put pipe() before fork().
...
if (pipe(fd) == -1)
{
perror(" pipe ");
exit(1);
}
pid = fork();
if (pid < 0)
{
perror("fork");
exit(1);
}
...
Please read the manual page of pipe(2) in which an example presented.
SO has this post fork() and pipes() in c explained this as well.
Update for terminating process(s)
This child process has no knowledge about existence of its siblings, but its parent process has. If not explicitly required, you can let the parent to do so, i.e. to "end" all child processes.
BTW, instead of sending signal SIGUSR1 it is better to send SIGTERM signal. Although SIGUSSR1 can cause the target process be terminated by default (see signal(7)).
To "finish", i.e. to kill (or terminate) all the child processes as well as parent process, you can simplly kill the parent. All its descendants got killed as well. Or, you can send signal to the same process group. See kill(2).
You are declaring a pointer buf, but did not initialize it. Subsequent calls to read() and scanf() will fail because the pointer is invalid.
You need to make sure buf is initialized and pointing to valid memory. A simple way to fix your code is to do:
char buf[10];
read(fd[0], buf, 10);
If you enable compiler warnings with -Wall, then the compiler will warn you about initialized variables.
Be aware of potential buffer overflows: if you declare char buf[10], make sure you will never write more than ten bytes into it. Also, check the return value of functions like read(), write(), scanf() to ensure no errors were encountered, otherwise the contents of the buffers or output files might not be as expected.
I am trying to write this little program, where the parent and the child communicate with each other via pipes, the code here works, unless you 'uncomment' the commented lines, than it comes to some sort of deadlock, and I cannot figure it out why? Any ideas?
int main(int argc, char **argv){
int fd[2];
int fd2[2];
pid_t pid;
pipe(fd);
pipe(fd2);
pid = fork();
if(pid==0){
close(fd[1]);
dup2(fd[0],fileno(stdin));
close(fd2[0]);
FILE *output = fdopen(fd2[1],"w");
char buffer[255];
while(fgets(buffer,255,stdin)!=NULL)
printf("child: %s",buffer);
// fprintf(output,"%s",buffer);
} else {
close(fd[0]);
close(fd2[1]);
FILE *output = fdopen(fd[1],"w");
char buffer[255];
while(fgets(buffer,255,stdin)!=NULL)
fprintf(output,"%s",buffer);
//FILE *input = fdopen(fd2[0],"r");
//while(fgets(buffer,255,input)!=NULL)
// printf("Parent: %s",buffer);
}
return 0;
}
The parent needs to close its side of the pipe to the child so that the child will detect end-of-file and terminate.
while(fgets(buffer,255,stdin)!=NULL)
fprintf(output,"%s",buffer);
fclose(output); // does close(fd[1]);
FILE *input = fdopen(fd2[0],"r");
while(fgets(buffer,255,input)!=NULL)
printf("Parent: %s",buffer);
Make sure everything gets closed. After
dup2(fd[0],fileno(stdin));
you should do:
close(fd[0]);
When you have both input and output pipes between two (single-threaded) processes, you can have some deadlock, so you need to have an event loop using a multiplexing syscall (generally poll(2)...) and you will either read or write, depending on what is possible. Of course you need to buffer! BTW, in that case, you'll better use low level syscalls(2) without using <stdio.h> (and if you still do use stdio, don't forget to fflush(3)....). See also this answer.
(of course I am supposing a POSIX or Linux system)
I can barely understand the man page for pipe, so I kinda need help understanding how to take a piped input in an external executable.
I have 2 programs: main.o & log.o
I written main.o to fork. Here is what it is doing:
Parent fork will pipe data to the child
Child fork will exec log.o
I need the child fork for main to pipe to STDIN of log.o
log.o simply takes STDIN & logs with time stamp to a file.
My code is composed of some code from various StackOverflow pages I dont remember & the man page for pipe:
printf("\n> ");
while(fgets(input, MAXINPUTLINE, stdin)){
char buf;
int fd[2], num, status;
if(pipe(fd)){
perror("Pipe broke, dood");
return 111;
}
switch(fork()){
case -1:
perror("Fork is sad fais");
return 111;
case 0: // Child
close(fd[1]); // Close unused write end
while (read(fd[0], &buf, 1) > 0) write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(fd[0]);
execlp("./log", "log", "log.txt", 0); // This is where I am confused
_exit(EXIT_FAILURE);
default: // Parent
data=stuff_happens_here();
close(fd[0]); // Close unused read end
write(fd[1], data, strlen(data));
close(fd[1]); // Reader will see EOF
wait(NULL); // Wait for child
}
printf("\n> ");
}
I suppose this is what you're going to do:
1. main fork, parent pass message to child via pipe.
2. child receive message from pipe, redirect message to STDIN, execute log.
3. log receive message from STDIN, do something.
the key to do this is dup2 to redirect file descriptor, from pipe to STDIN.
This is the modified simple version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char *argv[]) {
int fd[2];
char buf[] = "HELLO WORLD!";
if(pipe(fd)){
perror("pipe");
return -1;
}
switch(fork()){
case -1:
perror("fork");
return -1;
case 0:
// child
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execl("./log", NULL);
default:
// parent
close(fd[0]);
write(fd[1], buf, sizeof(buf));
close(fd[1]);
wait(NULL);
}
printf("END~\n");
return 0;
}
I can suggest a simpler approach. There's a function called popen(). It works very similar to the system() function except you can read or write to/from the child stdin/stdout.
Example:
int main(int argc, char* argv[])
{
FILE* fChild = popen("logApp.exe", "wb"); // the logger app is another application
if (NULL == fChild) return -1;
fprintf(fChild, "Hello world!\n");
pclose(fChild);
}
Write "man popen" in your console for a full description.
You could use dup2
See Mapping UNIX pipe descriptors to stdin and stdout in C