Class: Caliph::Shell

Inherits:
Object
  • Object
show all
Defined in:
lib/caliph/shell.rb

Overview

Operates something like a command line shell, except from a Ruby object perspective.

Basically, a Shell operates as your handle on creating, running and killing commands in Caliph.

Instance Attribute Summary collapse

Running Commands collapse

Instance Method Summary collapse

Instance Attribute Details

#output_streamObject

Returns the value of attribute output_stream.



15
16
17
# File 'lib/caliph/shell.rb', line 15

def output_stream
  @output_stream
end

#verboseObject

Returns the value of attribute verbose.



15
16
17
# File 'lib/caliph/shell.rb', line 15

def verbose
  @verbose
end

Instance Method Details

#collect_result(command, pid, host_stdout, host_stderr) ⇒ Object

Given a process ID for a running command and a pair of stdout/stdin streams, records the results of the command and returns them in a CommandRunResult instance.



67
68
69
70
71
# File 'lib/caliph/shell.rb', line 67

def collect_result(command, pid, host_stdout, host_stderr)
  result = CommandRunResult.new(pid, command, self)
  result.streams = {1 => host_stdout, 2 => host_stderr}
  return result
end

#execute(command_line) ⇒ Object

Run the command, wait for termination, and collect the results. Returns an instance of CommandRunResult that contains the output and exit code of the command.



92
93
94
95
96
# File 'lib/caliph/shell.rb', line 92

def execute(command_line)
  result = collect_result(command_line, *spawn_process(command_line))
  result.wait
  result
end

#kill_process(pid) ⇒ Object

Kill processes given a raw pid. In general, prefer CommandRunResult#kill

Parameters:

  • pid

    the process id to kill



39
40
41
42
43
# File 'lib/caliph/shell.rb', line 39

def kill_process(pid)
  Process.kill("INT", pid)
rescue Errno::ESRCH
  warn "Couldn't find process #{pid} to kill it"
end

#report(message, newline = true) ⇒ Object

Prints information to #output_stream which defaults to $stderr.



32
33
34
# File 'lib/caliph/shell.rb', line 32

def report(message, newline=true)
  output_stream.print(message + (newline ? "\n" : ""))
end

#report_verbose(message) ⇒ Object

Reports messages if verbose is true. Used internally to print messages about running commands



27
28
29
# File 'lib/caliph/shell.rb', line 27

def report_verbose(message)
  report(message) if verbose
end

#run(&block) ⇒ CommandRunResult #run(cmd, &block) ⇒ CommandRunResult #run(*cmd_strings, &block) ⇒ CommandRunResult

Run the command, wait for termination, and collect the results. Returns an instance of CommandRunResult that contains the output and exit code of the command. This version #reports some information to document that the command is running. For a terser version, call #execute directly

Overloads:

Yields:

  • (CommandLine)

    command about to be run (for configuration)

Returns:



113
114
115
116
117
118
119
120
121
122
123
# File 'lib/caliph/shell.rb', line 113

def run(*args, &block)
  command_line = normalize_command_line(*args, &block)

  report command_line.string_format + " ", false
  result = execute(command_line)
  report "=> #{result.exit_code}"
  report_verbose result.format_streams
  return result
ensure
  report_verbose ""
end

#run_as_replacement(&block) ⇒ CommandRunResult #run_as_replacement(cmd, &block) ⇒ CommandRunResult #run_as_replacement(*cmd_strings, &block) ⇒ CommandRunResult Also known as: replace_us

Completely replace the running process with a command. Good for setting up a command and then running it, without worrying about what happens after that. Uses ‘exec` under the hood.

Examples:

Using replace_us

# The last thing we'll ever do:
shell.run_as_replacement('echo', "Everything is okay")
# don't worry, we never get here.
shell.run("sudo", "shutdown -h now")

Overloads:

Yields:

  • (CommandLine)

    command about to be run (for configuration)

Returns:



134
135
136
137
138
139
140
# File 'lib/caliph/shell.rb', line 134

def run_as_replacement(*args, &block)
  command_line = normalize_command_line(*args, &block)

  report "Ceding execution to: "
  report command_line.string_format
  Process.exec(command_line.command_environment, command_line.command)
end

#run_detached(&block) ⇒ CommandRunResult #run_detached(cmd, &block) ⇒ CommandRunResult #run_detached(*cmd_strings, &block) ⇒ CommandRunResult Also known as: spin_off

Run the command in the background. The command can survive the caller. Works, for instance, to kick off some long running processes that we don’t care about. Note that this isn’t quite full daemonization - we don’t close the streams of the other process, or scrub its environment or anything.

Overloads:

Yields:

  • (CommandLine)

    command about to be run (for configuration)

Returns:



149
150
151
152
153
154
155
# File 'lib/caliph/shell.rb', line 149

def run_detached(*args, &block)
  command_line = normalize_command_line(*args, &block)

  pid, out, err = spawn_process(command_line)
  Process.detach(pid)
  return collect_result(command_line, pid, out, err)
end

#run_in_background(&block) ⇒ CommandRunResult #run_in_background(cmd, &block) ⇒ CommandRunResult #run_in_background(*cmd_strings, &block) ⇒ CommandRunResult Also known as: background

Run the command in parallel with the parent process - will kill it if it outlasts us. Good for running e.g. a web server that we need to interact with, or the like, without cluttering the system with a bunch of zombies.

Overloads:

Yields:

  • (CommandLine)

    command about to be run (for configuration)

Returns:



162
163
164
165
166
167
168
169
170
171
# File 'lib/caliph/shell.rb', line 162

def run_in_background(*args, &block)
  command_line = normalize_command_line(*args, &block)

  pid, out, err = spawn_process(command_line)
  Process.detach(pid)
  at_exit do
    kill_process(pid)
  end
  return collect_result(command_line, pid, out, err)
end

#spawn_process(command_line) ⇒ pid, ...

Creates a process to run a command. Handles connecting pipes to stardard streams, launching the process and returning a pid for it.

Returns:

  • (pid, host_stdout, host_stderr)

    the process id and streams associated with the child process



77
78
79
80
81
82
83
84
85
86
# File 'lib/caliph/shell.rb', line 77

def spawn_process(command_line)
  host_stdout, cmd_stdout = IO.pipe
  host_stderr, cmd_stderr = IO.pipe

  pid = Process.spawn(command_line.command_environment, command_line.command, :out => cmd_stdout, :err => cmd_stderr)
  cmd_stdout.close
  cmd_stderr.close

  return pid, host_stdout, host_stderr
end