chef 0.10.8 → 0.10.10.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (197) hide show
  1. data/distro/arch/etc/rc.d/chef-client +15 -1
  2. data/distro/common/html/chef-client.8.html +4 -4
  3. data/distro/common/html/chef-expander.8.html +4 -4
  4. data/distro/common/html/chef-expanderctl.8.html +4 -4
  5. data/distro/common/html/chef-server-webui.8.html +4 -4
  6. data/distro/common/html/chef-server.8.html +4 -4
  7. data/distro/common/html/chef-solo.8.html +4 -4
  8. data/distro/common/html/chef-solr.8.html +4 -4
  9. data/distro/common/html/knife-bootstrap.1.html +6 -10
  10. data/distro/common/html/knife-client.1.html +4 -4
  11. data/distro/common/html/knife-configure.1.html +4 -4
  12. data/distro/common/html/knife-cookbook-site.1.html +6 -6
  13. data/distro/common/html/knife-cookbook.1.html +4 -4
  14. data/distro/common/html/knife-data-bag.1.html +4 -4
  15. data/distro/common/html/knife-environment.1.html +4 -4
  16. data/distro/common/html/knife-exec.1.html +4 -4
  17. data/distro/common/html/knife-index.1.html +4 -4
  18. data/distro/common/html/knife-node.1.html +5 -5
  19. data/distro/common/html/knife-role.1.html +4 -4
  20. data/distro/common/html/knife-search.1.html +4 -4
  21. data/distro/common/html/knife-ssh.1.html +5 -6
  22. data/distro/common/html/knife-status.1.html +4 -4
  23. data/distro/common/html/knife-tag.1.html +4 -4
  24. data/distro/common/html/knife.1.html +7 -8
  25. data/distro/common/html/shef.1.html +4 -4
  26. data/distro/common/man/man1/knife-bootstrap.1 +4 -4
  27. data/distro/common/man/man1/knife-client.1 +1 -1
  28. data/distro/common/man/man1/knife-configure.1 +1 -1
  29. data/distro/common/man/man1/knife-cookbook-site.1 +4 -4
  30. data/distro/common/man/man1/knife-cookbook.1 +1 -1
  31. data/distro/common/man/man1/knife-data-bag.1 +1 -1
  32. data/distro/common/man/man1/knife-environment.1 +1 -1
  33. data/distro/common/man/man1/knife-exec.1 +1 -1
  34. data/distro/common/man/man1/knife-index.1 +1 -1
  35. data/distro/common/man/man1/knife-node.1 +2 -2
  36. data/distro/common/man/man1/knife-role.1 +1 -1
  37. data/distro/common/man/man1/knife-search.1 +1 -1
  38. data/distro/common/man/man1/knife-ssh.1 +3 -7
  39. data/distro/common/man/man1/knife-status.1 +1 -1
  40. data/distro/common/man/man1/knife-tag.1 +1 -1
  41. data/distro/common/man/man1/knife.1 +5 -9
  42. data/distro/common/man/man1/shef.1 +1 -1
  43. data/distro/common/man/man8/chef-client.8 +1 -1
  44. data/distro/common/man/man8/chef-expander.8 +1 -1
  45. data/distro/common/man/man8/chef-expanderctl.8 +1 -1
  46. data/distro/common/man/man8/chef-server-webui.8 +1 -1
  47. data/distro/common/man/man8/chef-server.8 +1 -1
  48. data/distro/common/man/man8/chef-solo.8 +1 -1
  49. data/distro/common/man/man8/chef-solr.8 +1 -1
  50. data/distro/common/markdown/man1/knife-bootstrap.mkd +3 -7
  51. data/distro/common/markdown/man1/knife-cookbook-site.mkd +3 -3
  52. data/distro/common/markdown/man1/knife-node.mkd +2 -2
  53. data/distro/common/markdown/man1/knife-ssh.mkd +2 -5
  54. data/distro/common/markdown/man1/knife.mkd +7 -9
  55. data/distro/debian/etc/init.d/chef-client +22 -1
  56. data/distro/redhat/etc/init.d/chef-client +12 -1
  57. data/distro/windows/service_manager.rb +164 -0
  58. data/lib/chef/application.rb +12 -6
  59. data/lib/chef/application/client.rb +4 -3
  60. data/lib/chef/application/knife.rb +7 -12
  61. data/lib/chef/application/solo.rb +2 -1
  62. data/lib/chef/application/windows_service.rb +224 -0
  63. data/lib/chef/checksum_cache.rb +1 -0
  64. data/lib/chef/client.rb +3 -16
  65. data/lib/chef/config.rb +42 -13
  66. data/lib/chef/cookbook/metadata.rb +1 -1
  67. data/lib/chef/cookbook/syntax_check.rb +2 -2
  68. data/lib/chef/cookbook_version.rb +5 -0
  69. data/lib/chef/daemon.rb +1 -1
  70. data/lib/chef/exceptions.rb +7 -1
  71. data/lib/chef/file_access_control.rb +13 -87
  72. data/lib/chef/file_access_control/unix.rb +119 -0
  73. data/lib/chef/file_access_control/windows.rb +257 -0
  74. data/lib/chef/handler/json_file.rb +7 -1
  75. data/lib/chef/knife.rb +10 -16
  76. data/lib/chef/knife/bootstrap.rb +15 -8
  77. data/lib/chef/knife/bootstrap/centos5-gems.erb +1 -1
  78. data/lib/chef/knife/bootstrap/chef-full.erb +59 -0
  79. data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +1 -0
  80. data/lib/chef/knife/configure.rb +2 -2
  81. data/lib/chef/knife/cookbook_site_download.rb +60 -21
  82. data/lib/chef/knife/cookbook_site_install.rb +16 -21
  83. data/lib/chef/knife/cookbook_upload.rb +77 -48
  84. data/lib/chef/knife/core/bootstrap_context.rb +3 -1
  85. data/lib/chef/knife/core/cookbook_scm_repo.rb +1 -1
  86. data/lib/chef/knife/core/node_editor.rb +1 -1
  87. data/lib/chef/knife/core/subcommand_loader.rb +1 -1
  88. data/lib/chef/knife/core/ui.rb +3 -2
  89. data/lib/chef/knife/help_topics.rb +1 -1
  90. data/lib/chef/knife/node_run_list_add.rb +14 -6
  91. data/lib/chef/knife/node_run_list_remove.rb +3 -3
  92. data/lib/chef/knife/ssh.rb +32 -13
  93. data/lib/chef/mash.rb +14 -0
  94. data/lib/chef/mixin/command.rb +1 -0
  95. data/lib/chef/mixin/command/unix.rb +5 -0
  96. data/lib/chef/mixin/convert_to_class_name.rb +2 -0
  97. data/lib/chef/mixin/deep_merge.rb +40 -18
  98. data/lib/chef/mixin/enforce_ownership_and_permissions.rb +39 -0
  99. data/lib/chef/mixin/language.rb +89 -3
  100. data/lib/chef/mixin/language_include_recipe.rb +8 -4
  101. data/lib/chef/mixin/path_sanity.rb +67 -0
  102. data/lib/chef/mixin/recipe_definition_dsl_core.rb +19 -11
  103. data/lib/chef/mixin/securable.rb +152 -0
  104. data/lib/chef/mixin/shell_out.rb +1 -1
  105. data/lib/chef/mixin/template.rb +8 -3
  106. data/lib/chef/mixins.rb +3 -0
  107. data/lib/chef/monkey_patches/moneta.rb +50 -0
  108. data/lib/chef/monkey_patches/string.rb +1 -1
  109. data/lib/chef/node.rb +2 -1
  110. data/lib/chef/platform.rb +34 -0
  111. data/lib/chef/provider.rb +23 -21
  112. data/lib/chef/provider/cron.rb +17 -12
  113. data/lib/chef/provider/cron/solaris.rb +6 -18
  114. data/lib/chef/provider/deploy.rb +14 -15
  115. data/lib/chef/provider/deploy/timestamped.rb +0 -1
  116. data/lib/chef/provider/directory.rb +1 -3
  117. data/lib/chef/provider/execute.rb +2 -2
  118. data/lib/chef/provider/file.rb +1 -75
  119. data/lib/chef/provider/git.rb +11 -9
  120. data/lib/chef/provider/group/gpasswd.rb +14 -9
  121. data/lib/chef/provider/link.rb +28 -59
  122. data/lib/chef/provider/mdadm.rb +2 -2
  123. data/lib/chef/provider/mount/mount.rb +1 -1
  124. data/lib/chef/provider/package.rb +10 -6
  125. data/lib/chef/provider/package/apt.rb +3 -1
  126. data/lib/chef/provider/package/dpkg.rb +1 -1
  127. data/lib/chef/provider/package/portage.rb +6 -3
  128. data/lib/chef/provider/package/rubygems.rb +75 -6
  129. data/lib/chef/provider/package/smartos.rb +84 -0
  130. data/lib/chef/provider/package/yum-dump.py +3 -2
  131. data/lib/chef/provider/package/yum.rb +51 -10
  132. data/lib/chef/provider/remote_directory.rb +24 -3
  133. data/lib/chef/provider/remote_file.rb +0 -6
  134. data/lib/chef/provider/route.rb +3 -3
  135. data/lib/chef/provider/service/debian.rb +2 -2
  136. data/lib/chef/provider/service/freebsd.rb +1 -1
  137. data/lib/chef/provider/service/macosx.rb +125 -0
  138. data/lib/chef/provider/service/windows.rb +5 -1
  139. data/lib/chef/provider/subversion.rb +10 -7
  140. data/lib/chef/providers.rb +3 -0
  141. data/lib/chef/resource.rb +181 -87
  142. data/lib/chef/resource/apt_package.rb +10 -1
  143. data/lib/chef/resource/chef_gem.rb +53 -0
  144. data/lib/chef/resource/conditional.rb +3 -0
  145. data/lib/chef/resource/cookbook_file.rb +12 -6
  146. data/lib/chef/resource/cron.rb +9 -0
  147. data/lib/chef/resource/directory.rb +14 -31
  148. data/lib/chef/resource/execute.rb +11 -9
  149. data/lib/chef/resource/file.rb +9 -33
  150. data/lib/chef/resource/link.rb +13 -8
  151. data/lib/chef/resource/mdadm.rb +10 -1
  152. data/lib/chef/resource/remote_directory.rb +13 -2
  153. data/lib/chef/resource/remote_file.rb +14 -7
  154. data/lib/chef/resource/smartos_package.rb +36 -0
  155. data/lib/chef/resource/template.rb +12 -5
  156. data/lib/chef/resource_platform_map.rb +153 -0
  157. data/lib/chef/resources.rb +2 -0
  158. data/lib/chef/rest.rb +55 -10
  159. data/lib/chef/rest/auth_credentials.rb +1 -0
  160. data/lib/chef/rest/rest_request.rb +24 -8
  161. data/lib/chef/role.rb +8 -2
  162. data/lib/chef/run_list.rb +1 -1
  163. data/lib/chef/run_list/run_list_expansion.rb +2 -2
  164. data/lib/chef/run_list/run_list_item.rb +7 -0
  165. data/lib/chef/runner.rb +4 -0
  166. data/lib/chef/shef.rb +2 -2
  167. data/lib/chef/shef/shef_session.rb +4 -5
  168. data/lib/chef/shell_out.rb +2 -245
  169. data/lib/chef/util/file_edit.rb +99 -89
  170. data/lib/chef/version.rb +1 -1
  171. data/lib/chef/win32/api.rb +349 -0
  172. data/lib/chef/win32/api/error.rb +921 -0
  173. data/lib/chef/win32/api/file.rb +289 -0
  174. data/lib/chef/win32/api/memory.rb +105 -0
  175. data/lib/chef/win32/api/process.rb +40 -0
  176. data/lib/chef/win32/api/psapi.rb +51 -0
  177. data/lib/chef/win32/api/security.rb +341 -0
  178. data/lib/chef/win32/api/system.rb +192 -0
  179. data/lib/chef/win32/api/unicode.rb +178 -0
  180. data/lib/chef/win32/error.rb +73 -0
  181. data/lib/chef/win32/file.rb +117 -0
  182. data/lib/chef/win32/file/info.rb +100 -0
  183. data/lib/chef/win32/handle.rb +48 -0
  184. data/lib/chef/win32/memory.rb +101 -0
  185. data/lib/chef/win32/process.rb +84 -0
  186. data/lib/chef/win32/security.rb +489 -0
  187. data/lib/chef/win32/security/ace.rb +125 -0
  188. data/lib/chef/win32/security/acl.rb +101 -0
  189. data/lib/chef/win32/security/securable_object.rb +109 -0
  190. data/lib/chef/win32/security/security_descriptor.rb +93 -0
  191. data/lib/chef/win32/security/sid.rb +199 -0
  192. data/lib/chef/win32/security/token.rb +64 -0
  193. data/lib/chef/win32/unicode.rb +43 -0
  194. data/lib/chef/win32/version.rb +119 -0
  195. metadata +104 -158
  196. data/lib/chef/shell_out/unix.rb +0 -223
  197. data/lib/chef/shell_out/windows.rb +0 -588
@@ -1,223 +0,0 @@
1
- class Chef
2
- class ShellOut
3
- module Unix
4
-
5
- # Run the command, writing the command's standard out and standard error
6
- # to +stdout+ and +stderr+, and saving its exit status object to +status+
7
- # === Returns
8
- # returns +self+; +stdout+, +stderr+, +status+, and +exitstatus+ will be
9
- # populated with results of the command
10
- # === Raises
11
- # * Errno::EACCES when you are not privileged to execute the command
12
- # * Errno::ENOENT when the command is not available on the system (or not
13
- # in the current $PATH)
14
- # * Chef::Exceptions::CommandTimeout when the command does not complete
15
- # within +timeout+ seconds (default: 60s)
16
- def run_command
17
- @child_pid = fork_subprocess
18
-
19
- configure_parent_process_file_descriptors
20
- propagate_pre_exec_failure
21
-
22
- @result = nil
23
- @execution_time = 0
24
-
25
- # Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
26
- # when calling IO.select and IO#read. Some OS Vendors are not interested
27
- # in updating their ruby packages (Apple, *cough*) and we *have to*
28
- # make it work. So I give you this epic hack:
29
- GC.disable
30
- until @status
31
- ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
32
- unless ready
33
- @execution_time += READ_WAIT_TIME
34
- if @execution_time >= timeout && !@result
35
- raise Chef::Exceptions::CommandTimeout, "command timed out:\n#{format_for_exception}"
36
- end
37
- end
38
-
39
- if ready && ready.first.include?(child_stdout)
40
- read_stdout_to_buffer
41
- end
42
- if ready && ready.first.include?(child_stderr)
43
- read_stderr_to_buffer
44
- end
45
-
46
- unless @status
47
- # make one more pass to get the last of the output after the
48
- # child process dies
49
- if results = Process.waitpid2(@child_pid, Process::WNOHANG)
50
- @status = results.last
51
- redo
52
- end
53
- end
54
- end
55
- self
56
- rescue Exception
57
- # do our best to kill zombies
58
- Process.waitpid2(@child_pid, Process::WNOHANG) rescue nil
59
- raise
60
- ensure
61
- # no matter what happens, turn the GC back on, and hope whatever busted
62
- # version of ruby we're on doesn't allocate some objects during the next
63
- # GC run.
64
- GC.enable
65
- close_all_pipes
66
- end
67
-
68
- private
69
-
70
- def set_user
71
- if user
72
- Process.euid = uid
73
- Process.uid = uid
74
- end
75
- end
76
-
77
- def set_group
78
- if group
79
- Process.egid = gid
80
- Process.gid = gid
81
- end
82
- end
83
-
84
- def set_environment
85
- environment.each do |env_var,value|
86
- ENV[env_var] = value
87
- end
88
- end
89
-
90
- def set_umask
91
- File.umask(umask) if umask
92
- end
93
-
94
- def set_cwd
95
- Dir.chdir(cwd) if cwd
96
- end
97
-
98
- def initialize_ipc
99
- @stdout_pipe, @stderr_pipe, @process_status_pipe = IO.pipe, IO.pipe, IO.pipe
100
- @process_status_pipe.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
101
- end
102
-
103
- def child_stdout
104
- @stdout_pipe[0]
105
- end
106
-
107
- def child_stderr
108
- @stderr_pipe[0]
109
- end
110
-
111
- def child_process_status
112
- @process_status_pipe[0]
113
- end
114
-
115
- def close_all_pipes
116
- child_stdout.close unless child_stdout.closed?
117
- child_stderr.close unless child_stderr.closed?
118
- child_process_status.close unless child_process_status.closed?
119
- end
120
-
121
- # replace stdout, and stderr with pipes to the parent, and close the
122
- # reader side of the error marshaling side channel. Close STDIN so when we
123
- # exec, the new program will know it's never getting input ever.
124
- def configure_subprocess_file_descriptors
125
- process_status_pipe.first.close
126
-
127
- # HACK: for some reason, just STDIN.close isn't good enough when running
128
- # under ruby 1.9.2, so make it good enough:
129
- stdin_reader, stdin_writer = IO.pipe
130
- stdin_writer.close
131
- STDIN.reopen stdin_reader
132
- stdin_reader.close
133
-
134
- stdout_pipe.first.close
135
- STDOUT.reopen stdout_pipe.last
136
- stdout_pipe.last.close
137
-
138
- stderr_pipe.first.close
139
- STDERR.reopen stderr_pipe.last
140
- stderr_pipe.last.close
141
-
142
- STDOUT.sync = STDERR.sync = true
143
- end
144
-
145
- def configure_parent_process_file_descriptors
146
- # Close the sides of the pipes we don't care about
147
- stdout_pipe.last.close
148
- stderr_pipe.last.close
149
- process_status_pipe.last.close
150
- # Get output as it happens rather than buffered
151
- child_stdout.sync = true
152
- child_stderr.sync = true
153
-
154
- true
155
- end
156
-
157
- # Some patch levels of ruby in wide use (in particular the ruby 1.8.6 on OSX)
158
- # segfault when you IO.select a pipe that's reached eof. Weak sauce.
159
- def open_pipes
160
- @open_pipes ||= [child_stdout, child_stderr]
161
- end
162
-
163
- def read_stdout_to_buffer
164
- while chunk = child_stdout.read_nonblock(READ_SIZE)
165
- @stdout << chunk
166
- @live_stream << chunk if @live_stream
167
- end
168
- rescue Errno::EAGAIN
169
- rescue EOFError
170
- open_pipes.delete_at(0)
171
- end
172
-
173
- def read_stderr_to_buffer
174
- while chunk = child_stderr.read_nonblock(READ_SIZE)
175
- @stderr << chunk
176
- end
177
- rescue Errno::EAGAIN
178
- rescue EOFError
179
- open_pipes.delete_at(1)
180
- end
181
-
182
- def fork_subprocess
183
- initialize_ipc
184
-
185
- fork do
186
- configure_subprocess_file_descriptors
187
-
188
- set_group
189
- set_user
190
- set_environment
191
- set_umask
192
- set_cwd
193
-
194
- begin
195
- command.kind_of?(Array) ? exec(*command) : exec(command)
196
-
197
- raise 'forty-two' # Should never get here
198
- rescue Exception => e
199
- Marshal.dump(e, process_status_pipe.last)
200
- process_status_pipe.last.flush
201
- end
202
- process_status_pipe.last.close unless (process_status_pipe.last.closed?)
203
- exit!
204
- end
205
- end
206
-
207
- # Attempt to get a Marshaled error from the side-channel.
208
- # If it's there, un-marshal it and raise. If it's not there,
209
- # assume everything went well.
210
- def propagate_pre_exec_failure
211
- begin
212
- e = Marshal.load child_process_status
213
- raise(Exception === e ? e : "unknown failure: #{e.inspect}")
214
- rescue EOFError # If we get an EOF error, then the exec was successful
215
- true
216
- ensure
217
- child_process_status.close
218
- end
219
- end
220
-
221
- end
222
- end
223
- end
@@ -1,588 +0,0 @@
1
- #--
2
- # Author:: Daniel DeLeo (<dan@opscode.com>)
3
- # Author:: John Keiser (<jkeiser@opscode.com>)
4
- # Copyright:: Copyright (c) 2011 Opscode, Inc.
5
- # License:: Apache License, Version 2.0
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
- #
19
-
20
- require 'win32/process'
21
- require 'windows/handle'
22
- require 'windows/process'
23
- require 'windows/synchronize'
24
-
25
- class Chef
26
- class ShellOut
27
- module Windows
28
-
29
- include ::Windows::Handle
30
- include ::Windows::Process
31
- include ::Windows::Synchronize
32
-
33
- TIME_SLICE = 0.05
34
-
35
- #--
36
- # Missing lots of features from the UNIX version, such as
37
- # uid, etc.
38
- def run_command
39
-
40
- #
41
- # Create pipes to capture stdout and stderr,
42
- #
43
- stdout_read, stdout_write = IO.pipe
44
- stderr_read, stderr_write = IO.pipe
45
- stdin_read, stdin_write = IO.pipe
46
- open_streams = [ stdout_read, stderr_read ]
47
-
48
- begin
49
-
50
- #
51
- # Set cwd, environment, appname, etc.
52
- #
53
- app_name, command_line = command_to_run
54
- create_process_args = {
55
- :app_name => app_name,
56
- :command_line => command_line,
57
- :startup_info => {
58
- :stdout => stdout_write,
59
- :stderr => stderr_write,
60
- :stdin => stdin_read
61
- },
62
- :environment => inherit_environment.map { |k,v| "#{k}=#{v}" },
63
- :close_handles => false
64
- }
65
- create_process_args[:cwd] = cwd if cwd
66
-
67
- #
68
- # Start the process
69
- #
70
- process = Process.create(create_process_args)
71
- begin
72
-
73
- #
74
- # Wait for the process to finish, consuming output as we go
75
- #
76
- start_wait = Time.now
77
- while true
78
- wait_status = WaitForSingleObject(process.process_handle, 0)
79
- case wait_status
80
- when WAIT_OBJECT_0
81
- # Get process exit code
82
- exit_code = [0].pack('l')
83
- unless GetExitCodeProcess(process.process_handle, exit_code)
84
- raise get_last_error
85
- end
86
- @status = ThingThatLooksSortOfLikeAProcessStatus.new
87
- @status.exitstatus = exit_code.unpack('l').first
88
-
89
- return self
90
- when WAIT_TIMEOUT
91
- # Kill the process
92
- if (Time.now - start_wait) > timeout
93
- raise Chef::Exceptions::CommandTimeout, "command timed out:\n#{format_for_exception}"
94
- end
95
-
96
- consume_output(open_streams, stdout_read, stderr_read)
97
- else
98
- raise "Unknown response from WaitForSingleObject(#{process.process_handle}, #{timeout*1000}): #{wait_status}"
99
- end
100
-
101
- end
102
-
103
- ensure
104
- CloseHandle(process.thread_handle)
105
- CloseHandle(process.process_handle)
106
- end
107
-
108
- ensure
109
- #
110
- # Consume all remaining data from the pipes until they are closed
111
- #
112
- stdout_write.close
113
- stderr_write.close
114
-
115
- while consume_output(open_streams, stdout_read, stderr_read)
116
- end
117
- end
118
- end
119
-
120
- private
121
-
122
- class ThingThatLooksSortOfLikeAProcessStatus
123
- attr_accessor :exitstatus
124
- def success?
125
- exitstatus == 0
126
- end
127
- end
128
-
129
- def consume_output(open_streams, stdout_read, stderr_read)
130
- return false if open_streams.length == 0
131
- ready = IO.select(open_streams, nil, nil, READ_WAIT_TIME)
132
- return true if ! ready
133
-
134
- if ready.first.include?(stdout_read)
135
- begin
136
- next_chunk = stdout_read.readpartial(READ_SIZE)
137
- @stdout << next_chunk
138
- @live_stream << next_chunk if @live_stream
139
- rescue EOFError
140
- stdout_read.close
141
- open_streams.delete(stdout_read)
142
- end
143
- end
144
-
145
- if ready.first.include?(stderr_read)
146
- begin
147
- @stderr << stderr_read.readpartial(READ_SIZE)
148
- rescue EOFError
149
- stderr_read.close
150
- open_streams.delete(stderr_read)
151
- end
152
- end
153
-
154
- return true
155
- end
156
-
157
- IS_BATCH_FILE = /\.bat|\.cmd$/i
158
-
159
- def command_to_run
160
- if command =~ /^\s*"(.*)"/
161
- # If we have quotes, do an exact match
162
- candidate = $1
163
- else
164
- # Otherwise check everything up to the first space
165
- candidate = command[0,command.index(/\s/) || command.length].strip
166
- end
167
-
168
- # Don't do searching for empty commands. Let it fail when it runs.
169
- if candidate.length == 0
170
- return [ nil, command ]
171
- end
172
-
173
- # Check if the exe exists directly. Otherwise, search PATH.
174
- exe = find_exe_at_location(candidate)
175
- if exe.nil? && exe !~ /[\\\/]/
176
- exe = which(command[0,command.index(/\s/) || command.length])
177
- end
178
-
179
- if exe.nil? || exe =~ IS_BATCH_FILE
180
- # Batch files MUST use cmd; and if we couldn't find the command we're looking for, we assume it must be a cmd builtin.
181
- [ ENV['COMSPEC'], "cmd /c #{command}" ]
182
- else
183
- [ exe, command ]
184
- end
185
- end
186
-
187
- def inherit_environment
188
- result = {}
189
- ENV.each_pair do |k,v|
190
- result[k] = v
191
- end
192
-
193
- environment.each_pair do |k,v|
194
- if v == nil
195
- result.delete(k)
196
- else
197
- result[k] = v
198
- end
199
- end
200
- result
201
- end
202
-
203
- def pathext
204
- @pathext ||= ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') + [''] : ['']
205
- end
206
-
207
- def which(cmd)
208
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
209
- exe = find_exe_at_location("#{path}/${cmd}")
210
- return exe if exe
211
- end
212
- return nil
213
- end
214
-
215
- def find_exe_at_location(path)
216
- return path if File.executable? path
217
- pathext.each do |ext|
218
- exe = "#{path}#{ext}"
219
- return exe if File.executable? exe
220
- end
221
- return nil
222
- end
223
- end # class
224
- end
225
- end
226
-
227
- #
228
- # Override module Windows::Process.CreateProcess to fix bug when
229
- # using both app_name and command_line
230
- #
231
- module Windows
232
- module Process
233
- API.new('CreateProcess', 'SPPPLLLPPP', 'B')
234
- end
235
- end
236
-
237
- #
238
- # Override Win32::Process.create to take a proper environment hash
239
- # so that variables can contain semicolons
240
- # (submitted patch to owner)
241
- #
242
- module Process
243
- def create(args)
244
- unless args.kind_of?(Hash)
245
- raise TypeError, 'Expecting hash-style keyword arguments'
246
- end
247
-
248
- valid_keys = %w/
249
- app_name command_line inherit creation_flags cwd environment
250
- startup_info thread_inherit process_inherit close_handles with_logon
251
- domain password
252
- /
253
-
254
- valid_si_keys = %/
255
- startf_flags desktop title x y x_size y_size x_count_chars
256
- y_count_chars fill_attribute sw_flags stdin stdout stderr
257
- /
258
-
259
- # Set default values
260
- hash = {
261
- 'app_name' => nil,
262
- 'creation_flags' => 0,
263
- 'close_handles' => true
264
- }
265
-
266
- # Validate the keys, and convert symbols and case to lowercase strings.
267
- args.each{ |key, val|
268
- key = key.to_s.downcase
269
- unless valid_keys.include?(key)
270
- raise ArgumentError, "invalid key '#{key}'"
271
- end
272
- hash[key] = val
273
- }
274
-
275
- si_hash = {}
276
-
277
- # If the startup_info key is present, validate its subkeys
278
- if hash['startup_info']
279
- hash['startup_info'].each{ |key, val|
280
- key = key.to_s.downcase
281
- unless valid_si_keys.include?(key)
282
- raise ArgumentError, "invalid startup_info key '#{key}'"
283
- end
284
- si_hash[key] = val
285
- }
286
- end
287
-
288
- # The +command_line+ key is mandatory unless the +app_name+ key
289
- # is specified.
290
- unless hash['command_line']
291
- if hash['app_name']
292
- hash['command_line'] = hash['app_name']
293
- hash['app_name'] = nil
294
- else
295
- raise ArgumentError, 'command_line or app_name must be specified'
296
- end
297
- end
298
-
299
- # The environment string should be passed as an array of A=B paths, or
300
- # as a string of ';' separated paths.
301
- if hash['environment']
302
- env = hash['environment']
303
- if !env.respond_to?(:join)
304
- # Backwards compat for ; separated paths
305
- env = hash['environment'].split(File::PATH_SEPARATOR)
306
- end
307
- # The argument format is a series of null-terminated strings, with an additional null terminator.
308
- env = env.map { |e| e + "\0" }.join("") + "\0"
309
- if hash['with_logon']
310
- env = env.multi_to_wide(e)
311
- end
312
- env = [env].pack('p*').unpack('L').first
313
- else
314
- env = nil
315
- end
316
-
317
- startinfo = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
318
- startinfo = startinfo.pack('LLLLLLLLLLLLSSLLLL')
319
- procinfo = [0,0,0,0].pack('LLLL')
320
-
321
- # Process SECURITY_ATTRIBUTE structure
322
- process_security = 0
323
- if hash['process_inherit']
324
- process_security = [0,0,0].pack('LLL')
325
- process_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
326
- process_security[8,4] = [1].pack('L') # TRUE
327
- end
328
-
329
- # Thread SECURITY_ATTRIBUTE structure
330
- thread_security = 0
331
- if hash['thread_inherit']
332
- thread_security = [0,0,0].pack('LLL')
333
- thread_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
334
- thread_security[8,4] = [1].pack('L') # TRUE
335
- end
336
-
337
- # Automatically handle stdin, stdout and stderr as either IO objects
338
- # or file descriptors. This won't work for StringIO, however.
339
- ['stdin', 'stdout', 'stderr'].each{ |io|
340
- if si_hash[io]
341
- if si_hash[io].respond_to?(:fileno)
342
- handle = get_osfhandle(si_hash[io].fileno)
343
- else
344
- handle = get_osfhandle(si_hash[io])
345
- end
346
-
347
- if handle == INVALID_HANDLE_VALUE
348
- raise Error, get_last_error
349
- end
350
-
351
- # Most implementations of Ruby on Windows create inheritable
352
- # handles by default, but some do not. RF bug #26988.
353
- bool = SetHandleInformation(
354
- handle,
355
- HANDLE_FLAG_INHERIT,
356
- HANDLE_FLAG_INHERIT
357
- )
358
-
359
- raise Error, get_last_error unless bool
360
-
361
- si_hash[io] = handle
362
- si_hash['startf_flags'] ||= 0
363
- si_hash['startf_flags'] |= STARTF_USESTDHANDLES
364
- hash['inherit'] = true
365
- end
366
- }
367
-
368
- # The bytes not covered here are reserved (null)
369
- unless si_hash.empty?
370
- startinfo[0,4] = [startinfo.size].pack('L')
371
- startinfo[8,4] = [si_hash['desktop']].pack('p*') if si_hash['desktop']
372
- startinfo[12,4] = [si_hash['title']].pack('p*') if si_hash['title']
373
- startinfo[16,4] = [si_hash['x']].pack('L') if si_hash['x']
374
- startinfo[20,4] = [si_hash['y']].pack('L') if si_hash['y']
375
- startinfo[24,4] = [si_hash['x_size']].pack('L') if si_hash['x_size']
376
- startinfo[28,4] = [si_hash['y_size']].pack('L') if si_hash['y_size']
377
- startinfo[32,4] = [si_hash['x_count_chars']].pack('L') if si_hash['x_count_chars']
378
- startinfo[36,4] = [si_hash['y_count_chars']].pack('L') if si_hash['y_count_chars']
379
- startinfo[40,4] = [si_hash['fill_attribute']].pack('L') if si_hash['fill_attribute']
380
- startinfo[44,4] = [si_hash['startf_flags']].pack('L') if si_hash['startf_flags']
381
- startinfo[48,2] = [si_hash['sw_flags']].pack('S') if si_hash['sw_flags']
382
- startinfo[56,4] = [si_hash['stdin']].pack('L') if si_hash['stdin']
383
- startinfo[60,4] = [si_hash['stdout']].pack('L') if si_hash['stdout']
384
- startinfo[64,4] = [si_hash['stderr']].pack('L') if si_hash['stderr']
385
- end
386
-
387
- if hash['with_logon']
388
- logon = multi_to_wide(hash['with_logon'])
389
- domain = multi_to_wide(hash['domain'])
390
- app = hash['app_name'].nil? ? nil : multi_to_wide(hash['app_name'])
391
- cmd = hash['command_line'].nil? ? nil : multi_to_wide(hash['command_line'])
392
- cwd = multi_to_wide(hash['cwd'])
393
- passwd = multi_to_wide(hash['password'])
394
-
395
- hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT
396
-
397
- process_ran = CreateProcessWithLogonW(
398
- logon, # User
399
- domain, # Domain
400
- passwd, # Password
401
- LOGON_WITH_PROFILE, # Logon flags
402
- app, # App name
403
- cmd, # Command line
404
- hash['creation_flags'], # Creation flags
405
- env, # Environment
406
- cwd, # Working directory
407
- startinfo, # Startup Info
408
- procinfo # Process Info
409
- )
410
- else
411
- process_ran = CreateProcess(
412
- hash['app_name'], # App name
413
- hash['command_line'], # Command line
414
- process_security, # Process attributes
415
- thread_security, # Thread attributes
416
- hash['inherit'], # Inherit handles?
417
- hash['creation_flags'], # Creation flags
418
- env, # Environment
419
- hash['cwd'], # Working directory
420
- startinfo, # Startup Info
421
- procinfo # Process Info
422
- )
423
- end
424
-
425
- # TODO: Close stdin, stdout and stderr handles in the si_hash unless
426
- # they're pointing to one of the standard handles already. [Maybe]
427
- if !process_ran
428
- raise_last_error("CreateProcess()")
429
- end
430
-
431
- # Automatically close the process and thread handles in the
432
- # PROCESS_INFORMATION struct unless explicitly told not to.
433
- if hash['close_handles']
434
- CloseHandle(procinfo[0,4].unpack('L').first)
435
- CloseHandle(procinfo[4,4].unpack('L').first)
436
- end
437
-
438
- ProcessInfo.new(
439
- procinfo[0,4].unpack('L').first, # hProcess
440
- procinfo[4,4].unpack('L').first, # hThread
441
- procinfo[8,4].unpack('L').first, # hProcessId
442
- procinfo[12,4].unpack('L').first # hThreadId
443
- )
444
- end
445
-
446
- def self.raise_last_error(operation)
447
- error_string = "#{operation} failed: #{get_last_error}"
448
- last_error_code = GetLastError()
449
- if ERROR_CODE_MAP.has_key?(last_error_code)
450
- raise ERROR_CODE_MAP[last_error_code], error_string
451
- else
452
- raise Error, error_string
453
- end
454
- end
455
-
456
- # List from ruby/win32/win32.c
457
- ERROR_CODE_MAP = {
458
- ERROR_INVALID_FUNCTION => Errno::EINVAL,
459
- ERROR_FILE_NOT_FOUND => Errno::ENOENT,
460
- ERROR_PATH_NOT_FOUND => Errno::ENOENT,
461
- ERROR_TOO_MANY_OPEN_FILES => Errno::EMFILE,
462
- ERROR_ACCESS_DENIED => Errno::EACCES,
463
- ERROR_INVALID_HANDLE => Errno::EBADF,
464
- ERROR_ARENA_TRASHED => Errno::ENOMEM,
465
- ERROR_NOT_ENOUGH_MEMORY => Errno::ENOMEM,
466
- ERROR_INVALID_BLOCK => Errno::ENOMEM,
467
- ERROR_BAD_ENVIRONMENT => Errno::E2BIG,
468
- ERROR_BAD_FORMAT => Errno::ENOEXEC,
469
- ERROR_INVALID_ACCESS => Errno::EINVAL,
470
- ERROR_INVALID_DATA => Errno::EINVAL,
471
- ERROR_INVALID_DRIVE => Errno::ENOENT,
472
- ERROR_CURRENT_DIRECTORY => Errno::EACCES,
473
- ERROR_NOT_SAME_DEVICE => Errno::EXDEV,
474
- ERROR_NO_MORE_FILES => Errno::ENOENT,
475
- ERROR_WRITE_PROTECT => Errno::EROFS,
476
- ERROR_BAD_UNIT => Errno::ENODEV,
477
- ERROR_NOT_READY => Errno::ENXIO,
478
- ERROR_BAD_COMMAND => Errno::EACCES,
479
- ERROR_CRC => Errno::EACCES,
480
- ERROR_BAD_LENGTH => Errno::EACCES,
481
- ERROR_SEEK => Errno::EIO,
482
- ERROR_NOT_DOS_DISK => Errno::EACCES,
483
- ERROR_SECTOR_NOT_FOUND => Errno::EACCES,
484
- ERROR_OUT_OF_PAPER => Errno::EACCES,
485
- ERROR_WRITE_FAULT => Errno::EIO,
486
- ERROR_READ_FAULT => Errno::EIO,
487
- ERROR_GEN_FAILURE => Errno::EACCES,
488
- ERROR_LOCK_VIOLATION => Errno::EACCES,
489
- ERROR_SHARING_VIOLATION => Errno::EACCES,
490
- ERROR_WRONG_DISK => Errno::EACCES,
491
- ERROR_SHARING_BUFFER_EXCEEDED => Errno::EACCES,
492
- # ERROR_BAD_NETPATH => Errno::ENOENT,
493
- # ERROR_NETWORK_ACCESS_DENIED => Errno::EACCES,
494
- # ERROR_BAD_NET_NAME => Errno::ENOENT,
495
- ERROR_FILE_EXISTS => Errno::EEXIST,
496
- ERROR_CANNOT_MAKE => Errno::EACCES,
497
- ERROR_FAIL_I24 => Errno::EACCES,
498
- ERROR_INVALID_PARAMETER => Errno::EINVAL,
499
- ERROR_NO_PROC_SLOTS => Errno::EAGAIN,
500
- ERROR_DRIVE_LOCKED => Errno::EACCES,
501
- ERROR_BROKEN_PIPE => Errno::EPIPE,
502
- ERROR_DISK_FULL => Errno::ENOSPC,
503
- ERROR_INVALID_TARGET_HANDLE => Errno::EBADF,
504
- ERROR_INVALID_HANDLE => Errno::EINVAL,
505
- ERROR_WAIT_NO_CHILDREN => Errno::ECHILD,
506
- ERROR_CHILD_NOT_COMPLETE => Errno::ECHILD,
507
- ERROR_DIRECT_ACCESS_HANDLE => Errno::EBADF,
508
- ERROR_NEGATIVE_SEEK => Errno::EINVAL,
509
- ERROR_SEEK_ON_DEVICE => Errno::EACCES,
510
- ERROR_DIR_NOT_EMPTY => Errno::ENOTEMPTY,
511
- # ERROR_DIRECTORY => Errno::ENOTDIR,
512
- ERROR_NOT_LOCKED => Errno::EACCES,
513
- ERROR_BAD_PATHNAME => Errno::ENOENT,
514
- ERROR_MAX_THRDS_REACHED => Errno::EAGAIN,
515
- # ERROR_LOCK_FAILED => Errno::EACCES,
516
- ERROR_ALREADY_EXISTS => Errno::EEXIST,
517
- ERROR_INVALID_STARTING_CODESEG => Errno::ENOEXEC,
518
- ERROR_INVALID_STACKSEG => Errno::ENOEXEC,
519
- ERROR_INVALID_MODULETYPE => Errno::ENOEXEC,
520
- ERROR_INVALID_EXE_SIGNATURE => Errno::ENOEXEC,
521
- ERROR_EXE_MARKED_INVALID => Errno::ENOEXEC,
522
- ERROR_BAD_EXE_FORMAT => Errno::ENOEXEC,
523
- ERROR_ITERATED_DATA_EXCEEDS_64k => Errno::ENOEXEC,
524
- ERROR_INVALID_MINALLOCSIZE => Errno::ENOEXEC,
525
- ERROR_DYNLINK_FROM_INVALID_RING => Errno::ENOEXEC,
526
- ERROR_IOPL_NOT_ENABLED => Errno::ENOEXEC,
527
- ERROR_INVALID_SEGDPL => Errno::ENOEXEC,
528
- ERROR_AUTODATASEG_EXCEEDS_64k => Errno::ENOEXEC,
529
- ERROR_RING2SEG_MUST_BE_MOVABLE => Errno::ENOEXEC,
530
- ERROR_RELOC_CHAIN_XEEDS_SEGLIM => Errno::ENOEXEC,
531
- ERROR_INFLOOP_IN_RELOC_CHAIN => Errno::ENOEXEC,
532
- ERROR_FILENAME_EXCED_RANGE => Errno::ENOENT,
533
- ERROR_NESTING_NOT_ALLOWED => Errno::EAGAIN,
534
- # ERROR_PIPE_LOCAL => Errno::EPIPE,
535
- ERROR_BAD_PIPE => Errno::EPIPE,
536
- ERROR_PIPE_BUSY => Errno::EAGAIN,
537
- ERROR_NO_DATA => Errno::EPIPE,
538
- ERROR_PIPE_NOT_CONNECTED => Errno::EPIPE,
539
- ERROR_OPERATION_ABORTED => Errno::EINTR,
540
- # ERROR_NOT_ENOUGH_QUOTA => Errno::ENOMEM,
541
- ERROR_MOD_NOT_FOUND => Errno::ENOENT,
542
- WSAEINTR => Errno::EINTR,
543
- WSAEBADF => Errno::EBADF,
544
- # WSAEACCES => Errno::EACCES,
545
- WSAEFAULT => Errno::EFAULT,
546
- WSAEINVAL => Errno::EINVAL,
547
- WSAEMFILE => Errno::EMFILE,
548
- WSAEWOULDBLOCK => Errno::EWOULDBLOCK,
549
- WSAEINPROGRESS => Errno::EINPROGRESS,
550
- WSAEALREADY => Errno::EALREADY,
551
- WSAENOTSOCK => Errno::ENOTSOCK,
552
- WSAEDESTADDRREQ => Errno::EDESTADDRREQ,
553
- WSAEMSGSIZE => Errno::EMSGSIZE,
554
- WSAEPROTOTYPE => Errno::EPROTOTYPE,
555
- WSAENOPROTOOPT => Errno::ENOPROTOOPT,
556
- WSAEPROTONOSUPPORT => Errno::EPROTONOSUPPORT,
557
- WSAESOCKTNOSUPPORT => Errno::ESOCKTNOSUPPORT,
558
- WSAEOPNOTSUPP => Errno::EOPNOTSUPP,
559
- WSAEPFNOSUPPORT => Errno::EPFNOSUPPORT,
560
- WSAEAFNOSUPPORT => Errno::EAFNOSUPPORT,
561
- WSAEADDRINUSE => Errno::EADDRINUSE,
562
- WSAEADDRNOTAVAIL => Errno::EADDRNOTAVAIL,
563
- WSAENETDOWN => Errno::ENETDOWN,
564
- WSAENETUNREACH => Errno::ENETUNREACH,
565
- WSAENETRESET => Errno::ENETRESET,
566
- WSAECONNABORTED => Errno::ECONNABORTED,
567
- WSAECONNRESET => Errno::ECONNRESET,
568
- WSAENOBUFS => Errno::ENOBUFS,
569
- WSAEISCONN => Errno::EISCONN,
570
- WSAENOTCONN => Errno::ENOTCONN,
571
- WSAESHUTDOWN => Errno::ESHUTDOWN,
572
- WSAETOOMANYREFS => Errno::ETOOMANYREFS,
573
- # WSAETIMEDOUT => Errno::ETIMEDOUT,
574
- WSAECONNREFUSED => Errno::ECONNREFUSED,
575
- WSAELOOP => Errno::ELOOP,
576
- WSAENAMETOOLONG => Errno::ENAMETOOLONG,
577
- WSAEHOSTDOWN => Errno::EHOSTDOWN,
578
- WSAEHOSTUNREACH => Errno::EHOSTUNREACH,
579
- # WSAEPROCLIM => Errno::EPROCLIM,
580
- # WSAENOTEMPTY => Errno::ENOTEMPTY,
581
- WSAEUSERS => Errno::EUSERS,
582
- WSAEDQUOT => Errno::EDQUOT,
583
- WSAESTALE => Errno::ESTALE,
584
- WSAEREMOTE => Errno::EREMOTE
585
- }
586
-
587
- module_function :create
588
- end