mixlib-shellout 2.2.5 → 2.2.6

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