1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "sysinfo.h"
22 #include "zbxregexp.h"
23 #include "log.h"
24 
25 #if (__FreeBSD_version) < 500000
26 #	define ZBX_COMMLEN	MAXCOMLEN
27 #	define ZBX_PROC_PID	kp_proc.p_pid
28 #	define ZBX_PROC_COMM	kp_proc.p_comm
29 #	define ZBX_PROC_STAT	kp_proc.p_stat
30 #	define ZBX_PROC_TSIZE	kp_eproc.e_vm.vm_tsize
31 #	define ZBX_PROC_DSIZE	kp_eproc.e_vm.vm_dsize
32 #	define ZBX_PROC_SSIZE	kp_eproc.e_vm.vm_ssize
33 #	define ZBX_PROC_RSSIZE	kp_eproc.e_vm.vm_rssize
34 #	define ZBX_PROC_VSIZE	kp_eproc.e_vm.vm_map.size
35 #else
36 #	define ZBX_COMMLEN	COMMLEN
37 #	define ZBX_PROC_PID	ki_pid
38 #	define ZBX_PROC_COMM	ki_comm
39 #	define ZBX_PROC_STAT	ki_stat
40 #	define ZBX_PROC_TSIZE	ki_tsize
41 #	define ZBX_PROC_DSIZE	ki_dsize
42 #	define ZBX_PROC_SSIZE	ki_ssize
43 #	define ZBX_PROC_RSSIZE	ki_rssize
44 #	define ZBX_PROC_VSIZE	ki_size
45 #endif
46 
47 #if (__FreeBSD_version) < 500000
48 #	define ZBX_PROC_FLAG 	kp_proc.p_flag
49 #	define ZBX_PROC_MASK	P_INMEM
50 #elif (__FreeBSD_version) < 700000
51 #	define ZBX_PROC_FLAG 	ki_sflag
52 #	define ZBX_PROC_MASK	PS_INMEM
53 #else
54 #	define ZBX_PROC_FLAG 	ki_flag
55 #	define ZBX_PROC_MASK	P_INMEM
56 #endif
57 
get_commandline(struct kinfo_proc * proc)58 static char	*get_commandline(struct kinfo_proc *proc)
59 {
60 	int		mib[4], i;
61 	size_t		sz;
62 	static char	*args = NULL;
63 	static int	args_alloc = 128;
64 
65 	if (NULL == args)
66 		args = zbx_malloc(args, args_alloc);
67 
68 	mib[0] = CTL_KERN;
69 	mib[1] = KERN_PROC;
70 	mib[2] = KERN_PROC_ARGS;
71 	mib[3] = proc->ZBX_PROC_PID;
72 retry:
73 	sz = (size_t)args_alloc;
74 	if (-1 == sysctl(mib, 4, args, &sz, NULL, 0))
75 	{
76 		if (errno == ENOMEM)
77 		{
78 			args_alloc *= 2;
79 			args = zbx_realloc(args, args_alloc);
80 			goto retry;
81 		}
82 		return NULL;
83 	}
84 
85 	for (i = 0; i < (int)(sz - 1); i++)
86 		if (args[i] == '\0')
87 			args[i] = ' ';
88 
89 	if (sz == 0)
90 		zbx_strlcpy(args, proc->ZBX_PROC_COMM, args_alloc);
91 
92 	return args;
93 }
94 
PROC_MEM(AGENT_REQUEST * request,AGENT_RESULT * result)95 int     PROC_MEM(AGENT_REQUEST *request, AGENT_RESULT *result)
96 {
97 #define ZBX_SIZE	1
98 #define ZBX_RSS		2
99 #define ZBX_VSIZE	3
100 #define ZBX_PMEM	4
101 #define ZBX_TSIZE	5
102 #define ZBX_DSIZE	6
103 #define ZBX_SSIZE	7
104 
105 	char		*procname, *proccomm, *param, *args, *mem_type = NULL;
106 	int		do_task, pagesize, count, i, proccount = 0, invalid_user = 0, mem_type_code, mib[4];
107 	unsigned int	mibs;
108 	zbx_uint64_t	mem_size = 0, byte_value = 0;
109 	double		pct_size = 0.0, pct_value = 0.0;
110 #if (__FreeBSD_version) < 500000
111 	int		mem_pages;
112 #else
113 	unsigned long 	mem_pages;
114 #endif
115 	size_t	sz;
116 
117 	struct kinfo_proc	*proc = NULL;
118 	struct passwd		*usrinfo;
119 
120 	if (5 < request->nparam)
121 	{
122 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
123 		return SYSINFO_RET_FAIL;
124 	}
125 
126 	procname = get_rparam(request, 0);
127 	param = get_rparam(request, 1);
128 
129 	if (NULL != param && '\0' != *param)
130 	{
131 		errno = 0;
132 
133 		if (NULL == (usrinfo = getpwnam(param)))
134 		{
135 			if (0 != errno)
136 			{
137 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
138 						zbx_strerror(errno)));
139 				return SYSINFO_RET_FAIL;
140 			}
141 
142 			invalid_user = 1;
143 		}
144 	}
145 	else
146 		usrinfo = NULL;
147 
148 	param = get_rparam(request, 2);
149 
150 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "sum"))
151 		do_task = ZBX_DO_SUM;
152 	else if (0 == strcmp(param, "avg"))
153 		do_task = ZBX_DO_AVG;
154 	else if (0 == strcmp(param, "max"))
155 		do_task = ZBX_DO_MAX;
156 	else if (0 == strcmp(param, "min"))
157 		do_task = ZBX_DO_MIN;
158 	else
159 	{
160 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
161 		return SYSINFO_RET_FAIL;
162 	}
163 
164 	proccomm = get_rparam(request, 3);
165 	mem_type = get_rparam(request, 4);
166 
167 	if (NULL == mem_type || '\0' == *mem_type || 0 == strcmp(mem_type, "size"))
168 	{
169 		mem_type_code = ZBX_SIZE;		/* size of process (code + data + stack) */
170 	}
171 	else if (0 == strcmp(mem_type, "rss"))
172 	{
173 		mem_type_code = ZBX_RSS;		/* resident set size */
174 	}
175 	else if (0 == strcmp(mem_type, "vsize"))
176 	{
177 		mem_type_code = ZBX_VSIZE;		/* virtual size */
178 	}
179 	else if (0 == strcmp(mem_type, "pmem"))
180 	{
181 		mem_type_code = ZBX_PMEM;		/* percentage of real memory used by process */
182 	}
183 	else if (0 == strcmp(mem_type, "tsize"))
184 	{
185 		mem_type_code = ZBX_TSIZE;		/* text size */
186 	}
187 	else if (0 == strcmp(mem_type, "dsize"))
188 	{
189 		mem_type_code = ZBX_DSIZE;		/* data size */
190 	}
191 	else if (0 == strcmp(mem_type, "ssize"))
192 	{
193 		mem_type_code = ZBX_SSIZE;		/* stack size */
194 	}
195 	else
196 	{
197 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
198 		return SYSINFO_RET_FAIL;
199 	}
200 
201 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
202 		goto out;
203 
204 	pagesize = getpagesize();
205 
206 	mib[0] = CTL_KERN;
207 	mib[1] = KERN_PROC;
208 	if (NULL != usrinfo)
209 	{
210 		mib[2] = KERN_PROC_UID;
211 		mib[3] = usrinfo->pw_uid;
212 		mibs = 4;
213 	}
214 	else
215 	{
216 #if (__FreeBSD_version) < 500000
217 		mib[2] = KERN_PROC_ALL;
218 #else
219 		mib[2] = KERN_PROC_PROC;
220 #endif
221 		mib[3] = 0;
222 		mibs = 3;
223 	}
224 
225 	if (ZBX_PMEM == mem_type_code)
226 	{
227 		sz = sizeof(mem_pages);
228 
229 		if (0 != sysctlbyname("hw.availpages", &mem_pages, &sz, NULL, (size_t)0))
230 		{
231 			SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain number of physical pages: %s",
232 					zbx_strerror(errno)));
233 			return SYSINFO_RET_FAIL;
234 		}
235 	}
236 
237 	sz = 0;
238 	if (0 != sysctl(mib, mibs, NULL, &sz, NULL, (size_t)0))
239 	{
240 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain necessary buffer size from system: %s",
241 				zbx_strerror(errno)));
242 		return SYSINFO_RET_FAIL;
243 	}
244 
245 	proc = (struct kinfo_proc *)zbx_malloc(proc, sz);
246 	if (0 != sysctl(mib, mibs, proc, &sz, NULL, (size_t)0))
247 	{
248 		zbx_free(proc);
249 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain process information: %s",
250 				zbx_strerror(errno)));
251 		return SYSINFO_RET_FAIL;
252 	}
253 
254 	count = sz / sizeof(struct kinfo_proc);
255 
256 	for (i = 0; i < count; i++)
257 	{
258 		if (NULL != procname && '\0' != *procname && 0 != strcmp(procname, proc[i].ZBX_PROC_COMM))
259 			continue;
260 
261 		if (NULL != proccomm && '\0' != *proccomm)
262 		{
263 			if (NULL == (args = get_commandline(&proc[i])))
264 				continue;
265 
266 			if (NULL == zbx_regexp_match(args, proccomm, NULL))
267 				continue;
268 		}
269 
270 		switch (mem_type_code)
271 		{
272 			case ZBX_SIZE:
273 				byte_value = (proc[i].ZBX_PROC_TSIZE + proc[i].ZBX_PROC_DSIZE + proc[i].ZBX_PROC_SSIZE)
274 						* pagesize;
275 				break;
276 			case ZBX_RSS:
277 				byte_value = proc[i].ZBX_PROC_RSSIZE * pagesize;
278 				break;
279 			case ZBX_VSIZE:
280 				byte_value = proc[i].ZBX_PROC_VSIZE;
281 				break;
282 			case ZBX_PMEM:
283 				if (0 != (proc[i].ZBX_PROC_FLAG & ZBX_PROC_MASK))
284 #if (__FreeBSD_version) < 500000
285 					pct_value = ((float)(proc[i].ZBX_PROC_RSSIZE + UPAGES) / mem_pages) * 100.0;
286 #else
287 					pct_value = ((float)proc[i].ZBX_PROC_RSSIZE / mem_pages) * 100.0;
288 #endif
289 				else
290 					pct_value = 0.0;
291 				break;
292 			case ZBX_TSIZE:
293 				byte_value = proc[i].ZBX_PROC_TSIZE * pagesize;
294 				break;
295 			case ZBX_DSIZE:
296 				byte_value = proc[i].ZBX_PROC_DSIZE * pagesize;
297 				break;
298 			case ZBX_SSIZE:
299 				byte_value = proc[i].ZBX_PROC_SSIZE * pagesize;
300 				break;
301 		}
302 
303 		if (ZBX_PMEM != mem_type_code)
304 		{
305 			if (0 != proccount++)
306 			{
307 				if (ZBX_DO_MAX == do_task)
308 					mem_size = MAX(mem_size, byte_value);
309 				else if (ZBX_DO_MIN == do_task)
310 					mem_size = MIN(mem_size, byte_value);
311 				else
312 					mem_size += byte_value;
313 			}
314 			else
315 				mem_size = byte_value;
316 		}
317 		else
318 		{
319 			if (0 != proccount++)
320 			{
321 				if (ZBX_DO_MAX == do_task)
322 					pct_size = MAX(pct_size, pct_value);
323 				else if (ZBX_DO_MIN == do_task)
324 					pct_size = MIN(pct_size, pct_value);
325 				else
326 					pct_size += pct_value;
327 			}
328 			else
329 				pct_size = pct_value;
330 		}
331 	}
332 
333 	zbx_free(proc);
334 out:
335 	if (ZBX_PMEM != mem_type_code)
336 	{
337 		if (ZBX_DO_AVG == do_task)
338 			SET_DBL_RESULT(result, 0 == proccount ? 0.0 : (double)mem_size / (double)proccount);
339 		else
340 			SET_UI64_RESULT(result, mem_size);
341 	}
342 	else
343 	{
344 		if (ZBX_DO_AVG == do_task)
345 			SET_DBL_RESULT(result, 0 == proccount ? 0.0 : pct_size / (double)proccount);
346 		else
347 			SET_DBL_RESULT(result, pct_size);
348 	}
349 
350 	return SYSINFO_RET_OK;
351 
352 #undef ZBX_SIZE
353 #undef ZBX_RSS
354 #undef ZBX_VSIZE
355 #undef ZBX_PMEM
356 #undef ZBX_TSIZE
357 #undef ZBX_DSIZE
358 #undef ZBX_SSIZE
359 }
360 
PROC_NUM(AGENT_REQUEST * request,AGENT_RESULT * result)361 int	PROC_NUM(AGENT_REQUEST *request, AGENT_RESULT *result)
362 {
363 	char			*procname, *proccomm, *param, *args;
364 	int			proccount = 0, invalid_user = 0, zbx_proc_stat;
365 	int			count, i, proc_ok, stat_ok, comm_ok, mib[4], mibs;
366 	size_t			sz;
367 	struct kinfo_proc	*proc = NULL;
368 	struct passwd		*usrinfo;
369 
370 	if (4 < request->nparam)
371 	{
372 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
373 		return SYSINFO_RET_FAIL;
374 	}
375 
376 	procname = get_rparam(request, 0);
377 	param = get_rparam(request, 1);
378 
379 	if (NULL != param && '\0' != *param)
380 	{
381 		errno = 0;
382 
383 		if (NULL == (usrinfo = getpwnam(param)))
384 		{
385 			if (0 != errno)
386 			{
387 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
388 						zbx_strerror(errno)));
389 				return SYSINFO_RET_FAIL;
390 			}
391 
392 			invalid_user = 1;
393 		}
394 	}
395 	else
396 		usrinfo = NULL;
397 
398 	param = get_rparam(request, 2);
399 
400 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "all"))
401 		zbx_proc_stat = ZBX_PROC_STAT_ALL;
402 	else if (0 == strcmp(param, "run"))
403 		zbx_proc_stat = ZBX_PROC_STAT_RUN;
404 	else if (0 == strcmp(param, "sleep"))
405 		zbx_proc_stat = ZBX_PROC_STAT_SLEEP;
406 	else if (0 == strcmp(param, "zomb"))
407 		zbx_proc_stat = ZBX_PROC_STAT_ZOMB;
408 	else
409 	{
410 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
411 		return SYSINFO_RET_FAIL;
412 	}
413 
414 	proccomm = get_rparam(request, 3);
415 
416 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
417 		goto out;
418 
419 	mib[0] = CTL_KERN;
420 	mib[1] = KERN_PROC;
421 	if (NULL != usrinfo)
422 	{
423 		mib[2] = KERN_PROC_UID;
424 		mib[3] = usrinfo->pw_uid;
425 		mibs = 4;
426 	}
427 	else
428 	{
429 #if (__FreeBSD_version) > 500000
430 		mib[2] = KERN_PROC_PROC;
431 #else
432 		mib[2] = KERN_PROC_ALL;
433 #endif
434 		mib[3] = 0;
435 		mibs = 3;
436 	}
437 
438 	sz = 0;
439 	if (0 != sysctl(mib, mibs, NULL, &sz, NULL, 0))
440 	{
441 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain necessary buffer size from system: %s",
442 				zbx_strerror(errno)));
443 		return SYSINFO_RET_FAIL;
444 	}
445 
446 	proc = (struct kinfo_proc *)zbx_malloc(proc, sz);
447 	if (0 != sysctl(mib, mibs, proc, &sz, NULL, 0))
448 	{
449 		zbx_free(proc);
450 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain process information: %s",
451 				zbx_strerror(errno)));
452 		return SYSINFO_RET_FAIL;
453 	}
454 
455 	count = sz / sizeof(struct kinfo_proc);
456 
457 	for (i = 0; i < count; i++)
458 	{
459 		proc_ok = 0;
460 		stat_ok = 0;
461 		comm_ok = 0;
462 
463 		if (NULL == procname || '\0' == *procname || 0 == strcmp(procname, proc[i].ZBX_PROC_COMM))
464 			proc_ok = 1;
465 
466 		if (zbx_proc_stat != ZBX_PROC_STAT_ALL)
467 		{
468 			switch (zbx_proc_stat) {
469 			case ZBX_PROC_STAT_RUN:
470 				if (proc[i].ZBX_PROC_STAT == SRUN)
471 					stat_ok = 1;
472 				break;
473 			case ZBX_PROC_STAT_SLEEP:
474 				if (proc[i].ZBX_PROC_STAT == SSLEEP)
475 					stat_ok = 1;
476 				break;
477 			case ZBX_PROC_STAT_ZOMB:
478 				if (proc[i].ZBX_PROC_STAT == SZOMB)
479 					stat_ok = 1;
480 				break;
481 			}
482 		}
483 		else
484 			stat_ok = 1;
485 
486 		if (NULL != proccomm && '\0' != *proccomm)
487 		{
488 			if (NULL != (args = get_commandline(&proc[i])))
489 				if (NULL != zbx_regexp_match(args, proccomm, NULL))
490 					comm_ok = 1;
491 		}
492 		else
493 			comm_ok = 1;
494 
495 		if (proc_ok && stat_ok && comm_ok)
496 			proccount++;
497 	}
498 	zbx_free(proc);
499 out:
500 	SET_UI64_RESULT(result, proccount);
501 
502 	return SYSINFO_RET_OK;
503 }
504