Class: Runnable
- Inherits:
-
Object
- Object
- Runnable
- Defined in:
- lib/runnable.rb
Overview
Convert a executable command in a Ruby-like class you are able to start, define params and send signals (like kill, or stop)
Constant Summary collapse
- HERTZ =
Constant to calculate cpu usage.
100
- @@processes =
List of runnable instances running on the system order by pid.
Hash.new
Instance Attribute Summary collapse
-
#group ⇒ Object
readonly
Process group.
-
#input ⇒ Object
Input file.
-
#output ⇒ Object
Set the output file.
-
#owner ⇒ Object
readonly
Process owner.
-
#pid ⇒ Object
readonly
Process id.
-
#pwd ⇒ Object
readonly
Directory where process was called from.
Class Method Summary collapse
-
.command_style(style) ⇒ nil
Define the parameter style to be used.
-
.processes ⇒ Hash
List of runnable instances running on the system.
Instance Method Summary collapse
-
#bandwidth(iface, sample_lapse = 0.1) ⇒ Number
Estimated bandwidth in kb/s.
-
#command_style ⇒ Symbol
Parameter style used for the command.
-
#cpu ⇒ Number
Estimated CPU usage in %.
-
#exceptions ⇒ Hash
abstract
Returns a hash of regular expressions and exceptions associated to them.
-
#failed(exceptions) ⇒ nil
abstract
Method called when command executions fail.
-
#finish ⇒ nil
abstract
Method called when command ends with no erros.
-
#initialize(option_hash = {}) ⇒ Runnable
constructor
Create a new instance of a runnable command.
-
#join ⇒ nil
Wait for command thread to finish it execution.
-
#kill ⇒ nil
Kill the comand.
-
#mem ⇒ Number
Calculate the estimated memory usage in Kb.
-
#method_missing(method, *params, &block) ⇒ nil
Convert undefined methods (ruby-like syntax) into parameters to be parsed at the execution time.
-
#run ⇒ nil
Start the execution of the command.
-
#running? ⇒ Bool
Check if prcess is running on the system.
-
#send_signal(signal) ⇒ Object
Send the desired signal to the command.
-
#std_err ⇒ String
Standar error output of the command.
-
#std_out ⇒ String
Standar output of command.
-
#stop ⇒ nil
Stop the command.
Constructor Details
#initialize(option_hash = {}) ⇒ Runnable
Create a new instance of a runnable command.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/runnable.rb', line 77 def initialize( option_hash = {} ) # keys :delete_log # :command_options # :log_path # If we have the command class in a namespace, we need to remove # the namespace name @command = self.class.to_s.split( "::" ).last.downcase # Set the default command option # Empty by default option_hash[:command_options] ||= "" @options = option_hash[:command_options] # Set the log path # Default path is "/var/log/runnable" option_hash[:log_path] ||= "/var/log/runnable/" @log_path = option_hash[:log_path] # Set the delete_log option # true by default if option_hash[:delete_log] == nil @delete_log = true else @delete_log = option_hash[:delete_log] end # Store input options @input = String.new # Store output options @output = String.new # Standar Outputs @std_output = { :out => "", :err => "" } # @todo: checks that command is in the PATH # ... # we dont set the pid, because we dont know until run @pid = nil @excep_array = [] # Metaprogramming part # Create a new instance of the parser class @command_line_interface = Object.const_get( command_style.to_s.capitalize.to_sym ).new # End Metaprogramming part #End of initialize instance variables create_log_directory end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *params, &block) ⇒ nil
Convert undefined methods (ruby-like syntax) into parameters to be parsed at the execution time. This only convert methods with zero or one parameters. A hash can be passed and each key will define a new method and method name will be ignored.
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
# File 'lib/runnable.rb', line 356 def method_missing( method, *params, &block ) if params.length > 1 super( method, params, block ) else if params[0].class == Hash # If only one param is passed and its a Hash # we need to expand the hash and call each key as a method with value as params # @see parse_hash for more information parse_hash( params[0] ) else @command_line_interface.add_param( method.to_s, params != nil ? params.join(",") : nil ) end end end |
Instance Attribute Details
#group ⇒ Object (readonly)
Process group.
40 41 42 |
# File 'lib/runnable.rb', line 40 def group @group end |
#input ⇒ Object
Input file
45 46 47 |
# File 'lib/runnable.rb', line 45 def input @input end |
#output ⇒ Object
Set the output file
48 49 50 |
# File 'lib/runnable.rb', line 48 def output @output end |
#owner ⇒ Object (readonly)
Process owner.
38 39 40 |
# File 'lib/runnable.rb', line 38 def owner @owner end |
#pid ⇒ Object (readonly)
Process id.
36 37 38 |
# File 'lib/runnable.rb', line 36 def pid @pid end |
#pwd ⇒ Object (readonly)
Directory where process was called from.
42 43 44 |
# File 'lib/runnable.rb', line 42 def pwd @pwd end |
Class Method Details
.command_style(style) ⇒ nil
Define the parameter style to be used.
54 55 56 57 58 |
# File 'lib/runnable.rb', line 54 def self.command_style( style ) define_method( :command_style ) do style end end |
.processes ⇒ Hash
List of runnable instances running on the system.
374 375 376 |
# File 'lib/runnable.rb', line 374 def self.processes @@processes end |
Instance Method Details
#bandwidth(iface, sample_lapse = 0.1) ⇒ Number
Estimated bandwidth in kb/s.
326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/runnable.rb', line 326 def bandwidth( iface, sample_lapse = 0.1 ) file = "/proc/#{@pid}/net/dev" File.open( file ).read =~ /#{iface}:\s+(\d+)\s+/ init = $1.to_i sleep sample_lapse File.open( file ).read =~ /#{iface}:\s+(\d+)\s+/ finish = $1.to_i (finish - init)*(1/sample_lapse)/1024 end |
#command_style ⇒ Symbol
Parameter style used for the command.
62 63 64 |
# File 'lib/runnable.rb', line 62 def command_style :gnu end |
#cpu ⇒ Number
Estimated CPU usage in %.
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/runnable.rb', line 288 def cpu # Open the proc stat file begin stat = File.open( "/proc/#{@pid}/stat" ).read.split # Get time variables # utime = User Time # stime = System Time # start_time = Time passed from process starting utime = stat[13].to_f stime = stat[14].to_f start_time = stat[21].to_f # uptime = Time passed from system starting uptime = File.open( "/proc/uptime" ).read.split[0].to_f # Total time that the process has been executed total_time = utime + stime # in jiffies # Seconds passed between start the process and now seconds = uptime - ( start_time / HERTZ ) # Percentage of used CPU ( ESTIMATED ) (total_time / seconds.to_f) rescue IOError # Fails to open file 0 rescue ZeroDivisionError # Seconds is Zero! 0 end end |
#exceptions ⇒ Hash
This method should be overwritten in child classes.
Returns a hash of regular expressions and exceptions associated to them. Command output is match against those regular expressions, if it does match an appropiate exception is included in the return value of execution.
392 393 394 |
# File 'lib/runnable.rb', line 392 def exceptions {} end |
#failed(exceptions) ⇒ nil
Method called when command executions fail. This method is a hook so it should be overwritten in child classes.
408 409 |
# File 'lib/runnable.rb', line 408 def failed( exceptions ) end |
#finish ⇒ nil
Method called when command ends with no erros. This method is a hook so it should be overwritten in child classes.
400 401 |
# File 'lib/runnable.rb', line 400 def finish end |
#join ⇒ nil
Wait for command thread to finish it execution.
258 259 260 |
# File 'lib/runnable.rb', line 258 def join @run_thread.join if @run_thread.alive? end |
#kill ⇒ nil
Raise an exeption if process is not running.
Kill the comand.
248 249 250 251 252 253 254 |
# File 'lib/runnable.rb', line 248 def kill send_signal( :kill ) # In order to maintain consistency of @@processes # we must assure that @run_thread finish correctly join end |
#mem ⇒ Number
Calculate the estimated memory usage in Kb.
282 283 284 |
# File 'lib/runnable.rb', line 282 def mem File.open( "/proc/#{@pid}/status" ).read.split( "\n" )[11].split( " " )[1].to_i end |
#run ⇒ nil
Start the execution of the command.
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/runnable.rb', line 136 def run # Create a new mutex @pid_mutex = Mutex.new # Create pipes to redirect Standar I/O out_rd, out_wr = IO.pipe # Redirect Error I/O err_rd, err_wr = IO.pipe # Reset exceptions array to not store exceptions for # past executions @excep_array = [] # Set up the command line command = [] #command << @command command << @input.to_s command << @options.to_s command << @command_line_interface.parse command << @output.to_s #command = command.join( " " ) command.flatten! command = command.select do |value| !value.to_s.strip.empty? end =begin # Debugging purpose puts "I: #{@input}" puts "OP: #{@options}" puts "CLI: #{@command_line_interface.parse}" puts "O: #{@output}" puts "C: #{command}" =end #@pid = Process.spawn( command, { :out => out_wr, :err => err_wr } ) @pid = Process.spawn( @command, *command, { :out => out_wr, :err => err_wr } ) # Include instance in class variable @@processes[@pid] = self # Prepare the process info file to be read file_status = File.open( "/proc/#{@pid}/status" ).read.split( "\n" ) # Owner: Read the owner of the process from /proc/@pid/status @owner = file_status[6].split( " " )[1] # Group: Read the Group owner from /proc/@pid/status @group = file_status[7].split( " " )[1] # Set @output_thread with new threads # wich execute the input/ouput loop create_logs(:out => [out_wr, out_rd], :err => [err_wr, err_rd]) # Create a new thread to avoid blocked processes @run_thread = Thread.new do # Wait to get the pid process even if it has finished Process.wait( @pid, Process::WUNTRACED ) # Wait each I/O thread @output_threads.each { |thread| thread.join } # Delete log if its necesary delete_log # Get the exit code from command exit_status = $?.exitstatus # In case of error add an Exception to the @excep_array @excep_array << SystemCallError.new( exit_status ) if exit_status != 0 # Call methods according to the exit code if @excep_array.empty? finish else failed( @excep_array ) end # This instance is finished and we remove it @@processes.delete( @pid ) end # Satuts Variables # PWD: Current Working Directory get by /proc/@pid/cwd # @rescue If a fast process is runned there isn't time to get # the correct PWD. If the readlink fails, we retry, if the process still alive # until the process finish. begin @pwd = File.readlink( "/proc/#{@pid}/cwd" ) rescue # If cwd is not available rerun @run_thread if @run_thread.alive? #If it is alive, we retry to get cwd @run_thread.run retry else #If process has terminated, we set pwd to current working directory of ruby @pwd = Dir.getwd end end end |
#running? ⇒ Bool
Check if prcess is running on the system.
264 265 266 |
# File 'lib/runnable.rb', line 264 def running? Dir.exists?( "/proc/#{@pid}") end |
#send_signal(signal) ⇒ Object
raise ESRCH if pid is not in system or EPERM if pid is not from user.
Send the desired signal to the command.
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/runnable.rb', line 415 def send_signal( signal ) if signal == :stop signal = :SIGINT elsif signal == :kill signal = :SIGKILL end `ps -ef`.each_line do |line| line = line.split pid = line[1] ppid = line[2] if ppid.to_i == @pid Process.kill( signal, pid.to_i ) end end begin Process.kill( signal, @pid ) rescue Errno::ESRCH # As we kill child processes, main process may have exit already end end |
#std_err ⇒ String
Standar error output of the command
276 277 278 |
# File 'lib/runnable.rb', line 276 def std_err @std_output[:err] end |
#std_out ⇒ String
Standar output of command
270 271 272 |
# File 'lib/runnable.rb', line 270 def std_out @std_output[:out] end |
#stop ⇒ nil
Raise an exception if process is not running.
Stop the command.
237 238 239 240 241 242 243 |
# File 'lib/runnable.rb', line 237 def stop send_signal( :stop ) # In order to maintain consistency of @@processes # we must assure that @run_thread finish correctly @run_thread.run if @run_thread.alive? end |