--- /dev/null
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <time.h>\r
+#include <unistd.h>\r
+#include <sys/types.h>\r
+#include <sys/time.h>\r
+#include <sys/wait.h>\r
+\r
+#include <sys/resource.h>\r
+#include <sys/stat.h>\r
+\r
+#include <errno.h>\r
+#include <stdarg.h>\r
+#include <locale.h>\r
+#include <ctype.h>\r
+#include <fcntl.h>\r
+\r
+#include <malloc.h>\r
+#include <inttypes.h>\r
+#include <dirent.h>\r
+#include <arpa/inet.h>\r
+\r
+#define MAX_COMPARE_NAME 15\r
+\r
+\r
+#define add_childs 1ULL\r
+\r
+unsigned long long Hertz = 1;\r
+\r
+long pid_max = 32768;\r
+int debug = 0;\r
+\r
+struct wanted {\r
+ char compare[MAX_COMPARE_NAME + 3];\r
+ char id[FILENAME_MAX+1];\r
+ char name[FILENAME_MAX+1];\r
+\r
+ unsigned long long minflt;\r
+ unsigned long long cminflt;\r
+ unsigned long long majflt;\r
+ unsigned long long cmajflt;\r
+ unsigned long long utime;\r
+ unsigned long long stime;\r
+ unsigned long long cutime;\r
+ unsigned long long cstime;\r
+ unsigned long long num_threads;\r
+ unsigned long long rss;\r
+\r
+ int merge_count;\r
+\r
+ struct wanted *target; // the one that will be reported to netdata\r
+ struct wanted *next;\r
+} *wanted_root = NULL;\r
+\r
+int update_every = 1;\r
+\r
+struct wanted *add_wanted(const char *id, struct wanted *target)\r
+{\r
+ struct wanted *w = calloc(sizeof(struct wanted), 1);\r
+ if(!w) {\r
+ fprintf(stderr, "Cannot allocate %ld bytes of memory\n", sizeof(struct wanted));\r
+ return NULL;\r
+ }\r
+\r
+ strncpy(w->id, id, FILENAME_MAX);\r
+ strncpy(w->name, id, FILENAME_MAX);\r
+ snprintf(w->compare, MAX_COMPARE_NAME+1, "(%.*s)", MAX_COMPARE_NAME, id);\r
+ w->target = target;\r
+\r
+ w->next = wanted_root;\r
+ wanted_root = w;\r
+\r
+ if(debug) fprintf(stderr, "Adding hook for process '%s', compare '%s' on target '%s'\n", w->id, w->compare, w->target?w->target->id:"");\r
+\r
+ return w;\r
+}\r
+\r
+void parse_args(int argc, char **argv)\r
+{\r
+ int i = 1;\r
+\r
+ debug = 0;\r
+ if(strcmp(argv[i], "debug") == 0) {\r
+ debug = 1;\r
+ i++;\r
+ }\r
+\r
+ update_every = atoi(argv[i++]);\r
+ if(update_every == 0) {\r
+ i = 1;\r
+ update_every = 1;\r
+ }\r
+\r
+ for(; i < argc ; i++) {\r
+ struct wanted *w = NULL;\r
+ char *s = argv[i];\r
+ char *t;\r
+\r
+ while((t = strsep(&s, " "))) {\r
+ if(w && strcmp(t, "as") == 0 && s && *s) {\r
+ strncpy(w->name, s, FILENAME_MAX);\r
+ if(debug) fprintf(stderr, "Setting dimension name to '%s' on target '%s'\n", w->name, w->id);\r
+ break;\r
+ }\r
+\r
+ struct wanted *n = add_wanted(t, w);\r
+ if(!w) w = n;\r
+ }\r
+ }\r
+}\r
+\r
+// see: man proc\r
+struct pid_stat {\r
+ int32_t pid;\r
+ char comm[FILENAME_MAX+1];\r
+ char state;\r
+ int32_t ppid;\r
+ int32_t pgrp;\r
+ int32_t session;\r
+ int32_t tty_nr;\r
+ int32_t tpgid;\r
+ uint64_t flags;\r
+ unsigned long long minflt;\r
+ unsigned long long cminflt;\r
+ unsigned long long majflt;\r
+ unsigned long long cmajflt;\r
+ unsigned long long utime;\r
+ unsigned long long stime;\r
+ unsigned long long cutime;\r
+ unsigned long long cstime;\r
+ int64_t priority;\r
+ int64_t nice;\r
+ int32_t num_threads;\r
+ int64_t itrealvalue;\r
+ unsigned long long starttime;\r
+ unsigned long long vsize;\r
+ unsigned long long rss;\r
+ unsigned long long rsslim;\r
+ unsigned long long starcode;\r
+ unsigned long long endcode;\r
+ unsigned long long startstack;\r
+ unsigned long long kstkesp;\r
+ unsigned long long kstkeip;\r
+ uint64_t signal;\r
+ uint64_t blocked;\r
+ uint64_t sigignore;\r
+ uint64_t sigcatch;\r
+ uint64_t wchan;\r
+ uint64_t nswap;\r
+ uint64_t cnswap;\r
+ int32_t exit_signal;\r
+ int32_t processor;\r
+ uint32_t rt_priority;\r
+ uint32_t policy;\r
+ unsigned long long delayacct_blkio_ticks;\r
+ uint64_t guest_time;\r
+ int64_t cguest_time;\r
+\r
+ int childs; // number of processes directly referencing this\r
+ int updated;\r
+ int merged;\r
+ int new_entry;\r
+ int merge_count;\r
+ struct wanted *target;\r
+ struct pid_stat *parent;\r
+ struct pid_stat *prev;\r
+ struct pid_stat *next;\r
+} *root = NULL;\r
+\r
+struct pid_stat **all_pids;\r
+\r
+#define PID_STAT_LINE_MAX 4096\r
+\r
+struct pid_stat *get_entry(pid_t pid)\r
+{\r
+ if(all_pids[pid]) {\r
+ all_pids[pid]->new_entry = 0;\r
+ return all_pids[pid];\r
+ }\r
+\r
+ all_pids[pid] = calloc(sizeof(struct pid_stat), 1);\r
+ if(!all_pids[pid]) {\r
+ fprintf(stderr, "Cannot allocate %lu bytes of memory", sizeof(struct pid_stat));\r
+ return NULL;\r
+ }\r
+\r
+ if(root) root->prev = all_pids[pid];\r
+ all_pids[pid]->next = root;\r
+ root = all_pids[pid];\r
+\r
+ all_pids[pid]->new_entry = 1;\r
+\r
+ return all_pids[pid];\r
+}\r
+\r
+void del_entry(pid_t pid)\r
+{\r
+ if(!all_pids[pid]) return;\r
+\r
+ if(root == all_pids[pid]) root = all_pids[pid]->next;\r
+ if(all_pids[pid]->next) all_pids[pid]->next->prev = all_pids[pid]->prev;\r
+ if(all_pids[pid]->prev) all_pids[pid]->prev->next = all_pids[pid]->next;\r
+\r
+ free(all_pids[pid]);\r
+ all_pids[pid] = NULL;\r
+}\r
+\r
+int update_from_proc(void)\r
+{\r
+ char buffer[PID_STAT_LINE_MAX + 1];\r
+ char filename[FILENAME_MAX+1];\r
+ DIR *dir = opendir("/proc");\r
+ if(!dir) return 0;\r
+\r
+ struct dirent *file = NULL;\r
+ struct pid_stat *p = NULL;\r
+\r
+ // mark them all as un-updated\r
+ for(p = root; p ; p = p->next) {\r
+ p->parent = NULL;\r
+ p->updated = 0;\r
+ p->childs = 0;\r
+ p->merged = 0;\r
+ p->new_entry = 0;\r
+ p->merge_count = 0;\r
+ }\r
+\r
+ while((file = readdir(dir))) {\r
+ char *endptr = file->d_name;\r
+ pid_t pid = strtoul(file->d_name, &endptr, 10);\r
+ if(pid <= 0 || pid > pid_max || endptr == file->d_name || *endptr != '\0') continue;\r
+\r
+ snprintf(filename, FILENAME_MAX, "/proc/%s/stat", file->d_name);\r
+\r
+ int fd = open(filename, O_RDONLY);\r
+ if(fd == -1) {\r
+ if(errno != ENOENT) fprintf(stderr, "Cannot open file '%s' for reading (%s).\n", filename, strerror(errno));\r
+ continue;\r
+ }\r
+\r
+ int bytes = read(fd, buffer, PID_STAT_LINE_MAX);\r
+ if(bytes == -1) {\r
+ fprintf(stderr, "Cannot read from file '%s' (%s).\n", filename, strerror(errno));\r
+ close(fd);\r
+ continue;\r
+ }\r
+\r
+ close(fd);\r
+ if(bytes < 100) continue;\r
+\r
+ p = get_entry(pid);\r
+ if(!p) continue;\r
+\r
+ int parsed = sscanf(buffer,\r
+ "%d %s %c" // pid, comm, state\r
+ " %d %d %d %d %d" // ppid, pgrp, session, tty_nr, tpgid\r
+ " %lu %llu %llu %llu %llu" // flags, minflt, cminflt, majflt, cmajflt\r
+ " %llu %llu %llu %llu" // utime, stime, cutime, cstime\r
+ " %ld %ld" // priority, nice\r
+ " %d" // num_threads\r
+ " %ld" // itrealvalue\r
+ " %llu" // starttime\r
+ " %llu" // vsize\r
+ " %llu" // rss\r
+ " %llu %llu %llu %llu %llu %llu" // rsslim, starcode, endcode, startstack, kstkesp, kstkeip\r
+ " %lu %lu %lu %lu" // signal, blocked, sigignore, sigcatch\r
+ " %lu %lu %lu" // wchan, nswap, cnswap\r
+ " %d %d" // exit_signal, processor\r
+ " %u %u" // rt_priority, policy\r
+ " %llu %lu %ld"\r
+ , &p->pid, p->comm, &p->state\r
+ , &p->ppid, &p->pgrp, &p->session, &p->tty_nr, &p->tpgid\r
+ , &p->flags, &p->minflt, &p->cminflt, &p->majflt, &p->cmajflt\r
+ , &p->utime, &p->stime, &p->cutime, &p->cstime\r
+ , &p->priority, &p->nice\r
+ , &p->num_threads\r
+ , &p->itrealvalue\r
+ , &p->starttime\r
+ , &p->vsize\r
+ , &p->rss\r
+ , &p->rsslim, &p->starcode, &p->endcode, &p->startstack, &p->kstkesp, &p->kstkeip\r
+ , &p->signal, &p->blocked, &p->sigignore, &p->sigcatch\r
+ , &p->wchan, &p->nswap, &p->cnswap\r
+ , &p->exit_signal, &p->processor\r
+ , &p->rt_priority, &p->policy\r
+ , &p->delayacct_blkio_ticks, &p->guest_time, &p->cguest_time\r
+ );\r
+\r
+ if(parsed < 39) fprintf(stderr, "file %s gave %d results (expected 44)\n", filename, parsed);\r
+\r
+ // check if it is wanted\r
+ // we do this only once, the first time this pid is loaded\r
+ if(p->new_entry) {\r
+ if(debug) fprintf(stderr, "\tJust added %s\n", p->comm);\r
+\r
+ struct wanted *w;\r
+ for(w = wanted_root; w ; w = w->next) {\r
+ // if(debug) fprintf(stderr, "\t\tcomparing '%s' with '%s'\n", w->compare, p->comm);\r
+\r
+ if(strcmp(w->compare, p->comm) == 0) {\r
+ if(w->target) p->target = w->target;\r
+ else p->target = w;\r
+\r
+ if(debug) fprintf(stderr, "\t\t%s linked to target %s\n", p->comm, p->target->name);\r
+ }\r
+ }\r
+ }\r
+\r
+ // just a few checks\r
+ if(p->ppid < 1 || p->ppid > pid_max) p->ppid = 1;\r
+\r
+ // mark it as updated\r
+ p->updated = 1;\r
+ }\r
+ closedir(dir);\r
+\r
+ // cleanup all un-updated\r
+ int c;\r
+ for(p = root, c = 0; p ; c++) {\r
+ if(!p->updated) {\r
+ pid_t r = p->pid;\r
+ p = p->next;\r
+ del_entry(r);\r
+ }\r
+ else p = p->next;\r
+ }\r
+\r
+ if(debug) fprintf(stderr, "There are %d processes active\n", c);\r
+\r
+ return 1;\r
+}\r
+\r
+void merge_processes(void)\r
+{\r
+ struct pid_stat *p = NULL;\r
+\r
+ // link all parents and update childs\r
+ for(p = root; p ; p = p->next) {\r
+ if(p->ppid > 1 && p->ppid <= pid_max && all_pids[p->ppid]) {\r
+ if(debug) fprintf(stderr, "\tParent of %d %s is %d %s\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm);\r
+ \r
+ p->parent = all_pids[p->ppid];\r
+ if(!p->parent) {\r
+ if(debug) fprintf(stderr, "\t\tpid %d %s states parent %d, but the later does not exist.\n", p->pid, p->comm, p->ppid);\r
+ continue;\r
+ }\r
+\r
+ p->parent->childs++;\r
+ }\r
+ }\r
+\r
+ // find all the procs with 0 childs and update their parents\r
+ // continue, until nothing more can be done.\r
+ int found = 1;\r
+ for( ; found ; ) {\r
+ found = 0;\r
+ for(p = root; p ; p = p->next) {\r
+ if(!p->childs && !p->merged && p->parent && p->parent->childs) {\r
+ if(debug) fprintf(stderr, "\tMerging %d %s to %d %s (count: %d)\n", p->pid, p->comm, p->ppid, all_pids[p->ppid]->comm, p->parent->merge_count+1);\r
+\r
+ p->parent->minflt += p->minflt;\r
+ p->parent->majflt += p->majflt;\r
+ p->parent->utime += p->utime;\r
+ p->parent->stime += p->stime;\r
+\r
+ p->parent->cminflt += p->cminflt;\r
+ p->parent->cmajflt += p->cmajflt;\r
+ p->parent->cutime += p->cutime;\r
+ p->parent->cstime += p->cstime;\r
+\r
+ p->parent->num_threads += p->num_threads;\r
+ p->parent->rss += p->rss;\r
+\r
+ p->parent->childs--;\r
+ p->merged = 1;\r
+\r
+ p->parent->merge_count++;\r
+\r
+ // the parent inherits the child's target, if it does not have a target itself\r
+ if(p->target && !p->parent->target) {\r
+ p->parent->target = p->target;\r
+ if(debug) fprintf(stderr, "\t\ttarget %s is inherited from %d %s by its child %d %s.\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);\r
+ }\r
+\r
+ found++;\r
+ }\r
+ }\r
+ if(debug) fprintf(stderr, "Merged %d processes\n", found);\r
+ }\r
+\r
+ if(debug) {\r
+ fprintf(stderr, "Root level processes: \n");\r
+ for(p = root, found = 0; p ; p = p->next) {\r
+ if(!p->childs && !p->merged && !p->parent) {\r
+ fprintf(stderr, "\t\t%s %d on target %s\n", p->comm, p->pid, p->target?p->target->name:"-");\r
+ }\r
+ }\r
+ }\r
+\r
+ // zero the targets\r
+ struct wanted *w;\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ w->minflt = 0;\r
+ w->majflt = 0;\r
+ w->utime = 0;\r
+ w->stime = 0;\r
+ w->cminflt = 0;\r
+ w->cmajflt = 0;\r
+ w->cutime = 0;\r
+ w->cstime = 0;\r
+ w->num_threads = 0;\r
+ w->rss = 0;\r
+ w->merge_count = 0;\r
+ }\r
+\r
+ // concentrate everything on the targets\r
+ for(p = root; p ; p = p->next) {\r
+ if(!p->childs && !p->merged && !p->parent && p->target) {\r
+ p->target->minflt += p->minflt;\r
+ p->target->majflt += p->majflt;\r
+ p->target->utime += p->utime;\r
+ p->target->stime += p->stime;\r
+ p->target->cminflt += p->cminflt;\r
+ p->target->cmajflt += p->cmajflt;\r
+ p->target->cutime += p->cutime;\r
+ p->target->cstime += p->cstime;\r
+ p->target->num_threads += p->num_threads;\r
+ p->target->rss += p->rss;\r
+\r
+ p->target->merge_count++;\r
+ if(debug) fprintf(stderr, "\tAgregating %s pid %d on %s (count: %d)\n", p->comm, p->pid, p->target->name, p->target->merge_count);\r
+ }\r
+ }\r
+}\r
+\r
+\r
+void show_dimensions(void)\r
+{\r
+ struct wanted *w;\r
+\r
+ fprintf(stdout, "BEGIN apps.cpu\n");\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + w->stime + (w->cutime * add_childs) + (w->cstime * add_childs));\r
+ }\r
+ fprintf(stdout, "END\n");\r
+\r
+ fprintf(stdout, "BEGIN apps.cpu_user\n");\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + (w->cutime * add_childs));\r
+ }\r
+ fprintf(stdout, "END\n");\r
+\r
+ fprintf(stdout, "BEGIN apps.cpu_system\n");\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + (w->cstime * add_childs));\r
+ }\r
+ fprintf(stdout, "END\n");\r
+\r
+ fprintf(stdout, "BEGIN apps.threads\n");\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);\r
+ }\r
+ fprintf(stdout, "END\n");\r
+\r
+ fprintf(stdout, "BEGIN apps.rss\n");\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "SET %s = %llu\n", w->name, (unsigned long long)w->rss);\r
+ }\r
+ fprintf(stdout, "END\n");\r
+\r
+ fprintf(stdout, "BEGIN apps.minor_faults\n");\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + (w->cminflt * add_childs));\r
+ }\r
+ fprintf(stdout, "END\n");\r
+\r
+ fprintf(stdout, "BEGIN apps.major_faults\n");\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + (w->cmajflt * add_childs));\r
+ }\r
+ fprintf(stdout, "END\n");\r
+\r
+ fflush(stdout);\r
+}\r
+\r
+void show_charts(void)\r
+{\r
+ struct wanted *w;\r
+\r
+ fprintf(stdout, "CHART apps.cpu '' 'Applications CPU Time' 'cpu time %%' apps apps stacked 20001 %d\n", update_every);\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz);\r
+ }\r
+\r
+ fprintf(stdout, "CHART apps.rss '' 'Applications Memory' 'MB' apps apps stacked 20002 %d\n", update_every);\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "DIMENSION %s '' absolute %ld %ld\n", w->name, sysconf(_SC_PAGESIZE), 1024L*1024L);\r
+ }\r
+\r
+ fprintf(stdout, "CHART apps.threads '' 'Applications Threads' 'threads' apps apps stacked 20005 %d\n", update_every);\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);\r
+ }\r
+\r
+ fprintf(stdout, "CHART apps.cpu_user '' 'Applications CPU User Time' 'cpu time %%' apps none stacked 20020 %d\n", update_every);\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz);\r
+ }\r
+\r
+ fprintf(stdout, "CHART apps.cpu_system '' 'Applications CPU System Time' 'cpu time %%' apps none stacked 20021 %d\n", update_every);\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "DIMENSION %s '' incremental 100 %llu\n", w->name, Hertz);\r
+ }\r
+\r
+ fprintf(stdout, "CHART apps.major_faults '' 'Applications Major Page Faults' 'page faults/s' apps apps stacked 20010 %d\n", update_every);\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w->name);\r
+ }\r
+\r
+ fprintf(stdout, "CHART apps.minor_faults '' 'Applications Minor Page Faults' 'page faults/s' apps apps stacked 20011 %d\n", update_every);\r
+ for (w = wanted_root; w ; w = w->next) {\r
+ if(w->target) continue;\r
+\r
+ fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w->name);\r
+ }\r
+\r
+ fflush(stdout);\r
+}\r
+\r
+unsigned long long get_hertz(void)\r
+{\r
+ unsigned long long hz = 1;\r
+\r
+#ifdef _SC_CLK_TCK\r
+ if((hz = sysconf(_SC_CLK_TCK)) > 0) {\r
+ return hz;\r
+ }\r
+#endif\r
+\r
+#ifdef HZ\r
+ hz = (unsigned long long)HZ; /* <asm/param.h> */\r
+#else\r
+ /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */\r
+ hz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;\r
+#endif\r
+\r
+ fprintf(stderr, "Unknown HZ value. Assuming %llu", hz);\r
+ return hz;\r
+}\r
+\r
+\r
+long get_pid_max(void)\r
+{\r
+ return 32768L;\r
+}\r
+\r
+unsigned long long usecdiff(struct timeval *now, struct timeval *last) {\r
+ return ((((now->tv_sec * 1000000ULL) + now->tv_usec) - ((last->tv_sec * 1000000ULL) + last->tv_usec)));\r
+}\r
+\r
+void mysleep(unsigned long long susec)\r
+{\r
+ while(susec) {\r
+ struct timeval t1, t2;\r
+\r
+ gettimeofday(&t1, NULL);\r
+\r
+ if(debug) fprintf(stderr, "Sleeping for %llu microseconds\n", susec);\r
+ usleep(susec);\r
+\r
+ gettimeofday(&t2, NULL);\r
+\r
+ unsigned long long diff = usecdiff(&t2, &t1);\r
+ \r
+ if(diff < susec) susec -= diff;\r
+ else susec = 0;\r
+ }\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+ Hertz = get_hertz();\r
+ pid_max = get_pid_max();\r
+\r
+ parse_args(argc, argv);\r
+\r
+ all_pids = calloc(sizeof(struct pid_stat *), pid_max);\r
+ if(!all_pids) {\r
+ fprintf(stderr, "Cannot allocate %lu bytes of memory.\n", sizeof(struct pid_stat *) * pid_max);\r
+ printf("DISABLE\n");\r
+ exit(1);\r
+ }\r
+\r
+ show_charts();\r
+\r
+ unsigned long long counter = 1;\r
+ unsigned long long usec = 0, susec = 0;\r
+ struct timeval last, now;\r
+ gettimeofday(&last, NULL);\r
+\r
+ for(;1; counter++) {\r
+ if(!update_from_proc()) {\r
+ fprintf(stderr, "Cannot allocate %lu bytes of memory.\n", sizeof(struct pid_stat *) * pid_max);\r
+ printf("DISABLE\n");\r
+ exit(1);\r
+ }\r
+\r
+ merge_processes();\r
+ show_dimensions();\r
+\r
+ if(debug) fprintf(stderr, "Done Loop No %llu\n", counter);\r
+ // if(counter == 1000) exit(0);\r
+\r
+ gettimeofday(&now, NULL);\r
+\r
+ usec = usecdiff(&now, &last) - susec;\r
+ if(debug) fprintf(stderr, "last loop took %llu usec (worked for %llu, sleeped for %llu).\n", usec + susec, usec, susec);\r
+\r
+ if(usec < (update_every * 1000000ULL)) susec = (update_every * 1000000ULL) - usec;\r
+ else susec = 0;\r
+\r
+ mysleep(susec);\r
+\r
+ bcopy(&now, &last, sizeof(struct timeval));\r
+ }\r
+}\r
for(i = sysconf(_SC_OPEN_MAX); i > 0; i--)
if(i != STDIN_FILENO && i != STDERR_FILENO && i != pipefd[PIPE_WRITE]) close(i);
+ // ignore all signals
+ for (i = 1 ; i < 65 ;i++) if(i != SIGSEGV) signal(i, SIG_DFL);
+
// move the pipe to stdout
if(pipefd[PIPE_WRITE] != STDOUT_FILENO) {
dup2(pipefd[PIPE_WRITE], STDOUT_FILENO);
return 0;
}
-unsigned long long rrd_stats_next(RRD_STATS *st)
+void rrd_stats_next(RRD_STATS *st)
{
pthread_mutex_lock(&st->mutex);
pthread_mutex_unlock(&st->mutex);
- return st->usec_since_last_update;
+ return;
}
-void rrd_stats_done(RRD_STATS *st)
+unsigned long long rrd_stats_done(RRD_STATS *st)
{
pthread_mutex_lock(&st->mutex);
st->enabled = 0;
pthread_mutex_unlock(&st->mutex);
// rrd_stats_free(st);
- return;
+ return st->update_every * 1000000ULL;
}
// calculate totals and count the dimensions
// store the time difference to the last entry
st->timediff[st->current_entry] = st->usec_since_last_update;
}
+ else st->usec_since_last_update = st->update_every * 1000000ULL;
st->last_updated.tv_sec = now.tv_sec;
st->last_updated.tv_usec = now.tv_usec;
st->counter++;
pthread_mutex_unlock(&st->mutex);
+
+ return st->usec_since_last_update;
}
}
}
else {
- unsigned long long usec = rrd_stats_next(st);
- debug(D_TC_LOOP, "TC: Committing TC device '%s' after %llu usec", d->name, usec);
+ rrd_stats_next(st);
if(strcmp(d->id, d->name) != 0) {
// update the device name with the new one
#define PLUGINSD_CMD_MAX (FILENAME_MAX*2)
#define PLUGINSD_LINE_MAX 1024
-struct chartd {
+struct plugind {
char id[CONFIG_MAX_NAME+1]; // config node id
char filename[FILENAME_MAX+1]; // just the filename
int obsolete;
int enabled;
- struct chartd *next;
+ struct plugind *next;
} *pluginsd_root = NULL;
// like strsep() but:
void *pluginsd_worker_thread(void *arg)
{
- struct chartd *cd = (struct chartd *)arg;
+ struct plugind *cd = (struct plugind *)arg;
char line[PLUGINSD_LINE_MAX + 1];
while(1) {
if(!s || !*s) continue;
else if(!strcmp(s, "SET")) {
+ char *t;
+ while((t = strchr(p, '='))) *t = ' ';
+
char *dimension = qstrsep(&p);
- char *equal = qstrsep(&p);
char *value = qstrsep(&p);
- if(!dimension || !equal || *equal != '=' || !value) {
- error("PLUGINSD: script %s is requesting a SET on chart '%s', like this: 'SET %s %s %s %s'", cd->fullfilename, st->id, dimension?dimension:"", equal?equal:"", value?value:"");
- continue;
+ if(!dimension || !*dimension || !value) {
+ error("PLUGINSD: script %s is requesting a SET on chart '%s', like this: 'SET %s = %s'. Disabling it.", cd->fullfilename, st->id, dimension?dimension:"", value?value:"");
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
}
if(!st) {
- error("PLUGINSD: script %s is requesting a SET, without a BEGIN", cd->fullfilename);
- continue;
+ error("PLUGINSD: script %s is requesting a SET on dimension %s with value %s, without a BEGIN. Disabling it.", cd->fullfilename, dimension, value);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
}
if(st->debug) debug(D_PLUGINSD, "PLUGINSD: script %s is setting dimension %s/%s to %s", cd->fullfilename, st->id, dimension, value);
else if(!strcmp(s, "BEGIN")) {
char *id = qstrsep(&p);
if(!id) {
- error("PLUGINSD: script %s is requesting a BEGIN without a chart id", cd->fullfilename);
- continue;
+ error("PLUGINSD: script %s is requesting a BEGIN without a chart id. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
}
st = rrd_stats_find(id);
if(!st) {
- error("PLUGINSD: script %s is requesting a BEGIN on chart '%s', which does not exist", cd->fullfilename, id);
- continue;
+ error("PLUGINSD: script %s is requesting a BEGIN on chart '%s', which does not exist. Disabling it.", cd->fullfilename, id);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
}
if(st->counter) rrd_stats_next(st);
}
else if(!strcmp(s, "END")) {
if(!st) {
- error("PLUGINSD: script %s is requesting an END, without a BEGIN", cd->fullfilename);
- continue;
+ error("PLUGINSD: script %s is requesting an END, without a BEGIN. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
}
if(st->debug) debug(D_PLUGINSD, "PLUGINSD: script %s is requesting a END on chart %s", cd->fullfilename, st->id);
- rrd_stats_done(st);
+
+ unsigned long long usec_since_last_update = rrd_stats_done(st);
+ if(usec_since_last_update < (cd->update_every * 1000000ULL / 2)) {
+ error("PLUGINSD: script '%s' updates charts too frequently. Chart %s updated after %llu microseconds, expected %llu microseconds. Disabling it.", cd->fullfilename, st->id, usec_since_last_update, cd->update_every * 1000000ULL);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
+ }
+
st = NULL;
}
else if(!strcmp(s, "CHART")) {
char *update_every_s = qstrsep(&p);
if(!type || !*type || !id || !*id) {
- error("PLUGINSD: script %s is requesting a CHART, without a type.id", cd->fullfilename);
- continue;
+ error("PLUGINSD: script %s is requesting a CHART, without a type.id. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
}
int priority = 1000;
char *hidden = qstrsep(&p);
if(!id || !*id) {
- error("PLUGINSD: script %s is requesting a DIMENSION, without an id", cd->fullfilename);
- continue;
+ error("PLUGINSD: script %s is requesting a DIMENSION, without an id. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
}
if(!st) {
- error("PLUGINSD: script %s is requesting a DIMENSION, without a CHART", cd->fullfilename);
- continue;
+ error("PLUGINSD: script %s is requesting a DIMENSION, without a CHART. Disabling it.", cd->fullfilename);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
}
long multiplier = 1;
char *pid_s = qstrsep(&p);
pid_t pid = atol(pid_s);
- if(pid != cd->pid)
- error("plugin %s reports wrong pid %d (expected %d).", cd->filename, pid, cd->pid);
+ if(pid != cd->pid) {
+ error("plugin %s reports wrong pid %d (expected %d). Disabling it.", cd->filename, pid, cd->pid);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
+ }
debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
}
else if(!strcmp(s, "DISABLE")) {
error("PLUGINSD: script '%s' called DISABLE. Disabling it.", cd->fullfilename);
cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
+ break;
+ }
+ else {
+ error("PLUGINSD: script %s is sending command '%s' which is not known by netdata. Disabling it.", cd->fullfilename, s);
+ cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
break;
}
- else error("PLUGINSD: script %s is sending command '%s' which is not known by netdata", cd->fullfilename, s);
}
// fgets() failed or loop broke
if(!count && cd->enabled) {
error("PLUGINSD: script '%s' does not generate usefull output. Disabling it.", cd->fullfilename);
cd->enabled = 0;
+ kill(cd->pid, SIGTERM);
}
if(cd->enabled) sleep(cd->update_every);
int scan_frequency = config_get_number("plugins", "check for new plugins every", 60);
DIR *dir = NULL;
struct dirent *file = NULL;
- struct chartd *cd;
+ struct plugind *cd;
if(scan_frequency < 1) scan_frequency = 1;
// it is not running
// allocate a new one, or use the obsolete one
if(!cd) {
- cd = calloc(sizeof(struct chartd), 1);
+ cd = calloc(sizeof(struct plugind), 1);
if(!cd) fatal("Cannot allocate memory for chart.d");
snprintf(cd->id, CONFIG_MAX_NAME, "plugin:%s", pluginname);
if(tc_child_pid) kill(tc_child_pid, SIGTERM);
tc_child_pid = 0;
- struct chartd *cd;
+ struct plugind *cd;
for(cd = pluginsd_root ; cd ; cd = cd->next)
if(cd->pid && !cd->obsolete) {
kill(cd->pid, SIGTERM);