3

I'm having an issue with my assignment.

I have to read data from terminal in child process, after parent process dies. It is clearly written, that the parent process must die right after executing child process, so solutions that I found ( such as using wait() ) are not usefull for me.

My Code

int main(void)
{ 
    printf("start main\n");
    if(fork() == 0){
        char buffer[64];
        fgets(buffer, 64, stdin);
        printf("Child process: %s\n", buffer);
    }
    else printf("end main\n");
    //Using WAIT() here is not allowed in my assignment.
    return 0;
}

It doesn't wait for me to enter data. It seems that after parent ends, child process is in the background and it can not read any data from terminal.

The Results

damian@damian-Virtualbox:-$ ./testuje
start main
end main
damian@damian-Virtualbox:-$ Child Process: 
echo test | ./testuje
start main
end
damian@damian-Virtualbox:-$ Child Process: test

What Program should do

print: start main
print: end main
then it should:
wait for user to type something
print: child process: text_typed_by_user

EDIT: I was suggested to use tee command. Do you have any idea how to use that to achive what I wanted?

br33f
  • 105
  • 9

5 Answers5

2

You might want to use vfork instead of fork (check documentation here):

pid_t vfork(void);

vfork - create a child process and block parent
jyvet
  • 2,021
  • 15
  • 22
  • I think vfork works similiar to fork + wait(). It is not allowed becouse it delays parent process. Parent process must die immediatly after fork. – br33f Jan 16 '16 at 12:29
1

Using wait is not an option because it is intended to be used by parent process to monitor child process' exit status.

One possible way to synchronize child process with the event of parent exit is to use the fact that all open file descriptors are closed upon process termination. Thus opening a connected pair of sockets with socketpair can be used to notify child that parent has exited. By default socketpair will create blocking sockets and when the parent exits and sp[0] is closed, it will notify sp[1] in child and read will return 0. Untested code follows:

int sp[2];

socketpair(AF_UNIX,SOCK_STREAM,0,sp); // needs error check
switch (fork()) {
    case -1: // error
        break;
    case 0: // parent
        close(sp[1]);
        ....
        exit; // will close sp[0] too
    default: // child
        close(sp[0]);
        read(sp[1],sp,sizeof(int)); // needs error check
        // here sp[0] was used as temp buffer
        ... // do your read
 }

EDIT: There is one more way, but it only looks simpler while not better, because it spins using CPU until the parent exits (assuming this is OK for an exercise). This may cause a race condition:

// in child
pid_t ppid=getppid();

while (ppid==getppid()); // loop until parent dies

The race may happen if parent exits before the first call to getppid.

This race may be fixed by saving parent process pid while still in parent and only loop in child:

// in parent, before fork
pid_t ppid=getpid();
....
// in child after fork
while (ppid==getppid()); // loop until parent dies
bbonev
  • 1,406
  • 2
  • 16
  • 33
  • Thank you very much for answer, however it does not solve my problem. Those methods doesn't really change anything. I want child process to be in a foreground after parent dies, so the user could write some text - and the process should print what user typed. The program works as expected with wait(NULL) after fork, but I can not keep parent process alive. – br33f Jan 16 '16 at 12:46
  • When run in terminal and parent exits, the child cannot be kept (as far as I know) in foreground, but still has the stdin/stdout. – bbonev Jan 16 '16 at 13:00
  • How can I access background process stdin/stdout then? – br33f Jan 16 '16 at 13:10
0

You have to call setsid() in the child process after fork. This will prevent a SIGHUP to the child when the parent terminates.

if(fork() == 0){
    setsid();
    char buffer[64];
    fgets(buffer, 64, stdin);
    printf("Child process: %s\n", buffer);
    exit(EXIT_SUCCESS);
}

However, there may be a race condition between main thread exit and the setsid()-call, which you may have to take care of (ugly but simple hack would be a sleep(1) in the parent after the fork()).

Ctx
  • 18,090
  • 24
  • 36
  • 51
  • Now it prints: `start main, end main` and it stucks. – br33f Jan 16 '16 at 14:18
  • Stuck? Not here. The main process terminates and the shell resumes. The child process remains and reads from stdin. Main problem is, that the shell also reads from stdin, so it may take several attempts until a newline is read by fgets (keep enter pressed for some time). But this cannot be avoided I think. – Ctx Jan 16 '16 at 15:46
0

You may use setsid (to create a new session with the child process) in conjuction with __fpurge to purge stdin and avoid trashing fgets input:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio_ext.h>


int main(void)
{
    printf("start main\n");
    if(fork() == 0){
        char buffer[64];

        setsid();
        __fpurge(stdin);

        fgets(buffer, 64, stdin);
        printf("Child reads: '%s'\n", buffer);
    }
    else printf("end main\n");

    return 0;
}

Note : POSIX standard for fflush() state that the behaviour is undefined on stdin. So you should avoid fflush(stdin). __fpurge (defined in stdio_ext.h) is one alternative as mentioned in this topic.

Community
  • 1
  • 1
jyvet
  • 2,021
  • 15
  • 22
0

I have achieved want I wanted to achive by running it with: tee | ./myprogram.

br33f
  • 105
  • 9