hierynomus / sshj

ssh, scp and sftp for java
Apache License 2.0
2.51k stars 601 forks source link

No content on Shell's ErrorStream #285

Open roshh opened 7 years ago

roshh commented 7 years ago

Hi,

I am having trouble getting the stderr content of a command from net.schmizz.sshj.connection.channel.direct.Session.Shell.getErrorStream(): All output is written exclusively to the stdout stream retrievable via net.schmizz.sshj.connection.channel.Channel.getInputStream().

From what I see in the debugger stderr-content would be written to the error stream if the received SSH message is of type CHANNEL_EXTENDED_DATA, but all output is received as CHANNEL_DATA messages. I have tried different terminal types ("vt100", "xterm" and "dumb") and setting the PTYModes IEXTEN and OPOST using allocatePTY(...) but could not change the behaviour.

Does somebody have suggestion on how to direct the stderr output into the Shells ErrorStream instead of it's InputStream?

The code I used is adapted from the RudimentaryPTY-example:

package net.schmizz.sshj.examples;

import java.io.*;

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Shell;
import net.schmizz.sshj.transport.verification.*;

/** A very rudimentary psuedo-terminal based on console I/O. */
class RudimentaryPTY {
    public static void main(final String... args)
            throws IOException, InterruptedException {
        final SSHClient ssh = new SSHClient();
        final File khFile = new File(OpenSSHKnownHosts.detectSSHDir(), "known_hosts");
        ssh.addHostKeyVerifier(new ConsoleKnownHostsVerifier(khFile, System.console()));
        ssh.connect("localhost");
        try {
            ssh.authPublickey(System.getProperty("user.name"));
            final Session session = ssh.startSession();
            try {
                session.allocateDefaultPTY();
                final Shell shell = session.startShell();
                final InputStream inputStream = shell.getInputStream();
                new StreamCopier(inputStream, new FileOutputStream("stdout.txt"), LoggerFactory.DEFAULT)
                        .bufSize(session.getLocalMaxPacketSize())
                        .spawn("stdout");

                final InputStream errorStream = shell.getErrorStream();
                new StreamCopier(errorStream, new FileOutputStream("stderr.txt"), LoggerFactory.DEFAULT)
                        .bufSize(session.getLocalMaxPacketSize())
                        .spawn("stderr");

                final OutputStream outputStream = shell.getOutputStream();
                outputStream.write("echo \"stdout\"; >&2 echo \"stderr\"\n".getBytes());
                outputStream.close();
                Thread.sleep(5000l);
            } finally {
                session.close();
            }
        } finally {
            ssh.disconnect();
        }
    }
}

stderr.txt is empty after the execution:

$ ls -lah std*
-rw-rw-r-- 1 [...] 0 Nov 30 15:00 stderr.txt
-rw-rw-r-- 1 [...] 494 Nov 30 15:00 stdout.txt

while all content is saved in stdout.txt

$ cat stdout.txt Welcome to Ubuntu 16.10 (GNU/Linux 4.8.0-27-generic x86_64)

0 packages can be updated.
0 updates are security updates.

System restart required
Last login: Wed Nov 30 14:45:39 2016 from 127.0.0.1
echo "stdout"; >&2 echo "stderr"
[...]$ echo "stdout"; >&2 echo "stderr"
stdout
stderr

Vrakfall commented 6 years ago

It's funny you've got every output going in the InputStream. I'm just facing a case where the whole output which has an exit status of 0 (so, no error), and the whole output goes to the error stream... And it is while calling a command as simple as /usr/local/bin/python2.7 --version. I need time to be able to rewrite an easily reproducible example, which I don't have right now.