mixlib-shellout 2.1.0-universal-mingw32

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []