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 "mutexs.h"
23 #include "threads.h"
24 #include "cfg.h"
25 #ifdef _WINDOWS
26 #	include "messages.h"
27 #	include "service.h"
28 #	include "sysinfo.h"
29 static HANDLE		system_log_handle = INVALID_HANDLE_VALUE;
30 #endif
31 
32 static char		log_filename[MAX_STRING_LEN];
33 static int		log_type = LOG_TYPE_UNDEFINED;
34 static zbx_mutex_t	log_access = ZBX_MUTEX_NULL;
35 int			zbx_log_level = LOG_LEVEL_WARNING;
36 
37 #ifdef _WINDOWS
38 #	define LOCK_LOG		zbx_mutex_lock(log_access)
39 #	define UNLOCK_LOG	zbx_mutex_unlock(log_access)
40 #else
41 #	define LOCK_LOG		lock_log()
42 #	define UNLOCK_LOG	unlock_log()
43 #endif
44 
45 #define ZBX_MESSAGE_BUF_SIZE	1024
46 
47 #ifdef _WINDOWS
48 #	define STDIN_FILENO	_fileno(stdin)
49 #	define STDOUT_FILENO	_fileno(stdout)
50 #	define STDERR_FILENO	_fileno(stderr)
51 
52 #	define ZBX_DEV_NULL	"NUL"
53 
54 #	define dup2(fd1, fd2)	_dup2(fd1, fd2)
55 #else
56 #	define ZBX_DEV_NULL	"/dev/null"
57 #endif
58 
59 #ifndef _WINDOWS
zabbix_get_log_level_string(void)60 const char	*zabbix_get_log_level_string(void)
61 {
62 	switch (zbx_log_level)
63 	{
64 		case LOG_LEVEL_EMPTY:
65 			return "0 (none)";
66 		case LOG_LEVEL_CRIT:
67 			return "1 (critical)";
68 		case LOG_LEVEL_ERR:
69 			return "2 (error)";
70 		case LOG_LEVEL_WARNING:
71 			return "3 (warning)";
72 		case LOG_LEVEL_DEBUG:
73 			return "4 (debug)";
74 		case LOG_LEVEL_TRACE:
75 			return "5 (trace)";
76 	}
77 
78 	THIS_SHOULD_NEVER_HAPPEN;
79 	exit(EXIT_FAILURE);
80 }
81 
zabbix_increase_log_level(void)82 int	zabbix_increase_log_level(void)
83 {
84 	if (LOG_LEVEL_TRACE == zbx_log_level)
85 		return FAIL;
86 
87 	zbx_log_level = zbx_log_level + 1;
88 
89 	return SUCCEED;
90 }
91 
zabbix_decrease_log_level(void)92 int	zabbix_decrease_log_level(void)
93 {
94 	if (LOG_LEVEL_EMPTY == zbx_log_level)
95 		return FAIL;
96 
97 	zbx_log_level = zbx_log_level - 1;
98 
99 	return SUCCEED;
100 }
101 #endif
102 
zbx_redirect_stdio(const char * filename)103 int	zbx_redirect_stdio(const char *filename)
104 {
105 	const char	default_file[] = ZBX_DEV_NULL;
106 	int		open_flags = O_WRONLY, fd;
107 
108 	if (NULL != filename && '\0' != *filename)
109 		open_flags |= O_CREAT | O_APPEND;
110 	else
111 		filename = default_file;
112 
113 	if (-1 == (fd = open(filename, open_flags, 0666)))
114 	{
115 		zbx_error("cannot open \"%s\": %s", filename, zbx_strerror(errno));
116 		return FAIL;
117 	}
118 
119 	fflush(stdout);
120 	if (-1 == dup2(fd, STDOUT_FILENO))
121 		zbx_error("cannot redirect stdout to \"%s\": %s", filename, zbx_strerror(errno));
122 
123 	fflush(stderr);
124 	if (-1 == dup2(fd, STDERR_FILENO))
125 		zbx_error("cannot redirect stderr to \"%s\": %s", filename, zbx_strerror(errno));
126 
127 	close(fd);
128 
129 	if (-1 == (fd = open(default_file, O_RDONLY)))
130 	{
131 		zbx_error("cannot open \"%s\": %s", default_file, zbx_strerror(errno));
132 		return FAIL;
133 	}
134 
135 	if (-1 == dup2(fd, STDIN_FILENO))
136 		zbx_error("cannot redirect stdin to \"%s\": %s", default_file, zbx_strerror(errno));
137 
138 	close(fd);
139 
140 	return SUCCEED;
141 }
142 
rotate_log(const char * filename)143 static void	rotate_log(const char *filename)
144 {
145 	zbx_stat_t		buf;
146 	zbx_uint64_t		new_size;
147 	static zbx_uint64_t	old_size = ZBX_MAX_UINT64;	/* redirect stdout and stderr */
148 #if !defined(_WINDOWS)
149 	static zbx_uint64_t	st_ino, st_dev;
150 #endif
151 
152 	if (0 != zbx_stat(filename, &buf))
153 	{
154 		zbx_redirect_stdio(filename);
155 		return;
156 	}
157 
158 	new_size = buf.st_size;
159 
160 	if (0 != CONFIG_LOG_FILE_SIZE && (zbx_uint64_t)CONFIG_LOG_FILE_SIZE * ZBX_MEBIBYTE < new_size)
161 	{
162 		char	filename_old[MAX_STRING_LEN];
163 
164 		strscpy(filename_old, filename);
165 		zbx_strlcat(filename_old, ".old", MAX_STRING_LEN);
166 		remove(filename_old);
167 #ifdef _WINDOWS
168 		zbx_redirect_stdio(NULL);
169 #endif
170 		if (0 != rename(filename, filename_old))
171 		{
172 			FILE	*log_file = NULL;
173 
174 			if (NULL != (log_file = fopen(filename, "w")))
175 			{
176 				long		milliseconds;
177 				struct tm	tm;
178 
179 				zbx_get_time(&tm, &milliseconds, NULL);
180 
181 				fprintf(log_file, "%6li:%.4d%.2d%.2d:%.2d%.2d%.2d.%03ld"
182 						" cannot rename log file \"%s\" to \"%s\": %s\n",
183 						zbx_get_thread_id(),
184 						tm.tm_year + 1900,
185 						tm.tm_mon + 1,
186 						tm.tm_mday,
187 						tm.tm_hour,
188 						tm.tm_min,
189 						tm.tm_sec,
190 						milliseconds,
191 						filename,
192 						filename_old,
193 						zbx_strerror(errno));
194 
195 				fprintf(log_file, "%6li:%.4d%.2d%.2d:%.2d%.2d%.2d.%03ld"
196 						" Logfile \"%s\" size reached configured limit"
197 						" LogFileSize but moving it to \"%s\" failed. The logfile"
198 						" was truncated.\n",
199 						zbx_get_thread_id(),
200 						tm.tm_year + 1900,
201 						tm.tm_mon + 1,
202 						tm.tm_mday,
203 						tm.tm_hour,
204 						tm.tm_min,
205 						tm.tm_sec,
206 						milliseconds,
207 						filename,
208 						filename_old);
209 
210 				zbx_fclose(log_file);
211 
212 				new_size = 0;
213 			}
214 		}
215 		else
216 			new_size = 0;
217 	}
218 
219 	if (old_size > new_size)
220 		zbx_redirect_stdio(filename);
221 #if !defined(_WINDOWS)
222 	else if (st_ino != buf.st_ino || st_dev != buf.st_dev)
223 	{
224 		st_ino = buf.st_ino;
225 		st_dev = buf.st_dev;
226 		zbx_redirect_stdio(filename);
227 	}
228 #endif
229 
230 	old_size = new_size;
231 }
232 
233 #ifndef _WINDOWS
234 static sigset_t	orig_mask;
235 
lock_log(void)236 static void	lock_log(void)
237 {
238 	sigset_t	mask;
239 
240 	/* block signals to prevent deadlock on log file mutex when signal handler attempts to lock log */
241 	sigemptyset(&mask);
242 	sigaddset(&mask, SIGUSR1);
243 	sigaddset(&mask, SIGUSR2);
244 	sigaddset(&mask, SIGTERM);
245 	sigaddset(&mask, SIGINT);
246 	sigaddset(&mask, SIGQUIT);
247 	sigaddset(&mask, SIGHUP);
248 
249 	if (0 > sigprocmask(SIG_BLOCK, &mask, &orig_mask))
250 		zbx_error("cannot set sigprocmask to block the user signal");
251 
252 	zbx_mutex_lock(log_access);
253 }
254 
unlock_log(void)255 static void	unlock_log(void)
256 {
257 	zbx_mutex_unlock(log_access);
258 
259 	if (0 > sigprocmask(SIG_SETMASK, &orig_mask, NULL))
260 		zbx_error("cannot restore sigprocmask");
261 }
262 #else
lock_log(void)263 static void	lock_log(void)
264 {
265 #ifdef ZABBIX_AGENT
266 	if (0 == (ZBX_MUTEX_LOGGING_DENIED & get_thread_global_mutex_flag()))
267 #endif
268 		LOCK_LOG;
269 }
270 
unlock_log(void)271 static void	unlock_log(void)
272 {
273 #ifdef ZABBIX_AGENT
274 	if (0 == (ZBX_MUTEX_LOGGING_DENIED & get_thread_global_mutex_flag()))
275 #endif
276 		UNLOCK_LOG;
277 }
278 #endif
279 
zbx_handle_log(void)280 void	zbx_handle_log(void)
281 {
282 	if (LOG_TYPE_FILE != log_type)
283 		return;
284 
285 	LOCK_LOG;
286 
287 	rotate_log(log_filename);
288 
289 	UNLOCK_LOG;
290 }
291 
zabbix_open_log(int type,int level,const char * filename,char ** error)292 int	zabbix_open_log(int type, int level, const char *filename, char **error)
293 {
294 	log_type = type;
295 	zbx_log_level = level;
296 
297 	if (LOG_TYPE_SYSTEM == type)
298 	{
299 #ifdef _WINDOWS
300 		wchar_t	*wevent_source;
301 
302 		wevent_source = zbx_utf8_to_unicode(ZABBIX_EVENT_SOURCE);
303 		system_log_handle = RegisterEventSource(NULL, wevent_source);
304 		zbx_free(wevent_source);
305 #else
306 		openlog(syslog_app_name, LOG_PID, LOG_DAEMON);
307 #endif
308 	}
309 	else if (LOG_TYPE_FILE == type)
310 	{
311 		FILE	*log_file = NULL;
312 
313 		if (MAX_STRING_LEN <= strlen(filename))
314 		{
315 			*error = zbx_strdup(*error, "too long path for logfile");
316 			return FAIL;
317 		}
318 
319 		if (SUCCEED != zbx_mutex_create(&log_access, ZBX_MUTEX_LOG, error))
320 			return FAIL;
321 
322 		if (NULL == (log_file = fopen(filename, "a+")))
323 		{
324 			*error = zbx_dsprintf(*error, "unable to open log file [%s]: %s", filename, zbx_strerror(errno));
325 			return FAIL;
326 		}
327 
328 		strscpy(log_filename, filename);
329 		zbx_fclose(log_file);
330 	}
331 	else if (LOG_TYPE_CONSOLE == type || LOG_TYPE_UNDEFINED == type)
332 	{
333 		if (SUCCEED != zbx_mutex_create(&log_access, ZBX_MUTEX_LOG, error))
334 		{
335 			*error = zbx_strdup(*error, "unable to create mutex for standard output");
336 			return FAIL;
337 		}
338 
339 		fflush(stderr);
340 		if (-1 == dup2(STDOUT_FILENO, STDERR_FILENO))
341 			zbx_error("cannot redirect stderr to stdout: %s", zbx_strerror(errno));
342 	}
343 	else
344 	{
345 		*error = zbx_strdup(*error, "unknown log type");
346 		return FAIL;
347 	}
348 
349 	return SUCCEED;
350 }
351 
zabbix_close_log(void)352 void	zabbix_close_log(void)
353 {
354 	if (LOG_TYPE_SYSTEM == log_type)
355 	{
356 #ifdef _WINDOWS
357 		if (NULL != system_log_handle)
358 			DeregisterEventSource(system_log_handle);
359 #else
360 		closelog();
361 #endif
362 	}
363 	else if (LOG_TYPE_FILE == log_type || LOG_TYPE_CONSOLE == log_type || LOG_TYPE_UNDEFINED == log_type)
364 	{
365 		zbx_mutex_destroy(&log_access);
366 	}
367 }
368 
__zbx_zabbix_log(int level,const char * fmt,...)369 void	__zbx_zabbix_log(int level, const char *fmt, ...)
370 {
371 	char		message[MAX_BUFFER_LEN];
372 	va_list		args;
373 #ifdef _WINDOWS
374 	WORD		wType;
375 	wchar_t		thread_id[20], *strings[2];
376 #endif
377 
378 #ifndef ZBX_ZABBIX_LOG_CHECK
379 	if (SUCCEED != ZBX_CHECK_LOG_LEVEL(level))
380 		return;
381 #endif
382 	if (LOG_TYPE_FILE == log_type)
383 	{
384 		FILE	*log_file;
385 
386 		LOCK_LOG;
387 
388 		if (0 != CONFIG_LOG_FILE_SIZE)
389 			rotate_log(log_filename);
390 
391 		if (NULL != (log_file = fopen(log_filename, "a+")))
392 		{
393 			long		milliseconds;
394 			struct tm	tm;
395 
396 			zbx_get_time(&tm, &milliseconds, NULL);
397 
398 			fprintf(log_file,
399 					"%6li:%.4d%.2d%.2d:%.2d%.2d%.2d.%03ld ",
400 					zbx_get_thread_id(),
401 					tm.tm_year + 1900,
402 					tm.tm_mon + 1,
403 					tm.tm_mday,
404 					tm.tm_hour,
405 					tm.tm_min,
406 					tm.tm_sec,
407 					milliseconds
408 					);
409 
410 			va_start(args, fmt);
411 			vfprintf(log_file, fmt, args);
412 			va_end(args);
413 
414 			fprintf(log_file, "\n");
415 
416 			zbx_fclose(log_file);
417 		}
418 		else
419 		{
420 			zbx_error("failed to open log file: %s", zbx_strerror(errno));
421 
422 			va_start(args, fmt);
423 			zbx_vsnprintf(message, sizeof(message), fmt, args);
424 			va_end(args);
425 
426 			zbx_error("failed to write [%s] into log file", message);
427 		}
428 
429 		UNLOCK_LOG;
430 
431 		return;
432 	}
433 
434 	if (LOG_TYPE_CONSOLE == log_type)
435 	{
436 		long		milliseconds;
437 		struct tm	tm;
438 
439 		LOCK_LOG;
440 
441 		zbx_get_time(&tm, &milliseconds, NULL);
442 
443 		fprintf(stdout,
444 				"%6li:%.4d%.2d%.2d:%.2d%.2d%.2d.%03ld ",
445 				zbx_get_thread_id(),
446 				tm.tm_year + 1900,
447 				tm.tm_mon + 1,
448 				tm.tm_mday,
449 				tm.tm_hour,
450 				tm.tm_min,
451 				tm.tm_sec,
452 				milliseconds
453 				);
454 
455 		va_start(args, fmt);
456 		vfprintf(stdout, fmt, args);
457 		va_end(args);
458 
459 		fprintf(stdout, "\n");
460 
461 		fflush(stdout);
462 
463 		UNLOCK_LOG;
464 
465 		return;
466 	}
467 
468 	va_start(args, fmt);
469 	zbx_vsnprintf(message, sizeof(message), fmt, args);
470 	va_end(args);
471 
472 	if (LOG_TYPE_SYSTEM == log_type)
473 	{
474 #ifdef _WINDOWS
475 		switch (level)
476 		{
477 			case LOG_LEVEL_CRIT:
478 			case LOG_LEVEL_ERR:
479 				wType = EVENTLOG_ERROR_TYPE;
480 				break;
481 			case LOG_LEVEL_WARNING:
482 				wType = EVENTLOG_WARNING_TYPE;
483 				break;
484 			default:
485 				wType = EVENTLOG_INFORMATION_TYPE;
486 				break;
487 		}
488 
489 		StringCchPrintf(thread_id, ARRSIZE(thread_id), TEXT("[%li]: "), zbx_get_thread_id());
490 		strings[0] = thread_id;
491 		strings[1] = zbx_utf8_to_unicode(message);
492 
493 		ReportEvent(
494 			system_log_handle,
495 			wType,
496 			0,
497 			MSG_ZABBIX_MESSAGE,
498 			NULL,
499 			sizeof(strings) / sizeof(*strings),
500 			0,
501 			strings,
502 			NULL);
503 
504 		zbx_free(strings[1]);
505 
506 #else	/* not _WINDOWS */
507 
508 		/* for nice printing into syslog */
509 		switch (level)
510 		{
511 			case LOG_LEVEL_CRIT:
512 				syslog(LOG_CRIT, "%s", message);
513 				break;
514 			case LOG_LEVEL_ERR:
515 				syslog(LOG_ERR, "%s", message);
516 				break;
517 			case LOG_LEVEL_WARNING:
518 				syslog(LOG_WARNING, "%s", message);
519 				break;
520 			case LOG_LEVEL_DEBUG:
521 			case LOG_LEVEL_TRACE:
522 				syslog(LOG_DEBUG, "%s", message);
523 				break;
524 			case LOG_LEVEL_INFORMATION:
525 				syslog(LOG_INFO, "%s", message);
526 				break;
527 			default:
528 				/* LOG_LEVEL_EMPTY - print nothing */
529 				break;
530 		}
531 
532 #endif	/* _WINDOWS */
533 	}	/* LOG_TYPE_SYSLOG */
534 	else	/* LOG_TYPE_UNDEFINED == log_type */
535 	{
536 		LOCK_LOG;
537 
538 		switch (level)
539 		{
540 			case LOG_LEVEL_CRIT:
541 				zbx_error("ERROR: %s", message);
542 				break;
543 			case LOG_LEVEL_ERR:
544 				zbx_error("Error: %s", message);
545 				break;
546 			case LOG_LEVEL_WARNING:
547 				zbx_error("Warning: %s", message);
548 				break;
549 			case LOG_LEVEL_DEBUG:
550 				zbx_error("DEBUG: %s", message);
551 				break;
552 			case LOG_LEVEL_TRACE:
553 				zbx_error("TRACE: %s", message);
554 				break;
555 			default:
556 				zbx_error("%s", message);
557 				break;
558 		}
559 
560 		UNLOCK_LOG;
561 	}
562 }
563 
zbx_get_log_type(const char * logtype)564 int	zbx_get_log_type(const char *logtype)
565 {
566 	const char	*logtypes[] = {ZBX_OPTION_LOGTYPE_SYSTEM, ZBX_OPTION_LOGTYPE_FILE, ZBX_OPTION_LOGTYPE_CONSOLE};
567 	int		i;
568 
569 	for (i = 0; i < (int)ARRSIZE(logtypes); i++)
570 	{
571 		if (0 == strcmp(logtype, logtypes[i]))
572 			return i + 1;
573 	}
574 
575 	return LOG_TYPE_UNDEFINED;
576 }
577 
zbx_validate_log_parameters(ZBX_TASK_EX * task)578 int	zbx_validate_log_parameters(ZBX_TASK_EX *task)
579 {
580 	if (LOG_TYPE_UNDEFINED == CONFIG_LOG_TYPE)
581 	{
582 		zabbix_log(LOG_LEVEL_CRIT, "invalid \"LogType\" configuration parameter: '%s'", CONFIG_LOG_TYPE_STR);
583 		return FAIL;
584 	}
585 
586 	if (LOG_TYPE_CONSOLE == CONFIG_LOG_TYPE && 0 == (task->flags & ZBX_TASK_FLAG_FOREGROUND) &&
587 			ZBX_TASK_START == task->task)
588 	{
589 		zabbix_log(LOG_LEVEL_CRIT, "\"LogType\" \"console\" parameter can only be used with the"
590 				" -f (--foreground) command line option");
591 		return FAIL;
592 	}
593 
594 	if (LOG_TYPE_FILE == CONFIG_LOG_TYPE && (NULL == CONFIG_LOG_FILE || '\0' == *CONFIG_LOG_FILE))
595 	{
596 		zabbix_log(LOG_LEVEL_CRIT, "\"LogType\" \"file\" parameter requires \"LogFile\" parameter to be set");
597 		return FAIL;
598 	}
599 
600 	return SUCCEED;
601 }
602 
603 /******************************************************************************
604  *                                                                            *
605  * Comments: replace strerror to print also the error number                  *
606  *                                                                            *
607  ******************************************************************************/
zbx_strerror(int errnum)608 char	*zbx_strerror(int errnum)
609 {
610 	/* !!! Attention: static !!! Not thread-safe for Win32 */
611 	static char	utf8_string[ZBX_MESSAGE_BUF_SIZE];
612 
613 	zbx_snprintf(utf8_string, sizeof(utf8_string), "[%d] %s", errnum, strerror(errnum));
614 
615 	return utf8_string;
616 }
617 
strerror_from_system(unsigned long error)618 char	*strerror_from_system(unsigned long error)
619 {
620 #ifdef _WINDOWS
621 	size_t		offset = 0;
622 	wchar_t		wide_string[ZBX_MESSAGE_BUF_SIZE];
623 	/* !!! Attention: static !!! Not thread-safe for Win32 */
624 	static char	utf8_string[ZBX_MESSAGE_BUF_SIZE];
625 
626 	offset += zbx_snprintf(utf8_string, sizeof(utf8_string), "[0x%08lX] ", error);
627 
628 	/* we don't know the inserts so we pass NULL and enable appropriate flag */
629 	if (0 == FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error,
630 			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), wide_string, ZBX_MESSAGE_BUF_SIZE, NULL))
631 	{
632 		zbx_snprintf(utf8_string + offset, sizeof(utf8_string) - offset,
633 				"unable to find message text [0x%08lX]", GetLastError());
634 
635 		return utf8_string;
636 	}
637 
638 	zbx_unicode_to_utf8_static(wide_string, utf8_string + offset, (int)(sizeof(utf8_string) - offset));
639 
640 	zbx_rtrim(utf8_string, "\r\n ");
641 
642 	return utf8_string;
643 #else	/* not _WINDOWS */
644 	ZBX_UNUSED(error);
645 
646 	return zbx_strerror(errno);
647 #endif	/* _WINDOWS */
648 }
649 
650 #ifdef _WINDOWS
strerror_from_module(unsigned long error,const wchar_t * module)651 char	*strerror_from_module(unsigned long error, const wchar_t *module)
652 {
653 	size_t		offset = 0;
654 	wchar_t		wide_string[ZBX_MESSAGE_BUF_SIZE];
655 	HMODULE		hmodule;
656 	/* !!! Attention: static !!! not thread-safe for Win32 */
657 	static char	utf8_string[ZBX_MESSAGE_BUF_SIZE];
658 
659 	*utf8_string = '\0';
660 	hmodule = GetModuleHandle(module);
661 
662 	offset += zbx_snprintf(utf8_string, sizeof(utf8_string), "[0x%08lX] ", error);
663 
664 	/* we don't know the inserts so we pass NULL and enable appropriate flag */
665 	if (0 == FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, hmodule, error,
666 			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), wide_string, sizeof(wide_string), NULL))
667 	{
668 		zbx_snprintf(utf8_string + offset, sizeof(utf8_string) - offset,
669 				"unable to find message text: %s", strerror_from_system(GetLastError()));
670 
671 		return utf8_string;
672 	}
673 
674 	zbx_unicode_to_utf8_static(wide_string, utf8_string + offset, (int)(sizeof(utf8_string) - offset));
675 
676 	zbx_rtrim(utf8_string, "\r\n ");
677 
678 	return utf8_string;
679 }
680 #endif	/* _WINDOWS */
681