diff options
Diffstat (limited to 'netbsd')
-rw-r--r-- | netbsd/NetBSDMachine.c | 285 | ||||
-rw-r--r-- | netbsd/NetBSDMachine.h | 54 | ||||
-rw-r--r-- | netbsd/NetBSDProcess.c | 277 | ||||
-rw-r--r-- | netbsd/NetBSDProcess.h | 32 | ||||
-rw-r--r-- | netbsd/NetBSDProcessTable.c | 273 | ||||
-rw-r--r-- | netbsd/NetBSDProcessTable.h | 24 | ||||
-rw-r--r-- | netbsd/Platform.c | 518 | ||||
-rw-r--r-- | netbsd/Platform.h | 138 | ||||
-rw-r--r-- | netbsd/ProcessField.h | 15 | ||||
-rw-r--r-- | netbsd/README.md | 32 |
10 files changed, 1648 insertions, 0 deletions
diff --git a/netbsd/NetBSDMachine.c b/netbsd/NetBSDMachine.c new file mode 100644 index 0000000..79c50c1 --- /dev/null +++ b/netbsd/NetBSDMachine.c @@ -0,0 +1,285 @@ +/* +htop - NetBSDMachine.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "netbsd/NetBSDMachine.h" + +#include <kvm.h> +#include <math.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mount.h> +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/sched.h> +#include <sys/swap.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <uvm/uvm_extern.h> + +#include "CRT.h" +#include "Machine.h" +#include "Macros.h" +#include "Object.h" +#include "Settings.h" +#include "XUtils.h" + + +static const struct { + const char* name; + long int scale; +} freqSysctls[] = { + { "machdep.est.frequency.current", 1 }, + { "machdep.powernow.frequency.current", 1 }, + { "machdep.intrepid.frequency.current", 1 }, + { "machdep.loongson.frequency.current", 1 }, + { "machdep.cpu.frequency.current", 1 }, + { "machdep.frequency.current", 1 }, + { "machdep.tsc_freq", 1000000 }, +}; + +static void NetBSDMachine_updateCPUcount(NetBSDMachine* this) { + Machine* super = &this->super; + + // Definitions for sysctl(3), cf. https://nxr.netbsd.org/xref/src/sys/sys/sysctl.h#813 + const int mib_ncpu_existing[] = { CTL_HW, HW_NCPU }; // Number of existing CPUs + const int mib_ncpu_online[] = { CTL_HW, HW_NCPUONLINE }; // Number of online/active CPUs + + int r; + unsigned int value; + size_t size; + + bool change = false; + + // Query the number of active/online CPUs. + size = sizeof(value); + r = sysctl(mib_ncpu_online, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = 1; + } + + if (value != super->activeCPUs) { + super->activeCPUs = value; + change = true; + } + + // Query the total number of CPUs. + size = sizeof(value); + r = sysctl(mib_ncpu_existing, 2, &value, &size, NULL, 0); + if (r < 0 || value < 1) { + value = super->activeCPUs; + } + + if (value != super->existingCPUs) { + this->cpuData = xReallocArray(this->cpuData, value + 1, sizeof(CPUData)); + super->existingCPUs = value; + change = true; + } + + // Reset CPU stats when number of online/existing CPU cores changed + if (change) { + CPUData* dAvg = &this->cpuData[0]; + memset(dAvg, '\0', sizeof(CPUData)); + dAvg->totalTime = 1; + dAvg->totalPeriod = 1; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + CPUData* d = &this->cpuData[i + 1]; + memset(d, '\0', sizeof(CPUData)); + d->totalTime = 1; + d->totalPeriod = 1; + } + } +} + +Machine* Machine_new(UsersTable* usersTable, uid_t userId) { + const int fmib[] = { CTL_KERN, KERN_FSCALE }; + size_t size; + char errbuf[_POSIX2_LINE_MAX]; + + NetBSDMachine* this = xCalloc(1, sizeof(NetBSDMachine)); + Machine* super = &this->super; + Machine_init(super, usersTable, userId); + + NetBSDMachine_updateCPUcount(this); + + size = sizeof(this->fscale); + if (sysctl(fmib, 2, &this->fscale, &size, NULL, 0) < 0 || this->fscale <= 0) { + CRT_fatalError("fscale sysctl call failed"); + } + + if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1) + CRT_fatalError("pagesize sysconf call failed"); + this->pageSizeKB = this->pageSize / ONE_K; + + this->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf); + if (this->kd == NULL) { + CRT_fatalError("kvm_openfiles() failed"); + } + + return super; +} + +void Machine_delete(Machine* super) { + NetBSDMachine* this = (NetBSDMachine*) super; + + Machine_done(super); + + if (this->kd) { + kvm_close(this->kd); + } + free(this->cpuData); + free(this); +} + +static void NetBSDMachine_scanMemoryInfo(NetBSDMachine* this) { + Machine* super = &this->super; + + static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2}; + struct uvmexp_sysctl uvmexp; + size_t size_uvmexp = sizeof(uvmexp); + + if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) { + CRT_fatalError("uvmexp sysctl call failed"); + } + + super->totalMem = uvmexp.npages * this->pageSizeKB; + super->buffersMem = 0; + super->cachedMem = (uvmexp.filepages + uvmexp.execpages) * this->pageSizeKB; + super->usedMem = (uvmexp.active + uvmexp.wired) * this->pageSizeKB; + super->totalSwap = uvmexp.swpages * this->pageSizeKB; + super->usedSwap = uvmexp.swpginuse * this->pageSizeKB; +} + +static void getKernelCPUTimes(int cpuId, u_int64_t* times) { + const int mib[] = { CTL_KERN, KERN_CP_TIME, cpuId }; + size_t length = sizeof(*times) * CPUSTATES; + if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) { + CRT_fatalError("sysctl kern.cp_time2 failed"); + } +} + +static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) { + unsigned long long totalTime = 0; + for (int i = 0; i < CPUSTATES; i++) { + totalTime += times[i]; + } + + unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS]; + + cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime); + cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime); + cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime); + cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime); + cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime); + cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime); + cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime); + + cpu->totalTime = totalTime; + cpu->userTime = times[CP_USER]; + cpu->niceTime = times[CP_NICE]; + cpu->sysTime = times[CP_SYS]; + cpu->sysAllTime = sysAllTime; + cpu->intrTime = times[CP_INTR]; + cpu->idleTime = times[CP_IDLE]; +} + +static void NetBSDMachine_scanCPUTime(NetBSDMachine* this) { + const Machine* super = &this->super; + + u_int64_t kernelTimes[CPUSTATES] = {0}; + u_int64_t avg[CPUSTATES] = {0}; + + for (unsigned int i = 0; i < super->existingCPUs; i++) { + getKernelCPUTimes(i, kernelTimes); + CPUData* cpu = &this->cpuData[i + 1]; + kernelCPUTimesToHtop(kernelTimes, cpu); + + avg[CP_USER] += cpu->userTime; + avg[CP_NICE] += cpu->niceTime; + avg[CP_SYS] += cpu->sysTime; + avg[CP_INTR] += cpu->intrTime; + avg[CP_IDLE] += cpu->idleTime; + } + + for (int i = 0; i < CPUSTATES; i++) { + avg[i] /= super->activeCPUs; + } + + kernelCPUTimesToHtop(avg, &this->cpuData[0]); +} + +static void NetBSDMachine_scanCPUFrequency(NetBSDMachine* this) { + const Machine* super = &this->super; + unsigned int cpus = super->existingCPUs; + bool match = false; + char name[64]; + long int freq = 0; + size_t freqSize; + + for (unsigned int i = 0; i < cpus; i++) { + this->cpuData[i + 1].frequency = NAN; + } + + /* newer hardware supports per-core frequency, for e.g. ARM big.LITTLE */ + for (unsigned int i = 0; i < cpus; i++) { + xSnprintf(name, sizeof(name), "machdep.cpufreq.cpu%u.current", i); + freqSize = sizeof(freq); + if (sysctlbyname(name, &freq, &freqSize, NULL, 0) != -1) { + this->cpuData[i + 1].frequency = freq; /* already in MHz */ + match = true; + } + } + + if (match) { + return; + } + + /* + * Iterate through legacy sysctl nodes for single-core frequency until + * we find a match... + */ + for (size_t i = 0; i < ARRAYSIZE(freqSysctls); i++) { + freqSize = sizeof(freq); + if (sysctlbyname(freqSysctls[i].name, &freq, &freqSize, NULL, 0) != -1) { + freq /= freqSysctls[i].scale; /* scale to MHz */ + match = true; + break; + } + } + + if (match) { + for (unsigned int i = 0; i < cpus; i++) { + this->cpuData[i + 1].frequency = freq; + } + } +} + +void Machine_scan(Machine* super) { + NetBSDMachine* this = (NetBSDMachine*) super; + + NetBSDMachine_scanMemoryInfo(this); + NetBSDMachine_scanCPUTime(this); + + if (super->settings->showCPUFrequency) { + NetBSDMachine_scanCPUFrequency(this); + } +} + +bool Machine_isCPUonline(const Machine* host, unsigned int id) { + assert(id < host->existingCPUs); + (void)host; (void)id; + + // TODO: Support detecting online / offline CPUs. + return true; +} diff --git a/netbsd/NetBSDMachine.h b/netbsd/NetBSDMachine.h new file mode 100644 index 0000000..9d3aa0b --- /dev/null +++ b/netbsd/NetBSDMachine.h @@ -0,0 +1,54 @@ +#ifndef HEADER_NetBSDMachine +#define HEADER_NetBSDMachine +/* +htop - NetBSDMachine.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include <kvm.h> +#include <stdbool.h> +#include <sys/types.h> + +#include "Machine.h" +#include "ProcessTable.h" + + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int userTime; + unsigned long long int niceTime; + unsigned long long int sysTime; + unsigned long long int sysAllTime; + unsigned long long int spinTime; + unsigned long long int intrTime; + unsigned long long int idleTime; + + unsigned long long int totalPeriod; + unsigned long long int userPeriod; + unsigned long long int nicePeriod; + unsigned long long int sysPeriod; + unsigned long long int sysAllPeriod; + unsigned long long int spinPeriod; + unsigned long long int intrPeriod; + unsigned long long int idlePeriod; + + double frequency; +} CPUData; + +typedef struct NetBSDMachine_ { + Machine super; + kvm_t* kd; + + long fscale; + int pageSize; + int pageSizeKB; + + CPUData* cpuData; +} NetBSDMachine; + +#endif diff --git a/netbsd/NetBSDProcess.c b/netbsd/NetBSDProcess.c new file mode 100644 index 0000000..f58cdf2 --- /dev/null +++ b/netbsd/NetBSDProcess.c @@ -0,0 +1,277 @@ +/* +htop - NetBSDProcess.c +(C) 2015 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "netbsd/NetBSDProcess.h" + +#include <stdlib.h> + +#include "CRT.h" +#include "Process.h" +#include "RichString.h" +#include "XUtils.h" + + +const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { + [0] = { + .name = "", + .title = NULL, + .description = NULL, + .flags = 0, + }, + [PID] = { + .name = "PID", + .title = "PID", + .description = "Process/thread ID", + .flags = 0, + .pidColumn = true, + }, + [COMM] = { + .name = "Command", + .title = "Command ", + .description = "Command line", + .flags = 0, + }, + [STATE] = { + .name = "STATE", + .title = "S ", + .description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)", + .flags = 0, + }, + [PPID] = { + .name = "PPID", + .title = "PPID", + .description = "Parent process ID", + .flags = 0, + .pidColumn = true, + }, + [PGRP] = { + .name = "PGRP", + .title = "PGRP", + .description = "Process group ID", + .flags = 0, + .pidColumn = true, + }, + [SESSION] = { + .name = "SESSION", + .title = "SESN", + .description = "Process's session ID", + .flags = 0, + .pidColumn = true, + }, + [TTY] = { + .name = "TTY", + .title = "TTY ", + .description = "Controlling terminal", + .flags = 0, + }, + [TPGID] = { + .name = "TPGID", + .title = "TPGID", + .description = "Process ID of the fg process group of the controlling terminal", + .flags = 0, + .pidColumn = true, + }, + [MINFLT] = { + .name = "MINFLT", + .title = " MINFLT ", + .description = "Number of minor faults which have not required loading a memory page from disk", + .flags = 0, + .defaultSortDesc = true, + }, + [MAJFLT] = { + .name = "MAJFLT", + .title = " MAJFLT ", + .description = "Number of major faults which have required loading a memory page from disk", + .flags = 0, + .defaultSortDesc = true, + }, + [PRIORITY] = { + .name = "PRIORITY", + .title = "PRI ", + .description = "Kernel's internal priority for the process", + .flags = 0, + }, + [NICE] = { + .name = "NICE", + .title = " NI ", + .description = "Nice value (the higher the value, the more it lets other processes take priority)", + .flags = 0, + }, + [STARTTIME] = { + .name = "STARTTIME", + .title = "START ", + .description = "Time the process was started", + .flags = 0, + }, + [ELAPSED] = { + .name = "ELAPSED", + .title = "ELAPSED ", + .description = "Time since the process was started", + .flags = 0, + }, + [PROCESSOR] = { + .name = "PROCESSOR", + .title = "CPU ", + .description = "Id of the CPU the process last executed on", + .flags = 0, + }, + [M_VIRT] = { + .name = "M_VIRT", + .title = " VIRT ", + .description = "Total program size in virtual memory", + .flags = 0, + .defaultSortDesc = true, + }, + [M_RESIDENT] = { + .name = "M_RESIDENT", + .title = " RES ", + .description = "Resident set size, size of the text and data sections, plus stack usage", + .flags = 0, + .defaultSortDesc = true, + }, + [ST_UID] = { + .name = "ST_UID", + .title = "UID", + .description = "User ID of the process owner", + .flags = 0, + }, + [PERCENT_CPU] = { + .name = "PERCENT_CPU", + .title = " CPU%", + .description = "Percentage of the CPU time the process used in the last sampling", + .flags = 0, + .defaultSortDesc = true, + .autoWidth = true, + }, + [PERCENT_NORM_CPU] = { + .name = "PERCENT_NORM_CPU", + .title = "NCPU%", + .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", + .flags = 0, + .defaultSortDesc = true, + .autoWidth = true, + }, + [PERCENT_MEM] = { + .name = "PERCENT_MEM", + .title = "MEM% ", + .description = "Percentage of the memory the process is using, based on resident memory size", + .flags = 0, + .defaultSortDesc = true, + }, + [USER] = { + .name = "USER", + .title = "USER ", + .description = "Username of the process owner (or user ID if name cannot be determined)", + .flags = 0, + }, + [TIME] = { + .name = "TIME", + .title = " TIME+ ", + .description = "Total time the process has spent in user and system time", + .flags = 0, + .defaultSortDesc = true, + }, + [NLWP] = { + .name = "NLWP", + .title = "NLWP ", + .description = "Number of threads in the process", + .flags = 0, + }, + [TGID] = { + .name = "TGID", + .title = "TGID", + .description = "Thread group ID (i.e. process ID)", + .flags = 0, + .pidColumn = true, + }, + [PROC_COMM] = { + .name = "COMM", + .title = "COMM ", + .description = "comm string of the process", + .flags = 0, + }, + [PROC_EXE] = { + .name = "EXE", + .title = "EXE ", + .description = "Basename of exe of the process", + .flags = 0, + }, + [CWD] = { + .name = "CWD", + .title = "CWD ", + .description = "The current working directory of the process", + .flags = PROCESS_FLAG_CWD, + }, + +}; + +Process* NetBSDProcess_new(const Machine* host) { + NetBSDProcess* this = xCalloc(1, sizeof(NetBSDProcess)); + Object_setClass(this, Class(NetBSDProcess)); + Process_init(&this->super, host); + return &this->super; +} + +void Process_delete(Object* cast) { + NetBSDProcess* this = (NetBSDProcess*) cast; + Process_done((Process*)cast); + free(this); +} + +static void NetBSDProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) { + const NetBSDProcess* np = (const NetBSDProcess*) super; + + char buffer[256]; buffer[255] = '\0'; + int attr = CRT_colors[DEFAULT_COLOR]; + //size_t n = sizeof(buffer) - 1; + + switch (field) { + // add NetBSD-specific fields here + default: + Process_writeField(&np->super, str, field); + return; + } + + RichString_appendWide(str, attr, buffer); +} + +static int NetBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) { + const NetBSDProcess* p1 = (const NetBSDProcess*)v1; + const NetBSDProcess* p2 = (const NetBSDProcess*)v2; + + // remove if actually used + (void)p1; (void)p2; + + switch (key) { + // add NetBSD-specific fields here + default: + return Process_compareByKey_Base(v1, v2, key); + } +} + +const ProcessClass NetBSDProcess_class = { + .super = { + .super = { + .extends = Class(Process), + .display = Row_display, + .delete = Process_delete, + .compare = Process_compare + }, + .isHighlighted = Process_rowIsHighlighted, + .isVisible = Process_rowIsVisible, + .matchesFilter = Process_rowMatchesFilter, + .compareByParent = Process_compareByParent, + .sortKeyString = Process_rowGetSortKey, + .writeField = NetBSDProcess_rowWriteField + }, + .compareByKey = NetBSDProcess_compareByKey +}; diff --git a/netbsd/NetBSDProcess.h b/netbsd/NetBSDProcess.h new file mode 100644 index 0000000..1c068a4 --- /dev/null +++ b/netbsd/NetBSDProcess.h @@ -0,0 +1,32 @@ +#ifndef HEADER_NetBSDProcess +#define HEADER_NetBSDProcess +/* +htop - NetBSDProcess.h +(C) 2015 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include <stdbool.h> + +#include "Machine.h" +#include "Object.h" +#include "Process.h" + + +typedef struct NetBSDProcess_ { + Process super; +} NetBSDProcess; + +extern const ProcessClass NetBSDProcess_class; + +extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; + +Process* NetBSDProcess_new(const Machine* host); + +void Process_delete(Object* cast); + +#endif diff --git a/netbsd/NetBSDProcessTable.c b/netbsd/NetBSDProcessTable.c new file mode 100644 index 0000000..71ae53e --- /dev/null +++ b/netbsd/NetBSDProcessTable.c @@ -0,0 +1,273 @@ +/* +htop - NetBSDProcessTable.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "netbsd/NetBSDProcessTable.h" + +#include <kvm.h> +#include <math.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mount.h> +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/sched.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <uvm/uvm_extern.h> + +#include "CRT.h" +#include "Macros.h" +#include "Object.h" +#include "Process.h" +#include "ProcessTable.h" +#include "Settings.h" +#include "XUtils.h" +#include "netbsd/NetBSDMachine.h" +#include "netbsd/NetBSDProcess.h" + + +ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) { + NetBSDProcessTable* this = xCalloc(1, sizeof(NetBSDProcessTable)); + Object_setClass(this, Class(ProcessTable)); + + ProcessTable* super = (ProcessTable*) this; + ProcessTable_init(super, Class(NetBSDProcess), host, pidMatchList); + + return super; +} + +void ProcessTable_delete(Object* cast) { + NetBSDProcessTable* this = (NetBSDProcessTable*) cast; + ProcessTable_done(&this->super); + free(this); +} + +static void NetBSDProcessTable_updateExe(const struct kinfo_proc2* kproc, Process* proc) { + const int mib[] = { CTL_KERN, KERN_PROC_ARGS, kproc->p_pid, KERN_PROC_PATHNAME }; + char buffer[2048]; + size_t size = sizeof(buffer); + if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) { + Process_updateExe(proc, NULL); + return; + } + + /* Kernel threads return an empty buffer */ + if (buffer[0] == '\0') { + Process_updateExe(proc, NULL); + return; + } + + Process_updateExe(proc, buffer); +} + +static void NetBSDProcessTable_updateCwd(const struct kinfo_proc2* kproc, Process* proc) { + const int mib[] = { CTL_KERN, KERN_PROC_ARGS, kproc->p_pid, KERN_PROC_CWD }; + char buffer[2048]; + size_t size = sizeof(buffer); + if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) { + free(proc->procCwd); + proc->procCwd = NULL; + return; + } + + /* Kernel threads return an empty buffer */ + if (buffer[0] == '\0') { + free(proc->procCwd); + proc->procCwd = NULL; + return; + } + + free_and_xStrdup(&proc->procCwd, buffer); +} + +static void NetBSDProcessTable_updateProcessName(kvm_t* kd, const struct kinfo_proc2* kproc, Process* proc) { + Process_updateComm(proc, kproc->p_comm); + + /* + * Like NetBSD's top(1), we try to fall back to the command name + * (argv[0]) if we fail to construct the full command. + */ + char** arg = kvm_getargv2(kd, kproc, 500); + if (arg == NULL || *arg == NULL) { + Process_updateCmdline(proc, kproc->p_comm, 0, strlen(kproc->p_comm)); + return; + } + + size_t len = 0; + for (int i = 0; arg[i] != NULL; i++) { + len += strlen(arg[i]) + 1; /* room for arg and trailing space or NUL */ + } + + /* don't use xMalloc here - we want to handle huge argv's gracefully */ + char* s; + if ((s = malloc(len)) == NULL) { + Process_updateCmdline(proc, kproc->p_comm, 0, strlen(kproc->p_comm)); + return; + } + + *s = '\0'; + + int start = 0; + int end = 0; + for (int i = 0; arg[i] != NULL; i++) { + size_t n = strlcat(s, arg[i], len); + if (i == 0) { + end = MINIMUM(n, len - 1); + /* check if cmdline ended earlier, e.g 'kdeinit5: Running...' */ + for (int j = end; j > 0; j--) { + if (arg[0][j] == ' ' && arg[0][j - 1] != '\\') { + end = (arg[0][j - 1] == ':') ? (j - 1) : j; + } + } + } + /* the trailing space should get truncated anyway */ + strlcat(s, " ", len); + } + + Process_updateCmdline(proc, s, start, end); + + free(s); +} + +/* + * Borrowed with modifications from NetBSD's top(1). + */ +static double getpcpu(const NetBSDMachine* nhost, const struct kinfo_proc2* kp) { + if (nhost->fscale == 0) + return 0.0; + + return 100.0 * (double)kp->p_pctcpu / nhost->fscale; +} + +void ProcessTable_goThroughEntries(ProcessTable* super) { + const Machine* host = super->super.host; + const NetBSDMachine* nhost = (const NetBSDMachine*) host; + const Settings* settings = host->settings; + bool hideKernelThreads = settings->hideKernelThreads; + bool hideUserlandThreads = settings->hideUserlandThreads; + int count = 0; + + const struct kinfo_proc2* kprocs = kvm_getproc2(nhost->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &count); + + for (int i = 0; i < count; i++) { + const struct kinfo_proc2* kproc = &kprocs[i]; + + bool preExisting = false; + Process* proc = ProcessTable_getProcess(super, kproc->p_pid, &preExisting, NetBSDProcess_new); + + proc->super.show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc))); + + if (!preExisting) { + Process_setPid(proc, kproc->p_pid); + Process_setParent(proc, kproc->p_ppid); + Process_setThreadGroup(proc, kproc->p_pid); + proc->tpgid = kproc->p_tpgid; + proc->session = kproc->p_sid; + proc->pgrp = kproc->p__pgid; + proc->isKernelThread = !!(kproc->p_flag & P_SYSTEM); + proc->isUserlandThread = Process_getPid(proc) != Process_getThreadGroup(proc); // eh? + proc->starttime_ctime = kproc->p_ustart_sec; + Process_fillStarttimeBuffer(proc); + ProcessTable_add(super, proc); + + proc->tty_nr = kproc->p_tdev; + // KERN_PROC_TTY_NODEV is a negative constant but the type of + // kproc->p_tdev may be unsigned. + const char* name = ((dev_t)~kproc->p_tdev != (dev_t)~(KERN_PROC_TTY_NODEV)) ? devname(kproc->p_tdev, S_IFCHR) : NULL; + if (!name) { + free(proc->tty_name); + proc->tty_name = NULL; + } else { + free_and_xStrdup(&proc->tty_name, name); + } + + NetBSDProcessTable_updateExe(kproc, proc); + NetBSDProcessTable_updateProcessName(nhost->kd, kproc, proc); + } else { + if (settings->updateProcessNames) { + NetBSDProcessTable_updateProcessName(nhost->kd, kproc, proc); + } + } + + if (settings->ss->flags & PROCESS_FLAG_CWD) { + NetBSDProcessTable_updateCwd(kproc, proc); + } + + if (proc->st_uid != kproc->p_uid) { + proc->st_uid = kproc->p_uid; + proc->user = UsersTable_getRef(host->usersTable, proc->st_uid); + } + + proc->m_virt = kproc->p_vm_vsize; + proc->m_resident = kproc->p_vm_rssize; + + proc->percent_mem = (proc->m_resident * nhost->pageSizeKB) / (double)(host->totalMem) * 100.0; + proc->percent_cpu = CLAMP(getpcpu(nhost, kproc), 0.0, host->activeCPUs * 100.0); + Process_updateCPUFieldWidths(proc->percent_cpu); + + proc->nlwp = kproc->p_nlwps; + proc->nice = kproc->p_nice - 20; + proc->time = 100 * (kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 1000000)); + proc->priority = kproc->p_priority - PZERO; + proc->processor = kproc->p_cpuid; + proc->minflt = kproc->p_uru_minflt; + proc->majflt = kproc->p_uru_majflt; + + int nlwps = 0; + const struct kinfo_lwp* klwps = kvm_getlwps(nhost->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps); + + /* TODO: According to the link below, SDYING should be a regarded state */ + /* Taken from: https://ftp.netbsd.org/pub/NetBSD/NetBSD-current/src/sys/sys/proc.h */ + switch (kproc->p_realstat) { + case SIDL: proc->state = IDLE; break; + case SACTIVE: + // We only consider the first LWP with a one of the below states. + for (int j = 0; j < nlwps; j++) { + if (klwps) { + switch (klwps[j].l_stat) { + case LSONPROC: proc->state = RUNNING; break; + case LSRUN: proc->state = RUNNABLE; break; + case LSSLEEP: proc->state = SLEEPING; break; + case LSSTOP: proc->state = STOPPED; break; + default: proc->state = UNKNOWN; + } + + if (proc->state != UNKNOWN) { + break; + } + } else { + proc->state = UNKNOWN; + break; + } + } + break; + case SSTOP: proc->state = STOPPED; break; + case SZOMB: proc->state = ZOMBIE; break; + case SDEAD: proc->state = DEFUNCT; break; + default: proc->state = UNKNOWN; + } + + if (Process_isKernelThread(proc)) { + super->kernelThreads++; + } else if (Process_isUserlandThread(proc)) { + super->userlandThreads++; + } + + super->totalTasks++; + if (proc->state == RUNNING) { + super->runningTasks++; + } + proc->super.updated = true; + } +} diff --git a/netbsd/NetBSDProcessTable.h b/netbsd/NetBSDProcessTable.h new file mode 100644 index 0000000..1bcfa98 --- /dev/null +++ b/netbsd/NetBSDProcessTable.h @@ -0,0 +1,24 @@ +#ifndef HEADER_NetBSDProcessTable +#define HEADER_NetBSDProcessTable +/* +htop - NetBSDProcessTable.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include <stdbool.h> +#include <sys/types.h> + +#include "Hashtable.h" +#include "ProcessTable.h" + + +typedef struct NetBSDProcessTable_ { + ProcessTable super; +} NetBSDProcessTable; + +#endif diff --git a/netbsd/Platform.c b/netbsd/Platform.c new file mode 100644 index 0000000..f458c23 --- /dev/null +++ b/netbsd/Platform.c @@ -0,0 +1,518 @@ +/* +htop - netbsd/Platform.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 Nia Alarie +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "netbsd/Platform.h" + +#include <errno.h> +#include <fcntl.h> +#include <ifaddrs.h> +#include <paths.h> +#include <unistd.h> +#include <kvm.h> +#include <limits.h> +#include <math.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <net/if.h> +#include <prop/proplib.h> +#include <sys/envsys.h> +#include <sys/iostat.h> +#include <sys/param.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> +#include <sys/types.h> + +#include "CPUMeter.h" +#include "ClockMeter.h" +#include "DateMeter.h" +#include "DateTimeMeter.h" +#include "FileDescriptorMeter.h" +#include "HostnameMeter.h" +#include "LoadAverageMeter.h" +#include "Macros.h" +#include "MemoryMeter.h" +#include "MemorySwapMeter.h" +#include "Meter.h" +#include "Settings.h" +#include "SignalsPanel.h" +#include "SwapMeter.h" +#include "SysArchMeter.h" +#include "TasksMeter.h" +#include "UptimeMeter.h" +#include "XUtils.h" +#include "generic/fdstat_sysctl.h" +#include "netbsd/NetBSDMachine.h" +#include "netbsd/NetBSDProcess.h" + +/* + * The older proplib APIs will be deprecated in NetBSD 10, but we still + * want to support the 9.x stable branch. + * + * Create aliases for the newer functions that are missing from 9.x. + */ +#if !__NetBSD_Prereq__(9,99,65) +#define prop_string_equals_string prop_string_equals_cstring +#define prop_number_signed_value prop_number_integer_value +#endif + +const ScreenDefaults Platform_defaultScreens[] = { + { + .name = "Main", + .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command", + .sortKey = "PERCENT_CPU", + }, +}; + +const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens); + +/* + * See /usr/include/sys/signal.h + */ +const SignalItem Platform_signals[] = { + { .name = " 0 Cancel", .number = 0 }, + { .name = " 1 SIGHUP", .number = 1 }, + { .name = " 2 SIGINT", .number = 2 }, + { .name = " 3 SIGQUIT", .number = 3 }, + { .name = " 4 SIGILL", .number = 4 }, + { .name = " 5 SIGTRAP", .number = 5 }, + { .name = " 6 SIGABRT", .number = 6 }, + { .name = " 6 SIGIOT", .number = 6 }, + { .name = " 7 SIGEMT", .number = 7 }, + { .name = " 8 SIGFPE", .number = 8 }, + { .name = " 9 SIGKILL", .number = 9 }, + { .name = "10 SIGBUS", .number = 10 }, + { .name = "11 SIGSEGV", .number = 11 }, + { .name = "12 SIGSYS", .number = 12 }, + { .name = "13 SIGPIPE", .number = 13 }, + { .name = "14 SIGALRM", .number = 14 }, + { .name = "15 SIGTERM", .number = 15 }, + { .name = "16 SIGURG", .number = 16 }, + { .name = "17 SIGSTOP", .number = 17 }, + { .name = "18 SIGTSTP", .number = 18 }, + { .name = "19 SIGCONT", .number = 19 }, + { .name = "20 SIGCHLD", .number = 20 }, + { .name = "21 SIGTTIN", .number = 21 }, + { .name = "22 SIGTTOU", .number = 22 }, + { .name = "23 SIGIO", .number = 23 }, + { .name = "24 SIGXCPU", .number = 24 }, + { .name = "25 SIGXFSZ", .number = 25 }, + { .name = "26 SIGVTALRM", .number = 26 }, + { .name = "27 SIGPROF", .number = 27 }, + { .name = "28 SIGWINCH", .number = 28 }, + { .name = "29 SIGINFO", .number = 29 }, + { .name = "30 SIGUSR1", .number = 30 }, + { .name = "31 SIGUSR2", .number = 31 }, + { .name = "32 SIGPWR", .number = 32 }, + { .name = "33 SIGRTMIN", .number = 33 }, + { .name = "34 SIGRTMIN+1", .number = 34 }, + { .name = "35 SIGRTMIN+2", .number = 35 }, + { .name = "36 SIGRTMIN+3", .number = 36 }, + { .name = "37 SIGRTMIN+4", .number = 37 }, + { .name = "38 SIGRTMIN+5", .number = 38 }, + { .name = "39 SIGRTMIN+6", .number = 39 }, + { .name = "40 SIGRTMIN+7", .number = 40 }, + { .name = "41 SIGRTMIN+8", .number = 41 }, + { .name = "42 SIGRTMIN+9", .number = 42 }, + { .name = "43 SIGRTMIN+10", .number = 43 }, + { .name = "44 SIGRTMIN+11", .number = 44 }, + { .name = "45 SIGRTMIN+12", .number = 45 }, + { .name = "46 SIGRTMIN+13", .number = 46 }, + { .name = "47 SIGRTMIN+14", .number = 47 }, + { .name = "48 SIGRTMIN+15", .number = 48 }, + { .name = "49 SIGRTMIN+16", .number = 49 }, + { .name = "50 SIGRTMIN+17", .number = 50 }, + { .name = "51 SIGRTMIN+18", .number = 51 }, + { .name = "52 SIGRTMIN+19", .number = 52 }, + { .name = "53 SIGRTMIN+20", .number = 53 }, + { .name = "54 SIGRTMIN+21", .number = 54 }, + { .name = "55 SIGRTMIN+22", .number = 55 }, + { .name = "56 SIGRTMIN+23", .number = 56 }, + { .name = "57 SIGRTMIN+24", .number = 57 }, + { .name = "58 SIGRTMIN+25", .number = 58 }, + { .name = "59 SIGRTMIN+26", .number = 59 }, + { .name = "60 SIGRTMIN+27", .number = 60 }, + { .name = "61 SIGRTMIN+28", .number = 61 }, + { .name = "62 SIGRTMIN+29", .number = 62 }, + { .name = "63 SIGRTMAX", .number = 63 }, +}; + +const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals); + +const MeterClass* const Platform_meterTypes[] = { + &CPUMeter_class, + &ClockMeter_class, + &DateMeter_class, + &DateTimeMeter_class, + &LoadAverageMeter_class, + &LoadMeter_class, + &MemoryMeter_class, + &SwapMeter_class, + &MemorySwapMeter_class, + &TasksMeter_class, + &UptimeMeter_class, + &BatteryMeter_class, + &HostnameMeter_class, + &SysArchMeter_class, + &AllCPUsMeter_class, + &AllCPUs2Meter_class, + &AllCPUs4Meter_class, + &AllCPUs8Meter_class, + &LeftCPUsMeter_class, + &RightCPUsMeter_class, + &LeftCPUs2Meter_class, + &RightCPUs2Meter_class, + &LeftCPUs4Meter_class, + &RightCPUs4Meter_class, + &LeftCPUs8Meter_class, + &RightCPUs8Meter_class, + &BlankMeter_class, + &DiskIOMeter_class, + &NetworkIOMeter_class, + &FileDescriptorMeter_class, + NULL +}; + +bool Platform_init(void) { + /* no platform-specific setup needed */ + return true; +} + +void Platform_done(void) { + /* no platform-specific cleanup needed */ +} + +void Platform_setBindings(Htop_Action* keys) { + /* no platform-specific key bindings */ + (void) keys; +} + +int Platform_getUptime(void) { + struct timeval bootTime, currTime; + const int mib[2] = { CTL_KERN, KERN_BOOTTIME }; + size_t size = sizeof(bootTime); + + int err = sysctl(mib, 2, &bootTime, &size, NULL, 0); + if (err) { + return -1; + } + gettimeofday(&currTime, NULL); + + return (int) difftime(currTime.tv_sec, bootTime.tv_sec); +} + +void Platform_getLoadAverage(double* one, double* five, double* fifteen) { + struct loadavg loadAverage; + const int mib[2] = { CTL_VM, VM_LOADAVG }; + size_t size = sizeof(loadAverage); + + int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0); + if (err) { + *one = 0; + *five = 0; + *fifteen = 0; + return; + } + *one = (double) loadAverage.ldavg[0] / loadAverage.fscale; + *five = (double) loadAverage.ldavg[1] / loadAverage.fscale; + *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; +} + +pid_t Platform_getMaxPid(void) { + // https://nxr.netbsd.org/xref/src/sys/sys/ansi.h#__pid_t + // pid is assigned as a 32bit Integer. + return INT32_MAX; +} + +double Platform_setCPUValues(Meter* this, int cpu) { + const Machine* host = this->host; + const NetBSDMachine* nhost = (const NetBSDMachine*) host; + const CPUData* cpuData = &nhost->cpuData[cpu]; + double total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod; + double totalPercent; + double* v = this->values; + + v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0; + v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0; + if (host->settings->detailedCPUTime) { + v[CPU_METER_KERNEL] = cpuData->sysPeriod / total * 100.0; + v[CPU_METER_IRQ] = cpuData->intrPeriod / total * 100.0; + v[CPU_METER_SOFTIRQ] = 0.0; + v[CPU_METER_STEAL] = 0.0; + v[CPU_METER_GUEST] = 0.0; + v[CPU_METER_IOWAIT] = 0.0; + v[CPU_METER_FREQUENCY] = NAN; + this->curItems = 8; + } else { + v[CPU_METER_KERNEL] = cpuData->sysAllPeriod / total * 100.0; + v[CPU_METER_IRQ] = 0.0; // No steal nor guest on NetBSD + this->curItems = 4; + } + totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ]; + totalPercent = CLAMP(totalPercent, 0.0, 100.0); + + v[CPU_METER_FREQUENCY] = cpuData->frequency; + v[CPU_METER_TEMPERATURE] = NAN; + + return totalPercent; +} + +void Platform_setMemoryValues(Meter* this) { + const Machine* host = this->host; + this->total = host->totalMem; + this->values[MEMORY_METER_USED] = host->usedMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux" + this->values[MEMORY_METER_BUFFERS] = host->buffersMem; + this->values[MEMORY_METER_CACHE] = host->cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" +} + +void Platform_setSwapValues(Meter* this) { + const Machine* host = this->host; + this->total = host->totalSwap; + this->values[SWAP_METER_USED] = host->usedSwap; + // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux" + // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux" +} + +char* Platform_getProcessEnv(pid_t pid) { + char errbuf[_POSIX2_LINE_MAX]; + char* env; + char** ptr; + int count; + kvm_t* kt; + const struct kinfo_proc2* kproc; + size_t capacity = 4096, size = 0; + + if ((kt = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) { + return NULL; + } + + if ((kproc = kvm_getproc2(kt, KERN_PROC_PID, pid, sizeof(struct kinfo_proc2), &count)) == NULL) { + (void) kvm_close(kt); + return NULL; + } + + if ((ptr = kvm_getenvv2(kt, kproc, 0)) == NULL) { + (void) kvm_close(kt); + return NULL; + } + + env = xMalloc(capacity); + for (char** p = ptr; *p; p++) { + size_t len = strlen(*p) + 1; + + while (size + len > capacity) { + if (capacity > (SIZE_MAX / 2)) { + free(env); + env = NULL; + goto end; + } + + capacity *= 2; + env = xRealloc(env, capacity); + } + + String_safeStrncpy(env + size, *p, len); + size += len; + } + + if (size < 2 || env[size - 1] || env[size - 2]) { + if (size + 2 < capacity) + env = xRealloc(env, capacity + 2); + env[size] = 0; + env[size + 1] = 0; + } + +end: + (void) kvm_close(kt); + return env; +} + +FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { + (void)pid; + return NULL; +} + +void Platform_getFileDescriptors(double* used, double* max) { + Generic_getFileDescriptors_sysctl(used, max); +} + +bool Platform_getDiskIO(DiskIOData* data) { + const int mib[] = { CTL_HW, HW_IOSTATS, sizeof(struct io_sysctl) }; + struct io_sysctl* iostats = NULL; + size_t size = 0; + + for (int retry = 3; retry > 0; retry--) { + /* get the size of the IO statistic array */ + if (sysctl(mib, __arraycount(mib), iostats, &size, NULL, 0) < 0) + CRT_fatalError("Unable to get size of io_sysctl"); + + if (size == 0) { + free(iostats); + return false; + } + + iostats = xRealloc(iostats, size); + + errno = 0; + + if (sysctl(mib, __arraycount(mib), iostats, &size, NULL, 0) == 0) + break; + + if (errno != ENOMEM) + CRT_fatalError("Unable to get disk IO statistics"); + } + + if (errno == ENOMEM) + CRT_fatalError("Unable to get disk IO statistics"); + + uint64_t bytesReadSum = 0; + uint64_t bytesWriteSum = 0; + uint64_t busyTimeSum = 0; + + for (size_t i = 0, count = size / sizeof(struct io_sysctl); i < count; i++) { + /* ignore NFS activity */ + if (iostats[i].type != IOSTAT_DISK) + continue; + + bytesReadSum += iostats[i].rbytes; + bytesWriteSum += iostats[i].wbytes; + busyTimeSum += iostats[i].busysum_usec; + } + + data->totalBytesRead = bytesReadSum; + data->totalBytesWritten = bytesWriteSum; + data->totalMsTimeSpend = busyTimeSum / 1000; + + free(iostats); + return true; +} + +bool Platform_getNetworkIO(NetworkIOData* data) { + struct ifaddrs* ifaddrs = NULL; + + if (getifaddrs(&ifaddrs) != 0) + return false; + + for (const struct ifaddrs* ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) + continue; + if (ifa->ifa_addr->sa_family != AF_LINK) + continue; + if (ifa->ifa_flags & IFF_LOOPBACK) + continue; + + const struct if_data* ifd = (const struct if_data*)ifa->ifa_data; + + data->bytesReceived += ifd->ifi_ibytes; + data->packetsReceived += ifd->ifi_ipackets; + data->bytesTransmitted += ifd->ifi_obytes; + data->packetsTransmitted += ifd->ifi_opackets; + } + + freeifaddrs(ifaddrs); + return true; +} + +void Platform_getBattery(double* percent, ACPresence* isOnAC) { + prop_dictionary_t dict, fields, props; + prop_object_t device, class; + + intmax_t totalCharge = 0; + intmax_t totalCapacity = 0; + + *percent = NAN; + *isOnAC = AC_ERROR; + + int fd = open(_PATH_SYSMON, O_RDONLY); + if (fd == -1) + goto error; + + if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict) != 0) + goto error; + + prop_object_iterator_t devIter = prop_dictionary_iterator(dict); + if (devIter == NULL) + goto error; + + while ((device = prop_object_iterator_next(devIter)) != NULL) { + prop_object_t fieldsArray = prop_dictionary_get_keysym(dict, device); + if (fieldsArray == NULL) + goto error; + + prop_object_iterator_t fieldsIter = prop_array_iterator(fieldsArray); + if (fieldsIter == NULL) + goto error; + + bool isACAdapter = false; + bool isBattery = false; + + /* only assume battery is not present if explicitly stated */ + intmax_t isPresent = 1; + intmax_t isConnected = 0; + intmax_t curCharge = 0; + intmax_t maxCharge = 0; + + while ((fields = prop_object_iterator_next(fieldsIter)) != NULL) { + props = prop_dictionary_get(fields, "device-properties"); + if (props != NULL) { + class = prop_dictionary_get(props, "device-class"); + + if (prop_string_equals_string(class, "ac-adapter")) { + isACAdapter = true; + } else if (prop_string_equals_string(class, "battery")) { + isBattery = true; + } + continue; + } + + prop_object_t curValue = prop_dictionary_get(fields, "cur-value"); + prop_object_t maxValue = prop_dictionary_get(fields, "max-value"); + prop_object_t descField = prop_dictionary_get(fields, "description"); + + if (descField == NULL || curValue == NULL) + continue; + + if (prop_string_equals_string(descField, "connected")) { + isConnected = prop_number_signed_value(curValue); + } else if (prop_string_equals_string(descField, "present")) { + isPresent = prop_number_signed_value(curValue); + } else if (prop_string_equals_string(descField, "charge")) { + if (maxValue == NULL) + continue; + curCharge = prop_number_signed_value(curValue); + maxCharge = prop_number_signed_value(maxValue); + } + } + + if (isBattery && isPresent) { + totalCharge += curCharge; + totalCapacity += maxCharge; + } + + if (isACAdapter && *isOnAC != AC_PRESENT) { + *isOnAC = isConnected ? AC_PRESENT : AC_ABSENT; + } + } + + *percent = ((double)totalCharge / (double)totalCapacity) * 100.0; + +error: + if (fd != -1) + close(fd); +} diff --git a/netbsd/Platform.h b/netbsd/Platform.h new file mode 100644 index 0000000..a543f52 --- /dev/null +++ b/netbsd/Platform.h @@ -0,0 +1,138 @@ +#ifndef HEADER_Platform +#define HEADER_Platform +/* +htop - netbsd/Platform.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +(C) 2021 Santhosh Raju +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/time.h> +#include <sys/types.h> + +#include "Action.h" +#include "BatteryMeter.h" +#include "DiskIOMeter.h" +#include "Meter.h" +#include "NetworkIOMeter.h" +#include "Process.h" +#include "ProcessLocksScreen.h" +#include "SignalsPanel.h" +#include "CommandLine.h" +#include "generic/gettime.h" +#include "generic/hostname.h" +#include "generic/uname.h" + + +/* There are no Long Options for NetBSD as of now. */ +#define PLATFORM_LONG_OPTIONS \ + // End of list + +extern const ScreenDefaults Platform_defaultScreens[]; + +extern const unsigned int Platform_numberOfDefaultScreens; + +/* see /usr/include/sys/signal.h */ +extern const SignalItem Platform_signals[]; + +extern const unsigned int Platform_numberOfSignals; + +extern const MeterClass* const Platform_meterTypes[]; + +bool Platform_init(void); + +void Platform_done(void); + +void Platform_setBindings(Htop_Action* keys); + +int Platform_getUptime(void); + +void Platform_getLoadAverage(double* one, double* five, double* fifteen); + +pid_t Platform_getMaxPid(void); + +double Platform_setCPUValues(Meter* this, int cpu); + +void Platform_setMemoryValues(Meter* this); + +void Platform_setSwapValues(Meter* this); + +char* Platform_getProcessEnv(pid_t pid); + +FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid); + +void Platform_getFileDescriptors(double* used, double* max); + +bool Platform_getDiskIO(DiskIOData* data); + +bool Platform_getNetworkIO(NetworkIOData* data); + +void Platform_getBattery(double* percent, ACPresence* isOnAC); + +static inline void Platform_getHostname(char* buffer, size_t size) { + Generic_hostname(buffer, size); +} + +static inline void Platform_getRelease(char** string) { + *string = Generic_uname(); +} + +static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { } + +static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) { + return STATUS_ERROR_EXIT; +} + +static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) { + Generic_gettime_realtime(tv, msec); +} + +static inline void Platform_gettime_monotonic(uint64_t* msec) { + Generic_gettime_monotonic(msec); +} + +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} + +static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } + +static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { } + +static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } + +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} + +static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } + +static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) { + return NULL; +} + +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} + +static inline Hashtable* Platform_dynamicScreens(void) { + return NULL; +} + +static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { } + +static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { } + +static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { } + +static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { } + +#endif diff --git a/netbsd/ProcessField.h b/netbsd/ProcessField.h new file mode 100644 index 0000000..87d4a69 --- /dev/null +++ b/netbsd/ProcessField.h @@ -0,0 +1,15 @@ +#ifndef HEADER_NetBSDProcessField +#define HEADER_NetBSDProcessField +/* +htop - netbsd/ProcessField.h +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + + +#define PLATFORM_PROCESS_FIELDS \ + // End of list + + +#endif /* HEADER_NetBSDProcessField */ diff --git a/netbsd/README.md b/netbsd/README.md new file mode 100644 index 0000000..ed7be0a --- /dev/null +++ b/netbsd/README.md @@ -0,0 +1,32 @@ +NetBSD support in htop(1) +=== + +This implementation utilizes kvm_getprocs(3), sysctl(3), etc, eliminating the +need for mount_procfs(8) with Linux compatibility enabled. + +The implementation was initially based on the OpenBSD support in htop(1). + +Notes on NetBSD curses +--- + +NetBSD is one of the last operating systems to use and maintain its own +implementation of Curses. + +htop(1) can be compiled against either ncurses or NetBSD's curses(3). +In order for NetBSD's libcurses to be used, htop(1) must be configured with +`--disable-unicode`. This is necessary because htop(1) with Unicode enabled +directly accesses ncurses's cchar_t struct, which has different contents +in NetBSD's curses. + +Versions of libcurses in NetBSD 9 and prior have no mouse support +(this is an ncurses extension). Newer versions contain no-op mouse functions +for compatibility with ncurses. + +What needs improvement +--- + +* Kernel and userspace threads are not displayed or counted - + maybe look at NetBSD top(1). +* Support for compiling using libcurses's Unicode support. +* Support for fstat(1) (view open files, like lsof(8) on Linux). +* Support for ktrace(1) (like strace(1) on Linux). |