Module: Chef::ShellOut::Windows

Includes:
Windows::Handle, Windows::Process, Windows::Synchronize
Included in:
Chef::ShellOut
Defined in:
lib/chef/shell_out/windows.rb

Defined Under Namespace

Classes: ThingThatLooksSortOfLikeAProcessStatus

Constant Summary collapse

TIME_SLICE =
0.05

Instance Method Summary collapse

Instance Method Details

#run_commandObject

– Missing lots of features from the UNIX version, such as uid, etc.


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
110
111
112
113
114
115
116
117
118
# File 'lib/chef/shell_out/windows.rb', line 38

def run_command

  #
  # Create pipes to capture stdout and stderr,
  #
  stdout_read, stdout_write = IO.pipe
  stderr_read, stderr_write = IO.pipe
  stdin_read, stdin_write = IO.pipe
  open_streams = [ stdout_read, stderr_read ]

  begin

    #
    # Set cwd, environment, appname, etc.
    #
    app_name, command_line = command_to_run
    create_process_args = {
      :app_name => app_name,
      :command_line => command_line,
      :startup_info => {
        :stdout => stdout_write,
        :stderr => stderr_write,
        :stdin => stdin_read
      },
      :environment => inherit_environment.map { |k,v| "#{k}=#{v}" },
      :close_handles => false
    }
    create_process_args[:cwd] = cwd if cwd

    #
    # Start the process
    #
    process = Process.create(create_process_args)
    begin

      #
      # Wait for the process to finish, consuming output as we go
      #
      start_wait = Time.now
      while true
        wait_status = WaitForSingleObject(process.process_handle, 0)
        case wait_status
          when WAIT_OBJECT_0
            # Get process exit code
            exit_code = [0].pack('l')
            unless GetExitCodeProcess(process.process_handle, exit_code)
              raise get_last_error
            end
            @status = ThingThatLooksSortOfLikeAProcessStatus.new
            @status.exitstatus = exit_code.unpack('l').first

            return self
          when WAIT_TIMEOUT
            # Kill the process
            if (Time.now - start_wait) > timeout
              raise Chef::Exceptions::CommandTimeout, "command timed out:\n#{format_for_exception}"
            end

            consume_output(open_streams, stdout_read, stderr_read)
          else
            raise "Unknown response from WaitForSingleObject(#{process.process_handle}, #{timeout*1000}): #{wait_status}"
        end

      end

    ensure
      CloseHandle(process.thread_handle)
      CloseHandle(process.process_handle)
    end

  ensure
    #
    # Consume all remaining data from the pipes until they are closed
    #
    stdout_write.close
    stderr_write.close

    while consume_output(open_streams, stdout_read, stderr_read)
    end
  end
end