Module: Sensu::Spawn

Defined in:
lib/sensu/spawn.rb

Class Method Summary collapse

Class Method Details

.build_child_process(command) ⇒ Array

Build a child process attached to a pipe, in order to capture its output (STDERR, STDOUT). The child process will be a platform dependent shell, that is responsible for executing the provided command.

Parameters:

  • command (String)

    to run.

Returns:

  • (Array)

    child object, pipe reader, pipe writer.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/sensu/spawn.rb', line 44

def build_child_process(command)
  reader, writer = IO.pipe
  shell = case RUBY_PLATFORM
  when /(ms|cyg|bcc)win|mingw|win32/
    ["cmd", "/c"]
  else
    ["sh", "-c"]
  end
  ChildProcess.posix_spawn = true
  shell_command = shell + [command]
  child = ChildProcess.build(*shell_command)
  child.io.stdout = child.io.stderr = writer
  child.leader = true
  [child, reader, writer]
end

.child_process(command, options = {}) ⇒ Array

Create a child process, return its output (STDERR & STDOUT), and exit status. The child process will have its own process group, may accept data via STDIN, and have a timeout.

The child process timeout functionality needs to be re-worked, as it currenty allows for a deadlock, when the child output is greater than the OS max buffer size.

Parameters:

  • command (String)

    to run.

  • options (Hash) (defaults to: {})

    to create a child process with.

Options Hash (options):

  • :data (String)

    to write to STDIN.

  • :timeout (Integer)

    in seconds.

Returns:

  • (Array)

    child process output and exit status.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/sensu/spawn.rb', line 87

def child_process(command, options={})
  child, reader, writer = build_child_process(command)
  child.duplex = true if options[:data]
  child.start
  writer.close
  if options[:data]
    child.io.stdin.write(options[:data])
    child.io.stdin.close
  end
  if options[:timeout]
    child.poll_for_exit(options[:timeout])
    output = read_until_eof(reader)
  else
    output = read_until_eof(reader)
    child.wait
  end
  [output, child.exit_code]
rescue ChildProcess::TimeoutError
  child.stop rescue nil
  ["Execution timed out", 2]
rescue => error
  child.stop rescue nil
  ["Unexpected error: #{error}", 3]
end

.process(command, options = {}, &callback) ⇒ Object

Spawn a child process. A maximum of 12 processes will be spawned at a time. The EventMachine reactor (loop) must be running for this method to work.

Parameters:

  • command (String)

    to run.

  • options (Hash) (defaults to: {})

    to create a child process with.

  • callback (Proc)

    called when the child process exits, its output and exit status are passed as parameters.

Options Hash (options):

  • :data (String)

    to write to STDIN.

  • :timeout (Integer)

    in seconds.



29
30
31
32
33
34
35
# File 'lib/sensu/spawn.rb', line 29

def process(command, options={}, &callback)
  create = Proc.new do
    child_process(command, options)
  end
  @process_worker ||= EM::Worker.new(:concurrency => 12)
  @process_worker.enqueue(create, callback)
end

.read_until_eof(reader) ⇒ String

Read a stream/file until end of file (EOF).

Parameters:

  • reader (Object)

    to read contents of until EOF.

Returns:

  • (String)

    the stream/file contents.



64
65
66
67
68
69
70
71
72
# File 'lib/sensu/spawn.rb', line 64

def read_until_eof(reader)
  output = ""
  begin
    loop { output << reader.readpartial(8192) }
  rescue EOFError
  end
  reader.close
  output
end