This is an automated email from the ASF dual-hosted git repository. lgoldstein pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit ab233a4295f0e9feb21687b0e61403191d381aad Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Fri Feb 19 11:54:22 2021 +0200 [SSHD-1127] Added capability to register a custom receiver for SFTP STDERR channel raw or stream data --- CHANGES.md | 3 +- docs/sftp.md | 22 +++++------- .../apache/sshd/server/channel/ChannelSession.java | 13 +++---- .../apache/sshd/server/command/AsyncCommand.java | 28 ++------------- .../command/AsyncCommandErrorStreamAware.java | 18 ++++++---- .../command/AsyncCommandInputStreamAware.java | 18 ++++++---- .../command/AsyncCommandOutputStreamAware.java | 17 +++++---- .../server/command/AsyncCommandStreamsAware.java | 9 ++--- .../org/apache/sshd/server/command/Command.java | 27 +-------------- .../command/CommandDirectErrorStreamAware.java | 18 ++++++---- .../command/CommandDirectInputStreamAware.java | 16 +++++---- .../command/CommandDirectOutputStreamAware.java | 18 ++++++---- .../server/command/CommandDirectStreamsAware.java | 9 ++--- ...a => SftpErrorDataChannelReceiverProvider.java} | 13 ++++--- .../org/apache/sshd/sftp/server/SftpSubsystem.java | 40 ++++++++++++++++++++-- .../sftp/server/SftpSubsystemConfigurator.java | 3 +- .../sshd/sftp/server/SftpSubsystemFactory.java | 18 ++++++++++ 17 files changed, 161 insertions(+), 129 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5f33b19..685e749 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,7 @@ * [SSHD-1111](https://issues.apache.org/jira/browse/SSHD-1111) Fixed SshClientCliSupport compression option detection * [SSHD-525](https://issues.apache.org/jira/browse/SSHD-525) Added support for SFTP **client-side** ["posix-ren...@openssh.com" extension](http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL?rev=1.28&content-type=text/x-cvsweb-markup) - see section 3.3 +* [SSHD-1127](https://issues.apache.org/jira/browse/SSHD-1127) Consolidated `SftpSubsystem` support implementations into `SftpSubsystemConfigurator` ## Behavioral changes and enhancements @@ -37,4 +38,4 @@ * [SSHD-1114](https://issues.apache.org/jira/browse/SSHD-1114) Added callbacks for client-side host-based authentication progress * [SSHD-1114](https://issues.apache.org/jira/browse/SSHD-1114) Added capability for interactive password authentication participation via UserInteraction * [SSHD-1114](https://issues.apache.org/jira/browse/SSHD-1114) Added capability for interactive key based authentication participation via UserInteraction -* [SSHD-1127](https://issues.apache.org/jira/browse/SSHD-1127) Consolidated `SftpSubsystem` support implementations into `SftpSubsystemConfigurator` \ No newline at end of file +* [SSHD-1127](https://issues.apache.org/jira/browse/SSHD-1127) Added capability to register a custom receiver for SFTP STDERR channel raw or stream data diff --git a/docs/sftp.md b/docs/sftp.md index 1afa4c2..08707d5 100644 --- a/docs/sftp.md +++ b/docs/sftp.md @@ -108,23 +108,19 @@ using a registered `SftpErrorStatusDataHandler`. The default implementation prov exception type. However, users may override it when creating the `SftpSubsystemFactory` and provide their own codes and/or messages - e.g., for debugging one can register a `DetailedSftpErrorStatusDataHandler` (see `sshd-contrib`) that "leaks" more information in the generated message. -If the registered handler implements `ChannelSessionAware` then it will also be informed of the registered `ChannelSession` when it is -provided to the `SftpSubsystem` itself. This can be used to register an extended data writer that can handle data sent via the STDERR -channel. **Note:** this feature is allowed according to [SFTP version 4 - section 3.1](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-3.1): +### Intercepting data sent via STDERR channel data from the client ->> Packets are sent and received on stdout and stdin. Data sent on stderr by the server SHOULD be considered debug ->> or supplemental error information, and MAY be displayed to the user. +If the registered handler implements `ChannelSessionAware` then it will also be informed of the registered `ChannelSession` when it is provided to the `SftpSubsystem` itself. This can be used to register an extended data writer that override the default (which ignores such data) and can handle data sent via the STDERR channel. **Note:** this feature is allowed according to [SFTP version 4 - section 3.1](https://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-3.1): -however, the current code provides no built-in support for this feature. +>> Packets are sent and received on stdout and stdin. Data sent on stderr by the server SHOULD be considered free format debug or supplemental error information, and MAY be displayed to the user. -If registering an extended data writer then one should take care of any race conditions that may occur where (extended) data -may arrive before the handler is informed of the existence of the `ChannelSession`. For this purpose one should configure a -reasonable buffer size by setting the `channel-session-max-extdata-bufsize` property. This way, if any data arrives before the -extended data handler is registered it will be buffered (up to the specified max. size). **Note:** if a buffer size is configured -but no extended data handler is registered when channel is spawning the command then an exception will occur. +however, the current code provides no built-in support for this feature other than ignoring any such sent data. -The same applies with any error I/O streams provided to the `SftpSubsystem` - if the handler implements the relevant `Aware` interface -then it will be provided with the relevant stream. +If registering an extended data writer one should take care of any race conditions that may occur where (extended) data may arrive before the handler is informed of the existence of the `ChannelSession`. For this purpose one should configure a reasonable buffer size by setting the `channel-session-max-extdata-bufsize` property. This way, if any data arrives before the extended data handler is registered it will be buffered (up to the specified max. size). **Note:** if a buffer size is co [...] + +### Sending custom data via STDERR channel data to the client + +Same logic as the STDERR incoming data applies to the outgoing error I/O streams provided to the `SftpSubsystem`. If the handler implements the relevant `CommandDirectErrorStreamAware` and/or `AsyncCommandErrorStreamAware` interface then it will be provided with the relevant error stream when the SFTP subsystem is initialized. **Note**: the current `SftpSubsystem`implementation uses *asynchronous* streams so `AsyncCommandErrorStreamAware` is the interface that will be invoked. However, i [...] ### Symbolic links handling diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java index 1124953..cfd3dad 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java @@ -68,7 +68,8 @@ import org.apache.sshd.server.ServerFactoryManager; import org.apache.sshd.server.SessionAware; import org.apache.sshd.server.Signal; import org.apache.sshd.server.StandardEnvironment; -import org.apache.sshd.server.command.AsyncCommand; +import org.apache.sshd.server.command.AsyncCommandInputStreamAware; +import org.apache.sshd.server.command.AsyncCommandStreamsAware; import org.apache.sshd.server.command.Command; import org.apache.sshd.server.command.CommandFactory; import org.apache.sshd.server.forward.AgentForwardingFilter; @@ -719,11 +720,11 @@ public class ChannelSession extends AbstractServerChannel { ((FileSystemAware) command).setFileSystemFactory(factory, session); } // If the shell wants to use non-blocking io - if (command instanceof AsyncCommand) { + if (command instanceof AsyncCommandStreamsAware) { asyncOut = new ChannelAsyncOutputStream(this, SshConstants.SSH_MSG_CHANNEL_DATA); asyncErr = new ChannelAsyncOutputStream(this, SshConstants.SSH_MSG_CHANNEL_EXTENDED_DATA); - ((AsyncCommand) command).setIoOutputStream(asyncOut); - ((AsyncCommand) command).setIoErrorStream(asyncErr); + ((AsyncCommandStreamsAware) command).setIoOutputStream(asyncOut); + ((AsyncCommandStreamsAware) command).setIoErrorStream(asyncErr); } else { Window wRemote = getRemoteWindow(); out = new ChannelOutputStream( @@ -742,10 +743,10 @@ public class ChannelSession extends AbstractServerChannel { if (this.receiver == null) { // if the command hasn't installed any ChannelDataReceiver, install the default // and give the command an InputStream - if (command instanceof AsyncCommand) { + if (command instanceof AsyncCommandInputStreamAware) { AsyncDataReceiver recv = new AsyncDataReceiver(this); setDataReceiver(recv); - ((AsyncCommand) command).setIoInputStream(recv.getIn()); + ((AsyncCommandInputStreamAware) command).setIoInputStream(recv.getIn()); } else { PipeDataReceiver recv = new PipeDataReceiver(this, getLocalWindow()); setDataReceiver(recv); diff --git a/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommand.java b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommand.java index 1391735..2b411e9 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommand.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommand.java @@ -18,34 +18,10 @@ */ package org.apache.sshd.server.command; -import org.apache.sshd.common.io.IoInputStream; -import org.apache.sshd.common.io.IoOutputStream; - /** * Represents a command capable of doing non-blocking io. If this interface is implemented by a command, the usual * blocking input / output / error streams won't be set. */ -public interface AsyncCommand extends Command { - - /** - * Set the input stream that can be used by the shell to read input. - * - * @param in The {@link IoInputStream} used by the shell to read input - */ - void setIoInputStream(IoInputStream in); - - /** - * Set the output stream that can be used by the shell to write its output. - * - * @param out The {@link IoOutputStream} used by the shell to write its output - */ - void setIoOutputStream(IoOutputStream out); - - /** - * Set the error stream that can be used by the shell to write its errors. - * - * @param err The {@link IoOutputStream} used by the shell to write its errors - */ - void setIoErrorStream(IoOutputStream err); - +public interface AsyncCommand extends Command, AsyncCommandStreamsAware { + // Nothing extra } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandErrorStreamAware.java similarity index 69% copy from sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java copy to sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandErrorStreamAware.java index 63dbe9c..106c47d 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandErrorStreamAware.java @@ -17,15 +17,21 @@ * under the License. */ -package org.apache.sshd.sftp.server; +package org.apache.sshd.server.command; -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; +import org.apache.sshd.common.io.IoOutputStream; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SftpSubsystemConfigurator - extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { - // Nothing extra +@FunctionalInterface +public interface AsyncCommandErrorStreamAware { + + /** + * Set the error stream that can be used by the shell to write its errors. + * + * @param err The {@link IoOutputStream} used by the shell to write its errors + */ + void setIoErrorStream(IoOutputStream err); + } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandInputStreamAware.java similarity index 70% copy from sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java copy to sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandInputStreamAware.java index 63dbe9c..2e747a3 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandInputStreamAware.java @@ -17,15 +17,21 @@ * under the License. */ -package org.apache.sshd.sftp.server; +package org.apache.sshd.server.command; -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; +import org.apache.sshd.common.io.IoInputStream; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SftpSubsystemConfigurator - extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { - // Nothing extra +@FunctionalInterface +public interface AsyncCommandInputStreamAware { + + /** + * Set the input stream that can be used by the shell to read input. + * + * @param in The {@link IoInputStream} used by the shell to read input + */ + void setIoInputStream(IoInputStream in); + } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandOutputStreamAware.java similarity index 69% copy from sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java copy to sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandOutputStreamAware.java index 63dbe9c..a443e2c 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandOutputStreamAware.java @@ -17,15 +17,20 @@ * under the License. */ -package org.apache.sshd.sftp.server; +package org.apache.sshd.server.command; -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; +import org.apache.sshd.common.io.IoOutputStream; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SftpSubsystemConfigurator - extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { - // Nothing extra +@FunctionalInterface +public interface AsyncCommandOutputStreamAware { + /** + * Set the output stream that can be used by the shell to write its output. + * + * @param out The {@link IoOutputStream} used by the shell to write its output + */ + void setIoOutputStream(IoOutputStream out); + } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandStreamsAware.java similarity index 74% copy from sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java copy to sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandStreamsAware.java index 63dbe9c..66d05cc 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/AsyncCommandStreamsAware.java @@ -17,15 +17,12 @@ * under the License. */ -package org.apache.sshd.sftp.server; - -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; +package org.apache.sshd.server.command; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SftpSubsystemConfigurator - extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { +public interface AsyncCommandStreamsAware + extends AsyncCommandInputStreamAware, AsyncCommandOutputStreamAware, AsyncCommandErrorStreamAware { // Nothing extra } diff --git a/sshd-core/src/main/java/org/apache/sshd/server/command/Command.java b/sshd-core/src/main/java/org/apache/sshd/server/command/Command.java index 16396d2..7c264ba 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/command/Command.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/Command.java @@ -18,9 +18,6 @@ */ package org.apache.sshd.server.command; -import java.io.InputStream; -import java.io.OutputStream; - import org.apache.sshd.server.ExitCallback; /** @@ -35,29 +32,7 @@ import org.apache.sshd.server.ExitCallback; * </p> * see {@link org.apache.sshd.server.shell.InvertedShellWrapper}. */ -public interface Command extends CommandLifecycle { - - /** - * Set the input stream that can be used by the shell to read input. - * - * @param in The {@link InputStream} used by the shell to read input. - */ - void setInputStream(InputStream in); - - /** - * Set the output stream that can be used by the shell to write its output. - * - * @param out The {@link OutputStream} used by the shell to write its output - */ - void setOutputStream(OutputStream out); - - /** - * Set the error stream that can be used by the shell to write its errors. - * - * @param err The {@link OutputStream} used by the shell to write its errors - */ - void setErrorStream(OutputStream err); - +public interface Command extends CommandLifecycle, CommandDirectStreamsAware { /** * Set the callback that the shell has to call when it is closed. * diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectErrorStreamAware.java similarity index 70% copy from sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java copy to sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectErrorStreamAware.java index 63dbe9c..7cce4af 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectErrorStreamAware.java @@ -17,15 +17,21 @@ * under the License. */ -package org.apache.sshd.sftp.server; +package org.apache.sshd.server.command; -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; +import java.io.OutputStream; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SftpSubsystemConfigurator - extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { - // Nothing extra +@FunctionalInterface +public interface CommandDirectErrorStreamAware { + + /** + * Set the error stream that can be used by the shell to write its errors. + * + * @param err The {@link OutputStream} used by the shell to write its errors + */ + void setErrorStream(OutputStream err); + } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectInputStreamAware.java similarity index 71% copy from sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java copy to sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectInputStreamAware.java index 63dbe9c..5c9010d 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectInputStreamAware.java @@ -17,15 +17,19 @@ * under the License. */ -package org.apache.sshd.sftp.server; +package org.apache.sshd.server.command; -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; +import java.io.InputStream; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SftpSubsystemConfigurator - extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { - // Nothing extra +@FunctionalInterface +public interface CommandDirectInputStreamAware { + /** + * Set the input stream that can be used by the shell to read input. + * + * @param in The {@link InputStream} used by the shell to read input. + */ + void setInputStream(InputStream in); } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectOutputStreamAware.java similarity index 70% copy from sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java copy to sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectOutputStreamAware.java index 63dbe9c..3dea058 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectOutputStreamAware.java @@ -17,15 +17,21 @@ * under the License. */ -package org.apache.sshd.sftp.server; +package org.apache.sshd.server.command; -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; +import java.io.OutputStream; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SftpSubsystemConfigurator - extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { - // Nothing extra +@FunctionalInterface +public interface CommandDirectOutputStreamAware { + + /** + * Set the output stream that can be used by the shell to write its output. + * + * @param out The {@link OutputStream} used by the shell to write its output + */ + void setOutputStream(OutputStream out); + } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectStreamsAware.java similarity index 74% copy from sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java copy to sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectStreamsAware.java index 63dbe9c..a243203 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/command/CommandDirectStreamsAware.java @@ -17,15 +17,12 @@ * under the License. */ -package org.apache.sshd.sftp.server; - -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; +package org.apache.sshd.server.command; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SftpSubsystemConfigurator - extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { +public interface CommandDirectStreamsAware + extends CommandDirectInputStreamAware, CommandDirectOutputStreamAware, CommandDirectErrorStreamAware { // Nothing extra } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpErrorDataChannelReceiverProvider.java similarity index 70% copy from sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java copy to sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpErrorDataChannelReceiverProvider.java index 63dbe9c..cabc821 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpErrorDataChannelReceiverProvider.java @@ -19,13 +19,16 @@ package org.apache.sshd.sftp.server; -import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; +import org.apache.sshd.server.channel.ChannelDataReceiver; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SftpSubsystemConfigurator - extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { - // Nothing extra +@FunctionalInterface +public interface SftpErrorDataChannelReceiverProvider { + /** + * @return A {@link ChannelDataReceiver} to handle optional STDERR data received during SFTP session. If + * {@code null} then any received such data is ignored. + */ + ChannelDataReceiver getErrorChannelDataReceiver(); } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java index 850f8e3..a5bb4ae 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystem.java @@ -71,7 +71,9 @@ import org.apache.sshd.server.SessionAware; import org.apache.sshd.server.channel.ChannelDataReceiver; import org.apache.sshd.server.channel.ChannelSession; import org.apache.sshd.server.command.AsyncCommand; +import org.apache.sshd.server.command.AsyncCommandErrorStreamAware; import org.apache.sshd.server.command.Command; +import org.apache.sshd.server.command.CommandDirectErrorStreamAware; import org.apache.sshd.server.session.ServerSession; import org.apache.sshd.sftp.SftpModuleProperties; import org.apache.sshd.sftp.common.SftpConstants; @@ -96,10 +98,10 @@ public class SftpSubsystem protected final Map<String, Handle> handles = new ConcurrentHashMap<>(); protected final Buffer buffer = new ByteArrayBuffer(1024); protected final BlockingQueue<Buffer> requests = new LinkedBlockingQueue<>(); + protected final ChannelDataReceiver errorDataChannelReceiver; protected ExitCallback callback; protected IoOutputStream out; - protected IoOutputStream err; protected Environment env; protected Random randomizer; protected int fileHandleSize = SftpModuleProperties.DEFAULT_FILE_HANDLE_SIZE; @@ -120,6 +122,31 @@ public class SftpSubsystem public SftpSubsystem(SftpSubsystemConfigurator configurator) { super(configurator); + ChannelDataReceiver receiver = configurator.getErrorChannelDataReceiver(); + if (receiver == null) { + errorDataChannelReceiver = new ChannelDataReceiver() { + @Override + @SuppressWarnings("synthetic-access") + public void close() throws IOException { + if (log.isDebugEnabled()) { + log.debug("stderrData({}) closing", getSession()); + } + + } + + @Override + @SuppressWarnings("synthetic-access") + public int data(ChannelSession channel, byte[] buf, int start, int len) throws IOException { + if (log.isDebugEnabled()) { + log.debug("stderrData({}) received {} data bytes", channel, len); + } + return len; + } + }; + } else { + errorDataChannelReceiver = receiver; + } + CloseableExecutorService executorService = configurator.getExecutorService(); if (executorService == null) { this.executorService = ThreadUtils.newSingleThreadExecutor(getClass().getSimpleName()); @@ -168,6 +195,7 @@ public class SftpSubsystem public void setChannelSession(ChannelSession session) { this.channelSession = session; session.setDataReceiver(this); + session.setExtendedDataWriter(errorDataChannelReceiver); SftpErrorStatusDataHandler errHandler = getErrorStatusDataHandler(); if (errHandler instanceof ChannelSessionAware) { @@ -201,7 +229,10 @@ public class SftpSubsystem @Override public void setErrorStream(OutputStream err) { - // Do nothing + SftpErrorStatusDataHandler errHandler = getErrorStatusDataHandler(); + if (errHandler instanceof CommandDirectErrorStreamAware) { + ((CommandDirectErrorStreamAware) errHandler).setErrorStream(err); + } } @Override @@ -216,7 +247,10 @@ public class SftpSubsystem @Override public void setIoErrorStream(IoOutputStream err) { - this.err = err; + SftpErrorStatusDataHandler errHandler = getErrorStatusDataHandler(); + if (errHandler instanceof AsyncCommandErrorStreamAware) { + ((AsyncCommandErrorStreamAware) errHandler).setIoErrorStream(err); + } } @Override diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java index 63dbe9c..09711fe 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemConfigurator.java @@ -26,6 +26,7 @@ import org.apache.sshd.common.util.threads.ExecutorServiceCarrier; */ public interface SftpSubsystemConfigurator extends ExecutorServiceCarrier, SftpFileSystemAccessorProvider, - SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider { + SftpUnsupportedAttributePolicyProvider, SftpErrorStatusDataHandlerProvider, + SftpErrorDataChannelReceiverProvider { // Nothing extra } diff --git a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemFactory.java b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemFactory.java index e3e0e61..444d7ca 100644 --- a/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemFactory.java +++ b/sshd-sftp/src/main/java/org/apache/sshd/sftp/server/SftpSubsystemFactory.java @@ -27,6 +27,7 @@ import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ObjectBuilder; import org.apache.sshd.common.util.threads.CloseableExecutorService; import org.apache.sshd.common.util.threads.ManagedExecutorServiceSupplier; +import org.apache.sshd.server.channel.ChannelDataReceiver; import org.apache.sshd.server.channel.ChannelSession; import org.apache.sshd.server.command.Command; import org.apache.sshd.server.subsystem.SubsystemFactory; @@ -49,6 +50,7 @@ public class SftpSubsystemFactory private UnsupportedAttributePolicy policy = DEFAULT_POLICY; private SftpFileSystemAccessor fileSystemAccessor = SftpFileSystemAccessor.DEFAULT; private SftpErrorStatusDataHandler errorStatusDataHandler = SftpErrorStatusDataHandler.DEFAULT; + private ChannelDataReceiver errorChannelDataReceiver; public Builder() { super(); @@ -74,6 +76,11 @@ public class SftpSubsystemFactory return this; } + public Builder withErrorChannelDataReceiver(ChannelDataReceiver receiver) { + errorChannelDataReceiver = receiver; + return this; + } + @Override public SftpSubsystemFactory build() { SftpSubsystemFactory factory = new SftpSubsystemFactory(); @@ -81,6 +88,7 @@ public class SftpSubsystemFactory factory.setUnsupportedAttributePolicy(policy); factory.setFileSystemAccessor(fileSystemAccessor); factory.setErrorStatusDataHandler(errorStatusDataHandler); + factory.setErrorChannelDataReceiver(errorChannelDataReceiver); GenericUtils.forEach(getRegisteredListeners(), factory::addSftpEventListener); return factory; } @@ -90,6 +98,7 @@ public class SftpSubsystemFactory private UnsupportedAttributePolicy policy = DEFAULT_POLICY; private SftpFileSystemAccessor fileSystemAccessor = SftpFileSystemAccessor.DEFAULT; private SftpErrorStatusDataHandler errorStatusDataHandler = SftpErrorStatusDataHandler.DEFAULT; + private ChannelDataReceiver errorChannelDataReceiver; public SftpSubsystemFactory() { super(); @@ -149,6 +158,15 @@ public class SftpSubsystemFactory } @Override + public ChannelDataReceiver getErrorChannelDataReceiver() { + return errorChannelDataReceiver; + } + + public void setErrorChannelDataReceiver(ChannelDataReceiver errorChannelDataReceiver) { + this.errorChannelDataReceiver = errorChannelDataReceiver; + } + + @Override public Command createSubsystem(ChannelSession channel) throws IOException { SftpSubsystem subsystem = new SftpSubsystem(this); GenericUtils.forEach(getRegisteredListeners(), subsystem::addSftpEventListener);