Can't run exe file using ProcessBuilder depending of computer - java

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.

Related

Shell script working fine on linux system but not running on windows machine

I am running a java project on windows machine which reads shell script file for getting the authorization token but getting following error :
java.io.IOException: Cannot run program "./token.sh": CreateProcess error=193, %1 is not a valid Win32 application
Java program for reading the shell script:
private static String execCommand(String username){
String line;
Process p = Runtime.getRuntime().exec("./token.sh -u " + username + " -p password123");
BufferedReader input = new BufferedReader(new
InputStreamReader(p.getInputStream()));
StringBuilder output = new StringBuilder();
while ((line = input.readLine()) != null) {
output.append(line);
}
How can i run the same code on windows machine.
It will not. You see the commands used in Windows CMD and Shell are different since they are completely different platforms. Even-though you use java to execute, it will not execute due to underlying fundamental difference. It is quite clear from the exception you are getting.
What can you do?
Read through the token.sh. Most probably the internal implementation
can be implemented in Windows. Then create an if condition which
checks System.getProperty("os.name") Then if its windows then
call the bat file and if the OS is unix based call the sh file. For
every other OS throw a valid exception.
Other probable way is that, if the token generated in machine
independent, you can use SSH(JSch or similar) to remote connect to a
UNIX server and get the token. If the token is machine dependent (if
its an auth token, then probably is), try using Cygwin interpreter
,which itself does not assure you the every shell file will run in
it.
Change the sh file and its implementation to python or ruby.Then respective interpreter may be installed on machines (which it might actually have,except for production machines).
Write the sh logic in Java itself rather than keeping a script file, since platform independence is actually a requirement here and you already has JRE up and running in both machines.
You cannot do that since the commands that the Linux bash script requires is a lot different than the windows commands.
For example -
To list the contents in a directory in Linux ls
To list the contents in a directory in Windows dir
You have to write a machine/architecture/OS independent code to run across all the operating systems.
Maybe, you can try using Python scripting for that.
Or else, you can ssh from windows machine to Linux machine and run that script from windows in Linux server.
You have to make a Windows specific implementation as well of this script. The most common and easy approach would be to use powershell.
If you want a version that works on both Windows and Unix, perhaps you should look into python.

Java sub-process command line execution with/without cmd.exe

My question consists of several things I don't understand about the use of "cmd.exe", "/c" when executing sub-processes from Java in Windows. Basically, I couldn't find a good explanation about when and why they're needed.
My specific problems: I have a small framework for sub-processes execution. One use is a Java application which "manages" several other JVMs created by ProcessBuilders. One of the key requirements is that when a sub-process is stuck, or the hosting application is terminating, it must be able to kill the sub-processes.
The problem is, on one hand, doing this:
new ProcessBuilder("java", "...").start();
Causes this:
Could not find or load main class ...
As if the system variables or directory are different (which they're not). On the other hand, wrapping it in a cmd.exe like this:
new ProcessBuilder("cmd.exe", "/c", "java", "...").start();
WORKS, but creates another cmd.exe process, which has a side effect: the child JVM is now a sub-sub-process, and process.destroy(); doesn't kill it (a known bug in the Windows JRE as I found).
This specific problem was handled on a different level, since all those applications are ours and we know their PIDs. But it's an example how cmd.exe makes everything work differently (or prevent the JVM from working at all). So I'd like to know what exactly happens there.
Here the framework itself comes into the picture as well. It's also going to be used by our testing platform. I'd like to provide an API which allows wrapping the command with a cmd.exe /c by a parameter. But, what exactly is the meaning of that parameter? How do the users decide if they want a cmd.exe wrapping?
AND a bonus I'd appreciate: is any of this relevant in other OS? Does it have some kind of an equivalent, say, in Linux?
Came across it again and found the problem, and some additional insights.
#HarryJohnston - good point, according to the javadoc the sub-process inherits the environment from both ProcessBuilder and Runtime.getRuntime.exec(...) APIs so it's not the issue (and java.exe is in fact found so PATH is obviously available).
But it seems certain command line features are lost. Among others it's this kind of %VARIABLE% usage in command line - they're not interpreted unless the command starts with cmd.exe /c.
In this case of class not found (it could also cause NoClassDefFoundError), I had several variables used in the classpath (perhaps I should have posted the entire command, but it's kinda long). When I replaced them with the literary paths, everything worked.
Basically:
java -cp %classpath% Main - bad
cmd.exe /c java -cp %classpath% Main - good
java -cp D:\proj\bin Main - good
So, on the general question of how's the behavior different with cmd.exe:
Variable references like this: %VARIABLE% are only interpreted by cmd.exe.
With cmd.exe you don't really need to split the command into String array of parameters, you can use one long String (array of "cmd.exe", "/c", "the rest...").
The sub-process will be cmd.exe and not the actual executable, so process.destroy() won't kill it. This might be a bug fixed in later versions (I used Java 7 and 8, Windows 7 and Server 2012).
File associations only work with cmd.exe, so you can't "execute" a file that is not a program or script ("CreateProcess error=193, %1 is not a valid Win32 application"). Calling executables defined in PATH does work.
start and call are only available under cmd.exe.
Command level errors behave differently as well: With cmd.exe the error will just be in the output stream, whereas without it the API throws an exception, which might also have a slightly different description.
For example:
nothing will cause java.io.IOException: CreateProcess error=2, The system cannot find the file specified
cmd.exe /c nothing will return output: 'nothing' is not recognized as an internal or external command, operable program or batch file, and a return value of 1 (the only indication something went wrong).
On the deeper question of why, and is this the full list of differences, I still don't really know. Looks like the JVM has its way of running commands, which you can "wrap" with cmd.exe for additional features and protection.
Another interesting fact is when you run a Batch, it's actually run under a cmd.exe (with all its features). From my tests it seems process.destroy() can only kill it if it's not waiting for anything external (if stuck on pause for example), with or without additional cmd.exe /c wrapping, but not if it runs a sub-process - which is consistent with the cmd.exe limitation mentioned in point 3.

How to give the java process a name in the operating system (other than java)

I'm playing around with some microservices and running them on my laptop, simply assigning each micro-service a new port. The problem is that I'd like to restart one of them I have to close them all because in the operating system the processes are all called java. And although I sometime can guess that the last started have the highest pid etc is isn't exacly a safe bet...
So, is there a way to start a java-application and assign it a name in the operating system? Perhaps something like
java --Dos.name MyFirstService -jar MyJar.jar.
Under Windows, you can't (unless installing some kind of posix subsystem).
Under Linux, you could use exec command with the -a "newName" option to alias the process you wish to spawn.
Like
exec -a "myJar" /path/to/java -jar /path/to/jar.jar
If you need to be able to differentiate between different java programs you can use the jps command that gives you a list of all java processes and running your program with
java -Dname=myFirstService -cp myFirstService.jar some.client.main.MyFirstService
then if you do a:
jps -v
You will see your process correctly.
If you need to change the process name at the OS level I recommend you use http://launch4j.sourceforge.net/

JAVA runtime exec throwing IOException for netstat command in mainframe USS environment

I am trying to execute netstat command from java using runtime execution, but it throwing below IOException.
It works fine for other commands, even the synonym command onetstat is working fine. I am trying understand why netstat alone is failing and how to make it work. any help is appreciated.
java.io.IOException: Cannot run program "netstat": netstat: not found
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1059)
at java.lang.Runtime.exec(Runtime.java:629)
at java.lang.Runtime.exec(Runtime.java:462)
at java.lang.Runtime.exec(Runtime.java:359)
at com.ca.RunCmd.executeCommand(RunCmd.java:30)
at com.ca.RunCmd.main(RunCmd.java:18)
Caused by: java.io.IOException: netstat: not found
at java.lang.UNIXProcess.fullPath(UNIXProcess.java:306)
To really understand what's going on here, you'll need to hunt down the actual executable, whether netstat or onetstat.
If netstat is aliased to onetstat in the shell - depending on which shell you're running, the "whence" or "alias" command will tell you. A simple solution might be to run the command via the shell (/bin/sh -c netstat) rather than running netstat directly.
Another possibility is that these commands are what's known as an "external link"...a way for a UNIX Service pathname to point to a conventional executable in a z/OS dataset. If this is the case, then you very well might have the netstat/onetstat in your path and otherwise correct, but you might not have the correct STEPLIB or LNKLST concatenation. When the system exec's the target (netstat/onetstat), it doesn't find the externally-linked program, and you get the "not found".
All sorts of things can go wrong here, especially when external links are involved. There can be system (APF) authorization issues, missing modules in your STEPLIB/LNKLST, not enough memory to load the program, etc etc etc. Unfortunately UNIX Services on z/OS doesn't always interpret every possible failure code, so sometimes it's necessary to go hunting. A good first start would be to catch the exception you're getting and look for the ERRNO/ERRNO2 values - they can give you a good hint.
If you have traditional z/OS facilities, your friend is the console log...SDSF's Log function or equivalent. There very well might be an x06 abend and a CSV... message on the console that would give you the clues about what to do next.
The solution is to use the actual command instead of the alias command in this case the actual command is onetstat, netstat is an alias command.
The problem is with the alias commands, when you try execute a alias command through the java runtime its failing to execute them. I am yet to find the exact reason but the problem could be replicated easily by the creating an alias for any command try executing them, you can try this in windows environment as well.

Execute shell command in Tomcat

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

Categories