Daemon-izing a Process in Linux
Introduction
A Linux process works either in foreground or background.
A process running in foreground can interact with the user in
front of the terminal. To run a.out in foreground we execute as shown below.
./a.out
When a process runs as background process then it runs by itself
without any user interaction. The user can check its status but he doesn't
(need to) know what it is doing. To run a.out in background we execute as shown
below.
$ ./a.out &
[1] 3665
As shown above when we run a process with & at the end then
the process runs in background and returns the process id (3665 in above
example).
what is a DAEMON Process?
A 'daemon' process is a process that runs in background, begins
execution at startup
(not neccessarily), runs forever, usually do not die or get
restarted, waits for requests to arrive and respond to them and frequently
spawn other processes to handle these requests.
So running a process in BACKGROUND with a while loop logic in code
to loop forever makes a Daemon ? Yes and also No. But there are certain things
to be considered when we create a daemon process. We follow a step-by-step
procedure as shown below to create a daemon process.
Daemonizing a process is different from making a process run in the
background ?
If I start a program in the background using & (for example
'./script &' ), what makes this process' execution different from the case
when the program turns itself into a daemon ?
A program running in the background is no longer, directly
controlled by the terminal (you can't simply ^C it), but it can still write to
the terminal and interfere with your work. Typically a daemon will separate
itself from the terminal (in addition to forking) and its output/error would be
redirected to files.
1. Create a separate child process - fork() it.
Using fork() system call create a copy of our process(child), then
let the parent process exit. Once the parent process exits the Orphaned child
process will become the child of init process (this is the initial system
process, in other words the parent of all processes). As a result our process
will be completely detached from its parent and start operating in background.
pid=fork();
if (pid<0) exit(1); /* fork error */
if (pid>0) exit(0); /* parent exits */
/* child (daemon) continues */
2. Make child process In-dependent - setsid()
Before we see how we gonna make a child process independent let us
talk Process group and Session ID.
A process group denotes a collection of one or more processes.
Process groups are used to control the distribution of signals. A signal
directed to a process group is delivered individually to all of the processes
that are members of the group.
Process groups are themselves grouped into sessions. Process
groups are not permitted to migrate from one session to another, and a process
may only create new process groups belonging to the same session as it itself
belongs to. Processes are not permitted to join process groups that are not in
the same session as they themselves are.
New process images created by a call to a function of the exec
family and fork() inherit the process group membership and the session
membership of the parent process image.
A process receives signals from the terminal that it is connected
to, and each process inherits its parent's controlling tty. A daemon process
should not receive signals from the process that started it, so it must detach
itself from its controlling tty.
In Unix systems, processes operates within a process group, so
that all processes within a group is treated as a single entity. Process group
or session is also inherited. A daemon process should operate independently
from other processes.
setsid();
setsid() system call is used to create a new session containing a
single (new) process group, with the current process as both the session leader
and the process group leader of that single process group. (setpgrp() is an
alternative for this).
NOTE: We have to create a child process and use setsid() to make
it independent. Trying on a parent process returns error saying EPERM.
3. Change current Running Directory - chdir()
A daemon process should run in a known directory. There are many
advantages, in fact the opposite has many disadvantages: suppose that our
daemon process is started in a user's home directory, it will not be able to
find some input and output files. If the home directory is a mounted filesystem
then it will even create many issues if the filesystem is accidentally
un-mounted.
chdir("/server/");
The root "/" directory may not be appropriate for every
server, it should be chosen carefully depending on the type of the server.
4. Close Inherited Descriptors and Standard I/O Descriptors
A child process inherits default standard I/O descriptors and
opened file descriptors from a parent process, this may cause the use of
resources un-neccessarily. Unnecessary file descriptors should be closed before
fork() system call (so that they are not inherited) or close all open
descriptors as soon as the child process starts running as shown below.
for ( i=getdtablesize(); i>=0; --i)
close(i); /* close all descriptors */
There are three standard I/O descriptors:
standard input 'stdin' (0),
standard output 'stdout' (1),
standard error 'stderr' (2).
For safety, these descriptors should be opened and connected to a
harmless I/O device (such as /dev/null).
int fd;
fd = open("/dev/null",O_RDWR, 0);
if (fd != -1)
{
dup2 (fd, STDIN_FILENO);
dup2 (fd, STDOUT_FILENO);
dup2 (fd, STDERR_FILENO);
if (fd > 2)
close (fd);
}
5. Reset File Creation Mask - umask()
Most Daemon processes runs as super-user, for security reasons
they should protect files that they create. Setting user mask will prevent
unsecure file priviliges that may occur on file creation.
umask(027);
This will restrict file creation mode to 750 (complement of 027).
Let us see a sample 'C' code which creates a daemon.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
static void daemonize(void)
{
pid_t pid, sid;
int fd;
/* already a daemon */
if ( getppid() == 1 ) return;
/* Fork off the parent process */
pid = fork();
if (pid < 0)
{
exit(EXIT_FAILURE);
}
if (pid > 0)
{
exit(EXIT_SUCCESS); /*Killing the
Parent Process*/
}
/* At this point we are executing as the child
process */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0)
{
exit(EXIT_FAILURE);
}
/* Change the current working directory. */
if ((chdir("/")) < 0)
{
exit(EXIT_FAILURE);
}
fd = open("/dev/null",O_RDWR, 0);
if (fd != -1)
{
dup2 (fd, STDIN_FILENO);
dup2 (fd, STDOUT_FILENO);
dup2 (fd, STDERR_FILENO);
if (fd > 2)
{
close (fd);
}
}
/*resettign File Creation Mask */
umask(027);
}
int main( int argc, char *argv[] )
{
daemonize();
while(1)
{
/* Now we are a daemon -- do the work for
which we were paid */
sleep(10);
}
return 0;
}