rib_fix 0.1.2-x86-mingw32
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.
@@ -0,0 +1,433 @@
|
|
1
|
+
# Author: Genady Petelko (nukegluk@gmail.com)
|
2
|
+
# Description: The file contains implementation of 'RunInBackground' module.
|
3
|
+
|
4
|
+
require 'params'
|
5
|
+
require 'log'
|
6
|
+
|
7
|
+
module BBFS
|
8
|
+
# This library provides a basic cross-platform functionality to run arbitrary ruby scripts
|
9
|
+
# in background and control them. <br>Supported platforms: Windows, Linux, Mac
|
10
|
+
# NOTE UAC (User Account Control) should be disabled to use library on Windows 7:
|
11
|
+
# * Click on the Windows Icon
|
12
|
+
# * Click on the Control Panel
|
13
|
+
# * Type in UAC in the search box (up-right corner of your window)
|
14
|
+
# * Click on "Change User Account Control settings"
|
15
|
+
# * Drag the slider down to either "Notify me when programs try to make changes to my computer"
|
16
|
+
# or to disable it completely
|
17
|
+
# * Reboot your computer when you're ready
|
18
|
+
# == General Limitations:
|
19
|
+
# * Only ruby scripts can be run in background.
|
20
|
+
# * No multiple instances with the same name.
|
21
|
+
# == Notes:
|
22
|
+
# * Linux/Mac specific methods have _linux suffix
|
23
|
+
# * Windows specific methods have _windows suffix
|
24
|
+
# * While enhancing windows code, take care that paths are in windows format,
|
25
|
+
# <br>e.i. with "\\" file separator while ruby by default uses a "/"
|
26
|
+
# * Additional functionality such as restart, reload, etc. will be added on demand
|
27
|
+
# * Remains support to provide platform specific options for start.
|
28
|
+
# <br>For more information regarding such options
|
29
|
+
# see documentation for win32-sevice (Windows), daemons (Linux/Mac)
|
30
|
+
# == Linux Notes:
|
31
|
+
# * <tt>pid_dir</tt> parameter contains absolute path to directory where pid files will be stored.
|
32
|
+
# <br>If directory doesn't exists then it will be created.
|
33
|
+
# <br>User should have a read/write permissions to this location.
|
34
|
+
# <br>Default location: <tt>$HOME/.bbfs/pids</tt>
|
35
|
+
# * User should check that default pid directory is free from pid files of "killed" daemons.
|
36
|
+
# <br>It may happen, for example, when system finished in abnormal way then pid files were
|
37
|
+
# not deleted by daemons library.
|
38
|
+
# <br>In such case incorrect results can be received, for example for <tt>exists?</tt> method
|
39
|
+
# <br>One of the suggested methods can be before starting a daemon to check with <tt>exists?</tt>
|
40
|
+
# method whether daemon already exists and with <tt>running?</tt> method does it running.
|
41
|
+
module RunInBackground
|
42
|
+
Params.string('bg_command', nil, 'Server\'s command. Commands are: start, delete and nil for' \
|
43
|
+
' not running in background.')
|
44
|
+
Params.string('service_name', File.basename($0), 'Background service name.')
|
45
|
+
|
46
|
+
# Maximal time in seconds to wait until OS will finish a requested operation,
|
47
|
+
# e.g. daemon start/delete.
|
48
|
+
TIMEOUT = 20
|
49
|
+
|
50
|
+
if RUBY_PLATFORM =~ /linux/ or RUBY_PLATFORM =~ /darwin/
|
51
|
+
begin
|
52
|
+
require 'daemons'
|
53
|
+
require 'fileutils'
|
54
|
+
rescue LoadError
|
55
|
+
require 'rubygems'
|
56
|
+
require 'daemons'
|
57
|
+
require 'fileutils'
|
58
|
+
end
|
59
|
+
|
60
|
+
OS = :LINUX
|
61
|
+
Params.string('pid_dir', File.expand_path(File.join(Dir.home, '.bbfs', 'pids')),
|
62
|
+
'Absolute path to directory, where pid files will be stored. ' + \
|
63
|
+
'User should have a read/write permissions to this location. ' + \
|
64
|
+
'If absent then it will be created. ' + \
|
65
|
+
'It\'s actual only for Linux/Mac. ' + \
|
66
|
+
'For more information see documentation on daemons module. ' +\
|
67
|
+
'Default location is: $HOME/.bbfs/pids')
|
68
|
+
|
69
|
+
if Dir.exists? Params['pid_dir']
|
70
|
+
unless File.directory? Params['pid_dir']
|
71
|
+
raise IOError.new("pid directory #{Params['pid_dir']} should be a directory")
|
72
|
+
end
|
73
|
+
unless File.readable?(Params['pid_dir']) && File.writable?(Params['pid_dir'])
|
74
|
+
raise IOError.new("you should have read/write permissions to pid dir: #{Params['pid_dir']}")
|
75
|
+
end
|
76
|
+
else
|
77
|
+
::FileUtils.mkdir_p Params['pid_dir']
|
78
|
+
end
|
79
|
+
elsif RUBY_PLATFORM =~ /mingw/ or RUBY_PLATFORM =~ /ms/ or RUBY_PLATFORM =~ /win/
|
80
|
+
require 'rbconfig'
|
81
|
+
begin
|
82
|
+
require 'win32/service'
|
83
|
+
require 'win32/daemon'
|
84
|
+
rescue LoadError
|
85
|
+
require 'rubygems'
|
86
|
+
require 'win32/service'
|
87
|
+
require 'win32/daemon'
|
88
|
+
end
|
89
|
+
include Win32
|
90
|
+
|
91
|
+
OS = :WINDOWS
|
92
|
+
# Get ruby interpreter path. Need it to run ruby binary.
|
93
|
+
# <br>TODO check whether this code works with Windows Ruby Version Management (e.g. Pik)
|
94
|
+
RUBY_INTERPRETER_PATH = File.join(Config::CONFIG["bindir"],
|
95
|
+
Config::CONFIG["RUBY_INSTALL_NAME"] +
|
96
|
+
Config::CONFIG["EXEEXT"]).tr!('/','\\')
|
97
|
+
|
98
|
+
# Path has to be absolute cause Win32-Service demands it.
|
99
|
+
wrapper = File.join(File.dirname(__FILE__), "..", "bin", File.basename(__FILE__, ".rb"), "daemon_wrapper")
|
100
|
+
# Wrapper script, that can receive commands from Windows Service Control and run user script,
|
101
|
+
# <br> provided as it's argument
|
102
|
+
WRAPPER_SCRIPT = File.expand_path(wrapper).tr!('/','\\')
|
103
|
+
else
|
104
|
+
raise "Unsupported platform #{RUBY_PLATFORM}"
|
105
|
+
end
|
106
|
+
|
107
|
+
# Start a service/daemon.
|
108
|
+
# <br>It important to delete it after usage.
|
109
|
+
# ==== Arguments
|
110
|
+
# * <tt>binary_path</tt> - absolute path to the script that should be run in background
|
111
|
+
# NOTE for Linux script should be executable and with UNIX end-of-lines (LF).
|
112
|
+
# * <tt>binary_args</tt> - Array (not nil) of script's command line arguments
|
113
|
+
# * <tt>name</tt> - service/daemon name.
|
114
|
+
# NOTE should be unique
|
115
|
+
# * <tt>opts_specific</tt> - Hash of platform specific options (only for more specific usage)
|
116
|
+
# For more information regarding such options see documentation for
|
117
|
+
# win32-sevice (Windows), daemons (Linux/Mac)
|
118
|
+
# ==== Example (LINUX)
|
119
|
+
# <tt> RunInBackground.start "/home/user/test_app", [], "daemon_test", {:monitor => true}</tt>
|
120
|
+
def RunInBackground.start binary_path, binary_args, name, opts_specific = {}
|
121
|
+
Log.debug1("executable that should be run as daemon/service: #{binary_path}")
|
122
|
+
Log.debug1("arguments: #{binary_args}")
|
123
|
+
Log.debug1("specific options: #{opts_specific}")
|
124
|
+
|
125
|
+
if binary_path == nil or binary_args == nil or name == nil
|
126
|
+
Log.error("binary path, binary args, name arguments must be defined")
|
127
|
+
raise ArgumentError.new("binary path, binary args, name arguments must be defined")
|
128
|
+
end
|
129
|
+
|
130
|
+
if OS == :WINDOWS
|
131
|
+
new_binary_path = String.new(binary_path)
|
132
|
+
new_binary_args = Array.new(binary_args)
|
133
|
+
wrap_windows new_binary_path, new_binary_args
|
134
|
+
start_windows new_binary_path, new_binary_args, name, opts_specific
|
135
|
+
else # OS == LINUX
|
136
|
+
start_linux binary_path, binary_args, name, opts_specific
|
137
|
+
end
|
138
|
+
|
139
|
+
0.upto(TIMEOUT) do
|
140
|
+
if exists?(name) && running?(name)
|
141
|
+
puts "daemon/service #{name} started\n"
|
142
|
+
Log.info("daemon/service #{name} started")
|
143
|
+
return
|
144
|
+
end
|
145
|
+
sleep 1
|
146
|
+
end
|
147
|
+
# if got here then something gone wrong and daemon/service wasn't started in timely manner
|
148
|
+
delete name if exists? name
|
149
|
+
Log.error("daemon/service #{name} wasn't started in timely manner")
|
150
|
+
sleep(Params['log_param_max_elapsed_time_in_seconds_from_last_flush'] + 0.5)
|
151
|
+
raise "daemon/service #{name} wasn't started in timely manner"
|
152
|
+
end
|
153
|
+
|
154
|
+
def RunInBackground.start_linux binary_path, binary_args, name, opts = {}
|
155
|
+
unless File.executable? binary_path
|
156
|
+
raise ArgumentError.new("#{binary_path} is not executable.")
|
157
|
+
end
|
158
|
+
|
159
|
+
if opts.has_key? :dir
|
160
|
+
raise ArgumentError.new("No support for user-defined pid directories. See help")
|
161
|
+
end
|
162
|
+
|
163
|
+
opts[:app_name] = name
|
164
|
+
opts[:ARGV] = ['start']
|
165
|
+
(opts[:ARGV] << '--').concat(binary_args) if !binary_args.nil? && binary_args.size > 0
|
166
|
+
opts[:dir] = Params['pid_dir']
|
167
|
+
opts[:dir_mode] = :normal
|
168
|
+
|
169
|
+
Log.debug1("binary path: #{binary_path}")
|
170
|
+
Log.debug1("opts: #{opts}")
|
171
|
+
|
172
|
+
# Current process, that creates daemon, will transfer control to the Daemons library.
|
173
|
+
# So to continue working with current process, daemon creation initiated from separate process.
|
174
|
+
pid = fork do
|
175
|
+
Daemons.run binary_path, opts
|
176
|
+
end
|
177
|
+
Process.waitpid pid
|
178
|
+
end
|
179
|
+
|
180
|
+
def RunInBackground.start_windows binary_path, binary_args, name, opts = {}
|
181
|
+
raise ArgumentError.new("#{binary_path} doesn't exist'") unless File.exists? binary_path
|
182
|
+
|
183
|
+
# path that contains spaces must be escaped to be interpreted correctly
|
184
|
+
binary_path = %Q{"#{binary_path}"} if binary_path =~ / /
|
185
|
+
binary_path.tr!('/','\\')
|
186
|
+
# service should be run with the same load path as a current application
|
187
|
+
load_path = get_load_path
|
188
|
+
binary_args_str = binary_args.join ' '
|
189
|
+
|
190
|
+
opts[:binary_path_name] = \
|
191
|
+
"#{RUBY_INTERPRETER_PATH} #{load_path} #{binary_path} #{binary_args_str}"
|
192
|
+
# quotation marks must be escaped cause of ARGV parsing
|
193
|
+
opts[:binary_path_name] = opts[:binary_path_name].gsub '"', '"\\"'
|
194
|
+
opts[:service_name] = name
|
195
|
+
opts[:description] = name unless opts.has_key? :description
|
196
|
+
opts[:display_name] = name unless opts.has_key? :display_name
|
197
|
+
opts[:service_type] = Service::WIN32_OWN_PROCESS unless opts.has_key? :service_type
|
198
|
+
opts[:start_type] = Service::DEMAND_START unless opts.has_key? :start_type
|
199
|
+
|
200
|
+
# NOTE most of examples uses these dependencies. Meanwhile no explanations were found
|
201
|
+
opts[:dependencies] = ['W32Time','Schedule'] unless opts.has_key? :dependencies
|
202
|
+
# NOTE most of examples uses this option as defined beneath. The default is nil.
|
203
|
+
#opts[:load_order_group] = 'Network' unless opts.has_key? :load_order_group
|
204
|
+
|
205
|
+
Log.debug1("create service options: #{opts}")
|
206
|
+
Service.create(opts)
|
207
|
+
begin
|
208
|
+
Service.start(opts[:service_name])
|
209
|
+
rescue
|
210
|
+
Service.delete(opts[:service_name]) if Service.exists?(opts[:service_name])
|
211
|
+
raise
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Rerun current script in background.
|
216
|
+
# <br>Current process will be closed.
|
217
|
+
# <br>It suggested to remove from ARGV any command line arguments that point
|
218
|
+
# to run script in background, <br>otherwise an unexpexted result can be received
|
219
|
+
def RunInBackground.start! name, opts = {}
|
220
|
+
# $0 is the executable name.
|
221
|
+
start(File.expand_path($0), ARGV, name, opts)
|
222
|
+
sleep Params['log_param_max_elapsed_time_in_seconds_from_last_flush'] + 0.5
|
223
|
+
exit!
|
224
|
+
end
|
225
|
+
|
226
|
+
# Run in background script that was written as Windows Service, i.e. can receive signals
|
227
|
+
# from Service Control.
|
228
|
+
# <br>The code that is run in this script should be an extension of Win32::Daemon class.
|
229
|
+
# <br>For more information see Win32::Daemon help and examples.
|
230
|
+
# <br>No need to wrap such a script.
|
231
|
+
def RunInBackground.start_win32service binary_path, binary_args, name, opts_specific = {}
|
232
|
+
Log.debug1("executable that should be run as service: #{binary_path}")
|
233
|
+
Log.debug1("arguments: #{binary_args}")
|
234
|
+
Log.debug1("specific options: #{opts_specific}")
|
235
|
+
|
236
|
+
if OS == :WINDOWS
|
237
|
+
start_windows binary_path, binary_args, name, opts_specific
|
238
|
+
else # OS == :LINUX
|
239
|
+
raise NotImplementedError.new("Unsupported method on #{OS}")
|
240
|
+
end
|
241
|
+
0.upto(TIMEOUT) do
|
242
|
+
if exists?(name) && running?(name)
|
243
|
+
puts "windows service #{name} started\n"
|
244
|
+
Log.info("windows service #{name} started")
|
245
|
+
return
|
246
|
+
end
|
247
|
+
sleep 1
|
248
|
+
end
|
249
|
+
# if got here then something gone wrong and daemon/service wasn't started in timely manner
|
250
|
+
delete name if exists? name
|
251
|
+
Log.error("daemon/service #{name} wasn't started in timely manner")
|
252
|
+
sleep(Params['log_param_max_elapsed_time_in_seconds_from_last_flush'] + 0.5)
|
253
|
+
raise "daemon/service #{name} wasn't started in timely manner"
|
254
|
+
end
|
255
|
+
|
256
|
+
# Wrap an arbitrary ruby script withing script that can be run as Windows Service.
|
257
|
+
# ==== Arguments
|
258
|
+
# * <tt>binary_path</tt> - absolute path of the script that should be run in background
|
259
|
+
# * <tt>binary_args</tt> - array (not nil) of scripts command line arguments
|
260
|
+
#
|
261
|
+
# NOTE binary_path and binary_args contents will be change
|
262
|
+
def RunInBackground.wrap_windows binary_path, binary_args
|
263
|
+
raise ArgumentError.new("#{binary_path} doesn't exists") unless File.exists? binary_path
|
264
|
+
|
265
|
+
# service should be run with the same load path as a current application
|
266
|
+
load_path = get_load_path
|
267
|
+
# path that contains spaces must be escaped to be interpreted correctly
|
268
|
+
binary_path = %Q{"#{binary_path}"} if binary_path =~ / /
|
269
|
+
binary_args.insert(0, RUBY_INTERPRETER_PATH, load_path, binary_path.tr('/','\\'))
|
270
|
+
binary_path.replace(WRAPPER_SCRIPT)
|
271
|
+
end
|
272
|
+
|
273
|
+
# NOTE if this method will become public then may be needed to change appropriately wrapper script
|
274
|
+
def RunInBackground.stop name
|
275
|
+
if not exists? name
|
276
|
+
raise ArgumentError.new("Daemon #{name} doesn't exists")
|
277
|
+
elsif OS == :WINDOWS
|
278
|
+
Service.stop(name)
|
279
|
+
else # OS == :LINUX
|
280
|
+
opts = {:app_name => name,
|
281
|
+
:ARGV => ['stop'],
|
282
|
+
:dir_mode => :normal,
|
283
|
+
:dir => Params['pid_dir']
|
284
|
+
}
|
285
|
+
# Current process, that creates daemon, will transfer control to the Daemons library.
|
286
|
+
# So to continue working with current process, daemon creation initiated from separate process.
|
287
|
+
# It looks that it holds only for start command
|
288
|
+
#pid = fork do
|
289
|
+
Daemons.run "", opts
|
290
|
+
#end
|
291
|
+
#Process.waitpid pid
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# Delete service/daemon.
|
296
|
+
# <br>If running then stop and delete.
|
297
|
+
def RunInBackground.delete name
|
298
|
+
if not exists? name
|
299
|
+
raise ArgumentError.new("Daemon #{name} doesn't exists")
|
300
|
+
elsif running? name
|
301
|
+
stop name
|
302
|
+
end
|
303
|
+
if OS == :WINDOWS
|
304
|
+
Service.delete name
|
305
|
+
else # OS == :LINUX
|
306
|
+
opts = {:app_name => name,
|
307
|
+
:ARGV => ['zap'],
|
308
|
+
:dir_mode => :normal,
|
309
|
+
:dir => Params['pid_dir']
|
310
|
+
}
|
311
|
+
# Current process, that creates daemon, will transfer control to the Daemons library.
|
312
|
+
# So to continue working with current process, daemon creation initiated from separate process.
|
313
|
+
# It looks that it holds only for start command
|
314
|
+
#pid = fork do
|
315
|
+
Daemons.run "", opts
|
316
|
+
#end
|
317
|
+
#Process.waitpid pid
|
318
|
+
end
|
319
|
+
0.upto(TIMEOUT) do
|
320
|
+
unless exists? name
|
321
|
+
puts "daemon/service #{name} deleted\n"
|
322
|
+
Log.info("daemon/service #{name} deleted")
|
323
|
+
return
|
324
|
+
end
|
325
|
+
sleep 1
|
326
|
+
end
|
327
|
+
# if got here then something gone wrong and daemon/service wasn't deleted in timely manner
|
328
|
+
Log.error("daemon/service #{name} wasn't deleted in timely manner")
|
329
|
+
sleep(Params['log_param_max_elapsed_time_in_seconds_from_last_flush'] + 0.5)
|
330
|
+
raise "daemon/service #{name} wasn't deleted in timely manner"
|
331
|
+
end
|
332
|
+
|
333
|
+
def RunInBackground.exists? name
|
334
|
+
if name == nil
|
335
|
+
raise ArgumentError.new("service/daemon name argument must be defined")
|
336
|
+
elsif OS == :WINDOWS
|
337
|
+
Service.exists? name
|
338
|
+
else # OS == :LINUX
|
339
|
+
pid_files = Daemons::PidFile.find_files(Params['pid_dir'], name)
|
340
|
+
pid_files != nil && pid_files.size > 0
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def RunInBackground.running? name
|
345
|
+
if not exists? name
|
346
|
+
raise ArgumentError.new("Daemon #{name} doesn't exists")
|
347
|
+
elsif OS == :WINDOWS
|
348
|
+
Service.status(name).current_state == 'running'
|
349
|
+
else # OS == :LINUX
|
350
|
+
Daemons::Pid.running? name
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# Returns absolute standard form of the path.
|
355
|
+
# It includes path separators accepted on this OS
|
356
|
+
def RunInBackground.get_abs_std_path path
|
357
|
+
path = File.expand_path path
|
358
|
+
path = path.tr('/','\\') if OS == :WINDOWS
|
359
|
+
path
|
360
|
+
end
|
361
|
+
|
362
|
+
# Returns load path as it provided in command line.
|
363
|
+
def RunInBackground.get_load_path
|
364
|
+
load_path = Array.new
|
365
|
+
$:.each do |location|
|
366
|
+
load_path << %Q{-I"#{get_abs_std_path(location)}"}
|
367
|
+
end
|
368
|
+
load_path.join ' '
|
369
|
+
end
|
370
|
+
|
371
|
+
# Prepare ARGV so it can be provided as a command line arguments.
|
372
|
+
# Remove bg_command from ARGV to prevent infinite recursion.
|
373
|
+
def RunInBackground.prepare_argv
|
374
|
+
new_argv = Array.new
|
375
|
+
ARGV.each do |arg|
|
376
|
+
# For each argument try splitting to 'name'='value'
|
377
|
+
arg_arr = arg.split '='
|
378
|
+
# If no '=' is argument, just copy paste.
|
379
|
+
if arg_arr.size == 1
|
380
|
+
arg = "\"#{arg}\"" if arg =~ / /
|
381
|
+
new_argv << arg
|
382
|
+
# If it is a 'name'='value' argument add "" so the value can be passed as argument again.
|
383
|
+
elsif arg_arr.size == 2
|
384
|
+
# Skip bg_command flag (remove infinite recursion)!
|
385
|
+
if arg_arr[0] !~ /bg_command/
|
386
|
+
arg_arr[1] = "\"#{arg_arr[1]}\"" if arg_arr[1] =~ / /
|
387
|
+
new_argv << arg_arr.join('=')
|
388
|
+
end
|
389
|
+
else
|
390
|
+
Log.warning("ARGV argument #{arg} wasn't processed")
|
391
|
+
new_argv << arg
|
392
|
+
end
|
393
|
+
end
|
394
|
+
ARGV.clear
|
395
|
+
ARGV.concat new_argv
|
396
|
+
end
|
397
|
+
|
398
|
+
def RunInBackground.run &b
|
399
|
+
case Params['bg_command']
|
400
|
+
when nil
|
401
|
+
yield b
|
402
|
+
when 'start'
|
403
|
+
# To prevent service enter loop cause of background parameter
|
404
|
+
# all options that points to run in background must be disabled
|
405
|
+
# (for more information see documentation for RunInBackground::start!)
|
406
|
+
Params['bg_command'] = nil
|
407
|
+
RunInBackground.prepare_argv
|
408
|
+
|
409
|
+
begin
|
410
|
+
RunInBackground.start! Params['service_name']
|
411
|
+
rescue Exception => e
|
412
|
+
Log.error("Start service command failed: #{e.message}")
|
413
|
+
raise
|
414
|
+
end
|
415
|
+
when 'delete'
|
416
|
+
if RunInBackground.exists? Params['service_name']
|
417
|
+
RunInBackground.delete Params['service_name']
|
418
|
+
else
|
419
|
+
msg = "Can't delete. Service #{Params['service_name']} already deleted"
|
420
|
+
puts msg
|
421
|
+
Log.warning(msg)
|
422
|
+
end
|
423
|
+
else
|
424
|
+
msg = "Unsupported command #{Params['bg_command']}. Supported commands are: start, delete"
|
425
|
+
puts msg
|
426
|
+
Log.error(msg)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
private_class_method :start_linux, :start_windows, :wrap_windows, :stop, :get_abs_std_path,\
|
431
|
+
:get_load_path
|
432
|
+
end
|
433
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'run_in_background'
|
3
|
+
|
4
|
+
module BBFS
|
5
|
+
# TODO break to number of small tests according to functionality
|
6
|
+
# TODO rewrite with Shoulda/RSpec
|
7
|
+
class TestRunInBackground < Test::Unit::TestCase
|
8
|
+
#include BBFS::RunInBackground
|
9
|
+
|
10
|
+
if RUBY_PLATFORM =~ /linux/ or RUBY_PLATFORM =~ /darwin/
|
11
|
+
OS = :LINUX
|
12
|
+
elsif RUBY_PLATFORM =~ /mingw/ or RUBY_PLATFORM =~ /ms/ or RUBY_PLATFORM =~ /win/
|
13
|
+
require 'sys/uname'
|
14
|
+
OS = :WINDOWS
|
15
|
+
else
|
16
|
+
raise "Unsupported platform #{RUBY_PLATFORM}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def setup
|
20
|
+
@good_daemon = "good_daemon_test"
|
21
|
+
@good_daemonize = "good_daemonize_test"
|
22
|
+
@good_win32daemon = "good_win32daemon_test"
|
23
|
+
@bad_daemon = "bad_daemon_test"
|
24
|
+
@binary = File.join(File.dirname(File.expand_path(__FILE__)), 'test_app')
|
25
|
+
@binary.tr!('/','\\') if OS == :WINDOWS
|
26
|
+
File.chmod(0755, @binary) if OS == :LINUX && !File.executable?(@binary)
|
27
|
+
@absent_binary = File.join(File.dirname(File.expand_path(__FILE__)), "test_app_absent")
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_functionality
|
31
|
+
if OS == :WINDOWS && Sys::Uname.sysname =~ /(Windows 7)/
|
32
|
+
skip "This test shouldn't be run on #{$1}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# test start
|
36
|
+
# test application should actually start here
|
37
|
+
assert_nothing_raised{ RunInBackground.start(@binary, ["1000"], @good_daemon) }
|
38
|
+
assert_raise(ArgumentError) { RunInBackground.start(@absent_binary, ["1000"], @bad_daemon) }
|
39
|
+
|
40
|
+
# start arguments have to be defined
|
41
|
+
assert_raise(ArgumentError) { RunInBackground.start(["1000"], @bad_daemon) }
|
42
|
+
assert_raise(ArgumentError) { RunInBackground.start(nil, ["1000"], @bad_daemon) }
|
43
|
+
assert_raise(ArgumentError) { RunInBackground.start(@absent_binary, nil, @bad_daemon) }
|
44
|
+
assert_raise(ArgumentError) { RunInBackground.start(@absent_binary, ["1000"], nil) }
|
45
|
+
|
46
|
+
# test exists?
|
47
|
+
assert_raise(ArgumentError) { RunInBackground.exists? }
|
48
|
+
assert_equal(false, RunInBackground.exists?(@bad_daemon))
|
49
|
+
assert_equal(true, RunInBackground.exists?(@good_daemon))
|
50
|
+
|
51
|
+
# test running?
|
52
|
+
# if stop method will be public need to add test checks actually stopped daemon
|
53
|
+
assert_raise(ArgumentError) { RunInBackground.running? }
|
54
|
+
assert_raise(ArgumentError) { RunInBackground.running?(@bad_daemon) }
|
55
|
+
assert_equal(true, RunInBackground.running?(@good_daemon))
|
56
|
+
|
57
|
+
# test daemonazing (start!)
|
58
|
+
# from the nature of daemonization need to run it from another process that will be daemonized
|
59
|
+
ruby = (OS == :WINDOWS ? RunInBackground::RUBY_INTERPRETER_PATH : "ruby")
|
60
|
+
cmd = "#{ruby} -Ilib #{@binary} 50 #{@good_daemonize}"
|
61
|
+
pid = spawn(cmd)
|
62
|
+
Process.waitpid pid
|
63
|
+
# checking that it indeed was daemonized
|
64
|
+
# e.i. process was killed and code rerun in background
|
65
|
+
# it takes time to the OS to remove process, so using a timeout here
|
66
|
+
0.upto(RunInBackground::TIMEOUT) do
|
67
|
+
begin
|
68
|
+
Process.kill(0, pid)
|
69
|
+
rescue Errno::ESRCH
|
70
|
+
break
|
71
|
+
end
|
72
|
+
sleep 1
|
73
|
+
end
|
74
|
+
assert_raise(Errno::ESRCH) { Process.kill(0, pid) }
|
75
|
+
assert_equal(true, RunInBackground.exists?(@good_daemonize))
|
76
|
+
assert_equal(true, RunInBackground.running?(@good_daemonize))
|
77
|
+
|
78
|
+
# test running win32 specific daemon (start_win32service)
|
79
|
+
# wrapper script will be run
|
80
|
+
if OS == :WINDOWS
|
81
|
+
win32service_arg = [RunInBackground::RUBY_INTERPRETER_PATH, @binary, 1000]
|
82
|
+
assert_nothing_raised{
|
83
|
+
RunInBackground.start_win32service(RunInBackground::WRAPPER_SCRIPT,
|
84
|
+
win32service_arg, @good_win32daemon)
|
85
|
+
}
|
86
|
+
assert_equal(true, RunInBackground.exists?(@good_win32daemon))
|
87
|
+
assert_equal(true, RunInBackground.running?(@good_win32daemon))
|
88
|
+
else
|
89
|
+
assert_raise(NotImplementedError) {
|
90
|
+
RunInBackground.start_win32service(@absent_binary, [], @bad_daemon)
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
# uncomment following lines if there is a suspicion that something gone wrong
|
95
|
+
# inspired by bug caused by coworking of daemon_wrapper an logger
|
96
|
+
#sleep 10
|
97
|
+
#assert_equal(true, RunInBackground.running?(@good_daemon))
|
98
|
+
#assert_equal(true, RunInBackground.running?(@good_daemonize))
|
99
|
+
#assert_equal(true, RunInBackground.running?(@good_win32daemon))
|
100
|
+
|
101
|
+
# test delete
|
102
|
+
# test application should actually stop here
|
103
|
+
assert_raise(ArgumentError) { RunInBackground.delete }
|
104
|
+
assert_raise(ArgumentError) { RunInBackground.delete(@bad_daemon) }
|
105
|
+
assert_nothing_raised { RunInBackground.delete(@good_daemon) }
|
106
|
+
assert_equal(false, RunInBackground.exists?(@good_daemon))
|
107
|
+
|
108
|
+
assert_nothing_raised { RunInBackground.delete(@good_daemonize) }
|
109
|
+
assert_equal(false, RunInBackground.exists?(@good_daemonize))
|
110
|
+
|
111
|
+
# actuall only for Windows platform
|
112
|
+
if RunInBackground.exists?(@good_win32daemon)
|
113
|
+
assert_nothing_raised { RunInBackground.delete(@good_win32daemon) }
|
114
|
+
assert_equal(false, RunInBackground.exists?(@good_win32daemon))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def teardown
|
119
|
+
RunInBackground.delete @good_daemon if RunInBackground.exists? @good_daemon
|
120
|
+
RunInBackground.delete @good_daemonize if RunInBackground.exists? @good_daemonize
|
121
|
+
RunInBackground.delete @good_win32daemon if RunInBackground.exists? @good_win32daemon
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Toy script used for RunInBackground tests.
|
4
|
+
# usage: $0 number_of_iterations [daemon_name_if_daemonized]
|
5
|
+
|
6
|
+
DBG = false # NOTE be sure to disable it in production
|
7
|
+
|
8
|
+
begin
|
9
|
+
begin
|
10
|
+
require 'log'
|
11
|
+
require 'params'
|
12
|
+
rescue LoadError
|
13
|
+
$:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', 'lib'))
|
14
|
+
$:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), '..', '..'))
|
15
|
+
require 'log'
|
16
|
+
require 'params'
|
17
|
+
end
|
18
|
+
|
19
|
+
# On WindowsXP log can be found under:
|
20
|
+
# C:/Documents and Settings/NetworkService/.bbfs/test_app_<pid>.log
|
21
|
+
BBFS::Params['log_file_name'] = File.join(Dir.home, '.bbfs', "#{File.basename(__FILE__)}_#{Process.pid}.log")
|
22
|
+
BBFS::Params['log_write_to_console'] = false
|
23
|
+
BBFS::Params['log_param_max_elapsed_time_in_seconds_from_last_flush'] = 1
|
24
|
+
if DBG
|
25
|
+
BBFS::Params['log_debug_level'] = 1
|
26
|
+
BBFS::Log.init
|
27
|
+
end
|
28
|
+
|
29
|
+
# app should be run in background
|
30
|
+
if ARGV.size == 2
|
31
|
+
begin
|
32
|
+
require 'run_in_background'
|
33
|
+
rescue LoadError
|
34
|
+
$:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', 'lib'))
|
35
|
+
$:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), '..', '..'))
|
36
|
+
require 'run_in_background'
|
37
|
+
end
|
38
|
+
|
39
|
+
BBFS::Log.debug1 "Before run in background: PID #{Process.pid}"
|
40
|
+
# ARGV.pop returns frozen string and thus causes a failure of Service.create
|
41
|
+
# to fix it new string with the same content created.
|
42
|
+
BBFS::RunInBackground.start!(String.new(ARGV.pop))
|
43
|
+
# if got here then error
|
44
|
+
BBFS::Log.error "After run in background: ERROR"
|
45
|
+
end
|
46
|
+
|
47
|
+
max = (ARGV.size > 0 && ARGV[0] != nil && ARGV[0].to_i > 0)? ARGV[0].to_i : 200
|
48
|
+
|
49
|
+
while max > 0
|
50
|
+
BBFS::Log.debug1 "#{max}"
|
51
|
+
sleep 1
|
52
|
+
max -= 1
|
53
|
+
end
|
54
|
+
|
55
|
+
rescue Exception => err
|
56
|
+
BBFS::Log.error "Wrapper error: #{err}"
|
57
|
+
raise
|
58
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rib_fix
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
prerelease:
|
6
|
+
platform: x86-mingw32
|
7
|
+
authors:
|
8
|
+
- Genady Petelko
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-14 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: log
|
16
|
+
requirement: &5726688 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *5726688
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: params
|
27
|
+
requirement: &5725812 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *5725812
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: win32-service
|
38
|
+
requirement: &5724432 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *5724432
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: sys-uname
|
49
|
+
requirement: &5723868 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *5723868
|
58
|
+
description: ! 'This library provides a basic cross-platform functionality to runarbitrary
|
59
|
+
ruby scripts in background and control them.Supported platforms: Windows, Linux,
|
60
|
+
Mac.'
|
61
|
+
email: nukegluk@gmail.com
|
62
|
+
executables: []
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- lib/run_in_background.rb
|
67
|
+
- lib/run_in_background/version.rb
|
68
|
+
- test/run_in_background/run_in_background_test.rb
|
69
|
+
- test/run_in_background/test_app
|
70
|
+
homepage: http://github.com/kolmanv/bbfs
|
71
|
+
licenses: []
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
requirements: []
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 1.8.8
|
91
|
+
signing_key:
|
92
|
+
specification_version: 3
|
93
|
+
summary: Cross-platform library for daemons management.
|
94
|
+
test_files:
|
95
|
+
- test/run_in_background/run_in_background_test.rb
|
96
|
+
- test/run_in_background/test_app
|