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 "log.h"
22 #include "setproctitle.h"
23
24 /* scheduler support */
25
26 #define ZBX_SCHEDULER_FILTER_DAY 1
27 #define ZBX_SCHEDULER_FILTER_HOUR 2
28 #define ZBX_SCHEDULER_FILTER_MINUTE 3
29 #define ZBX_SCHEDULER_FILTER_SECOND 4
30
31 typedef struct
32 {
33 int start_day; /* day of week when period starts */
34 int end_day; /* day of week when period ends, included */
35 int start_time; /* number of seconds from the beginning of the day when period starts */
36 int end_time; /* number of seconds from the beginning of the day when period ends, not included */
37 }
38 zbx_time_period_t;
39
40 typedef struct zbx_flexible_interval
41 {
42 zbx_time_period_t period;
43 int delay;
44
45 struct zbx_flexible_interval *next;
46 }
47 zbx_flexible_interval_t;
48
49 typedef struct zbx_scheduler_filter
50 {
51 int start;
52 int end;
53 int step;
54
55 struct zbx_scheduler_filter *next;
56 }
57 zbx_scheduler_filter_t;
58
59 typedef struct zbx_scheduler_interval
60 {
61 zbx_scheduler_filter_t *mdays;
62 zbx_scheduler_filter_t *wdays;
63 zbx_scheduler_filter_t *hours;
64 zbx_scheduler_filter_t *minutes;
65 zbx_scheduler_filter_t *seconds;
66
67 int filter_level;
68
69 struct zbx_scheduler_interval *next;
70 }
71 zbx_scheduler_interval_t;
72
73 struct zbx_custom_interval
74 {
75 zbx_flexible_interval_t *flexible;
76 zbx_scheduler_interval_t *scheduling;
77 };
78
79 const int INTERFACE_TYPE_PRIORITY[INTERFACE_TYPE_COUNT] =
80 {
81 INTERFACE_TYPE_AGENT,
82 INTERFACE_TYPE_SNMP,
83 INTERFACE_TYPE_JMX,
84 INTERFACE_TYPE_IPMI
85 };
86
87 static ZBX_THREAD_LOCAL volatile sig_atomic_t zbx_timed_out; /* 0 - no timeout occurred, 1 - SIGALRM took place */
88
89 double ZBX_DOUBLE_EPSILON = 2.22e-16;
90
91 #ifdef _WINDOWS
92
93 char ZABBIX_SERVICE_NAME[ZBX_SERVICE_NAME_LEN] = APPLICATION_NAME;
94 char ZABBIX_EVENT_SOURCE[ZBX_SERVICE_NAME_LEN] = APPLICATION_NAME;
95
__zbx_stat(const char * path,zbx_stat_t * buf)96 int __zbx_stat(const char *path, zbx_stat_t *buf)
97 {
98 int ret, fd;
99 wchar_t *wpath;
100
101 wpath = zbx_utf8_to_unicode(path);
102
103 if (-1 == (ret = _wstat64(wpath, buf)))
104 goto out;
105
106 if (0 != S_ISDIR(buf->st_mode) || 0 != buf->st_size)
107 goto out;
108
109 /* In the case of symlinks _wstat64 returns zero file size. */
110 /* Try to work around it by opening the file and using fstat. */
111
112 ret = -1;
113
114 if (-1 != (fd = _wopen(wpath, O_RDONLY)))
115 {
116 ret = _fstat64(fd, buf);
117 _close(fd);
118 }
119 out:
120 zbx_free(wpath);
121
122 return ret;
123 }
124
125 #endif
126
127 /******************************************************************************
128 * *
129 * Function: get_program_name *
130 * *
131 * Purpose: return program name without path *
132 * *
133 * Parameters: path *
134 * *
135 * Return value: program name without path *
136 * *
137 * Author: Eugene Grigorjev *
138 * *
139 ******************************************************************************/
get_program_name(const char * path)140 const char *get_program_name(const char *path)
141 {
142 const char *filename = NULL;
143
144 for (filename = path; path && *path; path++)
145 {
146 if ('\\' == *path || '/' == *path)
147 filename = path + 1;
148 }
149
150 return filename;
151 }
152
153 /******************************************************************************
154 * *
155 * Function: zbx_timespec *
156 * *
157 * Purpose: Gets the current time. *
158 * *
159 * Author: Alexander Vladishev *
160 * *
161 * Comments: Time in seconds since midnight (00:00:00), *
162 * January 1, 1970, coordinated universal time (UTC). *
163 * *
164 ******************************************************************************/
zbx_timespec(zbx_timespec_t * ts)165 void zbx_timespec(zbx_timespec_t *ts)
166 {
167 static ZBX_THREAD_LOCAL zbx_timespec_t last_ts = {0, 0};
168 static ZBX_THREAD_LOCAL int corr = 0;
169 #if defined(_WINDOWS) || defined(__MINGW32__)
170 static ZBX_THREAD_LOCAL LARGE_INTEGER tickPerSecond = {0};
171 struct _timeb tb;
172 #else
173 struct timeval tv;
174 int rc = -1;
175 # ifdef HAVE_TIME_CLOCK_GETTIME
176 struct timespec tp;
177 # endif
178 #endif
179 #if defined(_WINDOWS) || defined(__MINGW32__)
180
181 if (0 == tickPerSecond.QuadPart)
182 QueryPerformanceFrequency(&tickPerSecond);
183
184 _ftime(&tb);
185
186 ts->sec = (int)tb.time;
187 ts->ns = tb.millitm * 1000000;
188
189 if (0 != tickPerSecond.QuadPart)
190 {
191 LARGE_INTEGER tick;
192
193 if (TRUE == QueryPerformanceCounter(&tick))
194 {
195 static ZBX_THREAD_LOCAL LARGE_INTEGER last_tick = {0};
196
197 if (0 < last_tick.QuadPart)
198 {
199 LARGE_INTEGER qpc_tick = {0}, ntp_tick = {0};
200
201 /* _ftime () returns precision in milliseconds, but 'ns' could be increased up to 1ms */
202 if (last_ts.sec == ts->sec && last_ts.ns > ts->ns && 1000000 > (last_ts.ns - ts->ns))
203 {
204 ts->ns = last_ts.ns;
205 }
206 else
207 {
208 ntp_tick.QuadPart = tickPerSecond.QuadPart * (ts->sec - last_ts.sec) +
209 tickPerSecond.QuadPart * (ts->ns - last_ts.ns) / 1000000000;
210 }
211
212 /* host system time can shift backwards, then correction is not reasonable */
213 if (0 <= ntp_tick.QuadPart)
214 qpc_tick.QuadPart = tick.QuadPart - last_tick.QuadPart - ntp_tick.QuadPart;
215
216 if (0 < qpc_tick.QuadPart && qpc_tick.QuadPart < tickPerSecond.QuadPart)
217 {
218 int ns = (int)(1000000000 * qpc_tick.QuadPart / tickPerSecond.QuadPart);
219
220 if (1000000 > ns) /* value less than 1 millisecond */
221 {
222 ts->ns += ns;
223
224 while (ts->ns >= 1000000000)
225 {
226 ts->sec++;
227 ts->ns -= 1000000000;
228 }
229 }
230 }
231 }
232
233 last_tick = tick;
234 }
235 }
236 #else /* not _WINDOWS */
237 #ifdef HAVE_TIME_CLOCK_GETTIME
238 if (0 == (rc = clock_gettime(CLOCK_REALTIME, &tp)))
239 {
240 ts->sec = (int)tp.tv_sec;
241 ts->ns = (int)tp.tv_nsec;
242 }
243 #endif /* HAVE_TIME_CLOCK_GETTIME */
244
245 if (0 != rc && 0 == (rc = gettimeofday(&tv, NULL)))
246 {
247 ts->sec = (int)tv.tv_sec;
248 ts->ns = (int)tv.tv_usec * 1000;
249 }
250
251 if (0 != rc)
252 {
253 ts->sec = (int)time(NULL);
254 ts->ns = 0;
255 }
256 #endif /* not _WINDOWS */
257
258 if (last_ts.ns == ts->ns && last_ts.sec == ts->sec)
259 {
260 ts->ns += ++corr;
261
262 while (ts->ns >= 1000000000)
263 {
264 ts->sec++;
265 ts->ns -= 1000000000;
266 }
267 }
268 else
269 {
270 last_ts.sec = ts->sec;
271 last_ts.ns = ts->ns;
272 corr = 0;
273 }
274 }
275
276 /******************************************************************************
277 * *
278 * Function: zbx_time *
279 * *
280 * Purpose: Gets the current time. *
281 * *
282 * Return value: Time in seconds *
283 * *
284 * Author: Eugene Grigorjev *
285 * *
286 * Comments: Time in seconds since midnight (00:00:00), *
287 * January 1, 1970, coordinated universal time (UTC). *
288 * *
289 ******************************************************************************/
zbx_time(void)290 double zbx_time(void)
291 {
292 zbx_timespec_t ts;
293
294 zbx_timespec(&ts);
295
296 return (double)ts.sec + 1.0e-9 * (double)ts.ns;
297 }
298
299 /******************************************************************************
300 * *
301 * Function: zbx_current_time *
302 * *
303 * Purpose: Gets the current time including UTC offset *
304 * *
305 * Return value: Time in seconds *
306 * *
307 * Author: Eugene Grigorjev *
308 * *
309 ******************************************************************************/
zbx_current_time(void)310 double zbx_current_time(void)
311 {
312 return zbx_time() + ZBX_JAN_1970_IN_SEC;
313 }
314
315 /******************************************************************************
316 * *
317 * Function: is_leap_year *
318 * *
319 * Return value: SUCCEED - year is a leap year *
320 * FAIL - year is not a leap year *
321 * *
322 ******************************************************************************/
is_leap_year(int year)323 static int is_leap_year(int year)
324 {
325 return 0 == year % 4 && (0 != year % 100 || 0 == year % 400) ? SUCCEED : FAIL;
326 }
327
328 /******************************************************************************
329 * *
330 * Function: zbx_get_time *
331 * *
332 * Purpose: *
333 * get current time and store it in memory locations provided by caller *
334 * *
335 * Parameters: *
336 * tm - [OUT] broken-down representation of the current time *
337 * milliseconds - [OUT] milliseconds since the previous second *
338 * tz - [OUT] local time offset from UTC (optional) *
339 * *
340 * Comments: *
341 * On Windows localtime() and gmtime() return pointers to static, *
342 * thread-local storage locations. On Unix localtime() and gmtime() are *
343 * not thread-safe and re-entrant as they return pointers to static *
344 * storage locations which can be overwritten by localtime(), gmtime() *
345 * or other time functions in other threads or signal handlers. To avoid *
346 * this we use localtime_r() and gmtime_r(). *
347 * *
348 ******************************************************************************/
zbx_get_time(struct tm * tm,long * milliseconds,zbx_timezone_t * tz)349 void zbx_get_time(struct tm *tm, long *milliseconds, zbx_timezone_t *tz)
350 {
351 #if defined(_WINDOWS) || defined(__MINGW32__)
352 struct _timeb current_time;
353
354 _ftime(¤t_time);
355 *tm = *localtime(¤t_time.time); /* localtime() cannot return NULL if called with valid parameter */
356 *milliseconds = current_time.millitm;
357 #else
358 struct timeval current_time;
359
360 gettimeofday(¤t_time, NULL);
361 localtime_r(¤t_time.tv_sec, tm);
362 *milliseconds = current_time.tv_usec / 1000;
363 #endif
364 if (NULL != tz)
365 {
366 long offset;
367 #if defined(_WINDOWS) || defined(__MINGW32__)
368 offset = zbx_get_timezone_offset(current_time.time, tm);
369 #else
370 offset = zbx_get_timezone_offset(current_time.tv_sec, tm);
371 #endif
372 tz->tz_sign = (0 <= offset ? '+' : '-');
373 tz->tz_hour = labs(offset) / SEC_PER_HOUR;
374 tz->tz_min = (labs(offset) - tz->tz_hour * SEC_PER_HOUR) / SEC_PER_MIN;
375 /* assuming no remaining seconds like in historic Asia/Riyadh87, Asia/Riyadh88 and Asia/Riyadh89 */
376 }
377 }
378
379 /******************************************************************************
380 * *
381 * Function: zbx_get_timezone_offset *
382 * *
383 * Purpose: get time offset from UTC *
384 * *
385 * Parameters: t - [IN] input time to calculate offset with *
386 * tm - [OUT] broken-down representation of the current time *
387 * *
388 * Return value: Time offset from UTC in seconds *
389 * *
390 ******************************************************************************/
zbx_get_timezone_offset(time_t t,struct tm * tm)391 long zbx_get_timezone_offset(time_t t, struct tm *tm)
392 {
393 long offset;
394 #ifndef HAVE_TM_TM_GMTOFF
395 struct tm tm_utc;
396 #endif
397
398 *tm = *localtime(&t);
399
400 #ifdef HAVE_TM_TM_GMTOFF
401 offset = tm->tm_gmtoff;
402 #else
403 #if defined(_WINDOWS) || defined(__MINGW32__)
404 tm_utc = *gmtime(&t);
405 #else
406 gmtime_r(&t, &tm_utc);
407 #endif
408 offset = (tm->tm_yday - tm_utc.tm_yday) * SEC_PER_DAY +
409 (tm->tm_hour - tm_utc.tm_hour) * SEC_PER_HOUR +
410 (tm->tm_min - tm_utc.tm_min) * SEC_PER_MIN; /* assuming seconds are equal */
411
412 while (tm->tm_year > tm_utc.tm_year)
413 offset += (SUCCEED == is_leap_year(tm_utc.tm_year++) ? SEC_PER_YEAR + SEC_PER_DAY : SEC_PER_YEAR);
414
415 while (tm->tm_year < tm_utc.tm_year)
416 offset -= (SUCCEED == is_leap_year(--tm_utc.tm_year) ? SEC_PER_YEAR + SEC_PER_DAY : SEC_PER_YEAR);
417 #endif
418
419 return offset;
420 }
421
422 /******************************************************************************
423 * *
424 * Function: zbx_utc_time *
425 * *
426 * Purpose: get UTC time from time from broken down time elements *
427 * *
428 * Parameters: *
429 * year - [IN] year (1970-...) *
430 * month - [IN] month (1-12) *
431 * mday - [IN] day of month (1-..., depending on month and year) *
432 * hour - [IN] hours (0-23) *
433 * min - [IN] minutes (0-59) *
434 * sec - [IN] seconds (0-61, leap seconds are not strictly validated) *
435 * t - [OUT] Epoch timestamp *
436 * *
437 * Return value: SUCCEED - date is valid and resulting timestamp is positive *
438 * FAIL - otherwise *
439 * *
440 ******************************************************************************/
zbx_utc_time(int year,int mon,int mday,int hour,int min,int sec,int * t)441 int zbx_utc_time(int year, int mon, int mday, int hour, int min, int sec, int *t)
442 {
443 /* number of leap years before but not including year */
444 #define ZBX_LEAP_YEARS(year) (((year) - 1) / 4 - ((year) - 1) / 100 + ((year) - 1) / 400)
445
446 /* days since the beginning of non-leap year till the beginning of the month */
447 static const int month_day[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
448 static const int epoch_year = 1970;
449
450 if (epoch_year <= year && 1 <= mon && mon <= 12 && 1 <= mday && mday <= zbx_day_in_month(year, mon) &&
451 0 <= hour && hour <= 23 && 0 <= min && min <= 59 && 0 <= sec && sec <= 61 &&
452 0 <= (*t = (year - epoch_year) * SEC_PER_YEAR +
453 (ZBX_LEAP_YEARS(2 < mon ? year + 1 : year) - ZBX_LEAP_YEARS(epoch_year)) * SEC_PER_DAY +
454 (month_day[mon - 1] + mday - 1) * SEC_PER_DAY + hour * SEC_PER_HOUR + min * SEC_PER_MIN + sec))
455 {
456 return SUCCEED;
457 }
458
459 return FAIL;
460 #undef ZBX_LEAP_YEARS
461 }
462
463 /******************************************************************************
464 * *
465 * Function: zbx_day_in_month *
466 * *
467 * Purpose: returns number of days in a month *
468 * *
469 * Parameters: *
470 * year - [IN] year *
471 * mon - [IN] month (1-12) *
472 * *
473 * Return value: 28-31 depending on number of days in the month, defaults to *
474 * 30 if the month is outside of allowed range *
475 * *
476 * Author: Alexander Vladishev *
477 * *
478 ******************************************************************************/
zbx_day_in_month(int year,int mon)479 int zbx_day_in_month(int year, int mon)
480 {
481 /* number of days in the month of a non-leap year */
482 static const unsigned char month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
483
484 if (1 <= mon && mon <= 12) /* add one day in February of a leap year */
485 return month[mon - 1] + (2 == mon && SUCCEED == is_leap_year(year) ? 1 : 0);
486
487 return 30;
488 }
489
490 /******************************************************************************
491 * *
492 * Function: zbx_get_duration_ms *
493 * *
494 * Purpose: get duration in milliseconds since time stamp till current time *
495 * *
496 * Parameters: *
497 * start_time - [IN] time from when duration should be counted *
498 * *
499 * Return value: duration in milliseconds since time stamp till current time *
500 * *
501 ******************************************************************************/
zbx_get_duration_ms(const zbx_timespec_t * ts)502 zbx_uint64_t zbx_get_duration_ms(const zbx_timespec_t *ts)
503 {
504 zbx_timespec_t now;
505
506 zbx_timespec(&now);
507
508 return (now.sec - ts->sec) * 1e3 + (now.ns - ts->ns) / 1e6;
509 }
510
511 /******************************************************************************
512 * *
513 * Function: zbx_calloc2 *
514 * *
515 * Purpose: allocates nmemb * size bytes of memory and fills it with zeros *
516 * *
517 * Return value: returns a pointer to the newly allocated memory *
518 * *
519 * Author: Eugene Grigorjev, Rudolfs Kreicbergs *
520 * *
521 ******************************************************************************/
zbx_calloc2(const char * filename,int line,void * old,size_t nmemb,size_t size)522 void *zbx_calloc2(const char *filename, int line, void *old, size_t nmemb, size_t size)
523 {
524 int max_attempts;
525 void *ptr = NULL;
526
527 /* old pointer must be NULL */
528 if (NULL != old)
529 {
530 zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_calloc: allocating already allocated memory. "
531 "Please report this to Zabbix developers.",
532 filename, line);
533 }
534
535 for (
536 max_attempts = 10, nmemb = MAX(nmemb, 1), size = MAX(size, 1);
537 0 < max_attempts && NULL == ptr;
538 ptr = calloc(nmemb, size), max_attempts--
539 );
540
541 if (NULL != ptr)
542 return ptr;
543
544 zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_calloc: out of memory. Requested " ZBX_FS_SIZE_T " bytes.",
545 filename, line, (zbx_fs_size_t)size);
546
547 exit(EXIT_FAILURE);
548 }
549
550 /******************************************************************************
551 * *
552 * Function: zbx_malloc2 *
553 * *
554 * Purpose: allocates size bytes of memory *
555 * *
556 * Return value: returns a pointer to the newly allocated memory *
557 * *
558 * Author: Eugene Grigorjev *
559 * *
560 ******************************************************************************/
zbx_malloc2(const char * filename,int line,void * old,size_t size)561 void *zbx_malloc2(const char *filename, int line, void *old, size_t size)
562 {
563 int max_attempts;
564 void *ptr = NULL;
565
566 /* old pointer must be NULL */
567 if (NULL != old)
568 {
569 zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_malloc: allocating already allocated memory. "
570 "Please report this to Zabbix developers.",
571 filename, line);
572 }
573
574 for (
575 max_attempts = 10, size = MAX(size, 1);
576 0 < max_attempts && NULL == ptr;
577 ptr = malloc(size), max_attempts--
578 );
579
580 if (NULL != ptr)
581 return ptr;
582
583 zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_malloc: out of memory. Requested " ZBX_FS_SIZE_T " bytes.",
584 filename, line, (zbx_fs_size_t)size);
585
586 exit(EXIT_FAILURE);
587 }
588
589 /******************************************************************************
590 * *
591 * Function: zbx_realloc2 *
592 * *
593 * Purpose: changes the size of the memory block pointed to by old *
594 * to size bytes *
595 * *
596 * Return value: returns a pointer to the newly allocated memory *
597 * *
598 * Author: Eugene Grigorjev *
599 * *
600 ******************************************************************************/
zbx_realloc2(const char * filename,int line,void * old,size_t size)601 void *zbx_realloc2(const char *filename, int line, void *old, size_t size)
602 {
603 int max_attempts;
604 void *ptr = NULL;
605
606 for (
607 max_attempts = 10, size = MAX(size, 1);
608 0 < max_attempts && NULL == ptr;
609 ptr = realloc(old, size), max_attempts--
610 );
611
612 if (NULL != ptr)
613 return ptr;
614
615 zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_realloc: out of memory. Requested " ZBX_FS_SIZE_T " bytes.",
616 filename, line, (zbx_fs_size_t)size);
617
618 exit(EXIT_FAILURE);
619 }
620
zbx_strdup2(const char * filename,int line,char * old,const char * str)621 char *zbx_strdup2(const char *filename, int line, char *old, const char *str)
622 {
623 int retry;
624 char *ptr = NULL;
625
626 zbx_free(old);
627
628 for (retry = 10; 0 < retry && NULL == ptr; ptr = strdup(str), retry--)
629 ;
630
631 if (NULL != ptr)
632 return ptr;
633
634 zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_strdup: out of memory. Requested " ZBX_FS_SIZE_T " bytes.",
635 filename, line, (zbx_fs_size_t)(strlen(str) + 1));
636
637 exit(EXIT_FAILURE);
638 }
639
640 /****************************************************************************************
641 * *
642 * Function: zbx_guaranteed_memset *
643 * *
644 * Purpose: For overwriting sensitive data in memory. *
645 * Similar to memset() but should not be optimized out by a compiler. *
646 * *
647 * Derived from: *
648 * http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html *
649 * See also: *
650 * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1381.pdf on secure_memset() *
651 * *
652 ****************************************************************************************/
zbx_guaranteed_memset(void * v,int c,size_t n)653 void *zbx_guaranteed_memset(void *v, int c, size_t n)
654 {
655 volatile signed char *p = (volatile signed char *)v;
656
657 while (0 != n--)
658 *p++ = (signed char)c;
659
660 return v;
661 }
662
663 /******************************************************************************
664 * *
665 * Function: __zbx_zbx_setproctitle *
666 * *
667 * Purpose: set process title *
668 * *
669 * Author: Eugene Grigorjev *
670 * *
671 ******************************************************************************/
zbx_setproctitle(const char * fmt,...)672 void zbx_setproctitle(const char *fmt, ...)
673 {
674 #if defined(HAVE_FUNCTION_SETPROCTITLE) || defined(PS_OVERWRITE_ARGV) || defined(PS_PSTAT_ARGV)
675 char title[MAX_STRING_LEN];
676 va_list args;
677
678 va_start(args, fmt);
679 zbx_vsnprintf(title, sizeof(title), fmt, args);
680 va_end(args);
681
682 zabbix_log(LOG_LEVEL_DEBUG, "%s() title:'%s'", __func__, title);
683 #endif
684
685 #if defined(HAVE_FUNCTION_SETPROCTITLE)
686 setproctitle("%s", title);
687 #elif defined(PS_OVERWRITE_ARGV) || defined(PS_PSTAT_ARGV)
688 setproctitle_set_status(title);
689 #endif
690 }
691
692 /******************************************************************************
693 * *
694 * Function: check_time_period *
695 * *
696 * Purpose: check if current time is within given period *
697 * *
698 * Parameters: period - [IN] preprocessed time period *
699 * tm - [IN] broken-down time for comparison *
700 * *
701 * Return value: FAIL - out of period, SUCCEED - within the period *
702 * *
703 * Author: Alexei Vladishev *
704 * *
705 ******************************************************************************/
check_time_period(const zbx_time_period_t period,struct tm * tm)706 static int check_time_period(const zbx_time_period_t period, struct tm *tm)
707 {
708 int day, time;
709
710 day = 0 == tm->tm_wday ? 7 : tm->tm_wday;
711 time = SEC_PER_HOUR * tm->tm_hour + SEC_PER_MIN * tm->tm_min + tm->tm_sec;
712
713 return period.start_day <= day && day <= period.end_day && period.start_time <= time && time < period.end_time ?
714 SUCCEED : FAIL;
715 }
716
717 /******************************************************************************
718 * *
719 * Function: get_current_delay *
720 * *
721 * Purpose: return delay value that is currently applicable *
722 * *
723 * Parameters: default_delay - [IN] default delay value, can be overridden *
724 * flex_intervals - [IN] preprocessed flexible intervals *
725 * now - [IN] current time *
726 * *
727 * Return value: delay value - either default or minimum delay value *
728 * out of all applicable intervals *
729 * *
730 * Author: Alexei Vladishev, Alexander Vladishev, Aleksandrs Saveljevs *
731 * *
732 ******************************************************************************/
get_current_delay(int default_delay,const zbx_flexible_interval_t * flex_intervals,time_t now)733 static int get_current_delay(int default_delay, const zbx_flexible_interval_t *flex_intervals, time_t now)
734 {
735 int current_delay = -1;
736
737 while (NULL != flex_intervals)
738 {
739 if ((-1 == current_delay || flex_intervals->delay < current_delay) &&
740 SUCCEED == check_time_period(flex_intervals->period, localtime(&now)))
741 {
742 current_delay = flex_intervals->delay;
743 }
744
745 flex_intervals = flex_intervals->next;
746 }
747
748 return -1 == current_delay ? default_delay : current_delay;
749 }
750
751 /******************************************************************************
752 * *
753 * Function: get_next_delay_interval *
754 * *
755 * Purpose: return time when next delay settings take effect *
756 * *
757 * Parameters: flex_intervals - [IN] preprocessed flexible intervals *
758 * now - [IN] current time *
759 * next_interval - [OUT] start of next delay interval *
760 * *
761 * Return value: SUCCEED - there is a next interval *
762 * FAIL - otherwise (in this case, next_interval is unaffected) *
763 * *
764 * Author: Alexei Vladishev, Alexander Vladishev, Aleksandrs Saveljevs *
765 * *
766 ******************************************************************************/
get_next_delay_interval(const zbx_flexible_interval_t * flex_intervals,time_t now,time_t * next_interval)767 static int get_next_delay_interval(const zbx_flexible_interval_t *flex_intervals, time_t now, time_t *next_interval)
768 {
769 int day, time, next = 0, candidate;
770 struct tm *tm;
771
772 if (NULL == flex_intervals)
773 return FAIL;
774
775 tm = localtime(&now);
776 day = 0 == tm->tm_wday ? 7 : tm->tm_wday;
777 time = SEC_PER_HOUR * tm->tm_hour + SEC_PER_MIN * tm->tm_min + tm->tm_sec;
778
779 for (; NULL != flex_intervals; flex_intervals = flex_intervals->next)
780 {
781 const zbx_time_period_t *p = &flex_intervals->period;
782
783 if (p->start_day <= day && day <= p->end_day && time < p->end_time) /* will be active today */
784 {
785 if (time < p->start_time) /* hasn't been active today yet */
786 candidate = p->start_time;
787 else /* currently active */
788 candidate = p->end_time;
789 }
790 else if (day < p->end_day) /* will be active this week */
791 {
792 if (day < p->start_day) /* hasn't been active this week yet */
793 candidate = SEC_PER_DAY * (p->start_day - day) + p->start_time;
794 else /* has been active this week and will be active at least once more by the end of it */
795 candidate = SEC_PER_DAY + p->start_time; /* therefore will be active tomorrow */
796 }
797 else /* will be active next week */
798 candidate = SEC_PER_DAY * (p->start_day + 7 - day) + p->start_time;
799
800 if (0 == next || next > candidate)
801 next = candidate;
802 }
803
804 if (0 == next)
805 return FAIL;
806
807 *next_interval = now - time + next;
808 return SUCCEED;
809 }
810
811 /******************************************************************************
812 * *
813 * Function: time_parse *
814 * *
815 * Purpose: parses time of day *
816 * *
817 * Parameters: time - [OUT] number of seconds since the beginning of *
818 * the day corresponding to a given time of day *
819 * text - [IN] text to parse *
820 * len - [IN] number of characters available for parsing *
821 * parsed_len - [OUT] number of characters recognized as time *
822 * *
823 * Return value: SUCCEED - text was successfully parsed as time of day *
824 * FAIL - otherwise (time and parsed_len remain untouched) *
825 * *
826 * Comments: !!! Don't forget to sync code with PHP !!! *
827 * Supported formats are hh:mm, h:mm and 0h:mm; 0 <= hours <= 24; *
828 * 0 <= minutes <= 59; if hours == 24 then minutes must be 0. *
829 * *
830 ******************************************************************************/
time_parse(int * time,const char * text,int len,int * parsed_len)831 static int time_parse(int *time, const char *text, int len, int *parsed_len)
832 {
833 const int old_len = len;
834 const char *ptr;
835 int hours, minutes;
836
837 for (ptr = text; 0 < len && 0 != isdigit(*ptr) && 2 >= ptr - text; len--, ptr++)
838 ;
839
840 if (SUCCEED != is_uint_n_range(text, ptr - text, &hours, sizeof(hours), 0, 24))
841 return FAIL;
842
843 if (0 >= len-- || ':' != *ptr++)
844 return FAIL;
845
846 for (text = ptr; 0 < len && 0 != isdigit(*ptr) && 2 >= ptr - text; len--, ptr++)
847 ;
848
849 if (2 != ptr - text)
850 return FAIL;
851
852 if (SUCCEED != is_uint_n_range(text, 2, &minutes, sizeof(minutes), 0, 59))
853 return FAIL;
854
855 if (24 == hours && 0 != minutes)
856 return FAIL;
857
858 *parsed_len = old_len - len;
859 *time = SEC_PER_HOUR * hours + SEC_PER_MIN * minutes;
860 return SUCCEED;
861 }
862
863 /******************************************************************************
864 * *
865 * Function: time_period_parse *
866 * *
867 * Purpose: parses time period *
868 * *
869 * Parameters: period - [OUT] time period structure *
870 * text - [IN] text to parse *
871 * len - [IN] number of characters available for parsing *
872 * *
873 * Return value: SUCCEED - text was successfully parsed as time period *
874 * FAIL - otherwise *
875 * *
876 * Comments: !!! Don't forget to sync code with PHP !!! *
877 * Supported format is d[-d],time-time where 1 <= d <= 7 *
878 * *
879 ******************************************************************************/
time_period_parse(zbx_time_period_t * period,const char * text,int len)880 static int time_period_parse(zbx_time_period_t *period, const char *text, int len)
881 {
882 int parsed_len;
883
884 if (0 >= len-- || '1' > *text || '7' < *text)
885 return FAIL;
886
887 period->start_day = *text++ - '0';
888
889 if (0 >= len)
890 return FAIL;
891
892 if ('-' == *text)
893 {
894 text++;
895 len--;
896
897 if (0 >= len-- || '1' > *text || '7' < *text)
898 return FAIL;
899
900 period->end_day = *text++ - '0';
901
902 if (period->start_day > period->end_day)
903 return FAIL;
904 }
905 else
906 period->end_day = period->start_day;
907
908 if (0 >= len-- || ',' != *text++)
909 return FAIL;
910
911 if (SUCCEED != time_parse(&period->start_time, text, len, &parsed_len))
912 return FAIL;
913
914 text += parsed_len;
915 len -= parsed_len;
916
917 if (0 >= len-- || '-' != *text++)
918 return FAIL;
919
920 if (SUCCEED != time_parse(&period->end_time, text, len, &parsed_len))
921 return FAIL;
922
923 if (period->start_time >= period->end_time)
924 return FAIL;
925
926 if (0 != (len - parsed_len))
927 return FAIL;
928
929 return SUCCEED;
930 }
931
932 /******************************************************************************
933 * *
934 * Function: zbx_check_time_period *
935 * *
936 * Purpose: validate time period and check if specified time is within it *
937 * *
938 * Parameters: period - [IN] semicolon-separated list of time periods in one *
939 * of the following formats: *
940 * d1-d2,h1:m1-h2:m2 *
941 * or d1,h1:m1-h2:m2 *
942 * time - [IN] time to check *
943 * res - [OUT] check result: *
944 * SUCCEED - if time is within period *
945 * FAIL - otherwise *
946 * *
947 * Return value: validation result (SUCCEED - valid, FAIL - invalid) *
948 * *
949 * Comments: !!! Don't forget to sync code with PHP !!! *
950 * *
951 ******************************************************************************/
zbx_check_time_period(const char * period,time_t time,int * res)952 int zbx_check_time_period(const char *period, time_t time, int *res)
953 {
954 int res_total = FAIL;
955 const char *next;
956 struct tm *tm;
957 zbx_time_period_t tp;
958
959 tm = localtime(&time);
960
961 next = strchr(period, ';');
962 while (SUCCEED == time_period_parse(&tp, period, (NULL == next ? (int)strlen(period) : (int)(next - period))))
963 {
964 if (SUCCEED == check_time_period(tp, tm))
965 res_total = SUCCEED; /* no short-circuits, validate all periods before return */
966
967 if (NULL == next)
968 {
969 *res = res_total;
970 return SUCCEED;
971 }
972
973 period = next + 1;
974 next = strchr(period, ';');
975 }
976
977 return FAIL;
978 }
979
980 /******************************************************************************
981 * *
982 * Function: flexible_interval_free *
983 * *
984 * Purpose: frees flexible interval *
985 * *
986 * Parameters: interval - [IN] flexible interval *
987 * *
988 ******************************************************************************/
flexible_interval_free(zbx_flexible_interval_t * interval)989 static void flexible_interval_free(zbx_flexible_interval_t *interval)
990 {
991 zbx_flexible_interval_t *interval_next;
992
993 for (; NULL != interval; interval = interval_next)
994 {
995 interval_next = interval->next;
996 zbx_free(interval);
997 }
998 }
999
1000 /******************************************************************************
1001 * *
1002 * Function: flexible_interval_parse *
1003 * *
1004 * Purpose: parses flexible interval *
1005 * *
1006 * Parameters: interval - [IN/OUT] the first interval *
1007 * text - [IN] the text to parse *
1008 * len - [IN] the text length *
1009 * *
1010 * Return value: SUCCEED - the interval was successfully parsed *
1011 * FAIL - otherwise *
1012 * *
1013 * Comments: !!! Don't forget to sync code with PHP !!! *
1014 * Supported format is delay/period *
1015 * *
1016 ******************************************************************************/
flexible_interval_parse(zbx_flexible_interval_t * interval,const char * text,int len)1017 static int flexible_interval_parse(zbx_flexible_interval_t *interval, const char *text, int len)
1018 {
1019 const char *ptr;
1020
1021 for (ptr = text; 0 < len && '\0' != *ptr && '/' != *ptr; len--, ptr++)
1022 ;
1023
1024 if (SUCCEED != is_time_suffix(text, &interval->delay, (int)(ptr - text)))
1025 return FAIL;
1026
1027 if (0 >= len-- || '/' != *ptr++)
1028 return FAIL;
1029
1030 return time_period_parse(&interval->period, ptr, len);
1031 }
1032
1033 /******************************************************************************
1034 * *
1035 * Function: calculate_dayofweek *
1036 * *
1037 * Purpose: calculates day of week *
1038 * *
1039 * Parameters: year - [IN] the year (>1752) *
1040 * mon - [IN] the month (1-12) *
1041 * mday - [IN] the month day (1-31) *
1042 * *
1043 * Return value: The day of week: 1 - Monday, 2 - Tuesday, ... *
1044 * *
1045 ******************************************************************************/
calculate_dayofweek(int year,int mon,int mday)1046 static int calculate_dayofweek(int year, int mon, int mday)
1047 {
1048 static int mon_table[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
1049
1050 if (mon < 3)
1051 year--;
1052
1053 return (year + year / 4 - year / 100 + year / 400 + mon_table[mon - 1] + mday - 1) % 7 + 1;
1054 }
1055
1056 /******************************************************************************
1057 * *
1058 * Function: scheduler_filter_free *
1059 * *
1060 * Purpose: frees scheduler interval filter *
1061 * *
1062 * Parameters: filter - [IN] scheduler interval filter *
1063 * *
1064 ******************************************************************************/
scheduler_filter_free(zbx_scheduler_filter_t * filter)1065 static void scheduler_filter_free(zbx_scheduler_filter_t *filter)
1066 {
1067 zbx_scheduler_filter_t *filter_next;
1068
1069 for (; NULL != filter; filter = filter_next)
1070 {
1071 filter_next = filter->next;
1072 zbx_free(filter);
1073 }
1074 }
1075
1076 /******************************************************************************
1077 * *
1078 * Function: scheduler_interval_free *
1079 * *
1080 * Purpose: frees scheduler interval *
1081 * *
1082 * Parameters: interval - [IN] scheduler interval *
1083 * *
1084 ******************************************************************************/
scheduler_interval_free(zbx_scheduler_interval_t * interval)1085 static void scheduler_interval_free(zbx_scheduler_interval_t *interval)
1086 {
1087 zbx_scheduler_interval_t *interval_next;
1088
1089 for (; NULL != interval; interval = interval_next)
1090 {
1091 interval_next = interval->next;
1092
1093 scheduler_filter_free(interval->mdays);
1094 scheduler_filter_free(interval->wdays);
1095 scheduler_filter_free(interval->hours);
1096 scheduler_filter_free(interval->minutes);
1097 scheduler_filter_free(interval->seconds);
1098
1099 zbx_free(interval);
1100 }
1101 }
1102
1103 /******************************************************************************
1104 * *
1105 * Function: scheduler_parse_filter_r *
1106 * *
1107 * Purpose: parses text string into scheduler filter *
1108 * *
1109 * Parameters: filter - [IN/OUT] the first filter *
1110 * text - [IN] the text to parse *
1111 * len - [IN/OUT] the number of characters left to parse *
1112 * min - [IN] the minimal time unit value *
1113 * max - [IN] the maximal time unit value *
1114 * var_len - [IN] the maximum number of characters for a filter *
1115 * variable (<from>, <to>, <step>) *
1116 * *
1117 * Return value: SUCCEED - the filter was successfully parsed *
1118 * FAIL - otherwise *
1119 * *
1120 * Comments: This function recursively calls itself for each filter fragment. *
1121 * *
1122 ******************************************************************************/
scheduler_parse_filter_r(zbx_scheduler_filter_t ** filter,const char * text,int * len,int min,int max,int var_len)1123 static int scheduler_parse_filter_r(zbx_scheduler_filter_t **filter, const char *text, int *len, int min, int max,
1124 int var_len)
1125 {
1126 int start = 0, end = 0, step = 1;
1127 const char *pstart, *pend;
1128 zbx_scheduler_filter_t *filter_new;
1129
1130 pstart = pend = text;
1131 while (0 != isdigit(*pend) && 0 < *len)
1132 {
1133 pend++;
1134 (*len)--;
1135 }
1136
1137 if (pend != pstart)
1138 {
1139 if (pend - pstart > var_len)
1140 return FAIL;
1141
1142 if (SUCCEED != is_uint_n_range(pstart, pend - pstart, &start, sizeof(start), min, max))
1143 return FAIL;
1144
1145 if ('-' == *pend)
1146 {
1147 pstart = pend + 1;
1148
1149 do
1150 {
1151 pend++;
1152 (*len)--;
1153 }
1154 while (0 != isdigit(*pend) && 0 < *len);
1155
1156 /* empty or too long value, fail */
1157 if (pend == pstart || pend - pstart > var_len)
1158 return FAIL;
1159
1160 if (SUCCEED != is_uint_n_range(pstart, pend - pstart, &end, sizeof(end), min, max))
1161 return FAIL;
1162
1163 if (end < start)
1164 return FAIL;
1165 }
1166 else
1167 {
1168 /* step is valid only for defined range */
1169 if ('/' == *pend)
1170 return FAIL;
1171
1172 end = start;
1173 }
1174 }
1175 else
1176 {
1177 start = min;
1178 end = max;
1179 }
1180
1181 if ('/' == *pend)
1182 {
1183 pstart = pend + 1;
1184
1185 do
1186 {
1187 pend++;
1188 (*len)--;
1189 }
1190 while (0 != isdigit(*pend) && 0 < *len);
1191
1192 /* empty or too long step, fail */
1193 if (pend == pstart || pend - pstart > var_len)
1194 return FAIL;
1195
1196 if (SUCCEED != is_uint_n_range(pstart, pend - pstart, &step, sizeof(step), 1, end - start))
1197 return FAIL;
1198 }
1199 else
1200 {
1201 if (pend == text)
1202 return FAIL;
1203 }
1204
1205 if (',' == *pend)
1206 {
1207 /* no next filter after ',' */
1208 if (0 == --(*len))
1209 return FAIL;
1210
1211 pend++;
1212
1213 if (SUCCEED != scheduler_parse_filter_r(filter, pend, len, min, max, var_len))
1214 return FAIL;
1215 }
1216
1217 filter_new = (zbx_scheduler_filter_t *)zbx_malloc(NULL, sizeof(zbx_scheduler_filter_t));
1218 filter_new->start = start;
1219 filter_new->end = end;
1220 filter_new->step = step;
1221 filter_new->next = *filter;
1222 *filter = filter_new;
1223
1224 return SUCCEED;
1225 }
1226
1227 /******************************************************************************
1228 * *
1229 * Function: scheduler_parse_filter *
1230 * *
1231 * Purpose: parses text string into scheduler filter *
1232 * *
1233 * Parameters: filter - [IN/OUT] the first filter *
1234 * text - [IN] the text to parse *
1235 * len - [IN/OUT] the number of characters left to parse *
1236 * min - [IN] the minimal time unit value *
1237 * max - [IN] the maximal time unit value *
1238 * var_len - [IN] the maximum number of characters for a filter *
1239 * variable (<from>, <to>, <step>) *
1240 * *
1241 * Return value: SUCCEED - the filter was successfully parsed *
1242 * FAIL - otherwise *
1243 * *
1244 * Comments: This function will fail if a filter already exists. This *
1245 * user from defining multiple filters of the same time unit in a *
1246 * single interval. For example: h0h12 is invalid filter and its *
1247 * parsing must fail. *
1248 * *
1249 ******************************************************************************/
scheduler_parse_filter(zbx_scheduler_filter_t ** filter,const char * text,int * len,int min,int max,int var_len)1250 static int scheduler_parse_filter(zbx_scheduler_filter_t **filter, const char *text, int *len, int min, int max,
1251 int var_len)
1252 {
1253 if (NULL != *filter)
1254 return FAIL;
1255
1256 return scheduler_parse_filter_r(filter, text, len, min, max, var_len);
1257 }
1258
1259 /******************************************************************************
1260 * *
1261 * Function: scheduler_interval_parse *
1262 * *
1263 * Purpose: parses scheduler interval *
1264 * *
1265 * Parameters: interval - [IN/OUT] the first interval *
1266 * text - [IN] the text to parse *
1267 * len - [IN] the text length *
1268 * *
1269 * Return value: SUCCEED - the interval was successfully parsed *
1270 * FAIL - otherwise *
1271 * *
1272 ******************************************************************************/
scheduler_interval_parse(zbx_scheduler_interval_t * interval,const char * text,int len)1273 static int scheduler_interval_parse(zbx_scheduler_interval_t *interval, const char *text, int len)
1274 {
1275 int ret = SUCCEED;
1276
1277 if (0 == len)
1278 return FAIL;
1279
1280 while (SUCCEED == ret && 0 != len)
1281 {
1282 int old_len = len--;
1283
1284 switch (*text)
1285 {
1286 case '\0':
1287 return FAIL;
1288 case 'h':
1289 if (ZBX_SCHEDULER_FILTER_HOUR < interval->filter_level)
1290 return FAIL;
1291
1292 ret = scheduler_parse_filter(&interval->hours, text + 1, &len, 0, 23, 2);
1293 interval->filter_level = ZBX_SCHEDULER_FILTER_HOUR;
1294
1295 break;
1296 case 's':
1297 if (ZBX_SCHEDULER_FILTER_SECOND < interval->filter_level)
1298 return FAIL;
1299
1300 ret = scheduler_parse_filter(&interval->seconds, text + 1, &len, 0, 59, 2);
1301 interval->filter_level = ZBX_SCHEDULER_FILTER_SECOND;
1302
1303 break;
1304 case 'w':
1305 if ('d' != text[1])
1306 return FAIL;
1307
1308 if (ZBX_SCHEDULER_FILTER_DAY < interval->filter_level)
1309 return FAIL;
1310
1311 len--;
1312 ret = scheduler_parse_filter(&interval->wdays, text + 2, &len, 1, 7, 1);
1313 interval->filter_level = ZBX_SCHEDULER_FILTER_DAY;
1314
1315 break;
1316 case 'm':
1317 if ('d' == text[1])
1318 {
1319 if (ZBX_SCHEDULER_FILTER_DAY < interval->filter_level ||
1320 NULL != interval->wdays)
1321 {
1322 return FAIL;
1323 }
1324
1325 len--;
1326 ret = scheduler_parse_filter(&interval->mdays, text + 2, &len, 1, 31, 2);
1327 interval->filter_level = ZBX_SCHEDULER_FILTER_DAY;
1328 }
1329 else
1330 {
1331 if (ZBX_SCHEDULER_FILTER_MINUTE < interval->filter_level)
1332 return FAIL;
1333
1334 ret = scheduler_parse_filter(&interval->minutes, text + 1, &len, 0, 59, 2);
1335 interval->filter_level = ZBX_SCHEDULER_FILTER_MINUTE;
1336 }
1337
1338 break;
1339 default:
1340 return FAIL;
1341 }
1342
1343 text += old_len - len;
1344 }
1345
1346 return ret;
1347 }
1348
1349 /******************************************************************************
1350 * *
1351 * Function: scheduler_get_nearest_filter_value *
1352 * *
1353 * Purpose: gets the next nearest value that satisfies the filter chain *
1354 * *
1355 * Parameters: filter - [IN] the filter chain *
1356 * value - [IN] the current value *
1357 * [OUT] the next nearest value (>= than input value) *
1358 * *
1359 * Return value: SUCCEED - the next nearest value was successfully found *
1360 * FAIL - otherwise *
1361 * *
1362 ******************************************************************************/
scheduler_get_nearest_filter_value(const zbx_scheduler_filter_t * filter,int * value)1363 static int scheduler_get_nearest_filter_value(const zbx_scheduler_filter_t *filter, int *value)
1364 {
1365 const zbx_scheduler_filter_t *filter_next = NULL;
1366
1367 for (; NULL != filter; filter = filter->next)
1368 {
1369 /* find matching filter */
1370 if (filter->start <= *value && *value <= filter->end)
1371 {
1372 int next = *value, offset;
1373
1374 /* apply step */
1375 offset = (next - filter->start) % filter->step;
1376 if (0 != offset)
1377 next += filter->step - offset;
1378
1379 /* succeed if the calculated value is still in filter range */
1380 if (next <= filter->end)
1381 {
1382 *value = next;
1383 return SUCCEED;
1384 }
1385 }
1386
1387 /* find the next nearest filter */
1388 if (filter->start > *value && (NULL == filter_next || filter_next->start > filter->start))
1389 filter_next = filter;
1390 }
1391
1392 /* The value is not in a range of any filters, but we have next nearest filter. */
1393 if (NULL != filter_next)
1394 {
1395 *value = filter_next->start;
1396 return SUCCEED;
1397 }
1398
1399 return FAIL;
1400 }
1401
1402 /******************************************************************************
1403 * *
1404 * Function: scheduler_get_wday_nextcheck *
1405 * *
1406 * Purpose: calculates the next day that satisfies the week day filter *
1407 * *
1408 * Parameters: interval - [IN] the scheduler interval *
1409 * tm - [IN/OUT] the input/output date & time *
1410 * *
1411 * Return value: SUCCEED - the next day was found *
1412 * FAIL - the next day satisfying week day filter was not *
1413 * found in the current month *
1414 * *
1415 ******************************************************************************/
scheduler_get_wday_nextcheck(const zbx_scheduler_interval_t * interval,struct tm * tm)1416 static int scheduler_get_wday_nextcheck(const zbx_scheduler_interval_t *interval, struct tm *tm)
1417 {
1418 int value_now, value_next;
1419
1420 if (NULL == interval->wdays)
1421 return SUCCEED;
1422
1423 value_now = value_next = calculate_dayofweek(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
1424
1425 /* get the nearest week day from the current week day*/
1426 if (SUCCEED != scheduler_get_nearest_filter_value(interval->wdays, &value_next))
1427 {
1428 /* in the case of failure move month day to the next week, reset week day and try again */
1429 tm->tm_mday += 7 - value_now + 1;
1430 value_now = value_next = 1;
1431
1432 if (SUCCEED != scheduler_get_nearest_filter_value(interval->wdays, &value_next))
1433 {
1434 /* a valid week day filter must always match some day of a new week */
1435 THIS_SHOULD_NEVER_HAPPEN;
1436 return FAIL;
1437 }
1438 }
1439
1440 /* adjust the month day by the week day offset */
1441 tm->tm_mday += value_next - value_now;
1442
1443 /* check if the resulting month day is valid */
1444 return (tm->tm_mday <= zbx_day_in_month(tm->tm_year + 1970, tm->tm_mon + 1) ? SUCCEED : FAIL);
1445 }
1446
1447 /******************************************************************************
1448 * *
1449 * Function: scheduler_validate_wday_filter *
1450 * *
1451 * Purpose: checks if the specified date satisfies week day filter *
1452 * *
1453 * Parameters: interval - [IN] the scheduler interval *
1454 * tm - [IN] the date & time to validate *
1455 * *
1456 * Return value: SUCCEED - the input date satisfies week day filter *
1457 * FAIL - otherwise *
1458 * *
1459 ******************************************************************************/
scheduler_validate_wday_filter(const zbx_scheduler_interval_t * interval,struct tm * tm)1460 static int scheduler_validate_wday_filter(const zbx_scheduler_interval_t *interval, struct tm *tm)
1461 {
1462 const zbx_scheduler_filter_t *filter;
1463 int value;
1464
1465 if (NULL == interval->wdays)
1466 return SUCCEED;
1467
1468 value = calculate_dayofweek(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
1469
1470 /* check if the value match week day filter */
1471 for (filter = interval->wdays; NULL != filter; filter = filter->next)
1472 {
1473 if (filter->start <= value && value <= filter->end)
1474 {
1475 int next = value, offset;
1476
1477 /* apply step */
1478 offset = (next - filter->start) % filter->step;
1479 if (0 != offset)
1480 next += filter->step - offset;
1481
1482 /* succeed if the calculated value is still in filter range */
1483 if (next <= filter->end)
1484 return SUCCEED;
1485 }
1486 }
1487
1488 return FAIL;
1489 }
1490
1491 /******************************************************************************
1492 * *
1493 * Function: scheduler_get_day_nextcheck *
1494 * *
1495 * Purpose: calculates the next day that satisfies month and week day filters *
1496 * *
1497 * Parameters: interval - [IN] the scheduler interval *
1498 * tm - [IN/OUT] the input/output date & time *
1499 * *
1500 * Return value: SUCCEED - the next day was found *
1501 * FAIL - the next day satisfying day filters was not *
1502 * found in the current month *
1503 * *
1504 ******************************************************************************/
scheduler_get_day_nextcheck(const zbx_scheduler_interval_t * interval,struct tm * tm)1505 static int scheduler_get_day_nextcheck(const zbx_scheduler_interval_t *interval, struct tm *tm)
1506 {
1507 int tmp;
1508
1509 /* first check if the provided tm structure has valid date format */
1510 if (FAIL == zbx_utc_time(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
1511 &tmp))
1512 {
1513 return FAIL;
1514 }
1515
1516 if (NULL == interval->mdays)
1517 return scheduler_get_wday_nextcheck(interval, tm);
1518
1519 /* iterate through month days until week day filter matches or we have ran out of month days */
1520 while (SUCCEED == scheduler_get_nearest_filter_value(interval->mdays, &tm->tm_mday))
1521 {
1522 /* check if the date is still valid - we haven't ran out of month days */
1523 if (tm->tm_mday > zbx_day_in_month(tm->tm_year + 1970, tm->tm_mon + 1))
1524 break;
1525
1526 if (SUCCEED == scheduler_validate_wday_filter(interval, tm))
1527 return SUCCEED;
1528
1529 tm->tm_mday++;
1530
1531 /* check if the date is still valid - we haven't ran out of month days */
1532 if (tm->tm_mday > zbx_day_in_month(tm->tm_year + 1970, tm->tm_mon + 1))
1533 break;
1534 }
1535
1536 return FAIL;
1537 }
1538
1539 /******************************************************************************
1540 * *
1541 * Function: scheduler_get_filter_nextcheck *
1542 * *
1543 * Purpose: calculates the time/day that satisfies the specified filter *
1544 * *
1545 * Parameters: interval - [IN] the scheduler interval *
1546 * level - [IN] the filter level, see ZBX_SCHEDULER_FILTER_* *
1547 * defines *
1548 * tm - [IN/OUT] the input/output date & time *
1549 * *
1550 * Return value: SUCCEED - the next time/day was found *
1551 * FAIL - the next time/day was not found on the current *
1552 * filter level *
1553 * *
1554 ******************************************************************************/
scheduler_get_filter_nextcheck(const zbx_scheduler_interval_t * interval,int level,struct tm * tm)1555 static int scheduler_get_filter_nextcheck(const zbx_scheduler_interval_t *interval, int level, struct tm *tm)
1556 {
1557 const zbx_scheduler_filter_t *filter;
1558 int max, *value;
1559
1560 /* initialize data depending on filter level */
1561 switch (level)
1562 {
1563 case ZBX_SCHEDULER_FILTER_DAY:
1564 return scheduler_get_day_nextcheck(interval, tm);
1565 case ZBX_SCHEDULER_FILTER_HOUR:
1566 max = 23;
1567 filter = interval->hours;
1568 value = &tm->tm_hour;
1569 break;
1570 case ZBX_SCHEDULER_FILTER_MINUTE:
1571 max = 59;
1572 filter = interval->minutes;
1573 value = &tm->tm_min;
1574 break;
1575 case ZBX_SCHEDULER_FILTER_SECOND:
1576 max = 59;
1577 filter = interval->seconds;
1578 value = &tm->tm_sec;
1579 break;
1580 default:
1581 THIS_SHOULD_NEVER_HAPPEN;
1582 return FAIL;
1583 }
1584
1585 if (max < *value)
1586 return FAIL;
1587
1588 /* handle unspecified (default) filter */
1589 if (NULL == filter)
1590 {
1591 /* Empty filter matches all valid values if the filter level is less than */
1592 /* interval filter level. For example if interval filter level is minutes - m30, */
1593 /* then hour filter matches all hours. */
1594 if (interval->filter_level > level)
1595 return SUCCEED;
1596
1597 /* If the filter level is greater than interval filter level, then filter */
1598 /* matches only 0 value. For example if interval filter level is minutes - m30, */
1599 /* then seconds filter matches the 0th second. */
1600 return 0 == *value ? SUCCEED : FAIL;
1601 }
1602
1603 return scheduler_get_nearest_filter_value(filter, value);
1604 }
1605
1606 /******************************************************************************
1607 * *
1608 * Function: scheduler_apply_day_filter *
1609 * *
1610 * Purpose: applies day filter to the specified time/day calculating the next *
1611 * scheduled check *
1612 * *
1613 * Parameters: interval - [IN] the scheduler interval *
1614 * tm - [IN/OUT] the input/output date & time *
1615 * *
1616 ******************************************************************************/
scheduler_apply_day_filter(zbx_scheduler_interval_t * interval,struct tm * tm)1617 static void scheduler_apply_day_filter(zbx_scheduler_interval_t *interval, struct tm *tm)
1618 {
1619 int day = tm->tm_mday, mon = tm->tm_mon, year = tm->tm_year;
1620
1621 while (SUCCEED != scheduler_get_filter_nextcheck(interval, ZBX_SCHEDULER_FILTER_DAY, tm))
1622 {
1623 if (11 < ++tm->tm_mon)
1624 {
1625 tm->tm_mon = 0;
1626 tm->tm_year++;
1627 }
1628
1629 tm->tm_mday = 1;
1630 }
1631
1632 /* reset hours, minutes and seconds if the day has been changed */
1633 if (tm->tm_mday != day || tm->tm_mon != mon || tm->tm_year != year)
1634 {
1635 tm->tm_hour = 0;
1636 tm->tm_min = 0;
1637 tm->tm_sec = 0;
1638 }
1639 }
1640
1641 /******************************************************************************
1642 * *
1643 * Function: scheduler_apply_hour_filter *
1644 * *
1645 * Purpose: applies hour filter to the specified time/day calculating the *
1646 * next scheduled check *
1647 * *
1648 * Parameters: interval - [IN] the scheduler interval *
1649 * tm - [IN/OUT] the input/output date & time *
1650 * *
1651 ******************************************************************************/
scheduler_apply_hour_filter(zbx_scheduler_interval_t * interval,struct tm * tm)1652 static void scheduler_apply_hour_filter(zbx_scheduler_interval_t *interval, struct tm *tm)
1653 {
1654 int hour = tm->tm_hour;
1655
1656 while (SUCCEED != scheduler_get_filter_nextcheck(interval, ZBX_SCHEDULER_FILTER_HOUR, tm))
1657 {
1658 tm->tm_mday++;
1659 tm->tm_hour = 0;
1660
1661 /* day has been changed, we have to reapply day filter */
1662 scheduler_apply_day_filter(interval, tm);
1663 }
1664
1665 /* reset minutes and seconds if hours has been changed */
1666 if (tm->tm_hour != hour)
1667 {
1668 tm->tm_min = 0;
1669 tm->tm_sec = 0;
1670 }
1671 }
1672
1673 /******************************************************************************
1674 * *
1675 * Function: scheduler_apply_minute_filter *
1676 * *
1677 * Purpose: applies minute filter to the specified time/day calculating the *
1678 * next scheduled check *
1679 * *
1680 * Parameters: interval - [IN] the scheduler interval *
1681 * tm - [IN/OUT] the input/output date & time *
1682 * *
1683 ******************************************************************************/
scheduler_apply_minute_filter(zbx_scheduler_interval_t * interval,struct tm * tm)1684 static void scheduler_apply_minute_filter(zbx_scheduler_interval_t *interval, struct tm *tm)
1685 {
1686 int min = tm->tm_min;
1687
1688 while (SUCCEED != scheduler_get_filter_nextcheck(interval, ZBX_SCHEDULER_FILTER_MINUTE, tm))
1689 {
1690 tm->tm_hour++;
1691 tm->tm_min = 0;
1692
1693 /* hours have been changed, we have to reapply hour filter */
1694 scheduler_apply_hour_filter(interval, tm);
1695 }
1696
1697 /* reset seconds if minutes has been changed */
1698 if (tm->tm_min != min)
1699 tm->tm_sec = 0;
1700 }
1701
1702 /******************************************************************************
1703 * *
1704 * Function: scheduler_apply_second_filter *
1705 * *
1706 * Purpose: applies second filter to the specified time/day calculating the *
1707 * next scheduled check *
1708 * *
1709 * Parameters: interval - [IN] the scheduler interval *
1710 * tm - [IN/OUT] the input/output date & time *
1711 * *
1712 ******************************************************************************/
scheduler_apply_second_filter(zbx_scheduler_interval_t * interval,struct tm * tm)1713 static void scheduler_apply_second_filter(zbx_scheduler_interval_t *interval, struct tm *tm)
1714 {
1715 while (SUCCEED != scheduler_get_filter_nextcheck(interval, ZBX_SCHEDULER_FILTER_SECOND, tm))
1716 {
1717 tm->tm_min++;
1718 tm->tm_sec = 0;
1719
1720 /* minutes have been changed, we have to reapply minute filter */
1721 scheduler_apply_minute_filter(interval, tm);
1722 }
1723 }
1724
1725 /******************************************************************************
1726 * *
1727 * Function: scheduler_find_dst_change *
1728 * *
1729 * Purpose: finds daylight saving change time inside specified time period *
1730 * *
1731 * Parameters: time_start - [IN] the time period start *
1732 * time_end - [IN] the time period end *
1733 * *
1734 * Return Value: Time when the daylight saving changes should occur. *
1735 * *
1736 * Comments: The calculated time is cached and reused if it first the *
1737 * specified period. *
1738 * *
1739 ******************************************************************************/
scheduler_find_dst_change(time_t time_start,time_t time_end)1740 static time_t scheduler_find_dst_change(time_t time_start, time_t time_end)
1741 {
1742 static time_t time_dst = 0;
1743 struct tm *tm;
1744 time_t time_mid;
1745 int start, end, mid, dst_start;
1746
1747 if (time_dst < time_start || time_dst > time_end)
1748 {
1749 /* assume that daylight saving will change only on 0 seconds */
1750 start = time_start / 60;
1751 end = time_end / 60;
1752
1753 tm = localtime(&time_start);
1754 dst_start = tm->tm_isdst;
1755
1756 while (end > start + 1)
1757 {
1758 mid = (start + end) / 2;
1759 time_mid = mid * 60;
1760
1761 tm = localtime(&time_mid);
1762
1763 if (tm->tm_isdst == dst_start)
1764 start = mid;
1765 else
1766 end = mid;
1767 }
1768
1769 time_dst = end * 60;
1770 }
1771
1772 return time_dst;
1773 }
1774
1775 /******************************************************************************
1776 * *
1777 * Function: scheduler_tm_inc *
1778 * *
1779 * Purpose: increment struct tm value by one second *
1780 * *
1781 * Parameters: tm - [IN/OUT] the tm structure to increment *
1782 * *
1783 ******************************************************************************/
scheduler_tm_inc(struct tm * tm)1784 static void scheduler_tm_inc(struct tm *tm)
1785 {
1786 if (60 > ++tm->tm_sec)
1787 return;
1788
1789 tm->tm_sec = 0;
1790 if (60 > ++tm->tm_min)
1791 return;
1792
1793 tm->tm_min = 0;
1794 if (24 > ++tm->tm_hour)
1795 return;
1796
1797 tm->tm_hour = 0;
1798 if (zbx_day_in_month(tm->tm_year + 1900, tm->tm_mon + 1) >= ++tm->tm_mday)
1799 return;
1800
1801 tm->tm_mday = 1;
1802 if (12 > ++tm->tm_mon)
1803 return;
1804
1805 tm->tm_mon = 0;
1806 tm->tm_year++;
1807 return;
1808 }
1809
1810 /******************************************************************************
1811 * *
1812 * Function: scheduler_get_nextcheck *
1813 * *
1814 * Purpose: finds the next timestamp satisfying one of intervals. *
1815 * *
1816 * Parameters: interval - [IN] the scheduler interval *
1817 * now - [IN] the current timestamp *
1818 * *
1819 * Return Value: Timestamp when the next check must be scheduled. *
1820 * *
1821 ******************************************************************************/
scheduler_get_nextcheck(zbx_scheduler_interval_t * interval,time_t now)1822 static time_t scheduler_get_nextcheck(zbx_scheduler_interval_t *interval, time_t now)
1823 {
1824 struct tm tm_start, tm, tm_dst;
1825 time_t nextcheck = 0, current_nextcheck;
1826
1827 tm_start = *(localtime(&now));
1828
1829 for (; NULL != interval; interval = interval->next)
1830 {
1831 tm = tm_start;
1832
1833 do
1834 {
1835 scheduler_tm_inc(&tm);
1836 scheduler_apply_day_filter(interval, &tm);
1837 scheduler_apply_hour_filter(interval, &tm);
1838 scheduler_apply_minute_filter(interval, &tm);
1839 scheduler_apply_second_filter(interval, &tm);
1840
1841 tm.tm_isdst = tm_start.tm_isdst;
1842 }
1843 while (-1 == (current_nextcheck = mktime(&tm)));
1844
1845 tm_dst = *(localtime(¤t_nextcheck));
1846 if (tm_dst.tm_isdst != tm_start.tm_isdst)
1847 {
1848 int dst = tm_dst.tm_isdst;
1849 time_t time_dst;
1850
1851 time_dst = scheduler_find_dst_change(now, current_nextcheck);
1852 tm_dst = *localtime(&time_dst);
1853
1854 scheduler_apply_day_filter(interval, &tm_dst);
1855 scheduler_apply_hour_filter(interval, &tm_dst);
1856 scheduler_apply_minute_filter(interval, &tm_dst);
1857 scheduler_apply_second_filter(interval, &tm_dst);
1858
1859 tm_dst.tm_isdst = dst;
1860 current_nextcheck = mktime(&tm_dst);
1861 }
1862
1863 if (0 == nextcheck || current_nextcheck < nextcheck)
1864 nextcheck = current_nextcheck;
1865 }
1866
1867 return nextcheck;
1868 }
1869
1870 /******************************************************************************
1871 * *
1872 * Function: parse_user_macro *
1873 * *
1874 * Purpose: parses user macro and finds it's length *
1875 * *
1876 * Parameters: str - [IN] string to check *
1877 * len - [OUT] length of macro *
1878 * *
1879 * Return Value: *
1880 * SUCCEED - the macro was parsed successfully. *
1881 * FAIL - the macro parsing failed, the content of output variables *
1882 * is not defined. *
1883 * *
1884 ******************************************************************************/
parse_user_macro(const char * str,int * len)1885 static int parse_user_macro(const char *str, int *len)
1886 {
1887 int macro_r, context_l, context_r;
1888
1889 if ('{' != *str || '$' != *(str + 1) || SUCCEED != zbx_user_macro_parse(str, ¯o_r, &context_l, &context_r,
1890 NULL))
1891 {
1892 return FAIL;
1893 }
1894
1895 *len = macro_r + 1;
1896
1897 return SUCCEED;
1898 }
1899
1900 /******************************************************************************
1901 * *
1902 * Function: parse_simple_interval *
1903 * *
1904 * Purpose: parses user macro and finds it's length *
1905 * *
1906 * Parameters: str - [IN] string to check *
1907 * len - [OUT] length simple interval string until separator *
1908 * sep - [IN] separator to calculate length *
1909 * value - [OUT] interval value *
1910 * *
1911 * Return Value: *
1912 * SUCCEED - the macro was parsed successfully. *
1913 * FAIL - the macro parsing failed, the content of output variables *
1914 * is not defined. *
1915 * *
1916 ******************************************************************************/
parse_simple_interval(const char * str,int * len,char sep,int * value)1917 static int parse_simple_interval(const char *str, int *len, char sep, int *value)
1918 {
1919 const char *delim;
1920
1921 if (SUCCEED != is_time_suffix(str, value,
1922 (int)(NULL == (delim = strchr(str, sep)) ? ZBX_LENGTH_UNLIMITED : delim - str)))
1923 {
1924 return FAIL;
1925 }
1926
1927 *len = NULL == delim ? (int)strlen(str) : delim - str;
1928
1929 return SUCCEED;
1930 }
1931
1932 /******************************************************************************
1933 * *
1934 * Function: zbx_validate_interval *
1935 * *
1936 * Purpose: validate update interval, flexible and scheduling intervals *
1937 * *
1938 * Parameters: str - [IN] string to check *
1939 * error - [OUT] validation error *
1940 * *
1941 * Return Value: *
1942 * SUCCEED - parsed successfully. *
1943 * FAIL - parsing failed. *
1944 * *
1945 ******************************************************************************/
zbx_validate_interval(const char * str,char ** error)1946 int zbx_validate_interval(const char *str, char **error)
1947 {
1948 int simple_interval, interval, len, custom = 0, macro;
1949 const char *delim;
1950
1951 if (SUCCEED == parse_user_macro(str, &len) && ('\0' == *(delim = str + len) || ';' == *delim))
1952 {
1953 if ('\0' == *delim)
1954 delim = NULL;
1955
1956 simple_interval = 1;
1957 }
1958 else if (SUCCEED == parse_simple_interval(str, &len, ';', &simple_interval))
1959 {
1960 if ('\0' == *(delim = str + len))
1961 delim = NULL;
1962 }
1963 else
1964 {
1965 *error = zbx_dsprintf(*error, "Invalid update interval \"%.*s\".",
1966 NULL == (delim = strchr(str, ';')) ? (int)strlen(str) : (int)(delim - str), str);
1967 return FAIL;
1968 }
1969
1970 while (NULL != delim)
1971 {
1972 str = delim + 1;
1973
1974 if ((SUCCEED == (macro = parse_user_macro(str, &len)) ||
1975 SUCCEED == parse_simple_interval(str, &len, '/', &interval)) &&
1976 '/' == *(delim = str + len))
1977 {
1978 zbx_time_period_t period;
1979
1980 custom = 1;
1981
1982 if (SUCCEED == macro)
1983 interval = 1;
1984
1985 if (0 == interval && 0 == simple_interval)
1986 {
1987 *error = zbx_dsprintf(*error, "Invalid flexible interval \"%.*s\".", (int)(delim - str),
1988 str);
1989 return FAIL;
1990 }
1991
1992 str = delim + 1;
1993
1994 if (SUCCEED == parse_user_macro(str, &len) && ('\0' == *(delim = str + len) || ';' == *delim))
1995 {
1996 if ('\0' == *delim)
1997 delim = NULL;
1998
1999 continue;
2000 }
2001
2002 if (SUCCEED == time_period_parse(&period, str,
2003 NULL == (delim = strchr(str, ';')) ? (int)strlen(str) : (int)(delim - str)))
2004 {
2005 continue;
2006 }
2007
2008 *error = zbx_dsprintf(*error, "Invalid flexible period \"%.*s\".",
2009 NULL == delim ? (int)strlen(str) : (int)(delim - str), str);
2010 return FAIL;
2011 }
2012 else
2013 {
2014 zbx_scheduler_interval_t *new_interval;
2015
2016 custom = 1;
2017
2018 if (SUCCEED == macro && ('\0' == *(delim = str + len) || ';' == *delim))
2019 {
2020 if ('\0' == *delim)
2021 delim = NULL;
2022
2023 continue;
2024 }
2025
2026 new_interval = (zbx_scheduler_interval_t *)zbx_malloc(NULL, sizeof(zbx_scheduler_interval_t));
2027 memset(new_interval, 0, sizeof(zbx_scheduler_interval_t));
2028
2029 if (SUCCEED == scheduler_interval_parse(new_interval, str,
2030 NULL == (delim = strchr(str, ';')) ? (int)strlen(str) : (int)(delim - str)))
2031 {
2032 scheduler_interval_free(new_interval);
2033 continue;
2034 }
2035 scheduler_interval_free(new_interval);
2036
2037 *error = zbx_dsprintf(*error, "Invalid custom interval \"%.*s\".",
2038 NULL == delim ? (int)strlen(str) : (int)(delim - str), str);
2039
2040 return FAIL;
2041 }
2042 }
2043
2044 if ((0 == custom && 0 == simple_interval) || SEC_PER_DAY < simple_interval)
2045 {
2046 *error = zbx_dsprintf(*error, "Invalid update interval \"%d\"", simple_interval);
2047 return FAIL;
2048 }
2049
2050 return SUCCEED;
2051 }
2052
2053 /******************************************************************************
2054 * *
2055 * Function: zbx_interval_preproc *
2056 * *
2057 * Purpose: parses item and low-level discovery rule update intervals *
2058 * *
2059 * Parameters: interval_str - [IN] update interval string to parse *
2060 * simple_interval - [OUT] simple update interval *
2061 * custom_intervals - [OUT] flexible and scheduling intervals *
2062 * error - [OUT] error message *
2063 * *
2064 * Return value: SUCCEED - intervals are valid *
2065 * FAIL - otherwise *
2066 * *
2067 * Comments: !!! Don't forget to sync code with PHP !!! *
2068 * Supported format: *
2069 * SimpleInterval, {";", FlexibleInterval | SchedulingInterval}; *
2070 * *
2071 ******************************************************************************/
zbx_interval_preproc(const char * interval_str,int * simple_interval,zbx_custom_interval_t ** custom_intervals,char ** error)2072 int zbx_interval_preproc(const char *interval_str, int *simple_interval, zbx_custom_interval_t **custom_intervals,
2073 char **error)
2074 {
2075 zbx_flexible_interval_t *flexible = NULL;
2076 zbx_scheduler_interval_t *scheduling = NULL;
2077 const char *delim, *interval_type;
2078
2079 if (SUCCEED != is_time_suffix(interval_str, simple_interval,
2080 (int)(NULL == (delim = strchr(interval_str, ';')) ? ZBX_LENGTH_UNLIMITED : delim - interval_str)))
2081 {
2082 interval_type = "update";
2083 goto fail;
2084 }
2085
2086 if (NULL == custom_intervals) /* caller wasn't interested in custom intervals, don't parse them */
2087 return SUCCEED;
2088
2089 while (NULL != delim)
2090 {
2091 interval_str = delim + 1;
2092 delim = strchr(interval_str, ';');
2093
2094 if (0 != isdigit(*interval_str))
2095 {
2096 zbx_flexible_interval_t *new_interval;
2097
2098 new_interval = (zbx_flexible_interval_t *)zbx_malloc(NULL, sizeof(zbx_flexible_interval_t));
2099
2100 if (SUCCEED != flexible_interval_parse(new_interval, interval_str,
2101 (NULL == delim ? (int)strlen(interval_str) : (int)(delim - interval_str))) ||
2102 (0 == *simple_interval && 0 == new_interval->delay))
2103 {
2104 zbx_free(new_interval);
2105 interval_type = "flexible";
2106 goto fail;
2107 }
2108
2109 new_interval->next = flexible;
2110 flexible = new_interval;
2111 }
2112 else
2113 {
2114 zbx_scheduler_interval_t *new_interval;
2115
2116 new_interval = (zbx_scheduler_interval_t *)zbx_malloc(NULL, sizeof(zbx_scheduler_interval_t));
2117 memset(new_interval, 0, sizeof(zbx_scheduler_interval_t));
2118
2119 if (SUCCEED != scheduler_interval_parse(new_interval, interval_str,
2120 (NULL == delim ? (int)strlen(interval_str) : (int)(delim - interval_str))))
2121 {
2122 scheduler_interval_free(new_interval);
2123 interval_type = "scheduling";
2124 goto fail;
2125 }
2126
2127 new_interval->next = scheduling;
2128 scheduling = new_interval;
2129 }
2130 }
2131
2132 if ((NULL == flexible && NULL == scheduling && 0 == *simple_interval) || SEC_PER_DAY < *simple_interval)
2133 {
2134 interval_type = "update";
2135 goto fail;
2136 }
2137
2138 *custom_intervals = (zbx_custom_interval_t *)zbx_malloc(NULL, sizeof(zbx_custom_interval_t));
2139 (*custom_intervals)->flexible = flexible;
2140 (*custom_intervals)->scheduling = scheduling;
2141
2142 return SUCCEED;
2143 fail:
2144 if (NULL != error)
2145 {
2146 *error = zbx_dsprintf(*error, "Invalid %s interval \"%.*s\".", interval_type,
2147 (NULL == delim ? (int)strlen(interval_str) : (int)(delim - interval_str)),
2148 interval_str);
2149 }
2150
2151 flexible_interval_free(flexible);
2152 scheduler_interval_free(scheduling);
2153
2154 return FAIL;
2155 }
2156
2157 /******************************************************************************
2158 * *
2159 * Function: zbx_custom_interval_free *
2160 * *
2161 * Purpose: frees custom update intervals *
2162 * *
2163 * Parameters: custom_intervals - [IN] custom intervals *
2164 * *
2165 ******************************************************************************/
zbx_custom_interval_free(zbx_custom_interval_t * custom_intervals)2166 void zbx_custom_interval_free(zbx_custom_interval_t *custom_intervals)
2167 {
2168 flexible_interval_free(custom_intervals->flexible);
2169 scheduler_interval_free(custom_intervals->scheduling);
2170 zbx_free(custom_intervals);
2171 }
2172
2173 /******************************************************************************
2174 * *
2175 * Function: calculate_item_nextcheck *
2176 * *
2177 * Purpose: calculate nextcheck timestamp for item *
2178 * *
2179 * Parameters: seed - [IN] the seed value applied to delay to *
2180 * spread item checks over the delay *
2181 * period *
2182 * item_type - [IN] the item type *
2183 * simple_interval - [IN] default delay value, can be overridden *
2184 * custom_intervals - [IN] preprocessed custom intervals *
2185 * now - [IN] current timestamp *
2186 * *
2187 * Return value: nextcheck value *
2188 * *
2189 * Author: Alexei Vladishev, Aleksandrs Saveljevs *
2190 * *
2191 * Comments: if item check is forbidden with delay=0 (default and flexible), *
2192 * a timestamp very far in the future is returned *
2193 * *
2194 * Old algorithm: now+delay *
2195 * New one: preserve period, if delay==5, nextcheck = 0,5,10,15,... *
2196 * *
2197 ******************************************************************************/
calculate_item_nextcheck(zbx_uint64_t seed,int item_type,int simple_interval,const zbx_custom_interval_t * custom_intervals,time_t now)2198 int calculate_item_nextcheck(zbx_uint64_t seed, int item_type, int simple_interval,
2199 const zbx_custom_interval_t *custom_intervals, time_t now)
2200 {
2201 int nextcheck = 0;
2202
2203 /* special processing of active items to see better view in queue */
2204 if (ITEM_TYPE_ZABBIX_ACTIVE == item_type)
2205 {
2206 if (0 != simple_interval)
2207 nextcheck = (int)now + simple_interval;
2208 else
2209 nextcheck = ZBX_JAN_2038;
2210 }
2211 else
2212 {
2213 int current_delay, attempt = 0;
2214 time_t next_interval, t, tmax, scheduled_check = 0;
2215
2216 /* first try to parse out and calculate scheduled intervals */
2217 if (NULL != custom_intervals)
2218 scheduled_check = scheduler_get_nextcheck(custom_intervals->scheduling, now);
2219
2220 /* Try to find the nearest 'nextcheck' value with condition */
2221 /* 'now' < 'nextcheck' < 'now' + SEC_PER_YEAR. If it is not */
2222 /* possible to check the item within a year, fail. */
2223
2224 t = now;
2225 tmax = now + SEC_PER_YEAR;
2226
2227 while (t < tmax)
2228 {
2229 /* calculate 'nextcheck' value for the current interval */
2230 if (NULL != custom_intervals)
2231 current_delay = get_current_delay(simple_interval, custom_intervals->flexible, t);
2232 else
2233 current_delay = simple_interval;
2234
2235 if (0 != current_delay)
2236 {
2237 nextcheck = current_delay * (int)(t / (time_t)current_delay) +
2238 (int)(seed % (zbx_uint64_t)current_delay);
2239
2240 if (0 == attempt)
2241 {
2242 while (nextcheck <= t)
2243 nextcheck += current_delay;
2244 }
2245 else
2246 {
2247 while (nextcheck < t)
2248 nextcheck += current_delay;
2249 }
2250 }
2251 else
2252 nextcheck = ZBX_JAN_2038;
2253
2254 if (NULL == custom_intervals)
2255 break;
2256
2257 /* 'nextcheck' < end of the current interval ? */
2258 /* the end of the current interval is the beginning of the next interval - 1 */
2259 if (FAIL != get_next_delay_interval(custom_intervals->flexible, t, &next_interval) &&
2260 nextcheck >= next_interval)
2261 {
2262 /* 'nextcheck' is beyond the current interval */
2263 t = next_interval;
2264 attempt++;
2265 }
2266 else
2267 break; /* nextcheck is within the current interval */
2268 }
2269
2270 if (0 != scheduled_check && scheduled_check < nextcheck)
2271 nextcheck = (int)scheduled_check;
2272 }
2273
2274 return nextcheck;
2275 }
2276 /******************************************************************************
2277 * *
2278 * Function: calculate_item_nextcheck_unreachable *
2279 * *
2280 * Purpose: calculate nextcheck timestamp for item on unreachable host *
2281 * *
2282 * Parameters: simple_interval - [IN] default delay value, can be overridden *
2283 * custom_intervals - [IN] preprocessed custom intervals *
2284 * disable_until - [IN] timestamp for next check *
2285 * *
2286 * Return value: nextcheck value *
2287 * *
2288 ******************************************************************************/
calculate_item_nextcheck_unreachable(int simple_interval,const zbx_custom_interval_t * custom_intervals,time_t disable_until)2289 int calculate_item_nextcheck_unreachable(int simple_interval, const zbx_custom_interval_t *custom_intervals,
2290 time_t disable_until)
2291 {
2292 int nextcheck = 0;
2293 time_t next_interval, tmax, scheduled_check = 0;
2294
2295 /* first try to parse out and calculate scheduled intervals */
2296 if (NULL != custom_intervals)
2297 scheduled_check = scheduler_get_nextcheck(custom_intervals->scheduling, disable_until);
2298
2299 /* Try to find the nearest 'nextcheck' value with condition */
2300 /* 'now' < 'nextcheck' < 'now' + SEC_PER_YEAR. If it is not */
2301 /* possible to check the item within a year, fail. */
2302
2303 nextcheck = disable_until;
2304 tmax = disable_until + SEC_PER_YEAR;
2305
2306 if (NULL != custom_intervals)
2307 {
2308 while (nextcheck < tmax)
2309 {
2310 if (0 != get_current_delay(simple_interval, custom_intervals->flexible, nextcheck))
2311 break;
2312
2313 /* find the flexible interval change */
2314 if (FAIL == get_next_delay_interval(custom_intervals->flexible, nextcheck, &next_interval))
2315 {
2316 nextcheck = ZBX_JAN_2038;
2317 break;
2318 }
2319 nextcheck = next_interval;
2320 }
2321 }
2322
2323 if (0 != scheduled_check && scheduled_check < nextcheck)
2324 nextcheck = (int)scheduled_check;
2325
2326 return nextcheck;
2327 }
2328 /******************************************************************************
2329 * *
2330 * Function: calculate_proxy_nextcheck *
2331 * *
2332 * Purpose: calculate nextcheck timestamp for passive proxy *
2333 * *
2334 * Parameters: hostid - [IN] host identificator from database *
2335 * delay - [IN] default delay value, can be overridden *
2336 * now - [IN] current timestamp *
2337 * *
2338 * Return value: nextcheck value *
2339 * *
2340 * Author: Alexander Vladishev *
2341 * *
2342 ******************************************************************************/
calculate_proxy_nextcheck(zbx_uint64_t hostid,unsigned int delay,time_t now)2343 time_t calculate_proxy_nextcheck(zbx_uint64_t hostid, unsigned int delay, time_t now)
2344 {
2345 time_t nextcheck;
2346
2347 nextcheck = delay * (now / delay) + (unsigned int)(hostid % delay);
2348
2349 while (nextcheck <= now)
2350 nextcheck += delay;
2351
2352 return nextcheck;
2353 }
2354
2355 /******************************************************************************
2356 * *
2357 * Function: is_ip4 *
2358 * *
2359 * Purpose: is string IPv4 address *
2360 * *
2361 * Parameters: ip - string *
2362 * *
2363 * Return value: SUCCEED - is IPv4 address *
2364 * FAIL - otherwise *
2365 * *
2366 * Author: Alexei Vladishev, Alexander Vladishev *
2367 * *
2368 ******************************************************************************/
is_ip4(const char * ip)2369 int is_ip4(const char *ip)
2370 {
2371 const char *p = ip;
2372 int digits = 0, dots = 0, res = FAIL, octet = 0;
2373
2374 zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s'", __func__, ip);
2375
2376 while ('\0' != *p)
2377 {
2378 if (0 != isdigit(*p))
2379 {
2380 octet = octet * 10 + (*p - '0');
2381 digits++;
2382 }
2383 else if ('.' == *p)
2384 {
2385 if (0 == digits || 3 < digits || 255 < octet)
2386 break;
2387
2388 digits = 0;
2389 octet = 0;
2390 dots++;
2391 }
2392 else
2393 {
2394 digits = 0;
2395 break;
2396 }
2397
2398 p++;
2399 }
2400 if (3 == dots && 1 <= digits && 3 >= digits && 255 >= octet)
2401 res = SUCCEED;
2402
2403 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(res));
2404
2405 return res;
2406 }
2407
2408 /******************************************************************************
2409 * *
2410 * Function: is_ip6 *
2411 * *
2412 * Purpose: is string IPv6 address *
2413 * *
2414 * Parameters: ip - string *
2415 * *
2416 * Return value: SUCCEED - is IPv6 address *
2417 * FAIL - otherwise *
2418 * *
2419 * Author: Alexander Vladishev *
2420 * *
2421 ******************************************************************************/
is_ip6(const char * ip)2422 int is_ip6(const char *ip)
2423 {
2424 const char *p = ip, *last_colon;
2425 int xdigits = 0, only_xdigits = 0, colons = 0, dbl_colons = 0, res;
2426
2427 zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s'", __func__, ip);
2428
2429 while ('\0' != *p)
2430 {
2431 if (0 != isxdigit(*p))
2432 {
2433 xdigits++;
2434 only_xdigits = 1;
2435 }
2436 else if (':' == *p)
2437 {
2438 if (0 == xdigits && 0 < colons)
2439 {
2440 /* consecutive sections of zeros are replaced with a double colon */
2441 only_xdigits = 1;
2442 dbl_colons++;
2443 }
2444
2445 if (4 < xdigits || 1 < dbl_colons)
2446 break;
2447
2448 xdigits = 0;
2449 colons++;
2450 }
2451 else
2452 {
2453 only_xdigits = 0;
2454 break;
2455 }
2456
2457 p++;
2458 }
2459
2460 if (2 > colons || 7 < colons || 1 < dbl_colons || 4 < xdigits)
2461 res = FAIL;
2462 else if (1 == only_xdigits)
2463 res = SUCCEED;
2464 else if (7 > colons && (last_colon = strrchr(ip, ':')) < p)
2465 res = is_ip4(last_colon + 1); /* past last column is ipv4 mapped address */
2466 else
2467 res = FAIL;
2468
2469 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(res));
2470
2471 return res;
2472 }
2473
2474 /******************************************************************************
2475 * *
2476 * Function: is_supported_ip *
2477 * *
2478 * Purpose: is string IP address of supported version *
2479 * *
2480 * Parameters: ip - string *
2481 * *
2482 * Return value: SUCCEED - is IP address *
2483 * FAIL - otherwise *
2484 * *
2485 * Author: Alexander Vladishev *
2486 * *
2487 ******************************************************************************/
is_supported_ip(const char * ip)2488 int is_supported_ip(const char *ip)
2489 {
2490 if (SUCCEED == is_ip4(ip))
2491 return SUCCEED;
2492 #ifdef HAVE_IPV6
2493 if (SUCCEED == is_ip6(ip))
2494 return SUCCEED;
2495 #endif
2496 return FAIL;
2497 }
2498
2499 /******************************************************************************
2500 * *
2501 * Function: is_ip *
2502 * *
2503 * Purpose: is string IP address *
2504 * *
2505 * Parameters: ip - string *
2506 * *
2507 * Return value: SUCCEED - is IP address *
2508 * FAIL - otherwise *
2509 * *
2510 * Author: Alexander Vladishev *
2511 * *
2512 ******************************************************************************/
is_ip(const char * ip)2513 int is_ip(const char *ip)
2514 {
2515 return SUCCEED == is_ip4(ip) ? SUCCEED : is_ip6(ip);
2516 }
2517
2518 /******************************************************************************
2519 * *
2520 * Function: zbx_validate_hostname *
2521 * *
2522 * Purpose: check if string is a valid internet hostname *
2523 * *
2524 * Parameters: hostname - [IN] hostname string to be checked *
2525 * *
2526 * Return value: SUCCEED - could be a valid hostname, *
2527 * FAIL - definitely not a valid hostname *
2528 * Comments: *
2529 * Validation is not strict. Restrictions not checked: *
2530 * - individual label (component) length 1-63, *
2531 * - hyphens ('-') allowed only as interior characters in labels, *
2532 * - underscores ('_') allowed in domain name, but not in hostname. *
2533 * *
2534 ******************************************************************************/
zbx_validate_hostname(const char * hostname)2535 int zbx_validate_hostname(const char *hostname)
2536 {
2537 int component; /* periods ('.') are only allowed when they serve to delimit components */
2538 int len = MAX_ZBX_DNSNAME_LEN;
2539 const char *p;
2540
2541 /* the first character must be an alphanumeric character */
2542 if (0 == isalnum(*hostname))
2543 return FAIL;
2544
2545 /* check only up to the first 'len' characters, the 1st character is already successfully checked */
2546 for (p = hostname + 1, component = 1; '\0' != *p; p++)
2547 {
2548 if (0 == --len) /* hostname too long */
2549 return FAIL;
2550
2551 /* check for allowed characters */
2552 if (0 != isalnum(*p) || '-' == *p || '_' == *p)
2553 component = 1;
2554 else if ('.' == *p && 1 == component)
2555 component = 0;
2556 else
2557 return FAIL;
2558 }
2559
2560 return SUCCEED;
2561 }
2562
2563 /******************************************************************************
2564 * *
2565 * Function: ip_in_list *
2566 * *
2567 * Purpose: check if ip matches range of ip addresses *
2568 * *
2569 * Parameters: list - [IN] comma-separated list of ip ranges *
2570 * 192.168.0.1-64,192.168.0.128,10.10.0.0/24,12fc::21 *
2571 * ip - [IN] ip address *
2572 * *
2573 * Return value: FAIL - out of range, SUCCEED - within the range *
2574 * *
2575 ******************************************************************************/
ip_in_list(const char * list,const char * ip)2576 int ip_in_list(const char *list, const char *ip)
2577 {
2578 int ipaddress[8];
2579 zbx_iprange_t iprange;
2580 char *address = NULL;
2581 size_t address_alloc = 0, address_offset;
2582 const char *ptr;
2583 int ret = FAIL;
2584
2585 zabbix_log(LOG_LEVEL_DEBUG, "In %s() list:'%s' ip:'%s'", __func__, list, ip);
2586
2587 if (SUCCEED != iprange_parse(&iprange, ip))
2588 goto out;
2589 #ifndef HAVE_IPV6
2590 if (ZBX_IPRANGE_V6 == iprange.type)
2591 goto out;
2592 #endif
2593 iprange_first(&iprange, ipaddress);
2594
2595 for (ptr = list; '\0' != *ptr; list = ptr + 1)
2596 {
2597 if (NULL == (ptr = strchr(list, ',')))
2598 ptr = list + strlen(list);
2599
2600 address_offset = 0;
2601 zbx_strncpy_alloc(&address, &address_alloc, &address_offset, list, ptr - list);
2602
2603 if (SUCCEED != iprange_parse(&iprange, address))
2604 continue;
2605 #ifndef HAVE_IPV6
2606 if (ZBX_IPRANGE_V6 == iprange.type)
2607 continue;
2608 #endif
2609 if (SUCCEED == iprange_validate(&iprange, ipaddress))
2610 {
2611 ret = SUCCEED;
2612 break;
2613 }
2614 }
2615
2616 zbx_free(address);
2617 out:
2618 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
2619
2620 return ret;
2621 }
2622
2623 /******************************************************************************
2624 * *
2625 * Function: int_in_list *
2626 * *
2627 * Purpose: check if integer matches a list of integers *
2628 * *
2629 * Parameters: list - integers [i1-i2,i3,i4,i5-i6] (10-25,45,67-699) *
2630 * value - integer to check *
2631 * *
2632 * Return value: FAIL - out of period, SUCCEED - within the period *
2633 * *
2634 * Author: Alexei Vladishev *
2635 * *
2636 ******************************************************************************/
int_in_list(char * list,int value)2637 int int_in_list(char *list, int value)
2638 {
2639 char *start = NULL, *end = NULL, c = '\0';
2640 int i1, i2, ret = FAIL;
2641
2642 zabbix_log(LOG_LEVEL_DEBUG, "In %s() list:'%s' value:%d", __func__, list, value);
2643
2644 for (start = list; '\0' != *start;)
2645 {
2646 if (NULL != (end = strchr(start, ',')))
2647 {
2648 c = *end;
2649 *end = '\0';
2650 }
2651
2652 if (2 == sscanf(start, "%d-%d", &i1, &i2))
2653 {
2654 if (i1 <= value && value <= i2)
2655 {
2656 ret = SUCCEED;
2657 break;
2658 }
2659 }
2660 else
2661 {
2662 if (value == atoi(start))
2663 {
2664 ret = SUCCEED;
2665 break;
2666 }
2667 }
2668
2669 if (NULL != end)
2670 {
2671 *end = c;
2672 start = end + 1;
2673 }
2674 else
2675 break;
2676 }
2677
2678 if (NULL != end)
2679 *end = c;
2680
2681 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
2682
2683 return ret;
2684 }
2685
zbx_double_compare(double a,double b)2686 int zbx_double_compare(double a, double b)
2687 {
2688 return fabs(a - b) <= ZBX_DOUBLE_EPSILON ? SUCCEED : FAIL;
2689 }
2690
2691 /******************************************************************************
2692 * *
2693 * Function: is_double_suffix *
2694 * *
2695 * Purpose: check if the string is double *
2696 * *
2697 * Parameters: str - string to check *
2698 * flags - extra options including: *
2699 * ZBX_FLAG_DOUBLE_SUFFIX - allow suffixes *
2700 * *
2701 * Return value: SUCCEED - the string is double *
2702 * FAIL - otherwise *
2703 * *
2704 * Author: Alexei Vladishev *
2705 * *
2706 * Comments: the function automatically processes suffixes K, M, G, T and *
2707 * s, m, h, d, w *
2708 * *
2709 ******************************************************************************/
is_double_suffix(const char * str,unsigned char flags)2710 int is_double_suffix(const char *str, unsigned char flags)
2711 {
2712 int len;
2713
2714 if ('-' == *str) /* check leading sign */
2715 str++;
2716
2717 if (FAIL == zbx_number_parse(str, &len))
2718 return FAIL;
2719
2720 if ('\0' != *(str += len) && 0 != (flags & ZBX_FLAG_DOUBLE_SUFFIX) && NULL != strchr(ZBX_UNIT_SYMBOLS, *str))
2721 str++; /* allow valid suffix if flag is enabled */
2722
2723 return '\0' == *str ? SUCCEED : FAIL;
2724 }
2725
is_double_valid_syntax(const char * str)2726 static int is_double_valid_syntax(const char *str)
2727 {
2728 int len;
2729
2730 /* Valid syntax is a decimal number optionally followed by a decimal exponent. */
2731 /* Leading and trailing white space, NAN, INF and hexadecimal notation are not allowed. */
2732
2733 if ('-' == *str || '+' == *str) /* check leading sign */
2734 str++;
2735
2736 if (FAIL == zbx_number_parse(str, &len))
2737 return FAIL;
2738
2739 return '\0' == *(str + len) ? SUCCEED : FAIL;
2740 }
2741
2742 /******************************************************************************
2743 * *
2744 * Function: is_double *
2745 * *
2746 * Purpose: validate and optionally convert a string to a number of type *
2747 * 'double' *
2748 * *
2749 * Parameters: str - [IN] string to check *
2750 * value - [OUT] output buffer where to write the converted value *
2751 * (optional, can be NULL) *
2752 * *
2753 * Return value: SUCCEED - the string can be converted to 'double' and *
2754 * was converted if 'value' is not NULL *
2755 * FAIL - the string does not represent a valid 'double' or *
2756 * its value is outside of valid range *
2757 * *
2758 * Author: Alexei Vladishev, Aleksandrs Saveljevs *
2759 * *
2760 ******************************************************************************/
is_double(const char * str,double * value)2761 int is_double(const char *str, double *value)
2762 {
2763 double tmp;
2764 char *endptr;
2765
2766 /* Not all strings accepted by strtod() can be accepted in Zabbix. */
2767 /* Therefore additional, more strict syntax check is used before strtod(). */
2768
2769 if (SUCCEED != is_double_valid_syntax(str))
2770 return FAIL;
2771
2772 errno = 0;
2773 tmp = strtod(str, &endptr);
2774
2775 if ('\0' != *endptr || HUGE_VAL == tmp || -HUGE_VAL == tmp || EDOM == errno)
2776 return FAIL;
2777
2778 if (NULL != value)
2779 *value = tmp;
2780
2781 return SUCCEED;
2782 }
2783
2784 /******************************************************************************
2785 * *
2786 * Function: is_time_suffix *
2787 * *
2788 * Purpose: check if the string is a non-negative integer with or without *
2789 * supported time suffix *
2790 * *
2791 * Parameters: str - [IN] string to check *
2792 * value - [OUT] a pointer to converted value (optional) *
2793 * length - [IN] number of characters to validate, pass *
2794 * ZBX_LENGTH_UNLIMITED to validate full string *
2795 * *
2796 * Return value: SUCCEED - the string is valid and within reasonable limits *
2797 * FAIL - otherwise *
2798 * *
2799 * Author: Aleksandrs Saveljevs, Vladimir Levijev *
2800 * *
2801 * Comments: the function automatically processes suffixes s, m, h, d, w *
2802 * *
2803 ******************************************************************************/
is_time_suffix(const char * str,int * value,int length)2804 int is_time_suffix(const char *str, int *value, int length)
2805 {
2806 const int max = 0x7fffffff; /* minimum acceptable value for INT_MAX is 2 147 483 647 */
2807 int len = length;
2808 int value_tmp = 0, c, factor = 1;
2809
2810 if ('\0' == *str || 0 >= len || 0 == isdigit(*str))
2811 return FAIL;
2812
2813 while ('\0' != *str && 0 < len && 0 != isdigit(*str))
2814 {
2815 c = (int)(unsigned char)(*str - '0');
2816
2817 if ((max - c) / 10 < value_tmp)
2818 return FAIL; /* overflow */
2819
2820 value_tmp = value_tmp * 10 + c;
2821
2822 str++;
2823 len--;
2824 }
2825
2826 if ('\0' != *str && 0 < len)
2827 {
2828 switch (*str)
2829 {
2830 case 's':
2831 break;
2832 case 'm':
2833 factor = SEC_PER_MIN;
2834 break;
2835 case 'h':
2836 factor = SEC_PER_HOUR;
2837 break;
2838 case 'd':
2839 factor = SEC_PER_DAY;
2840 break;
2841 case 'w':
2842 factor = SEC_PER_WEEK;
2843 break;
2844 default:
2845 return FAIL;
2846 }
2847
2848 str++;
2849 len--;
2850 }
2851
2852 if ((ZBX_LENGTH_UNLIMITED == length && '\0' != *str) || (ZBX_LENGTH_UNLIMITED != length && 0 != len))
2853 return FAIL;
2854
2855 if (max / factor < value_tmp)
2856 return FAIL; /* overflow */
2857
2858 if (NULL != value)
2859 *value = value_tmp * factor;
2860
2861 return SUCCEED;
2862 }
2863
2864 #if defined(_WINDOWS) || defined(__MINGW32__)
_wis_uint(const wchar_t * wide_string)2865 int _wis_uint(const wchar_t *wide_string)
2866 {
2867 const wchar_t *wide_char = wide_string;
2868
2869 if (L'\0' == *wide_char)
2870 return FAIL;
2871
2872 while (L'\0' != *wide_char)
2873 {
2874 if (0 != iswdigit(*wide_char))
2875 {
2876 wide_char++;
2877 continue;
2878 }
2879 return FAIL;
2880 }
2881
2882 return SUCCEED;
2883 }
2884 #endif
2885
2886 /******************************************************************************
2887 * *
2888 * Function: is_uint_n_range *
2889 * *
2890 * Purpose: check if the string is unsigned integer within the specified *
2891 * range and optionally store it into value parameter *
2892 * *
2893 * Parameters: str - [IN] string to check *
2894 * n - [IN] string length or ZBX_MAX_UINT64_LEN *
2895 * value - [OUT] a pointer to output buffer where the converted *
2896 * value is to be written (optional, can be NULL) *
2897 * size - [IN] size of the output buffer (optional) *
2898 * min - [IN] the minimum acceptable value *
2899 * max - [IN] the maximum acceptable value *
2900 * *
2901 * Return value: SUCCEED - the string is unsigned integer *
2902 * FAIL - the string is not a number or its value is outside *
2903 * the specified range *
2904 * *
2905 * Author: Alexander Vladishev, Andris Zeila *
2906 * *
2907 ******************************************************************************/
is_uint_n_range(const char * str,size_t n,void * value,size_t size,zbx_uint64_t min,zbx_uint64_t max)2908 int is_uint_n_range(const char *str, size_t n, void *value, size_t size, zbx_uint64_t min, zbx_uint64_t max)
2909 {
2910 zbx_uint64_t value_uint64 = 0, c;
2911 const zbx_uint64_t max_uint64 = ~(zbx_uint64_t)__UINT64_C(0);
2912
2913 if ('\0' == *str || 0 == n || sizeof(zbx_uint64_t) < size || (0 == size && NULL != value))
2914 return FAIL;
2915
2916 while ('\0' != *str && 0 < n--)
2917 {
2918 if (0 == isdigit(*str))
2919 return FAIL; /* not a digit */
2920
2921 c = (zbx_uint64_t)(unsigned char)(*str - '0');
2922
2923 if ((max_uint64 - c) / 10 < value_uint64)
2924 return FAIL; /* maximum value exceeded */
2925
2926 value_uint64 = value_uint64 * 10 + c;
2927
2928 str++;
2929 }
2930
2931 if (min > value_uint64 || value_uint64 > max)
2932 return FAIL;
2933
2934 if (NULL != value)
2935 {
2936 /* On little endian architecture the output value will be stored starting from the first bytes */
2937 /* of 'value' buffer while on big endian architecture it will be stored starting from the last */
2938 /* bytes. We handle it by storing the offset in the most significant byte of short value and */
2939 /* then use the first byte as source offset. */
2940 unsigned short value_offset = (unsigned short)((sizeof(zbx_uint64_t) - size) << 8);
2941
2942 memcpy(value, (unsigned char *)&value_uint64 + *((unsigned char *)&value_offset), size);
2943 }
2944
2945 return SUCCEED;
2946 }
2947
2948 /******************************************************************************
2949 * *
2950 * Function: is_hex_n_range *
2951 * *
2952 * Purpose: check if the string is unsigned hexadecimal integer within the *
2953 * specified range and optionally store it into value parameter *
2954 * *
2955 * Parameters: str - [IN] string to check *
2956 * n - [IN] string length *
2957 * value - [OUT] a pointer to output buffer where the converted *
2958 * value is to be written (optional, can be NULL) *
2959 * size - [IN] size of the output buffer (optional) *
2960 * min - [IN] the minimum acceptable value *
2961 * max - [IN] the maximum acceptable value *
2962 * *
2963 * Return value: SUCCEED - the string is unsigned integer *
2964 * FAIL - the string is not a hexadecimal number or its value *
2965 * is outside the specified range *
2966 * *
2967 ******************************************************************************/
is_hex_n_range(const char * str,size_t n,void * value,size_t size,zbx_uint64_t min,zbx_uint64_t max)2968 int is_hex_n_range(const char *str, size_t n, void *value, size_t size, zbx_uint64_t min, zbx_uint64_t max)
2969 {
2970 zbx_uint64_t value_uint64 = 0, c;
2971 const zbx_uint64_t max_uint64 = ~(zbx_uint64_t)__UINT64_C(0);
2972 int len = 0;
2973
2974 if ('\0' == *str || 0 == n || sizeof(zbx_uint64_t) < size || (0 == size && NULL != value))
2975 return FAIL;
2976
2977 while ('\0' != *str && 0 < n--)
2978 {
2979 if ('0' <= *str && *str <= '9')
2980 c = *str - '0';
2981 else if ('a' <= *str && *str <= 'f')
2982 c = 10 + (*str - 'a');
2983 else if ('A' <= *str && *str <= 'F')
2984 c = 10 + (*str - 'A');
2985 else
2986 return FAIL; /* not a hexadecimal digit */
2987
2988 if (16 < ++len && (max_uint64 >> 4) < value_uint64)
2989 return FAIL; /* maximum value exceeded */
2990
2991 value_uint64 = (value_uint64 << 4) + c;
2992
2993 str++;
2994 }
2995 if (min > value_uint64 || value_uint64 > max)
2996 return FAIL;
2997
2998 if (NULL != value)
2999 {
3000 /* On little endian architecture the output value will be stored starting from the first bytes */
3001 /* of 'value' buffer while on big endian architecture it will be stored starting from the last */
3002 /* bytes. We handle it by storing the offset in the most significant byte of short value and */
3003 /* then use the first byte as source offset. */
3004 unsigned short value_offset = (unsigned short)((sizeof(zbx_uint64_t) - size) << 8);
3005
3006 memcpy(value, (unsigned char *)&value_uint64 + *((unsigned char *)&value_offset), size);
3007 }
3008
3009 return SUCCEED;
3010 }
3011
3012 /******************************************************************************
3013 * *
3014 * Function: is_boolean *
3015 * *
3016 * Purpose: check if the string is boolean *
3017 * *
3018 * Parameters: str - string to check *
3019 * *
3020 * Return value: SUCCEED - the string is boolean *
3021 * FAIL - otherwise *
3022 * *
3023 * Author: Aleksandrs Saveljevs *
3024 * *
3025 ******************************************************************************/
is_boolean(const char * str,zbx_uint64_t * value)3026 int is_boolean(const char *str, zbx_uint64_t *value)
3027 {
3028 double dbl_tmp;
3029 int res;
3030
3031 if (SUCCEED == (res = is_double(str, &dbl_tmp)))
3032 *value = (0 != dbl_tmp);
3033 else
3034 {
3035 char tmp[16];
3036
3037 strscpy(tmp, str);
3038 zbx_strlower(tmp);
3039
3040 if (SUCCEED == (res = str_in_list("true,t,yes,y,on,up,running,enabled,available,ok,master", tmp, ',')))
3041 {
3042 *value = 1;
3043 }
3044 else if (SUCCEED == (res = str_in_list("false,f,no,n,off,down,unused,disabled,unavailable,err,slave",
3045 tmp, ',')))
3046 {
3047 *value = 0;
3048 }
3049 }
3050
3051 return res;
3052 }
3053
3054 /******************************************************************************
3055 * *
3056 * Function: is_uoct *
3057 * *
3058 * Purpose: check if the string is unsigned octal *
3059 * *
3060 * Parameters: str - string to check *
3061 * *
3062 * Return value: SUCCEED - the string is unsigned octal *
3063 * FAIL - otherwise *
3064 * *
3065 * Author: Alexander Vladishev *
3066 * *
3067 ******************************************************************************/
is_uoct(const char * str)3068 int is_uoct(const char *str)
3069 {
3070 int res = FAIL;
3071
3072 while (' ' == *str) /* trim left spaces */
3073 str++;
3074
3075 for (; '\0' != *str; str++)
3076 {
3077 if (*str < '0' || *str > '7')
3078 break;
3079
3080 res = SUCCEED;
3081 }
3082
3083 while (' ' == *str) /* check right spaces */
3084 str++;
3085
3086 if ('\0' != *str)
3087 return FAIL;
3088
3089 return res;
3090 }
3091
3092 /******************************************************************************
3093 * *
3094 * Function: is_uhex *
3095 * *
3096 * Purpose: check if the string is unsigned hexadecimal representation of *
3097 * data in the form "0-9, a-f or A-F" *
3098 * *
3099 * Parameters: str - string to check *
3100 * *
3101 * Return value: SUCCEED - the string is unsigned hexadecimal *
3102 * FAIL - otherwise *
3103 * *
3104 * Author: Alexander Vladishev *
3105 * *
3106 ******************************************************************************/
is_uhex(const char * str)3107 int is_uhex(const char *str)
3108 {
3109 int res = FAIL;
3110
3111 while (' ' == *str) /* trim left spaces */
3112 str++;
3113
3114 for (; '\0' != *str; str++)
3115 {
3116 if (0 == isxdigit(*str))
3117 break;
3118
3119 res = SUCCEED;
3120 }
3121
3122 while (' ' == *str) /* check right spaces */
3123 str++;
3124
3125 if ('\0' != *str)
3126 return FAIL;
3127
3128 return res;
3129 }
3130
3131 /******************************************************************************
3132 * *
3133 * Function: is_hex_string *
3134 * *
3135 * Purpose: check if the string is a hexadecimal representation of data in *
3136 * the form "F4 CE 46 01 0C 44 8B F4\nA0 2C 29 74 5D 3F 13 49\n" *
3137 * *
3138 * Parameters: str - string to check *
3139 * *
3140 * Return value: SUCCEED - the string is formatted like the example above *
3141 * FAIL - otherwise *
3142 * *
3143 * Author: Aleksandrs Saveljevs *
3144 * *
3145 ******************************************************************************/
is_hex_string(const char * str)3146 int is_hex_string(const char *str)
3147 {
3148 if ('\0' == *str)
3149 return FAIL;
3150
3151 while ('\0' != *str)
3152 {
3153 if (0 == isxdigit(*str))
3154 return FAIL;
3155
3156 if (0 == isxdigit(*(str + 1)))
3157 return FAIL;
3158
3159 if ('\0' == *(str + 2))
3160 break;
3161
3162 if (' ' != *(str + 2) && '\n' != *(str + 2))
3163 return FAIL;
3164
3165 str += 3;
3166 }
3167
3168 return SUCCEED;
3169 }
3170
3171 /******************************************************************************
3172 * *
3173 * Function: get_nearestindex *
3174 * *
3175 * Purpose: get nearest index position of sorted elements in array *
3176 * *
3177 * Parameters: p - pointer to array of elements *
3178 * sz - element size *
3179 * num - number of elements *
3180 * id - index to look for *
3181 * *
3182 * Return value: index at which it would be possible to insert the element so *
3183 * that the array is still sorted *
3184 * *
3185 ******************************************************************************/
get_nearestindex(const void * p,size_t sz,int num,zbx_uint64_t id)3186 int get_nearestindex(const void *p, size_t sz, int num, zbx_uint64_t id)
3187 {
3188 int first_index, last_index, index;
3189 zbx_uint64_t element_id;
3190
3191 if (0 == num)
3192 return 0;
3193
3194 first_index = 0;
3195 last_index = num - 1;
3196
3197 while (1)
3198 {
3199 index = first_index + (last_index - first_index) / 2;
3200
3201 if (id == (element_id = *(const zbx_uint64_t *)((const char *)p + index * sz)))
3202 return index;
3203
3204 if (last_index == first_index)
3205 {
3206 if (element_id < id)
3207 index++;
3208 return index;
3209 }
3210
3211 if (element_id < id)
3212 first_index = index + 1;
3213 else
3214 last_index = index;
3215 }
3216 }
3217
3218 /******************************************************************************
3219 * *
3220 * Function: uint64_array_add *
3221 * *
3222 * Purpose: add uint64 value to dynamic array *
3223 * *
3224 * Author: Alexander Vladishev *
3225 * *
3226 ******************************************************************************/
uint64_array_add(zbx_uint64_t ** values,int * alloc,int * num,zbx_uint64_t value,int alloc_step)3227 int uint64_array_add(zbx_uint64_t **values, int *alloc, int *num, zbx_uint64_t value, int alloc_step)
3228 {
3229 int index;
3230
3231 index = get_nearestindex(*values, sizeof(zbx_uint64_t), *num, value);
3232 if (index < (*num) && (*values)[index] == value)
3233 return index;
3234
3235 if (*alloc == *num)
3236 {
3237 if (0 == alloc_step)
3238 {
3239 zbx_error("Unable to reallocate buffer");
3240 assert(0);
3241 }
3242
3243 *alloc += alloc_step;
3244 *values = (zbx_uint64_t *)zbx_realloc(*values, *alloc * sizeof(zbx_uint64_t));
3245 }
3246
3247 memmove(&(*values)[index + 1], &(*values)[index], sizeof(zbx_uint64_t) * (*num - index));
3248
3249 (*values)[index] = value;
3250 (*num)++;
3251
3252 return index;
3253 }
3254
3255 /******************************************************************************
3256 * *
3257 * Function: uint64_array_exists *
3258 * *
3259 * Author: Alexander Vladishev *
3260 * *
3261 ******************************************************************************/
uint64_array_exists(const zbx_uint64_t * values,int num,zbx_uint64_t value)3262 int uint64_array_exists(const zbx_uint64_t *values, int num, zbx_uint64_t value)
3263 {
3264 int index;
3265
3266 index = get_nearestindex(values, sizeof(zbx_uint64_t), num, value);
3267 if (index < num && values[index] == value)
3268 return SUCCEED;
3269
3270 return FAIL;
3271 }
3272
3273 /******************************************************************************
3274 * *
3275 * Function: uint64_array_remove *
3276 * *
3277 * Purpose: remove uint64 values from array *
3278 * *
3279 * Author: Alexander Vladishev *
3280 * *
3281 ******************************************************************************/
uint64_array_remove(zbx_uint64_t * values,int * num,const zbx_uint64_t * rm_values,int rm_num)3282 void uint64_array_remove(zbx_uint64_t *values, int *num, const zbx_uint64_t *rm_values, int rm_num)
3283 {
3284 int rindex, index;
3285
3286 for (rindex = 0; rindex < rm_num; rindex++)
3287 {
3288 index = get_nearestindex(values, sizeof(zbx_uint64_t), *num, rm_values[rindex]);
3289 if (index == *num || values[index] != rm_values[rindex])
3290 continue;
3291
3292 memmove(&values[index], &values[index + 1], sizeof(zbx_uint64_t) * ((*num) - index - 1));
3293 (*num)--;
3294 }
3295 }
3296
suffix2factor(char c)3297 zbx_uint64_t suffix2factor(char c)
3298 {
3299 switch (c)
3300 {
3301 case 'K':
3302 return ZBX_KIBIBYTE;
3303 case 'M':
3304 return ZBX_MEBIBYTE;
3305 case 'G':
3306 return ZBX_GIBIBYTE;
3307 case 'T':
3308 return ZBX_TEBIBYTE;
3309 case 's':
3310 return 1;
3311 case 'm':
3312 return SEC_PER_MIN;
3313 case 'h':
3314 return SEC_PER_HOUR;
3315 case 'd':
3316 return SEC_PER_DAY;
3317 case 'w':
3318 return SEC_PER_WEEK;
3319 default:
3320 return 1;
3321 }
3322 }
3323
3324 /******************************************************************************
3325 * *
3326 * Function: str2uint64 *
3327 * *
3328 * Purpose: convert string to 64bit unsigned integer *
3329 * *
3330 * Parameters: str - string to convert *
3331 * value - a pointer to converted value *
3332 * *
3333 * Return value: SUCCEED - the string is unsigned integer *
3334 * FAIL - otherwise *
3335 * *
3336 * Author: Alexander Vladishev *
3337 * *
3338 * Comments: the function automatically processes suffixes K, M, G, T *
3339 * *
3340 ******************************************************************************/
str2uint64(const char * str,const char * suffixes,zbx_uint64_t * value)3341 int str2uint64(const char *str, const char *suffixes, zbx_uint64_t *value)
3342 {
3343 size_t sz;
3344 const char *p;
3345 int ret;
3346 zbx_uint64_t factor = 1;
3347
3348 sz = strlen(str);
3349 p = str + sz - 1;
3350
3351 if (NULL != strchr(suffixes, *p))
3352 {
3353 factor = suffix2factor(*p);
3354
3355 sz--;
3356 }
3357
3358 if (SUCCEED == (ret = is_uint64_n(str, sz, value)))
3359 *value *= factor;
3360
3361 return ret;
3362 }
3363
3364 /******************************************************************************
3365 * *
3366 * Function: str2double *
3367 * *
3368 * Purpose: convert string to double *
3369 * *
3370 * Parameters: str - string to convert *
3371 * *
3372 * Return value: converted double value *
3373 * *
3374 * Author: Alexei Vladishev *
3375 * *
3376 * Comments: the function automatically processes suffixes K, M, G, T and *
3377 * s, m, h, d, w *
3378 * *
3379 ******************************************************************************/
str2double(const char * str)3380 double str2double(const char *str)
3381 {
3382 size_t sz;
3383
3384 sz = strlen(str) - 1;
3385
3386 return atof(str) * suffix2factor(str[sz]);
3387 }
3388
3389 /******************************************************************************
3390 * *
3391 * Function: is_hostname_char *
3392 * *
3393 * Return value: SUCCEED - the char is allowed in the host name *
3394 * FAIL - otherwise *
3395 * *
3396 * Author: Alexander Vladishev *
3397 * *
3398 * Comments: in host name allowed characters: '0-9a-zA-Z. _-' *
3399 * !!! Don't forget to sync the code with PHP !!! *
3400 * *
3401 ******************************************************************************/
is_hostname_char(unsigned char c)3402 int is_hostname_char(unsigned char c)
3403 {
3404 if (0 != isalnum(c))
3405 return SUCCEED;
3406
3407 if (c == '.' || c == ' ' || c == '_' || c == '-')
3408 return SUCCEED;
3409
3410 return FAIL;
3411 }
3412
3413 /******************************************************************************
3414 * *
3415 * Function: is_key_char *
3416 * *
3417 * Return value: SUCCEED - the char is allowed in the item key *
3418 * FAIL - otherwise *
3419 * *
3420 * Author: Alexander Vladishev *
3421 * *
3422 * Comments: in key allowed characters: '0-9a-zA-Z._-' *
3423 * !!! Don't forget to sync the code with PHP !!! *
3424 * *
3425 ******************************************************************************/
is_key_char(unsigned char c)3426 int is_key_char(unsigned char c)
3427 {
3428 if (0 != isalnum(c))
3429 return SUCCEED;
3430
3431 if (c == '.' || c == '_' || c == '-')
3432 return SUCCEED;
3433
3434 return FAIL;
3435 }
3436
3437 /******************************************************************************
3438 * *
3439 * Function: is_function_char *
3440 * *
3441 * Return value: SUCCEED - the char is allowed in the trigger function *
3442 * FAIL - otherwise *
3443 * *
3444 * Author: Alexander Vladishev *
3445 * *
3446 * Comments: in trigger function allowed characters: 'a-z' *
3447 * !!! Don't forget to sync the code with PHP !!! *
3448 * *
3449 ******************************************************************************/
is_function_char(unsigned char c)3450 int is_function_char(unsigned char c)
3451 {
3452 if (0 != islower(c))
3453 return SUCCEED;
3454
3455 return FAIL;
3456 }
3457
3458 /******************************************************************************
3459 * *
3460 * Function: is_macro_char *
3461 * *
3462 * Return value: SUCCEED - the char is allowed in the macro name *
3463 * FAIL - otherwise *
3464 * *
3465 * Author: Alexander Vladishev *
3466 * *
3467 * Comments: allowed characters in macro names: '0-9A-Z._' *
3468 * !!! Don't forget to sync the code with PHP !!! *
3469 * *
3470 ******************************************************************************/
is_macro_char(unsigned char c)3471 int is_macro_char(unsigned char c)
3472 {
3473 if (0 != isupper(c))
3474 return SUCCEED;
3475
3476 if ('.' == c || '_' == c)
3477 return SUCCEED;
3478
3479 if (0 != isdigit(c))
3480 return SUCCEED;
3481
3482 return FAIL;
3483 }
3484
3485 /******************************************************************************
3486 * *
3487 * Function: is_discovery_macro *
3488 * *
3489 * Purpose: checks if the name is a valid discovery macro *
3490 * *
3491 * Return value: SUCCEED - the name is a valid discovery macro *
3492 * FAIL - otherwise *
3493 * *
3494 ******************************************************************************/
is_discovery_macro(const char * name)3495 int is_discovery_macro(const char *name)
3496 {
3497 if ('{' != *name++ || '#' != *name++)
3498 return FAIL;
3499
3500 do
3501 {
3502 if (SUCCEED != is_macro_char(*name++))
3503 return FAIL;
3504
3505 } while ('}' != *name);
3506
3507 if ('\0' != name[1])
3508 return FAIL;
3509
3510 return SUCCEED;
3511 }
3512
3513 /******************************************************************************
3514 * *
3515 * Function: is_time_function *
3516 * *
3517 * Return value: SUCCEED - given function is time-based *
3518 * FAIL - otherwise *
3519 * *
3520 * Author: Aleksandrs Saveljevs *
3521 * *
3522 ******************************************************************************/
is_time_function(const char * func)3523 int is_time_function(const char *func)
3524 {
3525 return str_in_list("nodata,date,dayofmonth,dayofweek,time,now", func, ',');
3526 }
3527
3528 /******************************************************************************
3529 * *
3530 * Function: make_hostname *
3531 * *
3532 * Purpose: replace all not-allowed hostname characters in the string *
3533 * *
3534 * Parameters: host - the target C-style string *
3535 * *
3536 * Author: Dmitry Borovikov *
3537 * *
3538 * Comments: the string must be null-terminated, otherwise not secure! *
3539 * *
3540 ******************************************************************************/
make_hostname(char * host)3541 void make_hostname(char *host)
3542 {
3543 char *c;
3544
3545 assert(host);
3546
3547 for (c = host; '\0' != *c; ++c)
3548 {
3549 if (FAIL == is_hostname_char(*c))
3550 *c = '_';
3551 }
3552 }
3553
3554 /******************************************************************************
3555 * *
3556 * Function: get_interface_type_by_item_type *
3557 * *
3558 * Purpose: *
3559 * *
3560 * Parameters: *
3561 * *
3562 * Return value: Interface type *
3563 * *
3564 * Author: Alexander Vladishev *
3565 * *
3566 * Comments: !!! Don't forget to sync the code with PHP !!! *
3567 * *
3568 ******************************************************************************/
get_interface_type_by_item_type(unsigned char type)3569 unsigned char get_interface_type_by_item_type(unsigned char type)
3570 {
3571 switch (type)
3572 {
3573 case ITEM_TYPE_ZABBIX:
3574 return INTERFACE_TYPE_AGENT;
3575 case ITEM_TYPE_SNMP:
3576 case ITEM_TYPE_SNMPTRAP:
3577 return INTERFACE_TYPE_SNMP;
3578 case ITEM_TYPE_IPMI:
3579 return INTERFACE_TYPE_IPMI;
3580 case ITEM_TYPE_JMX:
3581 return INTERFACE_TYPE_JMX;
3582 case ITEM_TYPE_SIMPLE:
3583 case ITEM_TYPE_EXTERNAL:
3584 case ITEM_TYPE_SSH:
3585 case ITEM_TYPE_TELNET:
3586 case ITEM_TYPE_HTTPAGENT:
3587 return INTERFACE_TYPE_ANY;
3588 default:
3589 return INTERFACE_TYPE_UNKNOWN;
3590 }
3591 }
3592
3593 /******************************************************************************
3594 * *
3595 * Function: calculate_sleeptime *
3596 * *
3597 * Purpose: calculate sleep time for Zabbix processes *
3598 * *
3599 * Parameters: nextcheck - [IN] next check or -1 (FAIL) if nothing to do *
3600 * max_sleeptime - [IN] maximum sleep time, in seconds *
3601 * *
3602 * Return value: sleep time, in seconds *
3603 * *
3604 * Author: Alexander Vladishev *
3605 * *
3606 ******************************************************************************/
calculate_sleeptime(int nextcheck,int max_sleeptime)3607 int calculate_sleeptime(int nextcheck, int max_sleeptime)
3608 {
3609 int sleeptime;
3610
3611 if (FAIL == nextcheck)
3612 return max_sleeptime;
3613
3614 sleeptime = nextcheck - (int)time(NULL);
3615
3616 if (sleeptime < 0)
3617 return 0;
3618
3619 if (sleeptime > max_sleeptime)
3620 return max_sleeptime;
3621
3622 return sleeptime;
3623 }
3624
3625 /******************************************************************************
3626 * *
3627 * Function: parse_serveractive_element *
3628 * *
3629 * Purpose: parse a ServerActive element like "IP<:port>" or "[IPv6]<:port>" *
3630 * *
3631 ******************************************************************************/
parse_serveractive_element(char * str,char ** host,unsigned short * port,unsigned short port_default)3632 int parse_serveractive_element(char *str, char **host, unsigned short *port, unsigned short port_default)
3633 {
3634 #ifdef HAVE_IPV6
3635 char *r1 = NULL;
3636 #endif
3637 char *r2 = NULL;
3638 int res = FAIL;
3639
3640 *port = port_default;
3641
3642 #ifdef HAVE_IPV6
3643 if ('[' == *str)
3644 {
3645 str++;
3646
3647 if (NULL == (r1 = strchr(str, ']')))
3648 goto fail;
3649
3650 if (':' != r1[1] && '\0' != r1[1])
3651 goto fail;
3652
3653 if (':' == r1[1] && SUCCEED != is_ushort(r1 + 2, port))
3654 goto fail;
3655
3656 *r1 = '\0';
3657
3658 if (SUCCEED != is_ip6(str))
3659 goto fail;
3660
3661 *host = zbx_strdup(*host, str);
3662 }
3663 else if (SUCCEED == is_ip6(str))
3664 {
3665 *host = zbx_strdup(*host, str);
3666 }
3667 else
3668 {
3669 #endif
3670 if (NULL != (r2 = strchr(str, ':')))
3671 {
3672 if (SUCCEED != is_ushort(r2 + 1, port))
3673 goto fail;
3674
3675 *r2 = '\0';
3676 }
3677
3678 *host = zbx_strdup(NULL, str);
3679 #ifdef HAVE_IPV6
3680 }
3681 #endif
3682
3683 res = SUCCEED;
3684 fail:
3685 #ifdef HAVE_IPV6
3686 if (NULL != r1)
3687 *r1 = ']';
3688 #endif
3689 if (NULL != r2)
3690 *r2 = ':';
3691
3692 return res;
3693 }
3694
zbx_alarm_flag_set(void)3695 void zbx_alarm_flag_set(void)
3696 {
3697 zbx_timed_out = 1;
3698 }
3699
zbx_alarm_flag_clear(void)3700 void zbx_alarm_flag_clear(void)
3701 {
3702 zbx_timed_out = 0;
3703 }
3704
3705 #if !defined(_WINDOWS) && !defined(__MINGW32__)
zbx_alarm_on(unsigned int seconds)3706 unsigned int zbx_alarm_on(unsigned int seconds)
3707 {
3708 zbx_alarm_flag_clear();
3709
3710 return alarm(seconds);
3711 }
3712
zbx_alarm_off(void)3713 unsigned int zbx_alarm_off(void)
3714 {
3715 unsigned int ret;
3716
3717 ret = alarm(0);
3718 zbx_alarm_flag_clear();
3719 return ret;
3720 }
3721 #endif
3722
zbx_alarm_timed_out(void)3723 int zbx_alarm_timed_out(void)
3724 {
3725 return (0 == zbx_timed_out ? FAIL : SUCCEED);
3726 }
3727
3728 /******************************************************************************
3729 * *
3730 * Function: zbx_create_token *
3731 * *
3732 * Purpose: creates semi-unique token based on the seed and current timestamp *
3733 * *
3734 * Parameters: seed - [IN] the seed *
3735 * *
3736 * Return value: Hexadecimal token string, must be freed by caller *
3737 * *
3738 * Comments: if you change token creation algorithm do not forget to adjust *
3739 * ZBX_DATA_SESSION_TOKEN_SIZE definition *
3740 * *
3741 ******************************************************************************/
zbx_create_token(zbx_uint64_t seed)3742 char *zbx_create_token(zbx_uint64_t seed)
3743 {
3744 const char *hex = "0123456789abcdef";
3745 zbx_timespec_t ts;
3746 md5_state_t state;
3747 md5_byte_t hash[MD5_DIGEST_SIZE];
3748 int i;
3749 char *token, *ptr;
3750
3751 ptr = token = (char *)zbx_malloc(NULL, ZBX_DATA_SESSION_TOKEN_SIZE + 1);
3752
3753 zbx_timespec(&ts);
3754
3755 zbx_md5_init(&state);
3756 zbx_md5_append(&state, (const md5_byte_t *)&seed, (int)sizeof(seed));
3757 zbx_md5_append(&state, (const md5_byte_t *)&ts, (int)sizeof(ts));
3758 zbx_md5_finish(&state, hash);
3759
3760 for (i = 0; i < MD5_DIGEST_SIZE; i++)
3761 {
3762 *ptr++ = hex[hash[i] >> 4];
3763 *ptr++ = hex[hash[i] & 15];
3764 }
3765
3766 *ptr = '\0';
3767
3768 return token;
3769 }
3770
3771
3772 #if !defined(_WINDOWS) && defined(HAVE_RESOLV_H)
3773 /******************************************************************************
3774 * *
3775 * Function: update_resolver_conf *
3776 * *
3777 * Purpose: react to "/etc/resolv.conf" update *
3778 * *
3779 * Comments: it is intended to call this function in the end of each process *
3780 * main loop. The purpose of calling it at the end (instead of the *
3781 * beginning of main loop) is to let the first initialization of *
3782 * libc resolver proceed internally. *
3783 * *
3784 ******************************************************************************/
update_resolver_conf(void)3785 static void update_resolver_conf(void)
3786 {
3787 #define ZBX_RESOLV_CONF_FILE "/etc/resolv.conf"
3788
3789 static time_t mtime = 0;
3790 zbx_stat_t buf;
3791
3792 if (0 == zbx_stat(ZBX_RESOLV_CONF_FILE, &buf) && mtime != buf.st_mtime)
3793 {
3794 mtime = buf.st_mtime;
3795
3796 if (0 != res_init())
3797 zabbix_log(LOG_LEVEL_WARNING, "update_resolver_conf(): res_init() failed");
3798 }
3799
3800 #undef ZBX_RESOLV_CONF_FILE
3801 }
3802 #endif
3803
3804 /******************************************************************************
3805 * *
3806 * Function: zbx_update_env *
3807 * *
3808 * Purpose: throttling of update "/etc/resolv.conf" and "stdio" to the new *
3809 * log file after rotation *
3810 * *
3811 * Parameters: time_now - [IN] the time for compare in seconds *
3812 * *
3813 ******************************************************************************/
zbx_update_env(double time_now)3814 void zbx_update_env(double time_now)
3815 {
3816 static double time_update = 0;
3817
3818 /* handle /etc/resolv.conf update and log rotate less often than once a second */
3819 if (1.0 < time_now - time_update)
3820 {
3821 time_update = time_now;
3822 zbx_handle_log();
3823 #if !defined(_WINDOWS) && defined(HAVE_RESOLV_H)
3824 update_resolver_conf();
3825 #endif
3826 }
3827 }
3828
3829 /******************************************************************************
3830 * *
3831 * Function: zbx_dc_get_agent_item_nextcheck *
3832 * *
3833 * Purpose: calculate item nextcheck for Zabbix agent type items *
3834 * *
3835 ******************************************************************************/
zbx_get_agent_item_nextcheck(zbx_uint64_t itemid,const char * delay,unsigned char state,int now,int refresh_unsupported,int * nextcheck,char ** error)3836 int zbx_get_agent_item_nextcheck(zbx_uint64_t itemid, const char *delay, unsigned char state, int now,
3837 int refresh_unsupported, int *nextcheck, char **error)
3838 {
3839 if (ITEM_STATE_NORMAL == state)
3840 {
3841 int simple_interval;
3842 zbx_custom_interval_t *custom_intervals;
3843
3844 if (SUCCEED != zbx_interval_preproc(delay, &simple_interval, &custom_intervals, error))
3845 {
3846 *nextcheck = ZBX_JAN_2038;
3847 return FAIL;
3848 }
3849
3850 *nextcheck = calculate_item_nextcheck(itemid, ITEM_TYPE_ZABBIX, simple_interval, custom_intervals, now);
3851 zbx_custom_interval_free(custom_intervals);
3852 }
3853 else /* for items notsupported for other reasons use refresh_unsupported interval */
3854 {
3855 *nextcheck = calculate_item_nextcheck(itemid, ITEM_TYPE_ZABBIX, refresh_unsupported, NULL, now);
3856 }
3857
3858 return SUCCEED;
3859 }
3860
3861 /******************************************************************************
3862 * *
3863 * Function: zbx_md5buf2str *
3864 * *
3865 * Purpose: get a textual representation of md5 sum *
3866 * *
3867 * Parameters: *
3868 * md5 - [IN] buffer with md5 sum *
3869 * str - [OUT] preallocated string with a text representation of MD5 *
3870 * sum. String size must be at least *
3871 * ZBX_MD5_PRINT_BUF_LEN bytes. *
3872 * *
3873 ******************************************************************************/
zbx_md5buf2str(const md5_byte_t * md5,char * str)3874 void zbx_md5buf2str(const md5_byte_t *md5, char *str)
3875 {
3876 const char *hex = "0123456789abcdef";
3877 char *p = str;
3878 int i;
3879
3880 for (i = 0; i < MD5_DIGEST_SIZE; i++)
3881 {
3882 *p++ = hex[md5[i] >> 4];
3883 *p++ = hex[md5[i] & 15];
3884 }
3885
3886 *p = '\0';
3887 }
3888
3889 /******************************************************************************
3890 * *
3891 * Function: zbx_hex2bin *
3892 * *
3893 * Purpose: *
3894 * convert ASCII hex digit string to a binary representation (byte *
3895 * string) *
3896 * *
3897 * Parameters: *
3898 * p_hex - [IN] null-terminated input string *
3899 * buf - [OUT] output buffer *
3900 * buf_len - [IN] output buffer size *
3901 * *
3902 * Return value: *
3903 * Number of bytes written into 'buf' on successful conversion. *
3904 * -1 - an error occurred. *
3905 * *
3906 * Comments: *
3907 * In case of error incomplete useless data may be written into 'buf'. *
3908 * *
3909 ******************************************************************************/
zbx_hex2bin(const unsigned char * p_hex,unsigned char * buf,int buf_len)3910 int zbx_hex2bin(const unsigned char *p_hex, unsigned char *buf, int buf_len)
3911 {
3912 unsigned char *q = buf;
3913 int len = 0;
3914
3915 while ('\0' != *p_hex)
3916 {
3917 if (0 != isxdigit(*p_hex) && 0 != isxdigit(*(p_hex + 1)) && buf_len > len)
3918 {
3919 unsigned char hi = *p_hex & 0x0f;
3920 unsigned char lo;
3921
3922 if ('9' < *p_hex++)
3923 hi = (unsigned char)(hi + 9u);
3924
3925 lo = *p_hex & 0x0f;
3926
3927 if ('9' < *p_hex++)
3928 lo = (unsigned char)(lo + 9u);
3929
3930 *q++ = (unsigned char)(hi << 4 | lo);
3931 len++;
3932 }
3933 else
3934 return -1;
3935 }
3936
3937 return len;
3938 }
3939