Changeset 31699 for main/trunk/gli

Show
Ignore:
Timestamp:
24.05.2017 19:59:51 (2 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.

Files:
1 modified

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