Platform.c 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102
  1. /*
  2. htop - linux/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. #include "config.h" // IWYU pragma: keep
  8. #include "linux/Platform.h"
  9. #include <assert.h>
  10. #include <dirent.h>
  11. #include <errno.h>
  12. #include <fcntl.h>
  13. #include <inttypes.h>
  14. #include <math.h>
  15. #include <stdint.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <time.h>
  20. #include <unistd.h>
  21. #include <sys/sysmacros.h>
  22. #include "BatteryMeter.h"
  23. #include "ClockMeter.h"
  24. #include "Compat.h"
  25. #include "CPUMeter.h"
  26. #include "DateMeter.h"
  27. #include "DateTimeMeter.h"
  28. #include "DiskIOMeter.h"
  29. #include "FileDescriptorMeter.h"
  30. #include "HostnameMeter.h"
  31. #include "HugePageMeter.h"
  32. #include "LoadAverageMeter.h"
  33. #include "Machine.h"
  34. #include "Macros.h"
  35. #include "MainPanel.h"
  36. #include "Meter.h"
  37. #include "MemoryMeter.h"
  38. #include "MemorySwapMeter.h"
  39. #include "NetworkIOMeter.h"
  40. #include "Object.h"
  41. #include "Panel.h"
  42. #include "PressureStallMeter.h"
  43. #include "ProvideCurses.h"
  44. #include "Settings.h"
  45. #include "SwapMeter.h"
  46. #include "SysArchMeter.h"
  47. #include "TasksMeter.h"
  48. #include "UptimeMeter.h"
  49. #include "XUtils.h"
  50. #include "linux/GPUMeter.h"
  51. #include "linux/IOPriority.h"
  52. #include "linux/IOPriorityPanel.h"
  53. #include "linux/LinuxMachine.h"
  54. #include "linux/LinuxProcess.h"
  55. #include "linux/SELinuxMeter.h"
  56. #include "linux/SystemdMeter.h"
  57. #include "linux/ZramMeter.h"
  58. #include "linux/ZramStats.h"
  59. #include "linux/ZswapStats.h"
  60. #include "zfs/ZfsArcMeter.h"
  61. #include "zfs/ZfsArcStats.h"
  62. #include "zfs/ZfsCompressedArcMeter.h"
  63. #ifdef HAVE_LIBCAP
  64. #include <sys/capability.h>
  65. #endif
  66. #ifdef HAVE_SENSORS_SENSORS_H
  67. #include "LibSensors.h"
  68. #endif
  69. #ifndef O_PATH
  70. #define O_PATH 010000000 // declare for ancient glibc versions
  71. #endif
  72. #ifdef HAVE_LIBCAP
  73. enum CapMode {
  74. CAP_MODE_OFF,
  75. CAP_MODE_BASIC,
  76. CAP_MODE_STRICT
  77. };
  78. #endif
  79. bool Running_containerized = false;
  80. const ScreenDefaults Platform_defaultScreens[] = {
  81. {
  82. .name = "Main",
  83. .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command",
  84. .sortKey = "PERCENT_CPU",
  85. },
  86. {
  87. .name = "I/O",
  88. .columns = "PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command",
  89. .sortKey = "IO_RATE",
  90. },
  91. };
  92. const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens);
  93. const SignalItem Platform_signals[] = {
  94. { .name = " 0 Cancel", .number = 0 },
  95. { .name = " 1 SIGHUP", .number = 1 },
  96. { .name = " 2 SIGINT", .number = 2 },
  97. { .name = " 3 SIGQUIT", .number = 3 },
  98. { .name = " 4 SIGILL", .number = 4 },
  99. { .name = " 5 SIGTRAP", .number = 5 },
  100. { .name = " 6 SIGABRT", .number = 6 },
  101. { .name = " 6 SIGIOT", .number = 6 },
  102. { .name = " 7 SIGBUS", .number = 7 },
  103. { .name = " 8 SIGFPE", .number = 8 },
  104. { .name = " 9 SIGKILL", .number = 9 },
  105. { .name = "10 SIGUSR1", .number = 10 },
  106. { .name = "11 SIGSEGV", .number = 11 },
  107. { .name = "12 SIGUSR2", .number = 12 },
  108. { .name = "13 SIGPIPE", .number = 13 },
  109. { .name = "14 SIGALRM", .number = 14 },
  110. { .name = "15 SIGTERM", .number = 15 },
  111. { .name = "16 SIGSTKFLT", .number = 16 },
  112. { .name = "17 SIGCHLD", .number = 17 },
  113. { .name = "18 SIGCONT", .number = 18 },
  114. { .name = "19 SIGSTOP", .number = 19 },
  115. { .name = "20 SIGTSTP", .number = 20 },
  116. { .name = "21 SIGTTIN", .number = 21 },
  117. { .name = "22 SIGTTOU", .number = 22 },
  118. { .name = "23 SIGURG", .number = 23 },
  119. { .name = "24 SIGXCPU", .number = 24 },
  120. { .name = "25 SIGXFSZ", .number = 25 },
  121. { .name = "26 SIGVTALRM", .number = 26 },
  122. { .name = "27 SIGPROF", .number = 27 },
  123. { .name = "28 SIGWINCH", .number = 28 },
  124. { .name = "29 SIGIO", .number = 29 },
  125. { .name = "29 SIGPOLL", .number = 29 },
  126. { .name = "30 SIGPWR", .number = 30 },
  127. { .name = "31 SIGSYS", .number = 31 },
  128. };
  129. const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
  130. static enum { BAT_PROC, BAT_SYS, BAT_ERR } Platform_Battery_method = BAT_PROC;
  131. static time_t Platform_Battery_cacheTime;
  132. static double Platform_Battery_cachePercent = NAN;
  133. static ACPresence Platform_Battery_cacheIsOnAC;
  134. #ifdef HAVE_LIBCAP
  135. static enum CapMode Platform_capabilitiesMode = CAP_MODE_BASIC;
  136. #endif
  137. static Htop_Reaction Platform_actionSetIOPriority(State* st) {
  138. if (Settings_isReadonly())
  139. return HTOP_OK;
  140. const LinuxProcess* p = (const LinuxProcess*) Panel_getSelected((Panel*)st->mainPanel);
  141. if (!p)
  142. return HTOP_OK;
  143. IOPriority ioprio1 = p->ioPriority;
  144. Panel* ioprioPanel = IOPriorityPanel_new(ioprio1);
  145. const void* set = Action_pickFromVector(st, ioprioPanel, 20, true);
  146. if (set) {
  147. IOPriority ioprio2 = IOPriorityPanel_getIOPriority(ioprioPanel);
  148. bool ok = MainPanel_foreachRow(st->mainPanel, LinuxProcess_rowSetIOPriority, (Arg) { .i = ioprio2 }, NULL);
  149. if (!ok) {
  150. beep();
  151. }
  152. }
  153. Panel_delete((Object*)ioprioPanel);
  154. return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
  155. }
  156. static bool Platform_changeAutogroupPriority(MainPanel* panel, int delta) {
  157. if (LinuxProcess_isAutogroupEnabled() == false) {
  158. beep();
  159. return false;
  160. }
  161. bool anyTagged;
  162. bool ok = MainPanel_foreachRow(panel, LinuxProcess_rowChangeAutogroupPriorityBy, (Arg) { .i = delta }, &anyTagged);
  163. if (!ok)
  164. beep();
  165. return anyTagged;
  166. }
  167. static Htop_Reaction Platform_actionHigherAutogroupPriority(State* st) {
  168. if (Settings_isReadonly())
  169. return HTOP_OK;
  170. bool changed = Platform_changeAutogroupPriority(st->mainPanel, -1);
  171. return changed ? HTOP_REFRESH : HTOP_OK;
  172. }
  173. static Htop_Reaction Platform_actionLowerAutogroupPriority(State* st) {
  174. if (Settings_isReadonly())
  175. return HTOP_OK;
  176. bool changed = Platform_changeAutogroupPriority(st->mainPanel, 1);
  177. return changed ? HTOP_REFRESH : HTOP_OK;
  178. }
  179. void Platform_setBindings(Htop_Action* keys) {
  180. keys['i'] = Platform_actionSetIOPriority;
  181. keys['{'] = Platform_actionLowerAutogroupPriority;
  182. keys['}'] = Platform_actionHigherAutogroupPriority;
  183. keys[KEY_F(19)] = Platform_actionLowerAutogroupPriority; // Shift-F7
  184. keys[KEY_F(20)] = Platform_actionHigherAutogroupPriority; // Shift-F8
  185. }
  186. const MeterClass* const Platform_meterTypes[] = {
  187. &CPUMeter_class,
  188. &ClockMeter_class,
  189. &DateMeter_class,
  190. &DateTimeMeter_class,
  191. &LoadAverageMeter_class,
  192. &LoadMeter_class,
  193. &MemoryMeter_class,
  194. &SwapMeter_class,
  195. &MemorySwapMeter_class,
  196. &SysArchMeter_class,
  197. &HugePageMeter_class,
  198. &TasksMeter_class,
  199. &UptimeMeter_class,
  200. &BatteryMeter_class,
  201. &HostnameMeter_class,
  202. &AllCPUsMeter_class,
  203. &AllCPUs2Meter_class,
  204. &AllCPUs4Meter_class,
  205. &AllCPUs8Meter_class,
  206. &LeftCPUsMeter_class,
  207. &RightCPUsMeter_class,
  208. &LeftCPUs2Meter_class,
  209. &RightCPUs2Meter_class,
  210. &LeftCPUs4Meter_class,
  211. &RightCPUs4Meter_class,
  212. &LeftCPUs8Meter_class,
  213. &RightCPUs8Meter_class,
  214. &BlankMeter_class,
  215. &PressureStallCPUSomeMeter_class,
  216. &PressureStallIOSomeMeter_class,
  217. &PressureStallIOFullMeter_class,
  218. &PressureStallIRQFullMeter_class,
  219. &PressureStallMemorySomeMeter_class,
  220. &PressureStallMemoryFullMeter_class,
  221. &ZfsArcMeter_class,
  222. &ZfsCompressedArcMeter_class,
  223. &ZramMeter_class,
  224. &DiskIOMeter_class,
  225. &NetworkIOMeter_class,
  226. &SELinuxMeter_class,
  227. &SystemdMeter_class,
  228. &SystemdUserMeter_class,
  229. &FileDescriptorMeter_class,
  230. &GPUMeter_class,
  231. NULL
  232. };
  233. int Platform_getUptime(void) {
  234. char uptimedata[64] = {0};
  235. ssize_t uptimeread = xReadfile(PROCDIR "/uptime", uptimedata, sizeof(uptimedata));
  236. if (uptimeread < 1) {
  237. return 0;
  238. }
  239. double uptime = 0;
  240. double idle = 0;
  241. int n = sscanf(uptimedata, "%lf %lf", &uptime, &idle);
  242. if (n != 2) {
  243. return 0;
  244. }
  245. return floor(uptime);
  246. }
  247. void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
  248. char loaddata[128] = {0};
  249. *one = NAN;
  250. *five = NAN;
  251. *fifteen = NAN;
  252. ssize_t loadread = xReadfile(PROCDIR "/loadavg", loaddata, sizeof(loaddata));
  253. if (loadread < 1)
  254. return;
  255. double scanOne = NAN;
  256. double scanFive = NAN;
  257. double scanFifteen = NAN;
  258. int r = sscanf(loaddata, "%lf %lf %lf", &scanOne, &scanFive, &scanFifteen);
  259. if (r != 3)
  260. return;
  261. *one = scanOne;
  262. *five = scanFive;
  263. *fifteen = scanFifteen;
  264. }
  265. pid_t Platform_getMaxPid(void) {
  266. char piddata[32] = {0};
  267. ssize_t pidread = xReadfile(PROCDIR "/sys/kernel/pid_max", piddata, sizeof(piddata));
  268. if (pidread < 1)
  269. goto err;
  270. int pidmax = 0;
  271. int match = sscanf(piddata, "%32d", &pidmax);
  272. if (match != 1)
  273. goto err;
  274. return pidmax;
  275. err:
  276. return 0x3FFFFF; // 4194303
  277. }
  278. double Platform_setCPUValues(Meter* this, unsigned int cpu) {
  279. const LinuxMachine* lhost = (const LinuxMachine*) this->host;
  280. const Settings* settings = this->host->settings;
  281. const CPUData* cpuData = &(lhost->cpuData[cpu]);
  282. double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod);
  283. double percent;
  284. double* v = this->values;
  285. if (!cpuData->online) {
  286. this->curItems = 0;
  287. return NAN;
  288. }
  289. v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0;
  290. v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0;
  291. if (settings->detailedCPUTime) {
  292. v[CPU_METER_KERNEL] = cpuData->systemPeriod / total * 100.0;
  293. v[CPU_METER_IRQ] = cpuData->irqPeriod / total * 100.0;
  294. v[CPU_METER_SOFTIRQ] = cpuData->softIrqPeriod / total * 100.0;
  295. this->curItems = 5;
  296. v[CPU_METER_STEAL] = cpuData->stealPeriod / total * 100.0;
  297. v[CPU_METER_GUEST] = cpuData->guestPeriod / total * 100.0;
  298. if (settings->accountGuestInCPUMeter) {
  299. this->curItems = 7;
  300. }
  301. v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0;
  302. } else {
  303. v[CPU_METER_KERNEL] = cpuData->systemAllPeriod / total * 100.0;
  304. v[CPU_METER_IRQ] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0;
  305. this->curItems = 4;
  306. }
  307. percent = sumPositiveValues(v, this->curItems);
  308. percent = MINIMUM(percent, 100.0);
  309. if (settings->detailedCPUTime) {
  310. this->curItems = 8;
  311. }
  312. v[CPU_METER_FREQUENCY] = cpuData->frequency;
  313. #ifdef HAVE_SENSORS_SENSORS_H
  314. v[CPU_METER_TEMPERATURE] = cpuData->temperature;
  315. #else
  316. v[CPU_METER_TEMPERATURE] = NAN;
  317. #endif
  318. return percent;
  319. }
  320. void Platform_setMemoryValues(Meter* this) {
  321. const Machine* host = this->host;
  322. const LinuxMachine* lhost = (const LinuxMachine*) host;
  323. this->total = host->totalMem;
  324. this->values[MEMORY_METER_USED] = host->usedMem;
  325. this->values[MEMORY_METER_SHARED] = host->sharedMem;
  326. this->values[MEMORY_METER_COMPRESSED] = 0; /* compressed */
  327. this->values[MEMORY_METER_BUFFERS] = host->buffersMem;
  328. this->values[MEMORY_METER_CACHE] = host->cachedMem;
  329. this->values[MEMORY_METER_AVAILABLE] = host->availableMem;
  330. if (lhost->zfs.enabled != 0 && !Running_containerized) {
  331. // ZFS does not shrink below the value of zfs_arc_min.
  332. unsigned long long int shrinkableSize = 0;
  333. if (lhost->zfs.size > lhost->zfs.min)
  334. shrinkableSize = lhost->zfs.size - lhost->zfs.min;
  335. this->values[MEMORY_METER_USED] -= shrinkableSize;
  336. this->values[MEMORY_METER_CACHE] += shrinkableSize;
  337. this->values[MEMORY_METER_AVAILABLE] += shrinkableSize;
  338. }
  339. if (lhost->zswap.usedZswapOrig > 0 || lhost->zswap.usedZswapComp > 0) {
  340. this->values[MEMORY_METER_USED] -= lhost->zswap.usedZswapComp;
  341. this->values[MEMORY_METER_COMPRESSED] += lhost->zswap.usedZswapComp;
  342. }
  343. }
  344. void Platform_setSwapValues(Meter* this) {
  345. const Machine* host = this->host;
  346. const LinuxMachine* lhost = (const LinuxMachine*) host;
  347. this->total = host->totalSwap;
  348. this->values[SWAP_METER_USED] = host->usedSwap;
  349. this->values[SWAP_METER_CACHE] = host->cachedSwap;
  350. this->values[SWAP_METER_FRONTSWAP] = 0; /* frontswap -- memory that is accounted to swap but resides elsewhere */
  351. if (lhost->zswap.usedZswapOrig > 0 || lhost->zswap.usedZswapComp > 0) {
  352. /*
  353. * FIXME: Zswapped pages can be both SwapUsed and SwapCached, and we do not know which.
  354. *
  355. * Apparently, it is possible that Zswapped > SwapUsed. This means that some of Zswapped pages
  356. * were actually SwapCached, nor SwapUsed. Unfortunately, we cannot tell what exactly portion
  357. * of Zswapped pages were SwapCached.
  358. *
  359. * For now, subtract Zswapped from SwapUsed and only if Zswapped > SwapUsed, subtract the
  360. * overflow from SwapCached.
  361. */
  362. this->values[SWAP_METER_USED] -= lhost->zswap.usedZswapOrig;
  363. if (this->values[SWAP_METER_USED] < 0) {
  364. /* subtract the overflow from SwapCached */
  365. this->values[SWAP_METER_CACHE] += this->values[SWAP_METER_USED];
  366. this->values[SWAP_METER_USED] = 0;
  367. }
  368. this->values[SWAP_METER_FRONTSWAP] += lhost->zswap.usedZswapOrig;
  369. }
  370. }
  371. void Platform_setZramValues(Meter* this) {
  372. const LinuxMachine* lhost = (const LinuxMachine*) this->host;
  373. this->total = lhost->zram.totalZram;
  374. this->values[ZRAM_METER_COMPRESSED] = lhost->zram.usedZramComp;
  375. this->values[ZRAM_METER_UNCOMPRESSED] = lhost->zram.usedZramOrig - lhost->zram.usedZramComp;
  376. }
  377. void Platform_setZfsArcValues(Meter* this) {
  378. const LinuxMachine* lhost = (const LinuxMachine*) this->host;
  379. ZfsArcMeter_readStats(this, &(lhost->zfs));
  380. }
  381. void Platform_setZfsCompressedArcValues(Meter* this) {
  382. const LinuxMachine* lhost = (const LinuxMachine*) this->host;
  383. ZfsCompressedArcMeter_readStats(this, &(lhost->zfs));
  384. }
  385. char* Platform_getProcessEnv(pid_t pid) {
  386. char procname[128];
  387. xSnprintf(procname, sizeof(procname), PROCDIR "/%d/environ", pid);
  388. FILE* fp = fopen(procname, "r");
  389. if (!fp)
  390. return NULL;
  391. char* env = NULL;
  392. size_t capacity = 0;
  393. size_t size = 0;
  394. ssize_t bytes = 0;
  395. do {
  396. size += bytes;
  397. capacity += 4096;
  398. env = xRealloc(env, capacity);
  399. } while (!ferror(fp) && !feof(fp) && (bytes = fread(env + size, 1, capacity - size, fp)) > 0);
  400. fclose(fp);
  401. if (bytes < 0) {
  402. free(env);
  403. return NULL;
  404. }
  405. size += bytes;
  406. env = xRealloc(env, size + 2);
  407. env[size] = '\0';
  408. env[size + 1] = '\0';
  409. return env;
  410. }
  411. FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
  412. FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData));
  413. DIR* dirp;
  414. int dfd;
  415. char path[PATH_MAX];
  416. xSnprintf(path, sizeof(path), PROCDIR "/%d/fdinfo/", pid);
  417. if (strlen(path) >= (sizeof(path) - 2))
  418. goto err;
  419. if (!(dirp = opendir(path)))
  420. goto err;
  421. if ((dfd = dirfd(dirp)) == -1) {
  422. closedir(dirp);
  423. goto err;
  424. }
  425. FileLocks_LockData** data_ref = &pdata->locks;
  426. for (struct dirent* de; (de = readdir(dirp)); ) {
  427. if (String_eq(de->d_name, ".") || String_eq(de->d_name, ".."))
  428. continue;
  429. errno = 0;
  430. char* end = de->d_name;
  431. int file = strtoull(de->d_name, &end, 10);
  432. if (errno || *end)
  433. continue;
  434. int fd = openat(dfd, de->d_name, O_RDONLY | O_CLOEXEC);
  435. if (fd == -1)
  436. continue;
  437. FILE* fp = fdopen(fd, "r");
  438. if (!fp) {
  439. close(fd);
  440. continue;
  441. }
  442. for (char buffer[1024]; fgets(buffer, sizeof(buffer), fp); ) {
  443. if (!strchr(buffer, '\n'))
  444. continue;
  445. if (!String_startsWith(buffer, "lock:\t"))
  446. continue;
  447. FileLocks_Data data = {.fd = file};
  448. int _;
  449. unsigned int maj, min;
  450. char lock_end[25], locktype[32], exclusive[32], readwrite[32];
  451. if (10 != sscanf(buffer + strlen("lock:\t"), "%d: %31s %31s %31s %d %x:%x:%"PRIu64" %"PRIu64" %24s",
  452. &_, locktype, exclusive, readwrite, &_,
  453. &maj, &min, &data.inode,
  454. &data.start, lock_end))
  455. continue;
  456. data.locktype = xStrdup(locktype);
  457. data.exclusive = xStrdup(exclusive);
  458. data.readwrite = xStrdup(readwrite);
  459. data.dev = makedev(maj, min);
  460. if (String_eq(lock_end, "EOF"))
  461. data.end = ULLONG_MAX;
  462. else
  463. data.end = strtoull(lock_end, NULL, 10);
  464. xSnprintf(path, sizeof(path), PROCDIR "/%d/fd/%s", pid, de->d_name);
  465. char link[PATH_MAX];
  466. ssize_t link_len;
  467. if (strlen(path) < (sizeof(path) - 2) && (link_len = readlink(path, link, sizeof(link))) != -1)
  468. data.filename = xStrndup(link, link_len);
  469. *data_ref = xCalloc(1, sizeof(FileLocks_LockData));
  470. (*data_ref)->data = data;
  471. data_ref = &(*data_ref)->next;
  472. }
  473. fclose(fp);
  474. }
  475. closedir(dirp);
  476. return pdata;
  477. err:
  478. pdata->error = true;
  479. return pdata;
  480. }
  481. void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred) {
  482. *ten = *sixty = *threehundred = 0;
  483. char procname[128];
  484. xSnprintf(procname, sizeof(procname), PROCDIR "/pressure/%s", file);
  485. FILE* fp = fopen(procname, "r");
  486. if (!fp) {
  487. *ten = *sixty = *threehundred = NAN;
  488. return;
  489. }
  490. int total = fscanf(fp, "some avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred);
  491. if (total != EOF && !some) {
  492. total = fscanf(fp, "full avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred);
  493. }
  494. (void) total;
  495. assert(total == 3);
  496. fclose(fp);
  497. }
  498. void Platform_getFileDescriptors(double* used, double* max) {
  499. char buffer[128] = {0};
  500. *used = NAN;
  501. *max = 65536;
  502. ssize_t fdread = xReadfile(PROCDIR "/sys/fs/file-nr", buffer, sizeof(buffer));
  503. if (fdread < 1)
  504. return;
  505. unsigned long long v1, v2, v3;
  506. int total = sscanf(buffer, "%llu %llu %llu", &v1, &v2, &v3);
  507. if (total == 3) {
  508. *used = v1;
  509. *max = v3;
  510. }
  511. }
  512. bool Platform_getDiskIO(DiskIOData* data) {
  513. FILE* fp = fopen(PROCDIR "/diskstats", "r");
  514. if (!fp)
  515. return false;
  516. char lastTopDisk[32] = { '\0' };
  517. unsigned long long int read_sum = 0, write_sum = 0, timeSpend_sum = 0;
  518. char lineBuffer[256];
  519. while (fgets(lineBuffer, sizeof(lineBuffer), fp)) {
  520. char diskname[32];
  521. unsigned long long int read_tmp, write_tmp, timeSpend_tmp;
  522. if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %llu %*u %*u %*u %llu %*u %*u %llu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4) {
  523. if (String_startsWith(diskname, "dm-"))
  524. continue;
  525. if (String_startsWith(diskname, "zram"))
  526. continue;
  527. /* only count root disks, e.g. do not count IO from sda and sda1 twice */
  528. if (lastTopDisk[0] && String_startsWith(diskname, lastTopDisk))
  529. continue;
  530. /* This assumes disks are listed directly before any of their partitions */
  531. String_safeStrncpy(lastTopDisk, diskname, sizeof(lastTopDisk));
  532. read_sum += read_tmp;
  533. write_sum += write_tmp;
  534. timeSpend_sum += timeSpend_tmp;
  535. }
  536. }
  537. fclose(fp);
  538. /* multiply with sector size */
  539. data->totalBytesRead = 512 * read_sum;
  540. data->totalBytesWritten = 512 * write_sum;
  541. data->totalMsTimeSpend = timeSpend_sum;
  542. return true;
  543. }
  544. bool Platform_getNetworkIO(NetworkIOData* data) {
  545. FILE* fp = fopen(PROCDIR "/net/dev", "r");
  546. if (!fp)
  547. return false;
  548. memset(data, 0, sizeof(NetworkIOData));
  549. char lineBuffer[512];
  550. while (fgets(lineBuffer, sizeof(lineBuffer), fp)) {
  551. char interfaceName[32];
  552. unsigned long long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
  553. if (sscanf(lineBuffer, "%31s %llu %llu %*u %*u %*u %*u %*u %*u %llu %llu",
  554. interfaceName,
  555. &bytesReceived,
  556. &packetsReceived,
  557. &bytesTransmitted,
  558. &packetsTransmitted) != 5)
  559. continue;
  560. if (String_eq(interfaceName, "lo:"))
  561. continue;
  562. data->bytesReceived += bytesReceived;
  563. data->packetsReceived += packetsReceived;
  564. data->bytesTransmitted += bytesTransmitted;
  565. data->packetsTransmitted += packetsTransmitted;
  566. }
  567. fclose(fp);
  568. return true;
  569. }
  570. // Linux battery reading by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
  571. #define PROC_BATTERY_DIR PROCDIR "/acpi/battery"
  572. #define PROC_POWERSUPPLY_DIR PROCDIR "/acpi/ac_adapter"
  573. #define PROC_POWERSUPPLY_ACSTATE_FILE PROC_POWERSUPPLY_DIR "/AC/state"
  574. #define SYS_POWERSUPPLY_DIR "/sys/class/power_supply"
  575. // ----------------------------------------
  576. // READ FROM /proc
  577. // ----------------------------------------
  578. static double Platform_Battery_getProcBatInfo(void) {
  579. DIR* batteryDir = opendir(PROC_BATTERY_DIR);
  580. if (!batteryDir)
  581. return NAN;
  582. uint64_t totalFull = 0;
  583. uint64_t totalRemain = 0;
  584. struct dirent* dirEntry = NULL;
  585. while ((dirEntry = readdir(batteryDir))) {
  586. const char* entryName = dirEntry->d_name;
  587. if (!String_startsWith(entryName, "BAT"))
  588. continue;
  589. char filePath[256];
  590. char bufInfo[1024] = {0};
  591. xSnprintf(filePath, sizeof(filePath), "%s/%s/info", PROC_BATTERY_DIR, entryName);
  592. ssize_t r = xReadfile(filePath, bufInfo, sizeof(bufInfo));
  593. if (r < 0)
  594. continue;
  595. char bufState[1024] = {0};
  596. xSnprintf(filePath, sizeof(filePath), "%s/%s/state", PROC_BATTERY_DIR, entryName);
  597. r = xReadfile(filePath, bufState, sizeof(bufState));
  598. if (r < 0)
  599. continue;
  600. const char* line;
  601. //Getting total charge for all batteries
  602. char* buf = bufInfo;
  603. while ((line = strsep(&buf, "\n")) != NULL) {
  604. char field[100] = {0};
  605. int val = 0;
  606. if (2 != sscanf(line, "%99[^:]:%d", field, &val))
  607. continue;
  608. if (String_eq(field, "last full capacity")) {
  609. totalFull += val;
  610. break;
  611. }
  612. }
  613. //Getting remaining charge for all batteries
  614. buf = bufState;
  615. while ((line = strsep(&buf, "\n")) != NULL) {
  616. char field[100] = {0};
  617. int val = 0;
  618. if (2 != sscanf(line, "%99[^:]:%d", field, &val))
  619. continue;
  620. if (String_eq(field, "remaining capacity")) {
  621. totalRemain += val;
  622. break;
  623. }
  624. }
  625. }
  626. closedir(batteryDir);
  627. return totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
  628. }
  629. static ACPresence procAcpiCheck(void) {
  630. char buffer[1024] = {0};
  631. ssize_t r = xReadfile(PROC_POWERSUPPLY_ACSTATE_FILE, buffer, sizeof(buffer));
  632. if (r < 1)
  633. return AC_ERROR;
  634. return String_eq(buffer, "on-line") ? AC_PRESENT : AC_ABSENT;
  635. }
  636. static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
  637. *isOnAC = procAcpiCheck();
  638. *percent = AC_ERROR != *isOnAC ? Platform_Battery_getProcBatInfo() : NAN;
  639. }
  640. // ----------------------------------------
  641. // READ FROM /sys
  642. // ----------------------------------------
  643. static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
  644. *percent = NAN;
  645. *isOnAC = AC_ERROR;
  646. DIR* dir = opendir(SYS_POWERSUPPLY_DIR);
  647. if (!dir)
  648. return;
  649. uint64_t totalFull = 0;
  650. uint64_t totalRemain = 0;
  651. const struct dirent* dirEntry;
  652. while ((dirEntry = readdir(dir))) {
  653. const char* entryName = dirEntry->d_name;
  654. #ifdef HAVE_OPENAT
  655. int entryFd = openat(xDirfd(dir), entryName, O_DIRECTORY | O_PATH);
  656. if (entryFd < 0)
  657. continue;
  658. #else
  659. char entryFd[4096];
  660. xSnprintf(entryFd, sizeof(entryFd), SYS_POWERSUPPLY_DIR "/%s", entryName);
  661. #endif
  662. enum { AC, BAT } type;
  663. if (String_startsWith(entryName, "BAT")) {
  664. type = BAT;
  665. } else if (String_startsWith(entryName, "AC")) {
  666. type = AC;
  667. } else {
  668. char buffer[32];
  669. ssize_t ret = xReadfileat(entryFd, "type", buffer, sizeof(buffer));
  670. if (ret <= 0)
  671. goto next;
  672. /* drop optional trailing newlines */
  673. for (char* buf = &buffer[(size_t)ret - 1]; *buf == '\n'; buf--)
  674. *buf = '\0';
  675. if (String_eq(buffer, "Battery"))
  676. type = BAT;
  677. else if (String_eq(buffer, "Mains"))
  678. type = AC;
  679. else
  680. goto next;
  681. }
  682. if (type == BAT) {
  683. char buffer[1024];
  684. ssize_t r = xReadfileat(entryFd, "uevent", buffer, sizeof(buffer));
  685. if (r < 0)
  686. goto next;
  687. bool full = false;
  688. bool now = false;
  689. double fullCharge = 0;
  690. double capacityLevel = NAN;
  691. const char* line;
  692. char* buf = buffer;
  693. while ((line = strsep(&buf, "\n")) != NULL) {
  694. char field[100] = {0};
  695. int val = 0;
  696. if (2 != sscanf(line, "POWER_SUPPLY_%99[^=]=%d", field, &val))
  697. continue;
  698. if (String_eq(field, "CAPACITY")) {
  699. capacityLevel = val / 100.0;
  700. continue;
  701. }
  702. if (String_eq(field, "ENERGY_FULL") || String_eq(field, "CHARGE_FULL")) {
  703. fullCharge = val;
  704. totalFull += fullCharge;
  705. full = true;
  706. if (now)
  707. break;
  708. continue;
  709. }
  710. if (String_eq(field, "ENERGY_NOW") || String_eq(field, "CHARGE_NOW")) {
  711. totalRemain += val;
  712. now = true;
  713. if (full)
  714. break;
  715. continue;
  716. }
  717. }
  718. if (!now && full && isNonnegative(capacityLevel))
  719. totalRemain += capacityLevel * fullCharge;
  720. } else if (type == AC) {
  721. if (*isOnAC != AC_ERROR)
  722. goto next;
  723. char buffer[2];
  724. ssize_t r = xReadfileat(entryFd, "online", buffer, sizeof(buffer));
  725. if (r < 1) {
  726. *isOnAC = AC_ERROR;
  727. goto next;
  728. }
  729. if (buffer[0] == '0')
  730. *isOnAC = AC_ABSENT;
  731. else if (buffer[0] == '1')
  732. *isOnAC = AC_PRESENT;
  733. }
  734. next:
  735. Compat_openatArgClose(entryFd);
  736. }
  737. closedir(dir);
  738. *percent = totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
  739. }
  740. void Platform_getBattery(double* percent, ACPresence* isOnAC) {
  741. time_t now = time(NULL);
  742. // update battery reading is slow. Update it each 10 seconds only.
  743. if (now < Platform_Battery_cacheTime + 10) {
  744. *percent = Platform_Battery_cachePercent;
  745. *isOnAC = Platform_Battery_cacheIsOnAC;
  746. return;
  747. }
  748. if (Platform_Battery_method == BAT_PROC) {
  749. Platform_Battery_getProcData(percent, isOnAC);
  750. if (!isNonnegative(*percent))
  751. Platform_Battery_method = BAT_SYS;
  752. }
  753. if (Platform_Battery_method == BAT_SYS) {
  754. Platform_Battery_getSysData(percent, isOnAC);
  755. if (!isNonnegative(*percent))
  756. Platform_Battery_method = BAT_ERR;
  757. }
  758. if (Platform_Battery_method == BAT_ERR) {
  759. *percent = NAN;
  760. *isOnAC = AC_ERROR;
  761. } else {
  762. *percent = CLAMP(*percent, 0.0, 100.0);
  763. }
  764. Platform_Battery_cachePercent = *percent;
  765. Platform_Battery_cacheIsOnAC = *isOnAC;
  766. Platform_Battery_cacheTime = now;
  767. }
  768. void Platform_longOptionsUsage(const char* name)
  769. {
  770. #ifdef HAVE_LIBCAP
  771. printf(
  772. " --drop-capabilities[=off|basic|strict] Drop Linux capabilities when running as root\n"
  773. " off - do not drop any capabilities\n"
  774. " basic (default) - drop all capabilities not needed by %s\n"
  775. " strict - drop all capabilities except those needed for\n"
  776. " core functionality\n", name);
  777. #else
  778. (void) name;
  779. #endif
  780. }
  781. CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv) {
  782. #ifndef HAVE_LIBCAP
  783. (void) argc;
  784. (void) argv;
  785. #endif
  786. switch (opt) {
  787. #ifdef HAVE_LIBCAP
  788. case 160: {
  789. const char* mode = optarg;
  790. if (!mode && optind < argc && argv[optind] != NULL &&
  791. (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
  792. mode = argv[optind++];
  793. }
  794. if (!mode || String_eq(mode, "basic")) {
  795. Platform_capabilitiesMode = CAP_MODE_BASIC;
  796. } else if (String_eq(mode, "off")) {
  797. Platform_capabilitiesMode = CAP_MODE_OFF;
  798. } else if (String_eq(mode, "strict")) {
  799. Platform_capabilitiesMode = CAP_MODE_STRICT;
  800. } else {
  801. fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
  802. return STATUS_ERROR_EXIT;
  803. }
  804. return STATUS_OK;
  805. }
  806. #endif
  807. default:
  808. break;
  809. }
  810. return STATUS_ERROR_EXIT;
  811. }
  812. #ifdef HAVE_LIBCAP
  813. static int dropCapabilities(enum CapMode mode) {
  814. if (mode == CAP_MODE_OFF)
  815. return 0;
  816. /* capabilities we keep to operate */
  817. const cap_value_t keepcapsStrict[] = {
  818. CAP_DAC_READ_SEARCH,
  819. CAP_SYS_PTRACE,
  820. };
  821. const cap_value_t keepcapsBasic[] = {
  822. CAP_DAC_READ_SEARCH, /* read non world-readable process files of other users, like /proc/[pid]/io */
  823. CAP_KILL, /* send signals to processes of other users */
  824. CAP_SYS_NICE, /* lower process nice value / change nice value for arbitrary processes */
  825. CAP_SYS_PTRACE, /* read /proc/[pid]/exe */
  826. #ifdef HAVE_DELAYACCT
  827. CAP_NET_ADMIN, /* communicate over netlink socket for delay accounting */
  828. #endif
  829. };
  830. const cap_value_t* const keepcaps = (mode == CAP_MODE_BASIC) ? keepcapsBasic : keepcapsStrict;
  831. const size_t ncap = (mode == CAP_MODE_BASIC) ? ARRAYSIZE(keepcapsBasic) : ARRAYSIZE(keepcapsStrict);
  832. cap_t caps = cap_init();
  833. if (caps == NULL) {
  834. fprintf(stderr, "Error: can not initialize capabilities: %s\n", strerror(errno));
  835. return -1;
  836. }
  837. if (cap_clear(caps) < 0) {
  838. fprintf(stderr, "Error: can not clear capabilities: %s\n", strerror(errno));
  839. cap_free(caps);
  840. return -1;
  841. }
  842. cap_t currCaps = cap_get_proc();
  843. if (currCaps == NULL) {
  844. fprintf(stderr, "Error: can not get current process capabilities: %s\n", strerror(errno));
  845. cap_free(caps);
  846. return -1;
  847. }
  848. for (size_t i = 0; i < ncap; i++) {
  849. if (!CAP_IS_SUPPORTED(keepcaps[i]))
  850. continue;
  851. cap_flag_value_t current;
  852. if (cap_get_flag(currCaps, keepcaps[i], CAP_PERMITTED, &current) < 0) {
  853. fprintf(stderr, "Error: can not get current value of capability %d: %s\n", keepcaps[i], strerror(errno));
  854. cap_free(currCaps);
  855. cap_free(caps);
  856. return -1;
  857. }
  858. if (current != CAP_SET)
  859. continue;
  860. if (cap_set_flag(caps, CAP_PERMITTED, 1, &keepcaps[i], CAP_SET) < 0) {
  861. fprintf(stderr, "Error: can not set permitted capability %d: %s\n", keepcaps[i], strerror(errno));
  862. cap_free(currCaps);
  863. cap_free(caps);
  864. return -1;
  865. }
  866. if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &keepcaps[i], CAP_SET) < 0) {
  867. fprintf(stderr, "Error: can not set effective capability %d: %s\n", keepcaps[i], strerror(errno));
  868. cap_free(currCaps);
  869. cap_free(caps);
  870. return -1;
  871. }
  872. }
  873. if (cap_set_proc(caps) < 0) {
  874. fprintf(stderr, "Error: can not set process capabilities: %s\n", strerror(errno));
  875. cap_free(currCaps);
  876. cap_free(caps);
  877. return -1;
  878. }
  879. cap_free(currCaps);
  880. cap_free(caps);
  881. return 0;
  882. }
  883. #endif
  884. bool Platform_init(void) {
  885. #ifdef HAVE_LIBCAP
  886. if (dropCapabilities(Platform_capabilitiesMode) < 0)
  887. return false;
  888. #endif
  889. if (access(PROCDIR, R_OK) != 0) {
  890. fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
  891. return false;
  892. }
  893. #ifdef HAVE_SENSORS_SENSORS_H
  894. LibSensors_init();
  895. #endif
  896. char target[PATH_MAX];
  897. ssize_t ret = readlink(PROCDIR "/self/ns/pid", target, sizeof(target) - 1);
  898. if (ret > 0) {
  899. target[ret] = '\0';
  900. if (!String_eq("pid:[4026531836]", target)) { // magic constant PROC_PID_INIT_INO from include/linux/proc_ns.h#L46
  901. Running_containerized = true;
  902. return true; // early return
  903. }
  904. }
  905. FILE* fp = fopen(PROCDIR "/1/mounts", "r");
  906. if (fp) {
  907. char lineBuffer[256];
  908. while (fgets(lineBuffer, sizeof(lineBuffer), fp)) {
  909. // detect lxc or overlayfs and guess that this means we are running containerized
  910. if (String_startsWith(lineBuffer, "lxcfs /proc") || String_startsWith(lineBuffer, "overlay / overlay")) {
  911. Running_containerized = true;
  912. break;
  913. }
  914. }
  915. fclose(fp);
  916. }
  917. return true;
  918. }
  919. void Platform_done(void) {
  920. #ifdef HAVE_SENSORS_SENSORS_H
  921. LibSensors_cleanup();
  922. #endif
  923. }