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 	struct kinfo_proc2	*proc, *pproc;
71 	struct passwd		*usrinfo;
72 
73 	if (4 < request->nparam)
74 	{
75 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
76 		return SYSINFO_RET_FAIL;
77 	}
78 
79 	procname = get_rparam(request, 0);
80 	param = get_rparam(request, 1);
81 
82 	if (NULL != param && '\0' != *param)
83 	{
84 		errno = 0;
85 
86 		if (NULL == (usrinfo = getpwnam(param)))
87 		{
88 			if (0 != errno)
89 			{
90 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
91 						zbx_strerror(errno)));
92 				return SYSINFO_RET_FAIL;
93 			}
94 
95 			invalid_user = 1;
96 		}
97 	}
98 	else
99 		usrinfo = NULL;
100 
101 	param = get_rparam(request, 2);
102 
103 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "sum"))
104 		do_task = ZBX_DO_SUM;
105 	else if (0 == strcmp(param, "avg"))
106 		do_task = ZBX_DO_AVG;
107 	else if (0 == strcmp(param, "max"))
108 		do_task = ZBX_DO_MAX;
109 	else if (0 == strcmp(param, "min"))
110 		do_task = ZBX_DO_MIN;
111 	else
112 	{
113 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
114 		return SYSINFO_RET_FAIL;
115 	}
116 
117 	proccomm = get_rparam(request, 3);
118 
119 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
120 		goto out;
121 
122 	pagesize = getpagesize();
123 
124 	if (NULL == kd && NULL == (kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL)))
125 	{
126 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain a descriptor to access kernel virtual memory."));
127 		return SYSINFO_RET_FAIL;
128 	}
129 
130 	if (NULL != usrinfo)
131 	{
132 		op = KERN_PROC_UID;
133 		arg = (int)usrinfo->pw_uid;
134 	}
135 	else
136 	{
137 		op = KERN_PROC_ALL;
138 		arg = 0;
139 	}
140 
141 	if (NULL == (proc = kvm_getproc2(kd, op, arg, sizeof(struct kinfo_proc2), &count)))
142 	{
143 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain process information."));
144 		return SYSINFO_RET_FAIL;
145 	}
146 
147 	for (pproc = proc, i = 0; i < count; pproc++, i++)
148 	{
149 		proc_ok = 0;
150 		comm_ok = 0;
151 
152 		if (NULL == procname || '\0' == *procname || 0 == strcmp(procname, pproc->p_comm))
153 			proc_ok = 1;
154 
155 		if (NULL != proccomm && '\0' != *proccomm)
156 		{
157 			if (NULL != (args = proc_argv(pproc->p_pid)))
158 			{
159 				if (NULL != zbx_regexp_match(args, proccomm, NULL))
160 					comm_ok = 1;
161 			}
162 		}
163 		else
164 			comm_ok = 1;
165 
166 		if (proc_ok && comm_ok)
167 		{
168 			value = pproc->p_vm_tsize + pproc->p_vm_dsize + pproc->p_vm_ssize;
169 			value *= pagesize;
170 
171 			if (0 == proccount++)
172 				memsize = value;
173 			else
174 			{
175 				if (ZBX_DO_MAX == do_task)
176 					memsize = MAX(memsize, value);
177 				else if (ZBX_DO_MIN == do_task)
178 					memsize = MIN(memsize, value);
179 				else
180 					memsize += value;
181 			}
182 		}
183 	}
184 out:
185 	if (ZBX_DO_AVG == do_task)
186 		SET_DBL_RESULT(result, 0 == proccount ? 0 : memsize / proccount);
187 	else
188 		SET_UI64_RESULT(result, memsize);
189 
190 	return SYSINFO_RET_OK;
191 }
192 
PROC_NUM(AGENT_REQUEST * request,AGENT_RESULT * result)193 int	PROC_NUM(AGENT_REQUEST *request, AGENT_RESULT *result)
194 {
195 	char			*procname, *proccomm, *param, *args;
196 	int			proccount = 0, invalid_user = 0, zbx_proc_stat;
197 	int			count, i, proc_ok, stat_ok, comm_ok, op, arg;
198 	struct kinfo_proc2	*proc, *pproc;
199 	struct passwd		*usrinfo;
200 
201 	if (4 < request->nparam)
202 	{
203 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
204 		return SYSINFO_RET_FAIL;
205 	}
206 
207 	procname = get_rparam(request, 0);
208 	param = get_rparam(request, 1);
209 
210 	if (NULL != param && '\0' != *param)
211 	{
212 		errno = 0;
213 
214 		if (NULL == (usrinfo = getpwnam(param)))
215 		{
216 			if (0 != errno)
217 			{
218 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
219 						zbx_strerror(errno)));
220 				return SYSINFO_RET_FAIL;
221 			}
222 
223 			invalid_user = 1;
224 		}
225 	}
226 	else
227 		usrinfo = NULL;
228 
229 	param = get_rparam(request, 2);
230 
231 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "all"))
232 		zbx_proc_stat = ZBX_PROC_STAT_ALL;
233 	else if (0 == strcmp(param, "run"))
234 		zbx_proc_stat = ZBX_PROC_STAT_RUN;
235 	else if (0 == strcmp(param, "sleep"))
236 		zbx_proc_stat = ZBX_PROC_STAT_SLEEP;
237 	else if (0 == strcmp(param, "zomb"))
238 		zbx_proc_stat = ZBX_PROC_STAT_ZOMB;
239 	else if (0 == strcmp(param, "disk"))
240 		zbx_proc_stat = ZBX_PROC_STAT_DISK;
241 	else if (0 == strcmp(param, "trace"))
242 		zbx_proc_stat = ZBX_PROC_STAT_TRACE;
243 	else
244 	{
245 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
246 		return SYSINFO_RET_FAIL;
247 	}
248 
249 	proccomm = get_rparam(request, 3);
250 
251 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
252 		goto out;
253 
254 	if (NULL == kd && NULL == (kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL)))
255 	{
256 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain a descriptor to access kernel virtual memory."));
257 		return SYSINFO_RET_FAIL;
258 	}
259 
260 	if (NULL != usrinfo)
261 	{
262 		op = KERN_PROC_UID;
263 		arg = (int)usrinfo->pw_uid;
264 	}
265 	else
266 	{
267 		op = KERN_PROC_ALL;
268 		arg = 0;
269 	}
270 
271 	if (NULL == (proc = kvm_getproc2(kd, op, arg, sizeof(struct kinfo_proc2), &count)))
272 	{
273 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain process information."));
274 		return SYSINFO_RET_FAIL;
275 	}
276 
277 	for (pproc = proc, i = 0; i < count; pproc++, i++)
278 	{
279 		proc_ok = 0;
280 		stat_ok = 0;
281 		comm_ok = 0;
282 
283 		if (NULL == procname || '\0' == *procname || 0 == strcmp(procname, pproc->p_comm))
284 			proc_ok = 1;
285 
286 		if (ZBX_PROC_STAT_ALL != zbx_proc_stat)
287 		{
288 			switch (zbx_proc_stat)
289 			{
290 				case ZBX_PROC_STAT_RUN:
291 					if (LSRUN == pproc->p_stat || LSONPROC == pproc->p_stat)
292 						stat_ok = 1;
293 					break;
294 				case ZBX_PROC_STAT_SLEEP:
295 					if (LSSLEEP == pproc->p_stat && 0 != (pproc->p_flag & L_SINTR))
296 						stat_ok = 1;
297 					break;
298 				case ZBX_PROC_STAT_ZOMB:
299 					if (0 != P_ZOMBIE(pproc))
300 						stat_ok = 1;
301 					break;
302 				case ZBX_PROC_STAT_DISK:
303 					if (LSSLEEP == pproc->p_stat && 0 == (pproc->p_flag & L_SINTR))
304 						stat_ok = 1;
305 					break;
306 				case ZBX_PROC_STAT_TRACE:
307 					if (LSSTOP == pproc->p_stat)
308 						stat_ok = 1;
309 					break;
310 			}
311 		}
312 		else
313 			stat_ok = 1;
314 
315 		if (NULL != proccomm && '\0' != *proccomm)
316 		{
317 			if (NULL != (args = proc_argv(pproc->p_pid)))
318 			{
319 				if (NULL != zbx_regexp_match(args, proccomm, NULL))
320 					comm_ok = 1;
321 			}
322 		}
323 		else
324 			comm_ok = 1;
325 
326 		if (proc_ok && stat_ok && comm_ok)
327 			proccount++;
328 	}
329 out:
330 	SET_UI64_RESULT(result, proccount);
331 
332 	return SYSINFO_RET_OK;
333 }
334