daemons 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -112,6 +112,103 @@ module Daemonize
112
112
  module_function :safefork
113
113
 
114
114
 
115
+ def simulate(logfile_name = nil)
116
+ # NOTE: STDOUT and STDERR will not be redirected to the logfile!
117
+
118
+ Dir.chdir "/" # Release old working directory
119
+ File.umask 0000 # Insure sensible umask
120
+
121
+ # Make sure all file descriptors are closed
122
+ ObjectSpace.each_object(IO) do |io|
123
+ unless [STDIN, STDOUT, STDERR].include?(io)
124
+ begin
125
+ unless io.closed?
126
+ io.close
127
+ end
128
+ rescue ::Exception
129
+ end
130
+ end
131
+ end
132
+
133
+ # Free file descriptors and
134
+ # point them somewhere sensible
135
+ # STDOUT/STDERR should go to a logfile
136
+
137
+ STDIN.reopen "/dev/null" rescue nil
138
+ end
139
+ module_function :simulate
140
+
141
+
142
+ def call_as_daemon(block, logfile_name = nil, oldmode = 0)
143
+ rd, wr = IO.pipe
144
+
145
+ if tmppid = safefork
146
+ # parent
147
+ wr.close
148
+ pid = rd.read.to_i
149
+ rd.close
150
+
151
+ Process.waitpid(tmppid)
152
+
153
+ return pid
154
+ else
155
+ rd.close
156
+
157
+ # Detach from the controlling terminal
158
+ unless sess_id = Process.setsid
159
+ raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
160
+ end
161
+
162
+ # Prevent the possibility of acquiring a controlling terminal
163
+ if oldmode.zero?
164
+ trap 'SIGHUP', 'IGNORE'
165
+ exit if pid = safefork
166
+ end
167
+
168
+ wr.write Process.pid
169
+ wr.close
170
+
171
+ Dir.chdir "/" # Release old working directory
172
+ File.umask 0000 # Insure sensible umask
173
+
174
+ # Make sure all file descriptors are closed
175
+ ObjectSpace.each_object(IO) do |io|
176
+ unless [STDIN, STDOUT, STDERR].include?(io)
177
+ begin
178
+ unless io.closed?
179
+ io.close
180
+ end
181
+ rescue ::Exception
182
+ end
183
+ end
184
+ end
185
+
186
+ # Free file descriptors and
187
+ # point them somewhere sensible
188
+ # STDOUT/STDERR should go to a logfile
189
+
190
+ STDIN.reopen "/dev/null" rescue nil
191
+
192
+ if logfile_name
193
+ begin
194
+ STDOUT.reopen logfile_name, "a"
195
+ rescue ::Exception
196
+ STDOUT.reopen "/dev/null" rescue nil
197
+ end
198
+ else
199
+ STDOUT.reopen "/dev/null" rescue nil
200
+ end
201
+
202
+ STDERR.reopen STDOUT rescue nil
203
+
204
+ block.call
205
+
206
+ exit
207
+ end
208
+ end
209
+ module_function :call_as_daemon
210
+
211
+
115
212
  # This method causes the current running process to become a daemon
116
213
  def daemonize(logfile_name = nil, oldmode=0)
117
214
  srand # Split rand streams between spawning and daemonized process
@@ -119,7 +216,7 @@ module Daemonize
119
216
 
120
217
  # Detach from the controlling terminal
121
218
  unless sess_id = Process.setsid
122
- raise Daemons.RuntimeException.new('cannot detach from controlled terminal')
219
+ raise Daemons.RuntimeException.new('cannot detach from controlling terminal')
123
220
  end
124
221
 
125
222
  # Prevent the possibility of acquiring a controlling terminal
@@ -161,7 +258,6 @@ module Daemonize
161
258
 
162
259
  STDERR.reopen STDOUT rescue nil
163
260
 
164
-
165
261
  return oldmode ? sess_id : 0 # Return value is mostly irrelevant
166
262
  end
167
263
  module_function :daemonize
@@ -6,19 +6,19 @@ module Daemons
6
6
  class Monitor
7
7
 
8
8
  def self.find(dir, app_name)
9
- pid_file = PidFile.find_files(dir, app_name)[0]
9
+ pid = PidFile.find_files(dir, app_name)[0]
10
10
 
11
- if pid_file
12
- pid_file = PidFile.existing(pid_file)
11
+ if pid
12
+ pid = PidFile.existing(pid)
13
13
 
14
- unless PidFile.running?(pid_file.read)
15
- pid_file.remove rescue nil
14
+ unless PidFile.running?(pid.pid)
15
+ pid.cleanup rescue nil
16
16
  return
17
17
  end
18
18
 
19
19
  monitor = self.allocate
20
20
 
21
- monitor.instance_variable_set(:@pid_file, pid_file)
21
+ monitor.instance_variable_set(:@pid, pid)
22
22
 
23
23
  return monitor
24
24
  end
@@ -28,20 +28,44 @@ module Daemons
28
28
 
29
29
 
30
30
  def initialize(an_app)
31
- @pid_file = PidFile.new(an_app.pidfile_dir, an_app.group.app_name + '_monitor', false)
31
+ if an_app.pidfile_dir
32
+ @pid = PidFile.new(an_app.pidfile_dir, an_app.group.app_name + '_monitor', false)
33
+ else
34
+ @pid = PidMem.new
35
+ end
32
36
  end
33
37
 
34
- def start(applications)
35
- return if applications.empty?
38
+ def watch(applications)
39
+ sleep(30)
36
40
 
41
+ loop do
42
+ applications.each {|a|
43
+ sleep(10)
44
+
45
+ unless a.running?
46
+ a.zap!
47
+
48
+ Process.detach(fork { a.start })
49
+
50
+ sleep(10)
51
+ end
52
+ }
53
+
54
+ sleep(30)
55
+ end
56
+ end
57
+ private :watch
58
+
59
+
60
+ def start_with_pidfile(applications)
37
61
  fork do
38
62
  Daemonize.daemonize
39
63
 
40
64
  begin
41
- @pid_file.write
65
+ @pid.pid = Process.pid
42
66
 
43
67
  # at_exit {
44
- # @pid_file.remove rescue nil
68
+ # @pid.cleanup rescue nil
45
69
  # }
46
70
 
47
71
  # This part is needed to remove the pid-file if the application is killed by
@@ -50,50 +74,51 @@ module Daemons
50
74
  # 'TERM'.
51
75
  #
52
76
  # trap('TERM') {
53
- # @pid_file.remove rescue nil
77
+ # @pid.cleanup rescue nil
54
78
  # exit
55
79
  # }
56
80
 
57
- sleep(60)
58
-
59
- loop do
60
- applications.each {|a|
61
- sleep(10)
62
-
63
- unless a.running?
64
- a.zap!
65
-
66
- Process.detach(fork { a.start })
67
-
68
- sleep(10)
69
- end
70
- }
71
-
72
- sleep(30)
73
- end
81
+ watch(applications)
74
82
  rescue ::Exception => e
75
83
  begin
76
- File.open(File.join(@pid_file.dir, @pid_file.progname + '.log'), 'a') {|f|
84
+ File.open(File.join(@pid.dir, @pid.progname + '.log'), 'a') {|f|
77
85
  f.puts Time.now
78
86
  f.puts e
79
87
  f.puts e.backtrace.inspect
80
88
  }
81
89
  ensure
82
- @pid_file.remove rescue nil
90
+ @pid.cleanup rescue nil
83
91
  exit!
84
92
  end
85
93
  end
86
94
  end
95
+ end
96
+ private :start_with_pidfile
97
+
98
+ def start_without_pidfile(applications)
99
+ Thread.new { watch(applications) }
100
+ end
101
+ private :start_without_pidfile
102
+
103
+
104
+
105
+ def start(applications)
106
+ return if applications.empty?
87
107
 
108
+ if @pid.kind_of?(PidFile)
109
+ start_with_pidfile(applications)
110
+ else
111
+ start_without_pidfile(applications)
112
+ end
88
113
  end
89
114
 
90
115
 
91
116
  def stop
92
- Process.kill('TERM', @pid_file.read) rescue nil
117
+ Process.kill('TERM', @pid.pid) rescue nil
93
118
 
94
119
  # We try to remove the pid-files by ourselves, in case the application
95
120
  # didn't clean it up.
96
- @pid_file.remove rescue nil
121
+ @pid.cleanup rescue nil
97
122
  end
98
123
 
99
124
  end
@@ -0,0 +1,60 @@
1
+
2
+ module Daemons
3
+
4
+ class Pid
5
+
6
+ def Pid.running?(pid, additional = nil)
7
+ output = `ps ax`
8
+ return (/#{pid} / =~ output and (additional ? /#{additional}/ =~ output : true))
9
+ end
10
+
11
+
12
+ # Returns the directory that should be used to write the pid file to
13
+ # depending on the given mode.
14
+ #
15
+ # Some modes may require an additionaly hint, others may determine
16
+ # the directory automatically.
17
+ #
18
+ # If no valid directory is found, returns nil.
19
+ #
20
+ def Pid.dir(dir_mode, dir, script)
21
+ return nil unless script
22
+
23
+ case dir_mode
24
+ when :normal
25
+ return File.expand_path(dir_mode)
26
+ when :script
27
+ return File.expand_path(File.join(File.split(script)[0],dir))
28
+ when :system
29
+ return '/var/run'
30
+ else
31
+ raise Error.new("pid file mode '#{mode}' not implemented")
32
+ end
33
+ end
34
+
35
+ # Initialization method
36
+ def initialize
37
+ end
38
+
39
+
40
+ # Get method
41
+ def pid
42
+ end
43
+
44
+ # Set method
45
+ def pid=(p)
46
+ end
47
+
48
+ # Cleanup method
49
+ def cleanup
50
+ end
51
+
52
+ # Exists? method
53
+ def exists?
54
+ true
55
+ end
56
+
57
+ end
58
+
59
+
60
+ end
@@ -1,3 +1,5 @@
1
+ require 'daemons/pid'
2
+
1
3
 
2
4
  module Daemons
3
5
 
@@ -27,7 +29,7 @@ module Daemons
27
29
  # 2. in a directory relative to the current directory or the filesystem root
28
30
  # 3. in the preconfigured directory <tt>/var/run</tt>
29
31
  #
30
- class PidFile
32
+ class PidFile < Pid
31
33
 
32
34
  attr_reader :dir, :progname, :multiple, :number
33
35
 
@@ -39,35 +41,6 @@ module Daemons
39
41
  return files
40
42
  end
41
43
 
42
-
43
- def PidFile.running?(pid, additional = nil)
44
- output = `ps ax`
45
- return (/#{pid} / =~ output and (additional ? /#{additional}/ =~ output : true))
46
- end
47
-
48
-
49
- # Returns the directory that should be used to write the pid file to
50
- # depending on the given mode.
51
- #
52
- # Some modes may require an additionaly hint, others may determine
53
- # the directory automatically.
54
- #
55
- # If no valid directory is found, returns nil.
56
- #
57
- def PidFile.dir(dir_mode, dir, script)
58
- case dir_mode
59
- when :normal
60
- return File.expand_path(dir_mode)
61
- when :script
62
- return File.expand_path(File.join(File.split(script)[0],dir))
63
- when :system
64
- return '/var/run'
65
- else
66
- raise Error.new("pid file mode '#{mode}' not implemented")
67
- end
68
- end
69
-
70
-
71
44
  def PidFile.existing(path)
72
45
  new_instance = PidFile.allocate
73
46
 
@@ -95,7 +68,7 @@ module Daemons
95
68
  File.exists? filename
96
69
  end
97
70
 
98
- def write
71
+ def pid=(p)
99
72
  if multiple
100
73
  while File.exists?(filename) and @number < 1024
101
74
  @number += 1
@@ -107,15 +80,15 @@ module Daemons
107
80
  end
108
81
 
109
82
  File.open(filename, 'w') {|f|
110
- f.puts Process.pid
83
+ f.puts p #Process.pid
111
84
  }
112
85
  end
113
86
 
114
- def remove
87
+ def cleanup
115
88
  File.delete(filename)
116
89
  end
117
90
 
118
- def read
91
+ def pid
119
92
  File.open(filename) {|f|
120
93
  return f.gets.to_i
121
94
  }
@@ -0,0 +1,10 @@
1
+ require 'daemons/pid'
2
+
3
+
4
+ module Daemons
5
+
6
+ class PidMem < Pid
7
+ attr_accessor :pid
8
+ end
9
+
10
+ end
@@ -0,0 +1,12 @@
1
+ SCRIPT_DIR = File.split(File.expand_path(__FILE__))[0]
2
+
3
+ $LOAD_PATH << File.join(SCRIPT_DIR, '../lib')
4
+
5
+
6
+ require 'pp'
7
+
8
+ require 'daemons'
9
+
10
+ print Daemonize::call_as_daemon(File.join(SCRIPT_DIR, 'tmp/call_as_daemon.log')) {
11
+ print "hello"
12
+ }
metadata CHANGED
@@ -1,17 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.8
2
+ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: daemons
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.0
7
- date: 2005-04-21
8
- summary: A toolkit to convert your script to a controllable daemon
6
+ version: 0.4.0
7
+ date: 2005-07-30 00:00:00 +02:00
8
+ summary: A toolkit to create and control daemons in different ways
9
9
  require_paths:
10
10
  - lib
11
11
  email: th.uehlinger@gmx.ch
12
12
  homepage: http://daemons.rubyforge.org
13
- rubyforge_project:
14
- description:
13
+ rubyforge_project: daemons
14
+ description: "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. You can also call blocks as daemons and control
17
+ them from the parent or just daemonize the current process. Besides this basic
18
+ functionality, daemons offers many advanced features like exception backtracing
19
+ and logging (in case your ruby script crashes) and monitoring and automatic
20
+ restarting of your processes if they crash."
15
21
  autorequire: daemons
16
22
  default_executable:
17
23
  bindir: bin
@@ -24,6 +30,8 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
24
30
  version: 0.0.0
25
31
  version:
26
32
  platform: ruby
33
+ signing_key:
34
+ cert_chain:
27
35
  authors:
28
36
  - Thomas Uehlinger
29
37
  files:
@@ -33,26 +41,38 @@ files:
33
41
  - README
34
42
  - setup.rb
35
43
  - lib/daemons.rb
36
- - lib/daemons/daemonize.rb
37
- - lib/daemons/exceptions.rb
38
44
  - lib/daemons/cmdline.rb
45
+ - lib/daemons/exceptions.rb
46
+ - lib/daemons/daemonize.rb
39
47
  - lib/daemons/pidfile.rb
40
48
  - lib/daemons/monitor.rb
49
+ - lib/daemons/application_group.rb
50
+ - lib/daemons/controller.rb
51
+ - lib/daemons/pid.rb
52
+ - lib/daemons/pidmem.rb
53
+ - lib/daemons/application.rb
41
54
  - test/tmp
42
55
  - test/testapp.rb
43
- - test/tc_main.rb
44
56
  - test/test1.rb
45
- - examples/myserver.rb
46
- - examples/myserver_crashing.rb
47
- - examples/ctrl_crash.rb
48
- - examples/ctrl_ontop.rb
49
- - examples/ctrl_exec.rb
50
- - examples/ctrl_normal.rb
51
- - examples/ctrl_multiple.rb
52
- - examples/myserver_exiting.rb
53
- - examples/ctrl_exit.rb
54
- - examples/myserver_crashing.rb.output
55
- - examples/ctrl_monitor.rb
57
+ - test/call_as_daemon.rb
58
+ - test/tc_main.rb
59
+ - examples/run
60
+ - examples/call
61
+ - examples/daemonize
62
+ - examples/run/ctrl_exec.rb
63
+ - examples/run/ctrl_exit.rb
64
+ - examples/run/ctrl_multiple.rb
65
+ - examples/run/myserver_crashing.rb.output
66
+ - examples/run/ctrl_normal.rb
67
+ - examples/run/ctrl_monitor.rb
68
+ - examples/run/myserver.rb
69
+ - examples/run/myserver_crashing.rb
70
+ - examples/run/ctrl_ontop.rb
71
+ - examples/run/myserver_exiting.rb
72
+ - examples/run/ctrl_crash.rb
73
+ - examples/call/call_monitor.rb
74
+ - examples/call/call.rb
75
+ - examples/daemonize/daemonize.rb
56
76
  test_files:
57
77
  - test/tc_main.rb
58
78
  rdoc_options: []