1706 lines
44 KiB
C
1706 lines
44 KiB
C
/*
|
|
Copyright (c) 2017, Alastair Poole <netstar@gmail.com>
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#define VERSION "0.9.0"
|
|
#define _DEFAULT_SOURCE
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <math.h>
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <pthread.h>
|
|
|
|
#if defined(__APPLE__) && defined(__MACH__)
|
|
#define __MacOS__
|
|
# include <mach/mach.h>
|
|
# include <mach/vm_statistics.h>
|
|
# include <mach/mach_types.h>
|
|
# include <mach/mach_init.h>
|
|
# include <mach/mach_host.h>
|
|
# include <net/if_mib.h>
|
|
# include <AudioToolBox/AudioServices.h>
|
|
#endif
|
|
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
|
# include <sys/swap.h>
|
|
# include <sys/sched.h>
|
|
# include <sys/mount.h>
|
|
# include <sys/sensors.h>
|
|
# include <sys/audioio.h>
|
|
# include <net/if_types.h>
|
|
# include <ifaddrs.h>
|
|
#endif
|
|
|
|
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
|
# include <net/if_mib.h>
|
|
# include <vm/vm_param.h>
|
|
# include <sys/soundcard.h>
|
|
#endif
|
|
|
|
#if defined(__linux__)
|
|
# include <sys/soundcard.h>
|
|
#endif
|
|
|
|
#if defined(__linux__) && defined(HAVE_ALSA)
|
|
# include <alsa/asoundlib.h>
|
|
#endif
|
|
|
|
#if defined(__OpenBSD__)
|
|
# define CPU_STATES 6
|
|
#else
|
|
#define CPU_STATES 5
|
|
#endif
|
|
|
|
#define MAX_BATTERIES 5
|
|
#define INVALID_TEMP -999
|
|
|
|
/* Filter requests and results */
|
|
#define RESULTS_CPU 0x01
|
|
#define RESULTS_MEM 0x02
|
|
#define RESULTS_PWR 0x04
|
|
#define RESULTS_TMP 0x08
|
|
#define RESULTS_AUD 0x10
|
|
#define RESULTS_NET 0x20
|
|
#define RESULTS_DEFAULT 0x3f
|
|
#define RESULTS_MEM_MB 0x40
|
|
#define RESULTS_MEM_GB 0x80
|
|
#define RESULTS_CPU_CORES 0x100
|
|
|
|
typedef struct
|
|
{
|
|
float percent;
|
|
unsigned long total;
|
|
unsigned long idle;
|
|
} cpu_core_t;
|
|
|
|
typedef struct
|
|
{
|
|
unsigned long total;
|
|
unsigned long used;
|
|
unsigned long cached;
|
|
unsigned long buffered;
|
|
unsigned long shared;
|
|
unsigned long swap_total;
|
|
unsigned long swap_used;
|
|
} meminfo_t;
|
|
|
|
typedef struct
|
|
{
|
|
double charge_full;
|
|
double charge_current;
|
|
uint8_t percent;
|
|
} bat_t;
|
|
|
|
typedef struct
|
|
{
|
|
bool have_ac;
|
|
int battery_count;
|
|
|
|
bat_t **batteries;
|
|
|
|
char battery_names[256];
|
|
int *bat_mibs[MAX_BATTERIES];
|
|
int ac_mibs[5];
|
|
} power_t;
|
|
|
|
typedef struct
|
|
{
|
|
bool enabled;
|
|
uint8_t volume_left;
|
|
uint8_t volume_right;
|
|
} mixer_t;
|
|
|
|
typedef struct results_t results_t;
|
|
struct results_t
|
|
{
|
|
int cpu_count;
|
|
cpu_core_t **cores;
|
|
|
|
meminfo_t memory;
|
|
|
|
power_t power;
|
|
|
|
mixer_t mixer;
|
|
|
|
unsigned long incoming;
|
|
unsigned long outgoing;
|
|
|
|
int temperature;
|
|
};
|
|
|
|
static void
|
|
_memsize_bytes_to_kb(unsigned long *bytes)
|
|
{
|
|
*bytes = (unsigned int)*bytes >> 10;
|
|
}
|
|
|
|
#define _memsize_kb_to_mb _memsize_bytes_to_kb
|
|
|
|
static void
|
|
_memsize_kb_to_gb(unsigned long *bytes)
|
|
{
|
|
*bytes = (unsigned int)*bytes >> 20;
|
|
}
|
|
|
|
#if defined(__linux__)
|
|
static char *
|
|
Fcontents(const char *path)
|
|
{
|
|
char *buf;
|
|
char byte[1];
|
|
size_t count, bytes = 0;
|
|
FILE *f = fopen(path, "r");
|
|
if (!f) return NULL;
|
|
|
|
int n = 1024;
|
|
|
|
buf = malloc(n * sizeof(byte) + 1);
|
|
if (!buf) return NULL;
|
|
|
|
while ((count = (fread(byte, sizeof(byte), 1, f))) > 0)
|
|
{
|
|
bytes += sizeof(byte);
|
|
if (bytes == (n * sizeof(byte)))
|
|
{
|
|
n *= 2;
|
|
char *tmp = realloc(buf, n * sizeof(byte) + 1);
|
|
if (!tmp) return NULL;
|
|
buf = tmp;
|
|
}
|
|
memcpy(&buf[bytes - sizeof(byte)], byte, sizeof(byte));
|
|
}
|
|
|
|
if (!feof(f)) return NULL;
|
|
fclose(f);
|
|
|
|
buf[bytes] = 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)
|
|
{
|
|
int cores = 0;
|
|
#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__) || defined(__NetBSD__)
|
|
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;
|
|
}
|
|
|
|
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__) || defined(__NetBSD__)
|
|
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__)
|
|
struct cpustats cpu_times[CPU_STATES];
|
|
memset(&cpu_times, 0, CPU_STATES * sizeof(struct cpustats));
|
|
if (!ncpu)
|
|
return;
|
|
|
|
for (i = 0; i < ncpu; i++)
|
|
{
|
|
core = cores[i];
|
|
int cpu_time_mib[] = { CTL_KERN, KERN_CPUSTATS, 0 };
|
|
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 = Fcontents("/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(4 << 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
|
|
}
|
|
|
|
static cpu_core_t **
|
|
_cpu_cores_state_get(int *ncpu)
|
|
{
|
|
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(1000000);
|
|
_cpu_state_get(cores, *ncpu);
|
|
|
|
return cores;
|
|
}
|
|
|
|
#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
|
|
|
|
static void
|
|
_memory_usage_get(meminfo_t *memory)
|
|
{
|
|
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__)
|
|
size_t len = 0;
|
|
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;
|
|
|
|
fclose(f);
|
|
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
|
int total_pages = 0, free_pages = 0, inactive_pages = 0;
|
|
long int result = 0;
|
|
int page_size = getpagesize();
|
|
int mib[4] = { CTL_HW, HW_PHYSMEM, 0, 0 };
|
|
|
|
len = sizeof(memory->total);
|
|
if (sysctl(mib, 2, &memory->total, &len, NULL, 0) == -1)
|
|
return;
|
|
memory->total /= 1024;
|
|
|
|
total_pages =
|
|
_sysctlfromname("vm.stats.vm.v_page_count", mib, 4, &len);
|
|
if (total_pages < 0)
|
|
return;
|
|
|
|
free_pages = _sysctlfromname("vm.stats.vm.v_free_count", mib, 4, &len);
|
|
if (free_pages < 0)
|
|
return;
|
|
|
|
inactive_pages =
|
|
_sysctlfromname("vm.stats.vm.v_inactive_count", mib, 4, &len);
|
|
if (inactive_pages < 0)
|
|
return;
|
|
|
|
memory->used = (total_pages - free_pages - inactive_pages) * page_size;
|
|
_memsize_bytes_to_kb(&memory->used);
|
|
|
|
result = _sysctlfromname("vfs.bufspace", mib, 2, &len);
|
|
if (result < 0)
|
|
return;
|
|
memory->buffered = (result);
|
|
_memsize_bytes_to_kb(&memory->buffered);
|
|
|
|
result = _sysctlfromname("vm.stats.vm.v_active_count", mib, 4, &len);
|
|
if (result < 0)
|
|
return;
|
|
memory->cached = (result * page_size);
|
|
_memsize_bytes_to_kb(&memory->cached);
|
|
|
|
result = _sysctlfromname("vm.stats.vm.v_cache_count", mib, 4, &len);
|
|
if (result < 0)
|
|
return;
|
|
memory->shared = (result * page_size);
|
|
_memsize_bytes_to_kb(&memory->shared);
|
|
|
|
result = _sysctlfromname("vm.swap_total", mib, 2, &len);
|
|
if (result < 0)
|
|
return;
|
|
memory->swap_total = (result / 1024);
|
|
|
|
struct xswdev xsw;
|
|
/* previous mib is important for this one... */
|
|
|
|
while (i++)
|
|
{
|
|
mib[2] = i;
|
|
len = sizeof(xsw);
|
|
if (sysctl(mib, 3, &xsw, &len, NULL, 0) == -1)
|
|
break;
|
|
|
|
memory->swap_used += xsw.xsw_used * page_size;
|
|
}
|
|
|
|
memory->swap_used >>= 10;
|
|
|
|
#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;
|
|
|
|
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;
|
|
|
|
/* Don't fail if there's not swap! */
|
|
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));
|
|
}
|
|
}
|
|
swap_out:
|
|
if (swdev)
|
|
free(swdev);
|
|
|
|
memory->total /= 1024;
|
|
|
|
memory->cached = (uvmexp.pagesize * bcstats.numbufpages);
|
|
_memsize_bytes_to_kb(&memory->cached);
|
|
|
|
memory->used = (uvmexp.active * uvmexp.pagesize);
|
|
_memsize_bytes_to_kb(&memory->used);
|
|
|
|
memory->buffered = (uvmexp.pagesize * (uvmexp.npages - uvmexp.free));
|
|
_memsize_bytes_to_kb(&memory->buffered);
|
|
|
|
memory->shared = (uvmexp.pagesize * uvmexp.wired);
|
|
_memsize_bytes_to_kb(&memory->shared);
|
|
#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);
|
|
|
|
total >>= 10;
|
|
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.inactive_count + vm_stats.wire_count * page_size;
|
|
memory->used >>= 10;
|
|
memory->cached = vm_stats.active_count * page_size;
|
|
memory->cached >>= 10;
|
|
memory->shared = vm_stats.wire_count * page_size;
|
|
memory->shared >>= 10;
|
|
memory->buffered = vm_stats.inactive_count * page_size;
|
|
memory->buffered >>= 10;
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
static int
|
|
_mixer_master_volume_get(mixer_t *mixer)
|
|
{
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
|
int i, fd, devn;
|
|
char name[64];
|
|
mixer_devinfo_t dinfo;
|
|
mixer_ctrl_t *values = NULL;
|
|
mixer_devinfo_t *info = NULL;
|
|
|
|
fd = open("/dev/mixer", O_RDONLY);
|
|
if (fd < 0)
|
|
return 0;
|
|
|
|
for (devn = 0;; devn++) {
|
|
dinfo.index = devn;
|
|
if (ioctl(fd, AUDIO_MIXER_DEVINFO, &dinfo))
|
|
break;
|
|
}
|
|
|
|
info = calloc(devn, sizeof(*info));
|
|
if (!info)
|
|
return 0;
|
|
|
|
for (i = 0; i < devn; i++) {
|
|
info[i].index = i;
|
|
if (ioctl(fd, AUDIO_MIXER_DEVINFO, &info[i]) == -1)
|
|
{
|
|
--devn;
|
|
--i;
|
|
mixer->enabled = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
values = calloc(devn, sizeof(*values));
|
|
if (!values)
|
|
return 0;
|
|
|
|
for (i = 0; i < devn; i++) {
|
|
values[i].dev = i;
|
|
values[i].type = info[i].type;
|
|
if (info[i].type != AUDIO_MIXER_CLASS)
|
|
{
|
|
values[i].un.value.num_channels = 2;
|
|
if (ioctl(fd, AUDIO_MIXER_READ, &values[i]) == -1)
|
|
{
|
|
values[i].un.value.num_channels = 1;
|
|
if (ioctl(fd, AUDIO_MIXER_READ, &values[i])
|
|
== -1)
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < devn; i++) {
|
|
strlcpy(name, info[i].label.name, sizeof(name));
|
|
if (!strcmp("master", name))
|
|
{
|
|
mixer->volume_left = values[i].un.value.level[0];
|
|
mixer->volume_right = values[i].un.value.level[1];
|
|
mixer->enabled = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
|
|
if (values)
|
|
free(values);
|
|
if (info)
|
|
free(info);
|
|
|
|
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__linux__) && !defined(HAVE_ALSA)
|
|
int bar;
|
|
int fd = open("/dev/mixer", O_RDONLY);
|
|
if (fd == -1)
|
|
return 0;
|
|
|
|
if ((ioctl(fd, MIXER_READ(0), &bar)) == -1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
mixer->enabled = true;
|
|
mixer->volume_left = bar & 0x7f;
|
|
mixer->volume_right = (bar >> 8) & 0x7f;
|
|
close(fd);
|
|
#elif defined(__linux__) && defined(HAVE_ALSA)
|
|
snd_mixer_t *h;
|
|
snd_mixer_elem_t *elem;
|
|
snd_mixer_selem_id_t *id;
|
|
long int value;
|
|
double volume;
|
|
|
|
snd_mixer_selem_id_alloca(&id);
|
|
snd_mixer_selem_id_set_index(id, 0);
|
|
snd_mixer_selem_id_set_name(id, "Master");
|
|
|
|
if ((snd_mixer_open(&h, 0)) == -1) return (0);
|
|
if ((snd_mixer_attach(h, "default")) == -1) return(0);
|
|
if ((snd_mixer_selem_register(h, NULL, NULL)) == -1) goto out;
|
|
if ((snd_mixer_load(h)) == -1) goto out;
|
|
|
|
if (!(elem = snd_mixer_find_selem(h, id))) goto out;
|
|
|
|
long int max, min;
|
|
|
|
snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
|
|
snd_mixer_selem_get_playback_volume(elem, 0, &value);
|
|
double ratio = max - min / 100;
|
|
volume = value / ratio;
|
|
|
|
mixer->enabled = true;
|
|
mixer->volume_left = mixer->volume_right = volume * 100;
|
|
out:
|
|
snd_mixer_close(h);
|
|
#elif defined(__MacOS__)
|
|
AudioDeviceID id;
|
|
AudioObjectPropertyAddress prop;
|
|
float volume;
|
|
unsigned int id_size = sizeof(id);
|
|
unsigned int vol_size = sizeof(volume);
|
|
|
|
prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
|
prop.mScope = kAudioObjectPropertyScopeGlobal;
|
|
prop.mElement = kAudioObjectPropertyElementMaster;
|
|
|
|
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &id_size, &id))
|
|
return (0);
|
|
|
|
prop.mSelector = kAudioDevicePropertyVolumeScalar;
|
|
prop.mScope = kAudioDevicePropertyScopeOutput;
|
|
prop.mElement = 0;
|
|
|
|
if (AudioObjectGetPropertyData(id, &prop, 0, NULL, &vol_size, &volume))
|
|
return (0);
|
|
|
|
mixer->volume_left = mixer->volume_right = volume * 100;
|
|
|
|
mixer->enabled = true;
|
|
#endif
|
|
return mixer->enabled;
|
|
}
|
|
|
|
static void
|
|
_temperature_cpu_get(int *temperature)
|
|
{
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
|
int mibs[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 };
|
|
int devn, numt;
|
|
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;
|
|
else
|
|
continue;
|
|
}
|
|
if (!strcmp("cpu0", snsrdev.xname))
|
|
break;
|
|
else if (!strcmp("km0", snsrdev.xname))
|
|
break;
|
|
}
|
|
|
|
for (numt = 0; numt < snsrdev.maxnumt[SENSOR_TEMP]; numt++) {
|
|
mibs[4] = numt;
|
|
|
|
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)
|
|
{
|
|
*temperature = (snsr.value - 273150000) / 1000000.0;
|
|
}
|
|
else
|
|
*temperature = INVALID_TEMP;
|
|
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
|
unsigned int value;
|
|
size_t len = sizeof(value);
|
|
if ((sysctlbyname
|
|
("hw.acpi.thermal.tz0.temperature", &value, &len, NULL,
|
|
0)) != -1)
|
|
{
|
|
*temperature = (value - 2732) / 10;
|
|
}
|
|
else
|
|
*temperature = INVALID_TEMP;
|
|
#elif defined(__linux__)
|
|
struct dirent *dh;
|
|
DIR *dir;
|
|
char path[PATH_MAX];
|
|
|
|
*temperature = INVALID_TEMP;
|
|
|
|
dir = opendir("/sys/class/thermal");
|
|
if (!dir) return;
|
|
|
|
while ((dh = readdir(dir)) != NULL)
|
|
{
|
|
if (!strncmp(dh->d_name, "thermal_zone", 12))
|
|
{
|
|
snprintf(path, sizeof(path), "/sys/class/thermal/%s/type", dh->d_name);
|
|
char *type = Fcontents(path);
|
|
if (type)
|
|
{
|
|
/* This should ensure we get the highest available core temperature */
|
|
if (strstr(type, "_pkg_temp"))
|
|
{
|
|
snprintf(path, sizeof(path), "/sys/class/thermal/%s/temp", dh->d_name);
|
|
char *value = Fcontents(path);
|
|
if (value)
|
|
{
|
|
*temperature = atoi(value) / 1000;
|
|
free(value);
|
|
free(type);
|
|
break;
|
|
}
|
|
}
|
|
free(type);
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
#elif defined(__MacOS__)
|
|
*temperature = INVALID_TEMP;
|
|
#endif
|
|
}
|
|
|
|
static int
|
|
_power_battery_count_get(power_t *power)
|
|
{
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
|
struct sensordev snsrdev;
|
|
size_t sdlen = sizeof(struct sensordev);
|
|
int mib[5] = { CTL_HW, HW_SENSORS, 0, 0, 0 };
|
|
int i, devn;
|
|
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 < MAX_BATTERIES; i++) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "acpibat%d", i);
|
|
if (!strcmp(buf, snsrdev.xname))
|
|
{
|
|
power->bat_mibs[power->battery_count] =
|
|
malloc(sizeof(int) * 5);
|
|
int *tmp = power->bat_mibs[power->battery_count++];
|
|
tmp[0] = mib[0];
|
|
tmp[1] = mib[1];
|
|
tmp[2] = mib[2];
|
|
}
|
|
}
|
|
|
|
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.life", NULL, &len, NULL, 0)) != -1)
|
|
{
|
|
power->bat_mibs[power->battery_count] = malloc(sizeof(int) * 5);
|
|
sysctlnametomib("hw.acpi.battery.life",
|
|
power->bat_mibs[power->battery_count], &len);
|
|
power->battery_count = 1;
|
|
}
|
|
|
|
if ((sysctlbyname("hw.acpi.acline", NULL, &len, NULL, 0)) != -1)
|
|
{
|
|
sysctlnametomib("hw.acpi.acline", power->ac_mibs, &len);
|
|
}
|
|
#elif defined(__linux__)
|
|
struct dirent *dh;
|
|
DIR *dir;
|
|
|
|
dir = opendir("/sys/class/power_supply");
|
|
if (!dir) return 0;
|
|
|
|
while ((dh = readdir(dir)) != NULL)
|
|
{
|
|
if (dh->d_name[0] == '.') continue;
|
|
if (!strncmp(dh->d_name, "BAT", 3))
|
|
{
|
|
power->battery_names[power->battery_count++] = (char)dh->d_name[3];
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
#endif
|
|
|
|
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));
|
|
}
|
|
|
|
return power->battery_count;
|
|
}
|
|
|
|
static void
|
|
_battery_state_get(power_t *power, int *mib)
|
|
{
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
|
static int index = 0;
|
|
double charge_full = 0;
|
|
double charge_current = 0;
|
|
size_t slen = sizeof(struct sensor);
|
|
struct sensor snsr;
|
|
|
|
mib[3] = 7;
|
|
mib[4] = 0;
|
|
|
|
if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1)
|
|
charge_full = (double)snsr.value;
|
|
|
|
mib[3] = 7;
|
|
mib[4] = 3;
|
|
|
|
if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1)
|
|
charge_current = (double)snsr.value;
|
|
|
|
/* ACPI bug workaround... */
|
|
if (charge_current == 0 || charge_full == 0)
|
|
{
|
|
mib[3] = 8;
|
|
mib[4] = 0;
|
|
|
|
if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1)
|
|
charge_full = (double)snsr.value;
|
|
|
|
mib[3] = 8;
|
|
mib[4] = 3;
|
|
|
|
if (sysctl(mib, 5, &snsr, &slen, NULL, 0) != -1)
|
|
charge_current = (double)snsr.value;
|
|
}
|
|
|
|
power->batteries[index]->charge_full = charge_full;
|
|
power->batteries[index]->charge_current = charge_current;
|
|
++index;
|
|
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
|
unsigned int value;
|
|
size_t len = sizeof(value);
|
|
if ((sysctl(mib, 4, &value, &len, NULL, 0)) != -1)
|
|
power->batteries[0]->percent = value;
|
|
#elif defined(__linux__)
|
|
char path[PATH_MAX];
|
|
struct dirent *dh;
|
|
DIR *dir;
|
|
char *buf, *naming = NULL;
|
|
int i = 0;
|
|
unsigned long charge_full = 0;
|
|
unsigned long charge_current = 0;
|
|
|
|
while (power->battery_names[i] != '\0')
|
|
{
|
|
snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%c", power->battery_names[i]);
|
|
dir = opendir(path);
|
|
if (!dir) return;
|
|
while ((dh = readdir(dir)) != NULL)
|
|
{
|
|
if (!strcmp(dh->d_name, "energy_full"))
|
|
{
|
|
naming = "energy"; break;
|
|
}
|
|
else if (!strcmp(dh->d_name, "capacity_full"))
|
|
{
|
|
naming = "capacity"; break;
|
|
}
|
|
}
|
|
closedir(dir);
|
|
if (!naming) continue;
|
|
snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%c/%s_full", power->battery_names[i], naming);
|
|
buf = Fcontents(path);
|
|
if (buf)
|
|
{
|
|
charge_full = atol(buf);
|
|
free(buf);
|
|
}
|
|
snprintf(path, sizeof(path), "/sys/class/power_supply/BAT%c/%s_now", power->battery_names[i], naming);
|
|
buf = Fcontents(path);
|
|
if (buf)
|
|
{
|
|
charge_current = atol(buf);
|
|
free(buf);
|
|
}
|
|
power->batteries[i]->charge_full = charge_full;
|
|
power->batteries[i]->charge_current = charge_current;
|
|
naming = NULL;
|
|
i++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
_power_state_get(power_t *power)
|
|
{
|
|
int i;
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
|
struct sensor snsr;
|
|
int have_ac = 0;
|
|
size_t slen = sizeof(struct sensor);
|
|
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
|
unsigned int value;
|
|
size_t len;
|
|
#elif defined(__linux__)
|
|
char *buf;
|
|
int have_ac = 0;
|
|
#endif
|
|
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
|
power->ac_mibs[3] = 9;
|
|
power->ac_mibs[4] = 0;
|
|
|
|
if (sysctl(power->ac_mibs, 5, &snsr, &slen, NULL, 0) != -1)
|
|
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 = Fcontents("/sys/class/power_supply/AC/online");
|
|
if (buf)
|
|
{
|
|
have_ac = atoi(buf);
|
|
free(buf);
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < power->battery_count; i++)
|
|
_battery_state_get(power, power->bat_mibs[i]);
|
|
|
|
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__linux__)
|
|
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;
|
|
}
|
|
power->have_ac = have_ac;
|
|
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
|
len = sizeof(value);
|
|
if ((sysctl(power->bat_mibs[0], 4, &value, &len, NULL, 0)) == -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
power->batteries[0]->percent = value;
|
|
|
|
#endif
|
|
for (i = 0; i < power->battery_count; i++)
|
|
if (power->bat_mibs[i]) free(power->bat_mibs[i]);
|
|
}
|
|
|
|
#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
|
|
|
|
static void
|
|
_network_transfer_get(results_t *results)
|
|
{
|
|
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
|
|
results->incoming = last_in - first_in;
|
|
results->outgoing = last_out - first_out;
|
|
}
|
|
|
|
static int
|
|
percentage(int value, int max)
|
|
{
|
|
double avg = (max / 100.0);
|
|
double tmp = value / avg;
|
|
|
|
return round(tmp);
|
|
}
|
|
|
|
static void
|
|
results_pretty(results_t *results, int *order, int count)
|
|
{
|
|
int i, j, flags;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
flags = order[i];
|
|
if (flags & RESULTS_CPU_CORES)
|
|
{
|
|
if (results->cpu_count > 1)
|
|
printf(" [CPUs]: ");
|
|
else
|
|
printf(" [CPU]: ");
|
|
for (j = 0; j < results->cpu_count; j++) {
|
|
printf("%.2f%%", results->cores[j]->percent);
|
|
if (j < (results->cpu_count - 1))
|
|
printf(" ");
|
|
}
|
|
}
|
|
else if (flags & RESULTS_CPU)
|
|
{
|
|
double cpu_percent = 0;
|
|
for (j = 0; j < results->cpu_count; j++)
|
|
cpu_percent += results->cores[j]->percent;
|
|
|
|
printf(" [CPU]: %.2f%%", cpu_percent / results->cpu_count);
|
|
}
|
|
|
|
if (flags & RESULTS_MEM)
|
|
{
|
|
unsigned long used = results->memory.used;
|
|
unsigned long total = results->memory.total;
|
|
char unit;
|
|
|
|
if (flags & RESULTS_MEM_GB)
|
|
{
|
|
unit = 'G';
|
|
_memsize_kb_to_gb(&used);
|
|
_memsize_kb_to_gb(&total);
|
|
}
|
|
else if (flags & RESULTS_MEM_MB)
|
|
{
|
|
unit = 'M';
|
|
_memsize_kb_to_mb(&used);
|
|
_memsize_kb_to_mb(&total);
|
|
}
|
|
else
|
|
unit = 'K';
|
|
|
|
printf(" [MEM]: %lu/%lu%c", used, total, unit);
|
|
}
|
|
|
|
if (flags & RESULTS_NET)
|
|
{
|
|
const char *unit = "B/s";
|
|
double incoming = results->incoming;
|
|
double outgoing = results->outgoing;
|
|
|
|
if ((incoming > 1048576) || (outgoing > 1048576))
|
|
{
|
|
incoming = incoming / 1048576;
|
|
outgoing = outgoing / 1048576;
|
|
unit = "MB/s";
|
|
}
|
|
else if (((incoming > 1024) && (incoming < 1048576)) ||
|
|
((outgoing > 1024) && (outgoing < 1048576)))
|
|
{
|
|
incoming /= 1024;
|
|
outgoing /= 1024;
|
|
unit = "KB/s";
|
|
}
|
|
printf(" [NET] %.2f/%.2f %s", incoming, outgoing, unit);
|
|
}
|
|
|
|
if (flags & RESULTS_TMP)
|
|
{
|
|
if (results->temperature != INVALID_TEMP)
|
|
printf(" [T]: %dC", results->temperature);
|
|
}
|
|
|
|
if (flags & RESULTS_AUD)
|
|
{
|
|
if (results->mixer.enabled)
|
|
{
|
|
uint8_t level =
|
|
results->mixer.volume_right >
|
|
results->mixer.volume_left ? results->mixer.
|
|
volume_right : results->mixer.volume_left;
|
|
#if defined(__MacOS__) || defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
|
uint8_t perc = percentage(level, 100);
|
|
printf(" [VOL]: %d%%", perc);
|
|
#elif defined(__OpenBSD__) || defined(__NetBSD__)
|
|
uint8_t perc = percentage(level, 255);
|
|
printf(" [VOL]: %d%%", perc);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (flags & RESULTS_PWR)
|
|
{
|
|
if (results->power.have_ac)
|
|
printf(" [AC]");
|
|
else
|
|
printf(" [DC]");
|
|
|
|
if (results->power.battery_count != 0)
|
|
printf(":");
|
|
|
|
for (int i = 0; i < results->power.battery_count; i++)
|
|
{
|
|
printf(" %d%%", results->power.batteries[i]->percent);
|
|
}
|
|
}
|
|
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
results_cpu(cpu_core_t **cores, int cpu_count, int flags)
|
|
{
|
|
int i;
|
|
|
|
if (flags & RESULTS_CPU_CORES)
|
|
{
|
|
for (i = 0; i < cpu_count; i++)
|
|
printf("%.2f ", cores[i]->percent);
|
|
}
|
|
else
|
|
{
|
|
double total = 0;
|
|
for (i = 0; i < cpu_count; i++)
|
|
total += cores[i]->percent;
|
|
printf("%.2f", total / cpu_count);
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
results_mem(meminfo_t *mem, int flags)
|
|
{
|
|
unsigned long total, used, cached, buffered;
|
|
unsigned long shared, swap_total, swap_used;
|
|
|
|
total = mem->total;
|
|
used = mem->used;
|
|
cached = mem->cached;
|
|
buffered = mem->buffered;
|
|
shared = mem->shared;
|
|
swap_total = mem->swap_total;
|
|
swap_used = mem->swap_used;
|
|
|
|
if (flags & RESULTS_MEM_MB)
|
|
{
|
|
_memsize_kb_to_mb(&total);
|
|
_memsize_kb_to_mb(&used);
|
|
_memsize_kb_to_mb(&cached);
|
|
_memsize_kb_to_mb(&buffered);
|
|
_memsize_kb_to_mb(&shared);
|
|
_memsize_kb_to_mb(&swap_total);
|
|
_memsize_kb_to_mb(&swap_used);
|
|
}
|
|
else if (flags & RESULTS_MEM_GB)
|
|
{
|
|
_memsize_kb_to_gb(&total);
|
|
_memsize_kb_to_gb(&used);
|
|
_memsize_kb_to_gb(&cached);
|
|
_memsize_kb_to_gb(&buffered);
|
|
_memsize_kb_to_gb(&shared);
|
|
_memsize_kb_to_gb(&swap_total);
|
|
_memsize_kb_to_gb(&swap_used);
|
|
}
|
|
|
|
printf("%lu %lu %lu %lu %lu %lu %lu\n",
|
|
total, used, cached, buffered, shared, swap_total, swap_used);
|
|
}
|
|
|
|
static void
|
|
results_power(power_t *power)
|
|
{
|
|
printf("%d", power->have_ac);
|
|
for (int i = 0; i < power->battery_count; i++)
|
|
{
|
|
printf(" %d", power->batteries[i]->percent);
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
results_temperature(int temp)
|
|
{
|
|
printf("%d\n", temp);
|
|
}
|
|
|
|
static void
|
|
results_network(results_t *results)
|
|
{
|
|
printf("%lu %lu\n", results->incoming, results->outgoing);
|
|
}
|
|
|
|
static void
|
|
results_mixer(mixer_t *mixer)
|
|
{
|
|
if (!mixer->enabled)
|
|
return;
|
|
|
|
printf("%d %d\n", mixer->volume_left, mixer->volume_right);
|
|
}
|
|
|
|
static void
|
|
results_verbose(results_t *results, int *order, int count)
|
|
{
|
|
int i, flags;
|
|
for (i = 0; i < count; i++) {
|
|
flags = order[i];
|
|
if (flags & RESULTS_CPU)
|
|
results_cpu(results->cores, results->cpu_count, flags);
|
|
else if (flags & RESULTS_MEM)
|
|
results_mem(&results->memory, flags);
|
|
else if (flags & RESULTS_PWR)
|
|
results_power(&results->power);
|
|
else if (flags & RESULTS_TMP)
|
|
results_temperature(results->temperature);
|
|
else if (flags & RESULTS_NET)
|
|
results_network(results);
|
|
else if (flags & RESULTS_AUD)
|
|
results_mixer(&results->mixer);
|
|
}
|
|
}
|
|
|
|
static void *_network_transfer_get_thread_cb(void *arg)
|
|
{
|
|
results_t *results = arg;
|
|
|
|
_network_transfer_get(results);
|
|
|
|
return ((void *) 0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
void *ret = NULL;
|
|
results_t results;
|
|
bool have_battery, status_line = false;
|
|
int i, j = 0, flags = 0, error = 0;
|
|
int order[argc];
|
|
pthread_t tid;
|
|
|
|
memset(&order, 0, sizeof(int) * (argc));
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if ((!strcmp(argv[i], "-h")) ||
|
|
(!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
|
|
{
|
|
printf("Usage: tingle [OPTIONS]\n"
|
|
" Where OPTIONS can be a combination of\n"
|
|
" -c\n"
|
|
" Show average CPU usage.\n"
|
|
" -C\n"
|
|
" Show all CPU cores and usage.\n"
|
|
" -k (KB) -m (MB) -g (GB)\n"
|
|
" Show memory usage (unit).\n"
|
|
" -n\n"
|
|
" Show network usage.\n"
|
|
" -p\n"
|
|
" Show power status (ac and battery percentage).\n"
|
|
" -t\n"
|
|
" Show temperature sensors (temperature in celcius).\n"
|
|
" -a\n"
|
|
" Display mixer values (system values).\n"
|
|
" -s\n"
|
|
" Show all in a nicely formatted status bar format.\n"
|
|
" This is the default behaviour with no arguments.\n"
|
|
" With other flags specify (in any order) which\n"
|
|
" components to display in the status bar.\n"
|
|
" -v | -version | --version\n"
|
|
" Version information.\n"
|
|
" -h | -help | --help\n" " This help.\n");
|
|
exit(0);
|
|
}
|
|
|
|
if ((!strcmp(argv[i], "-v")) ||
|
|
(!strcmp(argv[i], "-version")) || (!strcmp(argv[i], "--version")))
|
|
{
|
|
printf("(c) Copyright 2017-2019. Alastair Poole (see COPYING for details)\n");
|
|
printf("Tingle version %s\n", VERSION);
|
|
exit(0);
|
|
}
|
|
|
|
if (!strcmp(argv[i], "-c"))
|
|
order[j] |= RESULTS_CPU;
|
|
else if (!strcmp(argv[i], "-C"))
|
|
order[j] |= RESULTS_CPU | RESULTS_CPU_CORES;
|
|
else if (!strcasecmp(argv[i], "-k"))
|
|
order[j] |= RESULTS_MEM;
|
|
else if (!strcasecmp(argv[i], "-m"))
|
|
order[j] |= RESULTS_MEM | RESULTS_MEM_MB;
|
|
else if (!strcasecmp(argv[i], "-g"))
|
|
order[j] |= RESULTS_MEM | RESULTS_MEM_GB;
|
|
else if (!strcasecmp(argv[i], "-p"))
|
|
order[j] |= RESULTS_PWR;
|
|
else if (!strcasecmp(argv[i], "-t"))
|
|
order[j] |= RESULTS_TMP;
|
|
else if (!strcasecmp(argv[i], "-a"))
|
|
order[j] |= RESULTS_AUD;
|
|
else if (!strcasecmp(argv[i], "-n"))
|
|
order[j] |= RESULTS_NET;
|
|
else if (!strcasecmp(argv[i], "-s"))
|
|
{
|
|
status_line = true;
|
|
continue;
|
|
}
|
|
flags |= order[j++];
|
|
}
|
|
|
|
if (flags == 0)
|
|
{
|
|
flags |= RESULTS_DEFAULT;
|
|
order[0] |= RESULTS_DEFAULT | RESULTS_MEM_MB;
|
|
status_line = true;
|
|
}
|
|
|
|
memset(&results, 0, sizeof(results_t));
|
|
|
|
if (flags & RESULTS_NET)
|
|
{
|
|
error = pthread_create(&tid, NULL, _network_transfer_get_thread_cb, &results);
|
|
if (error)
|
|
_network_transfer_get(&results);
|
|
}
|
|
|
|
if (flags & RESULTS_CPU)
|
|
{
|
|
results.cores = _cpu_cores_state_get(&results.cpu_count);
|
|
}
|
|
|
|
if (flags & RESULTS_MEM)
|
|
{
|
|
_memory_usage_get(&results.memory);
|
|
}
|
|
|
|
if (flags & RESULTS_PWR)
|
|
{
|
|
have_battery = _power_battery_count_get(&results.power);
|
|
if (have_battery)
|
|
_power_state_get(&results.power);
|
|
}
|
|
|
|
if (flags & RESULTS_TMP)
|
|
{
|
|
_temperature_cpu_get(&results.temperature);
|
|
}
|
|
|
|
if (flags & RESULTS_AUD)
|
|
{
|
|
_mixer_master_volume_get(&results.mixer);
|
|
}
|
|
|
|
if (flags & RESULTS_NET && !error)
|
|
{
|
|
pthread_join(tid, ret);
|
|
}
|
|
|
|
if (status_line)
|
|
{
|
|
results_pretty(&results, order, j ? j : 1);
|
|
}
|
|
else
|
|
{
|
|
results_verbose(&results, order, j);
|
|
}
|
|
|
|
if (flags & RESULTS_PWR)
|
|
{
|
|
for (i = 0; i < results.power.battery_count; i++)
|
|
free(results.power.batteries[i]);
|
|
}
|
|
|
|
if (flags & RESULTS_CPU)
|
|
{
|
|
for (i = 0; i < results.cpu_count; i++)
|
|
{
|
|
free(results.cores[i]);
|
|
}
|
|
free(results.cores);
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|