module Process
Constants
- ProcessInfo
- VALID_KEYS
Process.create
(key => value, …) =>ProcessInfo
This is a wrapper for the CreateProcess() function. It executes a process, returning a
ProcessInfo
struct. It accepts a hash as an argument. There are several primary keys:-
command_line (this or app_name must be present)
-
app_name (default: nil)
-
inherit (default: false)
-
process_inherit (default: false)
-
thread_inherit (default: false)
-
creation_flags (default: 0)
-
cwd (default: Dir.pwd)
-
startup_info (default: nil)
-
environment (default: nil)
-
close_handles (default: true)
-
with_logon (default: nil)
-
domain (default: nil)
-
password (default: nil, mandatory if with_logon)
Of these, the 'command_line' or 'app_name' must be specified or an error is raised. Both may be set individually, but 'command_line' should be preferred if only one of them is set because it does not (necessarily) require an explicit path or extension to work.
The 'domain' and 'password' options are only relevent in the context of 'with_logon'. If 'with_logon' is set, then the 'password' option is mandatory.
The startup_info key takes a hash. Its keys are attributes that are part of the StartupInfo struct, and are generally only meaningful for GUI or console processes. See the documentation on CreateProcess() and the StartupInfo struct on MSDN for more information.
-
desktop
-
title
-
x
-
y
-
x_size
-
y_size
-
x_count_chars
-
y_count_chars
-
fill_attribute
-
sw_flags
-
startf_flags
-
stdin
-
stdout
-
stderr
Note that the 'stdin', 'stdout' and 'stderr' options can be either Ruby IO objects or file descriptors (i.e. a fileno). However, StringIO objects are not currently supported. Unfortunately, setting these is not currently an option for JRuby.
If 'stdin', 'stdout' or 'stderr' are specified, then the
inherit
value is automatically set to true and the Process::STARTF_USESTDHANDLES flag is automatically OR'd to thestartf_flags
value.The
ProcessInfo
struct contains the following members:-
process_handle - The handle to the newly created process.
-
thread_handle - The handle to the primary thread of the process.
-
process_id -
Process
ID. -
thread_id - Thread ID.
If the 'close_handles' option is set to true (the default) then the process_handle and the thread_handle are automatically closed for you before the
ProcessInfo
struct is returned.If the 'with_logon' option is set, then the process runs the specified executable file in the security context of the specified credentials.
-
- VALID_SI_KEYS
Public Class Methods
# File lib/puppet/util/windows/monkey_patches/process.rb 121 def create(args) 122 # Validate that args is a Hash 123 validate_args(args) 124 125 initialize_defaults 126 127 # Validate the keys, and convert symbols and case to lowercase strings. 128 validate_keys(args) 129 130 # If the startup_info key is present, validate its subkeys 131 validate_startup_info if hash[:startup_info] 132 133 # validates that 'app_name' or 'command_line' is set 134 validate_command_line 135 136 if hash[:app_name] && !hash[:command_line] 137 hash[:command_line] = hash[:app_name] 138 hash[:app_name] = nil 139 end 140 141 # Setup stdin, stdout and stderr handlers 142 setup_std_handlers 143 144 if logon 145 create_process_with_logon 146 else 147 create_process 148 end 149 150 # Automatically close the process and thread handles in the 151 # PROCESS_INFORMATION struct unless explicitly told not to. 152 if hash[:close_handles] 153 FFI::WIN32.CloseHandle(procinfo[:hProcess]) 154 FFI::WIN32.CloseHandle(procinfo[:hThread]) 155 end 156 157 ProcessInfo.new( 158 procinfo[:hProcess], 159 procinfo[:hThread], 160 procinfo[:dwProcessId], 161 procinfo[:dwThreadId] 162 ) 163 end
Sets the priority class for the specified process id int
.
The kind
parameter is ignored but present for API compatibility. You can only retrieve process information, not process group or user information, so it is effectively always Process::PRIO_PROCESS.
Possible int_priority
values are:
-
Process::NORMAL_PRIORITY_CLASS
-
Process::IDLE_PRIORITY_CLASS
-
Process::HIGH_PRIORITY_CLASS
-
Process::REALTIME_PRIORITY_CLASS
-
Process::BELOW_NORMAL_PRIORITY_CLASS
-
Process::ABOVE_NORMAL_PRIORITY_CLASS
# File lib/puppet/util/windows/monkey_patches/process.rb 182 def setpriority(kind, int, int_priority) 183 raise TypeError unless kind.is_a?(Integer) 184 raise TypeError unless int.is_a?(Integer) 185 raise TypeError unless int_priority.is_a?(Integer) 186 187 int = Process.pid if int == 0 188 handle = OpenProcess(PROCESS_SET_INFORMATION, 0 , int) 189 190 if handle == 0 191 raise SystemCallError, FFI.errno, "OpenProcess" 192 end 193 194 begin 195 result = SetPriorityClass(handle, int_priority) 196 raise SystemCallError, FFI.errno, "SetPriorityClass" unless result 197 ensure 198 FFI::WIN32.CloseHandle(handle) 199 end 200 201 return 0 202 end
Private Class Methods
# File lib/puppet/util/windows/monkey_patches/process.rb 254 def app 255 wide_string(hash[:app_name]) 256 end
# File lib/puppet/util/windows/monkey_patches/process.rb 258 def cmd 259 wide_string(hash[:command_line]) 260 end
# File lib/puppet/util/windows/monkey_patches/process.rb 395 def create_process 396 inherit = hash[:inherit] ? 1 : 0 397 398 bool = CreateProcessW( 399 app, # App name 400 cmd, # Command line 401 process_security, # Process attributes 402 thread_security, # Thread attributes 403 inherit, # Inherit handles? 404 hash[:creation_flags], # Creation flags 405 env, # Environment 406 cwd, # Working directory 407 startinfo, # Startup Info 408 procinfo # Process Info 409 ) 410 411 raise SystemCallError.new('CreateProcess', FFI.errno) unless bool 412 end
# File lib/puppet/util/windows/monkey_patches/process.rb 373 def create_process_with_logon 374 raise ArgumentError, 'password must be specified if with_logon is used' unless password 375 376 hash[:creation_flags] |= CREATE_UNICODE_ENVIRONMENT 377 378 bool = CreateProcessWithLogonW( 379 logon, # User 380 domain, # Domain 381 password, # Password 382 LOGON_WITH_PROFILE, # Logon flags 383 app, # App name 384 cmd, # Command line 385 hash[:creation_flags], # Creation flags 386 env, # Environment 387 cwd, # Working directory 388 startinfo, # Startup Info 389 procinfo # Process Info 390 ) 391 392 raise SystemCallError.new('CreateProcessWithLogonW', FFI.errno) unless bool 393 end
# File lib/puppet/util/windows/monkey_patches/process.rb 262 def cwd 263 wide_string(hash[:cwd]) 264 end
# File lib/puppet/util/windows/monkey_patches/process.rb 274 def domain 275 wide_string(hash[:domain]) 276 end
# File lib/puppet/util/windows/monkey_patches/process.rb 278 def env 279 env = hash[:environment] 280 return unless env 281 282 env = env.split(File::PATH_SEPARATOR) unless env.respond_to?(:join) 283 env = env.map { |e| e + 0.chr }.join('') + 0.chr 284 env = wide_string(env) if hash[:with_logon] 285 env 286 end
# File lib/puppet/util/windows/monkey_patches/process.rb 246 def hash 247 @hash ||= {} 248 end
# File lib/puppet/util/windows/monkey_patches/process.rb 206 def initialize_defaults 207 @hash = { 208 app_name: nil, 209 creation_flags: 0, 210 close_handles: true 211 } 212 @si_hash = nil 213 @procinfo = nil 214 end
# File lib/puppet/util/windows/monkey_patches/process.rb 270 def logon 271 wide_string(hash[:with_logon]) 272 end
# File lib/puppet/util/windows/monkey_patches/process.rb 266 def password 267 wide_string(hash[:password]) 268 end
# File lib/puppet/util/windows/monkey_patches/process.rb 288 def process_security 289 return unless hash[:process_inherit] 290 291 process_security = SECURITY_ATTRIBUTES.new 292 process_security[:nLength] = SECURITY_ATTRIBUTES.size 293 process_security[:bInheritHandle] = 1 294 process_security 295 end
# File lib/puppet/util/windows/monkey_patches/process.rb 242 def procinfo 243 @procinfo ||= PROCESS_INFORMATION.new 244 end
Automatically handle stdin, stdout and stderr as either IO objects or file descriptors. This won't work for StringIO, however. It also will not work on JRuby because of the way it handles internal file descriptors.
# File lib/puppet/util/windows/monkey_patches/process.rb 310 def setup_std_handlers 311 %i[stdin stdout stderr].each do |io| 312 next unless si_hash[io] 313 314 handle = if si_hash[io].respond_to?(:fileno) 315 get_osfhandle(si_hash[io].fileno) 316 else 317 get_osfhandle(si_hash[io]) 318 end 319 320 if handle == INVALID_HANDLE_VALUE 321 ptr = FFI::MemoryPointer.new(:int) 322 323 errno = if get_errno(ptr).zero? 324 ptr.read_int 325 else 326 FFI.errno 327 end 328 329 raise SystemCallError.new('get_osfhandle', errno) 330 end 331 332 # Most implementations of Ruby on Windows create inheritable 333 # handles by default, but some do not. RF bug #26988. 334 bool = SetHandleInformation( 335 handle, 336 HANDLE_FLAG_INHERIT, 337 HANDLE_FLAG_INHERIT 338 ) 339 340 raise SystemCallError.new('SetHandleInformation', FFI.errno) unless bool 341 342 si_hash[io] = handle 343 si_hash[:startf_flags] ||= 0 344 si_hash[:startf_flags] |= STARTF_USESTDHANDLES 345 hash[:inherit] = true 346 end 347 end
# File lib/puppet/util/windows/monkey_patches/process.rb 250 def si_hash 251 @si_hash ||= {} 252 end
# File lib/puppet/util/windows/monkey_patches/process.rb 349 def startinfo 350 startinfo = STARTUPINFO.new 351 352 return startinfo if si_hash.empty? 353 354 startinfo[:cb] = startinfo.size 355 startinfo[:lpDesktop] = si_hash[:desktop] if si_hash[:desktop] 356 startinfo[:lpTitle] = si_hash[:title] if si_hash[:title] 357 startinfo[:dwX] = si_hash[:x] if si_hash[:x] 358 startinfo[:dwY] = si_hash[:y] if si_hash[:y] 359 startinfo[:dwXSize] = si_hash[:x_size] if si_hash[:x_size] 360 startinfo[:dwYSize] = si_hash[:y_size] if si_hash[:y_size] 361 startinfo[:dwXCountChars] = si_hash[:x_count_chars] if si_hash[:x_count_chars] 362 startinfo[:dwYCountChars] = si_hash[:y_count_chars] if si_hash[:y_count_chars] 363 startinfo[:dwFillAttribute] = si_hash[:fill_attribute] if si_hash[:fill_attribute] 364 startinfo[:dwFlags] = si_hash[:startf_flags] if si_hash[:startf_flags] 365 startinfo[:wShowWindow] = si_hash[:sw_flags] if si_hash[:sw_flags] 366 startinfo[:cbReserved2] = 0 367 startinfo[:hStdInput] = si_hash[:stdin] if si_hash[:stdin] 368 startinfo[:hStdOutput] = si_hash[:stdout] if si_hash[:stdout] 369 startinfo[:hStdError] = si_hash[:stderr] if si_hash[:stderr] 370 startinfo 371 end
# File lib/puppet/util/windows/monkey_patches/process.rb 297 def thread_security 298 return unless hash[:thread_inherit] 299 300 thread_security = SECURITY_ATTRIBUTES.new 301 thread_security[:nLength] = SECURITY_ATTRIBUTES.size 302 thread_security[:bInheritHandle] = 1 303 thread_security 304 end
# File lib/puppet/util/windows/monkey_patches/process.rb 216 def validate_args(args) 217 raise TypeError, 'hash keyword arguments expected' unless args.is_a?(Hash) 218 end
# File lib/puppet/util/windows/monkey_patches/process.rb 238 def validate_command_line 239 raise ArgumentError, 'command_line or app_name must be specified' unless hash[:app_name] || hash[:command_line] 240 end
# File lib/puppet/util/windows/monkey_patches/process.rb 220 def validate_keys(args) 221 args.each do |key, val| 222 key = key.to_s.to_sym 223 raise ArgumentError, "invalid key '#{key}'" unless VALID_KEYS.include?(key) 224 225 hash[key] = val 226 end 227 end
# File lib/puppet/util/windows/monkey_patches/process.rb 229 def validate_startup_info 230 hash[:startup_info].each do |key, val| 231 key = key.to_s.to_sym 232 raise ArgumentError, "invalid startup_info key '#{key}'" unless VALID_SI_KEYS.include?(key) 233 234 si_hash[key] = val 235 end 236 end