From a9a5a539cf59cb81d2a3da610b0d1ff56c1247cc Mon Sep 17 00:00:00 2001 From: Michael McConville Date: Fri, 18 Sep 2015 00:46:48 -0400 Subject: [PATCH] (Very) initial working OpenBSD port --- Makefile.am | 8 ++ StringUtils.c | 2 +- configure.ac | 8 ++ htop.c | 13 +- openbsd/Battery.c | 16 +++ openbsd/Battery.h | 16 +++ openbsd/OpenBSDCRT.c | 22 +++ openbsd/OpenBSDCRT.h | 16 +++ openbsd/OpenBSDProcess.c | 234 ++++++++++++++++++++++++++++++++ openbsd/OpenBSDProcess.h | 48 +++++++ openbsd/OpenBSDProcessList.c | 249 ++++++++++++++++++++++++++++++++++ openbsd/OpenBSDProcessList.h | 44 ++++++ openbsd/Platform.c | 254 +++++++++++++++++++++++++++++++++++ openbsd/Platform.h | 63 +++++++++ 14 files changed, 984 insertions(+), 9 deletions(-) create mode 100644 openbsd/Battery.c create mode 100644 openbsd/Battery.h create mode 100644 openbsd/OpenBSDCRT.c create mode 100644 openbsd/OpenBSDCRT.h create mode 100644 openbsd/OpenBSDProcess.c create mode 100644 openbsd/OpenBSDProcess.h create mode 100644 openbsd/OpenBSDProcessList.c create mode 100644 openbsd/OpenBSDProcessList.h create mode 100644 openbsd/Platform.c create mode 100644 openbsd/Platform.h diff --git a/Makefile.am b/Makefile.am index 8fa38197..b54b9a91 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,6 +51,14 @@ myhtopplatheaders = freebsd/Platform.h freebsd/FreeBSDProcessList.h \ freebsd/FreeBSDProcess.h freebsd/FreeBSDCRT.h freebsd/Battery.h endif +if HTOP_OPENBSD +myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \ +openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c + +myhtopplatheaders = openbsd/Platform.h openbsd/OpenBSDProcessList.h \ +openbsd/OpenBSDProcess.h openbsd/OpenBSDCRT.h openbsd/Battery.h +endif + if HTOP_DARWIN htop_LDFLAGS += -framework IOKit -framework CoreFoundation myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \ diff --git a/StringUtils.c b/StringUtils.c index 54a61584..ec123e9f 100644 --- a/StringUtils.c +++ b/StringUtils.c @@ -55,7 +55,7 @@ inline int String_eq(const char* s1, const char* s2) { char** String_split(const char* s, char sep, int* n) { *n = 0; const int rate = 10; - char** out = calloc(rate, sizeof(char**)); + char** out = calloc(rate, sizeof(char*)); int ctr = 0; int blocks = rate; char* where; diff --git a/configure.ac b/configure.ac index 13b09a6b..f865cbf2 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,9 @@ case "$target" in *freebsd*) my_htop_platform=freebsd ;; +*openbsd*) + my_htop_platform=openbsd + ;; *darwin*) my_htop_platform=darwin ;; @@ -168,6 +171,10 @@ if test "$my_htop_platform" = "freebsd"; then AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"]) fi +if test "$my_htop_platform" = "openbsd"; then + AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"]) +fi + AC_ARG_ENABLE(native_affinity, [AC_HELP_STRING([--enable-native-affinity], [enable native sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_native_affinity="yes") if test "x$enable_native_affinity" = xyes -a "x$cross_compiling" = xno; then AC_MSG_CHECKING([for usable sched_setaffinity]) @@ -211,6 +218,7 @@ AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-$year Hisham Muhammad", [Copyright messa # ---------------------------------------------------------------------- AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux]) AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd]) +AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd]) AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin]) AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported]) AC_SUBST(my_htop_platform) diff --git a/htop.c b/htop.c index a031cde3..32c0c510 100644 --- a/htop.c +++ b/htop.c @@ -108,24 +108,21 @@ static CommandLineSettings parseArguments(int argc, char** argv) { } flags.sortKey = ColumnsPanel_fieldNameToIndex(optarg); if (flags.sortKey == -1) { - fprintf(stderr, "Error: invalid column \"%s\".\n", optarg); - exit(1); - } + errx(1, stderr, "Error: invalid column \"%s\".\n", optarg); + } break; case 'd': if (sscanf(optarg, "%16d", &(flags.delay)) == 1) { if (flags.delay < 1) flags.delay = 1; if (flags.delay > 100) flags.delay = 100; } else { - fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg); - exit(1); + errx(1, stderr, "Error: invalid delay value \"%s\".\n", optarg); } break; case 'u': if (!Action_setUserOnly(optarg, &(flags.userId))) { - fprintf(stderr, "Error: invalid user \"%s\".\n", optarg); - exit(1); - } + errx(1, stderr, "Error: invalid user \"%s\".\n", optarg); + } break; case 'C': flags.useColors = false; diff --git a/openbsd/Battery.c b/openbsd/Battery.c new file mode 100644 index 00000000..f9d0969a --- /dev/null +++ b/openbsd/Battery.c @@ -0,0 +1,16 @@ +/* +htop - openbsd/Battery.c +(C) 2015 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "BatteryMeter.h" + +void Battery_getData(double* level, ACPresence* isOnAC) { + // TODO + *level = -1; + *isOnAC = AC_ERROR; +} + diff --git a/openbsd/Battery.h b/openbsd/Battery.h new file mode 100644 index 00000000..b1a4982e --- /dev/null +++ b/openbsd/Battery.h @@ -0,0 +1,16 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_Battery +#define HEADER_Battery +/* +htop - openbsd/Battery.h +(C) 2015 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +void Battery_getData(double* level, ACPresence* isOnAC); + + +#endif diff --git a/openbsd/OpenBSDCRT.c b/openbsd/OpenBSDCRT.c new file mode 100644 index 00000000..552ca30d --- /dev/null +++ b/openbsd/OpenBSDCRT.c @@ -0,0 +1,22 @@ +/* +htop - UnsupportedCRT.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" +#include "CRT.h" +#include +#include + +void CRT_handleSIGSEGV(int sgn) { + (void) sgn; + CRT_done(); + fprintf(stderr, "\n\nhtop " VERSION " aborting.\n"); + fprintf(stderr, "\nUnfortunately, you seem to be using an unsupported platform!"); + fprintf(stderr, "\nPlease contact your platform package mantainer!\n\n"); + abort(); +} + diff --git a/openbsd/OpenBSDCRT.h b/openbsd/OpenBSDCRT.h new file mode 100644 index 00000000..3af9787d --- /dev/null +++ b/openbsd/OpenBSDCRT.h @@ -0,0 +1,16 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_UnsupportedCRT +#define HEADER_UnsupportedCRT +/* +htop - UnsupportedCRT.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +void CRT_handleSIGSEGV(int sgn); + + +#endif diff --git a/openbsd/OpenBSDProcess.c b/openbsd/OpenBSDProcess.c new file mode 100644 index 00000000..f9c90842 --- /dev/null +++ b/openbsd/OpenBSDProcess.c @@ -0,0 +1,234 @@ +/* +htop - OpenBSDProcess.c +(C) 2015 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Process.h" +#include "ProcessList.h" +#include "OpenBSDProcess.h" +#include "Platform.h" +#include "CRT.h" + +#include +#include +#include + +/*{ + +typedef enum OpenBSDProcessFields { + // Add platform-specific fields here, with ids >= 100 + LAST_PROCESSFIELD = 100, +} OpenBSDProcessField; + +typedef struct OpenBSDProcess_ { + Process super; +} OpenBSDProcess; + +#ifndef Process_isKernelThread +#define Process_isKernelThread(_process) (_process->pgrp == 0) +#endif + +#ifndef Process_isUserlandThread +#define Process_isUserlandThread(_process) (_process->pid != _process->tgid) +#endif + +}*/ + +ProcessClass OpenBSDProcess_class = { + .super = { + .extends = Class(Process), + .display = Process_display, + .delete = Process_delete, + .compare = OpenBSDProcess_compare + }, + .writeField = (Process_WriteField) OpenBSDProcess_writeField, +}; + +ProcessFieldData Process_fields[] = { + [0] = { + .name = "", + .title = NULL, + .description = NULL, + .flags = 0, }, + [PID] = { + .name = "PID", + .title = " PID ", + .description = "Process/thread ID", + .flags = 0, }, + [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, }, + [PGRP] = { + .name = "PGRP", + .title = " PGRP ", + .description = "Process group ID", + .flags = 0, }, + [SESSION] = { + .name = "SESSION", + .title = " SESN ", + .description = "Process's session ID", + .flags = 0, }, + [TTY_NR] = { + .name = "TTY_NR", + .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, }, + [MINFLT] = { + .name = "MINFLT", + .title = " MINFLT ", + .description = "Number of minor faults which have not required loading a memory page from disk", + .flags = 0, }, + [MAJFLT] = { + .name = "MAJFLT", + .title = " MAJFLT ", + .description = "Number of major faults which have required loading a memory page from disk", + .flags = 0, }, + [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_SIZE] = { + .name = "M_SIZE", + .title = " VIRT ", + .description = "Total program size in virtual memory", + .flags = 0, }, + [M_RESIDENT] = { + .name = "M_RESIDENT", + .title = " RES ", + .description = "Resident set size, size of the text and data sections, plus stack usage", + .flags = 0, }, + [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, }, + [PERCENT_MEM] = { + .name = "PERCENT_MEM", + .title = "MEM% ", + .description = "Percentage of the memory the process is using, based on resident memory size", + .flags = 0, }, + [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, }, + [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, }, + [LAST_PROCESSFIELD] = { + .name = "*** report bug! ***", + .title = NULL, + .description = NULL, + .flags = 0, }, +}; + +ProcessPidColumn Process_pidColumns[] = { + { .id = PID, .label = "PID" }, + { .id = PPID, .label = "PPID" }, + { .id = TPGID, .label = "TPGID" }, + { .id = TGID, .label = "TGID" }, + { .id = PGRP, .label = "PGRP" }, + { .id = SESSION, .label = "SESN" }, + { .id = 0, .label = NULL }, +}; + +OpenBSDProcess* OpenBSDProcess_new(Settings* settings) { + OpenBSDProcess* this = calloc(sizeof(OpenBSDProcess), 1); + Object_setClass(this, Class(OpenBSDProcess)); + Process_init(&this->super, settings); + return this; +} + +void Process_delete(Object* cast) { + OpenBSDProcess* this = (OpenBSDProcess*) cast; + Process_done((Process*)cast); + free(this); +} + +void OpenBSDProcess_writeField(Process* this, RichString* str, ProcessField field) { + //OpenBSDProcess* fp = (OpenBSDProcess*) this; + char buffer[256]; buffer[255] = '\0'; + int attr = CRT_colors[DEFAULT_COLOR]; + //int n = sizeof(buffer) - 1; + switch (field) { + // add OpenBSD-specific fields here + default: + Process_writeField(this, str, field); + return; + } + RichString_append(str, attr, buffer); +} + +long OpenBSDProcess_compare(const void* v1, const void* v2) { + OpenBSDProcess *p1, *p2; + Settings *settings = ((Process*)v1)->settings; + if (settings->direction == 1) { + p1 = (OpenBSDProcess*)v1; + p2 = (OpenBSDProcess*)v2; + } else { + p2 = (OpenBSDProcess*)v1; + p1 = (OpenBSDProcess*)v2; + } + switch (settings->sortKey) { + // add OpenBSD-specific fields here + default: + return Process_compare(v1, v2); + } +} + +bool Process_isThread(Process* this) { + return (Process_isKernelThread(this)); +} diff --git a/openbsd/OpenBSDProcess.h b/openbsd/OpenBSDProcess.h new file mode 100644 index 00000000..ba55e5ea --- /dev/null +++ b/openbsd/OpenBSDProcess.h @@ -0,0 +1,48 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_OpenBSDProcess +#define HEADER_OpenBSDProcess +/* +htop - OpenBSDProcess.h +(C) 2015 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + + +typedef enum OpenBSDProcessFields { + // Add platform-specific fields here, with ids >= 100 + LAST_PROCESSFIELD = 100, +} OpenBSDProcessField; + +typedef struct OpenBSDProcess_ { + Process super; +} OpenBSDProcess; + +#ifndef Process_isKernelThread +#define Process_isKernelThread(_process) (_process->pgrp == 0) +#endif + +#ifndef Process_isUserlandThread +#define Process_isUserlandThread(_process) (_process->pid != _process->tgid) +#endif + + +extern ProcessClass OpenBSDProcess_class; + +extern ProcessFieldData Process_fields[]; + +extern ProcessPidColumn Process_pidColumns[]; + +OpenBSDProcess* OpenBSDProcess_new(Settings* settings); + +void Process_delete(Object* cast); + +void OpenBSDProcess_writeField(Process* this, RichString* str, ProcessField field); + +long OpenBSDProcess_compare(const void* v1, const void* v2); + +bool Process_isThread(Process* this); + +#endif diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c new file mode 100644 index 00000000..b4e3013c --- /dev/null +++ b/openbsd/OpenBSDProcessList.c @@ -0,0 +1,249 @@ +/* +htop - OpenBSDProcessList.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "ProcessList.h" +#include "OpenBSDProcessList.h" +#include "OpenBSDProcess.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*{ + +#include + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int totalPeriod; +} CPUData; + +typedef struct OpenBSDProcessList_ { + ProcessList super; + kvm_t* kd; + + CPUData* cpus; + +} OpenBSDProcessList; + +}*/ + +static int pageSizeKb; +static long fscale; + +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) { + int mib[] = { CTL_HW, HW_NCPU }; + int fmib[] = { CTL_KERN, KERN_FSCALE }; + int i; + OpenBSDProcessList* fpl = calloc(1, sizeof(OpenBSDProcessList)); + ProcessList* pl = (ProcessList*) fpl; + size_t size = sizeof(pl->cpuCount); + + ProcessList_init(pl, Class(OpenBSDProcess), usersTable, pidWhiteList, userId); + pl->cpuCount = 1; // default to 1 on sysctl() error + (void)sysctl(mib, 2, &pl->cpuCount, &size, NULL, 0); + fpl->cpus = realloc(fpl->cpus, pl->cpuCount * sizeof(CPUData)); + + size = sizeof(fscale); + if (sysctl(fmib, 2, &fscale, &size, NULL, 0) < 0) + err(1, "fscale sysctl call failed"); + + for (i = 0; i < pl->cpuCount; i++) { + fpl->cpus[i].totalTime = 1; + fpl->cpus[i].totalPeriod = 1; + } + + pageSizeKb = PAGE_SIZE_KB; + + // XXX: last arg should eventually be an errbuf + fpl->kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL); + assert(fpl->kd); + + return pl; +} + +void ProcessList_delete(ProcessList* this) { + const OpenBSDProcessList* fpl = (OpenBSDProcessList*) this; + if (fpl->kd) kvm_close(fpl->kd); + + ProcessList_done(this); + free(this); +} + +static inline void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) { + static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP}; + struct uvmexp uvmexp; + size_t size = sizeof(uvmexp); + + if (sysctl(uvmexp_mib, 2, &uvmexp, &size, NULL, 0) < 0) { + err(1, "uvmexp sysctl call failed"); + } + + //kb_pagesize = uvmexp.pagesize / 1024; + pl->usedMem = uvmexp.active * pageSizeKb; + pl->totalMem = uvmexp.npages * pageSizeKb; + + /* + const OpenBSDProcessList* fpl = (OpenBSDProcessList*) pl; + + size_t len = sizeof(pl->totalMem); + sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0); + pl->totalMem /= 1024; + sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(pl->usedMem), &len, NULL, 0); + pl->usedMem *= pageSizeKb; + pl->freeMem = pl->totalMem - pl->usedMem; + sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0); + pl->cachedMem *= pageSizeKb; + + struct kvm_swap swap[16]; + int nswap = kvm_getswapinfo(fpl->kd, swap, sizeof(swap)/sizeof(swap[0]), 0); + pl->totalSwap = 0; + pl->usedSwap = 0; + for (int i = 0; i < nswap; i++) { + pl->totalSwap += swap[i].ksw_total; + pl->usedSwap += swap[i].ksw_used; + } + pl->totalSwap *= pageSizeKb; + pl->usedSwap *= pageSizeKb; + + pl->sharedMem = 0; // currently unused + pl->buffersMem = 0; // not exposed to userspace + */ +} + +char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) { + char *str, *buf, **argv; + size_t cpsz; + size_t len = 500; + + argv = kvm_getargv(kd, kproc, 500); + + if (argv == NULL) + err(1, "kvm call failed"); + + str = buf = malloc(len+1); + if (str == NULL) + err(1, "out of memory"); + + while (*argv != NULL) { + cpsz = MIN(len, strlen(*argv)); + strncpy(buf, *argv, cpsz); + buf += cpsz; + len -= cpsz; + argv++; + if (len > 0) { + *buf = ' '; + buf++; + len--; + } + } + + *buf = '\0'; + return str; +} + +/* + * Taken from OpenBSD's ps(1). + */ +double getpcpu(const struct kinfo_proc *kp) { + if (fscale == 0) + return (0.0); + +#define fxtofl(fixpt) ((double)(fixpt) / fscale) + + return (100.0 * fxtofl(kp->p_pctcpu)); +} + +void ProcessList_goThroughEntries(ProcessList* this) { + OpenBSDProcessList* fpl = (OpenBSDProcessList*) this; + Settings* settings = this->settings; + bool hideKernelThreads = settings->hideKernelThreads; + bool hideUserlandThreads = settings->hideUserlandThreads; + struct kinfo_proc* kproc; + bool preExisting; + Process* proc; + OpenBSDProcess* fp; + int count = 0; + int i; + + OpenBSDProcessList_scanMemoryInfo(this); + + // use KERN_PROC_KTHREAD to also include kernel threads + struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count); + //struct kinfo_proc* kprocs = getprocs(KERN_PROC_ALL, 0, &count); + + for (i = 0; i < count; i++) { + kproc = &kprocs[i]; + + preExisting = false; + proc = ProcessList_getProcess(this, kproc->p_pid, &preExisting, (Process_New) OpenBSDProcess_new); + fp = (OpenBSDProcess*) 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; + proc->user = UsersTable_getRef(this->usersTable, proc->st_uid); + ProcessList_add((ProcessList*)this, proc); + proc->comm = OpenBSDProcessList_readProcessName(fpl->kd, kproc, &proc->basenameOffset); + } else { + if (settings->updateProcessNames) { + free(proc->comm); + proc->comm = OpenBSDProcessList_readProcessName(fpl->kd, kproc, &proc->basenameOffset); + } + } + + proc->m_size = kproc->p_vm_dsize; + proc->m_resident = kproc->p_vm_rssize; + proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0; + proc->percent_cpu = MAX(MIN(getpcpu(kproc), this->cpuCount*100.0), 0.0); + //proc->nlwp = kproc->p_numthreads; + //proc->time = kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 10); + proc->nice = kproc->p_nice - 20; + proc->time = kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 1000000); + proc->time *= 100; + proc->priority = kproc->p_priority - PZERO; + + 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->kernelThreads++; + } + + this->totalTasks++; + if (proc->state == 'R') + this->runningTasks++; + proc->updated = true; + } +} diff --git a/openbsd/OpenBSDProcessList.h b/openbsd/OpenBSDProcessList.h new file mode 100644 index 00000000..567dd25e --- /dev/null +++ b/openbsd/OpenBSDProcessList.h @@ -0,0 +1,44 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_OpenBSDProcessList +#define HEADER_OpenBSDProcessList +/* +htop - OpenBSDProcessList.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + + +#include + +typedef struct CPUData_ { + unsigned long long int totalTime; + unsigned long long int totalPeriod; +} CPUData; + +typedef struct OpenBSDProcessList_ { + ProcessList super; + kvm_t* kd; + + CPUData* cpus; + +} OpenBSDProcessList; + + + +ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId); + +void ProcessList_delete(ProcessList* this); + +char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd); + +/* + * Taken from OpenBSD's ps(1). + */ +double getpcpu(const struct kinfo_proc *kp); + +void ProcessList_goThroughEntries(ProcessList* this); + +#endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c new file mode 100644 index 00000000..675a5191 --- /dev/null +++ b/openbsd/Platform.c @@ -0,0 +1,254 @@ +/* +htop - openbsd/Platform.c +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Platform.h" +#include "Meter.h" +#include "CPUMeter.h" +#include "MemoryMeter.h" +#include "SwapMeter.h" +#include "TasksMeter.h" +#include "LoadAverageMeter.h" +#include "UptimeMeter.h" +#include "ClockMeter.h" +#include "HostnameMeter.h" +#include "OpenBSDProcess.h" +#include "OpenBSDProcessList.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*{ +#include "Action.h" +#include "BatteryMeter.h" + +extern ProcessFieldData Process_fields[]; + +}*/ + +#define MAXCPU 256 +// XXX: probably should be a struct member +static int64_t old_v[MAXCPU][5]; + +/* + * Copyright (c) 1984, 1989, William LeFebvre, Rice University + * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University + * + * Taken directly from OpenBSD's top(1). + * + * percentages(cnt, out, new, old, diffs) - calculate percentage change + * between array "old" and "new", putting the percentages in "out". + * "cnt" is size of each array and "diffs" is used for scratch space. + * The array "old" is updated on each call. + * The routine assumes modulo arithmetic. This function is especially + * useful on BSD machines for calculating cpu state percentages. + */ +static int percentages(int cnt, int64_t *out, int64_t *new, int64_t *old, int64_t *diffs) { + int64_t change, total_change, *dp, half_total; + int i; + + /* initialization */ + total_change = 0; + dp = diffs; + + /* calculate changes for each state and the overall change */ + for (i = 0; i < cnt; i++) { + if ((change = *new - *old) < 0) { + /* this only happens when the counter wraps */ + change = INT64_MAX - *old + *new; + } + total_change += (*dp++ = change); + *old++ = *new++; + } + + /* avoid divide by zero potential */ + if (total_change == 0) + total_change = 1; + + /* calculate percentages based on overall change, rounding up */ + half_total = total_change / 2l; + for (i = 0; i < cnt; i++) + *out++ = ((*diffs++ * 1000 + half_total) / total_change); + + /* return the total in case the caller wants to use it */ + return (total_change); +} + +ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; + +int Platform_numberOfFields = LAST_PROCESSFIELD; + +void Platform_setBindings(Htop_Action* keys) { + (void) keys; +} + +MeterClass* Platform_meterTypes[] = { + &CPUMeter_class, + &ClockMeter_class, + &LoadAverageMeter_class, + &LoadMeter_class, + &MemoryMeter_class, + &SwapMeter_class, + &TasksMeter_class, + &UptimeMeter_class, + &BatteryMeter_class, + &HostnameMeter_class, + &AllCPUsMeter_class, + &AllCPUs2Meter_class, + &LeftCPUsMeter_class, + &RightCPUsMeter_class, + &LeftCPUs2Meter_class, + &RightCPUs2Meter_class, + &BlankMeter_class, + NULL +}; + +// preserved from FreeBSD port +int Platform_getUptime() { + struct timeval bootTime, currTime; + 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; + 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 32766; +} + +double Platform_setCPUValues(Meter* this, int cpu) { + int i; + double perc; + + OpenBSDProcessList* pl = (OpenBSDProcessList*) this->pl; + CPUData* cpuData = &(pl->cpus[cpu]); + int64_t new_v[CPUSTATES], diff_v[CPUSTATES], scratch_v[CPUSTATES]; + double *v = this->values; + size_t size = sizeof(double) * CPUSTATES; + int mib[] = { CTL_KERN, KERN_CPTIME2, cpu-1 }; + if (sysctl(mib, 3, new_v, &size, NULL, 0) == -1) { + puts("err!"); + //return 0.; + } + + // XXX: why? + cpuData->totalPeriod = 1; + + percentages(CPUSTATES, diff_v, new_v, + (int64_t *)old_v[cpu-1], scratch_v); + + for (i = 0; i < CPUSTATES; i++) { + old_v[cpu-1][i] = new_v[i]; + v[i] = diff_v[i] / 10.; + } + + Meter_setItems(this, 4); + + perc = v[0] + v[1] + v[2] + v[3]; + + if (perc <= 100. && perc >= 0.) { + return perc; + } else { + return 12.34; + } +} + +void Platform_setMemoryValues(Meter* this) { + ProcessList* pl = (ProcessList*) 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; +} + +/* + * Copyright (c) 1994 Thorsten Lockert + * All rights reserved. + * + * Taken almost directly from OpenBSD's top(1) + */ +void Platform_setSwapValues(Meter* this) { + ProcessList* pl = (ProcessList*) this->pl; + struct swapent *swdev; + unsigned long long int total, used; + int nswap, rnswap, i; + nswap = swapctl(SWAP_NSWAP, 0, 0); + if (nswap == 0) { + return; + } + + swdev = calloc(nswap, sizeof(*swdev)); + if (swdev == NULL) { + return; + } + + rnswap = swapctl(SWAP_STATS, swdev, nswap); + if (rnswap == -1) { + free(swdev); + return; + } + + // if rnswap != nswap, then what? + + /* Total things up */ + total = used = 0; + for (i = 0; i < nswap; i++) { + if (swdev[i].se_flags & SWF_ENABLE) { + used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); + total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); + } + } + + this->total = pl->totalSwap = total; + this->values[0] = pl->usedSwap = used; + + free(swdev); +} + +void Platform_setTasksValues(Meter* this) { + // TODO +} diff --git a/openbsd/Platform.h b/openbsd/Platform.h new file mode 100644 index 00000000..f44fea2d --- /dev/null +++ b/openbsd/Platform.h @@ -0,0 +1,63 @@ +/* Do not edit this file. It was automatically generated. */ + +#ifndef HEADER_Platform +#define HEADER_Platform +/* +htop - openbsd/Platform.h +(C) 2014 Hisham H. Muhammad +(C) 2015 Michael McConville +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Action.h" +#include "BatteryMeter.h" + +extern ProcessFieldData Process_fields[]; + + +#define MAXCPU 256 +// XXX: probably should be a struct member +/* + * Copyright (c) 1984, 1989, William LeFebvre, Rice University + * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University + * + * Taken directly from OpenBSD's top(1). + * + * percentages(cnt, out, new, old, diffs) - calculate percentage change + * between array "old" and "new", putting the percentages in "out". + * "cnt" is size of each array and "diffs" is used for scratch space. + * The array "old" is updated on each call. + * The routine assumes modulo arithmetic. This function is especially + * useful on BSD machines for calculating cpu state percentages. + */ +extern ProcessField Platform_defaultFields[]; + +extern int Platform_numberOfFields; + +void Platform_setBindings(Htop_Action* keys); + +extern MeterClass* Platform_meterTypes[]; + +// preserved from FreeBSD port +int Platform_getUptime(); + +void Platform_getLoadAverage(double* one, double* five, double* fifteen); + +int Platform_getMaxPid(); + +double Platform_setCPUValues(Meter* this, int cpu); + +void Platform_setMemoryValues(Meter* this); + +/* + * Copyright (c) 1994 Thorsten Lockert + * All rights reserved. + * + * Taken almost directly from OpenBSD's top(1) + */ +void Platform_setSwapValues(Meter* this); + +void Platform_setTasksValues(Meter* this); + +#endif