Linux Daemon Writing HOWTO

来源:百度文库 编辑:神马文学网 时间:2024/04/19 17:32:35
Devin Watson
v1.0, May 2004This document shows how to write a daemon in Linux using GCC. Knowledge ofLinux and a familiarity with C are necessary to use this document. This HOWTO isCopyright by Devin Watson, under the terms of the BSD License.
Introduction:What is a Daemon?
GettingStarted
PlanningYour Daemon
What Is It Going To Do?
How Much Interaction?
BasicDaemon Structure
Forking The Parent Process
Changing The File Mode Mask (Umask)
Opening Logs For Writing
Creating a Unique Session ID (SID)
Changing The Working Directory
Closing Standard File Descriptors
Writingthe Daemon Code
Initialization
The Big Loop
PuttingIt All Together
Complete Sample
Introduction:What is a Daemon?
A daemon (or service) is a background process that is designed to runautonomously,with little or not user intervention. The Apache web server httpdaemon (httpd) is one such example of a daemon. It waits in the backgroundlistening on specific ports, and serves up pages or processes scripts, based onthe type of request.
Creating a daemon in Linux uses a specific set of rules in a given order.Knowing how they work will help you understand how daemons operate in userlandLinux, but can operate with calls to the kernel also. In fact, a few daemonsinterface with kernel modules that work with hardware devices, such as externalcontroller boards, printers,and PDAs. They are one of the fundamental buildingblocks in Linux that give it incredible flexibility and power.
Throughout this HOWTO, a very simple daemon will be built in C. As we goalong, more code will be added, showing the proper order of execution requiredto get a daemon up and running.
GettingStarted
First off, you‘ll need the following packages installed on your Linux machineto develop daemons, specifically:
GCC 3.2.2 or higher
Linux Development headers and libraries
If your system does not already have these installed (not likely, but checkanyway), you‘ll need them to develop the examples in this HOWTO. To find outwhat version of GCC you have installed, use:
gcc --version
PlanningYour Daemon
WhatIs It Going To Do?
A daemon should do one thing, and do it well. That one thing may be ascomplex as managing hundreds of mailboxes on multiple domains, or as simple aswriting a report and calling sendmail to mail it out to an admin.
In any case, you should have a good plan going in what the daemon should do.If it is going to interoperate with some other daemons that you may or may notbe writing, this is something else to consider as well.
HowMuch Interaction?
Daemons should never have direct communication with a user through aterminal. In fact, a daemon shouldn‘t communicate directly with a user at all.All communication should pass through some sort of interface (which you may ormay not have to write), which can be as complex as a GTK+ GUI, or as simple as asignal set.
BasicDaemon Structure
When a daemon starts up, it has to do some low-level housework to get itselfready for its real job. This involves a few steps:
Fork off the parent process
Change file mode mask (umask)
Open any logs for writing
Create a unique Session ID (SID)
Change the current working directory to a safe place
Close standard file descriptors
Enter actual daemon code
ForkingThe Parent Process
A daemon is started either by the system itself or a user in a terminal orscript. When it does start, the process is just like any other executable on thesystem. To make it truly autonomous, a child process must be createdwhere the actual code is executed. This is known as forking, and it uses thefork() function:
pid_t pid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
Notice the error check right after the call to fork(). When writinga daemon, you will have to code as defensively as possible. In fact, a goodpercentage of the total code in a daemon consists of nothing but error checking.
The fork() function returns either the process id (PID) of the childprocess (not equal to zero), or -1 on failure. If the process cannot fork achild, then the daemon should terminate right here.
If the PID returned from fork() did succeed, the parent process mustexit gracefully. This may seem strange to anyone who hasn‘t seen it, but byforking, the child process continues the execution from here on out in thecode.
ChangingThe File Mode Mask (Umask)
In order to write to any files (including logs) created by the daemon, thefile mode mask (umask) must be changed to ensure that they can be written to orread from properly. This is similar to running umask from the command line, butwe do it programmatically here. We can use the umask() function toaccomplish this:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
/* Log failure (use syslog if possible) */
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
By setting the umask to 0, we will have full access to the files generated bythe daemon. Even if you aren‘t planning on using any files, it is a good idea toset the umask here anyway, just in case you will be accessing files on thefilesystem.
OpeningLogs For Writing
This part is optional, but it is recommended that you open a log filesomewhere in the system for writing. This may be the only place you can look fordebug information about your daemon.
Creatinga Unique Session ID (SID)
From here, the child process must get a unique SID from the kernel in orderto operate. Otherwise, the child process becomes an orphan in the system. Thepid_t type, declared in the previous section, is also used to create a new SIDfor the child process:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failure */
exit(EXIT_FAILURE);
}
Again, the setsid() function has the same return type asfork(). We can apply the same error-checking routine here to see if thefunction created the SID for the child process.
ChangingThe Working Directory
The current working directory should be changed to some place that isguaranteed to always be there. Since many Linux distributions do not completelyfollow the Linux Filesystem Hierarchy standard, the only directory that isguaranteed to be there is the root (/). We can do this using thechdir() function:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failure here */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log any failure here */
exit(EXIT_FAILURE);
}
Once again, you can see the defensive coding taking place. Thechdir() function returns -1 on failure, so be sure to check for thatafter changing to the root directory within the daemon.
ClosingStandard File Descriptors
One of the last steps in setting up a daemon is closing out the standard filedescriptors (STDIN, STDOUT, STDERR). Since a daemon cannot use the terminal,these file descriptors are redundant and a potential security hazard.
The close() function can handle this for us:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failure here */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log any failure here */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
It‘s a good idea to stick with the constants defined for the filedescriptors, for the greatest portability between system versions.
Writingthe Daemon Code
Initialization
At this point, you have basically told Linux that you‘re a daemon, so nowit‘s time to write the actual daemon code. Initialization is the first stephere. Since there can be a multitude of different functions that can be calledhere to set up your daemon‘s task, I won‘t go too deep into here.
The big point here is that, when initializing anything in a daemon, the samedefensive coding guidelines apply here. Be as verbose as possible when writingeither to the syslog or your own logs. Debugging a daemon can be quite difficultwhen there isn‘t enough information available as to the status of thedaemon.
TheBig Loop
A daemon‘s main code is typically inside of an infinite loop. Technically, itisn‘t an infinite loop, but it is structured as one:
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log any failures here */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log any failures here */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Daemon-specific initialization goes here */
/* The Big Loop */
while (1) {
/* Do some task here ... */
sleep(30); /* wait 30 seconds */
}
This typical loop is usually a while loop that has an infiniteterminating condition, with a call to sleep in there to make it run atspecified intervals.
Think of it like a heartbeat: when your heart beats, it performs a few tasks,then waits until the next beat takes place. Many daemons follow this samemethodology.
PuttingIt All Together
CompleteSample
Listed below is a complete sample daemon that shows all of the stepsnecessary for setup and execution. To run this, simply compile using gcc, andstart execution from the command line. To terminate, use the kill commandafter finding its PID.
I‘ve also put in the correct include statements for interfacing with thesyslog, which is recommended at the very least for sending start/stop/pause/dielog statements, in addition to using your own logs with thefopen()/fwrite()/fclose() function calls.
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(void) {
/* Our process ID and Session ID */
pid_t pid, sid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
exit(EXIT_FAILURE);
}
/* If we got a good PID, then
we can exit the parent process. */
if (pid > 0) {
exit(EXIT_SUCCESS);
}
/* Change the file mode mask */
umask(0);
/* Open any logs here */
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Change the current working directory */
if ((chdir("/")) < 0) {
/* Log the failure */
exit(EXIT_FAILURE);
}
/* Close out the standard file descriptors */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
/* Daemon-specific initialization goes here */
/* The Big Loop */
while (1) {
/* Do some task here ... */
sleep(30); /* wait 30 seconds */
}
exit(EXIT_SUCCESS);
}
From here, you can use this skeleton to write your own daemons. Be sure toadd in your own logging (or use the syslog facility), and code defensively, codedefensively, code defensively!