Module: BBFS::RunInBackground
- Includes:
- Win32
- Defined in:
- lib/run_in_background.rb,
lib/run_in_background/version.rb
Overview
This library provides a basic cross-platform functionality to run arbitrary ruby scripts in background and control them.
Supported platforms: Windows, Linux, Mac
NOTE UAC (User Account Control) should be disabled to use library on Windows 7:
* Click on the Windows Icon
* Click on the Control Panel
* Type in UAC in the search box (up-right corner of your window)
* Click on "Change User Account Control settings"
* Drag the slider down to either "Notify me when programs try to make changes to my computer"
or to disable it completely
* Reboot your computer when you're ready
General Limitations:
-
Only ruby scripts can be run in background.
-
No multiple instances with the same name.
Notes:
-
Linux/Mac specific methods have _linux suffix
-
Windows specific methods have _windows suffix
-
While enhancing windows code, take care that paths are in windows format,
e.i. with “\” file separator while ruby by default uses a “/” -
Additional functionality such as restart, reload, etc. will be added on demand
-
Remains support to provide platform specific options for start.
For more information regarding such options see documentation for win32-sevice (Windows), daemons (Linux/Mac)
Linux Notes:
-
pid_dir
parameter contains absolute path to directory where pid files will be stored.
If directory doesn’t exists then it will be created.
User should have a read/write permissions to this location.
Default location:$HOME/.bbfs/pids
-
User should check that default pid directory is free from pid files of “killed” daemons.
It may happen, for example, when system finished in abnormal way then pid files were not deleted by daemons library.
In such case incorrect results can be received, for example forexists?
method
One of the suggested methods can be before starting a daemon to check withexists?
method whether daemon already exists and withrunning?
method does it running.
Constant Summary collapse
- TIMEOUT =
Maximal time in seconds to wait until OS will finish a requested operation, e.g. daemon start/delete.
20
- OS =
:WINDOWS
- RUBY_INTERPRETER_PATH =
Get ruby interpreter path. Need it to run ruby binary.
TODO check whether this code works with Windows Ruby Version Management (e.g. Pik) File.join(Config::CONFIG["bindir"], Config::CONFIG["RUBY_INSTALL_NAME"] + Config::CONFIG["EXEEXT"]).tr!('/','\\')
- WRAPPER_SCRIPT =
Wrapper script, that can receive commands from Windows Service Control and run user script,
provided as it’s argument File.(wrapper).tr!('/','\\')
- VERSION =
"0.1.2"
Class Method Summary collapse
-
.delete(name) ⇒ Object
Delete service/daemon.
- .exists?(name) ⇒ Boolean
-
.prepare_argv ⇒ Object
Prepare ARGV so it can be provided as a command line arguments.
- .run(&b) ⇒ Object
- .running?(name) ⇒ Boolean
-
.start(binary_path, binary_args, name, opts_specific = {}) ⇒ Object
Start a service/daemon.
-
.start!(name, opts = {}) ⇒ Object
Rerun current script in background.
-
.start_win32service(binary_path, binary_args, name, opts_specific = {}) ⇒ Object
Run in background script that was written as Windows Service, i.e.
Class Method Details
.delete(name) ⇒ Object
Delete service/daemon.
If running then stop and delete.
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/run_in_background.rb', line 297 def RunInBackground.delete name if not exists? name raise ArgumentError.new("Daemon #{name} doesn't exists") elsif running? name stop name end if OS == :WINDOWS Service.delete name else # OS == :LINUX opts = {:app_name => name, :ARGV => ['zap'], :dir_mode => :normal, :dir => Params['pid_dir'] } # Current process, that creates daemon, will transfer control to the Daemons library. # So to continue working with current process, daemon creation initiated from separate process. # It looks that it holds only for start command #pid = fork do Daemons.run "", opts #end #Process.waitpid pid end 0.upto(TIMEOUT) do unless exists? name puts "daemon/service #{name} deleted\n" Log.info("daemon/service #{name} deleted") return end sleep 1 end # if got here then something gone wrong and daemon/service wasn't deleted in timely manner Log.error("daemon/service #{name} wasn't deleted in timely manner") sleep(Params['log_param_max_elapsed_time_in_seconds_from_last_flush'] + 0.5) raise "daemon/service #{name} wasn't deleted in timely manner" end |
.exists?(name) ⇒ Boolean
333 334 335 336 337 338 339 340 341 342 |
# File 'lib/run_in_background.rb', line 333 def RunInBackground.exists? name if name == nil raise ArgumentError.new("service/daemon name argument must be defined") elsif OS == :WINDOWS Service.exists? name else # OS == :LINUX pid_files = Daemons::PidFile.find_files(Params['pid_dir'], name) pid_files != nil && pid_files.size > 0 end end |
.prepare_argv ⇒ Object
Prepare ARGV so it can be provided as a command line arguments. Remove bg_command from ARGV to prevent infinite recursion.
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
# File 'lib/run_in_background.rb', line 373 def RunInBackground.prepare_argv new_argv = Array.new ARGV.each do |arg| # For each argument try splitting to 'name'='value' arg_arr = arg.split '=' # If no '=' is argument, just copy paste. if arg_arr.size == 1 arg = "\"#{arg}\"" if arg =~ / / new_argv << arg # If it is a 'name'='value' argument add "" so the value can be passed as argument again. elsif arg_arr.size == 2 # Skip bg_command flag (remove infinite recursion)! if arg_arr[0] !~ /bg_command/ arg_arr[1] = "\"#{arg_arr[1]}\"" if arg_arr[1] =~ / / new_argv << arg_arr.join('=') end else Log.warning("ARGV argument #{arg} wasn't processed") new_argv << arg end end ARGV.clear ARGV.concat new_argv end |
.run(&b) ⇒ Object
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
# File 'lib/run_in_background.rb', line 398 def RunInBackground.run &b case Params['bg_command'] when nil yield b when 'start' # To prevent service enter loop cause of background parameter # all options that points to run in background must be disabled # (for more information see documentation for RunInBackground::start!) Params['bg_command'] = nil RunInBackground.prepare_argv begin RunInBackground.start! Params['service_name'] rescue Exception => e Log.error("Start service command failed: #{e.}") raise end when 'delete' if RunInBackground.exists? Params['service_name'] RunInBackground.delete Params['service_name'] else msg = "Can't delete. Service #{Params['service_name']} already deleted" puts msg Log.warning(msg) end else msg = "Unsupported command #{Params['bg_command']}. Supported commands are: start, delete" puts msg Log.error(msg) end end |
.running?(name) ⇒ Boolean
344 345 346 347 348 349 350 351 352 |
# File 'lib/run_in_background.rb', line 344 def RunInBackground.running? name if not exists? name raise ArgumentError.new("Daemon #{name} doesn't exists") elsif OS == :WINDOWS Service.status(name).current_state == 'running' else # OS == :LINUX Daemons::Pid.running? name end end |
.start(binary_path, binary_args, name, opts_specific = {}) ⇒ Object
Start a service/daemon.
It important to delete it after usage.
Arguments
-
binary_path
- absolute path to the script that should be run in backgroundNOTE for Linux script should be executable and with UNIX end-of-lines (LF).
-
binary_args
- Array (not nil) of script’s command line arguments -
name
- service/daemon name.NOTE should be unique
-
opts_specific
- Hash of platform specific options (only for more specific usage)For more information regarding such options see documentation for win32-sevice (Windows), daemons (Linux/Mac)
Example (LINUX)
RunInBackground.start "/home/user/test_app", [], "daemon_test", {:monitor => true}
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/run_in_background.rb', line 120 def RunInBackground.start binary_path, binary_args, name, opts_specific = {} Log.debug1("executable that should be run as daemon/service: #{binary_path}") Log.debug1("arguments: #{binary_args}") Log.debug1("specific options: #{opts_specific}") if binary_path == nil or binary_args == nil or name == nil Log.error("binary path, binary args, name arguments must be defined") raise ArgumentError.new("binary path, binary args, name arguments must be defined") end if OS == :WINDOWS new_binary_path = String.new(binary_path) new_binary_args = Array.new(binary_args) wrap_windows new_binary_path, new_binary_args start_windows new_binary_path, new_binary_args, name, opts_specific else # OS == LINUX start_linux binary_path, binary_args, name, opts_specific end 0.upto(TIMEOUT) do if exists?(name) && running?(name) puts "daemon/service #{name} started\n" Log.info("daemon/service #{name} started") return end sleep 1 end # if got here then something gone wrong and daemon/service wasn't started in timely manner delete name if exists? name Log.error("daemon/service #{name} wasn't started in timely manner") sleep(Params['log_param_max_elapsed_time_in_seconds_from_last_flush'] + 0.5) raise "daemon/service #{name} wasn't started in timely manner" end |
.start!(name, opts = {}) ⇒ Object
Rerun current script in background.
Current process will be closed.
It suggested to remove from ARGV any command line arguments that point to run script in background,
otherwise an unexpexted result can be received
219 220 221 222 223 224 |
# File 'lib/run_in_background.rb', line 219 def RunInBackground.start! name, opts = {} # $0 is the executable name. start(File.($0), ARGV, name, opts) sleep Params['log_param_max_elapsed_time_in_seconds_from_last_flush'] + 0.5 exit! end |
.start_win32service(binary_path, binary_args, name, opts_specific = {}) ⇒ Object
Run in background script that was written as Windows Service, i.e. can receive signals from Service Control.
The code that is run in this script should be an extension of Win32::Daemon class.
For more information see Win32::Daemon help and examples.
No need to wrap such a script.
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/run_in_background.rb', line 231 def RunInBackground.start_win32service binary_path, binary_args, name, opts_specific = {} Log.debug1("executable that should be run as service: #{binary_path}") Log.debug1("arguments: #{binary_args}") Log.debug1("specific options: #{opts_specific}") if OS == :WINDOWS start_windows binary_path, binary_args, name, opts_specific else # OS == :LINUX raise NotImplementedError.new("Unsupported method on #{OS}") end 0.upto(TIMEOUT) do if exists?(name) && running?(name) puts "windows service #{name} started\n" Log.info("windows service #{name} started") return end sleep 1 end # if got here then something gone wrong and daemon/service wasn't started in timely manner delete name if exists? name Log.error("daemon/service #{name} wasn't started in timely manner") sleep(Params['log_param_max_elapsed_time_in_seconds_from_last_flush'] + 0.5) raise "daemon/service #{name} wasn't started in timely manner" end |