mixlib-shellout 2.2.0-universal-mingw32 → 2.2.1-universal-mingw32

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