aboutsummaryrefslogtreecommitdiffstats
path: root/lib/alaveteli_external_command.rb
blob: ddf968f901be89bca89dfafe05262e73881ab1df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
require 'external_command'

module AlaveteliExternalCommand
    class << self
        # Final argument can be a hash of options.
        # Valid options are:
        # :append_to - string to append the output of the process to
        # :append_errors_to - string to append the errors produced by the process to
        # :stdin_string - stdin string to pass to the process
        # :binary_output - boolean flag for treating the output as binary or text encoded with
        #                   the default external encoding (only significant in ruby 1.9 and above)
        # :binary_input - boolean flag for treating the input as binary or as text encoded with
        #                   the default external encoding (only significant in ruby 1.9 and above)
        # :memory_limit - maximum amount of memory (in bytes) available to the process
        # :timeout - maximum amount of time (in s) to allow the process to run for
        # :env - hash of environment variables to set for the process
        def run(program_name, *args)
            # Run an external program, and return its output.
            # Standard error is suppressed unless the program
            # fails (i.e. returns a non-zero exit status).
            # If the program fails, returns nil and writes any error to stderr.
            # TODO: calling code should be able to specify error stream - may want to log it or
            # otherwise act upon it.
            opts = {}
            if !args.empty? && args.last.is_a?(Hash)
                opts = args.last
            end

            program_path = find_program(program_name)
            xc = ExternalCommand.new(program_path, *args)
            begin
                xc.run
            rescue ExternalCommand::ChildUnterminated => e
                $stderr.puts(e.message)
                return nil
            end

            if !xc.exited
                # Crash or timeout
                if xc.timed_out
                    $stderr.puts(%Q[External Command: "#{program_name} #{args.join(' ')}" timed out at #{opts[:timeout]}s])
                else
                    $stderr.puts(%Q[External Command: "#{program_name} #{args.join(' ')}" exited abnormally])
                end
                $stderr.print(xc.err)
                return nil

            elsif xc.status != 0
                # Error
                $stderr.puts(%Q[External Command: Error from command "#{program_name} #{args.join(' ')}":])
                $stderr.print(xc.err)
                return nil
            else
                if opts.has_key? :append_to
                    opts[:append_to] << "\n\n"
                else

                    return xc.out
                end
            end
        end

        def find_program(program_name)
            if program_name =~ %r(^/)
                return program_name
            else
                search_path = AlaveteliConfiguration::utility_search_path
                search_path.each do |d|
                    program_path = File.join(d, program_name)
                    return program_name if File.file? program_path and File.executable? program_path
                end
                raise "Could not find #{program_name} in any of #{search_path.join(', ')}"
            end
        end
    end
end