daemons 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []