Execute shell command in Tomcat - java

So I have the following problem: I have a web service running inside a Tomcat7 server on Linux. The web service however has to execute some commands (mostly file operations such as copy and mount). Copy I've replaced with java.nio, but I don't think that there is a replacement for mount.
So I'm trying to execute shell commands out of my Tomcat Java process. Unfortunately it doesn't execute my commands. I've implemented the execution of shell commands in Java before. So my code should be correct:
Process pr = Runtime.getRuntime().exec("mount -o loop -t iso9660 <myimage> <mymountpoint>");
pr.waitFor();
<myimage> and <mymountpoint> are absolute paths, so no issues there either.
I've debugged my commands and they are working when executed on the console.
I've tried sending other commands. Simple commands such as id and pwd are working!
I've tried using /bin/bash -c "<command>", which didn't work.
I've tried executing a shell script, which executes the command, which didn't work.
I've tried escaping the spaces in my command, which didn't work.
So I've digged even deeper and now I'm suspecting some Tomcat security policy (Sandbox?), which prevents me from executing the command. Since security is no issue for me (it's an internal system, completely isolated from the outside world), I've tried a hack, which became quite popular just recently:
System.setSecurityManager(null);
This didn't work either. I'm using Java7 and Tomcat7 on RHEL6. Tomcat7 is just extracted! I don't have any files in /etc/.. or any other folder than /opt/tomcat/, where I've extracted the zip from the Tomcat home page. I've searched the /opt/tomcat/conf folder for security settings, but all I could find was the file catalina.policy, where it didn't seem like I could set some security level for shell commands.
Any ideas?

A few things:
System.setSecurityManager(null);
you have just killed the security of your application.
Yes, Tomcat is running as root. If I execute id I'm root as well.
Fix this immediately!
Now on to the question. You shouldnt have Tomcat executing anything, you need to defer this to a separate process whether that be a shell script or another Java program. This should also remove what (I hope) was a dependency on root running Tomcat. It should be possible to perform this command as a non-privileged user that cannot log into the system normally. You would do this by configuring /etc/fstab and supplying that same user the permissions to do this. From a pure security POV the process that mounts should not be owned by the tomcat user. Nor should the tomcat user ever be root. So to recap:
1) Stop running Tomcat as root
2) Create a separate process outside of the context of Tomcat to run this mount
3) Create a tomcat user, this user should not be able to log into the system nor should it be a privileged user (admin,super user, etc)
4) Create a process user, this user should be configured exactly as the tomcat user
5) Edit /etc/fstab giving the process user the necessary permissions to mount correctly.

It's generally a bad idea to use the single-string form of Runtime.exec. A better option is to use ProcessBuilder, and split up the arguments yourself rather than relying on Java to split them for you (which it does very naïvely).
ProcessBuilder pb = new ProcessBuilder("/bin/mount", "-o", "loop", /*...*/);
pb.redirectErrorStream(true); // equivalent of 2>&1
Process p = pb.start();
You say you're on RHEL so do you have selinux active? Check your logs and see if this is what's blocking you (I think it's audit.log you're looking for, it's been a few years since I've used selinux). If this does turn out to be the problem then you should probably ask on superuser or serverfault rather than SO...

I'm not sure if that's the problem you are having, but I've seen issues when Runtime.exec() is used without reading the associated output buffers. You can find a detailed explanation and potential solutions here. Reading the output and error streams can also help you figure out what's going on at the OS level when you run the command.

I've recently had to do something like this from a Swing app.
You'll probably be able to pull it off with ProcessBuilder, as in Ian's answer, but I found that once things start to get complex, it's easier to write a shell script that does what you want, enabling you to pass as few parameters as possible. Then use ProcessBuilder to invoke the shell script.
If you're invoking anything that has more than really minimal output, you'll also have to read the output and error streams to keep the process from blocking when the output buffers fill, as it seems you are already doing.

I use sudo -S before command and for the tomcat7 user: tomcat7 ALL=(ALL) NOPASSWD:ALL

Related

Can't run exe file using ProcessBuilder depending of computer

I have come across a problem while working with Process and ProcessBuilder in Java.
I have a line of code looking like this :
Process process = new ProcessBuilder("des.exe", String... params);
It works fine on my personal computer, but not on my testing server and I can't figure out why.
The ErrorStream give me this:
des.exe: MZ����#���: not found
des.exe: Syntax error: "(" unexpected
Both computers are on Ubuntu 16.04.4 LTS with java 1.8.
Any idea where it might come from?
The most likely reason for the difference is that the file format of windows executables is not registered with the binfmt_misc handler in the kernel, which allows the kernel to execute various programs through helpers transparently. The error Syntax error: "(" unexpected typically means that the file got handed off to /bin/sh, instead of to wine.
So, check if the binfmt_misc file system is mounted (most modern linuxes do this automatically):
grep binfmt_misc /proc/self/mountinfo
should return some entries (might be autofs, might be binfmt_misc depends on the distro).
if it isn't then mount it:
sudo mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc
you can tell what's registered by looking at the content of this directory. To register for running dos executables with wine you need to register it:
echo ':DOSWin:M::MZ::/usr/bin/wine:' | sudo tee /proc/sys/fs/binfmt_misc/register
Once registered, executing the binary should happen transparently, and it should succeed through the ProcessBuilder.
Configuring this for the server at boot time is left as an exercise to the user.
If you want to skip this entire mess, then you can change the execution from des.exe to wine with the first argument of des.exe.

How to restart service when it get stopped in Linux [duplicate]

This question already has answers here:
How to Daemonize a Java Program?
(11 answers)
Closed 7 years ago.
I have built a little daemon in Java and I would like to run it as a service under Unix (e.g. Debian 5). I have read that there is a possibility of using a Java wrapper, but isn't there any other option which is easier to implement? Can't I just use a Unix command such as xxx java -jar program.jar?
Well, if you want to run your java program even when you exit out of your shell, the following is the most simple way:
$nohup java -jar program.jar &
You need to create an appropriate script in /etc/init.d and link it to /etc/rcX.d directories. The script should support at least start, stop, and status parameters. During start it should run java command with appropriate arguments, probably via nohup java <arguments> &. Then you should save PID of your newly-started process to file /var/run/yourservice.pid. stop command should read this PID file and kill this service.
The details vary from distribution to distribution, most distributions provide some macros to make whole job easier. It's best to look at examples of other services in /etc/init.d for your distribution.
Additionally:
If your service isn't accessed from other computers from the network, but it opens some port, make it unavailable with firewall.
If your service processes some 'delicate' data, it's good to add another user and invoke an appropriate sudo command in your /etc/init.d file.
You can start it as:
java -jar program.jar
Unix daemons are normally started by init or started by a script in /etc/init.d or /etc/rc.d, and started at specific runlevels - normally by soft links in /etc/rcX.d. (where X is the intended "runlevel" which is normally 3.
I think debian are moving to using "upstart", a init-replacement. It uses config files in /etc/init to define jobs, and they are quite easy to write. Check that out.
Daemons traditionally closes stdin, sdtout and stderr, and does a "double fork" when starting, in order to detach from the session and also to signal that they are ready to handle whatever they should handle. This is not really necessary, as long as the daemon is not started from the terminal.
If you want a simple shell wrapper to start you program; you just need to write a small shell script:
#!/bin/sh
/full/path/to/java -jar /full/path/to/program.jar
... and make it executable (chmod 755 )
This article contains a few useful tricks for running a Java application as a daemon:
http://barelyenough.org/blog/2005/03/java-daemon/
Alternatively, you can have a look at the Apache Commons Daemon project, although this requires native code (Unix and Win32 supported):
http://commons.apache.org/daemon/
You can use a cron job to schedule your program. You can also check out this article for details on how to run scripts on startup. You can write a script that runs your java program and run it on startup as mentioned in the article.

Find name of .jar in java(w).exe process list

In my company environment, there's this script that runs on plenty computers and that I occasionally ask the users to kill because it's known to lock up from time to time.
I was thinking of a way to kill it myself. I've noticed I can remotely list using pslist and then killing it using pskill.
Now the problem is, when somebody's running multiple java applications (ex. Eclipse, this application, another java.exe app,...) it becomes tricky to kill the correct application in the pslist, that would look something like this:
javaw 4214 .. ...
javaw 5000 .. ...
And so on. These are different applications, but they all run from javaw.exe. Is there a way of finding out the name of the .jar they are running, so I can kill the process based on that?
You can list java processes with
jps
or
jps -v
Jps is a tool provided with JDK and JRE, you'll find it in JDK_HOME/bin.
Option -v shows additional info (JVM start parameters)
You can use jps. It alone doesn't provide much helpful information to identify the java application. You need little bit extra details.
You can use the following options with jps to print the complete command line arguments passed to the java appliation (JVM arguments and main method arguments),
jps -lvm
Reference: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html

Wrapping a jar file in a Windows service

Have you had experience with running a jar file using a command line, wrapped in a Windows service?
I'm trying to find a way to run a jar file without being logged into the machine, and since it allows command shell, I was wondering if it's a good idea.
Thanks!
Original Post:
I'm trying to run Associated Press's Web Feeds Manager, which is basically a jar file that can be run when logged in by double clicking it.
I'd like to run the same file but without being logged in to the machine. In their manual (http://wfm.ap.org/admin/content/help/Running_Agent_on_a_Remote_Server.htm) they write how to do that, using a commandline parameter.
Basically I'd like the jar to run as a Windows service, regardless of who's logged in, but Googling it showed it was problematic.
Have you had experience with remotely running jar files? What are the pitfalls?
Thanks!
On a google search, I came across this article -
Running Jar Applications as a Windows Service
It mentions about open source Java Service Wrapper project from Tanukisoftware.org for accomplishing this task.
Note: I've not used this personally.
If you are not interested in having the service started/stopped at boot/shutdown, but you just want the program to be started manually and keep running after logout, here is what you do:
$ nohup java -jar foobar.jar > foobar.log 2>&1 &
which means: start my foobar.jar (java -jar) and keep it running after I logout (nohup) redirect stdout to foobar.log (>) and also the stderr (2>&1), and make it running in background (& at the end).
Instead, if you are interested in installing a "service" in your linux box, there are many options, depending on what distribution you are using.
The most common are upstart (for ubuntu) and System V init scripts (Redhat or others). Also cron can be used to start/stop services at startup/shutdown.
You can find an example of installing a java app (hudson) on an init system here, or doing the same thing with upstart. Or, as I said, cron could be an option.
On Windows, there is Java Service Wrapper. And not much more.
For windows Java Service Wrapper is a better choice
My favourite is the upstart on linux, but it is Ubuntu only.
On Windows I see many alternatives according to this forum.

Running a java application through shell script in a JSP/Servlet

I am running a shell script through a web application. This shell script looks something like
`#! /bin/bash
user=""
pass=""
db_url=""
db_instance=""
sqlplus -s $user/$pass#$db_url/$db_instance # ./SqlScripts/foo.sql
sqlplus -s $user/$pass#$db_url/$db_instance # ./SqlScripts/bar.sql
CLASS_PATH="./lib/*"
java -classpath $CLASS_PATH package.Main ./Data/inputfile`
I am using ProcessBuilder to run the script and everything but the last line works fine. Am I creating a problem by calling shell through the jvm then calling the jvm again to run the application?
The problem was the environment that the script execution process was running in. I changed some of the environment variables of the process and everything is working fine now. The script was initially a standalone shell script, but I wrote one script for each of the databases used. In order to control the workflow I wrote a web application for this which calls seperate threads for each script and can manage the threads. Thanks for the responses!
Often, app servers run their servlets in a 'clean room' environment - e.g. they strip away all the variables that would normally be set from the outside for security reasons. Try using a fully qualified path to the java binary, and also try setting a full/absolute path for your CLASS_PATH variable.
The parent JVM and the child JVM should be separate processes, no particular reason why they should interfere.
What error do you get?
is java on your PATH?
OK, adding more questions in response to your comments ...
Which thread is waiting? Presumably the parent?
The child java process, do you have any evidence as to whether is succesfully initalises. My guess woukld be that the child is in some way blocked. If you kill the child does the parent then come back to life?
Suppose it was a simple "hello world" application, would that work?
Most likely the line:
CLASS_PATH="./lib/*"
And
$CLASS_PATH
It won't be expanded by the process builder because that's usually shells' job, which in this situation is not being invoked.
Try creating the complete list of ./lib/* and append it directly into the last line of your script.
java -classpath ./lib/a.jar:./lib/b.jar
Side note:
Invoking all this from java looks just bad to me. I would rather have it in a standalone script and invoke it by other means, but that's me.

Categories