Class: WifiWand::CommandExecutor

Inherits:
Object
  • Object
show all
Defined in:
lib/wifi_wand/services/command_executor.rb

Defined Under Namespace

Classes: OsCommandError, OsCommandResult

Constant Summary collapse

COMMAND_KILL_WAIT_SECS =
1.0
READER_THREAD_JOIN_WAIT_SECS =
0.1
TRY_OS_COMMAND_RETRY_SLEEP_SECS =
0.001

Instance Method Summary collapse

Constructor Details

#initialize(verbose: false, utc: false, output: $stdout, runtime_config: nil) ⇒ CommandExecutor

Returns a new instance of CommandExecutor.



14
15
16
17
18
19
20
# File 'lib/wifi_wand/services/command_executor.rb', line 14

def initialize(verbose: false, utc: false, output: $stdout, runtime_config: nil)
  @runtime_config = runtime_config || RuntimeConfig.new(
    verbose:    verbose,
    utc:        utc,
    out_stream: output
  )
end

Instance Method Details

#command_available?(command) ⇒ Boolean

Checks if a command is available in the system PATH.

Parameters:

  • command (String)

    the command name to search for (e.g., 'git', 'nmcli')

Returns:

  • (Boolean)

    true if the command exists and is executable



207
208
209
210
211
212
# File 'lib/wifi_wand/services/command_executor.rb', line 207

def command_available?(command)
  ENV.fetch('PATH', '').split(File::PATH_SEPARATOR).any? do |path|
    executable = File.join(path, command)
    File.executable?(executable) && !File.directory?(executable)
  end
end

#run_command_using_args(command, raise_on_error: true, timeout_in_secs: nil, log_stdout: true, binary_stdout: false) ⇒ OsCommandResult

Executes a command using an argument array with no shell parsing.

Parameters:

  • command (Array)

    Command array of arguments

  • raise_on_error (Boolean) (defaults to: true)

    Whether to raise on non-zero exit

  • timeout_in_secs (Numeric, nil) (defaults to: nil)

    Optional command timeout in seconds

  • log_stdout (Boolean) (defaults to: true)

    Whether verbose mode should print captured stdout

  • binary_stdout (Boolean) (defaults to: false)

    Whether stdout should be captured as binary bytes

Returns:



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/wifi_wand/services/command_executor.rb', line 29

def run_command_using_args(command, raise_on_error: true, timeout_in_secs: nil, log_stdout: true,
  binary_stdout: false)
  unless command.is_a?(Array)
    raise ArgumentError,
      "run_command_using_args requires an Array; got #{command.class}"
  end

  command_array = command.map { |arg| arg.nil? ? '' : arg.to_s }
  command_display = command_array.join(' ')
  execute_command(command_array, command_display, raise_on_error: raise_on_error,
    timeout_in_secs: timeout_in_secs, log_stdout: log_stdout, binary_stdout: binary_stdout)
end

#run_command_using_shell(command, raise_on_error: true, timeout_in_secs: nil) ⇒ Object

Executes a command string through the shell when shell semantics are intended.



43
44
45
46
47
48
49
50
51
# File 'lib/wifi_wand/services/command_executor.rb', line 43

def run_command_using_shell(command, raise_on_error: true, timeout_in_secs: nil)
  unless command.is_a?(String)
    raise ArgumentError,
      "run_command_using_shell requires a String; got #{command.class}"
  end

  execute_command(['sh', '-c', command], command, raise_on_error: raise_on_error,
    timeout_in_secs: timeout_in_secs, log_stdout: true, binary_stdout: false)
end

#try_os_command_until(command, stop_condition, max_tries = 100) ⇒ Object

Tries an OS command until the stop condition is true. Failed attempts are throttled to avoid tight process-spawn loops for fast commands.

Returns:

  • the stdout produced by the command, or nil if max_tries was reached



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/wifi_wand/services/command_executor.rb', line 58

def try_os_command_until(command, stop_condition, max_tries = 100)
  report_attempt_count = ->(attempt_count) do
    err_output.puts "Command was executed #{attempt_count} time(s)." if verbose?
  end

  max_tries.times do |n|
    result = run_command_using_args(command)
    stdout_text = result.stdout
    if stop_condition.(stdout_text)
      report_attempt_count.(n + 1)
      return stdout_text
    end

    sleep TRY_OS_COMMAND_RETRY_SLEEP_SECS unless n == max_tries - 1
  end

  report_attempt_count.(max_tries)
  nil
end