Skip to content

Instantly share code, notes, and snippets.

@pganti
Created November 17, 2019 16:34
Show Gist options
  • Save pganti/dac9f7752716753afc9894f6a32ed5d4 to your computer and use it in GitHub Desktop.
Save pganti/dac9f7752716753afc9894f6a32ed5d4 to your computer and use it in GitHub Desktop.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MGROUP "224.0.0.251"
#define MPORT 6000
#define JSON_TEMPLATE "{\"ts\":%f,\"host\":\"%s\",\"cpuusage\":%f,\"coreusage\":[%s],\"loadavg\":%s,\"meminfo\":{%s}}"
#define MAX_LENGTH 4096
#define HOSTNAMEMAX 100
#define MAX_CORES 8
#define CPU_STAT_COUNT 10 /* user, nice, system, idle, iowait, irq, softirq, steal, guest, gnice */
//from http://www.mjmwired.net/kernel/Documentation/filesystems/proc.txt
//Various pieces of information about kernel activity are available in the
// /proc/stat file. All of the numbers reported in this file are aggregates
//since the system first booted.
//The very first "cpu" line aggregates the numbers in all of the other "cpuN"
//lines. These numbers identify the amount of time the CPU has spent performing
//different kinds of work. Time units are in USER_HZ (typically hundredths of a
//second). The meanings of the columns are as follows, from left to right:
//- user: normal processes executing in user mode
//- nice: niced processes executing in user mode
//- system: processes executing in kernel mode
//- idle: twiddling thumbs
//- iowait: waiting for I/O to complete
//- irq: servicing interrupts
//- softirq: servicing softirqs
//- steal: involuntary wait
//- guest: running a normal guest
//- guest_nice: running a niced guest
char loadavg_buffer[MAX_LENGTH];
char meminfo_buffer[MAX_LENGTH];
float core_usage[MAX_CORES+1];
/*
* Get the unix epoch time in ms
*/
double get_current_time_with_ms (void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
unsigned long long millisecondsSinceEpoch =
(unsigned long long)(tv.tv_sec) * 1000 +
(unsigned long long)(tv.tv_usec) / 1000;
return millisecondsSinceEpoch;
}
/*
* Get the hostname by reading off the /proc file
*/
char *get_hostname(char *hn) {
FILE *fp;
fp = fopen("/proc/sys/kernel/hostname", "r");
if (fp != NULL) {
fgets(hn, HOSTNAMEMAX, fp);
strtok(hn, " \n");
} else {
hn[strlen(hn)-2] = '\0';
}
fclose(fp);
return hn;
}
/*
* Get load average
*/
char *get_loadavg(void) {
FILE *fp;
char line[MAX_LENGTH];
char dummy[16];
double load[3];
int i;
fp = fopen("/proc/loadavg", "r");
if (fp != NULL) {
fgets(line, MAX_LENGTH, fp);
load[0] = strtod(strtok(line, " \n"), NULL);
for(i=1;i<3;i++)
load[i] = strtod(strtok(NULL, " \n"), NULL);
sprintf(loadavg_buffer, "[%f,%f,%f]", load[0],load[1],load[2]);
}
fclose(fp);
return loadavg_buffer;
}
/*
* Get CPU usage by measuring jiffie counters - returns number of cores sampled
*/
int get_cpuusage(int interval) {
FILE *fp = fopen("/proc/stat", "r");
char line[MAX_LENGTH];
long stat[CPU_STAT_COUNT*(MAX_CORES+1)] = {0};
long delta[CPU_STAT_COUNT*(MAX_CORES+1)] = {0};
int i, j, c, sum, offset = 0;
float usage;
char *word;
if (NULL == fp) {
fprintf(stderr, "Could not open '/proc/stat'\n");
exit(1);
}
/* First, sample current jiffie counters */
if (!feof (fp)) {
for(c=0;c<=MAX_CORES;c++) {
if (NULL == fgets(line, MAX_LENGTH, fp)) {
fclose(fp);
fprintf(stderr, "Could not parse '/proc/stat'\n");
exit(1);
}
word = strtok(line, " \n");
if(strncmp(word, "cpu", 3) == 0) {
offset = c * CPU_STAT_COUNT;
for(i=0;i<CPU_STAT_COUNT;i++)
stat[i+offset] = strtol(strtok(NULL, " \n"), NULL, 10);
}
else break;
}
}
fclose(fp);
/* Now give the kernel time to update them */
sleep(interval);
/* And resample */
fp = fopen("/proc/stat", "r");
if (!fp) { printf("Cannot Open /proc/stat file\n"); return 1;}
if (!feof (fp)) {
for(c=0;c<=MAX_CORES;c++) {
fgets(line, MAX_LENGTH, fp);
word = strtok(line, " \n");
if(word == NULL) continue;
if(strncmp(word, "cpu", 3) == 0) {
offset = c * CPU_STAT_COUNT;
for(i=0;i<CPU_STAT_COUNT;i++)
delta[i+offset] = strtol(strtok(NULL, " \n"), NULL, 10);
}
else break;
}
}
fclose(fp);
/* Now compute the deltas and total time spent in each state */
for(j=0;j<c;j++) {
sum = 0;
offset = j * CPU_STAT_COUNT;
for(i=0;i<7;i++) {
delta[i+offset] -= stat[i+offset];
sum += delta[i+offset];
}
if(sum > 0) { /* function of idle time */
core_usage[j] = 1.0 - (delta[offset+3] / (1.0 * sum));
}
else core_usage[j] = 0.0;
}
return c;
}
/*
* Get ram and swap info
*/
char *get_meminfo(void) {
FILE *fp;
char unit[2];
char label[128];
char buffer[128];
char *colon;
int value;
bzero(&meminfo_buffer, MAX_LENGTH);
fp = fopen("/proc/meminfo", "r");
if (fp != NULL) {
while (!feof(fp)) {
// fscanf is ugly, but serves us well here
if(fscanf(fp, "%s %d %2s", label, &value, unit) != 3)
break;
colon = strchr(label,':');
if(colon != NULL)
*colon = '\0';
if(!strncmp(unit, "kB", 2)) {
sprintf(buffer, "\"%s\":%d,", label, value);
strcat(meminfo_buffer, buffer);
}
}
}
fclose(fp);
value = strlen(meminfo_buffer);
if(value) {
meminfo_buffer[value-1] = '\0'; /* strip comma */
}
return meminfo_buffer;
}
int main(int argc, char *argv[]) {
struct sockaddr_in addr;
int i, msg_len, addr_len, sock, count;
char hn[HOSTNAMEMAX],host[HOSTNAMEMAX];
char msg[MAX_LENGTH], usage[MAX_LENGTH], scratch[MAX_LENGTH];
FILE *fp;
/* first get the host we are working on */
fp = fopen("/proc/sys/kernel/hostname", "r");
if (fp != NULL) {
fgets(host, HOSTNAMEMAX, fp);
strtok(host, " \n");
} else {
host[strlen(host)-1] = '\0';
}
fclose(fp);
if (argc < 2) {
printf("please provide the host prefix else using default\n");
exit(2);
}
/* now prefix with the customer supplied name */
strcpy(hn,argv[1]);
strcat(hn,"_");
strcat(hn,host);
/* set up socket */
char *opt;
opt = "eth0";
sock = socket(AF_INET, SOCK_DGRAM, 0);
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, opt, strlen(opt));
if (sock < 0) {
perror("Could not create socket, exiting");
exit(2);
}
bzero((char *)&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(MGROUP);
addr.sin_port = htons(MPORT);
addr_len = sizeof(addr);
while (1) {
bzero((char *)&usage, MAX_LENGTH);
count = get_cpuusage(2);
for(i=1;i<count;i++) { // skip aggregate count
sprintf(scratch, "%f,", core_usage[i]);
strcat(usage, scratch);
}
i = strlen(usage);
if(i) {
usage[i-1] = '\0'; /* strip comma */
}
msg_len = sprintf(msg, JSON_TEMPLATE, get_current_time_with_ms(),hn,core_usage[0], usage, get_loadavg(), get_meminfo());
// note that we're not sending msg_len + 1 data to avoid sending the \0.
count = sendto(sock, msg, msg_len, 0, (struct sockaddr *) &addr, addr_len);
if (count < 0) {
perror("Error sending message");
//exit(1); we shouldn't die due to transient failures
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment