diff --git a/log/src/main/java/org/apache/karaf/log/command/LogTail.java b/log/src/main/java/org/apache/karaf/log/command/LogTail.java index 7d4ab50b692..7de968ecd5c 100644 --- a/log/src/main/java/org/apache/karaf/log/command/LogTail.java +++ b/log/src/main/java/org/apache/karaf/log/command/LogTail.java @@ -22,6 +22,7 @@ import org.apache.karaf.shell.api.action.Command; import org.apache.karaf.shell.api.action.lifecycle.Reference; import org.apache.karaf.shell.api.action.lifecycle.Service; +import org.apache.karaf.shell.api.console.ChannelResourceCleaner; import org.apache.karaf.shell.api.console.Session; import org.ops4j.pax.logging.spi.PaxAppender; import org.osgi.framework.BundleContext; @@ -31,7 +32,8 @@ @Command(scope = "log", name = "tail", description = "Continuously display log entries. Use ctrl-c to quit this command") @Service -public class LogTail extends DisplayLog { +public class LogTail extends DisplayLog implements ChannelResourceCleaner { + @Reference Session session; @@ -52,6 +54,9 @@ public Object execute() throws Exception { PaxAppender appender = event -> printEvent(out, event, minLevel); ServiceTracker tracker = new LogServiceTracker(context, LogService.class, null, appender); tracker.open(); + + context.registerService(ChannelResourceCleaner.class, this, null); + try { synchronized (this) { wait(); @@ -65,6 +70,12 @@ public Object execute() throws Exception { out.println(); return null; } + + @Override + public void close() { + System.out.println("STOP TAIL"); + stopTail(); + } private synchronized void stopTail() { notifyAll(); diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/ChannelResourceCleaner.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/ChannelResourceCleaner.java new file mode 100644 index 00000000000..3328e91985b --- /dev/null +++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/ChannelResourceCleaner.java @@ -0,0 +1,25 @@ +/* + * 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.karaf.shell.api.console; + +public interface ChannelResourceCleaner { + + void close(); + +} diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java index e0657bef24a..cc3f7e349ab 100644 --- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java +++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java @@ -22,9 +22,11 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Duration; +import java.util.Collection; import java.util.Collections; import org.apache.karaf.shell.api.action.lifecycle.Manager; +import org.apache.karaf.shell.api.console.ChannelResourceCleaner; import org.apache.karaf.shell.api.console.CommandLoggingFilter; import org.apache.karaf.shell.api.console.Session; import org.apache.karaf.shell.api.console.SessionFactory; @@ -34,8 +36,11 @@ import org.apache.karaf.util.tracker.annotation.Managed; import org.apache.karaf.util.tracker.annotation.RequireService; import org.apache.karaf.util.tracker.annotation.Services; +import org.apache.sshd.common.channel.Channel; +import org.apache.sshd.common.channel.ChannelListener; import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory; import org.apache.sshd.common.keyprovider.KeyPairProvider; +import org.apache.sshd.common.session.SessionListener; import org.apache.sshd.core.CoreModuleProperties; import org.apache.sshd.scp.server.ScpCommandFactory; import org.apache.sshd.server.SshServer; @@ -189,6 +194,20 @@ protected SshServer createSshServer(SessionFactory sessionFactory) { server.setKeyExchangeFactories(SshUtils.buildKexAlgorithms(kexAlgorithms)); server.setSignatureFactories(SshUtils.buildSigAlgorithms(sigAlgorithms)); server.setShellFactory(new ShellFactoryImpl(sessionFactory)); + server.addSessionListener(new SessionListener() { + @Override + public void sessionDisconnect(org.apache.sshd.common.session.Session session, int reason, String msg, String language, boolean initiator) { + try { + Collection> references = bundleContext.getServiceReferences(ChannelResourceCleaner.class, null); + for (ServiceReference reference : references) { + ChannelResourceCleaner cleaner = bundleContext.getService(reference); + cleaner.close(); + } + } catch (Exception e) { + // no-op + } + } + }); if (sftpEnabled) { server.setCommandFactory(new ScpCommandFactory.Builder().withDelegate((channel, cmd) -> new ShellCommand(sessionFactory, cmd)).build());