package jenkins.slaves.restarter;
import com.sun.akuma.Daemon;
import com.sun.akuma.JavaVMArguments;
import com.sun.jna.Native;
import com.sun.jna.StringArray;
import hudson.Extension;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
import static hudson.util.jna.GNUCLibrary.*;
import static java.util.logging.Level.*;
/**
* On Unix, restart via exec-ing to itself.
*/
@Extension
public class UnixSlaveRestarter extends SlaveRestarter {
private transient JavaVMArguments args;
@Override
public boolean canWork() {
try {
if (File.pathSeparatorChar!=':')
return false; // quick test to reject non-Unix without loading all the rest of the classes
args = JavaVMArguments.current();
// go through the whole motion to make sure all the relevant classes are loaded now
LIBC.getdtablesize();
int v = LIBC.fcntl(99999, F_GETFD);
LIBC.fcntl(99999, F_SETFD, v);
Daemon.getCurrentExecutable();
LIBC.execv("positively/no/such/executable", new StringArray(new String[]{"a","b","c"}));
return true;
} catch (UnsupportedOperationException e) {
LOGGER.log(FINE, getClass()+" unsuitable", e);
return false;
} catch (LinkageError e) {
LOGGER.log(FINE, getClass()+" unsuitable", e);
return false;
} catch (IOException e) {
LOGGER.log(FINE, getClass()+" unsuitable", e);
return false;
}
}
public void restart() throws Exception {
// close all files upon exec, except stdin, stdout, and stderr
int sz = LIBC.getdtablesize();
for (int i = 3; i < sz; i++) {
int flags = LIBC.fcntl(i, F_GETFD);
if (flags < 0) continue;
LIBC.fcntl(i, F_SETFD, flags | FD_CLOEXEC);
}
// exec to self
String exe = Daemon.getCurrentExecutable();
LIBC.execv(exe, new StringArray(args.toArray(new String[args.size()])));
throw new IOException("Failed to exec '" + exe + "' " + LIBC.strerror(Native.getLastError()));
}
private static final Logger LOGGER = Logger.getLogger(UnixSlaveRestarter.class.getName());
private static final long serialVersionUID = 1L;
}