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