How to generate processes with inherited rights and permissions - java

Is there any way to make a process with all inherited rights of the process, i already own.
For example i have some process;
Process superUserShell = Runtime.getRuntime().exec("su");
and i am able to get output stream and execute commands like this
DataOutputStream outputStream = new DataOutputStream(superUserShell.getOutputStream());
// for example
outputStream.writeBytes("rm -rf /*");
outputStream.flush();
but i have no posobilities to handle results of executed commands, so what i really wana is to have separated Processes generated by another Process(for example by "superUserShell")
Any thoughts?
of course it is not for evil purposes ^_^ this just the first thing i got in mind.
actually i am working on small wraper of fbgrab for android...
p = Runtime.getRuntime().exec("su");//lets assume my android os grants super user premissions. this is not the question!!!!
DataOutputStream outputStream = new DataOutputStream(p.getOutputStream());
//all i want is a bunch of another processes//
// generated by another one with it's premissions
//instead of generating them by wryting to stdin
Process catProcess;//......
Process someAnotherBinaryExecutionProcess;//......
outputStream.writeBytes("cat /dev/graphics/fb0 > "+ getFilesDir() + "/fb0\n");
outputStream.writeBytes("exit\n");
outputStream.flush();
p.waitFor();

First of all, I hope this isn't being used for evil purposes. Your example of "rm -rf /*" causes me some concern.
If you do Runtime.getRuntime().exec("bash") you'll get a shell that you can send commands to and get responses from. So, for example, you could tie the console into it:
final Process process = Runtime.getRuntime().exec("bash");
new Thread() {
public void run() {
try {
InputStreamReader reader = new InputStreamReader(process.getInputStream());
for(int c = reader.read(); c != -1; c = reader.read()) {
System.out.print((char)c);
}
} catch(IOException e) {
e.printStackTrace();
}
}
}.start();
// (Same for redirecting the process's error stream to System.err if you want)
InputStreamReader fromKeyboard = new InputStreamReader(System.in);
OutputStreamWriter toProcess = new OutputStreamWriter(process.getOutputStream());
for(int c = fromKeyboard.read(); c != -1; c = fromKeyboard.read()) {
toProcess.write((char)c);
toProcess.flush();
}
This is a good way to experiment and see what your OS will let you do. On Mac OS, if I want to sudo a command from this process, I run into the problem that it can't accept my password from STDIN because it is not really a login shell. So, I have to do this:
SUDO_ASKPASS="password.sh" sudo -A <command>
... where "password.sh" just echoes my password, and is the command I want to run as root (I used the nice safe "pwd" instead of your wipe-my-root-filesystem example).

A few notes:
I suppose you already get output from this process, via Process.getInputStream()?
BufferedReader buf = new BufferedReader( new InputStreamReader(
superUserShell.getInputStream() ) ) ;
while ( ( String line ; line = buf.readLine() ) != null ) {
// do domething with data from process;
}
Try adding newline to the command, e.g. "rm -rf /* \r\n"
If you send multiple commands consecutively (and read reply) then you might want to send and receive data in separate threads.

Selvin's right, su returns immediately, and doesn't provide your application with a 'shell' type of situation like a real, interactive shell would. What you want to look into is something like sudo <command> to get su to run the command you want.

Related

I've already created a Process Builder. How do I run all of the programs in the Process builder?

Heere is the code I have so far. How do I have miktex-pdftex run?
List<String> processes = new ArrayList<String>();
processes.add("miktex-pdftex --output-directory=[Directory] [file_name].tex");
ProcessBuilder processbuild = new ProcessBuilder(processes);
First, you need to make sure the command you are using actually works at the command. If it does not, then it's not going to work in Java.
Next, one of the main reasons for using ProcessBuilder is to deals with spaces in the command/parameters better then Runtime#exec.
String command = "/Applications/MiKTeX Console.app/Contents/bin/miktex-pdftex";
String outputDir = System.getProperty("user.dir");
String sourceFile = "Sample.tex";
List<String> commands = new ArrayList<>();
commands.add(command);
commands.add("--interaction=nonstopmode");
commands.add("--output-directory=" + outputDir);
commands.add(sourceFile);
So the above is very simple...
The command I want to run is /Applications/MiKTeX Console.app/Contents/bin/miktex-pdftex (I'm running on MacOS and I couldn't get the command installed outside the application bundle)
I want the output-directory to be the same as the current working directory (System.getProperty("user.dir")), but you could supply what every you need
I'm running in "nonstopmode" (--interaction=nonstopmode) because otherwise I would be required to provide input, which is just more complex
And my input file (Sample.tex) which is also in the working directory.
Next, we build the ProcessBuilder and redirect the error stream into the InputStream, this just reduces the next to read these two streams separately...
ProcessBuilder pb = new ProcessBuilder(commands);
pb.redirectErrorStream(true);
Next, we run the command, read the contents of the InputStream (otherwise you can stall the process), you can do what ever you want with this, I've just echoed it to the screen
try {
Process process = pb.start();
InputStream is = process.getInputStream();
int in = -1;
while ((in = is.read()) != -1) {
System.out.print((char)in);
}
int exitValue = process.waitFor();
System.out.println("");
System.out.println("Did exit with " + exitValue);
} catch (IOException | InterruptedException ex) {
ex.printStackTrace();
}
The use int exitValue = process.waitFor(); here is just to ensure that command has completed and get the exit value it generated. Normally, 0 is success, but you'd need to read the documentation of the command to be sure

How do I run an sh file from Java and ask for root permissions to do so? [duplicate]

I'm using ProcessBuilder to execute bash commands:
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try {
Process pb = new ProcessBuilder("gedit").start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
But I want to make something like this:
Process pb = new ProcessBuilder("sudo", "gedit").start();
How to pass superuser password to bash?
("gksudo", "gedit") will not do the trick, because it was deleted since Ubuntu 13.04 and I need to do this with available by default commands.
EDIT
gksudo came back to Ubuntu 13.04 with the last update.
I think you can use this, but I'm a bit hesitant to post it. So I'll just say:
Use this at your own risk, not recommended, don't sue me, etc...
public static void main(String[] args) throws IOException {
String[] cmd = {"/bin/bash","-c","echo password| sudo -S ls"};
Process pb = Runtime.getRuntime().exec(cmd);
String line;
BufferedReader input = new BufferedReader(new InputStreamReader(pb.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println(line);
}
input.close();
}
Edit /etc/sudoers with visudo and grant your user a NOPASSWD right for a specific script:
username ALL=(ALL) NOPASSWD: /opt/yourscript.sh
My solution, doesn't exposes the password in the command line, it just feed the password to the output stream of the process. This is a more flexible solution because allows you to request the password to the user when it is needed.
public static boolean runWithPrivileges() {
InputStreamReader input;
OutputStreamWriter output;
try {
//Create the process and start it.
Process pb = new ProcessBuilder(new String[]{"/bin/bash", "-c", "/usr/bin/sudo -S /bin/cat /etc/sudoers 2>&1"}).start();
output = new OutputStreamWriter(pb.getOutputStream());
input = new InputStreamReader(pb.getInputStream());
int bytes, tryies = 0;
char buffer[] = new char[1024];
while ((bytes = input.read(buffer, 0, 1024)) != -1) {
if(bytes == 0)
continue;
//Output the data to console, for debug purposes
String data = String.valueOf(buffer, 0, bytes);
System.out.println(data);
// Check for password request
if (data.contains("[sudo] password")) {
// Here you can request the password to user using JOPtionPane or System.console().readPassword();
// I'm just hard coding the password, but in real it's not good.
char password[] = new char[]{'t','e','s','t'};
output.write(password);
output.write('\n');
output.flush();
// erase password data, to avoid security issues.
Arrays.fill(password, '\0');
tryies++;
}
}
return tryies < 3;
} catch (IOException ex) {
}
return false;
}
Do not try to write a system password plainly in a file, especially for a user that have the sudo privilege, just as #jointEffort answered, issued privilege should be solved by system administrators not by app writers.
sudo allow you to grant privileges for specific command to specific user, which is precisely enough, check this post
and you can choose to manage the privilege in a separated file other than the main sudoers file if you want just append #includedirs /etc/sudoers.d/ in the main /etc/sudoers file(most Linux distributions have already done that) and make a file like ifconfig-user with:
USER_NAME ALL=(ALL) NOPASSWD: /sbin/ifconfig
Another thing, remember to edit the config file with visudo in case you lost control of your system when there is syntax error.
I know this is an old thread but i just want to put this here:
you can use sudo -S *command* as a command that you pass to create the Process instance. Then get the output stream and write the password to it, and add at the end of it a new line and a c. return (\n\r). The return may not be required but i passed it just in case. Also it is a good idea to flush the stream, to make sure everything is written to it. I've done it a few times and it works like a charm. And DO NOT forget to close streams :).
Once you spawn a process you can extract the input and output streams. Just feed the password to the output stream (you output it into the proccess's input). So the code would look something like -
Process pb = new ProcessBuilder("gedit").start();
OutputStream out = pb.getOutputStream();
out.write(password);

How to make Runtime.getRuntime().exec execute command one by one and get output rather than executing all commands

I am trying to run a *.bat file (which is capable of running several commands and retrieve the output one by one) from my java application. My intention is to send one command, read output use this output for second command and again retrieve the output.
To achieve this, through Runtime.getRuntime().exec I am passing more than one command as an input to PrintWriter. Issue is that after completing all the steps only I can read the output from *.bat through buffer ,but my intention is to run one command get the output and manipulate this output to send second command.
Unfortunately is not working. Any resolution for this?..
I got the idea to send more than one command to Runtime.getRuntime().exec from this link (How to execute cmd commands via Java)
The following is the same code which I got from above link
String[] command =
{
"cmd",
};
Process p = Runtime.getRuntime().exec(command);
new Thread(new SyncPipe(p.getErrorStream(), System.err)).start();
new Thread(new SyncPipe(p.getInputStream(), System.out)).start();
PrintWriter stdin = new PrintWriter(p.getOutputStream());
stdin.println("dir c:\\ /A /Q");
// write any other commands you want here
stdin.close();
int returnCode = p.waitFor();
System.out.println("Return code = " + returnCode);
class SyncPipe implements Runnable
{
public SyncPipe(InputStream istrm, OutputStream ostrm) {
istrm_ = istrm;
ostrm_ = ostrm;
}
public void run() {
try
{
final byte[] buffer = new byte[1024];
for (int length = 0; (length = istrm_.read(buffer)) != -1; )
{
ostrm_.write(buffer, 0, length);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
private final OutputStream ostrm_;
private final InputStream istrm_;
}
In your case I would not use Threads, you want a sequential execution path.
Actually, instead of trying to reinvent the wheel, I strongly suggest you to use an expect-like java library to do that kind of thing.
Just because there are several things that you'll have to deal with, such as timeout between requests, waiting for the output to return, etc.
Take a look at these libraries
http://expectj.sourceforge.net/
https://code.google.com/p/expect4j/
https://github.com/ronniedong/Expect-for-Java
http://code.google.com/p/enchanter/
In particular, I use expectj in my project and it works pretty well (although I think expect4j is more popular)
With expectj, your code will look like this (from http://expectj.sourceforge.net/)
// Create a new ExpectJ object with a timeout of 5s
ExpectJ expectinator = new ExpectJ(5);
// Fork the process
Spawn shell = expectinator.spawn("/bin/sh");
// Talk to it
shell.send("echo Chunder\n");
shell.expect("Chunder");
shell.send("exit\n");
shell.expectClose();
You can do the redirection of output of one command to other in the bat file itself using pipe.
I am sorry, i hadn't noticed that you want to manipulate the output first.
So instead of using bat file, you can run the commands that are in bat file from java using exec , get the out put, and use the out put to execute the next command.

Runtime.exec().waitFor() not actually waiting for

I've got some code that uses Runtime.exec() to run an external .jar (built as an IzPack installer).
If I run this external.jar from the command line like so:
java -jar external.jar
Then the command prompt does not return control until the application is finished. However, if I run external.jar from within some java class, using:
Process p = Runtime.getRuntime().exec("java -jar external.jar");
int exitCode = p.waitFor();
System.out.println("Process p returned: " + exitCode);
Then p returns almost instantly with a success code of 0, despite external.jar having not yet completed execution (i've also tried this via the ProcessBuilder route of external file execution).
Why does it wait to return from the command line, but not when executed from within another java program?
I've also set up 3 jars, A, B and C where A calls B which calls C (using Runtime.exec()), where C Thread.sleeps for 10 seconds, as a simple test, and as expected, A doesn't return until 10 seconds after it runs.
I figure this is probably some kind of a threading issue with external.jar where execution is being handed over from one thing to another, but given that it works directly from the command line i kind of expected to see the same behaviour (perhaps naively) when called from within another java program.
I've tested this on Windows and Ubuntu with Java 6.
Thanks!
another possible way to achieve this might be to capture the output of the process and wait for it to finish.
For example:
Process tr = Runtime.getRuntime().exec( new String[]{"wkhtmltopdf",mainPage,mainPagePDF});
BufferedReader stdOut=new BufferedReader(new InputStreamReader(tr.getInputStream()));
String s;
while((s=stdOut.readLine())!=null){
//nothing or print
}
Normally the output stream is tr.getInputStream() but depending on the program you are executing the process output stream migh be:
tr.getInputStream()
tr.getErrorStream()
tr.getOutputStream()
By doing this while loop you force your program to wait the process to finish.
You can use Process Builder....
ProcessBuilder pb = new ProcessBuilder("java", "-jar", "/fielname.jar");
Process p = pb.start();
p.waitFor();
Are you spawning a new thread to handle the spawning of the process? If so the origional program will continue to operate independently of the spawned process and therefore waitFor() will only work on the new process and not the parent.
Process.waitFor() is useless for some native system command.
You need to get the process's output to determine if it is returned.
I wrote a sample code for you
/**
*
* #param cmdarray command and parameter of System call
* #param dir the directory execute system call
* #param returnImmediately true indicate return after system call immediately;
* false otherwise.
* if set true, the returned call result does not have reference value
* #return the return code of system call , default is -1
*/
public static int systemCall(String[] cmdarray,File dir,boolean returnImmediately)
{
int result = -1;
try {
Process p = Runtime.getRuntime().exec(cmdarray,null,dir);
if(!returnImmediately)
{
java.io.InputStream stdin = p.getInputStream();
java.io.InputStreamReader isr = new java.io.InputStreamReader(stdin);
java.io.BufferedReader br = new java.io.BufferedReader(isr);
String line = null;
while ( (line = br.readLine()) != null)
System.out.println(line);
}
try{result = p.exitValue();}
catch(Exception ie){;}
} catch (IOException e) {
e.printStackTrace();}
return result;
}
public static void main(String[] argc){
String[] cmdarray = {"jar","cvf","s2.jar","*"};
File dir = new File("D:\\src\\struts-2.3.1");
int k = systemCall(cmdarray,dir,true);
System.out.println("k="+k);
}
I had the same problem using processs to execute some software using the console, and i just solved it using process.waitFor()
For me it worked perfectly.
try{
Process tr = Runtime.getRuntime().exec( new String[]{ "wkhtmltopdf",frontPage,frontPagePDF});
tr.waitFor();
} catch (Exception ex) {
EverLogger.logEntry("Error al pasar a PDF la portada", "error", "activity");
return;
}
some more code here.

How to execute a interactive shell script using java Runtime?

I am wondering is there any way to execute following shell script, which waits for user input using java's Runtime class?
#!/bin/bash
echo "Please enter your name:"
read name
echo "Welcome $name"
I am using following java code to do this task but it just shows blank console.
public class TestShellScript {
public static void main(String[] args) {
File wd = new File("/mnt/client/");
System.out.println("Working Directory: " +wd);
Process proc = null;
try {
proc = Runtime.getRuntime().exec("sudo ./test.sh", null, wd);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Thing is when I execute above program, I believed it will execute a shell script and that shell script will wait for user input, but it just prints current directory and then exits. Is there any way to do this or it is not possible at all in java?
Thanks in advance
The reason it prints the current dir and exits is because your java app exits. You need to add a (threaded) listener to the input and error streams of your created process, and you'll probably want to add a printStream to the process's output stream
example:
proc = Runtime.getRuntime().exec(cmds);
PrintStream pw = new PrintStream(proc.getOutputStream());
FetcherListener fl = new FetcherListener() {
#Override
public void fetchedMore(byte[] buf, int start, int end) {
textOut.println(new String(buf, start, end - start));
}
#Override
public void fetchedAll(byte[] buf) {
}
};
IOUtils.loadDataASync(proc.getInputStream(), fl);
IOUtils.loadDataASync(proc.getErrorStream(), fl);
String home = System.getProperty("user.home");
//System.out.println("home: " + home);
String profile = IOUtils.loadTextFile(new File(home + "/.profile"));
pw.println(profile);
pw.flush();
To run this, you will need to download my sourceforge project: http://tus.sourceforge.net/ but hopefully the code snippet is instructive enough that you can just adapt to J2SE and whatever else you are using.
If you use a Java ProcessBuilder you should be able to get the Input, Error and Output streams of the Process you create.
These streams can be used to get information coming out of the process (like prompts for input) but they can also be written to to put information into the process directly too. For instance:
InputStream stdout = process.getInputStream ();
BufferedReader reader = new BufferedReader (new InputStreamReader(stdout));
String line;
while(true){
line = reader.readLine();
//...
That'll get you the output from the process directly. I've not done it myself, but I'm pretty sure that process.getOutputStream() gives you something that can be written to directly to send input to the process.
The problem with running interactive programs, such as sudo, from Runtime.exec is that it attaches their stdin and stdout to pipes rather than the console device they need. You can make it work by redirecting the input and output to /dev/tty.
You can achieve the same behaviour using the new ProcessBuilder class, setting up the redirection using ProcessBuilder.Redirect.INHERIT.
Note sure at all you can send input to your script from Java. However I very strongly recommend to have a look at Commons Exec if you are to execute external scripts from Java:
Commons Exec homepage
Commons Exec API

Categories

Resources