over-env/data.cpp

454 lines
9.6 KiB
C++
Executable file

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <sys/vfs.h>
#include <libgen.h>
#include <ctype.h>
#include <stdint.h>
class Space {
public:
Space(const char *name, float available, float total) {
if (strlen(name) > 15) {
strcpy(this->name, "!!!");
} else {
strcpy(this->name, name);
}
this->available = available;
if (total > 0) {
this->percent = available*100 / total;
this->active = true;
} else {
this->percent = 100;
this->active = false;
}
}
char name[16];
float available;
float percent;
bool active;
};
typedef struct {
float load;
char proc[16];
unsigned int color;
} Sysload;
typedef struct {
unsigned short int m1, m2;
bool long_names;
} Config;
/// Data gatherers
void get_available_space(std::vector<Space> &space);
void get_memswap(std::vector<Space> &space);
void get_sysload(Sysload &sysload);
/// Formatting and parsing
void append(char *output, const char *string, int color=-1);
const char *float_to_str(float value, char *output);
int space_to_color(Space &space);
float grepf(const std::string &data, const char *marker);
std::vector<std::string> read_lines_from_file(std::string filename);
bool column_contains_substr(std::string line, unsigned int column, std::string substr, int position=0);
std::string get_column(std::string line, unsigned int column);
bool str_in_list(std::string str, std::vector<std::string> list);
/// Global
std::map<int, std::string> color_map;
Config config;
std::vector<std::string> ignored;
enum {
COL_GREEN,
COL_DARKGREEN,
COL_YELLOW,
COL_BLUE,
COL_RED,
COL_RFLASH,
COL_CYAN,
COL_NONE
};
int main(int argc, char **argv) {
char output[512];
char tmp[16];
std::vector<Space> space;
Sysload sysload;
output[0] = 0;
color_map[COL_GREEN] = "\e[1;32m";
color_map[COL_DARKGREEN] = "\e[0;32m";
color_map[COL_YELLOW] = "\e[1;33m";
color_map[COL_BLUE] = "\e[1;34m";
color_map[COL_RED] = "\e[1;31m";
color_map[COL_RFLASH] = "\e[5;31m";
color_map[COL_CYAN] = "\e[1;36m";
color_map[COL_NONE] = "\e[0m";
ignored.push_back("rootfs");
ignored.push_back("proc");
ignored.push_back("sysfs");
ignored.push_back("udev");
ignored.push_back("devpts");
ignored.push_back("shm");
ignored.push_back("usbfs");
ignored.push_back("binfmt_misc");
ignored.push_back("fusectl");
if (argc == 4) {
config.m1 = atoi(argv[1]);
config.m2 = atoi(argv[2]);
if (strcmp(argv[3], "long") == 0) {
config.long_names = true;
} else {
config.long_names = false;
}
} else {
config.m1 = 20;
config.m2 = 10;
config.long_names = false;
}
get_available_space(space);
get_memswap(space);
get_sysload(sysload);
append(output, "%m", sysload.color); // host
append(output, ":");
append(output, "%l", COL_DARKGREEN); // tty
append(output, " ");
append(output, "%~", COL_DARKGREEN); // cwd
append(output, ":::");
for (unsigned int i = 0; i < space.size(); i++) {
append(output, space[i].name);
// TODO Active only
append(output, float_to_str(space[i].available, tmp), space_to_color(space[i]));
append(output, " ");
}
append(output, "| ");
append(output, float_to_str(sysload.load, tmp), sysload.color);
append(output, " ");
append(output, sysload.proc, sysload.color);
std::cout << output;
return 0;
}
void append(char *output, const char *input, int color) {
unsigned int len_o, len_i, len_c = 0;
len_o = strlen(output);
len_i = strlen(input);
if (color != -1) len_c = strlen(color_map[color].c_str());
if (color != -1) strcpy(output + len_o, color_map[color].c_str());
strcpy(output + len_o + len_c, input);
if (color != -1) strcpy(output + len_o + len_i + len_c, color_map[COL_NONE].c_str());
}
const char *float_to_str(float value, char *output) {
char suffix;
output[0] = 0;
if (value > 2*1099511627776.0) { // 2T
value /= 1099511627776.0;
suffix = 'T';
} else if (value > 2*1073741824.0) { // 2G
value /= 1073741824.0;
suffix = 'G';
} else if (value > 2*1048576.0) { // 2M
value /= 1048576.0;
suffix = 'M';
} else if (value > 2*1024.0) { // 2k
value /= 1024.0;
suffix = 'k';
} else {
suffix = 0;
}
sprintf(output, "%.2f%c", value, suffix);
return output;
}
int space_to_color(Space &space) {
if (space.percent >= config.m1) {
return COL_GREEN;
} else if (space.percent >= config.m2) {
return COL_YELLOW;
} else if (space.percent > (config.m2/32.0)) {
return COL_RED;
} else {
return COL_RFLASH;
}
}
void get_sysload(Sysload &sysload) {
float dump;
std::ifstream in;
in.open("/proc/loadavg", std::ifstream::in);
in >> sysload.load;
in >> dump;
in >> dump;
in >> sysload.proc;
in.close();
if (sysload.load > 12) {
sysload.color = COL_RFLASH;
} else if (sysload.load > 8) {
sysload.color = COL_RED;
} else if (sysload.load > 4) {
sysload.color = COL_YELLOW;
} else {
sysload.color = COL_GREEN;
}
}
float grepf(const std::string &data, const char *marker) {
unsigned int pos, offset;
char value[16];
float out;
offset = 0;
pos = data.find(marker) + strlen(marker);
// skip through spaces
while (data[pos] == ' ') {
pos++;
}
// read the number
for (; data[pos] != ' '; pos++, offset++) {
value[offset] = data[pos];
}
// terminator
value[offset] = 0;
out = atof(value);
return out;
}
void get_memswap(std::vector<Space> &space) {
std::string s;
std::ifstream in;
char c;
in.open("/proc/meminfo", std::ifstream::in);
while (true) {
c = in.get();
if (in.good()) {
s += c;
} else {
break;
}
}
in.close();
// memory
space.push_back(Space("mem", grepf(s, "MemFree:")*1024 + grepf(s, "Buffers:")*1024 + grepf(s, "Cached:")*1024, grepf(s, "MemTotal:")*1024));
// swap
space.push_back(Space("swp", grepf(s, "SwapFree:")*1024.0, grepf(s, "SwapTotal:")*1024.0));
}
void get_available_space(std::vector<Space> &space) {
// Go through physical RW mounts and query them re: free space
// Ignore /dev and everything below it
// Generate a unique name: mountpoint basename? or maybe shortened to smallest unique part.
std::vector<std::string> mounts;
struct statfs stats;
std::string fullname, name, mountpoint;
int ptr, len;
size_t str_pos;
bool OK;
mounts = read_lines_from_file("/etc/mtab");
for (unsigned int i = 0; i < mounts.size(); i++) {
if (column_contains_substr(mounts[i], 3, "rw") and column_contains_substr(mounts[i], 0, "/dev", -1)) {
// no CDs - loops have rw set
if (column_contains_substr(mounts[i], 2, "iso9660")) {
continue;
}
// no binds
if (column_contains_substr(mounts[i], 3, "bind")) {
continue;
}
mountpoint = get_column(mounts[i], 1);
// handle spaces in mount point names - /proc/mounts uses a 4-byte '\\040' sequence to signify a space
while (true) {
str_pos = mountpoint.find("\\040");
if (str_pos == std::string::npos) {
break;
} else {
mountpoint.replace(str_pos, 4, " ");
}
}
// generate name FIXME basenames are unique on systems with obsessive-compulsive roots only
fullname = basename((char*)mountpoint.c_str());
if (config.long_names or fullname == "/") {
name = fullname;
} else {
ptr = 1;
len = fullname.size();
name = fullname.substr(0, ptr);
OK = false;
while (not OK) {
OK = true;
for (unsigned int j = 0; j < space.size(); j++) {
if (name == space[j].name) {
OK = false;
if (ptr == len) {
// too bad, we give up
name += "!";
OK = true;
} else {
name = fullname.substr(0, ++ptr);
}
break;
}
}
}
}
// figure out free and total space
statfs(mountpoint.c_str(), &stats);
// pass it on
space.push_back(Space(name.c_str(), stats.f_bavail * stats.f_bsize, stats.f_blocks * stats.f_bsize));
}
}
}
std::string get_column(std::string line, unsigned int column) {
std::string output;
unsigned int col_counter;
bool eating_chars; // else eating whitespace
char c;
col_counter = -1; // Dirty? Maybe. It's called 'creative' where I come from.
eating_chars = false; // ignore leading whitespace
// start eating bytes
for (unsigned int i = 0; i < line.size(); i++) {
c = line[i];
if (eating_chars and isspace(c)) { // end of a column
eating_chars = false;
if (col_counter == column) {
break;
}
} else if (not (eating_chars or isspace(c))) { // beginning of a column
eating_chars = true;
col_counter++;
}
if (col_counter == column) {
output += c;
}
}
return output;
}
bool column_contains_substr(std::string line, unsigned int column, std::string substr, int position) {
std::string str;
str = get_column(line, column);
switch (position) {
case -1:
// beginning
if (str.substr(0, substr.size()) == substr) {
return true;
} else {
return false;
}
break;
case 0:
// anywhere
if (str.find(substr) == std::string::npos) {
return false;
} else {
return true;
}
break;
case 1:
// end
if (str.substr(str.size() - substr.size() - 1, substr.size()) == substr) {
return true;
} else {
return false;
}
break;
};
return false;
}
std::vector<std::string> read_lines_from_file(std::string filename) {
char buf[512];
std::ifstream in;
std::vector<std::string> lines;
in.open(filename.c_str(), std::ifstream::in);
while (in.good()) {
in.getline(buf, 512);
if (in.gcount()) {
lines.push_back(buf);
}
}
return lines;
}
bool str_in_list(std::string str, std::vector<std::string> list) {
for (unsigned int i = 0; i < list.size(); i++) {
if (list[i] == str) {
return true;
}
}
return false;
}