#include <arpa/inet.h>\r
\r
#define MAX_COMPARE_NAME 15\r
+#define MAX_NAME 100\r
\r
\r
#define add_childs 1ULL\r
\r
struct wanted {\r
char compare[MAX_COMPARE_NAME + 3];\r
- char id[FILENAME_MAX+1];\r
- char name[FILENAME_MAX+1];\r
+ char id[MAX_NAME + 1];\r
+ char name[MAX_NAME + 1];\r
\r
unsigned long long minflt;\r
unsigned long long cminflt;\r
unsigned long long rss;\r
\r
unsigned long merge_count; // how many processes have been merged to this\r
+ int exposed; // if set, we have sent this to netdata\r
\r
struct wanted *target; // the one that will be reported to netdata\r
struct wanted *next;\r
-} *wanted_root = NULL;\r
+} *wanted_root = NULL, *default_target = NULL;\r
\r
int update_every = 1;\r
\r
return NULL;\r
}\r
\r
- strncpy(w->id, id, FILENAME_MAX);\r
- strncpy(w->name, id, FILENAME_MAX);\r
+ strncpy(w->id, id, MAX_NAME);\r
+ strncpy(w->name, id, MAX_NAME);\r
snprintf(w->compare, MAX_COMPARE_NAME+1, "(%.*s)", MAX_COMPARE_NAME, id);\r
w->target = target;\r
\r
\r
while((t = strsep(&s, " "))) {\r
if(w && strcmp(t, "as") == 0 && s && *s) {\r
- strncpy(w->name, s, FILENAME_MAX);\r
+ strncpy(w->name, s, MAX_NAME);\r
if(debug) fprintf(stderr, "Setting dimension name to '%s' on target '%s'\n", w->name, w->id);\r
break;\r
}\r
if(!w) w = n;\r
}\r
}\r
+\r
+ default_target = add_wanted("all_other_processes", NULL);\r
+ strncpy(default_target->name, "other", MAX_NAME);\r
}\r
\r
// see: man proc\r
struct pid_stat {\r
int32_t pid;\r
- char comm[FILENAME_MAX+1];\r
+ char comm[MAX_COMPARE_NAME + 3];\r
char state;\r
int32_t ppid;\r
int32_t pgrp;\r
int new_entry;\r
unsigned long merge_count;\r
struct wanted *target;\r
+ int target_inherited;\r
struct pid_stat *parent;\r
struct pid_stat *prev;\r
struct pid_stat *next;\r
int update_from_proc(void)\r
{\r
char buffer[PID_STAT_LINE_MAX + 1];\r
+ char name[PID_STAT_LINE_MAX + 1];\r
char filename[FILENAME_MAX+1];\r
DIR *dir = opendir("/proc");\r
if(!dir) return 0;\r
p->merged = 0;\r
p->new_entry = 0;\r
p->merge_count = 0;\r
+\r
+ if(p->target_inherited) p->target = NULL;\r
}\r
\r
while((file = readdir(dir))) {\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->pid, name, &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->rt_priority, &p->policy\r
, &p->delayacct_blkio_ticks, &p->guest_time, &p->cguest_time\r
);\r
+ strncpy(p->comm, name, MAX_COMPARE_NAME + 2);\r
+ p->comm[MAX_COMPARE_NAME + 2] = '\0';\r
\r
if(parsed < 39) fprintf(stderr, "file %s gave %d results (expected 44)\n", filename, parsed);\r
\r
{\r
struct pid_stat *p = NULL;\r
\r
- // link all parents and update childs\r
+ // link all parents and update childs count\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
}\r
\r
- // find all the procs with 0 childs and update their parents\r
- // continue, until nothing more can be done.\r
+ // find all the procs with 0 childs and merge them to their parents\r
+ // repeat, 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 this process does not any childs, and\r
+ // is not already merged, and\r
+ // its parents has childs waiting to be merged, and\r
+ // the target of this process and its parent is the same, or the parent does not have a target, or this process does not have a parent\r
+ // then... merge them!\r
+ if(!p->childs && !p->merged && p->parent && p->parent->childs && (p->target == p->parent->target || !p->parent->target || !p->target)) {\r
if(debug) fprintf(stderr, "\tMerging %d %s to %d %s (count: %lu)\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
// 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
+ p->parent->target_inherited = 1;\r
+ if(debug) fprintf(stderr, "\t\ttarget %s is inherited by %d %s from its child %d %s.\n", p->target->name, p->parent->pid, p->parent->comm, p->pid, p->comm);\r
}\r
\r
found++;\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 += p->merge_count + 1;\r
- if(debug) fprintf(stderr, "\tAgregating %s pid %d on %s (count: %lu)\n", p->comm, p->pid, p->target->name, p->target->merge_count);\r
+ if(p->parent && !p->merged) fprintf(stderr, "\tprocess %s pid %d has a parent, but has not been merged!\n", p->comm, p->pid);\r
+ if(p->childs) fprintf(stderr, "\tprocess %s pid %d has %d childs that have not been merged!\n", p->comm, p->pid, p->childs);\r
+ if(p->merged) continue;\r
+\r
+ if(!p->target) {\r
+ p->target = default_target;\r
+ p->target_inherited = 1;\r
+ if(debug) fprintf(stderr, "\tprocess %s pid %d is orphan\n", p->comm, p->pid);\r
}\r
+\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 += p->merge_count + 1;\r
+ if(debug) fprintf(stderr, "\tAgregating %s pid %d on %s (count: %lu)\n", p->comm, p->pid, p->target->name, p->target->merge_count);\r
}\r
}\r
\r
\r
fprintf(stdout, "BEGIN apps.cpu\n");\r
for (w = wanted_root; w ; w = w->next) {\r
- if(w->target) continue;\r
+ if(w->target || (!w->merge_count && !w->exposed)) 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
\r
fprintf(stdout, "BEGIN apps.cpu_user\n");\r
for (w = wanted_root; w ; w = w->next) {\r
- if(w->target) continue;\r
+ if(w->target || (!w->merge_count && !w->exposed)) continue;\r
\r
fprintf(stdout, "SET %s = %llu\n", w->name, w->utime + (w->cutime * add_childs));\r
}\r
\r
fprintf(stdout, "BEGIN apps.cpu_system\n");\r
for (w = wanted_root; w ; w = w->next) {\r
- if(w->target) continue;\r
+ if(w->target || (!w->merge_count && !w->exposed)) continue;\r
\r
fprintf(stdout, "SET %s = %llu\n", w->name, w->stime + (w->cstime * add_childs));\r
}\r
\r
fprintf(stdout, "BEGIN apps.threads\n");\r
for (w = wanted_root; w ; w = w->next) {\r
- if(w->target) continue;\r
+ if(w->target || (!w->merge_count && !w->exposed)) continue;\r
\r
fprintf(stdout, "SET %s = %llu\n", w->name, w->num_threads);\r
}\r
\r
fprintf(stdout, "BEGIN apps.processes\n");\r
for (w = wanted_root; w ; w = w->next) {\r
- if(w->target) continue;\r
+ if(w->target || (!w->merge_count && !w->exposed)) continue;\r
\r
fprintf(stdout, "SET %s = %lu\n", w->name, w->merge_count);\r
}\r
\r
fprintf(stdout, "BEGIN apps.rss\n");\r
for (w = wanted_root; w ; w = w->next) {\r
- if(w->target) continue;\r
+ if(w->target || (!w->merge_count && !w->exposed)) continue;\r
\r
fprintf(stdout, "SET %s = %llu\n", w->name, (unsigned long long)w->rss);\r
}\r
\r
fprintf(stdout, "BEGIN apps.minor_faults\n");\r
for (w = wanted_root; w ; w = w->next) {\r
- if(w->target) continue;\r
+ if(w->target || (!w->merge_count && !w->exposed)) continue;\r
\r
fprintf(stdout, "SET %s = %llu\n", w->name, w->minflt + (w->cminflt * add_childs));\r
}\r
\r
fprintf(stdout, "BEGIN apps.major_faults\n");\r
for (w = wanted_root; w ; w = w->next) {\r
- if(w->target) continue;\r
+ if(w->target || (!w->merge_count && !w->exposed)) continue;\r
\r
fprintf(stdout, "SET %s = %llu\n", w->name, w->majflt + (w->cmajflt * add_childs));\r
}\r
void show_charts(void)\r
{\r
struct wanted *w;\r
+ int newly_added = 0;\r
\r
+ for(w = wanted_root ; w ; w = w->next)\r
+ if(!w->exposed && w->merge_count) {\r
+ newly_added++;\r
+ w->exposed = 1;\r
+ if(debug) fprintf(stderr, "%s just added - regenerating charts.\n", w->name);\r
+ }\r
+\r
+ // nothing more to show\r
+ if(!newly_added) return;\r
+\r
+ // we have something new to show\r
+ // update the charts\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
+ if(w->target || (!w->merge_count && !w->exposed)) 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
+ if(w->target || (!w->merge_count && !w->exposed)) 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
+ if(w->target || (!w->merge_count && !w->exposed)) continue;\r
\r
fprintf(stdout, "DIMENSION %s '' absolute 1 1\n", w->name);\r
}\r
\r
fprintf(stdout, "CHART apps.processes '' 'Applications Processes' 'processes' apps apps stacked 20004 %d\n", update_every);\r
for (w = wanted_root; w ; w = w->next) {\r
- if(w->target) continue;\r
+ if(w->target || (!w->merge_count && !w->exposed)) 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
+ if(w->target || (!w->merge_count && !w->exposed)) 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
+ if(w->target || (!w->merge_count && !w->exposed)) 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
+ if(w->target || (!w->merge_count && !w->exposed)) 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
+ if(w->target || (!w->merge_count && !w->exposed)) continue;\r
\r
fprintf(stdout, "DIMENSION %s '' incremental 1 1\n", w->name);\r
}\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
}\r
\r
merge_processes();\r
+ show_charts(); // this is smart enough to show only newly added apps, when needed\r
show_dimensions();\r
\r
if(debug) fprintf(stderr, "Done Loop No %llu\n", counter);\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);
close(pipefd[PIPE_WRITE]);
}
+/*
+ // fork again to become session leader
+ pid = fork();
+ if(pid == -1) fprintf(stderr, "Cannot fork again on pid %d\n", getpid());
+ if(pid != 0) {
+ // the parent
+ exit(0);
+ }
+
+ // set a new process group id for just this child
+ if( setpgid(0, 0) != 0 )
+ fprintf(stderr, "Cannot set a new process group for pid %d (%s)\n", getpid(), strerror(errno));
+
+ if( getpgid(0) != getpid() )
+ fprintf(stderr, "Process group set is incorrect. Expected %d, found %d\n", getpid(), getpgid(0));
+
+ if( setsid() != 0 )
+ fprintf(stderr, "Cannot set session id for pid %d (%s)\n", getpid(), strerror(errno));
+*/
+
+ // ignore all signals
+ for (i = 1 ; i < 65 ;i++) if(i != SIGSEGV) signal(i, SIG_DFL);
+
+ fprintf(stdout, "MYPID %d\n", getpid());
+ fflush(NULL);
+
fprintf(stderr, "executing command: '%s'\n", command);
execl("/bin/sh", "sh", "-c", command, NULL);
exit(1);
if(pid != tc_child_pid)
error("tc-qos-helper reports wrong pid %d (expected %d).", pid, tc_child_pid);
+ // tc_child_pid = pid;
+
debug(D_TC_LOOP, "TC: Child PID is %d.", tc_child_pid);
}
}
struct plugind *cd = (struct plugind *)arg;
char line[PLUGINSD_LINE_MAX + 1];
+ unsigned long long usec = 0, susec = 0;
+ struct timeval last, now;
+
while(1) {
// FILE *fp = popen(cd->cmd, "r");
FILE *fp = mypopen(cd->cmd, &cd->pid);
kill(cd->pid, SIGTERM);
break;
}
+ // cd->pid = pid;
debug(D_PLUGINSD, "PLUGINSD: %s is on pid %d", cd->id, cd->pid);
}
kill(cd->pid, SIGTERM);
break;
}
+ else if(!strcmp(s, "STOPPING_WAKE_ME_UP_PLEASE")) {
+ error("PLUGINSD: script '%s' (pid %d) called STOPPING_WAKE_ME_UP_PLEASE.", cd->fullfilename, cd->pid);
+
+ gettimeofday(&now, NULL);
+ if(!usec && !susec) {
+ // our first run
+ susec = cd->update_every * 1000000ULL;
+ }
+ else {
+ // second+ run
+ usec = usecdiff(&now, &last) - susec;
+ error("PLUGINSD: %s last loop took %llu usec (worked for %llu, sleeped for %llu).\n", cd->fullfilename, usec + susec, usec, susec);
+ if(usec < (update_every * 1000000ULL)) susec = (update_every * 1000000ULL) - usec;
+ else susec = 100000ULL;
+ }
+
+ error("PLUGINSD: %s sleeping for %llu. Will kill pid %d to wake it up.\n", cd->fullfilename, susec, cd->pid);
+ usleep(susec);
+ kill(cd->pid, SIGCONT);
+ bcopy(&now, &last, sizeof(struct timeval));
+ break;
+ }
else {
error("PLUGINSD: script %s is sending command '%s' which is not known by netdata. Disabling it.", cd->fullfilename, s);
cd->enabled = 0;
cd->update_every = config_get_number(cd->id, "update every", update_every);
char *def = "";
- if(strcmp(cd->id, "plugin:apps") == 0) def = "mplayer squid 'apache apache2 as apache' mysqld asterisk dovecot 'master as postfix' 'smbd nmbd as samba' sshd 'gdm as X' named 'clamd freshclam as clam' 'cupsd as cups' 'ntpd as ntp' 'deluge deluged as deluge' netdata";
+ if(strcmp(cd->id, "plugin:apps") == 0)
+ def =
+ " 'mplayer vlc xine mediatomb as media'"
+ " 'squid squid3 as squid'"
+ " 'apache apache2 as apache'"
+ " 'mysql mysqld as mysql'"
+ " asterisk"
+ " opensips"
+ " dovecot"
+ " nginx"
+ " lighttpd"
+ " 'proftpd in.tftpd as ftpd'"
+ " 'master as postfix'"
+ " 'smbd nmbd winbindd as samba'"
+ " 'rpcbind rpc.statd rpc.idmapd rpc.mountd as nfs'"
+ " 'ssh sshd as ssh'"
+ " 'gdm X lightdm xdm gnome-session gconfd-2 gnome-terminal gnome-screensaver gnome-settings-daemon pulseaudio as X'"
+ " 'named as bind'"
+ " 'clamd freshclam as clam'"
+ " 'cupsd cups-browsed as cups'"
+ " 'ntpq ntpd as ntp'"
+ " 'deluge deluged as deluge'"
+ " 'vboxwebsrv VBoxXPCOMIPCD VBoxSVC as vbox'"
+ " 'ulogd syslogd syslog-ng rsyslogd logrotate as log'"
+ " 'snmpd vnstatd smokeping zabbix_agentd monit munin-node mon as nms'"
+ " 'ppp pppd pptpd pptpctrl as ppp'"
+ " 'inetd xinetd as inetd'"
+ " openvpn"
+ " 'cron atd as cron'"
+ " 'corosync hs_logd stonithd as ha'"
+ " 'ipvs_syncmaster ipvs_syncbackup as ipvs'"
+ " netdata"
+ ;
snprintf(cd->cmd, PLUGINSD_CMD_MAX, "exec %s %d %s", cd->fullfilename, cd->update_every, config_get(cd->id, "command options", def));
// link it
}
}
-void bye(void)
-{
- error("exiting. bye...");
- kill_childs();
- tc_child_pid = 0;
-}
-
void sig_handler(int signo)
{
switch(signo) {
error("NetData started on pid %d", getpid());
- // make sure we cleanup correctly
- atexit(bye);
// catch all signals
for (i = 1 ; i < 65 ;i++) if(i != SIGSEGV) signal(i, sig_handler);