Projet

Général

Profil

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

root / plugins / system / 1sec / if1sec-c.c @ d0216f00

Historique | Voir | Annoter | Télécharger (6,32 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;
42
        if ( !(f=open(PROC_STAT, O_RDONLY)) ) {
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 system::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;
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

    
143
        /* open the spoolfile */
144
        int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR);
145

    
146
        /* loop each second */
147
        while (1) {
148
                /* wait until next second */
149
                time_t epoch = wait_until_next_second();
150

    
151
                const int buffer_size = 64 * 1024;
152
                char buffer[buffer_size];
153

    
154
                if (lseek(f, 0, SEEK_SET) < 0) {
155
                        return fail("cannot seek " PROC_STAT);
156
                }
157

    
158
                // whole PROC file can be read in 1 syscall
159
                if (read(f, buffer, buffer_size) <= 0) {
160
                        return fail("cannot read " PROC_STAT);
161
                }
162

    
163
                // ignore the 1rst line
164
                char* line; char *saveptr;
165
                const char* newl = "\n";
166

    
167
                /* lock */
168
                flock(cache_file, LOCK_EX);
169

    
170
                int nif = -2;
171
                for (line = strtok_r(buffer, newl, &saveptr); line; line = strtok_r(NULL, newl, &saveptr)) {
172
                        // Skip the header lines
173
                        if (nif ++ < 0) { continue; }
174

    
175
                        char if_id[64];
176
                        uint_fast64_t r_bytes, r_packets, r_errs, r_drop, r_fifo, r_frame, r_compressed, r_multicast;
177
                        uint_fast64_t t_bytes, t_packets, t_errs, t_drop, t_fifo, t_frame, t_compressed, t_multicast;
178
                        sscanf(line, "%s" 
179
                                " "
180
                                "%llu %llu %llu %llu %llu %llu %llu %llu" 
181
                                " " 
182
                                "%llu %llu %llu %llu %llu %llu %llu %llu"
183
                                , if_id
184
                                , &r_bytes, &r_packets, &r_errs, &r_drop, &r_fifo, &r_frame, &r_compressed, &r_multicast
185
                                , &t_bytes, &t_packets, &t_errs, &t_drop, &t_fifo, &t_frame, &t_compressed, &t_multicast
186
                        );
187

    
188
                        // Remove trailing ':' of if_id
189
                        if_id[strlen(if_id) - 1] = '\0';
190

    
191
                        char out_buffer[1024];
192
                        sprintf(out_buffer, 
193
                                "multigraph if_%s_1sec" "\n"
194
                                "up.value %ld:%llu" "\n"
195
                                "down.value %ld:%llu" "\n"
196
                                , if_id 
197
                                , epoch, r_bytes
198
                                , epoch, t_bytes
199
                        );
200

    
201
                        write(cache_file, out_buffer, strlen(out_buffer));
202
                }
203

    
204
                /* unlock */
205
                flock(cache_file, LOCK_UN);
206
        }
207

    
208
        close(cache_file);
209
        close(f);
210

    
211
        return 0;
212
}
213

    
214
int fetch() {
215
        FILE* cache_file = fopen(cache_filename, "r+");
216

    
217
        /* lock */
218
        flock(fileno(cache_file), LOCK_EX);
219

    
220
        /* cat the cache_file to stdout */
221
        char buffer[1024];
222
        while (fgets(buffer, 1024, cache_file)) {
223
                printf("%s", buffer);
224
        }
225

    
226
        ftruncate(fileno(cache_file), 0);
227
        fclose(cache_file);
228

    
229
        return 0;
230
}
231

    
232
int main(int argc, char **argv) {
233
        /* resolve paths */
234
        char *MUNIN_PLUGSTATE = getenv("MUNIN_PLUGSTATE");
235

    
236
        /* Default is current directory */
237
        if (! MUNIN_PLUGSTATE) MUNIN_PLUGSTATE = ".";
238

    
239
        size_t MUNIN_PLUGSTATE_length = strlen(MUNIN_PLUGSTATE);
240

    
241
        pid_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/" PLUGIN_NAME ".") + strlen("pid") + 1); pid_filename[0] = '\0';
242
        cache_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/" PLUGIN_NAME ".") + strlen("value") + 1); cache_filename[0] = '\0';
243

    
244
        strcat(pid_filename, MUNIN_PLUGSTATE);
245
        strcat(pid_filename, "/" PLUGIN_NAME "." "pid");
246

    
247
        strcat(cache_filename, MUNIN_PLUGSTATE);
248
        strcat(cache_filename, "/" PLUGIN_NAME "." "value");
249

    
250
        if (argc > 1) {
251
                char* first_arg = argv[1];
252
                if (! strcmp(first_arg, "config")) {
253
                        return config();
254
                }
255

    
256
                if (! strcmp(first_arg, "acquire")) {
257
                        return acquire();
258
                }
259
        }
260

    
261
        return fetch();
262
}
263

    
264
/***** DEMO
265
 
266
/proc/net/dev sample
267

268
Inter-|   Receive                                                |  Transmit
269
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
270
    lo:    4364      54    0    0    0     0          0         0     4364      54    0    0    0     0       0          0
271
  eth0: 3459461624 22016512    0   70    0     0          0         0 3670486138 18117144    0    0    0     0       0          0
272

273
*****/