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