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