first commit
Costa Tsaousis committed
Jun 17, 2013 at 21:45 UTC
608d937b4a31e8c2dc984aadf1168710b91fbf78
1 file changed
+244
netdata.c
new
+244
@@ -0,0 +1,244 @@
1
+#include <stdio.h>
2
+#include <stdlib.h>
3
+#include <string.h>
4
+#include <time.h>
5
+#include <unistd.h>
6
+#include <sys/types.h>
7
+
8
+// internal defaults
9
+#define UPDATE_EVERY 1
10
+#define HISTORY 3600
11
+#define SAVE_PATH "/tmp"
12
+
13
+// configuration
14
+#define MAX_IFACE_NAME 1024
15
+char save_path[FILENAME_MAX+1] = SAVE_PATH;
16
+int update_every = UPDATE_EVERY;
17
+int save_history = 60;
18
+
19
+struct iface_history {
20
+ time_t time;
21
+ unsigned long long rbytes;
22
+ unsigned long long tbytes;
23
+};
24
+
25
+struct iface_stats {
26
+ char name[MAX_IFACE_NAME + 1];
27
+
28
+ int last_history_id;
29
+ struct iface_history history[HISTORY];
30
+
31
+ struct iface_stats *next;
32
+} *interfaces = NULL;
33
+
34
+void update_iface_history(char *name, unsigned long long rbytes, unsigned long long tbytes) {
35
+ struct iface_stats *iface = NULL;
36
+
37
+ for(iface = interfaces; iface != NULL; iface = iface->next)
38
+ if(strcmp(iface->name, name) == 0) break;
39
+
40
+ if(!iface) {
41
+ int i;
42
+
43
+ // printf("Creating new interface for %s\n", name);
44
+
45
+ iface = malloc(sizeof(struct iface_stats));
46
+ if(!iface) return;
47
+
48
+ iface->last_history_id = HISTORY + 1;
49
+
50
+ // link it to beginning
51
+ iface->next = interfaces;
52
+ interfaces = iface;
53
+
54
+ for(i = 0; i < HISTORY ;i++) {
55
+ iface->history[i].time = time(NULL) - HISTORY + (i * update_every);
56
+ iface->history[i].rbytes = rbytes;
57
+ iface->history[i].tbytes = tbytes;
58
+ }
59
+ }
60
+
61
+ iface->last_history_id++;
62
+ if(iface->last_history_id >= HISTORY) iface->last_history_id = 0;
63
+
64
+ // printf("Updating values for interface %s at position %d, rbytes = %llu, tbytes = %llu\n", iface->name, iface->last_history_id, rbytes, tbytes);
65
+
66
+ strcpy(iface->name, name);
67
+ iface->history[iface->last_history_id].time = time(NULL);
68
+ iface->history[iface->last_history_id].rbytes = rbytes;
69
+ iface->history[iface->last_history_id].tbytes = tbytes;
70
+}
71
+
72
+void save_stats() {
73
+ struct iface_stats *iface = NULL;
74
+
75
+ for(iface = interfaces; iface != NULL; iface = iface->next) {
76
+ char tmp[FILENAME_MAX+1];
77
+ char filename[FILENAME_MAX+1];
78
+ FILE *fp;
79
+ int i, ld;
80
+
81
+ sprintf(tmp, "%s/%s.json.tmp.%d", save_path, iface->name, getpid());
82
+ sprintf(filename, "%s/%s.json", save_path, iface->name);
83
+ fp = fopen(tmp, "w");
84
+ if(!fp) {
85
+ perror(filename);
86
+ return;
87
+ }
88
+
89
+
90
+ fprintf(fp, "{\n \"cols\":\n [\n");
91
+ fprintf(fp, " {\"id\":\"\",\"label\":\"time\",\"pattern\":\"\",\"type\":\"timeofday\"},\n");
92
+ fprintf(fp, " {\"id\":\"\",\"label\":\"received\",\"pattern\":\"\",\"type\":\"number\"},\n");
93
+ fprintf(fp, " {\"id\":\"\",\"label\":\"sent\",\"pattern\":\"\",\"type\":\"number\"}\n");
94
+ fprintf(fp, " ],\n \"rows\":\n [\n");
95
+
96
+ ld = iface->last_history_id;
97
+ for(i = 1; i < save_history ; i++) {
98
+ int dt, s, d;
99
+ long long rb, tb;
100
+ struct tm *tm;
101
+ char dtm[1025];
102
+
103
+ d = iface->last_history_id - i;
104
+ if(d < 0) d += HISTORY;
105
+
106
+ dt = iface->history[ld].time - iface->history[d].time;
107
+ if(dt == 0) dt = 1;
108
+ rb = (iface->history[ld].rbytes - iface->history[d].rbytes) * 8 / dt / 1024;
109
+ tb = (iface->history[ld].tbytes - iface->history[d].tbytes) * 8 / dt / 1024;
110
+
111
+ s = time(NULL) - iface->history[ld].time;
112
+
113
+ tm = localtime(&iface->history[d].time);
114
+ if(!tm) { perror("localtime"); continue; }
115
+ // strftime(dtm, 1024, "%T", tm);
116
+ // strftime(dtm, 1024, "%H:%M %B %d, %Y %z", tm);
117
+ // strftime(dtm, 1024, "new Date(\"%B %d, %Y %T\")", tm);
118
+ strftime(dtm, 1024, "[%H, %M, %S, 0]", tm);
119
+
120
+ // fprintf(fp, " {\"c\":[{\"v\":\"%ds\",\"f\":null},{\"v\":%llu,\"f\":\"%llu\"},{\"v\":%llu,\"f\":\"%llu\"}]}", -s, rb, rb, tb, tb);
121
+ // fprintf(fp, " {\"c\":[{\"v\":%s,\"f\":null},{\"v\":%lld,\"f\":null},{\"v\":%lld,\"f\":null}]}", dtm, rb, tb);
122
+ fprintf(fp, " {\"c\":[{\"v\":%s},{\"v\":%lld},{\"v\":%lld}]}", dtm, rb, tb);
123
+ if(i == save_history - 1) fprintf(fp, "\n");
124
+ else fprintf(fp, ",\n");
125
+
126
+ ld = d;
127
+ }
128
+ fprintf(fp, " ]\n}\n");
129
+ fclose(fp);
130
+ unlink(filename);
131
+ link(tmp, filename);
132
+ unlink(tmp);
133
+ }
134
+}
135
+
136
+int main(int argc, char **argv) {
137
+ char iface[MAX_IFACE_NAME + 1] = "";
138
+ unsigned long long rbytes, rpackets, rerrors, rdrops, rfifo, rframe, rcompressed, rmulticast;
139
+ unsigned long long tbytes, tpackets, terrors, tdrops, tfifo, tcollisions, tcarrier, tcompressed;
140
+ int i;
141
+ int daemon = 0;
142
+
143
+ // parse the arguments
144
+ for(i = 1; i < argc ; i++) {
145
+ if(strcmp(argv[i], "-l") == 0 && (i+1) < argc) {
146
+ save_history = atoi(argv[i+1]);
147
+ if(save_history <= 0 || save_history > HISTORY) {
148
+ fprintf(stderr, "Invalid save lines %d given. Defaulting to %d.\n", save_history, HISTORY);
149
+ save_history = HISTORY;
150
+ }
151
+ else {
152
+ fprintf(stderr, "save lines set to %d.\n", save_history);
153
+ }
154
+ i++;
155
+ }
156
+ else if(strcmp(argv[i], "-u") == 0 && (i+1) < argc) {
157
+ update_every = atoi(argv[i+1]);
158
+ if(update_every <= 0 || update_every > 600) {
159
+ fprintf(stderr, "Invalid update timer %d given. Defaulting to %d.\n", update_every, UPDATE_EVERY);
160
+ update_every = UPDATE_EVERY;
161
+ }
162
+ else {
163
+ fprintf(stderr, "update timer set to %d.\n", update_every);
164
+ }
165
+ i++;
166
+ }
167
+ else if(strcmp(argv[i], "-o") == 0 && (i+1) < argc) {
168
+ strncpy(save_path, argv[i+1], FILENAME_MAX);
169
+ save_path[FILENAME_MAX]='\0';
170
+ fprintf(stderr, "Saving files to '%s'.\n", save_path);
171
+ i++;
172
+ }
173
+ else if(strcmp(argv[i], "-d") == 0) {
174
+ daemon = 1;
175
+ fprintf(stderr, "Enabled daemon mode.\n");
176
+ }
177
+ else {
178
+ fprintf(stderr, "Cannot understand option '%s'.\n", argv[i]);
179
+ fprintf(stderr, "\nUSAGE: %s [-d] [-l LINES_TO_SAVE] [-u UPDATE_TIMER] [-o PATH_TO_SAVE_FILES].\n\n", argv[0]);
180
+ fprintf(stderr, " -d enabled daemon mode.\n");
181
+ fprintf(stderr, " -l LINES_TO_SAVE can be from 0 to %d lines in JSON data. Default: %d.\n", HISTORY, save_history);
182
+ fprintf(stderr, " -u UPDATE_TIMER can be from 1 to %d seconds. Default: %d.\n", 600, update_every);
183
+ fprintf(stderr, " -o PATH_TO_SAVE_FILES is a directory to place the JSON files. Default: '%s'.\n", save_path);
184
+ exit(1);
185
+ }
186
+ }
187
+
188
+ if(daemon) {
189
+ i = fork();
190
+ if(i == -1) {
191
+ perror("cannot fork");
192
+ exit(1);
193
+ }
194
+ if(i != 0) exit(0); // the parent
195
+ close(0);
196
+ close(1);
197
+ close(2);
198
+ }
199
+
200
+ for(;1;) {
201
+ FILE *fp = fopen("/proc/net/dev", "r");
202
+ int r;
203
+
204
+ if(!fp) {
205
+ perror("/proc/net/dev");
206
+ sleep(1);
207
+ continue;
208
+ }
209
+
210
+ // skip the first two lines
211
+ fgets(iface, MAX_IFACE_NAME, fp);
212
+ fgets(iface, MAX_IFACE_NAME, fp);
213
+
214
+ // read the rest of the lines
215
+ for(;1;) {
216
+ r = fscanf(fp, "%s\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\t%llu\n",
217
+ iface,
218
+ &rbytes, &rpackets, &rerrors, &rdrops, &rfifo, &rframe, &rcompressed, &rmulticast,
219
+ &tbytes, &tpackets, &terrors, &tdrops, &tfifo, &tcollisions, &tcarrier, &tcompressed);
220
+ if(r == EOF) break;
221
+ if(r != 17) printf("Cannot read line\n");
222
+ else {
223
+ // printf("Read %d items\n", r);
224
+
225
+ // remove the ':' from the end of the name
226
+ int l = strlen(iface);
227
+ if(l > 0) iface[l-1] = '\0';
228
+
229
+ // update our data
230
+ update_iface_history(iface, rbytes, tbytes);
231
+ }
232
+ }
233
+
234
+ // done reading, close it
235
+ fclose(fp);
236
+
237
+ // save statistics
238
+ save_stats();
239
+
240
+ // printf("Received bytes = %llu, Transmit bytes = %llu\n", rbytes, tbytes);
241
+ sleep(update_every);
242
+ }
243
+}
244
+