Разделение стандартного вывода и вывода ошибок команды (sudo), выполняемой с помощью PTY с JSch Java - PullRequest
2 голосов
/ 27 мая 2020

Я использую Jsch, и моя задача - войти на сервер и выполнить следующую команду:

sudo "command"

Используя следующий код, я могу успешно подключаться и выполнять команды (без ошибки "sudo : извините, у вас должен быть tty ")

public String runSudo(RemoteHost remoteHost, String[] commands, OperationData operationData, String known_hosts, String id_rsa) {
    String result = "";
    Properties config = new Properties();
    config.put("StrictHostKeyChecking", "no");
    JSch jsch = new JSch();
    // Create a JSch session to connect to the server
    Session session = null;

    try {
        session = jsch.getSession(remoteHost.getUsername(), remoteHost.getHost(), remoteHost.getPort());

        if (remoteHost.getPassword() != null)
            session.setPassword(remoteHost.getPassword());
        else{
            session.setConfig("PreferredAuthentications", "publickey");
            jsch.setKnownHosts(known_hosts);
            jsch.addIdentity(id_rsa);
        }
        session.setConfig(config);
        // Establish the connection
        session.connect();
        logger.debug("Connected...");

        for (int k = 0; k < commands.length; k++) {
            ChannelExec channel = (ChannelExec) session.openChannel("exec");
            channel.setCommand(commands[k]);
            OutputStream outputStreamStdErr = new ByteArrayOutputStream();
            channel.setErrStream(outputStreamStdErr, true);
            channel.setPty(true);

            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;
                    }
                    result = result + new String(tmp, 0, i);
                }
                if (channel.isClosed()) {
                    this.exitStatus += channel.getExitStatus();
                    break;
                }
                Thread.sleep(1000);
            }
            String errors = readInputStream(outputstream2inputstream(outputStreamStdErr));
            if (!errors.equals("")) {
                operationData.setErrorMessage(errors);
            }
            channel.getErrStream().close();
            channel.disconnect();
        }
        session.disconnect();
    } catch (JSchException e) {
        operationData.setErrorMessage(e.getMessage());
        logger.error(e.getMessage());
        return result;
    } catch (IOException e) {
        operationData.setErrorMessage(e.getMessage());
        logger.error(e.getMessage());
        return result;
    } catch (InterruptedException e) {
        operationData.setErrorMessage(e.getMessage());
        logger.error(e.getMessage());
        return result;
    } finally {
        return result;
    }
}

Но, используя свойство channel.setPty(true);, я не могу прочитать поток ошибок, потому что он null и ошибки отображаются в выводе команды. Вместо этого, если я не использую свойство channel.setPty(true);, я могу прочитать поток ошибок, но метод возвращает ошибку "sudo: извините, у вас должен быть tty" , которого я хочу избежать.

Может ли кто-нибудь помочь мне использовать поток ошибок и свойство channel.setPty(true); в одном коде?

1 Ответ

1 голос
/ 27 мая 2020

Это стандартное поведение S SH (по крайней мере, на Linux), когда когда включена эмуляция терминала, весь вывод направляется в один поток в соединении S SH. Это не имеет ничего общего с вашим кодом Java / JSch.

Не используйте PTY для автоматизации команд. PTY предназначен для реализации интерактивного терминала для использования человеком.

Вы можете настроить sudo так, чтобы PTY не требовался.

Существуют также другие альтернативы.
См. Разрешение automati c выполнение команды как root на Linux с использованием S SH.


Другой обходной путь - перенаправить stdout и stderr во временные файлы на сервере. А затем распечатайте файлы отдельно в терминал / вывод.

Наивная реализация может быть такой:

sudo ... > /tmp/my_stdout 2> /tmp/my_stderr ; echo "stdout:" ; cat /tmp/my_stdout ; echo "stderr:" ; cat /tmp/my_stderr
...