Advertisement

Writing daemons

Started by October 04, 2003 03:18 PM
7 comments, last by superpig 21 years, 4 months ago
Hey all, I''m trying to write a cross-platform server package (think web server). I''ve figured out how to set up processes as services under Win32; my test service responds to start/stop calls, and the rest of the time just runs happily in the background. Now I want to write a minimal-functionality daemon under Linux, with the eventual goal of combining the Linux and Win32 code into a single ''service platform'' that I can build my server on top, and have it compile correctly under both platforms. So... As far as I can tell, a ''service'' under Linux is simply a program that responds to specific ''signals'' - SIGHUP, SIGUSR, SIGTERM, and so on, all the signals I could send via the kill command. Aside from that, it doesn''t interact with the user directly, and just sits taking up minimal resources. Is that all there is to it? If so, my first problem is that I can''t figure out how the ''signals'' mechanism is meant to work. Is there an ''onSignalRecieve'' callback function, like with Win32, that I have to register on program startup? After that, I think I know what I''m doing - just run the process itself in a seperate thread or something, and let the signals callback start/stop that thread. Right? (If anyone has any ''your first daemon'' type tutorial guides, I''d appreciate it ). Superpig - saving pigs from untimely fates, and when he''s not doing that, runs The Binary Refinery.
Enginuity1 | Enginuity2 | Enginuity3 | Enginuity4 ry. .ibu cy. .abu ry. dy. "sy. .ubu py. .ebu ry. py. .ibu gy." fy. .ibu ny. .ebu

Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

A really simple daemon (assuming I made no mistakes ):
/* for signal, SIG_ERR, SIGHUP, and SIGTERM */#include <signal.h>/* for exit */#include <stdlib.h>/* for daemon and sleep */#include <unistd.h>void sighup(int code) {	/* This is normally used for "restart the daemon" (re-read the configuration files and such) */}void sigterm(int code) {	/* Terminate the daemon after we clean up the process */	exit(0);}int main(int argc, char *argv[]) {	if(signal(SIGHUP, sighup) == SIG_ERR)		return 1;	if(signal(SIGTERM, sigterm) == SIG_ERR)		return 2;	if(daemon(0, 0) != 0)		return 3;	/* Do daemon stuff (infinite loop is merely filler) */	for( ; ; )		sleep(1);	return 0;}

Not every unix provides a daemon function, so here's an example daemon function. Also, this allows you to see what it does. (Once again, I wrote this right now, so we'll hope I didn't make any mistakes.):
/* For O_RDWR */#include <fcntl.h>/* For exit */#include <stdlib.h>/* For chdir, close, fork, open, and STD* stream macros */#include <unistd.h>#ifndef STDIN	#define STDIN 0#endif#ifndef STDOUT	#define STDOUT 1#endif#ifndef STDERR	#define STDERR 2#endifint daemon(int nochdir, int noclose) {	switch(fork()) {		case -1: /* parent, error */			return -1;		case 0: /* child */			break;		default: /* parent */			exit(0);			break;	}	/* change the current working directory to root */	if(!nochdir)		if(chdir("/") != 0)			return -1;	/* redirect the standard streams to /dev/null */	if(!noclose) {		int devnull = open("/dev/null", O_RDWR, 0);		if(devnull == -1)			return -1;		close(STDIN);		dup(devnull);		close(STDOUT);		dup(devnull);		close(STDERR);		dup(devnull);	}	return 0;}

Edit: some more comments and fixing a mistake (two times now, that'll teach me not to test the code I post better ).



[edited by - Null and Void on October 4, 2003 8:50:50 PM]
Advertisement
Use sigaction() to register your signal handlers and not the old signal(), which is flawed.

And daemons typically do a double fork() to detach themselves from the tty that started them.


[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
quote:
Original post by Fruny
Use sigaction() to register your signal handlers and not the old signal(), which is flawed.

And daemons typically do a double fork() to detach themselves from the tty that started them.


A double fork()? How does that work? I would have thought you just ended up with a 3-level tree of processes, with the tty at the root...

Actually, checking the man page for fork(), I don''t understand how it works at all. It would seem that calling fork() once will create a second instance of the program - but wouldn''t that second instance then just go on to call fork() again, ad infinitum?

And I take it that sigaction() is as standard as signal(), i.e. it''ll be in my headers?

Null and Void, I can''t see your code right now (browser bug) but from what I can tell from the HTML source it looks useful. I''ll look at it tomorrow when I can get to a machine with a decent browser.

Superpig
- saving pigs from untimely fates, and when he''s not doing that, runs The Binary Refinery.
Enginuity1 | Enginuity2 | Enginuity3 | Enginuity4
ry. .ibu cy. .abu ry. dy. "sy. .ubu py. .ebu ry. py. .ibu gy." fy. .ibu ny. .ebu

Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

signal() is kinda deprecated. sigaction can be used instead.

the double fork idea is unnecessary. pointless, even.

only thing that may be missing from the custom daemon is setsid().


quote:
W.R. Stevens, Advanced Unix Programming, 13.3.2 p 417

Under SVR4, some people recommend calling fork again [after setsid] and having the parent terminate. The second child continues as the daemon. This guarantees that the daemon is not a session leader, which prevents it from acquiring a controlling terminal under the SVR4 rules. Alternatively, to avoid acquiring a controlling terminal be sure to specify O_NOCTTY whenever opening a terminal device.



#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int daemon_init(){  pid_t pid = fork();  if( pid < 0 )     return -1        // failed  else if( pid != 0 )     exit(0);         // parent terminates   // child continues  setsid();    // create new session  pid = fork();  if( pid < 0 )     return -1        // failed  else if( pid != 0 )     exit(0);         // parent terminates   chdir("/");  // not doing it could prevent filesystems to be               // unmounted at reboot time, since the daemon               // would stay on them.  umask(0);    // don't inherit the file creation mask.  //  // Close any open file descriptor.  //  return 0;}



[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]


[edited by - Fruny on October 4, 2003 9:09:14 PM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Advertisement
quote:
Original post by superpig
It would seem that calling fork() once will create a second instance of the program - but wouldn''t that second instance then just go on to call fork() again, ad infinitum?


That doesn''t happen because execution continues after the call to fork.

BTW, it''s a good idea to make the daemonization (at least the forking part) optional - controlled by a command line option, for example. This will let another process control and supervise the daemon and may also make debugging easier.
quote:
Original post by spock
quote:
Original post by superpig
It would seem that calling fork() once will create a second instance of the program - but wouldn''t that second instance then just go on to call fork() again, ad infinitum?


That doesn''t happen because execution continues after the call to fork.


Ah! That''s what I''d missed. I thought the process was restarting.

Thanks, everyone. If I do manage to build a ''service framework,'' I''ll probably make it available for download, if anyone wants it.

Superpig
- saving pigs from untimely fates, and when he''s not doing that, runs The Binary Refinery.
Enginuity1 | Enginuity2 | Enginuity3 | Enginuity4
ry. .ibu cy. .abu ry. dy. "sy. .ubu py. .ebu ry. py. .ibu gy." fy. .ibu ny. .ebu

Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

quote:
Original post by superpig
Thanks, everyone. If I do manage to build a ''service framework,'' I''ll probably make it available for download, if anyone wants it.

That''d be really handy, since I want to embark on a similar project very soon and would just end up doing the same thing myself.



"The sun is the same in a relative way,
but you''re older"
--Pink Floyd

This topic is closed to new replies.

Advertisement