blob: 040972a83927545b42386a3a7e8cb09f60d7a4c3 [file] [log] [blame]
/*
* Copyright (C) Tildeslash Ltd. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
*
* You must obey the GNU Affero General Public License in all respects
* for all of the code used other than OpenSSL.
*/
#include "config.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <stdio.h>
#include "monit.h"
#include "process.h"
#include "process_sysdep.h"
/**
* General purpose /proc methods.
*
* @file
*/
/* ------------------------------------------------------------------ Public */
/**
* Initialize the proc information code
* @return TRUE if succeeded otherwise FALSE.
*/
int init_process_info(void) {
memset(&systeminfo, 0, sizeof(SystemInfo_T));
gettimeofday(&systeminfo.collected, NULL);
if(uname(&systeminfo.uname) < 0) {
LogError("'%s' resource monitoring initialization error -- uname failed: %s\n", Run.system->name, STRERROR);
return FALSE;
}
systeminfo.total_cpu_user_percent = -10;
systeminfo.total_cpu_syst_percent = -10;
systeminfo.total_cpu_wait_percent = -10;
return (init_process_info_sysdep());
}
/**
* Get the proc infomation (CPU percentage, MEM in MByte and percent,
* status), enduser version.
* @param p A Service object
* @param pid The process id
* @return TRUE if succeeded otherwise FALSE.
*/
int update_process_data(Service_T s, ProcessTree_T *pt, int treesize, pid_t pid) {
int leaf;
ASSERT(s);
ASSERT(systeminfo.mem_kbyte_max > 0);
/* save the previous pid and set actual one */
s->inf->priv.process._pid = s->inf->priv.process.pid;
s->inf->priv.process.pid = pid;
if ((leaf = findprocess(pid, pt, treesize)) != -1) {
/* save the previous ppid and set actual one */
s->inf->priv.process._ppid = s->inf->priv.process.ppid;
s->inf->priv.process.ppid = pt[leaf].ppid;
s->inf->priv.process.uptime = time(NULL) - pt[leaf].starttime;
s->inf->priv.process.children = pt[leaf].children_sum;
s->inf->priv.process.mem_kbyte = pt[leaf].mem_kbyte;
s->inf->priv.process.status_flag = pt[leaf].status_flag;
s->inf->priv.process.total_mem_kbyte = pt[leaf].mem_kbyte_sum;
s->inf->priv.process.cpu_percent = pt[leaf].cpu_percent;
s->inf->priv.process.total_cpu_percent = pt[leaf].cpu_percent_sum;
if (systeminfo.mem_kbyte_max == 0) {
s->inf->priv.process.total_mem_percent = 0;
s->inf->priv.process.mem_percent = 0;
} else {
s->inf->priv.process.total_mem_percent = (int)((double)pt[leaf].mem_kbyte_sum * 1000.0 / systeminfo.mem_kbyte_max);
s->inf->priv.process.mem_percent = (int)((double)pt[leaf].mem_kbyte * 1000.0 / systeminfo.mem_kbyte_max);
}
} else {
s->inf->priv.process.ppid = 0;
s->inf->priv.process.uptime = 0;
s->inf->priv.process.children = 0;
s->inf->priv.process.total_mem_kbyte = 0;
s->inf->priv.process.total_mem_percent = 0;
s->inf->priv.process.mem_kbyte = 0;
s->inf->priv.process.mem_percent = 0;
s->inf->priv.process.cpu_percent = 0;
s->inf->priv.process.total_cpu_percent = 0;
}
return TRUE;
}
/**
* Updates the system wide statistic
* @return TRUE if successful, otherwise FALSE
*/
int update_system_load() {
if (Run.doprocess) {
ASSERT(systeminfo.mem_kbyte_max > 0);
/** Get load average triplet */
if (-1 == getloadavg_sysdep(systeminfo.loadavg, 3)) {
LogError("'%s' statistic error -- load average gathering failed\n", Run.system->name);
goto error1;
}
/** Get memory usage statistic */
if (! used_system_memory_sysdep(&systeminfo)) {
LogError("'%s' statistic error -- memory usage gathering failed\n", Run.system->name);
goto error2;
}
systeminfo.total_mem_percent = (int)(1000 * (double)systeminfo.total_mem_kbyte / (double)systeminfo.mem_kbyte_max);
systeminfo.total_swap_percent = systeminfo.swap_kbyte_max ? (int)(1000 * (double)systeminfo.total_swap_kbyte / (double)systeminfo.swap_kbyte_max) : 0;
/** Get CPU usage statistic */
if (! used_system_cpu_sysdep(&systeminfo)) {
LogError("'%s' statistic error -- cpu usage gathering failed\n", Run.system->name);
goto error3;
}
return TRUE;
}
error1:
systeminfo.loadavg[0] = 0;
systeminfo.loadavg[1] = 0;
systeminfo.loadavg[2] = 0;
error2:
systeminfo.total_mem_kbyte = 0;
systeminfo.total_mem_percent = 0;
error3:
systeminfo.total_cpu_user_percent = 0;
systeminfo.total_cpu_syst_percent = 0;
systeminfo.total_cpu_wait_percent = 0;
return FALSE;
}
/**
* Initialize the process tree
* @return treesize >= 0 if succeeded otherwise < 0
*/
int initprocesstree(ProcessTree_T **pt_r, int *size_r, ProcessTree_T **oldpt_r, int *oldsize_r) {
int i;
int oldentry;
ProcessTree_T *pt;
ProcessTree_T *oldpt;
int root = -1;
if (*pt_r != NULL) {
if (oldpt_r && *oldpt_r != NULL)
delprocesstree(oldpt_r, oldsize_r);
*oldpt_r = *pt_r;
*oldsize_r = *size_r;
}
if ((*size_r = initprocesstree_sysdep(pt_r)) <= 0) {
DEBUG("system statistic error -- cannot initialize the process tree => process resource monitoring disabled\n");
Run.doprocess = FALSE;
return -1;
} else if (Run.doprocess == FALSE) {
DEBUG("system statistic -- initialization of the process tree succeeded => process resource monitoring enabled\n");
Run.doprocess = TRUE;
}
pt = *pt_r;
oldpt = *oldpt_r;
if (pt == NULL)
return 0;
for (i = 0; i < (volatile int)*size_r; i ++) {
if (oldpt && ((oldentry = findprocess(pt[i].pid, oldpt, *oldsize_r)) != -1)) {
pt[i].cputime_prev = oldpt[oldentry].cputime;
pt[i].time_prev = oldpt[oldentry].time;
/* The cpu_percent may be set already (for example by HPUX module) */
if (pt[i].cpu_percent == 0 && pt[i].cputime_prev != 0 && pt[i].cputime != 0 && pt[i].cputime > pt[i].cputime_prev) {
pt[i].cpu_percent = (int)((1000 * (double)(pt[i].cputime - pt[i].cputime_prev) / (pt[i].time - pt[i].time_prev)) / systeminfo.cpus);
if (pt[i].cpu_percent > 1000)
pt[i].cpu_percent = 1000;
}
} else {
pt[i].cputime_prev = 0;
pt[i].time_prev = 0.0;
pt[i].cpu_percent = 0;
}
if (pt[i].pid == pt[i].ppid) {
pt[i].parent = i;
continue;
}
if ((pt[i].parent = findprocess(pt[i].ppid, pt, *size_r)) == -1) {
/* Parent process wasn't found - on Linux this is normal: main process with PID 0 is not listed, similarly in FreeBSD jail.
* We create virtual process entry for missing parent so we can have full tree-like structure with root. */
int j = (*size_r)++;
pt = RESIZE(*pt_r, *size_r * sizeof(ProcessTree_T));
memset(&pt[j], 0, sizeof(ProcessTree_T));
pt[j].ppid = pt[j].pid = pt[i].ppid;
pt[i].parent = j;
}
if (! connectchild(pt, pt[i].parent, i)) {
/* connection to parent process has failed, this is usually caused in the part above */
DEBUG("system statistic error -- cannot connect process id %d to its parent %d\n", pt[i].pid, pt[i].ppid);
pt[i].pid = 0;
continue;
}
}
/* The main process in Solaris zones and FreeBSD host doesn't have pid 1, so try to find process which is parent of itself */
for (i = 0; i < *size_r; i++) {
if (pt[i].pid == pt[i].ppid) {
root = i;
break;
}
}
if (root == -1) {
DEBUG("system statistic error -- cannot find root process id\n");
return -1;
}
fillprocesstree(pt, root);
return *size_r;
}
/**
* Search a leaf in the processtree
* @param pid pid of the process
* @param pt processtree
* @param treesize size of the processtree
* @return process index if succeeded otherwise -1
*/
int findprocess(int pid, ProcessTree_T *pt, int size) {
int i;
ASSERT(pt);
if (size <= 0)
return -1;
for (i = 0; i < size; i++)
if (pid == pt[i].pid)
return i;
return -1;
}
/**
* Delete the process tree
*/
void delprocesstree(ProcessTree_T **reference, int *size) {
int i;
ProcessTree_T *pt = *reference;
if (pt) {
for (i = 0; i < *size; i++) {
FREE(pt[i].cmdline);
FREE(pt[i].children);
}
FREE(pt);
*reference = NULL;
*size = 0;
}
return;
}
void process_testmatch(char *pattern) {
#ifdef HAVE_REGEX_H
regex_t *regex_comp;
int reg_return;
#endif
#ifdef HAVE_REGEX_H
NEW(regex_comp);
if ((reg_return = regcomp(regex_comp, pattern, REG_NOSUB|REG_EXTENDED))) {
char errbuf[STRLEN];
regerror(reg_return, regex_comp, errbuf, STRLEN);
regfree(regex_comp);
FREE(regex_comp);
printf("Regex %s parsing error: %s\n", pattern, errbuf);
exit(1);
}
#endif
initprocesstree(&ptree, &ptreesize, &oldptree, &oldptreesize);
if (Run.doprocess) {
int i, count = 0;
printf("List of processes matching pattern \"%s\":\n", pattern);
printf("------------------------------------------\n");
for (i = 0; i < ptreesize; i++) {
int match = FALSE;
if (ptree[i].cmdline && ! strstr(ptree[i].cmdline, "procmatch")) {
#ifdef HAVE_REGEX_H
match = regexec(regex_comp, ptree[i].cmdline, 0, NULL, 0) ? FALSE : TRUE;
#else
match = strstr(ptree[i].cmdline, pattern) ? TRUE : FALSE;
#endif
if (match) {
printf("\t%s\n", ptree[i].cmdline);
count++;
}
}
}
printf("------------------------------------------\n");
printf("Total matches: %d\n", count);
if (count > 1)
printf("WARNING: multiple processes matched the pattern. The check is FIRST-MATCH based, please refine the pattern\n");
}
}