File: command.rb

package info (click to toggle)
ruby-activeldap 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 1,588 kB
  • sloc: ruby: 18,143; sh: 12; makefile: 5
file content (109 lines) | stat: -rw-r--r-- 2,602 bytes parent folder | download | duplicates (2)
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
require "thread"
require "socket"
require "shellwords"

module Command
  class Error < StandardError
    attr_reader :command, :result
    def initialize(command, result)
      @command = command
      @result = result
      super("#{command}: #{result}")
    end
  end

  module_function
  def detach_io
    require 'fcntl'
    [TCPSocket, ::File].each do |c|
      ObjectSpace.each_object(c) do |io|
        begin
          unless io.closed?
            io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
          end
        rescue SystemCallError, IOError
        end
      end
    end
  end

  def run(cmd, *args, &block)
    raise ArgumentError, "command isn't specified" if cmd.nil?
    if args.any? {|x| x.nil?}
      raise ArgumentError, "args has nil: #{args.inspect}"
    end
    args = args.collect {|arg| arg.to_s}
    return java_run(cmd, *args, &block) if Object.respond_to?(:java)
    in_r, in_w = IO.pipe
    out_r, out_w = IO.pipe
    verbose = $VERBOSE
    # ruby(>=1.8)'s fork terminates other threads with warning messages
    $VERBOSE = nil
    pid = fork do
      $VERBOSE = verbose
      detach_io
      STDIN.reopen(in_r)
      in_r.close
      STDOUT.reopen(out_w)
      STDERR.reopen(out_w)
      out_w.close
      exec(cmd, *args)
      exit!(-1)
    end
    $VERBOSE = verbose
    yield(out_r, in_w) if block_given?
    in_r.close unless in_r.closed?
    out_w.close unless out_w.closed?
    pid, status = Process.waitpid2(pid)
    [status.exited? && status.exitstatus.zero?, out_r.read]
  end

  def java_run(cmd, *args, &block)
    runtime = java.lang.Runtime.get_runtime
    process = runtime.exec([cmd, *args].to_java(:string))
    input = JavaReaderWrapper.new(process.get_input_stream)
    output = JavaWriterWrapper.new(process.get_output_stream)
    error = JavaReaderWrapper.new(process.get_error_stream)
    yield(input, output) if block_given?
    output.close
    success = process.wait_for.zero?

    [success, input.read + error.read]
  end

  class JavaReaderWrapper
    def initialize(input)
      @input = input
    end

    def read
      result = ""
      while (c = @input.read) != -1
        result << c.chr
      end
      result
    end
  end

  class JavaWriterWrapper
    def initialize(output)
      output = java.io.OutputStreamWriter.new(output)
      @output = java.io.BufferedWriter.new(output)
    end

    def puts(*messages)
      messages.each do |message|
        message += "\n" if /\n/ !~ message
        @output.write(message)
      end
    end

    def flush
      @output.flush
    end

    def close
      @output.close
    end
  end
end