0

I have a small utility that need to use fork() and wait(), however, I am facing an issue that the child process is not being terminated after the parent program is running. Any idea how I can fix it?

int test(void)
{
    pid_t PID;
    PID = fork();

    if (PID == 0) {

            sprintf(execmd, "/root/test);
            system(execmd);

            sprintf(filename, "test_results.txt);

            FILE *fp = fopen(filename, "r");

            fscanf (fp, "%s%s%s%s%s", &A, &B, &C, &D, &E);
            printf ("A=%s B=%s C=%s D=%s E=%s\n", A, B, C, D, E);


            fclose (fp);
    }
    else    // *** Parent Process *** 
    {
            int status;
            wait(&status);
    }

    return 0;
}
alk
  • 69,737
  • 10
  • 105
  • 255
user1087418
  • 47
  • 1
  • 6

1 Answers1

0

At the very beginning: your code should not compile at all, as you do not close your strings:

sprintf(execmd, "/root/test);
system(execmd); //         ^ missing quote!

(By the way, why don't you simply call system("/root/test");? At least from the code shown, I do not see any reason why you would need a copy...)

Then see wait documentation:

The wait() function shall suspend execution of the calling thread until status information for one of the terminated child processes of the calling process is available, or until delivery of a signal whose action is either to execute a signal-catching function or to terminate the process. If more than one thread is suspended in wait() or waitpid() awaiting termination of the same process, exactly one thread shall return the process status at the time of the target process termination. If status information is available prior to the call to wait(), return shall be immediate.

Return Value
If wait() or waitpid() returns because the status of a child process is available, these functions shall return a value equal to the process ID of the child process for which status is reported. If wait() or waitpid() returns due to the delivery of a signal to the calling process, -1 shall be returned and errno set to [EINTR]. If waitpid() was invoked with WNOHANG set in options, it has at least one child process specified by pid for which status is not available, and status is not available for any process specified by pid, 0 is returned. Otherwise, (pid_t)-1 shall be returned, and errno set to indicate the error.

So - if wait returns, you first should check if the process id was actually returned, otherwise you might re-enter into wait.

Then, if wait does not return, your child process is still running. I don't see why your process should be blocked because of the file handling (well, you do not perform any error checking, though, but that's a different matter unrelated to your problem), so most likely your child process is caught in the call to system.

What you now need is a timeout mechanism. My proposition is now as follows:

  1. create a pipe for self-triggering events
  2. install a signal handler for SIGCHLD using sigaction
  3. in the signal handler, do the wait and write a single byte to your pipe
  4. in your main function, where you currently wait for your child process, you would now first select or poll for your pipe with a timeout. If timeout occurs, send a signal to your child process (you could do that twice, first sending SIGTERM to allow your child to terminate gracefully, and if that does not help, send SIGKILL afterwards.

Alternative: If on linux, have a look at signalfd - you are not portable then, but get the same work done easier (select/poll for your fd and on success, you can call wait in the main function again).

Additionally, I recommend to re-structure your program a little: system will internally call fork and execve again, so you actually create another child process.

So I'd rather do it this way:

// preparations as described above

if (PID == 0)
{
    execl("/root/test", ""); // won't return unless on error!
    // some error handling?
    return -1;
}

// parent process
if(PID < 0)
{
    // error, the child process could not be created!
    return -1;
}

// select/poll
if(timeout)
{
    kill(PID, SIGTERM);
    // select/poll
    if(timeout)
        kill(PID, SIGKILL);
}

//***************************************************
// if using signalfd:
    int status;
    wait(&status);
    // yet to be done: check return value, status, errno
    // you might need a loop...
// otherwise, prefer doing this in the signal handler
// however, the following out put MUST NOT be done there, as
// file handling is not async safe!
// just set some global flag there if the process terminated successfully!
//***************************************************

if(child_was_successful)
{
    FILE* fp = fopen("test_results.txt", "r");
    if(fp) // only if successful!
    {
        // missing declarations (presumably global), adding them here:
        char A[32], B[32], C[32], D[32], E[32];
        // some fixes:
        // 1. checking return value
        // 2. arrays already decay to pointers when being passed
        //    -> you do not need to take the address of again!
        // 3. adding max length to your arrays prevents fscanf
        //    from writing beyond your array boundaries
        if(fscanf (fp, "%31s%31s%31s%31s%31s", A, B, C, D, E) == 5)
        //                                     ^ no ampersand!
        //               ^ 31: need to leave space for terminating 0 character            
        {
            printf ("A=%s B=%s C=%s D=%s E=%s\n", A, B, C, D, E);
        }

        fclose (fp);
    }
}
Community
  • 1
  • 1
Aconcagua
  • 24,880
  • 4
  • 34
  • 59