Charles
Charles

Reputation:

Using execl to execute a daemon

I'm writing a program in C on Linux which includes a module that allows a shell command to be executed on a remote machine. The easiest way to actually execute the command would of course be to simply use the system() function, or use popen and then grab the output. However, I chose to use a more low-level approach due to other design requirements which are not relevant to the current problem.

So basically, I set up a pipe and fork, and then call execl. This all works perfectly, except for one annoying exception. It doesn't work properly if the shell command to be executed is a daemon. In that case, it just hangs. I can't figure out why. My understanding is that when a daemon starts, it typically forks and then the parent exits. Since my application has an open pipe to the parent, the call to read() should fail when the parent exits. But instead the application just hangs.

Here is some bare bones code that reproduces the problem:


int main(int argc, char** argv)
{
        // Create a pipe and fork
        //
        int fd[2];
        int p = pipe(fd);
        pid_t pid = fork();

    if (pid > 0)
    {
            // Read from the pipe and output the result
            //
            close(fd[1]);
            char buf[1024] = { 0 };
            read(fd[0], buf, sizeof(buf));
            printf("%s\n", buf);

            // Wait for child to terminate
            int status;
            wait(&status);
    }
    else if (pid == 0)
    {
            // Redirect stdout and stderr to the pipe and execute the shell
            // command
            //
            dup2(fd[1], STDOUT_FILENO);
            dup2(fd[1], STDERR_FILENO);
            close(fd[0]);
            execl("/bin/sh", "sh", "-c", argv[1], 0);
    }

}

The code works fine if you use it with a normal shell command. But if you try to run a daemon, it just hangs instead of returning to the prompt as it should.

Upvotes: 2

Views: 3214

Answers (4)

pts
pts

Reputation: 87241

The most probable solution is adding close(fd[1]); above the execl().

The reason why your program hangs is that the read() function waits for the daemon to write something to its stdout/stderr. If the daemon (including the child process of your program, and also the child process' forked children who keep their stdout/stderr) doesn't write anything and there is at least one process holding the writable end of the pipe open, read() will never return. But which is that process, which is holding the writable end of the pipe open? It is most probably the child of your program's child, the long-running daemon process. Although it may have called close(0); and close(1); when daemonizing itself, most probably it hasn't called close(fd[1]);, so the writable end of the pipe is still open.

Upvotes: 2

shodanex
shodanex

Reputation: 15406

I think you should receive the SIGPIPE signal when waiting on the read to complete, since the other end of the pipe is closed. Did you make anything unusual with the signal ? I suggest you run your code with the strace command.

Upvotes: 0

Charles
Charles

Reputation:

As the child process is a deamon it wont terminate anytime soon.

Are you sure? Of course I would agree that a daemon won't terminate anytime soon - but when a daemon starts up it forks so the child can disassociate itself with the terminal, and then the parent exits. Since the wait() system call is waiting on the parent daemon process, it should exit.

Regardless, the same problem occurs without the call to wait().

Also, why doesn't the read() get an EOF? The read() is reading from an open pipe that is connected with the parent daemon process. So when the parent daemon process exits, the read() should return immediately with an EOF.

Upvotes: 0

James Anderson
James Anderson

Reputation: 27478

Your problem is proably here:-

// Wait for child to terminate int status; wait(&status);

As the child process is a deamon it wont terminate anytime soon.

Also your "read()" is likely to hang. You are going to have to decide how long you wait before abandoning any attempt to display output.

Upvotes: 0

Related Questions