| /* |
| * 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 TIME_WITH_SYS_TIME |
| #include <time.h> |
| |
| #ifdef HAVE_SYS_TIME_H |
| #include <sys/time.h> |
| #endif |
| #else |
| #include <time.h> |
| #endif |
| |
| #ifdef HAVE_SYS_LOADAVG_H |
| #include <sys/loadavg.h> |
| #endif |
| |
| #ifdef HAVE_STRING_H |
| #include <string.h> |
| #endif |
| |
| #ifdef HAVE_PROCFS_H |
| #include <procfs.h> |
| #endif |
| |
| #ifdef HAVE_GLOB_H |
| #include <glob.h> |
| #endif |
| |
| #ifdef HAVE_KSTAT_H |
| #include <kstat.h> |
| #endif |
| |
| #ifdef HAVE_SYS_SWAP_H |
| #define _SYS_VNODE_H |
| #include <sys/swap.h> |
| #endif |
| |
| #ifdef HAVE_SYS_SYSINFO_H |
| #include <sys/sysinfo.h> |
| #endif |
| |
| #include "monit.h" |
| #include "process.h" |
| #include "process_sysdep.h" |
| |
| /** |
| * System dependent resource gathering code for Solaris. |
| * |
| * @file |
| */ |
| |
| #define pagetok(size) ((size) << pageshift) |
| |
| static int page_size; |
| static int pageshift=0; |
| static long old_cpu_user=0; |
| static long old_cpu_syst=0; |
| static long old_cpu_wait=0; |
| static long old_total=0; |
| |
| #define MAXSTRSIZE 80 |
| |
| #ifndef LOG1024 |
| #define LOG1024 10 |
| #endif |
| |
| int init_process_info_sysdep(void) { |
| register int pagesize; |
| |
| systeminfo.cpus = sysconf( _SC_NPROCESSORS_ONLN); |
| |
| pagesize = sysconf(_SC_PAGESIZE); |
| pageshift = 0; |
| while (pagesize > 1) { |
| pageshift++; |
| pagesize >>= 1; |
| } |
| |
| /* we only need the amount of log(2)1024 for our conversion */ |
| pageshift -= LOG1024; |
| |
| systeminfo.mem_kbyte_max = pagetok(sysconf(_SC_PHYS_PAGES)); |
| page_size = getpagesize(); |
| |
| return (TRUE); |
| } |
| |
| double timestruc_to_tseconds(timestruc_t t) { |
| return t.tv_sec * 10 + t.tv_nsec / 100000000.0; |
| } |
| |
| |
| /** |
| * 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; |
| int rv; |
| int pid; |
| int treesize; |
| char buf[4096]; |
| glob_t globbuf; |
| pstatus_t pstatus; |
| psinfo_t *psinfo = (psinfo_t *)&buf; |
| ProcessTree_T *pt; |
| |
| ASSERT(reference); |
| |
| /* Find all processes in the /proc directory */ |
| if ((rv = glob("/proc/[0-9]*", NULL, NULL, &globbuf)) != 0) { |
| LogError("system statistic error -- glob failed: %d (%s)\n", rv, STRERROR); |
| return 0; |
| } |
| |
| treesize = globbuf.gl_pathc; |
| |
| /* Allocate the tree */ |
| pt = CALLOC(sizeof(ProcessTree_T), treesize); |
| |
| /* Insert data from /proc directory */ |
| for (i = 0; i < treesize; i++) { |
| pid = atoi(globbuf.gl_pathv[i] + strlen("/proc/")); |
| pt[i].pid = pid; |
| |
| /* get the actual time */ |
| pt[i].time = get_float_time(); |
| |
| if (! read_proc_file(buf, sizeof(buf), "psinfo", pt[i].pid, NULL)) { |
| pt[i].cputime = 0; |
| pt[i].cpu_percent = 0; |
| pt[i].mem_kbyte = 0; |
| continue; |
| } |
| |
| pt[i].ppid = psinfo->pr_ppid; |
| pt[i].starttime = psinfo->pr_start.tv_sec; |
| |
| /* If we don't have any light-weight processes (LWP) then we are definitely a zombie */ |
| if (psinfo->pr_nlwp == 0) { |
| pt[i].status_flag = PROCESS_ZOMBIE; |
| pt[i].cputime = 0; |
| pt[i].cpu_percent = 0; |
| pt[i].mem_kbyte = 0; |
| continue; |
| } |
| |
| pt[i].mem_kbyte = psinfo->pr_rssize; |
| |
| pt[i].cmdline = Str_dup(psinfo->pr_psargs); |
| if (! pt[i].cmdline || ! *pt[i].cmdline) |
| pt[i].cmdline = Str_dup(psinfo->pr_fname); |
| |
| if (! read_proc_file(buf, sizeof(buf), "status", pt[i].pid, NULL)) { |
| pt[i].cputime = 0; |
| pt[i].cpu_percent = 0; |
| } else { |
| memcpy(&pstatus, buf, sizeof(pstatus_t)); |
| pt[i].cputime = (timestruc_to_tseconds(pstatus.pr_utime) + timestruc_to_tseconds(pstatus.pr_stime)); |
| pt[i].cpu_percent = 0; |
| } |
| } |
| |
| *reference = pt; |
| |
| /* Free globbing buffer */ |
| 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 (or not available) |
| */ |
| int used_system_memory_sysdep(SystemInfo_T *si) { |
| int i, n, num; |
| kstat_ctl_t *kctl; |
| kstat_named_t *knamed; |
| kstat_t *kstat; |
| swaptbl_t *s; |
| char *strtab; |
| unsigned long long total = 0ULL; |
| unsigned long long used = 0ULL; |
| |
| /* Memory */ |
| kctl = kstat_open(); |
| kstat = kstat_lookup(kctl, "unix", 0, "system_pages"); |
| if (kstat_read(kctl, kstat, 0) == -1) { |
| LogError("system statistic error -- memory usage gathering failed\n"); |
| kstat_close(kctl); |
| return FALSE; |
| } |
| knamed = kstat_data_lookup(kstat, "freemem"); |
| if (knamed) |
| si->total_mem_kbyte = systeminfo.mem_kbyte_max-pagetok(knamed->value.ul); |
| kstat_close(kctl); |
| |
| /* Swap */ |
| again: |
| if ((num = swapctl(SC_GETNSWP, 0)) == -1) { |
| LogError("system statistic error -- swap usage gathering failed: %s\n", STRERROR); |
| return FALSE; |
| } |
| if (num == 0) { |
| DEBUG("system statistic -- no swap configured\n"); |
| si->swap_kbyte_max = 0; |
| return TRUE; |
| } |
| s = (swaptbl_t *)ALLOC(num * sizeof(swapent_t) + sizeof(struct swaptable)); |
| strtab = (char *)ALLOC((num + 1) * MAXSTRSIZE); |
| for (i = 0; i < (num + 1); i++) |
| s->swt_ent[i].ste_path = strtab + (i * MAXSTRSIZE); |
| s->swt_n = num + 1; |
| if ((n = swapctl(SC_LIST, s)) < 0) { |
| LogError("system statistic error -- swap usage gathering failed: %s\n", STRERROR); |
| si->swap_kbyte_max = 0; |
| FREE(s); |
| FREE(strtab); |
| return FALSE; |
| } |
| if (n > num) { |
| DEBUG("system statistic -- new swap added: deferring swap usage statistics to next cycle\n"); |
| FREE(s); |
| FREE(strtab); |
| goto again; |
| } |
| for (i = 0; i < n; i++) { |
| if (!(s->swt_ent[i].ste_flags & ST_INDEL) && !(s->swt_ent[i].ste_flags & ST_DOINGDEL)) { |
| total += s->swt_ent[i].ste_pages; |
| used += s->swt_ent[i].ste_pages - s->swt_ent[i].ste_free; |
| } |
| } |
| FREE(s); |
| FREE(strtab); |
| si->swap_kbyte_max = (unsigned long)(double)(total * page_size) / 1024.; |
| si->total_swap_kbyte = (unsigned long)(double)(used * page_size) / 1024.; |
| |
| return TRUE; |
| } |
| |
| |
| /** |
| * 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 i, ncpu = 0, ncpus; |
| long cpu_user = 0, cpu_syst = 0, cpu_wait = 0, total = 0, diff_total; |
| kstat_ctl_t *kctl; |
| kstat_named_t *knamed; |
| kstat_t *kstat; |
| kstat_t **cpu_ks; |
| cpu_stat_t *cpu_stat; |
| |
| si->total_cpu_user_percent = si->total_cpu_syst_percent = si->total_cpu_wait_percent = 0; |
| |
| kctl = kstat_open(); |
| kstat = kstat_lookup(kctl, "unix", 0, "system_misc"); |
| if (kstat_read(kctl, kstat, 0) == -1) { |
| LogError("system statistic -- failed to lookup unix::system_misc kstat\n"); |
| goto error; |
| } |
| |
| if (NULL == (knamed = kstat_data_lookup(kstat, "ncpus"))) { |
| LogError("system statistic -- ncpus kstat lookup failed\n"); |
| goto error; |
| } |
| |
| if ((ncpus = knamed->value.ui32) == 0) { |
| LogError("system statistic -- ncpus is 0\n"); |
| goto error; |
| } |
| |
| cpu_ks = (kstat_t **)ALLOC(ncpus * sizeof(kstat_t *)); |
| cpu_stat = (cpu_stat_t *)ALLOC(ncpus * sizeof(cpu_stat_t)); |
| |
| for (kstat = kctl->kc_chain; kstat; kstat = kstat->ks_next) { |
| if (strncmp(kstat->ks_name, "cpu_stat", 8) == 0) { |
| if (-1 == kstat_read(kctl, kstat, NULL)) { |
| LogError("system statistic -- failed to read cpu_stat kstat\n"); |
| goto error2; |
| } |
| cpu_ks[ncpu] = kstat; |
| if (++ncpu > ncpus) { |
| LogError("system statistic -- cpu count mismatch\n"); |
| goto error2; |
| } |
| } |
| } |
| |
| for (i = 0; i < ncpu; i++) { |
| if (-1 == kstat_read(kctl, cpu_ks[i], &cpu_stat[i])) { |
| LogError("system statistic -- failed to read cpu_stat kstat for cpu %d\n", i); |
| goto error2; |
| } |
| cpu_user += cpu_stat[i].cpu_sysinfo.cpu[CPU_USER]; |
| cpu_syst += cpu_stat[i].cpu_sysinfo.cpu[CPU_KERNEL]; |
| cpu_wait += cpu_stat[i].cpu_sysinfo.cpu[CPU_WAIT]; |
| total += (cpu_stat[i].cpu_sysinfo.cpu[0] + cpu_stat[i].cpu_sysinfo.cpu[1] + cpu_stat[i].cpu_sysinfo.cpu[2] + cpu_stat[i].cpu_sysinfo.cpu[3]); |
| } |
| |
| if (old_total == 0) { |
| si->total_cpu_user_percent = si->total_cpu_syst_percent = si->total_cpu_wait_percent = -10; |
| } else if ((diff_total = total - old_total) > 0) { |
| si->total_cpu_user_percent = (int)((1000 * (cpu_user - old_cpu_user)) / diff_total); |
| si->total_cpu_syst_percent = (int)((1000 * (cpu_syst - old_cpu_syst)) / diff_total); |
| si->total_cpu_wait_percent = (int)((1000 * (cpu_wait - old_cpu_wait)) / diff_total); |
| } |
| |
| old_cpu_user = cpu_user; |
| old_cpu_syst = cpu_syst; |
| old_cpu_wait = cpu_wait; |
| old_total = total; |
| |
| FREE(cpu_ks); |
| FREE(cpu_stat); |
| kstat_close(kctl); |
| return TRUE; |
| |
| error2: |
| old_total = 0; |
| FREE(cpu_ks); |
| FREE(cpu_stat); |
| |
| error: |
| kstat_close(kctl); |
| return FALSE; |
| } |
| |