2

I am trying to run commands which uses local Linux logical paths like cat $test_dir/test.dat, but the logical path $test_dir (which is a user environment variable) is not available via ChannelExec. But when I use interactive ChannelShell, I am able to see the user variables and the commands run fine on the interactive session. I can view system level environment variable only from "exec" session. Is that even possible using JSch library, if yes then how shall I achieve it and if not what library shall I use to achieve this?

Adding my class code below:

    
    private static final Logger logger = LogManager.getLogger(SecureShell.class);
    
    private String uName;
    private String pWord;
    private String hName;
    private int port;
    
    private Session session = null;
    private Channel channel = null;
    
    /**Create an instance to start and stop the remote shell and execute commands
     * remotely via java.
     * 
     * @param uName
     *          host username 
     * @param pWord
     *          host password
     * @param hName
     *          host name
     * @param port
     *          host port number
     */
    public SecureShell(String uName, String pWord, String hName, int port) {
        this.uName = uName;
        this.pWord = pWord;
        this.hName = hName;
        this.port = port;
    }
    
    /**Create an instance to start and stop the remote shell and execute commands
     * remotely via java.
     * 
     *@param uName
     *          host username 
     * @param pWord
     *          host password
     * @param hName
     *          host name
     */
    public SecureShell(String uName, String pWord, String hName) {
        this.uName = uName;
        this.pWord = pWord;
        this.hName = hName;
        this.port = 22;
    }
    
    /**Start the session with the host.
     * @return
     *      true if the session started successfully, false otherwise
     */
    public boolean startSession() {
        JSch jsch = new JSch();
        try {
            session = jsch.getSession(uName, hName, port);
            
            java.util.Properties config = new java.util.Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            session.setPassword(pWord);
            session.connect();
            
        } catch (JSchException jsche) {
            logger.error(jsche.getMessage());
            return false;
        } 
        
        return true;
    }
    
    /** Execute commands on the host;
     * @param command
     *          command to be executed on the host.
     * @return
     *      status of the execution
     */
    public int execute(String command) {
        
        int status = -1;
        if(session != null && session.isConnected()) {
            try {
                channel = session.openChannel("exec");
                //((ChannelExec)channel).setEnv("LC_XXX", "xxxxxxx");
                ((ChannelExec)channel).setPty(true);
                ((ChannelExec) channel).setCommand(command);
                
                InputStream in = channel.getInputStream();
        
                channel.connect();
                
                 byte[] buffer = new byte[1024];
                 while(true){
                     while(in.available()>0){
                         int i=in.read(buffer, 0, 1024);
                         System.out.print(new String(buffer, 0, i));
                         if(i<0)
                             break;
                    }
                     if(channel.isClosed()){
                         if(in.available()>0) 
                             continue; 
                         status = channel.getExitStatus();
                         break;
                     }
                }
            } catch (JSchException jsche) {
                logger.error(jsche.getMessage());
            } catch (IOException ioe) {
                logger.error(ioe.getMessage());
            } finally {
                if(channel!=null && channel.isConnected())
                    channel.disconnect();
            } 
        }
        
        return status;
    }
    
    /**Stop the session with the remote.
     * 
     */
    public void stopSession() {
        
        if(session!=null && session.isConnected())
            session.disconnect();
    }
    
    public static void main(String[] args) {
        SecureShell ssh  = new SecureShell("user", "password", "hostname");
        ssh.startSession();
        System.out.println(ssh.execute("env"));
        ssh.stopSession();
    }
}
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
user3221714
  • 41
  • 2
  • 6

3 Answers3

5

Since you're not opening up an interactive shell, your environment variables won't be set. However, you can use the bash command with --login (man bash for more details) to get the results you want

bash --login -c 'command arg1 ...'"
Zyxer22
  • 152
  • 8
2

The "exec" channel in the JSch (rightfully) does not allocate a pseudo terminal (PTY) for the session by default. 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 JSch "shell" session or when you use your SSH client.


Ways to fix this:

  1. Fix your startup scripts to set the environment variables the same for both interactive and non-interactive sessions.

  2. 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


For a similar issues, see

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
  • I tried setting the pty to true and also ran the environment script in the remote server with the command given below but I still couldn't use the logical paths which are otherwise available. The command I used was "sh environment.sh && env" – user3221714 Sep 21 '17 at 20:21
  • 1
    @user3221714: `sh environment.sh && env` creates a new shell process, call it sh#2, which executes script `environment.sh` which presumably sets some envvars. sh#2 then exits successfully, and its memory is discarded. The original shell then creates an `env` process which sees the original shell's unchanged set of envvars. If you want a script to set envvars (or other vars) in the _same_ shell process, use `source` or `.` depending on which shell you are using. That is effectively what shells do to their startup files aka profiles, although _which_ startup files varies by shell. – dave_thompson_085 Sep 22 '17 at 01:44
0

I was able to fix this issue by setting the environment variable in the .bashrc file. Setting the environment variable through setEnv method did not help me.

kkid
  • 71
  • 1
  • 2