module Process
def kill(signal, *pids)
Process.kill(1, 12345, :exit_proc => 'ExitProcess', :module => 'kernel32')
Example:
block until the process is actually signaled.
you may specify Process::INFINITE, and your code will
signaled and instead returns immediately. Alternatively,
then the process does not wait if the process is not
actually die. The default is 5ms. If you specify 0 here
:wait_time => The time, in milliseconds, to wait for the process to
The default is 'kernel32'.
:dll_module => The name of the .dll (or .exe) that contains :exit_proc.
is used. The default is 'ExitProcess'.
:exit_proc => The name of the exit function called when signal 1 or 4-8
Possible options for signals 1 and 4-8.
behaves.
allow finer control over how that process is killed and how your program
When using signals 1 or 4-8 you may specify additional options that
same as the default implementation.
chance to do internal cleanup before being killed. Signal 0 behaves the
Signals 1 and 4-8 kill the process more nicely, giving the process a
process harshly, given that process no chance to do any internal cleanup.
a ctrl-c or ctrl-break event, respectively. Signal 9 terminates the
Internally, signals 2 and 3 will generate a console control event, using
behavior.
it kills processes, but this version also gives you finer control over
implementation of Process.kill. The differences mainly reside in the way
Kill a given process with a specific signal. This overrides the default
def kill(signal, *pids) # Match the spec, require at least 2 arguments if pids.length == 0 raise ArgumentError, "wrong number of arguments (1 for at least 2)" end # Match the spec, signal may not be less than zero if numeric if signal.is_a?(Numeric) && signal < 0 # EINVAL raise SystemCallError.new(22) end # Match the spec, signal must be a numeric, string or symbol unless signal.is_a?(String) || signal.is_a?(Numeric) || signal.is_a?(Symbol) raise ArgumentError, "bad signal type #{signal.class}" end # Match the spec, making an exception for BRK/SIGBRK, if the signal name is invalid. # Older versions of JRuby did not include KILL, so we've made an explicit exception # for that here, too. if signal.is_a?(String) || signal.is_a?(Symbol) signal = signal.to_s.sub("SIG", "") unless Signal.list.keys.include?(signal) || %w{KILL BRK}.include?(signal) raise ArgumentError, "unsupported name '#{signal}'" end end # If the last argument is a hash, pop it and assume it's a hash of options if pids.last.is_a?(Hash) hash = pids.pop opts = {} valid = %w{exit_proc dll_module wait_time} hash.each { |k, v| k = k.to_s.downcase unless valid.include?(k) raise ArgumentError, "invalid option '#{k}'" end opts[k] = v } exit_proc = opts["exit_proc"] || "ExitProcess" dll_module = opts["dll_module"] || "kernel32" wait_time = opts["wait_time"] || 5 else wait_time = 5 exit_proc = "ExitProcess" dll_module = "kernel32" end count = 0 pids.each { |pid| raise TypeError unless pid.is_a?(Numeric) # Match spec, pid must be a number raise SystemCallError.new(22) if pid < 0 # Match spec, EINVAL if pid less than zero sigint = [Signal.list["INT"], "INT", "SIGINT", :INT, :SIGINT, 2] # Match the spec if pid == 0 && !sigint.include?(signal) raise SystemCallError.new(22) end if signal == 0 access = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ elsif signal == 9 access = PROCESS_TERMINATE else access = PROCESS_ALL_ACCESS end begin handle = OpenProcess(access, 0, pid) if signal != 0 && handle == 0 raise SystemCallError, FFI.errno, "OpenProcess" end case signal when 0 if handle != 0 count += 1 else if FFI.errno == ERROR_ACCESS_DENIED count += 1 else raise SystemCallError.new(3) # ESRCH end end when Signal.list["INT"], "INT", "SIGINT", :INT, :SIGINT, 2 if GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid) count += 1 else raise SystemCallError.new("GenerateConsoleCtrlEvent", FFI.errno) end when Signal.list["BRK"], "BRK", "SIGBRK", :BRK, :SIGBRK, 3 if GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid) count += 1 else raise SystemCallError.new("GenerateConsoleCtrlEvent", FFI.errno) end when Signal.list["KILL"], "KILL", "SIGKILL", :KILL, :SIGKILL, 9 if TerminateProcess(handle, pid) count += 1 else raise SystemCallError.new("TerminateProcess", FFI.errno) end else thread_id = FFI::MemoryPointer.new(:ulong) mod = GetModuleHandle(dll_module) if mod == 0 raise SystemCallError.new("GetModuleHandle: '#{dll_module}'", FFI.errno) end proc_addr = GetProcAddress(mod, exit_proc) if proc_addr == 0 raise SystemCallError.new("GetProcAddress: '#{exit_proc}'", FFI.errno) end thread = CreateRemoteThread(handle, nil, 0, proc_addr, nil, 0, thread_id) if thread > 0 WaitForSingleObject(thread, wait_time) count += 1 else raise SystemCallError.new("CreateRemoteThread", FFI.errno) end end ensure CloseHandle(handle) if handle end } count end