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 #define ARGS_START_SIZE 64
28 
29 /* in OpenBSD 5.1 KERN_PROC2 became KERN_PROC and structure kinfo_proc2 became kinfo_proc */
30 #if OpenBSD >= 201205		/* OpenBSD 5.1 version as year and month */
31 #	ifndef KERN_PROC2
32 #		define KERN_PROC2	KERN_PROC
33 #	endif
34 #	ifndef kinfo_proc2
35 #		define kinfo_proc2	kinfo_proc
36 #	endif
37 #endif
38 
39 #ifdef KERN_PROC2
40 #	define ZBX_P_COMM	p_comm
41 #	define ZBX_P_FLAG	p_flag
42 #	define ZBX_P_PID	p_pid
43 #	define ZBX_P_STAT	p_stat
44 #	define ZBX_P_VM_TSIZE	p_vm_tsize
45 #	define ZBX_P_VM_DSIZE	p_vm_dsize
46 #	define ZBX_P_VM_SSIZE	p_vm_ssize
47 #else
48 #	define ZBX_P_COMM	kp_proc.p_comm
49 #	define ZBX_P_FLAG	kp_proc.p_flag
50 #	define ZBX_P_PID	kp_proc.p_pid
51 #	define ZBX_P_STAT	kp_proc.p_stat
52 #	define ZBX_P_VM_TSIZE	kp_eproc.e_vm.vm_tsize
53 #	define ZBX_P_VM_DSIZE	kp_eproc.e_vm.vm_dsize
54 #	define ZBX_P_VM_SSIZE	kp_eproc.e_vm.vm_ssize
55 #endif
56 
proc_argv(pid_t pid,char *** argv,size_t * argv_alloc,int * argc)57 static int	proc_argv(pid_t pid, char ***argv, size_t *argv_alloc, int *argc)
58 {
59 	size_t	sz;
60 	int	mib[4];
61 
62 	if (NULL == *argv)
63 	{
64 		*argv_alloc = ARGS_START_SIZE;
65 		*argv = zbx_malloc(*argv, *argv_alloc);
66 	}
67 
68 	mib[0] = CTL_KERN;
69 	mib[1] = KERN_PROC_ARGS;
70 	mib[2] = (int)pid;
71 	mib[3] = KERN_PROC_ARGV;
72 retry:
73 	sz = *argv_alloc;
74 	if (0 != sysctl(mib, 4, *argv, &sz, NULL, 0))
75 	{
76 		if (errno == ENOMEM)
77 		{
78 			*argv_alloc *= 2;
79 			*argv = zbx_realloc(*argv, *argv_alloc);
80 			goto retry;
81 		}
82 		return FAIL;
83 	}
84 
85 	mib[3] = KERN_PROC_NARGV;
86 
87 	sz = sizeof(int);
88 	if (0 != sysctl(mib, 4, argc, &sz, NULL, 0))
89 		return FAIL;
90 
91 	return SUCCEED;
92 }
93 
collect_args(char ** argv,int argc,char ** args,size_t * args_alloc)94 static void	collect_args(char **argv, int argc, char **args, size_t *args_alloc)
95 {
96 	int	i;
97 	size_t	args_offset = 0;
98 
99 	if (0 == *args_alloc)
100 	{
101 		*args_alloc = ARGS_START_SIZE;
102 		*args = zbx_malloc(*args, *args_alloc);
103 	}
104 
105 	for (i = 0; i < argc; i++)
106 		zbx_snprintf_alloc(args, args_alloc, &args_offset, "%s ", argv[i]);
107 
108 	if (0 != args_offset)
109 		args_offset--; /* ' ' */
110 	(*args)[args_offset] = '\0';
111 }
112 
PROC_MEM(AGENT_REQUEST * request,AGENT_RESULT * result)113 int     PROC_MEM(AGENT_REQUEST *request, AGENT_RESULT *result)
114 {
115 	char			*procname, *proccomm, *param;
116 	int			do_task, pagesize, count, i, proccount = 0, invalid_user = 0, proc_ok, comm_ok;
117 	double			value = 0.0, memsize = 0;
118 	size_t			sz;
119 	struct passwd		*usrinfo;
120 #ifdef KERN_PROC2
121 	int			mib[6];
122 	struct kinfo_proc2	*proc = NULL;
123 #else
124 	int			mib[4];
125 	struct kinfo_proc	*proc = NULL;
126 #endif
127 	char			**argv = NULL, *args = NULL;
128 	size_t			argv_alloc = 0, args_alloc = 0;
129 	int			argc;
130 
131 	if (4 < request->nparam)
132 	{
133 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
134 		return SYSINFO_RET_FAIL;
135 	}
136 
137 	procname = get_rparam(request, 0);
138 	param = get_rparam(request, 1);
139 
140 	if (NULL != param && '\0' != *param)
141 	{
142 		errno = 0;
143 
144 		if (NULL == (usrinfo = getpwnam(param)))
145 		{
146 			if (0 != errno)
147 			{
148 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
149 						zbx_strerror(errno)));
150 				return SYSINFO_RET_FAIL;
151 			}
152 
153 			invalid_user = 1;
154 		}
155 	}
156 	else
157 		usrinfo = NULL;
158 
159 	param = get_rparam(request, 2);
160 
161 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "sum"))
162 		do_task = ZBX_DO_SUM;
163 	else if (0 == strcmp(param, "avg"))
164 		do_task = ZBX_DO_AVG;
165 	else if (0 == strcmp(param, "max"))
166 		do_task = ZBX_DO_MAX;
167 	else if (0 == strcmp(param, "min"))
168 		do_task = ZBX_DO_MIN;
169 	else
170 	{
171 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
172 		return SYSINFO_RET_FAIL;
173 	}
174 
175 	proccomm = get_rparam(request, 3);
176 
177 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
178 		goto out;
179 
180 	pagesize = getpagesize();
181 
182 	mib[0] = CTL_KERN;
183 	if (NULL != usrinfo)
184 	{
185 		mib[2] = KERN_PROC_UID;
186 		mib[3] = usrinfo->pw_uid;
187 	}
188 	else
189 	{
190 		mib[2] = KERN_PROC_ALL;
191 		mib[3] = 0;
192 	}
193 
194 #ifdef KERN_PROC2
195 	mib[1] = KERN_PROC2;
196 	mib[4] = sizeof(struct kinfo_proc2);
197 	mib[5] = 0;
198 
199 	sz = 0;
200 	if (0 != sysctl(mib, 6, NULL, &sz, NULL, 0))
201 	{
202 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain necessary buffer size from system: %s",
203 				zbx_strerror(errno)));
204 		return SYSINFO_RET_FAIL;
205 	}
206 
207 	proc = (struct kinfo_proc2 *)zbx_malloc(proc, sz);
208 	mib[5] = (int)(sz / sizeof(struct kinfo_proc2));
209 	if (0 != sysctl(mib, 6, proc, &sz, NULL, 0))
210 	{
211 		zbx_free(proc);
212 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain process information: %s",
213 				zbx_strerror(errno)));
214 		return SYSINFO_RET_FAIL;
215 	}
216 
217 	count = sz / sizeof(struct kinfo_proc2);
218 #else
219 	mib[1] = KERN_PROC;
220 
221 	sz = 0;
222 	if (0 != sysctl(mib, 4, NULL, &sz, NULL, 0))
223 	{
224 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain necessary buffer size from system: %s",
225 				zbx_strerror(errno)));
226 		return SYSINFO_RET_FAIL;
227 	}
228 
229 	proc = (struct kinfo_proc *)zbx_malloc(proc, sz);
230 	if (0 != sysctl(mib, 4, proc, &sz, NULL, 0))
231 	{
232 		zbx_free(proc);
233 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain process information: %s",
234 				zbx_strerror(errno)));
235 		return SYSINFO_RET_FAIL;
236 	}
237 
238 	count = sz / sizeof(struct kinfo_proc);
239 #endif
240 	for (i = 0; i < count; i++)
241 	{
242 		proc_ok = 0;
243 		comm_ok = 0;
244 
245 		if (NULL == procname || '\0' == *procname || 0 == strcmp(procname, proc[i].ZBX_P_COMM))
246 			proc_ok = 1;
247 
248 		if (NULL != proccomm && '\0' != *proccomm)
249 		{
250 			if (SUCCEED == proc_argv(proc[i].ZBX_P_PID, &argv, &argv_alloc, &argc))
251 			{
252 				collect_args(argv, argc, &args, &args_alloc);
253 				if (NULL != zbx_regexp_match(args, proccomm, NULL))
254 					comm_ok = 1;
255 			}
256 		}
257 		else
258 			comm_ok = 1;
259 
260 		if (proc_ok && comm_ok)
261 		{
262 			value = proc[i].ZBX_P_VM_TSIZE + proc[i].ZBX_P_VM_DSIZE + proc[i].ZBX_P_VM_SSIZE;
263 			value *= pagesize;
264 
265 			if (0 == proccount++)
266 				memsize = value;
267 			else
268 			{
269 				if (ZBX_DO_MAX == do_task)
270 					memsize = MAX(memsize, value);
271 				else if (ZBX_DO_MIN == do_task)
272 					memsize = MIN(memsize, value);
273 				else
274 					memsize += value;
275 			}
276 		}
277 	}
278 	zbx_free(proc);
279 	zbx_free(argv);
280 	zbx_free(args);
281 out:
282 	if (ZBX_DO_AVG == do_task)
283 		SET_DBL_RESULT(result, 0 == proccount ? 0 : memsize / proccount);
284 	else
285 		SET_UI64_RESULT(result, memsize);
286 
287 	return SYSINFO_RET_OK;
288 }
289 
PROC_NUM(AGENT_REQUEST * request,AGENT_RESULT * result)290 int	PROC_NUM(AGENT_REQUEST *request, AGENT_RESULT *result)
291 {
292 	char			*procname, *proccomm, *param;
293 	int			proccount = 0, invalid_user = 0, zbx_proc_stat, count, i, proc_ok, stat_ok, comm_ok;
294 	size_t			sz;
295 	struct passwd		*usrinfo;
296 #ifdef KERN_PROC2
297 	int			mib[6];
298 	struct kinfo_proc2	*proc = NULL;
299 #else
300 	int			mib[4];
301 	struct kinfo_proc	*proc = NULL;
302 #endif
303 	char			**argv = NULL, *args = NULL;
304 	size_t			argv_alloc = 0, args_alloc = 0;
305 	int			argc;
306 
307 	if (4 < request->nparam)
308 	{
309 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
310 		return SYSINFO_RET_FAIL;
311 	}
312 
313 	procname = get_rparam(request, 0);
314 	param = get_rparam(request, 1);
315 
316 	if (NULL != param && '\0' != *param)
317 	{
318 		errno = 0;
319 
320 		if (NULL == (usrinfo = getpwnam(param)))
321 		{
322 			if (0 != errno)
323 			{
324 				SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
325 						zbx_strerror(errno)));
326 				return SYSINFO_RET_FAIL;
327 			}
328 
329 			invalid_user = 1;
330 		}
331 	}
332 	else
333 		usrinfo = NULL;
334 
335 	param = get_rparam(request, 2);
336 
337 	if (NULL == param || '\0' == *param || 0 == strcmp(param, "all"))
338 		zbx_proc_stat = ZBX_PROC_STAT_ALL;
339 	else if (0 == strcmp(param, "run"))
340 		zbx_proc_stat = ZBX_PROC_STAT_RUN;
341 	else if (0 == strcmp(param, "sleep"))
342 		zbx_proc_stat = ZBX_PROC_STAT_SLEEP;
343 	else if (0 == strcmp(param, "zomb"))
344 		zbx_proc_stat = ZBX_PROC_STAT_ZOMB;
345 	else if (0 == strcmp(param, "disk"))
346 		zbx_proc_stat = ZBX_PROC_STAT_DISK;
347 	else if (0 == strcmp(param, "trace"))
348 		zbx_proc_stat = ZBX_PROC_STAT_TRACE;
349 	else
350 	{
351 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
352 		return SYSINFO_RET_FAIL;
353 	}
354 
355 	proccomm = get_rparam(request, 3);
356 
357 	if (1 == invalid_user)	/* handle 0 for non-existent user after all parameters have been parsed and validated */
358 		goto out;
359 
360 	mib[0] = CTL_KERN;
361 	if (NULL != usrinfo)
362 	{
363 		mib[2] = KERN_PROC_UID;
364 		mib[3] = usrinfo->pw_uid;
365 	}
366 	else
367 	{
368 		mib[2] = KERN_PROC_ALL;
369 		mib[3] = 0;
370 	}
371 
372 #ifdef KERN_PROC2
373 	mib[1] = KERN_PROC2;
374 	mib[4] = sizeof(struct kinfo_proc2);
375 	mib[5] = 0;
376 
377 	sz = 0;
378 	if (0 != sysctl(mib, 6, NULL, &sz, NULL, 0))
379 	{
380 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain necessary buffer size from system: %s",
381 				zbx_strerror(errno)));
382 		return SYSINFO_RET_FAIL;
383 	}
384 
385 	proc = (struct kinfo_proc2 *)zbx_malloc(proc, sz);
386 	mib[5] = (int)(sz / sizeof(struct kinfo_proc2));
387 	if (0 != sysctl(mib, 6, proc, &sz, NULL, 0))
388 	{
389 		zbx_free(proc);
390 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain process information: %s",
391 				zbx_strerror(errno)));
392 		return SYSINFO_RET_FAIL;
393 	}
394 
395 	count = sz / sizeof(struct kinfo_proc2);
396 #else
397 	mib[1] = KERN_PROC;
398 
399 	sz = 0;
400 	if (0 != sysctl(mib, 4, NULL, &sz, NULL, 0))
401 	{
402 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain necessary buffer size from system: %s",
403 				zbx_strerror(errno)));
404 		return SYSINFO_RET_FAIL;
405 	}
406 
407 	proc = (struct kinfo_proc *)zbx_malloc(proc, sz);
408 	if (0 != sysctl(mib, 4, proc, &sz, NULL, 0))
409 	{
410 		zbx_free(proc);
411 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain process information: %s",
412 				zbx_strerror(errno)));
413 		return SYSINFO_RET_FAIL;
414 	}
415 
416 	count = sz / sizeof(struct kinfo_proc);
417 #endif
418 
419 	for (i = 0; i < count; i++)
420 	{
421 		proc_ok = 0;
422 		stat_ok = 0;
423 		comm_ok = 0;
424 
425 		if (NULL == procname || '\0' == *procname || 0 == strcmp(procname, proc[i].ZBX_P_COMM))
426 			proc_ok = 1;
427 
428 		if (ZBX_PROC_STAT_ALL != zbx_proc_stat)
429 		{
430 			switch (zbx_proc_stat)
431 			{
432 				case ZBX_PROC_STAT_RUN:
433 					if (SRUN == proc[i].ZBX_P_STAT || SONPROC == proc[i].ZBX_P_STAT)
434 						stat_ok = 1;
435 					break;
436 				case ZBX_PROC_STAT_SLEEP:
437 					if (SSLEEP == proc[i].ZBX_P_STAT && 0 != (proc[i].ZBX_P_FLAG & P_SINTR))
438 						stat_ok = 1;
439 					break;
440 				case ZBX_PROC_STAT_ZOMB:
441 					if (SZOMB == proc[i].ZBX_P_STAT || SDEAD == proc[i].ZBX_P_STAT)
442 						stat_ok = 1;
443 					break;
444 				case ZBX_PROC_STAT_DISK:
445 					if (SSLEEP == proc[i].ZBX_P_STAT && 0 == (proc[i].ZBX_P_FLAG & P_SINTR))
446 						stat_ok = 1;
447 					break;
448 				case ZBX_PROC_STAT_TRACE:
449 					if (SSTOP == proc[i].ZBX_P_STAT)
450 						stat_ok = 1;
451 					break;
452 			}
453 		}
454 		else
455 			stat_ok = 1;
456 
457 		if (NULL != proccomm && '\0' != *proccomm)
458 		{
459 			if (SUCCEED == proc_argv(proc[i].ZBX_P_PID, &argv, &argv_alloc, &argc))
460 			{
461 				collect_args(argv, argc, &args, &args_alloc);
462 				if (NULL != zbx_regexp_match(args, proccomm, NULL))
463 					comm_ok = 1;
464 			}
465 		}
466 		else
467 			comm_ok = 1;
468 
469 		if (proc_ok && stat_ok && comm_ok)
470 			proccount++;
471 	}
472 	zbx_free(proc);
473 	zbx_free(argv);
474 	zbx_free(args);
475 out:
476 	SET_UI64_RESULT(result, proccount);
477 
478 	return SYSINFO_RET_OK;
479 }
480