Projet

Général

Profil

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

root / plugins / cpu / multicpu1sec-c.c @ a720b6c9

Historique | Voir | Annoter | Télécharger (4,84 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 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
                pid_t child_pid = fork();
99
                if (child_pid) {
100
                        printf("# acquire() launched as PID %d\n", child_pid);
101
                        return 0;
102
                }
103
                // we are the child, complete the daemonization
104

    
105
                /* Close standard IO */
106
                fclose(stdin);
107
                fclose(stdout);
108
                fclose(stderr);
109

    
110
                /* create new session and process group */
111
                setsid();
112
        }
113

    
114
        /* write the pid */
115
        FILE* pid_file = fopen(pid_filename, "w");
116
        fprintf(pid_file, "%d\n", getpid());
117
        fclose(pid_file);
118

    
119
        /* Reading /proc/stat */
120
        int f = open(PROC_STAT, O_RDONLY);
121

    
122
        /* open the spoolfile */
123
        FILE* cache_file = fopen(cache_filename, "a");
124
        if (!cache_file) {
125
                return fail("cannot create cache_file");
126
        }
127

    
128
        int cache_file_fd = fileno(cache_file);
129

    
130
        /* loop each second */
131
        while (1) {
132
                /* wait until next second */
133
                time_t epoch = wait_until_next_second();
134

    
135

    
136
                const int buffer_size = 64 * 1024;
137
                char buffer[buffer_size];
138

    
139
                if (lseek(f, 0, SEEK_SET) < 0) {
140
                        return fail("cannot seek " PROC_STAT);
141
                }
142

    
143
                // whole /proc/stat can be read in 1 syscall
144
                if (read(f, buffer, buffer_size) <= 0) {
145
                        return fail("cannot read " PROC_STAT);
146
                }
147

    
148
                // ignore the 1rst line
149
                char* line;
150
                const char* newl = "\n";
151
                line = strtok(buffer, newl);
152

    
153
                /* lock */
154
                flock(cache_file_fd, LOCK_EX);
155

    
156
                for (line = strtok(NULL, newl); line; line = strtok(NULL, newl)) {
157
                        // Not on CPU lines anymore
158
                        if (strncmp(line, "cpu", 3)) break;
159

    
160
                        char cpu_id[64];
161
                        long usr, nice, sys, idle, iowait, irq, softirq;
162
                        sscanf(line, "%s %ld %ld %ld %ld %ld %ld %ld", cpu_id, &usr, &nice, &sys, &idle, &iowait, &irq, &softirq);
163

    
164
                        long used = usr + nice + sys + iowait + irq + softirq;
165

    
166
                        fprintf(cache_file, "%s.value %ld:%ld\n", cpu_id, epoch, used);
167
                        fflush(cache_file);
168
                }
169

    
170
                /* unlock */
171
                flock(cache_file_fd, LOCK_UN);
172
        }
173

    
174
        fclose(cache_file);
175
        close(f);
176

    
177
        return 0;
178
}
179

    
180
int fetch() {
181
        FILE* cache_file = fopen(cache_filename, "r+");
182
        if (!cache_file) {
183
                return acquire();
184
        }
185

    
186
        /* lock */
187
        int cache_file_fd = fileno(cache_file);
188
        flock(cache_file_fd, LOCK_EX);
189

    
190
        /* cat the cache_file to stdout */
191
        char buffer[1024];
192
        while (fgets(buffer, 1024, cache_file)) {
193
                printf("%s", buffer);
194
        }
195

    
196
        ftruncate(cache_file_fd, 0);
197
        fclose(cache_file);
198

    
199
        return 0;
200
}
201

    
202
int main(int argc, char **argv) {
203
        /* resolve paths */
204
        char *MUNIN_PLUGSTATE = getenv("MUNIN_PLUGSTATE");
205

    
206
        /* Default is current directory */
207
        if (! MUNIN_PLUGSTATE) MUNIN_PLUGSTATE = ".";
208

    
209
        size_t MUNIN_PLUGSTATE_length = strlen(MUNIN_PLUGSTATE);
210

    
211
        pid_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/multicpu1sec.") + strlen("pid") + 1); pid_filename[0] = '\0';
212
        cache_filename = malloc(MUNIN_PLUGSTATE_length + strlen("/multicpu1sec.") + strlen("value") + 1); cache_filename[0] = '\0';
213

    
214
        strcat(pid_filename, MUNIN_PLUGSTATE);
215
        strcat(pid_filename, "/multicpu1sec.pid");
216

    
217
        strcat(cache_filename, MUNIN_PLUGSTATE);
218
        strcat(cache_filename, "/multicpu1sec.value");
219

    
220
        if (argc > 1) {
221
                char* first_arg = argv[1];
222
                if (! strcmp(first_arg, "config")) {
223
                        return config();
224
                }
225

    
226
                if (! strcmp(first_arg, "acquire")) {
227
                        return acquire();
228
                }
229
        }
230

    
231
        return fetch();
232
}