mixlib-shellout 2.1.0-universal-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,372 @@
1
+ #--
2
+ # Author:: Daniel DeLeo (<dan@opscode.com>)
3
+ # Author:: John Keiser (<jkeiser@opscode.com>)
4
+ # Copyright:: Copyright (c) 2011, 2012 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
+
22
+ # Add new constants for Logon
23
+ module Process::Constants
24
+ LOGON32_LOGON_INTERACTIVE = 0x00000002
25
+ LOGON32_PROVIDER_DEFAULT = 0x00000000
26
+ UOI_NAME = 0x00000002
27
+ end
28
+
29
+ # Define the functions needed to check with Service windows station
30
+ module Process::Functions
31
+ module FFI::Library
32
+ # Wrapper method for attach_function + private
33
+ def attach_pfunc(*args)
34
+ attach_function(*args)
35
+ private args[0]
36
+ end
37
+ end
38
+
39
+ extend FFI::Library
40
+
41
+ ffi_lib :advapi32
42
+
43
+ attach_pfunc :LogonUserW,
44
+ [:buffer_in, :buffer_in, :buffer_in, :ulong, :ulong, :pointer], :bool
45
+
46
+ attach_pfunc :CreateProcessAsUserW,
47
+ [:ulong, :buffer_in, :buffer_in, :pointer, :pointer, :bool,
48
+ :ulong, :buffer_in, :buffer_in, :pointer, :pointer], :bool
49
+
50
+ ffi_lib :user32
51
+
52
+ attach_pfunc :GetProcessWindowStation,
53
+ [], :ulong
54
+
55
+ attach_pfunc :GetUserObjectInformationA,
56
+ [:ulong, :uint, :buffer_out, :ulong, :pointer], :bool
57
+ end
58
+
59
+ # Override Process.create to check for running in the Service window station and doing
60
+ # a full logon with LogonUser, instead of a CreateProcessWithLogon
61
+ module Process
62
+ include Process::Constants
63
+ include Process::Structs
64
+
65
+ def create(args)
66
+ unless args.kind_of?(Hash)
67
+ raise TypeError, 'hash keyword arguments expected'
68
+ end
69
+
70
+ valid_keys = %w[
71
+ app_name command_line inherit creation_flags cwd environment
72
+ startup_info thread_inherit process_inherit close_handles with_logon
73
+ domain password
74
+ ]
75
+
76
+ valid_si_keys = %w[
77
+ startf_flags desktop title x y x_size y_size x_count_chars
78
+ y_count_chars fill_attribute sw_flags stdin stdout stderr
79
+ ]
80
+
81
+ # Set default values
82
+ hash = {
83
+ 'app_name' => nil,
84
+ 'creation_flags' => 0,
85
+ 'close_handles' => true
86
+ }
87
+
88
+ # Validate the keys, and convert symbols and case to lowercase strings.
89
+ args.each{ |key, val|
90
+ key = key.to_s.downcase
91
+ unless valid_keys.include?(key)
92
+ raise ArgumentError, "invalid key '#{key}'"
93
+ end
94
+ hash[key] = val
95
+ }
96
+
97
+ si_hash = {}
98
+
99
+ # If the startup_info key is present, validate its subkeys
100
+ if hash['startup_info']
101
+ hash['startup_info'].each{ |key, val|
102
+ key = key.to_s.downcase
103
+ unless valid_si_keys.include?(key)
104
+ raise ArgumentError, "invalid startup_info key '#{key}'"
105
+ end
106
+ si_hash[key] = val
107
+ }
108
+ end
109
+
110
+ # The +command_line+ key is mandatory unless the +app_name+ key
111
+ # is specified.
112
+ unless hash['command_line']
113
+ if hash['app_name']
114
+ hash['command_line'] = hash['app_name']
115
+ hash['app_name'] = nil
116
+ else
117
+ raise ArgumentError, 'command_line or app_name must be specified'
118
+ end
119
+ end
120
+
121
+ env = nil
122
+
123
+ # The env string should be passed as a string of ';' separated paths.
124
+ if hash['environment']
125
+ env = hash['environment']
126
+
127
+ unless env.respond_to?(:join)
128
+ env = hash['environment'].split(File::PATH_SEPARATOR)
129
+ end
130
+
131
+ env = env.map{ |e| e + 0.chr }.join('') + 0.chr
132
+ env.to_wide_string! if hash['with_logon']
133
+ end
134
+
135
+ # Process SECURITY_ATTRIBUTE structure
136
+ process_security = nil
137
+
138
+ if hash['process_inherit']
139
+ process_security = SECURITY_ATTRIBUTES.new
140
+ process_security[:nLength] = 12
141
+ process_security[:bInheritHandle] = true
142
+ end
143
+
144
+ # Thread SECURITY_ATTRIBUTE structure
145
+ thread_security = nil
146
+
147
+ if hash['thread_inherit']
148
+ thread_security = SECURITY_ATTRIBUTES.new
149
+ thread_security[:nLength] = 12
150
+ thread_security[:bInheritHandle] = true
151
+ end
152
+
153
+ # Automatically handle stdin, stdout and stderr as either IO objects
154
+ # or file descriptors. This won't work for StringIO, however. It also
155
+ # will not work on JRuby because of the way it handles internal file
156
+ # descriptors.
157
+ #
158
+ ['stdin', 'stdout', 'stderr'].each{ |io|
159
+ if si_hash[io]
160
+ if si_hash[io].respond_to?(:fileno)
161
+ handle = get_osfhandle(si_hash[io].fileno)
162
+ else
163
+ handle = get_osfhandle(si_hash[io])
164
+ end
165
+
166
+ if handle == INVALID_HANDLE_VALUE
167
+ ptr = FFI::MemoryPointer.new(:int)
168
+
169
+ if windows_version >= 6 && get_errno(ptr) == 0
170
+ errno = ptr.read_int
171
+ else
172
+ errno = FFI.errno
173
+ end
174
+
175
+ raise SystemCallError.new("get_osfhandle", errno)
176
+ end
177
+
178
+ # Most implementations of Ruby on Windows create inheritable
179
+ # handles by default, but some do not. RF bug #26988.
180
+ bool = SetHandleInformation(
181
+ handle,
182
+ HANDLE_FLAG_INHERIT,
183
+ HANDLE_FLAG_INHERIT
184
+ )
185
+
186
+ raise SystemCallError.new("SetHandleInformation", FFI.errno) unless bool
187
+
188
+ si_hash[io] = handle
189
+ si_hash['startf_flags'] ||= 0
190
+ si_hash['startf_flags'] |= STARTF_USESTDHANDLES
191
+ hash['inherit'] = true
192
+ end
193
+ }
194
+
195
+ procinfo = PROCESS_INFORMATION.new
196
+ startinfo = STARTUPINFO.new
197
+
198
+ unless si_hash.empty?
199
+ startinfo[:cb] = startinfo.size
200
+ startinfo[:lpDesktop] = si_hash['desktop'] if si_hash['desktop']
201
+ startinfo[:lpTitle] = si_hash['title'] if si_hash['title']
202
+ startinfo[:dwX] = si_hash['x'] if si_hash['x']
203
+ startinfo[:dwY] = si_hash['y'] if si_hash['y']
204
+ startinfo[:dwXSize] = si_hash['x_size'] if si_hash['x_size']
205
+ startinfo[:dwYSize] = si_hash['y_size'] if si_hash['y_size']
206
+ startinfo[:dwXCountChars] = si_hash['x_count_chars'] if si_hash['x_count_chars']
207
+ startinfo[:dwYCountChars] = si_hash['y_count_chars'] if si_hash['y_count_chars']
208
+ startinfo[:dwFillAttribute] = si_hash['fill_attribute'] if si_hash['fill_attribute']
209
+ startinfo[:dwFlags] = si_hash['startf_flags'] if si_hash['startf_flags']
210
+ startinfo[:wShowWindow] = si_hash['sw_flags'] if si_hash['sw_flags']
211
+ startinfo[:cbReserved2] = 0
212
+ startinfo[:hStdInput] = si_hash['stdin'] if si_hash['stdin']
213
+ startinfo[:hStdOutput] = si_hash['stdout'] if si_hash['stdout']
214
+ startinfo[:hStdError] = si_hash['stderr'] if si_hash['stderr']
215
+ end
216
+
217
+ app = nil
218
+ cmd = nil
219
+
220
+ # Convert strings to wide character strings if present
221
+ if hash['app_name']
222
+ app = hash['app_name'].to_wide_string
223
+ end
224
+
225
+ if hash['command_line']
226
+ cmd = hash['command_line'].to_wide_string
227
+ end
228
+
229
+ if hash['cwd']
230
+ cwd = hash['cwd'].to_wide_string
231
+ end
232
+
233
+ inherit = hash['inherit'] || false
234
+
235
+ if hash['with_logon']
236
+ logon = hash['with_logon'].to_wide_string
237
+
238
+ if hash['password']
239
+ passwd = hash['password'].to_wide_string
240
+ else
241
+ raise ArgumentError, 'password must be specified if with_logon is used'
242
+ end
243
+
244
+ if hash['domain']
245
+ domain = hash['domain'].to_wide_string
246
+ end
247
+
248
+ hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT
249
+
250
+ winsta_name = FFI::MemoryPointer.new(:char, 256)
251
+ return_size = FFI::MemoryPointer.new(:ulong)
252
+
253
+ bool = GetUserObjectInformationA(
254
+ GetProcessWindowStation(), # Window station handle
255
+ UOI_NAME, # Information to get
256
+ winsta_name, # Buffer to receive information
257
+ winsta_name.size, # Size of buffer
258
+ return_size # Size filled into buffer
259
+ )
260
+
261
+ unless bool
262
+ raise SystemCallError.new("GetUserObjectInformationA", FFI.errno)
263
+ end
264
+
265
+ winsta_name = winsta_name.read_string(return_size.read_ulong)
266
+
267
+ # If running in the service windows station must do a log on to get
268
+ # to the interactive desktop. Running process user account must have
269
+ # the 'Replace a process level token' permission. This is necessary as
270
+ # the logon (which happens with CreateProcessWithLogon) must have an
271
+ # interactive windows station to attach to, which is created with the
272
+ # LogonUser cann with the LOGON32_LOGON_INTERACTIVE flag.
273
+ if winsta_name =~ /^Service-0x0-.*$/i
274
+ token = FFI::MemoryPointer.new(:ulong)
275
+
276
+ bool = LogonUserW(
277
+ logon, # User
278
+ domain, # Domain
279
+ passwd, # Password
280
+ LOGON32_LOGON_INTERACTIVE, # Logon Type
281
+ LOGON32_PROVIDER_DEFAULT, # Logon Provider
282
+ token # User token handle
283
+ )
284
+
285
+ unless bool
286
+ raise SystemCallError.new("LogonUserW", FFI.errno)
287
+ end
288
+
289
+ token = token.read_ulong
290
+
291
+ begin
292
+ bool = CreateProcessAsUserW(
293
+ token, # User token handle
294
+ app, # App name
295
+ cmd, # Command line
296
+ process_security, # Process attributes
297
+ thread_security, # Thread attributes
298
+ inherit, # Inherit handles
299
+ hash['creation_flags'], # Creation Flags
300
+ env, # Environment
301
+ cwd, # Working directory
302
+ startinfo, # Startup Info
303
+ procinfo # Process Info
304
+ )
305
+ ensure
306
+ CloseHandle(token)
307
+ end
308
+
309
+ unless bool
310
+ raise SystemCallError.new("CreateProcessAsUserW (You must hold the 'Replace a process level token' permission)", FFI.errno)
311
+ end
312
+ else
313
+ bool = CreateProcessWithLogonW(
314
+ logon, # User
315
+ domain, # Domain
316
+ passwd, # Password
317
+ LOGON_WITH_PROFILE, # Logon flags
318
+ app, # App name
319
+ cmd, # Command line
320
+ hash['creation_flags'], # Creation flags
321
+ env, # Environment
322
+ cwd, # Working directory
323
+ startinfo, # Startup Info
324
+ procinfo # Process Info
325
+ )
326
+ end
327
+
328
+ unless bool
329
+ raise SystemCallError.new("CreateProcessWithLogonW", FFI.errno)
330
+ end
331
+ else
332
+ bool = CreateProcessW(
333
+ app, # App name
334
+ cmd, # Command line
335
+ process_security, # Process attributes
336
+ thread_security, # Thread attributes
337
+ inherit, # Inherit handles?
338
+ hash['creation_flags'], # Creation flags
339
+ env, # Environment
340
+ cwd, # Working directory
341
+ startinfo, # Startup Info
342
+ procinfo # Process Info
343
+ )
344
+
345
+ unless bool
346
+ raise SystemCallError.new("CreateProcessW", FFI.errno)
347
+ end
348
+ end
349
+
350
+ # Automatically close the process and thread handles in the
351
+ # PROCESS_INFORMATION struct unless explicitly told not to.
352
+ if hash['close_handles']
353
+ CloseHandle(procinfo[:hProcess]) if procinfo[:hProcess]
354
+ CloseHandle(procinfo[:hThread]) if procinfo[:hThread]
355
+
356
+ # Set fields to nil so callers don't attempt to close the handle
357
+ # which can result in the wrong handle being closed or an
358
+ # exception in some circumstances
359
+ procinfo[:hProcess] = nil
360
+ procinfo[:hThread] = nil
361
+ end
362
+
363
+ ProcessInfo.new(
364
+ procinfo[:hProcess],
365
+ procinfo[:hThread],
366
+ procinfo[:dwProcessId],
367
+ procinfo[:dwThreadId]
368
+ )
369
+ end
370
+
371
+ module_function :create
372
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mixlib-shellout
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.1.0
5
+ platform: universal-mingw32
6
+ authors:
7
+ - Opscode
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: win32-process
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.7.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.7.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: windows-pr
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.2.4
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.2.4
55
+ description: Run external commands on Unix or Windows
56
+ email: info@opscode.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files:
60
+ - README.md
61
+ - LICENSE
62
+ files:
63
+ - LICENSE
64
+ - README.md
65
+ - lib/mixlib/shellout.rb
66
+ - lib/mixlib/shellout/exceptions.rb
67
+ - lib/mixlib/shellout/unix.rb
68
+ - lib/mixlib/shellout/version.rb
69
+ - lib/mixlib/shellout/windows.rb
70
+ - lib/mixlib/shellout/windows/core_ext.rb
71
+ homepage: http://wiki.opscode.com/
72
+ licenses: []
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 1.9.3
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.4.6
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Run external commands on Unix or Windows
94
+ test_files: []