summaryrefslogtreecommitdiff
path: root/src/main/java/com/it_jaros/jscanner/ProgressBar.java
diff options
context:
space:
mode:
authorGravatar Matthias Jaros <jarlucmat@mailbox.org>2026-07-06 13:02:54 +0200
committerGravatar Matthias Jaros <jarlucmat@mailbox.org>2026-07-09 09:59:28 +0200
commit874866185ed05ae837a8852e44ac6ac8f253c3bc (patch)
tree0c57276eb5a45a22b55f4581ba522600a5f0491a /src/main/java/com/it_jaros/jscanner/ProgressBar.java
parentd2ab45e7ecdfee71806eb0388527cd50ba96975d (diff)
Completetly reworked progress output. Now instead of individual hosts
give a statusline that prints current stats to err
Diffstat (limited to 'src/main/java/com/it_jaros/jscanner/ProgressBar.java')
-rw-r--r--src/main/java/com/it_jaros/jscanner/ProgressBar.java175
1 files changed, 104 insertions, 71 deletions
diff --git a/src/main/java/com/it_jaros/jscanner/ProgressBar.java b/src/main/java/com/it_jaros/jscanner/ProgressBar.java
index 12a359c..d1679c4 100644
--- a/src/main/java/com/it_jaros/jscanner/ProgressBar.java
+++ b/src/main/java/com/it_jaros/jscanner/ProgressBar.java
@@ -2,29 +2,75 @@ package com.it_jaros.jscanner;
import java.io.BufferedReader;
import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
+import java.util.BitSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
public class ProgressBar {
- private final List<Progress> hosts = new ArrayList<>();
- private final ReentrantLock lock = new ReentrantLock();
private final ScheduledExecutorService ui = Executors.newSingleThreadScheduledExecutor();
+ private final Scan scan;
private final boolean quiet;
private final int columnWidth = ProgressBar.getColumnWidth();
+ private final Object outputLock = new Object();
- public ProgressBar(boolean quiet) {
+ public ProgressBar(Scan scan, boolean quiet) {
+ this.scan = scan;
this.quiet = quiet;
}
+ private void clearStatusLine() {
+ System.err.print("\r\033[2K");
+ System.err.flush();
+ }
+
+ public void printResult(ScanResult result, boolean showFilteredPorts) {
+ synchronized (outputLock) {
+ if (result.openPorts().isEmpty() && (!showFilteredPorts || result.filteredPorts().isEmpty())) {
+ return;
+ }
+
+ clearStatusLine();
+ printPortsResults(result, showFilteredPorts);
+ }
+ }
+
+ private void printPortsResults(ScanResult result, boolean showFilteredPorts) {
+ if (result.openPorts().isEmpty() && (!showFilteredPorts || result.filteredPorts().isEmpty())) {
+ return;
+ }
+
+ System.out.println(result.host());
+ if (!result.openPorts().isEmpty()) {
+ System.out.printf("\t (%d) open:\t%s%n", result.openPorts().cardinality(), map(result.openPorts(), result.bannerRecognition()));
+ }
+ if (showFilteredPorts && !result.filteredPorts().isEmpty()) {
+ System.out.printf("\t (%d) filtered:\t%s%n", result.filteredPorts().cardinality(), map(result.filteredPorts()));
+ }
+ System.out.println();
+ System.out.flush();
+ }
+
+ private static String map(BitSet ports) {
+ return ports.stream().mapToObj(String::valueOf).collect(Collectors.joining(","));
+ }
+
+ private static String map(BitSet ports, java.util.HashMap<Integer, ServiceType> bannerRecognition) {
+ return ports.stream().mapToObj(port -> {
+ ServiceType serviceType = bannerRecognition.getOrDefault(port, ServiceType.UNKNOWN);
+
+ if (serviceType == ServiceType.UNKNOWN) {
+ return String.valueOf(port);
+ }
+
+ return port + "/" + serviceType.name().toLowerCase();
+ }).collect(Collectors.joining(","));
+ }
+
private static int getColumnWidth() {
try {
Process process = new ProcessBuilder("sh", "-c", "stty size < /dev/tty").start();
@@ -47,86 +93,73 @@ public class ProgressBar {
}
public void start() {
- if (!quiet) {
- System.out.print("Scanning targets...\n\n");
+ if (quiet) {
+ return;
}
// although we don't print anything, we still need to
// gc all the done hosts
+ System.err.print("Scanning targets...\n\n");
ui.scheduleAtFixedRate(() -> {
- lock.lock();
- drawProgress(false);
- lock.unlock();
- }, 1, 500, TimeUnit.MILLISECONDS);
- }
-
- private void drawProgress(boolean last) {
- // sort -> the more progressed the host the more up it is
- hosts.sort(Comparator.comparingInt((Progress p) -> p.done().get()).reversed());
-
- // find and print finished hosts one last time
- for (Iterator<Progress> it = hosts.iterator(); it.hasNext(); ) {
- Progress p = it.next();
- if (p.done().get() == p.total()) {
- it.remove();
- drawBar(p);
- } else {
- break;
+ synchronized (outputLock) {
+ printStatusLine(scan);
}
- }
-
- for (Progress progress : hosts) {
- drawBar(progress);
- }
+ }, 1, 1000, TimeUnit.MILLISECONDS);
+ }
- if (!last) {
- // move cursor up again
- if (!quiet) {
- System.out.print("\u001b[" + hosts.size() + "A");
- }
+ public void stop() {
+ ui.shutdownNow();
+ try {
+ ui.awaitTermination(1, TimeUnit.SECONDS);
+ } catch (InterruptedException ignored) {
+ Thread.currentThread().interrupt();
}
+ printStats();
}
- public void add(Progress progress) {
- lock.lock();
- hosts.add(progress);
- lock.unlock();
+ private void printStats() {
+ System.out.println("\n--------------------");
+ System.out.println("Stats:");
+ System.out.println("Total hosts scanned: " + scan.getHostTotalCounter().max());
+ System.out.println("Peak concurrent connects: " + scan.getSocketCounter().max());
+ System.out.println("Peak concurrent threads: " + scan.getThreadCounter().max());
+ System.out.println("--------------------\n");
}
- private void drawBar(Progress progress) {
- if (quiet) return;
- System.out.print("\u001b[2K\r"); // clear whole line
- System.out.println(createBar(
- progress.host(),
- progress.open().get(),
- progress.filtered().get(),
- progress.done().get(),
- progress.total()));
+ private void printStatusLine(final Scan scan) {
+ this.printStatusLine(scan.getHostCounter(), scan.getHostTotalCounter(), scan.getSocketCounter(), scan.getThreadCounter());
}
- public String createBar(String label, int open, int filtered, int done, int total) {
- int terminalWidth = columnWidth;
- int bracketWidth = terminalWidth / 3;
- double pct = total == 0 ? 1.0 : (done / (double) total);
- int percent = (int) (pct * 100);
+ private void printStatusLine(
+ Counter hostCounter, Counter hostTotalCounter, Counter
+ socketCounter, Counter
+ threadCounter) {
+ int doneHosts = hostTotalCounter.current();
+ int runningHosts = hostCounter.current();
+ int total = doneHosts + runningHosts;
+ long percent = total == 0 ? 100 : doneHosts * 100L / total;
- String statsLeft = String.format("open:%d filtered:%d (%d/%d)", open, filtered, done, total);
- String statsRight = String.format("%3d%%", percent);
- int filled = (int) (pct * bracketWidth);
- String brackets = "[" + "#".repeat(filled) + "-".repeat(bracketWidth - filled) + "]";
+ String line = String.format(
+ "hosts: %d running, %d done | sockets: %d active, %d peak | workers: %d active, %d peak",
+ runningHosts,
+ doneHosts,
+ socketCounter.current(),
+ socketCounter.max(),
+ threadCounter.current(),
+ threadCounter.max()
+ );
- int spacesBetween = terminalWidth - bracketWidth - (label + statsLeft + statsRight).length() - 5;
- return String.format("%s%s%s %s %s", label, " ".repeat(spacesBetween), statsLeft, brackets, statsRight);
+ String barFormat = " | [%s] %3d%%";
+ int barWidth = Math.max(5, this.columnWidth - line.length() - barFormat.length());
+ String progressBar = progressBar(percent, barWidth);
+ String status = line + String.format(barFormat, progressBar, percent);
+ System.err.print("\r" + status);
+ System.err.flush();
}
- public void stop() {
- ui.shutdown();
- try {
- ui.awaitTermination(1, TimeUnit.SECONDS);
- // we need to draw one last time for the 100% to appear
- drawProgress(true);
- } catch (InterruptedException ignored) {
- Thread.currentThread().interrupt();
- }
+ private static String progressBar(long percent, int width) {
+ int filled = (int) (percent * width / 100);
+
+ return "#".repeat(filled) + "-".repeat(width - filled);
}
}