From dc4f5e252d22a5594d5d25f627de58700827cef7 Mon Sep 17 00:00:00 2001 From: lxsang Date: Wed, 3 Feb 2021 12:40:43 +0100 Subject: [PATCH] Add code --- Makefile.am | 14 + configure.ac | 52 +++ ini.c | 185 +++++++++++ ini.h | 77 +++++ sysmon.c | 832 ++++++++++++++++++++++++++++++++++++++++++++++++ sysmond | Bin 0 -> 86120 bytes sysmond.conf | 33 ++ sysmond.service | 11 + 8 files changed, 1204 insertions(+) create mode 100644 Makefile.am create mode 100644 configure.ac create mode 100644 ini.c create mode 100644 ini.h create mode 100644 sysmon.c create mode 100755 sysmond create mode 100644 sysmond.conf create mode 100644 sysmond.service diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..2f2dd25 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,14 @@ +AUTOMAKE_OPTIONS = foreign + +AM_CPPFLAGS = -W -Wall -g -std=c99 -DPREFIX="\"$(prefix)\"" + +# bin +bin_PROGRAMS = sysmond +# source files +sysmond_SOURCES = ini.c sysmon.c + +sysconf_DATA = sysmond.conf +install-data-local: + - [ -d /etc/systemd/system/ ] && cp sysmond.service /etc/systemd/system/ + +EXTRA_DIST = ini.h sysmond.conf sysmond.service \ No newline at end of file diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..1cbf1b9 --- /dev/null +++ b/configure.ac @@ -0,0 +1,52 @@ +# initialise autoconf and set up some basic information about the program we’re packaging +AC_INIT([sysmon], [0.1.0], [xsang.le@gmail.com]) + +# We’re going to use automake for this project +# [subdir-objects] if needed +AM_INIT_AUTOMAKE([subdir-objects]) + +# dependencies +# C compiler +AC_PROG_CC +# libtool for linking +AC_PROG_LIBTOOL + +AC_DEFINE([_GNU_SOURCE], [1],[Use GNU source]) +# AC_CANONICAL_HOST is needed to access the 'host_os' variable + +AC_CHECK_LIB([m],[pow],[],[ + AC_MSG_ERROR([The math library is required]) +]) + +AC_CANONICAL_HOST +build_linux=no +build_windows=no +build_mac=no +# Detect the target system +case "${host_os}" in + linux*) + AC_DEFINE([LINUX], [1],[Linux system]) + build_linux=yes + ;; + darwin*) + build_mac=yes + AC_DEFINE([MACOS], [1],[MacOS system]) + ;; + *) + AC_MSG_ERROR(["OS $host_os is not supported"]) + ;; +esac + +AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"]) +AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"]) +AM_CONDITIONAL([OSX], [test "$build_mac" = "yes"]) + +# find a file called Makefile.in, substitute placeholders +# like @PACKAGE_VERSION@ with values like 0.1.0, +# and write the results to Makefile. +AC_CONFIG_FILES([ + Makefile +]) + +# output the script: +AC_OUTPUT \ No newline at end of file diff --git a/ini.c b/ini.c new file mode 100644 index 0000000..d29fde4 --- /dev/null +++ b/ini.c @@ -0,0 +1,185 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#include +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char c or ';' comment in given string, or pointer to + null at end of string if neither found. ';' must be prefixed by a whitespace + character to register as a comment. */ +static char* find_char_or_comment(const char* s, char c) +{ + int was_whitespace = 0; + while (*s && *s != c && !(was_whitespace && *s == ';')) { + was_whitespace = isspace((unsigned char)(*s)); + s++; + } + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy(dest, src, size); + dest[size - 1] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, + int (*handler)(void*, const char*, const char*, + const char*), + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; +#else + char* line; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc(INI_MAX_LINE); + if (!line) { + return -2; + } +#endif + + /* Scan through file line by line */ + while (fgets(line, INI_MAX_LINE, file) != NULL) { + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (*start == ';' || *start == '#') { + /* Per Python ConfigParser, allow '#' comments at start of line */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-black line with leading whitespace, treat as continuation + of previous name's value (as per Python ConfigParser). */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_char_or_comment(start + 1, ']'); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start && *start != ';') { + /* Not a comment, must be a name[=:]value pair */ + end = find_char_or_comment(start, '='); + if (*end != '=') { + end = find_char_or_comment(start, ':'); + } + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); + end = find_char_or_comment(value, '\0'); + if (*end == ';') + *end = '\0'; + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!handler(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + +#if INI_STOP_ON_FIRST_ERROR + if (error) + break; +#endif + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, + int (*handler)(void*, const char*, const char*, const char*), + void* user) +{ + FILE* file; + int error; + + file = fopen(filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} diff --git a/ini.h b/ini.h new file mode 100644 index 0000000..273615e --- /dev/null +++ b/ini.h @@ -0,0 +1,77 @@ +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef __INI_H__ +#define __INI_H__ + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's ConfigParser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +int ini_parse(const char* filename, + int (*handler)(void* user, const char* section, + const char* name, const char* value), + void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FILE* file, + int (*handler)(void* user, const char* section, + const char* name, const char* value), + void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + ConfigParser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See http://code.google.com/p/inih/issues/detail?id=21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Nonzero to use stack, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Stop parsing on first error (default is to keep parsing). */ +#ifndef INI_STOP_ON_FIRST_ERROR +#define INI_STOP_ON_FIRST_ERROR 0 +#endif + +/* Maximum line length for any line in INI file. */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 512 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __INI_H__ */ diff --git a/sysmon.c b/sysmon.c new file mode 100644 index 0000000..257ec4e --- /dev/null +++ b/sysmon.c @@ -0,0 +1,832 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ini.h" + +#define DEFAULT_CONF_FILE (PREFIX "/etc/sysmond.conf") +#define MODULE_NAME "sysmon" +// #define DEFAULT_INPUT "/sys/class/hwmon/hwmon2/device/in3_input" +#define NET_INF_STAT_PT "/sys/class/net/%s/statistics/%s" + +#define LOG_INIT(m) do { \ + setlogmask (LOG_UPTO (LOG_NOTICE)); \ + openlog ((m), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER); \ + } while(0) + + +#define M_LOG(m, a,...) syslog ((LOG_NOTICE),m "_log@[%s: %d]: " a "\n", __FILE__, \ + __LINE__, ##__VA_ARGS__) + +#define M_ERROR(m, a,...) syslog ((LOG_ERR),m "_error@[%s: %d]: " a "\n", __FILE__, \ + __LINE__, ##__VA_ARGS__) + +#define JSON_FMT "{" \ + "\"stamp_sec\": %lu," \ + "\"stamp_usec\": %lu," \ + "\"battery\": %.3f," \ + "\"battery_percent\": %.3f," \ + "\"battery_max_voltage\": %d," \ + "\"battery_min_voltage\": %d," \ + "\"cpu_temp\": %d," \ + "\"gpu_temp\": %d," \ + "\"cpu_usages\":[%s]," \ + "\"mem_total\": %lu," \ + "\"mem_free\": %lu," \ + "\"mem_used\": %lu," \ + "\"mem_buff_cache\": %lu," \ + "\"mem_available\": %lu," \ + "\"mem_swap_total\": %lu," \ + "\"mem_swap_free\": %lu," \ + "\"disk_total\": %lu," \ + "\"disk_free\": %lu," \ + "\"net\":[%s]" \ + "}" + +#define JSON_NET_FMT "{" \ + "\"name\":\"%s\"," \ + "\"rx\": %lu," \ + "\"tx\": %lu," \ + "\"rx_rate\": %.3f," \ + "\"tx_rate\": %.3f" \ + "}," + +#define MAX_BUF 256 +#define EQU(a,b) (strncmp(a,b,MAX_BUF) == 0) +#define MAX_NETWORK_INF 8 +typedef struct +{ + char bat_in[MAX_BUF]; + uint16_t max_voltage; + uint16_t min_voltage; + uint16_t cutoff_voltage; + float ratio; + uint16_t read_voltage; + float percent; +} sys_bat_t; + +typedef struct +{ + char cpu_temp_file[MAX_BUF]; + char gpu_temp_file[MAX_BUF]; + uint16_t cpu; + uint16_t gpu; +} sys_temp_t; + +typedef struct { + char name[32]; + unsigned long tx; + unsigned long rx; + float rx_rate; + float tx_rate; +} sys_net_inf_t; + +typedef struct { + uint8_t n_intf; + /*Monitor up to 8 interfaces*/ + sys_net_inf_t interfaces[MAX_NETWORK_INF]; +} sys_net_t; + +typedef struct +{ + unsigned long last_idle; + unsigned long last_sum; + float percent; +} sys_cpu_t; + +typedef struct { + char mount_path[MAX_BUF]; + unsigned long d_total; + unsigned long d_free; +} sys_disk_t; + +typedef struct +{ + unsigned long m_total; + unsigned long m_free; + unsigned long m_available; + unsigned long m_cache; + unsigned long m_buffer; + unsigned long m_swap_total; + unsigned long m_swap_free; +} sys_mem_t; + +typedef struct { + char conf_file[MAX_BUF]; + char data_file_out[MAX_BUF]; + sys_bat_t bat_stat; + sys_cpu_t* cpus; + sys_mem_t mem; + sys_temp_t temp; + sys_net_t net; + sys_disk_t disk; + int n_cpus; + struct itimerspec sample_period; + int pwoff_cd; + uint8_t power_off_percent; +} app_data_t; + +static volatile int running = 1; +static char buf[MAX_BUF]; + +static void int_handler(int dummy) +{ + (void)dummy; + running = 0; +} + +static void help(const char *app) +{ + fprintf(stderr, + "Usage: %s options.\n" + "Options:\n" + "\t -f : config file\n" + "\t -h : this help message\n", + app); +} + +static void map(app_data_t* opt) +{ + float volt = opt->bat_stat.read_voltage*opt->bat_stat.ratio; + if(volt < opt->bat_stat.min_voltage) + { + opt->bat_stat.percent = 0.0; + return; + } + float result = 101 - (101 / pow(1 + pow(1.33 * (volt - opt->bat_stat.min_voltage) / + (opt->bat_stat.max_voltage - opt->bat_stat.min_voltage), 4.5), 3)); + if(result > 100.0) + result = 100.0; + + opt->bat_stat.percent = result; +} + +static int guard_write(int fd, void* buffer, size_t size) +{ + int n = 0; + int write_len; + int st; + while(n != (int)size) + { + write_len = (int)size - n; + st = write(fd,buffer + n,write_len); + if(st == -1) + { + M_ERROR(MODULE_NAME,"Unable to write to #%d: %s", fd, strerror(errno)); + return -1; + } + if(st == 0) + { + M_ERROR(MODULE_NAME,"Endpoint %d is closed", fd); + return -1; + } + n += st; + } + return n; +} + +static int read_line(int fd, char *buf, int size) +{ + int i = 0; + char c = '\0'; + int n; + while ((i < size - 1) && (c != '\n')) + { + n = read(fd, &c, 1); + if (n > 0) + { + buf[i] = c; + i++; + } + else + c = '\n'; + } + buf[i] = '\0'; + return i; +} + +static int read_voltage(app_data_t* opts) +{ + int fd, ret; + if(opts->bat_stat.bat_in[0] == '\0') + { + return 0; + } + fd = open(opts->bat_stat.bat_in, O_RDONLY); + if(fd < 0) + { + M_ERROR(MODULE_NAME, "Unable to open input: %s", opts->bat_stat.bat_in); + return -1; + } + (void)memset(buf, '\0', sizeof(buf)); + ret = read(fd, buf, sizeof(buf)); + if(ret > 0) + { + opts->bat_stat.read_voltage = atoi(buf); + map(opts); + } + (void)close(fd); + return 0; +} + +static int read_cpu_info(app_data_t* opts) +{ + int fd, ret, j, i = 0; + const char d[2] = " "; + char* token; + unsigned long sum = 0, idle = 0; + fd = open("/proc/stat", O_RDONLY); + if(fd < 0) + { + M_ERROR(MODULE_NAME, "Unable to open stat: %s", strerror(errno)); + return -1; + } + for (i = 0; i < opts->n_cpus; i++) + { + ret = read_line(fd, buf, MAX_BUF); + if(ret > 0 && buf[0] == 'c' && buf[1] == 'p' && buf[2] == 'u') + { + token = strtok(buf,d); + sum = 0; + j = 0; + while(token!=NULL) + { + token = strtok(NULL,d); + if(token!=NULL){ + sum += strtoul(token, NULL, 10); + if(j==3) + idle = strtoul(token, NULL, 10); + j++; + } + } + opts->cpus[i].percent = 100 - (idle-opts->cpus[i].last_idle)*100.0/(sum-opts->cpus[i].last_sum); + opts->cpus[i].last_idle = idle; + opts->cpus[i].last_sum = sum; + } + else + { + M_ERROR(MODULE_NAME, "Unable to read CPU infos at: %d", i); + break; + } + } + (void) close(fd); + if(i==0) + { + M_ERROR(MODULE_NAME, "No CPU info found"); + return -1; + } + return i; +} + +static int read_mem_info(app_data_t* opts) +{ + int fd, ret; + const char d[2] = " "; + unsigned long data[7]; + char* token; + fd = open("/proc/meminfo", O_RDONLY); + if(fd < 0) + { + M_ERROR(MODULE_NAME, "Unable to open meminfo: %s", strerror(errno)); + return -1; + } + for (int i = 0; i < 7; i++) { + ret = read_line(fd,buf,MAX_BUF); + token = strtok(buf,d); + token = strtok(NULL,d); + if(token != NULL) + { + data[i] = (unsigned long)strtoul(token, NULL, 10); + } + else + { + data[i] = 0; + } + if(i == 4) + { + for (int j = 0; j < 9; j++) { + ret = read_line(fd,buf,MAX_BUF); + // skip 10 line + } + } + } + opts->mem.m_total = data[0]; + opts->mem.m_free = data[1]; + opts->mem.m_available = data[2]; + opts->mem.m_buffer = data[3]; + opts->mem.m_cache = data[4]; + opts->mem.m_swap_total = data[5]; + opts->mem.m_swap_free = data[6]; + (void)ret; + (void)close(fd); + + + /*printf("total: %d used: %d, free: %d buffer/cache: %d, available: %d \n", + opts->mem.m_total / 1024, + (opts->mem.m_total - opts->mem.m_free - opts->mem.m_buffer-opts->mem.m_cache)/1024, + opts->mem.m_free/1024, + (opts->mem.m_buffer+opts->mem.m_cache)/1024, + opts->mem.m_available/1024);*/ + return 0; +} + +static int read_temp_file(const char* file, uint16_t* output) +{ + int fd, ret; + if(file[0] != '\0') + { + fd = open(file, O_RDONLY); + if(fd < 0) + { + M_ERROR(MODULE_NAME, "Unable to open temp file %s : %s", file, strerror(errno)); + return -1; + } + (void)memset(buf, '\0', sizeof(buf)); + ret = read(fd, buf, MAX_BUF); + if(ret < 0) + { + M_ERROR(MODULE_NAME, "Unable to read temperature: %s", strerror(errno)); + (void) close(fd); + return -1; + } + *output = (uint16_t)atoi(buf); + (void) close(fd); + } + return 0; +} + +static int read_cpu_temp(app_data_t* opts) +{ + if(read_temp_file(opts->temp.cpu_temp_file, &opts->temp.cpu) == -1) + { + return -1; + } + return read_temp_file(opts->temp.gpu_temp_file, &opts->temp.gpu); +} + +static int read_net_statistic(app_data_t* opts) +{ + int fd, ret; + float period; + long unsigned int bytes; + + period = ((float)opts->sample_period.it_value.tv_nsec) / 1.0e9; + for (int i = 0; i < opts->net.n_intf; i++) + { + // rx + (void)snprintf(buf, MAX_BUF-1, NET_INF_STAT_PT, opts->net.interfaces[i].name, "rx_bytes"); + fd = open(buf, O_RDONLY); + if(fd < 0) + { + M_ERROR(MODULE_NAME, "Unable to open %s: %s", buf, strerror(errno)); + return -1; + } + // read data to buff + (void)memset(buf,'\0', MAX_BUF); + ret = read(fd, buf, MAX_BUF); + (void)close(fd); + if(ret <= 0) + { + M_ERROR(MODULE_NAME, "Unable to read RX data of %s: %s", opts->net.interfaces[i].name, strerror(errno)); + return -1; + } + bytes = (unsigned long) strtoul(buf, NULL, 10); + opts->net.interfaces[i].rx_rate = ((float)(bytes - opts->net.interfaces[i].rx) / period); + opts->net.interfaces[i].rx = bytes; + + (void)snprintf(buf, MAX_BUF-1, NET_INF_STAT_PT, opts->net.interfaces[i].name, "tx_bytes"); + fd = open(buf, O_RDONLY); + if(fd < 0) + { + M_ERROR(MODULE_NAME, "Unable to open %s: %s", buf, strerror(errno)); + return -1; + } + // read data to buff + (void)memset(buf,'\0', MAX_BUF); + ret = read(fd, buf, MAX_BUF); + (void)close(fd); + if(ret <= 0) + { + M_ERROR(MODULE_NAME, "Unable to read TX data of %s: %s", opts->net.interfaces[i].name, strerror(errno)); + return -1; + } + bytes = (unsigned long) strtoul(buf, NULL, 10); + opts->net.interfaces[i].tx_rate = ((float)(bytes - opts->net.interfaces[i].tx) / period); + opts->net.interfaces[i].tx = bytes ; + } + return 0; +} + +static int read_disk_usage(app_data_t* opts) +{ + struct statvfs stat; + int ret = statvfs(opts->disk.mount_path, &stat); + if(ret < 0) + { + M_ERROR(MODULE_NAME, "Unable to query disk usage of %s: %s", opts->disk.mount_path, strerror(errno)); + return -1; + } + opts->disk.d_total = stat.f_blocks * stat.f_frsize; + opts->disk.d_free = stat.f_bfree * stat.f_frsize; + return 0; +} + +static int log_to_file(app_data_t* opts) +{ + int ret,fd; + char out_buf[1024]; + char net_buf[MAX_BUF]; + if(opts->data_file_out[0] == '\0') + { + return 0; + } + fd = open(opts->data_file_out, O_CREAT|O_WRONLY|O_APPEND | O_NONBLOCK, 0644); + if(fd < 0) + { + M_ERROR(MODULE_NAME, "Unable to open output file: %s", strerror(errno)); + return -1; + } + (void)memset(buf,'\0',MAX_BUF); + char* ptr = buf; + // CPU + size_t len = 0; + for (int i = 0; i < opts->n_cpus; i++) { + if(MAX_BUF - len -1 <= 0) + { + break; + } + snprintf(ptr, MAX_BUF - len -1, "%.3f,", opts->cpus[i].percent); + len = strlen(buf); + ptr = buf+len; + } + buf[len - 1] = '\0'; + + // NET + len = 0; + ptr = net_buf; + for (int i = 0; i < opts->net.n_intf; i++) { + if(MAX_BUF - len -1 < strlen(JSON_NET_FMT)) + { + break; + } + snprintf(ptr, MAX_BUF - len -1, JSON_NET_FMT, + opts->net.interfaces[i].name, + opts->net.interfaces[i].rx, + opts->net.interfaces[i].tx, + opts->net.interfaces[i].rx_rate, + opts->net.interfaces[i].tx_rate + ); + len = strlen(net_buf); + ptr = net_buf+len; + } + net_buf[len - 1] = '\0'; + + struct timeval now; + gettimeofday(&now, NULL); + snprintf(out_buf, sizeof(out_buf), JSON_FMT, + now.tv_sec, + now.tv_usec, + opts->bat_stat.read_voltage* opts->bat_stat.ratio, + opts->bat_stat.percent, + opts->bat_stat.max_voltage, + opts->bat_stat.min_voltage, + opts->temp.cpu, + opts->temp.gpu, + buf, + opts->mem.m_total, + opts->mem.m_free, + (opts->mem.m_total - opts->mem.m_free - opts->mem.m_buffer-opts->mem.m_cache), + opts->mem.m_buffer+opts->mem.m_cache, + opts->mem.m_available, + opts->mem.m_swap_total, + opts->mem.m_swap_free, + opts->disk.d_total, + opts->disk.d_free, + net_buf + ); + ret = guard_write(fd,out_buf,strlen(out_buf)); + if(ret <= 0) + { + M_ERROR(MODULE_NAME, "Unable to write data to output file"); + ret = -1; + } + if(ret != (int)strlen(out_buf)) + { + M_ERROR(MODULE_NAME, "Unable to write all battery info to output file"); + ret = -1; + } + else + { + // M_LOG(MODULE_NAME, "written %d bytes to file: %s", strlen(out_buf), opts->data_file_out); + ret = 0; + } + (void) close(fd); + return ret; +} + +static int ini_handle(void *user_data, const char *section, const char *name, const char *value) +{ + (void)section; + unsigned long period = 0; + const char d[2] = ","; + char* token; + + app_data_t* opts = (app_data_t*) user_data; + if(EQU(name, "battery_max_voltage")) + { + opts->bat_stat.max_voltage = atoi(value); + } + else if(EQU(name, "battery_min_voltage")) + { + opts->bat_stat.min_voltage = atoi(value); + } + else if(EQU(name, "battery_cutoff_votalge")) + { + opts->bat_stat.cutoff_voltage = atoi(value); + } + else if(EQU(name, "battery_divide_ratio")) + { + opts->bat_stat.ratio = atof(value); + } + else if(EQU(name, "battery_input")) + { + strncpy(opts->bat_stat.bat_in, value, MAX_BUF - 1); + } + else if(EQU(name, "sample_period")) + { + period = strtoul(value, NULL, 10)*1e6; + opts->sample_period.it_interval.tv_nsec = period; + opts->sample_period.it_value.tv_nsec = period; + } + else if(EQU(name, "cpu_core_number")) + { + opts->n_cpus = atoi(value) + 1; + } + else if(EQU(name, "power_off_count_down")) + { + opts->pwoff_cd = atoi(value); + } + else if(EQU(name, "power_off_percent")) + { + opts->power_off_percent = (uint8_t)atoi(value); + } + else if(EQU(name, "data_file_out")) + { + (void)strncpy(opts->data_file_out, value, MAX_BUF-1); + } + else if(EQU(name, "cpu_temperature_input")) + { + (void)strncpy(opts->temp.cpu_temp_file, value, MAX_BUF-1); + } + else if(EQU(name, "gpu_temperature_input")) + { + (void)strncpy(opts->temp.gpu_temp_file, value, MAX_BUF-1); + } + else if(EQU(name, "disk_mount_point")) + { + (void)strncpy(opts->disk.mount_path, value, MAX_BUF-1); + } + else if(EQU(name, "network_interfaces")) + { + // parsing the network interfaces + token = strtok((char*)value,d); + opts->net.n_intf = 0; + while(token != NULL) + { + (void) strncpy(opts->net.interfaces[opts->net.n_intf].name, token, sizeof(opts->net.interfaces[opts->net.n_intf].name) - 1); + opts->net.n_intf++; + if(opts->net.n_intf >= MAX_NETWORK_INF) + break; + token = strtok(NULL,d); + } + } + else + { + M_ERROR(MODULE_NAME, "Ignore unknown configuration %s = %s", name, value); + return 0; + } + + return 1; +} + +static int load_config(app_data_t* opts) +{ + // global + (void)memset(opts->data_file_out, '\0', MAX_BUF); + (void)memset(opts->temp.cpu_temp_file, '\0', MAX_BUF); + (void)memset(opts->temp.gpu_temp_file, '\0', MAX_BUF); + opts->pwoff_cd = 5; + opts->sample_period.it_interval.tv_sec = 0; + opts->sample_period.it_interval.tv_nsec = 3e+8; + opts->sample_period.it_value.tv_sec = 0; + opts->sample_period.it_value.tv_nsec = 3e+8; + opts->cpus = NULL; + opts->n_cpus = 2; + + //battery + (void)memset(opts->bat_stat.bat_in, '\0', MAX_BUF); + opts->bat_stat.max_voltage = 4200; + opts->bat_stat.min_voltage = 3300; + opts->bat_stat.cutoff_voltage = 3000; + opts->bat_stat.ratio = 1.0; + opts->bat_stat.read_voltage = 0.0; + opts->bat_stat.percent = 0.0; + opts->power_off_percent = 1; + + + (void)memset(&opts->mem, '\0', sizeof(opts->mem)); + (void)memset(&opts->temp, '\0', sizeof(opts->temp)); + (void)memset(&opts->net, '\0', sizeof(opts->net)); + (void)memset(&opts->disk, '\0', sizeof(opts->disk)); + opts->disk.mount_path[0] = '/'; + + M_LOG(MODULE_NAME, "Use configuration: %s", opts->conf_file); + if (ini_parse(opts->conf_file, ini_handle, opts) < 0) + { + M_ERROR(MODULE_NAME, "Can't load '%s'", opts->conf_file); + return -1; + } + // check battery configuration + if((opts->bat_stat.max_voltage < opts->bat_stat.min_voltage) || + (opts->bat_stat.max_voltage < opts->bat_stat.cutoff_voltage) || + (opts->bat_stat.min_voltage < opts->bat_stat.cutoff_voltage)) + { + M_ERROR(MODULE_NAME, "Battery configuration is invalid: max: %d, min: %d, cut off: %d", + opts->bat_stat.max_voltage, + opts->bat_stat.min_voltage, + opts->bat_stat.cutoff_voltage); + return -1; + } + return 0; +} + +int main(int argc, char *const *argv) +{ + int ret, tfd, count_down; + float volt; + uint64_t expirations_count; + app_data_t opts; + LOG_INIT(MODULE_NAME); + signal(SIGPIPE, SIG_IGN); + signal(SIGABRT, SIG_IGN); + signal(SIGINT, int_handler); + (void)strncpy(opts.conf_file, DEFAULT_CONF_FILE, MAX_BUF - 1); + while ((ret = getopt(argc, argv, "hf:")) != -1) + { + switch (ret) + { + case 'f': + (void)strncpy(opts.conf_file, optarg, MAX_BUF-1); + break; + default: + help(argv[0]); + return -1; + } + } + + if(optind > argc) + { + help(argv[0]); + return -1; + } + + if(load_config(&opts) != 0) + { + fprintf(stderr,"Unable to read config file\n"); + return -1; + } + + M_LOG(MODULE_NAME, "Data Output: %s", opts.data_file_out); + M_LOG(MODULE_NAME, "Battery input: %s", opts.bat_stat.bat_in); + M_LOG(MODULE_NAME, "Battery Max voltage: %d", opts.bat_stat.max_voltage); + M_LOG(MODULE_NAME, "Battery Min voltage: %d", opts.bat_stat.min_voltage); + M_LOG(MODULE_NAME, "Battery Cut off voltage: %d", opts.bat_stat.cutoff_voltage); + M_LOG(MODULE_NAME, "Battery Divide ratio: %.3f", opts.bat_stat.ratio); + M_LOG(MODULE_NAME, "Sample period: %d", (int)(opts.sample_period.it_value.tv_nsec / 1e6)); + M_LOG(MODULE_NAME, "CPU cores: %d", opts.n_cpus); + M_LOG(MODULE_NAME, "Power off count down: %d", opts.pwoff_cd); + M_LOG(MODULE_NAME,"CPU temp. input: %s",opts.temp.cpu_temp_file); + M_LOG(MODULE_NAME,"GPU temp. input: %s",opts.temp.gpu_temp_file); + M_LOG(MODULE_NAME, "Poweroff percent: %d", opts.power_off_percent); + + // init timerfd + tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + if (tfd == -1) + { + M_ERROR(MODULE_NAME, "Unable to create timerfd: %s", strerror(errno)); + fprintf(stderr,"Unable to create timer fd: %s\n", strerror(errno)); + return -1; + } + if (timerfd_settime(tfd, 0 /* no flags */, &opts.sample_period, NULL) == -1) + { + M_ERROR(MODULE_NAME, "Unable to set framerate period: %s", strerror(errno)); + (void)close(tfd); + return -1; + } + //init CPU monitors + opts.cpus = (sys_cpu_t*) malloc(opts.n_cpus*sizeof(sys_cpu_t)); + for(int i=0; i < opts.n_cpus; i++) + { + opts.cpus[i].last_sum = 0; + opts.cpus[i].last_idle = 0; + opts.cpus[i].percent = 0.0; + } + // loop + count_down = opts.pwoff_cd; + while(running) + { + if(opts.bat_stat.bat_in[0] != '\0') + { + // open the file + if(read_voltage(&opts) == -1) + { + M_ERROR(MODULE_NAME, "Unable to read system voltage"); + } + volt = opts.bat_stat.read_voltage*opts.bat_stat.ratio; + if(volt < opts.bat_stat.cutoff_voltage) + { + M_LOG(MODULE_NAME, "Invalid voltage read: %.3f", volt); + } + else + { + if(opts.bat_stat.percent <= (float)opts.power_off_percent) + { + count_down--; + M_LOG(MODULE_NAME, "Out of battery. Will shutdown after %d count down", count_down); + } + else + { + // reset the count_down + count_down = opts.pwoff_cd; + } + // check if we should shutdown + if(count_down <= 0) + { + M_LOG(MODULE_NAME, "Shutting down system"); + ret = system("poweroff"); + (void) ret; + // this should never happend + return 0; + } + } + } + // read cpu info + if(read_cpu_info(&opts) == -1) + { + M_ERROR(MODULE_NAME, "Unable to read CPU infos"); + } + // read memory usage + if(read_mem_info(&opts) == -1) + { + M_ERROR(MODULE_NAME, "Unable to read memory usage"); + } + // read CPU temperature + if(read_cpu_temp(&opts) == -1) + { + M_ERROR(MODULE_NAME, "Unable to read CPU temperature"); + } + if(read_net_statistic(&opts) == -1) + { + M_ERROR(MODULE_NAME, "Unable to query network statistic"); + } + if(read_disk_usage(&opts) == -1) + { + M_ERROR(MODULE_NAME, "Unable to query disk usage"); + } + // log to file + if(log_to_file(&opts) == -1) + { + M_ERROR(MODULE_NAME, "Unable to write sysinfo to output"); + } + // check timeout + if(read(tfd, &expirations_count, sizeof(expirations_count)) != (int)sizeof(expirations_count)) + { + M_ERROR(MODULE_NAME, "Unable to read timer: %s", strerror(errno)); + } + else if (expirations_count > 1u) + { + M_ERROR(MODULE_NAME, "LOOP OVERFLOW COUNT: %lu", (long unsigned int)expirations_count); + } + } + + if(opts.cpus) + free(opts.cpus); + if(tfd > 0) + { + (void)close(tfd); + } + return 0; +} \ No newline at end of file diff --git a/sysmond b/sysmond new file mode 100755 index 0000000000000000000000000000000000000000..d2778b983241903b4a298bacd3636ce3219904be GIT binary patch literal 86120 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 literal 0 HcmV?d00001 diff --git a/sysmond.conf b/sysmond.conf new file mode 100644 index 0000000..6c31c19 --- /dev/null +++ b/sysmond.conf @@ -0,0 +1,33 @@ +# Battery configuration +battery_max_voltage = 12600 +battery_min_voltage = 10000 +battery_cutoff_votalge = 9000 +battery_divide_ratio = 3.36 +battery_input = /sys/class/hwmon/hwmon2/device/in3_input + +# daemon configuration +# time period between loop step in ms +sample_period = 500 + +#number of cpus to monitor +cpu_core_number = 4 + +# network interfaces to monitor +network_interfaces = wlan0 +# e.g. wlan0,eth0 + +# disk mount point to monitor +disk_mount_point = / + +# when battery is low +# the system will be shutdown after n count down +power_off_count_down = 10 +# the system will bet shutdown if the battery voltage percent is below this value +power_off_percent = 3 + +cpu_temperature_input=/sys/devices/virtual/thermal/thermal_zone1/temp + +gpu_temperature_input=/sys/devices/virtual/thermal/thermal_zone2/temp + +# output system info to file +data_file_out = /var/jarvis:fbf070ddea3ea90d07f456540b405d302554ec82 \ No newline at end of file diff --git a/sysmond.service b/sysmond.service new file mode 100644 index 0000000..0808f3d --- /dev/null +++ b/sysmond.service @@ -0,0 +1,11 @@ +[Unit] +Description=System monitor + +[Service] +Type=simple +User=root +ExecStart=/usr/bin/sysmond +Restart=always + +[Install] +WantedBy=multi-user.target \ No newline at end of file