karansabhani karansabhani - 1 month ago 6
Java Question

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;
}
}

Answer

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.

So in your case, the PATH is probably set differently and consequently the air executable cannot be found.

You should fix your startup scripts to set the PATH same for both interactive and non-interactive sessions.

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 air ... command. Check Session > Close window on exit > Never and open the session. You should get the same "air not found" error.


You can of course work this around by using a full path to the air.


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?

Comments