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, ¤t) < 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(¤t.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