rsence 2.0.0.0.pre → 2.0.0.1.pre

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/lib/daemon/daemon.rb CHANGED
@@ -8,9 +8,6 @@
8
8
  ##
9
9
  #++
10
10
 
11
- # Use the default configuration:
12
- require 'conf/default'
13
-
14
11
  # Use rubygems to load rack
15
12
  require 'rubygems'
16
13
  require 'rack'
@@ -47,247 +44,336 @@ require 'transporter/transporter'
47
44
  require 'http/broker'
48
45
 
49
46
 
50
- module RSence
51
-
52
- # adapted from:
53
- # http://snippets.dzone.com/posts/show/2265
47
+ module ::RSence
54
48
 
55
- require 'fileutils'
49
+ @@launch_pid = Process.pid
50
+ def self.launch_pid
51
+ return @@launch_pid
52
+ end
56
53
 
54
+ # The Controller module handles the damonizing
55
+ # operations of the process referred to as +daemon+
57
56
  module Daemon
58
57
 
59
- class Base
60
- def self.pid_fn
61
- RSence.config[:daemon][:pid_fn]
62
- end
63
- def self.log_fn
64
- RSence.config[:daemon][:log_fn]
65
- end
66
- def self.daemonize
67
- Controller.daemonize(self)
58
+ # Writes the process id to disk, the pid_fn method of the daemon contains
59
+ # the name of the pid file.
60
+ def self.write_pid( daemon, pid )
61
+ File.open( daemon.pid_fn, 'w' ) do |pidfile|
62
+ pidfile.write( pid )
68
63
  end
69
64
  end
70
65
 
71
- module PidFile
72
- def self.store(daemon, pid)
73
- dir_path_pid = File.split( daemon.pid_fn )[0]
74
- FileUtils.mkdir_p( dir_path_pid ) unless File.exists? dir_path_pid
75
- File.open(daemon.pid_fn, 'w') {|f| f << pid}
76
- end
77
- def self.recall(daemon)
78
- IO.read(daemon.pid_fn).to_i rescue nil
79
- end
66
+ # Reads the process id from disk, the pid_fn method of the daemon contains
67
+ # the name of the pid file. Returns nil on errors.
68
+ def self.read_pid( daemon )
69
+ File.read( daemon.pid_fn ).to_i rescue nil
80
70
  end
81
71
 
82
- module Controller
83
-
84
- def self.print_status(daemon)
85
- is_running = self.status(daemon)
86
- puts "Riassence Framework is #{'not ' unless is_running}running"
72
+ def self.responds?( daemon )
73
+ wait_signal_response( daemon, 'USR2' )
74
+ end
75
+
76
+ # Reads the pid file and calls the process.
77
+ # Returns true if the process responds, false otherwise (no process)
78
+ def self.status( daemon )
79
+ pid = read_pid( daemon )
80
+ return nil if not pid
81
+ return responds?( daemon )
82
+ end
83
+
84
+ # Redirects standard input and errors to the log files
85
+ def self.start_logging( daemon )
86
+ outpath = "#{daemon.log_fn}.stdout"
87
+ errpath = "#{daemon.log_fn}.stderr"
88
+ STDOUT.reopen( outpath, (File.exist?( outpath ) ? 'a' : 'w') )
89
+ STDOUT.sync = true
90
+ STDERR.reopen( errpath, (File.exist?( errpath ) ? 'a' : 'w') )
91
+ STDERR.sync = true
92
+ end
93
+
94
+ def self.write_signal_response( daemon, signal )
95
+ sig_fn = daemon.pid_fn+'.response.'+signal
96
+ File.open(sig_fn,'w') do |file|
97
+ file.write(Process.pid.to_s)
87
98
  end
88
-
89
- ## Status is not entirely reliable
90
- def self.status(daemon)
91
- if File.file?(daemon.pid_fn)
92
- begin
93
- pid = File.read(daemon.pid_fn).to_i
94
- pid && Process.kill('USR2',pid)
95
- return true
96
- rescue Errno::ESRCH => e
97
- return false
98
- end
99
- end
100
- return false
99
+ end
100
+
101
+ def self.delete_signal_response( daemon, signal )
102
+ sig_fn = daemon.pid_fn+'.response.'+signal
103
+ if File.file?( sig_fn )
104
+ File.delete( sig_fn )
101
105
  end
102
-
103
- def self.open_log( outpath, errpath )
104
- dir_path_out = File.split( outpath )[0]
105
- FileUtils.mkdir_p( dir_path_out ) unless File.exists? dir_path_out
106
- dir_path_err = File.split( outpath )[0]
107
- FileUtils.mkdir_p( dir_path_err ) unless File.exists? dir_path_err
108
- if File.exist?( outpath )
109
- STDOUT.reopen( outpath, "a" )
110
- else
111
- STDOUT.reopen( outpath, "w" )
106
+ end
107
+
108
+ def self.wait_signal_response( daemon, signal, timeout = 10,
109
+ debug_pre = '<', debug_suf = '>', sleep_secs = 0.2 )
110
+ begin
111
+ if RSence.args[:verbose]
112
+ print debug_pre
113
+ STDOUT.flush
112
114
  end
113
- if File.exist?( errpath )
114
- STDERR.reopen( errpath, "a" )
115
- else
116
- STDERR.reopen( errpath, "w" )
115
+ pid = read_pid( daemon )
116
+ sig_fn = daemon.pid_fn+'.response.'+signal
117
+ File.delete( sig_fn ) if File.file?( sig_fn )
118
+ status = Process.kill( signal, pid )
119
+ time_out = Time.now + timeout
120
+ until time_out > Time.now or File.file?( sig_fn )
121
+ if RSence.args[:verbose]
122
+ print "."
123
+ STDOUT.flush
124
+ end
125
+ sleep sleep_secs
117
126
  end
118
- STDOUT.sync = true
119
- STDERR.sync = true
120
- end
121
-
122
- def self.log_io(daemon)
123
- outpath = "#{daemon.log_fn}.stdout"
124
- errpath = "#{daemon.log_fn}.stderr"
125
- if $DEBUG_MODE
126
- Thread.new do
127
- puts "Waiting 2 seconds before switching output to log file #{outpath} and .../#{File.split(errpath)[1]}"
128
- sleep 2
129
- puts "Switching to stdin and stdout to log mode. Follow the log file to see further server output."
130
- self.open_log( outpath, errpath )
127
+ sleep sleep_secs
128
+ if File.file?( sig_fn )
129
+ sig_pid = File.read( sig_fn ).to_i
130
+ if sig_pid != pid
131
+ puts "Warning, signal PID mismatch. Expected #{pid}, got #{sig_pid}"
131
132
  end
133
+ File.delete( sig_fn )
132
134
  else
133
- self.open_log( outpath, errpath )
135
+ puts "Warning, signal response file disappeared! Expected #{sig_fn}"
134
136
  end
137
+ puts debug_suf if RSence.args[:verbose]
138
+ return true
139
+ rescue Errno::ESRCH
140
+ return false
135
141
  end
142
+ end
143
+
144
+ # Traps common kill signals
145
+ def self.trap_signals( daemon )
136
146
 
137
- def self.daemonize(daemon)
138
- cmd = ARGV[0]
139
- if ['i386-mingw32','x86-mingw32'].include? RUBY_PLATFORM
140
- if cmd == 'start'
141
- warn "Running on Microsoft Windows: unable to background. Using the run command instead of start"
142
- cmd = 'run'
143
- elsif cmd != 'run'
144
- puts "Invalid command: #{cmd.inspect}. Windows supports only run and help."
145
- exit
146
- end
147
- elsif not cmd or not %w[status start stop restart save run].include?( cmd )
148
- puts "Invalid command. Please specify one of the following: run, start, stop, restart, status, save or help."
147
+ # Triggered with 'kill -USR1 `cat rsence.pid`'
148
+ Signal.trap( 'USR1' ) do
149
+ daemon.usr1
150
+ write_signal_response( daemon, 'USR1' )
151
+ end
152
+ Signal.trap('USR2') do
153
+ daemon.usr2
154
+ write_signal_response( daemon, 'USR2' )
155
+ end
156
+ ['INT', 'TERM', 'KILL'].each do | signal |
157
+ Signal.trap( signal ) do
158
+ puts "RSence killed with signal #{signal.inspect}" if RSence.args[:verbose]
159
+ daemon.usr1
160
+ daemon.stop
161
+ # delete_signal_response( daemon, 'USR1' )
162
+ # delete_signal_response( daemon, 'USR2' )
163
+ File.delete( daemon.pid_fn ) if File.file?( daemon.pid_fn )
164
+ write_signal_response( daemon, signal )
165
+ puts "Shutdown complete."
149
166
  exit
150
167
  end
151
- case cmd
152
- when 'run'
153
- puts "Starting as a foreground process."
154
- puts "Enter CTRL-C to stop."
155
- self.start_fg(daemon)
156
- when 'status'
157
- self.print_status(daemon)
158
- when 'start'
159
- self.start(daemon)
160
- when 'stop'
161
- self.stop(daemon)
162
- when 'restart'
163
- self.stop(daemon,true)
164
- self.start(daemon)
165
- when 'save'
166
- self.save(daemon)
167
- end
168
168
  end
169
- def self.start_fg(daemon)
170
- ['INT', 'TERM', 'KILL'].each do |signal|
171
- Signal.trap(signal) do
172
- puts "Got signal #{signal.inspect}"
173
- daemon.stop
174
- exit
169
+ # Signal.trap('HUP') do
170
+ # daemon.restart
171
+ # end
172
+ end
173
+
174
+ def self.handle_pid( daemon )
175
+ if RSence.pid_support?
176
+ is_running = status( daemon )
177
+ if is_running
178
+ puts "RSence is already running."
179
+ puts "Stop the existing process first: see 'rsence help stop'"
180
+ if RSence.launch_pid != Process.pid
181
+ Process.kill( 'INT', RSence.launch_pid )
175
182
  end
183
+ exit
184
+ elsif not is_running and File.file?( daemon.pid_fn )
185
+ puts "Stale pid file, removing.."
186
+ File.delete( daemon.pid_fn )
176
187
  end
188
+ trap_signals( daemon )
189
+ pid = Process.pid
190
+ write_pid( daemon, pid )
191
+ return pid
192
+ else
193
+ return false
194
+ end
195
+ end
196
+
197
+ def self.run( daemon )
198
+ handle_pid( daemon )
199
+ daemon.run
200
+ exit
201
+ end
202
+
203
+ def self.start( daemon )
204
+ fork do
205
+ exit if fork
206
+ handle_pid( daemon )
177
207
  daemon.start
208
+ end
209
+ Signal.trap('INT') do
210
+ puts "RSence startup failed. Please inspect the log and/or run in debug mode."
178
211
  exit
179
212
  end
180
- def self.start(daemon)
181
- is_running = self.status(daemon)
182
- if is_running
183
- puts "Riassence Framework is already running. Try restart."
184
- exit
185
- elsif not is_running and File.file?(daemon.pid_fn)
186
- puts "Stale pid file, removing.."
187
- FileUtils.rm(daemon.pid_fn)
188
- end
189
- fork do
190
- Process.setsid
191
- exit if fork
192
- PidFile.store(daemon, Process.pid)
193
- Signal.trap('USR1') do
194
- if @transporter != nil
195
- @transporter.plugins.shutdown if @transporter.plugins
196
- @transporter.sessions.shutdown if @transporter.session
197
- end
198
- end
199
- Signal.trap('USR2') do
200
- puts "Alive."
201
- end
202
- ['INT', 'TERM', 'KILL'].each do |signal|
203
- Signal.trap(signal) do
204
- puts "Got signal #{signal.inspect}"
205
- daemon.stop
206
- exit
207
- end
208
- end
209
- Signal.trap('HUP') {
210
- daemon.restart
211
- }
212
- STDIN.reopen( "/dev/null" )
213
- daemon.start
214
- end
215
-
216
- timeout = Time.now + 10
217
- sleep 0.1 until self.status(daemon) or timeout < Time.now
218
-
219
- if timeout < Time.now
220
- puts "Riassence Framework did not start, please check the logfile."
221
- else
222
- puts "Riassence Framework is running now."
223
- end
224
-
225
- sleep 2.5 if $DEBUG_MODE
226
-
227
- #Process.kill("USR2", File.read(daemon.pid_fn).to_i)
213
+ Signal.trap('TERM') do
214
+ puts "RSence is online at http://#{daemon.addr}:#{daemon.port}/"
215
+ exit
228
216
  end
229
- def self.save(daemon,is_restart=false)
230
- if !File.file?(daemon.pid_fn)
231
- puts "Pid file not found. Is Riassence Framework started?"
232
- return if is_restart
233
- exit
234
- end
235
- pid = File.read(daemon.pid_fn).to_i
236
- begin
237
- pid && Process.kill("USR1", pid)
217
+ sleep 1 while true
218
+ end
219
+
220
+ # Sends the USR1 signal to the process, which in turn
221
+ # calls the save method of the daemon.
222
+ def self.save( daemon )
223
+ status_ = status( daemon )
224
+ if status_
225
+ if wait_signal_response( daemon, 'USR1', 10, 'saving.', 'saved', 0.3 )
238
226
  puts "Session data saved."
239
- rescue
240
- puts "Error, no such pid (#{pid}) running"
227
+ else
228
+ puts "Warning: saving timed out! Session data not saved."
241
229
  end
230
+ elsif status_ == false
231
+ puts "Warning, no such process (#{pid}) running: unable to save."
232
+ elsif status_ == nil
233
+ puts "No pid file: unable to save."
234
+ else
235
+ throw "Unexpected process status: #{status_.inspect}"
242
236
  end
243
- def self.stop(daemon,is_restart=false)
244
- self.save(daemon,is_restart)
245
- if !File.file?(daemon.pid_fn)
246
- puts "Pid file not found. Is Riassence Framework started?"
247
- return if is_restart
248
- exit
249
- end
250
- pid = PidFile.recall(daemon)
251
- begin
252
- pid && Process.kill("TERM", pid)
253
- puts "Riassence Framework is stopped now."
254
- rescue
255
- puts "Error, no such pid (#{pid}) running"
237
+ end
238
+
239
+ # Sends the TERM signal to the process, which in turn
240
+ # calls the stop method of the daemon
241
+ def self.stop( daemon )#,is_restart=false)
242
+ status_ = status( daemon )
243
+ if status_
244
+ if wait_signal_response( daemon, 'TERM', 10, 'killing.', 'killed', 0.3 )
245
+ puts "RSence is terminated now."
246
+ else
247
+ puts "Warning: termination timed out!"
248
+ puts "RSence might still be running, please ensure manually."
256
249
  end
257
- FileUtils.rm(daemon.pid_fn)
250
+ elsif status_ == false
251
+ puts "Warning, no such process (#{pid}) running."
252
+ elsif status_ == nil
253
+ puts "Warning, no pid file (process not running)."
254
+ else
255
+ throw "Unexpected process status: #{status_.inspect}"
258
256
  end
259
257
  end
258
+
259
+ # Main entry point called from the daemon process passing +self+ as +daemon+.
260
+ def self.daemonize( daemon )
261
+
262
+ # Uses the command-line tool command to decide what to do.
263
+ case RSence.cmd
264
+ when :run
265
+ run( daemon )
266
+ when :start
267
+ start( daemon )
268
+ when :stop
269
+ stop( daemon )
270
+ when :restart
271
+ stop( daemon )
272
+ start( daemon )
273
+ when :save
274
+ save( daemon )
275
+ end
276
+ end
277
+
260
278
  end
261
279
 
262
- class HTTPDaemon < Daemon::Base
263
- def self.start
280
+ # Simple process control, constructed here and called from Daemon::Controller
281
+ class HTTPDaemon
282
+
283
+ def run
284
+ puts "Starting as a foreground process." if RSence.args[:verbose]
285
+ puts "Press CTRL-C to terminate."
286
+
264
287
  @transporter = Transporter.new
265
288
 
266
- unless ARGV.include?('--log-fg')
267
- Daemon::Controller.log_io(self)
289
+ conf = RSence.config[:http_server]
290
+
291
+ unless RSence.args[:log_fg]
292
+ Daemon.start_logging( self )
268
293
  end
269
294
 
270
295
  # This is the main http handler instance:
271
296
  @broker = Broker.start(
272
297
  @transporter,
273
- RSence.config[:http_server][:rack_handler],
274
- RSence.config[:http_server][:bind_address],
275
- RSence.config[:http_server][:port]
298
+ conf[:rack_handler],
299
+ conf[:bind_address],
300
+ conf[:port]
276
301
  )
277
302
 
278
- yield @broker if block_given?
279
-
280
303
  end
281
- def self.restart
282
- self.stop
283
- @broker = nil
284
- self.start
304
+
305
+ # Returns the pid file path.
306
+ def pid_fn
307
+ RSence.config[:daemon][:pid_fn]
308
+ end
309
+
310
+ # Returns the log path.
311
+ def log_fn
312
+ RSence.config[:daemon][:log_fn]
313
+ end
314
+
315
+ def addr
316
+ RSence.config[:http_server][:bind_address]
317
+ end
318
+
319
+ def port
320
+ RSence.config[:http_server][:port]
285
321
  end
286
- def self.stop
322
+
323
+ # Called by Controller#start, contains RSence-specific operations
324
+ def start
325
+
326
+ @transporter = Transporter.new
327
+
328
+ conf = RSence.config[:http_server]
329
+
330
+ unless RSence.args[:log_fg]
331
+ Daemon.start_logging( self )
332
+ STDIN.reopen( "/dev/null" )
333
+ end
334
+
335
+ Process.setsid
336
+
337
+ # This is the main http handler instance:
338
+ @broker = Broker.start(
339
+ @transporter,
340
+ conf[:rack_handler],
341
+ conf[:bind_address],
342
+ conf[:port]
343
+ )
344
+ yield @broker
345
+
346
+ end
347
+
348
+ # Called by Controller#stop, contains RSence-specific operations
349
+ def stop
287
350
  @transporter.plugins.shutdown
288
351
  @transporter.sessions.shutdown
289
352
  end
353
+
354
+ # Called on USR1 signals (save data)
355
+ def usr1
356
+ save
357
+ end
358
+
359
+ # Save state
360
+ def save
361
+ puts "Saving."
362
+ @transporter.plugins.delegate(:flush)
363
+ @transporter.sessions.store_sessions
364
+ puts "Saved."
365
+ end
366
+
367
+ # Called on USR2 signals ("Alive?")
368
+ def usr2
369
+ puts "Alive."
370
+ end
371
+
372
+ # Main entry point, daemonizes itself using Controller.
373
+ def daemonize!
374
+ Daemon.daemonize( self )
375
+ end
376
+
290
377
  end
291
378
 
292
379
  end
293
-
@@ -81,10 +81,9 @@ class PluginManager
81
81
  @sessions = transporter.sessions
82
82
  end
83
83
  @plugin_paths = plugin_paths
84
- puts "Loading plugins..."
84
+ puts "Loading plugins..." if RSence.args[:verbose]
85
85
  scan_plugins
86
- puts "Plugins loaded."
87
- puts "Riassence Framework is online."
86
+ puts "Plugins loaded." if RSence.args[:verbose]
88
87
  if autoreload
89
88
  @thr = Thread.new do
90
89
  Thread.pass
@@ -123,7 +122,7 @@ class PluginManager
123
122
  if @registry.has_key?( bundle_name.to_sym )
124
123
  puts "Disabling bundle #{bundle_name}..."
125
124
  unload_bundle( bundle_name.to_sym )
126
- if ARGV.include?('-say')
125
+ if RSence.args[:say]
127
126
  Thread.new do
128
127
  Thread.pass
129
128
  system(%{say "Unloaded #{bundle_name.to_s}."})
@@ -135,7 +134,7 @@ class PluginManager
135
134
  puts "Loading bundle #{bundle_name}..."
136
135
  load_bundle( bundle_path, bundle_name.to_sym, bundle_name+'.rb' )
137
136
  call( bundle_name.to_sym, :open )
138
- if ARGV.include?('-say')
137
+ if RSence.args[:say]
139
138
  Thread.new do
140
139
  Thread.pass
141
140
  system(%{say "Loaded #{bundle_name.to_s}."})
@@ -149,7 +148,7 @@ class PluginManager
149
148
  unload_bundle( bundle_name.to_sym )
150
149
  load_bundle( bundle_path, bundle_name.to_sym, bundle_name+'.rb' )
151
150
  call( bundle_name.to_sym, :open )
152
- if ARGV.include?('-say')
151
+ if RSence.args[:say]
153
152
  Thread.new do
154
153
  Thread.pass
155
154
  system(%{say "Reloaded #{bundle_name.to_s}."})
@@ -34,11 +34,9 @@ class Transporter
34
34
  @valuemanager = ValueManager.new
35
35
  @sessions = SessionManager.new( self )
36
36
  @plugins = PluginManager.new( ::RSence.config[:plugin_paths], self, $DEBUG_MODE )
37
-
38
- # Used by:
39
- # plugins/main/main.rb
40
- $SESSION = @sessions
41
-
37
+ if RSence.launch_pid != Process.pid
38
+ Process.kill( 'TERM', RSence.launch_pid )
39
+ end
42
40
  end
43
41
 
44
42
  def servlet( request_type, request, response )
@@ -47,7 +45,7 @@ class Transporter
47
45
  # if $DEBUG_MODE and uri == $config[:index_html][:respond_address] and request_type == :get
48
46
  # unless ARGV.include?('-no-rescan') or ARGV.include?('--no-rescan')
49
47
  # puts "Reloading plugins."
50
- # if ARGV.include?('-say')
48
+ # if RSence.args[:say]
51
49
  # Thread.new do
52
50
  # Thread.pass
53
51
  # system('say "Reloading plugins."')
@@ -55,7 +53,7 @@ class Transporter
55
53
  # end
56
54
  # @plugins.rescan
57
55
  # puts "Plugins reloaded."
58
- # if ARGV.include?('-say')
56
+ # if RSence.args[:say]
59
57
  # Thread.new do
60
58
  # Thread.pass
61
59
  # system('say "Plugins reloaded."')
@@ -63,14 +63,17 @@ class ClientPkg < Servlet
63
63
  @log_file = nil
64
64
  end
65
65
  def log( str )
66
- puts str
67
- return
68
- if @last_time < Time.now - 30
69
- @last_time = Time.now
70
- @log_file.write( %{--- #{@last_time.strftime("%Y-%m-%d %H:%M:%S")} ---\n} )
66
+ if ::RSence.args[:verbose]
67
+ puts str
68
+ return
69
+ else
70
+ if @last_time < Time.now - 30
71
+ @last_time = Time.now
72
+ @log_file.write( %{--- #{@last_time.strftime("%Y-%m-%d %H:%M:%S")} ---\n} )
73
+ end
74
+ @log_file.write( "#{str}\n" )
75
+ @log_file.flush
71
76
  end
72
- @log_file.write( "#{str}\n" )
73
- @log_file.flush
74
77
  end
75
78
  def open
76
79
  return if @log_file
@@ -104,7 +107,7 @@ class ClientPkg < Servlet
104
107
  if @client_build.bundle_changes( @last_change )
105
108
  rebuild_client
106
109
  puts "Autobuilt."
107
- if ARGV.include?('-say')
110
+ if RSence.args[:say]
108
111
  Thread.new do
109
112
  Thread.pass
110
113
  system('say "Autobuilt."')
@@ -169,7 +172,7 @@ class ClientPkg < Servlet
169
172
 
170
173
  @thr = false
171
174
 
172
- @build_logger = BuildLogger.new( File.join(@path,'log','build_log') )
175
+ @build_logger = BuildLogger.new( File.join(::RSence.args[:env_path],'log','build_log') )
173
176
  @build_logger.open
174
177
 
175
178
  @client_build = ClientPkgBuild.new( ::RSence.config[:client_pkg], @build_logger )
@@ -538,7 +538,7 @@ class ClientPkgBuild
538
538
  ## end
539
539
  ## flush
540
540
  ## run
541
- ## `say "Autobuild complete!"` if ARGV.include?('-say')
541
+ ## `say "Autobuild complete!"` if RSence.args[:say]
542
542
  ## end
543
543
  ##end
544
544