1 /*
2  * Copyright (C) Tildeslash Ltd. All rights reserved.
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Affero General Public License version 3.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU Affero General Public License
13  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
14  *
15  * In addition, as a special exception, the copyright holders give
16  * permission to link the code of portions of this program with the
17  * OpenSSL library under certain conditions as described in each
18  * individual source file, and distribute linked combinations
19  * including the two.
20  *
21  * You must obey the GNU Affero General Public License in all respects
22  * for all of the code used other than OpenSSL.
23  */
24 
25 
26 #include "config.h"
27 
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31 
32 #ifdef HAVE_ERRNO_H
33 #include <errno.h>
34 #endif
35 
36 #ifdef HAVE_FCNTL_H
37 #include <fcntl.h>
38 #endif
39 
40 #ifdef HAVE_SYS_PARAM_H
41 #include <sys/param.h>
42 #endif
43 
44 #ifdef HAVE_SYS_PROC_H
45 #include <sys/proc.h>
46 #endif
47 
48 #ifdef HAVE_KVM_H
49 #include <kvm.h>
50 #endif
51 
52 #ifdef HAVE_UVM_UVM_H
53 #include <uvm/uvm.h>
54 #endif
55 
56 #ifdef HAVE_UVM_UVM_EXTERN_H
57 #include <uvm/uvm_extern.h>
58 #endif
59 
60 #ifdef HAVE_SYS_SYSCTL_H
61 #include <sys/sysctl.h>
62 #endif
63 
64 #ifdef HAVE_SYS_DKSTAT_H
65 #include <sys/dkstat.h>
66 #endif
67 
68 #include "monit.h"
69 #include "ProcessTree.h"
70 #include "process_sysdep.h"
71 
72 // libmonit
73 #include "system/Time.h"
74 
75 /**
76  *  System dependent resource data collecting code for NetBSD.
77  *
78  *  @file
79  */
80 
81 
82 /* ----------------------------------------------------------------- Private */
83 
84 
85 static int      pagesize;
86 static long     total_old    = 0;
87 static long     cpu_user_old = 0;
88 static long     cpu_nice_old = 0;
89 static long     cpu_syst_old = 0;
90 static long     cpu_intr_old = 0;
91 static unsigned int maxslp;
92 
93 
94 /* ------------------------------------------------------------------ Public */
95 
96 
init_process_info_sysdep(void)97 bool init_process_info_sysdep(void) {
98         int mib[2] = {CTL_HW, HW_NCPU};
99         size_t len = sizeof(systeminfo.cpu.count);
100         if (sysctl(mib, 2, &systeminfo.cpu.count, &len, NULL, 0) == -1) {
101                 DEBUG("system statistic error -- cannot get cpu count: %s\n", STRERROR);
102                 return false;
103         }
104 
105         mib[1] = HW_PHYSMEM;
106         len    = sizeof(systeminfo.memory.size);
107         if (sysctl(mib, 2, &systeminfo.memory.size, &len, NULL, 0) == -1) {
108                 DEBUG("system statistic error -- cannot get real memory amount: %s\n", STRERROR);
109                 return false;
110         }
111 
112         mib[1] = HW_PAGESIZE;
113         len    = sizeof(pagesize);
114         if (sysctl(mib, 2, &pagesize, &len, NULL, 0) == -1) {
115                 DEBUG("system statistic error -- cannot get memory page size: %s\n", STRERROR);
116                 return false;
117         }
118 
119         struct timeval booted;
120         mib[0] = CTL_KERN;
121         mib[1] = KERN_BOOTTIME;
122         len = sizeof(booted);
123         if (sysctl(mib, 2, &booted, &len, NULL, 0) == -1) {
124                 DEBUG("system statistics error -- sysctl kern.boottime failed: %s\n", STRERROR);
125                 return false;
126         } else {
127                 systeminfo.booted = booted.tv_sec;
128         }
129 
130         return true;
131 }
132 
133 
134 /**
135  * Read all processes to initialize the information tree.
136  * @param reference reference of ProcessTree
137  * @param pflags Process engine flags
138  * @return treesize > 0 if succeeded otherwise 0
139  */
initprocesstree_sysdep(ProcessTree_T ** reference,ProcessEngine_Flags pflags)140 int initprocesstree_sysdep(ProcessTree_T **reference, ProcessEngine_Flags pflags) {
141         size_t size = sizeof(maxslp);
142         static int mib_maxslp[] = {CTL_VM, VM_MAXSLP};
143         if (sysctl(mib_maxslp, 2, &maxslp, &size, NULL, 0) < 0) {
144                 Log_error("system statistic error -- vm.maxslp failed\n");
145                 return 0;
146         }
147 
148         int mib_proc2[6] = {CTL_KERN, KERN_PROC2, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), 0};
149         if (sysctl(mib_proc2, 6, NULL, &size, NULL, 0) == -1) {
150                 Log_error("system statistic error -- kern.proc2 #1 failed\n");
151                 return 0;
152         }
153 
154         size *= 2; // Add reserve for new processes which were created between calls of sysctl
155         struct kinfo_proc2 *pinfo = CALLOC(1, size);
156         mib_proc2[5] = (int)(size / sizeof(struct kinfo_proc2));
157         if (sysctl(mib_proc2, 6, pinfo, &size, NULL, 0) == -1) {
158                 FREE(pinfo);
159                 Log_error("system statistic error -- kern.proc2 #2 failed\n");
160                 return 0;
161         }
162 
163         int treesize = (int)(size / sizeof(struct kinfo_proc2));
164 
165         ProcessTree_T *pt = CALLOC(sizeof(ProcessTree_T), treesize);
166 
167         char buf[_POSIX2_LINE_MAX];
168         kvm_t *kvm_handle = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
169         if (! kvm_handle) {
170                 FREE(pinfo);
171                 FREE(pt);
172                 Log_error("system statistic error -- kvm_openfiles failed: %s\n", buf);
173                 return 0;
174         }
175 
176         unsigned long long now = Time_milli();
177         StringBuffer_T cmdline = NULL;
178         if (pflags & ProcessEngine_CollectCommandLine)
179                 cmdline = StringBuffer_create(64);
180         for (int i = 0; i < treesize; i++) {
181                 pt[i].pid                 = pinfo[i].p_pid;
182                 pt[i].ppid                = pinfo[i].p_ppid;
183                 pt[i].cred.uid            = pinfo[i].p_ruid;
184                 pt[i].cred.euid           = pinfo[i].p_uid;
185                 pt[i].cred.gid            = pinfo[i].p_rgid;
186                 pt[i].threads.self        = pinfo[i].p_nlwps;
187                 pt[i].uptime              = systeminfo.time / 10. - pinfo[i].p_ustart_sec;
188                 pt[i].cpu.time            = pinfo[i].p_rtime_sec * 10 + (double)pinfo[i].p_rtime_usec / 100000.;
189                 pt[i].memory.usage        = (unsigned long long)pinfo[i].p_vm_rssize * (unsigned long long)pagesize;
190                 pt[i].zombie              = pinfo[i].p_stat == SZOMB ? true : false;
191                 pt[i].read.bytes          = -1;
192                 pt[i].read.bytesPhysical  = -1;
193                 pt[i].read.operations     = pinfo[i].p_uru_inblock;
194                 pt[i].read.time           = now;
195                 pt[i].write.bytes         = -1;
196                 pt[i].write.bytesPhysical = -1;
197                 pt[i].write.operations    = pinfo[i].p_uru_oublock;
198                 pt[i].write.time          = now;
199                 if (pflags & ProcessEngine_CollectCommandLine) {
200                         char **args = kvm_getargv2(kvm_handle, &pinfo[i], 0);
201                         if (args) {
202                                 StringBuffer_clear(cmdline);
203                                 for (int j = 0; args[j]; j++)
204                                         StringBuffer_append(cmdline, args[j + 1] ? "%s " : "%s", args[j]);
205                                 if (StringBuffer_length(cmdline))
206                                         pt[i].cmdline = Str_dup(StringBuffer_toString(StringBuffer_trim(cmdline)));
207                         }
208                         if (STR_UNDEF(pt[i].cmdline)) {
209                                 FREE(pt[i].cmdline);
210                                 pt[i].cmdline = Str_dup(pinfo[i].p_comm);
211                         }
212                 }
213         }
214         if (pflags & ProcessEngine_CollectCommandLine)
215                 StringBuffer_free(&cmdline);
216         FREE(pinfo);
217         kvm_close(kvm_handle);
218 
219         *reference = pt;
220 
221         return treesize;
222 }
223 
224 
225 /**
226  * This routine returns 'nelem' double precision floats containing
227  * the load averages in 'loadv'; at most 3 values will be returned.
228  * @param loadv destination of the load averages
229  * @param nelem number of averages
230  * @return: 0 if successful, -1 if failed (and all load averages are 0).
231  */
getloadavg_sysdep(double * loadv,int nelem)232 int getloadavg_sysdep (double *loadv, int nelem) {
233         return getloadavg(loadv, nelem);
234 }
235 
236 
237 /**
238  * This routine returns kbyte of real memory in use.
239  * @return: true if successful, false if failed (or not available)
240  */
used_system_memory_sysdep(SystemInfo_T * si)241 bool used_system_memory_sysdep(SystemInfo_T *si) {
242         struct uvmexp_sysctl vm;
243         int mib[2] = {CTL_VM, VM_UVMEXP2};
244         size_t len = sizeof(struct uvmexp_sysctl);
245         if (sysctl(mib, 2, &vm, &len, NULL, 0) == -1) {
246                 Log_error("system statistic error -- cannot get memory usage: %s\n", STRERROR);
247                 si->swap.size = 0ULL;
248                 return false;
249         }
250         si->memory.usage.bytes = (unsigned long long)(vm.active + vm.wired) * (unsigned long long)vm.pagesize;
251         si->swap.size = (unsigned long long)vm.swpages * (unsigned long long)vm.pagesize;
252         si->swap.usage.bytes = (unsigned long long)vm.swpginuse * (unsigned long long)vm.pagesize;
253         return true;
254 }
255 
256 
257 /**
258  * This routine returns system/user CPU time in use.
259  * @return: true if successful, false if failed
260  */
used_system_cpu_sysdep(SystemInfo_T * si)261 bool used_system_cpu_sysdep(SystemInfo_T *si) {
262         int       mib[] = {CTL_KERN, KERN_CP_TIME};
263         long long cp_time[CPUSTATES];
264         long      total_new = 0;
265         long      total;
266         size_t    len;
267 
268         len = sizeof(cp_time);
269         if (sysctl(mib, 2, &cp_time, &len, NULL, 0) == -1) {
270                 Log_error("system statistic error -- cannot get cpu time: %s\n", STRERROR);
271                 return false;
272         }
273 
274         for (int i = 0; i < CPUSTATES; i++)
275                 total_new += cp_time[i];
276         total     = total_new - total_old;
277         total_old = total_new;
278 
279         si->cpu.usage.user = (total > 0) ? (100. * (double)(cp_time[CP_USER] - cpu_user_old) / total) : -1.;
280         si->cpu.usage.nice = (total > 0) ? (100. * (double)(cp_time[CP_NICE] - cpu_nice_old) / total) : -1.;
281         si->cpu.usage.system = (total > 0) ? (100. * (double)(cp_time[CP_SYS] - cpu_syst_old) / total) : -1.;
282         si->cpu.usage.hardirq = (total > 0) ? (100. * (double)(cp_time[CP_INTR] - cpu_intr_old) / total) : -1.;
283 
284         cpu_user_old = cp_time[CP_USER];
285         cpu_nice_old = cp_time[CP_NICE];
286         cpu_syst_old = cp_time[CP_SYS];
287         cpu_intr_old = cp_time[CP_INTR];
288 
289         return true;
290 }
291 
292 
used_system_filedescriptors_sysdep(SystemInfo_T * si)293 bool used_system_filedescriptors_sysdep(__attribute__ ((unused)) SystemInfo_T *si) {
294         // Not implemented
295         return true;
296 }
297 
298 
available_statistics(SystemInfo_T * si)299 bool available_statistics(SystemInfo_T *si) {
300         si->statisticsAvailable = Statistics_CpuUser | Statistics_CpuSystem | Statistics_CpuNice | Statistics_CpuHardIRQ;
301         return true;
302 }
303 
304