Platform.c 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  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. uint64_t read_sum = 0, write_sum = 0, timeSpend_sum = 0;
  518. uint64_t numDisks = 0;
  519. char lineBuffer[256];
  520. while (fgets(lineBuffer, sizeof(lineBuffer), fp)) {
  521. char diskname[32];
  522. unsigned long long int read_tmp, write_tmp, timeSpend_tmp;
  523. if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %llu %*u %*u %*u %llu %*u %*u %llu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4) {
  524. if (String_startsWith(diskname, "dm-"))
  525. continue;
  526. if (String_startsWith(diskname, "zram"))
  527. continue;
  528. /* only count root disks, e.g. do not count IO from sda and sda1 twice */
  529. if (lastTopDisk[0] && String_startsWith(diskname, lastTopDisk))
  530. continue;
  531. /* This assumes disks are listed directly before any of their partitions */
  532. String_safeStrncpy(lastTopDisk, diskname, sizeof(lastTopDisk));
  533. read_sum += read_tmp;
  534. write_sum += write_tmp;
  535. timeSpend_sum += timeSpend_tmp;
  536. numDisks++;
  537. }
  538. }
  539. fclose(fp);
  540. /* multiply with sector size */
  541. data->totalBytesRead = 512 * read_sum;
  542. data->totalBytesWritten = 512 * write_sum;
  543. data->totalMsTimeSpend = timeSpend_sum;
  544. data->numDisks = numDisks;
  545. return true;
  546. }
  547. bool Platform_getNetworkIO(NetworkIOData* data) {
  548. FILE* fp = fopen(PROCDIR "/net/dev", "r");
  549. if (!fp)
  550. return false;
  551. memset(data, 0, sizeof(NetworkIOData));
  552. char lineBuffer[512];
  553. while (fgets(lineBuffer, sizeof(lineBuffer), fp)) {
  554. char interfaceName[32];
  555. unsigned long long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
  556. if (sscanf(lineBuffer, "%31s %llu %llu %*u %*u %*u %*u %*u %*u %llu %llu",
  557. interfaceName,
  558. &bytesReceived,
  559. &packetsReceived,
  560. &bytesTransmitted,
  561. &packetsTransmitted) != 5)
  562. continue;
  563. if (String_eq(interfaceName, "lo:"))
  564. continue;
  565. data->bytesReceived += bytesReceived;
  566. data->packetsReceived += packetsReceived;
  567. data->bytesTransmitted += bytesTransmitted;
  568. data->packetsTransmitted += packetsTransmitted;
  569. }
  570. fclose(fp);
  571. return true;
  572. }
  573. // Linux battery reading by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
  574. #define PROC_BATTERY_DIR PROCDIR "/acpi/battery"
  575. #define PROC_POWERSUPPLY_DIR PROCDIR "/acpi/ac_adapter"
  576. #define PROC_POWERSUPPLY_ACSTATE_FILE PROC_POWERSUPPLY_DIR "/AC/state"
  577. #define SYS_POWERSUPPLY_DIR "/sys/class/power_supply"
  578. // ----------------------------------------
  579. // READ FROM /proc
  580. // ----------------------------------------
  581. static double Platform_Battery_getProcBatInfo(void) {
  582. DIR* batteryDir = opendir(PROC_BATTERY_DIR);
  583. if (!batteryDir)
  584. return NAN;
  585. uint64_t totalFull = 0;
  586. uint64_t totalRemain = 0;
  587. struct dirent* dirEntry = NULL;
  588. while ((dirEntry = readdir(batteryDir))) {
  589. const char* entryName = dirEntry->d_name;
  590. if (!String_startsWith(entryName, "BAT"))
  591. continue;
  592. char filePath[256];
  593. char bufInfo[1024] = {0};
  594. xSnprintf(filePath, sizeof(filePath), "%s/%s/info", PROC_BATTERY_DIR, entryName);
  595. ssize_t r = xReadfile(filePath, bufInfo, sizeof(bufInfo));
  596. if (r < 0)
  597. continue;
  598. char bufState[1024] = {0};
  599. xSnprintf(filePath, sizeof(filePath), "%s/%s/state", PROC_BATTERY_DIR, entryName);
  600. r = xReadfile(filePath, bufState, sizeof(bufState));
  601. if (r < 0)
  602. continue;
  603. const char* line;
  604. //Getting total charge for all batteries
  605. char* buf = bufInfo;
  606. while ((line = strsep(&buf, "\n")) != NULL) {
  607. char field[100] = {0};
  608. int val = 0;
  609. if (2 != sscanf(line, "%99[^:]:%d", field, &val))
  610. continue;
  611. if (String_eq(field, "last full capacity")) {
  612. totalFull += val;
  613. break;
  614. }
  615. }
  616. //Getting remaining charge for all batteries
  617. buf = bufState;
  618. while ((line = strsep(&buf, "\n")) != NULL) {
  619. char field[100] = {0};
  620. int val = 0;
  621. if (2 != sscanf(line, "%99[^:]:%d", field, &val))
  622. continue;
  623. if (String_eq(field, "remaining capacity")) {
  624. totalRemain += val;
  625. break;
  626. }
  627. }
  628. }
  629. closedir(batteryDir);
  630. return totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
  631. }
  632. static ACPresence procAcpiCheck(void) {
  633. char buffer[1024] = {0};
  634. ssize_t r = xReadfile(PROC_POWERSUPPLY_ACSTATE_FILE, buffer, sizeof(buffer));
  635. if (r < 1)
  636. return AC_ERROR;
  637. return String_eq(buffer, "on-line") ? AC_PRESENT : AC_ABSENT;
  638. }
  639. static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
  640. *isOnAC = procAcpiCheck();
  641. *percent = AC_ERROR != *isOnAC ? Platform_Battery_getProcBatInfo() : NAN;
  642. }
  643. // ----------------------------------------
  644. // READ FROM /sys
  645. // ----------------------------------------
  646. static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
  647. *percent = NAN;
  648. *isOnAC = AC_ERROR;
  649. DIR* dir = opendir(SYS_POWERSUPPLY_DIR);
  650. if (!dir)
  651. return;
  652. uint64_t totalFull = 0;
  653. uint64_t totalRemain = 0;
  654. const struct dirent* dirEntry;
  655. while ((dirEntry = readdir(dir))) {
  656. const char* entryName = dirEntry->d_name;
  657. #ifdef HAVE_OPENAT
  658. int entryFd = openat(xDirfd(dir), entryName, O_DIRECTORY | O_PATH);
  659. if (entryFd < 0)
  660. continue;
  661. #else
  662. char entryFd[4096];
  663. xSnprintf(entryFd, sizeof(entryFd), SYS_POWERSUPPLY_DIR "/%s", entryName);
  664. #endif
  665. enum { AC, BAT } type;
  666. if (String_startsWith(entryName, "BAT")) {
  667. type = BAT;
  668. } else if (String_startsWith(entryName, "AC")) {
  669. type = AC;
  670. } else {
  671. char buffer[32];
  672. ssize_t ret = xReadfileat(entryFd, "type", buffer, sizeof(buffer));
  673. if (ret <= 0)
  674. goto next;
  675. /* drop optional trailing newlines */
  676. for (char* buf = &buffer[(size_t)ret - 1]; *buf == '\n'; buf--)
  677. *buf = '\0';
  678. if (String_eq(buffer, "Battery"))
  679. type = BAT;
  680. else if (String_eq(buffer, "Mains"))
  681. type = AC;
  682. else
  683. goto next;
  684. }
  685. if (type == BAT) {
  686. char buffer[1024];
  687. ssize_t r = xReadfileat(entryFd, "uevent", buffer, sizeof(buffer));
  688. if (r < 0)
  689. goto next;
  690. bool full = false;
  691. bool now = false;
  692. double fullCharge = 0;
  693. double capacityLevel = NAN;
  694. const char* line;
  695. char* buf = buffer;
  696. while ((line = strsep(&buf, "\n")) != NULL) {
  697. char field[100] = {0};
  698. int val = 0;
  699. if (2 != sscanf(line, "POWER_SUPPLY_%99[^=]=%d", field, &val))
  700. continue;
  701. if (String_eq(field, "CAPACITY")) {
  702. capacityLevel = val / 100.0;
  703. continue;
  704. }
  705. if (String_eq(field, "ENERGY_FULL") || String_eq(field, "CHARGE_FULL")) {
  706. fullCharge = val;
  707. totalFull += fullCharge;
  708. full = true;
  709. if (now)
  710. break;
  711. continue;
  712. }
  713. if (String_eq(field, "ENERGY_NOW") || String_eq(field, "CHARGE_NOW")) {
  714. totalRemain += val;
  715. now = true;
  716. if (full)
  717. break;
  718. continue;
  719. }
  720. }
  721. if (!now && full && isNonnegative(capacityLevel))
  722. totalRemain += capacityLevel * fullCharge;
  723. } else if (type == AC) {
  724. if (*isOnAC != AC_ERROR)
  725. goto next;
  726. char buffer[2];
  727. ssize_t r = xReadfileat(entryFd, "online", buffer, sizeof(buffer));
  728. if (r < 1) {
  729. *isOnAC = AC_ERROR;
  730. goto next;
  731. }
  732. if (buffer[0] == '0')
  733. *isOnAC = AC_ABSENT;
  734. else if (buffer[0] == '1')
  735. *isOnAC = AC_PRESENT;
  736. }
  737. next:
  738. Compat_openatArgClose(entryFd);
  739. }
  740. closedir(dir);
  741. *percent = totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
  742. }
  743. void Platform_getBattery(double* percent, ACPresence* isOnAC) {
  744. time_t now = time(NULL);
  745. // update battery reading is slow. Update it each 10 seconds only.
  746. if (now < Platform_Battery_cacheTime + 10) {
  747. *percent = Platform_Battery_cachePercent;
  748. *isOnAC = Platform_Battery_cacheIsOnAC;
  749. return;
  750. }
  751. if (Platform_Battery_method == BAT_PROC) {
  752. Platform_Battery_getProcData(percent, isOnAC);
  753. if (!isNonnegative(*percent))
  754. Platform_Battery_method = BAT_SYS;
  755. }
  756. if (Platform_Battery_method == BAT_SYS) {
  757. Platform_Battery_getSysData(percent, isOnAC);
  758. if (!isNonnegative(*percent))
  759. Platform_Battery_method = BAT_ERR;
  760. }
  761. if (Platform_Battery_method == BAT_ERR) {
  762. *percent = NAN;
  763. *isOnAC = AC_ERROR;
  764. } else {
  765. *percent = CLAMP(*percent, 0.0, 100.0);
  766. }
  767. Platform_Battery_cachePercent = *percent;
  768. Platform_Battery_cacheIsOnAC = *isOnAC;
  769. Platform_Battery_cacheTime = now;
  770. }
  771. void Platform_longOptionsUsage(const char* name)
  772. {
  773. #ifdef HAVE_LIBCAP
  774. printf(
  775. " --drop-capabilities[=off|basic|strict] Drop Linux capabilities when running as root\n"
  776. " off - do not drop any capabilities\n"
  777. " basic (default) - drop all capabilities not needed by %s\n"
  778. " strict - drop all capabilities except those needed for\n"
  779. " core functionality\n", name);
  780. #else
  781. (void) name;
  782. #endif
  783. }
  784. CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv) {
  785. #ifndef HAVE_LIBCAP
  786. (void) argc;
  787. (void) argv;
  788. #endif
  789. switch (opt) {
  790. #ifdef HAVE_LIBCAP
  791. case 160: {
  792. const char* mode = optarg;
  793. if (!mode && optind < argc && argv[optind] != NULL &&
  794. (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
  795. mode = argv[optind++];
  796. }
  797. if (!mode || String_eq(mode, "basic")) {
  798. Platform_capabilitiesMode = CAP_MODE_BASIC;
  799. } else if (String_eq(mode, "off")) {
  800. Platform_capabilitiesMode = CAP_MODE_OFF;
  801. } else if (String_eq(mode, "strict")) {
  802. Platform_capabilitiesMode = CAP_MODE_STRICT;
  803. } else {
  804. fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
  805. return STATUS_ERROR_EXIT;
  806. }
  807. return STATUS_OK;
  808. }
  809. #endif
  810. default:
  811. break;
  812. }
  813. return STATUS_ERROR_EXIT;
  814. }
  815. #ifdef HAVE_LIBCAP
  816. static int dropCapabilities(enum CapMode mode) {
  817. if (mode == CAP_MODE_OFF)
  818. return 0;
  819. /* capabilities we keep to operate */
  820. const cap_value_t keepcapsStrict[] = {
  821. CAP_DAC_READ_SEARCH,
  822. CAP_SYS_PTRACE,
  823. };
  824. const cap_value_t keepcapsBasic[] = {
  825. CAP_DAC_READ_SEARCH, /* read non world-readable process files of other users, like /proc/[pid]/io */
  826. CAP_KILL, /* send signals to processes of other users */
  827. CAP_SYS_NICE, /* lower process nice value / change nice value for arbitrary processes */
  828. CAP_SYS_PTRACE, /* read /proc/[pid]/exe */
  829. #ifdef HAVE_DELAYACCT
  830. CAP_NET_ADMIN, /* communicate over netlink socket for delay accounting */
  831. #endif
  832. };
  833. const cap_value_t* const keepcaps = (mode == CAP_MODE_BASIC) ? keepcapsBasic : keepcapsStrict;
  834. const size_t ncap = (mode == CAP_MODE_BASIC) ? ARRAYSIZE(keepcapsBasic) : ARRAYSIZE(keepcapsStrict);
  835. cap_t caps = cap_init();
  836. if (caps == NULL) {
  837. fprintf(stderr, "Error: can not initialize capabilities: %s\n", strerror(errno));
  838. return -1;
  839. }
  840. if (cap_clear(caps) < 0) {
  841. fprintf(stderr, "Error: can not clear capabilities: %s\n", strerror(errno));
  842. cap_free(caps);
  843. return -1;
  844. }
  845. cap_t currCaps = cap_get_proc();
  846. if (currCaps == NULL) {
  847. fprintf(stderr, "Error: can not get current process capabilities: %s\n", strerror(errno));
  848. cap_free(caps);
  849. return -1;
  850. }
  851. for (size_t i = 0; i < ncap; i++) {
  852. if (!CAP_IS_SUPPORTED(keepcaps[i]))
  853. continue;
  854. cap_flag_value_t current;
  855. if (cap_get_flag(currCaps, keepcaps[i], CAP_PERMITTED, &current) < 0) {
  856. fprintf(stderr, "Error: can not get current value of capability %d: %s\n", keepcaps[i], strerror(errno));
  857. cap_free(currCaps);
  858. cap_free(caps);
  859. return -1;
  860. }
  861. if (current != CAP_SET)
  862. continue;
  863. if (cap_set_flag(caps, CAP_PERMITTED, 1, &keepcaps[i], CAP_SET) < 0) {
  864. fprintf(stderr, "Error: can not set permitted capability %d: %s\n", keepcaps[i], strerror(errno));
  865. cap_free(currCaps);
  866. cap_free(caps);
  867. return -1;
  868. }
  869. if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &keepcaps[i], CAP_SET) < 0) {
  870. fprintf(stderr, "Error: can not set effective capability %d: %s\n", keepcaps[i], strerror(errno));
  871. cap_free(currCaps);
  872. cap_free(caps);
  873. return -1;
  874. }
  875. }
  876. if (cap_set_proc(caps) < 0) {
  877. fprintf(stderr, "Error: can not set process capabilities: %s\n", strerror(errno));
  878. cap_free(currCaps);
  879. cap_free(caps);
  880. return -1;
  881. }
  882. cap_free(currCaps);
  883. cap_free(caps);
  884. return 0;
  885. }
  886. #endif
  887. bool Platform_init(void) {
  888. #ifdef HAVE_LIBCAP
  889. if (dropCapabilities(Platform_capabilitiesMode) < 0)
  890. return false;
  891. #endif
  892. if (access(PROCDIR, R_OK) != 0) {
  893. fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
  894. return false;
  895. }
  896. #ifdef HAVE_SENSORS_SENSORS_H
  897. LibSensors_init();
  898. #endif
  899. char target[PATH_MAX];
  900. ssize_t ret = readlink(PROCDIR "/self/ns/pid", target, sizeof(target) - 1);
  901. if (ret > 0) {
  902. target[ret] = '\0';
  903. if (!String_eq("pid:[4026531836]", target)) { // magic constant PROC_PID_INIT_INO from include/linux/proc_ns.h#L46
  904. Running_containerized = true;
  905. return true; // early return
  906. }
  907. }
  908. FILE* fp = fopen(PROCDIR "/1/mounts", "r");
  909. if (fp) {
  910. char lineBuffer[256];
  911. while (fgets(lineBuffer, sizeof(lineBuffer), fp)) {
  912. // detect lxc or overlayfs and guess that this means we are running containerized
  913. if (String_startsWith(lineBuffer, "lxcfs /proc") || String_startsWith(lineBuffer, "overlay / overlay")) {
  914. Running_containerized = true;
  915. break;
  916. }
  917. }
  918. fclose(fp);
  919. }
  920. return true;
  921. }
  922. void Platform_done(void) {
  923. #ifdef HAVE_SENSORS_SENSORS_H
  924. LibSensors_cleanup();
  925. #endif
  926. }