10 #include "plugin_tc.h"
12 #define RRD_TYPE_TC "tc"
13 #define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC)
15 // ----------------------------------------------------------------------------
17 // this requires the script plugins.d/tc-qos-helper.sh
19 #define TC_LINE_MAX 1024
22 char id[RRD_STATS_NAME_MAX + 1];
23 char name[RRD_STATS_NAME_MAX + 1];
25 char leafid[RRD_STATS_NAME_MAX + 1];
26 char parentid[RRD_STATS_NAME_MAX + 1];
30 unsigned long long bytes;
32 struct tc_class *next;
36 char id[RRD_STATS_NAME_MAX + 1];
37 char name[RRD_STATS_NAME_MAX + 1];
38 char family[RRD_STATS_NAME_MAX + 1];
40 struct tc_class *classes;
43 void tc_device_commit(struct tc_device *d)
45 static int enable_new_interfaces = -1;
47 if(enable_new_interfaces == -1) enable_new_interfaces = config_get_boolean("plugin:tc", "enable new interfaces detected at runtime", 1);
49 // we only need to add leaf classes
50 struct tc_class *c, *x;
52 for ( c = d->classes ; c ; c = c->next)
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);
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);
73 for ( c = d->classes ; c ; c = c->next) {
74 if(c->isleaf && c->hasparent) break;
77 debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name);
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 RRD_STATS *st = rrd_stats_find_bytype(RRD_TYPE_TC, d->id);
86 debug(D_TC_LOOP, "TC: Committing new TC device '%s'", d->name);
88 st = rrd_stats_create(RRD_TYPE_TC, d->id, d->name, d->family, "Class Usage", "kilobits/s", 1000, update_every, CHART_TYPE_STACKED);
90 for ( c = d->classes ; c ; c = c->next) {
91 if(c->isleaf && c->hasparent)
92 rrd_stats_dimension_add(st, c->id, c->name, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
96 rrd_stats_next_plugins(st);
98 if(strcmp(d->id, d->name) != 0) rrd_stats_set_name(st, d->name);
101 for ( c = d->classes ; c ; c = c->next) {
102 if(c->isleaf && c->hasparent) {
103 if(rrd_stats_dimension_set(st, c->id, c->bytes) != 0) {
105 // new class, we have to add it
106 rrd_stats_dimension_add(st, c->id, c->name, 8, 1024 * update_every, RRD_DIMENSION_INCREMENTAL);
107 rrd_stats_dimension_set(st, c->id, c->bytes);
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
114 for(rd = st->dimensions ; rd ; rd = rd->next) {
115 if(strcmp(rd->id, c->id) == 0) { rrd_stats_dimension_set_name(st, rd, c->name); break; }
124 void tc_device_set_class_name(struct tc_device *d, char *id, char *name)
127 for ( c = d->classes ; c ; c = c->next) {
128 if(strcmp(c->id, id) == 0) {
129 strncpy(c->name, name, RRD_STATS_NAME_MAX);
130 // no need for null termination - it is already null
136 void tc_device_set_device_name(struct tc_device *d, char *name)
138 strncpy(d->name, name, RRD_STATS_NAME_MAX);
139 // no need for null termination - it is already null
142 void tc_device_set_device_family(struct tc_device *d, char *name)
144 strncpy(d->family, name, RRD_STATS_NAME_MAX);
145 // no need for null termination - it is already null
148 struct tc_device *tc_device_create(char *name)
152 d = calloc(1, sizeof(struct tc_device));
154 fatal("Cannot allocate memory for tc_device %s", name);
158 strncpy(d->id, name, RRD_STATS_NAME_MAX);
159 strcpy(d->name, d->id);
160 strcpy(d->family, d->id);
162 // no need for null termination on the strings, because of calloc()
167 struct tc_class *tc_class_add(struct tc_device *n, char *id, char *parentid, char *leafid)
171 c = calloc(1, sizeof(struct tc_class));
173 fatal("Cannot allocate memory for tc class");
177 c->next = n->classes;
180 strncpy(c->id, id, RRD_STATS_NAME_MAX);
181 strcpy(c->name, c->id);
182 if(parentid) strncpy(c->parentid, parentid, RRD_STATS_NAME_MAX);
183 if(leafid) strncpy(c->leafid, leafid, RRD_STATS_NAME_MAX);
185 // no need for null termination on the strings, because of calloc()
190 void tc_class_free(struct tc_class *c)
192 if(c->next) tc_class_free(c->next);
196 void tc_device_free(struct tc_device *n)
198 if(n->classes) tc_class_free(n->classes);
202 pid_t tc_child_pid = 0;
203 void *tc_main(void *ptr)
207 if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0)
208 error("Cannot set pthread cancel type to DEFERRED.");
210 if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
211 error("Cannot set pthread cancel state to ENABLE.");
213 char buffer[TC_LINE_MAX+1] = "";
217 struct tc_device *device = NULL;
218 struct tc_class *class = NULL;
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"), update_every);
221 debug(D_TC_LOOP, "executing '%s'", buffer);
222 // fp = popen(buffer, "r");
223 fp = mypopen(buffer, &tc_child_pid);
225 error("TC: Cannot popen(\"%s\", \"r\").", buffer);
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);
234 p = strsep(&b, " \n");
235 while (p && (*p == ' ' || *p == '\0')) p = strsep(&b, " \n");
238 if(strcmp(p, "END") == 0) {
240 if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0)
241 error("Cannot set pthread cancel state to DISABLE.");
243 tc_device_commit(device);
244 tc_device_free(device);
248 if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0)
249 error("Cannot set pthread cancel state to ENABLE.");
252 else if(strcmp(p, "BEGIN") == 0) {
254 tc_device_free(device);
259 p = strsep(&b, " \n");
261 device = tc_device_create(p);
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
275 && parentid && *parentid
277 (strcmp(parent, "parent") == 0 && parentid && *parentid)
278 || strcmp(parent, "root") == 0
281 if(strcmp(parent, "root") == 0) {
285 else if(!leaf || strcmp(leaf, "leaf") != 0)
288 char leafbuf[20 + 1] = "";
289 if(leafid && leafid[strlen(leafid) - 1] == ':') {
290 strncpy(leafbuf, leafid, 20 - 1);
291 strcat(leafbuf, "1");
295 class = tc_class_add(device, id, parentid, leafid);
298 else if(device && class && (strcmp(p, "Sent") == 0)) {
299 p = strsep(&b, " \n");
300 if(p && *p) class->bytes = atoll(p);
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);
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);
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);
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);
320 if(pid) tc_child_pid = pid;
322 debug(D_TC_LOOP, "TC: Child PID is %d.", tc_child_pid);
329 tc_device_free(device);