/* * Copyright (c) 2018 Alastair Roy Poole * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(__linux__) # include #endif #include #include #include #include #if defined(__APPLE__) && defined(__MACH__) #define __MacOS__ # include # include # include # include # include # include #endif #if defined(__OpenBSD__) # include # include # include # include # include # include #endif #if defined(__FreeBSD__) || defined(__DragonFly__) # include # include # include #endif #include "machine.h" #include "macros.h" #if defined(__OpenBSD__) # define CPU_STATES 6 #else # define CPU_STATES 5 #endif #if defined(__linux__) static char * file_contents(const char *path) { FILE *f; char *buf, *tmp; size_t n = 1, len = 0; const size_t block = 4096; f = fopen(path, "r"); if (!f) return NULL; buf = NULL; while ((!feof(f)) && (!ferror(f))) { tmp = realloc(buf, ++n * (sizeof(char) * block) + 1); if (!tmp) return NULL; buf = tmp; len += fread(buf + len, sizeof(char), block, f); } if (ferror(f)) { free(buf); fclose(f); return NULL; } fclose(f); buf[len] = 0; return buf; } #endif #if defined(__FreeBSD__) || defined(__DragonFly__) static long int _sysctlfromname(const char *name, void *mib, int depth, size_t *len) { long int result; if (sysctlnametomib(name, mib, len) < 0) return -1; *len = sizeof(result); if (sysctl(mib, depth, &result, len, NULL, 0) < 0) return -1; return result; } #endif static int cpu_count(void) { static int cores = 0; if (cores != 0) return cores; #if defined(__linux__) char buf[4096]; FILE *f; int line = 0; f = fopen("/proc/stat", "r"); if (!f) return 0; while (fgets(buf, sizeof(buf), f)) { if (line) { if (!strncmp(buf, "cpu", 3)) cores++; else break; } line++; } fclose(f); #elif defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) size_t len; int mib[2] = { CTL_HW, HW_NCPU }; len = sizeof(cores); if (sysctl(mib, 2, &cores, &len, NULL, 0) < 0) return 0; #endif return cores; } int system_cpu_online_count_get(void) { #if defined(__OpenBSD__) static int cores = 0; if (cores != 0) return cores; size_t len; int mib[2] = { CTL_HW, HW_NCPUONLINE }; len = sizeof(cores); if (sysctl(mib, 2, &cores, &len, NULL, 0) < 0) return cpu_count(); return cores; #else return cpu_count(); #endif } static void _cpu_state_get(cpu_core_t **cores, int ncpu) { int diff_total, diff_idle; double ratio, percent; unsigned long total, idle, used; cpu_core_t *core; #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) size_t size; int i, j; #endif #if defined(__FreeBSD__) || defined(__DragonFly__) if (!ncpu) return; size = sizeof(unsigned long) * (CPU_STATES * ncpu); unsigned long cpu_times[ncpu][CPU_STATES]; if (sysctlbyname("kern.cp_times", cpu_times, &size, NULL, 0) < 0) return; for (i = 0; i < ncpu; i++) { core = cores[i]; unsigned long *cpu = cpu_times[i]; total = 0; for (j = 0; j < CPU_STATES; j++) total += cpu[j]; idle = cpu[4]; diff_total = total - core->total; diff_idle = idle - core->idle; if (diff_total == 0) diff_total = 1; ratio = diff_total / 100.0; used = diff_total - diff_idle; percent = used / ratio; if (percent > 100) percent = 100; else if (percent < 0) percent = 0; core->percent = percent; core->total = total; core->idle = idle; } #elif defined(__OpenBSD__) static struct cpustats cpu_times[CPU_STATES]; static int cpu_time_mib[] = { CTL_KERN, KERN_CPUSTATS, 0 }; memset(&cpu_times, 0, CPU_STATES * sizeof(struct cpustats)); if (!ncpu) return; for (i = 0; i < ncpu; i++) { core = cores[i]; size = sizeof(struct cpustats); cpu_time_mib[2] = i; if (sysctl(cpu_time_mib, 3, &cpu_times[i], &size, NULL, 0) < 0) return; total = 0; for (j = 0; j < CPU_STATES; j++) total += cpu_times[i].cs_time[j]; idle = cpu_times[i].cs_time[CP_IDLE]; diff_total = total - core->total; if (diff_total == 0) diff_total = 1; diff_idle = idle - core->idle; ratio = diff_total / 100.0; used = diff_total - diff_idle; percent = used / ratio; if (percent > 100) percent = 100; else if (percent < 0) percent = 0; core->percent = percent; core->total = total; core->idle = idle; } #elif defined(__linux__) char *buf, name[128]; int i; buf = file_contents("/proc/stat"); if (!buf) return; for (i = 0; i < ncpu; i++) { core = cores[i]; snprintf(name, sizeof(name), "cpu%d", i); char *line = strstr(buf, name); if (line) { line = strchr(line, ' ') + 1; unsigned long cpu_times[4] = { 0 }; if (4 != sscanf(line, "%lu %lu %lu %lu", &cpu_times[0], &cpu_times[1], &cpu_times[2], &cpu_times[3])) return; total = cpu_times[0] + cpu_times[1] + cpu_times[2] + cpu_times[3]; idle = cpu_times[3]; diff_total = total - core->total; if (diff_total == 0) diff_total = 1; diff_idle = idle - core->idle; ratio = diff_total / 100.0; used = diff_total - diff_idle; percent = used / ratio; if (percent > 100) percent = 100; else if (percent < 0) percent = 0; core->percent = percent; core->total = total; core->idle = idle; } } free(buf); #elif defined(__MacOS__) mach_msg_type_number_t count; processor_cpu_load_info_t load; mach_port_t mach_port; unsigned int cpu_count; int i; cpu_count = ncpu; count = HOST_CPU_LOAD_INFO_COUNT; mach_port = mach_host_self(); if (host_processor_info(mach_port, PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)&load, &count) != KERN_SUCCESS) exit(-1); for (i = 0; i < ncpu; i++) { core = cores[i]; total = load[i].cpu_ticks[CPU_STATE_USER] + load[i].cpu_ticks[CPU_STATE_SYSTEM] + load[i].cpu_ticks[CPU_STATE_IDLE] + load[i].cpu_ticks[CPU_STATE_NICE]; idle = load[i].cpu_ticks[CPU_STATE_IDLE]; diff_total = total - core->total; if (diff_total == 0) diff_total = 1; diff_idle = idle - core->idle; ratio = diff_total / 100.0; used = diff_total - diff_idle; percent = used / ratio; if (percent > 100) percent = 100; else if (percent < 0) percent = 0; core->percent = percent; core->total = total; core->idle = idle; } #endif } cpu_core_t ** system_cpu_usage_delayed_get(int *ncpu, int usecs) { cpu_core_t **cores; int i; *ncpu = cpu_count(); cores = malloc((*ncpu) * sizeof(cpu_core_t *)); for (i = 0; i < *ncpu; i++) cores[i] = calloc(1, sizeof(cpu_core_t)); _cpu_state_get(cores, *ncpu); usleep(usecs); _cpu_state_get(cores, *ncpu); return cores; } cpu_core_t ** system_cpu_usage_get(int *ncpu) { return system_cpu_usage_delayed_get(ncpu, 1000000); } #if defined(__linux__) static unsigned long _meminfo_parse_line(const char *line) { char *p, *tok; p = strchr(line, ':') + 1; while (isspace(*p)) p++; tok = strtok(p, " "); return atol(tok); } #endif void system_memory_usage_get(meminfo_t *memory) { #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) size_t len = 0, miblen; int i = 0; #endif memset(memory, 0, sizeof(meminfo_t)); #if defined(__linux__) FILE *f; unsigned long swap_free = 0, tmp_free = 0, tmp_slab = 0; char line[256]; int fields = 0; f = fopen("/proc/meminfo", "r"); if (!f) return; while (fgets(line, sizeof(line), f) != NULL) { if (!strncmp("MemTotal:", line, 9)) { memory->total = _meminfo_parse_line(line); fields++; } else if (!strncmp("MemFree:", line, 8)) { tmp_free = _meminfo_parse_line(line); fields++; } else if (!strncmp("Cached:", line, 7)) { memory->cached = _meminfo_parse_line(line); fields++; } else if (!strncmp("Slab:", line, 5)) { tmp_slab = _meminfo_parse_line(line); fields++; } else if (!strncmp("Buffers:", line, 8)) { memory->buffered = _meminfo_parse_line(line); fields++; } else if (!strncmp("Shmem:", line, 6)) { memory->shared = _meminfo_parse_line(line); fields++; } else if (!strncmp("SwapTotal:", line, 10)) { memory->swap_total = _meminfo_parse_line(line); fields++; } else if (!strncmp("SwapFree:", line, 9)) { swap_free = _meminfo_parse_line(line); fields++; } if (fields >= 8) break; } memory->cached += tmp_slab; memory->used = memory->total - tmp_free - memory->cached - memory->buffered; memory->swap_used = memory->swap_total - swap_free; memory->total *= 1024; memory->used *= 1024; memory->buffered *= 1024; memory->cached *= 1024; memory->shared *= 1024; memory->swap_total *= 1024; memory->swap_used *= 1024; fclose(f); #elif defined(__FreeBSD__) || defined(__DragonFly__) unsigned int free = 0, active = 0, inactive = 0, wired = 0; unsigned int cached = 0, buffered = 0, zfs_arc = 0; long int result = 0; int page_size = getpagesize(); int mib[5] = { CTL_HW, HW_PHYSMEM, 0, 0, 0 }; len = sizeof(memory->total); if (sysctl(mib, 2, &memory->total, &len, NULL, 0) == -1) return; if ((active = _sysctlfromname("vm.stats.vm.v_active_count", mib, 4, &len)) < 0) return; if ((inactive = _sysctlfromname("vm.stats.vm.v_inactive_count", mib, 4, &len)) < 0) return; if ((wired = _sysctlfromname("vm.stats.vm.v_wire_count", mib, 4, &len)) < 0) return; if ((cached = _sysctlfromname("vm.stats.vm.v_cache_count", mib, 4, &len)) < 0) return; if ((free = _sysctlfromname("vm.stats.vm.v_free_count", mib, 4, &len)) < 0) return; if ((buffered = _sysctlfromname("vfs.bufspace", mib, 2, &len)) < 0) return; memory->used = ((active + wired + cached) * page_size); memory->buffered = buffered; memory->cached = (cached * page_size); result = _sysctlfromname("vm.swap_total", mib, 2, &len); if (result < 0) return; memory->swap_total = result; miblen = 3; if (sysctlnametomib("vm.swap_info", mib, &miblen) == -1) return; if ((zfs_arc = _sysctlfromname("kstat.zfs.misc.arcstats.c", mib, 5, &len)) != -1) { memory->zfs_arc_used = zfs_arc; } struct xswdev xsw; for (i = 0; ; i++) { mib[miblen] = i; len = sizeof(xsw); if (sysctl(mib, miblen + 1, &xsw, &len, NULL, 0) == -1) break; memory->swap_used += (unsigned long) xsw.xsw_used * page_size; } #elif defined(__OpenBSD__) static int mib[] = { CTL_HW, HW_PHYSMEM64 }; static int bcstats_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT }; struct bcachestats bcstats; static int uvmexp_mib[] = { CTL_VM, VM_UVMEXP }; struct uvmexp uvmexp; int nswap, rnswap; struct swapent *swdev = NULL; (void) miblen; len = sizeof(memory->total); if (sysctl(mib, 2, &memory->total, &len, NULL, 0) == -1) return; len = sizeof(uvmexp); if (sysctl(uvmexp_mib, 2, &uvmexp, &len, NULL, 0) == -1) return; len = sizeof(bcstats); if (sysctl(bcstats_mib, 3, &bcstats, &len, NULL, 0) == -1) return; nswap = swapctl(SWAP_NSWAP, 0, 0); if (nswap == 0) goto swap_out; swdev = calloc(nswap, sizeof(*swdev)); if (swdev == NULL) goto swap_out; rnswap = swapctl(SWAP_STATS, swdev, nswap); if (rnswap == -1) goto swap_out; for (i = 0; i < nswap; i++) { if (swdev[i].se_flags & SWF_ENABLE) { memory->swap_used += (swdev[i].se_inuse / (1024 / DEV_BSIZE)); memory->swap_total += (swdev[i].se_nblks / (1024 / DEV_BSIZE)); } } memory->swap_total *= 1024; memory->swap_used *= 1024; swap_out: if (swdev) free(swdev); memory->cached = U64(uvmexp.pagesize) * U64(bcstats.numbufpages); memory->used = U64(uvmexp.pagesize) * U64(uvmexp.active); memory->buffered = U64(uvmexp.pagesize) * (U64(uvmexp.npages) - U64(uvmexp.free)); memory->shared = U64(uvmexp.pagesize) * U64(uvmexp.wired); #elif defined(__MacOS__) int mib[2] = { CTL_HW, HW_MEMSIZE }; size_t total; vm_size_t page_size; mach_port_t mach_port; mach_msg_type_number_t count; vm_statistics64_data_t vm_stats; struct xsw_usage xsu; size_t len = sizeof(size_t); if (sysctl(mib, 2, &total, &len, NULL, 0) == -1) return; mach_port = mach_host_self(); count = sizeof(vm_stats) / sizeof(natural_t); memory->total = total; if (host_page_size(mach_port, &page_size) == KERN_SUCCESS && host_statistics64(mach_port, HOST_VM_INFO, (host_info64_t)&vm_stats, &count) == KERN_SUCCESS) { memory->used = (vm_stats.active_count + vm_stats.wire_count) * page_size; memory->cached = vm_stats.active_count * page_size; memory->shared = vm_stats.wire_count * page_size; memory->buffered = vm_stats.inactive_count * page_size; } total = sizeof(xsu); if (sysctlbyname("vm.swapusage", &xsu, &total, NULL, 0) != -1) { memory->swap_total = xsu.xsu_total; memory->swap_used = xsu.xsu_used; } #endif } sensor_t ** system_sensors_thermal_get(int *sensor_count) { sensor_t **sensors = NULL; #if defined(__OpenBSD__) sensor_t *sensor; int mibs[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 }; int devn, n; struct sensor snsr; size_t slen = sizeof(struct sensor); struct sensordev snsrdev; size_t sdlen = sizeof(struct sensordev); for (devn = 0;; devn++) { mibs[2] = devn; if (sysctl(mibs, 3, &snsrdev, &sdlen, NULL, 0) == -1) { if (errno == ENOENT) break; continue; } for (n = 0; n < snsrdev.maxnumt[SENSOR_TEMP]; n++) { mibs[4] = n; if (sysctl(mibs, 5, &snsr, &slen, NULL, 0) == -1) continue; if (slen > 0 && (snsr.flags & SENSOR_FINVALID) == 0) break; } if (sysctl(mibs, 5, &snsr, &slen, NULL, 0) == -1) continue; if (snsr.type != SENSOR_TEMP) continue; sensors = realloc(sensors, (1 + *sensor_count) * sizeof(sensor_t *)); sensors[(*sensor_count)++] = sensor = calloc(1, sizeof(sensor_t)); sensor->name = strdup(snsrdev.xname); sensor->value = (snsr.value - 273150000) / 1000000.0; // (uK -> C) } #elif defined(__FreeBSD__) || defined(__DragonFly__) sensor_t *sensor; int value; size_t len = sizeof(value); if ((sysctlbyname("hw.acpi.thermal.tz0.temperature", &value, &len, NULL, 0)) != -1) { sensors = realloc(sensors, (1 + *sensor_count) * sizeof(sensor_t *)); sensors[(*sensor_count)++] = sensor = calloc(1, sizeof(sensor_t)); sensor->name = strdup("hw.acpi.thermal.tz0"); sensor->value = (float) (value - 2732) / 10; } #elif defined(__linux__) sensor_t *sensor; char *type, *value; char path[PATH_MAX]; struct dirent **names; int i, n; n = scandir("/sys/class/thermal", &names, 0, alphasort); if (n < 0) return NULL; for (i = 0; i < n; i++) { if (strncmp(names[i]->d_name, "thermal_zone", 12)) { free(names[i]); continue; } snprintf(path, sizeof(path), "/sys/class/thermal/%s/type", names[i]->d_name); type = file_contents(path); if (type) { sensors = realloc(sensors, (1 + (*sensor_count)) * sizeof(sensor_t *)); sensors[(*sensor_count)++] = sensor = calloc(1, sizeof(sensor_t)); sensor->name = strdup(type); snprintf(path, sizeof(path), "/sys/class/thermal/%s/temp", names[i]->d_name); value = file_contents(path); if (!value) sensor->invalid = true; else { sensor->value = (float)atoi(value) / 1000.0; free(value); } free(type); } free(names[i]); } free(names); #elif defined(__MacOS__) #endif return sensors; } static int _power_battery_count_get(power_t *power) { #if defined(__OpenBSD__) struct sensordev snsrdev; size_t sdlen = sizeof(struct sensordev); int mib[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 }; int i, devn, id; for (devn = 0;; devn++) { mib[2] = devn; if (sysctl(mib, 3, &snsrdev, &sdlen, NULL, 0) == -1) { if (errno == ENXIO) continue; if (errno == ENOENT) break; } for (i = 0; i < 10; i++) { char buf[64]; snprintf(buf, sizeof(buf), "acpibat%d", i); if (!strcmp(buf, snsrdev.xname)) { id = power->battery_count; power->batteries = realloc(power->batteries, 1 + power->battery_count * sizeof(bat_t **)); power->batteries[id] = calloc(1, sizeof(bat_t)); power->batteries[id]->name = strdup(buf); power->batteries[id]->present = true; power->batteries[id]->mibs = malloc(sizeof(int) * 5); int *tmp = power->batteries[id]->mibs; tmp[0] = mib[0]; tmp[1] = mib[1]; tmp[2] = mib[2]; power->battery_count++; } } if (!strcmp("acpiac0", snsrdev.xname)) { power->ac_mibs[0] = mib[0]; power->ac_mibs[1] = mib[1]; power->ac_mibs[2] = mib[2]; } } #elif defined(__FreeBSD__) || defined(__DragonFly__) size_t len; if ((sysctlbyname("hw.acpi.battery.units", &power->battery_count, &len, NULL, 0)) < 0) { power->battery_count = 0; } if ((sysctlbyname("hw.acpi.acline", NULL, &len, NULL, 0)) != -1) { sysctlnametomib("hw.acpi.acline", power->ac_mibs, &len); } power->batteries = malloc(power->battery_count * sizeof(bat_t **)); for (int i = 0; i < power->battery_count; i++) { power->batteries[i] = calloc(1, sizeof(bat_t)); power->batteries[i]->present = true; } #elif defined(__linux__) char *type; char path[PATH_MAX]; struct dirent **names; int i, n, id; n = scandir("/sys/class/power_supply", &names, 0, alphasort); if (n < 0) return power->battery_count; for (i = 0; i < n; i++) { snprintf(path, sizeof(path), "/sys/class/power_supply/%s/type", names[i]->d_name); type = file_contents(path); if (type) { if (!strncmp(type, "Battery", 7)) { id = power->battery_count; power->batteries = realloc(power->batteries, (1 + power->battery_count) * sizeof(bat_t **)); power->batteries[id] = calloc(1, sizeof(bat_t)); power->batteries[id]->name = strdup(names[i]->d_name); power->batteries[id]->present = true; power->battery_count++; } free(type); } free(names[i]); } free(names); #endif return power->battery_count; } static void _battery_state_get(power_t *power) { #if defined(__OpenBSD__) int *mib; double charge_full, charge_current; size_t slen = sizeof(struct sensor); struct sensor snsr; for (int i = 0; i < power->battery_count; i++) { charge_full = charge_current = 0; mib = power->batteries[i]->mibs; mib[3] = SENSOR_WATTHOUR; mib[4] = 0; if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) charge_full = (double)snsr.value; mib[3] = SENSOR_WATTHOUR; mib[4] = 3; if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) charge_current = (double)snsr.value; if (charge_current == 0 || charge_full == 0) { mib[3] = SENSOR_AMPHOUR; mib[4] = 0; if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) charge_full = (double)snsr.value; mib[3] = SENSOR_AMPHOUR; mib[4] = 3; if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1) charge_current = (double)snsr.value; } power->batteries[i]->charge_full = charge_full; power->batteries[i]->charge_current = charge_current; } #elif defined(__FreeBSD__) || defined(__DragonFly__) int fd, i; union acpi_battery_ioctl_arg battio; char name[256]; if ((fd = open("/dev/acpi", O_RDONLY)) == -1) return; for (i = 0; i < power->battery_count; i++) { battio.unit = i; if (ioctl(fd, ACPIIO_BATT_GET_BIF, &battio) != -1) { if (battio.bif.lfcap == 0) power->batteries[i]->charge_full = battio.bif.dcap; else power->batteries[i]->charge_full = battio.bif.lfcap; } snprintf(name, sizeof(name), "%s %s", battio.bif.oeminfo, battio.bif.model); power->batteries[i]->name = strdup(name); battio.unit = i; if (ioctl(fd, ACPIIO_BATT_GET_BST, &battio) != -1) { power->batteries[i]->charge_current = battio.bst.cap; } if (battio.bst.state == ACPI_BATT_STAT_NOT_PRESENT) { power->batteries[i]->present = false; } } close(fd); #elif defined(__linux__) char path[PATH_MAX]; struct dirent *dh; struct stat st; DIR *dir; char *model, *vendor; char *buf, *naming = NULL; int i = 0; unsigned long charge_full = 0; unsigned long charge_current = 0; for (i = 0; i < power->battery_count; i++) { naming = NULL; snprintf(path, sizeof(path), "/sys/class/power_supply/%s", power->batteries[i]->name); if (stat(path, &st) < 0) continue; if (S_ISLNK(st.st_mode)) continue; if (!S_ISDIR(st.st_mode)) continue; dir = opendir(path); if (!dir) return; while ((dh = readdir(dir)) != NULL) { char *e; if (dh->d_name[0] == '.') continue; if ((e = strstr(dh->d_name, "_full\0"))) { naming = strndup(dh->d_name, e - dh->d_name); break; } } closedir(dir); if (!naming) continue; snprintf(path, sizeof(path), "/sys/class/power_supply/%s/%s_full", power->batteries[i]->name, naming); buf = file_contents(path); if (buf) { charge_full = atol(buf); free(buf); } snprintf(path, sizeof(path), "/sys/class/power_supply/%s/%s_now", power->batteries[i]->name, naming); buf = file_contents(path); if (buf) { charge_current = atol(buf); free(buf); } snprintf(path, sizeof(path), "/sys/class/power_supply/%s/manufacturer", power->batteries[i]->name); vendor = file_contents(path); snprintf(path, sizeof(path), "/sys/class/power_supply/%s/model_name", power->batteries[i]->name); model = file_contents(path); if (vendor && vendor[0] && model && model[0]) { char name[256]; int len; len = strlen(vendor); if (vendor[len - 1] == '\n' || vendor[len - 1] == '\r') { vendor[len - 1] = '\0'; } len = strlen(model); if (model[len - 1] == '\n' || model[len - 1] == '\r') { model[len - 1] = '\0'; } free(power->batteries[i]->name);; snprintf(name, sizeof(name), "%s %s", vendor, model); power->batteries[i]->name = strdup(name); } power->batteries[i]->charge_full = charge_full; power->batteries[i]->charge_current = charge_current; if (model) free(model); if (vendor) free(vendor); free(naming); } #endif } void system_power_state_get(power_t *power) { int i; #if defined(__OpenBSD__) struct sensor snsr; size_t slen = sizeof(struct sensor); #elif defined(__FreeBSD__) || defined(__DragonFly__) unsigned int value; size_t len; #elif defined(__linux__) char *buf; #endif if (!_power_battery_count_get(power)) return; #if defined(__OpenBSD__) power->ac_mibs[3] = 9; power->ac_mibs[4] = 0; if (sysctl(power->ac_mibs, 5, &snsr, &slen, NULL, 0) != -1) power->have_ac = (int)snsr.value; #elif defined(__FreeBSD__) || defined(__DragonFly__) len = sizeof(value); if ((sysctl(power->ac_mibs, 3, &value, &len, NULL, 0)) == -1) { return; } power->have_ac = value; #elif defined(__linux__) buf = file_contents("/sys/class/power_supply/AC/online"); if (buf) { power->have_ac = atoi(buf); free(buf); } #endif _battery_state_get(power); for (i = 0; i < power->battery_count; i++) { double percent = 100 * (power->batteries[i]->charge_current / power->batteries[i]->charge_full); power->batteries[i]->percent = percent; } } #if defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) static void _freebsd_generic_network_status(unsigned long int *in, unsigned long int *out) { struct ifmibdata *ifmd; size_t len; int i, count; len = sizeof(count); if (sysctlbyname ("net.link.generic.system.ifcount", &count, &len, NULL, 0) < 0) return; ifmd = malloc(sizeof(struct ifmibdata)); if (!ifmd) return; for (i = 1; i <= count; i++) { int mib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, i, IFDATA_GENERAL }; len = sizeof(*ifmd); if (sysctl(mib, 6, ifmd, &len, NULL, 0) < 0) continue; if (!strcmp(ifmd->ifmd_name, "lo0")) continue; *in += ifmd->ifmd_data.ifi_ibytes; *out += ifmd->ifmd_data.ifi_obytes; } free(ifmd); } #endif #if defined(__OpenBSD__) static void _openbsd_generic_network_status(unsigned long int *in, unsigned long int *out) { struct ifaddrs *interfaces, *ifa; if (getifaddrs(&interfaces) < 0) return; int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) return; for (ifa = interfaces; ifa; ifa = ifa->ifa_next) { struct ifreq ifreq; struct if_data if_data; ifreq.ifr_data = (void *)&if_data; strncpy(ifreq.ifr_name, ifa->ifa_name, IFNAMSIZ - 1); if (ioctl(sock, SIOCGIFDATA, &ifreq) < 0) return; struct if_data *const ifi = &if_data; if (ifi->ifi_type == IFT_ETHER || ifi->ifi_type == IFT_FASTETHER || ifi->ifi_type == IFT_GIGABITETHERNET || ifi->ifi_type == IFT_IEEE80211) { if (ifi->ifi_ibytes) *in += ifi->ifi_ibytes; if (ifi->ifi_obytes) *out += ifi->ifi_obytes; } } close(sock); } #endif #if defined(__linux__) static void _linux_generic_network_status(unsigned long int *in, unsigned long int *out) { FILE *f; char buf[4096], dummy_s[256]; unsigned long int tmp_in, tmp_out, dummy; f = fopen("/proc/net/dev", "r"); if (!f) return; while (fgets(buf, sizeof(buf), f)) { if (17 == sscanf(buf, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu " "%lu %lu %lu %lu %lu\n", dummy_s, &tmp_in, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &tmp_out, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy)) { *in += tmp_in; *out += tmp_out; } } fclose(f); } #endif void system_network_transfer_get(network_t *usage) { unsigned long first_in = 0, first_out = 0; unsigned long last_in = 0, last_out = 0; #if defined(__linux__) _linux_generic_network_status(&first_in, &first_out); usleep(1000000); _linux_generic_network_status(&last_in, &last_out); #elif defined(__OpenBSD__) _openbsd_generic_network_status(&first_in, &first_out); usleep(1000000); _openbsd_generic_network_status(&last_in, &last_out); #elif defined(__MacOS__) || defined(__FreeBSD__) || defined(__DragonFly__) _freebsd_generic_network_status(&first_in, &first_out); usleep(1000000); _freebsd_generic_network_status(&last_in, &last_out); #endif usage->incoming = last_in - first_in; usage->outgoing = last_out - first_out; } static void * _network_transfer_get_thread_cb(void *arg) { network_t *usage = arg; system_network_transfer_get(usage); return (void *)0; } void system_info_all_free(Sys_Info *info) { sensor_t *snsr; int i; for (i = 0; i < info->cpu_count; i++) { free(info->cores[i]); } free(info->cores); for (i = 0; i < info->sensor_count; i++) { snsr = info->sensors[i]; if (snsr->name) free(snsr->name); free(snsr); } if (info->sensors) free(info->sensors); for (i = 0; i < info->power.battery_count; i++) { if (info->power.batteries[i]->name) free(info->power.batteries[i]->name); #if defined(__OpenBSD__) if (info->power.batteries[i]->mibs) free(info->power.batteries[i]->mibs); #endif free(info->power.batteries[i]); } if (info->power.batteries) free(info->power.batteries); free(info); } Sys_Info * system_info_all_get(void) { Sys_Info *info; void *ret; pthread_t tid; int error; info = calloc(1, sizeof(Sys_Info)); if (!info) return NULL; info->cores = system_cpu_usage_get(&info->cpu_count); system_memory_usage_get(&info->memory); error = pthread_create(&tid, NULL, _network_transfer_get_thread_cb, &info->network_usage); if (error) system_network_transfer_get(&info->network_usage); system_power_state_get(&info->power); info->sensors = system_sensors_thermal_get(&info->sensor_count); if (!error) { ret = NULL; pthread_join(tid, ret); } return info; }