Run Oracle Exp command from Java SSH on Remote Server [duplicate] - java

I have a piece of code which connects to a Unix server and executes commands.
I have been trying with simple commands and they work fine.
I am able to login and get the output of the commands.
I need to run an Ab-initio graph through Java.
I am using the air sandbox run graph command for this.
It runs fine, when I login using SSH client and run the command. I am able to run the graph. However, when I try to run the command through Java it gives me a "air not found" error.
Is there any kind of limit on what kind of Unix commands JSch supports?
Any idea why I'm not able to run the command through my Java code?
Here's the code:
public static void connect(){
try{
JSch jsch=new JSch();
String host="*****";
String user="*****";
String config =
"Host foo\n"+
" User "+user+"\n"+
" Hostname "+host+"\n";
ConfigRepository configRepository =
com.jcraft.jsch.OpenSSHConfig.parse(config);
jsch.setConfigRepository(configRepository);
Session session=jsch.getSession("foo");
String passwd ="*****";
session.setPassword(passwd);
UserInfo ui = new MyUserInfo(){
public boolean promptYesNo(String message){
int foo = 0;
return foo==0;
}
};
session.setUserInfo(ui);
session.connect();
String command="air sandbox run <graph-path>";
Channel channel=session.openChannel("exec");
((ChannelExec)channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec)channel).setErrStream(System.err);
InputStream in=channel.getInputStream();
channel.connect();
byte[] tmp=new byte[1024];
while(true){
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)break;
page_message=new String(tmp, 0, i);
System.out.print(page_message);
}
if(channel.isClosed()){
if(in.available()>0) continue;
System.out.println("exit-status: "+channel.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
channel.disconnect();
session.disconnect();
}
catch(Exception e){
System.out.println(e);
}
}
public static void main(String arg[]){
connect();
}
public String return_message(){
String ret_message=page_message;
return ret_message;
}
public static abstract class MyUserInfo
implements UserInfo, UIKeyboardInteractive{
public String getPassword(){ return null; }
public boolean promptYesNo(String str){ return false; }
public String getPassphrase(){ return null; }
public boolean promptPassphrase(String message){ return false; }
public boolean promptPassword(String message){ return false; }
public void showMessage(String message){ }
public String[] promptKeyboardInteractive(String destination,
String name,
String instruction,
String[] prompt,
boolean[] echo){
return null;
}
}

The "exec" channel in the JSch (rightfully) does not allocate a pseudo terminal (PTY) for the session. As a consequence a different set of startup scripts is (might be) sourced (particularly for non-interactive sessions, .bash_profile is not sourced). And/or different branches in the scripts are taken, based on absence/presence of the TERM environment variable. So the environment might differ from the interactive session, you use with your SSH client.
So, in your case, the PATH is probably set differently; and consequently the air executable cannot be found.
To verify that this is the root cause, disable the pseudo terminal allocation in your SSH client. For example in PuTTY, it's Connection > SSH > TTY > Don't allocate a pseudo terminal. Then, go to Connection > SSH > Remote command and enter your air ... command. Check Session > Close window on exit > Never and open the session. You should get the same "air not found" error.
Ways to fix this, in preference order:
Fix the command not to rely on a specific environment. Use a full path to air in the command. E.g.:
/bin/air sandbox run <graph-path>
If you do not know the full path, on common *nix systems, you can use which air command in your interactive SSH session.
Fix your startup scripts to set the PATH the same for both interactive and non-interactive sessions.
Try running the script explicitly via login shell (use --login switch with common *nix shells):
bash --login -c "air sandbox run sandbox run <graph-path>"
If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:
String command="PATH=\"$PATH;/path/to/air\" && air sandbox run <graph-path>";
Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel using the .setPty method:
Channel channel = session.openChannel("exec");
((ChannelExec)channel).setPty(true);
Using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?
For a similar issues, see
Certain Unix commands fail with "... not found", when executed through Java using JSch even with setPty enabled
Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of "yes/"no")
JSch: Is there a way to expose user environment variables to "exec" channel?
Command (.4gl) executed with SSH.NET SshClient.RunCommand fails with "No such file or directory"

you could try to find out where "air" resides with
whereis air
and then use this outcome.
something like
/usr/bin/air sandbox run graph

You can use an ~/.ssh/environment file to set your AB_HOME and PATH variables.

Related

How to fix TERM environment not set in Intellij?

I am currently working on a java automation application incorporating Jsch. When I run my code however, it passes back an error saying that the TERM environment is not set up.
I already tried to manually add the environment in intellij by choosing environment variables. Then I add TERM=xterm. Though when I run that, it still fails.
import com.jcraft.jsch.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Driver {
public static void main(String[] args) throws Exception {
JSch jsch = new JSch();
Session session;
try {
// Open a Session to remote SSH server and Connect.
// Set User and IP of the remote host and SSH port.
session = jsch.getSession("username", "host", 22);
// When we do SSH to a remote host for the 1st time or if key at the remote host
// changes, we will be prompted to confirm the authenticity of remote host.
// This check feature is controlled by StrictHostKeyChecking ssh parameter.
// By default StrictHostKeyChecking is set to yes as a security measure.
session.setConfig("StrictHostKeyChecking", "no");
//Set password
session.setPassword("password");
session.connect();
// create the execution channel over the session
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
// Set the command to execute on the channel and execute the command
channelExec.setCommand("./script.sh");
channelExec.connect();
// Get an InputStream from this channel and read messages, generated
// by the executing command, from the remote side.
InputStream in = channelExec.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// Command execution completed here.
// Retrieve the exit status of the executed command
int exitStatus = channelExec.getExitStatus();
if (exitStatus > 0) {
System.out.println("Remote script exec error! " + exitStatus);
}
//Disconnect the Session
session.disconnect();
} catch (JSchException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Make sure that the variable is set in your current shell by either exporting it or running your code with a set variable for TERM.
Something similar to the following should work:
TERM=linux /path/to/your/executable --some-arguments
The following could possibly only be relevant to bash but there is also a way to export a variable so as to make it global.
After exporting a variable, you can verify its value using:
echo $TERM
Empty response means variable is not set. Else, well... you get it i am sure. In order to export it globally, in bash that is, you can use the command line directly or add the export command into your dotfiles, which should be loaded upon login
export TERM=linux
Either way you choose, the command stays the same. There are multiple terminals and types, 'linux' being a very very generic one. A more color-friendly solution could be to try using 'xterm-256color' instead.
export TERM=xterm-256color
You should check out the basics of terminal if you wish to learn more. I hope this can help you achive your desired outcome.
Cheers
IntelliJ IDEA Run console is not a real Terminal, hence the problem.
You can run the code manually outside of IntelliJ IDEA or in the Terminal tool window.
For debugging you can use the Remote debug.
Related request: Add option to run configuration to launch in real console.

Certain Unix commands fail with "... not found", when executed through Java using JSch

I have a piece of code which connects to a Unix server and executes commands.
I have been trying with simple commands and they work fine.
I am able to login and get the output of the commands.
I need to run an Ab-initio graph through Java.
I am using the air sandbox run graph command for this.
It runs fine, when I login using SSH client and run the command. I am able to run the graph. However, when I try to run the command through Java it gives me a "air not found" error.
Is there any kind of limit on what kind of Unix commands JSch supports?
Any idea why I'm not able to run the command through my Java code?
Here's the code:
public static void connect(){
try{
JSch jsch=new JSch();
String host="*****";
String user="*****";
String config =
"Host foo\n"+
" User "+user+"\n"+
" Hostname "+host+"\n";
ConfigRepository configRepository =
com.jcraft.jsch.OpenSSHConfig.parse(config);
jsch.setConfigRepository(configRepository);
Session session=jsch.getSession("foo");
String passwd ="*****";
session.setPassword(passwd);
UserInfo ui = new MyUserInfo(){
public boolean promptYesNo(String message){
int foo = 0;
return foo==0;
}
};
session.setUserInfo(ui);
session.connect();
String command="air sandbox run <graph-path>";
Channel channel=session.openChannel("exec");
((ChannelExec)channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec)channel).setErrStream(System.err);
InputStream in=channel.getInputStream();
channel.connect();
byte[] tmp=new byte[1024];
while(true){
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)break;
page_message=new String(tmp, 0, i);
System.out.print(page_message);
}
if(channel.isClosed()){
if(in.available()>0) continue;
System.out.println("exit-status: "+channel.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
channel.disconnect();
session.disconnect();
}
catch(Exception e){
System.out.println(e);
}
}
public static void main(String arg[]){
connect();
}
public String return_message(){
String ret_message=page_message;
return ret_message;
}
public static abstract class MyUserInfo
implements UserInfo, UIKeyboardInteractive{
public String getPassword(){ return null; }
public boolean promptYesNo(String str){ return false; }
public String getPassphrase(){ return null; }
public boolean promptPassphrase(String message){ return false; }
public boolean promptPassword(String message){ return false; }
public void showMessage(String message){ }
public String[] promptKeyboardInteractive(String destination,
String name,
String instruction,
String[] prompt,
boolean[] echo){
return null;
}
}
The "exec" channel in the JSch (rightfully) does not allocate a pseudo terminal (PTY) for the session. As a consequence a different set of startup scripts is (might be) sourced (particularly for non-interactive sessions, .bash_profile is not sourced). And/or different branches in the scripts are taken, based on absence/presence of the TERM environment variable. So the environment might differ from the interactive session, you use with your SSH client.
So, in your case, the PATH is probably set differently; and consequently the air executable cannot be found.
To verify that this is the root cause, disable the pseudo terminal allocation in your SSH client. For example in PuTTY, it's Connection > SSH > TTY > Don't allocate a pseudo terminal. Then, go to Connection > SSH > Remote command and enter your air ... command. Check Session > Close window on exit > Never and open the session. You should get the same "air not found" error.
Ways to fix this, in preference order:
Fix the command not to rely on a specific environment. Use a full path to air in the command. E.g.:
/bin/air sandbox run <graph-path>
If you do not know the full path, on common *nix systems, you can use which air command in your interactive SSH session.
Fix your startup scripts to set the PATH the same for both interactive and non-interactive sessions.
Try running the script explicitly via login shell (use --login switch with common *nix shells):
bash --login -c "air sandbox run sandbox run <graph-path>"
If the command itself relies on a specific environment setup and you cannot fix the startup scripts, you can change the environment in the command itself. Syntax for that depends on the remote system and/or the shell. In common *nix systems, this works:
String command="PATH=\"$PATH;/path/to/air\" && air sandbox run <graph-path>";
Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel using the .setPty method:
Channel channel = session.openChannel("exec");
((ChannelExec)channel).setPty(true);
Using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?
For a similar issues, see
Certain Unix commands fail with "... not found", when executed through Java using JSch even with setPty enabled
Commands executed using JSch behaves differently than in SSH terminal (bypasses confirm prompt message of "yes/"no")
JSch: Is there a way to expose user environment variables to "exec" channel?
Command (.4gl) executed with SSH.NET SshClient.RunCommand fails with "No such file or directory"
you could try to find out where "air" resides with
whereis air
and then use this outcome.
something like
/usr/bin/air sandbox run graph
You can use an ~/.ssh/environment file to set your AB_HOME and PATH variables.

Shell ping command with source option is failing when executed using JSch setCommand

I'm testing short ping test program. If I set simple ping command, "ping y.y.y.y -c 5 -s 500 " into setCommand() function, it works as designed. But if I add addition ping options, "ping source x.x.x.x host y.y.y.y -c 5 -s 500", I got
ping: unknown host source
message back. If I manually execute both commands from the x-terminal, the both commands works fine.
I need to make the program to ping from different source interface IPs. What is the difference between two commands using JSch setCommand?
ping y.y.y.y -c 5 -s 500 (working)
ping source x.x.x.x host y.y.y.y -c 5 -s 500 (not working)
Code:
public static void main(String[] arg){
try{
JSch jsch=new JSch();
String host=null;
if(arg.length>0){
host=arg[0];
}
else{
host=JOptionPane.showInputDialog("Enter username#hostname",
System.getProperty("user.name")+
"#localhost");
}
String user=host.substring(0, host.indexOf('#'));
host=host.substring(host.indexOf('#')+1);
Session session=jsch.getSession(user, host, 22);
UserInfo ui=new MyUserInfo();
session.setUserInfo(ui);
session.connect();
// this command works
// String command = "ping 20.5.1.15 -c " + count + " -s " + size;
// this command not working
String command = "ping source 20.5.1.10 host 20.5.1.15 -c " + count + " -s
" + size;
Channel channel=session.openChannel("exec");
((ChannelExec)channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec)channel).setErrStream(System.err);
InputStream in=channel.getInputStream();
channel.connect();
byte[] tmp=new byte[1024];
while(true){
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)break;
System.out.print(new String(tmp, 0, i));
}
if(channel.isClosed()){
if(in.available()>0) continue;
System.out.println("exit-status: "+channel.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
channel.disconnect();
session.disconnect();
}
catch(Exception e){
System.out.println(e);
}
Your ping source x.x.x.x host y.y.y.y syntax seems strange to me. But I will trust you that it works in the terminal.
The ping command probably depends on some environment variable or other configuration to resolve the source address.
The "exec" channel in the JSch (rightfully) does not allocate a pseudo terminal (PTY) for the session. As a consequence a different set of startup scripts is (might be) sourced. And/or different branches in the scripts are taken, based on absence/presence of the TERM environment variable. So the environment might differ from the interactive session you use with your SSH client.
If this breaks the ping command, it's clearly misconfiguration on the server-side, not JSch fault. The ping is not an interactive command, so it should work even on the non-interactive session. You should find out what is it that breaks the ping and fix your startup scripts accordingly.
To verify that this is the root cause, disable the pseudo terminal allocation in your SSH client. For example in PuTTY, it's Connection > SSH > TTY > Don't allocate a pseudo terminal. Then go to Connection > SSH > Remote command and ether your ping source ... command. Check Session > Close window on exit > Never and open the session.
Another (not recommended) approach is to force the pseudo terminal allocation for the "exec" channel using the .setPty method:
Channel channel=session.openChannel("exec");
((ChannelExec)channel).setPty(true);
Using the pseudo terminal to automate a command execution can bring you nasty side effects. See for example Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?
This is a special case of this common problem:
Certain Unix commands fail with "... not found", when executed through Java using JSch

Using Jsch to SSH to my Raspberry Pi and Shutdown command not working...

hopefully someone might be able to show me what I'm doing wrong.
I'm writing a small android app to send commands to SSH servers. I have the following code in a separate method running on its own thread into which I pass username, server, and command.
I send a command and it puts a toast on the screen showing the output of that command. Now the code works with my windows machine running an SSH server. It also works (sort of) with my raspberry pi. However, if I send a command like "pwd;" to the pi I get the current directory back. Cool. If i send "sudo touch /media/MYUSBSTICK/testfile" it will create a file in the path specified. If I send "ls /" I get a list of everything in the root directory. "sudo ls /" also works.
HOWEVER... if I send "sudo shutdown -h now", or even just "shutdown" as a command using the exact same code I get nothing. It doesn't seem to execute and I get nothing back in my input buffer. If i do it from a SSH client connected directly with the Pi this works. I tried "sudo halt" and this also did nothing and returned nothing.
How come some commands work and other don't? Am I missing something obvious here? Is there some obvious mistake in my code?
Thanks
Nat
I there something special about
try {
JSch jsch = new JSch();
Session session = jsch.getSession(username, server, 22);
session.setPassword(password);
// Avoid asking for key confirmation
Properties prop = new Properties();
prop.put("StrictHostKeyChecking", "no");
session.setConfig(prop);
session.connect(10000);
ChannelExec channel=(ChannelExec) session.openChannel("exec");
BufferedReader in=new BufferedReader(new InputStreamReader(channel.getInputStream()));
channel.setCommand(command); //could be "pwd;" or "sudo shutdown -h now"
channel.connect();
String msg=null;
String wholemessage = "";
while ((msg = in.readLine()) != null) {
wholemessage += msg + "\n";
}
Thread.sleep(8000);
channel.disconnect();
session.disconnect();
final String thereturnresult = wholemessage;
final String tempcommand = command;
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), thereturnresult ,Toast.LENGTH_SHORT).show();
}
});
}
Ok worked this out myself.
The trick is to capture the error data as well with channel.getErrStream() then all becomes clear...
Hope this helps someone.

Automatically close and disconnect a java sshj session

I am using the sshj library for java.
I need to open an ssh session on a remote server, execute a command which could run for many minutes, and then continue on executing in my java code. Once the command running on the remote server is finished, i would like the ssh session to close, and the sshclient to disconnect.
I have seen the following code example:
public class Exec {
public static void main(String... args) throws IOException {
final SSHClient ssh = new SSHClient();
ssh.loadKnownHosts();
ssh.connect("localhost");
try {
ssh.authPublickey(System.getProperty("user.name"));
final Session session = ssh.startSession();
try {
final Command cmd = session.exec("ping -c 1 google.com");
System.out.println(IOUtils.readFully(cmd.getInputStream()).toString());
cmd.join(5, TimeUnit.SECONDS);
System.out.println("\n** exit status: " + cmd.getExitStatus());
} finally {
session.close();
}
} finally {
ssh.disconnect();
}
}
}
Basically I don't want to wait for the command to finish (no cmd.join call) and I need the session.close() and ssh.disconnect() to be called automatically once the command has exited. Is this possible?
I may not be able to exactly answer my question, but maybe provide help for others trying to do something similar to what I was doing. When I posted, I was under the impression that the ssh session had to stay open for the command to finish, but I don't think that is the case.
So what I was trying to do was start a remote ssh session via java, and run a shell script on the remote machine. The shell script could take up to 10-15 mins to complete. I tested a short timeout (5 secs) and the script continued to run after the session was disconnected... which is what I wanted.

Categories