Add NetBSD platform support without procfs dependency

- TODO, clean up the code base and update comments in code.
This commit is contained in:
fraggerfox 2021-03-15 13:14:39 +05:30 committed by BenBE
parent 30dc4a2812
commit 4b49de44a8
10 changed files with 1278 additions and 0 deletions

View File

@ -236,6 +236,30 @@ myhtopplatheaders = $(dragonflybsd_platform_headers)
myhtopplatsources = $(dragonflybsd_platform_sources)
endif
# NetBSD
# -------
netbsd_platform_headers = \
netbsd/Platform.h \
netbsd/ProcessField.h \
netbsd/NetBSDProcessList.h \
netbsd/NetBSDProcess.h \
generic/hostname.h \
generic/uname.h
netbsd_platform_sources = \
netbsd/Platform.c \
netbsd/NetBSDProcessList.c \
netbsd/NetBSDProcess.c \
generic/hostname.c \
generic/uname.c
if HTOP_NETBSD
AM_LDFLAGS += -lkvm
myhtopplatheaders = $(netbsd_platform_headers)
myhtopplatsources = $(netbsd_platform_sources)
endif
# OpenBSD
# -------

View File

@ -31,6 +31,10 @@ freebsd*|kfreebsd*)
my_htop_platform=freebsd
AC_DEFINE([HTOP_FREEBSD], [], [Building for FreeBSD.])
;;
netbsd*)
my_htop_platform=netbsd
AC_DEFINE([HTOP_NETBSD], [], [Building for NetBSD.])
;;
openbsd*)
my_htop_platform=openbsd
AC_DEFINE([HTOP_OPENBSD], [], [Building for OpenBSD.])
@ -196,6 +200,10 @@ if test "$my_htop_platform" = linux; then
fi
fi
if test "$my_htop_platform" = netbsd; then
AC_SEARCH_LIBS([kvm_open], [kvm], [], [AC_MSG_ERROR([can not find required function kvm_open()])])
fi
if test "$my_htop_platform" = openbsd; then
AC_SEARCH_LIBS([kvm_open], [kvm], [], [AC_MSG_ERROR([can not find required function kvm_open()])])
fi
@ -630,6 +638,7 @@ AC_DEFINE_UNQUOTED([COPYRIGHT], ["(C) 2004-2019 Hisham Muhammad. (C) 2020-2021 h
AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux])
AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd])
AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd])
AM_CONDITIONAL([HTOP_NETBSD], [test "$my_htop_platform" = netbsd])
AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd])
AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin])
AM_CONDITIONAL([HTOP_SOLARIS], [test "$my_htop_platform" = solaris])

240
netbsd/NetBSDProcess.c Normal file
View File

@ -0,0 +1,240 @@
/*
htop - NetBSDProcess.c
(C) 2015 Hisham H. Muhammad
(C) 2021 Santhosh Raju
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "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,
},
[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,
},
[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,
},
[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,
},
};
Process* NetBSDProcess_new(const Settings* settings) {
NetBSDProcess* this = xCalloc(sizeof(NetBSDProcess), 1);
Object_setClass(this, Class(NetBSDProcess));
Process_init(&this->super, settings);
return &this->super;
}
void Process_delete(Object* cast) {
NetBSDProcess* this = (NetBSDProcess*) cast;
Process_done((Process*)cast);
free(this);
}
static void NetBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
//const NetBSDProcess* op = (const NetBSDProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
//int n = sizeof(buffer) - 1;
switch (field) {
// add NetBSD-specific fields here
default:
Process_writeField(this, 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 = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = NetBSDProcess_writeField,
.compareByKey = NetBSDProcess_compareByKey
};
bool Process_isThread(const Process* this) {
return Process_isKernelThread(this) || Process_isUserlandThread(this);
}

36
netbsd/NetBSDProcess.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef HEADER_NetBSDProcess
#define HEADER_NetBSDProcess
/*
htop - NetBSDProcess.h
(C) 2015 Hisham H. Muhammad
(C) 2015 Michael McConville
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <stdbool.h>
#include "Object.h"
#include "Process.h"
#include "Settings.h"
typedef struct NetBSDProcess_ {
Process super;
} NetBSDProcess;
#define Process_isKernelThread(_process) (_process->pgrp == 0)
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
extern const ProcessClass NetBSDProcess_class;
extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];
Process* NetBSDProcess_new(const Settings* settings);
void Process_delete(Object* cast);
bool Process_isThread(const Process* this);
#endif

398
netbsd/NetBSDProcessList.c Normal file
View File

@ -0,0 +1,398 @@
/*
htop - NetBSDProcessList.c
(C) 2014 Hisham H. Muhammad
(C) 2021 Santhosh Raju
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "NetBSDProcessList.h"
#include <kvm.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 "Macros.h"
#include "Object.h"
#include "NetBSDProcess.h"
#include "Process.h"
#include "ProcessList.h"
#include "Settings.h"
#include "XUtils.h"
static long fscale;
static int pageSize;
static int pageSizeKB;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
const int mib[] = { CTL_HW, HW_NCPU };
const int fmib[] = { CTL_KERN, KERN_FSCALE };
int r;
size_t size;
char errbuf[_POSIX2_LINE_MAX];
NetBSDProcessList* opl = xCalloc(1, sizeof(NetBSDProcessList));
ProcessList* pl = (ProcessList*) opl;
ProcessList_init(pl, Class(NetBSDProcess), usersTable, pidMatchList, userId);
size = sizeof(pl->cpuCount);
r = sysctl(mib, 2, &pl->cpuCount, &size, NULL, 0);
if (r < 0 || pl->cpuCount < 1) {
pl->cpuCount = 1;
}
opl->cpus = xCalloc(pl->cpuCount + 1, sizeof(CPUData));
size = sizeof(fscale);
if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) {
CRT_fatalError("fscale sysctl call failed");
}
if ((pageSize = sysconf(_SC_PAGESIZE)) == -1)
CRT_fatalError("pagesize sysconf call failed");
pageSizeKB = pageSize / ONE_K;
for (int i = 0; i <= pl->cpuCount; i++) {
CPUData* d = opl->cpus + i;
d->totalTime = 1;
d->totalPeriod = 1;
}
opl->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
if (opl->kd == NULL) {
CRT_fatalError("kvm_openfiles() failed");
}
return pl;
}
void ProcessList_delete(ProcessList* this) {
NetBSDProcessList* opl = (NetBSDProcessList*) this;
if (opl->kd) {
kvm_close(opl->kd);
}
free(opl->cpus);
ProcessList_done(this);
free(this);
}
static void NetBSDProcessList_scanMemoryInfo(ProcessList* pl) {
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");
}
pl->totalMem = uvmexp.npages * pageSizeKB;
// These calculations have been taken from sys/miscfs/procfs
// They need review for testing the correctness
//pl->freeMem = uvmexp.free * pageSizeKB;
pl->buffersMem = uvmexp.filepages * pageSizeKB;
pl->cachedMem = (uvmexp.anonpages + uvmexp.filepages + uvmexp.execpages) * pageSizeKB;
pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * pageSizeKB + pl->buffersMem + pl->cachedMem;
pl->totalSwap = uvmexp.swpages * pageSizeKB;
pl->usedSwap = uvmexp.swpginuse * pageSizeKB;
// const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP };
// struct uvmexp uvmexp;
// size_t size_uvmexp = sizeof(uvmexp);
//
// if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) {
// CRT_fatalError("uvmexp sysctl call failed");
// }
//
// pl->totalMem = uvmexp.npages * pageSizeKB;
// pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * pageSizeKB;
//
// // Taken from NetBSD systat/iostat.c, top/machine.c and uvm_sysctl(9)
// const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT };
// struct bcachestats bcstats;
// size_t size_bcstats = sizeof(bcstats);
//
// if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) {
// CRT_fatalError("cannot get vfs.bcachestat");
// }
//
// pl->cachedMem = bcstats.numbufpages * pageSizeKB;
//
// /*
// * Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
// * All rights reserved.
// *
// * Taken almost directly from OpenBSD's top(1)
// *
// * Originally released under a BSD-3 license
// * Modified through htop developers applying GPL-2
// */
// int nswap = swapctl(SWAP_NSWAP, 0, 0);
// if (nswap > 0) {
// struct swapent swdev[nswap];
// int rnswap = swapctl(SWAP_STATS, swdev, nswap);
//
// /* Total things up */
// unsigned long long int total = 0, used = 0;
// for (int i = 0; i < rnswap; i++) {
// if (swdev[i].se_flags & SWF_ENABLE) {
// used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
// total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
// }
// }
//
// pl->totalSwap = total;
// pl->usedSwap = used;
// } else {
// pl->totalSwap = pl->usedSwap = 0;
// }
}
static char* NetBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc2* kproc, int* basenameEnd) {
/*
* Like OpenBSD'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) {
*basenameEnd = strlen(kproc->p_comm);
return xStrdup(kproc->p_comm);
}
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) {
*basenameEnd = strlen(kproc->p_comm);
return xStrdup(kproc->p_comm);
}
*s = '\0';
for (int i = 0; arg[i] != NULL; i++) {
size_t n = strlcat(s, arg[i], len);
if (i == 0) {
*basenameEnd = MINIMUM(n, len - 1);
}
/* the trailing space should get truncated anyway */
strlcat(s, " ", len);
}
return s;
}
/*
* Taken from OpenBSD's ps(1).
*/
static double getpcpu(const struct kinfo_proc2* kp) {
if (fscale == 0)
return 0.0;
return 100.0 * (double)kp->p_pctcpu / fscale;
}
static void NetBSDProcessList_scanProcs(NetBSDProcessList* this) {
const Settings* settings = this->super.settings;
bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads;
int count = 0;
int nlwps = 0;
const struct kinfo_proc2* kprocs = kvm_getproc2(this->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 = ProcessList_getProcess(&this->super, kproc->p_pid, &preExisting, NetBSDProcess_new);
//NetBSDProcess* fp = (NetBSDProcess*) proc;
proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
if (!preExisting) {
proc->ppid = kproc->p_ppid;
proc->tpgid = kproc->p_tpgid;
proc->tgid = kproc->p_pid;
proc->session = kproc->p_sid;
proc->tty_nr = kproc->p_tdev;
proc->pgrp = kproc->p__pgid;
proc->st_uid = kproc->p_uid;
proc->starttime_ctime = kproc->p_ustart_sec;
Process_fillStarttimeBuffer(proc);
proc->user = UsersTable_getRef(this->super.usersTable, proc->st_uid);
ProcessList_add(&this->super, proc);
proc->comm = NetBSDProcessList_readProcessName(this->kd, kproc, &proc->basenameOffset);
} else {
if (settings->updateProcessNames) {
free(proc->comm);
proc->comm = NetBSDProcessList_readProcessName(this->kd, kproc, &proc->basenameOffset);
}
}
proc->m_virt = kproc->p_vm_vsize;
proc->m_resident = kproc->p_vm_rssize;
proc->percent_mem = (proc->m_resident * pageSizeKB) / (double)(this->super.totalMem) * 100.0;
proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->super.cpuCount * 100.0);
//proc->nlwp = kproc->p_numthreads;
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;
struct kinfo_lwp* klwps = kvm_getlwps(this->kd, kproc->p_pid, kproc->p_paddr, sizeof(struct kinfo_lwp), &nlwps);
proc->nlwp = nlwps;
switch (kproc->p_realstat) {
case SIDL: proc->state = 'I'; 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 = 'P'; break;
case LSRUN: proc->state = 'R'; break;
case LSSLEEP: proc->state = 'S'; break;
case LSSTOP: proc->state = 'T'; break;
default: proc->state = '?';
}
if (proc->state != '?')
break;
}
}
break;
case SSTOP: proc->state = 'T'; break;
case SZOMB: proc->state = 'Z'; break;
case SDEAD: proc->state = 'D'; break;
default: proc->state = '?';
}
// switch (kproc->p_stat) {
// case SIDL: proc->state = 'I'; break;
// case SRUN: proc->state = 'R'; break;
// case SSLEEP: proc->state = 'S'; break;
// case SSTOP: proc->state = 'T'; break;
// case SZOMB: proc->state = 'Z'; break;
// case SDEAD: proc->state = 'D'; break;
// case SONPROC: proc->state = 'P'; break;
// default: proc->state = '?';
// }
//
// if (Process_isKernelThread(proc)) {
// this->super.kernelThreads++;
// }
this->super.totalTasks++;
// SRUN ('R') means runnable, not running
if (proc->state == 'P') {
this->super.runningTasks++;
}
proc->updated = true;
}
}
static unsigned long long saturatingSub(unsigned long long a, unsigned long long b) {
return a > b ? a - b : 0;
}
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];
// XXX Not sure if CP_SPIN should be added to sysAllTime.
// See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c
#ifdef CP_SPIN
sysAllTime += times[CP_SPIN];
#endif
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);
#ifdef CP_SPIN
cpu->spinPeriod = saturatingSub(times[CP_SPIN], cpu->spinTime);
#endif
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;
#ifdef CP_SPIN
cpu->spinTime = times[CP_SPIN];
#endif
cpu->intrTime = times[CP_INTR];
cpu->idleTime = times[CP_IDLE];
}
static void NetBSDProcessList_scanCPUTime(NetBSDProcessList* this) {
u_int64_t kernelTimes[CPUSTATES] = {0};
u_int64_t avg[CPUSTATES] = {0};
for (int i = 0; i < this->super.cpuCount; i++) {
getKernelCPUTimes(i, kernelTimes);
CPUData* cpu = this->cpus + i + 1;
kernelCPUTimesToHtop(kernelTimes, cpu);
avg[CP_USER] += cpu->userTime;
avg[CP_NICE] += cpu->niceTime;
avg[CP_SYS] += cpu->sysTime;
#ifdef CP_SPIN
avg[CP_SPIN] += cpu->spinTime;
#endif
avg[CP_INTR] += cpu->intrTime;
avg[CP_IDLE] += cpu->idleTime;
}
for (int i = 0; i < CPUSTATES; i++) {
avg[i] /= this->super.cpuCount;
}
kernelCPUTimesToHtop(avg, this->cpus);
}
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
NetBSDProcessList* opl = (NetBSDProcessList*) super;
NetBSDProcessList_scanMemoryInfo(super);
NetBSDProcessList_scanCPUTime(opl);
// in pause mode only gather global data for meters (CPU/memory/...)
if (pauseProcessUpdate) {
return;
}
NetBSDProcessList_scanProcs(opl);
}

View File

@ -0,0 +1,55 @@
#ifndef HEADER_NetBSDProcessList
#define HEADER_NetBSDProcessList
/*
htop - NetBSDProcessList.h
(C) 2014 Hisham H. Muhammad
(C) 2021 Santhosh Raju
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 "Hashtable.h"
#include "ProcessList.h"
#include "UsersTable.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;
} CPUData;
typedef struct NetBSDProcessList_ {
ProcessList super;
kvm_t* kd;
CPUData* cpus;
} NetBSDProcessList;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
void ProcessList_delete(ProcessList* this);
void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif

380
netbsd/Platform.c Normal file
View File

@ -0,0 +1,380 @@
/*
htop - openbsd/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2021 Santhosh Raju
Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
#include <errno.h>
#include <kvm.h>
#include <limits.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/resource.h>
//#include <sys/sensors.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/types.h>
//#include <uvm/uvmexp.h>
#include "CPUMeter.h"
#include "ClockMeter.h"
#include "DateMeter.h"
#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "LoadAverageMeter.h"
#include "Macros.h"
#include "MemoryMeter.h"
#include "Meter.h"
#include "NetBSDProcess.h"
#include "NetBSDProcessList.h"
#include "ProcessList.h"
#include "Settings.h"
#include "SignalsPanel.h"
#include "SwapMeter.h"
#include "SysArchMeter.h"
#include "TasksMeter.h"
#include "UptimeMeter.h"
#include "XUtils.h"
const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
/*
* 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,
&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,
NULL
};
void Platform_init(void) {
/* no platform-specific setup needed */
}
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() {
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;
}
int Platform_getMaxPid() {
// this is hard-coded in sys/sys/proc.h - no sysctl exists
return 30000;
}
double Platform_setCPUValues(Meter* this, int cpu) {
const NetBSDProcessList* pl = (const NetBSDProcessList*) this->pl;
const CPUData* cpuData = &(pl->cpus[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 (this->pl->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;
totalPercent = v[0] + v[1] + v[2] + v[3];
} else {
v[2] = cpuData->sysAllPeriod / total * 100.0;
v[3] = 0.0; // No steal nor guest on NetBSD
totalPercent = v[0] + v[1] + v[2];
this->curItems = 4;
}
totalPercent = CLAMP(totalPercent, 0.0, 100.0);
v[CPU_METER_TEMPERATURE] = NAN;
return totalPercent;
}
void Platform_setMemoryValues(Meter* this) {
const ProcessList* pl = this->pl;
long int usedMem = pl->usedMem;
long int buffersMem = pl->buffersMem;
long int cachedMem = pl->cachedMem;
usedMem -= buffersMem + cachedMem;
this->total = pl->totalMem;
this->values[0] = usedMem;
this->values[1] = buffersMem;
this->values[2] = cachedMem;
}
void Platform_setSwapValues(Meter* this) {
const ProcessList* pl = this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
this->values[1] = NAN;
}
char* Platform_getProcessEnv(pid_t pid) {
char errbuf[_POSIX2_LINE_MAX];
char* env;
char** ptr;
int count;
kvm_t* kt;
struct kinfo_proc* kproc;
size_t capacity = 4096, size = 0;
if ((kt = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) {
return NULL;
}
if ((kproc = kvm_getprocs(kt, KERN_PROC_PID, pid, &count)) == NULL) {
(void) kvm_close(kt);
return NULL;
}
if ((ptr = kvm_getenvv(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;
if (size + len > capacity) {
capacity *= 2;
env = xRealloc(env, capacity);
}
strlcpy(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;
}
(void) kvm_close(kt);
return env;
}
char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
(void)pid;
(void)inode;
return NULL;
}
FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
(void)pid;
return NULL;
}
bool Platform_getDiskIO(DiskIOData* data) {
// TODO
(void)data;
return false;
}
bool Platform_getNetworkIO(NetworkIOData* data) {
// TODO
(void)data;
return false;
}
//static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) {
// for (int devn = 0;; devn++) {
// mib[2] = devn;
// if (sysctl(mib, 3, snsrdev, sdlen, NULL, 0) == -1) {
// if (errno == ENXIO)
// continue;
// if (errno == ENOENT)
// return false;
// }
// if (String_eq(name, snsrdev->xname)) {
// return true;
// }
// }
//}
void Platform_getBattery(double* percent, ACPresence* isOnAC) {
// TODO
(void)percent;
(void)isOnAC;
// int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0};
// struct sensor s;
// size_t slen = sizeof(struct sensor);
// struct sensordev snsrdev;
// size_t sdlen = sizeof(struct sensordev);
//
// bool found = findDevice("acpibat0", mib, &snsrdev, &sdlen);
//
// *percent = NAN;
// if (found) {
// /* last full capacity */
// mib[3] = 7;
// mib[4] = 0;
// double last_full_capacity = 0;
// if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1)
// last_full_capacity = s.value;
// if (last_full_capacity > 0) {
// /* remaining capacity */
// mib[3] = 7;
// mib[4] = 3;
// if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
// double charge = s.value;
// *percent = 100 * (charge / last_full_capacity);
// if (charge >= last_full_capacity) {
// *percent = 100;
// }
// }
// }
// }
//
// found = findDevice("acpiac0", mib, &snsrdev, &sdlen);
//
// *isOnAC = AC_ERROR;
// if (found) {
// mib[3] = 9;
// mib[4] = 0;
// if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1)
// *isOnAC = s.value;
// }
}

73
netbsd/Platform.h Normal file
View File

@ -0,0 +1,73 @@
#ifndef HEADER_Platform
#define HEADER_Platform
/*
htop - netbsd/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2021 Santhosh Raju
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 "Action.h"
#include "BatteryMeter.h"
#include "DiskIOMeter.h"
#include "Meter.h"
#include "NetworkIOMeter.h"
#include "Process.h"
#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
#include "generic/hostname.h"
#include "generic/uname.h"
extern const ProcessField Platform_defaultFields[];
/* see /usr/include/sys/signal.h */
extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[];
void 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);
int 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);
char* Platform_getInodeFilename(pid_t pid, ino_t inode);
FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
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();
}
#endif

15
netbsd/ProcessField.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef HEADER_NetBSDProcessField
#define HEADER_NetBSDProcessField
/*
htop - openbsd/ProcessField.h
(C) 2020 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 */

48
netbsd/README.md Normal file
View File

@ -0,0 +1,48 @@
NetBSD support in htop(1)
===
This implementation makes NetBSD use htop(1) without the need of mount_procfs(8).
The implementation has been copied over from the OpenBSD implemention in
htop(1).
Current implementation mimics the procfs based implementation in stats
collection.
Make NetBSD no longer masquerade as Linux.
Build notes
---
* Make sure python interpreter is correct in `scripts/MakeHeader.py`
What works
---
* Builds in NetBSD without any additional patch
* Uses sysctl(3) and kvm(3) to get basic information
* Shows basic meters CPU / Memory / Swap
* Shows basic process listing
What does *NOT* work
---
* Memory being split into used/buffers/cache
* Thread information and count may not be correct
What needs to be tested
---
* Basic features of htop(1) like kill / nice / sort / search works as expected
* If the meter displays are working as expected
* If the process states are displayed correctly
TODO
---
* Clean up the implementation, unused variables etc
* Remove unused / irrelevant files and useless / unused code
* Use kvm_getlwps(3) to get thread information
* Implement proper Memory usage display
* Make package in pkgsrc-wip for better testing