Changeset 31651 for main/trunk


Ignore:
Timestamp:
2017-05-05T21:47:15+12:00 (7 years ago)
Author:
ak19
Message:

Emacs tabbing and 2 extra lines of comments.

File:
1 edited

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   
Note: See TracChangeset for help on using the changeset viewer.