[SSHD-792] Register a PortForwardingEventListener on the SshServerMain#main execution...
authorLyor Goldstein <lgoldstein@apache.org>
Sun, 16 Dec 2018 10:53:15 +0000 (12:53 +0200)
committerLyor Goldstein <lgoldstein@apache.org>
Mon, 17 Dec 2018 11:26:56 +0000 (13:26 +0200)
13 files changed:
sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java
sshd-cli/src/main/java/org/apache/sshd/cli/client/ScpCommandMain.java
sshd-cli/src/main/java/org/apache/sshd/cli/client/SftpCommandMain.java
sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java
sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java
sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java
sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java
sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerEventListenerHelper.java [new file with mode: 0644]
sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerPortForwardingEventListener.java [new file with mode: 0644]
sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/SftpServerSubSystemEventListener.java
sshd-cli/src/test/java/org/apache/sshd/cli/client/ChannelExecMain.java
sshd-cli/src/test/java/org/apache/sshd/cli/server/SshFsMounter.java
sshd-common/src/main/java/org/apache/sshd/common/config/LogLevelValue.java

index 36c0a91..c3dd4d8 100644 (file)
@@ -23,6 +23,7 @@ import java.io.PrintStream;
 import java.net.SocketAddress;
 import java.util.Map;
 import java.util.Objects;
+import java.util.logging.Level;
 
 import org.apache.sshd.common.AttributeRepository;
 import org.apache.sshd.common.config.ConfigFileReaderSupport;
@@ -52,6 +53,15 @@ public abstract class CliSupport {
         return true;
     }
 
+    public static boolean isEnabledVerbosityLogging(Level level) {
+        if ((level == null) || Level.OFF.equals(level) || Level.CONFIG.equals(level)
+                || Level.SEVERE.equals(level) || Level.WARNING.equals(level)) {
+            return false;
+        }
+
+        return true;
+    }
+
     /**
      * Scans the arguments for the &quot;-io&quot; command line option and sets the I/O
      * service accordingly. If no specific option specified then {@link #DEFAULT_IO_SERVICE_FACTORY}
@@ -98,7 +108,7 @@ public abstract class CliSupport {
     }
 
     public static <M extends AbstractFactoryManager> M setupIoServiceFactory(
-            M manager, Map<String, ?> options, PrintStream stdout, PrintStream stderr, String... args) {
+            M manager, Map<String, ?> options, Level level, PrintStream stdout, PrintStream stderr, String... args) {
         BuiltinIoServiceFactoryFactories factory = resolveIoServiceFactory(stderr, args);
         if (factory == null) {
             return null;
@@ -106,22 +116,12 @@ public abstract class CliSupport {
 
         manager.setIoServiceFactoryFactory(factory.create());
 
-        String levelValue = (options == null) ? null : Objects.toString(options.get(ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP), null);
-        if (GenericUtils.isEmpty(levelValue)) {
-            return manager;
-        }
-
-        LogLevelValue level = LogLevelValue.fromName(levelValue);
-        if (level == null) {
-            throw new IllegalArgumentException("Unknown " + ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP + " option value: " + levelValue);
-        }
-
-        if ((level != LogLevelValue.FATAL) && (level != LogLevelValue.ERROR) && (level != LogLevelValue.INFO)) {
+        if (!isEnabledVerbosityLogging(level)) {
             return manager;
         }
 
         manager.setIoServiceEventListener(new IoServiceEventListener() {
-            private final PrintStream out = (level == LogLevelValue.INFO) ? stdout : stderr;
+            private final PrintStream out = Level.INFO.equals(level) ? stderr : stdout;
 
             @Override
             public void connectionEstablished(
@@ -168,6 +168,50 @@ public abstract class CliSupport {
                 reason.printStackTrace(out);
             }
         });
+
         return manager;
     }
+
+    public static Level resolveLoggingVerbosity(String... args) {
+        return resolveLoggingVerbosity(args, GenericUtils.length(args));
+    }
+
+    public static Level resolveLoggingVerbosity(String[] args, int maxIndex) {
+        for (int index = 0; index < maxIndex; index++) {
+            String argName = args[index];
+            if ("-v".equals(argName)) {
+                return Level.INFO;
+            } else if ("-vv".equals(argName)) {
+                return Level.FINE;
+            } else if ("-vvv".equals(argName)) {
+                return Level.FINEST;
+            }
+        }
+
+        return Level.CONFIG;
+    }
+
+    /**
+     * Looks for the {@link ConfigFileReaderSupport#LOG_LEVEL_CONFIG_PROP} in the options.
+     * If found, then uses it as the result. Otherwise, invokes {@link #resolveLoggingVerbosity(String...)}
+     *
+     * @param options The {@code -o} options specified by the user
+     * @param args The command line arguments
+     * @return The resolved verbosity level
+     */
+    public static Level resolveLoggingVerbosity(Map<String, ?> options, String... args) {
+        String levelValue = (options == null)
+            ? null
+            : Objects.toString(options.get(ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP), null);
+        if (GenericUtils.isEmpty(levelValue)) {
+            return resolveLoggingVerbosity(args);
+        }
+
+        LogLevelValue level = LogLevelValue.fromName(levelValue);
+        if (level == null) {
+            throw new IllegalArgumentException("Unknown " + ConfigFileReaderSupport.LOG_LEVEL_CONFIG_PROP + " option value: " + levelValue);
+        }
+
+        return level.getLoggingLevel();
+    }
 }
index 14233b1..3f11c19 100644 (file)
@@ -33,6 +33,7 @@ import java.util.List;
 import java.util.Set;
 import java.util.logging.Level;
 
+import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.client.scp.ScpClient;
 import org.apache.sshd.client.scp.ScpClient.Option;
 import org.apache.sshd.client.scp.ScpClientCreator;
@@ -165,10 +166,12 @@ public class ScpCommandMain extends SshClientCliSupport {
         try (BufferedReader stdin = new BufferedReader(
                 new InputStreamReader(new NoCloseInputStream(System.in), Charset.defaultCharset()))) {
             args = normalizeCommandArguments(stdout, stderr, args);
+
+            Level level = Level.SEVERE;
             int numArgs = GenericUtils.length(args);
             // see the way normalizeCommandArguments works...
             if (numArgs >= 2) {
-                Level level = resolveLoggingVerbosity(args, numArgs - 2);
+                level = CliSupport.resolveLoggingVerbosity(args, numArgs - 2);
                 logStream = resolveLoggingTargetStream(stdout, stderr, args, numArgs - 2);
                 if (logStream != null) {
                     setupLogging(level, stdout, stderr, logStream);
@@ -177,7 +180,7 @@ public class ScpCommandMain extends SshClientCliSupport {
 
             ScpClientCreator creator = resolveScpClientCreator(stderr, args);
             ClientSession session = ((logStream == null) || (creator == null) || GenericUtils.isEmpty(args))
-                ? null : setupClientSession(SCP_PORT_OPTION, stdin, stdout, stderr, args);
+                ? null : setupClientSession(SCP_PORT_OPTION, stdin, level, stdout, stderr, args);
             if (session == null) {
                 stderr.println("usage: scp [" + SCP_PORT_OPTION + " port] [-i identity] [-io nio2|mina|netty]"
                          + " [-v[v][v]] [-E logoutput] [-r] [-p] [-q] [-o option=value] [-o creator=class name]"
index b147267..922601d 100644 (file)
@@ -42,6 +42,7 @@ import java.util.TreeMap;
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 
+import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.cli.client.helper.SftpFileTransferProgressOutputStream;
 import org.apache.sshd.client.ClientFactoryManager;
 import org.apache.sshd.client.session.ClientSession;
@@ -317,7 +318,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
         PrintStream stderr = System.err;
         OutputStream logStream = stderr;
         try (BufferedReader stdin = new BufferedReader(new InputStreamReader(new NoCloseInputStream(System.in)))) {
-            Level level = resolveLoggingVerbosity(args);
+            Level level = CliSupport.resolveLoggingVerbosity(args);
             logStream = resolveLoggingTargetStream(stdout, stderr, args);
             if (logStream != null) {
                 setupLogging(level, stdout, stderr, logStream);
@@ -325,7 +326,7 @@ public class SftpCommandMain extends SshClientCliSupport implements Channel {
 
             ClientSession session = (logStream == null)
                 ? null
-                : setupClientSession(SFTP_PORT_OPTION, stdin, stdout, stderr, args);
+                : setupClientSession(SFTP_PORT_OPTION, stdin, level, stdout, stderr, args);
             if (session == null) {
                 System.err.println("usage: sftp [-v[v][v]] [-E logoutput] [-i identity] [-io nio2|mina|netty]"
                         + " [-l login] [" + SFTP_PORT_OPTION + " port] [-o option=value]"
index 6917c94..ebc8f78 100644 (file)
@@ -106,7 +106,7 @@ public abstract class SshClientCliSupport extends CliSupport {
 
     // NOTE: ClientSession#getFactoryManager is the SshClient
     public static ClientSession setupClientSession(
-            String portOption, BufferedReader stdin, PrintStream stdout, PrintStream stderr, String... args)
+            String portOption, BufferedReader stdin, Level level, PrintStream stdout, PrintStream stderr, String... args)
                 throws Exception {
         int port = -1;
         String host = null;
@@ -224,7 +224,9 @@ public abstract class SshClientCliSupport extends CliSupport {
             return null;
         }
 
-        SshClient client = setupClient(options, ciphers, macs, compressions, identities, stdin, stdout, stderr, args);
+        SshClient client = setupClient(
+            options, ciphers, macs, compressions, identities,
+            stdin, stdout, stderr, level, args);
         if (client == null) {
             return null;
         }
@@ -269,8 +271,9 @@ public abstract class SshClientCliSupport extends CliSupport {
         }
     }
 
-    public static SshClient setupDefaultClient(Map<String, ?> options, PrintStream stdout, PrintStream stderr, String... args) {
-        return setupIoServiceFactory(SshClient.setUpDefaultClient(), options, stdout, stderr, args);
+    public static SshClient setupDefaultClient(
+            Map<String, ?> options, Level level, PrintStream stdout, PrintStream stderr, String... args) {
+        return setupIoServiceFactory(SshClient.setUpDefaultClient(), options, level, stdout, stderr, args);
     }
 
     // returns null if error encountered
@@ -282,7 +285,7 @@ public abstract class SshClientCliSupport extends CliSupport {
             List<NamedFactory<Compression>> compressions,
             Collection<? extends Path> identities,
             BufferedReader stdin, PrintStream stdout, PrintStream stderr,
-            String[] args)
+            Level level, String[] args)
                 throws Exception {
         PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(options);
         if (GenericUtils.isEmpty(ciphers)) {
@@ -306,7 +309,7 @@ public abstract class SshClientCliSupport extends CliSupport {
             }
         }
 
-        SshClient client = setupDefaultClient(options, stdout, stderr, args);
+        SshClient client = setupDefaultClient(options, level, stdout, stderr, args);
         if (client == null) {
             return null;
         }
@@ -459,25 +462,6 @@ public abstract class SshClientCliSupport extends CliSupport {
         return current;
     }
 
-    public static Level resolveLoggingVerbosity(String... args) {
-        return resolveLoggingVerbosity(args, GenericUtils.length(args));
-    }
-
-    public static Level resolveLoggingVerbosity(String[] args, int maxIndex) {
-        for (int index = 0; index < maxIndex; index++) {
-            String argName = args[index];
-            if ("-v".equals(argName)) {
-                return Level.INFO;
-            } else if ("-vv".equals(argName)) {
-                return Level.FINE;
-            } else if ("-vvv".equals(argName)) {
-                return Level.FINEST;
-            }
-        }
-
-        return Level.WARNING;
-    }
-
     public static OutputStream resolveLoggingTargetStream(PrintStream stdout, PrintStream stderr, String... args) {
         return resolveLoggingTargetStream(stdout, stderr, args, GenericUtils.length(args));
     }
@@ -603,7 +587,7 @@ public abstract class SshClientCliSupport extends CliSupport {
         return new ArrayList<>(available);
     }
 
-    public static Handler setupLogging(Level level, final PrintStream stdout, final PrintStream stderr, final OutputStream outputStream) {
+    public static Handler setupLogging(Level level, PrintStream stdout, PrintStream stderr, OutputStream outputStream) {
         Handler fh = new ConsoleHandler() {
             {
                 setOutputStream(outputStream); // override the default (stderr)
index 9ebca2c..d2d414f 100644 (file)
@@ -28,6 +28,7 @@ import java.util.EnumSet;
 import java.util.List;
 import java.util.logging.Level;
 
+import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.channel.ClientChannel;
@@ -99,7 +100,7 @@ public class SshClientMain extends SshClientCliSupport {
             } else if (GenericUtils.isEmpty(command) && "-a".equals(argName)) {
                 agentForward = false;
             } else {
-                level = resolveLoggingVerbosity(args, i);
+                level = CliSupport.resolveLoggingVerbosity(args, i);
                 logStream = resolveLoggingTargetStream(stdout, stderr, args, i);
                 if (logStream == null) {
                     error = true;
@@ -122,7 +123,7 @@ public class SshClientMain extends SshClientCliSupport {
             if (!error) {
                 setupLogging(level, stdout, stderr, logStream);
 
-                session = setupClientSession(SSH_CLIENT_PORT_OPTION, stdin, stdout, stderr, args);
+                session = setupClientSession(SSH_CLIENT_PORT_OPTION, stdin, level, stdout, stderr, args);
                 if (session == null) {
                     error = true;
                 }
index 94da492..c2dc94f 100644 (file)
@@ -33,10 +33,12 @@ import java.util.List;
 import java.util.Objects;
 import java.util.ServiceLoader;
 import java.util.TreeSet;
+import java.util.logging.Level;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.sshd.cli.CliSupport;
+import org.apache.sshd.cli.server.helper.ServerPortForwardingEventListener;
 import org.apache.sshd.cli.server.helper.SftpServerSubSystemEventListener;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.PropertyResolver;
@@ -105,7 +107,6 @@ public abstract class SshServerCliSupport extends CliSupport {
                 keyAlgorithm = KeyUtils.EC_ALGORITHM;
             } else if (BuiltinIdentities.Constants.ED25519.equals(keyAlgorithm)) {
                 keyAlgorithm = SecurityUtils.EDDSA;
-                // TODO change the hostKeyProvider to one that supports read/write of EDDSA keys - see SSHD-703
             }
 
             // force re-generation of host key if not same algorithm
@@ -150,9 +151,13 @@ public abstract class SshServerCliSupport extends CliSupport {
         }
     }
 
-    public static ForwardingFilter setupServerForwarding(SshServer server, PropertyResolver options) {
+    public static ForwardingFilter setupServerForwarding(
+            SshServer server, Level level, PrintStream stdout, PrintStream stderr, PropertyResolver options) {
         ForwardingFilter forwardFilter = SshServerConfigFileReader.resolveServerForwarding(options);
         server.setForwardingFilter(forwardFilter);
+        if (isEnabledVerbosityLogging(level)) {
+            server.addPortForwardingEventListener(new ServerPortForwardingEventListener(stdout, stderr));
+        }
         return forwardFilter;
     }
 
@@ -163,7 +168,7 @@ public abstract class SshServerCliSupport extends CliSupport {
     }
 
     public static List<NamedFactory<Command>> resolveServerSubsystems(
-            PrintStream stdout, PrintStream stderr, PropertyResolver options)
+            Level level, PrintStream stdout, PrintStream stderr, PropertyResolver options)
                 throws Exception {
         ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(SubsystemFactory.class);
         String classList = System.getProperty(SubsystemFactory.class.getName());
@@ -174,7 +179,7 @@ public abstract class SshServerCliSupport extends CliSupport {
                 try {
                     Class<?> clazz = cl.loadClass(fqcn);
                     SubsystemFactory factory = SubsystemFactory.class.cast(clazz.newInstance());
-                    factory = registerSubsystemFactoryListeners(stdout, stderr, options, factory);
+                    factory = registerSubsystemFactoryListeners(level, stdout, stderr, options, factory);
                     subsystems.add(factory);
                 } catch (Exception e) {
                     stderr.append("ERROR: Failed (").append(e.getClass().getSimpleName()).append(')')
@@ -207,7 +212,7 @@ public abstract class SshServerCliSupport extends CliSupport {
                 continue;
             }
 
-            factory = registerSubsystemFactoryListeners(stdout, stderr, options, factory);
+            factory = registerSubsystemFactoryListeners(level, stdout, stderr, options, factory);
             subsystems.add(factory);
         }
 
@@ -215,9 +220,9 @@ public abstract class SshServerCliSupport extends CliSupport {
     }
 
     public static <F extends SubsystemFactory> F registerSubsystemFactoryListeners(
-            PrintStream stdout, PrintStream stderr, PropertyResolver options, F factory)
+            Level level, PrintStream stdout, PrintStream stderr, PropertyResolver options, F factory)
                 throws Exception {
-        if (factory instanceof SftpSubsystemFactory) {
+        if ((factory instanceof SftpSubsystemFactory) && isEnabledVerbosityLogging(level)) {
             SftpEventListener listener = new SftpServerSubSystemEventListener(stdout, stderr);
             ((SftpSubsystemFactory) factory).addSftpEventListener(listener);
         }
index ff70768..f9a294e 100644 (file)
@@ -25,6 +25,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.TreeMap;
+import java.util.logging.Level;
 
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.NamedResource;
@@ -147,9 +148,10 @@ public class SshServerMain extends SshServerCliSupport {
             }
         }
 
+        Level level = resolveLoggingVerbosity(options, args);
         SshServer sshd = error
             ? null
-            : setupIoServiceFactory(SshServer.setUpDefaultServer(), options, System.out, System.err, args);
+            : setupIoServiceFactory(SshServer.setUpDefaultServer(), options, level, System.out, System.err, args);
         if (sshd == null) {
             error = true;
         }
@@ -182,13 +184,13 @@ public class SshServerMain extends SshServerCliSupport {
 
         sshd.setPasswordAuthenticator((username, password, session) -> Objects.equals(username, password));
         sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE);
-        setupServerForwarding(sshd, resolver);
+        setupServerForwarding(sshd, level, System.out, System.err, resolver);
         sshd.setCommandFactory(new ScpCommandFactory.Builder()
             .withDelegate(ProcessShellCommandFactory.INSTANCE)
             .build());
 
         List<NamedFactory<Command>> subsystems =
-            resolveServerSubsystems(System.out, System.err, resolver);
+            resolveServerSubsystems(level, System.out, System.err, resolver);
         if (GenericUtils.isNotEmpty(subsystems)) {
             System.out.append("Setup subsystems=").println(NamedResource.getNames(subsystems));
             sshd.setSubsystemFactories(subsystems);
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerEventListenerHelper.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerEventListenerHelper.java
new file mode 100644 (file)
index 0000000..f4de148
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.cli.server.helper;
+
+import java.io.Flushable;
+import java.io.IOException;
+import java.util.Objects;
+
+import org.apache.sshd.common.NamedResource;
+
+public abstract class ServerEventListenerHelper implements NamedResource {
+    private final String name;
+    private final Appendable stdout;
+    private final Appendable stderr;
+
+    public ServerEventListenerHelper(String name, Appendable stdout, Appendable stderr) {
+        this.name = name;
+        this.stdout = Objects.requireNonNull(stdout, "No output target");
+        this.stderr = Objects.requireNonNull(stderr, "No error target");
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public Appendable getStdout() {
+        return stdout;
+    }
+
+    public Appendable getStderr() {
+        return stderr;
+    }
+
+    protected String outputErrorMessage(String format, Object... args) throws IOException {
+        return outputMessage(getStderr(), format, args);
+    }
+
+    protected String outputDebugMessage(String format, Object... args) throws IOException {
+        return outputMessage(getStdout(), format, args);
+    }
+
+    protected String outputMessage(Appendable out, String format, Object... args) throws IOException {
+        String message = String.format(format, args);
+        out.append(getName())
+            .append(": ").append(message)
+            .append(System.lineSeparator());
+        if (out instanceof Flushable) {
+            ((Flushable) out).flush();
+        }
+        return message;
+    }
+}
diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerPortForwardingEventListener.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/helper/ServerPortForwardingEventListener.java
new file mode 100644 (file)
index 0000000..fc072b8
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.cli.server.helper;
+
+import java.io.IOException;
+
+import org.apache.sshd.common.forward.PortForwardingEventListener;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+
+public class ServerPortForwardingEventListener extends ServerEventListenerHelper implements PortForwardingEventListener {
+    public ServerPortForwardingEventListener(Appendable stdout, Appendable stderr) {
+        super("PORT-FWD", stdout, stderr);
+    }
+
+    @Override
+    public void establishedExplicitTunnel(
+            Session session, SshdSocketAddress local, SshdSocketAddress remote,
+            boolean localForwarding, SshdSocketAddress boundAddress, Throwable reason)
+                throws IOException {
+        if (reason == null) {
+            outputDebugMessage("Estalibshed explicit tunnel for session=%s: local=%s, remote=%s, bound=%s, localForward=%s",
+                session, local, remote, boundAddress, localForwarding);
+        } else {
+            outputErrorMessage("Failed (%s) to establish explicit tunnel for session=%s, local=%s, remote=%s, bound=%s, localForward=%s: %s",
+                reason.getClass().getSimpleName(), session, local, remote, boundAddress, localForwarding, reason.getMessage());
+        }
+    }
+
+    @Override
+    public void tornDownExplicitTunnel(
+            Session session, SshdSocketAddress address, boolean localForwarding, SshdSocketAddress remoteAddress, Throwable reason)
+                throws IOException {
+        if (reason == null) {
+            outputDebugMessage("Torn down explicit tunnel for session=%s: address=%s, remote=%s, localForward=%s",
+                session, address, remoteAddress, localForwarding);
+        } else {
+            outputErrorMessage("Failed (%s) to tear down explicit tunnel for session=%s, address=%s, remote=%s, localForward=%s: %s",
+                reason.getClass().getSimpleName(), session, address, remoteAddress, localForwarding, reason.getMessage());
+        }
+    }
+
+    @Override
+    public void establishedDynamicTunnel(
+            Session session, SshdSocketAddress local, SshdSocketAddress boundAddress, Throwable reason)
+                throws IOException {
+        if (reason == null) {
+            outputDebugMessage("Estalibshed dynamic tunnel for session=%s: local=%s,  bound=%s", session, local, boundAddress);
+        } else {
+            outputErrorMessage("Failed (%s) to establish dynamic tunnel for session=%s, bound=%s: %s",
+                reason.getClass().getSimpleName(), session, local, boundAddress, reason.getMessage());
+        }
+    }
+
+    @Override
+    public void tornDownDynamicTunnel(
+            Session session, SshdSocketAddress address, Throwable reason)
+                throws IOException {
+        if (reason == null) {
+            outputDebugMessage("Tornd down dynamic tunnel for session=%s: address=%s", session);
+        } else {
+            outputErrorMessage("Failed (%s) to tear down dynamic tunnel for session=%s, address=%s: %s",
+                reason.getClass().getSimpleName(), session, address, reason.getMessage());
+        }
+    }
+}
index 1fc2a59..5a43e5f 100644 (file)
 
 package org.apache.sshd.cli.server.helper;
 
-import java.io.Flushable;
 import java.io.IOException;
 import java.nio.file.CopyOption;
 import java.nio.file.Path;
 import java.util.Collection;
 import java.util.Map;
-import java.util.Objects;
 
+import org.apache.sshd.common.subsystem.sftp.SftpConstants;
 import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.subsystem.sftp.SftpEventListener;
-import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
 
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
-public class SftpServerSubSystemEventListener implements SftpEventListener {
-    private final Appendable stdout;
-    private final Appendable stderr;
-
+public class SftpServerSubSystemEventListener extends ServerEventListenerHelper implements SftpEventListener {
     public SftpServerSubSystemEventListener(Appendable stdout, Appendable stderr) {
-        this.stdout = Objects.requireNonNull(stdout, "No output target");
-        this.stderr = Objects.requireNonNull(stderr, "No error target");
-    }
-
-    public Appendable getStdout() {
-        return stdout;
-    }
-
-    public Appendable getStderr() {
-        return stderr;
+        super(SftpConstants.SFTP_SUBSYSTEM_NAME,  stdout, stderr);
     }
 
     @Override
@@ -95,23 +81,4 @@ public class SftpServerSubSystemEventListener implements SftpEventListener {
                 thrown.getClass().getSimpleName(), path, session, thrown.getMessage());
         }
     }
-
-    protected String outputErrorMessage(String format, Object... args) throws IOException {
-        return outputMessage(getStderr(), format, args);
-    }
-
-    protected String outputDebugMessage(String format, Object... args) throws IOException {
-        return outputMessage(getStdout(), format, args);
-    }
-
-    protected String outputMessage(Appendable out, String format, Object... args) throws IOException {
-        String message = String.format(format, args);
-        out.append(SftpSubsystemFactory.NAME)
-            .append(": ").append(message)
-            .append(System.lineSeparator());
-        if (out instanceof Flushable) {
-            ((Flushable) out).flush();
-        }
-        return message;
-    }
 }
index 8afcd42..7bc2b7c 100644 (file)
@@ -24,6 +24,7 @@ import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.nio.charset.Charset;
 
+import org.apache.sshd.cli.CliSupport;
 import org.apache.sshd.client.SshClient;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.util.GenericUtils;
@@ -76,7 +77,7 @@ public class ChannelExecMain extends BaseTestSupport {
         PrintStream stderr = System.err;
         try (BufferedReader stdin = new BufferedReader(
                 new InputStreamReader(new NoCloseInputStream(System.in), Charset.defaultCharset()))) {
-            ClientSession session = SshClientCliSupport.setupClientSession("-P", stdin, stdout, stderr, args);
+            ClientSession session = SshClientCliSupport.setupClientSession("-P", stdin, CliSupport.resolveLoggingVerbosity(args), stdout, stderr, args);
             if (session == null) {
                 System.err.println("usage: channelExec [-i identity] [-l login] [-P port] [-o option=value]"
                         + " [-w password] [-c cipherlist]  [-m maclist] [-C] hostname/user@host");
index 6c17416..2333495 100644 (file)
@@ -297,7 +297,9 @@ public final class SshFsMounter extends SshServerCliSupport {
         }
 
         SshServer sshd = error ? null : setupIoServiceFactory(
-            CoreTestSupportUtils.setupTestServer(SshFsMounter.class), options, System.out, System.err, args);
+            CoreTestSupportUtils.setupTestServer(SshFsMounter.class), options,
+            resolveLoggingVerbosity(options, args),
+            System.out, System.err, args);
         if (sshd == null) {
             error = true;
         }
index 987f178..d3b8a6a 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.sshd.common.config;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.Set;
+import java.util.logging.Level;
 
 import org.apache.sshd.common.util.GenericUtils;
 
@@ -35,19 +36,29 @@ public enum LogLevelValue {
      * 1. DEBUG and DEBUG1 are EQUIVALENT
      * 2. Order is important (!!!)
      */
-    QUIET,
-    FATAL,
-    ERROR,
-    INFO,
-    VERBOSE,
-    DEBUG,
-    DEBUG1,
-    DEBUG2,
-    DEBUG3;
+    QUIET(Level.OFF),
+    FATAL(Level.SEVERE),
+    ERROR(Level.SEVERE),
+    INFO(Level.INFO),
+    VERBOSE(Level.FINE),
+    DEBUG(Level.FINE),
+    DEBUG1(Level.FINE),
+    DEBUG2(Level.FINER),
+    DEBUG3(Level.FINEST);
 
     public static final Set<LogLevelValue> VALUES =
         Collections.unmodifiableSet(EnumSet.allOf(LogLevelValue.class));
 
+    private final Level level;
+
+    LogLevelValue(Level level) {
+        this.level = level;
+    }
+
+    public Level getLoggingLevel() {
+        return level;
+    }
+
     public static LogLevelValue fromName(String n) {
         if (GenericUtils.isEmpty(n)) {
             return null;