diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index 5c82858f64..65864157e7 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -298,7 +298,10 @@ public abstract class Launcher { public Channel launchChannel(String[] cmd, OutputStream out, FilePath workDir, Map envVars) throws IOException { printCommandLine(cmd, workDir); - final Process proc = Runtime.getRuntime().exec(cmd, Util.mapToEnv(Launcher.inherit(envVars)), toFile(workDir)); + final EnvVars cookie = ProcessTreeKiller.createCookie(); + EnvVars map = Launcher.inherit(envVars); + map.putAll(cookie); + final Process proc = Runtime.getRuntime().exec(cmd, Util.mapToEnv(map), toFile(workDir)); final Thread t2 = new StreamCopyThread(cmd+": stderr copier", proc.getErrorStream(), out); t2.start(); @@ -311,7 +314,7 @@ public abstract class Launcher { */ protected synchronized void terminate(IOException e) { super.terminate(e); - ProcessTreeKiller.get().kill(proc); + ProcessTreeKiller.get().kill(proc,cookie); } public synchronized void close() throws IOException { @@ -451,7 +454,7 @@ public abstract class Launcher { /** * Expands the list of environment variables by inheriting current env variables. */ - private static Map inherit(Map overrides) { + private static EnvVars inherit(Map overrides) { EnvVars m = new EnvVars(EnvVars.masterEnvVars); m.overrideAll(overrides); return m; diff --git a/core/src/main/java/hudson/Proc.java b/core/src/main/java/hudson/Proc.java index e1b336c056..7c6024a0f6 100644 --- a/core/src/main/java/hudson/Proc.java +++ b/core/src/main/java/hudson/Proc.java @@ -56,6 +56,7 @@ public abstract class Proc { private final Process proc; private final Thread copier; private final OutputStream out; + private final EnvVars cookie; public LocalProc(String cmd, Map env, OutputStream out, File workDir) throws IOException { this(cmd,Util.mapToEnv(env),out,workDir); @@ -79,7 +80,7 @@ public abstract class Proc { public LocalProc(String[] cmd,String[] env,InputStream in,OutputStream out, File workDir) throws IOException { this( calcName(cmd), - environment(new ProcessBuilder(cmd),env).directory(workDir).redirectErrorStream(true).start(), + environment(new ProcessBuilder(cmd),env).directory(workDir).redirectErrorStream(true), in, out ); } @@ -95,10 +96,12 @@ public abstract class Proc { return pb; } - private LocalProc( String name, Process proc, InputStream in, OutputStream out ) throws IOException { + private LocalProc( String name, ProcessBuilder procBuilder, InputStream in, OutputStream out ) throws IOException { Logger.getLogger(Proc.class.getName()).log(Level.FINE, "Running: {0}", name); this.out = out; - this.proc = proc; + this.cookie = ProcessTreeKiller.createCookie(); + procBuilder.environment().putAll(cookie); + this.proc = procBuilder.start(); copier = new StreamCopyThread(name+": stdout copier", proc.getInputStream(), out); copier.start(); if(in!=null) @@ -160,7 +163,7 @@ public abstract class Proc { * Destroys the child process without join. */ private void destroy() { - ProcessTreeKiller.get().kill(proc); + ProcessTreeKiller.get().kill(proc,cookie); } private static class ByteCopier extends Thread { diff --git a/core/src/main/java/hudson/model/Slave.java b/core/src/main/java/hudson/model/Slave.java index 161d14f145..5546af7800 100644 --- a/core/src/main/java/hudson/model/Slave.java +++ b/core/src/main/java/hudson/model/Slave.java @@ -1,5 +1,6 @@ package hudson.model; +import hudson.EnvVars; import hudson.FilePath; import hudson.Launcher; import hudson.Launcher.RemoteLauncher; @@ -327,7 +328,11 @@ public final class Slave implements Node, Serializable { try { listener.getLogger().println(Messages.Slave_Launching(getTimestamp())); listener.getLogger().println("$ "+slave.agentCommand); - final Process proc = Runtime.getRuntime().exec(slave.agentCommand); + + ProcessBuilder pb = new ProcessBuilder(Util.tokenize(slave.agentCommand)); + final EnvVars cookie = ProcessTreeKiller.createCookie(); + pb.environment().putAll(cookie); + final Process proc = pb.start(); // capture error information from stderr. this will terminate itself // when the process is killed. @@ -338,7 +343,7 @@ public final class Slave implements Node, Serializable { public void onClosed(Channel channel, IOException cause) { if(cause!=null) cause.printStackTrace(listener.error(Messages.Slave_Terminated(getTimestamp()))); - ProcessTreeKiller.get().kill(proc); + ProcessTreeKiller.get().kill(proc,cookie); } }); diff --git a/core/src/main/java/hudson/util/ProcessTreeKiller.java b/core/src/main/java/hudson/util/ProcessTreeKiller.java index 5b5ea69a4d..e7be1c770c 100644 --- a/core/src/main/java/hudson/util/ProcessTreeKiller.java +++ b/core/src/main/java/hudson/util/ProcessTreeKiller.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -64,6 +65,16 @@ public abstract class ProcessTreeKiller { */ public abstract void kill(Process proc, Map modelEnvVars); + /** + * Creates a magic cookie that can be used as the model environment variable + * when we later kill the processes. + */ + public static EnvVars createCookie() { + EnvVars r = new EnvVars(); + r.put("HUDSON_COOKIE",UUID.randomUUID().toString()); + return r; + } + /** * Gets the {@link ProcessTreeKiller} suitable for the current system * that JVM runs in, or in the worst case return the default one @@ -354,13 +365,13 @@ public abstract class ProcessTreeKiller { return new LinuxSystem(); } - static class LinuxSystem extends UnixSystem { + static class LinuxSystem extends Unix.UnixSystem { protected LinuxProcess createProcess(int pid) throws IOException { return new LinuxProcess(this,pid); } } - static class LinuxProcess extends UnixProcess { + static class LinuxProcess extends Unix.UnixProcess { private final int pid; private int ppid = -1; private EnvVars envVars; @@ -428,13 +439,13 @@ public abstract class ProcessTreeKiller { return new SolarisSystem(); } - static class SolarisSystem extends UnixSystem { + static class SolarisSystem extends Unix.UnixSystem { protected SolarisProcess createProcess(int pid) throws IOException { return new SolarisProcess(this,pid); } } - static class SolarisProcess extends UnixProcess { + static class SolarisProcess extends Unix.UnixProcess { private final int pid; private final int ppid; /**