1 /*
2 htop - freebsd/Platform.c
3 (C) 2014 Hisham H. Muhammad
4 Released under the GNU GPLv2+, see the COPYING file
5 in the source distribution for its full text.
6 */
7 
8 #include "config.h" // IWYU pragma: keep
9 
10 #include "freebsd/Platform.h"
11 
12 #include <devstat.h>
13 #include <math.h>
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <time.h>
17 #include <net/if.h>
18 #include <net/if_mib.h>
19 #include <sys/_types.h>
20 #include <sys/devicestat.h>
21 #include <sys/param.h>
22 #include <sys/resource.h>
23 #include <sys/socket.h>
24 #include <sys/sysctl.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <vm/vm_param.h>
28 
29 #include "CPUMeter.h"
30 #include "ClockMeter.h"
31 #include "DateMeter.h"
32 #include "DateTimeMeter.h"
33 #include "DiskIOMeter.h"
34 #include "HostnameMeter.h"
35 #include "LoadAverageMeter.h"
36 #include "Macros.h"
37 #include "MemoryMeter.h"
38 #include "MemorySwapMeter.h"
39 #include "Meter.h"
40 #include "NetworkIOMeter.h"
41 #include "ProcessList.h"
42 #include "Settings.h"
43 #include "SwapMeter.h"
44 #include "SysArchMeter.h"
45 #include "TasksMeter.h"
46 #include "UptimeMeter.h"
47 #include "XUtils.h"
48 #include "freebsd/FreeBSDProcess.h"
49 #include "freebsd/FreeBSDProcessList.h"
50 #include "zfs/ZfsArcMeter.h"
51 #include "zfs/ZfsCompressedArcMeter.h"
52 
53 
54 const ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
55 
56 const SignalItem Platform_signals[] = {
57    { .name = " 0 Cancel",    .number =  0 },
58    { .name = " 1 SIGHUP",    .number =  1 },
59    { .name = " 2 SIGINT",    .number =  2 },
60    { .name = " 3 SIGQUIT",   .number =  3 },
61    { .name = " 4 SIGILL",    .number =  4 },
62    { .name = " 5 SIGTRAP",   .number =  5 },
63    { .name = " 6 SIGABRT",   .number =  6 },
64    { .name = " 7 SIGEMT",    .number =  7 },
65    { .name = " 8 SIGFPE",    .number =  8 },
66    { .name = " 9 SIGKILL",   .number =  9 },
67    { .name = "10 SIGBUS",    .number = 10 },
68    { .name = "11 SIGSEGV",   .number = 11 },
69    { .name = "12 SIGSYS",    .number = 12 },
70    { .name = "13 SIGPIPE",   .number = 13 },
71    { .name = "14 SIGALRM",   .number = 14 },
72    { .name = "15 SIGTERM",   .number = 15 },
73    { .name = "16 SIGURG",    .number = 16 },
74    { .name = "17 SIGSTOP",   .number = 17 },
75    { .name = "18 SIGTSTP",   .number = 18 },
76    { .name = "19 SIGCONT",   .number = 19 },
77    { .name = "20 SIGCHLD",   .number = 20 },
78    { .name = "21 SIGTTIN",   .number = 21 },
79    { .name = "22 SIGTTOU",   .number = 22 },
80    { .name = "23 SIGIO",     .number = 23 },
81    { .name = "24 SIGXCPU",   .number = 24 },
82    { .name = "25 SIGXFSZ",   .number = 25 },
83    { .name = "26 SIGVTALRM", .number = 26 },
84    { .name = "27 SIGPROF",   .number = 27 },
85    { .name = "28 SIGWINCH",  .number = 28 },
86    { .name = "29 SIGINFO",   .number = 29 },
87    { .name = "30 SIGUSR1",   .number = 30 },
88    { .name = "31 SIGUSR2",   .number = 31 },
89    { .name = "32 SIGTHR",    .number = 32 },
90    { .name = "33 SIGLIBRT",  .number = 33 },
91 };
92 
93 const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
94 
95 const MeterClass* const Platform_meterTypes[] = {
96    &CPUMeter_class,
97    &ClockMeter_class,
98    &DateMeter_class,
99    &DateTimeMeter_class,
100    &LoadAverageMeter_class,
101    &LoadMeter_class,
102    &MemoryMeter_class,
103    &SwapMeter_class,
104    &MemorySwapMeter_class,
105    &TasksMeter_class,
106    &UptimeMeter_class,
107    &BatteryMeter_class,
108    &HostnameMeter_class,
109    &SysArchMeter_class,
110    &AllCPUsMeter_class,
111    &AllCPUs2Meter_class,
112    &AllCPUs4Meter_class,
113    &AllCPUs8Meter_class,
114    &LeftCPUsMeter_class,
115    &RightCPUsMeter_class,
116    &LeftCPUs2Meter_class,
117    &RightCPUs2Meter_class,
118    &LeftCPUs4Meter_class,
119    &RightCPUs4Meter_class,
120    &LeftCPUs8Meter_class,
121    &RightCPUs8Meter_class,
122    &BlankMeter_class,
123    &ZfsArcMeter_class,
124    &ZfsCompressedArcMeter_class,
125    &DiskIOMeter_class,
126    &NetworkIOMeter_class,
127    NULL
128 };
129 
Platform_init(void)130 bool Platform_init(void) {
131    /* no platform-specific setup needed */
132    return true;
133 }
134 
Platform_done(void)135 void Platform_done(void) {
136    /* no platform-specific cleanup needed */
137 }
138 
Platform_setBindings(Htop_Action * keys)139 void Platform_setBindings(Htop_Action* keys) {
140    /* no platform-specific key bindings */
141    (void) keys;
142 }
143 
Platform_getUptime()144 int Platform_getUptime() {
145    struct timeval bootTime, currTime;
146    const int mib[2] = { CTL_KERN, KERN_BOOTTIME };
147    size_t size = sizeof(bootTime);
148 
149    int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
150    if (err) {
151       return -1;
152    }
153    gettimeofday(&currTime, NULL);
154 
155    return (int) difftime(currTime.tv_sec, bootTime.tv_sec);
156 }
157 
Platform_getLoadAverage(double * one,double * five,double * fifteen)158 void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
159    struct loadavg loadAverage;
160    const int mib[2] = { CTL_VM, VM_LOADAVG };
161    size_t size = sizeof(loadAverage);
162 
163    int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0);
164    if (err) {
165       *one = 0;
166       *five = 0;
167       *fifteen = 0;
168       return;
169    }
170    *one     = (double) loadAverage.ldavg[0] / loadAverage.fscale;
171    *five    = (double) loadAverage.ldavg[1] / loadAverage.fscale;
172    *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale;
173 }
174 
Platform_getMaxPid()175 int Platform_getMaxPid() {
176    int maxPid;
177    size_t size = sizeof(maxPid);
178    int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0);
179    if (err) {
180       return 99999;
181    }
182    return maxPid;
183 }
184 
Platform_setCPUValues(Meter * this,unsigned int cpu)185 double Platform_setCPUValues(Meter* this, unsigned int cpu) {
186    const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl;
187    unsigned int cpus = this->pl->activeCPUs;
188    const CPUData* cpuData;
189 
190    if (cpus == 1) {
191       // single CPU box has everything in fpl->cpus[0]
192       cpuData = &(fpl->cpus[0]);
193    } else {
194       cpuData = &(fpl->cpus[cpu]);
195    }
196 
197    double  percent;
198    double* v = this->values;
199 
200    v[CPU_METER_NICE]   = cpuData->nicePercent;
201    v[CPU_METER_NORMAL] = cpuData->userPercent;
202    if (this->pl->settings->detailedCPUTime) {
203       v[CPU_METER_KERNEL]  = cpuData->systemPercent;
204       v[CPU_METER_IRQ]     = cpuData->irqPercent;
205       this->curItems = 4;
206       percent = v[0] + v[1] + v[2] + v[3];
207    } else {
208       v[2] = cpuData->systemAllPercent;
209       this->curItems = 3;
210       percent = v[0] + v[1] + v[2];
211    }
212 
213    percent = CLAMP(percent, 0.0, 100.0);
214 
215    v[CPU_METER_FREQUENCY] = cpuData->frequency;
216    v[CPU_METER_TEMPERATURE] = cpuData->temperature;
217 
218    return percent;
219 }
220 
Platform_setMemoryValues(Meter * this)221 void Platform_setMemoryValues(Meter* this) {
222    const ProcessList* pl = this->pl;
223 
224    this->total = pl->totalMem;
225    this->values[0] = pl->usedMem;
226    this->values[1] = pl->buffersMem;
227    // this->values[2] = "shared memory, like tmpfs and shm"
228    this->values[3] = pl->cachedMem;
229    // this->values[4] = "available memory"
230 }
231 
Platform_setSwapValues(Meter * this)232 void Platform_setSwapValues(Meter* this) {
233    const ProcessList* pl = this->pl;
234    this->total = pl->totalSwap;
235    this->values[0] = pl->usedSwap;
236    this->values[1] = NAN;
237 }
238 
Platform_setZfsArcValues(Meter * this)239 void Platform_setZfsArcValues(Meter* this) {
240    const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl;
241 
242    ZfsArcMeter_readStats(this, &(fpl->zfs));
243 }
244 
Platform_setZfsCompressedArcValues(Meter * this)245 void Platform_setZfsCompressedArcValues(Meter* this) {
246    const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl;
247 
248    ZfsCompressedArcMeter_readStats(this, &(fpl->zfs));
249 }
250 
Platform_getProcessEnv(pid_t pid)251 char* Platform_getProcessEnv(pid_t pid) {
252    const int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ENV, pid };
253 
254    size_t capacity = ARG_MAX;
255    char* env = xMalloc(capacity);
256 
257    int err = sysctl(mib, 4, env, &capacity, NULL, 0);
258    if (err || capacity == 0) {
259       free(env);
260       return NULL;
261    }
262 
263    if (env[capacity - 1] || env[capacity - 2]) {
264       env = xRealloc(env, capacity + 2);
265       env[capacity] = 0;
266       env[capacity + 1] = 0;
267    }
268 
269    return env;
270 }
271 
Platform_getInodeFilename(pid_t pid,ino_t inode)272 char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
273    (void)pid;
274    (void)inode;
275    return NULL;
276 }
277 
Platform_getProcessLocks(pid_t pid)278 FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
279    (void)pid;
280    return NULL;
281 }
282 
Platform_getDiskIO(DiskIOData * data)283 bool Platform_getDiskIO(DiskIOData* data) {
284 
285    if (devstat_checkversion(NULL) < 0)
286       return false;
287 
288    // use static to plug memory leak; see #841
289    static struct devinfo info = { 0 };
290    struct statinfo current = { .dinfo = &info };
291 
292    // get number of devices
293    if (devstat_getdevs(NULL, &current) < 0)
294       return false;
295 
296    int count = current.dinfo->numdevs;
297 
298    unsigned long long int bytesReadSum = 0, bytesWriteSum = 0, timeSpendSum = 0;
299 
300    // get data
301    for (int i = 0; i < count; i++) {
302       uint64_t bytes_read, bytes_write;
303       long double busy_time;
304 
305       devstat_compute_statistics(&current.dinfo->devices[i],
306                                  NULL,
307                                  1.0,
308                                  DSM_TOTAL_BYTES_READ, &bytes_read,
309                                  DSM_TOTAL_BYTES_WRITE, &bytes_write,
310                                  DSM_TOTAL_BUSY_TIME, &busy_time,
311                                  DSM_NONE);
312 
313       bytesReadSum += bytes_read;
314       bytesWriteSum += bytes_write;
315       timeSpendSum += 1000 * busy_time;
316    }
317 
318    data->totalBytesRead = bytesReadSum;
319    data->totalBytesWritten = bytesWriteSum;
320    data->totalMsTimeSpend = timeSpendSum;
321    return true;
322 }
323 
Platform_getNetworkIO(NetworkIOData * data)324 bool Platform_getNetworkIO(NetworkIOData* data) {
325    // get number of interfaces
326    int count;
327    size_t countLen = sizeof(count);
328    const int countMib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_SYSTEM, IFMIB_IFCOUNT };
329 
330    int r = sysctl(countMib, ARRAYSIZE(countMib), &count, &countLen, NULL, 0);
331    if (r < 0)
332       return false;
333 
334    memset(data, 0, sizeof(NetworkIOData));
335    for (int i = 1; i <= count; i++) {
336       struct ifmibdata ifmd;
337       size_t ifmdLen = sizeof(ifmd);
338 
339       const int dataMib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, i, IFDATA_GENERAL };
340 
341       r = sysctl(dataMib, ARRAYSIZE(dataMib), &ifmd, &ifmdLen, NULL, 0);
342       if (r < 0)
343          continue;
344 
345       if (ifmd.ifmd_flags & IFF_LOOPBACK)
346          continue;
347 
348       data->bytesReceived += ifmd.ifmd_data.ifi_ibytes;
349       data->packetsReceived += ifmd.ifmd_data.ifi_ipackets;
350       data->bytesTransmitted += ifmd.ifmd_data.ifi_obytes;
351       data->packetsTransmitted += ifmd.ifmd_data.ifi_opackets;
352    }
353 
354    return true;
355 }
356 
Platform_getBattery(double * percent,ACPresence * isOnAC)357 void Platform_getBattery(double* percent, ACPresence* isOnAC) {
358    int life;
359    size_t life_len = sizeof(life);
360    if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
361       *percent = NAN;
362    else
363       *percent = life;
364 
365    int acline;
366    size_t acline_len = sizeof(acline);
367    if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1)
368       *isOnAC = AC_ERROR;
369    else
370       *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT;
371 }
372