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.



13
14
15
# File 'lib/caliph/shell.rb', line 13

def output_stream
  @output_stream
end

#verboseObject

Returns the value of attribute verbose.



13
14
15
# File 'lib/caliph/shell.rb', line 13

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.



63
64
65
66
67
# File 'lib/caliph/shell.rb', line 63

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.



88
89
90
91
92
# File 'lib/caliph/shell.rb', line 88

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



37
38
39
40
41
# File 'lib/caliph/shell.rb', line 37

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.



30
31
32
# File 'lib/caliph/shell.rb', line 30

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



25
26
27
# File 'lib/caliph/shell.rb', line 25

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

Yields:

  • (CommandLine)

    command about to be run (for configuration)



109
110
111
112
113
114
115
116
117
118
119
# File 'lib/caliph/shell.rb', line 109

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")

Yields:

  • (CommandLine)

    command about to be run (for configuration)



130
131
132
133
134
135
136
# File 'lib/caliph/shell.rb', line 130

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.

Yields:

  • (CommandLine)

    command about to be run (for configuration)



145
146
147
148
149
150
151
# File 'lib/caliph/shell.rb', line 145

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.

Yields:

  • (CommandLine)

    command about to be run (for configuration)



158
159
160
161
162
163
164
165
166
167
# File 'lib/caliph/shell.rb', line 158

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.



73
74
75
76
77
78
79
80
81
82
# File 'lib/caliph/shell.rb', line 73

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