I'm on Linux and I have a Java application (JAR archive) which is using exec() to do it's stuff. I need to find out which commands is that application exec()uting... I tried strace and jstack but without any results. Suppose that the app is calling exec("ls"), can I find that ls command just by grepping output of above programs?
So the question is:
Is there a simple way to watch what is Java application executing with exec() ?
Edit for better situation overview:
Suppose that in Java app i have a button with onclick listener which calls static function from another class.In that function is exec("ls"); called.
When I click that button I see this in strace:
futex(0x7f14a6f799d0, FUTEX_WAIT, 4968, NULLDownload button clicked !
Trying SCP FROM...
<unfinished ...>
Trying SCP FROM.. is just my sout in that button handler right before calling exec().
Another edit:
Thank you guys, but I'm talking from OS point of view... Suppose that I'm sysadmin and I downloaded JAR. I want to know (from outside) what is that JAR doing - I'm only interested in programs started from exec()
So I tried strace but it shows nothing about calling that command from exec... Maybe it is logging too much low level calls for this...
Then i tried jstack -m but I can't find anything looking like that command from exec. I tried grepping string but with no luck.
Ok, what I'm going to propose is a veeeeeeeeeery rudimentary way of doing things, but it might be what you are looking for.
As you probably know, a .jar file is just a ZIP archive comprised of Java .class files. If you just need to get a peek at which commands are going to be executed, and if you know the class that is supposed to execute them, you can just extract the class files from the jar file with gzip and then use strings on them to look for commands.
For example, here's the most simple class I could think of that uses exec():
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try {
Runtime.getRuntime().exec("/bin/ls");
} catch (IOException ignored) {}
}
}
If you do strings Main.class you should get something like this:
[...]$ strings Main.class
<init>
Code
LineNumberTable
LocalVariableTable
this
LMain;
main
([Ljava/lang/String;)V
args
[Ljava/lang/String;
StackMapTable
SourceFile
Main.java
/bin/ls
java/io/IOException
Main
java/lang/Object
java/lang/Runtime
getRuntime
()Ljava/lang/Runtime;
exec
'(Ljava/lang/String;)Ljava/lang/Process;
As you can see, /bin/ls can be identified as a string. This should work in most cases, unless your Java program is constructing commands in a weird way, like using a char array to create command strings during runtime just to obscure the commands being executed (in which case I'd be highly suspicious of such a program).
However, if you want to see the commands executing in real time, I'm afraid you'll need to resort to some monitoring utility, since most commands would be too short-lived to even appear on top and the like.
EDIT: Regarding strace: I had a look at Java's native C code for UNIX systems and it seems that it actually uses the execvpe() system call to run all commands launched with Runtime.exec():
execvpe(argv[0], argv, envv);
So, in theory, you should be able to run strace -e execvpe <java command...> to list every command executed (as well as every other call to execvpe() -- you'll need to filter a bit more, that's true).
Related
I am a high school student working on a project that will convert the video from a YouTube link into an MP3 and download it. However, the way that the video form YouTube is downloaded and converted is through the Mac OS terminal by using YouTube-dl.
This is what I put into the terminal:
youtube-dl -f bestvideo+bestaudio \"ytsearch:{https://www.youtube.com/watch?v=5X-Mrc2l1d0}\"
This works in terminal, but when I try to use:
Runtime.getRuntime().exec("cd /Users/poppa/Desktop/IA Vids");
and there's an error saying "No such file or directory"
Another Problem that I am having is running the code that is inputted into the Terminal from Java. I'm using IntelliJ IDEA if that helps. Thank You!
You have a space in the directory path. Try putting double quotes around like this:
Runtime.getRuntime().exec("cd \"/Users/zeidakel/Desktop/IA Vids\"");
Also note that executing cd command from JVM may have no effect on current user dir when (for example) creating files with new File("path")
If cd means change directory (and isn't the name of an executable), then it almost certainly won't take effect, even if it is executed correctly. The process spawned by exec() will have a working directory, and it can be changed -- but that change will only affect the spawned process.
In addition, having spaces in arguments to exec() is inherently problematic. exec() is not a shell, and you won't be able to protect the string from being split at the spaces by using shell mechanisms like quotes. Instead, you need to split the command into arguments yourself, knowing where the splits should be, and then use the form of exec() that takes a String[] as input. That is, split the arguments into an array of strings yourself, rather than relying on exec() to do it for you (wrongly).
Runtime.exec() is fraught with difficulties, and needs very careful handling. I've written extensively about this subject here:
http://kevinboone.me/exec.html
I just picked up my Ubuntu machine after a long time for some java related work and found that I have java already installed but not javac.
I made a Test.java file with a main method and a simple print statement. I wrote this in my terminal:
java Test.java
I expected that without javac this shouldn't compile and run but it printed the output on my console. I then installed a JDK to enable the javac and ran this:
javac Test.java
This created a Test.class file. Still to run the Test class I need to type java Test.java and on typing java Test it throws java.lang.NoClassDefFoundError.
Can someone please explain to me what's happening in the background of these commands?
Edit:
Here are the contents of my Test.java:
package Learning;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("Hello World!");
}
}
What you experience here is a new feature, added for Java 11:
In Java SE 11, you get the option to launch a single source code file directly, without intermediate compilation. Just for your convenience, so that newbies like you don't have to run javac + java (of course, leaving them confused why that is).
Quoted from here. For more details, see the corresponding JEP 330.
So: if you have a single self-contained .java file ... then the java binary recognizes that, compiles it, and directly runs it (when using Java 11 or newer).
But keep in mind: it is just that, a way to quickly run a single class. It isn't meant to replace the "real" way of doing things.
In general, you still use javac first, and then java. Or, more real world: you use a built system where you describe what to build, and then the build system invokes javac for you behind the covers.
GhostCat's answer is good, but here are a few additions taken from a longer post about this same behavior.
Can someone please explain to me what's happening in the background of these commands?
What you ran into – where you can use "java" (not "javac") to
compile and run a program in one command – JEP 330: Launch Single-File Source-Code Programs –
was designed to make it easier for "early stages of learning Java, and when writing small utility programs".
It's definitely
not meant to replace anything – use it if convenient, but nothing changes with the normal steps of "compile to .class file" and "run JVM using .class files or JARs".
There's some good info in the JEP 330 link, but also in the java command itself, namely that one option (among four total) is to provide a single filename (which is what you did)
To launch a single source-file program:
java [options] source-file [args ...]
Further, there's a nice summary in Using Source-File Mode to Launch Single-File Source-Code Programs:
In source-file mode, the effect is as though the source file is compiled into memory, and the first class found in the source file is executed. Any arguments placed after the name of the source file in the original command line are passed to the compiled class when it is executed.
I have a Main.java file and I want to run the program passing it test.txt
I know in command line I can write javac Main.java
After compiling I can write java Main test.txt and this will accomplish running the file and passing test.txt
If I wanted instead to be able to just write main test.txt and have that trigger my Main.class file to run is that possible and if so how?
(Edit: Based on your comment, let me expand to add a couple more situations)
If your goal is to have someone else run your program who does not have Java installed, and you do not wish to have them install a Java runtime environment before running your app, what you need is a program that converts the .class or .jar files into a native executable for the platform you are using. How to do this has been covered in other questions, eg: Compiling a java program into an executable . Essentially, you use a program like JCG (GNU Compiler for Java) or Excelsior JET (a commercial product) to expand the byte code into full native code with a mini-JRE built in.
If your goal is to save typing, there are a number of strategies. Others have suggested alias commands, which work well on linux.
A slightly more portable option that you could ship with your program would be a shell script. Granted, shell scripts only run on linux or other OS's with shell script interpreters installed.
Here is an example shell script. You paste this into a text editor and save it as main with no extensio. The $1 passes the parameter argument fyi.
#!/bin/sh
java Main $1
presuming you name your shell script just "main" with no extension, you could call main test.txt to execute your program now.
If you are on Windows, you might want to create a windows shortcut, and point the shortcut to "java Main test.text", using the full paths if necessary (if the paths are not already set). Of course, this does not make the parameter easy to change every time you run it, you would have to edit the shortcut.
add an alias
e.g. under a mac edit your .bash_profile with the following line
alias main='java main'
don't forget to open a new console to see your alias working
Depends on your operating system. On Linux with the bash shell, for instance, you can set up an alias to expand your main into java -cp myjar.jar main.
Linux can also be configured to 'understand' Java class flies as a binary format directly see here (linux kernel documentation).
If you're on windows, you'll have to wait for answer from someone with more knowledge about that than I.
Good luck!
everyone. I'm quite new here so please be tolerant if I make any mistakes.
I have a .bat file containing a command line to open up a .jar file that contains a program that has a GUI in it. The only line that's in the .bat file is:
java -jar "NewServer.jar"
I've been trying to use Runtime() to get this to run, but most the instructions I find to open a .bat file in a java program are for Windows. I'm currently using Fedora 12 (don't tell me to upgrade, I can't) if that makes a difference and programming using Eclipse. I also found this ProcessBuilder thing, but I couldn't get it to work so unless you have very explicit directions on how to use it, please don't include it in your answer. I would much rather use Runtime. It looked simpler.
Here's my code to test using Runtime in a java program. I'm hoping that if I can get this to work, I can get it to work in my real program.
import java.io.IOException;
public class testbat {
public static void main(String[] args) {
Process proc = null;
try {
proc = Runtime.getRuntime().exec("./ myServer.bat");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Cool");
}
The last line is just there for me to see if the program actually ran in case the GUI doesn't open. Also, I've already tried many combinations of things to include in the area after ".exec". I've tried using a path like "~/user/workspace/ProjectServer/dist/myServer.bat" to no avail.
I also already know that .bat files are for windows, but I'm able to execute it in linux, so I don't know if that makes a difference. I also tried using a .sh file the same way and it didn't work.
Please bear in mind that I'm not that great at Java, but I had to use it for this particular program, so if your answers could be really descriptive that would be awesome.
Just take that line out of the bat file, and run it. Yo're making it too hard.
$ java -jar "NewServer.jar"
will work. The quotes aren't necessary, so
$ java -jar NewServer.jar
will work as well. If you want to have the equivalent of your bat file, create a file named, say, run_newserver containing that line. Change its mode to executable:
$ cat > run_newserver
java -jar NewServer.jar
^D
$ chmod a+x run_newserver
$ ./run_newserver
Ideally, since you shouldn't have scripts without comments, do this. In your favorite editor, create a file run_newserver containing
#!/usr/bin/env bash
java -jar NewServer.jar
and chmod that. The line with #! -- often called a "shebang line" -- is UNIX magic that lets you say what interpreter you want. The program env in usr/bin finds your program and runs it (needed because different systems put bash in different directories.)
You could even put explanatory comments in the file too.
I'm a little unclear why you want to use Runtime#exec to run it at all -- it seems you'll just need a shell script to start that program.
Why are you using Java to run a Batch file, that in turn runs a Java program? Why have Batch in the loop at all? Just put the jar in your classpath and call it directly.
Batch (.bat) files are only for Windows environment. So, Try using shell script
Process proc = Runtime.getRuntime().exec("myServer.sh");
Just open up terminal and do this
vi /dir/to/exec/exec.sh
tap "i" and write this
#!/bin/sh
java -jar "NewServer.jar"
or if you want to run it in the background
#!/bin/sh
java -jar "NewServer.jar" & > /tmp/JavaServer.log
hit esc and type ":wq" and you have saved the file.
type this into the terminal
chmod +x /dir/to/exec/exec.sh
this give executable privileges and then you should run the file like
sh /dir/to/exec/exec.sh
Process is only initialized by your first call. You need to run:
proc.waitfor();
to get it to actually run your app.
I am trying to build a very simple python script to automate minifying/combining some css/js assets.
I am not sure how to properly handle the minification step. I use yui-compressor and usually call the jar directly from the command line.
Assuming the build script is in the same directory as rhino js.jar and yui-compressor.jar, I'd be able to compress a css/js file like so:
java -cp js.jar -jar yuicompressor-2.4.4.jar -o css/foo.min.css css/foo.css
Calling that from the terminal works fine, but in the python build file, it does not
eg, os.system("...")
The exit status being returned is 0, and no output is being returned from the command (for example, when using os.popen() instead of os.system())
I'm guessing it has something to do with paths, perhaps with java not resolving properly when calling to os.system()… any ideas?
Thanks for any help
I have a somewhat similar case, when I want a python program to build up some commands and then run them, with the output going to the user who fired off the script. The code I use is:
import subprocess
def run(cmd):
call = ["/bin/bash", "-c", cmd]
ret = subprocess.call(call, stdout=None, stderr=None)
if ret > 0:
print "Warning - result was %d" % ret
run("javac foo.java")
run("javac bar.java")
In my case, I want all commands to run error or not, which is why I don't have an exception raised on error. Also, I want any messages printed straight to the terminal, so I have stdout and stderr be None which causes them to not go to my python program. If your needs are slightly different for errors and messages, take a look at the http://docs.python.org/library/subprocess.html documentation for how to tweak what happens.
(I ask bash to run my command for me, so that I get my usual path, quoting etc)
os.system should return 0 when the command executes correctly. 0 is the standard return code for success.
Does it print output when run from the command line?
Why would you want to do this in Python? For tasks like this, especially Java, you are better off using Apache Ant. Write commands in xml and then ant runs for you.