Class: Servolux::Daemon

Overview

Synopsis

The Daemon takes care of the work of creating and managing daemon processes
from Ruby.

Details

A daemon process is a long running process on a UNIX system that is
detached from a TTY – i.e. it is not tied to a user session. These types of
processes are notoriously difficult to setup correctly. This Daemon class
encapsulates some best practices to ensure daemons startup properly and can
be shutdown gracefully.

Starting a daemon process involves forking a child process, setting the
child as a session leader, forking again, and detaching from the current
working directory and standard in/out/error file descriptors. Because of
this separation between the parent process and the daemon process, it is
difficult to know if the daemon started properly.

The Daemon class opens a pipe between the parent and the daemon. The PID of
the daemon is sent to the parent through this pipe. The PID is used to
check if the daemon is alive. Along with the PID, any errors from the
daemon process are marshalled through the pipe back to the parent. These
errors are wrapped in a StartupError and then raised in the parent.

If no errors are passed up the pipe, the parent process waits till the
daemon starts. This is determined by sending a signal to the daemon
process.

If a log file is given to the Daemon instance, then it is monitored for a
change in size and mtime. This lets the Daemon instance know that the
daemon process is updating the log file. Furthermore, the log file can be
watched for a specific pattern; this pattern signals that the daemon
process is up and running.

Shutting down the daemon process is a little simpler. An external shutdown
command can be used, or the Daemon instance will send an INT or TERM signal
to the daemon process.

Again, the Daemon instance will wait till the daemon process shuts down.
This is determined by attempting to signal the daemon process PID and then
returning when this signal fails – i.e. then the daemon process has died.

Examples

Bad Example

This is a bad example. The daemon will not start because the startup
command “/usr/bin/no-command-by-this-name” cannot be found on the file
system. The daemon process will send an Errno::ENOENT through the pipe back
to the parent which gets wrapped in a StartupError

Assign the startup command. Different calling semantics are used for each
type of command. See the startup_command method
for more details. [required]

:timeout(Numeric)
— default:
30
—

The time (in seconds) to wait for the daemon process to either startup or
shutdown. An error is raised when this timeout is exceeded.

:nochdir(Boolean)
— default:
false
—

When set to true this flag directs the daemon process to keep the current
working directory. By default, the process of daemonizing will cause the
current working directory to be changed to the root folder (thus preventing
the daemon process from holding onto the directory inode).

:noclose(Boolean)
— default:
false
—

When set to true this flag keeps the standard input/output streams from
being reopened to /dev/null when the daemon process is created. Reopening
the standard input/output streams frees the file descriptors which are
still being used by the parent process. This prevents zombie processes.

Assign the shutdown command. Different calling semantics are used for each
type of command.

:log_file(String)
— default:
nil
—

This log file will be monitored to determine if the daemon process has
successfully started.

:look_for(String, Regexp)
— default:
nil
—

This can be either a String or a Regexp. It defines a phrase to search for
in the log_file. When the daemon process is started, the parent process
will not return until this phrase is found in the log file. This is a
useful check for determining if the daemon process is fully started.

:after_fork(Proc, lambda)
— default:
nil
—

This proc will be called in the child process immediately after forking.

:before_exec(Proc, lambda)
— default:
nil
—

This proc will be called in the child process immediately before calling
`exec` to execute the desired process. This proc will be called after the
:after_fork proc if present.

#timeout ⇒ Object

Instance Method Details

#alive? ⇒ Boolean

Returns true if the daemon process is currently running. Returns
false if this is not the case. The status of the process is
determined by sending a signal to the process identified by the
pid_file.

291
292
293
294
295
296
297
298
299
300
301

# File 'lib/servolux/daemon.rb', line 291defalive?pid=retrieve_pidProcess.kill(0,pid)truerescueErrno::ESRCH,Errno::ENOENTfalserescueErrno::EACCES=>errlogger.error"You do not have access to the PID file at " \
"#{pid_file.inspect}: #{err.message}"falseend

# File 'lib/servolux/daemon.rb', line 241defstartup(do_exit=true)raiseError,"Fork is not supported in this Ruby environment."unless::Servolux.fork?returnifalive?logger.debug"About to fork ..."@piper=::Servolux::Piper.daemon(nochdir,noclose)# Make sure we have an idea of the state of the log file BEFORE the child# gets a chance to write to it.@logfile_reader.updated?if@logfile_reader@piper.parent{@piper.timeout=0.1wait_for_startupexit!(0)ifdo_exit}@piper.child{run_startup_command}selfend