aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlastair Poole <netstar@gmail.com>2018-10-17 10:39:58 +0100
committerAlastair Poole <netstar@gmail.com>2018-10-17 10:39:58 +0100
commit93d48c140393f69271f0a672d68ae35d561ca428 (patch)
tree0790af5a481519cf1a01cded2457b1ef059eb1e2
parentui: ensure we clean up properly before exit. (diff)
downloadevisum-93d48c140393f69271f0a672d68ae35d561ca428.tar.gz
evisum: add tingle command-line helper.
-rw-r--r--NEWS3
-rw-r--r--README.md17
-rw-r--r--makefile4
-rw-r--r--src/tingle/README47
-rw-r--r--src/tingle/makefile23
-rw-r--r--src/tingle/tingle.c1689
-rw-r--r--src/tingle/tmux.conf124
-rwxr-xr-xsrc/tingle/volctl142
8 files changed, 2042 insertions, 7 deletions
diff --git a/NEWS b/NEWS
index 4f1ef14..6873af3 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,9 @@
Evisum 0.2.0
============
+ * Add command-line utility (tingle) to complement
+ evisum.
+ * Fix object destruction befor exitting main loop.
* Add variable unit display (K, M and G).
* Minor refactoring.
diff --git a/README.md b/README.md
index 855bc8b..ce7eba4 100644
--- a/README.md
+++ b/README.md
@@ -2,13 +2,10 @@
System Information (EFL)
-This is a process monitor and system monitor.
+This is a process monitor and system monitor for Linux, macOS,
+FreeBSD, DragonFlyBSD and OpenBSD.
-Currently have full engines for Linux, FreeBSD, OpenBSD and MacOS.
-
-The plan is to rewrite the whole program to log system information
-over time to disk so we can do nice things with E and displaying
-system information (not duplicating loads of code).
+Includes 'tingle' a command-line utility for displaying sensor information.
REQUIREMENTS:
@@ -32,3 +29,11 @@ $ make PREFIX=/usr install
or even:
$ make PREFIX=/opt install
+
+NOTES
+
+The plan is to rewrite the whole program to log system information
+over time to disk so we can do nice things with E and displaying
+system information (not duplicating loads of code).
+
+For information on tingle, see src/tingle/README.
diff --git a/makefile b/makefile
index ebc0cd2..d4ec2fa 100644
--- a/makefile
+++ b/makefile
@@ -21,9 +21,10 @@ export LDFLAGS
default:
$(MAKE) -C src
+ $(MAKE) -C src/tingle
clean:
- $(MAKE) -C src clean
+ $(MAKE) -C src/tingle clean
install:
-mkdir -p $(PREFIX)/share/pixmaps
@@ -32,3 +33,4 @@ install:
install -m 0644 data/evisum.png $(PREFIX)/share/pixmaps
install -m 0644 data/evisum.desktop $(PREFIX)/share/applications
install -m 0755 evisum $(PREFIX)/bin
+ install -m 0755 src/tingle/tingle $(PREFIX)/bin
diff --git a/src/tingle/README b/src/tingle/README
new file mode 100644
index 0000000..f32a5f2
--- /dev/null
+++ b/src/tingle/README
@@ -0,0 +1,47 @@
+# UNIX sensors (tingle)
+
+# Query CPU, memory, network, power, audio and
+ temperature sensors.
+
+Supporting Linux, FreeBSD, OpenBSD, Mac OS and DragonFlyBSD.
+
+It's also probably a useful reference for querying common
+system information on the most-common Unix-like operating
+systems.
+
+This can be used with pipes by other processes to
+query the hardware. Or just to display a status
+line in screen or tmux or some Xorg program.
+
+If I'd thought more about it I'd have made it modular, so
+apologies for the crust!
+
+Install:
+ make (or gmake)
+
+Usage: tingle [OPTIONS]
+ Where OPTIONS can be a combination of
+ -c
+ Show average CPU usage.
+ -C
+ Show all CPU cores and usage.
+ -k (KB) -m (MB) -g (GB)
+ Show memory usage (unit).
+ -n
+ Show network usage.
+ -p
+ Show power status (ac and battery percentage).
+ -t
+ Show temperature sensors (temperature in celcius).
+ -a
+ Display mixer values (system values).
+ -s
+ Show all in a nicely formatted status bar format.
+ This is the default behaviour with no arguments.
+ With other flags specify (in any order) which
+ components to display in the status bar.
+ -h | -help | --help
+ This help.
+
+Al Poole <netstar@gmail.com>
+
diff --git a/src/tingle/makefile b/src/tingle/makefile
new file mode 100644
index 0000000..ef6f905
--- /dev/null
+++ b/src/tingle/makefile
@@ -0,0 +1,23 @@
+PROGRAM=tingle
+SOURCES=tingle.c
+CFLAGS=-O2 -Wall -pedantic -std=c99 -lpthread
+LDFLAGS=-lm
+HAVE_ALSA := 0
+
+ALSA_TEST := $(shell pkg-config --exists alsa 1>&2 2>/dev/null; echo $$?)
+HAVE_ALSA := $(shell if [ $(ALSA_TEST) -eq 0 ]; then echo "true"; else echo "false"; fi)
+
+UNAME := $(shell uname -s)
+
+ifeq ($(UNAME),Darwin)
+ CFLAGS += -framework AudioToolBox
+else ifeq ($(UNAME),Linux)
+ ifeq ($(HAVE_ALSA),true)
+ CFLAGS += -lasound -DHAVE_ALSA=1
+ endif
+endif
+
+default:
+ $(CC) $(CFLAGS) $(LDFLAGS) $(SOURCES) -o tingle
+clean:
+ -rm $(PROGRAM)
diff --git a/src/tingle/tingle.c b/src/tingle/tingle.c
new file mode 100644
index 0000000..1fe1603
--- /dev/null
+++ b/src/tingle/tingle.c
@@ -0,0 +1,1689 @@
+/*
+ Copyright (c) 2017, Al 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.
+ */
+
+/* Visit: http://haxlab.org */
+/* Build : cc -lm (file) -o (output) */
+/* What a shitstorm this turned into! hah! */
+
+#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/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
+
+#define CPU_STATES 5
+
+#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
+{
+ bool have_ac;
+ int battery_count;
+
+ double charge_full;
+ double charge_current;
+ uint8_t percent;
+
+ 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) exit(0 << 1);
+
+ 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) exit(1 << 1);
+ buf = tmp;
+ }
+ memcpy(&buf[bytes - sizeof(byte)], byte, sizeof(byte));
+ }
+
+ if (!feof(f)) exit(2 << 1);
+ 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__)
+ unsigned long cpu_times[CPU_STATES];
+ if (!ncpu)
+ return;
+ if (ncpu == 1)
+ {
+ core = cores[0];
+ int cpu_time_mib[] = { CTL_KERN, KERN_CPTIME };
+ size = CPU_STATES * sizeof(unsigned long);
+ if (sysctl(cpu_time_mib, 2, &cpu_times, &size, NULL, 0) < 0)
+ return;
+
+ total = 0;
+ for (j = 0; j < CPU_STATES; j++)
+ total += cpu_times[j];
+
+ idle = cpu_times[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;
+ }
+ else if (ncpu > 1)
+ {
+ for (i = 0; i < ncpu; i++) {
+ core = cores[i];
+ int cpu_time_mib[] = { CTL_KERN, KERN_CPTIME2, 0 };
+ size = CPU_STATES * sizeof(unsigned long);
+ cpu_time_mib[2] = i;
+ if (sysctl(cpu_time_mib, 3, &cpu_times, &size, NULL, 0) < 0)
+ return;
+
+ total = 0;
+ for (j = 0; j < CPU_STATES; j++)
+ total += cpu_times[j];
+
+ idle = cpu_times[4];
+
+ 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 (AudioHardwareServiceGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &id_size, &id))
+ return (0);
+
+ prop.mSelector = kAudioDevicePropertyVolumeScalar;
+ prop.mScope = kAudioDevicePropertyScopeOutput;
+ prop.mElement = 0;
+
+ if (AudioHardwareServiceGetPropertyData(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
+ return power->battery_count;
+}
+
+static void
+_battery_state_get(power_t *power, int *mib)
+{
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+ 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->charge_full += charge_full;
+ power->charge_current += charge_current;
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+ unsigned int value;
+ size_t len = sizeof(value);
+ if ((sysctl(mib, 4, &value, &len, NULL, 0)) != -1)
+ power->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->charge_full += charge_full;
+ power->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__)
+ double percent =
+ 100 * (power->charge_current / power->charge_full);
+
+ power->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->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]: %d%%", results->power.percent);
+ else if (results->power.battery_count == 0)
+ printf(" [DC]");
+ else
+ printf(" [DC]: %d%%", results->power.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 %d\n", power->have_ac, power->percent);
+}
+
+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"
+ " -h | -help | --help\n" " This help.\n");
+ 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_CPU)
+ {
+ for (i = 0; i < results.cpu_count; i++)
+ {
+ free(results.cores[i]);
+ }
+ free(results.cores);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/tingle/tmux.conf b/src/tingle/tmux.conf
new file mode 100644
index 0000000..01a8a23
--- /dev/null
+++ b/src/tingle/tmux.conf
@@ -0,0 +1,124 @@
+set-option -g prefix C-j
+unbind-key C-b
+bind-key b send-prefix
+# this is mainly taken from Richo Healey's dotfiles.
+# thanks mate.
+# TODO
+# Finish mapping up all keys
+# Learn how splits work
+# WISHLIST
+# Ideally, update the terminal title/hit the wm for activity on alert
+
+
+# Statusline
+
+set-window-option -g mode-keys vi # vi key
+set-option -g status-keys vi
+set -g default-terminal "screen-256color"
+# What's the point of vi keys without pane movement?
+bind l select-pane -R
+bind k select-pane -U
+bind j select-pane -D
+bind h select-pane -L
+
+bind < resize-pane -L 2
+bind > resize-pane -R 2
+bind - resize-pane -D 2
+bind + resize-pane -U 2
+
+bind ` next-layout
+#Ctrl-j-u volume up
+bind C-u run-shell -b 'volctl +10'
+#Ctrl-j-j volume down
+bind C-j run-shell -b 'volctl -10'
+
+# copy mode to escape key
+bind y copy-mode
+bind ] paste-buffer
+bind-key ^] send-keys Escape ":set paste\ri" \; paste-buffer\; send-keys Escape ":set nopaste\r"
+#20:03 < micahcowan> rich0_, just in case, something like ^^ [that]
+
+# move tmux copy buffer into x clipboard
+bind-key C-y save-buffer /tmp/tmux-buffer \; run-shell "cat /tmp/tmux-buffer | xclip"
+
+# splitting and cycling
+unbind %
+bind | split-window -h # horizontal split
+unbind '"'
+bind S split-window -v # vertical split
+
+set-window-option -g automatic-rename off # auto name
+
+# messages
+set-window-option -g mode-bg black
+set-window-option -g mode-fg white
+#set-option -g message-bg nta
+#set-option -g message-fg black
+
+# No visual activity
+set -g visual-activity off
+set -g visual-bell on
+set -g status-interval 120
+
+# Less obnoxious colors
+
+set -g pane-active-border-fg black
+set -g pane-active-border-bg white
+set -g pane-border-fg black
+set -g pane-border-bg white
+
+# Richo's screenlike bindings
+bind C-n next
+bind C-space next
+bind space next
+bind C-p prev
+bind C-d detach
+#unbind C-c
+bind C-c new-window
+bind K confirm-before kill-pane
+bind A command-prompt "rename-window '%%'"
+
+# bind M set-window-option monitor-activity (toggle)
+# bind _ set-window-option monitor-silence 15 (Toggle)
+
+bind-key C-a last-window
+bind-key / command-prompt "split-window -h 'exec man %%'"
+bind M set-window-option monitor-activity
+bind _ command-prompt "setw monitor-silence '%%'"
+
+bind I set-window-option synchronize-panes
+
+# Create an environment that zsh can make sense of
+# set-environment -g tmuxTERM $TERM
+set-environment -g INSCREEN yes
+
+# New shells should not inherit pwd
+# set -g default-path "."
+
+# status bar
+set-option -g status-bg black
+set-option -g status-fg white
+set-option -g message-attr none
+set-option -g message-bg black
+set-option -g message-fg white
+set-window-option -g window-status-bell-bg black
+set-window-option -g window-status-bell-attr bright
+set-window-option -g window-status-activity-bg red
+set-window-option -g window-status-activity-attr bright
+set-option -g status-interval 5
+set-option -g status-right-length 90
+set-option -g status-right "#[fg=colour255]#(tingle -s -c -m -a -p) #(date +'%H:%M %d-%m-%Y')"
+# Cheers Nei on freenode
+set-window-option -g window-status-current-format "[#[fg=white]#I:#W#F#[fg=red]]"
+set-option -g visual-activity on
+set-window-option -g monitor-activity off
+set-window-option -g window-status-current-fg red
+set-window-option -g window-status-current-attr bright
+
+# DEBUG
+# bind r source ~/.tmux.conf
+
+# clock
+set-window-option -g clock-mode-colour black
+set-window-option -g clock-mode-style 24
+# vim: ft=tmux
diff --git a/src/tingle/volctl b/src/tingle/volctl
new file mode 100755
index 0000000..02a2f8d
--- /dev/null
+++ b/src/tingle/volctl
@@ -0,0 +1,142 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my $OS = $^O;
+
+sub Error {
+ my ($str) = @_;
+
+ print STDERR "Error: $str\n";
+
+ exit (0 << 1);
+}
+
+sub mixer_levels_get {
+ my ($left, $right) = @_;
+ my $pipe_cmd = "";
+
+ if ($OS eq "openbsd" || $OS eq "netbsd")
+ {
+ $pipe_cmd = "mixerctl outputs.master|";
+ }
+ elsif ($OS eq "freebsd" || $OS eq "dragonfly")
+ {
+ $pipe_cmd = "mixer vol|";
+ }
+ elsif ($OS eq "darwin")
+ {
+ $pipe_cmd = "osascript -e 'get volume settings'|";
+ }
+ elsif ($OS eq "linux")
+ {
+ $pipe_cmd = "amixer get Master|";
+ }
+
+ open P, "$pipe_cmd" || die "Unable to determine mixer!\n";
+ my @lines = <P>;
+ close P;
+
+ my $have_mixer = 0;
+
+ foreach my $line (@lines)
+ {
+ if ($line =~ m/Left:.*\[(\d+)%\]/)
+ {
+ $$left = $1;
+ $have_mixer = 1;
+ }
+
+ if ($line =~ m/Right:.*\[(\d+)%\]/)
+ {
+ $$right = $1;
+ $have_mixer = 1;
+ }
+
+ if ($line =~ m/\Aoutput\svolume:(\d+),/)
+ {
+ $$left = $$right = $1;
+ return 1;
+ }
+ elsif ($line =~ m/(\d+),(\d+)/ || $line =~ m/(\d+):(\d+)/)
+ {
+ $$left = $1;
+ $$right = $2;
+ return 1;
+ }
+ }
+
+ return $have_mixer;
+}
+
+sub mixer_levels_set {
+ my ($new_left, $new_right) = @_;
+ my $cmd = "";
+
+ close (STDOUT);
+
+ if ($OS eq "openbsd" || $OS eq "netbsd")
+ {
+ $cmd = "mixerctl outputs.master=$new_left,$new_right";
+ }
+ elsif ($OS eq "freebsd" || $OS eq "dragonfly")
+ {
+ $cmd = "mixer $new_left:$new_right"
+ }
+ elsif ($OS eq "darwin")
+ {
+ my $greatest = $new_left > $new_right ? $new_left : $new_right;
+ my $ratio = 7 / 100;
+ my $volume = $greatest * $ratio;
+ $cmd = "osascript -e 'set volume $volume'";
+ }
+ elsif ($OS eq "linux")
+ {
+ my $greatest = $new_left > $new_right ? $new_left : $new_right;
+ $cmd = "amixer set \"Master\" $greatest%";
+ }
+
+ return system($cmd);
+}
+
+sub main {
+ my (@args) = @_;
+ my $argc = scalar(@args);
+ my ($inc, $dec) = (0, 0);
+ my ($left, $right) = (0, 0);
+
+ for (my $i = 0; $i < $argc; $i++)
+ {
+ if ($args[$i] =~ /\+(\d+)/)
+ {
+ $inc = $1;
+ }
+
+ if ($args[$i] =~ /\-(\d+)/)
+ {
+ $dec = $1
+ }
+ }
+
+ if (!mixer_levels_get(\$left, \$right))
+ {
+ Error("couldn't get current volumes");
+ }
+
+ $left -= $dec; $right -= $dec;
+ $left += $inc; $right += $inc;
+
+ if ($OS eq "linux" || $OS eq "darwin" || $OS eq "freebsd" || $OS eq "dragonfly")
+ {
+ if ($left < 0) { $left = 0; };
+ if ($left > 100) { $left = 100; };
+ if ($right < 0) { $right = 0; };
+ if ($right > 100) { $right = 100 };
+ }
+
+ return mixer_levels_set($left, $right);
+}
+
+exit(main(@ARGV));
+