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