Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Start Shell Script in Java and destroy all processes on Exit

I need some special kind of setup to controll a LED Wall. Sadly i cant really change the programming language i use. My setup looks like this:

Processing (some crazy java fork...) Sketch starts after Boot Process. The Processing Sketch scans a folder for subfolder (other sketches which can be started and controll the LED Wall) and starts a Webserver. The Server renders a List with all the scanned Folders. On Click the "Webserver" launches the selected Sketch via ProcessBuilder. The Processing Sketch looks like this:

import http.*;
import java.util.*;
import java.lang.*;

SimpleHTTPServer server;
String prog = "";
int ExitValue = 1;
ProcessBuilder preparedsketch;
Process runningsketch;

void setup() {
  SimpleHTTPServer.useIndexHtml = false;
  server = new SimpleHTTPServer(this);
  TemplateFileHandler templateHandler = new ResultFiles("index.ftl");
  server.createContext("", templateHandler);
}


class ResultFiles extends TemplateFileHandler {
  public ResultFiles(String templateFileName) {
    super(templateFileName);
  }
  void createMap() {
    Map<String, String> params = queryToMap();
    if (params.containsKey("prog")) {
      if (params.get("prog").equals(prog)) {
        println("Has not changed");
      } else {
        println("PrevProcess: " + runningsketch);
        if (runningsketch != null) {
          println("Killing: " + runningsketch);
          runningsketch.destroy();
        }
        prog = params.get("prog");
        try {
          runningsketch = new ProcessBuilder("/Users/kessleml/dev/pixelpusher/base/processing-quit.sh", "--sketch=/Users/kessleml/dev/pixelpusher/base/sketches/pixelpusher_colourcycle_halloween", "--run").start();
          // runningsketch = new ProcessBuilder("/usr/local/bin/processing-java", "--force", "--sketch=" + sketchPath("sketches/" + prog + "/"), "--no-java", "--run").start();
        } catch (IOException ex) {
          println(ex);
        }
        println("ProjChagned: " + prog);
        println("NewProcess: " + runningsketch);
      }
    }

    File files = new File(sketchPath("sketches"));
    String[] fileslist = files.list();
    addVariable("files", fileslist);
    addVariable("selectedprog", prog);
  }
}

Everything works till now. But of course i want to close a running (and looping) Sketch if i change (click on a other Sketch on the Website). The Problem is:

When i launch a selected Sketch via runninngsketch = new ProcessBuilder("Path/To/ProcessingCLI", "--sketch=Path/To/Selected/Sketch", "--run").start(); more than one process launches. The reason for this, is the ProcessingCLI File:

#!/bin/sh

# Prevents processing-java from stealing focus, see:
# https://github.com/processing/processing/issues/3996.
OPTION_FOR_HEADLESS_RUN=""
for ARG in "$@"
do
    if [ "$ARG" = "--build" ]; then
        OPTION_FOR_HEADLESS_RUN="-Djava.awt.headless=true"
    fi
done

cd "/Applications/Processing.app/Contents/Java" && /Applications/Processing.app/Contents/PlugIns/jdk1.8.0_74.jdk/Contents/Home/jre/bin/java -Djna.nosys=true $OPTION_FOR_HEADLESS_RUN -cp "ant-launcher.jar:ant.jar:core.jar:jna.jar:pde.jar:core/library/core.jar:core/library/gluegen-rt-natives-linux-amd64.jar:core/library/gluegen-rt-natives-linux-armv6hf.jar:core/library/gluegen-rt-natives-linux-i586.jar:core/library/gluegen-rt-natives-macosx-universal.jar:core/library/gluegen-rt-natives-windows-amd64.jar:core/library/gluegen-rt-natives-windows-i586.jar:core/library/gluegen-rt.jar:core/library/jogl-all-natives-linux-amd64.jar:core/library/jogl-all-natives-linux-armv6hf.jar:core/library/jogl-all-natives-linux-i586.jar:core/library/jogl-all-natives-macosx-universal.jar:core/library/jogl-all-natives-windows-amd64.jar:core/library/jogl-all-natives-windows-i586.jar:core/library/jogl-all.jar:modes/java/mode/antlr.jar:modes/java/mode/classpath-explorer-1.0.jar:modes/java/mode/com.ibm.icu.jar:modes/java/mode/JavaMode.jar:modes/java/mode/jdi.jar:modes/java/mode/jdimodel.jar:modes/java/mode/jdtCompilerAdapter.jar:modes/java/mode/jsoup-1.7.1.jar:modes/java/mode/org.eclipse.core.contenttype.jar:modes/java/mode/org.eclipse.core.jobs.jar:modes/java/mode/org.eclipse.core.resources.jar:modes/java/mode/org.eclipse.core.runtime.jar:modes/java/mode/org.eclipse.equinox.common.jar:modes/java/mode/org.eclipse.equinox.preferences.jar:modes/java/mode/org.eclipse.jdt.core.jar:modes/java/mode/org.eclipse.osgi.jar:modes/java/mode/org.eclipse.text.jar:modes/java/mode/org.netbeans.swing.outline.jar" processing.mode.java.Commander "$@"

So the ProcessBuilder starts three processes: One sh-process which launches two Java-Children-Processes. When i use runningsketch.destroy() it only kills the sh process. The two Java-processes continue running. (Not sure if this is also because of this bug: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4770092 since i am developing on MacOS Yosemite. The final Product should run on a Linux Machine.)

My solution was to write a new sh-script which kills all of its children via trap:

#!/bin/sh

OPTION_FOR_HEADLESS_RUN=""

function killAllChildren {
    kill -9 -$(ps -o pgid= $$ | grep -o '[0-9]*')
}

trap killAllChildren SIGTERM SIGKILL
# trap "trap - SIGTERM && kill -- $$" SIGINT SIGTERM EXIT

cd "/Applications/Processing.app/Contents/Java"

/Applications/Processing.app/Contents/PlugIns/jdk1.8.0_74.jdk/Contents/Home/jre/bin/java -Djna.nosys=true $OPTION_FOR_HEADLESS_RUN -cp "ant-launcher.jar:ant.jar:core.jar:jna.jar:pde.jar:core/library/core.jar:core/library/gluegen-rt-natives-linux-amd64.jar:core/library/gluegen-rt-natives-linux-armv6hf.jar:core/library/gluegen-rt-natives-linux-i586.jar:core/library/gluegen-rt-natives-macosx-universal.jar:core/library/gluegen-rt-natives-windows-amd64.jar:core/library/gluegen-rt-natives-windows-i586.jar:core/library/gluegen-rt.jar:core/library/jogl-all-natives-linux-amd64.jar:core/library/jogl-all-natives-linux-armv6hf.jar:core/library/jogl-all-natives-linux-i586.jar:core/library/jogl-all-natives-macosx-universal.jar:core/library/jogl-all-natives-windows-amd64.jar:core/library/jogl-all-natives-windows-i586.jar:core/library/jogl-all.jar:modes/java/mode/antlr.jar:modes/java/mode/classpath-explorer-1.0.jar:modes/java/mode/com.ibm.icu.jar:modes/java/mode/JavaMode.jar:modes/java/mode/jdi.jar:modes/java/mode/jdimodel.jar:modes/java/mode/jdtCompilerAdapter.jar:modes/java/mode/jsoup-1.7.1.jar:modes/java/mode/org.eclipse.core.contenttype.jar:modes/java/mode/org.eclipse.core.jobs.jar:modes/java/mode/org.eclipse.core.resources.jar:modes/java/mode/org.eclipse.core.runtime.jar:modes/java/mode/org.eclipse.equinox.common.jar:modes/java/mode/org.eclipse.equinox.preferences.jar:modes/java/mode/org.eclipse.jdt.core.jar:modes/java/mode/org.eclipse.osgi.jar:modes/java/mode/org.eclipse.text.jar:modes/java/mode/org.netbeans.swing.outline.jar" processing.mode.java.Commander "$@"

But somehow, also this doesnt work. Even with starting the new sh-script and sending for example a SIGTERM to the started sh-process, doesnt destroy the two Java-Processes neither the sh-process.

like image 611
DonHansDampf Avatar asked Oct 30 '22 03:10

DonHansDampf


1 Answers

I found the solution:

Java sends a SIGTERM signal, so i had to trap this signal. But the java/processing file wasnt the problem, the sh-script didn work as intended.

I had to add & wait to the and of my script. Otherwise SIGTERM cant be trapped (see this post: https://apple.stackexchange.com/questions/123631/why-does-a-shell-script-trapping-sigterm-work-when-run-manually-but-not-when-ru).

Also the killing process didn work out right. I have to kill all children, the sh-script itself BUT not the parent-processes of the sh-script (in this use case the webserver etc.). So i wrote a function to find all children processes and kill them. Things like kill -9 -$(ps -o pgid= $$ | grep -o '[0-9]*') didnt work since they killed the whole tree. In the end the sh-file looks like this:

#!/bin/sh

function killAllChildren {
    getChild $$
    pkill -TERM -P $$
}

function getChild() {
    cpids=`pgrep -P $1|xargs`
    for cpid in $cpids;
    do
        kill -15 $cpid
        getChild $cpid
    done
}

trap killAllChildren SIGUSR1 SIGTERM SIGKILL EXIT

cd "/Applications/Processing.app/Contents/Java"

/Applications/Processing.app/Contents/PlugIns/jdk1.8.0_74.jdk/Contents/Home/jre/bin/java -Djna.nosys=true -cp "ant-launcher.jar:ant.jar:core.jar:jna.jar:pde.jar:core/library/core.jar:core/library/gluegen-rt-natives-linux-amd64.jar:core/library/gluegen-rt-natives-linux-armv6hf.jar:core/library/gluegen-rt-natives-linux-i586.jar:core/library/gluegen-rt-natives-macosx-universal.jar:core/library/gluegen-rt-natives-windows-amd64.jar:core/library/gluegen-rt-natives-windows-i586.jar:core/library/gluegen-rt.jar:core/library/jogl-all-natives-linux-amd64.jar:core/library/jogl-all-natives-linux-armv6hf.jar:core/library/jogl-all-natives-linux-i586.jar:core/library/jogl-all-natives-macosx-universal.jar:core/library/jogl-all-natives-windows-amd64.jar:core/library/jogl-all-natives-windows-i586.jar:core/library/jogl-all.jar:modes/java/mode/antlr.jar:modes/java/mode/classpath-explorer-1.0.jar:modes/java/mode/com.ibm.icu.jar:modes/java/mode/JavaMode.jar:modes/java/mode/jdi.jar:modes/java/mode/jdimodel.jar:modes/java/mode/jdtCompilerAdapter.jar:modes/java/mode/jsoup-1.7.1.jar:modes/java/mode/org.eclipse.core.contenttype.jar:modes/java/mode/org.eclipse.core.jobs.jar:modes/java/mode/org.eclipse.core.resources.jar:modes/java/mode/org.eclipse.core.runtime.jar:modes/java/mode/org.eclipse.equinox.common.jar:modes/java/mode/org.eclipse.equinox.preferences.jar:modes/java/mode/org.eclipse.jdt.core.jar:modes/java/mode/org.eclipse.osgi.jar:modes/java/mode/org.eclipse.text.jar:modes/java/mode/org.netbeans.swing.outline.jar" processing.mode.java.Commander "$@" & wait
like image 135
DonHansDampf Avatar answered Nov 02 '22 09:11

DonHansDampf