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 <procfs.h>
21 #include "common.h"
22 #include "sysinfo.h"
23 #include "zbxregexp.h"
24 #include "log.h"
25 #include "stats.h"
26 
27 #if !defined(HAVE_ZONE_H) && defined(HAVE_SYS_UTSNAME_H)
28 #	include <sys/utsname.h>
29 #endif
30 
31 extern int	CONFIG_TIMEOUT;
32 
33 typedef struct
34 {
35 	pid_t		pid;
36 	uid_t		uid;
37 
38 	char		*name;
39 
40 	/* process name extracted from the first argument (usually executable path) */
41 	char		*name_arg0;
42 
43 	/* process command line in format <arg0> <arg1> ... <argN>\0 */
44 	char		*cmdline;
45 
46 
47 #ifdef HAVE_ZONE_H
48 	zoneid_t	zoneid;
49 #endif
50 }
51 zbx_sysinfo_proc_t;
52 
53 #ifndef HAVE_ZONE_H
54 /* helper functions for case if agent is compiled on Solaris 9 or earlier where zones are not supported */
55 /* but is running on a newer Solaris where zones are supported */
56 
57 /******************************************************************************
58  *                                                                            *
59  * Function: zbx_solaris_version_get                                          *
60  *                                                                            *
61  * Purpose: get Solaris version at runtime                                    *
62  *                                                                            *
63  * Parameters:                                                                *
64  *     major_version - [OUT] major version (e.g. 5)                           *
65  *     minor_version - [OUT] minor version (e.g. 9 for Solaris 9, 10 for      *
66  *                           Solaris 10, 11 for Solaris 11)                   *
67  * Return value:                                                              *
68  *     SUCCEED - no errors, FAIL - an error occurred                          *
69  *                                                                            *
70  ******************************************************************************/
zbx_solaris_version_get(unsigned int * major_version,unsigned int * minor_version)71 static int	zbx_solaris_version_get(unsigned int *major_version, unsigned int *minor_version)
72 {
73 	const char	*__function_name = "zbx_solaris_version_get";
74 	int		res;
75 	struct utsname	name;
76 
77 	if (-1 == (res = uname(&name)))
78 	{
79 		zabbix_log(LOG_LEVEL_WARNING, "%s(): uname() failed: %s", __function_name, zbx_strerror(errno));
80 
81 		return FAIL;
82 	}
83 
84 	/* expected result in name.release: "5.9" - Solaris 9, "5.10" - Solaris 10, "5.11" - Solaris 11 */
85 
86 	if (2 != sscanf(name.release, "%u.%u", major_version, minor_version))
87 	{
88 		zabbix_log(LOG_LEVEL_WARNING, "%s(): sscanf() failed on: \"%s\"", __function_name, name.release);
89 		THIS_SHOULD_NEVER_HAPPEN;
90 
91 		return FAIL;
92 	}
93 
94 	return SUCCEED;
95 }
96 
97 /******************************************************************************
98  *                                                                            *
99  * Function: zbx_detect_zone_support                                          *
100  *                                                                            *
101  * Purpose: find if zones are supported                                       *
102  *                                                                            *
103  * Return value:                                                              *
104  *     SUCCEED - zones supported                                              *
105  *     FAIL - zones not supported or error occurred. For our purposes error   *
106  *            counts as no support for zones.                                 *
107  *                                                                            *
108  ******************************************************************************/
zbx_detect_zone_support(void)109 static int	zbx_detect_zone_support(void)
110 {
111 #define ZBX_ZONE_SUPPORT_UNKNOWN	0
112 #define ZBX_ZONE_SUPPORT_YES		1
113 #define ZBX_ZONE_SUPPORT_NO		2
114 
115 	static int	zone_support = ZBX_ZONE_SUPPORT_UNKNOWN;
116 	unsigned int	major, minor;
117 
118 	switch (zone_support)
119 	{
120 		case ZBX_ZONE_SUPPORT_NO:
121 			return FAIL;
122 		case ZBX_ZONE_SUPPORT_YES:
123 			return SUCCEED;
124 		default:
125 			/* zones are supported in Solaris 10 and later (minimum version is "5.10") */
126 
127 			if (SUCCEED == zbx_solaris_version_get(&major, &minor) &&
128 					((5 == major && 10 <= minor) || 5 < major))
129 			{
130 				zone_support = ZBX_ZONE_SUPPORT_YES;
131 				return SUCCEED;
132 			}
133 			else	/* failure to get Solaris version also results in "zones not supported" */
134 			{
135 				zone_support = ZBX_ZONE_SUPPORT_NO;
136 				return FAIL;
137 			}
138 	}
139 }
140 #endif
141 
zbx_sysinfo_proc_clear(zbx_sysinfo_proc_t * proc)142 static void	zbx_sysinfo_proc_clear(zbx_sysinfo_proc_t *proc)
143 {
144 	zbx_free(proc->name);
145 	zbx_free(proc->cmdline);
146 	zbx_free(proc->name_arg0);
147 }
148 
149 /******************************************************************************
150  *                                                                            *
151  * Function: zbx_sysinfo_proc_free                                            *
152  *                                                                            *
153  * Purpose: frees process data structure                                      *
154  *                                                                            *
155  ******************************************************************************/
zbx_sysinfo_proc_free(zbx_sysinfo_proc_t * proc)156 static void	zbx_sysinfo_proc_free(zbx_sysinfo_proc_t *proc)
157 {
158 	zbx_sysinfo_proc_clear(proc);
159 	zbx_free(proc);
160 }
161 
check_procstate(psinfo_t * psinfo,int zbx_proc_stat)162 static int	check_procstate(psinfo_t *psinfo, int zbx_proc_stat)
163 {
164 	if (zbx_proc_stat == ZBX_PROC_STAT_ALL)
165 		return SUCCEED;
166 
167 	switch (zbx_proc_stat)
168 	{
169 		case ZBX_PROC_STAT_RUN:
170 			return (psinfo->pr_lwp.pr_state == SRUN || psinfo->pr_lwp.pr_state == SONPROC) ? SUCCEED : FAIL;
171 		case ZBX_PROC_STAT_SLEEP:
172 			return (psinfo->pr_lwp.pr_state == SSLEEP) ? SUCCEED : FAIL;
173 		case ZBX_PROC_STAT_ZOMB:
174 			return (psinfo->pr_lwp.pr_state == SZOMB) ? SUCCEED : FAIL;
175 	}
176 
177 	return FAIL;
178 }
179 
get_cmdline(FILE * f_cmd,char ** line,size_t * line_offset)180 static int	get_cmdline(FILE *f_cmd, char **line, size_t *line_offset)
181 {
182 	size_t	line_alloc = ZBX_KIBIBYTE, n;
183 
184 	rewind(f_cmd);
185 
186 	*line = (char *)zbx_malloc(*line, line_alloc + 2);
187 	*line_offset = 0;
188 
189 	while (0 != (n = fread(*line + *line_offset, 1, line_alloc - *line_offset, f_cmd)))
190 	{
191 		*line_offset += n;
192 
193 		if (0 != feof(f_cmd))
194 			break;
195 
196 		line_alloc *= 2;
197 		*line = (char *)zbx_realloc(*line, line_alloc + 2);
198 	}
199 
200 	if (0 == ferror(f_cmd))
201 	{
202 		if (0 == *line_offset || '\0' != (*line)[*line_offset - 1])
203 			(*line)[(*line_offset)++] = '\0';
204 		if (1 == *line_offset || '\0' != (*line)[*line_offset - 2])
205 			(*line)[(*line_offset)++] = '\0';
206 
207 		return SUCCEED;
208 	}
209 
210 	zbx_free(*line);
211 
212 	return FAIL;
213 }
214 
215 /******************************************************************************
216  *                                                                            *
217  * Function: proc_get_process_info                                            *
218  *                                                                            *
219  * Purpose: get single process information                                    *
220  *                                                                            *
221  * Parameters: pid    - [IN] the process pid                                  *
222  *             flags  - [IN] the flags specifying the process properties      *
223  *                          that must be returned                             *
224  *             proc   - [OUT] the process data                                *
225  *             psinfo - [OUT] the raw process information data                *
226  * Return value: SUCCEED - the process information was retrieved successfully *
227  *               FAIL    - otherwise                                          *
228  *                                                                            *
229  ******************************************************************************/
proc_get_process_info(const char * pid,unsigned int flags,zbx_sysinfo_proc_t * proc,psinfo_t * psinfo)230 static int	proc_get_process_info(const char *pid, unsigned int flags, zbx_sysinfo_proc_t *proc, psinfo_t *psinfo)
231 {
232 	int		fd, n;
233 	char		path[MAX_STRING_LEN];
234 	FILE		*fp;
235 	psinfo_t	psinfo_local;
236 
237 	/* skip entries not containing pids */
238 	memset(proc, 0, sizeof(zbx_sysinfo_proc_t));
239 	if (FAIL == is_uint32(pid, &proc->pid))
240 		return FAIL;
241 
242 	zbx_snprintf(path, sizeof(path), "/proc/%s/psinfo", pid);
243 
244 	if (-1 == (fd = open(path, O_RDONLY)))
245 		return FAIL;
246 
247 	if (NULL == psinfo)
248 		psinfo = &psinfo_local;
249 
250 	n = read(fd, psinfo, sizeof(*psinfo));
251 	close(fd);
252 
253 	if (-1 == n)
254 		return FAIL;
255 
256 #ifdef HAVE_ZONE_H
257 	proc->zoneid = psinfo->pr_zoneid;
258 #endif
259 
260 	if (0 != (flags & ZBX_SYSINFO_PROC_USER))
261 		proc->uid = psinfo->pr_uid;
262 
263 	if (0 != (flags & ZBX_SYSINFO_PROC_NAME))
264 		proc->name = zbx_strdup(NULL, psinfo->pr_fname);
265 
266 	if (0 != (flags & ZBX_SYSINFO_PROC_NAME) || 0 != (flags & ZBX_SYSINFO_PROC_CMDLINE))
267 	{
268 		zbx_snprintf(path, sizeof(path), "/proc/%s/cmdline", pid);
269 		if (NULL != (fp = fopen(path, "r")))
270 		{
271 			char	*line = NULL, *ptr;
272 			size_t	l;
273 
274 			if (SUCCEED == get_cmdline(fp, &line, &l))
275 			{
276 				if (0 != (flags & ZBX_SYSINFO_PROC_NAME))
277 				{
278 					if (NULL == (ptr = strrchr(line, '/')))
279 						proc->name_arg0 = zbx_strdup(NULL, line);
280 					else
281 						proc->name_arg0 = zbx_strdup(NULL, ptr + 1);
282 				}
283 
284 				if (0 != (flags & ZBX_SYSINFO_PROC_CMDLINE))
285 				{
286 					int	i;
287 
288 					for (i = 0, l -= 2; i < l; i++)
289 						if ('\0' == line[i])
290 							line[i] = ' ';
291 
292 					proc->cmdline = zbx_strdup(NULL, line);
293 				}
294 				zbx_free(line);
295 			}
296 			fclose(fp);
297 		}
298 
299 		if (0 != (flags & ZBX_SYSINFO_PROC_CMDLINE) && NULL == proc->cmdline)
300 			proc->cmdline = zbx_strdup(NULL, psinfo->pr_psargs);
301 	}
302 
303 	return SUCCEED;
304 }
305 
306 /******************************************************************************
307  *                                                                            *
308  * Function: proc_match_name                                                  *
309  *                                                                            *
310  * Purpose: checks if the process name matches filter                         *
311  *                                                                            *
312  ******************************************************************************/
proc_match_name(const zbx_sysinfo_proc_t * proc,const char * procname)313 static int	proc_match_name(const zbx_sysinfo_proc_t *proc, const char *procname)
314 {
315 	if (NULL == procname)
316 		return SUCCEED;
317 
318 	if (NULL != proc->name && 0 == strcmp(procname, proc->name))
319 		return SUCCEED;
320 
321 	if (NULL != proc->name_arg0 && 0 == strcmp(procname, proc->name_arg0))
322 		return SUCCEED;
323 
324 	return FAIL;
325 }
326 
327 /******************************************************************************
328  *                                                                            *
329  * Function: proc_match_user                                                  *
330  *                                                                            *
331  * Purpose: checks if the process user matches filter                         *
332  *                                                                            *
333  ******************************************************************************/
proc_match_user(const zbx_sysinfo_proc_t * proc,const struct passwd * usrinfo)334 static int	proc_match_user(const zbx_sysinfo_proc_t *proc, const struct passwd *usrinfo)
335 {
336 	if (NULL == usrinfo)
337 		return SUCCEED;
338 
339 	if (proc->uid == usrinfo->pw_uid)
340 		return SUCCEED;
341 
342 	return FAIL;
343 }
344 
345 /******************************************************************************
346  *                                                                            *
347  * Function: proc_match_cmdline                                               *
348  *                                                                            *
349  * Purpose: checks if the process command line matches filter                 *
350  *                                                                            *
351  ******************************************************************************/
proc_match_cmdline(const zbx_sysinfo_proc_t * proc,const char * cmdline)352 static int	proc_match_cmdline(const zbx_sysinfo_proc_t *proc, const char *cmdline)
353 {
354 	if (NULL == cmdline)
355 		return SUCCEED;
356 
357 	if (NULL != proc->cmdline && NULL != zbx_regexp_match(proc->cmdline, cmdline, NULL))
358 		return SUCCEED;
359 
360 	return FAIL;
361 }
362 
363 #ifdef HAVE_ZONE_H
364 /******************************************************************************
365  *                                                                            *
366  * Function: proc_match_zone                                                  *
367  *                                                                            *
368  * Purpose: checks if the process zone matches filter                         *
369  *                                                                            *
370  ******************************************************************************/
proc_match_zone(const zbx_sysinfo_proc_t * proc,zbx_uint64_t flags,zoneid_t zoneid)371 static int	proc_match_zone(const zbx_sysinfo_proc_t *proc, zbx_uint64_t flags, zoneid_t zoneid)
372 {
373 	if (0 != (ZBX_PROCSTAT_FLAGS_ZONE_ALL & flags))
374 		return SUCCEED;
375 
376 	if (proc->zoneid == zoneid)
377 		return SUCCEED;
378 
379 	return FAIL;
380 }
381 #endif
382 
383 /******************************************************************************
384  *                                                                            *
385  * Function: proc_match_props                                                 *
386  *                                                                            *
387  * Purpose: checks if the process properties (except zone) matches filter     *
388  *                                                                            *
389  ******************************************************************************/
proc_match_props(const zbx_sysinfo_proc_t * proc,const struct passwd * usrinfo,const char * procname,const char * cmdline)390 static int	proc_match_props(const zbx_sysinfo_proc_t *proc, const struct passwd *usrinfo, const char *procname,
391 		const char *cmdline)
392 {
393 	if (SUCCEED != proc_match_user(proc, usrinfo))
394 		return FAIL;
395 
396 	if (SUCCEED != proc_match_name(proc, procname))
397 		return FAIL;
398 
399 	if (SUCCEED != proc_match_cmdline(proc, cmdline))
400 		return FAIL;
401 
402 	return SUCCEED;
403 }
404 
PROC_MEM(AGENT_REQUEST * request,AGENT_RESULT * result)405 int	PROC_MEM(AGENT_REQUEST *request, AGENT_RESULT *result)
406 {
407 	char			tmp[MAX_STRING_LEN], *procname, *proccomm, *param, *memtype = NULL;
408 	DIR			*dir;
409 	struct dirent		*entries;
410 	struct passwd		*usrinfo;
411 	psinfo_t		psinfo;	/* In the correct procfs.h, the structure name is psinfo_t */
412 	int			do_task, proccount = 0, invalid_user = 0, proc_props = 0;
413 	zbx_uint64_t		mem_size = 0, byte_value = 0;
414 	double			pct_size = 0.0, pct_value = 0.0;
415 	size_t			*p_value;
416 
417 	if (5 < request->nparam)
418 	{
419 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
420 		return SYSINFO_RET_FAIL;
421 	}
422 
423 	if (NULL != (procname = get_rparam(request, 0)) && '\0' != *procname)
424 		proc_props |= ZBX_SYSINFO_PROC_NAME;
425 	else
426 		procname = NULL;
427 
428 	param = get_rparam(request, 1);
429 
430 	if (NULL != param && '\0' != *param)
431 	{
432 		proc_props |= ZBX_SYSINFO_PROC_USER;
433 		errno = 0;
434 
435 		if (NULL == (usrinfo = getpwnam(param)))
436 		{
437 			if (0 != errno)
438 			{
439 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
440 						zbx_strerror(errno)));
441 				return SYSINFO_RET_FAIL;
442 			}
443 
444 			invalid_user = 1;
445 		}
446 	}
447 	else
448 		usrinfo = NULL;
449 
450 	param = get_rparam(request, 2);
451 
452 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "sum"))
453 		do_task = ZBX_DO_SUM;
454 	else if (0 == strcmp(param, "avg"))
455 		do_task = ZBX_DO_AVG;
456 	else if (0 == strcmp(param, "max"))
457 		do_task = ZBX_DO_MAX;
458 	else if (0 == strcmp(param, "min"))
459 		do_task = ZBX_DO_MIN;
460 	else
461 	{
462 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
463 		return SYSINFO_RET_FAIL;
464 	}
465 
466 	if (NULL != (proccomm = get_rparam(request, 3)) && '\0' != *proccomm)
467 		proc_props |= ZBX_SYSINFO_PROC_CMDLINE;
468 	else
469 		proccomm = NULL;
470 
471 	memtype = get_rparam(request, 4);
472 
473 	if (NULL == memtype || '\0' == *memtype || 0 == strcmp(memtype, "vsize"))
474 	{
475 		p_value = &psinfo.pr_size;	/* size of process image in Kbytes */
476 	}
477 	else if (0 == strcmp(memtype, "rss"))
478 	{
479 		p_value = &psinfo.pr_rssize;	/* resident set size in Kbytes */
480 	}
481 	else if (0 == strcmp(memtype, "pmem"))
482 	{
483 		p_value = NULL;			/* for % of system memory used by process */
484 	}
485 	else
486 	{
487 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
488 		return SYSINFO_RET_FAIL;
489 	}
490 
491 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
492 		goto out;
493 
494 	if (NULL == (dir = opendir("/proc")))
495 	{
496 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot open /proc: %s", zbx_strerror(errno)));
497 		return SYSINFO_RET_FAIL;
498 	}
499 
500 	while (NULL != (entries = readdir(dir)))
501 	{
502 		zbx_sysinfo_proc_t	proc;
503 
504 		if (SUCCEED != proc_get_process_info(entries->d_name, proc_props, &proc, &psinfo))
505 			continue;
506 
507 		if (SUCCEED == proc_match_props(&proc, usrinfo, procname, proccomm))
508 		{
509 			if (NULL != p_value)
510 			{
511 				/* pr_size or pr_rssize in Kbytes */
512 				byte_value = *p_value << 10;	/* kB to Byte */
513 
514 				if (0 != proccount++)
515 				{
516 					if (ZBX_DO_MAX == do_task)
517 						mem_size = MAX(mem_size, byte_value);
518 					else if (ZBX_DO_MIN == do_task)
519 						mem_size = MIN(mem_size, byte_value);
520 					else
521 						mem_size += byte_value;
522 				}
523 				else
524 					mem_size = byte_value;
525 			}
526 			else
527 			{
528 				/* % of system memory used by process, measured in 16-bit binary fractions in the range */
529 				/* 0.0 - 1.0 with the binary point to the right of the most significant bit. 1.0 == 0x8000 */
530 				pct_value = (double)((int)psinfo.pr_pctmem * 100) / 32768.0;
531 
532 				if (0 != proccount++)
533 				{
534 					if (ZBX_DO_MAX == do_task)
535 						pct_size = MAX(pct_size, pct_value);
536 					else if (ZBX_DO_MIN == do_task)
537 						pct_size = MIN(pct_size, pct_value);
538 					else
539 						pct_size += pct_value;
540 				}
541 				else
542 					pct_size = pct_value;
543 			}
544 		}
545 
546 		zbx_sysinfo_proc_clear(&proc);
547 	}
548 
549 	closedir(dir);
550 out:
551 	if (NULL != p_value)
552 	{
553 		if (ZBX_DO_AVG == do_task)
554 			SET_DBL_RESULT(result, 0 == proccount ? 0.0 : (double)mem_size / (double)proccount);
555 		else
556 			SET_UI64_RESULT(result, mem_size);
557 	}
558 	else
559 	{
560 		if (ZBX_DO_AVG == do_task)
561 			SET_DBL_RESULT(result, 0 == proccount ? 0.0 : pct_size / (double)proccount);
562 		else
563 			SET_DBL_RESULT(result, pct_size);
564 	}
565 
566 	return SYSINFO_RET_OK;
567 }
568 
PROC_NUM(AGENT_REQUEST * request,AGENT_RESULT * result)569 int	PROC_NUM(AGENT_REQUEST *request, AGENT_RESULT *result)
570 {
571 	char			tmp[MAX_STRING_LEN], *procname, *proccomm, *param, *zone_parameter;
572 	DIR			*dir;
573 	struct dirent		*entries;
574 	zbx_stat_t		buf;
575 	struct passwd		*usrinfo;
576 	psinfo_t		psinfo;	/* In the correct procfs.h, the structure name is psinfo_t */
577 	int			proccount = 0, invalid_user = 0, proc_props = 0, zbx_proc_stat;
578 #ifdef HAVE_ZONE_H
579 	zoneid_t		zoneid;
580 	int			zoneflag;
581 #endif
582 
583 	if (5 < request->nparam)
584 	{
585 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
586 		return SYSINFO_RET_FAIL;
587 	}
588 
589 	if (NULL != (procname = get_rparam(request, 0)) && '\0' != *procname)
590 		proc_props |= ZBX_SYSINFO_PROC_NAME;
591 	else
592 		procname = NULL;
593 
594 	param = get_rparam(request, 1);
595 
596 	if (NULL != param && '\0' != *param)
597 	{
598 		proc_props |= ZBX_SYSINFO_PROC_USER;
599 		errno = 0;
600 
601 		if (NULL == (usrinfo = getpwnam(param)))
602 		{
603 			if (0 != errno)
604 			{
605 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
606 						zbx_strerror(errno)));
607 				return SYSINFO_RET_FAIL;
608 			}
609 
610 			invalid_user = 1;
611 		}
612 	}
613 	else
614 		usrinfo = NULL;
615 
616 	param = get_rparam(request, 2);
617 
618 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "all"))
619 		zbx_proc_stat = ZBX_PROC_STAT_ALL;
620 	else if (0 == strcmp(param, "run"))
621 		zbx_proc_stat = ZBX_PROC_STAT_RUN;
622 	else if (0 == strcmp(param, "sleep"))
623 		zbx_proc_stat = ZBX_PROC_STAT_SLEEP;
624 	else if (0 == strcmp(param, "zomb"))
625 		zbx_proc_stat = ZBX_PROC_STAT_ZOMB;
626 	else
627 	{
628 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
629 		return SYSINFO_RET_FAIL;
630 	}
631 
632 	if (NULL != (proccomm = get_rparam(request, 3)) && '\0' != *proccomm)
633 		proc_props |= ZBX_SYSINFO_PROC_CMDLINE;
634 	else
635 		proccomm = NULL;
636 
637 	if (NULL == (zone_parameter = get_rparam(request, 4)) || '\0' == *zone_parameter
638 			|| 0 == strcmp(zone_parameter, "current"))
639 	{
640 #ifdef HAVE_ZONE_H
641 		zoneflag = ZBX_PROCSTAT_FLAGS_ZONE_CURRENT;
642 #else
643 		if (SUCCEED == zbx_detect_zone_support())
644 		{
645 			/* Agent has been compiled on Solaris 9 or earlier where zones are not supported */
646 			/* but now it is running on a system with zone support. This agent cannot limit */
647 			/* results to only current zone. */
648 
649 			SET_MSG_RESULT(result, zbx_strdup(NULL, "The fifth parameter value \"current\" cannot be used"
650 					" with agent running on a Solaris version with zone support, but compiled on"
651 					" a Solaris version without zone support. Consider using \"all\" or install"
652 					" agent with Solaris zone support."));
653 			return SYSINFO_RET_FAIL;
654 		}
655 #endif
656 	}
657 	else if (0 == strcmp(zone_parameter, "all"))
658 	{
659 #ifdef HAVE_ZONE_H
660 		zoneflag = ZBX_PROCSTAT_FLAGS_ZONE_ALL;
661 #endif
662 	}
663 	else
664 	{
665 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
666 		return SYSINFO_RET_FAIL;
667 	}
668 #ifdef HAVE_ZONE_H
669 	zoneid = getzoneid();
670 #endif
671 
672 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
673 		goto out;
674 
675 	if (NULL == (dir = opendir("/proc")))
676 	{
677 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot open /proc: %s", zbx_strerror(errno)));
678 		return SYSINFO_RET_FAIL;
679 	}
680 
681 	while (NULL != (entries = readdir(dir)))
682 	{
683 		zbx_sysinfo_proc_t	proc;
684 
685 		if (SUCCEED != proc_get_process_info(entries->d_name, proc_props, &proc, &psinfo))
686 			continue;
687 
688 		if (SUCCEED == proc_match_props(&proc, usrinfo, procname, proccomm))
689 		{
690 #ifdef HAVE_ZONE_H
691 			if (SUCCEED != proc_match_zone(&proc, zoneflag, zoneid))
692 			{
693 				zbx_sysinfo_proc_clear(&proc);
694 				continue;
695 			}
696 
697 #endif
698 			proccount++;
699 		}
700 
701 		zbx_sysinfo_proc_clear(&proc);
702 	}
703 
704 	closedir(dir);
705 out:
706 	SET_UI64_RESULT(result, proccount);
707 
708 	return SYSINFO_RET_OK;
709 }
710 
711 
712 /******************************************************************************
713  *                                                                            *
714  * Function: proc_read_cpu_util                                               *
715  *                                                                            *
716  * Purpose: reads process cpu utilization values from /proc/[pid]/usage file  *
717  *                                                                            *
718  * Parameters: procutil - [IN/OUT] the process cpu utilization data           *
719  *                                                                            *
720  * Return value: SUCCEED - the process cpu utilization data was read          *
721  *                         successfully                                       *
722  *               <0      - otherwise, -errno code is returned                 *
723  *                                                                            *
724  * Comments: we use /proc/[pid]/usage since /proc/[pid]/status contains       *
725  *           sensitive information and by default can only be read by the     *
726  *           owner or privileged user.                                        *
727  *                                                                            *
728  *           In addition to user and system-call CPU time the                 *
729  *           /proc/[pid]/usage also contains CPU time spent in trap context   *
730  *           Currently trap CPU time is not taken into account.               *
731  *                                                                            *
732  *           prstat(1) skips processes 0 (sched), 2 (pageout) and 3 (fsflush) *
733  *           however we take them into account.                               *
734  *                                                                            *
735  ******************************************************************************/
proc_read_cpu_util(zbx_procstat_util_t * procutil)736 static int	proc_read_cpu_util(zbx_procstat_util_t *procutil)
737 {
738 	int		fd, n;
739 	char		tmp[MAX_STRING_LEN];
740 	psinfo_t	psinfo;
741 	prusage_t	prusage;
742 
743 	zbx_snprintf(tmp, sizeof(tmp), "/proc/%d/psinfo", (int)procutil->pid);
744 
745 	if (-1 == (fd = open(tmp, O_RDONLY)))
746 		return -errno;
747 
748 	n = read(fd, &psinfo, sizeof(psinfo));
749 	close(fd);
750 
751 	if (-1 == n)
752 		return -errno;
753 
754 	procutil->starttime = psinfo.pr_start.tv_sec;
755 
756 	zbx_snprintf(tmp, sizeof(tmp), "/proc/%d/usage", (int)procutil->pid);
757 
758 	if (-1 == (fd = open(tmp, O_RDONLY)))
759 		return -errno;
760 
761 	n = read(fd, &prusage, sizeof(prusage));
762 	close(fd);
763 
764 	if (-1 == n)
765 		return -errno;
766 
767 	/* convert cpu utilization time to clock ticks */
768 	procutil->utime = ((zbx_uint64_t)prusage.pr_utime.tv_sec * 1e9 + prusage.pr_utime.tv_nsec) *
769 			sysconf(_SC_CLK_TCK) / 1e9;
770 
771 	procutil->stime = ((zbx_uint64_t)prusage.pr_stime.tv_sec * 1e9 + prusage.pr_stime.tv_nsec) *
772 			sysconf(_SC_CLK_TCK) / 1e9;
773 
774 	return SUCCEED;
775 }
776 
777 /******************************************************************************
778  *                                                                            *
779  * Function: zbx_proc_get_process_stats                                       *
780  *                                                                            *
781  * Purpose: get process cpu utilization data                                  *
782  *                                                                            *
783  * Parameters: procs     - [IN/OUT] an array of process utilization data      *
784  *             procs_num - [IN] the number of items in procs array            *
785  *                                                                            *
786  ******************************************************************************/
zbx_proc_get_process_stats(zbx_procstat_util_t * procs,int procs_num)787 void	zbx_proc_get_process_stats(zbx_procstat_util_t *procs, int procs_num)
788 {
789 	const char	*__function_name = "zbx_proc_get_process_stats";
790 	int		i;
791 
792 	zabbix_log(LOG_LEVEL_TRACE, "In %s() procs_num:%d", __function_name, procs_num);
793 
794 	for (i = 0; i < procs_num; i++)
795 		procs[i].error = proc_read_cpu_util(&procs[i]);
796 
797 	zabbix_log(LOG_LEVEL_TRACE, "End of %s()", __function_name);
798 }
799 
800 /******************************************************************************
801  *                                                                            *
802  * Function: zbx_proc_get_processes                                           *
803  *                                                                            *
804  * Purpose: get system processes                                              *
805  *                                                                            *
806  * Parameters: processes - [OUT] the system processes                         *
807  *             flags     - [IN] the flags specifying the process properties   *
808  *                              that must be returned                         *
809  *                                                                            *
810  * Return value: SUCCEED - the system processes were retrieved successfully   *
811  *               FAIL    - failed to open /proc directory                     *
812  *                                                                            *
813  ******************************************************************************/
zbx_proc_get_processes(zbx_vector_ptr_t * processes,unsigned int flags)814 int	zbx_proc_get_processes(zbx_vector_ptr_t *processes, unsigned int flags)
815 {
816 	const char		*__function_name = "zbx_proc_get_processes";
817 
818 	DIR			*dir;
819 	struct dirent		*entries;
820 	char			tmp[MAX_STRING_LEN];
821 	int			pid, ret = FAIL, fd = -1, n;
822 	zbx_sysinfo_proc_t	*proc = NULL;
823 
824 	zabbix_log(LOG_LEVEL_TRACE, "In %s()", __function_name);
825 
826 	if (NULL == (dir = opendir("/proc")))
827 		goto out;
828 
829 	while (NULL != (entries = readdir(dir)))
830 	{
831 		if (NULL == proc)
832 		{
833 			proc = (zbx_sysinfo_proc_t *)zbx_malloc(NULL, sizeof(zbx_sysinfo_proc_t));
834 			memset(proc, 0, sizeof(zbx_sysinfo_proc_t));
835 		}
836 
837 		if (SUCCEED == proc_get_process_info(entries->d_name, flags, proc, NULL))
838 		{
839 			zbx_vector_ptr_append(processes, proc);
840 			proc = NULL;
841 		}
842 	}
843 	closedir(dir);
844 
845 	zbx_free(proc);
846 
847 	ret = SUCCEED;
848 out:
849 	zabbix_log(LOG_LEVEL_TRACE, "End of %s(): %s", __function_name, zbx_result_string(ret));
850 
851 	return ret;
852 }
853 
854 /******************************************************************************
855  *                                                                            *
856  * Function: zbx_proc_free_processes                                          *
857  *                                                                            *
858  * Purpose: frees process vector read by zbx_proc_get_processes function      *
859  *                                                                            *
860  * Parameters: processes - [IN/OUT] the process vector to free                *
861  *                                                                            *
862  ******************************************************************************/
zbx_proc_free_processes(zbx_vector_ptr_t * processes)863 void	zbx_proc_free_processes(zbx_vector_ptr_t *processes)
864 {
865 	zbx_vector_ptr_clear_ext(processes, (zbx_mem_free_func_t)zbx_sysinfo_proc_free);
866 }
867 
868 /******************************************************************************
869  *                                                                            *
870  * Function: zbx_proc_get_matching_pids                                       *
871  *                                                                            *
872  * Purpose: get pids matching the specified process name, user name and       *
873  *          command line                                                      *
874  *                                                                            *
875  * Parameters: procname    - [IN] the process name, NULL - all                *
876  *             username    - [IN] the user name, NULL - all                   *
877  *             cmdline     - [IN] the command line, NULL - all                *
878  *             pids        - [OUT] the vector of matching pids                *
879  *                                                                            *
880  * Return value: SUCCEED   - the pids were read successfully                  *
881  *               -errno    - failed to read pids                              *
882  *                                                                            *
883  ******************************************************************************/
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)884 void	zbx_proc_get_matching_pids(const zbx_vector_ptr_t *processes, const char *procname, const char *username,
885 		const char *cmdline, zbx_uint64_t flags, zbx_vector_uint64_t *pids)
886 {
887 	const char		*__function_name = "zbx_proc_get_matching_pids";
888 	struct passwd		*usrinfo;
889 	int			i;
890 	zbx_sysinfo_proc_t	*proc;
891 #ifdef HAVE_ZONE_H
892 	zoneid_t		zoneid;
893 #endif
894 
895 	zabbix_log(LOG_LEVEL_TRACE, "In %s() procname:%s username:%s cmdline:%s zone:%d", __function_name,
896 			ZBX_NULL2EMPTY_STR(procname), ZBX_NULL2EMPTY_STR(username), ZBX_NULL2EMPTY_STR(cmdline), flags);
897 
898 	if (NULL != username)
899 	{
900 		/* in the case of invalid user there are no matching processes, return empty vector */
901 		if (NULL == (usrinfo = getpwnam(username)))
902 			goto out;
903 	}
904 	else
905 		usrinfo = NULL;
906 
907 #ifdef HAVE_ZONE_H
908 	zoneid = getzoneid();
909 #endif
910 
911 	for (i = 0; i < processes->values_num; i++)
912 	{
913 		proc = (zbx_sysinfo_proc_t *)processes->values[i];
914 
915 		if (SUCCEED != proc_match_props(proc, usrinfo, procname, cmdline))
916 			continue;
917 
918 #ifdef HAVE_ZONE_H
919 		if (SUCCEED != proc_match_zone(proc, flags, zoneid))
920 			continue;
921 #endif
922 
923 		zbx_vector_uint64_append(pids, (zbx_uint64_t)proc->pid);
924 	}
925 out:
926 	zabbix_log(LOG_LEVEL_TRACE, "End of %s()", __function_name);
927 }
928 
PROC_CPU_UTIL(AGENT_REQUEST * request,AGENT_RESULT * result)929 int	PROC_CPU_UTIL(AGENT_REQUEST *request, AGENT_RESULT *result)
930 {
931 	const char	*procname, *username, *cmdline, *tmp, *flags;
932 	char		*errmsg = NULL;
933 	int		period, type;
934 	double		value;
935 	zbx_uint64_t	zoneflag;
936 	zbx_timespec_t	ts_timeout, ts;
937 
938 	/* proc.cpu.util[<procname>,<username>,(user|system),<cmdline>,(avg1|avg5|avg15),(current|all)] */
939 	if (6 < request->nparam)
940 	{
941 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
942 		return SYSINFO_RET_FAIL;
943 	}
944 
945 	/* zbx_procstat_get_* functions expect NULL for default values -       */
946 	/* convert empty procname, username and cmdline strings to NULL values */
947 	if (NULL != (procname = get_rparam(request, 0)) && '\0' == *procname)
948 		procname = NULL;
949 
950 	if (NULL != (username = get_rparam(request, 1)) && '\0' == *username)
951 		username = NULL;
952 
953 	if (NULL != (cmdline = get_rparam(request, 3)) && '\0' == *cmdline)
954 		cmdline = NULL;
955 
956 	/* utilization type parameter (user|system) */
957 	if (NULL == (tmp = get_rparam(request, 2)) || '\0' == *tmp || 0 == strcmp(tmp, "total"))
958 	{
959 		type = ZBX_PROCSTAT_CPU_TOTAL;
960 	}
961 	else if (0 == strcmp(tmp, "user"))
962 	{
963 		type = ZBX_PROCSTAT_CPU_USER;
964 	}
965 	else if (0 == strcmp(tmp, "system"))
966 	{
967 		type = ZBX_PROCSTAT_CPU_SYSTEM;
968 	}
969 	else
970 	{
971 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
972 		return SYSINFO_RET_FAIL;
973 	}
974 
975 	/* mode parameter (avg1|avg5|avg15) */
976 	if (NULL == (tmp = get_rparam(request, 4)) || '\0' == *tmp || 0 == strcmp(tmp, "avg1"))
977 	{
978 		period = SEC_PER_MIN;
979 	}
980 	else if (0 == strcmp(tmp, "avg5"))
981 	{
982 		period = SEC_PER_MIN * 5;
983 	}
984 	else if (0 == strcmp(tmp, "avg15"))
985 	{
986 		period = SEC_PER_MIN * 15;
987 	}
988 	else
989 	{
990 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
991 		return SYSINFO_RET_FAIL;
992 	}
993 
994 	if (NULL == (flags = get_rparam(request, 5)) || '\0' == *flags || 0 == strcmp(flags, "current"))
995 	{
996 #ifndef HAVE_ZONE_H
997 		if (SUCCEED == zbx_detect_zone_support())
998 		{
999 			/* Agent has been compiled on Solaris 9 or earlier where zones are not supported */
1000 			/* but now it is running on a system with zone support. This agent cannot limit */
1001 			/* results to only current zone. */
1002 
1003 			SET_MSG_RESULT(result, zbx_strdup(NULL, "The sixth parameter value \"current\" cannot be used"
1004 					" with agent running on a Solaris version with zone support, but compiled on"
1005 					" a Solaris version without zone support. Consider using \"all\" or install"
1006 					" agent with Solaris zone support."));
1007 			return SYSINFO_RET_FAIL;
1008 		}
1009 
1010 		/* zones are not supported, the agent can accept 6th parameter with default value "current" */
1011 #endif
1012 		zoneflag = ZBX_PROCSTAT_FLAGS_ZONE_CURRENT;
1013 	}
1014 	else if (0 == strcmp(flags, "all"))
1015 	{
1016 		zoneflag = ZBX_PROCSTAT_FLAGS_ZONE_ALL;
1017 	}
1018 	else
1019 	{
1020 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid sixth parameter."));
1021 		return SYSINFO_RET_FAIL;
1022 	}
1023 
1024 	if (SUCCEED != zbx_procstat_collector_started())
1025 	{
1026 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Collector is not started."));
1027 		return SYSINFO_RET_FAIL;
1028 	}
1029 
1030 	zbx_timespec(&ts_timeout);
1031 	ts_timeout.sec += CONFIG_TIMEOUT;
1032 
1033 	while (SUCCEED != zbx_procstat_get_util(procname, username, cmdline, zoneflag, period, type, &value, &errmsg))
1034 	{
1035 		/* zbx_procstat_get_* functions will return FAIL when either a collection   */
1036 		/* error was registered or if less than 2 data samples were collected.      */
1037 		/* In the first case the errmsg will contain error message.                 */
1038 		if (NULL != errmsg)
1039 		{
1040 			SET_MSG_RESULT(result, errmsg);
1041 			return SYSINFO_RET_FAIL;
1042 		}
1043 
1044 		zbx_timespec(&ts);
1045 
1046 		if (0 > zbx_timespec_compare(&ts_timeout, &ts))
1047 		{
1048 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Timeout while waiting for collector data."));
1049 			return SYSINFO_RET_FAIL;
1050 		}
1051 
1052 		sleep(1);
1053 	}
1054 
1055 	SET_DBL_RESULT(result, value);
1056 
1057 	return SYSINFO_RET_OK;
1058 }
1059