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