All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
oshi.software.os.unix.openbsd.OpenBsdOSProcess Maven / Gradle / Ivy
/*
* Copyright 2021-2023 The OSHI Project Contributors
* SPDX-License-Identifier: MIT
*/
package oshi.software.os.unix.openbsd;
import static oshi.software.os.OSProcess.State.INVALID;
import static oshi.software.os.OSProcess.State.OTHER;
import static oshi.software.os.OSProcess.State.RUNNING;
import static oshi.software.os.OSProcess.State.SLEEPING;
import static oshi.software.os.OSProcess.State.STOPPED;
import static oshi.software.os.OSProcess.State.WAITING;
import static oshi.software.os.OSProcess.State.ZOMBIE;
import static oshi.software.os.OSThread.ThreadFiltering.VALID_THREAD;
import static oshi.util.Memoizer.memoize;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.sun.jna.platform.unix.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.unix.LibCAPI.size_t;
import oshi.annotation.concurrent.ThreadSafe;
import oshi.jna.ByRef.CloseableSizeTByReference;
import oshi.jna.platform.unix.OpenBsdLibc;
import oshi.software.common.AbstractOSProcess;
import oshi.software.os.OSThread;
import oshi.software.os.unix.openbsd.OpenBsdOperatingSystem.PsKeywords;
import oshi.util.ExecutingCommand;
import oshi.util.ParseUtil;
import oshi.util.platform.unix.openbsd.FstatUtil;
/**
* OSProcess implementation
*/
@ThreadSafe
public class OpenBsdOSProcess extends AbstractOSProcess {
private static final Logger LOG = LoggerFactory.getLogger(OpenBsdOSProcess.class);
/*
* Package-private for use by OpenBsdOSThread
*/
enum PsThreadColumns {
TID, STATE, ETIME, CPUTIME, NIVCSW, NVCSW, MAJFLT, MINFLT, PRI, ARGS;
}
static final String PS_THREAD_COLUMNS = Arrays.stream(PsThreadColumns.values()).map(Enum::name)
.map(name -> name.toLowerCase(Locale.ROOT)).collect(Collectors.joining(","));
private static final int ARGMAX;
static {
int[] mib = new int[2];
mib[0] = 1; // CTL_KERN
mib[1] = 8; // KERN_ARGMAX
try (Memory m = new Memory(Integer.BYTES);
CloseableSizeTByReference size = new CloseableSizeTByReference(Integer.BYTES)) {
if (OpenBsdLibc.INSTANCE.sysctl(mib, mib.length, m, size, null, size_t.ZERO) == 0) {
ARGMAX = m.getInt(0);
} else {
LOG.warn("Failed sysctl call for process arguments max size (kern.argmax). Error code: {}",
Native.getLastError());
ARGMAX = 0;
}
}
}
private final OpenBsdOperatingSystem os;
private Supplier commandLine = memoize(this::queryCommandLine);
private Supplier> arguments = memoize(this::queryArguments);
private Supplier> environmentVariables = memoize(this::queryEnvironmentVariables);
private String name;
private String path = "";
private String user;
private String userID;
private String group;
private String groupID;
private State state = INVALID;
private int parentProcessID;
private int threadCount;
private int priority;
private long virtualSize;
private long residentSetSize;
private long kernelTime;
private long userTime;
private long startTime;
private long upTime;
private long bytesRead;
private long bytesWritten;
private long minorFaults;
private long majorFaults;
private long contextSwitches;
private int bitness;
private String commandLineBackup;
public OpenBsdOSProcess(int pid, Map psMap, OpenBsdOperatingSystem os) {
super(pid);
this.os = os;
// OpenBSD does not maintain a compatibility layer.
// Process bitness is OS bitness
this.bitness = Native.LONG_SIZE * 8;
updateThreadCount();
updateAttributes(psMap);
}
@Override
public String getName() {
return this.name;
}
@Override
public String getPath() {
return this.path;
}
@Override
public String getCommandLine() {
return this.commandLine.get();
}
private String queryCommandLine() {
String cl = String.join(" ", getArguments());
return cl.isEmpty() ? this.commandLineBackup : cl;
}
@Override
public List getArguments() {
return arguments.get();
}
private List queryArguments() {
if (ARGMAX > 0) {
// Get arguments via sysctl(3)
int[] mib = new int[4];
mib[0] = 1; // CTL_KERN
mib[1] = 55; // KERN_PROC_ARGS
mib[2] = getProcessID();
mib[3] = 1; // KERN_PROC_ARGV
// Allocate memory for arguments
try (Memory m = new Memory(ARGMAX);
CloseableSizeTByReference size = new CloseableSizeTByReference(ARGMAX)) {
// Fetch arguments
if (OpenBsdLibc.INSTANCE.sysctl(mib, mib.length, m, size, null, size_t.ZERO) == 0) {
// Returns a null-terminated list of pointers to the actual data
List args = new ArrayList<>();
// To iterate the pointer-list
long offset = 0;
// Get the data base address to calculate offsets
long baseAddr = Pointer.nativeValue(m);
long maxAddr = baseAddr + size.getValue().longValue();
// Get the address of the data. If null (0) we're done iterating
long argAddr = Pointer.nativeValue(m.getPointer(offset));
while (argAddr > baseAddr && argAddr < maxAddr) {
args.add(m.getString(argAddr - baseAddr));
offset += Native.POINTER_SIZE;
argAddr = Pointer.nativeValue(m.getPointer(offset));
}
return Collections.unmodifiableList(args);
}
}
}
return Collections.emptyList();
}
@Override
public Map getEnvironmentVariables() {
return environmentVariables.get();
}
private Map queryEnvironmentVariables() {
// Get environment variables via sysctl(3)
int[] mib = new int[4];
mib[0] = 1; // CTL_KERN
mib[1] = 55; // KERN_PROC_ARGS
mib[2] = getProcessID();
mib[3] = 3; // KERN_PROC_ENV
// Allocate memory for environment variables
try (Memory m = new Memory(ARGMAX); CloseableSizeTByReference size = new CloseableSizeTByReference(ARGMAX)) {
// Fetch environment variables
if (OpenBsdLibc.INSTANCE.sysctl(mib, mib.length, m, size, null, size_t.ZERO) == 0) {
// Returns a null-terminated list of pointers to the actual data
Map env = new LinkedHashMap<>();
// To iterate the pointer-list
long offset = 0;
// Get the data base address to calculate offsets
long baseAddr = Pointer.nativeValue(m);
long maxAddr = baseAddr + size.longValue();
// Get the address of the data. If null (0) we're done iterating
long argAddr = Pointer.nativeValue(m.getPointer(offset));
while (argAddr > baseAddr && argAddr < maxAddr) {
String envStr = m.getString(argAddr - baseAddr);
int idx = envStr.indexOf('=');
if (idx > 0) {
env.put(envStr.substring(0, idx), envStr.substring(idx + 1));
}
offset += Native.POINTER_SIZE;
argAddr = Pointer.nativeValue(m.getPointer(offset));
}
return Collections.unmodifiableMap(env);
}
}
return Collections.emptyMap();
}
@Override
public String getCurrentWorkingDirectory() {
return FstatUtil.getCwd(getProcessID());
}
@Override
public String getUser() {
return this.user;
}
@Override
public String getUserID() {
return this.userID;
}
@Override
public String getGroup() {
return this.group;
}
@Override
public String getGroupID() {
return this.groupID;
}
@Override
public State getState() {
return this.state;
}
@Override
public int getParentProcessID() {
return this.parentProcessID;
}
@Override
public int getThreadCount() {
return this.threadCount;
}
@Override
public int getPriority() {
return this.priority;
}
@Override
public long getVirtualSize() {
return this.virtualSize;
}
@Override
public long getResidentSetSize() {
return this.residentSetSize;
}
@Override
public long getKernelTime() {
return this.kernelTime;
}
@Override
public long getUserTime() {
return this.userTime;
}
@Override
public long getUpTime() {
return this.upTime;
}
@Override
public long getStartTime() {
return this.startTime;
}
@Override
public long getBytesRead() {
return this.bytesRead;
}
@Override
public long getBytesWritten() {
return this.bytesWritten;
}
@Override
public long getOpenFiles() {
return FstatUtil.getOpenFiles(getProcessID());
}
@Override
public long getSoftOpenFileLimit() {
if (getProcessID() == this.os.getProcessId()) {
final Resource.Rlimit rlimit = new Resource.Rlimit();
OpenBsdLibc.INSTANCE.getrlimit(OpenBsdLibc.RLIMIT_NOFILE, rlimit);
return rlimit.rlim_cur;
} else {
return -1L; // not supported
}
}
@Override
public long getHardOpenFileLimit() {
if (getProcessID() == this.os.getProcessId()) {
final Resource.Rlimit rlimit = new Resource.Rlimit();
OpenBsdLibc.INSTANCE.getrlimit(OpenBsdLibc.RLIMIT_NOFILE, rlimit);
return rlimit.rlim_max;
} else {
return -1L; // not supported
}
}
@Override
public int getBitness() {
return this.bitness;
}
@Override
public long getAffinityMask() {
long bitMask = 0L;
// Would prefer to use native cpuset_getaffinity call but variable sizing is
// kernel-dependent and requires C macros, so we use commandline instead.
String cpuset = ExecutingCommand.getFirstAnswer("cpuset -gp " + getProcessID());
// Sample output:
// pid 8 mask: 0, 1
// cpuset: getaffinity: No such process
String[] split = cpuset.split(":");
if (split.length > 1) {
String[] bits = split[1].split(",");
for (String bit : bits) {
int bitToSet = ParseUtil.parseIntOrDefault(bit.trim(), -1);
if (bitToSet >= 0) {
bitMask |= 1L << bitToSet;
}
}
}
return bitMask;
}
@Override
public List getThreadDetails() {
String psCommand = "ps -aHwwxo " + PS_THREAD_COLUMNS;
if (getProcessID() >= 0) {
psCommand += " -p " + getProcessID();
}
Predicate> hasColumnsArgs = threadMap -> threadMap
.containsKey(PsThreadColumns.ARGS);
return ExecutingCommand.runNative(psCommand).stream().skip(1)
.map(thread -> ParseUtil.stringToEnumMap(PsThreadColumns.class, thread.trim(), ' '))
.filter(hasColumnsArgs).map(threadMap -> new OpenBsdOSThread(getProcessID(), threadMap))
.filter(VALID_THREAD).collect(Collectors.toList());
}
@Override
public long getMinorFaults() {
return this.minorFaults;
}
@Override
public long getMajorFaults() {
return this.majorFaults;
}
@Override
public long getContextSwitches() {
return this.contextSwitches;
}
@Override
public boolean updateAttributes() {
// 'ps' does not provide threadCount or kernelTime on OpenBSD
String psCommand = "ps -awwxo " + OpenBsdOperatingSystem.PS_COMMAND_ARGS + " -p " + getProcessID();
List procList = ExecutingCommand.runNative(psCommand);
if (procList.size() > 1) {
// skip header row
Map psMap = ParseUtil.stringToEnumMap(PsKeywords.class, procList.get(1).trim(), ' ');
// Check if last (thus all) value populated
if (psMap.containsKey(PsKeywords.ARGS)) {
updateThreadCount();
return updateAttributes(psMap);
}
}
this.state = INVALID;
return false;
}
private boolean updateAttributes(Map psMap) {
long now = System.currentTimeMillis();
switch (psMap.get(PsKeywords.STATE).charAt(0)) {
case 'R':
this.state = RUNNING;
break;
case 'I':
case 'S':
this.state = SLEEPING;
break;
case 'D':
case 'L':
case 'U':
this.state = WAITING;
break;
case 'Z':
this.state = ZOMBIE;
break;
case 'T':
this.state = STOPPED;
break;
default:
this.state = OTHER;
break;
}
this.parentProcessID = ParseUtil.parseIntOrDefault(psMap.get(PsKeywords.PPID), 0);
this.user = psMap.get(PsKeywords.USER);
this.userID = psMap.get(PsKeywords.UID);
this.group = psMap.get(PsKeywords.GROUP);
this.groupID = psMap.get(PsKeywords.GID);
this.priority = ParseUtil.parseIntOrDefault(psMap.get(PsKeywords.PRI), 0);
// These are in KB, multiply
this.virtualSize = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.VSZ), 0) * 1024;
this.residentSetSize = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.RSS), 0) * 1024;
// Avoid divide by zero for processes up less than a second
long elapsedTime = ParseUtil.parseDHMSOrDefault(psMap.get(PsKeywords.ETIME), 0L);
this.upTime = elapsedTime < 1L ? 1L : elapsedTime;
this.startTime = now - this.upTime;
this.userTime = ParseUtil.parseDHMSOrDefault(psMap.get(PsKeywords.CPUTIME), 0L);
// kernel time is included in user time
this.kernelTime = 0L;
this.path = psMap.get(PsKeywords.COMM);
this.name = this.path.substring(this.path.lastIndexOf('/') + 1);
this.minorFaults = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.MINFLT), 0L);
this.majorFaults = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.MAJFLT), 0L);
long nonVoluntaryContextSwitches = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.NIVCSW), 0L);
long voluntaryContextSwitches = ParseUtil.parseLongOrDefault(psMap.get(PsKeywords.NVCSW), 0L);
this.contextSwitches = voluntaryContextSwitches + nonVoluntaryContextSwitches;
this.commandLineBackup = psMap.get(PsKeywords.ARGS);
return true;
}
private void updateThreadCount() {
List threadList = ExecutingCommand.runNative("ps -axHo tid -p " + getProcessID());
if (!threadList.isEmpty()) {
// Subtract 1 for header
this.threadCount = threadList.size() - 1;
}
this.threadCount = 1;
}
}