Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / network / if1sec-c.c @ cc0b0949

Historique | Voir | Annoter | Télécharger (6,52 ko)

1
/*
2
 * if1sec C plugin
3
 */
4
#include <stdlib.h>
5
#include <stdio.h>
6
#include <string.h>
7
#include <unistd.h>
8
#include <stdint.h>
9
#include <inttypes.h>
10
#include <fcntl.h>
11

    
12
#include <time.h>
13

    
14
#include <sys/file.h>
15

    
16
#define PROC_STAT "/proc/net/dev"
17
#define PLUGIN_NAME "if1sec-c"
18

    
19
int fail(char* msg) {
20
        perror(msg);
21

    
22
        return 1;
23
}
24

    
25
/* Returns the ifname from a /proc/net/dev line
26
 * It will return an inside pointer to line, and modifiy the end with a \0
27
 */
28
char* get_ifname_from_procstatline(char* line) {
29
        char *ifname;
30
        for (ifname = line; (*ifname) == ' '; ifname ++);
31

    
32
        char *ifname_end;
33
        for (ifname_end = ifname; (*ifname_end) != ':'; ifname_end ++);
34
        (*ifname_end) = '\0';
35

    
36
        return ifname;
37
}
38

    
39
int config() {
40
        /* Get the number of if */
41
        int f = open(PROC_STAT, O_RDONLY);
42
        if ( f == -1 ) {
43
                return fail("cannot open " PROC_STAT);
44
        }
45

    
46
        // Starting with -2, since the 2 lines on top are header lines
47
        int nif = -2;
48

    
49
        const int buffer_size = 64 * 1024;
50
        char buffer[buffer_size];
51

    
52
        // whole /proc/stat can be read in 1 syscall
53
        if (read(f, buffer, buffer_size) <= 0) {
54
                return fail("cannot read " PROC_STAT);
55
        }
56

    
57
        // tokenization per-line
58
        char* line; char *saveptr;
59
        char* newl = "\n";
60
        for (line = strtok_r(buffer, newl, &saveptr); line; line = strtok_r(NULL, newl, &saveptr)) {
61
                // Skip the header lines
62
                if (nif ++ < 0) { continue; }
63

    
64
                char* if_name = get_ifname_from_procstatline(line);
65
                printf(
66
                        "multigraph if_%s_1sec" "\n"
67
                        "graph_order down up" "\n"
68
                        "graph_title %s traffic" "\n"
69
                        "graph_category 1sec" "\n"
70
                        "graph_vlabel bits in (-) / out (+) per ${graph_period}" "\n"
71
                        "graph_data_size custom 1d, 10s for 1w, 1m for 1t, 5m for 1y" "\n"
72
                        , if_name, if_name
73
                );
74

    
75
                printf(
76
                        "down.label -" "\n"
77
                        "down.type DERIVE" "\n"
78
                        "down.graph no" "\n"
79
                        "down.cdef down,8,*" "\n"
80
                        "down.min 0" "\n"
81
                                                
82
                        "up.label bps" "\n"
83
                        "up.type DERIVE" "\n"
84
                        "up.negative down" "\n"
85
                        "up.cdef down,8,*" "\n"
86
                        "up.min 0" "\n"
87
                );
88
        }
89

    
90
        close(f);
91

    
92

    
93
        return 0;
94
}
95

    
96
char* pid_filename;
97
char* cache_filename;
98

    
99
/* Wait until the next second, and return the EPOCH */
100
time_t wait_until_next_second() {
101
        struct timespec tp;
102
        clock_gettime(CLOCK_REALTIME, &tp);
103

    
104
        time_t current_epoch = tp.tv_sec;
105
        long nsec_to_sleep = 1000*1000*1000 - tp.tv_nsec;
106

    
107

    
108
        /* Only sleep if needed */
109
        if (nsec_to_sleep > 0) {
110
                tp.tv_sec = 0;
111
                tp.tv_nsec = nsec_to_sleep;
112
                nanosleep(&tp, NULL);
113
        }
114

    
115
        return current_epoch + 1;
116
}
117

    
118
int acquire() {
119

    
120
        /* fork ourselves if not asked otherwise */
121
        char* no_fork = getenv("no_fork");
122
        if (! no_fork || strcmp("1", no_fork)) {
123
                if (fork()) return 0;
124
                // we are the child, complete the daemonization
125

    
126
                /* Close standard IO */
127
                fclose(stdin);
128
                fclose(stdout);
129
                fclose(stderr);
130

    
131
                /* create new session and process group */
132
                setsid();
133
        }
134

    
135
        /* write the pid */
136
        FILE* pid_file = fopen(pid_filename, "w");
137
        fprintf(pid_file, "%d\n", getpid());
138
        fclose(pid_file);
139

    
140
        /* Reading /proc/stat */
141
        int f = open(PROC_STAT, O_RDONLY);
142
        if ( f == -1 ) {
143
                return fail("cannot open " PROC_STAT);
144
        }
145

    
146
        /* open the spoolfile */
147
        int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR);
148
        if ( cache_file == -1 ) {
149
                return fail("cannot open the spoolfile");
150
        }
151

    
152
        /* loop each second */
153
        while (1) {
154
                /* wait until next second */
155
                time_t epoch = wait_until_next_second();
156

    
157
                const int buffer_size = 64 * 1024;
158
                char buffer[buffer_size];
159

    
160
                if (lseek(f, 0, SEEK_SET) < 0) {
161
                        return fail("cannot seek " PROC_STAT);
162
                }
163

    
164
                // whole PROC file can be read in 1 syscall
165
                if (read(f, buffer, buffer_size) <= 0) {
166
                        return fail("cannot read " PROC_STAT);
167
                }
168

    
169
                // ignore the 1rst line
170
                char* line; char *saveptr;
171
                const char* newl = "\n";
172

    
173
                /* lock */
174
                flock(cache_file, LOCK_EX);
175

    
176
                int nif = -2;
177
                for (line = strtok_r(buffer, newl, &saveptr); line; line = strtok_r(NULL, newl, &saveptr)) {
178
                        // Skip the header lines
179
                        if (nif ++ < 0) { continue; }
180

    
181
                        char if_id[64];
182
                        uint_fast64_t r_bytes, r_packets, r_errs, r_drop, r_fifo, r_frame, r_compressed, r_multicast;
183
                        uint_fast64_t t_bytes, t_packets, t_errs, t_drop, t_fifo, t_frame, t_compressed, t_multicast;
184
                        sscanf(line, "%s" 
185
                                " "
186
                                "%llu %llu %llu %llu %llu %llu %llu %llu" 
187
                                " " 
188
                                "%llu %llu %llu %llu %llu %llu %llu %llu"
189
                                , if_id
190
                                , &r_bytes, &r_packets, &r_errs, &r_drop, &r_fifo, &r_frame, &r_compressed, &r_multicast
191
                                , &t_bytes, &t_packets, &t_errs, &t_drop, &t_fifo, &t_frame, &t_compressed, &t_multicast
192
                        );
193

    
194
                        // Remove trailing ':' of if_id
195
                        if_id[strlen(if_id) - 1] = '\0';
196

    
197
                        char out_buffer[1024];
198
                        sprintf(out_buffer, 
199
                                "multigraph if_%s_1sec" "\n"
200
                                "up.value %ld:%llu" "\n"
201
                                "down.value %ld:%llu" "\n"
202
                                , if_id 
203
                                , epoch, r_bytes
204
                                , epoch, t_bytes
205
                        );
206

    
207
                        write(cache_file, out_buffer, strlen(out_buffer));
208
                }
209

    
210
                /* unlock */
211
                flock(cache_file, LOCK_UN);
212
        }
213

    
214
        close(cache_file);
215
        close(f);
216

    
217
        return 0;
218
}
219

    
220
int fetch() {
221
        FILE* cache_file = fopen(cache_filename, "r+");
222
        if ( !cache_file ) {
223
                return fail("cannot open cache file");
224
        }
225

    
226
        /* lock */
227
        flock(fileno(cache_file), LOCK_EX);
228

    
229
        /* cat the cache_file to stdout */
230
        char buffer[1024];
231
        while (fgets(buffer, 1024, cache_file)) {
232
                printf("%s", buffer);
233
        }
234

    
235
        ftruncate(fileno(cache_file), 0);
236
        fclose(cache_file);
237

    
238
        return 0;
239
}
240

    
241
int main(int argc, char **argv) {
242
        /* resolve paths */
243
        char *MUNIN_PLUGSTATE = getenv("MUNIN_PLUGSTATE");
244

    
245
        /* Default is current directory */
246
        if (! MUNIN_PLUGSTATE) MUNIN_PLUGSTATE = ".";
247

    
248
        size_t MUNIN_PLUGSTATE_length = strlen(MUNIN_PLUGSTATE);
249

    
250
        pid_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/" PLUGIN_NAME ".") + strlen("pid") + 1); pid_filename[0] = '\0';
251
        cache_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/" PLUGIN_NAME ".") + strlen("value") + 1); cache_filename[0] = '\0';
252

    
253
        strcat(pid_filename, MUNIN_PLUGSTATE);
254
        strcat(pid_filename, "/" PLUGIN_NAME "." "pid");
255

    
256
        strcat(cache_filename, MUNIN_PLUGSTATE);
257
        strcat(cache_filename, "/" PLUGIN_NAME "." "value");
258

    
259
        if (argc > 1) {
260
                char* first_arg = argv[1];
261
                if (! strcmp(first_arg, "config")) {
262
                        return config();
263
                }
264

    
265
                if (! strcmp(first_arg, "acquire")) {
266
                        return acquire();
267
                }
268
        }
269

    
270
        return fetch();
271
}
272

    
273
/***** DEMO
274
 
275
/proc/net/dev sample
276

277
Inter-|   Receive                                                |  Transmit
278
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
279
    lo:    4364      54    0    0    0     0          0         0     4364      54    0    0    0     0       0          0
280
  eth0: 3459461624 22016512    0   70    0     0          0         0 3670486138 18117144    0    0    0     0       0          0
281

282
*****/