From 1d5b01f3ce395b3bdff010a4fcc69b639f85a073 Mon Sep 17 00:00:00 2001 From: George Gastaldi Date: Mon, 8 Mar 2021 11:14:34 -0300 Subject: [PATCH] Introduce Process.getAllProcess Fixes #79 --- .../common/os/GetAllProcessesInfoAction.java | 117 ++++++++++++++++++ .../common/os/GetProcessInfoAction.java | 1 + .../java/io/smallrye/common/os/Process.java | 31 ++++- .../io/smallrye/common/os/ProcessInfo.java | 22 ++++ .../common/os/GetAllProcessesInfoAction.java | 15 +++ .../io/smallrye/common/os/ProcessTest.java | 7 ++ 6 files changed, 187 insertions(+), 6 deletions(-) create mode 100644 os/src/main/java/io/smallrye/common/os/GetAllProcessesInfoAction.java create mode 100644 os/src/main/java/io/smallrye/common/os/ProcessInfo.java create mode 100644 os/src/main/java9/io/smallrye/common/os/GetAllProcessesInfoAction.java diff --git a/os/src/main/java/io/smallrye/common/os/GetAllProcessesInfoAction.java b/os/src/main/java/io/smallrye/common/os/GetAllProcessesInfoAction.java new file mode 100644 index 00000000..9877033a --- /dev/null +++ b/os/src/main/java/io/smallrye/common/os/GetAllProcessesInfoAction.java @@ -0,0 +1,117 @@ +package io.smallrye.common.os; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; +import java.util.function.Predicate; +import java.util.regex.Pattern; + +final class GetAllProcessesInfoAction implements PrivilegedAction> { + + private final static Predicate IS_NUMBER = Pattern.compile("\\d+").asPredicate(); + + @Override + public List run() { + // The ProcessHandle API does not exist, so we have to rely on external processes to get this information + switch (OS.current()) { + case LINUX: + return readLinuxProcesses(); + case MAC: + return readMacProcesses(); + case WINDOWS: + return readWindowsProcesses(); + default: + throw new UnsupportedOperationException( + "Listing all processes is not supported in JDK 8 in " + OS.current().name()); + } + } + + private List readLinuxProcesses() { + List processes = new ArrayList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(Paths.get("/proc"))) { + for (Path procPath : stream) { + String name = procPath.getFileName().toString(); + if (IS_NUMBER.test(name)) { + long pid = Long.parseLong(name); + try (BufferedReader reader = Files + .newBufferedReader(procPath.resolve("cmdline"), StandardCharsets.UTF_8)) { + String line = reader.readLine(); + if (line != null) { + int idx = line.indexOf(0); + String cmdLine = idx == -1 ? line : line.substring(0, idx); + processes.add(new ProcessInfo(pid, cmdLine)); + } + } + } + } + } catch (IOException ignored) { + // ignore + } + return processes; + } + + private List readMacProcesses() { + List processes = new ArrayList<>(); + java.lang.Process process = null; + try { + // PID CMD + // 1 /usr/lib/systemd/systemd --switched-root --system --deserialize 34 + process = new ProcessBuilder("ps", "-ax", "-o", "pid,cmd").start(); + try (Scanner scanner = new Scanner(process.getInputStream())) { + if (scanner.hasNextLine()) { + scanner.nextLine(); + } + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + int separator = line.indexOf(" "); + long pid = Long.parseLong(line.substring(0, separator)); + String cmd = line.substring(separator + 1); + processes.add(new ProcessInfo(pid, cmd)); + } + } + } catch (IOException e) { + // ignored + } finally { + if (process != null) { + process.destroy(); + } + } + return processes; + } + + private List readWindowsProcesses() { + List processes = new ArrayList<>(); + java.lang.Process process = null; + try { + process = new ProcessBuilder("tasklist.exe", "/fo", "csv", "/nh").start(); + try (Scanner sc = new Scanner(process.getInputStream())) { + // Skip first line + if (sc.hasNextLine()) { + sc.nextLine(); + } + while (sc.hasNextLine()) { + String line = sc.nextLine(); + String[] parts = line.split(","); + String cmdLine = parts[0].substring(1).replaceFirst(".$", ""); + long pid = Long.parseLong(parts[1].substring(1).replaceFirst(".$", "")); + processes.add(new ProcessInfo(pid, cmdLine)); + } + } + } catch (IOException e) { + // ignored + } finally { + if (process != null) { + process.destroy(); + } + } + return processes; + } +} diff --git a/os/src/main/java/io/smallrye/common/os/GetProcessInfoAction.java b/os/src/main/java/io/smallrye/common/os/GetProcessInfoAction.java index 0148b722..e74b08ae 100644 --- a/os/src/main/java/io/smallrye/common/os/GetProcessInfoAction.java +++ b/os/src/main/java/io/smallrye/common/os/GetProcessInfoAction.java @@ -31,6 +31,7 @@ final class GetProcessInfoAction implements PrivilegedAction { GetProcessInfoAction() { } + @Override public Object[] run() { long pid = -1L; String processName = ""; diff --git a/os/src/main/java/io/smallrye/common/os/Process.java b/os/src/main/java/io/smallrye/common/os/Process.java index b9b102ef..dab005e7 100644 --- a/os/src/main/java/io/smallrye/common/os/Process.java +++ b/os/src/main/java/io/smallrye/common/os/Process.java @@ -20,19 +20,19 @@ import static java.security.AccessController.doPrivileged; +import java.util.List; + /** * Utilities for getting information about the current process. * * @author David M. Lloyd */ public final class Process { - private static final long processId; - private static final String processName; + private static final ProcessInfo currentProcess; static { Object[] array = doPrivileged(new GetProcessInfoAction()); - processId = (Long) array[0]; - processName = (String) array[1]; + currentProcess = new ProcessInfo((Long) array[0], (String) array[1]); } private Process() { @@ -44,7 +44,7 @@ private Process() { * @return the process name (not {@code null}) */ public static String getProcessName() { - return processName; + return currentProcess.getCommand(); } /** @@ -54,6 +54,25 @@ public static String getProcessName() { * @return the ID of this process, or -1 if it cannot be determined */ public static long getProcessId() { - return processId; + return currentProcess.getId(); + } + + /** + * Returns information about the current process + * + * @return the current process + */ + public static ProcessInfo getCurrentProcess() { + return currentProcess; + } + + /** + * Returns all the running processes. + * + * @return a list of all the running processes. May throw an exception if running on an unsupported JDK + * @throws UnsupportedOperationException if running on JDK 8 + */ + public static List getAllProcesses() { + return doPrivileged(new GetAllProcessesInfoAction()); } } diff --git a/os/src/main/java/io/smallrye/common/os/ProcessInfo.java b/os/src/main/java/io/smallrye/common/os/ProcessInfo.java new file mode 100644 index 00000000..fde7b6d0 --- /dev/null +++ b/os/src/main/java/io/smallrye/common/os/ProcessInfo.java @@ -0,0 +1,22 @@ +package io.smallrye.common.os; + +/** + * Returns information about a Process + */ +public class ProcessInfo { + private final long id; + private final String command; + + public ProcessInfo(long id, String command) { + this.id = id; + this.command = command; + } + + public long getId() { + return id; + } + + public String getCommand() { + return command; + } +} diff --git a/os/src/main/java9/io/smallrye/common/os/GetAllProcessesInfoAction.java b/os/src/main/java9/io/smallrye/common/os/GetAllProcessesInfoAction.java new file mode 100644 index 00000000..d16de967 --- /dev/null +++ b/os/src/main/java9/io/smallrye/common/os/GetAllProcessesInfoAction.java @@ -0,0 +1,15 @@ +package io.smallrye.common.os; + +import java.security.PrivilegedAction; +import java.util.List; +import java.util.stream.Collectors; + +final class GetAllProcessesInfoAction implements PrivilegedAction> { + + @Override + public List run() { + return ProcessHandle.allProcesses() + .map(processHandle -> new ProcessInfo(processHandle.pid(), processHandle.info().command().orElse(null))) + .collect(Collectors.toList()); + } +} diff --git a/os/src/test/java/io/smallrye/common/os/ProcessTest.java b/os/src/test/java/io/smallrye/common/os/ProcessTest.java index d3de07c0..c84571b5 100644 --- a/os/src/test/java/io/smallrye/common/os/ProcessTest.java +++ b/os/src/test/java/io/smallrye/common/os/ProcessTest.java @@ -1,5 +1,6 @@ package io.smallrye.common.os; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -11,6 +12,12 @@ class ProcessTest { public void testProcessInfo() { assertNotEquals(0L, Process.getProcessId()); assertNotNull(Process.getProcessName()); + assertNotNull(Process.getCurrentProcess()); + } + + @Test + public void testAllProcessInfo() { + assertFalse(Process.getAllProcesses().isEmpty()); } }