Why is calling fork via JNA in Java unreliable? - java

I have my own terminal app written in Java and it works sometimes. To use bash on Linux you have to create a pty, fork and exec bash with the pty assigned to it's stdin/out/err. I've done this using JNA in a few different ways but nothing is reliable. About 30% of the time the child process has problems and hangs. I have to kill the process. I've heard a lot about fork problems in Java, is there something I need to do? When it fails I will see the message "child process started" but some point after that before it calls execvpe it stops. It is not using 100% cpu, I have no idea what it is doing. I've looked at JPty and similar projects and they seem to do the same. Are they reliable?
Here is my code using forkpty()
private boolean fork_pty(String cmd, String args[], String env[]) {
IntByReference masterRef = new IntByReference();
pid = util.forkpty(masterRef, null, null, null);
if (pid == 0) {
System.out.println("child process started");
//child process (slave)
c.execvpe(cmd, args, env); //searches path for cmd
System.exit(0); //should not happen
}
//parent process (master)
master = masterRef.getValue();
new Thread() {
public void run() {
c.waitpid(pid, new IntByReference(), 0);
close();
}
}.start();
return true;
}
Any ideas? What if I forked before AWT is started, might that help? Could the gc be an issue???

I found a solution. Instead of fork()ing I use ProcessBuilder to fork() for me. I tried using ProcessBuilder to exec bash directly using redirectInput/Output/Error to the slave pty but then I couldn't call setsid() and bash was messed up. Then I used ProcessBuilder to exec another java function that completed the child end of the process which sets up stdin/out/err and then uses c.execvpe to run bash and that works every time.
Full source will be available in JavaForce/7.35 # javaforce.sourceforge.net (see javaforce.jna.LnxPty)
-1 to those who thought it could not be done :-P
Here is my fork function:
private boolean fork_nofork(String cmd, String args[], String env[]) {
JFLog.log("fork:no fork version");
String slaveName;
master = c.posix_openpt(O_RDWR | O_NOCTTY);
if (master == -1) return false;
slaveName = c.ptsname(master);
if (slaveName == null) {
JFLog.log("LnxPty:slave pty == null");
return false;
}
if (c.grantpt(master) != 0) {
JFLog.log("LnxPty:grantpt() failed");
return false;
}
if (c.unlockpt(master) != 0) {
JFLog.log("LnxPty:unlockpt() failed");
return false;
}
ArrayList<String> cmdline = new ArrayList<String>();
cmdline.add("java");
cmdline.add("-cp");
cmdline.add("/usr/share/java/javaforce.jar:/usr/share/java/jna.jar");
cmdline.add("javaforce.jna.LnxPty");
cmdline.add(slaveName);
cmdline.add(cmd);
cmdline.add("" + (args.length-1)); //# args
for(int a=0;a<args.length;a++) {
if (args[a] == null) break;
cmdline.add(args[a]);
}
for(int a=0;a<env.length;a++) {
if (env[a] == null) break;
cmdline.add(env[a]);
}
String cl[] = cmdline.toArray(new String[0]);
try {
ProcessBuilder pb = new ProcessBuilder(cl);
pb.directory(new File("/home/" + System.getenv("USER")));
p = pb.start();
} catch (Exception e) {
JFLog.log(e);
return false;
}
writeBuf = Native.malloc(1024);
readBuf = Native.malloc(1024);
new Thread() {
public void run() {
try {p.waitFor();} catch (Exception e) {}
close();
}
}.start();
return true;
}
And here is the main() function that runs in the child process:
/** This becomes the child process. */
public static void main(String args[]) {
if (args == null || args.length < 3) {
System.out.println("Usage : LnxPty slaveName, cmd, #args, [args...], [env...]");
return;
}
init();
String slaveName = args[0];
String cmd = args[1];
int noArgs = JF.atoi(args[2]);
int p = 3;
ArrayList<String> process_args = new ArrayList<String>();
ArrayList<String> process_env = new ArrayList<String>();
for(int a=0;a<noArgs;a++) {
process_args.add(args[p++]);
}
while (p < args.length) {
process_env.add(args[p++]);
}
termios attrs = new termios();
try {
int slave = c.open(slaveName, O_RDWR); //should open this in child process
if (slave == -1) {
System.out.println("LnxPty:unable to open slave pty");
System.exit(0);
}
if (c.setsid() == -1) {
System.out.println("LnxPty:unable to setsid");
System.exit(0);
}
c.tcgetattr(slave, attrs);
// Assume input is UTF-8; this allows character-erase to be correctly performed in cooked mode.
attrs.c_iflag |= IUTF8;
// Humans don't need XON/XOFF flow control of output, and it only serves to confuse those who accidentally hit ^S or ^Q, so turn it off.
attrs.c_iflag &= ~IXON;
// ???
attrs.c_cc[VERASE] = 127;
c.tcsetattr(slave, TCSANOW, attrs);
c.dup2(slave, STDIN_FILENO);
c.dup2(slave, STDOUT_FILENO);
c.dup2(slave, STDERR_FILENO);
c.signal(SIGINT, SIG_DFL);
c.signal(SIGQUIT, SIG_DFL);
c.signal(SIGCHLD, SIG_DFL);
c.execvpe(cmd, process_args.toArray(new String[0]), process_env.toArray(new String[0]));
System.exit(0); //should not happen
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}

Related

How to get PID of a process running in Windows using Java

I have used OSHI libraries available, but the getProcessID function is not working. I need to find the PID of a process entered by the user.
I have now used this code
public static String getProcessPID(String processName, boolean... ignoreLetterCase) {
String pid = "";
boolean ignoreCase = true;
if (ignoreLetterCase.length > 0) {
ignoreCase = ignoreLetterCase[0];
}
// Acquire the Task List from Windows
ProcessBuilder processBuilder = new ProcessBuilder("tasklist.exe");
Process process;
try {
process = processBuilder.start();
}
catch (java.io.IOException ex) {
return "";
}
// Read the list and grab the desired PID
String tasksList;
try (Scanner scanner = new Scanner(process.getInputStream(), "UTF-8").useDelimiter("\\A")) {
int counter = 0;
String strg = "";
while (scanner.hasNextLine()) {
strg = scanner.nextLine();
// Uncomment the line below to print the current Tasks List to Console Window.
// System.out.println(strg);
if (!strg.isEmpty()) {
counter++;
if (counter > 2) {
if (ignoreCase) {
if (strg.toLowerCase().contains(processName.toLowerCase())) {
String[] tmpSplit = strg.split("\\s+");
pid += (pid.isEmpty()) ? tmpSplit[1] : ", " + tmpSplit[1];
}
}
else {
if (strg.contains(processName)) {
String[] tmpSplit = strg.split("\\s+");
pid += (pid.isEmpty()) ? tmpSplit[1] : ", " + tmpSplit[1];
}
}
}
}
}
}
return pid;
}
This fails for processes with multiple instances running such as Chrome. So, how do I get Parent ProcessID or a process with a space in between the name?
Don’t use tasklist.exe. Use the ProcessHandle class. Not only will your code be shorter and easier to maintain, it will also work on systems other than Windows, with no additional effort.
Also, don’t use a varargs argument when you only want zero or one values. Use method overloads for that.
public static OptionalLong getProcessPID(String processName) {
return getProcessPID(processName, true);
}
public static OptionalLong getProcessPID(String processName, boolean ignoreLetterCase) {
Predicate<String> matcher = cmd -> (ignoreLetterCase
? cmd.toLowerCase().contains(processName.toLowerCase())
: cmd.contains(processName));
try (Stream<ProcessHandle> processes = ProcessHandle.allProcesses()) {
return processes
.filter(p -> p.info().command().filter(matcher).isPresent())
.mapToLong(p -> p.pid())
.findFirst();
}
}

execute julia scripts from Java

I'm coding Julia script with ZeroMQ.
My goal is to communicate with ZMQ between two scripts. Here is an example:
# script1
using ZMQ
ctx = ZMQ.Context()
sockDealer = ZMQ.Socket(ctx, DEALER)
ZMQ.set_identity(sockDealer, "idA")
ZMQ.connect(sockDealer, "tcp://localhost:5555")
ZMQ.send(sockDealer, "hello world!")
ZMQ.close(sockDealer)
ZMQ.close(ctx)
#script2
using ZMQ
function pollrecv(socket::ZMQ.Socket,zmsg::Message)
rc = -1
while true
rc = ccall((:zmq_msg_recv, ZMQ.zmq), Cint, (Ptr{Message}, Ptr{Void}, Cint),
&zmsg, socket.data, ZMQ.ZMQ_DONTWAIT)
if rc == -1
# Base.Libc.EAGAIN = 11
# Problem unsolved: Failure to find Base.Libc.EAGAIN
if !(ZMQ.zmq_errno() == 11)
throw(ZMQ.StateError(ZMQ.jl_zmq_error_str()))
end
return false
else
ZMQ.get_events(socket) != 0 && notify(socket)
break
end
end
return true
end
ctx = ZMQ.Context()
sockRouter = ZMQ.Socket(ctx, ROUTER)
ZMQ.bind(sockRouter, "tcp://*:5555")
fini = false
while !fini
println("listening...")
idSock = Message()
while pollrecv(sockRouter, idSock)
msg = ZMQ.recv(sockRouter)
println("msg recv: " * bytestring(msg))
fini = true
end
sleep(1)
end
ZMQ.close(sockRouter)
ZMQ.close(ctx)
I can execute them with Julia on the command prompt. Everything goes fine. Script 2 can receive the message of Script 1.
Now, I need to execute them from Java. Meaning that I need to create a java project which is just like a controller. Here is my Java project:
public class Container {
private Vector<String[]> commands;
public Container() {
this.commands = new Vector<String[]>();
}
public void addCommand(String[] strs) {
this.commands.addElement(strs);
}
public void execute() {
for(int i = 0; i < this.commands.size(); i++) {
try {
Process p = Runtime.getRuntime().exec(this.commands.get(i));
if(p.waitFor() != 0){
System.err.println("exit value = " + p.exitValue());
}
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuffer stringBuffer = new StringBuffer();
String line = null;
while((line = in.readLine()) != null){
stringBuffer.append(line + "-");
}
System.out.println(stringBuffer.toString());
} catch (IOException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
} catch(InterruptedException e){
System.err.println(e);
}
}
}
}
//main
public class Main {
public static void main(String[] args) {
Container c = new Container();
String[] script1 = {"/usr/bin/julia", "/home/thomas/Julia/script1.jl"};
String[] script2 = {"/usr/bin/julia", "/home/thomas/Julia/script2.jl"};
c.addCommand(script1);
c.addCommand(script2);
c.execute();
}
}
However, when I run my java project, I can see that it keeps running but I can't see anything on the console: no result, no message, no error.
I think there is something wrong in my java project.
You'll want to run the two scripts concurrently: script2 is the server script, so it should be running when you run script1. As it is now, Process.waitFor() will wait for script1, the client script, to complete, before executing the server script script2 in the next for iteration.
You could start them as such:
String[] clientScript = { "/usr/bin/julia", "/home/thomas/Julia/script1.jl" };
String[] serverScript = { "/usr/bin/julia", "/home/thomas/Julia/script2.jl" };
Process server = Runtime.getRuntime().exec(serverScript);
Process client = Runtime.getRuntime().exec(clientScript);
and instantiate two threads to read their outputs:
(new ProcessReader(server)).start();
(new ProcessReader(client)).start();
using
public class ProcessReader extends Thread {
private Process p;
public ProcessReader(Process p) {
this.p = p;
}
#Override
public void run() {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
try {
String line;
while ((line = in.readLine()) != null) {
System.out.println("Read: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Although, since the clientScript doesn't produce any output, you could just
start the scripts, and only read the output from the server script - no thread needed.
There's one more thing to consider: the serverScript must be listening... before the clientScript attempts to connect. So you may want to do this:
Process server = Runtime.getRuntime().exec(serverScript);
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
if ( in.readLine().equals("listening...") ) {
Process client = Runtime.getRuntime().exec(clientScript);
String line;
while ( (line=in.readLine()) != null )
System.out.println("Read: " + line );
}
Since this is not a specific answer to your question, this may help to you or other users that attempt to similar jobs.
JuliaCaller is an early stage library for calling Julia from Java. It executes the Julia executable as a Java process and runs a script in the Julia side. This script opens a TCP server that listens on a given port number. Every command, statement or expression sent from Java is then executed and results are sent back to Java in JSON format.
This library also implements the standard javax.script interface, that means, Julia libraries, functions, and programs can run like a scripting language that is implemented in Java (mimics).
Here is the example:
Constants.setProperties(Constants.JULIA_PATH, "/usr/local/bin/julia");
Constants.setProperties(Constants.JULIA_PORT, "8001");
// Creating a scripting interface for Julia
manager = new ScriptEngineManager();
engine = manager.getEngineByName("Julia");
// Sending command 'a = 3' to Julia from Java
engine.eval("a = 3");
// Handling the result in Java
Object a = engine.get("a");
More examples are given in the GitHub page.
Source code with Apache License

How to run a bat by java and can stop it when time out?

I need a function whose argument is bat's fileName and a float means timeOut. I've used Process in java to do it. But if I want to stop it, I find the p.destroy() cannot stop the exe file which be called by bat file and still runs.So ,how can I stop it like "Ctrl"+"C" in cmd?
public void exec(String path, float timeOutFloat) throws IOException, InterruptedException {
Runtime r = Runtime.getRuntime();
Process p = r.exec(new String[] { path });
ThreadReadExec2 thread = new ThreadReadExec2(p.getInputStream());
thread.start();
long timeOut = (long) ((float) timeOutFloat) * 1000;
long time = 0;
long onceTime = 100;
while (thread.isAlive()) {
Thread.sleep(onceTime);
time += onceTime;
if (time > timeOut) {
p.destroy();
Thread.sleep(onceTime);
}
}
int res = p.waitFor();
System.out.println("res:" + res);
}
class ThreadReadExec2 extends Thread {
InputStream input;
public ThreadReadExec2(InputStream input) {
this.input = input;
}
#Override
public void run() {
BufferedReader ir = new BufferedReader(new InputStreamReader(input));
try {
String line;
while ((line = ir.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
}
}
}
The reason that this is happening is likely because the destroy method is killing the command shell you invoked via the batch file, but not the process that that batch file in turn invoked.
You essentially need to kill the process tree of the batch file.
There are some methods in this question that suggest ways to accomplish this in Java, although it's somewhat roundabout (link to the one that seems the most promising):
https://stackoverflow.com/a/7627648/3583500

How to use java program to run command prompt commands?

this is my first time posting here, so I'm not really sure what to say/ask.
Anyways, I am trying to make a simple java program that runs command prompt commands from the java program, mainly used for ping flood (ping flooding myself).
Here is my current code
public class Core extends JFrame {
JTextField ipTextField;
int packets = 0;
boolean running = false;
public Core() {
super("Fatique");
Container container = getContentPane();
JButton bAttack = new JButton("Start Attack");
JButton bStop = new JButton("Stop Attack");
JPanel jPanel = new JPanel();
container.setLayout(new FlowLayout());
ipTextField = new JTextField("IP Address", 30);
container.add(ipTextField);
bAttack.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String input = ipTextField.getText();
String[] value = input.split(":");
int amountOfPackets = Integer.parseInt(value[1]);
exec("cmd /c" + input + " -t -n " + amountOfPackets);
running = true;
}
});
bStop.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
stop();
}
});
if(!running) {
jPanel.add(bAttack);
} else {
jPanel.add(bStop);
}
add(jPanel);
}
public void exec(String cmd) {
try {
Process p = Runtime.getRuntime().exec(cmd);
System.out.println(getOutput(p) + " - " + getPacketsSent());
} catch (IOException e) {
e.printStackTrace();
}
}
public String getOutput(Process p) {
String output = null;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
output = line;
packets++;
}
return output;
} catch (IOException e) {
System.err.println(e.getStackTrace());
}
return null;
}
public int getPacketsSent() {
return packets;
}
public void stop() {
exec("cmd /c break");
running = false;
}
public static void main(String[] args) {
Core c = new Core();
c.setSize(500, 300);
c.setVisible(true);
c.setResizable(false);
c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.setLocationRelativeTo(null);
}
I'm quite new at java, so that might not do what I want it to do.
What I want it to do is I enter an ip address in the textfield, and split it with ":", and after that the amount of packets, for instance
127.0.0.1:100
Though now when I try to use that ip and packet amount, it returns "null - 0" (from exec method), and I'm not even sure if it did anything related to ping.
What I am trying to accomplish is as I already said, ping flood myself, and then output whatever I get as response, though I have no idea if this code does anything even related to that, I mostly use logic when coding java.
public String getOutput(Process p) {
String output = null;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
output = line;
packets++;
}
return output;
} catch (IOException e) {
System.err.println(e.getStackTrace());
}
return null;
}
Could someone explain me why my code code is not working how I want it to work? Please don't judge, as I already said, I'm quite new to java programming.
EDIT: Here is a quick "informative" explanation of what I am trying to accomplish.
I type in an ip address and how many packets I want to send. In this explanation, I am using localhost ip, and 5 packets.
I start the attack. At this part, I want the program to run cmd prompt command
ping 127.0.0.1 -t -n 5
127.0.0.1 being the ip that I put in the textfield in my program, and 5 is the amount of packets I put in the textfield.
I started the attack, so this is what should happen in the command prompt:
The language is Finnish, but still the same thing.
This is the basic explanation of what I am trying to accomplish, hopefully someone understood and can help/tell why my code is not working, or is working but not printing the proper lines in eclipse console.
There is a problem with your getOutput method. It looks like you intend to collect every line of output. But in fact, since you are assigning line to output, you will only return the last line before the end of stream.
To fix this, change
output = line;
to
output += line + "\n";
Or to be more correct:
output += line + LINE_SEPARATOR;
where you previously declared the latter as:
final String LINE_SEPARATOR = System.getProperty("line.separator");
That doesn't directly explain why you are getting null, but that might be because the command you are running is writing output to the 'error' stream rather than the 'output' stream.
Try something like this:
try {
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("ping 192.168.16.67");
InputStream in = p.getInputStream();
OutputStream out = p.getOutputStream ();
InputStream err = p.getErrorStream();
p.destroy();
} catch(Exception exc) {}
Then, you'll have to read the out variable to parse the ping command output continuously.
bAttack.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String input = ipTextField.getText();
String[] value = input.split(":");
int amountOfPackets = Integer.parseInt(value[1]);
try {
p=Runtime.getRuntime().exec("ping -n "+amountOfPackets+" "+value[0]);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
running = true;
}
Just a small modification of your code. get output is as:
public String getOutput(Process p) {
String output = null;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
output =output+ line+"\n";
packets++;
}
return output;
} catch (IOException e) {
System.err.println(e.getStackTrace());
}
return null;
}
Here output is JTextArea I have taken to display the output of PING process. I cannot show you the output because I lack reputation.
I don't know why first line is null. Anyway, it works.
Hope this help you. Have good time coding.

Java blocks after calling runtime.exec()

First some code:
Runtime runtime = Runtime.getRuntime();
String args[] = new String[2];
// args[0] = "/bin/bash";
// args[1] = "-c";
// args[2] = "/usr/bin/rpm2cpio "+archiveFile.getCanonicalPath()+" | /bin/cpio -idmv";
args[0] = "/usr/bin/rpm2cpio";
args[1] = archiveFile.getCanonicalPath();
Process rpm2cpioProcess = runtime.exec(args, null, dir);
// System.out.println("started rpm2cpio");
String args2[] = new String[3];
args2[0] = "/bin/cpio";
args2[1] = "-idmu";
args2[2] = "--quiet";
Process cpioProcess = runtime.exec(args2, null, dir);
// System.out.println("started cpio");
InputStream fromRpm2cpio = rpm2cpioProcess.getInputStream();
new ProcessInputStreamer(rpm2cpioProcess.getErrorStream());
OutputStream fromCpio = cpioProcess.getOutputStream();
new PipedStreamer(fromRpm2cpio, fromCpio);
new ProcessInputStreamer(cpioProcess.getErrorStream());
// System.out.println("pipe created");
while(cpioProcess!=null && fromRpm2cpio!=null) {
boolean doSleep = true;
// System.out.println("waking up");
if (cpioProcess!=null) {
try {
if (cpioProcess.exitValue()==0) {
cpioProcess = null;
doSleep = false;
}
} catch(IllegalThreadStateException e) {
}
}
if (rpm2cpioProcess!=null) {
try {
if (rpm2cpioProcess.exitValue()==0) {
rpm2cpioProcess = null;
doSleep = false;
}
} catch(IllegalThreadStateException e) {
}
}
if (doSleep) {
Thread.sleep(30);
}
// System.out.println("still running");
}
I'm trying to extract the content of an rpm archive. This code works fine after multiple modifications. My first attempt was to execute the next code through Java:
/bin/bash -c '/usr/bin/rpm2cpio <archive-file> | /bin/cpio -idmv'
Which worked fine the first time I ran it (you can see it in the code commented above). The second time I ran the code it got blocked since the extracted files already existed. So I thought maybe it has to do with the piping and thus split the call into two separate processes. This didn't help much either. So I then modified the arguments of the /bin/cpio from '-idmv' to '-idmu --quiet' and now it works. Unfortunately the -u option overwrites existing files 'unconditionally' which is not really needed. My question is why does it block with -idmv and why doesn't it block with -idmu ?
It could be waiting on standard input for some inputs. Redirect your standard input and/or output to /dev/null
I'd guess that your ProcessInputStreamer and/or PipedStreamer implement Runnable or extent Thread and you're not running them anywhere.

Categories

Resources