Приглашаем посетить
Добычин (dobychin.lit-info.ru)

Writing Daemons

Previous
Table of Contents
Next

Writing Daemons

A daemon is a process that runs in the background, which means that once it is started, it takes no input from the user's terminal and does not exit when the user's session ends.

Once started, daemons traditionally run forever (or until stopped) to perform recurrent tasks or to handle tasks that might last beyond the length of the user's session. The Apache Web server, sendmail, and the cron daemon crond are examples of common daemons that may be running on your system. Daemonizing scripts is useful for handling long jobs and recurrent back-end tasks.

To successfully be daemonized, a process needs to complete the two following tasks:

  • Process detachment

  • Process independence

In addition, a well-written daemon may optionally perform the following:

  • Setting its working directory

  • Dropping privileges

  • Guaranteeing exclusivity

You learned about process detachment earlier in this chapter, in the section "Creating and Managing Child Processes." The logic is the same as for daemonizing processes, except that you want to end the parent process so that the only running process is detached from the shell. To do this, you execute pnctl_fork() and exit if you are in the parent process (that is, if the return value is greater than zero).

In Unix systems, processes are associated with process groups, so if you kill the leader of a process group, all its associates will terminate as well. The parent process for everything you start in your shell is your shell's process. Thus, if you create a new process with fork() and do nothing else, the process will still exit when you close the shell. To avoid having this happen, you need the forked process to disassociate itself from its parent process. This is accomplished by calling pcntl_setsid(), which makes the calling process the leader of its own process group.

Finally, to sever any ties between the parent and the child, you need to fork the process a second time. This completes the detachment process. In code, this detachment process looks like this:

if(pcntl_fork()) {
  exit;
}
pcntl_setsid();
if(pcntl_fork()) {
  exit;
}
# process is now completely daemonized

It is important for the parent to exit after both calls to pcntl_fork(); otherwise, multiple processes will be executing the same code.

Changing the Working Directory

When you're writing a daemon, it is usually advisable to have it set its own working directory. That way, if you read from or write to any files via a relative path, they will be in the place you expect them to be. Always qualifying your paths is of course a good practice in and of itself, but so is defensive coding. The safest way to change your working directory is to use not only chdir(), but to use chroot() as well.

chroot() is available inside the PHP CLI and CGI versions and requires the program to be running as root. chroot() actually changes the root directory for the process to the specified directory. This makes it impossible to execute any files that do not lie within that directory. chroot() is often used by servers as a security device to ensure that it is impossible for malicious code to modify files outside a specific directory. Keep in mind that while chroot() prevents you from accessing any files outside your new directory, any currently open file resources can still be accessed. For example, the following code opens a logfile, calls chroot() to switch to a data directory, and can still successfully log to the open file resource:

<?php

$logfile = fopen("/var/log/chroot.log", "w");
chroot("/Users/george");
fputs($logfile, "Hello From Inside The Chroot\n");

?>

If chroot() is not acceptable for an application, you can call chdir() to set the working directory. This is useful, for instance, if the code needs to load code that can be located anywhere on the system. Note that chdir() provides no security to prevent opening of unauthorized filesonly symbolic protection against sloppy coding.

Giving Up Privileges

A classic security precaution when writing Unix daemons is having them drop all unneeded privileges. Like being able to access files outside where they need to be, possessing unneeded privileges is a recipe for trouble. In the event that the code (or PHP itself) has an exploitable flaw, you can minimize damage by ensuring that a daemon is running as a user with minimal rights to alter files on the system.

One way to approach this is to simply execute the daemon as the unprivileged user. This is usually inadequate if the program needs to initially open resources (logfiles, data files, sockets, and so on) that the unprivileged user does not have rights to.

If you are running as the root user, you can drop your privileges by using the posix_setuid() and posiz_setgid() functions. Here is an example that changes the running program's privileges to those of the user nobody:

$pw= posix_getpwnam('nobody');
posix_setuid($pw['uid']);
posix_setgid($pw['gid']);

As with chroot(), any privileged resources that were open prior to dropping privileges remain open, but new ones cannot be created.

Guaranteeing Exclusivity

You often want to require that only one instance of a script can be running at any given time. For daemonizing scripts, this is especially important because running in the background makes it easy to accidentally invoke instances multiple times.

The standard technique for guaranteeing exclusivity is to have scripts lock a specific file (often a lockfile, used exclusively for that purpose) by using flock(). If the lock fails, the script should exit with an error. Here's an example:

$fp = fopen("/tmp/.lockfile", "a");
if(!$fp || !flock($fp, LOCK_EX | LOCK_NB)) {
  fputs(STDERR, "Failed to acquire lock\n");
  exit;
}
/* lock successful safe to perform work */

Locking mechanisms are discussed in greater depth in Chapter 10, "Data Component Caching."


Previous
Table of Contents
Next