I'm moving our servlets (pure Java, running in Tomcat 6) from CentOS to Debian, and faced the problem with executing commands with Runtime.exec().
(The command should be ImageMagick's convert in production, but I have simplified the calls to find the source of problems, so all the following code with echo is tested and not working as well).
String command = "echo test123 > /tmp/tomcat6-tmp/1";
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command);
int exitVal = process.waitFor();
Seems to be pretty common way to call an external program. It does run, returns 0 in exitVal, but fails to create file and put text in it.
So does low-level approach:
ProcessBuilder pb = new ProcessBuilder("echo", "test123 > /tmp/tomcat6-tmp/3");
Process process = pb.start();
int resInt = process.waitFor();
But it is possible to create a file and put some text in it using Java code placed in the same method:
String fname = "/tmp/tomcat6-tmp/2";
File file = new File(fname);
file.createNewFile();
FileWriter fileWriter = new FileWriter(file);
fileWriter.write("test123");
fileWriter.close();
Runtime.exec("whoami") successfully returns tomcat6, the folder /tmp/tomcat6-tmp/ does exist, all permissions are set correctly.
$ ls -al /tmp/tomcat6-tmp/
total 60
drwxr-xr-x 2 tomcat6 root 4096 Mar 2 15:26 .
drwxrwxrwt 6 root root 4096 Mar 2 15:25 ..
-rw-r--r-- 1 tomcat6 tomcat6 7 Mar 2 15:26 2
All commands without need to access files in system are seem to execute normally with Runtime.exec() in the same context.
I use fresh install of debian squeeze with tomcat6 installed from packages, without any modifications in configuration:
$ aptitude show tomcat6
Package: tomcat6
State: installed
Version: 6.0.28-9+squeeze1
.....
$ cat /etc/issue
Debian GNU/Linux 6.0 \n \l
How can I solve the issue?
Or at least where should I look? I've googled every imaginable reason for Java to misbehave this way, but failed to find a clue.
P.S. As this is default installation, Java security manager is disabled in /etc/init.d/tomcat6
# Use the Java security manager? (yes/no)
TOMCAT6_SECURITY=no
Put the action you want into a single executable shell script, then exec the shell script.
Java's Runtime.exec() is a wrapper around the exec system call, which will run the process directly, rather than under a sub-shell. The > redirection is carried out by the shell, and will not work as an argument to a directly execed process.
Don't know if this "echo test123 > /tmp/tomcat6-tmp/1" can be run as one command. I remember that I had similar problem and I had to split it, so try to run "echo test123" and then obtain an input stream with the output of the command. If you have a stream you can easily write to file.
Moreover, you execute command with args so try to use method that takes array as a parameter.
Related
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.
I want to execute wsl commands from java.
I'm trying to do this using Process and ProcessBuilder.
As I understand, there are two ways to do this:
Run wsl along with command as argument (for example: wsl ls -l) (do this per-command).
Run wsl, and then execute the commands one by one.
But there are some problems with 1 and 2.
With point 1:
When the command terminates, the process does not stop. So even if i run wsl ls, I can not determine the moment when I can call next command.
wsl does not save the state between such calls, so it's not very convenient
With point 2:
Since wsl does not show bash prompt, I can not track when the command stopped displaying information. For example:
ProcessBuilder pb = new ProcessBuilder("wsl");
pb.redirectErrorStream(true);
Process p = pb.start();
Thread.sleep(1000);
OutputStreamWriter osw = new OutputStreamWriter(p.getOutputStream());
osw.write("ls\n");
osw.flush();
And all I can read is:
build.gradle
gradle
gradlew
gradlew.bat
out
settings.gradle
src
No selya#selya-pc:/mnt/c/Users/selya$ read. So I can't use it as a separator between commands. I think that wsl somehow tracks, in what environment it was launched, for example, through isatty(), and therefore the output is different.
With both:
Аs far as I know, for programs that are running outside the terminal, stdout is not buffered. So there are some problems with, for example, sudo, because it asks fro a password without newline/flush ([sudo] password for selya:), therefore I can't read this line...
I found a solution - pass command as argument to unbuffer util, for example:
wsl unbuffer -p sudo apt-get update
(-p stands for pipeline).
But other problems still remain. Is there any way to do this? Or maybe there is lib for it? Even c or c++ lib will suit my needs...
P.S. I tried to find a solution for several days. Russian-speaking SO didn't help me, that's why I'm here. Sorry for my English...
The problem was solved with the help of pty4j. It works on linux, mac and windows. (It works like pseudo terminal on linux, but have nice java interface).
On Ubuntu 14.04:
I would like to execute Bro using the Jave Runtime as follows:
String[] command = {"gksudo", "bro -r ../../pcaps/test1.pcap"};
process = Runtime.getRuntime().exec(c);
Where the bro commands are correct and the path to the pcap is relative from the source of my java project.
I used gksudo to have sudo privileges, but nothing happens. There are no log-files created.
Other commands (like mkdir) work and provides me of the right results, however with Bro it does not.
It appears that gksudo cannot initiate Bro because it is not actually a root user.
How can I bypass this?
I tried to execute the following docker command from Java code:
command: ***docker exec -it reverent_hoover date***
(Here, reverente_hoover is the container name.)
When I executed the above command from Linux, it gave me the following output:
Wed May 6 05:19:28 UTC 2015
But when I tried to execute it from Java code, it gave me this error:
time="2015-05-05T19:31:19+05:30" level="fatal" msg="cannot enable tty
mode on non tty input"
I don't know how to solve this problem.
Issue 10734 mentions:
The java process is not providing a TTY to the docker CLI but you've asked for a TTY by specifying -t in your command.
So, does the image actually need a TTY? If not, don't specifiy -t... if the image doesn't need stdin at all don't specify -i either.
If it does need the TTY, then you'll need to setup the TTY for your command and it should work.
For example, see "persistent local tty session with java"
Instead of Runtime.getRuntime().exec("command"); do Runtime.getRuntime().exec("/bin/sh"); and hold on to the Process object
"Runtime.exec with Unix console programs" illustrates that in the context of using less:
Process p = Runtime.getRuntime().exec(new String[] {"sh", "-c",
"less >/dev/tty"});
OutputStream out = p.getOutputStream();
out.write("Lengthy message".getBytes());
out.close();
System.out.println("=> "+p.waitFor());
jps.exe which found on JDK 1.5 and later could monitor all Java process but is there a way to detect the specify command line and terminate the correct pid?
What if the user have JRE, is there a similar code allow us to terminate any process easily?
Prefer to keep the topic on Windows which I am working on.
The jps command supports a number of options that modify the output of the command. These options are subject to change or removal in the future.
-q Suppress the output of the class name, JAR file name, and arguments passed to the main method, producing only a list of local VM identifiers.
-m Output the arguments passed to the main method. The output may be null for embedded JVMs.
-l Output the full package name for the application's main class or the full path name to the application's JAR file.
-v Output the arguments passed to the JVM.
-V Output the arguments passed to the JVM through the flags file (the .hotspotrc file or the file specified by the -XX:Flags= argument).
Pipe the output of jps to grep or sed or awk or perl or even another Java program for further matching, parsing and action. On Windows, the easiest way to get those utilities is through Cygwin.
Here are some Microsoft downloadable command line utilities which are useful for working with processes on Windows:
pskill
pslist
and the rest of the Sysinternals Suite
If the user don't have jps, you can use ps. The command line options for ps differs between platforms, see man ps on you system. I use ps -C java -o pid,time,cmd to list java processes on a CentOS system. Then kill to terminate.