Created
November 17, 2019 16:34
-
-
Save pganti/dac9f7752716753afc9894f6a32ed5d4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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