Module: 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)
Defined Under Namespace
Modules: ClassMethods
Accessors for the module class variables collapse
- HERTZ =
Constant to calculate cpu usage.
100
Accessors for the module class variables collapse
-
#group ⇒ Object
readonly
Process group.
-
#log_path ⇒ Object
Process log output.
-
#options ⇒ Object
Process options.
-
#output ⇒ Object
Process output.
-
#owner ⇒ Object
readonly
Process owner.
-
#pid ⇒ Object
readonly
Process id.
-
#pwd ⇒ Object
readonly
Directory where process was called from.
Accessors for the module class variables collapse
-
.processes ⇒ Hash
List of runnable instances running on the system.
-
#bandwidth(iface, sample_lapse = 0.1) ⇒ Number
Estimated bandwidth in kb/s.
-
#command ⇒ String
Default command to be executed.
-
#command_style ⇒ Symbol
Parameter style used for the command.
-
#cpu ⇒ Number
Estimated CPU usage in %.
-
#input=(opt) ⇒ Object
Sets the command input to be passed to the command execution.
-
#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(name = nil, opts = nil, log_path = nil) ⇒ 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.
Class Method Summary collapse
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.
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 |
# File 'lib/runnable.rb', line 364 def method_missing( method, *params, &block ) @command_line_interface ||= Object.const_get( command_style.to_s.capitalize.to_sym ).new 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.
127 128 129 |
# File 'lib/runnable.rb', line 127 def group @group end |
#log_path ⇒ Object
Process log output
137 138 139 |
# File 'lib/runnable.rb', line 137 def log_path @log_path end |
#options ⇒ Object
Process options
135 136 137 |
# File 'lib/runnable.rb', line 135 def @options end |
#output ⇒ Object
Process output
132 133 134 |
# File 'lib/runnable.rb', line 132 def output @output end |
#owner ⇒ Object (readonly)
Process owner.
125 126 127 |
# File 'lib/runnable.rb', line 125 def owner @owner end |
#pid ⇒ Object (readonly)
Process id.
123 124 125 |
# File 'lib/runnable.rb', line 123 def pid @pid end |
#pwd ⇒ Object (readonly)
Directory where process was called from.
129 130 131 |
# File 'lib/runnable.rb', line 129 def pwd @pwd end |
Class Method Details
.included(klass) ⇒ Object
38 39 40 |
# File 'lib/runnable.rb', line 38 def self.included(klass) klass.extend ClassMethods end |
.processes ⇒ Hash
List of runnable instances running on the system.
383 384 385 |
# File 'lib/runnable.rb', line 383 def self.processes @@processes end |
Instance Method Details
#bandwidth(iface, sample_lapse = 0.1) ⇒ Number
Estimated bandwidth in kb/s.
334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/runnable.rb', line 334 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 ⇒ String
Default command to be executed
149 150 151 |
# File 'lib/runnable.rb', line 149 def command self.class.to_s.split( "::" ).last.downcase end |
#command_style ⇒ Symbol
Parameter style used for the command.
143 144 145 |
# File 'lib/runnable.rb', line 143 def command_style :gnu end |
#cpu ⇒ Number
Estimated CPU usage in %.
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 |
# File 'lib/runnable.rb', line 297 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 |
#input=(opt) ⇒ Object
Sets the command input to be passed to the command execution
279 280 281 |
# File 'lib/runnable.rb', line 279 def input=( opt ) @command_input = opt end |
#join ⇒ nil
Wait for command thread to finish it execution.
254 255 256 257 |
# File 'lib/runnable.rb', line 254 def join @run_thread.join if @run_thread.alive? @output unless @output.empty? end |
#kill ⇒ nil
Raise an exeption if process is not running.
Kill the comand.
244 245 246 247 248 249 250 |
# File 'lib/runnable.rb', line 244 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.
291 292 293 |
# File 'lib/runnable.rb', line 291 def mem File.open( "/proc/#{@pid}/status" ).read.split( "\n" )[11].split( " " )[1].to_i end |
#run(name = nil, opts = nil, log_path = nil) ⇒ nil
Start the execution of the command.
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 |
# File 'lib/runnable.rb', line 158 def run(name = nil, opts = nil, log_path = nil) return false if @pid # Create a new mutex @pid_mutex = Mutex.new # Log path should be an instance variable to avoid a mess @log_path = log_path || @log_path # 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 command_argument = opts ? opts.split(" ") : compose_command @pid = Process.spawn( command.to_s, *command_argument, { :out => out_wr, :err => err_wr } ) # Include instance in class variable self.class.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 stream_info = { :out => [out_wr, out_rd], :err => [err_wr, err_rd] } if name cmd_info = self.class.commands[name] stream_processors = { :outputs => cmd_info[:outputs], :exceptions => cmd_info[:exceptions] } end output_threads = process_streams( stream_info, stream_processors ) # Create a new thread to avoid blocked processes @run_thread = threaded_process(@pid, output_threads) # 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 Errno::ENOENT # 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 rescue #Errno::EACCESS @pwd = Dir.getwd end end |
#running? ⇒ Bool
Check if prcess is running on the system.
261 262 263 |
# File 'lib/runnable.rb', line 261 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.
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/runnable.rb', line 391 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
273 274 275 |
# File 'lib/runnable.rb', line 273 def std_err @std_err ||= "" end |
#std_out ⇒ String
Standar output of command
267 268 269 |
# File 'lib/runnable.rb', line 267 def std_out @std_out ||= "" end |
#stop ⇒ nil
Raise an exception if process is not running.
Stop the command.
233 234 235 236 237 238 239 |
# File 'lib/runnable.rb', line 233 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 |