package jenkins.slaves;

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

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 extends MasterToSlaveCallable<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");
}

@@ -1283,16 +1283,17 @@
 Windows%0A
+%0A
         
@@ -1296,16 +1296,47 @@
         
+Channel c = Channel.current();%0A
 %0A       
@@ -1340,16 +1340,24 @@
         
+Standard
 OutputSt
@@ -1365,29 +1365,210 @@
 eam 
-o = Channel.current()
+sos = (StandardOutputStream) c.getProperty(StandardOutputStream.class);%0A            if (sos!=null) %7B%0A                swap(sos);%0A                return true;%0A            %7D%0A%0A            OutputStream o = c
 .get
@@ -1648,32 +1648,38 @@
                 
+swap((
 StandardOutputSt
@@ -1686,48 +1686,287 @@
 ream
- stdout = (StandardOutputStream)o;%0A%0A    
+) o);%0A                return true;%0A            %7D%0A%0A            return false;%0A        %7D%0A%0A        private void swap(StandardOutputStream stdout) throws IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException %7B%0A
     
@@ -2059,20 +2059,16 @@
         
-    
 int out 
@@ -2102,28 +2102,24 @@
             
-   
- 
 if (out%3C0)  
@@ -2161,28 +2161,24 @@
 o dup(1)%22);%0A
-    
             
@@ -2257,36 +2257,32 @@
 tor(int.class);%0A
-    
             c.se
@@ -2312,20 +2312,16 @@
         
-    
 FileOutp
@@ -2374,28 +2374,24 @@
 nce(out));%0A%0A
-    
             
@@ -2468,20 +2468,16 @@
         
- 
-   
 stdout.s
@@ -2483,28 +2483,24 @@
 swap(fos);%0A%0A
-    
             
@@ -2573,36 +2573,32 @@
 ut)%0A            
-  
-  
 GNUCLibrary.LIBC
@@ -2612,36 +2612,32 @@
 1);%0A            
-    
 GNUCLibrary.LIBC
@@ -2652,77 +2652,8 @@
 1);%0A
-                return true;%0A            %7D%0A            return false;%0A
     

Make stdout swapping more reliable.