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_TDFLAG	ki_tdflags
55 #	define ZBX_PROC_FLAG	ki_flag
56 #	define ZBX_PROC_MASK	P_INMEM
57 #endif
58 
get_commandline(struct kinfo_proc * proc)59 static char	*get_commandline(struct kinfo_proc *proc)
60 {
61 	int		mib[4], i;
62 	size_t		sz;
63 	static char	*args = NULL;
64 	static int	args_alloc = 0;
65 
66 	mib[0] = CTL_KERN;
67 	mib[1] = KERN_PROC;
68 	mib[2] = KERN_PROC_ARGS;
69 	mib[3] = proc->ZBX_PROC_PID;
70 
71 	if (-1 == sysctl(mib, 4, NULL, &sz, NULL, 0))
72 		return NULL;
73 
74 	if (NULL == args)
75 	{
76 		args = zbx_malloc(args, sz);
77 		args_alloc = sz;
78 	}
79 	else if (sz > args_alloc)
80 	{
81 		args = zbx_realloc(args, sz);
82 		args_alloc = sz;
83 	}
84 
85 	if (-1 == sysctl(mib, 4, args, &sz, NULL, 0))
86 		return NULL;
87 
88 	for (i = 0; i < (int)(sz - 1); i++)
89 		if (args[i] == '\0')
90 			args[i] = ' ';
91 
92 	if (sz == 0)
93 		zbx_strlcpy(args, proc->ZBX_PROC_COMM, args_alloc);
94 
95 	return args;
96 }
97 
PROC_MEM(AGENT_REQUEST * request,AGENT_RESULT * result)98 int     PROC_MEM(AGENT_REQUEST *request, AGENT_RESULT *result)
99 {
100 #define ZBX_SIZE	1
101 #define ZBX_RSS		2
102 #define ZBX_VSIZE	3
103 #define ZBX_PMEM	4
104 #define ZBX_TSIZE	5
105 #define ZBX_DSIZE	6
106 #define ZBX_SSIZE	7
107 
108 	char		*procname, *proccomm, *param, *args, *mem_type = NULL;
109 	int		do_task, pagesize, count, i, proccount = 0, invalid_user = 0, mem_type_code, mib[4];
110 	unsigned int	mibs;
111 	zbx_uint64_t	mem_size = 0, byte_value = 0;
112 	double		pct_size = 0.0, pct_value = 0.0;
113 #if (__FreeBSD_version) < 500000
114 	int		mem_pages;
115 #else
116 	unsigned long 	mem_pages;
117 #endif
118 	size_t	sz;
119 
120 	struct kinfo_proc	*proc = NULL;
121 	struct passwd		*usrinfo;
122 
123 	if (5 < request->nparam)
124 	{
125 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
126 		return SYSINFO_RET_FAIL;
127 	}
128 
129 	procname = get_rparam(request, 0);
130 	param = get_rparam(request, 1);
131 
132 	if (NULL != param && '\0' != *param)
133 	{
134 		errno = 0;
135 
136 		if (NULL == (usrinfo = getpwnam(param)))
137 		{
138 			if (0 != errno)
139 			{
140 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
141 						zbx_strerror(errno)));
142 				return SYSINFO_RET_FAIL;
143 			}
144 
145 			invalid_user = 1;
146 		}
147 	}
148 	else
149 		usrinfo = NULL;
150 
151 	param = get_rparam(request, 2);
152 
153 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "sum"))
154 		do_task = ZBX_DO_SUM;
155 	else if (0 == strcmp(param, "avg"))
156 		do_task = ZBX_DO_AVG;
157 	else if (0 == strcmp(param, "max"))
158 		do_task = ZBX_DO_MAX;
159 	else if (0 == strcmp(param, "min"))
160 		do_task = ZBX_DO_MIN;
161 	else
162 	{
163 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
164 		return SYSINFO_RET_FAIL;
165 	}
166 
167 	proccomm = get_rparam(request, 3);
168 	mem_type = get_rparam(request, 4);
169 
170 	if (NULL == mem_type || '\0' == *mem_type || 0 == strcmp(mem_type, "size"))
171 	{
172 		mem_type_code = ZBX_SIZE;		/* size of process (code + data + stack) */
173 	}
174 	else if (0 == strcmp(mem_type, "rss"))
175 	{
176 		mem_type_code = ZBX_RSS;		/* resident set size */
177 	}
178 	else if (0 == strcmp(mem_type, "vsize"))
179 	{
180 		mem_type_code = ZBX_VSIZE;		/* virtual size */
181 	}
182 	else if (0 == strcmp(mem_type, "pmem"))
183 	{
184 		mem_type_code = ZBX_PMEM;		/* percentage of real memory used by process */
185 	}
186 	else if (0 == strcmp(mem_type, "tsize"))
187 	{
188 		mem_type_code = ZBX_TSIZE;		/* text size */
189 	}
190 	else if (0 == strcmp(mem_type, "dsize"))
191 	{
192 		mem_type_code = ZBX_DSIZE;		/* data size */
193 	}
194 	else if (0 == strcmp(mem_type, "ssize"))
195 	{
196 		mem_type_code = ZBX_SSIZE;		/* stack size */
197 	}
198 	else
199 	{
200 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
201 		return SYSINFO_RET_FAIL;
202 	}
203 
204 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
205 		goto out;
206 
207 	pagesize = getpagesize();
208 
209 	mib[0] = CTL_KERN;
210 	mib[1] = KERN_PROC;
211 	if (NULL != usrinfo)
212 	{
213 		mib[2] = KERN_PROC_UID;
214 		mib[3] = usrinfo->pw_uid;
215 		mibs = 4;
216 	}
217 	else
218 	{
219 #if (__FreeBSD_version) < 500000
220 		mib[2] = KERN_PROC_ALL;
221 #else
222 		mib[2] = KERN_PROC_PROC;
223 #endif
224 		mib[3] = 0;
225 		mibs = 3;
226 	}
227 
228 	if (ZBX_PMEM == mem_type_code)
229 	{
230 		sz = sizeof(mem_pages);
231 
232 		if (0 != sysctlbyname("hw.availpages", &mem_pages, &sz, NULL, (size_t)0))
233 		{
234 			SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain number of physical pages: %s",
235 					zbx_strerror(errno)));
236 			return SYSINFO_RET_FAIL;
237 		}
238 	}
239 
240 	sz = 0;
241 	if (0 != sysctl(mib, mibs, NULL, &sz, NULL, (size_t)0))
242 	{
243 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain necessary buffer size from system: %s",
244 				zbx_strerror(errno)));
245 		return SYSINFO_RET_FAIL;
246 	}
247 
248 	proc = (struct kinfo_proc *)zbx_malloc(proc, sz);
249 	if (0 != sysctl(mib, mibs, proc, &sz, NULL, (size_t)0))
250 	{
251 		zbx_free(proc);
252 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain process information: %s",
253 				zbx_strerror(errno)));
254 		return SYSINFO_RET_FAIL;
255 	}
256 
257 	count = sz / sizeof(struct kinfo_proc);
258 
259 	for (i = 0; i < count; i++)
260 	{
261 		if (NULL != procname && '\0' != *procname && 0 != strcmp(procname, proc[i].ZBX_PROC_COMM))
262 			continue;
263 
264 		if (NULL != proccomm && '\0' != *proccomm)
265 		{
266 			if (NULL == (args = get_commandline(&proc[i])))
267 				continue;
268 
269 			if (NULL == zbx_regexp_match(args, proccomm, NULL))
270 				continue;
271 		}
272 
273 		switch (mem_type_code)
274 		{
275 			case ZBX_SIZE:
276 				byte_value = (proc[i].ZBX_PROC_TSIZE + proc[i].ZBX_PROC_DSIZE + proc[i].ZBX_PROC_SSIZE)
277 						* pagesize;
278 				break;
279 			case ZBX_RSS:
280 				byte_value = proc[i].ZBX_PROC_RSSIZE * pagesize;
281 				break;
282 			case ZBX_VSIZE:
283 				byte_value = proc[i].ZBX_PROC_VSIZE;
284 				break;
285 			case ZBX_PMEM:
286 				if (0 != (proc[i].ZBX_PROC_FLAG & ZBX_PROC_MASK))
287 #if (__FreeBSD_version) < 500000
288 					pct_value = ((float)(proc[i].ZBX_PROC_RSSIZE + UPAGES) / mem_pages) * 100.0;
289 #else
290 					pct_value = ((float)proc[i].ZBX_PROC_RSSIZE / mem_pages) * 100.0;
291 #endif
292 				else
293 					pct_value = 0.0;
294 				break;
295 			case ZBX_TSIZE:
296 				byte_value = proc[i].ZBX_PROC_TSIZE * pagesize;
297 				break;
298 			case ZBX_DSIZE:
299 				byte_value = proc[i].ZBX_PROC_DSIZE * pagesize;
300 				break;
301 			case ZBX_SSIZE:
302 				byte_value = proc[i].ZBX_PROC_SSIZE * pagesize;
303 				break;
304 		}
305 
306 		if (ZBX_PMEM != mem_type_code)
307 		{
308 			if (0 != proccount++)
309 			{
310 				if (ZBX_DO_MAX == do_task)
311 					mem_size = MAX(mem_size, byte_value);
312 				else if (ZBX_DO_MIN == do_task)
313 					mem_size = MIN(mem_size, byte_value);
314 				else
315 					mem_size += byte_value;
316 			}
317 			else
318 				mem_size = byte_value;
319 		}
320 		else
321 		{
322 			if (0 != proccount++)
323 			{
324 				if (ZBX_DO_MAX == do_task)
325 					pct_size = MAX(pct_size, pct_value);
326 				else if (ZBX_DO_MIN == do_task)
327 					pct_size = MIN(pct_size, pct_value);
328 				else
329 					pct_size += pct_value;
330 			}
331 			else
332 				pct_size = pct_value;
333 		}
334 	}
335 
336 	zbx_free(proc);
337 out:
338 	if (ZBX_PMEM != mem_type_code)
339 	{
340 		if (ZBX_DO_AVG == do_task)
341 			SET_DBL_RESULT(result, 0 == proccount ? 0.0 : (double)mem_size / (double)proccount);
342 		else
343 			SET_UI64_RESULT(result, mem_size);
344 	}
345 	else
346 	{
347 		if (ZBX_DO_AVG == do_task)
348 			SET_DBL_RESULT(result, 0 == proccount ? 0.0 : pct_size / (double)proccount);
349 		else
350 			SET_DBL_RESULT(result, pct_size);
351 	}
352 
353 	return SYSINFO_RET_OK;
354 
355 #undef ZBX_SIZE
356 #undef ZBX_RSS
357 #undef ZBX_VSIZE
358 #undef ZBX_PMEM
359 #undef ZBX_TSIZE
360 #undef ZBX_DSIZE
361 #undef ZBX_SSIZE
362 }
363 
PROC_NUM(AGENT_REQUEST * request,AGENT_RESULT * result)364 int	PROC_NUM(AGENT_REQUEST *request, AGENT_RESULT *result)
365 {
366 	char			*procname, *proccomm, *param, *args;
367 	int			proccount = 0, invalid_user = 0, zbx_proc_stat;
368 	int			count, i, proc_ok, stat_ok, comm_ok, mib[4], mibs;
369 	size_t			sz;
370 	struct kinfo_proc	*proc = NULL;
371 	struct passwd		*usrinfo;
372 
373 	if (4 < request->nparam)
374 	{
375 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
376 		return SYSINFO_RET_FAIL;
377 	}
378 
379 	procname = get_rparam(request, 0);
380 	param = get_rparam(request, 1);
381 
382 	if (NULL != param && '\0' != *param)
383 	{
384 		errno = 0;
385 
386 		if (NULL == (usrinfo = getpwnam(param)))
387 		{
388 			if (0 != errno)
389 			{
390 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
391 						zbx_strerror(errno)));
392 				return SYSINFO_RET_FAIL;
393 			}
394 
395 			invalid_user = 1;
396 		}
397 	}
398 	else
399 		usrinfo = NULL;
400 
401 	param = get_rparam(request, 2);
402 
403 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "all"))
404 		zbx_proc_stat = ZBX_PROC_STAT_ALL;
405 	else if (0 == strcmp(param, "run"))
406 		zbx_proc_stat = ZBX_PROC_STAT_RUN;
407 	else if (0 == strcmp(param, "sleep"))
408 		zbx_proc_stat = ZBX_PROC_STAT_SLEEP;
409 	else if (0 == strcmp(param, "zomb"))
410 		zbx_proc_stat = ZBX_PROC_STAT_ZOMB;
411 	else if (0 == strcmp(param, "disk"))
412 		zbx_proc_stat = ZBX_PROC_STAT_DISK;
413 	else if (0 == strcmp(param, "trace"))
414 		zbx_proc_stat = ZBX_PROC_STAT_TRACE;
415 	else
416 	{
417 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
418 		return SYSINFO_RET_FAIL;
419 	}
420 
421 	proccomm = get_rparam(request, 3);
422 
423 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
424 		goto out;
425 
426 	mib[0] = CTL_KERN;
427 	mib[1] = KERN_PROC;
428 	if (NULL != usrinfo)
429 	{
430 		mib[2] = KERN_PROC_UID;
431 		mib[3] = usrinfo->pw_uid;
432 		mibs = 4;
433 	}
434 	else
435 	{
436 #if (__FreeBSD_version) > 500000
437 		mib[2] = KERN_PROC_PROC;
438 #else
439 		mib[2] = KERN_PROC_ALL;
440 #endif
441 		mib[3] = 0;
442 		mibs = 3;
443 	}
444 
445 	sz = 0;
446 	if (0 != sysctl(mib, mibs, NULL, &sz, NULL, 0))
447 	{
448 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain necessary buffer size from system: %s",
449 				zbx_strerror(errno)));
450 		return SYSINFO_RET_FAIL;
451 	}
452 
453 	proc = (struct kinfo_proc *)zbx_malloc(proc, sz);
454 	if (0 != sysctl(mib, mibs, proc, &sz, NULL, 0))
455 	{
456 		zbx_free(proc);
457 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain process information: %s",
458 				zbx_strerror(errno)));
459 		return SYSINFO_RET_FAIL;
460 	}
461 
462 	count = sz / sizeof(struct kinfo_proc);
463 
464 	for (i = 0; i < count; i++)
465 	{
466 		proc_ok = 0;
467 		stat_ok = 0;
468 		comm_ok = 0;
469 
470 		if (NULL == procname || '\0' == *procname || 0 == strcmp(procname, proc[i].ZBX_PROC_COMM))
471 			proc_ok = 1;
472 
473 		if (zbx_proc_stat != ZBX_PROC_STAT_ALL)
474 		{
475 			switch (zbx_proc_stat) {
476 			case ZBX_PROC_STAT_RUN:
477 				if (SRUN == proc[i].ZBX_PROC_STAT)
478 					stat_ok = 1;
479 				break;
480 			case ZBX_PROC_STAT_SLEEP:
481 				if (SSLEEP == proc[i].ZBX_PROC_STAT && 0 != (proc[i].ZBX_PROC_TDFLAG & TDF_SINTR))
482 					stat_ok = 1;
483 				break;
484 			case ZBX_PROC_STAT_ZOMB:
485 				if (SZOMB == proc[i].ZBX_PROC_STAT)
486 					stat_ok = 1;
487 				break;
488 			case ZBX_PROC_STAT_DISK:
489 				if (SSLEEP == proc[i].ZBX_PROC_STAT && 0 == (proc[i].ZBX_PROC_TDFLAG & TDF_SINTR))
490 					stat_ok = 1;
491 				break;
492 			case ZBX_PROC_STAT_TRACE:
493 				if (SSTOP == proc[i].ZBX_PROC_STAT)
494 					stat_ok = 1;
495 				break;
496 			}
497 		}
498 		else
499 			stat_ok = 1;
500 
501 		if (NULL != proccomm && '\0' != *proccomm)
502 		{
503 			if (NULL != (args = get_commandline(&proc[i])))
504 				if (NULL != zbx_regexp_match(args, proccomm, NULL))
505 					comm_ok = 1;
506 		}
507 		else
508 			comm_ok = 1;
509 
510 		if (proc_ok && stat_ok && comm_ok)
511 			proccount++;
512 	}
513 	zbx_free(proc);
514 out:
515 	SET_UI64_RESULT(result, proccount);
516 
517 	return SYSINFO_RET_OK;
518 }
519