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