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_ERRNO_H
29 #include <errno.h>
30 #endif
31
32 #ifdef HAVE_STRING_H
33 #include <string.h>
34 #endif
35
36 #ifdef HAVE_FCNTL_H
37 #include <fcntl.h>
38 #endif
39
40 #ifdef HAVE_SYS_SYSCTL_H
41 #include <sys/sysctl.h>
42 #endif
43
44 #ifdef HAVE_MACH_MACH_H
45 #include <mach/mach.h>
46 #endif
47
48 #ifdef HAVE_LIBPROC_H
49 #include <libproc.h>
50 #endif
51
52 #include "monit.h"
53 #include "ProcessTree.h"
54 #include "process_sysdep.h"
55
56 // libmonit
57 #include "system/Time.h"
58
59
60 /**
61 * System dependent resource data collecting code for MacOS X.
62 *
63 * @file
64 */
65
66
67 #define ARGSSIZE 8192
68
69
70 /* ----------------------------------------------------------------- Private */
71
72
73 static int pagesize;
74 static long total_old = 0;
75 static long cpu_user_old = 0;
76 static long cpu_nice_old = 0;
77 static long cpu_syst_old = 0;
78
79
80 /* ------------------------------------------------------------------ Public */
81
82
init_process_info_sysdep(void)83 bool init_process_info_sysdep(void) {
84 size_t size = sizeof(systeminfo.cpu.count);
85 if (sysctlbyname("hw.logicalcpu", &systeminfo.cpu.count, &size, NULL, 0) == -1) {
86 DEBUG("system statistics error -- sysctl hw.logicalcpu failed: %s\n", STRERROR);
87 return false;
88 }
89
90 size = sizeof(systeminfo.memory.size);
91 if (sysctlbyname("hw.memsize", &systeminfo.memory.size, &size, NULL, 0) == -1) {
92 DEBUG("system statistics error -- sysctl hw.memsize failed: %s\n", STRERROR);
93 return false;
94 }
95
96 size = sizeof(pagesize);
97 if (sysctlbyname("hw.pagesize", &pagesize, &size, NULL, 0) == -1) {
98 DEBUG("system statistics error -- sysctl hw.pagesize failed: %s\n", STRERROR);
99 return false;
100 }
101
102 size = sizeof(systeminfo.argmax);
103 if (sysctlbyname("kern.argmax", &systeminfo.argmax, &size, NULL, 0) == -1) {
104 DEBUG("system statistics error -- sysctl kern.argmax failed: %s\n", STRERROR);
105 return false;
106 }
107
108 struct timeval booted;
109 size = sizeof(booted);
110 if (sysctlbyname("kern.boottime", &booted, &size, NULL, 0) == -1) {
111 DEBUG("system statistics error -- sysctl kern.boottime failed: %s\n", STRERROR);
112 return false;
113 } else {
114 systeminfo.booted = booted.tv_sec;
115 }
116
117 return true;
118 }
119
120
121 /**
122 * Read all processes to initialize the information tree.
123 * @param reference reference of ProcessTree
124 * @param pflags Process engine flags
125 * @return treesize > 0 if succeeded otherwise 0
126 */
initprocesstree_sysdep(ProcessTree_T ** reference,ProcessEngine_Flags pflags)127 int initprocesstree_sysdep(ProcessTree_T **reference, ProcessEngine_Flags pflags) {
128 size_t pinfo_size = 0;
129 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
130 if (sysctl(mib, 4, NULL, &pinfo_size, NULL, 0) < 0) {
131 Log_error("system statistic error -- sysctl failed: %s\n", STRERROR);
132 return 0;
133 }
134 struct kinfo_proc *pinfo = CALLOC(1, pinfo_size);
135 if (sysctl(mib, 4, pinfo, &pinfo_size, NULL, 0)) {
136 FREE(pinfo);
137 Log_error("system statistic error -- sysctl failed: %s\n", STRERROR);
138 return 0;
139 }
140 size_t treesize = pinfo_size / sizeof(struct kinfo_proc);
141 ProcessTree_T *pt = CALLOC(sizeof(ProcessTree_T), treesize);
142
143 char *args = NULL;
144 StringBuffer_T cmdline = NULL;
145 if (pflags & ProcessEngine_CollectCommandLine) {
146 cmdline = StringBuffer_create(64);
147 args = CALLOC(1, systeminfo.argmax + 1);
148 }
149 for (size_t i = 0; i < treesize; i++) {
150 pt[i].uptime = systeminfo.time / 10. - pinfo[i].kp_proc.p_starttime.tv_sec;
151 pt[i].zombie = pinfo[i].kp_proc.p_stat == SZOMB ? true : false;
152 pt[i].pid = pinfo[i].kp_proc.p_pid;
153 pt[i].ppid = pinfo[i].kp_eproc.e_ppid;
154 pt[i].cred.uid = pinfo[i].kp_eproc.e_pcred.p_ruid;
155 pt[i].cred.euid = pinfo[i].kp_eproc.e_ucred.cr_uid;
156 pt[i].cred.gid = pinfo[i].kp_eproc.e_pcred.p_rgid;
157 if (pflags & ProcessEngine_CollectCommandLine) {
158 size_t size = systeminfo.argmax;
159 mib[0] = CTL_KERN;
160 mib[1] = KERN_PROCARGS2;
161 mib[2] = pt[i].pid;
162 if (sysctl(mib, 3, args, &size, NULL, 0) != -1) {
163 /* KERN_PROCARGS2 sysctl() returns following pseudo structure:
164 * struct {
165 * int argc
166 * char execname[];
167 * char argv[argc][];
168 * char env[][];
169 * }
170 * The strings are terminated with '\0' and may have variable '\0' padding
171 */
172 int argc = *args;
173 char *p = args + sizeof(int); // arguments beginning
174 StringBuffer_clear(cmdline);
175 p += strlen(p); // skip exename
176 while (argc && p < args + systeminfo.argmax) {
177 if (*p == 0) { // skip terminating 0 and variable length 0 padding
178 p++;
179 continue;
180 }
181 StringBuffer_append(cmdline, argc-- ? "%s " : "%s", p);
182 p += strlen(p);
183 }
184 if (StringBuffer_length(cmdline))
185 pt[i].cmdline = Str_dup(StringBuffer_toString(StringBuffer_trim(cmdline)));
186 }
187 if (STR_UNDEF(pt[i].cmdline)) {
188 char cmdpath[PROC_PIDPATHINFO_MAXSIZE] = {};
189 FREE(pt[i].cmdline);
190 if (proc_pidpath(pt[i].pid, cmdpath, sizeof(cmdpath)) > 0) {
191 pt[i].cmdline = Str_dup(cmdpath);
192 } else {
193 pt[i].cmdline = Str_dup(pinfo[i].kp_proc.p_comm);
194 }
195 }
196 }
197 if (! pt[i].zombie) {
198 // CPU, memory, threads
199 struct proc_taskinfo tinfo;
200 int rv = proc_pidinfo(pt[i].pid, PROC_PIDTASKINFO, 0, &tinfo, sizeof(tinfo)); // If the process is zombie, skip this
201 if (rv <= 0) {
202 if (errno != EPERM)
203 DEBUG("proc_pidinfo for pid %d failed -- %s\n", pt[i].pid, STRERROR);
204 } else if ((unsigned long)rv < sizeof(tinfo)) {
205 Log_error("proc_pidinfo for pid %d -- invalid result size\n", pt[i].pid);
206 } else {
207 pt[i].memory.usage = (unsigned long long)tinfo.pti_resident_size;
208 pt[i].cpu.time = (double)(tinfo.pti_total_user + tinfo.pti_total_system) / 100000000.; // The time is in nanoseconds, we store it as 1/10s
209 pt[i].threads.self = tinfo.pti_threadnum;
210 }
211 #ifdef rusage_info_current
212 // Disk IO
213 rusage_info_current rusage;
214 if (proc_pid_rusage(pt[i].pid, RUSAGE_INFO_CURRENT, (rusage_info_t *)&rusage) < 0) {
215 if (errno != EPERM)
216 DEBUG("proc_pid_rusage for pid %d failed -- %s\n", pt[i].pid, STRERROR);
217 } else {
218 pt[i].read.time = pt[i].write.time = Time_milli();
219 pt[i].read.bytes = -1;
220 pt[i].read.bytesPhysical = rusage.ri_diskio_bytesread;
221 pt[i].read.operations = -1;
222 pt[i].write.bytes = -1;
223 pt[i].write.bytesPhysical = rusage.ri_diskio_byteswritten;
224 pt[i].write.operations = -1;
225 }
226 #endif
227 }
228 }
229 if (pflags & ProcessEngine_CollectCommandLine) {
230 StringBuffer_free(&cmdline);
231 FREE(args);
232 }
233 FREE(pinfo);
234
235 *reference = pt;
236
237 return (int)treesize;
238 }
239
240
241 /**
242 * This routine returns 'nelem' double precision floats containing
243 * the load averages in 'loadv'; at most 3 values will be returned.
244 * @param loadv destination of the load averages
245 * @param nelem number of averages
246 * @return: 0 if successful, -1 if failed (and all load averages are 0).
247 */
getloadavg_sysdep(double * loadv,int nelem)248 int getloadavg_sysdep (double *loadv, int nelem) {
249 return getloadavg(loadv, nelem);
250 }
251
252
253 /**
254 * This routine returns real memory in use.
255 * @return: true if successful, false if failed (or not available)
256 */
used_system_memory_sysdep(SystemInfo_T * si)257 bool used_system_memory_sysdep(SystemInfo_T *si) {
258 /* Memory */
259 vm_statistics_data_t page_info;
260 mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
261 kern_return_t kret = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&page_info, &count);
262 if (kret != KERN_SUCCESS) {
263 DEBUG("system statistic error -- cannot get memory usage\n");
264 return false;
265 }
266 si->memory.usage.bytes = (unsigned long long)(page_info.wire_count + page_info.active_count) * (unsigned long long)pagesize;
267
268 /* Swap */
269 int mib[2] = {CTL_VM, VM_SWAPUSAGE};
270 size_t len = sizeof(struct xsw_usage);
271 struct xsw_usage swap;
272 if (sysctl(mib, 2, &swap, &len, NULL, 0) == -1) {
273 DEBUG("system statistic error -- cannot get swap usage: %s\n", STRERROR);
274 si->swap.size = 0ULL;
275 return false;
276 }
277 si->swap.size = (unsigned long long)swap.xsu_total;
278 si->swap.usage.bytes = (unsigned long long)swap.xsu_used;
279
280 return true;
281 }
282
283
284 /**
285 * This routine returns system/user CPU time in use.
286 * @return: true if successful, false if failed
287 */
used_system_cpu_sysdep(SystemInfo_T * si)288 bool used_system_cpu_sysdep(SystemInfo_T *si) {
289 long total;
290 long total_new = 0;
291 kern_return_t kret;
292 host_cpu_load_info_data_t cpu_info;
293 mach_msg_type_number_t count;
294
295 count = HOST_CPU_LOAD_INFO_COUNT;
296 kret = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpu_info, &count);
297 if (kret == KERN_SUCCESS) {
298 for (int i = 0; i < CPU_STATE_MAX; i++)
299 total_new += cpu_info.cpu_ticks[i];
300 total = total_new - total_old;
301 total_old = total_new;
302
303 si->cpu.usage.user = (total > 0) ? (100. * (double)(cpu_info.cpu_ticks[CPU_STATE_USER] - cpu_user_old) / total) : -1.;
304 si->cpu.usage.nice = (total > 0) ? (100. * (double)(cpu_info.cpu_ticks[CPU_STATE_NICE] - cpu_nice_old) / total) : -1.;
305 si->cpu.usage.system = (total > 0) ? (100. * (double)(cpu_info.cpu_ticks[CPU_STATE_SYSTEM] - cpu_syst_old) / total) : -1.;
306
307 cpu_user_old = cpu_info.cpu_ticks[CPU_STATE_USER];
308 cpu_nice_old = cpu_info.cpu_ticks[CPU_STATE_NICE];
309 cpu_syst_old = cpu_info.cpu_ticks[CPU_STATE_SYSTEM];
310
311 return true;
312 }
313 return false;
314 }
315
316
used_system_filedescriptors_sysdep(SystemInfo_T * si)317 bool used_system_filedescriptors_sysdep(__attribute__ ((unused)) SystemInfo_T *si) {
318 // Open files
319 size_t len = sizeof(si->filedescriptors.allocated);
320 if (sysctlbyname("kern.num_files", &si->filedescriptors.allocated, &len, NULL, 0) == -1) {
321 DEBUG("system statistics error -- sysctl kern.openfiles failed: %s\n", STRERROR);
322 return false;
323 }
324 // Max files
325 int mib[2] = {CTL_KERN, KERN_MAXFILES};
326 len = sizeof(si->filedescriptors.maximum);
327 if (sysctl(mib, 2, &si->filedescriptors.maximum, &len, NULL, 0) == -1) {
328 DEBUG("system statistics error -- sysctl kern.maxfiles failed: %s\n", STRERROR);
329 return false;
330 }
331 return true;
332 }
333
334
available_statistics(SystemInfo_T * si)335 bool available_statistics(SystemInfo_T *si) {
336 si->statisticsAvailable = Statistics_CpuUser | Statistics_CpuSystem | Statistics_CpuNice | Statistics_FiledescriptorsPerSystem;
337 return true;
338 }
339
340