Use JSch sudo example and Channel.setPty for running sudo command on remote host

15,154

I have similar code in a project I am working on, and was getting the same error. I resolved this using the setPty(true) as you did.

I think you're getting this error because you don't close out the streams in your code. If you are using Java 1.7, you can use a resource block as follows:

try( InputStream in=channel.getInputStream() ) {
    try( OutputStream out = channel.getOutputStream() ) {
    ...
    }
}

or the try...finally block pattern from past versions.

Share:
15,154
waqas
Author by

waqas

Updated on June 29, 2022

Comments

  • waqas
    waqas almost 2 years

    I have used JSch Sudo example under following link:

    http://www.jcraft.com/jsch/examples/Sudo.java.html

    And changed it a bit and get rid of all the dialogs as I have to use it for EC2 instances using PuTTY.

    Now my code looks like this:

    import com.jcraft.jsch.*;
    import java.awt.*;
    import javax.swing.*;
    import java.io.*;
    
    public class sudo{
      public static void main(String[] arg){
        try{
          JSch jsch=new JSch();
    
          String host=null;
          if(arg.length>0){
            host=arg[0];
          }
    
          String privateKey = "my private key.pem";
    
          jsch.addIdentity(privateKey, "");
          Session session=jsch.getSession("ec2-user", "xx.xx.xx.xx", 22);
    
          session.setPassword("");
          java.util.Properties config = new java.util.Properties();
          config.put("StrictHostKeyChecking", "no");
          session.setConfig(config);
    
          session.connect();
    
          String command="sudo mkdir /data";
          String sudo_pass="";
    
          Channel channel=session.openChannel("exec");
    
          // man sudo
          // -S The -S (stdin) option causes sudo to read the password from the
          // standard input instead of the terminal device.
          // -p The -p (prompt) option allows you to override the default
          // password prompt and use a custom one.
          ((ChannelExec)channel).setCommand("sudo -S -p '' "+command);
    
          InputStream in=channel.getInputStream();
          OutputStream out=channel.getOutputStream();
          ((ChannelExec)channel).setErrStream(System.err);
    
          channel.connect();
    
          out.write((sudo_pass+"\n").getBytes());
          out.flush();
    
          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()){
              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);
        }
      }
    }
    

    But I am getting the error

    sorry you must have a tty to run sudo

    I also tried to use ((ChannelExec) channel).setPty(true) but I can run the program once but next time, for same EC2 instance, I get the following error but for new instance it works fine for first time again.

    com.jcraft.jsch.JSchException: Session.connect: java.io.IOException: End of IO Stream Read
    sudo mkdir /data
    Exception in thread "main" com.jcraft.jsch.JSchException: session is down
            at com.jcraft.jsch.Session.openChannel(Session.java:791)
    

    And then also I could not ssh the remote host from command line as well.

    Can someone please guide me that what I need to do to run sudo command on remote host.