Projet

Général

Profil

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

root / plugins / network / if1sec-c.c @ 17f78427

Historique | Voir | Annoter | Télécharger (6,57 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
                pid_t child_pid = fork();
124
                if (child_pid) {
125
                        printf("# acquire() launched as PID %d\n", child_pid);
126
                        return 0;
127
                }
128

    
129
                // we are the child, complete the daemonization
130

    
131
                /* Close standard IO */
132
                fclose(stdin);
133
                fclose(stdout);
134
                fclose(stderr);
135

    
136
                /* create new session and process group */
137
                setsid();
138
        }
139

    
140
        /* write the pid */
141
        FILE* pid_file = fopen(pid_filename, "w");
142
        fprintf(pid_file, "%d\n", getpid());
143
        fclose(pid_file);
144

    
145
        /* Reading /proc/stat */
146
        int f = open(PROC_STAT, O_RDONLY);
147
        if ( f == -1 ) {
148
                return fail("cannot open " PROC_STAT);
149
        }
150

    
151
        /* open the spoolfile */
152
        int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR);
153
        if ( cache_file == -1 ) {
154
                return fail("# cannot open cache_file");
155
        }
156

    
157
        /* loop each second */
158
        while (1) {
159
                /* wait until next second */
160
                time_t epoch = wait_until_next_second();
161

    
162
                const int buffer_size = 64 * 1024;
163
                char buffer[buffer_size];
164

    
165
                if (lseek(f, 0, SEEK_SET) < 0) {
166
                        return fail("cannot seek " PROC_STAT);
167
                }
168

    
169
                // whole PROC file can be read in 1 syscall
170
                if (read(f, buffer, buffer_size) <= 0) {
171
                        return fail("cannot read " PROC_STAT);
172
                }
173

    
174
                // ignore the 1rst line
175
                char* line; char *saveptr;
176
                const char* newl = "\n";
177

    
178
                /* lock */
179
                flock(cache_file, LOCK_EX);
180

    
181
                int nif = -2;
182
                for (line = strtok_r(buffer, newl, &saveptr); line; line = strtok_r(NULL, newl, &saveptr)) {
183
                        // Skip the header lines
184
                        if (nif ++ < 0) { continue; }
185

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

    
199
                        // Remove trailing ':' of if_id
200
                        if_id[strlen(if_id) - 1] = '\0';
201

    
202
                        char out_buffer[1024];
203
                        sprintf(out_buffer,
204
                                "multigraph if_%s_1sec" "\n"
205
                                "up.value %ld:%llu" "\n"
206
                                "down.value %ld:%llu" "\n"
207
                                , if_id
208
                                , epoch, r_bytes
209
                                , epoch, t_bytes
210
                        );
211

    
212
                        write(cache_file, out_buffer, strlen(out_buffer));
213
                }
214

    
215
                /* unlock */
216
                flock(cache_file, LOCK_UN);
217
        }
218

    
219
        close(cache_file);
220
        close(f);
221

    
222
        return 0;
223
}
224

    
225
int fetch() {
226
        FILE* cache_file = fopen(cache_filename, "r+");
227
        if ( !cache_file ) {
228
                return acquire();
229
        }
230

    
231
        /* lock */
232
        flock(fileno(cache_file), LOCK_EX);
233

    
234
        /* cat the cache_file to stdout */
235
        char buffer[1024];
236
        while (fgets(buffer, 1024, cache_file)) {
237
                printf("%s", buffer);
238
        }
239

    
240
        ftruncate(fileno(cache_file), 0);
241
        fclose(cache_file);
242

    
243
        return 0;
244
}
245

    
246
int main(int argc, char **argv) {
247
        /* resolve paths */
248
        char *MUNIN_PLUGSTATE = getenv("MUNIN_PLUGSTATE");
249

    
250
        /* Default is current directory */
251
        if (! MUNIN_PLUGSTATE) MUNIN_PLUGSTATE = ".";
252

    
253
        size_t MUNIN_PLUGSTATE_length = strlen(MUNIN_PLUGSTATE);
254

    
255
        pid_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/" PLUGIN_NAME ".") + strlen("pid") + 1); pid_filename[0] = '\0';
256
        cache_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/" PLUGIN_NAME ".") + strlen("value") + 1); cache_filename[0] = '\0';
257

    
258
        strcat(pid_filename, MUNIN_PLUGSTATE);
259
        strcat(pid_filename, "/" PLUGIN_NAME "." "pid");
260

    
261
        strcat(cache_filename, MUNIN_PLUGSTATE);
262
        strcat(cache_filename, "/" PLUGIN_NAME "." "value");
263

    
264
        if (argc > 1) {
265
                char* first_arg = argv[1];
266
                if (! strcmp(first_arg, "config")) {
267
                        return config();
268
                }
269

    
270
                if (! strcmp(first_arg, "acquire")) {
271
                        return acquire();
272
                }
273
        }
274

    
275
        return fetch();
276
}
277

    
278
/***** DEMO
279

280
/proc/net/dev sample
281

282
Inter-|   Receive                                                |  Transmit
283
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
284
    lo:    4364      54    0    0    0     0          0         0     4364      54    0    0    0     0       0          0
285
  eth0: 3459461624 22016512    0   70    0     0          0         0 3670486138 18117144    0    0    0     0       0          0
286

287
*****/