Class: OpenVZ::Shell

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

Overview

This code is completely borrowed from marionette-collective.

Wrapper around systemu that handles executing of system commands in a way that makes stdout, stderr and status available. Supports timeouts and sets a default sane environment.

s = Shell.new("date", opts)
s.runcommand
puts s.stdout
puts s.stderr
puts s.status.exitcode

Options hash can have:

cwd         - the working directory the command will be run from
stdin       - a string that will be sent to stdin of the program
stdout      - a variable that will receive stdout, must support <<
stderr      - a variable that will receive stdin, must support <<
environment - the shell environment, defaults to include LC_ALL=C
              set to nil to clear the environment even of LC_ALL

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command, options = {}) ⇒ Shell

Returns a new instance of Shell.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/openvz/shell.rb', line 27

def initialize(command, options={})
    @environment = {"LC_ALL" => "C"}
    @command = command
    @status = nil
    @stdout = ""
    @stderr = ""
    @stdin = nil
    @cwd = "/tmp"

    options.each do |opt, val|
        case opt.to_s
            when "stdout"
                raise "stdout should support <<" unless val.respond_to?("<<")
                @stdout = val

            when "stderr"
                raise "stderr should support <<" unless val.respond_to?("<<")
                @stderr = val

            when "stdin"
                raise "stdin should be a String" unless val.is_a?(String)
                @stdin = val

            when "cwd"
                raise "Directory #{val} does not exist" unless File.directory?(val)
                @cwd = val

            when "environment"
                if val.nil?
                    @environment = {}
                else
                    @environment.merge!(val.dup)
                end
        end
    end
end

Instance Attribute Details

#commandObject (readonly)

Returns the value of attribute command.



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

def command
  @command
end

#cwdObject (readonly)

Returns the value of attribute cwd.



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

def cwd
  @cwd
end

#environmentObject (readonly)

Returns the value of attribute environment.



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

def environment
  @environment
end

#statusObject (readonly)

Returns the value of attribute status.



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

def status
  @status
end

#stderrObject (readonly)

Returns the value of attribute stderr.



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

def stderr
  @stderr
end

#stdinObject (readonly)

Returns the value of attribute stdin.



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

def stdin
  @stdin
end

#stdoutObject (readonly)

Returns the value of attribute stdout.



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

def stdout
  @stdout
end

Instance Method Details

#runcommandObject

Actually does the systemu call passing in the correct environment, stdout and stderr



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/openvz/shell.rb', line 65

def runcommand
    opts = {"env"    => @environment,
            "stdout" => @stdout,
            "stderr" => @stderr,
            "cwd"    => @cwd}

    opts["stdin"] = @stdin if @stdin

    # Running waitpid on the cid here will start a thread
    # with the waitpid in it, this way even if the thread
    # that started this process gets killed due to agent
    # timeout or such there will still be a waitpid waiting
    # for the child to exit and not leave zombies.
    @status = systemu(@command, opts) do |cid|
        begin
            sleep 1
            Process::waitpid(cid)
        rescue SystemExit
        rescue Errno::ECHILD
        rescue Exception => e
            Log.info("Unexpected exception received while waiting for child process: #{e.class}: #{e}")
        end
    end
end