1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "sysinfo.h"
22 #include "zbxregexp.h"
23 #include "log.h"
24 
25 #include <sys/sysctl.h>
26 
27 static kvm_t	*kd = NULL;
28 
proc_argv(pid_t pid)29 static char	*proc_argv(pid_t pid)
30 {
31 	size_t		sz = 0;
32 	int		mib[4], ret;
33 	int		i, len;
34 	static char	*argv = NULL;
35 	static size_t	argv_alloc = 0;
36 
37 	mib[0] = CTL_KERN;
38 	mib[1] = KERN_PROC_ARGS;
39 	mib[2] = (int)pid;
40 	mib[3] = KERN_PROC_ARGV;
41 
42 	if (0 != sysctl(mib, 4, NULL, &sz, NULL, 0))
43 		return NULL;
44 
45 	if (argv_alloc < sz)
46 	{
47 		argv_alloc = sz;
48 		if (NULL == argv)
49 			argv = zbx_malloc(argv, argv_alloc);
50 		else
51 			argv = zbx_realloc(argv, argv_alloc);
52 	}
53 
54 	sz = argv_alloc;
55 	if (0 != sysctl(mib, 4, argv, &sz, NULL, 0))
56 		return NULL;
57 
58 	for (i = 0; i < (int)(sz - 1); i++ )
59 		if (argv[i] == '\0')
60 			argv[i] = ' ';
61 
62 	return argv;
63 }
64 
PROC_MEM(AGENT_REQUEST * request,AGENT_RESULT * result)65 int     PROC_MEM(AGENT_REQUEST *request, AGENT_RESULT *result)
66 {
67 	char			*procname, *proccomm, *param, *args;
68 	int			do_task, pagesize, count, i, proccount = 0, invalid_user = 0, proc_ok, comm_ok, op, arg;
69 	double			value = 0.0, memsize = 0;
70 	size_t			sz;
71 	struct kinfo_proc2	*proc, *pproc;
72 	struct passwd		*usrinfo;
73 
74 	if (4 < request->nparam)
75 	{
76 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
77 		return SYSINFO_RET_FAIL;
78 	}
79 
80 	procname = get_rparam(request, 0);
81 	param = get_rparam(request, 1);
82 
83 	if (NULL != param && '\0' != *param)
84 	{
85 		errno = 0;
86 
87 		if (NULL == (usrinfo = getpwnam(param)))
88 		{
89 			if (0 != errno)
90 			{
91 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
92 						zbx_strerror(errno)));
93 				return SYSINFO_RET_FAIL;
94 			}
95 
96 			invalid_user = 1;
97 		}
98 	}
99 	else
100 		usrinfo = NULL;
101 
102 	param = get_rparam(request, 2);
103 
104 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "sum"))
105 		do_task = ZBX_DO_SUM;
106 	else if (0 == strcmp(param, "avg"))
107 		do_task = ZBX_DO_AVG;
108 	else if (0 == strcmp(param, "max"))
109 		do_task = ZBX_DO_MAX;
110 	else if (0 == strcmp(param, "min"))
111 		do_task = ZBX_DO_MIN;
112 	else
113 	{
114 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
115 		return SYSINFO_RET_FAIL;
116 	}
117 
118 	proccomm = get_rparam(request, 3);
119 
120 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
121 		goto out;
122 
123 	pagesize = getpagesize();
124 
125 	if (NULL == kd && NULL == (kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL)))
126 	{
127 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain a descriptor to access kernel virtual memory."));
128 		return SYSINFO_RET_FAIL;
129 	}
130 
131 	if (NULL != usrinfo)
132 	{
133 		op = KERN_PROC_UID;
134 		arg = (int)usrinfo->pw_uid;
135 	}
136 	else
137 	{
138 		op = KERN_PROC_ALL;
139 		arg = 0;
140 	}
141 
142 	if (NULL == (proc = kvm_getproc2(kd, op, arg, sizeof(struct kinfo_proc2), &count)))
143 	{
144 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain process information."));
145 		return SYSINFO_RET_FAIL;
146 	}
147 
148 	for (pproc = proc, i = 0; i < count; pproc++, i++)
149 	{
150 		proc_ok = 0;
151 		comm_ok = 0;
152 
153 		if (NULL == procname || '\0' == *procname || 0 == strcmp(procname, pproc->p_comm))
154 			proc_ok = 1;
155 
156 		if (NULL != proccomm && '\0' != *proccomm)
157 		{
158 			if (NULL != (args = proc_argv(pproc->p_pid)))
159 			{
160 				if (NULL != zbx_regexp_match(args, proccomm, NULL))
161 					comm_ok = 1;
162 			}
163 		}
164 		else
165 			comm_ok = 1;
166 
167 		if (proc_ok && comm_ok)
168 		{
169 			value = pproc->p_vm_tsize + pproc->p_vm_dsize + pproc->p_vm_ssize;
170 			value *= pagesize;
171 
172 			if (0 == proccount++)
173 				memsize = value;
174 			else
175 			{
176 				if (ZBX_DO_MAX == do_task)
177 					memsize = MAX(memsize, value);
178 				else if (ZBX_DO_MIN == do_task)
179 					memsize = MIN(memsize, value);
180 				else
181 					memsize += value;
182 			}
183 		}
184 	}
185 out:
186 	if (ZBX_DO_AVG == do_task)
187 		SET_DBL_RESULT(result, 0 == proccount ? 0 : memsize / proccount);
188 	else
189 		SET_UI64_RESULT(result, memsize);
190 
191 	return SYSINFO_RET_OK;
192 }
193 
PROC_NUM(AGENT_REQUEST * request,AGENT_RESULT * result)194 int	PROC_NUM(AGENT_REQUEST *request, AGENT_RESULT *result)
195 {
196 	char			*procname, *proccomm, *param, *args;
197 	int			proccount = 0, invalid_user = 0, zbx_proc_stat;
198 	int			count, i, proc_ok, stat_ok, comm_ok, op, arg;
199 	size_t			sz;
200 	struct kinfo_proc2	*proc, *pproc;
201 	struct passwd		*usrinfo;
202 
203 	if (4 < request->nparam)
204 	{
205 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
206 		return SYSINFO_RET_FAIL;
207 	}
208 
209 	procname = get_rparam(request, 0);
210 	param = get_rparam(request, 1);
211 
212 	if (NULL != param && '\0' != *param)
213 	{
214 		errno = 0;
215 
216 		if (NULL == (usrinfo = getpwnam(param)))
217 		{
218 			if (0 != errno)
219 			{
220 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
221 						zbx_strerror(errno)));
222 				return SYSINFO_RET_FAIL;
223 			}
224 
225 			invalid_user = 1;
226 		}
227 	}
228 	else
229 		usrinfo = NULL;
230 
231 	param = get_rparam(request, 2);
232 
233 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "all"))
234 		zbx_proc_stat = ZBX_PROC_STAT_ALL;
235 	else if (0 == strcmp(param, "run"))
236 		zbx_proc_stat = ZBX_PROC_STAT_RUN;
237 	else if (0 == strcmp(param, "sleep"))
238 		zbx_proc_stat = ZBX_PROC_STAT_SLEEP;
239 	else if (0 == strcmp(param, "zomb"))
240 		zbx_proc_stat = ZBX_PROC_STAT_ZOMB;
241 	else
242 	{
243 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
244 		return SYSINFO_RET_FAIL;
245 	}
246 
247 	proccomm = get_rparam(request, 3);
248 
249 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
250 		goto out;
251 
252 	if (NULL == kd && NULL == (kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL)))
253 	{
254 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain a descriptor to access kernel virtual memory."));
255 		return SYSINFO_RET_FAIL;
256 	}
257 
258 	if (NULL != usrinfo)
259 	{
260 		op = KERN_PROC_UID;
261 		arg = (int)usrinfo->pw_uid;
262 	}
263 	else
264 	{
265 		op = KERN_PROC_ALL;
266 		arg = 0;
267 	}
268 
269 	if (NULL == (proc = kvm_getproc2(kd, op, arg, sizeof(struct kinfo_proc2), &count)))
270 	{
271 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain process information."));
272 		return SYSINFO_RET_FAIL;
273 	}
274 
275 	for (pproc = proc, i = 0; i < count; pproc++, i++)
276 	{
277 		proc_ok = 0;
278 		stat_ok = 0;
279 		comm_ok = 0;
280 
281 		if (NULL == procname || '\0' == *procname || 0 == strcmp(procname, pproc->p_comm))
282 			proc_ok = 1;
283 
284 		if (ZBX_PROC_STAT_ALL != zbx_proc_stat)
285 		{
286 			switch (zbx_proc_stat)
287 			{
288 				case ZBX_PROC_STAT_RUN:
289 					if (LSRUN == pproc->p_stat || LSONPROC == pproc->p_stat)
290 						stat_ok = 1;
291 					break;
292 				case ZBX_PROC_STAT_SLEEP:
293 					if (LSSLEEP == pproc->p_stat)
294 						stat_ok = 1;
295 					break;
296 				case ZBX_PROC_STAT_ZOMB:
297 					if (SZOMB == pproc->p_stat || LSDEAD == pproc->p_stat)
298 						stat_ok = 1;
299 					break;
300 			}
301 		}
302 		else
303 			stat_ok = 1;
304 
305 		if (NULL != proccomm && '\0' != *proccomm)
306 		{
307 			if (NULL != (args = proc_argv(pproc->p_pid)))
308 			{
309 				if (NULL != zbx_regexp_match(args, proccomm, NULL))
310 					comm_ok = 1;
311 			}
312 		}
313 		else
314 			comm_ok = 1;
315 
316 		if (proc_ok && stat_ok && comm_ok)
317 			proccount++;
318 	}
319 out:
320 	SET_UI64_RESULT(result, proccount);
321 
322 	return SYSINFO_RET_OK;
323 }
324