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