1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "sysinfo.h"
22 #include "zbxregexp.h"
23 #include "log.h"
24 #include "stats.h"
25 #include "proc.h"
26
27 extern int CONFIG_TIMEOUT;
28
29 typedef struct
30 {
31 pid_t pid;
32 uid_t uid;
33
34 char *name;
35
36 /* the process name taken from the 0th argument */
37 char *name_arg0;
38
39 /* process command line in format <arg0> <arg1> ... <argN>\0 */
40 char *cmdline;
41 }
42 zbx_sysinfo_proc_t;
43
44 /******************************************************************************
45 * *
46 * Function: zbx_sysinfo_proc_free *
47 * *
48 * Purpose: frees process data structure *
49 * *
50 ******************************************************************************/
zbx_sysinfo_proc_free(zbx_sysinfo_proc_t * proc)51 static void zbx_sysinfo_proc_free(zbx_sysinfo_proc_t *proc)
52 {
53 zbx_free(proc->name);
54 zbx_free(proc->name_arg0);
55 zbx_free(proc->cmdline);
56
57 zbx_free(proc);
58 }
59
get_cmdline(FILE * f_cmd,char ** line,size_t * line_offset)60 static int get_cmdline(FILE *f_cmd, char **line, size_t *line_offset)
61 {
62 size_t line_alloc = ZBX_KIBIBYTE, n;
63
64 rewind(f_cmd);
65
66 *line = (char *)zbx_malloc(*line, line_alloc + 2);
67 *line_offset = 0;
68
69 while (0 != (n = fread(*line + *line_offset, 1, line_alloc - *line_offset, f_cmd)))
70 {
71 *line_offset += n;
72
73 if (0 != feof(f_cmd))
74 break;
75
76 line_alloc *= 2;
77 *line = (char *)zbx_realloc(*line, line_alloc + 2);
78 }
79
80 if (0 == ferror(f_cmd))
81 {
82 if (0 == *line_offset || '\0' != (*line)[*line_offset - 1])
83 (*line)[(*line_offset)++] = '\0';
84 if (1 == *line_offset || '\0' != (*line)[*line_offset - 2])
85 (*line)[(*line_offset)++] = '\0';
86
87 return SUCCEED;
88 }
89
90 zbx_free(*line);
91
92 return FAIL;
93 }
94
cmp_status(FILE * f_stat,const char * procname)95 static int cmp_status(FILE *f_stat, const char *procname)
96 {
97 char tmp[MAX_STRING_LEN];
98
99 rewind(f_stat);
100
101 while (NULL != fgets(tmp, (int)sizeof(tmp), f_stat))
102 {
103 if (0 != strncmp(tmp, "Name:\t", 6))
104 continue;
105
106 zbx_rtrim(tmp + 6, "\n");
107 if (0 == strcmp(tmp + 6, procname))
108 return SUCCEED;
109 break;
110 }
111
112 return FAIL;
113 }
114
check_procname(FILE * f_cmd,FILE * f_stat,const char * procname)115 static int check_procname(FILE *f_cmd, FILE *f_stat, const char *procname)
116 {
117 char *tmp = NULL, *p;
118 size_t l;
119 int ret = SUCCEED;
120
121 if (NULL == procname || '\0' == *procname)
122 return SUCCEED;
123
124 /* process name in /proc/[pid]/status contains limited number of characters */
125 if (SUCCEED == cmp_status(f_stat, procname))
126 return SUCCEED;
127
128 if (SUCCEED == get_cmdline(f_cmd, &tmp, &l))
129 {
130 if (NULL == (p = strrchr(tmp, '/')))
131 p = tmp;
132 else
133 p++;
134
135 if (0 == strcmp(p, procname))
136 goto clean;
137 }
138
139 ret = FAIL;
140 clean:
141 zbx_free(tmp);
142
143 return ret;
144 }
145
check_user(FILE * f_stat,struct passwd * usrinfo)146 static int check_user(FILE *f_stat, struct passwd *usrinfo)
147 {
148 char tmp[MAX_STRING_LEN], *p, *p1;
149 uid_t uid;
150
151 if (NULL == usrinfo)
152 return SUCCEED;
153
154 rewind(f_stat);
155
156 while (NULL != fgets(tmp, (int)sizeof(tmp), f_stat))
157 {
158 if (0 != strncmp(tmp, "Uid:\t", 5))
159 continue;
160
161 p = tmp + 5;
162
163 if (NULL != (p1 = strchr(p, '\t')))
164 *p1 = '\0';
165
166 uid = (uid_t)atoi(p);
167
168 if (usrinfo->pw_uid == uid)
169 return SUCCEED;
170 break;
171 }
172
173 return FAIL;
174 }
175
check_proccomm(FILE * f_cmd,const char * proccomm)176 static int check_proccomm(FILE *f_cmd, const char *proccomm)
177 {
178 char *tmp = NULL;
179 size_t i, l;
180 int ret = SUCCEED;
181
182 if (NULL == proccomm || '\0' == *proccomm)
183 return SUCCEED;
184
185 if (SUCCEED == get_cmdline(f_cmd, &tmp, &l))
186 {
187 for (i = 0, l -= 2; i < l; i++)
188 if ('\0' == tmp[i])
189 tmp[i] = ' ';
190
191 if (NULL != zbx_regexp_match(tmp, proccomm, NULL))
192 goto clean;
193 }
194
195 ret = FAIL;
196 clean:
197 zbx_free(tmp);
198
199 return ret;
200 }
201
check_procstate(FILE * f_stat,int zbx_proc_stat)202 static int check_procstate(FILE *f_stat, int zbx_proc_stat)
203 {
204 char tmp[MAX_STRING_LEN], *p;
205
206 if (ZBX_PROC_STAT_ALL == zbx_proc_stat)
207 return SUCCEED;
208
209 rewind(f_stat);
210
211 while (NULL != fgets(tmp, (int)sizeof(tmp), f_stat))
212 {
213 if (0 != strncmp(tmp, "State:\t", 7))
214 continue;
215
216 p = tmp + 7;
217
218 switch (zbx_proc_stat)
219 {
220 case ZBX_PROC_STAT_RUN:
221 return ('R' == *p) ? SUCCEED : FAIL;
222 case ZBX_PROC_STAT_SLEEP:
223 return ('S' == *p) ? SUCCEED : FAIL;
224 case ZBX_PROC_STAT_ZOMB:
225 return ('Z' == *p) ? SUCCEED : FAIL;
226 case ZBX_PROC_STAT_DISK:
227 return ('D' == *p) ? SUCCEED : FAIL;
228 case ZBX_PROC_STAT_TRACE:
229 return ('T' == *p) ? SUCCEED : FAIL;
230 default:
231 return FAIL;
232 }
233 }
234
235 return FAIL;
236 }
237
238 /******************************************************************************
239 * *
240 * Function: byte_value_from_proc_file *
241 * *
242 * Purpose: Read amount of memory in bytes from a string in /proc file. *
243 * For example, reading "VmSize: 176712 kB" from /proc/1/status *
244 * will produce a result 176712*1024 = 180953088 bytes *
245 * *
246 * Parameters: *
247 * f - [IN] file to read from *
248 * label - [IN] label to look for, e.g. "VmData:\t" *
249 * guard - [IN] label before which to stop, e.g. "VmStk:\t" (optional) *
250 * bytes - [OUT] result in bytes *
251 * *
252 * Return value: SUCCEED - successful reading, *
253 * NOTSUPPORTED - the search string was not found. For example, *
254 * /proc/NNN/status files for kernel threads do *
255 * not contain "VmSize:" string. *
256 * FAIL - the search string was found but could not be parsed. *
257 * *
258 ******************************************************************************/
byte_value_from_proc_file(FILE * f,const char * label,const char * guard,zbx_uint64_t * bytes)259 int byte_value_from_proc_file(FILE *f, const char *label, const char *guard, zbx_uint64_t *bytes)
260 {
261 char buf[MAX_STRING_LEN], *p_value, *p_unit;
262 size_t label_len, guard_len;
263 long pos = 0;
264 int ret = NOTSUPPORTED;
265
266 label_len = strlen(label);
267 p_value = buf + label_len;
268
269 if (NULL != guard)
270 {
271 guard_len = strlen(guard);
272 if (0 > (pos = ftell(f)))
273 return FAIL;
274 }
275
276 while (NULL != fgets(buf, (int)sizeof(buf), f))
277 {
278 if (NULL != guard)
279 {
280 if (0 == strncmp(buf, guard, guard_len))
281 {
282 if (0 != fseek(f, pos, SEEK_SET))
283 ret = FAIL;
284 break;
285 }
286
287 if (0 > (pos = ftell(f)))
288 {
289 ret = FAIL;
290 break;
291 }
292 }
293
294 if (0 != strncmp(buf, label, label_len))
295 continue;
296
297 if (NULL == (p_unit = strrchr(p_value, ' ')))
298 {
299 ret = FAIL;
300 break;
301 }
302
303 *p_unit++ = '\0';
304
305 while (' ' == *p_value)
306 p_value++;
307
308 if (FAIL == is_uint64(p_value, bytes))
309 {
310 ret = FAIL;
311 break;
312 }
313
314 zbx_rtrim(p_unit, "\n");
315
316 if (0 == strcasecmp(p_unit, "kB"))
317 *bytes <<= 10;
318 else if (0 == strcasecmp(p_unit, "mB"))
319 *bytes <<= 20;
320 else if (0 == strcasecmp(p_unit, "GB"))
321 *bytes <<= 30;
322 else if (0 == strcasecmp(p_unit, "TB"))
323 *bytes <<= 40;
324
325 ret = SUCCEED;
326 break;
327 }
328
329 return ret;
330 }
331
get_total_memory(zbx_uint64_t * total_memory)332 static int get_total_memory(zbx_uint64_t *total_memory)
333 {
334 FILE *f;
335 int ret = FAIL;
336
337 if (NULL != (f = fopen("/proc/meminfo", "r")))
338 {
339 ret = byte_value_from_proc_file(f, "MemTotal:", NULL, total_memory);
340 zbx_fclose(f);
341 }
342
343 return ret;
344 }
345
PROC_MEM(AGENT_REQUEST * request,AGENT_RESULT * result)346 int PROC_MEM(AGENT_REQUEST *request, AGENT_RESULT *result)
347 {
348 #define ZBX_SIZE 0
349 #define ZBX_RSS 1
350 #define ZBX_VSIZE 2
351 #define ZBX_PMEM 3
352 #define ZBX_VMPEAK 4
353 #define ZBX_VMSWAP 5
354 #define ZBX_VMLIB 6
355 #define ZBX_VMLCK 7
356 #define ZBX_VMPIN 8
357 #define ZBX_VMHWM 9
358 #define ZBX_VMDATA 10
359 #define ZBX_VMSTK 11
360 #define ZBX_VMEXE 12
361 #define ZBX_VMPTE 13
362
363 char tmp[MAX_STRING_LEN], *procname, *proccomm, *param;
364 DIR *dir;
365 struct dirent *entries;
366 struct passwd *usrinfo;
367 FILE *f_cmd = NULL, *f_stat = NULL;
368 zbx_uint64_t mem_size = 0, byte_value = 0, total_memory;
369 double pct_size = 0.0, pct_value = 0.0;
370 int do_task, res, proccount = 0, invalid_user = 0, invalid_read = 0;
371 int mem_type_tried = 0, mem_type_code;
372 char *mem_type = NULL;
373 const char *mem_type_search = NULL;
374
375 if (5 < request->nparam)
376 {
377 SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
378 return SYSINFO_RET_FAIL;
379 }
380
381 procname = get_rparam(request, 0);
382 param = get_rparam(request, 1);
383
384 if (NULL != param && '\0' != *param)
385 {
386 errno = 0;
387
388 if (NULL == (usrinfo = getpwnam(param)))
389 {
390 if (0 != errno)
391 {
392 SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
393 zbx_strerror(errno)));
394 return SYSINFO_RET_FAIL;
395 }
396
397 invalid_user = 1;
398 }
399 }
400 else
401 usrinfo = NULL;
402
403 param = get_rparam(request, 2);
404
405 if (NULL == param || '\0' == *param || 0 == strcmp(param, "sum"))
406 do_task = ZBX_DO_SUM;
407 else if (0 == strcmp(param, "avg"))
408 do_task = ZBX_DO_AVG;
409 else if (0 == strcmp(param, "max"))
410 do_task = ZBX_DO_MAX;
411 else if (0 == strcmp(param, "min"))
412 do_task = ZBX_DO_MIN;
413 else
414 {
415 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
416 return SYSINFO_RET_FAIL;
417 }
418
419 proccomm = get_rparam(request, 3);
420 mem_type = get_rparam(request, 4);
421
422 /* Comments for process memory types were compiled from: */
423 /* man 5 proc */
424 /* https://www.kernel.org/doc/Documentation/filesystems/proc.txt */
425 /* Himanshu Arora, Linux Processes explained - Part II, http://mylinuxbook.com/linux-processes-part2/ */
426
427 if (NULL == mem_type || '\0' == *mem_type || 0 == strcmp(mem_type, "vsize"))
428 {
429 mem_type_code = ZBX_VSIZE; /* current virtual memory size (total program size) */
430 mem_type_search = "VmSize:\t";
431 }
432 else if (0 == strcmp(mem_type, "rss"))
433 {
434 mem_type_code = ZBX_RSS; /* current resident set size (size of memory portions) */
435 mem_type_search = "VmRSS:\t";
436 }
437 else if (0 == strcmp(mem_type, "pmem"))
438 {
439 mem_type_code = ZBX_PMEM; /* percentage of real memory used by process */
440 }
441 else if (0 == strcmp(mem_type, "size"))
442 {
443 mem_type_code = ZBX_SIZE; /* size of process (code + data + stack) */
444 }
445 else if (0 == strcmp(mem_type, "peak"))
446 {
447 mem_type_code = ZBX_VMPEAK; /* peak virtual memory size */
448 mem_type_search = "VmPeak:\t";
449 }
450 else if (0 == strcmp(mem_type, "swap"))
451 {
452 mem_type_code = ZBX_VMSWAP; /* size of swap space used */
453 mem_type_search = "VmSwap:\t";
454 }
455 else if (0 == strcmp(mem_type, "lib"))
456 {
457 mem_type_code = ZBX_VMLIB; /* size of shared libraries */
458 mem_type_search = "VmLib:\t";
459 }
460 else if (0 == strcmp(mem_type, "lck"))
461 {
462 mem_type_code = ZBX_VMLCK; /* size of locked memory */
463 mem_type_search = "VmLck:\t";
464 }
465 else if (0 == strcmp(mem_type, "pin"))
466 {
467 mem_type_code = ZBX_VMPIN; /* size of pinned pages, they are never swappable */
468 mem_type_search = "VmPin:\t";
469 }
470 else if (0 == strcmp(mem_type, "hwm"))
471 {
472 mem_type_code = ZBX_VMHWM; /* peak resident set size ("high water mark") */
473 mem_type_search = "VmHWM:\t";
474 }
475 else if (0 == strcmp(mem_type, "data"))
476 {
477 mem_type_code = ZBX_VMDATA; /* size of data segment */
478 mem_type_search = "VmData:\t";
479 }
480 else if (0 == strcmp(mem_type, "stk"))
481 {
482 mem_type_code = ZBX_VMSTK; /* size of stack segment */
483 mem_type_search = "VmStk:\t";
484 }
485 else if (0 == strcmp(mem_type, "exe"))
486 {
487 mem_type_code = ZBX_VMEXE; /* size of text (code) segment */
488 mem_type_search = "VmExe:\t";
489 }
490 else if (0 == strcmp(mem_type, "pte"))
491 {
492 mem_type_code = ZBX_VMPTE; /* size of page table entries */
493 mem_type_search = "VmPTE:\t";
494 }
495 else
496 {
497 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
498 return SYSINFO_RET_FAIL;
499 }
500
501 if (1 == invalid_user) /* handle 0 for non-existent user after all parameters have been parsed and validated */
502 goto out;
503
504 if (ZBX_PMEM == mem_type_code)
505 {
506 if (SUCCEED != get_total_memory(&total_memory))
507 {
508 SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain amount of total memory: %s",
509 zbx_strerror(errno)));
510 return SYSINFO_RET_FAIL;
511 }
512
513 if (0 == total_memory) /* this should never happen but anyway - avoid crash due to dividing by 0 */
514 {
515 SET_MSG_RESULT(result, zbx_strdup(NULL, "Total memory reported is 0."));
516 return SYSINFO_RET_FAIL;
517 }
518 }
519
520 if (NULL == (dir = opendir("/proc")))
521 {
522 SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot open /proc: %s", zbx_strerror(errno)));
523 return SYSINFO_RET_FAIL;
524 }
525
526 while (NULL != (entries = readdir(dir)))
527 {
528 zbx_fclose(f_cmd);
529 zbx_fclose(f_stat);
530
531 if (0 == atoi(entries->d_name))
532 continue;
533
534 zbx_snprintf(tmp, sizeof(tmp), "/proc/%s/cmdline", entries->d_name);
535
536 if (NULL == (f_cmd = fopen(tmp, "r")))
537 continue;
538
539 zbx_snprintf(tmp, sizeof(tmp), "/proc/%s/status", entries->d_name);
540
541 if (NULL == (f_stat = fopen(tmp, "r")))
542 continue;
543
544 if (FAIL == check_procname(f_cmd, f_stat, procname))
545 continue;
546
547 if (FAIL == check_user(f_stat, usrinfo))
548 continue;
549
550 if (FAIL == check_proccomm(f_cmd, proccomm))
551 continue;
552
553 rewind(f_stat);
554
555 if (0 == mem_type_tried)
556 mem_type_tried = 1;
557
558 switch (mem_type_code)
559 {
560 case ZBX_VSIZE:
561 case ZBX_RSS:
562 case ZBX_VMPEAK:
563 case ZBX_VMSWAP:
564 case ZBX_VMLIB:
565 case ZBX_VMLCK:
566 case ZBX_VMPIN:
567 case ZBX_VMHWM:
568 case ZBX_VMDATA:
569 case ZBX_VMSTK:
570 case ZBX_VMEXE:
571 case ZBX_VMPTE:
572 res = byte_value_from_proc_file(f_stat, mem_type_search, NULL, &byte_value);
573
574 if (NOTSUPPORTED == res)
575 continue;
576
577 if (FAIL == res)
578 {
579 invalid_read = 1;
580 goto clean;
581 }
582 break;
583 case ZBX_SIZE:
584 {
585 zbx_uint64_t m;
586
587 /* VmData, VmStk and VmExe follow in /proc/PID/status file in that order. */
588 /* Therefore we do not rewind f_stat between calls. */
589
590 mem_type_search = "VmData:\t";
591
592 if (SUCCEED == (res = byte_value_from_proc_file(f_stat, mem_type_search, NULL,
593 &byte_value)))
594 {
595 mem_type_search = "VmStk:\t";
596
597 if (SUCCEED == (res = byte_value_from_proc_file(f_stat, mem_type_search,
598 NULL, &m)))
599 {
600 byte_value += m;
601 mem_type_search = "VmExe:\t";
602
603 if (SUCCEED == (res = byte_value_from_proc_file(f_stat,
604 mem_type_search, NULL, &m)))
605 {
606 byte_value += m;
607 }
608 }
609 }
610
611 if (SUCCEED != res)
612 {
613 if (NOTSUPPORTED == res)
614 {
615 /* NOTSUPPORTED - at least one of data strings not found in */
616 /* the /proc/PID/status file */
617 continue;
618 }
619 else /* FAIL */
620 {
621 invalid_read = 1;
622 goto clean;
623 }
624 }
625 }
626 break;
627 case ZBX_PMEM:
628 mem_type_search = "VmRSS:\t";
629 res = byte_value_from_proc_file(f_stat, mem_type_search, NULL, &byte_value);
630
631 if (SUCCEED == res)
632 {
633 pct_value = ((double)byte_value / (double)total_memory) * 100.0;
634 }
635 else if (NOTSUPPORTED == res)
636 {
637 continue;
638 }
639 else /* FAIL */
640 {
641 invalid_read = 1;
642 goto clean;
643 }
644 break;
645 }
646
647 if (ZBX_PMEM != mem_type_code)
648 {
649 if (0 != proccount++)
650 {
651 if (ZBX_DO_MAX == do_task)
652 mem_size = MAX(mem_size, byte_value);
653 else if (ZBX_DO_MIN == do_task)
654 mem_size = MIN(mem_size, byte_value);
655 else
656 mem_size += byte_value;
657 }
658 else
659 mem_size = byte_value;
660 }
661 else
662 {
663 if (0 != proccount++)
664 {
665 if (ZBX_DO_MAX == do_task)
666 pct_size = MAX(pct_size, pct_value);
667 else if (ZBX_DO_MIN == do_task)
668 pct_size = MIN(pct_size, pct_value);
669 else
670 pct_size += pct_value;
671 }
672 else
673 pct_size = pct_value;
674 }
675 }
676 clean:
677 zbx_fclose(f_cmd);
678 zbx_fclose(f_stat);
679 closedir(dir);
680
681 if ((0 == proccount && 0 != mem_type_tried) || 0 != invalid_read)
682 {
683 char *s;
684
685 s = zbx_strdup(NULL, mem_type_search);
686 zbx_rtrim(s, ":\t");
687 SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot get amount of \"%s\" memory.", s));
688 zbx_free(s);
689 return SYSINFO_RET_FAIL;
690 }
691 out:
692 if (ZBX_PMEM != mem_type_code)
693 {
694 if (ZBX_DO_AVG == do_task)
695 SET_DBL_RESULT(result, 0 == proccount ? 0 : (double)mem_size / (double)proccount);
696 else
697 SET_UI64_RESULT(result, mem_size);
698 }
699 else
700 {
701 if (ZBX_DO_AVG == do_task)
702 SET_DBL_RESULT(result, 0 == proccount ? 0 : pct_size / (double)proccount);
703 else
704 SET_DBL_RESULT(result, pct_size);
705 }
706
707 return SYSINFO_RET_OK;
708
709 #undef ZBX_SIZE
710 #undef ZBX_RSS
711 #undef ZBX_VSIZE
712 #undef ZBX_PMEM
713 #undef ZBX_VMPEAK
714 #undef ZBX_VMSWAP
715 #undef ZBX_VMLIB
716 #undef ZBX_VMLCK
717 #undef ZBX_VMPIN
718 #undef ZBX_VMHWM
719 #undef ZBX_VMDATA
720 #undef ZBX_VMSTK
721 #undef ZBX_VMEXE
722 #undef ZBX_VMPTE
723 }
724
PROC_NUM(AGENT_REQUEST * request,AGENT_RESULT * result)725 int PROC_NUM(AGENT_REQUEST *request, AGENT_RESULT *result)
726 {
727 char tmp[MAX_STRING_LEN], *procname, *proccomm, *param;
728 DIR *dir;
729 struct dirent *entries;
730 struct passwd *usrinfo;
731 FILE *f_cmd = NULL, *f_stat = NULL;
732 int proccount = 0, invalid_user = 0, zbx_proc_stat;
733
734 if (4 < request->nparam)
735 {
736 SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
737 return SYSINFO_RET_FAIL;
738 }
739
740 procname = get_rparam(request, 0);
741 param = get_rparam(request, 1);
742
743 if (NULL != param && '\0' != *param)
744 {
745 errno = 0;
746
747 if (NULL == (usrinfo = getpwnam(param)))
748 {
749 if (0 != errno)
750 {
751 SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain user information: %s",
752 zbx_strerror(errno)));
753 return SYSINFO_RET_FAIL;
754 }
755
756 invalid_user = 1;
757 }
758 }
759 else
760 usrinfo = NULL;
761
762 param = get_rparam(request, 2);
763
764 if (NULL == param || '\0' == *param || 0 == strcmp(param, "all"))
765 zbx_proc_stat = ZBX_PROC_STAT_ALL;
766 else if (0 == strcmp(param, "run"))
767 zbx_proc_stat = ZBX_PROC_STAT_RUN;
768 else if (0 == strcmp(param, "sleep"))
769 zbx_proc_stat = ZBX_PROC_STAT_SLEEP;
770 else if (0 == strcmp(param, "zomb"))
771 zbx_proc_stat = ZBX_PROC_STAT_ZOMB;
772 else if (0 == strcmp(param, "disk"))
773 zbx_proc_stat = ZBX_PROC_STAT_DISK;
774 else if (0 == strcmp(param, "trace"))
775 zbx_proc_stat = ZBX_PROC_STAT_TRACE;
776 else
777 {
778 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
779 return SYSINFO_RET_FAIL;
780 }
781
782 proccomm = get_rparam(request, 3);
783
784 if (1 == invalid_user) /* handle 0 for non-existent user after all parameters have been parsed and validated */
785 goto out;
786
787 if (NULL == (dir = opendir("/proc")))
788 {
789 SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot open /proc: %s", zbx_strerror(errno)));
790 return SYSINFO_RET_FAIL;
791 }
792
793 while (NULL != (entries = readdir(dir)))
794 {
795 zbx_fclose(f_cmd);
796 zbx_fclose(f_stat);
797
798 if (0 == atoi(entries->d_name))
799 continue;
800
801 zbx_snprintf(tmp, sizeof(tmp), "/proc/%s/cmdline", entries->d_name);
802
803 if (NULL == (f_cmd = fopen(tmp, "r")))
804 continue;
805
806 zbx_snprintf(tmp, sizeof(tmp), "/proc/%s/status", entries->d_name);
807
808 if (NULL == (f_stat = fopen(tmp, "r")))
809 continue;
810
811 if (FAIL == check_procname(f_cmd, f_stat, procname))
812 continue;
813
814 if (FAIL == check_user(f_stat, usrinfo))
815 continue;
816
817 if (FAIL == check_proccomm(f_cmd, proccomm))
818 continue;
819
820 if (FAIL == check_procstate(f_stat, zbx_proc_stat))
821 continue;
822
823 proccount++;
824 }
825 zbx_fclose(f_cmd);
826 zbx_fclose(f_stat);
827 closedir(dir);
828 out:
829 SET_UI64_RESULT(result, proccount);
830
831 return SYSINFO_RET_OK;
832 }
833
834 /******************************************************************************
835 * *
836 * Function: proc_get_process_name *
837 * *
838 * Purpose: returns process name *
839 * *
840 * Parameters: pid - [IN] the process identifier *
841 * procname - [OUT] the process name *
842 * *
843 * Return value: SUCCEED *
844 * FAIL *
845 * *
846 * Comments: The process name is allocated by this function and must be freed *
847 * by the caller. *
848 * *
849 ******************************************************************************/
proc_get_process_name(pid_t pid,char ** procname)850 static int proc_get_process_name(pid_t pid, char **procname)
851 {
852 int n, fd;
853 char tmp[MAX_STRING_LEN], *pend, *pstart;
854
855 zbx_snprintf(tmp, sizeof(tmp), "/proc/%d/stat", (int)pid);
856
857 if (-1 == (fd = open(tmp, O_RDONLY)))
858 return FAIL;
859
860 n = read(fd, tmp, sizeof(tmp));
861 close(fd);
862
863 if (-1 == n)
864 return FAIL;
865
866 for (pend = tmp + n - 1; ')' != *pend && pend > tmp; pend--)
867 ;
868
869 *pend = '\0';
870
871 if (NULL == (pstart = strchr(tmp, '(')))
872 return FAIL;
873
874 *procname = zbx_strdup(NULL, pstart + 1);
875
876 return SUCCEED;
877 }
878
879 /******************************************************************************
880 * *
881 * Function: proc_get_process_cmdline *
882 * *
883 * Purpose: returns process command line *
884 * *
885 * Parameters: pid - [IN] the process identifier *
886 * cmdline - [OUT] the process command line *
887 * cmdline_nbytes - [OUT] the number of bytes in the command line *
888 * *
889 * Return value: SUCCEED *
890 * FAIL *
891 * *
892 * Comments: The command line is allocated by this function and must be freed *
893 * by the caller. *
894 * *
895 ******************************************************************************/
proc_get_process_cmdline(pid_t pid,char ** cmdline,size_t * cmdline_nbytes)896 static int proc_get_process_cmdline(pid_t pid, char **cmdline, size_t *cmdline_nbytes)
897 {
898 char tmp[MAX_STRING_LEN];
899 int fd, n;
900 size_t cmdline_alloc = ZBX_KIBIBYTE;
901
902 *cmdline_nbytes = 0;
903 zbx_snprintf(tmp, sizeof(tmp), "/proc/%d/cmdline", (int)pid);
904
905 if (-1 == (fd = open(tmp, O_RDONLY)))
906 return FAIL;
907
908 *cmdline = (char *)zbx_malloc(NULL, cmdline_alloc);
909
910 while (0 < (n = read(fd, *cmdline + *cmdline_nbytes, cmdline_alloc - *cmdline_nbytes)))
911 {
912 *cmdline_nbytes += n;
913
914 if (*cmdline_nbytes == cmdline_alloc)
915 {
916 cmdline_alloc *= 2;
917 *cmdline = (char *)zbx_realloc(*cmdline, cmdline_alloc);
918 }
919 }
920
921 close(fd);
922
923 if (0 < *cmdline_nbytes)
924 {
925 /* add terminating NUL if it is missing due to processes setting their titles or other reasons */
926 if ('\0' != (*cmdline)[*cmdline_nbytes - 1])
927 {
928 if (*cmdline_nbytes == cmdline_alloc)
929 {
930 cmdline_alloc += 1;
931 *cmdline = (char *)zbx_realloc(*cmdline, cmdline_alloc);
932 }
933
934 (*cmdline)[*cmdline_nbytes] = '\0';
935 *cmdline_nbytes += 1;
936 }
937 }
938 else
939 {
940 zbx_free(*cmdline);
941 }
942
943 return SUCCEED;
944 }
945
946 /******************************************************************************
947 * *
948 * Function: proc_get_process_uid *
949 * *
950 * Purpose: returns process user identifier *
951 * *
952 * Parameters: pid - [IN] the process identifier *
953 * uid - [OUT] the user identifier *
954 * *
955 * Return value: SUCCEED *
956 * FAIL *
957 * *
958 ******************************************************************************/
proc_get_process_uid(pid_t pid,uid_t * uid)959 static int proc_get_process_uid(pid_t pid, uid_t *uid)
960 {
961 char tmp[MAX_STRING_LEN];
962 zbx_stat_t st;
963
964 zbx_snprintf(tmp, sizeof(tmp), "/proc/%d", (int)pid);
965
966 if (0 != zbx_stat(tmp, &st))
967 return FAIL;
968
969 *uid = st.st_uid;
970
971 return SUCCEED;
972 }
973
974 /******************************************************************************
975 * *
976 * Function: proc_read_value *
977 * *
978 * Purpose: read 64 bit unsigned space or zero character terminated integer *
979 * from a text string *
980 * *
981 * Parameters: ptr - [IN] the text string *
982 * value - [OUT] the parsed value *
983 * *
984 * Return value: The length of the parsed text or FAIL if parsing failed. *
985 * *
986 ******************************************************************************/
proc_read_value(const char * ptr,zbx_uint64_t * value)987 static int proc_read_value(const char *ptr, zbx_uint64_t *value)
988 {
989 const char *start = ptr;
990 int len;
991
992 while (' ' != *ptr && '\0' != *ptr)
993 ptr++;
994
995 len = ptr - start;
996
997 if (SUCCEED == is_uint64_n(start, len, value))
998 return len;
999
1000 return FAIL;
1001 }
1002
1003 /******************************************************************************
1004 * *
1005 * Function: proc_read_cpu_util *
1006 * *
1007 * Purpose: reads process cpu utilization values from /proc/[pid]/stat file *
1008 * *
1009 * Parameters: procutil - [IN/OUT] the process cpu utilization data *
1010 * *
1011 * Return value: SUCCEED - the process cpu utilization data was read *
1012 * successfully *
1013 * <0 - otherwise, -errno code is returned *
1014 * *
1015 ******************************************************************************/
proc_read_cpu_util(zbx_procstat_util_t * procutil)1016 static int proc_read_cpu_util(zbx_procstat_util_t *procutil)
1017 {
1018 int n, offset, fd, ret = SUCCEED;
1019 char tmp[MAX_STRING_LEN], *ptr;
1020
1021 zbx_snprintf(tmp, sizeof(tmp), "/proc/%d/stat", (int)procutil->pid);
1022
1023 if (-1 == (fd = open(tmp, O_RDONLY)))
1024 return -errno;
1025
1026 if (-1 == (n = read(fd, tmp, sizeof(tmp) - 1)))
1027 {
1028 ret = -errno;
1029 goto out;
1030 }
1031
1032 tmp[n] = '\0';
1033
1034 /* skip to the end of process name to avoid dealing with possible spaces in process name */
1035 if (NULL == (ptr = strrchr(tmp, ')')))
1036 {
1037 ret = -EFAULT;
1038 goto out;
1039 }
1040
1041 n = 0;
1042
1043 while ('\0' != *ptr)
1044 {
1045 if (' ' != *ptr++)
1046 continue;
1047
1048 switch (++n)
1049 {
1050 case 12:
1051 if (FAIL == (offset = proc_read_value(ptr, &procutil->utime)))
1052 {
1053 ret = -EINVAL;
1054 goto out;
1055 }
1056 ptr += offset;
1057
1058 break;
1059 case 13:
1060 if (FAIL == (offset = proc_read_value(ptr, &procutil->stime)))
1061 {
1062 ret = -EINVAL;
1063 goto out;
1064 }
1065 ptr += offset;
1066
1067 break;
1068 case 20:
1069 if (FAIL == proc_read_value(ptr, &procutil->starttime))
1070 {
1071 ret = -EINVAL;
1072 goto out;
1073 }
1074
1075 goto out;
1076 }
1077 }
1078
1079 ret = -ENODATA;
1080 out:
1081 close(fd);
1082
1083 return ret;
1084 }
1085
1086 /******************************************************************************
1087 * *
1088 * Function: proc_match_name *
1089 * *
1090 * Purpose: checks if the process name matches filter *
1091 * *
1092 ******************************************************************************/
proc_match_name(const zbx_sysinfo_proc_t * proc,const char * procname)1093 static int proc_match_name(const zbx_sysinfo_proc_t *proc, const char *procname)
1094 {
1095 if (NULL == procname)
1096 return SUCCEED;
1097
1098 if (NULL != proc->name && 0 == strcmp(procname, proc->name))
1099 return SUCCEED;
1100
1101 if (NULL != proc->name_arg0 && 0 == strcmp(procname, proc->name_arg0))
1102 return SUCCEED;
1103
1104 return FAIL;
1105 }
1106
1107 /******************************************************************************
1108 * *
1109 * Function: proc_match_user *
1110 * *
1111 * Purpose: checks if the process user matches filter *
1112 * *
1113 ******************************************************************************/
proc_match_user(const zbx_sysinfo_proc_t * proc,const struct passwd * usrinfo)1114 static int proc_match_user(const zbx_sysinfo_proc_t *proc, const struct passwd *usrinfo)
1115 {
1116 if (NULL == usrinfo)
1117 return SUCCEED;
1118
1119 if (proc->uid == usrinfo->pw_uid)
1120 return SUCCEED;
1121
1122 return FAIL;
1123 }
1124
1125 /******************************************************************************
1126 * *
1127 * Function: proc_match_cmdline *
1128 * *
1129 * Purpose: checks if the process command line matches filter *
1130 * *
1131 ******************************************************************************/
proc_match_cmdline(const zbx_sysinfo_proc_t * proc,const char * cmdline)1132 static int proc_match_cmdline(const zbx_sysinfo_proc_t *proc, const char *cmdline)
1133 {
1134 if (NULL == cmdline)
1135 return SUCCEED;
1136
1137 if (NULL != proc->cmdline && NULL != zbx_regexp_match(proc->cmdline, cmdline, NULL))
1138 return SUCCEED;
1139
1140 return FAIL;
1141 }
1142
1143 /******************************************************************************
1144 * *
1145 * Function: zbx_proc_get_process_stats *
1146 * *
1147 * Purpose: get process cpu utilization data *
1148 * *
1149 * Parameters: procs - [IN/OUT] an array of process utilization data *
1150 * procs_num - [IN] the number of items in procs array *
1151 * *
1152 ******************************************************************************/
zbx_proc_get_process_stats(zbx_procstat_util_t * procs,int procs_num)1153 void zbx_proc_get_process_stats(zbx_procstat_util_t *procs, int procs_num)
1154 {
1155 int i;
1156
1157 zabbix_log(LOG_LEVEL_TRACE, "In %s() procs_num:%d", __func__, procs_num);
1158
1159 for (i = 0; i < procs_num; i++)
1160 procs[i].error = proc_read_cpu_util(&procs[i]);
1161
1162 zabbix_log(LOG_LEVEL_TRACE, "End of %s()", __func__);
1163 }
1164
1165 /******************************************************************************
1166 * *
1167 * Function: proc_create *
1168 * *
1169 * Purpose: create process object with the specified properties *
1170 * *
1171 * Parameters: pid - [IN] the process identifier *
1172 * flags - [IN] the flags specifying properties to set *
1173 * *
1174 * Return value: The created process object or NULL if property reading *
1175 * failed. *
1176 * *
1177 ******************************************************************************/
proc_create(int pid,unsigned int flags)1178 static zbx_sysinfo_proc_t *proc_create(int pid, unsigned int flags)
1179 {
1180 char *procname = NULL, *cmdline = NULL, *name_arg0 = NULL;
1181 uid_t uid = (uid_t)-1;
1182 zbx_sysinfo_proc_t *proc = NULL;
1183 int ret = FAIL;
1184 size_t cmdline_nbytes;
1185
1186 if (0 != (flags & ZBX_SYSINFO_PROC_USER) && SUCCEED != proc_get_process_uid(pid, &uid))
1187 goto out;
1188
1189 if (0 != (flags & (ZBX_SYSINFO_PROC_CMDLINE | ZBX_SYSINFO_PROC_NAME)) &&
1190 SUCCEED != proc_get_process_cmdline(pid, &cmdline, &cmdline_nbytes))
1191 {
1192 goto out;
1193 }
1194
1195 if (0 != (flags & ZBX_SYSINFO_PROC_NAME) && SUCCEED != proc_get_process_name(pid, &procname))
1196 goto out;
1197
1198 if (NULL != cmdline)
1199 {
1200 char *ptr;
1201 unsigned int i;
1202
1203 if (0 != (flags & ZBX_SYSINFO_PROC_NAME))
1204 {
1205 if (NULL == (ptr = strrchr(cmdline, '/')))
1206 name_arg0 = zbx_strdup(NULL, cmdline);
1207 else
1208 name_arg0 = zbx_strdup(NULL, ptr + 1);
1209 }
1210
1211 /* according to proc(5) the arguments are separated by '\0' */
1212 for (i = 0; i < cmdline_nbytes - 1; i++)
1213 if ('\0' == cmdline[i])
1214 cmdline[i] = ' ';
1215 }
1216
1217 ret = SUCCEED;
1218 out:
1219 if (SUCCEED == ret)
1220 {
1221 proc = (zbx_sysinfo_proc_t *)zbx_malloc(NULL, sizeof(zbx_sysinfo_proc_t));
1222
1223 proc->pid = pid;
1224 proc->uid = uid;
1225 proc->name = procname;
1226 proc->cmdline = cmdline;
1227 proc->name_arg0 = name_arg0;
1228 }
1229 else
1230 {
1231 zbx_free(procname);
1232 zbx_free(cmdline);
1233 zbx_free(name_arg0);
1234 }
1235
1236 return proc;
1237 }
1238
1239 /******************************************************************************
1240 * *
1241 * Function: zbx_proc_get_processes *
1242 * *
1243 * Purpose: get system processes *
1244 * *
1245 * Parameters: processes - [OUT] the system processes *
1246 * flags - [IN] the flags specifying the process properties *
1247 * that must be returned *
1248 * *
1249 * Return value: SUCCEED - the system processes were retrieved successfully *
1250 * FAIL - failed to open /proc directory *
1251 * *
1252 ******************************************************************************/
zbx_proc_get_processes(zbx_vector_ptr_t * processes,unsigned int flags)1253 int zbx_proc_get_processes(zbx_vector_ptr_t *processes, unsigned int flags)
1254 {
1255 DIR *dir;
1256 struct dirent *entries;
1257 int ret = FAIL, pid;
1258 zbx_sysinfo_proc_t *proc;
1259
1260 zabbix_log(LOG_LEVEL_TRACE, "In %s()", __func__);
1261
1262 if (NULL == (dir = opendir("/proc")))
1263 goto out;
1264
1265 while (NULL != (entries = readdir(dir)))
1266 {
1267 /* skip entries not containing pids */
1268 if (FAIL == is_uint32(entries->d_name, &pid))
1269 continue;
1270
1271 if (NULL == (proc = proc_create(pid, flags)))
1272 continue;
1273
1274 zbx_vector_ptr_append(processes, proc);
1275 }
1276
1277 closedir(dir);
1278
1279 ret = SUCCEED;
1280 out:
1281 zabbix_log(LOG_LEVEL_TRACE, "End of %s(): %s, processes:%d", __func__, zbx_result_string(ret),
1282 processes->values_num);
1283
1284 return ret;
1285 }
1286
1287 /******************************************************************************
1288 * *
1289 * Function: zbx_proc_free_processes *
1290 * *
1291 * Purpose: frees process vector read by zbx_proc_get_processes function *
1292 * *
1293 * Parameters: processes - [IN/OUT] the process vector to free *
1294 * *
1295 ******************************************************************************/
zbx_proc_free_processes(zbx_vector_ptr_t * processes)1296 void zbx_proc_free_processes(zbx_vector_ptr_t *processes)
1297 {
1298 zbx_vector_ptr_clear_ext(processes, (zbx_mem_free_func_t)zbx_sysinfo_proc_free);
1299 }
1300
1301 /******************************************************************************
1302 * *
1303 * Function: zbx_proc_get_matching_pids *
1304 * *
1305 * Purpose: get pids matching the specified process name, user name and *
1306 * command line *
1307 * *
1308 * Parameters: processes - [IN] the list of system processes *
1309 * procname - [IN] the process name, NULL - all *
1310 * username - [IN] the user name, NULL - all *
1311 * cmdline - [IN] the command line, NULL - all *
1312 * pids - [OUT] the vector of matching pids *
1313 * *
1314 * Return value: SUCCEED - the pids were read successfully *
1315 * -errno - failed to read pids *
1316 * *
1317 ******************************************************************************/
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)1318 void zbx_proc_get_matching_pids(const zbx_vector_ptr_t *processes, const char *procname, const char *username,
1319 const char *cmdline, zbx_uint64_t flags, zbx_vector_uint64_t *pids)
1320 {
1321 struct passwd *usrinfo;
1322 int i;
1323 zbx_sysinfo_proc_t *proc;
1324
1325 zabbix_log(LOG_LEVEL_TRACE, "In %s() procname:%s username:%s cmdline:%s flags:" ZBX_FS_UI64, __func__,
1326 ZBX_NULL2EMPTY_STR(procname), ZBX_NULL2EMPTY_STR(username), ZBX_NULL2EMPTY_STR(cmdline), flags);
1327
1328 if (NULL != username)
1329 {
1330 /* in the case of invalid user there are no matching processes, return empty vector */
1331 if (NULL == (usrinfo = getpwnam(username)))
1332 goto out;
1333 }
1334 else
1335 usrinfo = NULL;
1336
1337 for (i = 0; i < processes->values_num; i++)
1338 {
1339 proc = (zbx_sysinfo_proc_t *)processes->values[i];
1340
1341 if (SUCCEED != proc_match_user(proc, usrinfo))
1342 continue;
1343
1344 if (SUCCEED != proc_match_name(proc, procname))
1345 continue;
1346
1347 if (SUCCEED != proc_match_cmdline(proc, cmdline))
1348 continue;
1349
1350 zbx_vector_uint64_append(pids, (zbx_uint64_t)proc->pid);
1351 }
1352 out:
1353 zabbix_log(LOG_LEVEL_TRACE, "End of %s()", __func__);
1354 }
1355
PROC_CPU_UTIL(AGENT_REQUEST * request,AGENT_RESULT * result)1356 int PROC_CPU_UTIL(AGENT_REQUEST *request, AGENT_RESULT *result)
1357 {
1358 const char *procname, *username, *cmdline, *tmp;
1359 char *errmsg = NULL;
1360 int period, type;
1361 double value;
1362 zbx_timespec_t ts_timeout, ts;
1363
1364 /* proc.cpu.util[<procname>,<username>,(user|system),<cmdline>,(avg1|avg5|avg15)] */
1365 /* 0 1 2 3 4 */
1366 if (5 < request->nparam)
1367 {
1368 SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
1369 return SYSINFO_RET_FAIL;
1370 }
1371
1372 /* zbx_procstat_get_* functions expect NULL for default values - */
1373 /* convert empty procname, username and cmdline strings to NULL values */
1374 if (NULL != (procname = get_rparam(request, 0)) && '\0' == *procname)
1375 procname = NULL;
1376
1377 if (NULL != (username = get_rparam(request, 1)) && '\0' == *username)
1378 username = NULL;
1379
1380 if (NULL != (cmdline = get_rparam(request, 3)) && '\0' == *cmdline)
1381 cmdline = NULL;
1382
1383 /* utilization type parameter (user|system) */
1384 if (NULL == (tmp = get_rparam(request, 2)) || '\0' == *tmp || 0 == strcmp(tmp, "total"))
1385 {
1386 type = ZBX_PROCSTAT_CPU_TOTAL;
1387 }
1388 else if (0 == strcmp(tmp, "user"))
1389 {
1390 type = ZBX_PROCSTAT_CPU_USER;
1391 }
1392 else if (0 == strcmp(tmp, "system"))
1393 {
1394 type = ZBX_PROCSTAT_CPU_SYSTEM;
1395 }
1396 else
1397 {
1398 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
1399 return SYSINFO_RET_FAIL;
1400 }
1401
1402 /* mode parameter (avg1|avg5|avg15) */
1403 if (NULL == (tmp = get_rparam(request, 4)) || '\0' == *tmp || 0 == strcmp(tmp, "avg1"))
1404 {
1405 period = SEC_PER_MIN;
1406 }
1407 else if (0 == strcmp(tmp, "avg5"))
1408 {
1409 period = SEC_PER_MIN * 5;
1410 }
1411 else if (0 == strcmp(tmp, "avg15"))
1412 {
1413 period = SEC_PER_MIN * 15;
1414 }
1415 else
1416 {
1417 SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid fifth parameter."));
1418 return SYSINFO_RET_FAIL;
1419 }
1420
1421 if (SUCCEED != zbx_procstat_collector_started())
1422 {
1423 SET_MSG_RESULT(result, zbx_strdup(NULL, "Collector is not started."));
1424 return SYSINFO_RET_FAIL;
1425 }
1426
1427 zbx_timespec(&ts_timeout);
1428 ts_timeout.sec += CONFIG_TIMEOUT;
1429
1430 while (SUCCEED != zbx_procstat_get_util(procname, username, cmdline, 0, period, type, &value, &errmsg))
1431 {
1432 /* zbx_procstat_get_* functions will return FAIL when either a collection */
1433 /* error was registered or if less than 2 data samples were collected. */
1434 /* In the first case the errmsg will contain error message. */
1435 if (NULL != errmsg)
1436 {
1437 SET_MSG_RESULT(result, errmsg);
1438 return SYSINFO_RET_FAIL;
1439 }
1440
1441 zbx_timespec(&ts);
1442
1443 if (0 > zbx_timespec_compare(&ts_timeout, &ts))
1444 {
1445 SET_MSG_RESULT(result, zbx_strdup(NULL, "Timeout while waiting for collector data."));
1446 return SYSINFO_RET_FAIL;
1447 }
1448
1449 sleep(1);
1450 }
1451
1452 SET_DBL_RESULT(result, value);
1453
1454 return SYSINFO_RET_OK;
1455 }
1456