void *tc_main(void *ptr) { netdata_thread_cleanup_push(tc_main_cleanup, ptr); struct rusage thread; char command[FILENAME_MAX + 1]; char *words[PLUGINSD_MAX_WORDS] = { NULL }; uint32_t BEGIN_HASH = simple_hash("BEGIN"); uint32_t END_HASH = simple_hash("END"); uint32_t QDISC_HASH = simple_hash("qdisc"); uint32_t CLASS_HASH = simple_hash("class"); uint32_t SENT_HASH = simple_hash("Sent"); uint32_t LENDED_HASH = simple_hash("lended:"); uint32_t TOKENS_HASH = simple_hash("tokens:"); uint32_t SETDEVICENAME_HASH = simple_hash("SETDEVICENAME"); uint32_t SETDEVICEGROUP_HASH = simple_hash("SETDEVICEGROUP"); uint32_t SETCLASSNAME_HASH = simple_hash("SETCLASSNAME"); uint32_t WORKTIME_HASH = simple_hash("WORKTIME"); #ifdef DETACH_PLUGINS_FROM_NETDATA uint32_t MYPID_HASH = simple_hash("MYPID"); #endif uint32_t first_hash; snprintfz(command, TC_LINE_MAX, "%s/tc-qos-helper.sh", netdata_configured_primary_plugins_dir); char *tc_script = config_get("plugin:tc", "script to run to get tc values", command); while(!netdata_exit) { FILE *fp; struct tc_device *device = NULL; struct tc_class *class = NULL; snprintfz(command, TC_LINE_MAX, "exec %s %d", tc_script, localhost->rrd_update_every); debug(D_TC_LOOP, "executing '%s'", command); fp = mypopen(command, (pid_t *)&tc_child_pid); if(unlikely(!fp)) { error("TC: Cannot popen(\"%s\", \"r\").", command); goto cleanup; } char buffer[TC_LINE_MAX+1] = ""; while(fgets(buffer, TC_LINE_MAX, fp) != NULL) { if(unlikely(netdata_exit)) break; buffer[TC_LINE_MAX] = '\0'; // debug(D_TC_LOOP, "TC: read '%s'", buffer); tc_split_words(buffer, words, PLUGINSD_MAX_WORDS); if(unlikely(!words[0] || !*words[0])) { // debug(D_TC_LOOP, "empty line"); continue; } // else debug(D_TC_LOOP, "First word is '%s'", words[0]); first_hash = simple_hash(words[0]); if(unlikely(device && ((first_hash == CLASS_HASH && strcmp(words[0], "class") == 0) || (first_hash == QDISC_HASH && strcmp(words[0], "qdisc") == 0)))) { // debug(D_TC_LOOP, "CLASS line on class id='%s', parent='%s', parentid='%s', leaf='%s', leafid='%s'", words[2], words[3], words[4], words[5], words[6]); char *type = words[1]; // the class/qdisc type: htb, fq_codel, etc char *id = words[2]; // the class/qdisc major:minor char *parent = words[3]; // the word 'parent' or 'root' char *parentid = words[4]; // parentid char *leaf = words[5]; // the word 'leaf' char *leafid = words[6]; // leafid int parent_is_root = 0; int parent_is_parent = 0; if(likely(parent)) { parent_is_parent = !strcmp(parent, "parent"); if(!parent_is_parent) parent_is_root = !strcmp(parent, "root"); } if(likely(type && id && (parent_is_root || parent_is_parent))) { char qdisc = 0; if(first_hash == QDISC_HASH) { qdisc = 1; if(!strcmp(type, "ingress")) { // we don't want to get the ingress qdisc // there should be an IFB interface for this class = NULL; continue; } if(parent_is_parent && parentid) { // eliminate the minor number from parentid // why: parentid is the id of the parent class // but major: is also the id of the parent qdisc char *s = parentid; while(*s && *s != ':') s++; if(*s == ':') s[1] = '\0'; } } if(parent_is_root) { parentid = NULL; leafid = NULL; } else if(!leaf || strcmp(leaf, "leaf") != 0) leafid = NULL; char leafbuf[20 + 1] = ""; if(leafid && leafid[strlen(leafid) - 1] == ':') { strncpyz(leafbuf, leafid, 20 - 1); strcat(leafbuf, "1"); leafid = leafbuf; } class = tc_class_add(device, id, qdisc, parentid, leafid); } else {
void *tc_main(void *ptr) { if(ptr) { ; } info("TC thread created with task id %d", gettid()); if(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) != 0) error("Cannot set pthread cancel type to DEFERRED."); if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); struct rusage thread; RRDSET *stcpu = NULL, *sttime = NULL; char buffer[TC_LINE_MAX+1] = ""; char *words[MAX_WORDS] = { NULL }; uint32_t BEGIN_HASH = simple_hash("BEGIN"); uint32_t END_HASH = simple_hash("END"); uint32_t CLASS_HASH = simple_hash("class"); uint32_t SENT_HASH = simple_hash("Sent"); uint32_t LENDED_HASH = simple_hash("lended:"); uint32_t TOKENS_HASH = simple_hash("tokens:"); uint32_t SETDEVICENAME_HASH = simple_hash("SETDEVICENAME"); uint32_t SETDEVICEGROUP_HASH = simple_hash("SETDEVICEGROUP"); uint32_t SETCLASSNAME_HASH = simple_hash("SETCLASSNAME"); uint32_t WORKTIME_HASH = simple_hash("WORKTIME"); #ifdef DETACH_PLUGINS_FROM_NETDATA uint32_t MYPID_HASH = simple_hash("MYPID"); #endif uint32_t first_hash; for(;1;) { if(unlikely(netdata_exit)) break; FILE *fp; struct tc_device *device = NULL; struct tc_class *class = NULL; snprintfz(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); debug(D_TC_LOOP, "executing '%s'", buffer); // fp = popen(buffer, "r"); fp = mypopen(buffer, &tc_child_pid); if(!fp) { error("TC: Cannot popen(\"%s\", \"r\").", buffer); pthread_exit(NULL); return NULL; } while(fgets(buffer, TC_LINE_MAX, fp) != NULL) { if(unlikely(netdata_exit)) break; buffer[TC_LINE_MAX] = '\0'; // debug(D_TC_LOOP, "TC: read '%s'", buffer); tc_split_words(buffer, words, MAX_WORDS); if(!words[0] || !*words[0]) { // debug(D_TC_LOOP, "empty line"); continue; } // else debug(D_TC_LOOP, "First word is '%s'", words[0]); first_hash = simple_hash(words[0]); if(device && first_hash == CLASS_HASH && strcmp(words[0], "class") == 0) { // debug(D_TC_LOOP, "CLASS line on class id='%s', parent='%s', parentid='%s', leaf='%s', leafid='%s'", words[2], words[3], words[4], words[5], words[6]); // clear the last class class = NULL; // words[1] : class type // words[2] : N:XX // words[3] : parent or root if(words[1] && words[2] && words[3] && (strcmp(words[3], "parent") == 0 || strcmp(words[3], "root") == 0)) { //char *type = words[1]; // the class: htb, fq_codel, etc // we are only interested for HTB classes //if(strcmp(type, "htb") != 0) continue; char *id = words[2]; // the class major:minor char *parent = words[3]; // 'parent' or 'root' char *parentid = words[4]; // the parent's id char *leaf = words[5]; // 'leaf' char *leafid = words[6]; // leafid if(strcmp(parent, "root") == 0) { parentid = NULL; leafid = NULL; } else if(!leaf || strcmp(leaf, "leaf") != 0) leafid = NULL; char leafbuf[20 + 1] = ""; if(leafid && leafid[strlen(leafid) - 1] == ':') { strncpyz(leafbuf, leafid, 20 - 1); strcat(leafbuf, "1"); leafid = leafbuf; } class = tc_class_add(device, id, parentid, leafid); } } else if(first_hash == END_HASH && strcmp(words[0], "END") == 0) { // debug(D_TC_LOOP, "END line"); if(device) { if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) != 0) error("Cannot set pthread cancel state to DISABLE."); tc_device_commit(device); // tc_device_free(device); device = NULL; class = NULL; if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) error("Cannot set pthread cancel state to ENABLE."); } } else if(first_hash == BEGIN_HASH && strcmp(words[0], "BEGIN") == 0) {