Class: SimpleDaemon
- Inherits:
-
Object
- Object
- SimpleDaemon
- Defined in:
- lib/flick/simple_daemon.rb
Overview
Double-forking Unix daemon
- Author
-
Rufus Post ([email protected])
How does it work?
According to Stevens’s Advanced Programming in the UNIX Environment chapter 13, this is the procedure to make a well-behaved Unix daemon:
Fork and have the parent exit. This makes the shell or boot script think the command is done. Also, the child process is guaranteed not to be a process group leader (a prerequisite for setsid next) Call setsid to create a new session. This does three things:
-
The process becomes a session leader of a new session
-
The process becomes the process group leader of a new process group
-
The process has no controlling terminal
Optionally fork again and have the parent exit. This guarantes that the daemon is not a session leader nor can it acquire a controlling terminal (under SVR4)
grandparent - the current process
\_ parent - exits immediately
\_ simple daemon - writes out its pid to file
Change the current working directory to / to avoid interfering with mounting and unmounting. By default don’t bother to chdir(“/”) here because we might to run inside APP_ROOT.
Set file mode creation mask to 000 to allow creation of files with any required permission later. By default umask is whatever was set by the parent process at startup and can be set in config.ru and config_file, so making it 0000 and potentially exposing sensitive log data can be bad policy.
Close unneeded file descriptors inherited from the parent (there is no controlling terminal anyway): stdout, stderr, and stdin.
Nowadays there is a file to track the PID which is used heavily by Linux distribution boot scripts. Be sure to write out the PID of the grandchild, either the return value of the second fork (step 3) or the value of getpid() after step 3.
Class Method Summary collapse
-
.daemonize!(out = '/dev/null', err = '/dev/null', safe = true) ⇒ Object
In the directory where you want your daemon add a git submodule to your project and create a daemon launcher script:.
-
.kill_pid ⇒ Object
Try and read the existing pid from the pid file and signal HUP to process.
-
.redirect(out, err) ⇒ Object
Redirect file descriptors inherited from the parent.
-
.write(pid, pidfile) ⇒ Object
Attempts to write the pid of the forked process to the pid file.
Class Method Details
.daemonize!(out = '/dev/null', err = '/dev/null', safe = true) ⇒ Object
In the directory where you want your daemon add a git submodule to your project and create a daemon launcher script:
#!/usr/bin/env ruby
require 'simple_daemon'
$0 = "my daemon"
SimpleDaemon.daemonize! ARGV[0], ARGV[1], ARGV[2]
loop do
sleep 5
puts "tick"
sleep 5
puts "tock"
end
make your script executable and run:
$ chmod +x launcher
$ launcher tmp/daemon.pid log/daemon.stdout.log log/daemon.stderr.log
check that it is running by with the following:
$ ps aux | grep "my daemon"
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/flick/simple_daemon.rb', line 79 def self.daemonize! out = '/dev/null', err = '/dev/null', safe = true raise 'First fork failed' if (pid = fork) == -1 exit unless pid.nil? Process.setsid raise 'Second fork failed' if (pid = fork) == -1 exit unless pid.nil? kill_pid @file = Tempfile.new("flick-temp") @file.write Process.pid @file.rewind unless safe Dir.chdir '/' File.umask 0000 end redirect out, err end |
.kill_pid ⇒ Object
Try and read the existing pid from the pid file and signal HUP to process.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/flick/simple_daemon.rb', line 110 def self.kill_pid unless @file.nil? opid = @file.read Process.kill "HUP", opid @file.unlink end rescue TypeError $stdout.puts "#{pidfile} was empty: TypeError" rescue Errno::ENOENT $stdout.puts "#{pidfile} did not exist: Errno::ENOENT" rescue Errno::ESRCH $stdout.puts "The process #{opid} did not exist: Errno::ESRCH" rescue Errno::EPERM raise "Lack of privileges to manage the process #{opid}: Errno::EPERM" rescue ::Exception => e raise "While signaling the PID, unexpected #{e.class}: #{e}" end |
.redirect(out, err) ⇒ Object
Redirect file descriptors inherited from the parent.
129 130 131 132 133 134 |
# File 'lib/flick/simple_daemon.rb', line 129 def self.redirect out, err $stdin.reopen '/dev/null' $stdout.reopen File.new(out, "a") $stderr.reopen File.new(err, "a") $stdout.sync = $stderr.sync = true end |
.write(pid, pidfile) ⇒ Object
Attempts to write the pid of the forked process to the pid file. Kills process if write unsuccesfull.
98 99 100 101 102 103 104 105 106 |
# File 'lib/flick/simple_daemon.rb', line 98 def self.write pid, pidfile File.open pidfile, "w" do |f| f.write pid f.close end $stdout.puts "Daemon running with pid: #{pid}" rescue ::Exception => e raise "While writing the PID to file, unexpected #{e.class}: #{e}" end |