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