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