daemons 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,4 +1,4 @@
1
- = Daemons Version 0.0.1
1
+ = Daemons Version 0.2.0
2
2
 
3
3
  (See Releases for release-specific information)
4
4
 
data/Rakefile CHANGED
@@ -16,14 +16,15 @@ PKG_FILES = FileList[
16
16
  "Rakefile", "Releases", "TODO", "README",
17
17
  "setup.rb",
18
18
  "lib/**/*.rb",
19
- "test/**/*"
19
+ "test/**/*",
20
+ "examples/**/*"
20
21
  ]
21
- PKG_FILES.exclude(%r(^test/tmp/.+))
22
-
22
+ #PKG_FILES.exclude(%r(^test/tmp/.+))
23
+ PKG_FILES.exclude(%r(\.pid$))
24
+ PKG_FILES.exclude(%r(\.log$))
23
25
 
24
26
  spec = Gem::Specification.new do |s|
25
27
  s.name = PKG_NAME
26
- #s.version = "0.0.1"
27
28
  s.version = Daemons::VERSION
28
29
  s.author = "Thomas Uehlinger"
29
30
  s.email = "th.uehlinger@gmx.ch"
data/Releases CHANGED
@@ -3,4 +3,11 @@
3
3
  == Release 0.0.1: Feb 8, 2005
4
4
 
5
5
  * Initial release
6
-
6
+
7
+
8
+ == Release 0.2.0: Mar 21, 2005
9
+
10
+ * Exception backtrace functionality added
11
+ * Exec functionality added
12
+ * More examples added
13
+ * New commands: status, zap
data/TODO CHANGED
@@ -1,2 +1,3 @@
1
- * write the README (2005-02-07)
1
+ * write the README (2005-02-07) *DONE*
2
2
  * write some real tests (2005-02-08)
3
+ * document the new options (2005-03-14)
@@ -0,0 +1,16 @@
1
+ lib_dir = File.expand_path(File.join(File.split(__FILE__)[0], '../lib'))
2
+
3
+ if File.exists?(File.join(lib_dir, 'daemons.rb'))
4
+ $LOAD_PATH.unshift lib_dir
5
+ else
6
+ require 'rubygems' rescue nil
7
+ end
8
+
9
+ require 'daemons'
10
+
11
+
12
+ options = {
13
+ :backtrace => true
14
+ }
15
+
16
+ Daemons.run(File.join(File.split(__FILE__)[0], 'myserver_crashing.rb'), options)
@@ -0,0 +1,16 @@
1
+ lib_dir = File.expand_path(File.join(File.split(__FILE__)[0], '../lib'))
2
+
3
+ if File.exists?(File.join(lib_dir, 'daemons.rb'))
4
+ $LOAD_PATH.unshift lib_dir
5
+ else
6
+ require 'rubygems' rescue nil
7
+ end
8
+
9
+ require 'daemons'
10
+
11
+
12
+ options = {
13
+ :exec => true
14
+ }
15
+
16
+ Daemons.run(File.join(File.split(__FILE__)[0], 'myserver.rb'), options)
@@ -0,0 +1,12 @@
1
+ lib_dir = File.expand_path(File.join(File.split(__FILE__)[0], '../lib'))
2
+
3
+ if File.exists?(File.join(lib_dir, 'daemons.rb'))
4
+ $LOAD_PATH.unshift lib_dir
5
+ else
6
+ require 'rubygems' rescue nil
7
+ end
8
+
9
+ require 'daemons'
10
+
11
+
12
+ Daemons.run(File.join(File.split(__FILE__)[0], 'myserver.rb'))
@@ -0,0 +1,16 @@
1
+ lib_dir = File.expand_path(File.join(File.split(__FILE__)[0], '../lib'))
2
+
3
+ if File.exists?(File.join(lib_dir, 'daemons.rb'))
4
+ $LOAD_PATH.unshift lib_dir
5
+ else
6
+ require 'rubygems' rescue nil
7
+ end
8
+
9
+ require 'daemons'
10
+
11
+
12
+ options = {
13
+ :ontop => true
14
+ }
15
+
16
+ Daemons.run(File.join(File.split(__FILE__)[0], 'myserver.rb'), options)
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ # This is myserver.rb, an example server that is to be controlled by daemons
5
+ # and that does nothing really useful at the moment.
6
+ #
7
+ # Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts.
8
+
9
+ loop do
10
+ puts 'ping from myserver.rb!'
11
+ sleep(3)
12
+ end
@@ -0,0 +1,14 @@
1
+ # This is myserver.rb, an example server that is to be controlled by daemons
2
+ # and that does nothing really useful at the moment.
3
+ #
4
+ # Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts.
5
+
6
+ loop do
7
+ puts 'ping from myserver.rb!'
8
+ puts 'this example server will crash in 3 seconds...'
9
+
10
+ sleep(3)
11
+
12
+ puts 'CRASH!'
13
+ raise 'CRASH!'
14
+ end
@@ -1,6 +1,5 @@
1
1
  require 'optparse'
2
2
  require 'optparse/time'
3
- #require 'ostruct'
4
3
 
5
4
  require 'daemons/pidfile'
6
5
  require 'daemons/cmdline'
@@ -18,7 +17,7 @@ require 'daemons/exceptions'
18
17
  #
19
18
  module Daemons
20
19
 
21
- VERSION = "0.0.1"
20
+ VERSION = "0.2.0"
22
21
 
23
22
  require 'daemons/daemonize'
24
23
 
@@ -28,8 +27,12 @@ module Daemons
28
27
  attr_accessor :app_argv
29
28
  attr_accessor :controller_argv
30
29
 
30
+ # the PidFile instance belonging to this application
31
31
  attr_reader :pid_file
32
32
 
33
+ # the ApplicationGroup the application belongs to
34
+ attr_reader :group
35
+
33
36
 
34
37
  def initialize(group, pid_file = nil)
35
38
  @group = group
@@ -46,42 +49,145 @@ module Daemons
46
49
  end
47
50
 
48
51
  def start
49
- #puts "starting..."
52
+ opts = @group.controller.options
50
53
 
51
- unless @group.controller.options[:ontop]
54
+ unless opts[:ontop]
52
55
  Daemonize.daemonize()
53
56
  end
54
57
 
55
58
  @pid_file.write
56
59
 
57
- at_exit {
58
- @pid_file.remove rescue nil
59
- }
60
-
61
- trap('TERM') {
62
- @pid_file.remove rescue nil
63
- exit
64
- }
65
-
66
- run()
60
+ if opts[:exec]
61
+ run_via_exec()
62
+ else
63
+ # We need this to remove the pid-file if the applications exits by itself.
64
+ # Note that <tt>at_text</tt> will only be run if the applications exits by calling
65
+ # <tt>exit</tt>, and not if it calls <tt>exit!</tt>.
66
+ #
67
+ at_exit {
68
+ @pid_file.remove rescue nil
69
+
70
+ # If the option <tt>:backtrace</tt> is used and the application did exit by itself
71
+ # create a exception log.
72
+ if opts[:backtrace] and not opts[:ontop] and not $daemons_sigterm
73
+ exception_log() rescue nil
74
+ end
75
+
76
+ }
77
+
78
+ # This part is needed to remove the pid-file if the application is killed by
79
+ # daemons or manually by the user.
80
+ # Note that the applications is not supposed to overwrite the signal handler for
81
+ # 'TERM'.
82
+ #
83
+ trap('TERM') {
84
+ @pid_file.remove rescue nil
85
+ $daemons_sigterm = true
86
+
87
+ exit
88
+ }
89
+
90
+ run_via_load()
91
+ end
67
92
  end
68
93
 
69
94
  def run
95
+ if @group.controller.options[:exec]
96
+ run_via_exec()
97
+ else
98
+ run_via_load()
99
+ end
100
+ end
101
+
102
+ def run_via_exec
103
+ ENV['DAEMONS_ARGV'] = @controller_argv.join(' ') # haven't tested yet if this is really passed to the exec'd process...
104
+
105
+ Kernel.exec(script(), *ARGV)
106
+ end
107
+
108
+ def run_via_load
70
109
  $DAEMONS_ARGV = @controller_argv
110
+ ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
71
111
 
72
112
  ARGV.clear
73
113
  ARGV.concat @app_argv if @app_argv
74
114
 
115
+ # TODO: begin - rescue - end around this and exception logging
75
116
  load script()
76
117
  end
77
118
 
119
+ # This is a nice little function for debugging purposes:
120
+ # In case a multi-threaded ruby script exits due to an uncaught exception
121
+ # it may be difficult to find out where the exception came from because
122
+ # one cannot catch exceptions that are thrown in threads other than the main
123
+ # thread.
124
+ #
125
+ # This function searches for all exceptions in memory and outputs them to STDERR
126
+ # (if it is connected) and to a log file in the pid-file directory.
127
+ #
128
+ def exception_log
129
+ require 'logger'
130
+
131
+ l_file = Logger.new(File.join(pidfile_dir(), @group.app_name + '.log'))
132
+
133
+
134
+ # the code below only logs the last exception
135
+ e = nil
136
+
137
+ ObjectSpace.each_object {|o|
138
+ if ::Exception === o
139
+ e = o
140
+ end
141
+ }
142
+
143
+ l_file.error e
144
+ l_file.close
145
+
146
+ e = nil
147
+
148
+ # this code logs every exception found in memory
149
+ # ObjectSpace.each_object {|o|
150
+ # if ::Exception === o
151
+ # l_file.error o
152
+ # end
153
+ # }
154
+ #
155
+ # l_file.close
156
+ end
157
+
158
+
78
159
  def stop
79
160
  Process.kill('TERM', @pid_file.read)
80
161
 
81
- # begin
82
- # @pidfile.remove
83
- # rescue ::Exception
84
- # end
162
+ # We try to remove the pid-files by ourselves, in case the application
163
+ # didn't clean it up.
164
+ @pid_file.remove rescue nil
165
+
166
+ end
167
+
168
+ def zap
169
+ @pid_file.remove
170
+ end
171
+
172
+ def show_status
173
+ running = self.running?
174
+
175
+ puts "#{self.group.app_name}: #{running ? '' : 'not'} running#{(running and @pid_file.exists?) ? ' [pid ' + @pid_file.read.to_s + ']' : ''}#{(@pid_file.exists? and not running) ? ' (but pid-file exists: ' + @pid_file.read.to_s + ')' : ''}"
176
+ end
177
+
178
+ # This function implements a (probably too simle) method to detect
179
+ # whether the program with the pid found in the pid-file is still running.
180
+ # It just searches for the pid in the output of <tt>ps ax</tt>, which
181
+ # is probably not a good idea in some cases.
182
+ # Alternatives would be to use a direct access method the unix process control
183
+ # system.
184
+ #
185
+ def running?
186
+ if @pid_file.exists?
187
+ return / #{@pid_file.read} / =~ `ps ax`
188
+ end
189
+
190
+ return false
85
191
  end
86
192
  end
87
193
 
@@ -93,11 +199,6 @@ module Daemons
93
199
 
94
200
  attr_reader :controller
95
201
 
96
- # True if one can start multiple instances of the application
97
- #attr_reader :multiple
98
-
99
- #attr_reader :options
100
-
101
202
  attr_reader :applications
102
203
 
103
204
  attr_accessor :controller_argv
@@ -106,8 +207,10 @@ module Daemons
106
207
  attr_accessor :dir_mode
107
208
  attr_accessor :dir
108
209
 
210
+ # true if the application is supposed to run in multiple instances
109
211
  attr_reader :multiple
110
212
 
213
+
111
214
  def initialize(app_name, script, controller) #multiple = false)
112
215
  @app_name = app_name
113
216
  @script = script
@@ -120,8 +223,6 @@ module Daemons
120
223
  @dir_mode = options[:dir_mode] || :script
121
224
  @dir = options[:dir] || ''
122
225
 
123
- #@multiple = multiple
124
-
125
226
  @applications = find_applications(pidfile_dir())
126
227
  end
127
228
 
@@ -159,10 +260,18 @@ module Daemons
159
260
  def stop_all
160
261
  @applications.each {|a| a.stop}
161
262
  end
263
+
264
+ def zap_all
265
+ @applications.each {|a| a.zap}
266
+ end
267
+
268
+ def show_status
269
+ @applications.each {|a| a.show_status}
270
+ end
271
+
162
272
  end
163
273
 
164
274
 
165
-
166
275
  class Controller
167
276
 
168
277
  attr_reader :app_name
@@ -173,7 +282,9 @@ module Daemons
173
282
  'start',
174
283
  'stop',
175
284
  'restart',
176
- 'run'
285
+ 'run',
286
+ 'zap',
287
+ 'status'
177
288
  ]
178
289
 
179
290
  def initialize(script, argv = [])
@@ -190,6 +301,11 @@ module Daemons
190
301
  end
191
302
 
192
303
 
304
+ # This function is used to do a final update of the options passed to the application
305
+ # before they are really used.
306
+ #
307
+ # Note that this function should only update <tt>@options</tt> and no other variables.
308
+ #
193
309
  def setup_options
194
310
  #@options[:ontop] ||= true
195
311
  end
@@ -218,6 +334,14 @@ module Daemons
218
334
  @group.stop_all
219
335
  sleep 1
220
336
  @group.start_all
337
+ when 'zap'
338
+ @group.zap_all
339
+ when 'status'
340
+ unless @group.applications.empty?
341
+ @group.show_status
342
+ else
343
+ puts "#{@group.app_name}: no instances running"
344
+ end
221
345
  when nil
222
346
  raise CmdException.new('no command given')
223
347
  #puts "ERROR: No command given"; puts
@@ -225,7 +349,7 @@ module Daemons
225
349
  #print_usage()
226
350
  #raise('usage function not implemented')
227
351
  else
228
- raise Error.new("not implemented command '#{@command}'")
352
+ raise Error.new("command '#{@command}' not implemented")
229
353
  end
230
354
  end
231
355
 
@@ -282,6 +406,12 @@ module Daemons
282
406
  # <tt>:dir</tt>:: Used in combination with <tt>:dir_mode</tt> (description above)
283
407
  # <tt>:multiple</tt>:: Specifies whether multiple instances of the same script are allowed to run at the
284
408
  # same time
409
+ # <tt>:ontop</tt>:: When given, stay on top, i.e. do not daemonize the application
410
+ # (but the pid-file and other things are written as usual)
411
+ # <tt>:exec</tt>:: When given, do not start the application by <tt>load</tt>-ing the script file,
412
+ # but by exec'ing the script file
413
+ # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
414
+ # pid-file directory if the application exits due to an uncaught exception
285
415
  #
286
416
  # -----
287
417
  #
@@ -289,7 +419,10 @@ module Daemons
289
419
  # options = {
290
420
  # :dir_mode => :script,
291
421
  # :dir => 'pids',
292
- # :multiple => true
422
+ # :multiple => true,
423
+ # :ontop => true,
424
+ # :exec => true,
425
+ # :backtrace => true
293
426
  # }
294
427
  #
295
428
  # Daemons.run(File.join(File.split(__FILE__)[0], 'myscript.rb'), options)
@@ -78,6 +78,7 @@ module Daemons
78
78
  puts " stop stop all instances of the application"
79
79
  puts " restart stop all instances and restart them afterwards"
80
80
  puts " run start the application and stay on top"
81
+ puts " zap set the application to a stopped state"
81
82
  puts
82
83
  puts "* and where <options> may contain several of the following:"
83
84
 
@@ -1,10 +1,12 @@
1
+ #--
1
2
  ###############################################################################
2
3
  # daemonize.rb is a slightly modified version of daemonize.rb was #
3
4
  # from the Daemonize Library written by Travis Whitton #
4
5
  # for details, read the notice below #
5
6
  ###############################################################################
6
-
7
-
7
+ #++
8
+ #
9
+ #
8
10
  # =Daemonize Library
9
11
  #
10
12
  # February. 4, 2005 Travis Whitton <whitton@atlantic.net>
@@ -132,7 +134,9 @@ module Daemonize
132
134
  # Make sure all file descriptors are closed
133
135
  ObjectSpace.each_object(IO) do |io|
134
136
  unless [STDIN, STDOUT, STDERR].include?(io)
135
- io.close rescue nil
137
+ unless io.closed?
138
+ io.close rescue nil
139
+ end
136
140
  end
137
141
  end
138
142
 
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.4
2
+ rubygems_version: 0.8.8
3
3
  specification_version: 1
4
4
  name: daemons
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2005-02-08
6
+ version: 0.2.0
7
+ date: 2005-03-21
8
8
  summary: A toolkit to convert your script to a controllable daemon
9
9
  require_paths:
10
10
  - lib
@@ -41,6 +41,12 @@ files:
41
41
  - test/tc_main.rb
42
42
  - test/test1.rb
43
43
  - test/tmp
44
+ - examples/myserver.rb
45
+ - examples/myserver_crashing.rb
46
+ - examples/ctrl_crash.rb
47
+ - examples/ctrl_ontop.rb
48
+ - examples/ctrl_exec.rb
49
+ - examples/ctrl_normal.rb
44
50
  test_files:
45
51
  - test/tc_main.rb
46
52
  rdoc_options: []