2014-11-24 20:55:49 +00:00
|
|
|
/*
|
|
|
|
htop - linux/Platform.c
|
|
|
|
(C) 2014 Hisham H. Muhammad
|
2021-09-22 09:33:00 +00:00
|
|
|
Released under the GNU GPLv2+, see the COPYING file
|
2014-11-24 20:55:49 +00:00
|
|
|
in the source distribution for its full text.
|
|
|
|
*/
|
|
|
|
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2021-04-29 18:13:36 +00:00
|
|
|
#include "linux/Platform.h"
|
2020-09-19 11:55:23 +00:00
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
2020-11-11 21:15:35 +00:00
|
|
|
#include <dirent.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <inttypes.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <math.h>
|
2020-11-18 13:26:30 +00:00
|
|
|
#include <stdint.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
#include <stdio.h>
|
2020-11-18 13:26:30 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
2020-11-17 07:12:38 +00:00
|
|
|
#include <unistd.h>
|
2020-09-19 11:55:23 +00:00
|
|
|
|
|
|
|
#include "BatteryMeter.h"
|
|
|
|
#include "ClockMeter.h"
|
2020-11-11 21:15:35 +00:00
|
|
|
#include "Compat.h"
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "CPUMeter.h"
|
|
|
|
#include "DateMeter.h"
|
|
|
|
#include "DateTimeMeter.h"
|
|
|
|
#include "DiskIOMeter.h"
|
|
|
|
#include "HostnameMeter.h"
|
2021-01-05 14:50:23 +00:00
|
|
|
#include "HugePageMeter.h"
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "LoadAverageMeter.h"
|
|
|
|
#include "Macros.h"
|
|
|
|
#include "MainPanel.h"
|
2014-11-27 21:18:14 +00:00
|
|
|
#include "Meter.h"
|
|
|
|
#include "MemoryMeter.h"
|
2021-08-10 19:32:05 +00:00
|
|
|
#include "MemorySwapMeter.h"
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "NetworkIOMeter.h"
|
|
|
|
#include "Object.h"
|
|
|
|
#include "Panel.h"
|
|
|
|
#include "PressureStallMeter.h"
|
|
|
|
#include "ProcessList.h"
|
|
|
|
#include "ProvideCurses.h"
|
2021-04-29 18:13:36 +00:00
|
|
|
#include "linux/SELinuxMeter.h"
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "Settings.h"
|
2014-11-27 21:18:14 +00:00
|
|
|
#include "SwapMeter.h"
|
2021-01-27 09:45:48 +00:00
|
|
|
#include "SysArchMeter.h"
|
2014-11-27 21:18:14 +00:00
|
|
|
#include "TasksMeter.h"
|
|
|
|
#include "UptimeMeter.h"
|
2020-10-14 18:21:09 +00:00
|
|
|
#include "XUtils.h"
|
2021-04-29 18:13:36 +00:00
|
|
|
#include "linux/IOPriority.h"
|
|
|
|
#include "linux/IOPriorityPanel.h"
|
|
|
|
#include "linux/LinuxProcess.h"
|
|
|
|
#include "linux/LinuxProcessList.h"
|
|
|
|
#include "linux/SystemdMeter.h"
|
|
|
|
#include "linux/ZramMeter.h"
|
|
|
|
#include "linux/ZramStats.h"
|
2020-09-19 11:55:23 +00:00
|
|
|
#include "zfs/ZfsArcMeter.h"
|
|
|
|
#include "zfs/ZfsArcStats.h"
|
|
|
|
#include "zfs/ZfsCompressedArcMeter.h"
|
2014-11-27 21:41:14 +00:00
|
|
|
|
2021-03-17 03:29:40 +00:00
|
|
|
#ifdef HAVE_LIBCAP
|
2021-04-29 15:12:43 +00:00
|
|
|
#include <errno.h>
|
2021-03-17 03:29:40 +00:00
|
|
|
#include <sys/capability.h>
|
|
|
|
#endif
|
|
|
|
|
2020-12-01 12:59:19 +00:00
|
|
|
#ifdef HAVE_SENSORS_SENSORS_H
|
|
|
|
#include "LibSensors.h"
|
2020-11-19 01:32:07 +00:00
|
|
|
#endif
|
2014-11-24 20:55:49 +00:00
|
|
|
|
2021-12-08 19:27:54 +00:00
|
|
|
#ifndef O_PATH
|
|
|
|
#define O_PATH 010000000 // declare for ancient glibc versions
|
|
|
|
#endif
|
|
|
|
|
2020-12-01 12:59:19 +00:00
|
|
|
|
2021-03-17 03:29:40 +00:00
|
|
|
#ifdef HAVE_LIBCAP
|
|
|
|
enum CapMode {
|
2021-03-25 13:33:27 +00:00
|
|
|
CAP_MODE_OFF,
|
2021-03-17 03:29:40 +00:00
|
|
|
CAP_MODE_BASIC,
|
|
|
|
CAP_MODE_STRICT
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2022-04-02 10:58:02 +00:00
|
|
|
bool Running_containerized = false;
|
|
|
|
|
2021-12-16 14:40:50 +00:00
|
|
|
const ScreenDefaults Platform_defaultScreens[] = {
|
2021-08-31 05:38:52 +00:00
|
|
|
{
|
|
|
|
.name = "Main",
|
2021-12-16 14:43:35 +00:00
|
|
|
.columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command",
|
2021-08-31 05:38:52 +00:00
|
|
|
.sortKey = "PERCENT_CPU",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "I/O",
|
2021-12-16 14:43:35 +00:00
|
|
|
.columns = "PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command",
|
2021-08-31 05:38:52 +00:00
|
|
|
.sortKey = "IO_RATE",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens);
|
2015-03-15 23:29:13 +00:00
|
|
|
|
2016-08-30 12:41:17 +00:00
|
|
|
const SignalItem Platform_signals[] = {
|
2015-10-06 06:02:49 +00:00
|
|
|
{ .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 SIGBUS", .number = 7 },
|
|
|
|
{ .name = " 8 SIGFPE", .number = 8 },
|
|
|
|
{ .name = " 9 SIGKILL", .number = 9 },
|
|
|
|
{ .name = "10 SIGUSR1", .number = 10 },
|
|
|
|
{ .name = "11 SIGSEGV", .number = 11 },
|
|
|
|
{ .name = "12 SIGUSR2", .number = 12 },
|
|
|
|
{ .name = "13 SIGPIPE", .number = 13 },
|
|
|
|
{ .name = "14 SIGALRM", .number = 14 },
|
|
|
|
{ .name = "15 SIGTERM", .number = 15 },
|
|
|
|
{ .name = "16 SIGSTKFLT", .number = 16 },
|
|
|
|
{ .name = "17 SIGCHLD", .number = 17 },
|
|
|
|
{ .name = "18 SIGCONT", .number = 18 },
|
|
|
|
{ .name = "19 SIGSTOP", .number = 19 },
|
|
|
|
{ .name = "20 SIGTSTP", .number = 20 },
|
|
|
|
{ .name = "21 SIGTTIN", .number = 21 },
|
|
|
|
{ .name = "22 SIGTTOU", .number = 22 },
|
|
|
|
{ .name = "23 SIGURG", .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 SIGIO", .number = 29 },
|
|
|
|
{ .name = "29 SIGPOLL", .number = 29 },
|
|
|
|
{ .name = "30 SIGPWR", .number = 30 },
|
|
|
|
{ .name = "31 SIGSYS", .number = 31 },
|
|
|
|
};
|
|
|
|
|
2020-09-28 19:14:50 +00:00
|
|
|
const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
|
2015-10-06 06:02:49 +00:00
|
|
|
|
2020-11-17 22:19:42 +00:00
|
|
|
static enum { BAT_PROC, BAT_SYS, BAT_ERR } Platform_Battery_method = BAT_PROC;
|
|
|
|
static time_t Platform_Battery_cacheTime;
|
2020-11-25 11:46:00 +00:00
|
|
|
static double Platform_Battery_cachePercent = NAN;
|
2020-11-17 22:19:42 +00:00
|
|
|
static ACPresence Platform_Battery_cacheIsOnAC;
|
|
|
|
|
2021-03-17 03:29:40 +00:00
|
|
|
#ifdef HAVE_LIBCAP
|
|
|
|
static enum CapMode Platform_capabilitiesMode = CAP_MODE_BASIC;
|
2020-11-19 01:32:07 +00:00
|
|
|
#endif
|
|
|
|
|
2015-01-22 01:27:31 +00:00
|
|
|
static Htop_Reaction Platform_actionSetIOPriority(State* st) {
|
2021-01-21 19:27:37 +00:00
|
|
|
if (Settings_isReadonly())
|
|
|
|
return HTOP_OK;
|
|
|
|
|
2021-02-05 13:12:49 +00:00
|
|
|
const LinuxProcess* p = (const LinuxProcess*) Panel_getSelected((Panel*)st->mainPanel);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (!p)
|
|
|
|
return HTOP_OK;
|
|
|
|
|
2020-08-25 10:01:54 +00:00
|
|
|
IOPriority ioprio1 = p->ioPriority;
|
|
|
|
Panel* ioprioPanel = IOPriorityPanel_new(ioprio1);
|
2021-08-09 23:06:22 +00:00
|
|
|
const void* set = Action_pickFromVector(st, ioprioPanel, 20, true);
|
2014-11-24 20:55:49 +00:00
|
|
|
if (set) {
|
2020-08-25 10:01:54 +00:00
|
|
|
IOPriority ioprio2 = IOPriorityPanel_getIOPriority(ioprioPanel);
|
2021-02-05 13:12:49 +00:00
|
|
|
bool ok = MainPanel_foreachProcess(st->mainPanel, LinuxProcess_setIOPriority, (Arg) { .i = ioprio2 }, NULL);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (!ok) {
|
2014-11-24 20:55:49 +00:00
|
|
|
beep();
|
2020-11-01 00:09:51 +00:00
|
|
|
}
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
|
|
|
Panel_delete((Object*)ioprioPanel);
|
|
|
|
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
|
|
|
}
|
|
|
|
|
2021-08-06 06:45:30 +00:00
|
|
|
static bool Platform_changeAutogroupPriority(MainPanel* panel, int delta) {
|
|
|
|
if (LinuxProcess_isAutogroupEnabled() == false) {
|
|
|
|
beep();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool anyTagged;
|
|
|
|
bool ok = MainPanel_foreachProcess(panel, LinuxProcess_changeAutogroupPriorityBy, (Arg) { .i = delta }, &anyTagged);
|
|
|
|
if (!ok)
|
|
|
|
beep();
|
|
|
|
return anyTagged;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Htop_Reaction Platform_actionHigherAutogroupPriority(State* st) {
|
|
|
|
if (Settings_isReadonly())
|
|
|
|
return HTOP_OK;
|
|
|
|
|
|
|
|
bool changed = Platform_changeAutogroupPriority(st->mainPanel, -1);
|
|
|
|
return changed ? HTOP_REFRESH : HTOP_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Htop_Reaction Platform_actionLowerAutogroupPriority(State* st) {
|
|
|
|
if (Settings_isReadonly())
|
|
|
|
return HTOP_OK;
|
|
|
|
|
|
|
|
bool changed = Platform_changeAutogroupPriority(st->mainPanel, 1);
|
|
|
|
return changed ? HTOP_REFRESH : HTOP_OK;
|
|
|
|
}
|
|
|
|
|
2014-11-24 20:55:49 +00:00
|
|
|
void Platform_setBindings(Htop_Action* keys) {
|
|
|
|
keys['i'] = Platform_actionSetIOPriority;
|
2021-08-06 06:45:30 +00:00
|
|
|
keys['{'] = Platform_actionLowerAutogroupPriority;
|
|
|
|
keys['}'] = Platform_actionHigherAutogroupPriority;
|
2021-08-09 06:23:09 +00:00
|
|
|
keys[KEY_F(19)] = Platform_actionLowerAutogroupPriority; // Shift-F7
|
|
|
|
keys[KEY_F(20)] = Platform_actionHigherAutogroupPriority; // Shift-F8
|
2014-11-24 20:55:49 +00:00
|
|
|
}
|
2014-11-27 21:18:14 +00:00
|
|
|
|
2020-10-05 11:19:50 +00:00
|
|
|
const MeterClass* const Platform_meterTypes[] = {
|
2014-11-27 21:18:14 +00:00
|
|
|
&CPUMeter_class,
|
|
|
|
&ClockMeter_class,
|
2020-10-05 11:52:58 +00:00
|
|
|
&DateMeter_class,
|
|
|
|
&DateTimeMeter_class,
|
2014-11-27 21:18:14 +00:00
|
|
|
&LoadAverageMeter_class,
|
|
|
|
&LoadMeter_class,
|
|
|
|
&MemoryMeter_class,
|
|
|
|
&SwapMeter_class,
|
2021-08-10 19:32:05 +00:00
|
|
|
&MemorySwapMeter_class,
|
2021-01-27 09:45:48 +00:00
|
|
|
&SysArchMeter_class,
|
2021-01-05 14:50:23 +00:00
|
|
|
&HugePageMeter_class,
|
2014-11-27 21:18:14 +00:00
|
|
|
&TasksMeter_class,
|
|
|
|
&UptimeMeter_class,
|
|
|
|
&BatteryMeter_class,
|
|
|
|
&HostnameMeter_class,
|
|
|
|
&AllCPUsMeter_class,
|
|
|
|
&AllCPUs2Meter_class,
|
2020-06-11 21:21:52 +00:00
|
|
|
&AllCPUs4Meter_class,
|
2020-09-24 19:56:40 +00:00
|
|
|
&AllCPUs8Meter_class,
|
2014-11-27 21:18:14 +00:00
|
|
|
&LeftCPUsMeter_class,
|
|
|
|
&RightCPUsMeter_class,
|
|
|
|
&LeftCPUs2Meter_class,
|
|
|
|
&RightCPUs2Meter_class,
|
2020-06-11 21:21:52 +00:00
|
|
|
&LeftCPUs4Meter_class,
|
|
|
|
&RightCPUs4Meter_class,
|
2020-09-24 19:56:40 +00:00
|
|
|
&LeftCPUs8Meter_class,
|
|
|
|
&RightCPUs8Meter_class,
|
2014-11-27 21:18:14 +00:00
|
|
|
&BlankMeter_class,
|
2020-08-20 03:59:41 +00:00
|
|
|
&PressureStallCPUSomeMeter_class,
|
|
|
|
&PressureStallIOSomeMeter_class,
|
|
|
|
&PressureStallIOFullMeter_class,
|
|
|
|
&PressureStallMemorySomeMeter_class,
|
|
|
|
&PressureStallMemoryFullMeter_class,
|
2019-07-07 02:37:02 +00:00
|
|
|
&ZfsArcMeter_class,
|
2019-09-03 18:26:02 +00:00
|
|
|
&ZfsCompressedArcMeter_class,
|
2020-09-22 11:54:15 +00:00
|
|
|
&ZramMeter_class,
|
2020-09-13 17:46:34 +00:00
|
|
|
&DiskIOMeter_class,
|
2020-10-08 14:34:54 +00:00
|
|
|
&NetworkIOMeter_class,
|
2020-10-07 15:18:02 +00:00
|
|
|
&SELinuxMeter_class,
|
2020-10-07 13:42:13 +00:00
|
|
|
&SystemdMeter_class,
|
2014-11-27 21:18:14 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2014-11-27 21:41:14 +00:00
|
|
|
int Platform_getUptime() {
|
|
|
|
double uptime = 0;
|
|
|
|
FILE* fd = fopen(PROCDIR "/uptime", "r");
|
|
|
|
if (fd) {
|
2015-02-23 06:34:06 +00:00
|
|
|
int n = fscanf(fd, "%64lf", &uptime);
|
2014-11-27 21:41:14 +00:00
|
|
|
fclose(fd);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (n <= 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
2014-11-27 21:41:14 +00:00
|
|
|
}
|
2020-09-23 11:52:49 +00:00
|
|
|
return floor(uptime);
|
2014-11-27 21:41:14 +00:00
|
|
|
}
|
2014-11-27 21:57:24 +00:00
|
|
|
|
|
|
|
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
|
2020-10-31 22:28:02 +00:00
|
|
|
FILE* fd = fopen(PROCDIR "/loadavg", "r");
|
2021-03-28 16:10:13 +00:00
|
|
|
if (!fd)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
double scanOne, scanFive, scanFifteen;
|
|
|
|
int r = fscanf(fd, "%lf %lf %lf", &scanOne, &scanFive, &scanFifteen);
|
|
|
|
fclose(fd);
|
|
|
|
if (r != 3)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
*one = scanOne;
|
|
|
|
*five = scanFive;
|
|
|
|
*fifteen = scanFifteen;
|
|
|
|
return;
|
|
|
|
|
2021-07-14 17:25:15 +00:00
|
|
|
err:
|
2021-03-28 16:10:13 +00:00
|
|
|
*one = NAN;
|
|
|
|
*five = NAN;
|
|
|
|
*fifteen = NAN;
|
2014-11-27 21:57:24 +00:00
|
|
|
}
|
|
|
|
|
2014-11-27 22:10:23 +00:00
|
|
|
int Platform_getMaxPid() {
|
|
|
|
FILE* file = fopen(PROCDIR "/sys/kernel/pid_max", "r");
|
2020-11-01 00:09:51 +00:00
|
|
|
if (!file)
|
|
|
|
return -1;
|
|
|
|
|
2014-11-27 22:10:23 +00:00
|
|
|
int maxPid = 4194303;
|
2015-05-15 09:33:25 +00:00
|
|
|
int match = fscanf(file, "%32d", &maxPid);
|
|
|
|
(void) match;
|
2014-11-27 22:10:23 +00:00
|
|
|
fclose(file);
|
|
|
|
return maxPid;
|
|
|
|
}
|
|
|
|
|
2021-02-17 16:38:35 +00:00
|
|
|
double Platform_setCPUValues(Meter* this, unsigned int cpu) {
|
2020-10-21 19:25:50 +00:00
|
|
|
const LinuxProcessList* pl = (const LinuxProcessList*) this->pl;
|
2021-06-12 16:17:28 +00:00
|
|
|
const CPUData* cpuData = &(pl->cpuData[cpu]);
|
2015-01-22 01:27:31 +00:00
|
|
|
double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod);
|
|
|
|
double percent;
|
|
|
|
double* v = this->values;
|
2021-06-12 16:17:28 +00:00
|
|
|
|
|
|
|
if (!cpuData->online) {
|
|
|
|
this->curItems = 0;
|
|
|
|
return NAN;
|
|
|
|
}
|
|
|
|
|
2015-10-23 15:46:21 +00:00
|
|
|
v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0;
|
|
|
|
v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0;
|
2015-01-22 01:27:31 +00:00
|
|
|
if (this->pl->settings->detailedCPUTime) {
|
2015-10-23 15:46:21 +00:00
|
|
|
v[CPU_METER_KERNEL] = cpuData->systemPeriod / total * 100.0;
|
|
|
|
v[CPU_METER_IRQ] = cpuData->irqPeriod / total * 100.0;
|
|
|
|
v[CPU_METER_SOFTIRQ] = cpuData->softIrqPeriod / total * 100.0;
|
|
|
|
v[CPU_METER_STEAL] = cpuData->stealPeriod / total * 100.0;
|
|
|
|
v[CPU_METER_GUEST] = cpuData->guestPeriod / total * 100.0;
|
|
|
|
v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0;
|
2020-10-04 15:55:08 +00:00
|
|
|
this->curItems = 8;
|
2015-01-22 01:27:31 +00:00
|
|
|
if (this->pl->settings->accountGuestInCPUMeter) {
|
2020-10-31 22:28:02 +00:00
|
|
|
percent = v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6];
|
2015-01-22 01:27:31 +00:00
|
|
|
} else {
|
2020-10-31 22:28:02 +00:00
|
|
|
percent = v[0] + v[1] + v[2] + v[3] + v[4];
|
2015-10-06 12:04:22 +00:00
|
|
|
}
|
2015-01-22 01:27:31 +00:00
|
|
|
} else {
|
|
|
|
v[2] = cpuData->systemAllPeriod / total * 100.0;
|
|
|
|
v[3] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0;
|
2020-10-04 15:55:08 +00:00
|
|
|
this->curItems = 4;
|
2020-10-31 22:28:02 +00:00
|
|
|
percent = v[0] + v[1] + v[2] + v[3];
|
2015-01-22 01:27:31 +00:00
|
|
|
}
|
Introduce CLAMP macro. Unify all MIN(MAX(a,b),c) uses.
With the CLAMP macro replacing the combination of MIN and MAX, we will
have at least two advantages:
1. It's more obvious semantically.
2. There are no more mixes of confusing uses like MIN(MAX(a,b),c) and
MAX(MIN(a,b),c) and MIN(a,MAX(b,c)) appearing everywhere. We unify
the 'clamping' with a single macro.
Note that the behavior of this CLAMP macro is different from
the combination `MAX(low,MIN(x,high))`.
* This CLAMP macro expands to two comparisons instead of three from
MAX and MIN combination. In theory, this makes the code slightly
smaller, in case that (low) or (high) or both are computed at
runtime, so that compilers cannot optimize them. (The third
comparison will matter if (low)>(high); see below.)
* CLAMP has a side effect, that if (low)>(high) it will produce weird
results. Unlike MIN & MAX which will force either (low) or (high) to
win. No assertion of ((low)<=(high)) is done in this macro, for now.
This CLAMP macro is implemented like described in glib
<http://developer.gnome.org/glib/stable/glib-Standard-Macros.html>
and does not handle weird uses like CLAMP(a++, low++, high--) .
2016-01-15 12:26:01 +00:00
|
|
|
percent = CLAMP(percent, 0.0, 100.0);
|
2020-11-01 00:09:51 +00:00
|
|
|
if (isnan(percent)) {
|
|
|
|
percent = 0.0;
|
|
|
|
}
|
2019-08-10 04:34:48 +00:00
|
|
|
|
2019-08-11 05:19:32 +00:00
|
|
|
v[CPU_METER_FREQUENCY] = cpuData->frequency;
|
2019-08-10 04:34:48 +00:00
|
|
|
|
2020-12-01 12:59:19 +00:00
|
|
|
#ifdef HAVE_SENSORS_SENSORS_H
|
2020-09-10 17:56:33 +00:00
|
|
|
v[CPU_METER_TEMPERATURE] = cpuData->temperature;
|
|
|
|
#else
|
|
|
|
v[CPU_METER_TEMPERATURE] = NAN;
|
|
|
|
#endif
|
|
|
|
|
2015-01-22 01:27:31 +00:00
|
|
|
return percent;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Platform_setMemoryValues(Meter* this) {
|
2020-10-21 19:25:50 +00:00
|
|
|
const ProcessList* pl = this->pl;
|
|
|
|
const LinuxProcessList* lpl = (const LinuxProcessList*) pl;
|
2020-09-24 18:13:25 +00:00
|
|
|
|
2021-10-07 05:52:25 +00:00
|
|
|
this->total = pl->totalMem;
|
|
|
|
this->values[0] = pl->usedMem;
|
2021-01-06 17:11:24 +00:00
|
|
|
this->values[1] = pl->buffersMem;
|
2021-03-17 15:32:16 +00:00
|
|
|
this->values[2] = pl->sharedMem;
|
|
|
|
this->values[3] = pl->cachedMem;
|
2021-03-03 18:48:30 +00:00
|
|
|
this->values[4] = pl->availableMem;
|
2020-09-24 18:13:25 +00:00
|
|
|
|
2022-04-02 10:58:02 +00:00
|
|
|
if (lpl->zfs.enabled != 0 && !Running_containerized) {
|
2020-09-24 18:13:25 +00:00
|
|
|
this->values[0] -= lpl->zfs.size;
|
2021-03-17 15:32:16 +00:00
|
|
|
this->values[3] += lpl->zfs.size;
|
2020-09-24 18:13:25 +00:00
|
|
|
}
|
2015-01-22 01:27:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Platform_setSwapValues(Meter* this) {
|
2020-10-21 19:25:50 +00:00
|
|
|
const ProcessList* pl = this->pl;
|
2015-01-22 01:27:31 +00:00
|
|
|
this->total = pl->totalSwap;
|
|
|
|
this->values[0] = pl->usedSwap;
|
2021-01-07 13:38:18 +00:00
|
|
|
this->values[1] = pl->cachedSwap;
|
2015-01-22 01:27:31 +00:00
|
|
|
}
|
2015-12-03 21:16:10 +00:00
|
|
|
|
2020-09-22 11:54:15 +00:00
|
|
|
void Platform_setZramValues(Meter* this) {
|
|
|
|
const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
|
|
|
|
this->total = lpl->zram.totalZram;
|
|
|
|
this->values[0] = lpl->zram.usedZramComp;
|
|
|
|
this->values[1] = lpl->zram.usedZramOrig;
|
|
|
|
}
|
|
|
|
|
2019-07-07 02:37:02 +00:00
|
|
|
void Platform_setZfsArcValues(Meter* this) {
|
2020-10-21 19:25:50 +00:00
|
|
|
const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
|
2019-07-07 02:37:02 +00:00
|
|
|
|
2019-09-03 18:21:33 +00:00
|
|
|
ZfsArcMeter_readStats(this, &(lpl->zfs));
|
2019-07-07 02:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-09-03 18:26:02 +00:00
|
|
|
void Platform_setZfsCompressedArcValues(Meter* this) {
|
2020-10-21 19:25:50 +00:00
|
|
|
const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
|
2019-09-03 18:26:02 +00:00
|
|
|
|
|
|
|
ZfsCompressedArcMeter_readStats(this, &(lpl->zfs));
|
|
|
|
}
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2015-12-03 21:16:10 +00:00
|
|
|
char* Platform_getProcessEnv(pid_t pid) {
|
2020-09-15 10:29:46 +00:00
|
|
|
char procname[128];
|
|
|
|
xSnprintf(procname, sizeof(procname), PROCDIR "/%d/environ", pid);
|
2015-12-03 21:16:10 +00:00
|
|
|
FILE* fd = fopen(procname, "r");
|
2020-10-31 19:52:20 +00:00
|
|
|
if (!fd)
|
2020-10-26 18:18:29 +00:00
|
|
|
return NULL;
|
|
|
|
|
2020-10-31 22:28:02 +00:00
|
|
|
char* env = NULL;
|
2020-10-26 18:18:29 +00:00
|
|
|
|
|
|
|
size_t capacity = 0;
|
|
|
|
size_t size = 0;
|
|
|
|
ssize_t bytes = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
size += bytes;
|
|
|
|
capacity += 4096;
|
|
|
|
env = xRealloc(env, capacity);
|
|
|
|
} while ((bytes = fread(env + size, 1, capacity - size, fd)) > 0);
|
|
|
|
|
|
|
|
fclose(fd);
|
|
|
|
|
|
|
|
if (bytes < 0) {
|
|
|
|
free(env);
|
|
|
|
return NULL;
|
2015-12-03 21:16:10 +00:00
|
|
|
}
|
2020-10-26 18:18:29 +00:00
|
|
|
|
|
|
|
size += bytes;
|
|
|
|
|
|
|
|
env = xRealloc(env, size + 2);
|
|
|
|
|
|
|
|
env[size] = '\0';
|
2020-10-31 22:28:02 +00:00
|
|
|
env[size + 1] = '\0';
|
2020-10-26 18:18:29 +00:00
|
|
|
|
2015-12-03 21:16:10 +00:00
|
|
|
return env;
|
|
|
|
}
|
2020-08-20 03:59:41 +00:00
|
|
|
|
2020-11-11 21:15:35 +00:00
|
|
|
/*
|
|
|
|
* Return the absolute path of a file given its pid&inode number
|
|
|
|
*
|
|
|
|
* Based on implementation of lslocks from util-linux:
|
|
|
|
* https://sources.debian.org/src/util-linux/2.36-3/misc-utils/lslocks.c/#L162
|
|
|
|
*/
|
|
|
|
char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
|
|
|
|
struct stat sb;
|
2021-07-14 17:24:18 +00:00
|
|
|
const struct dirent* de;
|
|
|
|
DIR* dirp;
|
2020-11-24 16:40:00 +00:00
|
|
|
ssize_t len;
|
2020-11-11 21:15:35 +00:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
char path[PATH_MAX];
|
|
|
|
char sym[PATH_MAX];
|
|
|
|
char* ret = NULL;
|
|
|
|
|
|
|
|
memset(path, 0, sizeof(path));
|
|
|
|
memset(sym, 0, sizeof(sym));
|
|
|
|
|
|
|
|
xSnprintf(path, sizeof(path), "%s/%d/fd/", PROCDIR, pid);
|
|
|
|
if (strlen(path) >= (sizeof(path) - 2))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(dirp = opendir(path)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if ((fd = dirfd(dirp)) < 0 )
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while ((de = readdir(dirp))) {
|
|
|
|
if (String_eq(de->d_name, ".") || String_eq(de->d_name, ".."))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* care only for numerical descriptors */
|
|
|
|
if (!strtoull(de->d_name, (char **) NULL, 10))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!Compat_fstatat(fd, path, de->d_name, &sb, 0) && inode != sb.st_ino)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((len = Compat_readlinkat(fd, path, de->d_name, sym, sizeof(sym) - 1)) < 1)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
sym[len] = '\0';
|
|
|
|
|
|
|
|
ret = xStrdup(sym);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
closedir(dirp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
|
|
|
|
FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData));
|
|
|
|
|
|
|
|
FILE* f = fopen(PROCDIR "/locks", "r");
|
|
|
|
if (!f) {
|
|
|
|
pdata->error = true;
|
|
|
|
return pdata;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buffer[1024];
|
|
|
|
FileLocks_LockData** data_ref = &pdata->locks;
|
|
|
|
while(fgets(buffer, sizeof(buffer), f)) {
|
|
|
|
if (!strchr(buffer, '\n'))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int lock_id;
|
|
|
|
char lock_type[16];
|
|
|
|
char lock_excl[16];
|
|
|
|
char lock_rw[16];
|
|
|
|
pid_t lock_pid;
|
|
|
|
unsigned int lock_dev[2];
|
|
|
|
uint64_t lock_inode;
|
|
|
|
char lock_start[25];
|
|
|
|
char lock_end[25];
|
|
|
|
|
|
|
|
if (10 != sscanf(buffer, "%d: %15s %15s %15s %d %x:%x:%"PRIu64" %24s %24s",
|
|
|
|
&lock_id, lock_type, lock_excl, lock_rw, &lock_pid,
|
|
|
|
&lock_dev[0], &lock_dev[1], &lock_inode,
|
|
|
|
lock_start, lock_end))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pid != lock_pid)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
FileLocks_LockData* ldata = xCalloc(1, sizeof(FileLocks_LockData));
|
|
|
|
FileLocks_Data* data = &ldata->data;
|
|
|
|
data->id = lock_id;
|
2020-11-13 20:34:21 +00:00
|
|
|
data->locktype = xStrdup(lock_type);
|
|
|
|
data->exclusive = xStrdup(lock_excl);
|
|
|
|
data->readwrite = xStrdup(lock_rw);
|
|
|
|
data->filename = Platform_getInodeFilename(lock_pid, lock_inode);
|
2020-11-11 21:15:35 +00:00
|
|
|
data->dev[0] = lock_dev[0];
|
|
|
|
data->dev[1] = lock_dev[1];
|
|
|
|
data->inode = lock_inode;
|
|
|
|
data->start = strtoull(lock_start, NULL, 10);
|
|
|
|
if (!String_eq(lock_end, "EOF")) {
|
|
|
|
data->end = strtoull(lock_end, NULL, 10);
|
|
|
|
} else {
|
|
|
|
data->end = ULLONG_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
*data_ref = ldata;
|
|
|
|
data_ref = &ldata->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
return pdata;
|
|
|
|
}
|
|
|
|
|
2020-10-31 22:28:02 +00:00
|
|
|
void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred) {
|
2020-08-20 03:59:41 +00:00
|
|
|
*ten = *sixty = *threehundred = 0;
|
2020-11-28 18:33:07 +00:00
|
|
|
char procname[128];
|
|
|
|
xSnprintf(procname, sizeof(procname), PROCDIR "/pressure/%s", file);
|
2020-10-31 22:28:02 +00:00
|
|
|
FILE* fd = fopen(procname, "r");
|
2020-08-20 03:59:41 +00:00
|
|
|
if (!fd) {
|
|
|
|
*ten = *sixty = *threehundred = NAN;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int total = fscanf(fd, "some avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred);
|
|
|
|
if (!some) {
|
|
|
|
total = fscanf(fd, "full avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred);
|
|
|
|
}
|
|
|
|
(void) total;
|
|
|
|
assert(total == 3);
|
|
|
|
fclose(fd);
|
|
|
|
}
|
2020-09-13 17:46:34 +00:00
|
|
|
|
2020-10-21 15:06:32 +00:00
|
|
|
bool Platform_getDiskIO(DiskIOData* data) {
|
2020-10-31 22:28:02 +00:00
|
|
|
FILE* fd = fopen(PROCDIR "/diskstats", "r");
|
2020-10-20 19:40:51 +00:00
|
|
|
if (!fd)
|
|
|
|
return false;
|
|
|
|
|
2021-08-21 19:47:19 +00:00
|
|
|
char lastTopDisk[32] = { '\0' };
|
|
|
|
|
2021-03-01 01:10:18 +00:00
|
|
|
unsigned long long int read_sum = 0, write_sum = 0, timeSpend_sum = 0;
|
2020-09-13 17:46:34 +00:00
|
|
|
char lineBuffer[256];
|
|
|
|
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
|
|
|
|
char diskname[32];
|
2021-03-01 01:10:18 +00:00
|
|
|
unsigned long long int read_tmp, write_tmp, timeSpend_tmp;
|
|
|
|
if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %llu %*u %*u %*u %llu %*u %*u %llu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4) {
|
2020-09-13 17:46:34 +00:00
|
|
|
if (String_startsWith(diskname, "dm-"))
|
|
|
|
continue;
|
|
|
|
|
2021-08-10 17:58:13 +00:00
|
|
|
if (String_startsWith(diskname, "zram"))
|
|
|
|
continue;
|
|
|
|
|
2020-09-13 17:46:34 +00:00
|
|
|
/* only count root disks, e.g. do not count IO from sda and sda1 twice */
|
2021-08-21 19:47:19 +00:00
|
|
|
if (lastTopDisk[0] && String_startsWith(diskname, lastTopDisk))
|
2020-09-13 17:46:34 +00:00
|
|
|
continue;
|
|
|
|
|
2021-08-21 19:47:19 +00:00
|
|
|
/* This assumes disks are listed directly before any of their partitions */
|
|
|
|
String_safeStrncpy(lastTopDisk, diskname, sizeof(lastTopDisk));
|
2020-09-13 17:46:34 +00:00
|
|
|
|
|
|
|
read_sum += read_tmp;
|
|
|
|
write_sum += write_tmp;
|
|
|
|
timeSpend_sum += timeSpend_tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(fd);
|
|
|
|
/* multiply with sector size */
|
2020-10-21 15:06:32 +00:00
|
|
|
data->totalBytesRead = 512 * read_sum;
|
|
|
|
data->totalBytesWritten = 512 * write_sum;
|
|
|
|
data->totalMsTimeSpend = timeSpend_sum;
|
2020-10-20 19:40:51 +00:00
|
|
|
return true;
|
2020-09-13 17:46:34 +00:00
|
|
|
}
|
2020-10-08 14:34:54 +00:00
|
|
|
|
2021-03-01 00:55:15 +00:00
|
|
|
bool Platform_getNetworkIO(NetworkIOData* data) {
|
2020-10-31 22:28:02 +00:00
|
|
|
FILE* fd = fopen(PROCDIR "/net/dev", "r");
|
2020-10-20 19:40:51 +00:00
|
|
|
if (!fd)
|
|
|
|
return false;
|
2020-10-08 14:34:54 +00:00
|
|
|
|
2021-03-01 00:55:15 +00:00
|
|
|
memset(data, 0, sizeof(NetworkIOData));
|
2020-10-08 14:34:54 +00:00
|
|
|
char lineBuffer[512];
|
|
|
|
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
|
|
|
|
char interfaceName[32];
|
2021-03-01 00:55:15 +00:00
|
|
|
unsigned long long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
|
|
|
|
if (sscanf(lineBuffer, "%31s %llu %llu %*u %*u %*u %*u %*u %*u %llu %llu",
|
2020-10-20 19:40:51 +00:00
|
|
|
interfaceName,
|
2021-03-01 00:55:15 +00:00
|
|
|
&bytesReceived,
|
|
|
|
&packetsReceived,
|
|
|
|
&bytesTransmitted,
|
|
|
|
&packetsTransmitted) != 5)
|
2020-10-08 14:34:54 +00:00
|
|
|
continue;
|
|
|
|
|
2020-10-03 19:20:43 +00:00
|
|
|
if (String_eq(interfaceName, "lo:"))
|
2020-10-08 14:34:54 +00:00
|
|
|
continue;
|
|
|
|
|
2021-03-01 00:55:15 +00:00
|
|
|
data->bytesReceived += bytesReceived;
|
|
|
|
data->packetsReceived += packetsReceived;
|
|
|
|
data->bytesTransmitted += bytesTransmitted;
|
|
|
|
data->packetsTransmitted += packetsTransmitted;
|
2020-10-08 14:34:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fd);
|
|
|
|
|
2020-10-20 19:40:51 +00:00
|
|
|
return true;
|
2020-10-08 14:34:54 +00:00
|
|
|
}
|
2020-11-17 07:12:38 +00:00
|
|
|
|
|
|
|
// Linux battery reading by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
|
|
|
|
|
|
|
|
#define PROC_BATTERY_DIR PROCDIR "/acpi/battery"
|
|
|
|
#define PROC_POWERSUPPLY_DIR PROCDIR "/acpi/ac_adapter"
|
2021-10-06 10:47:59 +00:00
|
|
|
#define PROC_POWERSUPPLY_ACSTATE_FILE PROC_POWERSUPPLY_DIR "/AC/state"
|
2020-11-17 07:12:38 +00:00
|
|
|
#define SYS_POWERSUPPLY_DIR "/sys/class/power_supply"
|
|
|
|
|
|
|
|
// ----------------------------------------
|
|
|
|
// READ FROM /proc
|
|
|
|
// ----------------------------------------
|
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
static double Platform_Battery_getProcBatInfo(void) {
|
|
|
|
DIR* batteryDir = opendir(PROC_BATTERY_DIR);
|
2020-11-17 07:12:38 +00:00
|
|
|
if (!batteryDir)
|
2021-10-06 10:47:59 +00:00
|
|
|
return NAN;
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
uint64_t totalFull = 0;
|
|
|
|
uint64_t totalRemain = 0;
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
struct dirent* dirEntry = NULL;
|
|
|
|
while ((dirEntry = readdir(batteryDir))) {
|
2021-01-05 22:42:55 +00:00
|
|
|
const char* entryName = dirEntry->d_name;
|
2020-11-17 07:12:38 +00:00
|
|
|
if (!String_startsWith(entryName, "BAT"))
|
|
|
|
continue;
|
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
char filePath[256];
|
|
|
|
char bufInfo[1024] = {0};
|
|
|
|
xSnprintf(filePath, sizeof(filePath), "%s/%s/info", PROC_BATTERY_DIR, entryName);
|
|
|
|
ssize_t r = xReadfile(filePath, bufInfo, sizeof(bufInfo));
|
|
|
|
if (r < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
char bufState[1024] = {0};
|
|
|
|
xSnprintf(filePath, sizeof(filePath), "%s/%s/state", PROC_BATTERY_DIR, entryName);
|
|
|
|
r = xReadfile(filePath, bufState, sizeof(bufState));
|
|
|
|
if (r < 0)
|
|
|
|
continue;
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
const char* line;
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
//Getting total charge for all batteries
|
|
|
|
char* buf = bufInfo;
|
|
|
|
while ((line = strsep(&buf, "\n")) != NULL) {
|
|
|
|
char field[100] = {0};
|
|
|
|
int val = 0;
|
|
|
|
if (2 != sscanf(line, "%99[^:]:%d", field, &val))
|
|
|
|
continue;
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
if (String_eq(field, "last full capacity")) {
|
|
|
|
totalFull += val;
|
2020-11-17 07:12:38 +00:00
|
|
|
break;
|
2021-10-06 10:47:59 +00:00
|
|
|
}
|
2020-11-17 07:12:38 +00:00
|
|
|
}
|
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
//Getting remaining charge for all batteries
|
|
|
|
buf = bufState;
|
|
|
|
while ((line = strsep(&buf, "\n")) != NULL) {
|
|
|
|
char field[100] = {0};
|
|
|
|
int val = 0;
|
|
|
|
if (2 != sscanf(line, "%99[^:]:%d", field, &val))
|
|
|
|
continue;
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
if (String_eq(field, "remaining capacity")) {
|
|
|
|
totalRemain += val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-11-17 07:12:38 +00:00
|
|
|
}
|
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
closedir(batteryDir);
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
return totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
|
2020-11-17 07:12:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ACPresence procAcpiCheck(void) {
|
2021-10-06 10:47:59 +00:00
|
|
|
char buffer[1024] = {0};
|
|
|
|
ssize_t r = xReadfile(PROC_POWERSUPPLY_ACSTATE_FILE, buffer, sizeof(buffer));
|
|
|
|
if (r < 1)
|
2020-11-17 07:12:38 +00:00
|
|
|
return AC_ERROR;
|
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
return String_eq(buffer, "on-line") ? AC_PRESENT : AC_ABSENT;
|
2020-11-17 07:12:38 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 11:46:00 +00:00
|
|
|
static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
|
2020-11-17 07:12:38 +00:00
|
|
|
*isOnAC = procAcpiCheck();
|
2020-11-25 11:46:00 +00:00
|
|
|
*percent = AC_ERROR != *isOnAC ? Platform_Battery_getProcBatInfo() : NAN;
|
2020-11-17 07:12:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------
|
|
|
|
// READ FROM /sys
|
|
|
|
// ----------------------------------------
|
|
|
|
|
2020-11-25 11:46:00 +00:00
|
|
|
static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
|
|
|
|
*percent = NAN;
|
2020-11-17 07:12:38 +00:00
|
|
|
*isOnAC = AC_ERROR;
|
|
|
|
|
|
|
|
DIR* dir = opendir(SYS_POWERSUPPLY_DIR);
|
|
|
|
if (!dir)
|
|
|
|
return;
|
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
uint64_t totalFull = 0;
|
|
|
|
uint64_t totalRemain = 0;
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-12-08 19:27:54 +00:00
|
|
|
const struct dirent* dirEntry;
|
2021-10-06 10:47:59 +00:00
|
|
|
while ((dirEntry = readdir(dir))) {
|
2020-11-17 07:12:38 +00:00
|
|
|
const char* entryName = dirEntry->d_name;
|
|
|
|
|
2021-12-08 19:27:54 +00:00
|
|
|
#ifdef HAVE_OPENAT
|
|
|
|
int entryFd = openat(dirfd(dir), entryName, O_DIRECTORY | O_PATH);
|
|
|
|
if (entryFd < 0)
|
|
|
|
continue;
|
|
|
|
#else
|
|
|
|
char entryFd[4096];
|
|
|
|
xSnprintf(entryFd, sizeof(entryFd), SYS_POWERSUPPLY_DIR "/%s", entryName);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
enum { AC, BAT } type;
|
2021-10-06 10:47:59 +00:00
|
|
|
if (String_startsWith(entryName, "BAT")) {
|
2021-12-08 19:27:54 +00:00
|
|
|
type = BAT;
|
|
|
|
} else if (String_startsWith(entryName, "AC")) {
|
|
|
|
type = AC;
|
|
|
|
} else {
|
|
|
|
char buffer[32];
|
|
|
|
ssize_t ret = xReadfileat(entryFd, "type", buffer, sizeof(buffer));
|
|
|
|
if (ret <= 0)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
/* drop optional trailing newlines */
|
|
|
|
for (char* buf = &buffer[(size_t)ret - 1]; *buf == '\n'; buf--)
|
|
|
|
*buf = '\0';
|
|
|
|
|
|
|
|
if (String_eq(buffer, "Battery"))
|
|
|
|
type = BAT;
|
|
|
|
else if (String_eq(buffer, "Mains"))
|
|
|
|
type = AC;
|
|
|
|
else
|
|
|
|
goto next;
|
|
|
|
}
|
2020-11-25 11:42:36 +00:00
|
|
|
|
2021-12-08 19:27:54 +00:00
|
|
|
if (type == BAT) {
|
|
|
|
char buffer[1024];
|
|
|
|
ssize_t r = xReadfileat(entryFd, "uevent", buffer, sizeof(buffer));
|
2021-10-06 10:47:59 +00:00
|
|
|
if (r < 0)
|
2021-12-08 19:27:54 +00:00
|
|
|
goto next;
|
2020-11-25 11:42:36 +00:00
|
|
|
|
2020-11-17 07:12:38 +00:00
|
|
|
bool full = false;
|
|
|
|
bool now = false;
|
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
double fullCharge = 0;
|
|
|
|
double capacityLevel = NAN;
|
|
|
|
const char* line;
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-10-06 10:47:59 +00:00
|
|
|
char* buf = buffer;
|
2020-11-17 07:12:38 +00:00
|
|
|
while ((line = strsep(&buf, "\n")) != NULL) {
|
2021-10-06 10:47:59 +00:00
|
|
|
char field[100] = {0};
|
|
|
|
int val = 0;
|
|
|
|
if (2 != sscanf(line, "POWER_SUPPLY_%99[^=]=%d", field, &val))
|
2020-11-17 07:12:38 +00:00
|
|
|
continue;
|
2021-10-06 10:47:59 +00:00
|
|
|
|
|
|
|
if (String_eq(field, "CAPACITY")) {
|
|
|
|
capacityLevel = val / 100.0;
|
2020-11-17 07:12:38 +00:00
|
|
|
continue;
|
2021-10-06 10:47:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (String_eq(field, "ENERGY_FULL") || String_eq(field, "CHARGE_FULL")) {
|
|
|
|
fullCharge = val;
|
|
|
|
totalFull += fullCharge;
|
2020-11-17 07:12:38 +00:00
|
|
|
full = true;
|
|
|
|
if (now)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
2021-10-06 10:47:59 +00:00
|
|
|
|
|
|
|
if (String_eq(field, "ENERGY_NOW") || String_eq(field, "CHARGE_NOW")) {
|
|
|
|
totalRemain += val;
|
2020-11-17 07:12:38 +00:00
|
|
|
now = true;
|
|
|
|
if (full)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!now && full && !isnan(capacityLevel))
|
2021-10-06 10:47:59 +00:00
|
|
|
totalRemain += capacityLevel * fullCharge;
|
2020-11-17 07:12:38 +00:00
|
|
|
|
2021-12-08 19:27:54 +00:00
|
|
|
} else if (type == AC) {
|
2020-11-17 07:12:38 +00:00
|
|
|
if (*isOnAC != AC_ERROR)
|
2021-12-08 19:27:54 +00:00
|
|
|
goto next;
|
2020-11-25 11:42:36 +00:00
|
|
|
|
2021-12-08 19:27:54 +00:00
|
|
|
char buffer[2];
|
|
|
|
ssize_t r = xReadfileat(entryFd, "online", buffer, sizeof(buffer));
|
2020-11-25 11:42:36 +00:00
|
|
|
if (r < 1) {
|
2021-10-06 10:47:59 +00:00
|
|
|
*isOnAC = AC_ERROR;
|
2021-12-08 19:27:54 +00:00
|
|
|
goto next;
|
2020-11-17 07:12:38 +00:00
|
|
|
}
|
2020-11-25 11:42:36 +00:00
|
|
|
|
2020-11-17 07:12:38 +00:00
|
|
|
if (buffer[0] == '0')
|
|
|
|
*isOnAC = AC_ABSENT;
|
|
|
|
else if (buffer[0] == '1')
|
|
|
|
*isOnAC = AC_PRESENT;
|
|
|
|
}
|
2021-12-08 19:27:54 +00:00
|
|
|
|
|
|
|
next:
|
|
|
|
Compat_openatArgClose(entryFd);
|
2020-11-17 07:12:38 +00:00
|
|
|
}
|
2021-10-06 10:47:59 +00:00
|
|
|
|
2020-11-17 07:12:38 +00:00
|
|
|
closedir(dir);
|
|
|
|
|
2020-11-25 11:46:00 +00:00
|
|
|
*percent = totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
|
2020-11-17 07:12:38 +00:00
|
|
|
}
|
|
|
|
|
2020-11-25 11:46:00 +00:00
|
|
|
void Platform_getBattery(double* percent, ACPresence* isOnAC) {
|
2020-11-17 07:12:38 +00:00
|
|
|
time_t now = time(NULL);
|
|
|
|
// update battery reading is slow. Update it each 10 seconds only.
|
|
|
|
if (now < Platform_Battery_cacheTime + 10) {
|
2020-11-25 11:46:00 +00:00
|
|
|
*percent = Platform_Battery_cachePercent;
|
2020-11-17 07:12:38 +00:00
|
|
|
*isOnAC = Platform_Battery_cacheIsOnAC;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Platform_Battery_method == BAT_PROC) {
|
2020-11-25 11:46:00 +00:00
|
|
|
Platform_Battery_getProcData(percent, isOnAC);
|
|
|
|
if (isnan(*percent))
|
2020-11-17 07:12:38 +00:00
|
|
|
Platform_Battery_method = BAT_SYS;
|
|
|
|
}
|
|
|
|
if (Platform_Battery_method == BAT_SYS) {
|
2020-11-25 11:46:00 +00:00
|
|
|
Platform_Battery_getSysData(percent, isOnAC);
|
|
|
|
if (isnan(*percent))
|
2020-11-17 07:12:38 +00:00
|
|
|
Platform_Battery_method = BAT_ERR;
|
|
|
|
}
|
|
|
|
if (Platform_Battery_method == BAT_ERR) {
|
2020-11-25 11:46:00 +00:00
|
|
|
*percent = NAN;
|
2020-11-17 07:12:38 +00:00
|
|
|
*isOnAC = AC_ERROR;
|
|
|
|
} else {
|
2020-11-25 11:46:00 +00:00
|
|
|
*percent = CLAMP(*percent, 0.0, 100.0);
|
2020-11-17 07:12:38 +00:00
|
|
|
}
|
2020-11-25 11:46:00 +00:00
|
|
|
Platform_Battery_cachePercent = *percent;
|
2020-11-17 07:12:38 +00:00
|
|
|
Platform_Battery_cacheIsOnAC = *isOnAC;
|
|
|
|
Platform_Battery_cacheTime = now;
|
|
|
|
}
|
2021-03-17 03:29:40 +00:00
|
|
|
|
2021-03-22 03:49:07 +00:00
|
|
|
void Platform_longOptionsUsage(const char* name)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_LIBCAP
|
|
|
|
printf(
|
2021-03-25 13:33:27 +00:00
|
|
|
" --drop-capabilities[=off|basic|strict] Drop Linux capabilities when running as root\n"
|
|
|
|
" off - do not drop any capabilities\n"
|
2021-03-22 03:49:07 +00:00
|
|
|
" basic (default) - drop all capabilities not needed by %s\n"
|
|
|
|
" strict - drop all capabilities except those needed for\n"
|
|
|
|
" core functionality\n", name);
|
|
|
|
#else
|
|
|
|
(void) name;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-10-06 07:45:07 +00:00
|
|
|
CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv) {
|
2021-03-17 03:29:40 +00:00
|
|
|
#ifndef HAVE_LIBCAP
|
|
|
|
(void) argc;
|
|
|
|
(void) argv;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch (opt) {
|
|
|
|
#ifdef HAVE_LIBCAP
|
2021-01-21 19:27:37 +00:00
|
|
|
case 160: {
|
2021-03-17 03:29:40 +00:00
|
|
|
const char* mode = optarg;
|
|
|
|
if (!mode && optind < argc && argv[optind] != NULL &&
|
|
|
|
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
|
|
|
|
mode = argv[optind++];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mode || String_eq(mode, "basic")) {
|
|
|
|
Platform_capabilitiesMode = CAP_MODE_BASIC;
|
2021-03-25 13:33:27 +00:00
|
|
|
} else if (String_eq(mode, "off")) {
|
|
|
|
Platform_capabilitiesMode = CAP_MODE_OFF;
|
2021-03-17 03:29:40 +00:00
|
|
|
} else if (String_eq(mode, "strict")) {
|
|
|
|
Platform_capabilitiesMode = CAP_MODE_STRICT;
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
|
2021-10-06 07:45:07 +00:00
|
|
|
return STATUS_ERROR_EXIT;
|
2021-03-17 03:29:40 +00:00
|
|
|
}
|
2021-10-06 07:45:07 +00:00
|
|
|
return STATUS_OK;
|
2021-03-17 03:29:40 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2021-10-06 07:45:07 +00:00
|
|
|
return STATUS_ERROR_EXIT;
|
2021-03-17 03:29:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBCAP
|
|
|
|
static int dropCapabilities(enum CapMode mode) {
|
|
|
|
|
2021-03-25 13:33:27 +00:00
|
|
|
if (mode == CAP_MODE_OFF)
|
2021-03-17 03:29:40 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* capabilities we keep to operate */
|
|
|
|
const cap_value_t keepcapsStrict[] = {
|
|
|
|
CAP_DAC_READ_SEARCH,
|
|
|
|
CAP_SYS_PTRACE,
|
|
|
|
};
|
|
|
|
const cap_value_t keepcapsBasic[] = {
|
|
|
|
CAP_DAC_READ_SEARCH, /* read non world-readable process files of other users, like /proc/[pid]/io */
|
|
|
|
CAP_KILL, /* send signals to processes of other users */
|
|
|
|
CAP_SYS_NICE, /* lower process nice value / change nice value for arbitrary processes */
|
|
|
|
CAP_SYS_PTRACE, /* read /proc/[pid]/exe */
|
|
|
|
#ifdef HAVE_DELAYACCT
|
|
|
|
CAP_NET_ADMIN, /* communicate over netlink socket for delay accounting */
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
const cap_value_t* const keepcaps = (mode == CAP_MODE_BASIC) ? keepcapsBasic : keepcapsStrict;
|
|
|
|
const size_t ncap = (mode == CAP_MODE_BASIC) ? ARRAYSIZE(keepcapsBasic) : ARRAYSIZE(keepcapsStrict);
|
|
|
|
|
|
|
|
cap_t caps = cap_init();
|
|
|
|
if (caps == NULL) {
|
|
|
|
fprintf(stderr, "Error: can not initialize capabilities: %s\n", strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cap_clear(caps) < 0) {
|
|
|
|
fprintf(stderr, "Error: can not clear capabilities: %s\n", strerror(errno));
|
|
|
|
cap_free(caps);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cap_t currCaps = cap_get_proc();
|
|
|
|
if (currCaps == NULL) {
|
|
|
|
fprintf(stderr, "Error: can not get current process capabilities: %s\n", strerror(errno));
|
|
|
|
cap_free(caps);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < ncap; i++) {
|
|
|
|
if (!CAP_IS_SUPPORTED(keepcaps[i]))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
cap_flag_value_t current;
|
|
|
|
if (cap_get_flag(currCaps, keepcaps[i], CAP_PERMITTED, ¤t) < 0) {
|
|
|
|
fprintf(stderr, "Error: can not get current value of capability %d: %s\n", keepcaps[i], strerror(errno));
|
|
|
|
cap_free(currCaps);
|
|
|
|
cap_free(caps);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (current != CAP_SET)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (cap_set_flag(caps, CAP_PERMITTED, 1, &keepcaps[i], CAP_SET) < 0) {
|
|
|
|
fprintf(stderr, "Error: can not set permitted capability %d: %s\n", keepcaps[i], strerror(errno));
|
|
|
|
cap_free(currCaps);
|
|
|
|
cap_free(caps);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &keepcaps[i], CAP_SET) < 0) {
|
|
|
|
fprintf(stderr, "Error: can not set effective capability %d: %s\n", keepcaps[i], strerror(errno));
|
|
|
|
cap_free(currCaps);
|
|
|
|
cap_free(caps);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cap_set_proc(caps) < 0) {
|
|
|
|
fprintf(stderr, "Error: can not set process capabilities: %s\n", strerror(errno));
|
|
|
|
cap_free(currCaps);
|
|
|
|
cap_free(caps);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cap_free(currCaps);
|
|
|
|
cap_free(caps);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-10-06 07:45:07 +00:00
|
|
|
bool Platform_init(void) {
|
2021-03-17 03:29:40 +00:00
|
|
|
#ifdef HAVE_LIBCAP
|
|
|
|
if (dropCapabilities(Platform_capabilitiesMode) < 0)
|
2021-10-06 07:45:07 +00:00
|
|
|
return false;
|
2021-03-17 03:29:40 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (access(PROCDIR, R_OK) != 0) {
|
|
|
|
fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
|
2021-10-06 07:45:07 +00:00
|
|
|
return false;
|
2021-03-17 03:29:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_SENSORS_SENSORS_H
|
2021-06-12 16:17:28 +00:00
|
|
|
LibSensors_init();
|
2021-03-17 03:29:40 +00:00
|
|
|
#endif
|
2021-10-06 07:45:07 +00:00
|
|
|
|
2022-04-02 10:58:02 +00:00
|
|
|
char target[PATH_MAX];
|
|
|
|
ssize_t ret = readlink(PROCDIR "/self/ns/pid", target, sizeof(target) - 1);
|
|
|
|
if (ret > 0) {
|
|
|
|
target[ret] = '\0';
|
|
|
|
|
|
|
|
if (!String_eq("pid:[4026531836]", target)) { // magic constant PROC_PID_INIT_INO from include/linux/proc_ns.h#L46
|
|
|
|
Running_containerized = true;
|
|
|
|
return true; // early return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE* fd = fopen(PROCDIR "/1/mounts", "r");
|
|
|
|
if (fd) {
|
|
|
|
char lineBuffer[256];
|
|
|
|
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
|
|
|
|
// detect lxc or overlayfs and guess that this means we are running containerized
|
2022-05-08 18:46:53 +00:00
|
|
|
if (String_startsWith(lineBuffer, "lxcfs /proc") || String_startsWith(lineBuffer, "overlay ")) {
|
2022-04-02 10:58:02 +00:00
|
|
|
Running_containerized = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(fd);
|
|
|
|
} // if (fd)
|
|
|
|
|
2021-10-06 07:45:07 +00:00
|
|
|
return true;
|
2021-03-17 03:29:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Platform_done(void) {
|
|
|
|
#ifdef HAVE_SENSORS_SENSORS_H
|
|
|
|
LibSensors_cleanup();
|
|
|
|
#endif
|
|
|
|
}
|