Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 86 additions & 17 deletions src/org/jruby/RubyProcess.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
***** END LICENSE BLOCK *****/
package org.jruby;

import jnr.ffi.*;
import jnr.constants.platform.Signal;
import jnr.constants.platform.Sysconf;
import jnr.posix.Times;
Expand Down Expand Up @@ -874,7 +875,15 @@ private static int parseSignalString(Ruby runtime, String value) {
throw runtime.newArgumentError("unsupported name `SIG" + signalName + "'");
}
}


public static interface Kernel32 {
jnr.ffi.Pointer OpenProcess(int dwDesiredAccess, int bInheritHandle, int dwProcessId);
int CloseHandle(jnr.ffi.Pointer handle);
int GetLastError();
int GetExitCodeProcess(jnr.ffi.Pointer hProcess, jnr.ffi.Pointer pointerToExitCodeDword);
int TerminateProcess(jnr.ffi.Pointer hProcess, int uExitCode);
}

@Deprecated
public static IRubyObject kill(IRubyObject recv, IRubyObject[] args) {
return kill(recv.getRuntime(), args);
Expand All @@ -888,12 +897,6 @@ public static IRubyObject kill(Ruby runtime, IRubyObject[] args) {
throw runtime.newArgumentError("wrong number of arguments -- kill(sig, pid...)");
}

// Windows does not support these functions, so we won't even try
// This also matches Ruby behavior for JRUBY-2353.
if (Platform.IS_WINDOWS) {
return runtime.getNil();
}

int signal;
if (args[0] instanceof RubyFixnum) {
signal = (int) ((RubyFixnum) args[0]).getLongValue();
Expand All @@ -907,18 +910,84 @@ public static IRubyObject kill(Ruby runtime, IRubyObject[] args) {

boolean processGroupKill = signal < 0;

if (processGroupKill) signal = -signal;

POSIX posix = runtime.getPosix();
for (int i = 1; i < args.length; i++) {
int pid = RubyNumeric.num2int(args[i]);

// FIXME: It may be possible to killpg on systems which support it. POSIX library
// needs to tell whether a particular method works or not
if (pid == 0) pid = runtime.getPosix().getpid();
checkErrno(runtime, posix.kill(processGroupKill ? -pid : pid, signal));
if (processGroupKill) {
if (Platform.IS_WINDOWS) {
throw runtime.newErrnoEINVALError("group signals not implemented in windows");
}
signal = -signal;
}

if (Platform.IS_WINDOWS) {
int PROCESS_QUERY_INFORMATION = 0x0400;
int ERROR_INVALID_PARAMETER = 0x57;
int PROCESS_TERMINATE = 0x0001;
int STILL_ACTIVE = 259;
Kernel32 libc = Library.loadLibrary("kernel32", Kernel32.class);
jnr.ffi.Pointer status = Memory.allocate(Library.getRuntime(libc), 4);
for (int i = 1; i < args.length; i++) {
int pid = RubyNumeric.num2int(args[i]);
if (signal == 0) {
jnr.ffi.Pointer ptr = libc.OpenProcess(PROCESS_QUERY_INFORMATION, 0, pid);
if(ptr != null && ptr.address() != -1) {
try {
if(libc.GetExitCodeProcess(ptr, status) == 0) {
throw runtime.newErrnoEPERMError("unable to call GetExitCodeProcess " + pid);
} else {
if(status.getInt(0) != STILL_ACTIVE) {
throw runtime.newErrnoEPERMError("Process exists but is not alive anymore " + pid);
}
}
} finally {
libc.CloseHandle(ptr);
}

} else {
if (libc.GetLastError() == ERROR_INVALID_PARAMETER) {
throw runtime.newErrnoESRCHError();
} else {
throw runtime.newErrnoEPERMError("Process does not exist " + pid);
}
}
} else if (signal == 9) { //SIGKILL
jnr.ffi.Pointer ptr = libc.OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, 0, pid);
if(ptr != null && ptr.address() != -1) {
try {
if(libc.GetExitCodeProcess(ptr, status) == 0) {
throw runtime.newErrnoEPERMError("unable to call GetExitCodeProcess " + pid); // todo better error messages
} else {
if (status.getInt(0) == STILL_ACTIVE) {
if (libc.TerminateProcess(ptr, 0) == 0) {
throw runtime.newErrnoEPERMError("unable to call TerminateProcess " + pid);
}
// success
}
}
} finally {
libc.CloseHandle(ptr);
}
} else {
if (libc.GetLastError() == ERROR_INVALID_PARAMETER) {
throw runtime.newErrnoESRCHError();
} else {
throw runtime.newErrnoEPERMError("Process does not exist " + pid);
}
}
} else {
throw runtime.newNotImplementedError("this signal not yet implemented in windows");
}
}
} else {
POSIX posix = runtime.getPosix();
for (int i = 1; i < args.length; i++) {
int pid = RubyNumeric.num2int(args[i]);

// FIXME: It may be possible to killpg on systems which support it. POSIX library
// needs to tell whether a particular method works or not
if (pid == 0) pid = runtime.getPosix().getpid();
checkErrno(runtime, posix.kill(processGroupKill ? -pid : pid, signal));
}
}

return runtime.newFixnum(args.length - 1);

}
Expand Down