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.
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/runnable.rb', line 360 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.
123 124 125 |
# File 'lib/runnable.rb', line 123 def group @group end |
#log_path ⇒ Object
Process log output
133 134 135 |
# File 'lib/runnable.rb', line 133 def log_path @log_path end |
#options ⇒ Object
Process options
131 132 133 |
# File 'lib/runnable.rb', line 131 def end |
#output ⇒ Object
Process output
128 129 130 |
# File 'lib/runnable.rb', line 128 def output @output end |
#owner ⇒ Object (readonly)
Process owner.
121 122 123 |
# File 'lib/runnable.rb', line 121 def owner @owner end |
#pid ⇒ Object (readonly)
Process id.
119 120 121 |
# File 'lib/runnable.rb', line 119 def pid @pid end |
#pwd ⇒ Object (readonly)
Directory where process was called from.
125 126 127 |
# File 'lib/runnable.rb', line 125 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.
379 380 381 |
# File 'lib/runnable.rb', line 379 def self.processes @@processes end |
Instance Method Details
#bandwidth(iface, sample_lapse = 0.1) ⇒ Number
Estimated bandwidth in kb/s.
330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/runnable.rb', line 330 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
145 146 147 |
# File 'lib/runnable.rb', line 145 def command self.class.to_s.split( "::" ).last.downcase end |
#command_style ⇒ Symbol
Parameter style used for the command.
139 140 141 |
# File 'lib/runnable.rb', line 139 def command_style :gnu end |
#cpu ⇒ Number
Estimated CPU usage in %.
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 320 321 322 323 |
# File 'lib/runnable.rb', line 293 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
275 276 277 |
# File 'lib/runnable.rb', line 275 def input=( opt ) @command_input = opt end |
#join ⇒ nil
Wait for command thread to finish it execution.
250 251 252 253 |
# File 'lib/runnable.rb', line 250 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.
240 241 242 243 244 245 246 |
# File 'lib/runnable.rb', line 240 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.
287 288 289 |
# File 'lib/runnable.rb', line 287 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.
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 |
# File 'lib/runnable.rb', line 154 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.
257 258 259 |
# File 'lib/runnable.rb', line 257 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.
387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
# File 'lib/runnable.rb', line 387 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
269 270 271 |
# File 'lib/runnable.rb', line 269 def std_err @std_err ||= "" end |
#std_out ⇒ String
Standar output of command
263 264 265 |
# File 'lib/runnable.rb', line 263 def std_out @std_out ||= "" end |
#stop ⇒ nil
Raise an exception if process is not running.
Stop the command.
229 230 231 232 233 234 235 |
# File 'lib/runnable.rb', line 229 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 |