PDA

View Full Version : Detaching from Console



HalNineThousand
04-08-2008, 10:43 PM
I know I can run a program like this:

cprog >/dev/null 2>&1 &

and it'll basically run on its own and it is essentially detached from the console so I can go ahead and use the console for other tasks.

Is there any way, from within a C or C++ program, on Linux, for the program to detach itself from the console and operate as a daemon and be independent of the console instance from which it was started?

brewbuck
04-08-2008, 11:35 PM
Is there any way, from within a C or C++ program, on Linux, for the program to detach itself from the console and operate as a daemon and be independent of the console instance from which it was started?

Of course. The basic steps:



#include <unistd.h>

pid = fork();
if(pid < 0)
{
/* Report error and exit */
}
if(pid == 0)
{
/* The child becomes the daemon. */
/* Detach all standard I/O descriptors */
close(0);
close(1);
close(2);
/* Enter a new session */
setsid();
/* We are now daemonized */
run_the_daemon();
exit(0);
}
else
{
/* The parent need not exist anymore */
exit(0);
}

HalNineThousand
04-09-2008, 12:43 AM
When you use


run_the_daemon();


Do you mean to call the actual daemon program through a system call, or do you mean to simply just call the function in this program itself? I'm assuming you mean the latter, but just wanted to check.

As for the fork(), is that because when it forks, the child is "spun off" on its own and the parent just exits? What happens if you issue the close statements before forking?

That also answers another question I've had along the way. After reading this over and examining it, I take it the close statements are closing STDERR, STDOUT, and STDIN, so that would explain why whenever I opened my first file in other programs, it would always get 3 as a file descriptor. Is that right?

Thanks!

CornedBee
04-09-2008, 01:52 AM
You must fork because the shell is waiting for the process it started to exit.

brewbuck
04-09-2008, 09:16 AM
Do you mean to call the actual daemon program through a system call, or do you mean to simply just call the function in this program itself? I'm assuming you mean the latter, but just wanted to check.

I mean the latter. You don't necessarily have to implement it that way, though. You could bundle this section of code into a function called "become_daemon()" and then just return from it instead of calling run_the_daemon(). Now the main program just calls this function to become daemonized.



As for the fork(), is that because when it forks, the child is "spun off" on its own and the parent just exits? What happens if you issue the close statements before forking?

You COULD call close() before forking, but if fork() fails, you will now have no way of printing an error message.


That also answers another question I've had along the way. After reading this over and examining it, I take it the close statements are closing STDERR, STDOUT, and STDIN, so that would explain why whenever I opened my first file in other programs, it would always get 3 as a file descriptor. Is that right?

Correct.

HalNineThousand
04-09-2008, 09:24 AM
I was hoping that you did mean run_the_daemon() as a call to any function because I was thinking just what you suggested: use it as a subroutine, so I could put it in my personal library to make it easier in the long run.

I follow your reasoning on the closing and fork(). I'm still so new to C++ that I always want to make sure I know about any side effects that aren't obvious.

What would make fork() fail?

And thanks for all the help!

brewbuck
04-09-2008, 09:58 AM
I follow your reasoning on the closing and fork(). I'm still so new to C++ that I always want to make sure I know about any side effects that aren't obvious.

What would make fork() fail?

fork() can fail if you have reached your user process limit, or if the system is completely out of space in the process table, or if not enough memory is available. If fork() is failing because of not enough memory, the system is in a poor, poor state already, but you at least would want to print an error message.

HalNineThousand
04-09-2008, 10:07 AM
Okay. Makes sense. And yes, I would want to print an error. I prefer programs that communicate too much as opposed to not giving enough info.

Thank you!

birkelbach
04-12-2008, 11:58 AM
Don't forget to...

chdir("/");

...or the directory that you started the deamon from will be locked until the program is stopped.

HalNineThousand
04-12-2008, 02:28 PM
You mean locked as in can't delete it, right?

And I take it you mean the directory it was started from, not the one the executable was in, right?

CornedBee
04-12-2008, 03:36 PM
Right and right. You can't delete it, and you can't unmount the volume it is on either.

heras
04-13-2008, 05:59 AM
What will happen then when something tries to write to any of the closed descriptors? As opposed to when you connect them to /dev/null like so:

for (i=getdtablesize();i>=0;--i) close(i); /* close all descriptors */
i=open("/dev/null",O_RDWR); /* open stdin */
dup(i); /* stdout */
dup(i); /* stderr */
(Taken from this example (http://www.enderunix.org/documents/eng/daemon.php).)

Or is it good enough to redirect everything to a log file?

CornedBee
04-13-2008, 06:17 AM
The write() system call fails if you try to write to a closed descriptor, and read() fails if you try to read from a closed descriptor.

heras
04-13-2008, 06:39 AM
Thanks.
So I guess "good practice" depends on what kind of daemon you're making and what it does.

brewbuck
04-14-2008, 10:17 AM
Thanks.
So I guess "good practice" depends on what kind of daemon you're making and what it does.

Obviously you are in control of your own code. A write() to stdout will return an error if the descriptor is closed. But since you are writing this program specifically as a daemon, you would have no reason to write() to stdout in the first place. And even if you did, you would be perfectly free to ignore the harmless error which would result.

Connecting the standard descriptors to /dev/null is a practice used when you might exec() a program that doesn't expect to have those descriptors closed. In your case, it is unnecessary.

The thing about chdir("/") is a good piece of advice I forgot about in my original post. Again, however, not strictly necessary, since the superuser can just go to single user mode if a misbehaving program is interfering with unmounting a filesystem (and programs misbehave often).