daemons 0.0.1 → 0.2.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.
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: []