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