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