forked from external/yambar
200 lines
5.1 KiB
C
200 lines
5.1 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <math.h>
|
|
#include <poll.h>
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
|
|
#define LOG_MODULE "mem"
|
|
#define LOG_ENABLE_DBG 0
|
|
#include "../bar/bar.h"
|
|
#include "../config-verify.h"
|
|
#include "../config.h"
|
|
#include "../log.h"
|
|
#include "../plugin.h"
|
|
|
|
static const long min_poll_interval = 250;
|
|
|
|
struct private
|
|
{
|
|
struct particle *label;
|
|
uint16_t interval;
|
|
uint64_t mem_free;
|
|
uint64_t mem_total;
|
|
};
|
|
|
|
static void
|
|
destroy(struct module *mod)
|
|
{
|
|
struct private *m = mod->private;
|
|
m->label->destroy(m->label);
|
|
free(m);
|
|
module_default_destroy(mod);
|
|
}
|
|
|
|
static const char *
|
|
description(const struct module *mod)
|
|
{
|
|
return "mem";
|
|
}
|
|
|
|
static bool
|
|
get_mem_stats(uint64_t *mem_free, uint64_t *mem_total)
|
|
{
|
|
bool mem_total_found = false;
|
|
bool mem_free_found = false;
|
|
|
|
FILE *fp = NULL;
|
|
char *line = NULL;
|
|
size_t len = 0;
|
|
ssize_t read = 0;
|
|
|
|
fp = fopen("/proc/meminfo", "re");
|
|
if (NULL == fp) {
|
|
LOG_ERRNO("unable to open /proc/meminfo");
|
|
return false;
|
|
}
|
|
|
|
while ((read = getline(&line, &len, fp)) != -1) {
|
|
if (strncmp(line, "MemTotal:", sizeof("MemTotal:") - 1) == 0) {
|
|
read = sscanf(line + sizeof("MemTotal:") - 1, "%" SCNu64, mem_total);
|
|
mem_total_found = (read == 1);
|
|
}
|
|
if (strncmp(line, "MemAvailable:", sizeof("MemAvailable:") - 1) == 0) {
|
|
read = sscanf(line + sizeof("MemAvailable:"), "%" SCNu64, mem_free);
|
|
mem_free_found = (read == 1);
|
|
}
|
|
}
|
|
free(line);
|
|
|
|
fclose(fp);
|
|
|
|
return mem_free_found && mem_total_found;
|
|
}
|
|
|
|
static struct exposable *
|
|
content(struct module *mod)
|
|
{
|
|
const struct private *p = mod->private;
|
|
|
|
mtx_lock(&mod->lock);
|
|
|
|
const uint64_t mem_free = p->mem_free;
|
|
const uint64_t mem_total = p->mem_total;
|
|
const uint64_t mem_used = mem_total - mem_free;
|
|
|
|
double percent_used = ((double)mem_used * 100) / (mem_total + 1);
|
|
double percent_free = ((double)mem_free * 100) / (mem_total + 1);
|
|
|
|
struct tag_set tags = {
|
|
.tags = (struct tag *[]){tag_new_int(mod, "free", mem_free * 1024), tag_new_int(mod, "used", mem_used * 1024),
|
|
tag_new_int(mod, "total", mem_total * 1024),
|
|
tag_new_int_range(mod, "percent_free", round(percent_free), 0, 100),
|
|
tag_new_int_range(mod, "percent_used", round(percent_used), 0, 100)},
|
|
.count = 5,
|
|
};
|
|
|
|
struct exposable *exposable = p->label->instantiate(p->label, &tags);
|
|
tag_set_destroy(&tags);
|
|
mtx_unlock(&mod->lock);
|
|
return exposable;
|
|
}
|
|
|
|
static int
|
|
run(struct module *mod)
|
|
{
|
|
const struct bar *bar = mod->bar;
|
|
bar->refresh(bar);
|
|
struct private *p = mod->private;
|
|
while (true) {
|
|
struct pollfd fds[] = {{.fd = mod->abort_fd, .events = POLLIN}};
|
|
|
|
int res = poll(fds, 1, p->interval);
|
|
if (res < 0) {
|
|
if (EINTR == errno) {
|
|
continue;
|
|
}
|
|
|
|
LOG_ERRNO("unable to poll abort fd");
|
|
return -1;
|
|
}
|
|
|
|
if (fds[0].revents & POLLIN)
|
|
break;
|
|
|
|
mtx_lock(&mod->lock);
|
|
p->mem_free = 0;
|
|
p->mem_total = 0;
|
|
if (!get_mem_stats(&p->mem_free, &p->mem_total)) {
|
|
LOG_ERR("unable to retrieve the memory stats");
|
|
}
|
|
mtx_unlock(&mod->lock);
|
|
bar->refresh(bar);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct module *
|
|
mem_new(uint16_t interval, struct particle *label)
|
|
{
|
|
struct private *p = calloc(1, sizeof(*p));
|
|
p->label = label;
|
|
p->interval = interval;
|
|
|
|
struct module *mod = module_common_new();
|
|
mod->private = p;
|
|
mod->run = &run;
|
|
mod->destroy = &destroy;
|
|
mod->content = &content;
|
|
mod->description = &description;
|
|
return mod;
|
|
}
|
|
|
|
static struct module *
|
|
from_conf(const struct yml_node *node, struct conf_inherit inherited)
|
|
{
|
|
const struct yml_node *interval = yml_get_value(node, "poll-interval");
|
|
const struct yml_node *c = yml_get_value(node, "content");
|
|
|
|
return mem_new(interval == NULL ? min_poll_interval : yml_value_as_int(interval), conf_to_particle(c, inherited));
|
|
}
|
|
|
|
static bool
|
|
conf_verify_poll_interval(keychain_t *chain, const struct yml_node *node)
|
|
{
|
|
if (!conf_verify_unsigned(chain, node))
|
|
return false;
|
|
|
|
if (yml_value_as_int(node) < min_poll_interval) {
|
|
LOG_ERR("%s: interval value cannot be less than %ldms", conf_err_prefix(chain, node), min_poll_interval);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
verify_conf(keychain_t *chain, const struct yml_node *node)
|
|
{
|
|
static const struct attr_info attrs[] = {
|
|
{"poll-interval", false, &conf_verify_poll_interval},
|
|
MODULE_COMMON_ATTRS,
|
|
};
|
|
|
|
return conf_verify_dict(chain, node, attrs);
|
|
}
|
|
|
|
const struct module_iface module_mem_iface = {
|
|
.verify_conf = &verify_conf,
|
|
.from_conf = &from_conf,
|
|
};
|
|
|
|
#if defined(CORE_PLUGINS_AS_SHARED_LIBRARIES)
|
|
extern const struct module_iface iface __attribute__((weak, alias("module_mem_iface")));
|
|
#endif
|