[Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Using execl to execute a daemon

Posted on 2009-04-28
9
Medium Priority
?
352 Views
Last Modified: 2012-05-06
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 kills its parent.  Since my application has an open pipe to the parent, the call to read() should fail when the daemon kills its parent.  But instead the application just hangs.

Attached below is some bare bones code that reproduces the problem.  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.
int main(int argc, char** argv)
{
	if (argc < 2) {
		printf("Please specify a shell command\n");
		return 0;
	}
 
	// 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);
	}
}

Open in new window

0
Comment
Question by:chsalvia
  • 7
  • 2
9 Comments
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 24256830
>>  My understanding is that when a daemon starts, it typically forks and then kills its parent.  Since my application has an open pipe to the parent, the call to read() should fail when the daemon kills its parent.  But instead the application just hangs.

No, a daemon parent exits on its own. A typical daemon forks twice, once to return control to the shell/prompt, and second to disassociate from the controlling terminal. The child does not kill the parent.

But, the problem in your case is probably because a daemon closes its file descriptors. So any pipes you created before exec will be closed on the daemon end and you get EOF.
0
 

Author Comment

by:chsalvia
ID: 24256851
>> But, the problem in your case is probably because a daemon closes its file descriptors. So any pipes you created before exec will be closed on the daemon end and you get EOF.

Right, I'm counting on getting an EOF if I execute a daemon.  But the problem is that the call to read() doesn't return 0 as it should if it gets an EOF.  Instead, it just hangs waiting for data to become available on the pipe.  

Regardless, although a daemon does close standard file descriptors, doesn't it usually do that after it has forked?  Therefore, the daemons parent should still have standard file descriptors open.
0
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 24256905
>>Regardless, although a daemon does close standard file descriptors, doesn't it usually do that after it has forked?  Therefore, the daemons parent should still have standard file descriptors open.

Only for the amount of time it takes for the parent to fork + exit, which should close the descriptor. Are you sure it is exiting? Are you sure it is a proper daemon?
0
Windows Server 2016: All you need to know

Learn about Hyper-V features that increase functionality and usability of Microsoft Windows Server 2016. Also, throughout this eBook, you’ll find some basic PowerShell examples that will help you leverage the scripts in your environments!

 

Author Comment

by:chsalvia
ID: 24256995
>>Only for the amount of time it takes for the parent to fork + exit, which should close the descriptor. Are you sure it is exiting?
ps -A shows that the daemon parent has not exited, but is still there as a zombie process.

>>Are you sure it is a proper daemon?
Well, I tried it on two different daemons.  One was httpd from apache which is a pretty widely used daemon.  
0
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 24257042
>>ps -A shows that the daemon parent has not exited, but is still there as a zombie process.

That means it did exit, but has not been reaped by your own process that forked the child that execed due to the fact that your wait() call hasn't happened since it is blocked on the read(). But the descriptors will be closed anyway. So this is odd, and I am missing something too, I will take a closer look.



0
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 24257097
Ah, it dawned on me. Even though you dup() the pipe descriptors onto STDOUT and STDERR, you still have the pipe descriptor itself open. So when the execed daemon closes STDIN, STDOUT, STDERR, it still hangs onto whatever descriptor the pipe was. Your shell also needs to close the pipe because it might be descriptor 7, and the daemon won't know to close 7, so it hangs. As long as there is one reference to a duped handle, the handle is open.

dup2(fd[1], STDOUT_FILENO);
dup2(fd[1], STDERR_FILENO);
close(fd[1]); /* not needed anymore, we have stdout/stderr */
0
 
LVL 40

Accepted Solution

by:
mrjoltcola earned 2000 total points
ID: 24257104

// corrected shell program
int main(int argc, char** argv)
{
        if (argc < 2) {
                printf("Please specify a shell command\n");
                return 0;
        }
 
        // 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[1]);  // not needed anymore, execed processes won't know about fd[1]
                close(fd[0]);
                execl("/bin/sh", "sh", "-c", argv[1], 0);
        }
}

Open in new window

0
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 24257105
Here is a quicky test daemon for you to use for verification. Good luck.
// test daemon
int main() {
 
  if(fork() > 0)
    exit(0);
  setgid(0);
 
  if(fork() > 0)
    exit(0);
 
  close(0);
  close(1);
  close(2);
  sleep(5);
  printf("daemon exit\n");
}

Open in new window

0
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 24259524
Reading the solution, I see my comment is misleading for another reader. For clarification, the line of code "close(fd[1]);" _is_ needed and is the part that was added. The comment // not needed anymore... refers to the pipe descriptor in fd[1] not being needed anymore, so we close it.
0

Featured Post

Prep for the ITIL® Foundation Certification Exam

December’s Course of the Month is now available! Enroll to learn ITIL® Foundation best practices for delivering IT services effectively and efficiently.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever been frustrated by having to click seven times in order to retrieve a small bit of information from the web, always the same seven clicks, scrolling down and down until you reach your target? When you know the benefits of the command l…
The purpose of this article is to demonstrate how we can upgrade Python from version 2.7.6 to Python 2.7.10 on the Linux Mint operating system. I am using an Oracle Virtual Box where I have installed Linux Mint operating system version 17.2. Once yo…
The goal of this video is to provide viewers with basic examples to understand how to create, access, and change arrays in the C programming language.
The goal of this video is to provide viewers with basic examples to understand and use switch statements in the C programming language.

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question