Projet

Général

Profil

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

root / plugins / system / multicpu1sec / multicpu1sec-c.c @ c5238dbc

Historique | Voir | Annoter | Télécharger (4,65 ko)

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

    
10
#include <time.h>
11

    
12
#include <sys/file.h>
13

    
14
#define PROC_STAT "/proc/stat"
15

    
16
int fail(char* msg) {
17
        perror(msg);
18

    
19
        return 1;
20
}
21

    
22
int config() {
23
        /* Get the number of CPU */
24
        int f;
25
        if ( !(f=open(PROC_STAT, O_RDONLY)) ) {
26
                return fail("cannot open " PROC_STAT);
27
        }
28

    
29
        // Starting with -1, since the first line is the "global cpu line"
30
        int ncpu = -1;
31

    
32
        const int buffer_size = 64 * 1024;
33
        char buffer[buffer_size];
34

    
35
        // whole /proc/stat can be read in 1 syscall
36
        if (read(f, buffer, buffer_size) <= 0) {
37
                return fail("cannot read " PROC_STAT);
38
        }
39

    
40
        // tokenization per-line
41
        char* line;
42
        char* newl = "\n";
43
        for (line = strtok(buffer, newl); line; line = strtok(NULL, newl)) {
44
                if (! strncmp(line, "cpu", 3)) ncpu ++;
45
        }
46

    
47
        close(f);
48

    
49
        printf(
50
                "graph_title multicpu1sec\n"
51
                "graph_category system::1sec\n"
52
                "graph_vlabel average cpu use %%\n"
53
                "graph_scale no\n"
54
                "graph_total All CPUs\n"
55
                "update_rate 1\n"
56
                "graph_data_size custom 1d, 10s for 1w, 1m for 1t, 5m for 1y\n"
57
        );
58

    
59
        int i;
60
        for (i = 0; i < ncpu; i++) {
61
                printf("cpu%d.label CPU %d\n", i, i);
62
                printf("cpu%d.draw %s\n", i, "AREASTACK");
63
                printf("cpu%d.type %s\n", i, "DERIVE");
64
                printf("cpu%d.min 0\n", i);
65
        }
66

    
67

    
68
        return 0;
69
}
70

    
71
char* pid_filename;
72
char* cache_filename;
73

    
74
/* Wait until the next second, and return the EPOCH */
75
time_t wait_until_next_second() {
76
        struct timespec tp;
77
        clock_gettime(CLOCK_REALTIME, &tp);
78

    
79
        time_t current_epoch = tp.tv_sec;
80
        long nsec_to_sleep = 1000*1000*1000 - tp.tv_nsec;
81

    
82

    
83
        /* Only sleep if needed */
84
        if (nsec_to_sleep > 0) {
85
                tp.tv_sec = 0;
86
                tp.tv_nsec = nsec_to_sleep;
87
                nanosleep(&tp, NULL);
88
        }
89

    
90
        return current_epoch + 1;
91
}
92

    
93
int acquire() {
94

    
95
        /* fork ourselves if not asked otherwise */
96
        char* no_fork = getenv("no_fork");
97
        if (! no_fork || strcmp("1", no_fork)) {
98
                if (fork()) return;
99
                // we are the child, complete the daemonization
100

    
101
                /* Close standard IO */
102
                fclose(stdin);
103
                fclose(stdout);
104
                fclose(stderr);
105

    
106
                /* create new session and process group */
107
                setsid();
108
        }
109

    
110
        /* write the pid */
111
        FILE* pid_file = fopen(pid_filename, "w");
112
        fprintf(pid_file, "%d\n", getpid());
113
        fclose(pid_file);
114

    
115
        /* Reading /proc/stat */
116
        int f = open(PROC_STAT, O_RDONLY);
117

    
118
        /* open the spoolfile */
119
        int cache_file = open(cache_filename, O_CREAT | O_APPEND | O_WRONLY);
120

    
121
        /* loop each second */
122
        while (1) {
123
                /* wait until next second */
124
                time_t epoch = wait_until_next_second();
125

    
126

    
127
                const int buffer_size = 64 * 1024;
128
                char buffer[buffer_size];
129

    
130
                if (lseek(f, 0, SEEK_SET) < 0) {
131
                        return fail("cannot seek " PROC_STAT);
132
                }
133

    
134
                // whole /proc/stat can be read in 1 syscall
135
                if (read(f, buffer, buffer_size) <= 0) {
136
                        return fail("cannot read " PROC_STAT);
137
                }
138

    
139
                // ignore the 1rst line
140
                char* line;
141
                const char* newl = "\n";
142
                line = strtok(buffer, newl);
143

    
144
                /* lock */
145
                flock(cache_file, LOCK_EX);
146

    
147
                for (line = strtok(NULL, newl); line; line = strtok(NULL, newl)) {
148
                        // Not on CPU lines anymore
149
                        if (strncmp(line, "cpu", 3)) break;
150

    
151
                        char cpu_id[64];
152
                        long usr, nice, sys, idle, iowait, irq, softirq;
153
                        sscanf(line, "%s %ld %ld %ld %ld %ld %ld %ld", cpu_id, &usr, &nice, &sys, &idle, &iowait, &irq, &softirq);
154

    
155
                        long used = usr + nice + sys + iowait + irq + softirq;
156

    
157
                        char out_buffer[1024];
158
                        sprintf(out_buffer, "%s.value %ld:%ld\n", cpu_id, epoch, used);
159

    
160
                        write(cache_file, out_buffer, strlen(out_buffer));
161
                }
162

    
163
                /* unlock */
164
                flock(cache_file, LOCK_UN);
165
        }
166

    
167
        close(cache_file);
168
        close(f);
169

    
170
        return 0;
171
}
172

    
173
int fetch() {
174
        FILE* cache_file = fopen(cache_filename, "r+");
175

    
176
        /* lock */
177
        flock(fileno(cache_file), LOCK_EX);
178

    
179
        /* cat the cache_file to stdout */
180
        char buffer[1024];
181
        while (fgets(buffer, 1024, cache_file)) {
182
                printf("%s", buffer);
183
        }
184

    
185
        ftruncate(fileno(cache_file), 0);
186
        fclose(cache_file);
187

    
188
        return 0;
189
}
190

    
191
int main(int argc, char **argv) {
192
        /* resolve paths */
193
        char *MUNIN_PLUGSTATE = getenv("MUNIN_PLUGSTATE");
194

    
195
        /* Default is current directory */
196
        if (! MUNIN_PLUGSTATE) MUNIN_PLUGSTATE = ".";
197

    
198
        size_t MUNIN_PLUGSTATE_length = strlen(MUNIN_PLUGSTATE);
199

    
200
        pid_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/multicpu1sec.") + strlen("pid") + 1); pid_filename[0] = '\0';
201
        cache_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/multicpu1sec.") + strlen("value") + 1); cache_filename[0] = '\0';
202

    
203
        strcat(pid_filename, MUNIN_PLUGSTATE);
204
        strcat(pid_filename, "/multicpu1sec.pid");
205

    
206
        strcat(cache_filename, MUNIN_PLUGSTATE);
207
        strcat(cache_filename, "/multicpu1sec.value");
208

    
209
        if (argc > 1) {
210
                char* first_arg = argv[1];
211
                if (! strcmp(first_arg, "config")) {
212
                        return config();
213
                }
214

    
215
                if (! strcmp(first_arg, "acquire")) {
216
                        return acquire();
217
                }
218
        }
219

    
220
        return fetch();
221
}