1 package hudson; 2 3 import hudson.model.Computer; 4 import hudson.model.Hudson; 5 import hudson.model.Slave.ComputerImpl; 6 import hudson.remoting.Channel; 7 import hudson.remoting.Channel.Listener; 8 import hudson.util.TextFile; 9 10 import java.io.DataInputStream ; 11 import java.io.IOException ; 12 import java.io.PrintWriter ; 13 import java.io.File ; 14 import java.net.ServerSocket ; 15 import java.net.Socket ; 16 import java.util.logging.Level ; 17 import java.util.logging.Logger ; 18 import java.security.SecureRandom ; 19 20 47 public class TcpSlaveAgentListener extends Thread { 48 49 private final ServerSocket serverSocket; 50 private volatile boolean shuttingDown; 51 private final String secretKey; 52 53 public TcpSlaveAgentListener() throws IOException { 54 serverSocket = new ServerSocket (0); 55 56 LOGGER.info("JNLP slave agent listener started on TCP port "+getPort()); 57 58 TextFile secretFile = new TextFile(new File(Hudson.getInstance().getRootDir(),"secret.key")); 60 if(secretFile.exists()) { 61 secretKey = secretFile.readTrim(); 62 } else { 63 SecureRandom sr = new SecureRandom (); 64 byte[] random = new byte[32]; 65 sr.nextBytes(random); 66 secretKey = Util.toHexString(random); 67 secretFile.write(secretKey); 68 } 69 70 start(); 71 } 72 73 76 public int getPort() { 77 return serverSocket.getLocalPort(); 78 } 79 80 public String getSecretKey() { 81 return secretKey; 82 } 83 84 public void run() { 85 try { 86 87 while (true) { 89 Socket s = serverSocket.accept(); 90 new ConnectionHandler(s).start(); 91 } 92 } catch (IOException e) { 93 if(!shuttingDown) { 94 LOGGER.log(Level.SEVERE,"Failed to accept JNLP slave agent connections",e); 95 } 96 } 97 } 98 99 102 public void shutdown() { 103 shuttingDown = true; 104 try { 105 serverSocket.close(); 106 } catch (IOException e) { 107 LOGGER.log(Level.WARNING, "Failed to close down TCP port",e); 108 } 109 } 110 111 private final class ConnectionHandler extends Thread { 112 private final Socket s; 113 116 private final int id; 117 118 public ConnectionHandler(Socket s) { 119 this.s = s; 120 synchronized(getClass()) { 121 id = iotaGen++; 122 } 123 } 124 125 public void run() { 126 try { 127 LOGGER.info("Accepted connection #"+id+" from "+s.getRemoteSocketAddress()); 128 129 DataInputStream in = new DataInputStream (s.getInputStream()); 130 PrintWriter out = new PrintWriter (s.getOutputStream(),true); 131 132 if(!secretKey.equals(in.readUTF())) { 133 error(out, "Unauthorized access"); 134 return; 135 } 136 137 String nodeName = in.readUTF(); 138 Computer computer = Hudson.getInstance().getComputer(nodeName); 139 if(computer==null) { 140 error(out, "No such slave: "+nodeName); 141 return; 142 } 143 144 if(computer.getChannel()!=null) { 145 error(out, "Already connected"); 146 return; 147 } 148 149 out.println("Welcome"); 150 151 ((ComputerImpl)computer).setChannel(s.getInputStream(),s.getOutputStream(),null, 152 new Listener() { 153 public void onClosed(Channel channel, IOException cause) { 154 if(cause!=null) 155 LOGGER.log(Level.WARNING, "Connection #"+id+" terminated",cause); 156 try { 157 s.close(); 158 } catch (IOException e) { 159 } 161 } 162 }); 163 } catch (InterruptedException e) { 164 LOGGER.log(Level.WARNING,"Connection #"+id+" aborted",e); 165 try { 166 s.close(); 167 } catch (IOException _) { 168 } 170 } catch (IOException e) { 171 LOGGER.log(Level.WARNING,"Connection #"+id+" failed",e); 172 try { 173 s.close(); 174 } catch (IOException _) { 175 } 177 } 178 } 179 180 private void error(PrintWriter out, String msg) throws IOException { 181 out.println(msg); 182 LOGGER.log(Level.WARNING,"Connection #"+id+" is aborted: "+msg); 183 s.close(); 184 } 185 } 186 187 private static int iotaGen=1; 188 189 private static final Logger LOGGER = Logger.getLogger(TcpSlaveAgentListener.class.getName()); 190 } 191 192 | Popular Tags |