jscanner

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 05a47b70919628cf439751ea5bad9fb955a9c065
parent 0f6d9340e21936112ac36309e4ff23724756a38a
Author: Matthias Jaros <jaros@mailbox.org>
Date:   Wed,  1 Jul 2026 22:07:44 +0200

Make scanner gracefully shutdown on Strg-C

Diffstat:
Msrc/main/java/com/it_jaros/jscanner/App.java | 27+++++++++++++++++++++++++++
Msrc/main/java/com/it_jaros/jscanner/Scanner.java | 36++++++++++++++++++++++++++++--------
2 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/src/main/java/com/it_jaros/jscanner/App.java b/src/main/java/com/it_jaros/jscanner/App.java @@ -5,11 +5,16 @@ import java.util.List; import java.util.stream.Collectors; public class App { + + private static Thread shutdownHook; + public static void main(String[] args) { CliParser cliParser = new CliParser(); + try { ScanOptions options = cliParser.parseArgs(args); Scanner scanner = new Scanner(options); + addShutdownHook(scanner, options.showFilteredPorts()); printResults(scanner.scan(options), options.showFilteredPorts()); } catch (IllegalArgumentException e) { System.out.printf("Error while parsing arguments: %s", e.getMessage()); @@ -17,6 +22,28 @@ public class App { cliParser.usageAndExit(); } catch (Exception e) { System.out.printf("ERROR: %s", e.getMessage()); + } finally { + removeShutdownHook(); + } + } + + private static void addShutdownHook(Scanner scanner, boolean showFilteredPorts) { + shutdownHook = new Thread(() -> { + System.err.println("\nCtrl-C received."); + scanner.cancel(); + }); + Runtime.getRuntime().addShutdownHook(shutdownHook); + } + + private static void removeShutdownHook() { + if (shutdownHook == null) { + return; + } + + try { + Runtime.getRuntime().removeShutdownHook(shutdownHook); + } catch (IllegalStateException ignored) { + // JVM is already shutting down } } diff --git a/src/main/java/com/it_jaros/jscanner/Scanner.java b/src/main/java/com/it_jaros/jscanner/Scanner.java @@ -17,10 +17,13 @@ import java.util.concurrent.locks.LockSupport; import java.util.stream.Collectors; import java.util.stream.Stream; -public class Scanner { +public class Scanner implements AutoCloseable { private static final int READ_BUFFER_SIZE = 1024; + private ArrayList<ScanResult> scanResult; + private volatile boolean cancelled = false; + private final Counter counter = new Counter(); private final ProgressBar progressBar = new ProgressBar(); private final Semaphore socketLimit; @@ -75,7 +78,7 @@ public class Scanner { } public List<ScanResult> scanHosts(Stream<String> hosts, String ports) throws IOException { - List<ScanResult> results = new ArrayList<>(); + this.scanResult = new ArrayList<>(); progressBar.start(quiet); try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { CompletionService<ScanResult> completionService = new ExecutorCompletionService<>(executor); @@ -83,9 +86,9 @@ public class Scanner { int hostsToProcess = hostsInQueue.size(); int activeHostWorkers = 0; - while (hostsToProcess > 0) { + while (hostsToProcess > 0 && !cancelled) { // process queue - while (!hostsInQueue.isEmpty() && activeHostWorkers <= maxHostsLimit) { + while (!hostsInQueue.isEmpty() && activeHostWorkers <= maxHostsLimit && !cancelled) { activeHostWorkers++; final String host = hostsInQueue.poll(); completionService.submit(() -> scanHost(host, ports)); @@ -98,13 +101,17 @@ public class Scanner { } activeHostWorkers--; hostsToProcess--; - results.add(finishedHost.get()); + this.scanResult.add(finishedHost.get()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (ExecutionException e) { System.out.printf("%s -> %s%n", e.getClass().getSimpleName(), e.getMessage()); } } + + if (cancelled) { + executor.shutdown(); + } } progressBar.stop(quiet); @@ -113,7 +120,7 @@ public class Scanner { System.out.println("Stats:"); System.out.println("Peak concurrent connects: " + counter.max()); System.out.println("--------------------"); - return results; + return scanResult; } private ScanResult scanHost(String host, String ports) { @@ -138,8 +145,8 @@ public class Scanner { List<Throwable> errors = new ArrayList<>(); int activePerHostWorkers = 0; int portsToProcess = portRange.getTotal(); - while (portsToProcess > 0) { - while (portRange.hasNext() && activePerHostWorkers <= maxWorkersPerHost) { + while (portsToProcess > 0 && !cancelled) { + while (!cancelled && portRange.hasNext() && activePerHostWorkers <= maxWorkersPerHost) { activePerHostWorkers++; final int currentPort = portRange.next(); completionService.submit(() -> { @@ -174,6 +181,10 @@ public class Scanner { } } + if (!cancelled) { + executor.shutdown(); + } + if (!errors.isEmpty()) { System.out.printf("Errors happened during scan of host %s%nErrors:%s -> %s", host, errors.size(), errors); } @@ -240,4 +251,13 @@ public class Scanner { } return null; } + + public void cancel() { + cancelled = true; + } + + @Override + public void close() throws Exception { + cancel(); + } }