The Linux Daemon Process is Not Dangerous

The Linux Daemon Process is Not Dangerous

#OldisGold.

It is really interesting to see how the daemon process functions. This write up covers the fundamental blocks needed to write a daemon program in Linux. This was one of my favourite topic.

1. Linux Process relationship as in Human relationship.

Parent, Child , Orphan, Left outs, daemons are the certain categories or type of people. Linux do have similar type, but for the processes. A program in execution is said to be a process , and when the process invokes fork API ( for example), a new process is created, invoking process is titled the name parent, and a new process titled the name child. Of course, you can not be a parent for someone unless you have a child. If the parent dies before the child dies, child becomes orphan. Interestingly, Linux have courtesy to set 'init' process as a parent for any orphan process. Naturally child can also die before parent dies. A process that which exited, it is dead, and if its parent have not claimed its dead child, then child is useless process that hangs around the system for no use. Such a process is named zombie. To avoid such zombie process , its parent have to claim it by using wait () API.

2. Program to Process:

A program is an executable file, loaded into memory by loader for execution. In this state it is called a process. While running ./a.out for example, shell invokes the loader in turn invokes execve () that copies the a.out image into memory, and transfers control to main function. The utility strace , a system call tracer gives the sequence of sys call invocation, and we can see execve() is invoked.

#strace ./a.out

execve("./a.out", ["./a.out"], [/* 46 vars */]) = 0
brk(0)
….
exit_group(11)  

  


3. Process to Daemon Process.

There are many daemon processes which does quite important tasks in Linux. Hence it is vital to understand how to implement a daemon process. We browse Internet as every day activity. As a result of hitting URL in browser, we are replied with HTML page.

Have you ever thought, who is responding you with result page after you click on URL?. Having no automated process ( Long running guy), and think the scenario where you have to respond for each request that comes to you manually? You should be a superficial person ( precisely person is alive all time) to respond in no time, a million requests that comes in, and it is impossible. Instead, having a process which does the same job for you automatically without your intervention, reduces the human effort drastically. That is what httpd daemon is for and it serves the web page for your request.  

3.1 Daemon Process.

As we said earlier, We need to invoke certain system calls in order to make a process behave as a daemon. Let's discuss what are those steps?.

 Lets assume you have a sample Pre-KG hello world program.

/* PROGRAM #1 */

#include <stdio.h>

int main()

{

 printf (“Hello World ”);

 while (1); // Or working with some other task 

 return 0;

} 

After compilation we run the program by any of the following way ( basically running in background with respect to terminal )

#./a.out [ or ] ./a.out & 

Now, having closed the terminal, a.out is also terminated. What if i do not want ./a.out process not to get terminated?. The solution could be ./a.out should not obey the action taken by controlling terminal ( the terminal in which you are running your program), which can be achieved by detaching ./a.out from controlling terminal. Doing so, process gets freedom from Controlling Terminal. Why such freedom ?. Assume you wrote a server program which keeps on running and responding for its clients, and you start the program in the terminal, and it is very much obvious you do want your server program not to get terminated by closing terminal.  

3.1.2 How Controlling terminal controls our process ?

Existing from controlling terminal, sends the SIGHUP signal to all the processes belonging to session which is attached to controlling terminal. Let’s prove this with the following program.

/* PROGRAM #2 */

#include <stdio.h>

#include <stdlib.h>

#include <sys/ioctl.h>

#include <syslog.h>

#include <signal.h>

#include <unistd.h>

#include <sys/types.h>

void mycallback (int signo, siginfo_t *siginfo, void *p) 

{

      char buff[512] = {0};

      openlog ("Killer: ", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0);

      sprintf (buff, "Sender Id %d ", siginfo->si_pid);

      syslog(LOG_INFO, buff);

      closelog (); 

};

int main()

{

     struct sigaction sigact;

     sigact.sa_sigaction = &mycallback;

     sigact.sa_flags = SA_SIGINFO;

     sigaction (SIGHUP, &sigact, NULL);

     while (1);

     return 0;

}


For logging purpose we use syslog which writes message into /var/log/syslog. The following command can be used to view the output of our sample program. For further details about syslog please refer man page of syslog.  

Let's look at 'ps' out in our terminal.

#ps

26839 pts/3 00:00:00 bash

Let's run our program.

#./a.out

Let us close the terminal where we invoked ./a.out, and have seen the following output in /var/log/syslog

Jun 29 10:06:12 bttest Killer: [27412]: Sender Id 26839

 It clearly indicates that bash as a session leader sends the SIGHUP signal to ./a.out, and we will see in detail below what the session leader means !

3.1.3 How can i detach the controlling terminal ?

In general, each process is associated with a session, which is inherited from the parent process, and session is associated with at most one control terminal. You see below the nontrivial terms that you should remember to continue further reading. In brief

Session means “ Collection of one or more process groups. “

Process Groups means “ Collection of one or more processes."

Session Leader means “ A process inside the session, which has created the session, also known as controlling process., identified by Session ID."

Process Group Leader is Identified by Group Id.

Let’s look at who is the session Leader for the process, by identifying session id.

1) Let me open a gnome-terminal , and run the following program,

/* PROGRAM #3 */

#include <unistd.h> 

int main() 

{ 

pid_t sid; 

printf ("Session id is %d ", getsid(0)); 

return 0;

}


#./a.out -> I get the the value 1437 , and lets see which process is assigned this pid.

#Ps | grep 1437   => 1437 pts/1 00:00:00 bash

Surprisingly bash pid is equivalent to the session id which we retrieved from the previous program. It means that bash is the session leader, who created this session, and it has attached with the controlling terminal pts/1. Now, any process that i create in this session will carry the same controlling terminal.  

 Let us run [program #1 ] again.

#./a.out &
#ps
PID TTY TIME CMD

1437 pts/1 00:00:01 bash 

2786 pts/1 00:00:00 ./a.out 

 To conclude, any process that runs in this session carries the control terminal pts/1 which is attached with the session leader [ bash ].

So, the catch now is:

We need a session with no controlling terminal. You remember why?, because if we close ther controlling terminal, process also dies.:)

creating a fresh session could be one way to bring out having no control terminal.

We have been blessed with the API called setsuid () which creates new session without control terminal. 

3.1.4 . New Session

But Wait, For successful execution of setsid (), You need to follow a rules imposed by setsid. It says you that the calling process should not be a group leader.

3.1.5 How can i make a process not to be a group leader ?

'Group' literally means a collection of one or multiple objects, So, we say ' process group' is a collection of one ore more processes. In other words, process needs a group leader, basically a group id. This group id is not an randomised value. Here we see the cleverness of actual computer engineers. The first process id of a group turns into the group leader. ( First come First Leader ;) . So that catch here is : Each time you execute a program, it itself becomes the process group leader of new group.

/* Program #4 */ 

#include <stdlib.h>

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

int main ( int argc, char ** argv)

{

pid_t pid, sid;

pid = getpid (); 

printf (" Processid: %d , GroupId: %d, SessionId %d ", pid, getpgid (pid), getsid (pid));

return 0;

}

#a.out

Processid: 27563 , GroupId: 27563, SessionId 1437

What you see here is the process itself is a group leader , because group id is equivalent to this process id, which means it can not invoke setsid () successfully in this process. So, we create a child process by fork ()

pid = fork ()

The child process becomes part of the parent’s group, but of course not a leader. Hence the child process is eligible to run setsid () .

Lets consider the following program. 

/* PROGRAM 5 */

#include <stdlib.h>

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

int main ( int argc, char ** argv)

{

pid_t pid, sid, pcid;

int status;

pid = getpid (); 

printf (" Parent [ Processid: %d , GroupId: %d, SessionId %d ]\n", pid, getpgid (pid), getsid (pid));

pcid = fork (); 

if (pcid == 0) /* Child */

 { 

 /* Invoke setsid (); to see the difference */

 pid = getpid (); 

 printf ("Child: [ Processid: %d , GroupId: %d, SessionId %d ]\n", pid, getpgid (pid), getsid (pid));

 }

wait(NULL); /* Not interested in retrieving Child Status */

return 0;

}

 #./a.out results the following output

 Parent [ Processid: 27742 , GroupId: 27742, SessionId 1437]

Child: [ Processid: 27743 , GroupId: 27742, SessionId 1437 ]

 We prove here that child process belongs to the same group as parent belongs but not the group leader. Hence the child process can invoke setsid ().

As a result of setsid(), the child process becomes the leader of session and its own process group, and the session does not have any controlling terminal. Lets prove it. Add a API setsid () in the PROGRAM 5 in mentioned place. I see the output something like

Parent [ Processid: 27859 , GroupId: 27859, SessionId 1437]

Child: [ Processid: 27860 , GroupId: 27860, SessionId 27860 ]

3.1.6 File Handling:

3.1.6.1 Permission Issue:

Now, there is much possibility that your daemon code might need to create a new file. Who stops creating a file?. Still there is a dependency from the parent. The child process inherits the umask value from its parent, as a result, there may be chance of not having adequate permission set for file handling. Hence to avoid such issue, you invoke umask.

Umask (0);

Setting umask value to 0, allows newly created file file to have permission (666 – 000 ) and newly created directory to have permission (777 -000) 

3.1.6.2 File Location:

if a daemon tries to create file where will it get stored ? Of course in the present working directory where you invoked the process from. What is the guarantee that the PWD presents always in the system ?. To avoid such dependency you will have to choose the default directory location which exists forever. So, I choose a root location /Mydaemon. [ make sure you have a write permission on folder ]

chdir (“/Mydaemon”)

3.1.6.3 Closure of Redundant File Descriptors:

Child process inherits the file descriptors opened by parent including standard file descriptors as such stdin, stdout, and stderror. We do not know where these default file descriptors are directed to, hence we close all the opened file descriptor by the parent. But still closing this file descriptors are up-to your decision. There is no point keeping the files open if it is not used, there by at least you reduce number of open files count. You know this count matters.. ;)

max_fds = sysconf (_SC_OPEN_MAX);

 for (i=0; i<max_fds; i++) {

close (i);

}

 Cool!!. All set now to run our damon. Here is the final code :

/* Simple Daemon: PROGRAM 6 */

#include <stdio.h>

#include <stdlib.h>

#include <sys/ioctl.h>

#include <unistd.h>

int main()

{

 pid_t pid;

 pid = fork (); 

 if( pid == 0) /* Child process */

 {

 int ret, i;

 int max_fds;

 ret = setsid(); 

 if ( ret == -1) {

 perror ("setsid:");

 return (-1);

 } 

 umask (0);

 chdir ("/MyDaemon"); 

 max_fds = sysconf (_SC_OPEN_MAX);

 printf ("Max_fds %d \n", max_fds);

 for (i=0; i<max_fds; i++)

 close (i);

 /* Start Doing its own activities */

 while (1); /* For demo purpose to keep the child alive */

 }

}


That's all. We made our daemon program. Cool!!. Now we know how to make a process a daemon. we also understood the importance of the daemon process, controlling the terminal , session leader, group leader, relations between them at beginners level. I hope you enjoyed the reading and Of course Happy Sharing.

To view or add a comment, sign in

More articles by Ramprakash Jelari

  • Principal Components - The analysis in Abstract

    Orthogonality of the two-dimension provides better coverage and the representation of any given data than the two…

  • Singular Value Decomposition.

    Every theory and innovations are the solutions to some problems. Unless we know what the problem it resolves, the…

  • Power to the Matrix - An Eigen Way

    Here is a very short summary. The objective is to prove how handy it is the Eigenvalues, Eigenvectors.

  • HDFS - Hadoop Distributed File System. An Abstract Discussion.

    Disclaimer: If you know already what HDFS is then, this may not interest to you. Here I discuss the abstract of HDFS.

    2 Comments
  • GTK+ Signaling Mechanism - Dev Notes.

    #OldIsGold. To Understand what is Gtk+ please refer this official Link: https://www.

    1 Comment
  • Time Complexity - Simple Note

    #OldIsGold. Straight to the point may not be a bad idea.

  • Angle Between Two Vectors.

    It is very interesting to look back at School Math. Let's see today how the angle between two vectors shall be measured.

  • Address Binding

    Moto: Revisit the olds All you need is an address at the end to run your program. The variables and functions that you…

Others also viewed

Explore content categories