| /* |
| * 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"); |
| } |
| } |
| |
| |