I use sshj library for communication with linux from my Java app.
And I need change user with command: su
SSHClient ssh = new SSHClient();
ssh.connect(host);
ssh.authPassword(login, password);
Session s = ssh.startSession();
Command cmd = s.exec("su my_user");
List<String> resultLines = IOUtils.readLines(cmd.getInputStream(), Charset.defaultCharset());
But, in IOUtils.readLines(cmd.getInputStream(), ... app is waits and does not go next.
Thanks for any help.
So, a couple of things for you to do to check this. Given what you're describing, the IOUtils.readLines() method is never returning due to the InputStream never reaching the end of stream.
The only way I've ever seen this happen is if the command you've run is stuck awaiting input. My bet would be that it's prompting for a password and is sat waiting for a response that will never come.
The following steps should help you debug:
1) Add the following line before the exec command to allocate a pseudo-terminal and ensure that any prompts will definitely be written to the InputStream:
s.allocateDefaultPTY();
2) Change your output handling to print the output character by character to the console, instead of waiting for the end of stream to be reached. Something like the following would do the trick:
InputStream in = cmd.getInputStream();
System.out.println("Starting SSH output.");
int cInt;
while ((cInt = in.read()) >= 0) {
char c = (char) cInt;
System.out.print(c);
}
This will allow you to see in your console exactly what the prompt is that is causing your command to never finish executing.
If there is a prompt there, the best ways I've found to respond to them are either to:
1) use an expect script to look for the prompt and respond to it.
2) If you'd prefer to keep it within your java code, use the session.startShell() method instead of session.exec() in order to allow you to open a full shell session where you can use Input and Output streams to send your commands and monitor the output for prompts then handle them by writing your response to the provided OutputStream. This is definitely the longer and more involved approach however!
Related
I'm very nubby at Java, so please excuse me if is a dummy question.
I have the following piece of code, and the execution flow is different from my intention:
channel = session.openChannel("shell");
OutputStream ops = channel.getOutputStream();
PrintStream ps = new PrintStream(ops, true);
channel.connect();
String sudo = "sudo su - user";
String copy = "copy from a to b;
String cd = "cd a directory";
String runload = "run a scrip in unix;
String cd1 = "cd a directoryu";
String executeload = "run a scrip in unix";
ps.println(sudo);
ps.println(copy);
ps.println(cd);
ps.println(runload);
ps.println(cd1 );
if (db.runload().contains("SUCCESS")) {
ps.println(executeload);
//execute this only if runload was success
} else {
System.exit(1);
}
The point is that "if block" is executed before previous lines. runload is script which load information in a table and is a precondition for executeload. In this case executeload will throw java.lang.NullPointerException.
My question is: it Is a way to control the flow in this piece of code? Every previous line is a precondition to the next one. My intention is to execute the next line when the current is finally executed.
The println just sends ("types") the command to the server. It does not wait for the command to be completed.
There's actually no way to wait for a command to be completed. A "shell" is a just a black-box with an input and an output. There's even no way to tell, what parts of the output corresponds to which input.
In general, you should use an "exec" channel for automation. The "shell" channel is intended for implementing an interactive terminal.
The "exec" channel closes with its command, so you can clearly tell that a command has finished. You can then open a new "exec" channel for further commands.
See JSch example for "exec" channel.
See also JSch Shell channel execute commands one by one testing result before proceeding.
Things get complicated though with your use of sudo.
See Running command after sudo login.
I am currently trying to make a litlle handy tool, you see I am a network administrator and my boss told me that he wanted me to monitor the network and block certain sites and ip for some game servers, so for the monitoring part we are going to redirect all traffic on the network to a server where we can monitor the traffic before sending it to the gateway.
For this we are going to use arpspoof in linux and I have finished a solution for the blocking of sites and servers, and what I am going to make is a GUI that makes it easier for me to handle and control these things and when I tried running arpspoof from java using a ProcessBuilder it does not work and I get no output?
It also does not enter the while loop. I can't really think of more to write atm, but if I can think of more I will update this thread.
My code:
new Thread() {
public void run() {
try {
System.out.println("running arpspoof...");
Process prb = new ProcessBuilder("gksudo", "arpspoof", "-i", "wlan0", Gateway).start();
InputStream is = prb.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println("Output: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
I have never used gksudo, but I googled it and it says it's a GUI version of sudo. I'm guessing that you just launched a GUI app which does not write anything to stdout and which does not return. If so, then the code is doing what I would expect. It is blocking until the process writes a line of text that it can read - which never occurs so it blocks indefinitely.
First test your ProcessBuilder code using a trivial command like "echo" to make sure your Java code is working as expected. Then work your way back. Try running your program as root so you don't need the sudo argument and see if that works. Then finally try to run it using sudo instead of gksudo.
I think #user is on the right track, but there are a couple of other possible explanations.
The gksudo command could be asking for a password. I'm not sure where it would ask, but there's a good chance that it won't be the "stdout" stream of the "gksudo" process.
If "gksudo" or the command that you are "gksudo"-ing fails to launch, there is a good chance that it will write an error message to its "stderr" stream. But you are not reading "stderr".
To help diagnose this, you need to try the following:
Look in the log file that for "sudo" - it is "/var/log/secure" on my box.
Use "ps -efl" (or similar) to see what processes exist while your application is blocked waiting for output. (If that is happening ...)
Look to see if "gksudo" is prompting for a password in an unexpected place.
Try temporarily tweaking the "sudoers" file to allow the "arpspoof" command to be "sudo"-ed without a password.
I have written a small program to start to Hive Server. Command to start to Hive Server is in shell file. When I call the shell file to start Hive Server it tends to start it and get Hang. Is there any problem in program?
Code:
try
{
String cmd = "/home/hadoop/sqoop-1.3.0-cdh3u1/bin/StartServer.sh"; // this is the command to execute in the Unix shell
// create a process for the shell
ProcessBuilder pb = new ProcessBuilder("bash", "-c", cmd);
pb.redirectErrorStream(true); // use this to capture messages sent to stderr
Process shell = pb.start();
InputStream shellIn = shell.getInputStream(); // this captures the output from the command
// wait for the shell to finish and get the return code
// at this point you can process the output issued by the command
// for instance, this reads the output and writes it to System.out:
int c;
while ((c = shellIn.read()) != -1)
{
System.out.write(c);
}
// close the stream
shellIn.close();
}
catch(Exception e)
{
e.printStackTrace();
e.printStackTrace(pw);
pw.flush();
System.exit(1);
}
Please let me know this. Is something I missed in the program?
Thanks.
It looks like your program is doing what you've told it to do.
The first few lines should indeed start the Hive server. The lines after that, read from the standard output of the server process, and echo each character to your Java process' console. Your Java process sits in a loop (making blocking I/O calls) for as long as the Hive server's output stream exists.
That is, your Java process will sit in a loop, echoing output, for as long as the Hive server is running.
Is this what you want it to do? If so, then it obviously won't be able to exit. If not, then there's no reason for you to read from the server's input stream at all, and your Java program can exit after it has started the server process. Alternatively, if you want to listen for output and do other things in your Java process, you'll need to use multiple threads in order to do two things at once.
As far as I can see you are starting server, i.e. application that starts and does not terminates soon. It remains running. This means that the STDOUT of this applcation (the server) is not closed (unless you are killing the server).
Method shellIn.read() is blocking. It reads the next byte from input stream and returns when the byte is read or when stream is closed.
So, in your case the stream is never closed, therefore your program got stuck: it is waiting forever for the input (and probably reading it).
To solve this problem you need separate thread: either in java or in OS. You can run your server from separate java thread or compose command line to run server in background (for example using trailing &).
I'd like to set up a Java application that works as a server, accepting (blocking) queries from a Bash script. The Java part is written using nio, and has a main loop that looks like this:
ServerSocketChannel ssc = ...; // bound to localhost:8011
Charset charset = ...;
// Waits for connections forever.
while(true) {
SocketChannel sc = ssc.accept();
StringBuffer sb = new StringBuffer();
int read = 0;
// Builds up a string representing the query.
while(true) {
ByteBuffer bb = ByteBuffer.allocate(1024);
read = sc.read(bb);
if(read == -1) break;
bb.flip();
CharBuffer cb = charset.decode(bb);
sb.append(cb.toString());
}
// Do something with the query.
sc.write(charset.encode(CharBuffer.wrap(sb.toString())));
sc.close();
}
The Bash part relies on the /dev/tcp/ magic:
exec 3<> /dev/tcp/localhost/8011
echo "message" 1>&3
I can see that the message sent from Bash does reach the Java part (if I add a System.out.println(cb); in the inner loop, I can see the parts), but the inner loop doesn't terminate unless I kill the Bash script.
My question is really quite simple: how can the Bash script signal to the Java server that its communication has come to an end? I've tried adding
echo -en "\004" 1>&3
in my Bash script, but that didn't help.
Try closing the file descriptor. This should be seen by Java as a closed stream, and allow the inner loop to terminate.
exec 3>&-
It is possible for a socket can be "half-open" (that is, shut down in one direction but still open in the other). A Socket instance has methods to detect this state.
I haven't tested whether the pipe-socket hybrid created by bash supports this or not. If it doesn't, you'll have to design a protocol with some internal length-encoding or delimiting sequences to indicate message boundaries.
What I'm trying to achieve is to get Java to think that the
communication has ended for now but may resume later.
You need to understand that this requirement is a contradiction in terms. Either the communication has ended or it may resume later. There is nothing in TCP that supports this, ergo nothing in Java either.
I am building a simple telnet connection daemon for communications between internal network applications, and I ran into an issue when reading the first line from BufferedReader.
This code snippet is not complete due to the fact there is a lot of other junk in there so I have stripped it down only to include the object creation and read from the steam.
in = new BufferedReader(new InputStreamReader(this.client.getInputStream()));
out = new PrintWriter(this.client.getOutputStream(), true);
String line;
while (true) {
out.println(flag); // flag is just an integer
System.out.println(line);
// Processing the line and updating 'flag' accordingly
}
Entering test into the telnet connection yielded v? v v? v' ²? v? ²?test in the console that was running the program. This does not happen to lines sent after the first one.
Is there a way to clear that garbage out before the user interfaces with it so it doesn't get sent with the first line? or is this issue caused by my telnet client (and might be fixed when I write a client that interfaces with this)?
I strongly suspect it's the telnet protocol negotiation. Ideally, you should handle it having read RFC 854 carefully.
Note that you shouldn't just use InputStreamReader without specifying the character encoding - it's very unlikely that the platform default encoding is the one you want.