]> arthur.barton.de Git - netdata.git/blob - src/plugin_tc.c
added libavl for supporting balanced binary trees - this improves search performance...
[netdata.git] / src / plugin_tc.c
1 #include <inttypes.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include "log.h"
7 #include "config.h"
8 #include "rrd.h"
9 #include "popen.h"
10 #include "plugin_tc.h"
11
12 #define RRD_TYPE_TC                                     "tc"
13 #define RRD_TYPE_TC_LEN                         strlen(RRD_TYPE_TC)
14
15 // ----------------------------------------------------------------------------
16 // /sbin/tc processor
17 // this requires the script plugins.d/tc-qos-helper.sh
18
19 #define TC_LINE_MAX 1024
20
21 struct tc_class {
22         char id[RRD_ID_LENGTH_MAX + 1];
23         char name[RRD_ID_LENGTH_MAX + 1];
24
25         char leafid[RRD_ID_LENGTH_MAX + 1];
26         char parentid[RRD_ID_LENGTH_MAX + 1];
27
28         int hasparent;
29         int isleaf;
30         unsigned long long bytes;
31
32         struct tc_class *next;
33 };
34
35 struct tc_device {
36         char id[RRD_ID_LENGTH_MAX + 1];
37         char name[RRD_ID_LENGTH_MAX + 1];
38         char family[RRD_ID_LENGTH_MAX + 1];
39
40         struct tc_class *classes;
41 };
42
43 void tc_device_commit(struct tc_device *d)
44 {
45         static int enable_new_interfaces = -1;
46
47         if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean("plugin:tc", "enable new interfaces detected at runtime", 1);
48         
49         // we only need to add leaf classes
50         struct tc_class *c, *x;
51
52         for ( c = d->classes ; c ; c = c->next)
53                 c->isleaf = 1;
54
55         for ( c = d->classes ; c ; c = c->next) {
56                 for ( x = d->classes ; x ; x = x->next) {
57                         if(x->parentid[0] && (strcmp(c->id, x->parentid) == 0 || strcmp(c->leafid, x->parentid) == 0)) {
58                                 // debug(D_TC_LOOP, "TC: In device '%s', class '%s' (leafid: '%s') has leaf the class '%s' (parentid: '%s').", d->name, c->name, c->leafid, x->name, x->parentid);
59                                 c->isleaf = 0;
60                                 x->hasparent = 1;
61                         }
62                 }
63         }
64         
65         // debugging:
66         /*
67         for ( c = d->classes ; c ; c = c->next) {
68                 if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device %s, class %s, OK", d->name, c->id);
69                 else debug(D_TC_LOOP, "TC: Device %s, class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name, c->id, c->isleaf, c->hasparent, c->parentid);
70         }
71         */
72
73         for ( c = d->classes ; c ; c = c->next) {
74                 if(c->isleaf && c->hasparent) break;
75         }
76         if(!c) {
77                 debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name);
78                 return;
79         }
80
81         char var_name[4096 + 1];
82         snprintf(var_name, 4096, "qos for %s", d->id);
83         if(config_get_boolean("plugin:tc", var_name, enable_new_interfaces)) {
84                 RRDSET *st = rrdset_find_bytype(RRD_TYPE_TC, d->id);
85                 if(!st) {
86                         debug(D_TC_LOOP, "TC: Committing new TC device '%s'", d->name);
87
88                         st = rrdset_create(RRD_TYPE_TC, d->id, d->name, d->family, "Class Usage", "kilobits/s", 1000, rrd_update_every, RRDSET_TYPE_STACKED);
89
90                         for ( c = d->classes ; c ; c = c->next) {
91                                 if(c->isleaf && c->hasparent)
92                                         rrddim_add(st, c->id, c->name, 8, 1024 * rrd_update_every, RRDDIM_INCREMENTAL);
93                         }
94                 }
95                 else {
96                         rrdset_next_plugins(st);
97
98                         if(strcmp(d->id, d->name) != 0) rrdset_set_name(st, d->name);
99                 }
100
101                 for ( c = d->classes ; c ; c = c->next) {
102                         if(c->isleaf && c->hasparent) {
103                                 if(rrddim_set(st, c->id, c->bytes) != 0) {
104                                         
105                                         // new class, we have to add it
106                                         rrddim_add(st, c->id, c->name, 8, 1024 * rrd_update_every, RRDDIM_INCREMENTAL);
107                                         rrddim_set(st, c->id, c->bytes);
108                                 }
109
110                                 // if it has a name, different to the id
111                                 if(strcmp(c->id, c->name) != 0) {
112                                         // update the rrd dimension with the new name
113                                         RRDDIM *rd;
114                                         for(rd = st->dimensions ; rd ; rd = rd->next) {
115                                                 if(strcmp(rd->id, c->id) == 0) { rrddim_set_name(st, rd, c->name); break; }
116                                         }
117                                 }
118                         }
119                 }
120                 rrdset_done(st);
121         }
122 }
123
124 void tc_device_set_class_name(struct tc_device *d, char *id, char *name)
125 {
126         struct tc_class *c;
127         for ( c = d->classes ; c ; c = c->next) {
128                 if(strcmp(c->id, id) == 0) {
129                         strncpy(c->name, name, RRD_ID_LENGTH_MAX);
130                         // no need for null termination - it is already null
131                         break;
132                 }
133         }
134 }
135
136 void tc_device_set_device_name(struct tc_device *d, char *name)
137 {
138         strncpy(d->name, name, RRD_ID_LENGTH_MAX);
139         // no need for null termination - it is already null
140 }
141
142 void tc_device_set_device_family(struct tc_device *d, char *name)
143 {
144         strncpy(d->family, name, RRD_ID_LENGTH_MAX);
145         // no need for null termination - it is already null
146 }
147
148 struct tc_device *tc_device_create(char *name)
149 {
150         struct tc_device *d;
151
152         d = calloc(1, sizeof(struct tc_device));
153         if(!d) {
154                 fatal("Cannot allocate memory for tc_device %s", name);
155                 return NULL;
156         }
157
158         strncpy(d->id, name, RRD_ID_LENGTH_MAX);
159         strcpy(d->name, d->id);
160         strcpy(d->family, d->id);
161
162         // no need for null termination on the strings, because of calloc()
163
164         return(d);
165 }
166
167 struct tc_class *tc_class_add(struct tc_device *n, char *id, char *parentid, char *leafid)
168 {
169         struct tc_class *c;
170
171         c = calloc(1, sizeof(struct tc_class));
172         if(!c) {
173                 fatal("Cannot allocate memory for tc class");
174                 return NULL;
175         }
176
177         c->next = n->classes;
178         n->classes = c;
179
180         strncpy(c->id, id, RRD_ID_LENGTH_MAX);
181         strcpy(c->name, c->id);
182         if(parentid) strncpy(c->parentid, parentid, RRD_ID_LENGTH_MAX);
183         if(leafid) strncpy(c->leafid, leafid, RRD_ID_LENGTH_MAX);
184
185         // no need for null termination on the strings, because of calloc()
186
187         return(c);
188 }
189
190 void tc_class_free(struct tc_class *c)
191 {
192         if(c->next) tc_class_free(c->next);
193         free(c);
194 }
195
196 void tc_device_free(struct tc_device *n)
197 {
198         if(n->classes) tc_class_free(n->classes);
199         free(n);
200 }
201
202 pid_t tc_child_pid = 0;
203 void *tc_main(void *ptr)
204 {
205         if(ptr) { ; }
206
207         if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
208                 error("Cannot set pthread cancel type to DEFERRED.");
209
210         if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
211                 error("Cannot set pthread cancel state to ENABLE.");
212
213         char buffer[TC_LINE_MAX+1] = "";
214
215         for(;1;) {
216                 FILE *fp;
217                 struct tc_device *device = NULL;
218                 struct tc_class *class = NULL;
219
220                 snprintf(buffer, TC_LINE_MAX, "exec %s %d", config_get("plugin:tc", "script to run to get tc values", PLUGINS_DIR "/tc-qos-helper.sh"), rrd_update_every);
221                 debug(D_TC_LOOP, "executing '%s'", buffer);
222                 // fp = popen(buffer, "r");
223                 fp = mypopen(buffer, &tc_child_pid);
224                 if(!fp) {
225                         error("TC: Cannot popen(\"%s\", \"r\").", buffer);
226                         return NULL;
227                 }
228
229                 while(fgets(buffer, TC_LINE_MAX, fp) != NULL) {
230                         buffer[TC_LINE_MAX] = '\0';
231                         char *b = buffer, *p;
232                         // debug(D_TC_LOOP, "TC: read '%s'", buffer);
233
234                         p = strsep(&b, " \n");
235                         while (p && (*p == ' ' || *p == '\0')) p = strsep(&b, " \n");
236                         if(!p) continue;
237
238                         if(strcmp(p, "END") == 0) {
239                                 if(device) {
240                                         if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0)
241                                                 error("Cannot set pthread cancel state to DISABLE.");
242
243                                         tc_device_commit(device);
244                                         tc_device_free(device);
245                                         device = NULL;
246                                         class = NULL;
247
248                                         if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
249                                                 error("Cannot set pthread cancel state to ENABLE.");
250                                 }
251                         }
252                         else if(strcmp(p, "BEGIN") == 0) {
253                                 if(device) {
254                                         tc_device_free(device);
255                                         device = NULL;
256                                         class = NULL;
257                                 }
258
259                                 p = strsep(&b, " \n");
260                                 if(p && *p) {
261                                         device = tc_device_create(p);
262                                         class = NULL;
263                                 }
264                         }
265                         else if(device && (strcmp(p, "class") == 0)) {
266                                 p = strsep(&b, " \n"); // the class: htb, fq_codel, etc
267                                 char *id       = strsep(&b, " \n"); // the class major:minor
268                                 char *parent   = strsep(&b, " \n"); // 'parent' or 'root'
269                                 char *parentid = strsep(&b, " \n"); // the parent's id
270                                 char *leaf     = strsep(&b, " \n"); // 'leaf'
271                                 char *leafid   = strsep(&b, " \n"); // leafid
272
273                                 if(id && *id
274                                         && parent && *parent
275                                         && parentid && *parentid
276                                         && (
277                                                 (strcmp(parent, "parent") == 0 && parentid && *parentid)
278                                                 || strcmp(parent, "root") == 0
279                                         )) {
280
281                                         if(strcmp(parent, "root") == 0) {
282                                                 parentid = NULL;
283                                                 leafid = NULL;
284                                         }
285                                         else if(!leaf || strcmp(leaf, "leaf") != 0)
286                                                 leafid = NULL;
287
288                                         char leafbuf[20 + 1] = "";
289                                         if(leafid && leafid[strlen(leafid) - 1] == ':') {
290                                                 strncpy(leafbuf, leafid, 20 - 1);
291                                                 strcat(leafbuf, "1");
292                                                 leafid = leafbuf;
293                                         }
294
295                                         class = tc_class_add(device, id, parentid, leafid);
296                                 }
297                         }
298                         else if(device && class && (strcmp(p, "Sent") == 0)) {
299                                 p = strsep(&b, " \n");
300                                 if(p && *p) class->bytes = atoll(p);
301                         }
302                         else if(device && (strcmp(p, "SETDEVICENAME") == 0)) {
303                                 char *name = strsep(&b, " \n");
304                                 if(name && *name) tc_device_set_device_name(device, name);
305                         }
306                         else if(device && (strcmp(p, "SETDEVICEGROUP") == 0)) {
307                                 char *name = strsep(&b, " \n");
308                                 if(name && *name) tc_device_set_device_family(device, name);
309                         }
310                         else if(device && (strcmp(p, "SETCLASSNAME") == 0)) {
311                                 char *id    = strsep(&b, " \n");
312                                 char *path  = strsep(&b, " \n");
313                                 if(id && *id && path && *path) tc_device_set_class_name(device, id, path);
314                         }
315 #ifdef DETACH_PLUGINS_FROM_NETDATA
316                         else if((strcmp(p, "MYPID") == 0)) {
317                                 char *id = strsep(&b, " \n");
318                                 pid_t pid = atol(id);
319
320                                 if(pid) tc_child_pid = pid;
321
322                                 debug(D_TC_LOOP, "TC: Child PID is %d.", tc_child_pid);
323                         }
324 #endif
325                 }
326                 mypclose(fp);
327
328                 if(device) {
329                         tc_device_free(device);
330                         device = NULL;
331                         class = NULL;
332                 }
333
334                 sleep(rrd_update_every);
335         }
336
337         return NULL;
338 }
339