Changeset 31651 for main/trunk/gli

Show
Ignore:
Timestamp:
05.05.2017 21:47:15 (3 years ago)
Author:
ak19
Message:

Emacs tabbing and 2 extra lines of comments.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • main/trunk/gli/src/org/greenstone/gatherer/util/SafeProcess.java

    r31650 r31651  
    3838    public static final int STDOUT = 1; 
    3939    public static final int STDIN = 2; 
    40     // can't make this variable final and init in a static block, because it needs to use other SafeProcess static methods which rely on this in turn: 
    41     public static String WIN_KILL_CMD;  
     40    // can't make this variable final and init in a static block, because it needs to use other SafeProcess static methods which rely on this in turn: 
     41    public static String WIN_KILL_CMD;  
    4242         
    4343     
     
    118118    } 
    119119 
    120     // In future, think of changing the method doRuntimeExec() over to using ProcessBuilder 
    121     // instead of Runtime.exec(). ProcessBuilder seems to have been introduced from Java 5. 
    122     // https://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html 
    123     // https://zeroturnaround.com/rebellabs/how-to-deal-with-subprocesses-in-java/ 
    124     // which suggests using Apache Common Exec to launch processes and says what will be forthcoming in Java 9 
     120    // In future, think of changing the method doRuntimeExec() over to using ProcessBuilder 
     121    // instead of Runtime.exec(). ProcessBuilder seems to have been introduced from Java 5. 
     122    // https://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html 
     123    // https://zeroturnaround.com/rebellabs/how-to-deal-with-subprocesses-in-java/ 
     124    // which suggests using Apache Common Exec to launch processes and says what will be forthcoming in Java 9 
     125     
    125126    private Process doRuntimeExec() throws IOException { 
    126127    Process prcs = null; 
     
    550551public static long getProcessID(Process p) 
    551552{ 
    552     long pid = -1; 
    553     try 
    554     { 
    555         //for windows 
    556         if (p.getClass().getName().equals("java.lang.Win32Process") || 
    557                p.getClass().getName().equals("java.lang.ProcessImpl"))  
    558         { 
    559             Field f = p.getClass().getDeclaredField("handle"); 
    560             f.setAccessible(true);               
    561             long handl = f.getLong(p); 
    562             Kernel32 kernel = Kernel32.INSTANCE; 
    563             WinNT.HANDLE hand = new WinNT.HANDLE(); 
    564             hand.setPointer(Pointer.createConstant(handl)); 
    565             pid = kernel.GetProcessId(hand); 
    566             f.setAccessible(false); 
    567         } 
    568         //for unix based operating systems 
    569         else if (p.getClass().getName().equals("java.lang.UNIXProcess"))  
    570         { 
    571             Field f = p.getClass().getDeclaredField("pid"); 
    572             f.setAccessible(true); 
    573             pid = f.getLong(p); 
    574             f.setAccessible(false); 
    575         } 
    576     } 
    577     catch(Exception ex) 
    578     { 
    579         log("SafeProcess.getProcessID(): Exception when attempting to get process ID for process " + ex.getMessage(), ex);   
    580         pid = -1; 
    581     } 
    582     return pid; 
     553    long pid = -1; 
     554    try { 
     555    //for windows 
     556    if (p.getClass().getName().equals("java.lang.Win32Process") || 
     557        p.getClass().getName().equals("java.lang.ProcessImpl"))  
     558        { 
     559        Field f = p.getClass().getDeclaredField("handle"); 
     560        f.setAccessible(true);               
     561        long handl = f.getLong(p); 
     562        Kernel32 kernel = Kernel32.INSTANCE; 
     563        WinNT.HANDLE hand = new WinNT.HANDLE(); 
     564        hand.setPointer(Pointer.createConstant(handl)); 
     565        pid = kernel.GetProcessId(hand); 
     566        f.setAccessible(false); 
     567        } 
     568    //for unix based operating systems 
     569    else if (p.getClass().getName().equals("java.lang.UNIXProcess"))  
     570        { 
     571        Field f = p.getClass().getDeclaredField("pid"); 
     572        f.setAccessible(true); 
     573        pid = f.getLong(p); 
     574        f.setAccessible(false); 
     575        } 
     576 
     577    } catch(Exception ex) { 
     578    log("SafeProcess.getProcessID(): Exception when attempting to get process ID for process " + ex.getMessage(), ex);   
     579    pid = -1; 
     580    } 
     581    return pid; 
    583582} 
    584  
     583     
    585584 
    586585// stackoverflow.com/questions/1835885/send-ctrl-c-to-process-open-by-java   
     
    589588// Searching for: "forcibly terminate external process launched by Java on Windows"  
    590589static void killWinProcessWithID(long processID) { 
    591      
    592     String cmd = SafeProcess.getWinProcessKillCmd(processID); 
    593     if (cmd == null) return; 
    594      
    595     try{         
    596         log("\tAttempting to terminate Win subprocess with pid: " + processID); 
    597         SafeProcess proc = new SafeProcess(cmd);             
    598         int exitValue = proc.runProcess(); // no IOstreams for Taskkill, but for "wmic process pid delete" 
    599             // there is output that needs flushing, so don't use runBasicProcess() 
     590     
     591    String cmd = SafeProcess.getWinProcessKillCmd(processID); 
     592    if (cmd == null) return; 
     593     
     594    try {        
     595    log("\tAttempting to terminate Win subprocess with pid: " + processID); 
     596    SafeProcess proc = new SafeProcess(cmd);             
     597    int exitValue = proc.runProcess(); // no IOstreams for Taskkill, but for "wmic process pid delete" 
     598    // there is output that needs flushing, so don't use runBasicProcess() 
    600599             
    601     } catch(Exception e) { 
    602         log("@@@ Exception attempting to stop perl " + e.getMessage(), e);       
    603     } 
     600    } catch(Exception e) { 
     601    log("@@@ Exception attempting to stop perl " + e.getMessage(), e);       
     602    } 
    604603} 
    605604 
     
    609608// e.g. full-import.pl may be terminated with p.destroy(), but it launches import.pl which is left running until it naturally terminates. 
    610609static void destroyProcess(Process p) { 
    611     // If it isn't windows, process.destroy() terminates any child processes too 
    612     if(!Utility.isWindows()) { 
    613         p.destroy(); 
    614         return; 
    615     }    
    616  
    617     if(!SafeProcess.isAvailable("wmic")) { 
    618         log("wmic, used to kill subprocesses, is not available. Unable to terminate subprocesses..."); 
    619         log("Kill them manually from the TaskManager or they will proceed to run to termination"); 
    620          
    621         // At least we can get rid of the top level process we launched 
    622         p.destroy(); 
    623         return; 
    624     }    
    625      
    626     // get the process id of the process we launched, 
    627     // so we can use it to find the pids of any subprocesses it launched in order to terminate those too. 
    628      
    629     long processID = SafeProcess.getProcessID(p);        
    630     log("Attempting to terminate sub processes of Windows process with pid " + processID); 
    631     terminateSubProcessesRecursively(processID, p); 
    632      
     610    // If it isn't windows, process.destroy() terminates any child processes too 
     611    if(!Utility.isWindows()) { 
     612    p.destroy(); 
     613    return; 
     614    }    
     615     
     616    if(!SafeProcess.isAvailable("wmic")) { 
     617    log("wmic, used to kill subprocesses, is not available. Unable to terminate subprocesses..."); 
     618    log("Kill them manually from the TaskManager or they will proceed to run to termination"); 
     619     
     620    // At least we can get rid of the top level process we launched 
     621    p.destroy(); 
     622    return; 
     623    }    
     624     
     625    // get the process id of the process we launched, 
     626    // so we can use it to find the pids of any subprocesses it launched in order to terminate those too. 
     627     
     628    long processID = SafeProcess.getProcessID(p);        
     629    log("Attempting to terminate sub processes of Windows process with pid " + processID); 
     630    terminateSubProcessesRecursively(processID, p); 
     631     
    633632} 
    634633 
     
    642641private static void terminateSubProcessesRecursively(long parent_pid, Process p) { 
    643642     
    644     // Use Windows wmic to find the pids of any sub processes launched by the process denoted by parent_pid  
    645     SafeProcess proc = new SafeProcess("wmic process where (parentprocessid="+parent_pid+") get processid"); 
    646     proc.setSplitStdOutputNewLines(true); // since this is windows, splits lines by \r\n 
    647     int exitValue = proc.runProcess(); // exitValue (%ERRORLEVEL%) is 0 either way.  
    648     //log("@@@@ Return value from proc: " + exitValue); 
    649      
    650     // need output from both stdout and stderr: stderr will say there are no pids, stdout will contain pids 
    651     String stdOutput = proc.getStdOutput(); 
    652     String stdErrOutput = proc.getStdError(); 
    653      
    654      
    655     // Now we know the pids of the immediate subprocesses, we can get rid of the parent process 
    656     if(p != null) { // we're the top level process, terminate java way 
    657         p.destroy(); 
    658     } else { // terminate windows way        
    659         SafeProcess.killWinProcessWithID(parent_pid); // get rid off current pid         
    660     } 
    661      
    662     // parse the output to get the sub processes' pids   
    663     // Output looks like: 
    664     // ProcessId 
    665     // 6040 
    666     // 180 
    667     // 4948 
    668     // 1084 
    669     // 6384 
    670     // If no children, then STDERR output starts with the following, possibly succeeded by empty lines: 
    671     // No Instance(s) Available. 
    672      
    673     // base step of the recursion 
    674     if(stdErrOutput.indexOf("No Instance(s) Available.") != -1) {  
    675         //log("@@@@ Got output on stderr: " + stdErrOutput); 
    676         // No further child processes. And we already terminated the parent process, so we're done 
    677         return; 
    678     } else { 
    679         //log("@@@@ Got output on stdout:\n" + stdOutput); 
    680      
    681         // http://stackoverflow.com/questions/691184/scanner-vs-stringtokenizer-vs-string-split  
    682      
    683         // find all childprocesses for that pid and terminate them too: 
    684         Stack<Long> subprocs = new Stack<Long>(); 
    685         Scanner sc = new Scanner(stdOutput); 
    686         while (sc.hasNext()) { 
    687           if(!sc.hasNextLong()) { 
    688               sc.next(); // discard the current token since it's not a Long 
    689           } else { 
    690             long child_pid = sc.nextLong(); 
    691             subprocs.push(new Long(child_pid));          
    692           } 
    693         } 
    694         sc.close();      
    695          
    696         // recursion step if subprocs is not empty (but if it is empty, then it's another base step) 
    697         if(!subprocs.empty()) { 
    698             long child_pid = subprocs.pop().longValue(); 
    699             terminateSubProcessesRecursively(child_pid, null); 
    700         }    
    701     } 
     643    // Use Windows wmic to find the pids of any sub processes launched by the process denoted by parent_pid  
     644    SafeProcess proc = new SafeProcess("wmic process where (parentprocessid="+parent_pid+") get processid"); 
     645    proc.setSplitStdOutputNewLines(true); // since this is windows, splits lines by \r\n 
     646    int exitValue = proc.runProcess(); // exitValue (%ERRORLEVEL%) is 0 either way.  
     647    //log("@@@@ Return value from proc: " + exitValue); 
     648     
     649    // need output from both stdout and stderr: stderr will say there are no pids, stdout will contain pids 
     650    String stdOutput = proc.getStdOutput(); 
     651    String stdErrOutput = proc.getStdError(); 
     652     
     653     
     654    // Now we know the pids of the immediate subprocesses, we can get rid of the parent process 
     655    // We know the children remain running: since the whole problem on Windows is that these 
     656    // child processes remain running as orphans after the parent is forcibly terminated. 
     657    if(p != null) { // we're the top level process, terminate the java way 
     658    p.destroy(); 
     659    } else { // terminate windows way        
     660    SafeProcess.killWinProcessWithID(parent_pid); // get rid off current pid         
     661    } 
     662     
     663    // parse the output to get the sub processes' pids   
     664    // Output looks like: 
     665    // ProcessId 
     666    // 6040 
     667    // 180 
     668    // 4948 
     669    // 1084 
     670    // 6384 
     671    // If no children, then STDERR output starts with the following, possibly succeeded by empty lines: 
     672    // No Instance(s) Available. 
     673     
     674    // base step of the recursion 
     675    if(stdErrOutput.indexOf("No Instance(s) Available.") != -1) {  
     676    //log("@@@@ Got output on stderr: " + stdErrOutput); 
     677    // No further child processes. And we already terminated the parent process, so we're done 
     678    return; 
     679    } else { 
     680    //log("@@@@ Got output on stdout:\n" + stdOutput); 
     681     
     682    // http://stackoverflow.com/questions/691184/scanner-vs-stringtokenizer-vs-string-split  
     683     
     684    // find all childprocesses for that pid and terminate them too: 
     685    Stack<Long> subprocs = new Stack<Long>(); 
     686    Scanner sc = new Scanner(stdOutput); 
     687    while (sc.hasNext()) { 
     688        if(!sc.hasNextLong()) { 
     689        sc.next(); // discard the current token since it's not a Long 
     690        } else { 
     691        long child_pid = sc.nextLong(); 
     692        subprocs.push(new Long(child_pid));          
     693        } 
     694    } 
     695    sc.close();      
     696     
     697    // recursion step if subprocs is not empty (but if it is empty, then it's another base step) 
     698    if(!subprocs.empty()) { 
     699        long child_pid = subprocs.pop().longValue(); 
     700        terminateSubProcessesRecursively(child_pid, null); 
     701    }    
     702    } 
    702703} 
    703704 
    704705// This method should only be called on a Windows OS 
    705706private static String getWinProcessKillCmd(Long processID) { 
    706     // check if we first need to init WIN_KILL_CMD. We do this only once, but can't do it in a static codeblock 
    707      
    708     if(WIN_KILL_CMD == null) {       
    709         if(SafeProcess.isAvailable("wmic")) { 
    710             // https://isc.sans.edu/diary/Windows+Command-Line+Kung+Fu+with+WMIC/1229 
    711             WIN_KILL_CMD = "wmic process _PROCID_ delete"; // like "kill -9" on Windows 
    712         } 
    713         else if(SafeProcess.isAvailable("taskkill")) { // check if we have taskkill or else use the longstanding tskill 
    714          
    715             WIN_KILL_CMD = "taskkill /f /t /PID _PROCID_"; // need to forcefully /f terminate the process                
    716                 //  /t "Terminates the specified process and any child processes which were started by it." 
    717                 // But despite the /T flag, the above doesn't kill subprocesses. 
    718         } 
    719         else { //if(SafeProcess.isAvailable("tskill")) { can't check availability since "which tskill" doesn't ever succeed 
    720             WIN_KILL_CMD = "tskill _PROCID_"; // https://ss64.com/nt/tskill.html 
    721         }        
    722     } 
    723      
    724     if(WIN_KILL_CMD == null) { // can happen if none of the above cmds were available 
    725         return null; 
    726     } 
    727     return WIN_KILL_CMD.replace( "_PROCID_", Long.toString(processID) ); 
     707    // check if we first need to init WIN_KILL_CMD. We do this only once, but can't do it in a static codeblock 
     708     
     709    if(WIN_KILL_CMD == null) {       
     710    if(SafeProcess.isAvailable("wmic")) { 
     711        // https://isc.sans.edu/diary/Windows+Command-Line+Kung+Fu+with+WMIC/1229 
     712        WIN_KILL_CMD = "wmic process _PROCID_ delete"; // like "kill -9" on Windows 
     713    } 
     714    else if(SafeProcess.isAvailable("taskkill")) { // check if we have taskkill or else use the longstanding tskill 
     715         
     716        WIN_KILL_CMD = "taskkill /f /t /PID _PROCID_"; // need to forcefully /f terminate the process                
     717            //  /t "Terminates the specified process and any child processes which were started by it." 
     718            // But despite the /T flag, the above doesn't kill subprocesses. 
     719    } 
     720    else { //if(SafeProcess.isAvailable("tskill")) { can't check availability since "which tskill" doesn't ever succeed 
     721        WIN_KILL_CMD = "tskill _PROCID_"; // https://ss64.com/nt/tskill.html 
     722    }        
     723    } 
     724     
     725    if(WIN_KILL_CMD == null) { // can happen if none of the above cmds were available 
     726    return null; 
     727    } 
     728    return WIN_KILL_CMD.replace( "_PROCID_", Long.toString(processID) ); 
    728729} 
    729730 
     
    735736// On windows, "which tskill" fails but "which" succeeds on taskkill|wmic|browser names. 
    736737public static boolean isAvailable(String program) {      
    737     try { 
    738         // On linux `which bla` does nothing, prompt is returned; on Windows, it prints "which: no bla in" 
    739         // `which grep` returns a line of output with the path to grep. On windows too, the location of the program is printed 
    740         SafeProcess prcs = new SafeProcess("which " + program);      
    741         prcs.runProcess(); 
    742         String output = prcs.getStdOutput(); 
    743         if(output.equals("")) { 
    744         return false; 
    745         } 
    746         //System.err.println("*** 'which " + program + "' returned: " + output); 
    747         return true; 
    748     } catch (Exception exc) { 
     738    try { 
     739    // On linux `which bla` does nothing, prompt is returned; on Windows, it prints "which: no bla in" 
     740    // `which grep` returns a line of output with the path to grep. On windows too, the location of the program is printed 
     741    SafeProcess prcs = new SafeProcess("which " + program);      
     742    prcs.runProcess(); 
     743    String output = prcs.getStdOutput(); 
     744    if(output.equals("")) { 
    749745        return false; 
    750746    } 
     747    //System.err.println("*** 'which " + program + "' returned: " + output); 
     748    return true; 
     749    } catch (Exception exc) { 
     750    return false; 
     751    } 
    751752}    
    752753