mlanett-daemons 1.0.13 → 1.1.10.2

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.
Files changed (76) hide show
  1. data/Gemfile.lock +12 -2
  2. data/LICENSE +1 -8
  3. data/README-mlanett.rdoc +8 -0
  4. data/{README → README.rdoc} +6 -15
  5. data/Releases +84 -9
  6. data/TODO +1 -5
  7. data/daemons.gemspec +1 -0
  8. data/examples/call/call.rb +1 -0
  9. data/examples/daemonize/daemonize.rb +10 -3
  10. data/examples/run/ctrl_hanging.rb +19 -0
  11. data/examples/run/ctrl_monitor_multiple.rb +18 -0
  12. data/examples/run/ctrl_normal.rb +0 -1
  13. data/examples/run/ctrl_proc.rb.output +20 -0
  14. data/examples/run/ctrl_proc_rand.rb +23 -0
  15. data/examples/run/ctrl_slowstop.rb +16 -0
  16. data/examples/run/myserver_crashing.rb +2 -2
  17. data/examples/run/myserver_hanging.rb +21 -0
  18. data/examples/run/myserver_slowstop.rb +21 -0
  19. data/lib/daemons.rb +38 -10
  20. data/lib/daemons/application.rb +131 -26
  21. data/lib/daemons/application_group.rb +58 -10
  22. data/lib/daemons/change_privilege.rb +19 -0
  23. data/lib/daemons/cmdline.rb +10 -6
  24. data/lib/daemons/controller.rb +5 -2
  25. data/lib/daemons/daemonize.rb +64 -150
  26. data/lib/daemons/etc_extension.rb +12 -0
  27. data/lib/daemons/exceptions.rb +3 -0
  28. data/lib/daemons/monitor.rb +27 -19
  29. data/lib/daemons/pid.rb +13 -14
  30. data/lib/daemons/pidfile.rb +14 -7
  31. data/lib/daemons/pidmem.rb +9 -0
  32. data/lib/daemons/version.rb +1 -1
  33. data/spec/pidfile_spec.rb +12 -0
  34. data/spec/spec_helper.rb +1 -0
  35. metadata +31 -47
  36. data/daemons.tmproj +0 -56
  37. data/examples/run/myserver_crashing.rb.output +0 -30
  38. data/html/classes/Daemonize.html +0 -497
  39. data/html/classes/Daemons.html +0 -683
  40. data/html/classes/Daemons/Application.html +0 -836
  41. data/html/classes/Daemons/ApplicationGroup.html +0 -508
  42. data/html/classes/Daemons/CmdException.html +0 -113
  43. data/html/classes/Daemons/Controller.html +0 -429
  44. data/html/classes/Daemons/Error.html +0 -113
  45. data/html/classes/Daemons/Exception.html +0 -111
  46. data/html/classes/Daemons/Monitor.html +0 -263
  47. data/html/classes/Daemons/Optparse.html +0 -244
  48. data/html/classes/Daemons/Pid.html +0 -339
  49. data/html/classes/Daemons/PidFile.html +0 -441
  50. data/html/classes/Daemons/PidMem.html +0 -126
  51. data/html/classes/Daemons/RuntimeException.html +0 -113
  52. data/html/classes/Daemons/SystemError.html +0 -163
  53. data/html/created.rid +0 -1
  54. data/html/files/README.html +0 -377
  55. data/html/files/Releases.html +0 -342
  56. data/html/files/TODO.html +0 -121
  57. data/html/files/lib/daemons/application_group_rb.html +0 -101
  58. data/html/files/lib/daemons/application_rb.html +0 -110
  59. data/html/files/lib/daemons/cmdline_rb.html +0 -101
  60. data/html/files/lib/daemons/controller_rb.html +0 -101
  61. data/html/files/lib/daemons/daemonize_rb.html +0 -207
  62. data/html/files/lib/daemons/exceptions_rb.html +0 -101
  63. data/html/files/lib/daemons/monitor_rb.html +0 -108
  64. data/html/files/lib/daemons/pid_rb.html +0 -108
  65. data/html/files/lib/daemons/pidfile_rb.html +0 -108
  66. data/html/files/lib/daemons/pidmem_rb.html +0 -108
  67. data/html/files/lib/daemons_rb.html +0 -117
  68. data/html/fr_class_index.html +0 -41
  69. data/html/fr_file_index.html +0 -40
  70. data/html/fr_method_index.html +0 -91
  71. data/html/index.html +0 -24
  72. data/html/rdoc-style.css +0 -208
  73. data/test/call_as_daemon.rb +0 -12
  74. data/test/tc_main.rb +0 -24
  75. data/test/test1.rb +0 -19
  76. data/test/testapp.rb +0 -11
data/lib/daemons.rb CHANGED
@@ -7,7 +7,6 @@ require 'daemons/cmdline'
7
7
  require 'daemons/exceptions'
8
8
  require 'daemons/monitor'
9
9
 
10
-
11
10
  require 'daemons/application'
12
11
  require 'daemons/application_group'
13
12
  require 'daemons/controller'
@@ -27,7 +26,7 @@ require "daemons/version"
27
26
  # 2. <tt>Daemons.run_proc(app_name, options) { (...) }</tt>:
28
27
  # This is used in wrapper-scripts that are supposed to control a proc.
29
28
  # Control is completely passed to the daemons library.
30
- # Such wrapper script need to be invoked with command line options like 'start' or 'stop'
29
+ # Such wrapper scripts need to be invoked with command line options like 'start' or 'stop'
31
30
  # to do anything useful.
32
31
  #
33
32
  # 3. <tt>Daemons.call(options) { block }</tt>:
@@ -66,8 +65,6 @@ require "daemons/version"
66
65
  #
67
66
  module Daemons
68
67
 
69
- VERSION = "1.0.11"
70
-
71
68
  require 'daemons/daemonize'
72
69
 
73
70
 
@@ -81,7 +78,9 @@ module Daemons
81
78
  # Please note that Daemons runs this script with <tt>load <script></tt>.
82
79
  # Also note that Daemons cannot detect the directory in which the controlling
83
80
  # script resides, so this has to be either an absolute path or you have to run
84
- # the controlling script from the appropriate directory.
81
+ # the controlling script from the appropriate directory. Your script name should not
82
+ # end with _monitor because that name is reserved for a monitor process which is
83
+ # there to restart your daemon if it crashes.
85
84
  #
86
85
  # +options+:: A hash that may contain one or more of the options listed below
87
86
  #
@@ -97,7 +96,7 @@ module Daemons
97
96
  # If not given, ARGV (the parameters given to the Ruby process) will be used.
98
97
  # <tt>:dir_mode</tt>:: Either <tt>:script</tt> (the directory for writing the pid files to
99
98
  # given by <tt>:dir</tt> is interpreted relative
100
- # to the script location given by +script+) or <tt>:normal</tt> (the directory given by
99
+ # to the script location given by +script+, the default) or <tt>:normal</tt> (the directory given by
101
100
  # <tt>:dir</tt> is interpreted as a (absolute or relative) path) or <tt>:system</tt>
102
101
  # (<tt>/var/run</tt> is used as the pid file directory)
103
102
  #
@@ -107,14 +106,19 @@ module Daemons
107
106
  # <tt>:ontop</tt>:: When given (i.e. set to true), stay on top, i.e. do not daemonize the application
108
107
  # (but the pid-file and other things are written as usual)
109
108
  # <tt>:mode</tt>:: <tt>:load</tt> Load the script with <tt>Kernel.load</tt>;
109
+ # note that :stop_proc only works for the :load (and :proc) mode.
110
110
  # <tt>:exec</tt> Execute the script file with <tt>Kernel.exec</tt>
111
111
  # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
112
112
  # pid-file directory if the application exits due to an uncaught exception
113
113
  # <tt>:monitor</tt>:: Monitor the programs and restart crashed instances
114
+ # <tt>:log_dir</tt>:: A specific directory to put the log files into (when not given, resort to the default
115
+ # location as derived from the :dir_mode and :dir options
114
116
  # <tt>:log_output</tt>:: When given (i.e. set to true), redirect both STDOUT and STDERR to a logfile named '[app_name].output' in the pid-file directory
115
117
  # <tt>:keep_pid_files</tt>:: When given do not delete lingering pid-files (files for which the process is no longer running).
116
118
  # <tt>:hard_exit</tt>:: When given use exit! to end a daemons instead of exit (this will for example
117
119
  # not call at_exit handlers).
120
+ # <tt>:stop_proc</tt>:: A proc that will be called when the daemonized process receives a request to stop (works only for :load and :proc mode)
121
+ #
118
122
  # -----
119
123
  #
120
124
  # === Example:
@@ -159,6 +163,7 @@ module Daemons
159
163
  # +options+:: A hash that may contain one or more of the options listed in the documentation for Daemons.run
160
164
  #
161
165
  # A block must be given to this function. The block will be used as the :proc entry in the options hash.
166
+ #
162
167
  # -----
163
168
  #
164
169
  # === Example:
@@ -180,7 +185,7 @@ module Daemons
180
185
  # we do not have a script location so the the :script :dir_mode cannot be used, change it to :normal
181
186
  if [nil, :script].include? options[:dir_mode]
182
187
  options[:dir_mode] = :normal
183
- options[:dir] = File.expand_path('.')
188
+ options[:dir] ||= File.expand_path('.')
184
189
  end
185
190
 
186
191
  @controller = Controller.new(options, options[:ARGV] || ARGV)
@@ -198,6 +203,8 @@ module Daemons
198
203
  # Execute the block in a new daemon. <tt>Daemons.call</tt> will return immediately
199
204
  # after spawning the daemon with the new Application object as a return value.
200
205
  #
206
+ # +app_name+:: The name of the application.
207
+ #
201
208
  # +options+:: A hash that may contain one or more of the options listed below
202
209
  #
203
210
  # +block+:: The block to call in the daemon.
@@ -212,6 +219,7 @@ module Daemons
212
219
  #
213
220
  # === Example:
214
221
  # options = {
222
+ # :app_name => "myproc",
215
223
  # :backtrace => true,
216
224
  # :monitor => true,
217
225
  # :ontop => true
@@ -233,7 +241,9 @@ module Daemons
233
241
  options[:proc] = block
234
242
  options[:mode] = :proc
235
243
 
236
- @group ||= ApplicationGroup.new('proc', options)
244
+ options[:app_name] ||= 'proc'
245
+
246
+ @group ||= ApplicationGroup.new(options[:app_name], options)
237
247
 
238
248
  new_app = @group.new_application(options)
239
249
  new_app.start
@@ -251,12 +261,27 @@ module Daemons
251
261
  # <tt>:ontop</tt>:: When given, stay on top, i.e. do not daemonize the application
252
262
  # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
253
263
  # pid-file directory if the application exits due to an uncaught exception
264
+ # <tt>:app_name</tt>:: The name of the application. This will be
265
+ # used to contruct the name of the pid files
266
+ # and log files. Defaults to the basename of
267
+ # the script.
268
+ # <tt>:dir_mode</tt>:: Either <tt>:script</tt> (the directory for writing files to
269
+ # given by <tt>:dir</tt> is interpreted relative
270
+ # to the script location given by +script+, the default) or <tt>:normal</tt> (the directory given by
271
+ # <tt>:dir</tt> is interpreted as a (absolute or relative) path) or <tt>:system</tt>
272
+ # (<tt>/var/run</tt> is used as the file directory)
273
+ #
274
+ # <tt>:dir</tt>:: Used in combination with <tt>:dir_mode</tt> (description above)
275
+ # <tt>:log_dir</tt>:: A specific directory to put the log files into (when not given, resort to the default
276
+ # location as derived from the :dir_mode and :dir options
277
+ # <tt>:log_output</tt>:: When given (i.e. set to true), redirect both STDOUT and STDERR to a logfile named '[app_name].output' in the pid-file directory
254
278
  # -----
255
279
  #
256
280
  # === Example:
257
281
  # options = {
258
282
  # :backtrace => true,
259
- # :ontop => true
283
+ # :ontop => true,
284
+ # :log_output => true
260
285
  # }
261
286
  #
262
287
  # Daemons.daemonize(options)
@@ -264,11 +289,14 @@ module Daemons
264
289
  # # Server loop:
265
290
  # loop {
266
291
  # conn = accept_conn()
292
+ # puts "some text which goes to the output logfile"
267
293
  # serve(conn)
268
294
  # }
269
295
  #
270
296
  def daemonize(options = {})
271
- @group ||= ApplicationGroup.new('self', options)
297
+ options[:script] ||= File.basename(__FILE__)
298
+
299
+ @group ||= ApplicationGroup.new(options[:app_name] || options[:script], options)
272
300
 
273
301
  @group.new_application(:mode => :none).start
274
302
  end
@@ -1,5 +1,10 @@
1
1
  require 'daemons/pidfile'
2
2
  require 'daemons/pidmem'
3
+ require 'daemons/change_privilege'
4
+ require 'daemons/daemonize'
5
+ require 'daemons/exceptions'
6
+
7
+ require 'timeout'
3
8
 
4
9
 
5
10
  module Daemons
@@ -29,8 +34,12 @@ module Daemons
29
34
 
30
35
  @dir_mode = @dir = @script = nil
31
36
 
37
+ @force_kill_waittime = @options[:force_kill_waittime] || 20
38
+
32
39
  unless @pid = pid
33
- if dir = pidfile_dir
40
+ if @options[:no_pidfiles]
41
+ @pid = PidMem.new
42
+ elsif dir = pidfile_dir
34
43
  @pid = PidFile.new(dir, @group.app_name, @group.multiple)
35
44
  else
36
45
  @pid = PidMem.new
@@ -38,6 +47,12 @@ module Daemons
38
47
  end
39
48
  end
40
49
 
50
+ def change_privilege
51
+ user = options[:user]
52
+ group = options[:group]
53
+ CurrentProcess.change_privilege(user, group) if user
54
+ end
55
+
41
56
  def script
42
57
  @script || @group.script
43
58
  end
@@ -46,22 +61,28 @@ module Daemons
46
61
  Pid.dir(@dir_mode || @group.dir_mode, @dir || @group.dir, @script || @group.script)
47
62
  end
48
63
 
64
+ def logdir
65
+ logdir = options[:log_dir]
66
+ unless logdir
67
+ logdir = options[:dir_mode] == :system ? '/var/log' : pidfile_dir
68
+ end
69
+ logdir
70
+ end
71
+
49
72
  def output_logfile
50
- logdir = options[:dir_mode] == :system ? '/var/log' : pidfile_dir
51
73
  (options[:log_output] && logdir) ? File.join(logdir, @group.app_name + '.output') : nil
52
74
  end
53
75
 
54
76
  def logfile
55
- logdir = options[:dir_mode] == :system ? '/var/log' : pidfile_dir
56
77
  logdir ? File.join(logdir, @group.app_name + '.log') : nil
57
78
  end
58
79
 
59
80
  # this function is only used to daemonize the currently running process (Daemons.daemonize)
60
81
  def start_none
61
82
  unless options[:ontop]
62
- Daemonize.daemonize(nil, @group.app_name) #(logfile)
83
+ Daemonize.daemonize(output_logfile, @group.app_name)
63
84
  else
64
- Daemonize.simulate
85
+ Daemonize.simulate(output_logfile)
65
86
  end
66
87
 
67
88
  @pid.pid = Process.pid
@@ -113,14 +134,12 @@ module Daemons
113
134
 
114
135
  # note that we cannot remove the pid file if we run in :ontop mode (i.e. 'ruby ctrl_exec.rb run')
115
136
  @pid.pid = Process.pid
116
-
137
+
117
138
  ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
118
139
  # haven't tested yet if this is really passed to the exec'd process...
119
140
 
120
-
121
-
141
+ started()
122
142
  Kernel.exec(script(), *(@app_argv || []))
123
- #Kernel.exec(script(), *ARGV)
124
143
  end
125
144
 
126
145
  def start_load
@@ -134,7 +153,7 @@ module Daemons
134
153
 
135
154
 
136
155
  # We need this to remove the pid-file if the applications exits by itself.
137
- # Note that <tt>at_text</tt> will only be run if the applications exits by calling
156
+ # Note that <tt>at_exit</tt> will only be run if the applications exits by calling
138
157
  # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
139
158
  # in your application!
140
159
  #
@@ -154,7 +173,15 @@ module Daemons
154
173
  # Note that the applications is not supposed to overwrite the signal handler for
155
174
  # 'TERM'.
156
175
  #
176
+ $daemons_stop_proc = options[:stop_proc]
157
177
  trap(SIGNAL) {
178
+ begin
179
+ if $daemons_stop_proc
180
+ $daemons_stop_proc.call
181
+ end
182
+ rescue ::Exception
183
+ end
184
+
158
185
  begin; @pid.cleanup; rescue ::Exception; end
159
186
  $daemons_sigterm = true
160
187
 
@@ -172,6 +199,7 @@ module Daemons
172
199
  ARGV.clear
173
200
  ARGV.concat @app_argv if @app_argv
174
201
 
202
+ started()
175
203
  # TODO: begin - rescue - end around this and exception logging
176
204
  load script()
177
205
  end
@@ -180,6 +208,9 @@ module Daemons
180
208
  return unless p = options[:proc]
181
209
 
182
210
  myproc = proc do
211
+
212
+ @pid.pid = Process.pid
213
+
183
214
  # We need this to remove the pid-file if the applications exits by itself.
184
215
  # Note that <tt>at_text</tt> will only be run if the applications exits by calling
185
216
  # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
@@ -201,7 +232,15 @@ module Daemons
201
232
  # Note that the applications is not supposed to overwrite the signal handler for
202
233
  # 'TERM'.
203
234
  #
235
+ $daemons_stop_proc = options[:stop_proc]
204
236
  trap(SIGNAL) {
237
+ begin
238
+ if $daemons_stop_proc
239
+ $daemons_stop_proc.call
240
+ end
241
+ rescue ::Exception
242
+ end
243
+
205
244
  begin; @pid.cleanup; rescue ::Exception; end
206
245
  $daemons_sigterm = true
207
246
 
@@ -212,16 +251,17 @@ module Daemons
212
251
  end
213
252
  }
214
253
 
254
+ started()
255
+
215
256
  p.call()
216
257
  end
217
258
 
218
259
  unless options[:ontop]
219
- @pid.pid = Daemonize.call_as_daemon(myproc, output_logfile, @group.app_name)
260
+ Daemonize.call_as_daemon(myproc, output_logfile, @group.app_name)
261
+
220
262
  else
221
263
  Daemonize.simulate(output_logfile)
222
264
 
223
- @pid.pid = Process.pid
224
-
225
265
  myproc.call
226
266
 
227
267
  # why did we use this??
@@ -237,12 +277,17 @@ module Daemons
237
277
  # Process.detach(@pid.pid)
238
278
  # end
239
279
  end
280
+
240
281
  end
241
282
 
242
283
 
243
- def start
244
- @group.create_monitor(@group.applications[0] || self) unless options[:ontop] # we don't monitor applications in the foreground
284
+ def start(restart = false)
285
+ change_privilege
245
286
 
287
+ if not restart
288
+ @group.create_monitor(self) unless options[:ontop] # we don't monitor applications in the foreground
289
+ end
290
+
246
291
  case options[:mode]
247
292
  when :none
248
293
  # this is only used to daemonize the currently running process
@@ -258,6 +303,14 @@ module Daemons
258
303
  end
259
304
  end
260
305
 
306
+ def started
307
+ if pid = @pid.pid
308
+ puts "#{self.group.app_name}: process with pid #{pid} started."
309
+ STDOUT.flush
310
+ end
311
+ end
312
+
313
+
261
314
  # def run
262
315
  # if @group.controller.options[:exec]
263
316
  # run_via_exec()
@@ -274,6 +327,18 @@ module Daemons
274
327
  #
275
328
  # end
276
329
 
330
+ def reload
331
+ if @pid.pid == 0
332
+ zap
333
+ start
334
+ else
335
+ begin
336
+ Process.kill('HUP', @pid.pid)
337
+ rescue
338
+ # ignore
339
+ end
340
+ end
341
+ end
277
342
 
278
343
  # This is a nice little function for debugging purposes:
279
344
  # In case a multi-threaded ruby script exits due to an uncaught exception
@@ -316,30 +381,70 @@ module Daemons
316
381
  end
317
382
 
318
383
 
319
- def stop
320
- if options[:force] and not running?
384
+ def stop(no_wait = false)
385
+ if not running?
321
386
  self.zap
322
387
  return
323
388
  end
324
389
 
390
+ pid = @pid.pid
391
+
325
392
  # Catch errors when trying to kill a process that doesn't
326
393
  # exist. This happens when the process quits and hasn't been
327
394
  # restarted by the monitor yet. By catching the error, we allow the
328
395
  # pid file clean-up to occur.
329
- pid = @pid.pid
330
396
  begin
331
397
  Process.kill(SIGNAL, pid)
332
- while Pid.running?(pid)
333
- sleep 0.1
334
- end
335
398
  rescue Errno::ESRCH => e
336
- puts "#{e} #{@pid.pid}"
399
+ puts "#{e} #{pid}"
337
400
  puts "deleting pid-file."
338
401
  end
339
402
 
340
- # We try to remove the pid-files by ourselves, in case the application
341
- # didn't clean it up.
342
- begin; @pid.cleanup; rescue ::Exception; end
403
+ if not no_wait
404
+ if @force_kill_waittime > 0
405
+ puts "#{self.group.app_name}: trying to stop process with pid #{pid}..."
406
+ STDOUT.flush
407
+
408
+ begin
409
+ Timeout::timeout(@force_kill_waittime, TimeoutError) {
410
+ while Pid.running?(pid)
411
+ sleep(0.2)
412
+ end
413
+ }
414
+ rescue TimeoutError
415
+ puts "#{self.group.app_name}: process with pid #{pid} won't stop, we forcefully kill it..."
416
+ STDOUT.flush
417
+
418
+ begin
419
+ Process.kill('KILL', pid)
420
+ rescue Errno::ESRCH
421
+ end
422
+
423
+ begin
424
+ Timeout::timeout(20, TimeoutError) {
425
+ while Pid.running?(pid)
426
+ sleep(1)
427
+ end
428
+ }
429
+ rescue TimeoutError
430
+ puts "#{self.group.app_name}: unable to forcefully kill process with pid #{pid}."
431
+ STDOUT.flush
432
+ end
433
+ end
434
+ end
435
+
436
+
437
+ end
438
+
439
+ sleep(0.1)
440
+ unless Pid.running?(pid)
441
+ # We try to remove the pid-files by ourselves, in case the application
442
+ # didn't clean it up.
443
+ begin; @pid.cleanup; rescue ::Exception; end
444
+
445
+ puts "#{self.group.app_name}: process with pid #{pid} successfully stopped."
446
+ STDOUT.flush
447
+ end
343
448
 
344
449
  end
345
450
 
@@ -373,4 +478,4 @@ module Daemons
373
478
  end
374
479
  end
375
480
 
376
- end
481
+ end