run_in_background 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ module BBFS
2
+ module RunInBackground
3
+ VERSION = "0.0.8"
4
+ end
5
+ end
@@ -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,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: run_in_background
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.8
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Genady Petelko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: log
16
+ requirement: !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: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: params
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: daemons
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: win32-service
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: sys-uname
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: ! 'This library provides a basic cross-platform functionality to runarbitrary
95
+ ruby scripts in background and control them.Supported platforms: Windows, Linux,
96
+ Mac.'
97
+ email: nukegluk@gmail.com
98
+ executables: []
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - lib/run_in_background.rb
103
+ - lib/run_in_background/version.rb
104
+ - test/run_in_background/run_in_background_test.rb
105
+ - test/run_in_background/test_app
106
+ homepage: http://github.com/kolmanv/bbfs
107
+ licenses: []
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ! '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 1.8.23
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: Cross-platform library for daemons management.
130
+ test_files:
131
+ - test/run_in_background/run_in_background_test.rb
132
+ - test/run_in_background/test_app