Skip to content
Permalink
Browse files

[JENKINS-37559] cannot abort shell scripts on 64-bit Solaris (#2507)

  • Loading branch information
csiden authored and oleg-nenashev committed Aug 27, 2016
1 parent c505dec commit 0f364e3a554a1bc6cddbfe7740993b835cae010e
@@ -25,6 +25,9 @@

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.LastErrorException;
import com.sun.jna.ptr.IntByReference;
import hudson.EnvVars;
import hudson.FilePath;
@@ -752,24 +755,52 @@ public synchronized EnvVars getEnvironmentVariables() {
/**
* Implementation for Solaris that uses <tt>/proc</tt>.
*
* Amazingly, this single code works for both 32bit and 64bit Solaris, despite the fact
* that does a lot of pointer manipulation and what not.
* /proc/PID/psinfo contains a psinfo_t struct. We use it to determine where the
* process arguments and environment are located in PID's address space.
* Note that the psinfo_t struct is different (different sized elements) for 32-bit
* vs 64-bit processes and the kernel will provide the version of the struct that
* matches the _reader_ (this Java process) regardless of whether PID is a
* 32-bit or 64-bit process.
*
* Note that this means that if PID is a 64-bit process, then a 32-bit Java
* process can not get meaningful values for envp and argv out of the psinfo_t. The
* values will have been truncated to 32-bits.
*
* /proc/PID/as contains the address space of the process we are inspecting. We can
* follow the envp and argv pointers from psinfo_t to find the environment variables
* and process arguments. When following pointers in this address space we need to
* make sure to use 32-bit or 64-bit pointers depending on what sized pointers
* PID uses, regardless of what size pointers the Java process uses.
*
* Note that the size of a 64-bit address space is larger than Long.MAX_VALUE (because
* longs are signed). So normal Java utilities like RandomAccessFile and FileChannel
* (which use signed longs as offsets) are not able to read from the end of the address
* space, where envp and argv will be. Therefore we need to use LIBC.pread() directly.
* when accessing this file.
*/
static class Solaris extends ProcfsUnix {
protected OSProcess createProcess(final int pid) throws IOException {
return new SolarisProcess(pid);
}

private class SolarisProcess extends UnixProcess {
private static final byte PR_MODEL_ILP32 = 1;
private static final byte PR_MODEL_LP64 = 2;

/*
* True if target process is 64-bit (Java process may be different).
*/
private final boolean b64;

private final int ppid;
/**
* Address of the environment vector. Even on 64bit Solaris this is still 32bit pointer.
* Address of the environment vector.
*/
private final int envp;
private final long envp;
/**
* Similarly, address of the arguments vector.
* Similarly, address of the arguments vector.
*/
private final int argp;
private final long argp;
private final int argc;
private EnvVars envVars;
private List<String> arguments;
@@ -821,10 +852,23 @@ private SolarisProcess(int pid) throws IOException {
throw new IOException("psinfo PID mismatch"); // sanity check
ppid = adjust(psinfo.readInt());

psinfo.seek(188); // now jump to pr_argc
argc = adjust(psinfo.readInt());
argp = adjust(psinfo.readInt());
envp = adjust(psinfo.readInt());
/*
* Read the remainder of psinfo_t differently depending on whether the
* Java process is 32-bit or 64-bit.
*/
if (Pointer.SIZE == 8) {
psinfo.seek(236); // offset of pr_argc
argc = adjust(psinfo.readInt());
argp = adjustL(psinfo.readLong());
envp = adjustL(psinfo.readLong());
b64 = (psinfo.readByte() == PR_MODEL_LP64);
} else {
psinfo.seek(188); // offset of pr_argc
argc = adjust(psinfo.readInt());
argp = to64(adjust(psinfo.readInt()));
envp = to64(adjust(psinfo.readInt()));
b64 = (psinfo.readByte() == PR_MODEL_LP64);
}
} finally {
psinfo.close();
}
@@ -842,23 +886,28 @@ public OSProcess getParent() {
return arguments;

arguments = new ArrayList<String>(argc);
if (argc == 0) {
return arguments;
}

int psize = b64 ? 8 : 4;
Memory m = new Memory(psize);
try {
RandomAccessFile as = new RandomAccessFile(getFile("as"),"r");
if(LOGGER.isLoggable(FINER))
LOGGER.finer("Reading "+getFile("as"));
int fd = LIBC.open(getFile("as").getAbsolutePath(), 0);
try {
for( int n=0; n<argc; n++ ) {
// read a pointer to one entry
as.seek(to64(argp+n*4));
int p = adjust(as.readInt());
LIBC.pread(fd, m, new NativeLong(psize), new NativeLong(argp+n*psize));
long addr = b64 ? m.getLong(0) : m.getInt(0);

arguments.add(readLine(as, p, "argv["+ n +"]"));
arguments.add(readLine(fd, addr, "argv["+ n +"]"));
}
} finally {
as.close();
LIBC.close(fd);
}
} catch (IOException e) {
} catch (IOException | LastErrorException e) {
// failed to read. this can happen under normal circumstances (most notably permission denied)
// so don't report this as an error.
}
@@ -872,44 +921,51 @@ public synchronized EnvVars getEnvironmentVariables() {
return envVars;
envVars = new EnvVars();

if (envp == 0) {
return envVars;
}

int psize = b64 ? 8 : 4;
Memory m = new Memory(psize);
try {
RandomAccessFile as = new RandomAccessFile(getFile("as"),"r");
if(LOGGER.isLoggable(FINER))
LOGGER.finer("Reading "+getFile("as"));
int fd = LIBC.open(getFile("as").getAbsolutePath(), 0);
try {
for( int n=0; ; n++ ) {
// read a pointer to one entry
as.seek(to64(envp+n*4));
int p = adjust(as.readInt());
if(p==0)
break; // completed the walk
LIBC.pread(fd, m, new NativeLong(psize), new NativeLong(envp+n*psize));
long addr = b64 ? m.getLong(0) : m.getInt(0);
if (addr == 0) // completed the walk
break;

// now read the null-terminated string
envVars.addLine(readLine(as, p, "env["+ n +"]"));
envVars.addLine(readLine(fd, addr, "env["+ n +"]"));
}
} finally {
as.close();
LIBC.close(fd);
}
} catch (IOException e) {
} catch (IOException | LastErrorException e) {
// failed to read. this can happen under normal circumstances (most notably permission denied)
// so don't report this as an error.
}

return envVars;
}

private String readLine(RandomAccessFile as, int p, String prefix) throws IOException {
private String readLine(int fd, long addr, String prefix) throws IOException {
if(LOGGER.isLoggable(FINEST))
LOGGER.finest("Reading "+prefix+" at "+p);
LOGGER.finest("Reading "+prefix+" at "+addr);

as.seek(to64(p));
Memory m = new Memory(1);
byte ch = 1;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
int ch,i=0;
while((ch=as.read())>0) {
if((++i)%100==0 && LOGGER.isLoggable(FINEST))
LOGGER.finest(prefix +" is so far "+buf.toString());

while(true) {
LIBC.pread(fd, m, new NativeLong(1), new NativeLong(addr));
ch = m.getByte(0);
if (ch == 0)
break;
buf.write(ch);
addr++;
}
String line = buf.toString();
if(LOGGER.isLoggable(FINEST))
@@ -936,6 +992,13 @@ private static int adjust(int i) {
return i;
}

public static long adjustL(long i) {
if(IS_LITTLE_ENDIAN) {
return Long.reverseBytes(i);
} else {
return i;
}
}
}

/**
@@ -29,6 +29,7 @@
import com.sun.jna.Native;
import com.sun.jna.Memory;
import com.sun.jna.NativeLong;
import com.sun.jna.LastErrorException;
import com.sun.jna.ptr.IntByReference;
import jnr.posix.POSIX;
import org.jvnet.libpam.impl.CLibrary.passwd;
@@ -74,8 +75,10 @@
int chown(String fileName, int uid, int gid);
int chmod(String fileName, int i);

int open(String pathname, int flags) throws LastErrorException;
int dup(int old);
int dup2(int old, int _new);
long pread(int fd, Memory buffer, NativeLong size, NativeLong offset) throws LastErrorException;
int close(int fd);

// see http://www.gnu.org/s/libc/manual/html_node/Renaming-Files.html

0 comments on commit 0f364e3

Please sign in to comment.
You can’t perform that action at this time.