Executing shell commands - java

I'm having some trouble getting my app to execute that command, it asks for root access but does not run the kill $(pidof cameraserver) part.
If I run kill $(pidof cameraserver) from the terminal on my phone it works, but not when I hit the button on my app.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button action = findViewById(R.id.buttonAction);
action.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
Process process;
Process secondProcess;
process = Runtime.getRuntime().exec("su kill $pidof(cameraserver)");
BufferedReader in = new BufferedReader
(new InputStreamReader(process.getInputStream()));
}
catch (IOException e){
e.printStackTrace();
}
}
});
}

I doubt that $(pidof cameraserver) will be expanded by the exec() method. This is a shell feature. It works in the terminal, because that's a shell. You would have to exec pidof cameraserver first and use the result of that for a second call to exec.
I also believe, that you can not use the single argument overload of exec this way. You would rather have to provide a string array that contains the command and its parameter as separate array items.

You're confused. Your post title is Executing shell commands, and yet what you're doing is.. not that.
Runtime.exec does not run shell commands.
It runs executables, with its own extremely crappy shell-like behaviour you should not rely on.
Just su, in bash, will apply aliases, check if it's a bash built-in, and if none of those apply, scans the PATH. Runtime.exec does almost none of those things (it kinda scans PATH, but don't rely on that). bash will expand ${pid}, *, and many other things. Runtime.exec doesn't do any of these things.
For starters, take Runtime.getRuntime().exec and put it on your verboten list. You should never ever call this method. Let it join Thread.stop and all those silent char-to-byte conversion methods that don't include a charset.
The right way to do process control is to use ProcessBuilder. This makes a lot more clear what is happening:
Java will run the exact application you provide (so, don't do su, do /bin/su), and pass arguments verbatim with absolutely no parsing whatsoever. With the exec method you use, java will try to apply quote separation and otherwise treats spaces as separators, and does nothing more. This confusing mix of some bashisms (such as splitting your string on spaces and then treating each part as an argument) and lack of bashisms (not applying *.txt, or ${var} and friends, or supporting any of the built-ins) is just going to lead to a lot of confusion.
You have two general options:
Realize java just tells the OS to fire app A with argslist B with no processing whatsoever, and 'bash' is not built into the OS. There is simply no way to do any of these things directly, you have to program them. Whatever pidof does, you'd have to program it in java.
Start bash, not su. Then tell bash to run this command.
option 2 is possibly easier here:
ProcessBuilder pb = new ProcessBuilder();
pb.command("/bin/bash", "-c", "su kill $pidof(cameraserver)");
Process p = pb.start();
Then you're stuck trying to deal with the fact you're calling su, which goes out of its way to stop you from doing this. Having a script just casually pick up root rights is something that is just going to lead to a ton of security issues.
Find another way. For example, make a shell script that runs this command, then use some linux knowhow to give this shell SUID rights (so that it automatically and silently is run with the rights of the owner), then make it owned by root, and then use processbuilder to exec "/bin/bash", "-c", "/path/to/that/script". This has a chance of being safe: That shell can only be written to by root (make sure of that!), and thus you have now decided, as operator of the hardware you run this on, that this specific job is acceptable even for non-root users to run.
NB: If you've configured your su to just let this happen without asking for passwords, go back. Undo that mistake. Keep your system safe. root access is hidden behind a few doors for a reason. Stop replacing the locks on your safe with paper and sticks.

I finally fixed the issue, I have my phone rooted with Magisk so all i had to do was use their 'su' file, I'm new to Java and android development so i don't really know how to explain myself in proper terms but here's the code:
ProcessBuilder pb = new ProcessBuilder();
pb.command("/sbin/su", "-c","pkill cameraserver");
Process p = pb.start();
This limits the user to use Magisk, but since this is for personal use it wont matter much.
Thanks to EricSchaefer and rzwitserloot for their knowledge and help.

Related

When/why does Java Runtime.exec() require cmd.exe?

In my Java code I have found quite significant performance differences between two similar commands:
execString=new String[]{"CMD.EXE","/C", path_to_executable };
Runtime.getRuntime().exec(command)
runs my executable almost twice as quickly (6-7mins vs 3-4mins) as:
execString=new String[]{" path_to_executable };
Runtime.getRuntime().exec(command)
Please can someone educate me as to why? One seems to be telling the executable to run directly, whereas the other is telling cmd.exe to run the executable...?
Thanks in advance :-)
EDIT:
The same performance discrepancies were noted when using ProcessBuilder:
ProcessBuilder myPB = new ProcessBuilder(execString);
Process myProcess = myPB.start();
I have discovered the answer here:
https://stackoverflow.com/a/24676491/1961025
From the API doc of java.lang.Process:
Because some native platforms only provide limited buffer size for
standard input and output streams, failure to promptly write the input
stream or read the output stream of the subprocess may cause the
subprocess to block, or even deadlock.
Basically, you need to make sure that the process is handling the input, output and error streams. Mine wasn't. When using cmd.exe, I think it kind of wraps the executable so it's not an issue. Using the gobblers from https://www.infoworld.com/article/2071275/when-runtime-exec---won-t.html?page=2 works a treat!
Thanks!

Java Process immediately exits

I am creating a Process as follows:
Process p = Runtime.getRuntime().exec("nohup /usr/bin/python " + filePath + " &");
Process q = Runtime.getRuntime().exec("/usr/bin/python -m -webbrowser " + url);
I am trying to run the p in the background so that q can run without a problem. Also, when the Java program ends, the python script is no longer running.
The command you ran is nohup, and it did exit immediately. nohup started a separate Python process, but that's not the process p is controlling.
Why are you using nohup and & if that isn't the functionality you want? If you simply want to execute a Python subprocess just call:
Process p = Runtime.getRuntime().exec("/usr/bin/python " + filePath);
Note that it's also safer to use the overload of exec that takes an array, so you don't have to do string-concatenation yourself (and therefore avoid a type of security exploit). The syntax changes slightly to:
Process p = Runtime.getRuntime().exec(new String[]{"/usr/bin/python", filePath});
Or with ProcessBuilder:
Process p = new ProcessBuilder("/usr/bin/python", filePath).start();
Take a look at the documentation for the & operator here. Your process is running in the background. As far as the Java program you're writing (and the shell, for that matter) are concerned, the call you made has already ended. It's most certainly still running in the background, but you're telling the shell (and by extension your Java program) to not pay attention to the output and move on.
As Sean Bright mentioned take the '&' character out of the end of the script that you are running. Basically on all *nix systems. If you call make a type a command and use the ampersand, it will run that process in the background. That's why the call to java process is returning finished.
It would probably be better to not execute the command in the background (no '&') and just call Process.isAlive() periodically to determine if the process is finished.

how to increase the performance of cmd command in window

I am using java to call arp -s command and waiting for the process to finish in order to complete function .
String command ="arp -s "+entryIpAddress+" ee-ee-ee-ee-ee-ee-ee";
Process p=Runtime.getRuntime().exec(command);
p.waitFor();
but the calling of this process is taking more than the usual time so, is there a way to increase the performance of this method. i cannot remove the p.waitFor() my next function depend on the added entry.
Your code doesn't show any sign of effort towards allowing the process to complete ever. You are not consuming its output, so its output buffer will soon fill up, blocking further progress.
To make this task easier on yourself, please use the ProcessBuilder.

Java process.waitFor() does not return

On Windows 7 64 bit, running 64 bit Java 1.7.0_17 , the p.waitFor() shown below never returns.
String move_command="cmd.exe /c xcopy /Y /E "+x86_release+" "+path+"\\";
Process p;
p = Runtime.getRuntime().exec(move_command);
p.waitFor();
If I use Windows Explorer, it looks like all the files are copied (same number, same size, etc.)
If I do the below, it waitFor() does return:
String move_command="cmd.exe /c move /Y "+x86_release+" "+path+"\\";
Process p;
p = Runtime.getRuntime().exec(move_command);
p.waitFor();
What could be so different between an xcopy and a move that keeps waitFor() from returning, or am I on the wrong track entirely?
xcopy probably just happens to produce more output than move, filling up the out-buffer and blocking until it is flushed. The default behavior in Java is to pipe the subprocess's stdout/stderr into InputStreams that you are then required to read programmatically lest the subprocess's buffers overflow.
If the latter is the case, the solution is simple, and in fact you should do that anyway: use ProcessBuilder to prepare the system call and call inheritIO on it. This will reuse your parent process`s stdin and stdout for the subprocess.
A side note, xcopy is a regular .exe file and doesn't need wrapping into cmd.exe /c.
I suspect you're not consuming the process standard out/err, and that's blocking the process. If your code doesn't consume this output, then the spawned process will hang (and you'll hang waiting for that process!). Why the difference in behaviour between the two commands ? Probably due to the quantity of data returned and the impact on the publishing buffers.
See this answer for more details.
I would also investigate Apache Commons FileUtils.copyDirectory() such that you don't have to spawn a whole new process to copy files.

How to kill subprocesses of a Java process?

I am creating a process P1 by using Process P1= Runtime.exec(...). My process P1 is creating another process say P2, P3....
Then I want to kill process P1 and all the processes created by P1 i.e. P2, P3...
P1.destroy() is killing P1 only, not its sub processes.
I also Googled it and found it's a Java bug:
http://bugs.sun.com/view_bug.do?bug_id=4770092
Does anyone have any ideas on how to do it?
Yes, it is a Bug, but if you read the evaluation the underlying problem is that it is next to impossible to implement "kill all the little children" on Windows.
The answer is that P1 needs to be responsible for doing its own tidy-up.
I had a similar issue where I started a PowerShell Process which started a Ping Process, and when I stopped my Java Application the PowerShell Process would die (I would use Process.destroy() to kill it) but the Ping Process it created wouldn't.
After messing around with it this method was able to do the trick:
private void stopProcess(Process process) {
process.descendants().forEach(new Consumer<ProcessHandle>() {
#Override
public void accept(ProcessHandle t) {
t.destroy();
}
});
process.destroy();
}
It kills the given Process and all of its sub-processes.
PS: You need Java 9 to use the Process.descendants() method.
Java does not expose any information on process grandchildren with good reason. If your child process starts another process then it is up to the child process to manage them.
I would suggest either
Refactoring your design so that your parent creates/controls all child processes, or
Using operating system commands to destroy processes, or
Using another mechanism of control like some form of Inter-Process Communication (there are plenty of Java libraries out there designed for this).
Props to #Giacomo for suggesting the IPC before me.
Is you writing other processes' code or they are something you cannot change?
If you can, I would consider modifying them so that they accept some kind of messages (even through standard streams) so they nicely terminate upon request, terminating children if they have, on their own.
I don't find that "destroying process" something clean.
if it is bug, as you say then you must keep track pf process tree of child process and kill all child process from tree when you want to kill parent process
you need to use data structure tree for that, if you have only couple of process than use list
Because the Runtime.exec() return a instance of Process, you can use some array to store their reference and kill them later by Process.destroy().

Categories