daemons 1.1.9 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -1
  3. data/README.md +206 -0
  4. data/Releases +17 -0
  5. data/examples/call/call.rb +13 -16
  6. data/examples/call/call_monitor.rb +13 -17
  7. data/examples/daemonize/daemonize.rb +4 -8
  8. data/examples/run/ctrl_crash.rb +0 -1
  9. data/examples/run/ctrl_custom_logfiles.rb +18 -0
  10. data/examples/run/ctrl_exec.rb +0 -1
  11. data/examples/run/ctrl_exit.rb +0 -1
  12. data/examples/run/ctrl_keep_pid_files.rb +1 -3
  13. data/examples/run/ctrl_monitor.rb +0 -1
  14. data/examples/run/ctrl_monitor_multiple.rb +17 -0
  15. data/examples/run/ctrl_multiple.rb +0 -1
  16. data/examples/run/ctrl_ontop.rb +0 -1
  17. data/examples/run/ctrl_optionparser.rb +4 -6
  18. data/examples/run/ctrl_proc.rb +8 -9
  19. data/examples/run/ctrl_proc_multiple.rb +4 -6
  20. data/examples/run/ctrl_proc_rand.rb +2 -4
  21. data/examples/run/ctrl_proc_simple.rb +0 -1
  22. data/examples/run/myserver.rb +0 -1
  23. data/examples/run/myserver_crashing.rb +5 -5
  24. data/examples/run/myserver_exiting.rb +2 -2
  25. data/examples/run/myserver_hanging.rb +4 -5
  26. data/examples/run/myserver_slowstop.rb +5 -6
  27. data/lib/daemons.rb +66 -68
  28. data/lib/daemons/application.rb +171 -188
  29. data/lib/daemons/application_group.rb +99 -92
  30. data/lib/daemons/change_privilege.rb +3 -3
  31. data/lib/daemons/cmdline.rb +43 -54
  32. data/lib/daemons/controller.rb +36 -53
  33. data/lib/daemons/daemonize.rb +54 -64
  34. data/lib/daemons/etc_extension.rb +3 -2
  35. data/lib/daemons/exceptions.rb +10 -11
  36. data/lib/daemons/monitor.rb +60 -62
  37. data/lib/daemons/pid.rb +24 -56
  38. data/lib/daemons/pidfile.rb +38 -40
  39. data/lib/daemons/pidmem.rb +5 -9
  40. data/lib/daemons/version.rb +3 -0
  41. metadata +45 -45
  42. data/README +0 -214
  43. data/Rakefile +0 -90
  44. data/TODO +0 -2
  45. data/setup.rb +0 -1360
data/lib/daemons/pid.rb CHANGED
@@ -1,12 +1,10 @@
1
-
1
+ require 'daemons/exceptions'
2
2
 
3
3
  module Daemons
4
-
5
4
  class Pid
6
-
7
- def Pid.running?(pid)
5
+ def self.running?(pid)
8
6
  return false unless pid
9
-
7
+
10
8
  # Check if process is in existence
11
9
  # The simplest way to do this is to send signal '0'
12
10
  # (which is a single system call) that doesn't actually
@@ -14,95 +12,65 @@ module Daemons
14
12
  begin
15
13
  Process.kill(0, pid)
16
14
  return true
15
+ rescue TimeoutError
16
+ raise
17
17
  rescue Errno::ESRCH
18
18
  return false
19
19
  rescue ::Exception # for example on EPERM (process exists but does not belong to us)
20
20
  return true
21
- #rescue Errno::EPERM
21
+ # rescue Errno::EPERM
22
22
  # return false
23
23
  end
24
24
  end
25
-
26
- # def Pid.running?(pid, additional = nil)
27
- # match_pid = Regexp.new("^\\s*#{pid}\\s")
28
- # got_match = false
29
- #
30
- # #ps_all = IO.popen('ps ax') # the correct syntax is without a dash (-) !
31
- # ps_in, ps_out, ps_err = Open3.popen3('ps ax') # the correct syntax is without a dash (-) !
32
- #
33
- # return true unless ps_out.gets
34
- #
35
- # begin
36
- # ps_out.each { |psline|
37
- # next unless psline =~ match_pid
38
- # got_match = true
39
- # got_match = false if additional and psline !~ /#{additional}/
40
- # break
41
- # }
42
- # ensure
43
- # begin; begin; ps_in.close; rescue ::Exception; end; begin; ps_out.close; rescue ::Exception; end; ps_err.close; rescue ::Exception; end
44
- # end
45
- #
46
- # # an alternative would be to use the code below, but I don't know whether this is portable
47
- # # `ps axo pid=`.split.include? pid.to_s
48
- #
49
- # return got_match
50
- # end
51
-
52
-
53
-
25
+
54
26
  # Returns the directory that should be used to write the pid file to
55
27
  # depending on the given mode.
56
- #
57
- # Some modes may require an additionaly hint, others may determine
28
+ #
29
+ # Some modes may require an additionaly hint, others may determine
58
30
  # the directory automatically.
59
31
  #
60
32
  # If no valid directory is found, returns nil.
61
33
  #
62
- def Pid.dir(dir_mode, dir, script)
34
+ def self.dir(dir_mode, dir, script)
63
35
  # nil script parameter is allowed as long as dir_mode is not :script
64
- return nil if dir_mode == :script && script.nil?
65
-
36
+ return nil if dir_mode == :script && script.nil?
37
+
66
38
  case dir_mode
67
39
  when :normal
68
40
  return File.expand_path(dir)
69
41
  when :script
70
- return File.expand_path(File.join(File.dirname(script),dir))
71
- when :system
42
+ return File.expand_path(File.join(File.dirname(script), dir))
43
+ when :system
72
44
  return '/var/run'
73
45
  else
74
- raise Error.new("pid file mode '#{dir_mode}' not implemented")
46
+ fail Error.new("pid file mode '#{dir_mode}' not implemented")
75
47
  end
76
48
  end
77
-
49
+
78
50
  # Initialization method
79
51
  def initialize
80
52
  end
81
-
82
-
53
+
83
54
  # Get method
84
55
  def pid
85
56
  end
86
-
57
+
87
58
  # Set method
88
59
  def pid=(p)
89
60
  end
90
-
61
+
91
62
  # Check whether the process is running
92
63
  def running?
93
- return Pid.running?(pid())
64
+ Pid.running?(pid)
94
65
  end
95
-
66
+
96
67
  # Cleanup method
97
68
  def cleanup
98
69
  end
99
-
70
+
100
71
  # Exist? method
101
72
  def exist?
102
73
  true
103
74
  end
104
-
105
- end
106
-
107
-
108
- end
75
+ end
76
+ end
@@ -1,8 +1,6 @@
1
1
  require 'daemons/pid'
2
2
 
3
-
4
3
  module Daemons
5
-
6
4
  # === What is a Pid-File?
7
5
  # A <i>Pid-File</i> is a file containing the <i>process identification number</i>
8
6
  # (pid) that is stored in a well-defined location of the filesystem thus allowing other
@@ -20,9 +18,9 @@ module Daemons
20
18
  # run at any time)
21
19
  #
22
20
  # Each file just contains one line with the pid as string (for example <tt>6432</tt>).
23
- #
21
+ #
24
22
  # === Where are the Pid-Files stored?
25
- #
23
+ #
26
24
  # Daemons is configurable to store the Pid-Files relative to three different locations:
27
25
  # 1. in a directory relative to the directory where the script (the one that is supposed to run
28
26
  # as a daemon) resides (<tt>:script</tt> option for <tt>:dir_mode</tt>)
@@ -30,17 +28,17 @@ module Daemons
30
28
  # 3. in the preconfigured directory <tt>/var/run</tt> (<tt>:system</tt> option for <tt>:dir_mode</tt>)
31
29
  #
32
30
  class PidFile < Pid
33
-
34
31
  attr_reader :dir, :progname, :multiple, :number
35
32
 
36
- def PidFile.find_files(dir, progname, delete = false)
37
- files = Dir[File.join(dir, "#{progname}*.pid")]
38
-
39
- files.delete_if {|f| not (File.file?(f) and File.readable?(f))}
40
- if delete
33
+ def self.find_files(dir, progname, delete = false)
34
+ files = Dir[File.join(dir, "#{progname}_num*.pid")]
35
+ files = Dir[File.join(dir, "#{progname}.pid")] if files.size == 0
36
+
37
+ files.delete_if { |f| not (File.file?(f) and File.readable?(f)) }
38
+ if delete
41
39
  files.delete_if do |f|
42
- pid = File.open(f) {|h| h.read}.to_i
43
- rsl = ! Pid.running?(pid)
40
+ pid = File.open(f) { |h| h.read }.to_i
41
+ rsl = !Pid.running?(pid)
44
42
  if rsl
45
43
  puts "pid-file for killed process #{pid} found (#{f}), deleting."
46
44
  begin; File.unlink(f); rescue ::Exception; end
@@ -48,53 +46,53 @@ module Daemons
48
46
  rsl
49
47
  end
50
48
  end
51
-
52
- return files
49
+
50
+ files
53
51
  end
54
-
55
- def PidFile.existing(path)
52
+
53
+ def self.existing(path)
56
54
  new_instance = PidFile.allocate
57
-
55
+
58
56
  new_instance.instance_variable_set(:@path, path)
59
-
57
+
60
58
  def new_instance.filename
61
- return @path
59
+ @path
62
60
  end
63
-
64
- return new_instance
61
+
62
+ new_instance
65
63
  end
66
-
64
+
67
65
  def initialize(dir, progname, multiple = false)
68
66
  @dir = File.expand_path(dir)
69
67
  @progname = progname
70
68
  @multiple = multiple
71
69
  @number = nil
72
70
  @number = 0 if multiple
73
-
71
+
74
72
  if multiple
75
- while File.exist?(filename) and @number < 1024
73
+ while File.exist?(filename) && @number < 1024
76
74
  @number += 1
77
75
  end
78
-
79
- if @number == 1024
80
- raise RuntimeException('cannot run more than 1024 instances of the application')
76
+
77
+ if @number >= 1024
78
+ fail RuntimeException('cannot run more than 1024 instances of the application')
81
79
  end
82
80
  end
83
81
  end
84
-
82
+
85
83
  def filename
86
- File.join(@dir, "#{@progname}#{ @number or '' }.pid")
84
+ File.join(@dir, "#{@progname}#{@number ? '_num' + @number.to_s : '' }.pid")
87
85
  end
88
-
86
+
89
87
  def exist?
90
88
  File.exist? filename
91
89
  end
92
-
90
+
93
91
  def pid=(p)
94
- File.open(filename, 'w') {|f|
92
+ File.open(filename, 'w') do |f|
95
93
  f.chmod(0644)
96
- f.puts p #Process.pid
97
- }
94
+ f.puts p # Process.pid
95
+ end
98
96
  end
99
97
 
100
98
  def cleanup
@@ -103,14 +101,14 @@ module Daemons
103
101
 
104
102
  def pid
105
103
  begin
106
- File.open(filename) {|f|
107
- return f.gets.to_i
108
- }
104
+ File.open(filename) do |f|
105
+ p = f.gets.to_i
106
+ return nil if p == 0 # Otherwise an invalid pid file becomes pid 0
107
+ return p
108
+ end
109
109
  rescue ::Exception
110
110
  return nil
111
111
  end
112
112
  end
113
-
114
113
  end
115
-
116
- end
114
+ end
@@ -1,19 +1,15 @@
1
1
  require 'daemons/pid'
2
2
 
3
-
4
3
  module Daemons
5
-
6
4
  class PidMem < Pid
7
5
  attr_accessor :pid
8
-
9
- def PidMem.existing(numeric_pid)
6
+
7
+ def self.existing(numeric_pid)
10
8
  new_instance = PidMem.allocate
11
-
9
+
12
10
  new_instance.instance_variable_set(:@pid, numeric_pid)
13
-
14
- return new_instance
11
+
12
+ new_instance
15
13
  end
16
-
17
14
  end
18
-
19
15
  end
@@ -0,0 +1,3 @@
1
+ module Daemons
2
+ VERSION = '1.2.1'
3
+ end
metadata CHANGED
@@ -1,56 +1,45 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: daemons
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.9
5
- prerelease:
4
+ version: 1.2.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Thomas Uehlinger
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-08-10 00:00:00.000000000 Z
11
+ date: 2015-03-08 00:00:00.000000000 Z
13
12
  dependencies: []
14
- description: Daemons provides an easy way to wrap existing ruby scripts (for example
15
- a self-written server) to be run as a daemon and to be controlled by simple start/stop/restart
16
- commands. You can also call blocks as daemons and control them from the parent
17
- or just daemonize the current process. Besides this basic functionality, daemons
18
- offers many advanced features like exception backtracing and logging (in case your
19
- ruby script crashes) and monitoring and automatic restarting of your processes if
20
- they crash.
21
- email: th.uehlinger@gmx.ch
13
+ description: |2
14
+ Daemons provides an easy way to wrap existing ruby scripts (for example a
15
+ self-written server) to be run as a daemon and to be controlled by simple
16
+ start/stop/restart commands.
17
+
18
+ You can also call blocks as daemons and control them from the parent or just
19
+ daemonize the current process.
20
+
21
+ Besides this basic functionality, daemons offers many advanced features like
22
+ exception backtracing and logging (in case your ruby script crashes) and
23
+ monitoring and automatic restarting of your processes if they crash.
24
+ email: thomas.uehinger@gmail.com
22
25
  executables: []
23
26
  extensions: []
24
- extra_rdoc_files:
25
- - README
26
- - Releases
27
- - TODO
27
+ extra_rdoc_files: []
28
28
  files:
29
- - Rakefile
30
- - Releases
31
- - TODO
32
- - README
33
29
  - LICENSE
34
- - setup.rb
35
- - lib/daemons.rb
36
- - lib/daemons/application.rb
37
- - lib/daemons/application_group.rb
38
- - lib/daemons/change_privilege.rb
39
- - lib/daemons/cmdline.rb
40
- - lib/daemons/controller.rb
41
- - lib/daemons/daemonize.rb
42
- - lib/daemons/etc_extension.rb
43
- - lib/daemons/exceptions.rb
44
- - lib/daemons/monitor.rb
45
- - lib/daemons/pid.rb
46
- - lib/daemons/pidfile.rb
47
- - lib/daemons/pidmem.rb
30
+ - README.md
31
+ - Releases
32
+ - examples/call/call.rb
33
+ - examples/call/call_monitor.rb
34
+ - examples/daemonize/daemonize.rb
48
35
  - examples/run/ctrl_crash.rb
36
+ - examples/run/ctrl_custom_logfiles.rb
49
37
  - examples/run/ctrl_exec.rb
50
38
  - examples/run/ctrl_exit.rb
51
39
  - examples/run/ctrl_hanging.rb
52
40
  - examples/run/ctrl_keep_pid_files.rb
53
41
  - examples/run/ctrl_monitor.rb
42
+ - examples/run/ctrl_monitor_multiple.rb
54
43
  - examples/run/ctrl_multiple.rb
55
44
  - examples/run/ctrl_normal.rb
56
45
  - examples/run/ctrl_ontop.rb
@@ -65,31 +54,42 @@ files:
65
54
  - examples/run/myserver_exiting.rb
66
55
  - examples/run/myserver_hanging.rb
67
56
  - examples/run/myserver_slowstop.rb
68
- - examples/call/call.rb
69
- - examples/call/call_monitor.rb
70
- - examples/daemonize/daemonize.rb
71
- homepage: http://daemons.rubyforge.org
72
- licenses: []
57
+ - lib/daemons.rb
58
+ - lib/daemons/application.rb
59
+ - lib/daemons/application_group.rb
60
+ - lib/daemons/change_privilege.rb
61
+ - lib/daemons/cmdline.rb
62
+ - lib/daemons/controller.rb
63
+ - lib/daemons/daemonize.rb
64
+ - lib/daemons/etc_extension.rb
65
+ - lib/daemons/exceptions.rb
66
+ - lib/daemons/monitor.rb
67
+ - lib/daemons/pid.rb
68
+ - lib/daemons/pidfile.rb
69
+ - lib/daemons/pidmem.rb
70
+ - lib/daemons/version.rb
71
+ homepage: https://github.com/thuehlinger/daemons
72
+ licenses:
73
+ - MIT
74
+ metadata: {}
73
75
  post_install_message:
74
76
  rdoc_options: []
75
77
  require_paths:
76
78
  - lib
77
79
  required_ruby_version: !ruby/object:Gem::Requirement
78
- none: false
79
80
  requirements:
80
- - - ! '>='
81
+ - - '>='
81
82
  - !ruby/object:Gem::Version
82
83
  version: '0'
83
84
  required_rubygems_version: !ruby/object:Gem::Requirement
84
- none: false
85
85
  requirements:
86
- - - ! '>='
86
+ - - '>='
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
89
  requirements: []
90
- rubyforge_project: daemons
91
- rubygems_version: 1.8.23
90
+ rubyforge_project:
91
+ rubygems_version: 2.4.6
92
92
  signing_key:
93
- specification_version: 2
93
+ specification_version: 4
94
94
  summary: A toolkit to create and control daemons in different ways
95
95
  test_files: []
data/README DELETED
@@ -1,214 +0,0 @@
1
- = Daemons Version 1.1.9
2
-
3
- (See Releases for release-specific information)
4
-
5
- == What is Daemons?
6
-
7
- Daemons provides an easy way to wrap existing ruby scripts (for example a self-written server)
8
- to be <i>run as a daemon</i> and to be <i>controlled by simple start/stop/restart commands</i>.
9
-
10
- If you want, you can also use daemons to <i>run blocks of ruby code in a daemon process</i> and to control
11
- these processes from the main application.
12
-
13
- Besides this basic functionality, daemons offers many advanced features like <i>exception backtracing</i>
14
- and logging (in case your ruby script crashes) and <i>monitoring</i> and automatic restarting of your processes
15
- if they crash.
16
-
17
- == Basic Usage
18
-
19
- You can use Daemons in four different ways:
20
-
21
- === 1. Create wrapper scripts for your server scripts or applications
22
-
23
- Layout: suppose you have your self-written server <tt>myserver.rb</tt>:
24
-
25
- # this is myserver.rb
26
- # it does nothing really useful at the moment
27
-
28
- loop do
29
- sleep(5)
30
- end
31
-
32
- To use <tt>myserver.rb</tt> in a production environment, you need to be able to
33
- run <tt>myserver.rb</tt> in the _background_ (this means detach it from the console, fork it
34
- in the background, release all directories and file descriptors).
35
-
36
- Just create <tt>myserver_control.rb</tt> like this:
37
-
38
- # this is myserver_control.rb
39
-
40
- require 'rubygems' # if you use RubyGems
41
- require 'daemons'
42
-
43
- Daemons.run('myserver.rb')
44
-
45
- And use it like this from the console:
46
-
47
- $ ruby myserver_control.rb start
48
- (myserver.rb is now running in the background)
49
- $ ruby myserver_control.rb restart
50
- (...)
51
- $ ruby myserver_control.rb stop
52
-
53
- For testing purposes you can even run <tt>myserver.rb</tt> <i>without forking</i> in the background:
54
-
55
- $ ruby myserver_control.rb run
56
-
57
- An additional nice feature of Daemons is that you can pass <i>additional arguments</i> to the script that
58
- should be daemonized by seperating them by two _hyphens_:
59
-
60
- $ ruby myserver_control.rb start -- --file=anyfile --a_switch another_argument
61
-
62
-
63
- === 2. Create wrapper scripts that include your server procs
64
-
65
- Layout: suppose you have some code you want to run in the background and control that background process
66
- from a script:
67
-
68
- # this is your code
69
- # it does nothing really useful at the moment
70
-
71
- loop do
72
- sleep(5)
73
- end
74
-
75
- To run this code as a daemon create <tt>myproc_control.rb</tt> like this and include your code:
76
-
77
- # this is myproc_control.rb
78
-
79
- require 'rubygems' # if you use RubyGems
80
- require 'daemons'
81
-
82
- Daemons.run_proc('myproc.rb') do
83
- loop do
84
- sleep(5)
85
- end
86
- end
87
-
88
- And use it like this from the console:
89
-
90
- $ ruby myproc_control.rb start
91
- (myproc.rb is now running in the background)
92
- $ ruby myproc_control.rb restart
93
- (...)
94
- $ ruby myproc_control.rb stop
95
-
96
- For testing purposes you can even run <tt>myproc.rb</tt> <i>without forking</i> in the background:
97
-
98
- $ ruby myproc_control.rb run
99
-
100
- === 3. Control a bunch of daemons from another application
101
-
102
- Layout: you have an application <tt>my_app.rb</tt> that wants to run a bunch of
103
- server tasks as daemon processes.
104
-
105
- # this is my_app.rb
106
-
107
- require 'rubygems' # if you use RubyGems
108
- require 'daemons'
109
-
110
- task1 = Daemons.call(:multiple => true) do
111
- # first server task
112
-
113
- loop {
114
- conn = accept_conn()
115
- serve(conn)
116
- }
117
- end
118
-
119
- task2 = Daemons.call do
120
- # second server task
121
-
122
- loop {
123
- something_different()
124
- }
125
- end
126
-
127
- # the parent process continues to run
128
-
129
- # we can even control our tasks, for example stop them
130
- task1.stop
131
- task2.stop
132
-
133
- exit
134
-
135
- === 4. Daemonize the currently running process
136
-
137
- Layout: you have an application <tt>my_daemon.rb</tt> that wants to run as a daemon
138
- (but without the ability to be controlled by daemons via start/stop commands)
139
-
140
- # this is my_daemons.rb
141
-
142
- require 'rubygems' # if you use RubyGems
143
- require 'daemons'
144
-
145
- # Initialize the app while we're not a daemon
146
- init()
147
-
148
- # Become a daemon
149
- Daemons.daemonize
150
-
151
- # The server loop
152
- loop {
153
- conn = accept_conn()
154
- serve(conn)
155
- }
156
-
157
-
158
- <b>For further documentation, refer to the module documentation of Daemons.</b>
159
-
160
-
161
- == Download and Installation
162
-
163
- *Download*: just go to http://rubyforge.org/projects/daemons/
164
-
165
- Installation *with* RubyGems:
166
- $ su
167
- # gem install daemons
168
-
169
- Installation *without* RubyGems:
170
- $ tar xfz daemons-x.x.x.tar.gz
171
- $ cd daemons-x.x.x
172
- $ su
173
- # ruby setup.rb
174
-
175
- == Documentation
176
-
177
- For further documentation, refer to the module documentation of Daemons (click on Daemons).
178
-
179
- The RDoc documentation is also online at http://daemons.rubyforge.org
180
-
181
-
182
- == Author
183
-
184
- Written 2005-2010 by Thomas Uehlinger <mailto:th.uehlinger@gmx.ch>.
185
- Anonymous SVN checkout is available with "svn checkout http://daemons.rubyforge.org/svn/".
186
-
187
- == License
188
-
189
- Copyright (c) 2005-2012 Thomas Uehlinger
190
-
191
- Permission is hereby granted, free of charge, to any person
192
- obtaining a copy of this software and associated documentation
193
- files (the "Software"), to deal in the Software without
194
- restriction, including without limitation the rights to use,
195
- copy, modify, merge, publish, distribute, sublicense, and/or sell
196
- copies of the Software, and to permit persons to whom the
197
- Software is furnished to do so, subject to the following
198
- conditions:
199
-
200
- The above copyright notice and this permission notice shall be
201
- included in all copies or substantial portions of the Software.
202
-
203
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
204
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
205
- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
206
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
207
- HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
208
- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
209
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
210
- OTHER DEALINGS IN THE SOFTWARE.
211
-
212
- == Feedback and other resources
213
-
214
- At http://rubyforge.org/projects/daemons.