Platform.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /*
  2. htop - netbsd/Platform.c
  3. (C) 2014 Hisham H. Muhammad
  4. (C) 2015 Michael McConville
  5. (C) 2021 Santhosh Raju
  6. (C) 2021 Nia Alarie
  7. (C) 2021 htop dev team
  8. Released under the GNU GPLv2+, see the COPYING file
  9. in the source distribution for its full text.
  10. */
  11. #include "config.h" // IWYU pragma: keep
  12. #include "netbsd/Platform.h"
  13. #include <errno.h>
  14. #include <fcntl.h>
  15. #include <ifaddrs.h>
  16. #include <paths.h>
  17. #include <unistd.h>
  18. #include <kvm.h>
  19. #include <limits.h>
  20. #include <math.h>
  21. #include <stdbool.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <time.h>
  25. #include <net/if.h>
  26. #include <prop/proplib.h>
  27. #include <sys/envsys.h>
  28. #include <sys/iostat.h>
  29. #include <sys/param.h>
  30. #include <sys/resource.h>
  31. #include <sys/socket.h>
  32. #include <sys/sysctl.h>
  33. #include <sys/time.h>
  34. #include <sys/types.h>
  35. #include "CPUMeter.h"
  36. #include "ClockMeter.h"
  37. #include "DateMeter.h"
  38. #include "DateTimeMeter.h"
  39. #include "FileDescriptorMeter.h"
  40. #include "HostnameMeter.h"
  41. #include "LoadAverageMeter.h"
  42. #include "Macros.h"
  43. #include "MemoryMeter.h"
  44. #include "MemorySwapMeter.h"
  45. #include "Meter.h"
  46. #include "Settings.h"
  47. #include "SignalsPanel.h"
  48. #include "SwapMeter.h"
  49. #include "SysArchMeter.h"
  50. #include "TasksMeter.h"
  51. #include "UptimeMeter.h"
  52. #include "XUtils.h"
  53. #include "generic/fdstat_sysctl.h"
  54. #include "netbsd/NetBSDMachine.h"
  55. #include "netbsd/NetBSDProcess.h"
  56. /*
  57. * The older proplib APIs will be deprecated in NetBSD 10, but we still
  58. * want to support the 9.x stable branch.
  59. *
  60. * Create aliases for the newer functions that are missing from 9.x.
  61. */
  62. #if !__NetBSD_Prereq__(9,99,65)
  63. #define prop_string_equals_string prop_string_equals_cstring
  64. #define prop_number_signed_value prop_number_integer_value
  65. #endif
  66. const ScreenDefaults Platform_defaultScreens[] = {
  67. {
  68. .name = "Main",
  69. .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
  70. .sortKey = "PERCENT_CPU",
  71. },
  72. };
  73. const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens);
  74. /*
  75. * See /usr/include/sys/signal.h
  76. */
  77. const SignalItem Platform_signals[] = {
  78. { .name = " 0 Cancel", .number = 0 },
  79. { .name = " 1 SIGHUP", .number = 1 },
  80. { .name = " 2 SIGINT", .number = 2 },
  81. { .name = " 3 SIGQUIT", .number = 3 },
  82. { .name = " 4 SIGILL", .number = 4 },
  83. { .name = " 5 SIGTRAP", .number = 5 },
  84. { .name = " 6 SIGABRT", .number = 6 },
  85. { .name = " 6 SIGIOT", .number = 6 },
  86. { .name = " 7 SIGEMT", .number = 7 },
  87. { .name = " 8 SIGFPE", .number = 8 },
  88. { .name = " 9 SIGKILL", .number = 9 },
  89. { .name = "10 SIGBUS", .number = 10 },
  90. { .name = "11 SIGSEGV", .number = 11 },
  91. { .name = "12 SIGSYS", .number = 12 },
  92. { .name = "13 SIGPIPE", .number = 13 },
  93. { .name = "14 SIGALRM", .number = 14 },
  94. { .name = "15 SIGTERM", .number = 15 },
  95. { .name = "16 SIGURG", .number = 16 },
  96. { .name = "17 SIGSTOP", .number = 17 },
  97. { .name = "18 SIGTSTP", .number = 18 },
  98. { .name = "19 SIGCONT", .number = 19 },
  99. { .name = "20 SIGCHLD", .number = 20 },
  100. { .name = "21 SIGTTIN", .number = 21 },
  101. { .name = "22 SIGTTOU", .number = 22 },
  102. { .name = "23 SIGIO", .number = 23 },
  103. { .name = "24 SIGXCPU", .number = 24 },
  104. { .name = "25 SIGXFSZ", .number = 25 },
  105. { .name = "26 SIGVTALRM", .number = 26 },
  106. { .name = "27 SIGPROF", .number = 27 },
  107. { .name = "28 SIGWINCH", .number = 28 },
  108. { .name = "29 SIGINFO", .number = 29 },
  109. { .name = "30 SIGUSR1", .number = 30 },
  110. { .name = "31 SIGUSR2", .number = 31 },
  111. { .name = "32 SIGPWR", .number = 32 },
  112. { .name = "33 SIGRTMIN", .number = 33 },
  113. { .name = "34 SIGRTMIN+1", .number = 34 },
  114. { .name = "35 SIGRTMIN+2", .number = 35 },
  115. { .name = "36 SIGRTMIN+3", .number = 36 },
  116. { .name = "37 SIGRTMIN+4", .number = 37 },
  117. { .name = "38 SIGRTMIN+5", .number = 38 },
  118. { .name = "39 SIGRTMIN+6", .number = 39 },
  119. { .name = "40 SIGRTMIN+7", .number = 40 },
  120. { .name = "41 SIGRTMIN+8", .number = 41 },
  121. { .name = "42 SIGRTMIN+9", .number = 42 },
  122. { .name = "43 SIGRTMIN+10", .number = 43 },
  123. { .name = "44 SIGRTMIN+11", .number = 44 },
  124. { .name = "45 SIGRTMIN+12", .number = 45 },
  125. { .name = "46 SIGRTMIN+13", .number = 46 },
  126. { .name = "47 SIGRTMIN+14", .number = 47 },
  127. { .name = "48 SIGRTMIN+15", .number = 48 },
  128. { .name = "49 SIGRTMIN+16", .number = 49 },
  129. { .name = "50 SIGRTMIN+17", .number = 50 },
  130. { .name = "51 SIGRTMIN+18", .number = 51 },
  131. { .name = "52 SIGRTMIN+19", .number = 52 },
  132. { .name = "53 SIGRTMIN+20", .number = 53 },
  133. { .name = "54 SIGRTMIN+21", .number = 54 },
  134. { .name = "55 SIGRTMIN+22", .number = 55 },
  135. { .name = "56 SIGRTMIN+23", .number = 56 },
  136. { .name = "57 SIGRTMIN+24", .number = 57 },
  137. { .name = "58 SIGRTMIN+25", .number = 58 },
  138. { .name = "59 SIGRTMIN+26", .number = 59 },
  139. { .name = "60 SIGRTMIN+27", .number = 60 },
  140. { .name = "61 SIGRTMIN+28", .number = 61 },
  141. { .name = "62 SIGRTMIN+29", .number = 62 },
  142. { .name = "63 SIGRTMAX", .number = 63 },
  143. };
  144. const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
  145. const MeterClass* const Platform_meterTypes[] = {
  146. &CPUMeter_class,
  147. &ClockMeter_class,
  148. &DateMeter_class,
  149. &DateTimeMeter_class,
  150. &LoadAverageMeter_class,
  151. &LoadMeter_class,
  152. &MemoryMeter_class,
  153. &SwapMeter_class,
  154. &MemorySwapMeter_class,
  155. &TasksMeter_class,
  156. &UptimeMeter_class,
  157. &BatteryMeter_class,
  158. &HostnameMeter_class,
  159. &SysArchMeter_class,
  160. &AllCPUsMeter_class,
  161. &AllCPUs2Meter_class,
  162. &AllCPUs4Meter_class,
  163. &AllCPUs8Meter_class,
  164. &LeftCPUsMeter_class,
  165. &RightCPUsMeter_class,
  166. &LeftCPUs2Meter_class,
  167. &RightCPUs2Meter_class,
  168. &LeftCPUs4Meter_class,
  169. &RightCPUs4Meter_class,
  170. &LeftCPUs8Meter_class,
  171. &RightCPUs8Meter_class,
  172. &BlankMeter_class,
  173. &DiskIOMeter_class,
  174. &NetworkIOMeter_class,
  175. &FileDescriptorMeter_class,
  176. NULL
  177. };
  178. bool Platform_init(void) {
  179. /* no platform-specific setup needed */
  180. return true;
  181. }
  182. void Platform_done(void) {
  183. /* no platform-specific cleanup needed */
  184. }
  185. void Platform_setBindings(Htop_Action* keys) {
  186. /* no platform-specific key bindings */
  187. (void) keys;
  188. }
  189. int Platform_getUptime(void) {
  190. struct timeval bootTime, currTime;
  191. const int mib[2] = { CTL_KERN, KERN_BOOTTIME };
  192. size_t size = sizeof(bootTime);
  193. int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
  194. if (err) {
  195. return -1;
  196. }
  197. gettimeofday(&currTime, NULL);
  198. return (int) difftime(currTime.tv_sec, bootTime.tv_sec);
  199. }
  200. void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
  201. struct loadavg loadAverage;
  202. const int mib[2] = { CTL_VM, VM_LOADAVG };
  203. size_t size = sizeof(loadAverage);
  204. int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0);
  205. if (err) {
  206. *one = 0;
  207. *five = 0;
  208. *fifteen = 0;
  209. return;
  210. }
  211. *one = (double) loadAverage.ldavg[0] / loadAverage.fscale;
  212. *five = (double) loadAverage.ldavg[1] / loadAverage.fscale;
  213. *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale;
  214. }
  215. pid_t Platform_getMaxPid(void) {
  216. // https://nxr.netbsd.org/xref/src/sys/sys/ansi.h#__pid_t
  217. // pid is assigned as a 32bit Integer.
  218. return INT32_MAX;
  219. }
  220. double Platform_setCPUValues(Meter* this, int cpu) {
  221. const Machine* host = this->host;
  222. const NetBSDMachine* nhost = (const NetBSDMachine*) host;
  223. const CPUData* cpuData = &nhost->cpuData[cpu];
  224. double total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod;
  225. double totalPercent;
  226. double* v = this->values;
  227. v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0;
  228. v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0;
  229. if (host->settings->detailedCPUTime) {
  230. v[CPU_METER_KERNEL] = cpuData->sysPeriod / total * 100.0;
  231. v[CPU_METER_IRQ] = cpuData->intrPeriod / total * 100.0;
  232. v[CPU_METER_SOFTIRQ] = 0.0;
  233. v[CPU_METER_STEAL] = 0.0;
  234. v[CPU_METER_GUEST] = 0.0;
  235. v[CPU_METER_IOWAIT] = 0.0;
  236. v[CPU_METER_FREQUENCY] = NAN;
  237. this->curItems = 8;
  238. } else {
  239. v[CPU_METER_KERNEL] = cpuData->sysAllPeriod / total * 100.0;
  240. v[CPU_METER_IRQ] = 0.0; // No steal nor guest on NetBSD
  241. this->curItems = 4;
  242. }
  243. totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ];
  244. totalPercent = CLAMP(totalPercent, 0.0, 100.0);
  245. v[CPU_METER_FREQUENCY] = cpuData->frequency;
  246. v[CPU_METER_TEMPERATURE] = NAN;
  247. return totalPercent;
  248. }
  249. void Platform_setMemoryValues(Meter* this) {
  250. const Machine* host = this->host;
  251. this->total = host->totalMem;
  252. this->values[MEMORY_METER_USED] = host->usedMem;
  253. // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm"
  254. // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux"
  255. this->values[MEMORY_METER_BUFFERS] = host->buffersMem;
  256. this->values[MEMORY_METER_CACHE] = host->cachedMem;
  257. // this->values[MEMORY_METER_AVAILABLE] = "available memory"
  258. }
  259. void Platform_setSwapValues(Meter* this) {
  260. const Machine* host = this->host;
  261. this->total = host->totalSwap;
  262. this->values[SWAP_METER_USED] = host->usedSwap;
  263. // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux"
  264. // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux"
  265. }
  266. char* Platform_getProcessEnv(pid_t pid) {
  267. char errbuf[_POSIX2_LINE_MAX];
  268. char* env;
  269. char** ptr;
  270. int count;
  271. kvm_t* kt;
  272. const struct kinfo_proc2* kproc;
  273. size_t capacity = 4096, size = 0;
  274. if ((kt = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) {
  275. return NULL;
  276. }
  277. if ((kproc = kvm_getproc2(kt, KERN_PROC_PID, pid, sizeof(struct kinfo_proc2), &count)) == NULL) {
  278. (void) kvm_close(kt);
  279. return NULL;
  280. }
  281. if ((ptr = kvm_getenvv2(kt, kproc, 0)) == NULL) {
  282. (void) kvm_close(kt);
  283. return NULL;
  284. }
  285. env = xMalloc(capacity);
  286. for (char** p = ptr; *p; p++) {
  287. size_t len = strlen(*p) + 1;
  288. while (size + len > capacity) {
  289. if (capacity > (SIZE_MAX / 2)) {
  290. free(env);
  291. env = NULL;
  292. goto end;
  293. }
  294. capacity *= 2;
  295. env = xRealloc(env, capacity);
  296. }
  297. String_safeStrncpy(env + size, *p, len);
  298. size += len;
  299. }
  300. if (size < 2 || env[size - 1] || env[size - 2]) {
  301. if (size + 2 < capacity)
  302. env = xRealloc(env, capacity + 2);
  303. env[size] = 0;
  304. env[size + 1] = 0;
  305. }
  306. end:
  307. (void) kvm_close(kt);
  308. return env;
  309. }
  310. FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
  311. (void)pid;
  312. return NULL;
  313. }
  314. void Platform_getFileDescriptors(double* used, double* max) {
  315. Generic_getFileDescriptors_sysctl(used, max);
  316. }
  317. bool Platform_getDiskIO(DiskIOData* data) {
  318. const int mib[] = { CTL_HW, HW_IOSTATS, sizeof(struct io_sysctl) };
  319. struct io_sysctl* iostats = NULL;
  320. size_t size = 0;
  321. for (int retry = 3; retry > 0; retry--) {
  322. /* get the size of the IO statistic array */
  323. if (sysctl(mib, __arraycount(mib), iostats, &size, NULL, 0) < 0)
  324. CRT_fatalError("Unable to get size of io_sysctl");
  325. if (size == 0) {
  326. free(iostats);
  327. return false;
  328. }
  329. iostats = xRealloc(iostats, size);
  330. errno = 0;
  331. if (sysctl(mib, __arraycount(mib), iostats, &size, NULL, 0) == 0)
  332. break;
  333. if (errno != ENOMEM)
  334. CRT_fatalError("Unable to get disk IO statistics");
  335. }
  336. if (errno == ENOMEM)
  337. CRT_fatalError("Unable to get disk IO statistics");
  338. uint64_t bytesReadSum = 0;
  339. uint64_t bytesWriteSum = 0;
  340. uint64_t busyTimeSum = 0;
  341. for (size_t i = 0, count = size / sizeof(struct io_sysctl); i < count; i++) {
  342. /* ignore NFS activity */
  343. if (iostats[i].type != IOSTAT_DISK)
  344. continue;
  345. bytesReadSum += iostats[i].rbytes;
  346. bytesWriteSum += iostats[i].wbytes;
  347. busyTimeSum += iostats[i].busysum_usec;
  348. }
  349. data->totalBytesRead = bytesReadSum;
  350. data->totalBytesWritten = bytesWriteSum;
  351. data->totalMsTimeSpend = busyTimeSum / 1000;
  352. free(iostats);
  353. return true;
  354. }
  355. bool Platform_getNetworkIO(NetworkIOData* data) {
  356. struct ifaddrs* ifaddrs = NULL;
  357. if (getifaddrs(&ifaddrs) != 0)
  358. return false;
  359. for (const struct ifaddrs* ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
  360. if (!ifa->ifa_addr)
  361. continue;
  362. if (ifa->ifa_addr->sa_family != AF_LINK)
  363. continue;
  364. if (ifa->ifa_flags & IFF_LOOPBACK)
  365. continue;
  366. const struct if_data* ifd = (const struct if_data*)ifa->ifa_data;
  367. data->bytesReceived += ifd->ifi_ibytes;
  368. data->packetsReceived += ifd->ifi_ipackets;
  369. data->bytesTransmitted += ifd->ifi_obytes;
  370. data->packetsTransmitted += ifd->ifi_opackets;
  371. }
  372. freeifaddrs(ifaddrs);
  373. return true;
  374. }
  375. void Platform_getBattery(double* percent, ACPresence* isOnAC) {
  376. prop_dictionary_t dict, fields, props;
  377. prop_object_t device, class;
  378. intmax_t totalCharge = 0;
  379. intmax_t totalCapacity = 0;
  380. *percent = NAN;
  381. *isOnAC = AC_ERROR;
  382. int fd = open(_PATH_SYSMON, O_RDONLY);
  383. if (fd == -1)
  384. goto error;
  385. if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict) != 0)
  386. goto error;
  387. prop_object_iterator_t devIter = prop_dictionary_iterator(dict);
  388. if (devIter == NULL)
  389. goto error;
  390. while ((device = prop_object_iterator_next(devIter)) != NULL) {
  391. prop_object_t fieldsArray = prop_dictionary_get_keysym(dict, device);
  392. if (fieldsArray == NULL)
  393. goto error;
  394. prop_object_iterator_t fieldsIter = prop_array_iterator(fieldsArray);
  395. if (fieldsIter == NULL)
  396. goto error;
  397. bool isACAdapter = false;
  398. bool isBattery = false;
  399. /* only assume battery is not present if explicitly stated */
  400. intmax_t isPresent = 1;
  401. intmax_t isConnected = 0;
  402. intmax_t curCharge = 0;
  403. intmax_t maxCharge = 0;
  404. while ((fields = prop_object_iterator_next(fieldsIter)) != NULL) {
  405. props = prop_dictionary_get(fields, "device-properties");
  406. if (props != NULL) {
  407. class = prop_dictionary_get(props, "device-class");
  408. if (prop_string_equals_string(class, "ac-adapter")) {
  409. isACAdapter = true;
  410. } else if (prop_string_equals_string(class, "battery")) {
  411. isBattery = true;
  412. }
  413. continue;
  414. }
  415. prop_object_t curValue = prop_dictionary_get(fields, "cur-value");
  416. prop_object_t maxValue = prop_dictionary_get(fields, "max-value");
  417. prop_object_t descField = prop_dictionary_get(fields, "description");
  418. if (descField == NULL || curValue == NULL)
  419. continue;
  420. if (prop_string_equals_string(descField, "connected")) {
  421. isConnected = prop_number_signed_value(curValue);
  422. } else if (prop_string_equals_string(descField, "present")) {
  423. isPresent = prop_number_signed_value(curValue);
  424. } else if (prop_string_equals_string(descField, "charge")) {
  425. if (maxValue == NULL)
  426. continue;
  427. curCharge = prop_number_signed_value(curValue);
  428. maxCharge = prop_number_signed_value(maxValue);
  429. }
  430. }
  431. if (isBattery && isPresent) {
  432. totalCharge += curCharge;
  433. totalCapacity += maxCharge;
  434. }
  435. if (isACAdapter && *isOnAC != AC_PRESENT) {
  436. *isOnAC = isConnected ? AC_PRESENT : AC_ABSENT;
  437. }
  438. }
  439. *percent = ((double)totalCharge / (double)totalCapacity) * 100.0;
  440. error:
  441. if (fd != -1)
  442. close(fd);
  443. }