Changeset 31699


Ignore:
Timestamp:
2017-05-24T19:59:51+12:00 (7 years ago)
Author:
ak19
Message:

Mac testing of SafeProcess: everything else worked (User comments, documenting editing if server not run from GLI), but discovered issues with canceling a build in progress from GLI. Found out that on Mac, with full-import (but not full-buildcol), subprocesses that are launched by a process don't get terminated when the process is killed. This was a problem in versions of GS before SafeProcess, such as in gs version 3.08. Running the scripts from the commandline had weird behaviour too, but from the cmdline, not only did terminating full-import with a TERM or KILL signal not kill subprocesses (like import.pl) but terminating full-buildcol.pl did not kill subprocesses now either (like buildcol.pl). So resorted to attempting a solution from GLI's Java code as for Windows. First wanted to do a recursive terminate, as for windows, by obtaining the childpids of every process using cmd: pgrep -P pid. However, while full-import.pl would return childpids when built from GLI, full-buildcol had no childpids to return. Mystifyingly, from the cmdline both would return childpids. Then sought and attempted several alternatives that are supposed to kill an entire process tree on Unix systems, but settled on the only one that worked on the mac: pkill -TERM -P pid.

File:
1 edited

Legend:

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

    r31697 r31699  
    751751}
    752752
     753// https://stackoverflow.com/questions/8533377/why-child-process-still-alive-after-parent-process-was-killed-in-linux
     754// Didn't work for when build scripts run from GLI: kill -TERM -pid
     755// but the other suggestion did work: pkill -TERM -P pid did work
     756// https://unix.stackexchange.com/questions/117227/why-pidof-and-pgrep-are-behaving-differently
     757// https://unix.stackexchange.com/questions/67635/elegantly-get-list-of-children-processes
     758// https://stackoverflow.com/questions/994033/mac-os-x-quickest-way-to-kill-quit-an-entire-process-tree-from-within-a-cocoa-a
     759// https://unix.stackexchange.com/questions/132224/is-it-possible-to-get-process-group-id-from-procŧŧ
     760
     761/**
     762 * On Unix, will kill the process denoted by processID and any subprocessed this launched. Tested on a Mac.
     763 * @force if true will send the -KILL (-9) signal, which may result in abrupt termination
     764 * if false, will send the -TERM (-15) signal, which will allow cleanup before termination
     765 * @killEntireTree if false, will terminate only the process denoted by processID, otherwise all descendants too.
     766 * @return true if running the kill process returned an exit value of 0
     767 *
     768*/
     769static boolean killUnixProcessTreeWithID(long processID, boolean force, boolean killEntireTree) {
     770    // Kill signals, their names and numerical equivalents: http://www.faqs.org/qa/qa-831.html
     771
     772    /*
     773    String cmd = force ? "kill -KILL" : "kill -TERM"; // kill -15 vs kill -9
     774    // https://stackoverflow.com/questions/8533377/why-child-process-still-alive-after-parent-process-was-killed-in-linux
     775    cmd += killEntireTree ? " -" : " "; // prefix hyphen to pid to kill all subprocesses launched by pid
     776    cmd = cmd + processID;
     777    */
     778
     779    String cmd = "pkill -TERM -P " + processID;
     780    if(force) {
     781    cmd = "pkill -KILL -P " + processID;
     782    }
     783
     784    SafeProcess proc = new SafeProcess(cmd);
     785    int exitValue = proc.runProcess();
     786    if(exitValue != 0) {
     787    log("@@@ Not able to successfull terminate process, got exitvalue " + exitValue);
     788    log("@@@ Got output " + proc.getStdOutput());
     789    log("@@@ Got err output " + proc.getStdError());
     790    // caller can try again with kill -KILL, by setting force parameter to true
     791    return false;
     792    } else {
     793    if(force) {
     794        log("@@@ Successfully sent SIGKILL to unix process tree rooted at " + processID);
     795    } else {
     796        log("@@@ Successfully sent SIGTERM to unix process tree rooted at " + processID);
     797    }
     798    return true;
     799    }   
     800}
     801
     802/** UNUSED. Kills only the process represented by the processID.
     803 * @force if true will send the -KILL (-9) signal, which may result in abrupt termination
     804 * if false, will send the -TERM (-15) signal, which will allow cleanup before termination
     805 * @return true if running the kill process returned an exit value of 0
     806*/
     807static boolean killUnixProcessWithID(long processID, boolean force) {
     808    // Kill signals, their names and numerical equivalents: http://www.faqs.org/qa/qa-831.html
     809    String cmd = force ? "kill -KILL" : "kill -TERM"; // kill -15 vs kill -9
     810    cmd = cmd + " " + processID;
     811
     812    SafeProcess proc = new SafeProcess(cmd);
     813    int exitValue = proc.runProcess();
     814    if(exitValue != 0) {
     815    log("@@@ Not able to successfull terminate process, got exitvalue " + exitValue);
     816    log("@@@ Got output " + proc.getStdOutput());
     817    log("@@@ Got err output " + proc.getStdError());
     818    // caller can try again with kill -KILL, by setting force parameter to true
     819    return false;
     820    } else {
     821    if(force) {
     822        log("@@@ Successfully sent SIGKILL to unix process " + processID);
     823    } else {
     824        log("@@@ Successfully sent SIGTERM to unix process " + processID);
     825    }
     826    return true;
     827    }   
     828}
     829
    753830
    754831// On linux and mac, p.destroy() suffices to kill processes launched by p as well.
     
    758835    log("### in SafeProcess.destroyProcess(Process p)");
    759836
     837    String osName = Utility.getOSdirName();
     838
    760839    // If it isn't windows, process.destroy() terminates any child processes too
    761     if(!Utility.isWindows()) {
     840    if(osName.equals("linux")) {
    762841    p.destroy();
    763842    return;
    764     }   
    765    
     843    }
     844
     845    if(osName.equals("darwin")) {
     846    long pid = SafeProcess.getProcessID(p);
     847    /*
     848    // On Macs (all Unix?) can't get the child processes of a process once it's been destroyed
     849    macTerminateSubProcessesRecursively(pid, p); // pid, true) 
     850    */
     851   
     852    if(pid == -1) {
     853        p.destroy(); // at minimum, will have no effect if the process had already terminated
     854    } else {
     855        boolean forceKill = false;
     856        boolean killEntireProcessTree = true;
     857        if(!killUnixProcessTreeWithID(pid, !forceKill, killEntireProcessTree)) { // send sig TERM (kill -15 or kill -TERM)
     858        killUnixProcessTreeWithID(pid, forceKill, killEntireProcessTree); // send sig KILL (kill -9 or kill -KILL)
     859        }
     860    }
     861
     862    return;
     863    }
     864   
     865    // else we're on windows:
     866
    766867    if(!SafeProcess.isAvailable("wmic")) {
    767868    log("wmic, used to kill subprocesses, is not available. Unable to terminate subprocesses...");
     
    784885    }
    785886   
     887}
     888
     889
     890// UNUSED
     891// But if this method is needed, then need to parse childpids printed by "pgrep -P pid" and write recursive step
     892// The childpids are probably listed one per line, see https://unix.stackexchange.com/questions/117227/why-pidof-and-pgrep-are-behaving-differently
     893private static void macTerminateSubProcessesRecursively(long parent_pid, Process p) { //boolean isTopLevelProcess) {
     894    log("@@@ Attempting to terminate mac process recursively");
     895
     896    // https://unix.stackexchange.com/questions/67635/elegantly-get-list-of-children-processes
     897    SafeProcess proc = new SafeProcess("pgrep -P "+parent_pid);
     898    int exitValue = proc.runProcess();
     899    String stdOutput = proc.getStdOutput();
     900    String stdErrOutput = proc.getStdError();
     901
     902    // now we have the child processes, can terminate the parent process
     903    if(p != null) { // top level process, can just be terminated the java way with p.destroy()
     904    p.destroy();
     905    } else {
     906    // get rid of process with current pid
     907    if(!SafeProcess.killUnixProcessWithID(parent_pid, false)) { // send kill -TERM, kill -15
     908        SafeProcess.killUnixProcessWithID(parent_pid, true); // send kill -9, kill -KILL
     909    }
     910    }
     911   
     912    /*
     913    // get rid of any process with current pid
     914    if(!isTopLevelProcess && !SafeProcess.killUnixProcessWithID(parent_pid, false)) { // send kill -TERM, kill -15
     915    SafeProcess.killUnixProcessWithID(parent_pid, true); // send kill -9, kill -KILL
     916    }
     917    */
     918
     919    if(stdOutput.trim().equals("") && stdErrOutput.trim().equals("") && exitValue == 1) {
     920    log("No child processes");
     921    // we're done
     922    return;
     923    } else {
     924    log("Got childpids on STDOUT: " + stdOutput);
     925    log("Got childpids on STDERR: " + stdErrOutput);
     926    }
    786927}
    787928
     
    812953    p.destroy();
    813954    } else { // terminate windows way       
    814     SafeProcess.killWinProcessWithID(parent_pid); // get rid off current pid       
     955    SafeProcess.killWinProcessWithID(parent_pid); // get rid of process with current pid       
    815956    }
    816957   
Note: See TracChangeset for help on using the changeset viewer.