Как написать демона linux

Daemons are processes that do not run directly under the control of the user but serve in the background. Usually, they start on system startup and run continuously until the system shuts down. The only difference between these and normal processes is that they do not send messages to the console or screen in any way.

Here’s how you can create a daemon on a Linux machine.

A Brief Introduction to How Daemons Are Created

A lot of daemons run on the system and some familiar daemon examples are as follows:

  • crond: Makes commands run at the specified time
  • sshd: Allows login to the system from remote machines
  • httpd: Serves web pages
  • nfsd: Allows file sharing over the network

Also, daemon processes are usually named to end with the letter d, although it’s not mandatory.

For a process to run as a daemon, the following path is followed:

  • Initial operations, such as reading configuration files or obtaining necessary system resources, must be performed before the process becomes a daemon. This way, the system can report the received errors to the user and the process will be terminated with an appropriate error code.
  • A background running process is created with init as its parent process. For this purpose, a sub-process is forked from the init process first, and then the upper process is terminated with exit.
  • A new session should open by calling the setsid function, and the process should be disconnected from the terminal.
  • All open file descriptors inherited from the parent process are closed.
  • Standard input, output, and error messages are redirected to /dev/null.
  • The working directory of the process must change.

What Are Daemon Sessions?

After logging into the system via a terminal, users can run many applications through the shell program. These processes should close when the user exits the system. The operating system groups these processes into session and process groups.

Each session consists of process groups. You can describe this situation as follows:

daemon-sessions-diagrams

The terminal where the processes receive their inputs and send their outputs is called the controlling terminal. A controlling terminal is associated with only one session at a time.

A session and the process groups in it have identification (ID) numbers; these identification numbers are the process identification numbers (PID) of the session and process group leaders. A child process shares the same group as its parent process. When multiple processes are communicating with the pipe mechanism, the first process becomes the process group leader.

Creating a Daemon Process on Linux

Here you will see how you can create a daemon function. For this purpose, you will create a function named _daemon. You can start by naming the application code that will run as a daemon as test.c, and the code that you will create the daemon function as daemon.c.

 //test.c
#include <stdio.h>

int _daemon(int, int);

int main()
{
getchar();
_daemon(0, 0);
getchar();
return 0;
}

 //daemon.c
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>
#include <linux/limits.h>
int _daemon(int nochdir, int noclose) {
 pid_t pid;
 pid = fork(); // Fork off the parent process
 if (pid < 0) {
   exit(EXIT_FAILURE);
 }
 if (pid > 0) {
   exit(EXIT_SUCCESS);
 }
return 0;
}

To create a daemon, you need a background process whose parent process is init. In the code above, _daemon creates a child process and then kills the parent process. In this case, your new process will be a subprocess of init and will continue to run in the background.

Now compile the application with the following command and examine the status of the process before and after _deamon is called:

 gcc -o test test.c daemon.c 

Run the application and switch to a different terminal without pressing any other keys:

 ./test 

You can see that the values related to your process are as follows. Here, you’ll have to use the ps command to get process-related information. In this case, the _daemon function has not been called yet.

 ps -C test -o "pid ppid pgid sid tty stat command"

# Output
PID PPID PGID SID TT STAT COMMAND
10296 5119 10296 5117 pts/2 S+ ./test

When you look at the STAT field, you see that your process is running but waiting for an off-schedule event to occur which will cause it to run in the foreground.

Abbreviation

Meaning

S

Waiting asleep for an event to happen

T

Application stopped

s

Session leader

+

The application is running in the foreground

You can see that the parent process of your application is the shell as expected.

 ps -jp 5119 
# Output
PID PGID SID TTY TIME CMD
5119 5119 5117 pts/2 00:00:02 zsh

Now return to the terminal where you are running your application and press Enter to invoke the _daemon function. Then look at the process information on the other terminal again.

 ps -C test -o "pid ppid pgid sid tty stat command"

# Output
PID PPID PGID SID TT STAT COMMAND
22504 1 22481 5117 pts/2 S ./test

First of all, you can say that the new subprocess is running in the background since you do not see the + character in the STAT field. Now examine who is the parent process of the process using the following command:

 ps -jp 1 

​​​​​​​# Output
PID PGID SID TTY TIME CMD
1 1 1 ? 00:00:01 systemd

You can now see that the parent process of your process is the systemd process. It is mentioned above that for the next step, a new session should open and the process should be disconnected from the control terminal. For this, you use the setsid function. Add this call to your _daemon function.

The piece of code to add is as follows:

 if (setsid() == -1) 
return -1;

Now that you’ve inspected the state before _daemon called, you can now remove the first getchar function in the test.c code.

 //test.c
#include <stdio.h>
int _daemon(int, int);
int main()
{
_daemon(0, 0);
getchar();
return 0;
}

After compiling and running the application again, go to the terminal where you made your reviews. The new status of your process is as follows:

 ps -C test -o "pid ppid pgid sid tty stat command"

​​​​​​​# Output
PID PPID PGID SID TT STAT COMMAND
25494 1 25494 25494 ? Ss ./test

The ? sign in the TT field indicates that your process is no longer connected to a terminal. Notice that the PID, PGID, and SID values of your process are the same. Your process is now a session leader.

In the next step, change the working directory to the root directory according to the value of the argument you passed. You can add the following snippet to the _daemon function for this:

 if (!nochdir) {
   if (chdir("/") == -1)
      return -1;
}

Now, according to the argument passed, all file descriptors can be closed. Add the following code to the _daemon function:

 #define NR_OPEN 1024
if (!noclose) {
   for (i = 0; i < NR_OPEN; i++)
      close(i);
   open("/dev/null", O_RDWR);
   dup(0);
   dup(0);
}

After all file descriptors are closed, new files opened by daemon will be shown with the descriptors 0, 1, and 2 respectively. In this case, for example, the printf commands in the code will be directed to the second opened file. To avoid this, the first three identifiers point to the /dev/null device.

In this case, the final state of the _daemon function will be as follows:

 #include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
int _daemon(void) {
// PID: Process ID
// SID: Session ID
 pid_t pid, sid;
 pid = fork(); // Fork off the parent process
 if (pid < 0) {
   exit(EXIT_FAILURE);
 }
 if (pid > 0) {
   exit(EXIT_SUCCESS);
 }
 // Create a SID for child
 sid = setsid();
 if (sid < 0) {
   // FAIL
   exit(EXIT_FAILURE);
 }
if ((chdir("/")) < 0) {
   // FAIL
   exit(EXIT_FAILURE);
 }
 close(STDIN_FILENO);
 close(STDOUT_FILENO);
 close(STDERR_FILENO);
while (1) {
   // Some Tasks
   sleep(30);
 }
exit(EXIT_SUCCESS);
}

Here’s an example of a code snippet that runs the sshd application as a daemon:

 ...
if (!(debug_flag || inetd_flag || no_daemon_flag)) {
    int fd;
    if (daemon(0, 0) < 0)
        fatal("daemon() failed: %.200s", strerror(errno));
    /* Disconnect from the controlling tty. */
    fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
    if (fd >= 0) {
        (void) ioctl(fd, TIOCNOTTY, NULL);
        close(fd);
    }
}
...

Daemons Are Important for Linux System Programming

Daemons are programs that perform various actions in a predefined manner set in response to certain events. They run silently on your Linux machine. They are not under the direct control of the user and each service running in the background has its daemon.

It is important to master daemons to learn the kernel structure of the Linux operating system and to understand the working of various system architectures.

  • This is an answer to a question on stackoverflow: Creating a daemon in Linux
  • Fork the skeleton code: Basic skeleton of a linux daemon written in C
  • Read the article here: How to create a c-style daemon

Basic skeleton of a linux daemon written in C

Daemons work in the background and (usually…) don’t belong to a TTY that’s why you can’t use stdout/stderr in the way you probably want.
Usually a syslog daemon (syslogd) is used for logging messages to files (debug, error,…).

Besides that, there are a few required steps to daemonize a process.

Required Steps

  • fork off the parent process & let it terminate if forking was successful. -> Because the parent process has terminated, the child process now runs in the background.
  • setsid — Create a new session. The calling process becomes the leader of the new session and the process group leader of the new process group. The process is now detached from its controlling terminal (CTTY).
  • Catch signals — Ignore and/or handle signals.
  • fork again & let the parent process terminate to ensure that you get rid of the session leading process. (Only session leaders may get a TTY again.)
  • chdir — Change the working directory of the daemon.
  • umask — Change the file mode mask according to the needs of the daemon.
  • close — Close all open file descriptors that may be inherited from the parent process.

Look at this skeleton code that shows the basic steps:

/*
* daemonize.c
* This example daemonizes a process, writes a few log messages,
* sleeps 20 seconds and terminates afterwards.
* This is an answer to the stackoverflow question:
* https://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux/17955149#17955149
* Fork this code: https://github.com/pasce/daemon-skeleton-linux-c
* Read about it: https://nullraum.net/how-to-create-a-daemon-in-c/
*/
    
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>
   
static void skeleton_daemon()
{
    pid_t pid;
    
    /* Fork off the parent process */
    pid = fork();
    
    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);
    
     /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);
    
    /* On success: The child process becomes session leader */
    if (setsid() < 0)
        exit(EXIT_FAILURE);
    
    /* Catch, ignore and handle signals */
    /*TODO: Implement a working signal handler */
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP, SIG_IGN);
    
    /* Fork off for the second time*/
    pid = fork();
    
    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);
    
    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);
    
    /* Set new file permissions */
    umask(0);
    
    /* Change the working directory to the root directory */
    /* or another appropriated directory */
    chdir("/");
    
    /* Close all open file descriptors */
    int x;
    for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
    {
        close (x);
    }
    
    /* Open the log file */
    openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
int main()
{
    skeleton_daemon();
    
    while (1)
    {
        //TODO: Insert daemon code here.
        syslog (LOG_NOTICE, "First daemon started.");
        sleep (20);
        break;
    }
   
    syslog (LOG_NOTICE, "First daemon terminated.");
    closelog();
    
    return EXIT_SUCCESS;
}

Compile and run

  • Compile the code: gcc -o firstdaemon daemonize.c
  • Start the daemon: ./firstdaemon
  • Check if everything is working properly: ps -xj | grep firstdaemon

Test the output

  • The output should be similar to this one:
+------+------+------+------+-----+-------+------+------+------+-----+
| PPID | PID  | PGID | SID  | TTY | TPGID | STAT | UID  | TIME | CMD |
+------+------+------+------+-----+-------+------+------+------+-----+
|    1 | 3387 | 3386 | 3386 | ?   |    -1 | S    | 1000 | 0:00 | ./  |
+------+------+------+------+-----+-------+------+------+------+-----+

What you should see here is:

  • The daemon has no controlling terminal (TTY = ?)
  • The parent process ID (PPID) is 1 (The init process)
  • The PID != SID which means that our process is NOT the session leader
    (because of the second fork())
  • Because PID != SID our process can’t take control of a TTY again

Reading the syslog:

  • Locate your syslog file. Mine is here: /var/log/syslog

  • Do a: grep firstdaemon /var/log/syslog

  • The output should be similar to this one:

  firstdaemon[3387]: First daemon started.
  firstdaemon[3387]: First daemon terminated.

A note:
In reality you would also want to implement a signal handler and set up the logging properly (Files, log levels…).

Further reading:

  • Linux-UNIX-Programmierung — German

You cannot create a process in linux that cannot be killed. The root user (uid=0) can send a signal to a process, and there are two signals which cannot be caught, SIGKILL=9, SIGSTOP=19. And other signals (when uncaught) can also result in process termination.

You may want a more general daemonize function, where you can specify a name for your program/daemon, and a path to run your program (perhaps «/» or «/tmp»). You may also want to provide file(s) for stderr and stdout (and possibly a control path using stdin).

Here are the necessary includes:

#include <stdio.h>    //printf(3)
#include <stdlib.h>   //exit(3)
#include <unistd.h>   //fork(3), chdir(3), sysconf(3)
#include <signal.h>   //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h>   //syslog(3), openlog(3), closelog(3)

And here is a more general function,

int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
    if(!path) { path="/"; }
    if(!name) { name="medaemon"; }
    if(!infile) { infile="/dev/null"; }
    if(!outfile) { outfile="/dev/null"; }
    if(!errfile) { errfile="/dev/null"; }
    //printf("%s %s %s %sn",name,path,outfile,infile);
    pid_t child;
    //fork, detach from process group leader
    if( (child=fork())<0 ) { //failed fork
        fprintf(stderr,"error: failed forkn");
        exit(EXIT_FAILURE);
    }
    if (child>0) { //parent
        exit(EXIT_SUCCESS);
    }
    if( setsid()<0 ) { //failed to become session leader
        fprintf(stderr,"error: failed setsidn");
        exit(EXIT_FAILURE);
    }

    //catch/ignore signals
    signal(SIGCHLD,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    //fork second time
    if ( (child=fork())<0) { //failed fork
        fprintf(stderr,"error: failed forkn");
        exit(EXIT_FAILURE);
    }
    if( child>0 ) { //parent
        exit(EXIT_SUCCESS);
    }

    //new file permissions
    umask(0);
    //change to path directory
    chdir(path);

    //Close all open file descriptors
    int fd;
    for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
    {
        close(fd);
    }

    //reopen stdin, stdout, stderr
    stdin=fopen(infile,"r");   //fd=0
    stdout=fopen(outfile,"w+");  //fd=1
    stderr=fopen(errfile,"w+");  //fd=2

    //open syslog
    openlog(name,LOG_PID,LOG_DAEMON);
    return(0);
}

Here is a sample program, which becomes a daemon, hangs around, and then leaves.

int
main()
{
    int res;
    int ttl=120;
    int delay=5;
    if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
        fprintf(stderr,"error: daemonize failedn");
        exit(EXIT_FAILURE);
    }
    while( ttl>0 ) {
        //daemon code here
        syslog(LOG_NOTICE,"daemon ttl %d",ttl);
        sleep(delay);
        ttl-=delay;
    }
    syslog(LOG_NOTICE,"daemon ttl expired");
    closelog();
    return(EXIT_SUCCESS);
}

Note that SIG_IGN indicates to catch and ignore the signal. You could build a signal handler that can log signal receipt, and set flags (such as a flag to indicate graceful shutdown).

Reading Time: 3 minutes

Hello readers, in this blog we will be looking at what are daemons and how can we create a custom daemons in our systems. Daemon is called as a type of program which quietly runs in the background rather than under the direct control of a user. It means that a daemon does not interact with the user.

Systemd

Management of daemons is done using systemd. It is a system and service manager for Linux operating systems. It is designed to be backwards compatible with SysV init scripts, and provides a number of features such as parallel startup of system services at boot time, on-demand activation of daemons, or dependency-based service control logic.

Units

Systemd introduced us with the concept of systemd units. These units are represented by unit configuration files located in one of the directories listed below:

Directory Description
/usr/lib/systemd/system/ Systemd unit files distributed with installed RPM packages.
/run/systemd/system/ Systemd unit files created at run time. This directory takes precedence over the directory with installed service unit files.
/etc/systemd/system/ Systemd unit files created by systemctl enable as well as unit files added for extending a service. This directory takes precedence over the directory with runtime unit files.

We have multiple unit types available to us. The below table gives a brief description for each of them.

Unit Type File Extension Description
Service unit .service A system service.
Target unit .target A group of systemd units.
Automount unit .automount A file system automount point.
Device unit .device A device file recognized by the kernel.
Mount unit .mount A file system mount point.
Path unit .path A file or directory in a file system.
Scope unit .scope An externally created process.
Slice unit .slice A group of hierarchically organized units that manage system processes.
Snapshot unit .snapshot A saved state of the systemd manager.
Socket unit .socket An inter-process communication socket.
Swap unit .swap A swap device or a swap file.
Timer unit .timer A systemd timer.

In this blog, we will be looking at Service unit and how to use them to create daemons.

Creating Our Own Daemon

At many times we will want to create our own services for different purposes. For this blog, we will be using a Java application, packaged as a jar file and then we will make it run as a service.

Step 1: JAR File

The first step is to acquire a jar file. We have used a jar file which has implemented a few routes in it.

Step 2: Script

Secondly, we will be creating a bash script that will be running our jar file. Note that there is no problem in using the jar file directly in the unit file, but it is considered a good practice to call it from a script. It is also recommended to store our jar files and bash script in /usr/bin directory even though we can use it from any location on our systems.

#!/bin/bash
/usr/bin/java -jar <name-of-jar-file>.jar

Make sure that you make this script executable before running it: chmod +x <script-name>.sh

Step 3: Units File

Now that we have created an executable script, we will be using it into make our service.

We have here a very basic .service unit file.

[Unit]
Description=A Simple Java Service

[Service]
WorkingDirectory=/usr/bin
ExecStart= /bin/bash /usr/bin/java-app.sh
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

In this file, the Description tag is used to give some detail about our service when someone will want to see the status of the service.
The WorkingDirectory is used to give path of our executables.
ExecStart tag is used to execute the command when we start the service.
The Restart tag configures whether the service shall be restarted when the service process exits, is killed, or a timeout is reached.
multi-user.target normally defines a system state where all network services are started up and the system will accept logins, but a local GUI is not started. This is the typical default system state for server systems, which might be rack-mounted headless systems in a remote server room.

Step 4: Starting Our Daemon Service

Let us now look at the commands which we will use to run our custom daemon.

sudo systemctl daemon-reload
# Uncomment the below line to start your service at the time of system boot
# sudo systemctl enable <name-of-service>.service
sudo systemctl start <name-of-service>
# OR
# sudo service <name-of-service> start
sudo systemctl status <name-of-service>
# OR
# sudo service <name-of-service> status

Conclusion

In this blog, we have looked how to make custom daemons and check their status as well. Also, we observed that it is fairly easy to make these daemons and use them. We hope that everyone is now comfortable enough to make daemons on their own.

References:

https://dzone.com/articles/run-your-java-application-as-a-service-on-ubuntu
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/system_administrators_guide/chap-managing_services_with_systemd


Тема написание демонов широко освещена в интернете (Гугл), так что написанное тут не будет ни для кого секретом.
Просто опишу здесь пример реализации демона, выполняющий каждые 10 минут произвольную команду linux (в данном случае — who, список подключенных пользователей) и записывающий результат в лог.
Код на Си(c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>

int Daemon(void);
char* getTime();
int writeLog(char msg[256]);
char* getCommand(char command[128]);

char* getTime() { //функция возвращает форматированную дату и время
    time_t now;
    struct tm *ptr;
    static char tbuf[64];
    bzero(tbuf,64);
    time(&now);
    ptr = localtime(&now);
    strftime(tbuf,64, "%Y-%m-%e %H:%M:%S", ptr);
    return tbuf;
}

char* getCommand(char command[128]) { //функция возвращает результат выполнения linux команды
    FILE *pCom;
    static char comText[256];
    bzero(comText, 256);
    char  buf[64];
    pCom = popen(command, "r"); //выполняем
    if(pCom == NULL) {
        writeLog("Error Command");
        return "";
    }
    strcpy(comText, "");
    while(fgets(buf, 64, pCom) != NULL) { //читаем результат
        strcat(comText, buf);
    }
    pclose(pCom);
    return comText;
}

int writeLog(char msg[256]) { //функция записи строки в лог
    FILE * pLog;
    pLog = fopen("/home/CENTRAL/skan/daemon/daemon.log", "a");
    if(pLog == NULL) {
        return 1;
    }
    char str[312];
    bzero(str, 312);
    strcpy(str, getTime());
    strcat(str, " ==========================n");
    strcat(str, msg);
    strcat(str, "n");
    fputs(str, pLog);
    //fwrite(msg, 1, sizeof(msg), pLog);
    fclose(pLog);
    return 0;
}

int main(int argc, char* argv[]) {
    writeLog("Daemon Start");

    pid_t parpid, sid;
    
    parpid = fork(); //создаем дочерний процесс
    if(parpid < 0) {
        exit(1);
    } else if(parpid != 0) {
        exit(0);
    } 
    umask(0);//даем права на работу с фс
    sid = setsid();//генерируем уникальный индекс процесса
    if(sid < 0) {
        exit(1);
    }
    if((chdir("/")) < 0) {//выходим в корень фс
        exit(1);
    }
    close(STDIN_FILENO);//закрываем доступ к стандартным потокам ввода-вывода
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    
    return Daemon();
}

int Daemon(void) { //собственно наш бесконечный цикл демона
    char *log;
    while(1) {
        log = getCommand("who");
        if(strlen(log) > 5) { //если в онлайне кто-то есть, то пишем в лог
          writeLog(log);
        }
        sleep(600);//ждем 10 минут до следующей итерации
    }
    return 0;
}

Like this post? Please share to your friends:
  • Как написать делягину михаилу письмо
  • Как написать дельту на клавиатуре
  • Как написать дельта на клавиатуре компьютера
  • Как написать деловой текст
  • Как написать деловой email на английском