I have long wondered how to run Java programs in Linux without supporting Bash scripts. I did not see an acceptable solution, except for the “bash script payload” method, when a binary file is placed at the end of the script.
But last week I accidentally stumbled upon the kernel module binfmt_misc, with which you can intercept the execution of a file by its magic number. To do this, use own-handler via update-binfmts to get the name of the executable file and the user's arguments.
First discovery
As it turned out in my Ubuntu 16.04, a handler for JAR files is already registered:
update-binfmts --display ... jar (enabled): package = openjdk-8 type = magic offset = 0 magic = PK\x03\x04 mask = interpreter = /usr/bin/jexec detector =
Having given the command
chmod + x foo.bar, I happily rubbed my hands, but the reality turned out to be harsh - the launch of
./foo.jar produced the following:
')
invalid file (bad magic number): Exec format error
Googling, I found a moss-covered bug
bugs.java.com/bugdatabase/view_bug.do?bug_id=6401361 As it turns out,
building via Maven does not add “0xcafe” to the beginning of the JAR file. No less irresponsible is the maven-assembly-plugin plugin. What doesn’t like is / usr / bin / jexec, the registered default handler.
Googling more, I found a solution to the problem through the installation of the jarwrapper package. After installation, the new handler / usr / bin / jarwrapper and insurance / usr / bin / jardetector are added (checks by META-INF that this is really a JAR). But having studied the handler code, I didn’t like a bunch of extra work that the script does when running a lot of helper programs.
Therefore, the decision was its own handler:
Next, open the
sudo gedit / var / lib / binfmts / jar file and register the handler by replacing the line with / usr / bin / jexec with / usr / bin / jarinvoke. In fact, this is a bad decision and it is better to create your own group (more on this below), but it will come down for primary understanding.
For the changes to take effect, you may need to complete:
sudo update-binfmts --disable jar && sudo update-binfmts --enable jar
Then you can run JAR files like any other executable files.
Executable classes
Now you can go ahead and make executable files from Java classes where jarwrapper cannot help. The handler will only work for classes with a default package (that is, classes with a missing package header). It can be done better, but I had enough of this functionality for “scripting” in Java:
After that, we register our own handler (in the same way, you can create a new handler for JARs without editing / usr / bin / jexec):
sudo update-binfmts --package clsinvoke --install clsinvoke /usr/bin/clsinvoke --magic '\xca\xfe\xba\xbe'
Testing:
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World"); } }
javac HellWorld.java chmod +x HelloWorld.class ./HelloWorld.class Hello, World
You can go further by making a more complex handler, which by importing classes will determine which libraries to add to CLASSPATH from ~ / .m2, but this is a separate story. Now an interesting look from the outside, comments, additions, if any. After that, I think to arrange it in the deb package and put everything on the githabe.