提交 9b604944 编写于 作者: K kohsuke

Added Mac OS X support

git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@14980 71c3de6d-444a-0410-be80-ed276b4c234a
上级 90e4f996
package hudson.util;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import static com.sun.jna.Pointer.NULL;
import com.sun.jna.ptr.IntByReference;
import hudson.EnvVars;
import hudson.Util;
import static hudson.util.jna.GNUCLibrary.LIBC;
import org.apache.commons.io.FileUtils;
import org.jvnet.winp.WinProcess;
import org.jvnet.winp.WinpException;
......@@ -18,6 +23,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
......@@ -25,11 +31,10 @@ import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.util.logging.Level.FINEST;
import static java.util.logging.Level.FINER;
import static java.util.logging.Level.FINEST;
import java.util.logging.Logger;
/**
* Kills a process tree to clean up the mess left by a build.
......@@ -95,6 +100,8 @@ public abstract class ProcessTreeKiller {
return new Linux();
if(os.equals("SunOS"))
return new Solaris();
if(os.equals("Mac OS X"))
return new Darwin();
return DEFAULT;
}
......@@ -251,9 +258,25 @@ public abstract class ProcessTreeKiller {
* The object represents a snapshot of the system state.
*/
static abstract class UnixSystem<P extends UnixProcess<P>> implements Iterable<P> {
private final Map<Integer/*pid*/,P> processes = new HashMap<Integer,P>();
/**
* To be filled in the constructor of the derived type.
*/
protected final Map<Integer/*pid*/,P> processes = new HashMap<Integer,P>();
public P get(int pid) {
return processes.get(pid);
}
UnixSystem() {
public Iterator<P> iterator() {
return processes.values().iterator();
}
}
/**
* {@link UnixSystem} that has /proc.
*/
static abstract class ProcfsUnixSystem<P extends UnixProcess<P>> extends UnixSystem<P> {
ProcfsUnixSystem() {
File[] processes = new File("/proc").listFiles(new FileFilter() {
public boolean accept(File f) {
return f.isDirectory();
......@@ -281,14 +304,6 @@ public abstract class ProcessTreeKiller {
}
protected abstract P createProcess(int pid) throws IOException;
public P get(int pid) {
return processes.get(pid);
}
public Iterator<P> iterator() {
return processes.values().iterator();
}
}
/**
......@@ -382,7 +397,7 @@ public abstract class ProcessTreeKiller {
return new LinuxSystem();
}
static class LinuxSystem extends Unix.UnixSystem<LinuxProcess> {
static class LinuxSystem extends Unix.ProcfsUnixSystem<LinuxProcess> {
protected LinuxProcess createProcess(int pid) throws IOException {
return new LinuxProcess(this,pid);
}
......@@ -481,7 +496,7 @@ public abstract class ProcessTreeKiller {
return new SolarisSystem();
}
static class SolarisSystem extends Unix.UnixSystem<SolarisProcess> {
static class SolarisSystem extends Unix.ProcfsUnixSystem<SolarisProcess> {
protected SolarisProcess createProcess(int pid) throws IOException {
return new SolarisProcess(this,pid);
}
......@@ -669,6 +684,212 @@ public abstract class ProcessTreeKiller {
}
}
/**
* Implementation for Mac OS X based on sysctl(3).
*/
private static final class Darwin extends Unix<Darwin.DarwinSystem> {
protected DarwinSystem createSystem() {
return new DarwinSystem();
}
static class DarwinSystem extends Unix.UnixSystem<DarwinProcess> {
DarwinSystem() {
try {
IntByReference _ = new IntByReference(sizeOfInt);
IntByReference size = new IntByReference(sizeOfInt);
Memory m;
int nRetry = 0;
while(true) {
// find out how much memory we need to do this
if(LIBC.sysctl(MIB_PROC_ALL,3, NULL, size, NULL, _)!=0)
throw new IOException("Failed to obtain memory requirement: "+LIBC.strerror(Native.getLastError()));
// now try the real call
m = new Memory(size.getValue());
if(LIBC.sysctl(MIB_PROC_ALL,3, m, size, NULL, _)!=0) {
if(Native.getLastError()==ENOMEM && nRetry++<16)
continue; // retry
throw new IOException("Failed to call kern.proc.all: "+LIBC.strerror(Native.getLastError()));
}
break;
}
int count = size.getValue()/sizeOf_kinfo_proc;
LOGGER.fine("Found "+count+" processes");
for( int base=0; base<size.getValue(); base+=sizeOf_kinfo_proc) {
int pid = m.getInt(base+24);
int ppid = m.getInt(base+416);
// int effective_uid = m.getInt(base+304);
// byte[] comm = new byte[16];
// m.read(base+163,comm,0,16);
processes.put(pid,new DarwinProcess(this,pid,ppid));
}
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Failed to obtain process list",e);
}
}
}
static class DarwinProcess extends Unix.UnixProcess<DarwinProcess> {
private final int pid;
private final int ppid;
private EnvVars envVars;
private List<String> arguments;
DarwinProcess(DarwinSystem system, int pid, int ppid) {
super(system);
this.pid = pid;
this.ppid = ppid;
}
public int getPid() {
return pid;
}
public DarwinProcess getParent() {
return system.get(ppid);
}
public synchronized EnvVars getEnvVars() {
if(envVars !=null)
return envVars;
parse();
return envVars;
}
public List<String> getArguments() {
if(arguments !=null)
return arguments;
parse();
return arguments;
}
private void parse() {
try {
// allocate them first, so that the parse error wil result in empty data
// and avoid retry.
arguments = new ArrayList<String>();
envVars = new EnvVars();
IntByReference _ = new IntByReference();
IntByReference argmaxRef = new IntByReference(0);
IntByReference size = new IntByReference(sizeOfInt);
// for some reason, I was never able to get sysctlbyname work.
// if(LIBC.sysctlbyname("kern.argmax", argmaxRef.getPointer(), size, NULL, _)!=0)
if(LIBC.sysctl(new int[]{CTL_KERN,KERN_ARGMAX},2, argmaxRef.getPointer(), size, NULL, _)!=0)
throw new IOException("Failed to get kernl.argmax: "+LIBC.strerror(Native.getLastError()));
int argmax = argmaxRef.getValue();
class StringArrayMemory extends Memory {
private long offset=0;
StringArrayMemory(long l) {
super(l);
}
int readInt() {
int r = getInt(offset);
offset+=sizeOfInt;
return r;
}
byte peek() {
return getByte(offset);
}
String readString() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte ch;
while((ch = getByte(offset++))!='\0')
baos.write(ch);
return baos.toString();
}
void skip0() {
// skip trailing '\0's
while(getByte(offset)=='\0')
offset++;
}
}
StringArrayMemory m = new StringArrayMemory(argmax);
size.setValue(argmax);
if(LIBC.sysctl(new int[]{CTL_KERN,KERN_PROCARGS2,pid},3, m, size, NULL, _)!=0)
throw new IOException("Failed to obtain ken.procargs2: "+LIBC.strerror(Native.getLastError()));
/*
* Make a sysctl() call to get the raw argument space of the
* process. The layout is documented in start.s, which is part
* of the Csu project. In summary, it looks like:
*
* /---------------\ 0x00000000
* : :
* : :
* |---------------|
* | argc |
* |---------------|
* | arg[0] |
* |---------------|
* : :
* : :
* |---------------|
* | arg[argc - 1] |
* |---------------|
* | 0 |
* |---------------|
* | env[0] |
* |---------------|
* : :
* : :
* |---------------|
* | env[n] |
* |---------------|
* | 0 |
* |---------------| <-- Beginning of data returned by sysctl()
* | exec_path | is here.
* |:::::::::::::::|
* | |
* | String area. |
* | |
* |---------------| <-- Top of stack.
* : :
* : :
* \---------------/ 0xffffffff
*/
int nargs = m.readInt();
m.readString(); // exec path
for( int i=0; i<nargs; i++) {
m.skip0();
arguments.add(m.readString());
}
// this is how you can read environment variables
while(m.peek()!=0)
envVars.addLine(m.readString());
} catch (IOException e) {
// this happens with insufficient permissions, so just ignore the problem.
}
}
}
// local constants
private static final int sizeOf_kinfo_proc = 492; // TODO:checked on 32bit Mac OS X. is this different on 64bit?
private static final int CTL_KERN = 1;
private static final int KERN_PROC = 14;
private static final int KERN_PROC_ALL = 0;
private static final int KERN_ARGMAX = 8;
private static final int KERN_PROCARGS2 = 49;
private static final int ENOMEM = 12;
private static final int sizeOfInt = Native.getNativeSize(int.class);
private static int[] MIB_PROC_ALL = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
}
// public static void main(String[] args) {
// // dump everything
// LOGGER.setLevel(Level.ALL);
......
package hudson.util.jna;
import com.sun.jna.Library;
import com.sun.jna.StringArray;
import com.sun.jna.Pointer;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
/**
* GNU C library.
*
* @author Kohsuke Kawaguchi
*/
public interface GNUCLibrary extends Library {
int fork();
int kill(int pid, int signum);
int setsid();
int umask(int mask);
int getpid();
int getppid();
int chdir(String dir);
int execv(String file, StringArray args);
int setenv(String name, String value);
int unsetenv(String name);
void perror(String msg);
String strerror(int errno);
// this is listed in http://developer.apple.com/DOCUMENTATION/Darwin/Reference/ManPages/man3/sysctlbyname.3.html
// but not in http://www.gnu.org/software/libc/manual/html_node/System-Parameters.html#index-sysctl-3493
// perhaps it is only supported on BSD?
int sysctlbyname(String name, Pointer oldp, IntByReference oldlenp, Pointer newp, IntByReference newlen);
int sysctl(int[] mib, int nameLen, Pointer oldp, IntByReference oldlenp, Pointer newp, IntByReference newlen);
int sysctlnametomib(String name, Pointer mibp, IntByReference size);
public static final GNUCLibrary LIBC = (GNUCLibrary) Native.loadLibrary("c",GNUCLibrary.class);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册