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 #include "stats.h"
25 #include "proc.h"
26 
27 extern int	CONFIG_TIMEOUT;
28 
29 typedef struct
30 {
31 	pid_t		pid;
32 	uid_t		uid;
33 
34 	char		*name;
35 
36 	/* the process name taken from the 0th argument */
37 	char		*name_arg0;
38 
39 	/* process command line in format <arg0> <arg1> ... <argN>\0 */
40 	char		*cmdline;
41 }
42 zbx_sysinfo_proc_t;
43 
44 /******************************************************************************
45  *                                                                            *
46  * Function: zbx_sysinfo_proc_free                                            *
47  *                                                                            *
48  * Purpose: frees process data structure                                      *
49  *                                                                            *
50  ******************************************************************************/
zbx_sysinfo_proc_free(zbx_sysinfo_proc_t * proc)51 static void	zbx_sysinfo_proc_free(zbx_sysinfo_proc_t *proc)
52 {
53 	zbx_free(proc->name);
54 	zbx_free(proc->name_arg0);
55 	zbx_free(proc->cmdline);
56 
57 	zbx_free(proc);
58 }
59 
get_cmdline(FILE * f_cmd,char ** line,size_t * line_offset)60 static int	get_cmdline(FILE *f_cmd, char **line, size_t *line_offset)
61 {
62 	size_t	line_alloc = ZBX_KIBIBYTE, n;
63 
64 	rewind(f_cmd);
65 
66 	*line = (char *)zbx_malloc(*line, line_alloc + 2);
67 	*line_offset = 0;
68 
69 	while (0 != (n = fread(*line + *line_offset, 1, line_alloc - *line_offset, f_cmd)))
70 	{
71 		*line_offset += n;
72 
73 		if (0 != feof(f_cmd))
74 			break;
75 
76 		line_alloc *= 2;
77 		*line = (char *)zbx_realloc(*line, line_alloc + 2);
78 	}
79 
80 	if (0 == ferror(f_cmd))
81 	{
82 		if (0 == *line_offset || '\0' != (*line)[*line_offset - 1])
83 			(*line)[(*line_offset)++] = '\0';
84 		if (1 == *line_offset || '\0' != (*line)[*line_offset - 2])
85 			(*line)[(*line_offset)++] = '\0';
86 
87 		return SUCCEED;
88 	}
89 
90 	zbx_free(*line);
91 
92 	return FAIL;
93 }
94 
cmp_status(FILE * f_stat,const char * procname)95 static int	cmp_status(FILE *f_stat, const char *procname)
96 {
97 	char	tmp[MAX_STRING_LEN];
98 
99 	rewind(f_stat);
100 
101 	while (NULL != fgets(tmp, (int)sizeof(tmp), f_stat))
102 	{
103 		if (0 != strncmp(tmp, "Name:\t", 6))
104 			continue;
105 
106 		zbx_rtrim(tmp + 6, "\n");
107 		if (0 == strcmp(tmp + 6, procname))
108 			return SUCCEED;
109 		break;
110 	}
111 
112 	return FAIL;
113 }
114 
check_procname(FILE * f_cmd,FILE * f_stat,const char * procname)115 static int	check_procname(FILE *f_cmd, FILE *f_stat, const char *procname)
116 {
117 	char	*tmp = NULL, *p;
118 	size_t	l;
119 	int	ret = SUCCEED;
120 
121 	if (NULL == procname || '\0' == *procname)
122 		return SUCCEED;
123 
124 	/* process name in /proc/[pid]/status contains limited number of characters */
125 	if (SUCCEED == cmp_status(f_stat, procname))
126 		return SUCCEED;
127 
128 	if (SUCCEED == get_cmdline(f_cmd, &tmp, &l))
129 	{
130 		if (NULL == (p = strrchr(tmp, '/')))
131 			p = tmp;
132 		else
133 			p++;
134 
135 		if (0 == strcmp(p, procname))
136 			goto clean;
137 	}
138 
139 	ret = FAIL;
140 clean:
141 	zbx_free(tmp);
142 
143 	return ret;
144 }
145 
check_user(FILE * f_stat,struct passwd * usrinfo)146 static int	check_user(FILE *f_stat, struct passwd *usrinfo)
147 {
148 	char	tmp[MAX_STRING_LEN], *p, *p1;
149 	uid_t	uid;
150 
151 	if (NULL == usrinfo)
152 		return SUCCEED;
153 
154 	rewind(f_stat);
155 
156 	while (NULL != fgets(tmp, (int)sizeof(tmp), f_stat))
157 	{
158 		if (0 != strncmp(tmp, "Uid:\t", 5))
159 			continue;
160 
161 		p = tmp + 5;
162 
163 		if (NULL != (p1 = strchr(p, '\t')))
164 			*p1 = '\0';
165 
166 		uid = (uid_t)atoi(p);
167 
168 		if (usrinfo->pw_uid == uid)
169 			return SUCCEED;
170 		break;
171 	}
172 
173 	return FAIL;
174 }
175 
check_proccomm(FILE * f_cmd,const char * proccomm)176 static int	check_proccomm(FILE *f_cmd, const char *proccomm)
177 {
178 	char	*tmp = NULL;
179 	size_t	i, l;
180 	int	ret = SUCCEED;
181 
182 	if (NULL == proccomm || '\0' == *proccomm)
183 		return SUCCEED;
184 
185 	if (SUCCEED == get_cmdline(f_cmd, &tmp, &l))
186 	{
187 		for (i = 0, l -= 2; i < l; i++)
188 			if ('\0' == tmp[i])
189 				tmp[i] = ' ';
190 
191 		if (NULL != zbx_regexp_match(tmp, proccomm, NULL))
192 			goto clean;
193 	}
194 
195 	ret = FAIL;
196 clean:
197 	zbx_free(tmp);
198 
199 	return ret;
200 }
201 
check_procstate(FILE * f_stat,int zbx_proc_stat)202 static int	check_procstate(FILE *f_stat, int zbx_proc_stat)
203 {
204 	char	tmp[MAX_STRING_LEN], *p;
205 
206 	if (ZBX_PROC_STAT_ALL == zbx_proc_stat)
207 		return SUCCEED;
208 
209 	rewind(f_stat);
210 
211 	while (NULL != fgets(tmp, (int)sizeof(tmp), f_stat))
212 	{
213 		if (0 != strncmp(tmp, "State:\t", 7))
214 			continue;
215 
216 		p = tmp + 7;
217 
218 		switch (zbx_proc_stat)
219 		{
220 			case ZBX_PROC_STAT_RUN:
221 				return ('R' == *p) ? SUCCEED : FAIL;
222 			case ZBX_PROC_STAT_SLEEP:
223 				return ('S' == *p) ? SUCCEED : FAIL;
224 			case ZBX_PROC_STAT_ZOMB:
225 				return ('Z' == *p) ? SUCCEED : FAIL;
226 			case ZBX_PROC_STAT_DISK:
227 				return ('D' == *p) ? SUCCEED : FAIL;
228 			case ZBX_PROC_STAT_TRACE:
229 				return ('T' == *p) ? SUCCEED : FAIL;
230 			default:
231 				return FAIL;
232 		}
233 	}
234 
235 	return FAIL;
236 }
237 
238 /******************************************************************************
239  *                                                                            *
240  * Function: byte_value_from_proc_file                                        *
241  *                                                                            *
242  * Purpose: Read amount of memory in bytes from a string in /proc file.       *
243  *          For example, reading "VmSize:   176712 kB" from /proc/1/status    *
244  *          will produce a result 176712*1024 = 180953088 bytes               *
245  *                                                                            *
246  * Parameters:                                                                *
247  *     f     - [IN] file to read from                                         *
248  *     label - [IN] label to look for, e.g. "VmData:\t"                       *
249  *     guard - [IN] label before which to stop, e.g. "VmStk:\t" (optional)    *
250  *     bytes - [OUT] result in bytes                                          *
251  *                                                                            *
252  * Return value: SUCCEED - successful reading,                                *
253  *               NOTSUPPORTED - the search string was not found. For example, *
254  *                              /proc/NNN/status files for kernel threads do  *
255  *                              not contain "VmSize:" string.                 *
256  *               FAIL - the search string was found but could not be parsed.  *
257  *                                                                            *
258  ******************************************************************************/
byte_value_from_proc_file(FILE * f,const char * label,const char * guard,zbx_uint64_t * bytes)259 int	byte_value_from_proc_file(FILE *f, const char *label, const char *guard, zbx_uint64_t *bytes)
260 {
261 	char	buf[MAX_STRING_LEN], *p_value, *p_unit;
262 	size_t	label_len, guard_len;
263 	long	pos = 0;
264 	int	ret = NOTSUPPORTED;
265 
266 	label_len = strlen(label);
267 	p_value = buf + label_len;
268 
269 	if (NULL != guard)
270 	{
271 		guard_len = strlen(guard);
272 		if (0 > (pos = ftell(f)))
273 			return FAIL;
274 	}
275 
276 	while (NULL != fgets(buf, (int)sizeof(buf), f))
277 	{
278 		if (NULL != guard)
279 		{
280 			if (0 == strncmp(buf, guard, guard_len))
281 			{
282 				if (0 != fseek(f, pos, SEEK_SET))
283 					ret = FAIL;
284 				break;
285 			}
286 
287 			if (0 > (pos = ftell(f)))
288 			{
289 				ret = FAIL;
290 				break;
291 			}
292 		}
293 
294 		if (0 != strncmp(buf, label, label_len))
295 			continue;
296 
297 		if (NULL == (p_unit = strrchr(p_value, ' ')))
298 		{
299 			ret = FAIL;
300 			break;
301 		}
302 
303 		*p_unit++ = '\0';
304 
305 		while (' ' == *p_value)
306 			p_value++;
307 
308 		if (FAIL == is_uint64(p_value, bytes))
309 		{
310 			ret = FAIL;
311 			break;
312 		}
313 
314 		zbx_rtrim(p_unit, "\n");
315 
316 		if (0 == strcasecmp(p_unit, "kB"))
317 			*bytes <<= 10;
318 		else if (0 == strcasecmp(p_unit, "mB"))
319 			*bytes <<= 20;
320 		else if (0 == strcasecmp(p_unit, "GB"))
321 			*bytes <<= 30;
322 		else if (0 == strcasecmp(p_unit, "TB"))
323 			*bytes <<= 40;
324 
325 		ret = SUCCEED;
326 		break;
327 	}
328 
329 	return ret;
330 }
331 
get_total_memory(zbx_uint64_t * total_memory)332 static int	get_total_memory(zbx_uint64_t *total_memory)
333 {
334 	FILE	*f;
335 	int	ret = FAIL;
336 
337 	if (NULL != (f = fopen("/proc/meminfo", "r")))
338 	{
339 		ret = byte_value_from_proc_file(f, "MemTotal:", NULL, total_memory);
340 		zbx_fclose(f);
341 	}
342 
343 	return ret;
344 }
345 
PROC_MEM(AGENT_REQUEST * request,AGENT_RESULT * result)346 int	PROC_MEM(AGENT_REQUEST *request, AGENT_RESULT *result)
347 {
348 #define ZBX_SIZE	0
349 #define ZBX_RSS		1
350 #define ZBX_VSIZE	2
351 #define ZBX_PMEM	3
352 #define ZBX_VMPEAK	4
353 #define ZBX_VMSWAP	5
354 #define ZBX_VMLIB	6
355 #define ZBX_VMLCK	7
356 #define ZBX_VMPIN	8
357 #define ZBX_VMHWM	9
358 #define ZBX_VMDATA	10
359 #define ZBX_VMSTK	11
360 #define ZBX_VMEXE	12
361 #define ZBX_VMPTE	13
362 
363 	char		tmp[MAX_STRING_LEN], *procname, *proccomm, *param;
364 	DIR		*dir;
365 	struct dirent	*entries;
366 	struct passwd	*usrinfo;
367 	FILE		*f_cmd = NULL, *f_stat = NULL;
368 	zbx_uint64_t	mem_size = 0, byte_value = 0, total_memory;
369 	double		pct_size = 0.0, pct_value = 0.0;
370 	int		do_task, res, proccount = 0, invalid_user = 0, invalid_read = 0;
371 	int		mem_type_tried = 0, mem_type_code;
372 	char		*mem_type = NULL;
373 	const char	*mem_type_search = NULL;
374 
375 	if (5 < request->nparam)
376 	{
377 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
378 		return SYSINFO_RET_FAIL;
379 	}
380 
381 	procname = get_rparam(request, 0);
382 	param = get_rparam(request, 1);
383 
384 	if (NULL != param && '\0' != *param)
385 	{
386 		errno = 0;
387 
388 		if (NULL == (usrinfo = getpwnam(param)))
389 		{
390 			if (0 != errno)
391 			{
392 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
393 							zbx_strerror(errno)));
394 				return SYSINFO_RET_FAIL;
395 			}
396 
397 			invalid_user = 1;
398 		}
399 	}
400 	else
401 		usrinfo = NULL;
402 
403 	param = get_rparam(request, 2);
404 
405 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "sum"))
406 		do_task = ZBX_DO_SUM;
407 	else if (0 == strcmp(param, "avg"))
408 		do_task = ZBX_DO_AVG;
409 	else if (0 == strcmp(param, "max"))
410 		do_task = ZBX_DO_MAX;
411 	else if (0 == strcmp(param, "min"))
412 		do_task = ZBX_DO_MIN;
413 	else
414 	{
415 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
416 		return SYSINFO_RET_FAIL;
417 	}
418 
419 	proccomm = get_rparam(request, 3);
420 	mem_type = get_rparam(request, 4);
421 
422 	/* Comments for process memory types were compiled from: */
423 	/*    man 5 proc */
424 	/*    https://www.kernel.org/doc/Documentation/filesystems/proc.txt */
425 	/*    Himanshu Arora, Linux Processes explained - Part II, http://mylinuxbook.com/linux-processes-part2/ */
426 
427 	if (NULL == mem_type || '\0' == *mem_type || 0 == strcmp(mem_type, "vsize"))
428 	{
429 		mem_type_code = ZBX_VSIZE;		/* current virtual memory size (total program size) */
430 		mem_type_search = "VmSize:\t";
431 	}
432 	else if (0 == strcmp(mem_type, "rss"))
433 	{
434 		mem_type_code = ZBX_RSS;		/* current resident set size (size of memory portions) */
435 		mem_type_search = "VmRSS:\t";
436 	}
437 	else if (0 == strcmp(mem_type, "pmem"))
438 	{
439 		mem_type_code = ZBX_PMEM;		/* percentage of real memory used by process */
440 	}
441 	else if (0 == strcmp(mem_type, "size"))
442 	{
443 		mem_type_code = ZBX_SIZE;		/* size of process (code + data + stack) */
444 	}
445 	else if (0 == strcmp(mem_type, "peak"))
446 	{
447 		mem_type_code = ZBX_VMPEAK;		/* peak virtual memory size */
448 		mem_type_search = "VmPeak:\t";
449 	}
450 	else if (0 == strcmp(mem_type, "swap"))
451 	{
452 		mem_type_code = ZBX_VMSWAP;		/* size of swap space used */
453 		mem_type_search = "VmSwap:\t";
454 	}
455 	else if (0 == strcmp(mem_type, "lib"))
456 	{
457 		mem_type_code = ZBX_VMLIB;		/* size of shared libraries */
458 		mem_type_search = "VmLib:\t";
459 	}
460 	else if (0 == strcmp(mem_type, "lck"))
461 	{
462 		mem_type_code = ZBX_VMLCK;		/* size of locked memory */
463 		mem_type_search = "VmLck:\t";
464 	}
465 	else if (0 == strcmp(mem_type, "pin"))
466 	{
467 		mem_type_code = ZBX_VMPIN;		/* size of pinned pages, they are never swappable */
468 		mem_type_search = "VmPin:\t";
469 	}
470 	else if (0 == strcmp(mem_type, "hwm"))
471 	{
472 		mem_type_code = ZBX_VMHWM;		/* peak resident set size ("high water mark") */
473 		mem_type_search = "VmHWM:\t";
474 	}
475 	else if (0 == strcmp(mem_type, "data"))
476 	{
477 		mem_type_code = ZBX_VMDATA;		/* size of data segment */
478 		mem_type_search = "VmData:\t";
479 	}
480 	else if (0 == strcmp(mem_type, "stk"))
481 	{
482 		mem_type_code = ZBX_VMSTK;		/* size of stack segment */
483 		mem_type_search = "VmStk:\t";
484 	}
485 	else if (0 == strcmp(mem_type, "exe"))
486 	{
487 		mem_type_code = ZBX_VMEXE;		/* size of text (code) segment */
488 		mem_type_search = "VmExe:\t";
489 	}
490 	else if (0 == strcmp(mem_type, "pte"))
491 	{
492 		mem_type_code = ZBX_VMPTE;		/* size of page table entries */
493 		mem_type_search = "VmPTE:\t";
494 	}
495 	else
496 	{
497 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
498 		return SYSINFO_RET_FAIL;
499 	}
500 
501 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
502 		goto out;
503 
504 	if (ZBX_PMEM == mem_type_code)
505 	{
506 		if (SUCCEED != get_total_memory(&total_memory))
507 		{
508 			SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain amount of total memory: %s",
509 					zbx_strerror(errno)));
510 			return SYSINFO_RET_FAIL;
511 		}
512 
513 		if (0 == total_memory)	/* this should never happen but anyway - avoid crash due to dividing by 0 */
514 		{
515 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Total memory reported is 0."));
516 			return SYSINFO_RET_FAIL;
517 		}
518 	}
519 
520 	if (NULL == (dir = opendir("/proc")))
521 	{
522 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot open /proc: %s", zbx_strerror(errno)));
523 		return SYSINFO_RET_FAIL;
524 	}
525 
526 	while (NULL != (entries = readdir(dir)))
527 	{
528 		zbx_fclose(f_cmd);
529 		zbx_fclose(f_stat);
530 
531 		if (0 == atoi(entries->d_name))
532 			continue;
533 
534 		zbx_snprintf(tmp, sizeof(tmp), "/proc/%s/cmdline", entries->d_name);
535 
536 		if (NULL == (f_cmd = fopen(tmp, "r")))
537 			continue;
538 
539 		zbx_snprintf(tmp, sizeof(tmp), "/proc/%s/status", entries->d_name);
540 
541 		if (NULL == (f_stat = fopen(tmp, "r")))
542 			continue;
543 
544 		if (FAIL == check_procname(f_cmd, f_stat, procname))
545 			continue;
546 
547 		if (FAIL == check_user(f_stat, usrinfo))
548 			continue;
549 
550 		if (FAIL == check_proccomm(f_cmd, proccomm))
551 			continue;
552 
553 		rewind(f_stat);
554 
555 		if (0 == mem_type_tried)
556 			mem_type_tried = 1;
557 
558 		switch (mem_type_code)
559 		{
560 			case ZBX_VSIZE:
561 			case ZBX_RSS:
562 			case ZBX_VMPEAK:
563 			case ZBX_VMSWAP:
564 			case ZBX_VMLIB:
565 			case ZBX_VMLCK:
566 			case ZBX_VMPIN:
567 			case ZBX_VMHWM:
568 			case ZBX_VMDATA:
569 			case ZBX_VMSTK:
570 			case ZBX_VMEXE:
571 			case ZBX_VMPTE:
572 				res = byte_value_from_proc_file(f_stat, mem_type_search, NULL, &byte_value);
573 
574 				if (NOTSUPPORTED == res)
575 					continue;
576 
577 				if (FAIL == res)
578 				{
579 					invalid_read = 1;
580 					goto clean;
581 				}
582 				break;
583 			case ZBX_SIZE:
584 				{
585 					zbx_uint64_t	m;
586 
587 					/* VmData, VmStk and VmExe follow in /proc/PID/status file in that order. */
588 					/* Therefore we do not rewind f_stat between calls. */
589 
590 					mem_type_search = "VmData:\t";
591 
592 					if (SUCCEED == (res = byte_value_from_proc_file(f_stat, mem_type_search, NULL,
593 							&byte_value)))
594 					{
595 						mem_type_search = "VmStk:\t";
596 
597 						if (SUCCEED == (res = byte_value_from_proc_file(f_stat, mem_type_search,
598 								NULL, &m)))
599 						{
600 							byte_value += m;
601 							mem_type_search = "VmExe:\t";
602 
603 							if (SUCCEED == (res = byte_value_from_proc_file(f_stat,
604 									mem_type_search, NULL, &m)))
605 							{
606 								byte_value += m;
607 							}
608 						}
609 					}
610 
611 					if (SUCCEED != res)
612 					{
613 						if (NOTSUPPORTED == res)
614 						{
615 							/* NOTSUPPORTED - at least one of data strings not found in */
616 							/* the /proc/PID/status file */
617 							continue;
618 						}
619 						else	/* FAIL */
620 						{
621 							invalid_read = 1;
622 							goto clean;
623 						}
624 					}
625 				}
626 				break;
627 			case ZBX_PMEM:
628 				mem_type_search = "VmRSS:\t";
629 				res = byte_value_from_proc_file(f_stat, mem_type_search, NULL, &byte_value);
630 
631 				if (SUCCEED == res)
632 				{
633 					pct_value = ((double)byte_value / (double)total_memory) * 100.0;
634 				}
635 				else if (NOTSUPPORTED == res)
636 				{
637 					continue;
638 				}
639 				else	/* FAIL */
640 				{
641 					invalid_read = 1;
642 					goto clean;
643 				}
644 				break;
645 		}
646 
647 		if (ZBX_PMEM != mem_type_code)
648 		{
649 			if (0 != proccount++)
650 			{
651 				if (ZBX_DO_MAX == do_task)
652 					mem_size = MAX(mem_size, byte_value);
653 				else if (ZBX_DO_MIN == do_task)
654 					mem_size = MIN(mem_size, byte_value);
655 				else
656 					mem_size += byte_value;
657 			}
658 			else
659 				mem_size = byte_value;
660 		}
661 		else
662 		{
663 			if (0 != proccount++)
664 			{
665 				if (ZBX_DO_MAX == do_task)
666 					pct_size = MAX(pct_size, pct_value);
667 				else if (ZBX_DO_MIN == do_task)
668 					pct_size = MIN(pct_size, pct_value);
669 				else
670 					pct_size += pct_value;
671 			}
672 			else
673 				pct_size = pct_value;
674 		}
675 	}
676 clean:
677 	zbx_fclose(f_cmd);
678 	zbx_fclose(f_stat);
679 	closedir(dir);
680 
681 	if ((0 == proccount && 0 != mem_type_tried) || 0 != invalid_read)
682 	{
683 		char	*s;
684 
685 		s = zbx_strdup(NULL, mem_type_search);
686 		zbx_rtrim(s, ":\t");
687 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot get amount of \"%s\" memory.", s));
688 		zbx_free(s);
689 		return SYSINFO_RET_FAIL;
690 	}
691 out:
692 	if (ZBX_PMEM != mem_type_code)
693 	{
694 		if (ZBX_DO_AVG == do_task)
695 			SET_DBL_RESULT(result, 0 == proccount ? 0 : (double)mem_size / (double)proccount);
696 		else
697 			SET_UI64_RESULT(result, mem_size);
698 	}
699 	else
700 	{
701 		if (ZBX_DO_AVG == do_task)
702 			SET_DBL_RESULT(result, 0 == proccount ? 0 : pct_size / (double)proccount);
703 		else
704 			SET_DBL_RESULT(result, pct_size);
705 	}
706 
707 	return SYSINFO_RET_OK;
708 
709 #undef ZBX_SIZE
710 #undef ZBX_RSS
711 #undef ZBX_VSIZE
712 #undef ZBX_PMEM
713 #undef ZBX_VMPEAK
714 #undef ZBX_VMSWAP
715 #undef ZBX_VMLIB
716 #undef ZBX_VMLCK
717 #undef ZBX_VMPIN
718 #undef ZBX_VMHWM
719 #undef ZBX_VMDATA
720 #undef ZBX_VMSTK
721 #undef ZBX_VMEXE
722 #undef ZBX_VMPTE
723 }
724 
PROC_NUM(AGENT_REQUEST * request,AGENT_RESULT * result)725 int	PROC_NUM(AGENT_REQUEST *request, AGENT_RESULT *result)
726 {
727 	char		tmp[MAX_STRING_LEN], *procname, *proccomm, *param;
728 	DIR		*dir;
729 	struct dirent	*entries;
730 	struct passwd	*usrinfo;
731 	FILE		*f_cmd = NULL, *f_stat = NULL;
732 	int		proccount = 0, invalid_user = 0, zbx_proc_stat;
733 
734 	if (4 < request->nparam)
735 	{
736 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
737 		return SYSINFO_RET_FAIL;
738 	}
739 
740 	procname = get_rparam(request, 0);
741 	param = get_rparam(request, 1);
742 
743 	if (NULL != param && '\0' != *param)
744 	{
745 		errno = 0;
746 
747 		if (NULL == (usrinfo = getpwnam(param)))
748 		{
749 			if (0 != errno)
750 			{
751 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
752 							zbx_strerror(errno)));
753 				return SYSINFO_RET_FAIL;
754 			}
755 
756 			invalid_user = 1;
757 		}
758 	}
759 	else
760 		usrinfo = NULL;
761 
762 	param = get_rparam(request, 2);
763 
764 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "all"))
765 		zbx_proc_stat = ZBX_PROC_STAT_ALL;
766 	else if (0 == strcmp(param, "run"))
767 		zbx_proc_stat = ZBX_PROC_STAT_RUN;
768 	else if (0 == strcmp(param, "sleep"))
769 		zbx_proc_stat = ZBX_PROC_STAT_SLEEP;
770 	else if (0 == strcmp(param, "zomb"))
771 		zbx_proc_stat = ZBX_PROC_STAT_ZOMB;
772 	else if (0 == strcmp(param, "disk"))
773 		zbx_proc_stat = ZBX_PROC_STAT_DISK;
774 	else if (0 == strcmp(param, "trace"))
775 		zbx_proc_stat = ZBX_PROC_STAT_TRACE;
776 	else
777 	{
778 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
779 		return SYSINFO_RET_FAIL;
780 	}
781 
782 	proccomm = get_rparam(request, 3);
783 
784 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
785 		goto out;
786 
787 	if (NULL == (dir = opendir("/proc")))
788 	{
789 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot open /proc: %s", zbx_strerror(errno)));
790 		return SYSINFO_RET_FAIL;
791 	}
792 
793 	while (NULL != (entries = readdir(dir)))
794 	{
795 		zbx_fclose(f_cmd);
796 		zbx_fclose(f_stat);
797 
798 		if (0 == atoi(entries->d_name))
799 			continue;
800 
801 		zbx_snprintf(tmp, sizeof(tmp), "/proc/%s/cmdline", entries->d_name);
802 
803 		if (NULL == (f_cmd = fopen(tmp, "r")))
804 			continue;
805 
806 		zbx_snprintf(tmp, sizeof(tmp), "/proc/%s/status", entries->d_name);
807 
808 		if (NULL == (f_stat = fopen(tmp, "r")))
809 			continue;
810 
811 		if (FAIL == check_procname(f_cmd, f_stat, procname))
812 			continue;
813 
814 		if (FAIL == check_user(f_stat, usrinfo))
815 			continue;
816 
817 		if (FAIL == check_proccomm(f_cmd, proccomm))
818 			continue;
819 
820 		if (FAIL == check_procstate(f_stat, zbx_proc_stat))
821 			continue;
822 
823 		proccount++;
824 	}
825 	zbx_fclose(f_cmd);
826 	zbx_fclose(f_stat);
827 	closedir(dir);
828 out:
829 	SET_UI64_RESULT(result, proccount);
830 
831 	return SYSINFO_RET_OK;
832 }
833 
834 /******************************************************************************
835  *                                                                            *
836  * Function: proc_get_process_name                                            *
837  *                                                                            *
838  * Purpose: returns process name                                              *
839  *                                                                            *
840  * Parameters: pid -      [IN] the process identifier                         *
841  *             procname - [OUT] the process name                              *
842  *                                                                            *
843  * Return value: SUCCEED                                                      *
844  *               FAIL                                                         *
845  *                                                                            *
846  * Comments: The process name is allocated by this function and must be freed *
847  *           by the caller.                                                   *
848  *                                                                            *
849  ******************************************************************************/
proc_get_process_name(pid_t pid,char ** procname)850 static int	proc_get_process_name(pid_t pid, char **procname)
851 {
852 	int	n, fd;
853 	char	tmp[MAX_STRING_LEN], *pend, *pstart;
854 
855 	zbx_snprintf(tmp, sizeof(tmp), "/proc/%d/stat", (int)pid);
856 
857 	if (-1 == (fd = open(tmp, O_RDONLY)))
858 		return FAIL;
859 
860 	n = read(fd, tmp, sizeof(tmp));
861 	close(fd);
862 
863 	if (-1 == n)
864 		return FAIL;
865 
866 	for (pend = tmp + n - 1; ')' != *pend && pend > tmp; pend--)
867 		;
868 
869 	*pend = '\0';
870 
871 	if (NULL == (pstart = strchr(tmp, '(')))
872 		return FAIL;
873 
874 	*procname = zbx_strdup(NULL, pstart + 1);
875 
876 	return SUCCEED;
877 }
878 
879 /******************************************************************************
880  *                                                                            *
881  * Function: proc_get_process_cmdline                                         *
882  *                                                                            *
883  * Purpose: returns process command line                                      *
884  *                                                                            *
885  * Parameters: pid            - [IN] the process identifier                   *
886  *             cmdline        - [OUT] the process command line                *
887  *             cmdline_nbytes - [OUT] the number of bytes in the command line *
888  *                                                                            *
889  * Return value: SUCCEED                                                      *
890  *               FAIL                                                         *
891  *                                                                            *
892  * Comments: The command line is allocated by this function and must be freed *
893  *           by the caller.                                                   *
894  *                                                                            *
895  ******************************************************************************/
proc_get_process_cmdline(pid_t pid,char ** cmdline,size_t * cmdline_nbytes)896 static int	proc_get_process_cmdline(pid_t pid, char **cmdline, size_t *cmdline_nbytes)
897 {
898 	char	tmp[MAX_STRING_LEN];
899 	int	fd, n;
900 	size_t	cmdline_alloc = ZBX_KIBIBYTE;
901 
902 	*cmdline_nbytes = 0;
903 	zbx_snprintf(tmp, sizeof(tmp), "/proc/%d/cmdline", (int)pid);
904 
905 	if (-1 == (fd = open(tmp, O_RDONLY)))
906 		return FAIL;
907 
908 	*cmdline = (char *)zbx_malloc(NULL, cmdline_alloc);
909 
910 	while (0 < (n = read(fd, *cmdline + *cmdline_nbytes, cmdline_alloc - *cmdline_nbytes)))
911 	{
912 		*cmdline_nbytes += n;
913 
914 		if (*cmdline_nbytes == cmdline_alloc)
915 		{
916 			cmdline_alloc *= 2;
917 			*cmdline = (char *)zbx_realloc(*cmdline, cmdline_alloc);
918 		}
919 	}
920 
921 	close(fd);
922 
923 	if (0 < *cmdline_nbytes)
924 	{
925 		/* add terminating NUL if it is missing due to processes setting their titles or other reasons */
926 		if ('\0' != (*cmdline)[*cmdline_nbytes - 1])
927 		{
928 			if (*cmdline_nbytes == cmdline_alloc)
929 			{
930 				cmdline_alloc += 1;
931 				*cmdline = (char *)zbx_realloc(*cmdline, cmdline_alloc);
932 			}
933 
934 			(*cmdline)[*cmdline_nbytes] = '\0';
935 			*cmdline_nbytes += 1;
936 		}
937 	}
938 	else
939 	{
940 		zbx_free(*cmdline);
941 	}
942 
943 	return SUCCEED;
944 }
945 
946 /******************************************************************************
947  *                                                                            *
948  * Function: proc_get_process_uid                                             *
949  *                                                                            *
950  * Purpose: returns process user identifier                                   *
951  *                                                                            *
952  * Parameters: pid - [IN] the process identifier                              *
953  *             uid - [OUT] the user identifier                                *
954  *                                                                            *
955  * Return value: SUCCEED                                                      *
956  *               FAIL                                                         *
957  *                                                                            *
958  ******************************************************************************/
proc_get_process_uid(pid_t pid,uid_t * uid)959 static int	proc_get_process_uid(pid_t pid, uid_t *uid)
960 {
961 	char		tmp[MAX_STRING_LEN];
962 	zbx_stat_t	st;
963 
964 	zbx_snprintf(tmp, sizeof(tmp), "/proc/%d", (int)pid);
965 
966 	if (0 != zbx_stat(tmp, &st))
967 		return FAIL;
968 
969 	*uid = st.st_uid;
970 
971 	return SUCCEED;
972 }
973 
974 /******************************************************************************
975  *                                                                            *
976  * Function: proc_read_value                                                  *
977  *                                                                            *
978  * Purpose: read 64 bit unsigned space or zero character terminated integer   *
979  *          from a text string                                                *
980  *                                                                            *
981  * Parameters: ptr   - [IN] the text string                                   *
982  *             value - [OUT] the parsed value                                 *
983  *                                                                            *
984  * Return value: The length of the parsed text or FAIL if parsing failed.     *
985  *                                                                            *
986  ******************************************************************************/
proc_read_value(const char * ptr,zbx_uint64_t * value)987 static int	proc_read_value(const char *ptr, zbx_uint64_t *value)
988 {
989 	const char	*start = ptr;
990 	int		len;
991 
992 	while (' ' != *ptr && '\0' != *ptr)
993 		ptr++;
994 
995 	len = ptr - start;
996 
997 	if (SUCCEED == is_uint64_n(start, len, value))
998 		return len;
999 
1000 	return FAIL;
1001 }
1002 
1003 /******************************************************************************
1004  *                                                                            *
1005  * Function: proc_read_cpu_util                                               *
1006  *                                                                            *
1007  * Purpose: reads process cpu utilization values from /proc/[pid]/stat file   *
1008  *                                                                            *
1009  * Parameters: procutil - [IN/OUT] the process cpu utilization data           *
1010  *                                                                            *
1011  * Return value: SUCCEED - the process cpu utilization data was read          *
1012  *                         successfully                                       *
1013  *               <0      - otherwise, -errno code is returned                 *
1014  *                                                                            *
1015  ******************************************************************************/
proc_read_cpu_util(zbx_procstat_util_t * procutil)1016 static int	proc_read_cpu_util(zbx_procstat_util_t *procutil)
1017 {
1018 	int	n, offset, fd, ret = SUCCEED;
1019 	char	tmp[MAX_STRING_LEN], *ptr;
1020 
1021 	zbx_snprintf(tmp, sizeof(tmp), "/proc/%d/stat", (int)procutil->pid);
1022 
1023 	if (-1 == (fd = open(tmp, O_RDONLY)))
1024 		return -errno;
1025 
1026 	if (-1 == (n = read(fd, tmp, sizeof(tmp) - 1)))
1027 	{
1028 		ret = -errno;
1029 		goto out;
1030 	}
1031 
1032 	tmp[n] = '\0';
1033 
1034 	/* skip to the end of process name to avoid dealing with possible spaces in process name */
1035 	if (NULL == (ptr = strrchr(tmp, ')')))
1036 	{
1037 		ret = -EFAULT;
1038 		goto out;
1039 	}
1040 
1041 	n = 0;
1042 
1043 	while ('\0' != *ptr)
1044 	{
1045 		if (' ' != *ptr++)
1046 			continue;
1047 
1048 		switch (++n)
1049 		{
1050 			case 12:
1051 				if (FAIL == (offset = proc_read_value(ptr, &procutil->utime)))
1052 				{
1053 					ret = -EINVAL;
1054 					goto out;
1055 				}
1056 				ptr += offset;
1057 
1058 				break;
1059 			case 13:
1060 				if (FAIL == (offset = proc_read_value(ptr, &procutil->stime)))
1061 				{
1062 					ret = -EINVAL;
1063 					goto out;
1064 				}
1065 				ptr += offset;
1066 
1067 				break;
1068 			case 20:
1069 				if (FAIL == proc_read_value(ptr, &procutil->starttime))
1070 				{
1071 					ret = -EINVAL;
1072 					goto out;
1073 				}
1074 
1075 				goto out;
1076 		}
1077 	}
1078 
1079 	ret = -ENODATA;
1080 out:
1081 	close(fd);
1082 
1083 	return ret;
1084 }
1085 
1086 /******************************************************************************
1087  *                                                                            *
1088  * Function: proc_match_name                                                  *
1089  *                                                                            *
1090  * Purpose: checks if the process name matches filter                         *
1091  *                                                                            *
1092  ******************************************************************************/
proc_match_name(const zbx_sysinfo_proc_t * proc,const char * procname)1093 static int	proc_match_name(const zbx_sysinfo_proc_t *proc, const char *procname)
1094 {
1095 	if (NULL == procname)
1096 		return SUCCEED;
1097 
1098 	if (NULL != proc->name && 0 == strcmp(procname, proc->name))
1099 		return SUCCEED;
1100 
1101 	if (NULL != proc->name_arg0 && 0 == strcmp(procname, proc->name_arg0))
1102 		return SUCCEED;
1103 
1104 	return FAIL;
1105 }
1106 
1107 /******************************************************************************
1108  *                                                                            *
1109  * Function: proc_match_user                                                  *
1110  *                                                                            *
1111  * Purpose: checks if the process user matches filter                         *
1112  *                                                                            *
1113  ******************************************************************************/
proc_match_user(const zbx_sysinfo_proc_t * proc,const struct passwd * usrinfo)1114 static int	proc_match_user(const zbx_sysinfo_proc_t *proc, const struct passwd *usrinfo)
1115 {
1116 	if (NULL == usrinfo)
1117 		return SUCCEED;
1118 
1119 	if (proc->uid == usrinfo->pw_uid)
1120 		return SUCCEED;
1121 
1122 	return FAIL;
1123 }
1124 
1125 /******************************************************************************
1126  *                                                                            *
1127  * Function: proc_match_cmdline                                               *
1128  *                                                                            *
1129  * Purpose: checks if the process command line matches filter                 *
1130  *                                                                            *
1131  ******************************************************************************/
proc_match_cmdline(const zbx_sysinfo_proc_t * proc,const char * cmdline)1132 static int	proc_match_cmdline(const zbx_sysinfo_proc_t *proc, const char *cmdline)
1133 {
1134 	if (NULL == cmdline)
1135 		return SUCCEED;
1136 
1137 	if (NULL != proc->cmdline && NULL != zbx_regexp_match(proc->cmdline, cmdline, NULL))
1138 		return SUCCEED;
1139 
1140 	return FAIL;
1141 }
1142 
1143 /******************************************************************************
1144  *                                                                            *
1145  * Function: zbx_proc_get_process_stats                                       *
1146  *                                                                            *
1147  * Purpose: get process cpu utilization data                                  *
1148  *                                                                            *
1149  * Parameters: procs     - [IN/OUT] an array of process utilization data      *
1150  *             procs_num - [IN] the number of items in procs array            *
1151  *                                                                            *
1152  ******************************************************************************/
zbx_proc_get_process_stats(zbx_procstat_util_t * procs,int procs_num)1153 void	zbx_proc_get_process_stats(zbx_procstat_util_t *procs, int procs_num)
1154 {
1155 	int	i;
1156 
1157 	zabbix_log(LOG_LEVEL_TRACE, "In %s() procs_num:%d", __func__, procs_num);
1158 
1159 	for (i = 0; i < procs_num; i++)
1160 		procs[i].error = proc_read_cpu_util(&procs[i]);
1161 
1162 	zabbix_log(LOG_LEVEL_TRACE, "End of %s()", __func__);
1163 }
1164 
1165 /******************************************************************************
1166  *                                                                            *
1167  * Function: proc_create                                                      *
1168  *                                                                            *
1169  * Purpose: create process object with the specified properties               *
1170  *                                                                            *
1171  * Parameters: pid   - [IN] the process identifier                            *
1172  *             flags - [IN] the flags specifying properties to set            *
1173  *                                                                            *
1174  * Return value: The created process object or NULL if property reading       *
1175  *               failed.                                                      *
1176  *                                                                            *
1177  ******************************************************************************/
proc_create(int pid,unsigned int flags)1178 static zbx_sysinfo_proc_t	*proc_create(int pid, unsigned int flags)
1179 {
1180 	char			*procname = NULL, *cmdline = NULL, *name_arg0 = NULL;
1181 	uid_t			uid = (uid_t)-1;
1182 	zbx_sysinfo_proc_t	*proc = NULL;
1183 	int			ret = FAIL;
1184 	size_t			cmdline_nbytes;
1185 
1186 	if (0 != (flags & ZBX_SYSINFO_PROC_USER) && SUCCEED != proc_get_process_uid(pid, &uid))
1187 		goto out;
1188 
1189 	if (0 != (flags & (ZBX_SYSINFO_PROC_CMDLINE | ZBX_SYSINFO_PROC_NAME)) &&
1190 			SUCCEED != proc_get_process_cmdline(pid, &cmdline, &cmdline_nbytes))
1191 	{
1192 		goto out;
1193 	}
1194 
1195 	if (0 != (flags & ZBX_SYSINFO_PROC_NAME) && SUCCEED != proc_get_process_name(pid, &procname))
1196 		goto out;
1197 
1198 	if (NULL != cmdline)
1199 	{
1200 		char		*ptr;
1201 		unsigned int	i;
1202 
1203 		if (0 != (flags & ZBX_SYSINFO_PROC_NAME))
1204 		{
1205 			if (NULL == (ptr = strrchr(cmdline, '/')))
1206 				name_arg0 = zbx_strdup(NULL, cmdline);
1207 			else
1208 				name_arg0 = zbx_strdup(NULL, ptr + 1);
1209 		}
1210 
1211 		/* according to proc(5) the arguments are separated by '\0' */
1212 		for (i = 0; i < cmdline_nbytes - 1; i++)
1213 			if ('\0' == cmdline[i])
1214 				cmdline[i] = ' ';
1215 	}
1216 
1217 	ret = SUCCEED;
1218 out:
1219 	if (SUCCEED == ret)
1220 	{
1221 		proc = (zbx_sysinfo_proc_t *)zbx_malloc(NULL, sizeof(zbx_sysinfo_proc_t));
1222 
1223 		proc->pid = pid;
1224 		proc->uid = uid;
1225 		proc->name = procname;
1226 		proc->cmdline = cmdline;
1227 		proc->name_arg0 = name_arg0;
1228 	}
1229 	else
1230 	{
1231 		zbx_free(procname);
1232 		zbx_free(cmdline);
1233 		zbx_free(name_arg0);
1234 	}
1235 
1236 	return proc;
1237 }
1238 
1239 /******************************************************************************
1240  *                                                                            *
1241  * Function: zbx_proc_get_processes                                           *
1242  *                                                                            *
1243  * Purpose: get system processes                                              *
1244  *                                                                            *
1245  * Parameters: processes - [OUT] the system processes                         *
1246  *             flags     - [IN] the flags specifying the process properties   *
1247  *                              that must be returned                         *
1248  *                                                                            *
1249  * Return value: SUCCEED - the system processes were retrieved successfully   *
1250  *               FAIL    - failed to open /proc directory                     *
1251  *                                                                            *
1252  ******************************************************************************/
zbx_proc_get_processes(zbx_vector_ptr_t * processes,unsigned int flags)1253 int	zbx_proc_get_processes(zbx_vector_ptr_t *processes, unsigned int flags)
1254 {
1255 	DIR			*dir;
1256 	struct dirent		*entries;
1257 	int			ret = FAIL, pid;
1258 	zbx_sysinfo_proc_t	*proc;
1259 
1260 	zabbix_log(LOG_LEVEL_TRACE, "In %s()", __func__);
1261 
1262 	if (NULL == (dir = opendir("/proc")))
1263 		goto out;
1264 
1265 	while (NULL != (entries = readdir(dir)))
1266 	{
1267 		/* skip entries not containing pids */
1268 		if (FAIL == is_uint32(entries->d_name, &pid))
1269 			continue;
1270 
1271 		if (NULL == (proc = proc_create(pid, flags)))
1272 			continue;
1273 
1274 		zbx_vector_ptr_append(processes, proc);
1275 	}
1276 
1277 	closedir(dir);
1278 
1279 	ret = SUCCEED;
1280 out:
1281 	zabbix_log(LOG_LEVEL_TRACE, "End of %s(): %s, processes:%d", __func__, zbx_result_string(ret),
1282 			processes->values_num);
1283 
1284 	return ret;
1285 }
1286 
1287 /******************************************************************************
1288  *                                                                            *
1289  * Function: zbx_proc_free_processes                                          *
1290  *                                                                            *
1291  * Purpose: frees process vector read by zbx_proc_get_processes function      *
1292  *                                                                            *
1293  * Parameters: processes - [IN/OUT] the process vector to free                *
1294  *                                                                            *
1295  ******************************************************************************/
zbx_proc_free_processes(zbx_vector_ptr_t * processes)1296 void	zbx_proc_free_processes(zbx_vector_ptr_t *processes)
1297 {
1298 	zbx_vector_ptr_clear_ext(processes, (zbx_mem_free_func_t)zbx_sysinfo_proc_free);
1299 }
1300 
1301 /******************************************************************************
1302  *                                                                            *
1303  * Function: zbx_proc_get_matching_pids                                       *
1304  *                                                                            *
1305  * Purpose: get pids matching the specified process name, user name and       *
1306  *          command line                                                      *
1307  *                                                                            *
1308  * Parameters: processes   - [IN] the list of system processes                *
1309  *             procname    - [IN] the process name, NULL - all                *
1310  *             username    - [IN] the user name, NULL - all                   *
1311  *             cmdline     - [IN] the command line, NULL - all                *
1312  *             pids        - [OUT] the vector of matching pids                *
1313  *                                                                            *
1314  * Return value: SUCCEED   - the pids were read successfully                  *
1315  *               -errno    - failed to read pids                              *
1316  *                                                                            *
1317  ******************************************************************************/
zbx_proc_get_matching_pids(const zbx_vector_ptr_t * processes,const char * procname,const char * username,const char * cmdline,zbx_uint64_t flags,zbx_vector_uint64_t * pids)1318 void	zbx_proc_get_matching_pids(const zbx_vector_ptr_t *processes, const char *procname, const char *username,
1319 		const char *cmdline, zbx_uint64_t flags, zbx_vector_uint64_t *pids)
1320 {
1321 	struct passwd		*usrinfo;
1322 	int			i;
1323 	zbx_sysinfo_proc_t	*proc;
1324 
1325 	zabbix_log(LOG_LEVEL_TRACE, "In %s() procname:%s username:%s cmdline:%s flags:" ZBX_FS_UI64, __func__,
1326 			ZBX_NULL2EMPTY_STR(procname), ZBX_NULL2EMPTY_STR(username), ZBX_NULL2EMPTY_STR(cmdline), flags);
1327 
1328 	if (NULL != username)
1329 	{
1330 		/* in the case of invalid user there are no matching processes, return empty vector */
1331 		if (NULL == (usrinfo = getpwnam(username)))
1332 			goto out;
1333 	}
1334 	else
1335 		usrinfo = NULL;
1336 
1337 	for (i = 0; i < processes->values_num; i++)
1338 	{
1339 		proc = (zbx_sysinfo_proc_t *)processes->values[i];
1340 
1341 		if (SUCCEED != proc_match_user(proc, usrinfo))
1342 			continue;
1343 
1344 		if (SUCCEED != proc_match_name(proc, procname))
1345 			continue;
1346 
1347 		if (SUCCEED != proc_match_cmdline(proc, cmdline))
1348 			continue;
1349 
1350 		zbx_vector_uint64_append(pids, (zbx_uint64_t)proc->pid);
1351 	}
1352 out:
1353 	zabbix_log(LOG_LEVEL_TRACE, "End of %s()", __func__);
1354 }
1355 
PROC_CPU_UTIL(AGENT_REQUEST * request,AGENT_RESULT * result)1356 int	PROC_CPU_UTIL(AGENT_REQUEST *request, AGENT_RESULT *result)
1357 {
1358 	const char	*procname, *username, *cmdline, *tmp;
1359 	char		*errmsg = NULL;
1360 	int		period, type;
1361 	double		value;
1362 	zbx_timespec_t	ts_timeout, ts;
1363 
1364 	/* proc.cpu.util[<procname>,<username>,(user|system),<cmdline>,(avg1|avg5|avg15)] */
1365 	/*                   0          1           2            3             4          */
1366 	if (5 < request->nparam)
1367 	{
1368 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
1369 		return SYSINFO_RET_FAIL;
1370 	}
1371 
1372 	/* zbx_procstat_get_* functions expect NULL for default values -       */
1373 	/* convert empty procname, username and cmdline strings to NULL values */
1374 	if (NULL != (procname = get_rparam(request, 0)) && '\0' == *procname)
1375 		procname = NULL;
1376 
1377 	if (NULL != (username = get_rparam(request, 1)) && '\0' == *username)
1378 		username = NULL;
1379 
1380 	if (NULL != (cmdline = get_rparam(request, 3)) && '\0' == *cmdline)
1381 		cmdline = NULL;
1382 
1383 	/* utilization type parameter (user|system) */
1384 	if (NULL == (tmp = get_rparam(request, 2)) || '\0' == *tmp || 0 == strcmp(tmp, "total"))
1385 	{
1386 		type = ZBX_PROCSTAT_CPU_TOTAL;
1387 	}
1388 	else if (0 == strcmp(tmp, "user"))
1389 	{
1390 		type = ZBX_PROCSTAT_CPU_USER;
1391 	}
1392 	else if (0 == strcmp(tmp, "system"))
1393 	{
1394 		type = ZBX_PROCSTAT_CPU_SYSTEM;
1395 	}
1396 	else
1397 	{
1398 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
1399 		return SYSINFO_RET_FAIL;
1400 	}
1401 
1402 	/* mode parameter (avg1|avg5|avg15) */
1403 	if (NULL == (tmp = get_rparam(request, 4)) || '\0' == *tmp || 0 == strcmp(tmp, "avg1"))
1404 	{
1405 		period = SEC_PER_MIN;
1406 	}
1407 	else if (0 == strcmp(tmp, "avg5"))
1408 	{
1409 		period = SEC_PER_MIN * 5;
1410 	}
1411 	else if (0 == strcmp(tmp, "avg15"))
1412 	{
1413 		period = SEC_PER_MIN * 15;
1414 	}
1415 	else
1416 	{
1417 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
1418 		return SYSINFO_RET_FAIL;
1419 	}
1420 
1421 	if (SUCCEED != zbx_procstat_collector_started())
1422 	{
1423 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Collector is not started."));
1424 		return SYSINFO_RET_FAIL;
1425 	}
1426 
1427 	zbx_timespec(&ts_timeout);
1428 	ts_timeout.sec += CONFIG_TIMEOUT;
1429 
1430 	while (SUCCEED != zbx_procstat_get_util(procname, username, cmdline, 0, period, type, &value, &errmsg))
1431 	{
1432 		/* zbx_procstat_get_* functions will return FAIL when either a collection   */
1433 		/* error was registered or if less than 2 data samples were collected.      */
1434 		/* In the first case the errmsg will contain error message.                 */
1435 		if (NULL != errmsg)
1436 		{
1437 			SET_MSG_RESULT(result, errmsg);
1438 			return SYSINFO_RET_FAIL;
1439 		}
1440 
1441 		zbx_timespec(&ts);
1442 
1443 		if (0 > zbx_timespec_compare(&ts_timeout, &ts))
1444 		{
1445 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Timeout while waiting for collector data."));
1446 			return SYSINFO_RET_FAIL;
1447 		}
1448 
1449 		sleep(1);
1450 	}
1451 
1452 	SET_DBL_RESULT(result, value);
1453 
1454 	return SYSINFO_RET_OK;
1455 }
1456