Skip to content

Commit

Permalink
[JENKINS-8856] evacuate stdout at OS level to avoid interference with
Browse files Browse the repository at this point in the history
JVM.
  • Loading branch information
kohsuke committed Aug 26, 2011
1 parent 9c9379e commit 3f4d01e
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 1 deletion.
3 changes: 3 additions & 0 deletions changelog.html
Expand Up @@ -67,6 +67,9 @@
<li class=bug>
Jenkins didn't start on IBM JDK.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-10810">issue 10810</a>)
<li class=rfe>
stdin/stdout based remote slaves, such as ones launched via SSH or script, now does a better redirect to avoid interference with JVM output to stdout.
(<a href="https://issues.jenkins-ci.org/browse/JENKINS-8856">issue 8856</a>)
</ul>
</div><!--=TRUNK-END=-->

Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/hudson/util/jna/GNUCLibrary.java
Expand Up @@ -72,6 +72,10 @@ public interface GNUCLibrary extends Library {
int chown(String fileName, int uid, int gid);
int chmod(String fileName, int i);

int dup(int old);
int dup2(int old, int _new);
int close(int fd);

// see http://www.gnu.org/s/libc/manual/html_node/Renaming-Files.html
int rename(String oldname, String newname);

Expand Down
67 changes: 67 additions & 0 deletions core/src/main/java/jenkins/slaves/StandardOutputSwapper.java
@@ -0,0 +1,67 @@
package jenkins.slaves;

import hudson.Extension;
import hudson.FilePath;
import hudson.model.Computer;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.StandardOutputStream;
import hudson.slaves.ComputerListener;
import hudson.util.jna.GNUCLibrary;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.util.logging.Logger;

/**
* @author Kohsuke Kawaguchi
*/
@Extension
public class StandardOutputSwapper extends ComputerListener {
@Override
public void preOnline(Computer c, Channel channel, FilePath root, TaskListener listener) {
if (disabled) return;

try {
if (channel.call(new ChannelSwapper()))
listener.getLogger().println("Evacuated stdout");
} catch (Throwable e) {
LOGGER.fine("Fatal problem swapping file descriptors " + c.getName());
}
}

private static final class ChannelSwapper implements Callable<Boolean,Exception> {
public Boolean call() throws Exception {
if (File.pathSeparatorChar==';') return false; // Windows

OutputStream o = Channel.current().getUnderlyingOutput();
if (o instanceof StandardOutputStream) {
StandardOutputStream stdout = (StandardOutputStream)o;

// duplicate the OS file descriptor and create FileOutputStream around it
int out = GNUCLibrary.LIBC.dup(1);
if (out<0) throw new IOException("Failed to dup(1)");
Constructor<FileDescriptor> c = FileDescriptor.class.getDeclaredConstructor(int.class);
c.setAccessible(true);
FileOutputStream fos = new FileOutputStream(c.newInstance(out));

// swap it into channel so that it'll use the new file descriptor
stdout.swap(fos);

// close fd=1 (stdout) and duplicate fd=2 (stderr) into fd=1 (stdout)
GNUCLibrary.LIBC.close(1);
GNUCLibrary.LIBC.dup2(2,1);
return true;
}
return false;
}
}

private static final Logger LOGGER = Logger.getLogger(StandardOutputSwapper.class.getName());
public static boolean disabled = Boolean.getBoolean(StandardOutputSwapper.class.getName()+".disabled");
}
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -133,7 +133,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>remoting</artifactId>
<version>2.4</version>
<version>2.5</version>
</dependency>
</dependencies>
</dependencyManagement>
Expand Down

0 comments on commit 3f4d01e

Please sign in to comment.