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