blob: 6957f485ed8a4f275e2224b6955e35ab4202e913 [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_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;
}