File: //usr/lib/ruby/vendor_ruby/childprocess/windows/lib.rb
module ChildProcess
module Windows
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000
PROCESS_ALL_ACCESS = 0x1F0FFF
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010
PROCESS_STILL_ACTIVE = 259
INFINITE = 0xFFFFFFFF
WIN_SIGINT = 2
WIN_SIGBREAK = 3
WIN_SIGKILL = 9
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
CREATE_BREAKAWAY_FROM_JOB = 0x01000000
DETACHED_PROCESS = 0x00000008
STARTF_USESTDHANDLES = 0x00000100
INVALID_HANDLE_VALUE = -1
HANDLE_FLAG_INHERIT = 0x00000001
DUPLICATE_SAME_ACCESS = 0x00000002
CREATE_UNICODE_ENVIRONMENT = 0x00000400
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000
JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
JOB_OBJECT_EXTENDED_LIMIT_INFORMATION = 9
JOB_OBJECT_BASIC_LIMIT_INFORMATION = 2
module Lib
enum :wait_status, [
:wait_object_0, 0,
:wait_timeout, 0x102,
:wait_abandoned, 0x80,
:wait_failed, 0xFFFFFFFF
]
#
# BOOL WINAPI CreateProcess(
# __in_opt LPCTSTR lpApplicationName,
# __inout_opt LPTSTR lpCommandLine,
# __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
# __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
# __in BOOL bInheritHandles,
# __in DWORD dwCreationFlags,
# __in_opt LPVOID lpEnvironment,
# __in_opt LPCTSTR lpCurrentDirectory,
# __in LPSTARTUPINFO lpStartupInfo,
# __out LPPROCESS_INFORMATION lpProcessInformation
# );
#
attach_function :create_process, :CreateProcessW, [
:pointer,
:buffer_inout,
:pointer,
:pointer,
:bool,
:ulong,
:pointer,
:pointer,
:pointer,
:pointer], :bool
#
# DWORD WINAPI FormatMessage(
# __in DWORD dwFlags,
# __in_opt LPCVOID lpSource,
# __in DWORD dwMessageId,
# __in DWORD dwLanguageId,
# __out LPTSTR lpBuffer,
# __in DWORD nSize,
# __in_opt va_list *Arguments
# );
#
attach_function :format_message, :FormatMessageA, [
:ulong,
:pointer,
:ulong,
:ulong,
:pointer,
:ulong,
:pointer], :ulong
attach_function :close_handle, :CloseHandle, [:pointer], :bool
#
# HANDLE WINAPI OpenProcess(
# __in DWORD dwDesiredAccess,
# __in BOOL bInheritHandle,
# __in DWORD dwProcessId
# );
#
attach_function :open_process, :OpenProcess, [:ulong, :bool, :ulong], :pointer
#
# HANDLE WINAPI CreateJobObject(
# _In_opt_ LPSECURITY_ATTRIBUTES lpJobAttributes,
# _In_opt_ LPCTSTR lpName
# );
#
attach_function :create_job_object, :CreateJobObjectA, [:pointer, :pointer], :pointer
#
# BOOL WINAPI AssignProcessToJobObject(
# _In_ HANDLE hJob,
# _In_ HANDLE hProcess
# );
attach_function :assign_process_to_job_object, :AssignProcessToJobObject, [:pointer, :pointer], :bool
#
# BOOL WINAPI SetInformationJobObject(
# _In_ HANDLE hJob,
# _In_ JOBOBJECTINFOCLASS JobObjectInfoClass,
# _In_ LPVOID lpJobObjectInfo,
# _In_ DWORD cbJobObjectInfoLength
# );
#
attach_function :set_information_job_object, :SetInformationJobObject, [:pointer, :int, :pointer, :ulong], :bool
#
#
# DWORD WINAPI WaitForSingleObject(
# __in HANDLE hHandle,
# __in DWORD dwMilliseconds
# );
#
attach_function :wait_for_single_object, :WaitForSingleObject, [:pointer, :ulong], :wait_status, :blocking => true
#
# BOOL WINAPI GetExitCodeProcess(
# __in HANDLE hProcess,
# __out LPDWORD lpExitCode
# );
#
attach_function :get_exit_code, :GetExitCodeProcess, [:pointer, :pointer], :bool
#
# BOOL WINAPI GenerateConsoleCtrlEvent(
# __in DWORD dwCtrlEvent,
# __in DWORD dwProcessGroupId
# );
#
attach_function :generate_console_ctrl_event, :GenerateConsoleCtrlEvent, [:ulong, :ulong], :bool
#
# BOOL WINAPI TerminateProcess(
# __in HANDLE hProcess,
# __in UINT uExitCode
# );
#
attach_function :terminate_process, :TerminateProcess, [:pointer, :uint], :bool
#
# intptr_t _get_osfhandle(
# int fd
# );
#
attach_function :get_osfhandle, :_get_osfhandle, [:int], :intptr_t
#
# int _open_osfhandle (
# intptr_t osfhandle,
# int flags
# );
#
attach_function :open_osfhandle, :_open_osfhandle, [:pointer, :int], :int
# BOOL WINAPI SetHandleInformation(
# __in HANDLE hObject,
# __in DWORD dwMask,
# __in DWORD dwFlags
# );
attach_function :set_handle_information, :SetHandleInformation, [:pointer, :ulong, :ulong], :bool
# BOOL WINAPI GetHandleInformation(
# __in HANDLE hObject,
# __out LPDWORD lpdwFlags
# );
attach_function :get_handle_information, :GetHandleInformation, [:pointer, :pointer], :bool
# BOOL WINAPI CreatePipe(
# __out PHANDLE hReadPipe,
# __out PHANDLE hWritePipe,
# __in_opt LPSECURITY_ATTRIBUTES lpPipeAttributes,
# __in DWORD nSize
# );
attach_function :create_pipe, :CreatePipe, [:pointer, :pointer, :pointer, :ulong], :bool
#
# HANDLE WINAPI GetCurrentProcess(void);
#
attach_function :current_process, :GetCurrentProcess, [], :pointer
#
# BOOL WINAPI DuplicateHandle(
# __in HANDLE hSourceProcessHandle,
# __in HANDLE hSourceHandle,
# __in HANDLE hTargetProcessHandle,
# __out LPHANDLE lpTargetHandle,
# __in DWORD dwDesiredAccess,
# __in BOOL bInheritHandle,
# __in DWORD dwOptions
# );
#
attach_function :_duplicate_handle, :DuplicateHandle, [
:pointer,
:pointer,
:pointer,
:pointer,
:ulong,
:bool,
:ulong
], :bool
class << self
def kill(signal, *pids)
case signal
when 'SIGINT', 'INT', :SIGINT, :INT
signal = WIN_SIGINT
when 'SIGBRK', 'BRK', :SIGBREAK, :BRK
signal = WIN_SIGBREAK
when 'SIGKILL', 'KILL', :SIGKILL, :KILL
signal = WIN_SIGKILL
when 0..9
# Do nothing
else
raise Error, "invalid signal #{signal.inspect}"
end
pids.map { |pid| pid if Lib.send_signal(signal, pid) }.compact
end
def waitpid(pid, flags = 0)
wait_for_pid(pid, no_hang?(flags))
end
def waitpid2(pid, flags = 0)
code = wait_for_pid(pid, no_hang?(flags))
[pid, code] if code
end
def dont_inherit(file)
unless file.respond_to?(:fileno)
raise ArgumentError, "expected #{file.inspect} to respond to :fileno"
end
set_handle_inheritance(handle_for(file.fileno), false)
end
def last_error_message
errnum = FFI.errno
buf = FFI::MemoryPointer.new :char, 512
size = format_message(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
nil, errnum, 0, buf, buf.size, nil
)
str = buf.read_string(size).strip
if errnum == 0
"Unknown error (Windows says #{str.inspect}, but it did not.)"
else
"#{str} (#{errnum})"
end
end
def each_child_of(pid, &blk)
raise NotImplementedError
# http://stackoverflow.com/questions/1173342/terminate-a-process-tree-c-for-windows?rq=1
# for each process entry
# if pe.th32ParentProcessID == pid
# Handle.open(pe.pe.th32ProcessId, &blk)
# end
#
end
def handle_for(fd_or_io)
if fd_or_io.kind_of?(IO) || fd_or_io.respond_to?(:fileno)
if ChildProcess.jruby?
handle = ChildProcess::JRuby.windows_handle_for(fd_or_io)
else
handle = get_osfhandle(fd_or_io.fileno)
end
elsif fd_or_io.kind_of?(Integer)
handle = get_osfhandle(fd_or_io)
elsif fd_or_io.respond_to?(:to_io)
io = fd_or_io.to_io
unless io.kind_of?(IO)
raise TypeError, "expected #to_io to return an instance of IO"
end
handle = get_osfhandle(io.fileno)
else
raise TypeError, "invalid type: #{fd_or_io.inspect}"
end
if handle == INVALID_HANDLE_VALUE
raise Error, last_error_message
end
FFI::Pointer.new handle
end
def io_for(handle, flags = File::RDONLY)
fd = open_osfhandle(handle, flags)
if fd == -1
raise Error, last_error_message
end
FFI::IO.for_fd fd, flags
end
def duplicate_handle(handle)
dup = FFI::MemoryPointer.new(:pointer)
proc = current_process
ok = Lib._duplicate_handle(
proc,
handle,
proc,
dup,
0,
false,
DUPLICATE_SAME_ACCESS
)
check_error ok
dup.read_pointer
ensure
close_handle proc
end
def set_handle_inheritance(handle, bool)
status = set_handle_information(
handle,
HANDLE_FLAG_INHERIT,
bool ? HANDLE_FLAG_INHERIT : 0
)
check_error status
end
def get_handle_inheritance(handle)
flags = FFI::MemoryPointer.new(:uint)
status = get_handle_information(
handle,
flags
)
check_error status
flags.read_uint
end
def check_error(bool)
bool or raise Error, last_error_message
end
def alive?(pid)
handle = Lib.open_process(PROCESS_ALL_ACCESS, false, pid)
if handle.null?
false
else
ptr = FFI::MemoryPointer.new :ulong
Lib.check_error Lib.get_exit_code(handle, ptr)
ptr.read_ulong == PROCESS_STILL_ACTIVE
end
end
def no_hang?(flags)
(flags & Process::WNOHANG) == Process::WNOHANG
end
def wait_for_pid(pid, no_hang)
code = Handle.open(pid) { |handle|
handle.wait unless no_hang
handle.exit_code
}
code if code != PROCESS_STILL_ACTIVE
end
end
end # Lib
end # Windows
end # ChildProcess