1 /*
2 ** rrdproc.c Copyright 1999 Damien Miller <djm@mindrot.org>
3 **
4 **
5 ** This program is a very lightweight collecter for rrdtool. It reads
6 ** and parses /proc/net/dev and sends a rrdtool remote control update
7 ** command to stdout.
8 **
9 ** rrdproc will sleep a user-defined amount of time between reads from
10 ** /proc/net/dev. This time should match the sample rate you created your
11 ** RRD files with.
12 **
13 ** Example:
14 **
15 ** rrdproc --interface=ppp0 \
16 ** --wait 30 \
17 ** --filename=/home/djm/traffic-ppp0.rrd | rrdtool -
18 **
19 ** This will update the RRD file /home/djm/traffic-eth0.rrd with bytes
20 ** received and sent on eth0 every 30 seconds.
21 **
22 ** rrdproc --interface=eth0 \
23 ** --wait 300 \
24 ** --frame \
25 ** --filename=/home/djm/traffic-eth0.rrd | rrdtool -
26 **
27 ** Will update /home/djm/traffic-eth0.rrd with counts of framing errors and
28 ** collisions every 5 minutes
29 **
30 ** rrdproc is licensed under the GNU GPL version 2. Please refer to
31 ** http://www.fsf.org/copyleft/gpl.html for details.
32 **
33 */
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <syslog.h>
39 #include <getopt.h>
40 #include <unistd.h>
41 #include <time.h>
43 #define PROC_DEV "/proc/net/dev"
45 #define DEFAULT_SLEEP_TIME 3
46 #define DEFAULT_INTERFACE "eth0"
47 #define DEFAULT_FILENAME "traffic"
49 #define COLUMN_BYTES 0
50 #define COLUMN_PACKETS 1
51 #define COLUMN_ERRORS 2
52 #define COLUMN_DROPPED 3
53 #define COLUMN_FIFO 4
54 #define COLUMN_FRAME 5
56 void get_stats(FILE *devstats, const char *interface, int start_column);
57 const char *skip_columns(const char *p, int num_columns);
58 void help(void);
59 void version(void);
61 static struct option long_options[] =
62 {
63 { "wait", 1, NULL, 'w'},
64 { "interface", 1, NULL, 'i'},
65 { "filename", 1, NULL, 'f'},
66 { "help", 0, NULL, 'h'},
67 { "version", 0, NULL, 'v'},
68 { "bytes", 0, NULL, 'B'},
69 { "packets", 0, NULL, 'P'},
70 { "errors", 0, NULL, 'E'},
71 { "dropped", 0, NULL, 'D'},
72 { "fifo", 0, NULL, 'F'},
73 { "frame", 0, NULL, 'R'},
74 };
76 int main(int argc, char **argv)
77 {
78 int sleep_time = DEFAULT_SLEEP_TIME;
79 char *interface = DEFAULT_INTERFACE;
80 char *filename = DEFAULT_FILENAME;
81 int start_column = COLUMN_BYTES;
82 int c;
83 FILE *devstats;
85 extern char *optarg;
87 openlog("if-update", LOG_PERROR|LOG_PID, LOG_DAEMON);
89 while(1)
90 {
91 c = getopt_long(argc, argv, "w:i:f:hvBPEDFRCM", long_options, NULL);
92 if (c == -1)
93 break;
95 switch(c)
96 {
97 case 'w':
98 sleep_time = atoi(optarg);
99 break;
100 case 'i':
101 interface = strdup(optarg);
102 break;
103 case 'f':
104 filename = strdup(optarg);
105 break;
106 case 'h':
107 help();
108 exit(0);
109 case 'v':
110 version();
111 exit(0);
112 case 'B':
113 start_column = COLUMN_BYTES;
114 break;
115 case 'P':
116 start_column = COLUMN_PACKETS;
117 break;
118 case 'E':
119 start_column = COLUMN_ERRORS;
120 break;
121 case 'D':
122 start_column = COLUMN_DROPPED;
123 break;
124 case 'F':
125 start_column = COLUMN_FIFO;
126 break;
127 case 'R':
128 start_column = COLUMN_FRAME;
129 break;
130 default:
131 fprintf(stderr, "Invalid commandline options.\n");
132 help();
133 exit(1);
134 }
135 }
137 setlinebuf(stdout);
139 while(1)
140 {
141 devstats = fopen(PROC_DEV, "r");
142 if (devstats == NULL)
143 {
144 syslog(LOG_ERR, "Couldn't open proc file \"%s\" for reading: %m", PROC_DEV);
145 exit(1);
146 }
147 printf("update %s N:", filename);
148 get_stats(devstats, interface, start_column);
149 sleep(sleep_time);
150 fclose(devstats);
151 }
152 exit(0);
153 }
155 void get_stats(FILE *devstats, const char *interface, int start_column)
156 {
157 char buffer[2048];
158 const char *p;
159 int if_len;
160 unsigned long long in;
161 unsigned long long out;
163 if_len = strlen(interface);
165 while(fgets(buffer, sizeof(buffer), devstats) != NULL)
166 {
167 p = buffer;
169 /* skip space at start of line */
170 while(*p && (*p == ' '))
171 p++;
173 if (strncmp(p, interface, if_len) == 0)
174 {
175 /* Skip to the statistic we wnt to report */
176 p = skip_columns(p + if_len + 1, start_column);
178 in = strtoull(p, NULL, 10);
180 /* Skip from received column to transmit column */
181 p = skip_columns(p, 8);
183 out = strtoull(p, NULL, 10);
185 printf("%Lu:%Lu\n", in, out);
187 return;
188 }
189 }
191 /* Non-fatal error if interface not found */
192 syslog(LOG_WARNING, "Couldn't find statistics for interface \"%s\"", interface);
193 printf("U:U\n");
194 return;
195 }
197 void help(void)
198 {
199 fprintf(stderr, "\
200 rrdproc - Update rrd file using statistics in /proc\n\
201 \n\
202 rrdproc will periodically read /proc/net/dev and update an RRD database\n\
203 with the numbers that it finds there.\n\
204 \n\
205 Options:\n\
206 --wait, -w [time] Time to wait between statistics updates\n\
207 --interface, -i [name] Name of network interface to report on\n\
208 --filename, -f [filename] Path of RRD file to update\n\
209 --help, -h Display this help\n\
210 --version, -v Display version information\n\
211 --bytes, -B Report bytes in / out\n\
212 --packets, -P Report packets in / out\n\
213 --errors, -E Report errors in / out\n\
214 --dropped, -D Report dropped packets in / out\n\
215 --fifo, -F Report fifo in / out\n\
216 --frame, -R Report framing errors / collisions\n\
217 ");
218 }
220 void version(void)
221 {
222 fprintf(stderr, "rrdproc v0.0\n");
223 }
225 const char *skip_columns(const char *p, int num_columns)
226 {
227 int c;
229 for(c = 0; c < num_columns; c++)
230 {
231 /* Skip numbers */
232 while(*p && (*p != ' '))
233 p++;
235 /* Skip space */
236 while(*p && (*p == ' '))
237 p++;
239 if (!*p)
240 {
241 /* Line finished early */
242 printf("U:U\n");
243 syslog(LOG_WARNING, "Couldn't parse interface statistics");
244 return;
245 }
246 }
248 return(p);
249 }