/* Copyright or (C) or Copr. GET / ENST, Telecom-Paris, Ludovic Apvrille * * ludovic.apvrille AT enst.fr * * This software is a computer program whose purpose is to allow the * edition of TURTLE analysis, design and deployment diagrams, to * allow the generation of RT-LOTOS or Java code from this diagram, * and at last to allow the analysis of formal validation traces * obtained from external tools, e.g. RTL from LAAS-CNRS and CADP * from INRIA Rhone-Alpes. * * This software is governed by the CeCILL license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL license and that you accept its terms. */ package launcher; import myutil.TraceManager; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Class ExecutionThread * For remote execution of processes * Creation: 2001 * @version 1.1 01/12/2003 * @author Ludovic APVRILLE */ class ExecutionThread extends Thread { private static final String ERROR_KEY = "error="; private static final Pattern ERROR_PATTERN = Pattern.compile( ".*(" + ERROR_KEY + "\\d+,).*" ); private final String cmd; private final int port; private final RshServer rsh; private ServerSocket server;// = null; private boolean go; private BufferedReader proc_in; private BufferedReader proc_err; private Process proc; //private boolean piped; private ExecutionThread parentExecThread; private boolean mustWaitForPiped; private OutputStream pipe; private boolean isStarted = false; private boolean sendReturnCode; private Integer returnCode; public ExecutionThread( final String _cmd, final int startPortnumber, final RshServer _rsh ) { cmd = _cmd; //port = _port; rsh = _rsh; server = null; go = true; sendReturnCode = false; returnCode = null; mustWaitForPiped = false; parentExecThread = null; pipe = null; proc = null; proc_in = null; proc_err = null; port = findPortNumber( startPortnumber ); } public Integer getReturnCode() { return returnCode; } public boolean isSendReturnCode() { return sendReturnCode; } public void setSendReturnCode(boolean sendReturnCode) { this.sendReturnCode = sendReturnCode; } public boolean isStarted() { return isStarted; } public void setPiped(ExecutionThread _et) { parentExecThread = _et; //piped = true; } public void setWaitForPipe() { mustWaitForPiped = true; } public synchronized void waitingForPipe() { while(pipe == null) { try { TraceManager.addDev("Waiting for pipe"); wait(); } catch (InterruptedException ie) { } } } public synchronized void setMyPipe( final OutputStream os ) { pipe = os; notifyAll(); } public int getPort() { return port; } private int findPortNumber( final int startPortnumber ) { for( int i = startPortnumber + 1; i < startPortnumber + 1000; i++ ) { try { server = new ServerSocket(i); server.setSoTimeout(60000); return i; //return; } catch (Exception e) { } } return startPortnumber; } private Socket waitForClient() { TraceManager.addDev( "process # " + port + " is waiting for client..." ); try { final Socket socket = server.accept(); TraceManager.addDev( "Processe # " + port + " got client." ); return socket; } catch (Exception e) { TraceManager.addError( e ); return null; } } public void closeConnect(Socket s) { try { s.close(); } catch (IOException io) { TraceManager.addError( io ); } } public void stopProcess() { go = false; // Issue #18: It may happen that the process is requested to be stopped before it had time to start // in which case it will be null if ( proc != null /*&& proc.isAlive()*/ ) { proc.destroy(); } if ( proc_in != null ) { try { proc_in.close(); } catch (IOException e) { e.printStackTrace(); } proc_in = null; } if ( proc_err != null ) { try { proc_err.close(); } catch (IOException e) { e.printStackTrace(); } proc_err = null; } } private void respond( final PrintStream out, final ResponseCode code, final String message ) { SocketComHelper.send( out, code, message); //try { //out.println( co de.name() + message ); //out.flush(); // } catch ( IOException e) { // } } /** * Issue #35: Handle the case where the executable to be run does not exists or is not accessible * @param ex * @param out * @throws InterruptedException */ private void handleReturnCode( final IOException ex, final PrintStream out ) throws InterruptedException { if ( ex.getMessage() == null ) { returnCode = - 1; } else { returnCode = parseErrorCode( ex.getMessage() ); } final String message = "Error executing command " + cmd + " with return code " + returnCode + "."; TraceManager.addError( message ); respond( out, ResponseCode.PROCESS_OUTPUT, message ); respond( out, ResponseCode.PROCESS_OUTPUT, ex.getMessage() ); respond( out, ResponseCode.PROCESS_END, null );//"5"); } private Integer parseErrorCode( final String message ) { final Matcher matcher = ERROR_PATTERN.matcher( message ); if ( matcher.matches() ) { if ( matcher.groupCount() > 1 ) { final String expr = matcher.group( 1 ); final String errorNum = expr.substring( ERROR_KEY.length(), expr.length() - 1 ); return Integer.decode( errorNum ); } } return -1; } private void handleReturnCode( PrintStream out ) throws InterruptedException { if ( sendReturnCode ) { returnCode = proc.waitFor(); final String message = "Ended command " + cmd + " with return code " + returnCode + "."; TraceManager.addDev( message ); respond( out, ResponseCode.PROCESS_OUTPUT, message ); respond( out, ResponseCode.PROCESS_END, null );//"5"); } else { returnCode = null; respond( out, ResponseCode.PROCESS_END, null );//"5"); } } @Override public void run() { isStarted = true; TraceManager.addDev( "Starting process for command " + cmd ); proc = null; // BufferedReader in = null; //String str; try { // print output in pipe if ( mustWaitForPiped ) { //try { proc = Runtime.getRuntime().exec( cmd ); if ( parentExecThread != null ) { TraceManager.addDev( "Giving my pipe to the other..." ); parentExecThread.setMyPipe( proc.getOutputStream() ); } TraceManager.addDev( "Waiting for pipe..." ); waitingForPipe(); TraceManager.addDev( "Got pipe." ); proc_in = new BufferedReader( new InputStreamReader( proc.getInputStream() ) ); proc_err = new BufferedReader( new InputStreamReader( proc.getErrorStream() ) ); String str; try { while ( go && ( str = proc_in.readLine() ) != null ) { TraceManager.addDev( "Writing " + str + " to pipe..." ); pipe.write( ( str + "\n" ).getBytes() ); } while ( go && ( str = proc_err.readLine() ) != null ) { TraceManager.addError( "Writing " + str + " to pipe..." ); pipe.write( (str + "\n").getBytes() ); } // } // catch (IOException e) { // TraceManager.addError( e ); // } // } // catch (Exception e) { // TraceManager.addError("Exception [" + e.getMessage() + "] occured when executing " + cmd, e ); // } // try { } catch (IOException e) { TraceManager.addError("Exception [" + e.getMessage() + "] occured when executing " + cmd + "!", e ); } finally { pipe.flush(); pipe.close(); } TraceManager.addDev( "Ending piped command " + cmd + "..." ); } else { // print output on socket Socket s = waitForClient(); if (s == null) { TraceManager.addDev("Client did not connect on time!"); rsh.removeProcess( this ); return; } // TraceManager.addDev("Going to start command " + cmd + "..." ); final PrintStream out = new PrintStream( s.getOutputStream(), true ); try { proc = Runtime.getRuntime().exec(cmd); if ( parentExecThread != null ) { // TraceManager.addDev( "Giving my pipe to the other..." ); parentExecThread.setMyPipe( proc.getOutputStream() ); } proc_in = new BufferedReader( new InputStreamReader( proc.getInputStream() ) ); String str; //TraceManager.addDev("Reading the output stream of the process " + cmd); while ( go && ( str = proc_in.readLine() ) != null ) { // TraceManager.addDev( "Sending " + str + " from " + port + " to client..." ); respond( out, ResponseCode.PROCESS_OUTPUT, str ); } proc_err = new BufferedReader(new InputStreamReader(proc.getErrorStream())); while ( go && ( str = proc_err.readLine() ) != null ) { TraceManager.addError( str ); respond( out, ResponseCode.PROCESS_OUTPUT_ERROR, str ); } handleReturnCode( out ); } catch ( final IOException ex ) { handleReturnCode( ex, out ); } finally { if ( s != null ) { closeConnect( s ); } } } } catch ( Throwable ex ) { TraceManager.addError( "Exception occured when executing " + cmd, ex ); } finally { if ( proc != null ) { proc.destroy(); } if ( !sendReturnCode ) { rsh.removeProcess(this); } } } }