| /* |
| * 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_STDIO_H |
| #include <stdio.h> |
| #endif |
| |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| |
| #ifdef HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.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_UNISTD_H |
| #include <unistd.h> |
| #endif |
| |
| #ifdef TIME_WITH_SYS_TIME |
| #include <time.h> |
| |
| #ifdef HAVE_SYS_TIME_H |
| #include <sys/time.h> |
| #endif |
| #else |
| #include <time.h> |
| #endif |
| |
| #ifdef HAVE_STRING_H |
| #include <string.h> |
| #endif |
| |
| #ifdef HAVE_ASM_PARAM_H |
| #include <asm/param.h> |
| #endif |
| |
| #ifdef HAVE_GLOB_H |
| #include <glob.h> |
| #endif |
| |
| #ifndef HZ |
| # define HZ sysconf(_SC_CLK_TCK) |
| #endif |
| |
| #include "monit.h" |
| #include "process.h" |
| #include "process_sysdep.h" |
| |
| |
| /** |
| * System dependent resource gathering code for Linux. |
| * |
| * @file |
| */ |
| |
| |
| /* ----------------------------------------------------------------- Private */ |
| |
| |
| #define MEMTOTAL "MemTotal:" |
| #define MEMFREE "MemFree:" |
| #define MEMBUF "Buffers:" |
| #define MEMCACHE "Cached:" |
| #define SWAPTOTAL "SwapTotal:" |
| #define SWAPFREE "SwapFree:" |
| |
| #define NSEC_PER_SEC 1000000000L |
| |
| static unsigned long long old_cpu_user = 0; |
| static unsigned long long old_cpu_syst = 0; |
| static unsigned long long old_cpu_wait = 0; |
| static unsigned long long old_cpu_total = 0; |
| static int page_shift_to_kb = 0; |
| |
| |
| /** |
| * Get system start time |
| * @return seconds since unix epoch |
| */ |
| static time_t get_starttime() { |
| char buf[1024]; |
| double up = 0; |
| |
| if (! read_proc_file(buf, 1024, "uptime", -1, NULL)) { |
| LogError("system statistic error -- cannot get system uptime\n"); |
| return 0; |
| } |
| |
| if (sscanf(buf, "%lf", &up) != 1) { |
| LogError("system statistic error -- invalid uptime\n"); |
| return 0; |
| } |
| |
| return time(NULL) - (time_t)up; |
| } |
| |
| |
| /* ------------------------------------------------------------------ Public */ |
| |
| |
| int init_process_info_sysdep(void) { |
| char *ptr; |
| char buf[1024]; |
| long page_size; |
| int page_shift; |
| |
| if (! read_proc_file(buf, sizeof(buf), "meminfo", -1, NULL)) |
| return FALSE; |
| if (! (ptr = strstr(buf, MEMTOTAL))) { |
| DEBUG("system statistic error -- cannot get real memory amount\n"); |
| return FALSE; |
| } |
| if (sscanf(ptr+strlen(MEMTOTAL), "%ld", &systeminfo.mem_kbyte_max) != 1) { |
| DEBUG("system statistic error -- cannot get real memory amount\n"); |
| return FALSE; |
| } |
| |
| if ((systeminfo.cpus = sysconf(_SC_NPROCESSORS_CONF)) < 0) { |
| DEBUG("system statistic error -- cannot get cpu count: %s\n", STRERROR); |
| return FALSE; |
| } else if (systeminfo.cpus == 0) { |
| DEBUG("system reports cpu count 0, setting dummy cpu count 1\n"); |
| systeminfo.cpus = 1; |
| } |
| |
| if ((page_size = sysconf(_SC_PAGESIZE)) <= 0) { |
| DEBUG("system statistic error -- cannot get page size: %s\n", STRERROR); |
| return FALSE; |
| } |
| |
| for (page_shift = 0; page_size != 1; page_size >>= 1, page_shift++); |
| page_shift_to_kb = page_shift - 10; |
| |
| return TRUE; |
| } |
| |
| |
| /** |
| * Read all processes of the proc files system to initialize |
| * the process tree (sysdep version... but should work for |
| * all procfs based unices) |
| * @param reference reference of ProcessTree |
| * @return treesize>0 if succeeded otherwise =0. |
| */ |
| int initprocesstree_sysdep(ProcessTree_T ** reference) { |
| int i = 0, j; |
| int rv, bytes = 0; |
| int treesize = 0; |
| int stat_ppid = 0; |
| char *tmp = NULL; |
| char procname[STRLEN]; |
| char buf[1024]; |
| char stat_item_state; |
| long stat_item_cutime = 0; |
| long stat_item_cstime = 0; |
| long stat_item_rss = 0; |
| glob_t globbuf; |
| unsigned long stat_item_utime = 0; |
| unsigned long stat_item_stime = 0; |
| unsigned long long stat_item_starttime = 0ULL; |
| ProcessTree_T *pt = NULL; |
| |
| ASSERT(reference); |
| |
| /* Find all processes in the /proc directory */ |
| if ((rv = glob("/proc/[0-9]*", GLOB_ONLYDIR, NULL, &globbuf))) { |
| LogError("system statistic error -- glob failed: %d (%s)\n", rv, STRERROR); |
| return FALSE; |
| } |
| |
| treesize = globbuf.gl_pathc; |
| |
| pt = CALLOC(sizeof(ProcessTree_T), treesize); |
| |
| /* Insert data from /proc directory */ |
| for (i = 0; i < treesize; i++) { |
| |
| pt[i].pid = atoi(globbuf.gl_pathv[i] + strlen("/proc/")); |
| |
| if (!read_proc_file(buf, sizeof(buf), "stat", pt[i].pid, NULL)) { |
| DEBUG("system statistic error -- cannot read /proc/%d/stat\n", pt[i].pid); |
| continue; |
| } |
| |
| pt[i].time = get_float_time(); |
| |
| if (!(tmp = strrchr(buf, ')'))) { |
| DEBUG("system statistic error -- file /proc/%d/stat parse error\n", pt[i].pid); |
| continue; |
| } |
| *tmp = 0; |
| if (sscanf(buf, "%*d (%256s", procname) != 1) { |
| DEBUG("system statistic error -- file /proc/%d/stat process name parse error\n", pt[i].pid); |
| continue; |
| } |
| |
| tmp += 2; |
| |
| /* This implementation is done by using fs/procfs/array.c as a basis |
| * it is also worth looking into the source of the procps utils */ |
| if (sscanf(tmp, |
| "%c %d %*d %*d %*d %*d %*u %*u" |
| "%*u %*u %*u %lu %lu %ld %ld %*d %*d %*d " |
| "%*u %llu %*u %ld %*u %*u %*u %*u %*u " |
| "%*u %*u %*u %*u %*u %*u %*u %*u %*d %*d\n", |
| &stat_item_state, |
| &stat_ppid, |
| &stat_item_utime, |
| &stat_item_stime, |
| &stat_item_cutime, |
| &stat_item_cstime, |
| &stat_item_starttime, |
| &stat_item_rss) != 8) { |
| DEBUG("system statistic error -- file /proc/%d/stat parse error\n", pt[i].pid); |
| continue; |
| } |
| |
| pt[i].ppid = stat_ppid; |
| pt[i].starttime = get_starttime() + (time_t)(stat_item_starttime / HZ); |
| |
| /* jiffies -> seconds = 1 / HZ |
| * HZ is defined in "asm/param.h" and it is usually 1/100s but on |
| * alpha system it is 1/1024s */ |
| pt[i].cputime = ((float)(stat_item_utime + stat_item_stime) * 10.0) / HZ; |
| pt[i].cpu_percent = 0; |
| |
| /* State is Zombie -> then we are a Zombie ... clear or? (-: */ |
| if (stat_item_state == 'Z') |
| pt[i].status_flag |= PROCESS_ZOMBIE; |
| |
| if (page_shift_to_kb < 0) |
| pt[i].mem_kbyte = (stat_item_rss >> abs(page_shift_to_kb)); |
| else |
| pt[i].mem_kbyte = (stat_item_rss << abs(page_shift_to_kb)); |
| |
| if (! read_proc_file(buf, sizeof(buf), "cmdline", pt[i].pid, &bytes)) { |
| DEBUG("system statistic error -- cannot read /proc/%d/cmdline\n", pt[i].pid); |
| continue; |
| } |
| /* The cmdline file contains argv elements/strings terminated separated by '\0' => join the string: */ |
| for (j = 0; j < (bytes - 1); j++) |
| if (buf[j] == 0) |
| buf[j] = ' '; |
| pt[i].cmdline = *buf ? Str_dup(buf) : Str_dup(procname); |
| } |
| |
| *reference = pt; |
| globfree(&globbuf); |
| |
| return treesize; |
| } |
| |
| |
| /** |
| * This routine returns 'nelem' double precision floats containing |
| * the load averages in 'loadv'; at most 3 values will be returned. |
| * @param loadv destination of the load averages |
| * @param nelem number of averages |
| * @return: 0 if successful, -1 if failed (and all load averages are 0). |
| */ |
| int getloadavg_sysdep(double *loadv, int nelem) { |
| #ifdef HAVE_GETLOADAVG |
| return getloadavg(loadv, nelem); |
| #else |
| char buf[STRLEN]; |
| double load[3]; |
| if (! read_proc_file(buf, sizeof(buf), "loadavg", -1, NULL)) |
| return -1; |
| if (sscanf(buf, "%lf %lf %lf", &load[0], &load[1], &load[2]) != 3) { |
| DEBUG("system statistic error -- cannot get load average\n"); |
| return -1; |
| } |
| for (int i = 0; i < nelem; i++) |
| loadv[i] = load[i]; |
| return 0; |
| #endif |
| } |
| |
| |
| /** |
| * This routine returns kbyte of real memory in use. |
| * @return: TRUE if successful, FALSE if failed |
| */ |
| int used_system_memory_sysdep(SystemInfo_T *si) { |
| char *ptr; |
| char buf[1024]; |
| unsigned long mem_free = 0UL; |
| unsigned long buffers = 0UL; |
| unsigned long cached = 0UL; |
| unsigned long swap_total = 0UL; |
| unsigned long swap_free = 0UL; |
| |
| if (! read_proc_file(buf, 1024, "meminfo", -1, NULL)) { |
| LogError("system statistic error -- cannot get real memory free amount\n"); |
| goto error; |
| } |
| |
| /* Memory */ |
| if (! (ptr = strstr(buf, MEMFREE)) || sscanf(ptr + strlen(MEMFREE), "%ld", &mem_free) != 1) { |
| LogError("system statistic error -- cannot get real memory free amount\n"); |
| goto error; |
| } |
| if (! (ptr = strstr(buf, MEMBUF)) || sscanf(ptr + strlen(MEMBUF), "%ld", &buffers) != 1) |
| DEBUG("system statistic error -- cannot get real memory buffers amount\n"); |
| if (! (ptr = strstr(buf, MEMCACHE)) || sscanf(ptr + strlen(MEMCACHE), "%ld", &cached) != 1) |
| DEBUG("system statistic error -- cannot get real memory cache amount\n"); |
| si->total_mem_kbyte = systeminfo.mem_kbyte_max - mem_free - buffers - cached; |
| |
| /* Swap */ |
| if (! (ptr = strstr(buf, SWAPTOTAL)) || sscanf(ptr + strlen(SWAPTOTAL), "%ld", &swap_total) != 1) { |
| LogError("system statistic error -- cannot get swap total amount\n"); |
| goto error; |
| } |
| if (! (ptr = strstr(buf, SWAPFREE)) || sscanf(ptr + strlen(SWAPFREE), "%ld", &swap_free) != 1) { |
| LogError("system statistic error -- cannot get swap free amount\n"); |
| goto error; |
| } |
| si->swap_kbyte_max = swap_total; |
| si->total_swap_kbyte = swap_total - swap_free; |
| |
| return TRUE; |
| |
| error: |
| si->total_mem_kbyte = 0; |
| si->swap_kbyte_max = 0; |
| return FALSE; |
| } |
| |
| |
| /** |
| * This routine returns system/user CPU time in use. |
| * @return: TRUE if successful, FALSE if failed (or not available) |
| */ |
| int used_system_cpu_sysdep(SystemInfo_T *si) { |
| int rv; |
| unsigned long long cpu_total; |
| unsigned long long cpu_user; |
| unsigned long long cpu_nice; |
| unsigned long long cpu_syst; |
| unsigned long long cpu_idle; |
| unsigned long long cpu_wait; |
| unsigned long long cpu_irq; |
| unsigned long long cpu_softirq; |
| char buf[1024]; |
| |
| if (!read_proc_file(buf, 1024, "stat", -1, NULL)) { |
| LogError("system statistic error -- cannot read /proc/stat\n"); |
| goto error; |
| } |
| |
| rv = sscanf(buf, "cpu %llu %llu %llu %llu %llu %llu %llu", |
| &cpu_user, |
| &cpu_nice, |
| &cpu_syst, |
| &cpu_idle, |
| &cpu_wait, |
| &cpu_irq, |
| &cpu_softirq); |
| if (rv < 4) { |
| LogError("system statistic error -- cannot read cpu usage\n"); |
| goto error; |
| } else if (rv == 4) { |
| /* linux 2.4.x doesn't support these values */ |
| cpu_wait = 0; |
| cpu_irq = 0; |
| cpu_softirq = 0; |
| } |
| |
| cpu_total = cpu_user + cpu_nice + cpu_syst + cpu_idle + cpu_wait + cpu_irq + cpu_softirq; |
| cpu_user = cpu_user + cpu_nice; |
| |
| if (old_cpu_total == 0) { |
| si->total_cpu_user_percent = -10; |
| si->total_cpu_syst_percent = -10; |
| si->total_cpu_wait_percent = -10; |
| } else { |
| unsigned long long delta = cpu_total - old_cpu_total; |
| |
| si->total_cpu_user_percent = (int)(1000 * (double)(cpu_user - old_cpu_user) / delta); |
| si->total_cpu_syst_percent = (int)(1000 * (double)(cpu_syst - old_cpu_syst) / delta); |
| si->total_cpu_wait_percent = (int)(1000 * (double)(cpu_wait - old_cpu_wait) / delta); |
| } |
| |
| old_cpu_user = cpu_user; |
| old_cpu_syst = cpu_syst; |
| old_cpu_wait = cpu_wait; |
| old_cpu_total = cpu_total; |
| return TRUE; |
| |
| error: |
| si->total_cpu_user_percent = 0; |
| si->total_cpu_syst_percent = 0; |
| si->total_cpu_wait_percent = 0; |
| return FALSE; |
| } |
| |
| |