mirror of
https://github.com/lxsang/sysmond.git
synced 2024-12-25 02:38:22 +01:00
Add code
This commit is contained in:
parent
6b3240f7ea
commit
dc4f5e252d
14
Makefile.am
Normal file
14
Makefile.am
Normal file
@ -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
|
52
configure.ac
Normal file
52
configure.ac
Normal file
@ -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
|
185
ini.c
Normal file
185
ini.c
Normal file
@ -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 <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ini.h"
|
||||
|
||||
#if !INI_USE_STACK
|
||||
#include <stdlib.h>
|
||||
#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;
|
||||
}
|
77
ini.h
Normal file
77
ini.h
Normal file
@ -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 <stdio.h>
|
||||
|
||||
/* 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__ */
|
832
sysmon.c
Normal file
832
sysmon.c
Normal file
@ -0,0 +1,832 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
#include <signal.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <math.h>
|
||||
|
||||
#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 <value>: config file\n"
|
||||
"\t -h <value>: 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;
|
||||
}
|
33
sysmond.conf
Normal file
33
sysmond.conf
Normal file
@ -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
|
11
sysmond.service
Normal file
11
sysmond.service
Normal file
@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=System monitor
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/bin/sysmond
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
Loading…
Reference in New Issue
Block a user