/* * Copyright (C), 2000-2007 by the monit project group. * All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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 General Public License * along with this program. If not, see . */ #include #ifdef HAVE_STDIO_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef TIME_WITH_SYS_TIME #include #ifdef HAVE_SYS_TIME_H #include #endif #else #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_ASM_PARAM_H #include #endif #ifdef HAVE_GLOB_H #include #endif #ifndef HZ # define HZ sysconf(_SC_CLK_TCK) #endif #include "monitor.h" #include "process.h" #include "process_sysdep.h" /** * System dependent resource gathering code for Linux. * * @author Jan-Henrik Haukeland, * @author Christian Hopp * @author Martin Pala * @author Arkadiusz Miskiewicz * * @version \$Id: sysdep_LINUX.c,v 1.44 2007/07/25 12:54:32 hauk Exp $ * * @file */ /* ----------------------------------------------------------------- Private */ #define MEMTOTAL "MemTotal:" #define MEMFREE "MemFree:" #define MEMBUF "Buffers:" #define MEMCACHE "Cached:" 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; /* ------------------------------------------------------------------ Public */ int init_process_info_sysdep(void) { char *ptr; char buf[1024]; long page_size; int page_shift; if(!read_proc_file(buf, 1024, "meminfo", -1)) { 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((num_cpus = sysconf(_SC_NPROCESSORS_CONF)) == -1) { DEBUG("system statistic error -- cannot get cpu count: %s\n", STRERROR); return FALSE; } 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; int treesize = 0; int stat_ppid = 0; char *tmp = NULL; char buf[4096]; 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; ProcessTree_T *pt = NULL; ASSERT(reference); /* Find all processes in the /proc directory */ if(glob("/proc/[0-9]*", GLOB_ONLYDIR, NULL, &globbuf)) { LogError("system statistic error -- glob failed\n"); return FALSE; } treesize = globbuf.gl_pathc; pt = xcalloc(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, 4096, "stat", pt[i].pid)) { DEBUG( "system statistic error -- cannot read /proc/%d/stat\n", pt[i].pid); continue; } pt[i].time = get_float_time(); /* Move along the buffer to get past the process name */ if(!(tmp = strrchr(buf, ')'))) { DEBUG( "system statistic error -- file /proc/%d/stat 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 " "%*d %*u %*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_rss) != 7) { DEBUG( "system statistic error -- file /proc/%d/stat parse error\n", pt[i].pid); continue; } /* abs to please the compiler... we dont want to shift negatively. * why doesn't C understand this??? */ pt[i].ppid = stat_ppid; /* 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; /* 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)); } } *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) { return getloadavg(loadv, nelem); } /** * 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; unsigned long buffers; unsigned long cached; if(!read_proc_file(buf, 1024, "meminfo", -1)) { LogError("system statistic error -- cannot get real memory free amount\n"); goto error; } if(!(ptr= strstr(buf, MEMFREE))) { LogError("system statistic error -- cannot get real memory free amount\n"); goto error; } if(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))) { LogError("system statistic error -- cannot get real memory buffers " "amount\n"); goto error; } if(sscanf(ptr + strlen(MEMBUF), "%ld", &buffers) != 1) { LogError("system statistic error -- cannot get real memory buffers " "amount\n"); goto error; } if(!(ptr= strstr(buf, MEMCACHE))) { LogError("system statistic error -- cannot get real memory cache amount\n"); goto error; } if(sscanf(ptr + strlen(MEMCACHE), "%ld", &cached) != 1) { LogError("system statistic error -- cannot get real memory cache free " "amount\n"); goto error; } si->total_mem_kbyte = systeminfo.mem_kbyte_max - mem_free - buffers - cached; if(si->total_mem_kbyte < 0) { LogError("system statistic error -- memory usage statistic error\n"); goto error; } return TRUE; error: LogError("system statistic error -- memory usage gathering failed\n"); si->total_mem_kbyte = 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)) { 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 = 0; si->total_cpu_syst_percent = 0; si->total_cpu_wait_percent = 0; } 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; }