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 "threads.h"
22 #include "module.h"
23
24 #include "zbxcrypto.h"
25
26 #ifdef HAVE_ICONV
27 # include <iconv.h>
28 #endif
29
30 static const char copyright_message[] =
31 "Copyright (C) 2021 Zabbix SIA\n"
32 "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n"
33 "This is free software: you are free to change and redistribute it according to\n"
34 "the license. There is NO WARRANTY, to the extent permitted by law.";
35
36 static const char help_message_footer[] =
37 "Report bugs to: <https://support.zabbix.com>\n"
38 "Zabbix home page: <http://www.zabbix.com>\n"
39 "Documentation: <https://www.zabbix.com/documentation>";
40
41 /******************************************************************************
42 * *
43 * Function: version *
44 * *
45 * Purpose: print version and compilation time of application on stdout *
46 * by application request with parameter '-V' *
47 * *
48 * Author: Eugene Grigorjev *
49 * *
50 * Comments: title_message - is global variable which must be initialized *
51 * in each zabbix application *
52 * *
53 ******************************************************************************/
version(void)54 void version(void)
55 {
56 printf("%s (Zabbix) %s\n", title_message, ZABBIX_VERSION);
57 printf("Revision %s %s, compilation time: %s %s\n\n", ZABBIX_REVISION, ZABBIX_REVDATE, __DATE__, __TIME__);
58 puts(copyright_message);
59 #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
60 printf("\n");
61 zbx_tls_version();
62 #endif
63 }
64
65 /******************************************************************************
66 * *
67 * Function: usage *
68 * *
69 * Purpose: print application parameters on stdout with layout suitable for *
70 * 80-column terminal *
71 * *
72 * Author: Eugene Grigorjev *
73 * *
74 * Comments: usage_message - is global variable which must be initialized *
75 * in each zabbix application *
76 * *
77 ******************************************************************************/
usage(void)78 void usage(void)
79 {
80 #define ZBX_MAXCOL 79
81 #define ZBX_SPACE1 " " /* left margin for the first line */
82 #define ZBX_SPACE2 " " /* left margin for subsequent lines */
83 const char **p = usage_message;
84
85 if (NULL != *p)
86 printf("usage:\n");
87
88 while (NULL != *p)
89 {
90 size_t pos;
91
92 printf("%s%s", ZBX_SPACE1, progname);
93 pos = ZBX_CONST_STRLEN(ZBX_SPACE1) + strlen(progname);
94
95 while (NULL != *p)
96 {
97 size_t len;
98
99 len = strlen(*p);
100
101 if (ZBX_MAXCOL > pos + len)
102 {
103 pos += len + 1;
104 printf(" %s", *p);
105 }
106 else
107 {
108 pos = ZBX_CONST_STRLEN(ZBX_SPACE2) + len + 1;
109 printf("\n%s %s", ZBX_SPACE2, *p);
110 }
111
112 p++;
113 }
114
115 printf("\n");
116 p++;
117 }
118 #undef ZBX_MAXCOL
119 #undef ZBX_SPACE1
120 #undef ZBX_SPACE2
121 }
122
123 /******************************************************************************
124 * *
125 * Function: help *
126 * *
127 * Purpose: print help of application parameters on stdout by application *
128 * request with parameter '-h' *
129 * *
130 * Author: Eugene Grigorjev *
131 * *
132 * Comments: help_message - is global variable which must be initialized *
133 * in each zabbix application *
134 * *
135 ******************************************************************************/
help(void)136 void help(void)
137 {
138 const char **p = help_message;
139
140 usage();
141 printf("\n");
142
143 while (NULL != *p)
144 printf("%s\n", *p++);
145
146 printf("\n");
147 puts(help_message_footer);
148 }
149
150 /******************************************************************************
151 * *
152 * Function: zbx_error *
153 * *
154 * Purpose: Print error text to the stderr *
155 * *
156 * Parameters: fmt - format of message *
157 * *
158 * Return value: *
159 * *
160 * Author: Eugene Grigorjev *
161 * *
162 ******************************************************************************/
zbx_error(const char * fmt,...)163 void zbx_error(const char *fmt, ...)
164 {
165 va_list args;
166
167 va_start(args, fmt);
168
169 fprintf(stderr, "%s [%li]: ", progname, zbx_get_thread_id());
170 vfprintf(stderr, fmt, args);
171 fprintf(stderr, "\n");
172 fflush(stderr);
173
174 va_end(args);
175 }
176
177 /******************************************************************************
178 * *
179 * Function: zbx_snprintf *
180 * *
181 * Purpose: Secure version of snprintf function. *
182 * Add zero character at the end of string. *
183 * *
184 * Parameters: str - destination buffer pointer *
185 * count - size of destination buffer *
186 * fmt - format *
187 * *
188 * Return value: *
189 * *
190 * Author: Eugene Grigorjev *
191 * *
192 ******************************************************************************/
zbx_snprintf(char * str,size_t count,const char * fmt,...)193 size_t zbx_snprintf(char *str, size_t count, const char *fmt, ...)
194 {
195 size_t written_len;
196 va_list args;
197
198 va_start(args, fmt);
199 written_len = zbx_vsnprintf(str, count, fmt, args);
200 va_end(args);
201
202 return written_len;
203 }
204
205 /******************************************************************************
206 * *
207 * Function: zbx_snprintf_alloc *
208 * *
209 * Purpose: Secure version of snprintf function. *
210 * Add zero character at the end of string. *
211 * Reallocs memory if not enough. *
212 * *
213 * Parameters: str - [IN/OUT] destination buffer pointer *
214 * alloc_len - [IN/OUT] already allocated memory *
215 * offset - [IN/OUT] offset for writing *
216 * fmt - [IN] format *
217 * *
218 * Return value: *
219 * *
220 * Author: Alexei Vladishev, Alexander Vladishev *
221 * *
222 ******************************************************************************/
zbx_snprintf_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * fmt,...)223 void zbx_snprintf_alloc(char **str, size_t *alloc_len, size_t *offset, const char *fmt, ...)
224 {
225 va_list args;
226 size_t avail_len, written_len;
227 retry:
228 if (NULL == *str)
229 {
230 /* zbx_vsnprintf() returns bytes actually written instead of bytes to write, */
231 /* so we have to use the standard function */
232 va_start(args, fmt);
233 *alloc_len = vsnprintf(NULL, 0, fmt, args) + 2; /* '\0' + one byte to prevent the operation retry */
234 va_end(args);
235 *offset = 0;
236 *str = (char *)zbx_malloc(*str, *alloc_len);
237 }
238
239 avail_len = *alloc_len - *offset;
240 va_start(args, fmt);
241 written_len = zbx_vsnprintf(*str + *offset, avail_len, fmt, args);
242 va_end(args);
243
244 if (written_len == avail_len - 1)
245 {
246 *alloc_len *= 2;
247 *str = (char *)zbx_realloc(*str, *alloc_len);
248
249 goto retry;
250 }
251
252 *offset += written_len;
253 }
254
255 /******************************************************************************
256 * *
257 * Function: zbx_vsnprintf *
258 * *
259 * Purpose: Secure version of vsnprintf function. *
260 * Add zero character at the end of string. *
261 * *
262 * Parameters: str - [IN/OUT] destination buffer pointer *
263 * count - [IN] size of destination buffer *
264 * fmt - [IN] format *
265 * *
266 * Return value: the number of characters in the output buffer *
267 * (not including the trailing '\0') *
268 * *
269 * Author: Alexei Vladishev (see also zbx_snprintf) *
270 * *
271 ******************************************************************************/
zbx_vsnprintf(char * str,size_t count,const char * fmt,va_list args)272 size_t zbx_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
273 {
274 int written_len = 0;
275
276 if (0 < count)
277 {
278 if (0 > (written_len = vsnprintf(str, count, fmt, args)))
279 written_len = (int)count - 1; /* count an output error as a full buffer */
280 else
281 written_len = MIN(written_len, (int)count - 1); /* result could be truncated */
282 }
283 str[written_len] = '\0'; /* always write '\0', even if buffer size is 0 or vsnprintf() error */
284
285 return (size_t)written_len;
286 }
287
288 /******************************************************************************
289 * *
290 * Function: zbx_strncpy_alloc, zbx_strcpy_alloc, zbx_chrcpy_alloc *
291 * *
292 * Purpose: If there is no '\0' byte among the first n bytes of src, *
293 * then all n bytes will be placed into the dest buffer. *
294 * In other case only strlen() bytes will be placed there. *
295 * Add zero character at the end of string. *
296 * Reallocs memory if not enough. *
297 * *
298 * Parameters: str - [IN/OUT] destination buffer pointer *
299 * alloc_len - [IN/OUT] already allocated memory *
300 * offset - [IN/OUT] offset for writing *
301 * src - [IN] copied string *
302 * n - [IN] maximum number of bytes to copy *
303 * *
304 * Author: Alexander Vladishev *
305 * *
306 ******************************************************************************/
zbx_strncpy_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * src,size_t n)307 void zbx_strncpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src, size_t n)
308 {
309 if (NULL == *str)
310 {
311 *alloc_len = n + 1;
312 *offset = 0;
313 *str = (char *)zbx_malloc(*str, *alloc_len);
314 }
315 else if (*offset + n >= *alloc_len)
316 {
317 while (*offset + n >= *alloc_len)
318 *alloc_len *= 2;
319 *str = (char *)zbx_realloc(*str, *alloc_len);
320 }
321
322 while (0 != n && '\0' != *src)
323 {
324 (*str)[(*offset)++] = *src++;
325 n--;
326 }
327
328 (*str)[*offset] = '\0';
329 }
330
zbx_str_memcpy_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * src,size_t n)331 void zbx_str_memcpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src, size_t n)
332 {
333 if (NULL == *str)
334 {
335 *alloc_len = n + 1;
336 *offset = 0;
337 *str = (char *)zbx_malloc(*str, *alloc_len);
338 }
339 else if (*offset + n >= *alloc_len)
340 {
341 while (*offset + n >= *alloc_len)
342 *alloc_len *= 2;
343 *str = (char *)zbx_realloc(*str, *alloc_len);
344 }
345
346 memcpy(*str + *offset, src, n);
347 *offset += n;
348 (*str)[*offset] = '\0';
349 }
350
zbx_strcpy_alloc(char ** str,size_t * alloc_len,size_t * offset,const char * src)351 void zbx_strcpy_alloc(char **str, size_t *alloc_len, size_t *offset, const char *src)
352 {
353 zbx_strncpy_alloc(str, alloc_len, offset, src, strlen(src));
354 }
355
zbx_chrcpy_alloc(char ** str,size_t * alloc_len,size_t * offset,char c)356 void zbx_chrcpy_alloc(char **str, size_t *alloc_len, size_t *offset, char c)
357 {
358 zbx_strncpy_alloc(str, alloc_len, offset, &c, 1);
359 }
360
361 /* Has to be rewritten to avoid malloc */
string_replace(const char * str,const char * sub_str1,const char * sub_str2)362 char *string_replace(const char *str, const char *sub_str1, const char *sub_str2)
363 {
364 char *new_str = NULL;
365 const char *p;
366 const char *q;
367 const char *r;
368 char *t;
369 long len, diff, count = 0;
370
371 assert(str);
372 assert(sub_str1);
373 assert(sub_str2);
374
375 len = (long)strlen(sub_str1);
376
377 /* count the number of occurrences of sub_str1 */
378 for ( p=str; (p = strstr(p, sub_str1)); p+=len, count++ );
379
380 if (0 == count)
381 return zbx_strdup(NULL, str);
382
383 diff = (long)strlen(sub_str2) - len;
384
385 /* allocate new memory */
386 new_str = (char *)zbx_malloc(new_str, (size_t)(strlen(str) + count*diff + 1)*sizeof(char));
387
388 for (q=str,t=new_str,p=str; (p = strstr(p, sub_str1)); )
389 {
390 /* copy until next occurrence of sub_str1 */
391 for ( ; q < p; *t++ = *q++);
392 q += len;
393 p = q;
394 for ( r = sub_str2; (*t++ = *r++); );
395 --t;
396 }
397 /* copy the tail of str */
398 for( ; *q ; *t++ = *q++ );
399
400 *t = '\0';
401
402 return new_str;
403 }
404
405 /******************************************************************************
406 * *
407 * Function: del_zeros *
408 * *
409 * Purpose: delete all right '0' and '.' for the string *
410 * *
411 * Parameters: s - string to trim '0' *
412 * *
413 * Return value: string without right '0' *
414 * *
415 * Author: Alexei Vladishev *
416 * *
417 * Comments: 10.0100 => 10.01, 10. => 10 *
418 * *
419 ******************************************************************************/
del_zeros(char * s)420 void del_zeros(char *s)
421 {
422 int trim = 0;
423 size_t len = 0;
424
425 while ('\0' != s[len])
426 {
427 if ('e' == s[len] || 'E' == s[len])
428 {
429 /* don't touch numbers that are written in scientific notation */
430 return;
431 }
432
433 if ('.' == s[len])
434 {
435 /* number has decimal part */
436
437 if (1 == trim)
438 {
439 /* don't touch invalid numbers with more than one decimal separator */
440 return;
441 }
442
443 trim = 1;
444 }
445
446 len++;
447 }
448
449 if (1 == trim)
450 {
451 size_t i;
452
453 for (i = len - 1; ; i--)
454 {
455 if ('0' == s[i])
456 {
457 s[i] = '\0';
458 }
459 else if ('.' == s[i])
460 {
461 s[i] = '\0';
462 break;
463 }
464 else
465 {
466 break;
467 }
468 }
469 }
470 }
471
472 /******************************************************************************
473 * *
474 * Function: zbx_rtrim *
475 * *
476 * Purpose: Strip characters from the end of a string *
477 * *
478 * Parameters: str - string for processing *
479 * charlist - null terminated list of characters *
480 * *
481 * Return value: number of trimmed characters *
482 * *
483 * Author: Eugene Grigorjev, Aleksandrs Saveljevs *
484 * *
485 ******************************************************************************/
zbx_rtrim(char * str,const char * charlist)486 int zbx_rtrim(char *str, const char *charlist)
487 {
488 char *p;
489 int count = 0;
490
491 if (NULL == str || '\0' == *str)
492 return count;
493
494 for (p = str + strlen(str) - 1; p >= str && NULL != strchr(charlist, *p); p--)
495 {
496 *p = '\0';
497 count++;
498 }
499
500 return count;
501 }
502
503 /******************************************************************************
504 * *
505 * Function: zbx_ltrim *
506 * *
507 * Purpose: Strip characters from the beginning of a string *
508 * *
509 * Parameters: str - string for processing *
510 * charlist - null terminated list of characters *
511 * *
512 * Return value: *
513 * *
514 * Author: Eugene Grigorjev *
515 * *
516 ******************************************************************************/
zbx_ltrim(char * str,const char * charlist)517 void zbx_ltrim(char *str, const char *charlist)
518 {
519 char *p;
520
521 if (NULL == str || '\0' == *str)
522 return;
523
524 for (p = str; '\0' != *p && NULL != strchr(charlist, *p); p++)
525 ;
526
527 if (p == str)
528 return;
529
530 while ('\0' != *p)
531 *str++ = *p++;
532
533 *str = '\0';
534 }
535
536 /******************************************************************************
537 * *
538 * Function: zbx_lrtrim *
539 * *
540 * Purpose: Removes leading and trailing characters from the specified *
541 * character string *
542 * *
543 * Parameters: str - [IN/OUT] string for processing *
544 * charlist - [IN] null terminated list of characters *
545 * *
546 ******************************************************************************/
zbx_lrtrim(char * str,const char * charlist)547 void zbx_lrtrim(char *str, const char *charlist)
548 {
549 zbx_rtrim(str, charlist);
550 zbx_ltrim(str, charlist);
551 }
552
553 /******************************************************************************
554 * *
555 * Function: zbx_remove_chars *
556 * *
557 * Purpose: Remove characters 'charlist' from the whole string *
558 * *
559 * Parameters: str - string for processing *
560 * charlist - null terminated list of characters *
561 * *
562 * Return value: *
563 * *
564 * Author: Alexander Vladishev *
565 * *
566 ******************************************************************************/
zbx_remove_chars(char * str,const char * charlist)567 void zbx_remove_chars(char *str, const char *charlist)
568 {
569 char *p;
570
571 if (NULL == str || NULL == charlist || '\0' == *str || '\0' == *charlist)
572 return;
573
574 for (p = str; '\0' != *p; p++)
575 {
576 if (NULL == strchr(charlist, *p))
577 *str++ = *p;
578 }
579
580 *str = '\0';
581 }
582
583 /******************************************************************************
584 * *
585 * Function: zbx_str_printable_dyn *
586 * *
587 * Purpose: converts text to printable string by converting special *
588 * characters to escape sequences *
589 * *
590 * Parameters: text - [IN] the text to convert *
591 * *
592 * Return value: The text converted in printable format *
593 * *
594 ******************************************************************************/
zbx_str_printable_dyn(const char * text)595 char *zbx_str_printable_dyn(const char *text)
596 {
597 size_t out_alloc = 0;
598 const char *pin;
599 char *out, *pout;
600
601 for (pin = text; '\0' != *pin; pin++)
602 {
603 switch (*pin)
604 {
605 case '\n':
606 case '\t':
607 case '\r':
608 out_alloc += 2;
609 break;
610 default:
611 out_alloc++;
612 break;
613 }
614 }
615
616 out = zbx_malloc(NULL, ++out_alloc);
617
618 for (pin = text, pout = out; '\0' != *pin; pin++)
619 {
620 switch (*pin)
621 {
622 case '\n':
623 *pout++ = '\\';
624 *pout++ = 'n';
625 break;
626 case '\t':
627 *pout++ = '\\';
628 *pout++ = 't';
629 break;
630 case '\r':
631 *pout++ = '\\';
632 *pout++ = 'r';
633 break;
634 default:
635 *pout++ = *pin;
636 break;
637 }
638 }
639 *pout = '\0';
640
641 return out;
642 }
643
644 /******************************************************************************
645 * *
646 * Function: zbx_strlcpy *
647 * *
648 * Purpose: Copy src to string dst of size siz. At most siz - 1 characters *
649 * will be copied. Always null terminates (unless siz == 0). *
650 * *
651 * Return value: the number of characters copied (excluding the null byte) *
652 * *
653 ******************************************************************************/
zbx_strlcpy(char * dst,const char * src,size_t siz)654 size_t zbx_strlcpy(char *dst, const char *src, size_t siz)
655 {
656 const char *s = src;
657
658 if (0 != siz)
659 {
660 while (0 != --siz && '\0' != *s)
661 *dst++ = *s++;
662
663 *dst = '\0';
664 }
665
666 return s - src; /* count does not include null */
667 }
668
669 /******************************************************************************
670 * *
671 * Function: zbx_strlcat *
672 * *
673 * Purpose: Appends src to string dst of size siz (unlike strncat, size is *
674 * the full size of dst, not space left). At most siz - 1 characters *
675 * will be copied. Always null terminates (unless *
676 * siz <= strlen(dst)). *
677 * *
678 ******************************************************************************/
zbx_strlcat(char * dst,const char * src,size_t siz)679 void zbx_strlcat(char *dst, const char *src, size_t siz)
680 {
681 while ('\0' != *dst)
682 {
683 dst++;
684 siz--;
685 }
686
687 zbx_strlcpy(dst, src, siz);
688 }
689
690 /******************************************************************************
691 * *
692 * Function: zbx_strlcpy_utf8 *
693 * *
694 * Purpose: copies utf-8 string + terminating zero character into specified *
695 * buffer *
696 * *
697 * Return value: the number of copied bytes excluding terminating zero *
698 * character. *
699 * *
700 * Comments: If the source string is larger than destination buffer then the *
701 * string is truncated after last valid utf-8 character rather than *
702 * byte. *
703 * *
704 ******************************************************************************/
zbx_strlcpy_utf8(char * dst,const char * src,size_t size)705 size_t zbx_strlcpy_utf8(char *dst, const char *src, size_t size)
706 {
707 size = zbx_strlen_utf8_nbytes(src, size - 1);
708 memcpy(dst, src, size);
709 dst[size] = '\0';
710
711 return size;
712 }
713
714 /******************************************************************************
715 * *
716 * Function: zbx_dvsprintf *
717 * *
718 * Purpose: dynamical formatted output conversion *
719 * *
720 * Return value: formatted string *
721 * *
722 * Author: Eugene Grigorjev *
723 * *
724 * Comments: returns a pointer to allocated memory *
725 * *
726 ******************************************************************************/
zbx_dvsprintf(char * dest,const char * f,va_list args)727 char *zbx_dvsprintf(char *dest, const char *f, va_list args)
728 {
729 char *string = NULL;
730 int n, size = MAX_STRING_LEN >> 1;
731
732 va_list curr;
733
734 while (1)
735 {
736 string = (char *)zbx_malloc(string, size);
737
738 va_copy(curr, args);
739 n = vsnprintf(string, size, f, curr);
740 va_end(curr);
741
742 if (0 <= n && n < size)
743 break;
744
745 /* result was truncated */
746 if (-1 == n)
747 size = size * 3 / 2 + 1; /* the length is unknown */
748 else
749 size = n + 1; /* n bytes + trailing '\0' */
750
751 zbx_free(string);
752 }
753
754 zbx_free(dest);
755
756 return string;
757 }
758
759 /******************************************************************************
760 * *
761 * Function: zbx_dsprintf *
762 * *
763 * Purpose: dynamical formatted output conversion *
764 * *
765 * Return value: formatted string *
766 * *
767 * Author: Eugene Grigorjev *
768 * *
769 * Comments: returns a pointer to allocated memory *
770 * *
771 ******************************************************************************/
zbx_dsprintf(char * dest,const char * f,...)772 char *zbx_dsprintf(char *dest, const char *f, ...)
773 {
774 char *string;
775 va_list args;
776
777 va_start(args, f);
778
779 string = zbx_dvsprintf(dest, f, args);
780
781 va_end(args);
782
783 return string;
784 }
785
786 /******************************************************************************
787 * *
788 * Function: zbx_strdcat *
789 * *
790 * Purpose: dynamical cating of strings *
791 * *
792 * Return value: new pointer of string *
793 * *
794 * Author: Eugene Grigorjev *
795 * *
796 * Comments: returns a pointer to allocated memory *
797 * zbx_strdcat(NULL, "") will return "", not NULL! *
798 * *
799 ******************************************************************************/
zbx_strdcat(char * dest,const char * src)800 char *zbx_strdcat(char *dest, const char *src)
801 {
802 size_t len_dest, len_src;
803
804 if (NULL == src)
805 return dest;
806
807 if (NULL == dest)
808 return zbx_strdup(NULL, src);
809
810 len_dest = strlen(dest);
811 len_src = strlen(src);
812
813 dest = (char *)zbx_realloc(dest, len_dest + len_src + 1);
814
815 zbx_strlcpy(dest + len_dest, src, len_src + 1);
816
817 return dest;
818 }
819
820 /******************************************************************************
821 * *
822 * Function: zbx_strdcatf *
823 * *
824 * Purpose: dynamical cating of formatted strings *
825 * *
826 * Return value: new pointer of string *
827 * *
828 * Author: Eugene Grigorjev *
829 * *
830 * Comments: returns a pointer to allocated memory *
831 * *
832 ******************************************************************************/
zbx_strdcatf(char * dest,const char * f,...)833 char *zbx_strdcatf(char *dest, const char *f, ...)
834 {
835 char *string, *result;
836 va_list args;
837
838 va_start(args, f);
839 string = zbx_dvsprintf(NULL, f, args);
840 va_end(args);
841
842 result = zbx_strdcat(dest, string);
843
844 zbx_free(string);
845
846 return result;
847 }
848
849 /******************************************************************************
850 * *
851 * Function: zbx_check_hostname *
852 * *
853 * Purpose: check a byte stream for a valid hostname *
854 * *
855 * Parameters: hostname - pointer to the first char of hostname *
856 * error - pointer to the error message (can be NULL) *
857 * *
858 * Return value: return SUCCEED if hostname is valid *
859 * or FAIL if hostname contains invalid chars, is empty *
860 * or is longer than MAX_ZBX_HOSTNAME_LEN *
861 * *
862 * Author: Alexander Vladishev *
863 * *
864 ******************************************************************************/
zbx_check_hostname(const char * hostname,char ** error)865 int zbx_check_hostname(const char *hostname, char **error)
866 {
867 int len = 0;
868
869 while ('\0' != hostname[len])
870 {
871 if (FAIL == is_hostname_char(hostname[len]))
872 {
873 if (NULL != error)
874 *error = zbx_dsprintf(NULL, "name contains invalid character '%c'", hostname[len]);
875 return FAIL;
876 }
877
878 len++;
879 }
880
881 if (0 == len)
882 {
883 if (NULL != error)
884 *error = zbx_strdup(NULL, "name is empty");
885 return FAIL;
886 }
887
888 if (MAX_ZBX_HOSTNAME_LEN < len)
889 {
890 if (NULL != error)
891 *error = zbx_dsprintf(NULL, "name is too long (max %d characters)", MAX_ZBX_HOSTNAME_LEN);
892 return FAIL;
893 }
894
895 return SUCCEED;
896 }
897
898 /******************************************************************************
899 * *
900 * Function: parse_key *
901 * *
902 * Purpose: advances pointer to first invalid character in string *
903 * ensuring that everything before it is a valid key *
904 * *
905 * e.g., system.run[cat /etc/passwd | awk -F: '{ print $1 }'] *
906 * *
907 * Parameters: exp - [IN/OUT] pointer to the first char of key *
908 * *
909 * e.g., {host:system.run[cat /etc/passwd | awk -F: '{ print $1 }'].last(0)} *
910 * ^ *
911 * Return value: returns FAIL only if no key is present (length 0), *
912 * or the whole string is invalid. SUCCEED otherwise. *
913 * *
914 * Author: Aleksandrs Saveljevs *
915 * *
916 * Comments: the pointer is advanced to the first invalid character even if *
917 * FAIL is returned (meaning there is a syntax error in item key). *
918 * If necessary, the caller must keep a copy of pointer original *
919 * value. *
920 * *
921 ******************************************************************************/
parse_key(const char ** exp)922 int parse_key(const char **exp)
923 {
924 const char *s;
925
926 for (s = *exp; SUCCEED == is_key_char(*s); s++)
927 ;
928
929 if (*exp == s) /* the key is empty */
930 return FAIL;
931
932 if ('[' == *s) /* for instance, net.tcp.port[,80] */
933 {
934 int state = 0; /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
935 int array = 0; /* array nest level */
936
937 for (s++; '\0' != *s; s++)
938 {
939 switch (state)
940 {
941 /* init state */
942 case 0:
943 if (',' == *s)
944 ;
945 else if ('"' == *s)
946 state = 1;
947 else if ('[' == *s)
948 {
949 if (0 == array)
950 array = 1;
951 else
952 goto fail; /* incorrect syntax: multi-level array */
953 }
954 else if (']' == *s && 0 != array)
955 {
956 array = 0;
957 s++;
958
959 while (' ' == *s) /* skip trailing spaces after closing ']' */
960 s++;
961
962 if (']' == *s)
963 goto succeed;
964
965 if (',' != *s)
966 goto fail; /* incorrect syntax */
967 }
968 else if (']' == *s && 0 == array)
969 goto succeed;
970 else if (' ' != *s)
971 state = 2;
972 break;
973 /* quoted */
974 case 1:
975 if ('"' == *s)
976 {
977 while (' ' == s[1]) /* skip trailing spaces after closing quotes */
978 s++;
979
980 if (0 == array && ']' == s[1])
981 {
982 s++;
983 goto succeed;
984 }
985
986 if (',' != s[1] && !(0 != array && ']' == s[1]))
987 {
988 s++;
989 goto fail; /* incorrect syntax */
990 }
991
992 state = 0;
993 }
994 else if ('\\' == *s && '"' == s[1])
995 s++;
996 break;
997 /* unquoted */
998 case 2:
999 if (',' == *s || (']' == *s && 0 != array))
1000 {
1001 s--;
1002 state = 0;
1003 }
1004 else if (']' == *s && 0 == array)
1005 goto succeed;
1006 break;
1007 }
1008 }
1009 fail:
1010 *exp = s;
1011 return FAIL;
1012 succeed:
1013 s++;
1014 }
1015
1016 *exp = s;
1017 return SUCCEED;
1018 }
1019
1020 /******************************************************************************
1021 * *
1022 * Function: parse_host_key *
1023 * *
1024 * Purpose: return hostname and key *
1025 * <hostname:>key *
1026 * *
1027 * Parameters: *
1028 * exp - pointer to the first char of hostname *
1029 * host:key[key params] *
1030 * ^ *
1031 * *
1032 * Return value: return SUCCEED or FAIL *
1033 * *
1034 * Author: Alexander Vladishev *
1035 * *
1036 ******************************************************************************/
parse_host_key(char * exp,char ** host,char ** key)1037 int parse_host_key(char *exp, char **host, char **key)
1038 {
1039 char *p, *s;
1040
1041 if (NULL == exp || '\0' == *exp)
1042 return FAIL;
1043
1044 for (p = exp, s = exp; '\0' != *p; p++) /* check for optional hostname */
1045 {
1046 if (':' == *p) /* hostname:vfs.fs.size[/,total]
1047 * --------^
1048 */
1049 {
1050 *p = '\0';
1051 *host = zbx_strdup(NULL, s);
1052 *p++ = ':';
1053
1054 s = p;
1055 break;
1056 }
1057
1058 if (SUCCEED != is_hostname_char(*p))
1059 break;
1060 }
1061
1062 *key = zbx_strdup(NULL, s);
1063
1064 return SUCCEED;
1065 }
1066
1067 /******************************************************************************
1068 * *
1069 * Function: zbx_get_escape_string_len *
1070 * *
1071 * Purpose: calculate the required size for the escaped string *
1072 * *
1073 * Parameters: src - [IN] null terminated source string *
1074 * charlist - [IN] null terminated to-be-escaped character list *
1075 * *
1076 * Return value: size of the escaped string *
1077 * *
1078 * Author: Alexander Vladishev *
1079 * *
1080 ******************************************************************************/
zbx_get_escape_string_len(const char * src,const char * charlist)1081 size_t zbx_get_escape_string_len(const char *src, const char *charlist)
1082 {
1083 size_t sz = 0;
1084
1085 for (; '\0' != *src; src++, sz++)
1086 {
1087 if (NULL != strchr(charlist, *src))
1088 sz++;
1089 }
1090
1091 return sz;
1092 }
1093
1094 /******************************************************************************
1095 * *
1096 * Function: zbx_dyn_escape_string *
1097 * *
1098 * Purpose: escape characters in the source string *
1099 * *
1100 * Parameters: src - [IN] null terminated source string *
1101 * charlist - [IN] null terminated to-be-escaped character list *
1102 * *
1103 * Return value: the escaped string *
1104 * *
1105 * Author: Alexander Vladishev *
1106 * *
1107 ******************************************************************************/
zbx_dyn_escape_string(const char * src,const char * charlist)1108 char *zbx_dyn_escape_string(const char *src, const char *charlist)
1109 {
1110 size_t sz;
1111 char *d, *dst = NULL;
1112
1113 sz = zbx_get_escape_string_len(src, charlist) + 1;
1114
1115 dst = (char *)zbx_malloc(dst, sz);
1116
1117 for (d = dst; '\0' != *src; src++)
1118 {
1119 if (NULL != strchr(charlist, *src))
1120 *d++ = '\\';
1121
1122 *d++ = *src;
1123 }
1124
1125 *d = '\0';
1126
1127 return dst;
1128 }
1129
1130 /******************************************************************************
1131 * *
1132 * Function: zbx_escape_string *
1133 * *
1134 * Purpose: escape characters in the source string to fixed output buffer *
1135 * *
1136 * Parameters: dst - [OUT] the output buffer *
1137 * len - [IN] the output buffer size *
1138 * src - [IN] null terminated source string *
1139 * charlist - [IN] null terminated to-be-escaped character list *
1140 * *
1141 * Return value: SUCCEED - the string was escaped successfully. *
1142 * FAIL - output buffer is too small. *
1143 * *
1144 ******************************************************************************/
zbx_escape_string(char * dst,size_t len,const char * src,const char * charlist)1145 int zbx_escape_string(char *dst, size_t len, const char *src, const char *charlist)
1146 {
1147 for (; '\0' != *src; src++)
1148 {
1149 if (NULL != strchr(charlist, *src))
1150 {
1151 if (0 == --len)
1152 return FAIL;
1153 *dst++ = '\\';
1154 }
1155 else
1156 {
1157 if (0 == --len)
1158 return FAIL;
1159 }
1160
1161 *dst++ = *src;
1162 }
1163
1164 *dst = '\0';
1165
1166 return SUCCEED;
1167 }
1168
zbx_age2str(int age)1169 char *zbx_age2str(int age)
1170 {
1171 size_t offset = 0;
1172 int days, hours, minutes, seconds;
1173 static char buffer[32];
1174
1175 days = (int)((double)age / SEC_PER_DAY);
1176 hours = (int)((double)(age - days * SEC_PER_DAY) / SEC_PER_HOUR);
1177 minutes = (int)((double)(age - days * SEC_PER_DAY - hours * SEC_PER_HOUR) / SEC_PER_MIN);
1178 seconds = (int)((double)(age - days * SEC_PER_DAY - hours * SEC_PER_HOUR - minutes * SEC_PER_MIN));
1179
1180 if (0 != days)
1181 offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%dd ", days);
1182 if (0 != days || 0 != hours)
1183 offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%dh ", hours);
1184 if (0 != days || 0 != hours || 0 != minutes)
1185 offset += zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%dm ", minutes);
1186
1187 zbx_snprintf(buffer + offset, sizeof(buffer) - offset, "%ds", seconds);
1188
1189 return buffer;
1190 }
1191
zbx_date2str(time_t date)1192 char *zbx_date2str(time_t date)
1193 {
1194 static char buffer[11];
1195 struct tm *tm;
1196
1197 tm = localtime(&date);
1198 zbx_snprintf(buffer, sizeof(buffer), "%.4d.%.2d.%.2d",
1199 tm->tm_year + 1900,
1200 tm->tm_mon + 1,
1201 tm->tm_mday);
1202
1203 return buffer;
1204 }
1205
zbx_time2str(time_t time)1206 char *zbx_time2str(time_t time)
1207 {
1208 static char buffer[9];
1209 struct tm *tm;
1210
1211 tm = localtime(&time);
1212 zbx_snprintf(buffer, sizeof(buffer), "%.2d:%.2d:%.2d",
1213 tm->tm_hour,
1214 tm->tm_min,
1215 tm->tm_sec);
1216 return buffer;
1217 }
1218
zbx_strncasecmp(const char * s1,const char * s2,size_t n)1219 int zbx_strncasecmp(const char *s1, const char *s2, size_t n)
1220 {
1221 if (NULL == s1 && NULL == s2)
1222 return 0;
1223
1224 if (NULL == s1)
1225 return 1;
1226
1227 if (NULL == s2)
1228 return -1;
1229
1230 while (0 != n && '\0' != *s1 && '\0' != *s2 &&
1231 tolower((unsigned char)*s1) == tolower((unsigned char)*s2))
1232 {
1233 s1++;
1234 s2++;
1235 n--;
1236 }
1237
1238 return 0 == n ? 0 : tolower((unsigned char)*s1) - tolower((unsigned char)*s2);
1239 }
1240
zbx_strcasestr(const char * haystack,const char * needle)1241 char *zbx_strcasestr(const char *haystack, const char *needle)
1242 {
1243 size_t sz_h, sz_n;
1244 const char *p;
1245
1246 if (NULL == needle || '\0' == *needle)
1247 return (char *)haystack;
1248
1249 if (NULL == haystack || '\0' == *haystack)
1250 return NULL;
1251
1252 sz_h = strlen(haystack);
1253 sz_n = strlen(needle);
1254 if (sz_h < sz_n)
1255 return NULL;
1256
1257 for (p = haystack; p <= &haystack[sz_h - sz_n]; p++)
1258 {
1259 if (0 == zbx_strncasecmp(p, needle, sz_n))
1260 return (char *)p;
1261 }
1262
1263 return NULL;
1264 }
1265
cmp_key_id(const char * key_1,const char * key_2)1266 int cmp_key_id(const char *key_1, const char *key_2)
1267 {
1268 const char *p, *q;
1269
1270 for (p = key_1, q = key_2; *p == *q && '\0' != *q && '[' != *q; p++, q++)
1271 ;
1272
1273 return ('\0' == *p || '[' == *p) && ('\0' == *q || '[' == *q) ? SUCCEED : FAIL;
1274 }
1275
1276 /******************************************************************************
1277 * *
1278 * Function: get_process_type_string *
1279 * *
1280 * Purpose: Returns process name *
1281 * *
1282 * Parameters: proc_type - [IN] process type; ZBX_PROCESS_TYPE_* *
1283 * *
1284 * Author: Alexander Vladishev *
1285 * *
1286 * Comments: used in internals checks zabbix["process",...], process titles *
1287 * and log files *
1288 * *
1289 ******************************************************************************/
get_process_type_string(unsigned char proc_type)1290 const char *get_process_type_string(unsigned char proc_type)
1291 {
1292 switch (proc_type)
1293 {
1294 case ZBX_PROCESS_TYPE_POLLER:
1295 return "poller";
1296 case ZBX_PROCESS_TYPE_UNREACHABLE:
1297 return "unreachable poller";
1298 case ZBX_PROCESS_TYPE_IPMIPOLLER:
1299 return "ipmi poller";
1300 case ZBX_PROCESS_TYPE_PINGER:
1301 return "icmp pinger";
1302 case ZBX_PROCESS_TYPE_JAVAPOLLER:
1303 return "java poller";
1304 case ZBX_PROCESS_TYPE_HTTPPOLLER:
1305 return "http poller";
1306 case ZBX_PROCESS_TYPE_TRAPPER:
1307 return "trapper";
1308 case ZBX_PROCESS_TYPE_SNMPTRAPPER:
1309 return "snmp trapper";
1310 case ZBX_PROCESS_TYPE_PROXYPOLLER:
1311 return "proxy poller";
1312 case ZBX_PROCESS_TYPE_ESCALATOR:
1313 return "escalator";
1314 case ZBX_PROCESS_TYPE_HISTSYNCER:
1315 return "history syncer";
1316 case ZBX_PROCESS_TYPE_DISCOVERER:
1317 return "discoverer";
1318 case ZBX_PROCESS_TYPE_ALERTER:
1319 return "alerter";
1320 case ZBX_PROCESS_TYPE_TIMER:
1321 return "timer";
1322 case ZBX_PROCESS_TYPE_HOUSEKEEPER:
1323 return "housekeeper";
1324 case ZBX_PROCESS_TYPE_DATASENDER:
1325 return "data sender";
1326 case ZBX_PROCESS_TYPE_CONFSYNCER:
1327 return "configuration syncer";
1328 case ZBX_PROCESS_TYPE_HEARTBEAT:
1329 return "heartbeat sender";
1330 case ZBX_PROCESS_TYPE_SELFMON:
1331 return "self-monitoring";
1332 case ZBX_PROCESS_TYPE_VMWARE:
1333 return "vmware collector";
1334 case ZBX_PROCESS_TYPE_COLLECTOR:
1335 return "collector";
1336 case ZBX_PROCESS_TYPE_LISTENER:
1337 return "listener";
1338 case ZBX_PROCESS_TYPE_ACTIVE_CHECKS:
1339 return "active checks";
1340 case ZBX_PROCESS_TYPE_TASKMANAGER:
1341 return "task manager";
1342 case ZBX_PROCESS_TYPE_IPMIMANAGER:
1343 return "ipmi manager";
1344 case ZBX_PROCESS_TYPE_ALERTMANAGER:
1345 return "alert manager";
1346 case ZBX_PROCESS_TYPE_PREPROCMAN:
1347 return "preprocessing manager";
1348 case ZBX_PROCESS_TYPE_PREPROCESSOR:
1349 return "preprocessing worker";
1350 case ZBX_PROCESS_TYPE_LLDMANAGER:
1351 return "lld manager";
1352 case ZBX_PROCESS_TYPE_LLDWORKER:
1353 return "lld worker";
1354 case ZBX_PROCESS_TYPE_ALERTSYNCER:
1355 return "alert syncer";
1356 }
1357
1358 THIS_SHOULD_NEVER_HAPPEN;
1359 exit(EXIT_FAILURE);
1360 }
1361
get_process_type_by_name(const char * proc_type_str)1362 int get_process_type_by_name(const char *proc_type_str)
1363 {
1364 int i;
1365
1366 for (i = 0; i < ZBX_PROCESS_TYPE_COUNT; i++)
1367 {
1368 if (0 == strcmp(proc_type_str, get_process_type_string(i)))
1369 return i;
1370 }
1371
1372 return ZBX_PROCESS_TYPE_UNKNOWN;
1373 }
1374
get_program_type_string(unsigned char program_type)1375 const char *get_program_type_string(unsigned char program_type)
1376 {
1377 switch (program_type)
1378 {
1379 case ZBX_PROGRAM_TYPE_SERVER:
1380 return "server";
1381 case ZBX_PROGRAM_TYPE_PROXY_ACTIVE:
1382 case ZBX_PROGRAM_TYPE_PROXY_PASSIVE:
1383 return "proxy";
1384 case ZBX_PROGRAM_TYPE_AGENTD:
1385 return "agent";
1386 case ZBX_PROGRAM_TYPE_SENDER:
1387 return "sender";
1388 case ZBX_PROGRAM_TYPE_GET:
1389 return "get";
1390 default:
1391 return "unknown";
1392 }
1393 }
1394
zbx_permission_string(int perm)1395 const char *zbx_permission_string(int perm)
1396 {
1397 switch (perm)
1398 {
1399 case PERM_DENY:
1400 return "dn";
1401 case PERM_READ:
1402 return "r";
1403 case PERM_READ_WRITE:
1404 return "rw";
1405 default:
1406 return "unknown";
1407 }
1408 }
1409
zbx_agent_type_string(zbx_item_type_t item_type)1410 const char *zbx_agent_type_string(zbx_item_type_t item_type)
1411 {
1412 switch (item_type)
1413 {
1414 case ITEM_TYPE_ZABBIX:
1415 return "Zabbix agent";
1416 case ITEM_TYPE_SNMP:
1417 return "SNMP agent";
1418 case ITEM_TYPE_IPMI:
1419 return "IPMI agent";
1420 case ITEM_TYPE_JMX:
1421 return "JMX agent";
1422 default:
1423 return "generic";
1424 }
1425 }
1426
zbx_item_value_type_string(zbx_item_value_type_t value_type)1427 const char *zbx_item_value_type_string(zbx_item_value_type_t value_type)
1428 {
1429 switch (value_type)
1430 {
1431 case ITEM_VALUE_TYPE_FLOAT:
1432 return "Numeric (float)";
1433 case ITEM_VALUE_TYPE_STR:
1434 return "Character";
1435 case ITEM_VALUE_TYPE_LOG:
1436 return "Log";
1437 case ITEM_VALUE_TYPE_UINT64:
1438 return "Numeric (unsigned)";
1439 case ITEM_VALUE_TYPE_TEXT:
1440 return "Text";
1441 default:
1442 return "unknown";
1443 }
1444 }
1445
zbx_interface_type_string(zbx_interface_type_t type)1446 const char *zbx_interface_type_string(zbx_interface_type_t type)
1447 {
1448 switch (type)
1449 {
1450 case INTERFACE_TYPE_AGENT:
1451 return "Zabbix agent";
1452 case INTERFACE_TYPE_SNMP:
1453 return "SNMP";
1454 case INTERFACE_TYPE_IPMI:
1455 return "IPMI";
1456 case INTERFACE_TYPE_JMX:
1457 return "JMX";
1458 case INTERFACE_TYPE_ANY:
1459 return "any";
1460 case INTERFACE_TYPE_UNKNOWN:
1461 default:
1462 return "unknown";
1463 }
1464 }
1465
zbx_sysinfo_ret_string(int ret)1466 const char *zbx_sysinfo_ret_string(int ret)
1467 {
1468 switch (ret)
1469 {
1470 case SYSINFO_RET_OK:
1471 return "SYSINFO_SUCCEED";
1472 case SYSINFO_RET_FAIL:
1473 return "SYSINFO_FAIL";
1474 default:
1475 return "SYSINFO_UNKNOWN";
1476 }
1477 }
1478
zbx_result_string(int result)1479 const char *zbx_result_string(int result)
1480 {
1481 switch (result)
1482 {
1483 case SUCCEED:
1484 return "SUCCEED";
1485 case FAIL:
1486 return "FAIL";
1487 case CONFIG_ERROR:
1488 return "CONFIG_ERROR";
1489 case NOTSUPPORTED:
1490 return "NOTSUPPORTED";
1491 case NETWORK_ERROR:
1492 return "NETWORK_ERROR";
1493 case TIMEOUT_ERROR:
1494 return "TIMEOUT_ERROR";
1495 case AGENT_ERROR:
1496 return "AGENT_ERROR";
1497 case GATEWAY_ERROR:
1498 return "GATEWAY_ERROR";
1499 default:
1500 return "unknown";
1501 }
1502 }
1503
zbx_item_logtype_string(unsigned char logtype)1504 const char *zbx_item_logtype_string(unsigned char logtype)
1505 {
1506 switch (logtype)
1507 {
1508 case ITEM_LOGTYPE_INFORMATION:
1509 return "Information";
1510 case ITEM_LOGTYPE_WARNING:
1511 return "Warning";
1512 case ITEM_LOGTYPE_ERROR:
1513 return "Error";
1514 case ITEM_LOGTYPE_FAILURE_AUDIT:
1515 return "Failure Audit";
1516 case ITEM_LOGTYPE_SUCCESS_AUDIT:
1517 return "Success Audit";
1518 case ITEM_LOGTYPE_CRITICAL:
1519 return "Critical";
1520 case ITEM_LOGTYPE_VERBOSE:
1521 return "Verbose";
1522 default:
1523 return "unknown";
1524 }
1525 }
1526
zbx_dservice_type_string(zbx_dservice_type_t service)1527 const char *zbx_dservice_type_string(zbx_dservice_type_t service)
1528 {
1529 switch (service)
1530 {
1531 case SVC_SSH:
1532 return "SSH";
1533 case SVC_LDAP:
1534 return "LDAP";
1535 case SVC_SMTP:
1536 return "SMTP";
1537 case SVC_FTP:
1538 return "FTP";
1539 case SVC_HTTP:
1540 return "HTTP";
1541 case SVC_POP:
1542 return "POP";
1543 case SVC_NNTP:
1544 return "NNTP";
1545 case SVC_IMAP:
1546 return "IMAP";
1547 case SVC_TCP:
1548 return "TCP";
1549 case SVC_AGENT:
1550 return "Zabbix agent";
1551 case SVC_SNMPv1:
1552 return "SNMPv1 agent";
1553 case SVC_SNMPv2c:
1554 return "SNMPv2c agent";
1555 case SVC_SNMPv3:
1556 return "SNMPv3 agent";
1557 case SVC_ICMPPING:
1558 return "ICMP ping";
1559 case SVC_HTTPS:
1560 return "HTTPS";
1561 case SVC_TELNET:
1562 return "Telnet";
1563 default:
1564 return "unknown";
1565 }
1566 }
1567
zbx_alert_type_string(unsigned char type)1568 const char *zbx_alert_type_string(unsigned char type)
1569 {
1570 switch (type)
1571 {
1572 case ALERT_TYPE_MESSAGE:
1573 return "message";
1574 default:
1575 return "script";
1576 }
1577 }
1578
zbx_alert_status_string(unsigned char type,unsigned char status)1579 const char *zbx_alert_status_string(unsigned char type, unsigned char status)
1580 {
1581 switch (status)
1582 {
1583 case ALERT_STATUS_SENT:
1584 return (ALERT_TYPE_MESSAGE == type ? "sent" : "executed");
1585 case ALERT_STATUS_NOT_SENT:
1586 return "in progress";
1587 default:
1588 return "failed";
1589 }
1590 }
1591
zbx_escalation_status_string(unsigned char status)1592 const char *zbx_escalation_status_string(unsigned char status)
1593 {
1594 switch (status)
1595 {
1596 case ESCALATION_STATUS_ACTIVE:
1597 return "active";
1598 case ESCALATION_STATUS_SLEEP:
1599 return "sleep";
1600 case ESCALATION_STATUS_COMPLETED:
1601 return "completed";
1602 default:
1603 return "unknown";
1604 }
1605 }
1606
zbx_trigger_value_string(unsigned char value)1607 const char *zbx_trigger_value_string(unsigned char value)
1608 {
1609 switch (value)
1610 {
1611 case TRIGGER_VALUE_PROBLEM:
1612 return "PROBLEM";
1613 case TRIGGER_VALUE_OK:
1614 return "OK";
1615 default:
1616 return "unknown";
1617 }
1618 }
1619
zbx_trigger_state_string(unsigned char state)1620 const char *zbx_trigger_state_string(unsigned char state)
1621 {
1622 switch (state)
1623 {
1624 case TRIGGER_STATE_NORMAL:
1625 return "Normal";
1626 case TRIGGER_STATE_UNKNOWN:
1627 return "Unknown";
1628 default:
1629 return "unknown";
1630 }
1631 }
1632
zbx_item_state_string(unsigned char state)1633 const char *zbx_item_state_string(unsigned char state)
1634 {
1635 switch (state)
1636 {
1637 case ITEM_STATE_NORMAL:
1638 return "Normal";
1639 case ITEM_STATE_NOTSUPPORTED:
1640 return "Not supported";
1641 default:
1642 return "unknown";
1643 }
1644 }
1645
zbx_event_value_string(unsigned char source,unsigned char object,unsigned char value)1646 const char *zbx_event_value_string(unsigned char source, unsigned char object, unsigned char value)
1647 {
1648 if (EVENT_SOURCE_TRIGGERS == source)
1649 {
1650 switch (value)
1651 {
1652 case EVENT_STATUS_PROBLEM:
1653 return "PROBLEM";
1654 case EVENT_STATUS_RESOLVED:
1655 return "RESOLVED";
1656 default:
1657 return "unknown";
1658 }
1659 }
1660
1661 if (EVENT_SOURCE_INTERNAL == source)
1662 {
1663 switch (object)
1664 {
1665 case EVENT_OBJECT_TRIGGER:
1666 return zbx_trigger_state_string(value);
1667 case EVENT_OBJECT_ITEM:
1668 case EVENT_OBJECT_LLDRULE:
1669 return zbx_item_state_string(value);
1670 }
1671 }
1672
1673 return "unknown";
1674 }
1675
1676 #if defined(_WINDOWS) || defined(__MINGW32__)
get_codepage(const char * encoding,unsigned int * codepage)1677 static int get_codepage(const char *encoding, unsigned int *codepage)
1678 {
1679 typedef struct
1680 {
1681 unsigned int codepage;
1682 const char *name;
1683 }
1684 codepage_t;
1685
1686 int i;
1687 char buf[16];
1688 codepage_t cp[] = {{0, "ANSI"}, {37, "IBM037"}, {437, "IBM437"}, {500, "IBM500"}, {708, "ASMO-708"},
1689 {709, NULL}, {710, NULL}, {720, "DOS-720"}, {737, "IBM737"}, {775, "IBM775"}, {850, "IBM850"},
1690 {852, "IBM852"}, {855, "IBM855"}, {857, "IBM857"}, {858, "IBM00858"}, {860, "IBM860"},
1691 {861, "IBM861"}, {862, "DOS-862"}, {863, "IBM863"}, {864, "IBM864"}, {865, "IBM865"},
1692 {866, "CP866"}, {869, "IBM869"}, {870, "IBM870"}, {874, "WINDOWS-874"}, {875, "CP875"},
1693 {932, "SHIFT_JIS"}, {936, "GB2312"}, {949, "KS_C_5601-1987"}, {950, "BIG5"}, {1026, "IBM1026"},
1694 {1047, "IBM01047"}, {1140, "IBM01140"}, {1141, "IBM01141"}, {1142, "IBM01142"},
1695 {1143, "IBM01143"}, {1144, "IBM01144"}, {1145, "IBM01145"}, {1146, "IBM01146"},
1696 {1147, "IBM01147"}, {1148, "IBM01148"}, {1149, "IBM01149"}, {1200, "UTF-16"},
1697 {1201, "UNICODEFFFE"}, {1250, "WINDOWS-1250"}, {1251, "WINDOWS-1251"}, {1252, "WINDOWS-1252"},
1698 {1253, "WINDOWS-1253"}, {1254, "WINDOWS-1254"}, {1255, "WINDOWS-1255"}, {1256, "WINDOWS-1256"},
1699 {1257, "WINDOWS-1257"}, {1258, "WINDOWS-1258"}, {1361, "JOHAB"}, {10000, "MACINTOSH"},
1700 {10001, "X-MAC-JAPANESE"}, {10002, "X-MAC-CHINESETRAD"}, {10003, "X-MAC-KOREAN"},
1701 {10004, "X-MAC-ARABIC"}, {10005, "X-MAC-HEBREW"}, {10006, "X-MAC-GREEK"},
1702 {10007, "X-MAC-CYRILLIC"}, {10008, "X-MAC-CHINESESIMP"}, {10010, "X-MAC-ROMANIAN"},
1703 {10017, "X-MAC-UKRAINIAN"}, {10021, "X-MAC-THAI"}, {10029, "X-MAC-CE"},
1704 {10079, "X-MAC-ICELANDIC"}, {10081, "X-MAC-TURKISH"}, {10082, "X-MAC-CROATIAN"},
1705 {12000, "UTF-32"}, {12001, "UTF-32BE"}, {20000, "X-CHINESE_CNS"}, {20001, "X-CP20001"},
1706 {20002, "X_CHINESE-ETEN"}, {20003, "X-CP20003"}, {20004, "X-CP20004"}, {20005, "X-CP20005"},
1707 {20105, "X-IA5"}, {20106, "X-IA5-GERMAN"}, {20107, "X-IA5-SWEDISH"}, {20108, "X-IA5-NORWEGIAN"},
1708 {20127, "US-ASCII"}, {20261, "X-CP20261"}, {20269, "X-CP20269"}, {20273, "IBM273"},
1709 {20277, "IBM277"}, {20278, "IBM278"}, {20280, "IBM280"}, {20284, "IBM284"}, {20285, "IBM285"},
1710 {20290, "IBM290"}, {20297, "IBM297"}, {20420, "IBM420"}, {20423, "IBM423"}, {20424, "IBM424"},
1711 {20833, "X-EBCDIC-KOREANEXTENDED"}, {20838, "IBM-THAI"}, {20866, "KOI8-R"}, {20871, "IBM871"},
1712 {20880, "IBM880"}, {20905, "IBM905"}, {20924, "IBM00924"}, {20932, "EUC-JP"},
1713 {20936, "X-CP20936"}, {20949, "X-CP20949"}, {21025, "CP1025"}, {21027, NULL}, {21866, "KOI8-U"},
1714 {28591, "ISO-8859-1"}, {28592, "ISO-8859-2"}, {28593, "ISO-8859-3"}, {28594, "ISO-8859-4"},
1715 {28595, "ISO-8859-5"}, {28596, "ISO-8859-6"}, {28597, "ISO-8859-7"}, {28598, "ISO-8859-8"},
1716 {28599, "ISO-8859-9"}, {28603, "ISO-8859-13"}, {28605, "ISO-8859-15"}, {29001, "X-EUROPA"},
1717 {38598, "ISO-8859-8-I"}, {50220, "ISO-2022-JP"}, {50221, "CSISO2022JP"}, {50222, "ISO-2022-JP"},
1718 {50225, "ISO-2022-KR"}, {50227, "X-CP50227"}, {50229, NULL}, {50930, NULL}, {50931, NULL},
1719 {50933, NULL}, {50935, NULL}, {50936, NULL}, {50937, NULL}, {50939, NULL}, {51932, "EUC-JP"},
1720 {51936, "EUC-CN"}, {51949, "EUC-KR"}, {51950, NULL}, {52936, "HZ-GB-2312"}, {54936, "GB18030"},
1721 {57002, "X-ISCII-DE"}, {57003, "X-ISCII-BE"}, {57004, "X-ISCII-TA"}, {57005, "X-ISCII-TE"},
1722 {57006, "X-ISCII-AS"}, {57007, "X-ISCII-OR"}, {57008, "X-ISCII-KA"}, {57009, "X-ISCII-MA"},
1723 {57010, "X-ISCII-GU"}, {57011, "X-ISCII-PA"}, {65000, "UTF-7"}, {65001, "UTF-8"}, {0, NULL}};
1724
1725 if ('\0' == *encoding)
1726 {
1727 *codepage = 0; /* ANSI */
1728 return SUCCEED;
1729 }
1730
1731 /* by name */
1732 for (i = 0; 0 != cp[i].codepage || NULL != cp[i].name; i++)
1733 {
1734 if (NULL == cp[i].name)
1735 continue;
1736
1737 if (0 == strcmp(encoding, cp[i].name))
1738 {
1739 *codepage = cp[i].codepage;
1740 return SUCCEED;
1741 }
1742 }
1743
1744 /* by number */
1745 for (i = 0; 0 != cp[i].codepage || NULL != cp[i].name; i++)
1746 {
1747 _itoa_s(cp[i].codepage, buf, sizeof(buf), 10);
1748 if (0 == strcmp(encoding, buf))
1749 {
1750 *codepage = cp[i].codepage;
1751 return SUCCEED;
1752 }
1753 }
1754
1755 /* by 'cp' + number */
1756 for (i = 0; 0 != cp[i].codepage || NULL != cp[i].name; i++)
1757 {
1758 zbx_snprintf(buf, sizeof(buf), "cp%li", cp[i].codepage);
1759 if (0 == strcmp(encoding, buf))
1760 {
1761 *codepage = cp[i].codepage;
1762 return SUCCEED;
1763 }
1764 }
1765
1766 return FAIL;
1767 }
1768
1769 /* convert from selected code page to unicode */
zbx_to_unicode(unsigned int codepage,const char * cp_string)1770 static wchar_t *zbx_to_unicode(unsigned int codepage, const char *cp_string)
1771 {
1772 wchar_t *wide_string = NULL;
1773 int wide_size;
1774
1775 wide_size = MultiByteToWideChar(codepage, 0, cp_string, -1, NULL, 0);
1776 wide_string = (wchar_t *)zbx_malloc(wide_string, (size_t)wide_size * sizeof(wchar_t));
1777
1778 /* convert from cp_string to wide_string */
1779 MultiByteToWideChar(codepage, 0, cp_string, -1, wide_string, wide_size);
1780
1781 return wide_string;
1782 }
1783
1784 /* convert from Windows ANSI code page to unicode */
zbx_acp_to_unicode(const char * acp_string)1785 wchar_t *zbx_acp_to_unicode(const char *acp_string)
1786 {
1787 return zbx_to_unicode(CP_ACP, acp_string);
1788 }
1789
1790 /* convert from Windows OEM code page to unicode */
zbx_oemcp_to_unicode(const char * oemcp_string)1791 wchar_t *zbx_oemcp_to_unicode(const char *oemcp_string)
1792 {
1793 return zbx_to_unicode(CP_OEMCP, oemcp_string);
1794 }
1795
zbx_acp_to_unicode_static(const char * acp_string,wchar_t * wide_string,int wide_size)1796 int zbx_acp_to_unicode_static(const char *acp_string, wchar_t *wide_string, int wide_size)
1797 {
1798 /* convert from acp_string to wide_string */
1799 if (0 == MultiByteToWideChar(CP_ACP, 0, acp_string, -1, wide_string, wide_size))
1800 return FAIL;
1801
1802 return SUCCEED;
1803 }
1804
1805 /* convert from UTF-8 to unicode */
zbx_utf8_to_unicode(const char * utf8_string)1806 wchar_t *zbx_utf8_to_unicode(const char *utf8_string)
1807 {
1808 return zbx_to_unicode(CP_UTF8, utf8_string);
1809 }
1810
1811 /* convert from unicode to utf8 */
zbx_unicode_to_utf8(const wchar_t * wide_string)1812 char *zbx_unicode_to_utf8(const wchar_t *wide_string)
1813 {
1814 char *utf8_string = NULL;
1815 int utf8_size;
1816
1817 utf8_size = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
1818 utf8_string = (char *)zbx_malloc(utf8_string, (size_t)utf8_size);
1819
1820 /* convert from wide_string to utf8_string */
1821 WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, utf8_string, utf8_size, NULL, NULL);
1822
1823 return utf8_string;
1824 }
1825
1826 /* convert from unicode to utf8 */
zbx_unicode_to_utf8_static(const wchar_t * wide_string,char * utf8_string,int utf8_size)1827 char *zbx_unicode_to_utf8_static(const wchar_t *wide_string, char *utf8_string, int utf8_size)
1828 {
1829 /* convert from wide_string to utf8_string */
1830 if (0 == WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, utf8_string, utf8_size, NULL, NULL))
1831 *utf8_string = '\0';
1832
1833 return utf8_string;
1834 }
1835 #endif
1836
zbx_strlower(char * str)1837 void zbx_strlower(char *str)
1838 {
1839 for (; '\0' != *str; str++)
1840 *str = tolower(*str);
1841 }
1842
zbx_strupper(char * str)1843 void zbx_strupper(char *str)
1844 {
1845 for (; '\0' != *str; str++)
1846 *str = toupper(*str);
1847 }
1848
1849 #if defined(_WINDOWS) || defined(__MINGW32__)
1850 #include "log.h"
convert_to_utf8(char * in,size_t in_size,const char * encoding)1851 char *convert_to_utf8(char *in, size_t in_size, const char *encoding)
1852 {
1853 #define STATIC_SIZE 1024
1854 wchar_t wide_string_static[STATIC_SIZE], *wide_string = NULL;
1855 int wide_size;
1856 char *utf8_string = NULL;
1857 int utf8_size;
1858 unsigned int codepage;
1859 int bom_detected = 0;
1860
1861 /* try to guess encoding using BOM if it exists */
1862 if (3 <= in_size && 0 == strncmp("\xef\xbb\xbf", in, 3))
1863 {
1864 bom_detected = 1;
1865
1866 if ('\0' == *encoding)
1867 encoding = "UTF-8";
1868 }
1869 else if (2 <= in_size && 0 == strncmp("\xff\xfe", in, 2))
1870 {
1871 bom_detected = 1;
1872
1873 if ('\0' == *encoding)
1874 encoding = "UTF-16";
1875 }
1876 else if (2 <= in_size && 0 == strncmp("\xfe\xff", in, 2))
1877 {
1878 bom_detected = 1;
1879
1880 if ('\0' == *encoding)
1881 encoding = "UNICODEFFFE";
1882 }
1883
1884 if ('\0' == *encoding || FAIL == get_codepage(encoding, &codepage))
1885 {
1886 utf8_size = (int)in_size + 1;
1887 utf8_string = zbx_malloc(utf8_string, utf8_size);
1888 memcpy(utf8_string, in, in_size);
1889 utf8_string[in_size] = '\0';
1890 return utf8_string;
1891 }
1892
1893 zabbix_log(LOG_LEVEL_DEBUG, "convert_to_utf8() in_size:%d encoding:'%s' codepage:%u", in_size, encoding,
1894 codepage);
1895
1896 if (65001 == codepage)
1897 {
1898 /* remove BOM */
1899 if (bom_detected)
1900 in += 3;
1901 }
1902
1903 if (1200 == codepage) /* Unicode UTF-16, little-endian byte order */
1904 {
1905 wide_size = (int)in_size / 2;
1906
1907 /* remove BOM */
1908 if (bom_detected)
1909 {
1910 in += 2;
1911 wide_size--;
1912 }
1913
1914 wide_string = (wchar_t *)in;
1915
1916 }
1917 else if (1201 == codepage) /* unicodeFFFE UTF-16, big-endian byte order */
1918 {
1919 wchar_t *wide_string_be;
1920 int i;
1921
1922 wide_size = (int)in_size / 2;
1923
1924 /* remove BOM */
1925 if (bom_detected)
1926 {
1927 in += 2;
1928 wide_size--;
1929 }
1930
1931 wide_string_be = (wchar_t *)in;
1932
1933 if (wide_size > STATIC_SIZE)
1934 wide_string = (wchar_t *)zbx_malloc(wide_string, (size_t)wide_size * sizeof(wchar_t));
1935 else
1936 wide_string = wide_string_static;
1937
1938 /* convert from big-endian 'in' to little-endian 'wide_string' */
1939 for (i = 0; i < wide_size; i++)
1940 wide_string[i] = ((wide_string_be[i] << 8) & 0xff00) | ((wide_string_be[i] >> 8) & 0xff);
1941 }
1942 else
1943 {
1944 wide_size = MultiByteToWideChar(codepage, 0, in, (int)in_size, NULL, 0);
1945
1946 if (wide_size > STATIC_SIZE)
1947 wide_string = (wchar_t *)zbx_malloc(wide_string, (size_t)wide_size * sizeof(wchar_t));
1948 else
1949 wide_string = wide_string_static;
1950
1951 /* convert from 'in' to 'wide_string' */
1952 MultiByteToWideChar(codepage, 0, in, (int)in_size, wide_string, wide_size);
1953 }
1954
1955 utf8_size = WideCharToMultiByte(CP_UTF8, 0, wide_string, wide_size, NULL, 0, NULL, NULL);
1956 utf8_string = (char *)zbx_malloc(utf8_string, (size_t)utf8_size + 1/* '\0' */);
1957
1958 /* convert from 'wide_string' to 'utf8_string' */
1959 WideCharToMultiByte(CP_UTF8, 0, wide_string, wide_size, utf8_string, utf8_size, NULL, NULL);
1960 utf8_string[utf8_size] = '\0';
1961
1962 if (wide_string != wide_string_static && wide_string != (wchar_t *)in)
1963 zbx_free(wide_string);
1964
1965 return utf8_string;
1966 }
1967 #elif defined(HAVE_ICONV)
convert_to_utf8(char * in,size_t in_size,const char * encoding)1968 char *convert_to_utf8(char *in, size_t in_size, const char *encoding)
1969 {
1970 iconv_t cd;
1971 size_t in_size_left, out_size_left, sz, out_alloc = 0;
1972 const char to_code[] = "UTF-8";
1973 char *out = NULL, *p;
1974
1975 out_alloc = in_size + 1;
1976 p = out = (char *)zbx_malloc(out, out_alloc);
1977
1978 /* try to guess encoding using BOM if it exists */
1979 if ('\0' == *encoding)
1980 {
1981 if (3 <= in_size && 0 == strncmp("\xef\xbb\xbf", in, 3))
1982 {
1983 encoding = "UTF-8";
1984 }
1985 else if (2 <= in_size && 0 == strncmp("\xff\xfe", in, 2))
1986 {
1987 encoding = "UTF-16LE";
1988 }
1989 else if (2 <= in_size && 0 == strncmp("\xfe\xff", in, 2))
1990 {
1991 encoding = "UTF-16BE";
1992 }
1993 }
1994
1995 if ('\0' == *encoding || (iconv_t)-1 == (cd = iconv_open(to_code, encoding)))
1996 {
1997 memcpy(out, in, in_size);
1998 out[in_size] = '\0';
1999 return out;
2000 }
2001
2002 in_size_left = in_size;
2003 out_size_left = out_alloc - 1;
2004
2005 while ((size_t)(-1) == iconv(cd, &in, &in_size_left, &p, &out_size_left))
2006 {
2007 if (E2BIG != errno)
2008 break;
2009
2010 sz = (size_t)(p - out);
2011 out_alloc += in_size;
2012 out_size_left += in_size;
2013 p = out = (char *)zbx_realloc(out, out_alloc);
2014 p += sz;
2015 }
2016
2017 *p = '\0';
2018
2019 iconv_close(cd);
2020
2021 /* remove BOM */
2022 if (3 <= p - out && 0 == strncmp("\xef\xbb\xbf", out, 3))
2023 memmove(out, out + 3, (size_t)(p - out - 2));
2024
2025 return out;
2026 }
2027 #endif /* HAVE_ICONV */
2028
zbx_strlen_utf8(const char * text)2029 size_t zbx_strlen_utf8(const char *text)
2030 {
2031 size_t n = 0;
2032
2033 while ('\0' != *text)
2034 {
2035 if (0x80 != (0xc0 & *text++))
2036 n++;
2037 }
2038
2039 return n;
2040 }
2041
2042 /******************************************************************************
2043 * *
2044 * Function: zbx_utf8_char_len *
2045 * *
2046 * Purpose: Returns the size (in bytes) of an UTF-8 encoded character or 0 *
2047 * if the character is not a valid UTF-8. *
2048 * *
2049 * Parameters: text - [IN] pointer to the 1st byte of UTF-8 character *
2050 * *
2051 ******************************************************************************/
zbx_utf8_char_len(const char * text)2052 size_t zbx_utf8_char_len(const char *text)
2053 {
2054 if (0 == (*text & 0x80)) /* ASCII */
2055 return 1;
2056 else if (0xc0 == (*text & 0xe0)) /* 11000010-11011111 starts a 2-byte sequence */
2057 return 2;
2058 else if (0xe0 == (*text & 0xf0)) /* 11100000-11101111 starts a 3-byte sequence */
2059 return 3;
2060 else if (0xf0 == (*text & 0xf8)) /* 11110000-11110100 starts a 4-byte sequence */
2061 return 4;
2062 #if ZBX_MAX_BYTES_IN_UTF8_CHAR != 4
2063 # error "zbx_utf8_char_len() is not synchronized with ZBX_MAX_BYTES_IN_UTF8_CHAR"
2064 #endif
2065 return 0; /* not a valid UTF-8 character */
2066 }
2067
2068 /******************************************************************************
2069 * *
2070 * Function: zbx_strlen_utf8_nchars *
2071 * *
2072 * Purpose: calculates number of bytes in utf8 text limited by utf8_maxlen *
2073 * characters *
2074 * *
2075 ******************************************************************************/
zbx_strlen_utf8_nchars(const char * text,size_t utf8_maxlen)2076 size_t zbx_strlen_utf8_nchars(const char *text, size_t utf8_maxlen)
2077 {
2078 size_t sz = 0, csz = 0;
2079 const char *next;
2080
2081 while ('\0' != *text && 0 < utf8_maxlen && 0 != (csz = zbx_utf8_char_len(text)))
2082 {
2083 next = text + csz;
2084 while (next > text)
2085 {
2086 if ('\0' == *text++)
2087 return sz;
2088 }
2089 sz += csz;
2090 utf8_maxlen--;
2091 }
2092
2093 return sz;
2094 }
2095
2096 /******************************************************************************
2097 * *
2098 * Function: zbx_strlen_utf8_nbytes *
2099 * *
2100 * Purpose: calculates number of bytes in utf8 text limited by maxlen bytes *
2101 * *
2102 ******************************************************************************/
zbx_strlen_utf8_nbytes(const char * text,size_t maxlen)2103 size_t zbx_strlen_utf8_nbytes(const char *text, size_t maxlen)
2104 {
2105 size_t sz;
2106
2107 sz = strlen(text);
2108
2109 if (sz > maxlen)
2110 {
2111 sz = maxlen;
2112
2113 /* ensure that the string is not cut in the middle of UTF-8 sequence */
2114 while (0x80 == (0xc0 & text[sz]) && 0 < sz)
2115 sz--;
2116 }
2117
2118 return sz;
2119 }
2120
2121 /******************************************************************************
2122 * *
2123 * Function: zbx_charcount_utf8_nbytes *
2124 * *
2125 * Purpose: calculates number of chars in utf8 text limited by maxlen bytes *
2126 * *
2127 ******************************************************************************/
zbx_charcount_utf8_nbytes(const char * text,size_t maxlen)2128 size_t zbx_charcount_utf8_nbytes(const char *text, size_t maxlen)
2129 {
2130 size_t n = 0;
2131
2132 maxlen = zbx_strlen_utf8_nbytes(text, maxlen);
2133
2134 while ('\0' != *text && maxlen > 0)
2135 {
2136 if (0x80 != (0xc0 & *text++))
2137 n++;
2138
2139 maxlen--;
2140 }
2141
2142 return n;
2143 }
2144
2145 /******************************************************************************
2146 * *
2147 * Function: zbx_is_utf8 *
2148 * *
2149 * Purpose: check UTF-8 sequences *
2150 * *
2151 * Parameters: text - [IN] pointer to the string *
2152 * *
2153 * Return value: SUCCEED if string is valid or FAIL otherwise *
2154 * *
2155 ******************************************************************************/
zbx_is_utf8(const char * text)2156 int zbx_is_utf8(const char *text)
2157 {
2158 unsigned int utf32;
2159 unsigned char *utf8;
2160 size_t i, mb_len, expecting_bytes = 0;
2161
2162 while ('\0' != *text)
2163 {
2164 /* single ASCII character */
2165 if (0 == (*text & 0x80))
2166 {
2167 text++;
2168 continue;
2169 }
2170
2171 /* unexpected continuation byte or invalid UTF-8 bytes '\xfe' & '\xff' */
2172 if (0x80 == (*text & 0xc0) || 0xfe == (*text & 0xfe))
2173 return FAIL;
2174
2175 /* multibyte sequence */
2176
2177 utf8 = (unsigned char *)text;
2178
2179 if (0xc0 == (*text & 0xe0)) /* 2-bytes multibyte sequence */
2180 expecting_bytes = 1;
2181 else if (0xe0 == (*text & 0xf0)) /* 3-bytes multibyte sequence */
2182 expecting_bytes = 2;
2183 else if (0xf0 == (*text & 0xf8)) /* 4-bytes multibyte sequence */
2184 expecting_bytes = 3;
2185 else if (0xf8 == (*text & 0xfc)) /* 5-bytes multibyte sequence */
2186 expecting_bytes = 4;
2187 else if (0xfc == (*text & 0xfe)) /* 6-bytes multibyte sequence */
2188 expecting_bytes = 5;
2189
2190 mb_len = expecting_bytes + 1;
2191 text++;
2192
2193 for (; 0 != expecting_bytes; expecting_bytes--)
2194 {
2195 /* not a continuation byte */
2196 if (0x80 != (*text++ & 0xc0))
2197 return FAIL;
2198 }
2199
2200 /* overlong sequence */
2201 if (0xc0 == (utf8[0] & 0xfe) ||
2202 (0xe0 == utf8[0] && 0x00 == (utf8[1] & 0x20)) ||
2203 (0xf0 == utf8[0] && 0x00 == (utf8[1] & 0x30)) ||
2204 (0xf8 == utf8[0] && 0x00 == (utf8[1] & 0x38)) ||
2205 (0xfc == utf8[0] && 0x00 == (utf8[1] & 0x3c)))
2206 {
2207 return FAIL;
2208 }
2209
2210 utf32 = 0;
2211
2212 if (0xc0 == (utf8[0] & 0xe0))
2213 utf32 = utf8[0] & 0x1f;
2214 else if (0xe0 == (utf8[0] & 0xf0))
2215 utf32 = utf8[0] & 0x0f;
2216 else if (0xf0 == (utf8[0] & 0xf8))
2217 utf32 = utf8[0] & 0x07;
2218 else if (0xf8 == (utf8[0] & 0xfc))
2219 utf32 = utf8[0] & 0x03;
2220 else if (0xfc == (utf8[0] & 0xfe))
2221 utf32 = utf8[0] & 0x01;
2222
2223 for (i = 1; i < mb_len; i++)
2224 {
2225 utf32 <<= 6;
2226 utf32 += utf8[i] & 0x3f;
2227 }
2228
2229 /* according to the Unicode standard the high and low
2230 * surrogate halves used by UTF-16 (U+D800 through U+DFFF)
2231 * and values above U+10FFFF are not legal
2232 */
2233 if (utf32 > 0x10ffff || 0xd800 == (utf32 & 0xf800))
2234 return FAIL;
2235 }
2236
2237 return SUCCEED;
2238 }
2239
2240 /******************************************************************************
2241 * *
2242 * Function: zbx_replace_invalid_utf8 *
2243 * *
2244 * Purpose: replace invalid UTF-8 sequences of bytes with '?' character *
2245 * *
2246 * Parameters: text - [IN/OUT] pointer to the first char *
2247 * *
2248 ******************************************************************************/
zbx_replace_invalid_utf8(char * text)2249 void zbx_replace_invalid_utf8(char *text)
2250 {
2251 char *out = text;
2252
2253 while ('\0' != *text)
2254 {
2255 if (0 == (*text & 0x80)) /* single ASCII character */
2256 *out++ = *text++;
2257 else if (0x80 == (*text & 0xc0) || /* unexpected continuation byte */
2258 0xfe == (*text & 0xfe)) /* invalid UTF-8 bytes '\xfe' & '\xff' */
2259 {
2260 *out++ = ZBX_UTF8_REPLACE_CHAR;
2261 text++;
2262 }
2263 else /* multibyte sequence */
2264 {
2265 unsigned int utf32;
2266 unsigned char *utf8 = (unsigned char *)out;
2267 size_t i, mb_len, expecting_bytes = 0;
2268 int ret = SUCCEED;
2269
2270 if (0xc0 == (*text & 0xe0)) /* 2-bytes multibyte sequence */
2271 expecting_bytes = 1;
2272 else if (0xe0 == (*text & 0xf0)) /* 3-bytes multibyte sequence */
2273 expecting_bytes = 2;
2274 else if (0xf0 == (*text & 0xf8)) /* 4-bytes multibyte sequence */
2275 expecting_bytes = 3;
2276 else if (0xf8 == (*text & 0xfc)) /* 5-bytes multibyte sequence */
2277 expecting_bytes = 4;
2278 else if (0xfc == (*text & 0xfe)) /* 6-bytes multibyte sequence */
2279 expecting_bytes = 5;
2280
2281 *out++ = *text++;
2282
2283 for (; 0 != expecting_bytes; expecting_bytes--)
2284 {
2285 if (0x80 != (*text & 0xc0)) /* not a continuation byte */
2286 {
2287 ret = FAIL;
2288 break;
2289 }
2290
2291 *out++ = *text++;
2292 }
2293
2294 mb_len = out - (char *)utf8;
2295
2296 if (SUCCEED == ret)
2297 {
2298 if (0xc0 == (utf8[0] & 0xfe) || /* overlong sequence */
2299 (0xe0 == utf8[0] && 0x00 == (utf8[1] & 0x20)) ||
2300 (0xf0 == utf8[0] && 0x00 == (utf8[1] & 0x30)) ||
2301 (0xf8 == utf8[0] && 0x00 == (utf8[1] & 0x38)) ||
2302 (0xfc == utf8[0] && 0x00 == (utf8[1] & 0x3c)))
2303 {
2304 ret = FAIL;
2305 }
2306 }
2307
2308 if (SUCCEED == ret)
2309 {
2310 utf32 = 0;
2311
2312 if (0xc0 == (utf8[0] & 0xe0))
2313 utf32 = utf8[0] & 0x1f;
2314 else if (0xe0 == (utf8[0] & 0xf0))
2315 utf32 = utf8[0] & 0x0f;
2316 else if (0xf0 == (utf8[0] & 0xf8))
2317 utf32 = utf8[0] & 0x07;
2318 else if (0xf8 == (utf8[0] & 0xfc))
2319 utf32 = utf8[0] & 0x03;
2320 else if (0xfc == (utf8[0] & 0xfe))
2321 utf32 = utf8[0] & 0x01;
2322
2323 for (i = 1; i < mb_len; i++)
2324 {
2325 utf32 <<= 6;
2326 utf32 += utf8[i] & 0x3f;
2327 }
2328
2329 /* according to the Unicode standard the high and low
2330 * surrogate halves used by UTF-16 (U+D800 through U+DFFF)
2331 * and values above U+10FFFF are not legal
2332 */
2333 if (utf32 > 0x10ffff || 0xd800 == (utf32 & 0xf800))
2334 ret = FAIL;
2335 }
2336
2337 if (SUCCEED != ret)
2338 {
2339 out -= mb_len;
2340 *out++ = ZBX_UTF8_REPLACE_CHAR;
2341 }
2342 }
2343 }
2344
2345 *out = '\0';
2346 }
2347
2348 /******************************************************************************
2349 * *
2350 * Function: utf8_decode_3byte_sequence *
2351 * *
2352 * Purpose: decodes 3-byte utf-8 sequence *
2353 * *
2354 * Parameters: ptr - [IN] pointer to the 3 byte sequence *
2355 * out - [OUT] the decoded value *
2356 * *
2357 * Return value: SUCCEED on success *
2358 * FAIL on failure *
2359 * *
2360 ******************************************************************************/
utf8_decode_3byte_sequence(const char * ptr,zbx_uint32_t * out)2361 static int utf8_decode_3byte_sequence(const char *ptr, zbx_uint32_t *out)
2362 {
2363 *out = ((unsigned char)*ptr++ & 0xF) << 12;
2364 if (0x80 != (*ptr & 0xC0))
2365 return FAIL;
2366
2367 *out |= ((unsigned char)*ptr++ & 0x3F) << 6;
2368 if (0x80 != (*ptr & 0xC0))
2369 return FAIL;
2370
2371 *out |= ((unsigned char)*ptr & 0x3F);
2372 return SUCCEED;
2373 }
2374
2375 /******************************************************************************
2376 * *
2377 * Function: zbx_cesu8_to_utf8 *
2378 * *
2379 * Purpose: convert cesu8 encoded string to utf8 *
2380 * *
2381 * Parameters: cesu8 - [IN] pointer to the first char of NULL terminated CESU8*
2382 * string *
2383 * utf8 - [OUT] on success, pointer to pointer to the first char *
2384 * of allocated NULL terminated UTF8 string *
2385 * *
2386 * Return value: SUCCEED on success *
2387 * FAIL on failure *
2388 * *
2389 ******************************************************************************/
zbx_cesu8_to_utf8(const char * cesu8,char ** utf8)2390 int zbx_cesu8_to_utf8(const char *cesu8, char **utf8)
2391 {
2392 const char *in, *end;
2393 char *out;
2394 size_t len;
2395
2396 len = strlen(cesu8);
2397 out = *utf8 = zbx_malloc(*utf8, len + 1);
2398 end = cesu8 + len;
2399
2400 for (in = cesu8; in < end;)
2401 {
2402 if (0x7f >= (unsigned char)*in)
2403 {
2404 *out++ = *in++;
2405 continue;
2406 }
2407
2408 if (0xdf >= (unsigned char)*in)
2409 {
2410 if (2 > end - in)
2411 goto fail;
2412
2413 *out++ = *in++;
2414 *out++ = *in++;
2415 continue;
2416 }
2417
2418 if (0xef >= (unsigned char)*in)
2419 {
2420 zbx_uint32_t c1, c2, u;
2421
2422 if (3 > end - in || FAIL == utf8_decode_3byte_sequence(in, &c1))
2423 goto fail;
2424
2425 if (0xd800 > c1 || 0xdbff < c1)
2426 {
2427 /* normal 3-byte sequence */
2428 *out++ = *in++;
2429 *out++ = *in++;
2430 *out++ = *in++;
2431 continue;
2432 }
2433
2434 /* decode unicode supplementary character represented as surrogate pair */
2435 in += 3;
2436 if (3 > end - in || FAIL == utf8_decode_3byte_sequence(in, &c2) || 0xdc00 > c2 || 0xdfff < c2)
2437 goto fail;
2438
2439 u = 0x10000 + ((((zbx_uint32_t)c1 & 0x3ff) << 10) | (c2 & 0x3ff));
2440 *out++ = 0xf0 | u >> 18;
2441 *out++ = 0x80 | (u >> 12 & 0x3f);
2442 *out++ = 0x80 | (u >> 6 & 0x3f);
2443 *out++ = 0x80 | (u & 0x3f);
2444 in += 3;
2445 continue;
2446 }
2447
2448 /* the four-byte UTF-8 style supplementary character sequence is not supported by CESU-8 */
2449 goto fail;
2450 }
2451 *out = '\0';
2452 return SUCCEED;
2453 fail:
2454 zbx_free(*utf8);
2455 return FAIL;
2456 }
2457
dos2unix(char * str)2458 void dos2unix(char *str)
2459 {
2460 char *o = str;
2461
2462 while ('\0' != *str)
2463 {
2464 if ('\r' == str[0] && '\n' == str[1]) /* CR+LF (Windows) */
2465 str++;
2466 *o++ = *str++;
2467 }
2468 *o = '\0';
2469 }
2470
is_ascii_string(const char * str)2471 int is_ascii_string(const char *str)
2472 {
2473 while ('\0' != *str)
2474 {
2475 if (0 != ((1 << 7) & *str)) /* check for range 0..127 */
2476 return FAIL;
2477
2478 str++;
2479 }
2480
2481 return SUCCEED;
2482 }
2483
2484 /******************************************************************************
2485 * *
2486 * Function: str_linefeed *
2487 * *
2488 * Purpose: wrap long string at specified position with linefeeds *
2489 * *
2490 * Parameters: src - input string *
2491 * maxline - maximum length of a line *
2492 * delim - delimiter to use as linefeed (default "\n" if NULL) *
2493 * *
2494 * Return value: newly allocated copy of input string with linefeeds *
2495 * *
2496 * Author: Vladimir Levijev *
2497 * *
2498 * Comments: allocates memory *
2499 * *
2500 ******************************************************************************/
str_linefeed(const char * src,size_t maxline,const char * delim)2501 char *str_linefeed(const char *src, size_t maxline, const char *delim)
2502 {
2503 size_t src_size, dst_size, delim_size, left;
2504 int feeds; /* number of feeds */
2505 char *dst = NULL; /* output with linefeeds */
2506 const char *p_src;
2507 char *p_dst;
2508
2509 assert(NULL != src);
2510 assert(0 < maxline);
2511
2512 /* default delimiter */
2513 if (NULL == delim)
2514 delim = "\n";
2515
2516 src_size = strlen(src);
2517 delim_size = strlen(delim);
2518
2519 /* make sure we don't feed the last line */
2520 feeds = (int)(src_size / maxline - (0 != src_size % maxline || 0 == src_size ? 0 : 1));
2521
2522 left = src_size - feeds * maxline;
2523 dst_size = src_size + feeds * delim_size + 1;
2524
2525 /* allocate memory for output */
2526 dst = (char *)zbx_malloc(dst, dst_size);
2527
2528 p_src = src;
2529 p_dst = dst;
2530
2531 /* copy chunks appending linefeeds */
2532 while (0 < feeds--)
2533 {
2534 memcpy(p_dst, p_src, maxline);
2535 p_src += maxline;
2536 p_dst += maxline;
2537
2538 memcpy(p_dst, delim, delim_size);
2539 p_dst += delim_size;
2540 }
2541
2542 if (0 < left)
2543 {
2544 /* copy what's left */
2545 memcpy(p_dst, p_src, left);
2546 p_dst += left;
2547 }
2548
2549 *p_dst = '\0';
2550
2551 return dst;
2552 }
2553
2554 /******************************************************************************
2555 * *
2556 * Function: zbx_strarr_init *
2557 * *
2558 * Purpose: initialize dynamic string array *
2559 * *
2560 * Parameters: arr - a pointer to array of strings *
2561 * *
2562 * Return value: *
2563 * *
2564 * Author: Vladimir Levijev *
2565 * *
2566 * Comments: allocates memory, calls assert() if that fails *
2567 * *
2568 ******************************************************************************/
zbx_strarr_init(char *** arr)2569 void zbx_strarr_init(char ***arr)
2570 {
2571 *arr = (char **)zbx_malloc(*arr, sizeof(char *));
2572 **arr = NULL;
2573 }
2574
2575 /******************************************************************************
2576 * *
2577 * Function: zbx_strarr_add *
2578 * *
2579 * Purpose: add a string to dynamic string array *
2580 * *
2581 * Parameters: arr - a pointer to array of strings *
2582 * entry - string to add *
2583 * *
2584 * Return value: *
2585 * *
2586 * Author: Vladimir Levijev *
2587 * *
2588 * Comments: allocates memory, calls assert() if that fails *
2589 * *
2590 ******************************************************************************/
zbx_strarr_add(char *** arr,const char * entry)2591 void zbx_strarr_add(char ***arr, const char *entry)
2592 {
2593 int i;
2594
2595 assert(entry);
2596
2597 for (i = 0; NULL != (*arr)[i]; i++)
2598 ;
2599
2600 *arr = (char **)zbx_realloc(*arr, sizeof(char *) * (i + 2));
2601
2602 (*arr)[i] = zbx_strdup((*arr)[i], entry);
2603 (*arr)[++i] = NULL;
2604 }
2605
2606 /******************************************************************************
2607 * *
2608 * Function: zbx_strarr_free *
2609 * *
2610 * Purpose: free dynamic string array memory *
2611 * *
2612 * Parameters: arr - array of strings *
2613 * *
2614 * Return value: *
2615 * *
2616 * Author: Vladimir Levijev *
2617 * *
2618 ******************************************************************************/
zbx_strarr_free(char ** arr)2619 void zbx_strarr_free(char **arr)
2620 {
2621 char **p;
2622
2623 for (p = arr; NULL != *p; p++)
2624 zbx_free(*p);
2625 zbx_free(arr);
2626 }
2627
2628 /******************************************************************************
2629 * *
2630 * Function: zbx_replace_string *
2631 * *
2632 * Purpose: replace data block with 'value' *
2633 * *
2634 * Parameters: data - [IN/OUT] pointer to the string *
2635 * l - [IN] left position of the block *
2636 * r - [IN/OUT] right position of the block *
2637 * value - [IN] the string to replace the block with *
2638 * *
2639 * Author: Alexander Vladishev *
2640 * *
2641 ******************************************************************************/
zbx_replace_string(char ** data,size_t l,size_t * r,const char * value)2642 void zbx_replace_string(char **data, size_t l, size_t *r, const char *value)
2643 {
2644 size_t sz_data, sz_block, sz_value;
2645 char *src, *dst;
2646
2647 sz_value = strlen(value);
2648 sz_block = *r - l + 1;
2649
2650 if (sz_value != sz_block)
2651 {
2652 sz_data = *r + strlen(*data + *r);
2653 sz_data += sz_value - sz_block;
2654
2655 if (sz_value > sz_block)
2656 *data = (char *)zbx_realloc(*data, sz_data + 1);
2657
2658 src = *data + l + sz_block;
2659 dst = *data + l + sz_value;
2660
2661 memmove(dst, src, sz_data - l - sz_value + 1);
2662
2663 *r = l + sz_value - 1;
2664 }
2665
2666 memcpy(&(*data)[l], value, sz_value);
2667 }
2668
2669 /******************************************************************************
2670 * *
2671 * Function: zbx_trim_str_list *
2672 * *
2673 * Purpose: remove whitespace surrounding a string list item delimiters *
2674 * *
2675 * Parameters: list - the list (a string containing items separated by *
2676 * delimiter) *
2677 * delimiter - the list delimiter *
2678 * *
2679 * Author: Andris Zeila *
2680 * *
2681 ******************************************************************************/
zbx_trim_str_list(char * list,char delimiter)2682 void zbx_trim_str_list(char *list, char delimiter)
2683 {
2684 /* NB! strchr(3): "terminating null byte is considered part of the string" */
2685 const char *whitespace = " \t";
2686 char *out, *in;
2687
2688 out = in = list;
2689
2690 while ('\0' != *in)
2691 {
2692 /* trim leading spaces from list item */
2693 while ('\0' != *in && NULL != strchr(whitespace, *in))
2694 in++;
2695
2696 /* copy list item */
2697 while (delimiter != *in && '\0' != *in)
2698 *out++ = *in++;
2699
2700 /* trim trailing spaces from list item */
2701 if (out > list)
2702 {
2703 while (NULL != strchr(whitespace, *(--out)))
2704 ;
2705 out++;
2706 }
2707 if (delimiter == *in)
2708 *out++ = *in++;
2709 }
2710 *out = '\0';
2711 }
2712
2713 /******************************************************************************
2714 * *
2715 * Function: zbx_strcmp_null *
2716 * *
2717 * Purpose: *
2718 * compares two strings where any of them can be a NULL pointer *
2719 * *
2720 * Parameters: same as strcmp() except NULL values are allowed *
2721 * *
2722 * Return value: same as strcmp() *
2723 * *
2724 * Comments: NULL is less than any string *
2725 * *
2726 ******************************************************************************/
zbx_strcmp_null(const char * s1,const char * s2)2727 int zbx_strcmp_null(const char *s1, const char *s2)
2728 {
2729 if (NULL == s1)
2730 return NULL == s2 ? 0 : -1;
2731
2732 if (NULL == s2)
2733 return 1;
2734
2735 return strcmp(s1, s2);
2736 }
2737
2738 /******************************************************************************
2739 * *
2740 * Function: zbx_user_macro_parse *
2741 * *
2742 * Purpose: *
2743 * parses user macro and finds its end position and context location *
2744 * *
2745 * Parameters: *
2746 * macro - [IN] the macro to parse *
2747 * macro_r - [OUT] the position of ending '}' character *
2748 * context_l - [OUT] the position of context start character (first non *
2749 * space character after context separator ':') *
2750 * 0 if macro does not have context specified. *
2751 * context_r - [OUT] the position of context end character (either the *
2752 * ending '"' for quoted context values or the last *
2753 * character before the ending '}' character) *
2754 * 0 if macro does not have context specified. *
2755 * context_op - [OUT] the context matching operator (optional): *
2756 * CONDITION_OPERATOR_EQUAL *
2757 * CONDITION_OPERATOR_REGEXP *
2758 * *
2759 * Return value: *
2760 * SUCCEED - the macro was parsed successfully. *
2761 * FAIL - the macro parsing failed, the content of output variables *
2762 * is not defined. *
2763 * *
2764 ******************************************************************************/
zbx_user_macro_parse(const char * macro,int * macro_r,int * context_l,int * context_r,unsigned char * context_op)2765 int zbx_user_macro_parse(const char *macro, int *macro_r, int *context_l, int *context_r, unsigned char *context_op)
2766 {
2767 int i;
2768
2769 /* find the end of macro name by skipping {$ characters and iterating through */
2770 /* valid macro name characters */
2771 for (i = 2; SUCCEED == is_macro_char(macro[i]); i++)
2772 ;
2773
2774 /* check for empty macro name */
2775 if (2 == i)
2776 return FAIL;
2777
2778 if ('}' == macro[i])
2779 {
2780 /* no macro context specified, parsing done */
2781 *macro_r = i;
2782 *context_l = 0;
2783 *context_r = 0;
2784
2785 if (NULL != context_op)
2786 *context_op = CONDITION_OPERATOR_EQUAL;
2787
2788 return SUCCEED;
2789 }
2790
2791 /* fail if the next character is not a macro context separator */
2792 if (':' != macro[i])
2793 return FAIL;
2794
2795 i++;
2796 if (NULL != context_op)
2797 {
2798 if (0 == strncmp(macro + i, ZBX_MACRO_REGEX_PREFIX, ZBX_CONST_STRLEN(ZBX_MACRO_REGEX_PREFIX)))
2799 {
2800 *context_op = CONDITION_OPERATOR_REGEXP;
2801 i += ZBX_CONST_STRLEN(ZBX_MACRO_REGEX_PREFIX);
2802 }
2803 else
2804 *context_op = CONDITION_OPERATOR_EQUAL;
2805 }
2806
2807 /* skip the whitespace after macro context separator */
2808 while (' ' == macro[i])
2809 i++;
2810
2811 *context_l = i;
2812
2813 if ('"' == macro[i])
2814 {
2815 i++;
2816
2817 /* process quoted context */
2818 for (; '"' != macro[i]; i++)
2819 {
2820 if ('\0' == macro[i])
2821 return FAIL;
2822
2823 if ('\\' == macro[i] && '"' == macro[i + 1])
2824 i++;
2825 }
2826
2827 *context_r = i;
2828
2829 while (' ' == macro[++i])
2830 ;
2831 }
2832 else
2833 {
2834 /* process unquoted context */
2835 for (; '}' != macro[i]; i++)
2836 {
2837 if ('\0' == macro[i])
2838 return FAIL;
2839 }
2840
2841 *context_r = i - 1;
2842 }
2843
2844 if ('}' != macro[i])
2845 return FAIL;
2846
2847 *macro_r = i;
2848
2849 return SUCCEED;
2850 }
2851
2852 /******************************************************************************
2853 * *
2854 * Function: zbx_user_macro_parse_dyn *
2855 * *
2856 * Purpose: *
2857 * parses user macro {$MACRO:<context>} into {$MACRO} and <context> *
2858 * strings *
2859 * *
2860 * Parameters: *
2861 * macro - [IN] the macro to parse *
2862 * name - [OUT] the macro name without context *
2863 * context - [OUT] the unquoted macro context, NULL for macros without *
2864 * context *
2865 * length - [OUT] the length of parsed macro (optional) *
2866 * context_op - [OUT] the context matching operator (optional): *
2867 * CONDITION_OPERATOR_EQUAL *
2868 * CONDITION_OPERATOR_REGEXP *
2869 * *
2870 * Return value: *
2871 * SUCCEED - the macro was parsed successfully *
2872 * FAIL - the macro parsing failed, invalid parameter syntax *
2873 * *
2874 ******************************************************************************/
zbx_user_macro_parse_dyn(const char * macro,char ** name,char ** context,int * length,unsigned char * context_op)2875 int zbx_user_macro_parse_dyn(const char *macro, char **name, char **context, int *length, unsigned char *context_op)
2876 {
2877 const char *ptr;
2878 int macro_r, context_l, context_r;
2879 size_t len;
2880
2881 if (SUCCEED != zbx_user_macro_parse(macro, ¯o_r, &context_l, &context_r, context_op))
2882 return FAIL;
2883
2884 zbx_free(*context);
2885
2886 if (0 != context_l)
2887 {
2888 ptr = macro + context_l;
2889
2890 /* find the context separator ':' by stripping spaces before context */
2891 while (' ' == *(--ptr))
2892 ;
2893
2894 /* remove regex: prefix from macro name for regex contexts */
2895 if (NULL != context_op && CONDITION_OPERATOR_REGEXP == *context_op)
2896 ptr -= ZBX_CONST_STRLEN(ZBX_MACRO_REGEX_PREFIX);
2897
2898 /* extract the macro name and close with '}' character */
2899 len = ptr - macro + 1;
2900 *name = (char *)zbx_realloc(*name, len + 1);
2901 memcpy(*name, macro, len - 1);
2902 (*name)[len - 1] = '}';
2903 (*name)[len] = '\0';
2904
2905 *context = zbx_user_macro_unquote_context_dyn(macro + context_l, context_r - context_l + 1);
2906 }
2907 else
2908 {
2909 *name = (char *)zbx_realloc(*name, macro_r + 2);
2910 zbx_strlcpy(*name, macro, macro_r + 2);
2911 }
2912
2913 if (NULL != length)
2914 *length = macro_r + 1;
2915
2916 return SUCCEED;
2917 }
2918
2919 /******************************************************************************
2920 * *
2921 * Function: zbx_user_macro_unquote_context_dyn *
2922 * *
2923 * Purpose: *
2924 * extracts the macro context unquoting if necessary *
2925 * *
2926 * Parameters: *
2927 * context - [IN] the macro context inside a user macro *
2928 * len - [IN] the macro context length (including quotes for quoted *
2929 * contexts) *
2930 * *
2931 * Return value: *
2932 * A string containing extracted macro context. This string must be freed *
2933 * by the caller. *
2934 * *
2935 ******************************************************************************/
zbx_user_macro_unquote_context_dyn(const char * context,int len)2936 char *zbx_user_macro_unquote_context_dyn(const char *context, int len)
2937 {
2938 int quoted = 0;
2939 char *buffer, *ptr;
2940
2941 ptr = buffer = (char *)zbx_malloc(NULL, len + 1);
2942
2943 if ('"' == *context)
2944 {
2945 quoted = 1;
2946 context++;
2947 len--;
2948 }
2949
2950 while (0 < len)
2951 {
2952 if (1 == quoted && '\\' == *context && '"' == context[1])
2953 {
2954 context++;
2955 len--;
2956 }
2957
2958 *ptr++ = *context++;
2959 len--;
2960 }
2961
2962 if (1 == quoted)
2963 ptr--;
2964
2965 *ptr = '\0';
2966
2967 return buffer;
2968 }
2969
2970 /******************************************************************************
2971 * *
2972 * Function: zbx_user_macro_quote_context_dyn *
2973 * *
2974 * Purpose: *
2975 * quotes user macro context if necessary *
2976 * *
2977 * Parameters: *
2978 * context - [IN] the macro context *
2979 * force_quote - [IN] if non zero then context quoting is enforced *
2980 * *
2981 * Return value: *
2982 * A string containing quoted macro context. This string must be freed by *
2983 * the caller. *
2984 * *
2985 ******************************************************************************/
zbx_user_macro_quote_context_dyn(const char * context,int force_quote)2986 char *zbx_user_macro_quote_context_dyn(const char *context, int force_quote)
2987 {
2988 int len, quotes = 0;
2989 char *buffer, *ptr_buffer;
2990 const char *ptr_context = context;
2991
2992 if ('"' == *ptr_context || ' ' == *ptr_context)
2993 force_quote = 1;
2994
2995 for (; '\0' != *ptr_context; ptr_context++)
2996 {
2997 if ('}' == *ptr_context)
2998 force_quote = 1;
2999
3000 if ('"' == *ptr_context)
3001 quotes++;
3002 }
3003
3004 if (0 == force_quote)
3005 return zbx_strdup(NULL, context);
3006
3007 len = (int)strlen(context) + 2 + quotes;
3008 ptr_buffer = buffer = (char *)zbx_malloc(NULL, len + 1);
3009
3010 *ptr_buffer++ = '"';
3011
3012 while ('\0' != *context)
3013 {
3014 if ('"' == *context)
3015 *ptr_buffer++ = '\\';
3016
3017 *ptr_buffer++ = *context++;
3018 }
3019
3020 *ptr_buffer++ = '"';
3021 *ptr_buffer++ = '\0';
3022
3023 return buffer;
3024 }
3025
3026 /******************************************************************************
3027 * *
3028 * Function: zbx_dyn_escape_shell_single_quote *
3029 * *
3030 * Purpose: escape single quote in shell command arguments *
3031 * *
3032 * Parameters: arg - [IN] the argument to escape *
3033 * *
3034 * Return value: The escaped argument. *
3035 * *
3036 ******************************************************************************/
zbx_dyn_escape_shell_single_quote(const char * arg)3037 char *zbx_dyn_escape_shell_single_quote(const char *arg)
3038 {
3039 int len = 1; /* include terminating zero character */
3040 const char *pin;
3041 char *arg_esc, *pout;
3042
3043 for (pin = arg; '\0' != *pin; pin++)
3044 {
3045 if ('\'' == *pin)
3046 len += 3;
3047 len++;
3048 }
3049
3050 pout = arg_esc = (char *)zbx_malloc(NULL, len);
3051
3052 for (pin = arg; '\0' != *pin; pin++)
3053 {
3054 if ('\'' == *pin)
3055 {
3056 *pout++ = '\'';
3057 *pout++ = '\\';
3058 *pout++ = '\'';
3059 *pout++ = '\'';
3060 }
3061 else
3062 *pout++ = *pin;
3063 }
3064
3065 *pout = '\0';
3066
3067 return arg_esc;
3068 }
3069
3070 /******************************************************************************
3071 * *
3072 * Function: function_parse_name *
3073 * *
3074 * Purpose: parses function name *
3075 * *
3076 * Parameters: expr - [IN] the function expression: func(p1, p2,...) *
3077 * length - [OUT] the function name length or the amount of *
3078 * characters that can be safely skipped *
3079 * *
3080 * Return value: SUCCEED - the function name was successfully parsed *
3081 * FAIL - failed to parse function name *
3082 * *
3083 ******************************************************************************/
function_parse_name(const char * expr,size_t * length)3084 static int function_parse_name(const char *expr, size_t *length)
3085 {
3086 const char *ptr;
3087
3088 for (ptr = expr; SUCCEED == is_function_char(*ptr); ptr++)
3089 ;
3090
3091 *length = ptr - expr;
3092
3093 return ptr != expr && '(' == *ptr ? SUCCEED : FAIL;
3094 }
3095
3096 /******************************************************************************
3097 * *
3098 * Function: zbx_function_param_parse *
3099 * *
3100 * Purpose: parses function parameter *
3101 * *
3102 * Parameters: expr - [IN] pre-validated function parameter list *
3103 * param_pos - [OUT] the parameter position, excluding leading *
3104 * whitespace *
3105 * length - [OUT] the parameter length including trailing *
3106 * whitespace for unquoted parameter *
3107 * sep_pos - [OUT] the parameter separator character *
3108 * (',' or '\0' or ')') position *
3109 * *
3110 ******************************************************************************/
zbx_function_param_parse(const char * expr,size_t * param_pos,size_t * length,size_t * sep_pos)3111 void zbx_function_param_parse(const char *expr, size_t *param_pos, size_t *length, size_t *sep_pos)
3112 {
3113 const char *ptr = expr;
3114
3115 /* skip the leading whitespace */
3116 while (' ' == *ptr)
3117 ptr++;
3118
3119 *param_pos = ptr - expr;
3120
3121 if ('"' == *ptr) /* quoted parameter */
3122 {
3123 for (ptr++; '"' != *ptr || '\\' == *(ptr - 1); ptr++)
3124 ;
3125
3126 *length = ++ptr - expr - *param_pos;
3127
3128 /* skip trailing whitespace to find the next parameter */
3129 while (' ' == *ptr)
3130 ptr++;
3131 }
3132 else /* unquoted parameter */
3133 {
3134 for (ptr = expr; '\0' != *ptr && ')' != *ptr && ',' != *ptr; ptr++)
3135 ;
3136
3137 *length = ptr - expr - *param_pos;
3138 }
3139
3140 *sep_pos = ptr - expr;
3141 }
3142
3143 /******************************************************************************
3144 * *
3145 * Function: zbx_function_param_unquote_dyn *
3146 * *
3147 * Purpose: unquotes function parameter *
3148 * *
3149 * Parameters: param - [IN] the parameter to unquote *
3150 * len - [IN] the parameter length *
3151 * quoted - [OUT] the flag that specifies whether parameter was *
3152 * quoted before extraction *
3153 * *
3154 * Return value: The unquoted parameter. This value must be freed by the *
3155 * caller. *
3156 * *
3157 ******************************************************************************/
zbx_function_param_unquote_dyn(const char * param,size_t len,int * quoted)3158 char *zbx_function_param_unquote_dyn(const char *param, size_t len, int *quoted)
3159 {
3160 char *out;
3161
3162 out = (char *)zbx_malloc(NULL, len + 1);
3163
3164 if (0 == (*quoted = (0 != len && '"' == *param)))
3165 {
3166 /* unquoted parameter - simply copy it */
3167 memcpy(out, param, len);
3168 out[len] = '\0';
3169 }
3170 else
3171 {
3172 /* quoted parameter - remove enclosing " and replace \" with " */
3173 const char *pin;
3174 char *pout = out;
3175
3176 for (pin = param + 1; (size_t)(pin - param) < len - 1; pin++)
3177 {
3178 if ('\\' == pin[0] && '"' == pin[1])
3179 pin++;
3180
3181 *pout++ = *pin;
3182 }
3183
3184 *pout = '\0';
3185 }
3186
3187 return out;
3188 }
3189
3190 /******************************************************************************
3191 * *
3192 * Function: zbx_function_param_quote *
3193 * *
3194 * Purpose: quotes function parameter *
3195 * *
3196 * Parameters: param - [IN/OUT] function parameter *
3197 * forced - [IN] 1 - enclose parameter in " even if it does not *
3198 * contain any special characters *
3199 * 0 - do nothing if the parameter does not *
3200 * contain any special characters *
3201 * *
3202 * Return value: SUCCEED - if parameter was successfully quoted or quoting *
3203 * was not necessary *
3204 * FAIL - if parameter needs to but cannot be quoted due to *
3205 * backslash in the end *
3206 * *
3207 ******************************************************************************/
zbx_function_param_quote(char ** param,int forced)3208 int zbx_function_param_quote(char **param, int forced)
3209 {
3210 size_t sz_src, sz_dst;
3211
3212 if (0 == forced && '"' != **param && ' ' != **param && NULL == strchr(*param, ',') &&
3213 NULL == strchr(*param, ')'))
3214 {
3215 return SUCCEED;
3216 }
3217
3218 if (0 != (sz_src = strlen(*param)) && '\\' == (*param)[sz_src - 1])
3219 return FAIL;
3220
3221 sz_dst = zbx_get_escape_string_len(*param, "\"") + 3;
3222
3223 *param = (char *)zbx_realloc(*param, sz_dst);
3224
3225 (*param)[--sz_dst] = '\0';
3226 (*param)[--sz_dst] = '"';
3227
3228 while (0 < sz_src)
3229 {
3230 (*param)[--sz_dst] = (*param)[--sz_src];
3231 if ('"' == (*param)[sz_src])
3232 (*param)[--sz_dst] = '\\';
3233 }
3234 (*param)[--sz_dst] = '"';
3235
3236 return SUCCEED;
3237 }
3238
3239 /******************************************************************************
3240 * *
3241 * Function: zbx_function_get_param_dyn *
3242 * *
3243 * Purpose: return parameter by index (Nparam) from parameter list (params) *
3244 * *
3245 * Parameters: *
3246 * params - [IN] parameter list *
3247 * Nparam - [IN] requested parameter index (from 1) *
3248 * *
3249 * Return value: *
3250 * NULL - requested parameter missing *
3251 * otherwise - requested parameter *
3252 * *
3253 ******************************************************************************/
zbx_function_get_param_dyn(const char * params,int Nparam)3254 char *zbx_function_get_param_dyn(const char *params, int Nparam)
3255 {
3256 const char *ptr;
3257 size_t sep_pos, params_len;
3258 char *out = NULL;
3259 int idx = 0;
3260
3261 params_len = strlen(params) + 1;
3262
3263 for (ptr = params; ++idx <= Nparam && ptr < params + params_len; ptr += sep_pos + 1)
3264 {
3265 size_t param_pos, param_len;
3266 int quoted;
3267
3268 zbx_function_param_parse(ptr, ¶m_pos, ¶m_len, &sep_pos);
3269
3270 if (idx == Nparam)
3271 out = zbx_function_param_unquote_dyn(ptr + param_pos, param_len, "ed);
3272 }
3273
3274 return out;
3275 }
3276
3277 /******************************************************************************
3278 * *
3279 * Function: function_validate_parameters *
3280 * *
3281 * Purpose: validate parameters and give position of terminator if found and *
3282 * not quoted *
3283 * *
3284 * Parameters: expr - [IN] string to parse that contains parameters *
3285 * *
3286 * terminator - [IN] use ')' if parameters end with *
3287 * parenthesis or '\0' if ends with NULL *
3288 * terminator *
3289 * par_r - [OUT] position of the terminator if found *
3290 * lpp_offset - [OUT] offset of the last parsed parameter *
3291 * lpp_len - [OUT] length of the last parsed parameter *
3292 * *
3293 * Return value: SUCCEED - closing parenthesis was found or other custom *
3294 * terminator and not quoted and return info about a *
3295 * last processed parameter. *
3296 * FAIL - does not look like a valid function parameter *
3297 * list and return info about a last processed *
3298 * parameter. *
3299 * *
3300 ******************************************************************************/
function_validate_parameters(const char * expr,char terminator,size_t * par_r,size_t * lpp_offset,size_t * lpp_len)3301 static int function_validate_parameters(const char *expr, char terminator, size_t *par_r, size_t *lpp_offset,
3302 size_t *lpp_len)
3303 {
3304 #define ZBX_FUNC_PARAM_NEXT 0
3305 #define ZBX_FUNC_PARAM_QUOTED 1
3306 #define ZBX_FUNC_PARAM_UNQUOTED 2
3307 #define ZBX_FUNC_PARAM_POSTQUOTED 3
3308
3309 const char *ptr;
3310 int state = ZBX_FUNC_PARAM_NEXT;
3311
3312 *lpp_offset = 0;
3313
3314 for (ptr = expr; '\0' != *ptr; ptr++)
3315 {
3316 if (terminator == *ptr && ZBX_FUNC_PARAM_QUOTED != state)
3317 {
3318 *par_r = ptr - expr;
3319 return SUCCEED;
3320 }
3321
3322 switch (state)
3323 {
3324 case ZBX_FUNC_PARAM_NEXT:
3325 *lpp_offset = ptr - expr;
3326 if ('"' == *ptr)
3327 state = ZBX_FUNC_PARAM_QUOTED;
3328 else if (' ' != *ptr && ',' != *ptr)
3329 state = ZBX_FUNC_PARAM_UNQUOTED;
3330 break;
3331 case ZBX_FUNC_PARAM_QUOTED:
3332 if ('"' == *ptr && '\\' != *(ptr - 1))
3333 state = ZBX_FUNC_PARAM_POSTQUOTED;
3334 break;
3335 case ZBX_FUNC_PARAM_UNQUOTED:
3336 if (',' == *ptr)
3337 state = ZBX_FUNC_PARAM_NEXT;
3338 break;
3339 case ZBX_FUNC_PARAM_POSTQUOTED:
3340 if (',' == *ptr)
3341 {
3342 state = ZBX_FUNC_PARAM_NEXT;
3343 }
3344 else if (' ' != *ptr)
3345 {
3346 *lpp_len = ptr - (expr + *lpp_offset);
3347 return FAIL;
3348 }
3349 break;
3350 default:
3351 THIS_SHOULD_NEVER_HAPPEN;
3352 }
3353 }
3354
3355 *lpp_len = ptr - (expr + *lpp_offset);
3356
3357 if (terminator == *ptr && ZBX_FUNC_PARAM_QUOTED != state)
3358 {
3359 *par_r = ptr - expr;
3360 return SUCCEED;
3361 }
3362
3363 return FAIL;
3364
3365 #undef ZBX_FUNC_PARAM_NEXT
3366 #undef ZBX_FUNC_PARAM_QUOTED
3367 #undef ZBX_FUNC_PARAM_UNQUOTED
3368 #undef ZBX_FUNC_PARAM_POSTQUOTED
3369 }
3370
3371 /******************************************************************************
3372 * *
3373 * Function: function_match_parenthesis *
3374 * *
3375 * Purpose: given the position of opening function parenthesis find the *
3376 * position of a closing one *
3377 * *
3378 * Parameters: expr - [IN] string to parse *
3379 * par_l - [IN] position of the opening parenthesis *
3380 * par_r - [OUT] position of the closing parenthesis *
3381 * lpp_offset - [OUT] offset of the last parsed parameter *
3382 * lpp_len - [OUT] length of the last parsed parameter *
3383 * *
3384 * Return value: SUCCEED - closing parenthesis was found *
3385 * FAIL - string after par_l does not look like a valid *
3386 * function parameter list *
3387 * *
3388 ******************************************************************************/
function_match_parenthesis(const char * expr,size_t par_l,size_t * par_r,size_t * lpp_offset,size_t * lpp_len)3389 static int function_match_parenthesis(const char *expr, size_t par_l, size_t *par_r, size_t *lpp_offset,
3390 size_t *lpp_len)
3391 {
3392 if (SUCCEED == function_validate_parameters(expr + par_l + 1, ')', par_r, lpp_offset, lpp_len))
3393 {
3394 *par_r += par_l + 1;
3395 return SUCCEED;
3396 }
3397
3398 *lpp_offset += par_l + 1;
3399 return FAIL;
3400 }
3401
3402 /******************************************************************************
3403 * *
3404 * Function: zbx_function_validate_parameters *
3405 * *
3406 * Purpose: validate parameters that end with '\0' *
3407 * *
3408 * Parameters: expr - [IN] string to parse that contains parameters *
3409 * length - [OUT] length of parameters *
3410 * *
3411 * Return value: SUCCEED - null termination encountered when quotes are *
3412 * closed and no other error *
3413 * FAIL - does not look like a valid *
3414 * function parameter list *
3415 * *
3416 ******************************************************************************/
zbx_function_validate_parameters(const char * expr,size_t * length)3417 int zbx_function_validate_parameters(const char *expr, size_t *length)
3418 {
3419 size_t offset, len;
3420
3421 return function_validate_parameters(expr, '\0', length, &offset, &len);
3422 }
3423
3424 /******************************************************************************
3425 * *
3426 * Function: zbx_function_validate *
3427 * *
3428 * Purpose: check whether expression starts with a valid function *
3429 * *
3430 * Parameters: expr - [IN] string to parse *
3431 * par_l - [OUT] position of the opening parenthesis *
3432 * or the amount of characters to skip *
3433 * par_r - [OUT] position of the closing parenthesis *
3434 * error - [OUT] error message *
3435 * max_error_len - [IN] error size *
3436 * *
3437 * Return value: SUCCEED - string starts with a valid function *
3438 * FAIL - string does not start with a function and par_l *
3439 * characters can be safely skipped *
3440 * *
3441 ******************************************************************************/
zbx_function_validate(const char * expr,size_t * par_l,size_t * par_r,char * error,int max_error_len)3442 static int zbx_function_validate(const char *expr, size_t *par_l, size_t *par_r, char *error, int max_error_len)
3443 {
3444 size_t lpp_offset, lpp_len;
3445
3446 /* try to validate function name */
3447 if (SUCCEED == function_parse_name(expr, par_l))
3448 {
3449 /* now we know the position of '(', try to find ')' */
3450 if (SUCCEED == function_match_parenthesis(expr, *par_l, par_r, &lpp_offset, &lpp_len))
3451 return SUCCEED;
3452
3453 if (NULL != error && *par_l > *par_r)
3454 {
3455 zbx_snprintf(error, max_error_len, "Incorrect function '%.*s' expression. "
3456 "Check expression part starting from: %.*s",
3457 (int)*par_l, expr, (int)lpp_len, expr + lpp_offset);
3458
3459 return FAIL;
3460 }
3461 }
3462
3463 if (NULL != error)
3464 zbx_snprintf(error, max_error_len, "Incorrect function expression: %s", expr);
3465
3466 return FAIL;
3467 }
3468
3469 /******************************************************************************
3470 * *
3471 * Function: zbx_strcmp_natural *
3472 * *
3473 * Purpose: performs natural comparison of two strings *
3474 * *
3475 * Parameters: s1 - [IN] the first string *
3476 * s2 - [IN] the second string *
3477 * *
3478 * Return value: 0: the strings are equal *
3479 * <0: s1 < s2 *
3480 * >0: s1 > s2 *
3481 * *
3482 ******************************************************************************/
zbx_strcmp_natural(const char * s1,const char * s2)3483 int zbx_strcmp_natural(const char *s1, const char *s2)
3484 {
3485 int ret, value1, value2;
3486
3487 for (;'\0' != *s1 && '\0' != *s2; s1++, s2++)
3488 {
3489 if (0 == isdigit(*s1) || 0 == isdigit(*s2))
3490 {
3491 if (0 != (ret = *s1 - *s2))
3492 return ret;
3493
3494 continue;
3495 }
3496
3497 value1 = 0;
3498 while (0 != isdigit(*s1))
3499 value1 = value1 * 10 + *s1++ - '0';
3500
3501 value2 = 0;
3502 while (0 != isdigit(*s2))
3503 value2 = value2 * 10 + *s2++ - '0';
3504
3505 if (0 != (ret = value1 - value2))
3506 return ret;
3507
3508 if ('\0' == *s1 || '\0' == *s2)
3509 break;
3510 }
3511
3512 return *s1 - *s2;
3513 }
3514
3515 /******************************************************************************
3516 * *
3517 * Function: zbx_token_parse_user_macro *
3518 * *
3519 * Purpose: parses user macro token *
3520 * *
3521 * Parameters: expression - [IN] the expression *
3522 * macro - [IN] the beginning of the token *
3523 * token - [OUT] the token data *
3524 * *
3525 * Return value: SUCCEED - the user macro was parsed successfully *
3526 * FAIL - macro does not point at valid user macro *
3527 * *
3528 * Comments: If the macro points at valid user macro in the expression then *
3529 * the generic token fields are set and the token->data.user_macro *
3530 * structure is filled with user macro specific data. *
3531 * *
3532 ******************************************************************************/
zbx_token_parse_user_macro(const char * expression,const char * macro,zbx_token_t * token)3533 static int zbx_token_parse_user_macro(const char *expression, const char *macro, zbx_token_t *token)
3534 {
3535 size_t offset;
3536 int macro_r, context_l, context_r;
3537 zbx_token_user_macro_t *data;
3538
3539 if (SUCCEED != zbx_user_macro_parse(macro, ¯o_r, &context_l, &context_r, NULL))
3540 return FAIL;
3541
3542 offset = macro - expression;
3543
3544 /* initialize token */
3545 token->type = ZBX_TOKEN_USER_MACRO;
3546 token->loc.l = offset;
3547 token->loc.r = offset + macro_r;
3548
3549 /* initialize token data */
3550 data = &token->data.user_macro;
3551 data->name.l = offset + 2;
3552
3553 if (0 != context_l)
3554 {
3555 const char *ptr = macro + context_l;
3556
3557 /* find the context separator ':' by stripping spaces before context */
3558 while (' ' == *(--ptr))
3559 ;
3560
3561 data->name.r = offset + (ptr - macro) - 1;
3562
3563 data->context.l = offset + context_l;
3564 data->context.r = offset + context_r;
3565 }
3566 else
3567 {
3568 data->name.r = token->loc.r - 1;
3569 data->context.l = 0;
3570 data->context.r = 0;
3571 }
3572
3573 return SUCCEED;
3574 }
3575
3576 /******************************************************************************
3577 * *
3578 * Function: zbx_token_parse_lld_macro *
3579 * *
3580 * Purpose: parses lld macro token *
3581 * *
3582 * Parameters: expression - [IN] the expression *
3583 * macro - [IN] the beginning of the token *
3584 * token - [OUT] the token data *
3585 * *
3586 * Return value: SUCCEED - the lld macro was parsed successfully *
3587 * FAIL - macro does not point at valid lld macro *
3588 * *
3589 * Comments: If the macro points at valid lld macro in the expression then *
3590 * the generic token fields are set and the token->data.lld_macro *
3591 * structure is filled with lld macro specific data. *
3592 * *
3593 ******************************************************************************/
zbx_token_parse_lld_macro(const char * expression,const char * macro,zbx_token_t * token)3594 static int zbx_token_parse_lld_macro(const char *expression, const char *macro, zbx_token_t *token)
3595 {
3596 const char *ptr;
3597 size_t offset;
3598 zbx_token_macro_t *data;
3599
3600 /* find the end of lld macro by validating its name until the closing bracket } */
3601 for (ptr = macro + 2; '}' != *ptr; ptr++)
3602 {
3603 if ('\0' == *ptr)
3604 return FAIL;
3605
3606 if (SUCCEED != is_macro_char(*ptr))
3607 return FAIL;
3608 }
3609
3610 /* empty macro name */
3611 if (2 == ptr - macro)
3612 return FAIL;
3613
3614 offset = macro - expression;
3615
3616 /* initialize token */
3617 token->type = ZBX_TOKEN_LLD_MACRO;
3618 token->loc.l = offset;
3619 token->loc.r = offset + (ptr - macro);
3620
3621 /* initialize token data */
3622 data = &token->data.lld_macro;
3623 data->name.l = offset + 2;
3624 data->name.r = token->loc.r - 1;
3625
3626 return SUCCEED;
3627 }
3628
3629 /******************************************************************************
3630 * *
3631 * Function: zbx_token_parse_objectid *
3632 * *
3633 * Purpose: parses object id token *
3634 * *
3635 * Parameters: expression - [IN] the expression *
3636 * macro - [IN] the beginning of the token *
3637 * token - [OUT] the token data *
3638 * *
3639 * Return value: SUCCEED - the object id was parsed successfully *
3640 * FAIL - macro does not point at valid object id *
3641 * *
3642 * Comments: If the macro points at valid object id in the expression then *
3643 * the generic token fields are set and the token->data.objectid *
3644 * structure is filled with object id specific data. *
3645 * *
3646 ******************************************************************************/
zbx_token_parse_objectid(const char * expression,const char * macro,zbx_token_t * token)3647 static int zbx_token_parse_objectid(const char *expression, const char *macro, zbx_token_t *token)
3648 {
3649 const char *ptr;
3650 size_t offset;
3651 zbx_token_macro_t *data;
3652
3653 /* find the end of object id by checking if it contains digits until the closing bracket } */
3654 for (ptr = macro + 1; '}' != *ptr; ptr++)
3655 {
3656 if ('\0' == *ptr)
3657 return FAIL;
3658
3659 if (0 == isdigit(*ptr))
3660 return FAIL;
3661 }
3662
3663 /* empty object id */
3664 if (1 == ptr - macro)
3665 return FAIL;
3666
3667
3668 offset = macro - expression;
3669
3670 /* initialize token */
3671 token->type = ZBX_TOKEN_OBJECTID;
3672 token->loc.l = offset;
3673 token->loc.r = offset + (ptr - macro);
3674
3675 /* initialize token data */
3676 data = &token->data.objectid;
3677 data->name.l = offset + 1;
3678 data->name.r = token->loc.r - 1;
3679
3680 return SUCCEED;
3681 }
3682
3683 /******************************************************************************
3684 * *
3685 * Function: zbx_token_parse_macro_segment *
3686 * *
3687 * Purpose: parses macro name segment *
3688 * *
3689 * Parameters: expression - [IN] the expression *
3690 * segment - [IN] the segment start *
3691 * strict - [OUT] 1 - macro contains only standard characters *
3692 * (upper case alphanumeric characters, *
3693 * dots and underscores) *
3694 * 0 - the last segment contains lowercase or *
3695 * quoted characters *
3696 * next - [OUT] offset of the next character after the *
3697 * segment *
3698 * *
3699 * Return value: SUCCEED - the segment was parsed successfully *
3700 * FAIL - otherwise *
3701 * *
3702 ******************************************************************************/
zbx_token_parse_macro_segment(const char * expression,const char * segment,int * strict,int * next)3703 static int zbx_token_parse_macro_segment(const char *expression, const char *segment, int *strict, int *next)
3704 {
3705 const char *ptr = segment;
3706
3707 if ('"' != *ptr)
3708 {
3709 for (*strict = 1; '\0' != *ptr; ptr++)
3710 {
3711 if (0 != isalpha((unsigned char)*ptr))
3712 {
3713 if (0 == isupper((unsigned char)*ptr))
3714 *strict = 0;
3715 continue;
3716 }
3717
3718 if (0 != isdigit((unsigned char)*ptr))
3719 continue;
3720
3721 if ('_' == *ptr)
3722 continue;
3723
3724 break;
3725 }
3726
3727 /* check for empty segment */
3728 if (ptr == segment)
3729 return FAIL;
3730
3731 *next = ptr - expression;
3732 }
3733 else
3734 {
3735 for (*strict = 0, ptr++; '"' != *ptr; ptr++)
3736 {
3737 if ('\0' == *ptr)
3738 return FAIL;
3739
3740 if ('\\' == *ptr)
3741 {
3742 ptr++;
3743 if ('\\' != *ptr && '"' != *ptr)
3744 return FAIL;
3745 }
3746 }
3747
3748 /* check for empty segment */
3749 if (1 == ptr - segment)
3750 return FAIL;
3751
3752 *next = ptr - expression + 1;
3753 }
3754
3755 return SUCCEED;
3756 }
3757
3758 /******************************************************************************
3759 * *
3760 * Function: zbx_token_parse_macro_name *
3761 * *
3762 * Purpose: parses macro name *
3763 * *
3764 * Parameters: expression - [IN] the expression *
3765 * ptr - [IN] the beginning of macro name *
3766 * loc - [OUT] the macro name location *
3767 * *
3768 * Return value: SUCCEED - the simple macro was parsed successfully *
3769 * FAIL - macro does not point at valid macro *
3770 * *
3771 * Comments: Note that the character following macro name must be inspected *
3772 * to draw any conclusions. For example for normal macros it must *
3773 * be '}' or it's not a valid macro. *
3774 * *
3775 ******************************************************************************/
zbx_token_parse_macro_name(const char * expression,const char * ptr,zbx_strloc_t * loc)3776 static int zbx_token_parse_macro_name(const char *expression, const char *ptr, zbx_strloc_t *loc)
3777 {
3778 int strict, offset, ret;
3779
3780 loc->l = ptr - expression;
3781
3782 while (SUCCEED == (ret = zbx_token_parse_macro_segment(expression, ptr, &strict, &offset)))
3783 {
3784 if (0 == strict && expression + loc->l == ptr)
3785 return FAIL;
3786
3787 ptr = expression + offset;
3788
3789 if ('.' != *ptr || 0 == strict)
3790 {
3791 loc->r = ptr - expression - 1;
3792 break;
3793 }
3794 ptr++;
3795 }
3796 return ret;
3797 }
3798
3799 /******************************************************************************
3800 * *
3801 * Function: zbx_token_parse_macro *
3802 * *
3803 * Purpose: parses normal macro token *
3804 * *
3805 * Parameters: expression - [IN] the expression *
3806 * macro - [IN] the beginning of the token *
3807 * token - [OUT] the token data *
3808 * *
3809 * Return value: SUCCEED - the simple macro was parsed successfully *
3810 * FAIL - macro does not point at valid macro *
3811 * *
3812 * Comments: If the macro points at valid macro in the expression then *
3813 * the generic token fields are set and the token->data.macro *
3814 * structure is filled with simple macro specific data. *
3815 * *
3816 ******************************************************************************/
zbx_token_parse_macro(const char * expression,const char * macro,zbx_token_t * token)3817 static int zbx_token_parse_macro(const char *expression, const char *macro, zbx_token_t *token)
3818 {
3819 zbx_strloc_t loc;
3820 zbx_token_macro_t *data;
3821
3822 if (SUCCEED != zbx_token_parse_macro_name(expression, macro + 1, &loc))
3823 return FAIL;
3824
3825 if ('}' != expression[loc.r + 1])
3826 return FAIL;
3827
3828 /* initialize token */
3829 token->type = ZBX_TOKEN_MACRO;
3830 token->loc.l = loc.l - 1;
3831 token->loc.r = loc.r + 1;
3832
3833 /* initialize token data */
3834 data = &token->data.macro;
3835 data->name = loc;
3836
3837 return SUCCEED;
3838 }
3839
3840 /******************************************************************************
3841 * *
3842 * Function: zbx_token_parse_function *
3843 * *
3844 * Purpose: parses function inside token *
3845 * *
3846 * Parameters: expression - [IN] the expression *
3847 * func - [IN] the beginning of the function *
3848 * func_loc - [OUT] the function location relative to the *
3849 * expression (including parameters) *
3850 * *
3851 * Return value: SUCCEED - the function was parsed successfully *
3852 * FAIL - func does not point at valid function *
3853 * *
3854 ******************************************************************************/
zbx_token_parse_function(const char * expression,const char * func,zbx_strloc_t * func_loc,zbx_strloc_t * func_param)3855 static int zbx_token_parse_function(const char *expression, const char *func,
3856 zbx_strloc_t *func_loc, zbx_strloc_t *func_param)
3857 {
3858 size_t par_l, par_r;
3859
3860 if (SUCCEED != zbx_function_validate(func, &par_l, &par_r, NULL, 0))
3861 return FAIL;
3862
3863 func_loc->l = func - expression;
3864 func_loc->r = func_loc->l + par_r;
3865
3866 func_param->l = func_loc->l + par_l;
3867 func_param->r = func_loc->l + par_r;
3868
3869 return SUCCEED;
3870 }
3871
3872 /******************************************************************************
3873 * *
3874 * Function: zbx_token_parse_func_macro *
3875 * *
3876 * Purpose: parses function macro token *
3877 * *
3878 * Parameters: expression - [IN] the expression *
3879 * macro - [IN] the beginning of the token *
3880 * func - [IN] the beginning of the macro function in the *
3881 * token *
3882 * token - [OUT] the token data *
3883 * token_type - [IN] type flag ZBX_TOKEN_FUNC_MACRO or *
3884 * ZBX_TOKEN_LLD_FUNC_MACRO *
3885 * *
3886 * Return value: SUCCEED - the function macro was parsed successfully *
3887 * FAIL - macro does not point at valid function macro *
3888 * *
3889 * Comments: If the macro points at valid function macro in the expression *
3890 * then the generic token fields are set and the *
3891 * token->data.func_macro or token->data.lld_func_macro structures *
3892 * depending on token type flag are filled with function macro *
3893 * specific data. *
3894 * *
3895 ******************************************************************************/
zbx_token_parse_func_macro(const char * expression,const char * macro,const char * func,zbx_token_t * token,int token_type)3896 static int zbx_token_parse_func_macro(const char *expression, const char *macro, const char *func,
3897 zbx_token_t *token, int token_type)
3898 {
3899 zbx_strloc_t func_loc, func_param;
3900 zbx_token_func_macro_t *data;
3901 const char *ptr;
3902 size_t offset;
3903
3904 if ('\0' == *func)
3905 return FAIL;
3906
3907 if (SUCCEED != zbx_token_parse_function(expression, func, &func_loc, &func_param))
3908 return FAIL;
3909
3910 ptr = expression + func_loc.r + 1;
3911
3912 /* skip trailing whitespace and verify that token ends with } */
3913
3914 while (' ' == *ptr)
3915 ptr++;
3916
3917 if ('}' != *ptr)
3918 return FAIL;
3919
3920 offset = macro - expression;
3921
3922 /* initialize token */
3923 token->type = token_type;
3924 token->loc.l = offset;
3925 token->loc.r = ptr - expression;
3926
3927 /* initialize token data */
3928 data = ZBX_TOKEN_FUNC_MACRO == token_type ? &token->data.func_macro : &token->data.lld_func_macro;
3929 data->macro.l = offset + 1;
3930 data->macro.r = func_loc.l - 2;
3931
3932 data->func = func_loc;
3933 data->func_param = func_param;
3934
3935 return SUCCEED;
3936 }
3937
3938 /******************************************************************************
3939 * *
3940 * Function: zbx_token_parse_simple_macro_key *
3941 * *
3942 * Purpose: parses simple macro token with given key *
3943 * *
3944 * Parameters: expression - [IN] the expression *
3945 * macro - [IN] the beginning of the token *
3946 * key - [IN] the beginning of host key inside the token *
3947 * token - [OUT] the token data *
3948 * *
3949 * Return value: SUCCEED - the function macro was parsed successfully *
3950 * FAIL - macro does not point at valid simple macro *
3951 * *
3952 * Comments: Simple macros have format {<host>:<key>.<func>(<params>)} *
3953 * {HOST.HOSTn} macro can be used for host name and {ITEM.KEYn} *
3954 * macro can be used for item key. *
3955 * *
3956 * If the macro points at valid simple macro in the expression *
3957 * then the generic token fields are set and the *
3958 * token->data.simple_macro structure is filled with simple macro *
3959 * specific data. *
3960 * *
3961 ******************************************************************************/
zbx_token_parse_simple_macro_key(const char * expression,const char * macro,const char * key,zbx_token_t * token)3962 static int zbx_token_parse_simple_macro_key(const char *expression, const char *macro, const char *key,
3963 zbx_token_t *token)
3964 {
3965 size_t offset;
3966 zbx_token_simple_macro_t *data;
3967 const char *ptr = key;
3968 zbx_strloc_t key_loc, func_loc, func_param;
3969
3970 if (SUCCEED != parse_key(&ptr))
3971 {
3972 zbx_token_t key_token;
3973
3974 if (SUCCEED != zbx_token_parse_macro(expression, key, &key_token))
3975 return FAIL;
3976
3977 ptr = expression + key_token.loc.r + 1;
3978 }
3979
3980 /* If the key is without parameters, then parse_key() will move cursor past function name - */
3981 /* at the start of its parameters. In this case move cursor back before function. */
3982 if ('(' == *ptr)
3983 {
3984 while ('.' != *(--ptr))
3985 ;
3986 }
3987
3988 /* check for empty key */
3989 if (0 == ptr - key)
3990 return FAIL;
3991
3992 if (SUCCEED != zbx_token_parse_function(expression, ptr + 1, &func_loc, &func_param))
3993 return FAIL;
3994
3995 key_loc.l = key - expression;
3996 key_loc.r = ptr - expression - 1;
3997
3998 ptr = expression + func_loc.r + 1;
3999
4000 /* skip trailing whitespace and verify that token ends with } */
4001
4002 while (' ' == *ptr)
4003 ptr++;
4004
4005 if ('}' != *ptr)
4006 return FAIL;
4007
4008 offset = macro - expression;
4009
4010 /* initialize token */
4011 token->type = ZBX_TOKEN_SIMPLE_MACRO;
4012 token->loc.l = offset;
4013 token->loc.r = ptr - expression;
4014
4015 /* initialize token data */
4016 data = &token->data.simple_macro;
4017 data->host.l = offset + 1;
4018 data->host.r = offset + (key - macro) - 2;
4019
4020 data->key = key_loc;
4021 data->func = func_loc;
4022 data->func_param = func_param;
4023
4024 return SUCCEED;
4025 }
4026
4027 /******************************************************************************
4028 * *
4029 * Function: zbx_token_parse_simple_macro *
4030 * *
4031 * Purpose: parses simple macro token *
4032 * *
4033 * Parameters: expression - [IN] the expression *
4034 * macro - [IN] the beginning of the token *
4035 * token - [OUT] the token data *
4036 * *
4037 * Return value: SUCCEED - the simple macro was parsed successfully *
4038 * FAIL - macro does not point at valid simple macro *
4039 * *
4040 * Comments: Simple macros have format {<host>:<key>.<func>(<params>)} *
4041 * {HOST.HOSTn} macro can be used for host name and {ITEM.KEYn} *
4042 * macro can be used for item key. *
4043 * *
4044 * If the macro points at valid simple macro in the expression *
4045 * then the generic token fields are set and the *
4046 * token->data.simple_macro structure is filled with simple macro *
4047 * specific data. *
4048 * *
4049 ******************************************************************************/
zbx_token_parse_simple_macro(const char * expression,const char * macro,zbx_token_t * token)4050 static int zbx_token_parse_simple_macro(const char *expression, const char *macro, zbx_token_t *token)
4051 {
4052 const char *ptr;
4053
4054 /* Find the end of host name by validating its name until the closing bracket }. */
4055 /* {HOST.HOSTn} macro usage in the place of host name is handled by nested macro parsing. */
4056 for (ptr = macro + 1; ':' != *ptr; ptr++)
4057 {
4058 if ('\0' == *ptr)
4059 return FAIL;
4060
4061 if (SUCCEED != is_hostname_char(*ptr))
4062 return FAIL;
4063 }
4064
4065 /* check for empty host name */
4066 if (1 == ptr - macro)
4067 return FAIL;
4068
4069 return zbx_token_parse_simple_macro_key(expression, macro, ptr + 1, token);
4070 }
4071
4072 /******************************************************************************
4073 * *
4074 * Function: zbx_token_parse_nested_macro *
4075 * *
4076 * Purpose: parses token with nested macros *
4077 * *
4078 * Parameters: expression - [IN] the expression *
4079 * macro - [IN] the beginning of the token *
4080 * token - [OUT] the token data *
4081 * *
4082 * Return value: SUCCEED - the token was parsed successfully *
4083 * FAIL - macro does not point at valid function or simple *
4084 * macro *
4085 * *
4086 * Comments: This function parses token with a macro inside it. There are *
4087 * three types of nested macros - low-level discovery function *
4088 * macros, function macros and a specific case of simple macros *
4089 * where {HOST.HOSTn} macro is used as host name. *
4090 * *
4091 * If the macro points at valid macro in the expression then *
4092 * the generic token fields are set and either the *
4093 * token->data.lld_func_macro, token->data.func_macro or *
4094 * token->data.simple_macro (depending on token type) structure is *
4095 * filled with macro specific data. *
4096 * *
4097 ******************************************************************************/
zbx_token_parse_nested_macro(const char * expression,const char * macro,zbx_token_t * token)4098 static int zbx_token_parse_nested_macro(const char *expression, const char *macro, zbx_token_t *token)
4099 {
4100 const char *ptr;
4101
4102 if ('#' == macro[2])
4103 {
4104 /* find the end of the nested macro by validating its name until the closing bracket '}' */
4105 for (ptr = macro + 3; '}' != *ptr; ptr++)
4106 {
4107 if ('\0' == *ptr)
4108 return FAIL;
4109
4110 if (SUCCEED != is_macro_char(*ptr))
4111 return FAIL;
4112 }
4113
4114 /* empty macro name */
4115 if (3 == ptr - macro)
4116 return FAIL;
4117 }
4118 else
4119 {
4120 zbx_strloc_t loc;
4121
4122 if (SUCCEED != zbx_token_parse_macro_name(expression, macro + 2, &loc))
4123 return FAIL;
4124
4125 if ('}' != expression[loc.r + 1])
4126 return FAIL;
4127
4128 ptr = expression + loc.r + 1;
4129 }
4130
4131 /* Determine the token type. */
4132 /* Nested macros formats: */
4133 /* low-level discovery function macros {{#MACRO}.function()} */
4134 /* function macros {{MACRO}.function()} */
4135 /* simple macros {{MACRO}:key.function()} */
4136 if ('.' == ptr[1])
4137 {
4138 return zbx_token_parse_func_macro(expression, macro, ptr + 2, token, '#' == macro[2] ?
4139 ZBX_TOKEN_LLD_FUNC_MACRO : ZBX_TOKEN_FUNC_MACRO);
4140 }
4141 else if ('#' != macro[2] && ':' == ptr[1])
4142 return zbx_token_parse_simple_macro_key(expression, macro, ptr + 2, token);
4143
4144 return FAIL;
4145 }
4146
4147 /******************************************************************************
4148 * *
4149 * Function: zbx_token_find *
4150 * *
4151 * Purpose: finds token {} inside expression starting at specified position *
4152 * also searches for reference if requested *
4153 * *
4154 * Parameters: expression - [IN] the expression *
4155 * pos - [IN] the starting position *
4156 * token - [OUT] the token data *
4157 * token_search - [IN] specify if references will be searched *
4158 * *
4159 * Return value: SUCCEED - the token was parsed successfully *
4160 * FAIL - expression does not contain valid token. *
4161 * *
4162 * Comments: The token field locations are specified as offsets from the *
4163 * beginning of the expression. *
4164 * *
4165 * Simply iterating through tokens can be done with: *
4166 * *
4167 * zbx_token_t token = {0}; *
4168 * *
4169 * while (SUCCEED == zbx_token_find(expression, token.loc.r + 1, *
4170 * &token)) *
4171 * { *
4172 * process_token(expression, &token); *
4173 * } *
4174 * *
4175 ******************************************************************************/
zbx_token_find(const char * expression,int pos,zbx_token_t * token,zbx_token_search_t token_search)4176 int zbx_token_find(const char *expression, int pos, zbx_token_t *token, zbx_token_search_t token_search)
4177 {
4178 int ret = FAIL;
4179 const char *ptr = expression + pos, *dollar = ptr;
4180
4181 while (SUCCEED != ret)
4182 {
4183 ptr = strchr(ptr, '{');
4184
4185 switch (token_search)
4186 {
4187 case ZBX_TOKEN_SEARCH_BASIC:
4188 break;
4189 case ZBX_TOKEN_SEARCH_REFERENCES:
4190 while (NULL != (dollar = strchr(dollar, '$')) && (NULL == ptr || ptr > dollar))
4191 {
4192 if (0 == isdigit(dollar[1]))
4193 {
4194 dollar++;
4195 continue;
4196 }
4197
4198 token->data.reference.index = dollar[1] - '0';
4199 token->type = ZBX_TOKEN_REFERENCE;
4200 token->loc.l = dollar - expression;
4201 token->loc.r = token->loc.l + 1;
4202 return SUCCEED;
4203 }
4204
4205 if (NULL == dollar)
4206 token_search = ZBX_TOKEN_SEARCH_BASIC;
4207
4208 break;
4209 default:
4210 THIS_SHOULD_NEVER_HAPPEN;
4211 }
4212
4213 if (NULL == ptr)
4214 return FAIL;
4215
4216 if ('\0' == ptr[1])
4217 return FAIL;
4218
4219 switch (ptr[1])
4220 {
4221 case '$':
4222 ret = zbx_token_parse_user_macro(expression, ptr, token);
4223 break;
4224 case '#':
4225 ret = zbx_token_parse_lld_macro(expression, ptr, token);
4226 break;
4227
4228 case '{':
4229 ret = zbx_token_parse_nested_macro(expression, ptr, token);
4230 break;
4231 case '0':
4232 case '1':
4233 case '2':
4234 case '3':
4235 case '4':
4236 case '5':
4237 case '6':
4238 case '7':
4239 case '8':
4240 case '9':
4241 if (SUCCEED == (ret = zbx_token_parse_objectid(expression, ptr, token)))
4242 break;
4243 ZBX_FALLTHROUGH;
4244 default:
4245 if (SUCCEED != (ret = zbx_token_parse_macro(expression, ptr, token)))
4246 ret = zbx_token_parse_simple_macro(expression, ptr, token);
4247 }
4248
4249 ptr++;
4250 }
4251
4252 return ret;
4253 }
4254
4255 /******************************************************************************
4256 * *
4257 * Function: zbx_no_function *
4258 * *
4259 * Purpose: count calculated item (prototype) formula characters that can be *
4260 * skipped without the risk of missing a function *
4261 * *
4262 ******************************************************************************/
zbx_no_function(const char * expr)4263 static size_t zbx_no_function(const char *expr)
4264 {
4265 const char *ptr = expr;
4266 int inside_quote = 0, len, c_l, c_r;
4267 zbx_token_t token;
4268
4269 while ('\0' != *ptr)
4270 {
4271 switch (*ptr)
4272 {
4273 case '\\':
4274 if (0 != inside_quote)
4275 ptr++;
4276 break;
4277 case '"':
4278 inside_quote = !inside_quote;
4279 ptr++;
4280 continue;
4281 }
4282
4283 if (inside_quote)
4284 {
4285 if ('\0' == *ptr)
4286 break;
4287 ptr++;
4288 continue;
4289 }
4290
4291 if ('{' == *ptr && '$' == *(ptr + 1) && SUCCEED == zbx_user_macro_parse(ptr, &len, &c_l, &c_r, NULL))
4292 {
4293 ptr += len + 1; /* skip to the position after user macro */
4294 }
4295 else if ('{' == *ptr && '{' == *(ptr + 1) && '#' == *(ptr + 2) &&
4296 SUCCEED == zbx_token_parse_nested_macro(ptr, ptr, &token))
4297 {
4298 ptr += token.loc.r - token.loc.l + 1;
4299 }
4300 else if (SUCCEED != is_function_char(*ptr))
4301 {
4302 ptr++; /* skip one character which cannot belong to function name */
4303 }
4304 else if ((0 == strncmp("and", ptr, len = ZBX_CONST_STRLEN("and")) ||
4305 0 == strncmp("not", ptr, len = ZBX_CONST_STRLEN("not")) ||
4306 0 == strncmp("or", ptr, len = ZBX_CONST_STRLEN("or"))) &&
4307 NULL != strchr("()" ZBX_WHITESPACE, ptr[len]))
4308 {
4309 ptr += len; /* skip to the position after and/or/not operator */
4310 }
4311 else if (ptr > expr && 0 != isdigit(*(ptr - 1)) && NULL != strchr(ZBX_UNIT_SYMBOLS, *ptr))
4312 {
4313 ptr++; /* skip unit suffix symbol if it's preceded by a digit */
4314 }
4315 else
4316 break;
4317 }
4318
4319 return ptr - expr;
4320 }
4321
4322 /******************************************************************************
4323 * *
4324 * Function: zbx_function_find *
4325 * *
4326 * Purpose: find the location of the next function and its parameters in *
4327 * calculated item (prototype) formula *
4328 * *
4329 * Parameters: expr - [IN] string to parse *
4330 * func_pos - [OUT] function position in the string *
4331 * par_l - [OUT] position of the opening parenthesis *
4332 * par_r - [OUT] position of the closing parenthesis *
4333 * error - [OUT] error message *
4334 * max_error_len - [IN] error size *
4335 * *
4336 * *
4337 * Return value: SUCCEED - function was found at func_pos *
4338 * FAIL - there are no functions in the expression *
4339 * *
4340 ******************************************************************************/
zbx_function_find(const char * expr,size_t * func_pos,size_t * par_l,size_t * par_r,char * error,int max_error_len)4341 int zbx_function_find(const char *expr, size_t *func_pos, size_t *par_l, size_t *par_r, char *error,
4342 int max_error_len)
4343 {
4344 const char *ptr;
4345
4346 for (ptr = expr; '\0' != *ptr; ptr += *par_l)
4347 {
4348 /* skip the part of expression that is definitely not a function */
4349 ptr += zbx_no_function(ptr);
4350 *par_r = 0;
4351
4352 /* try to validate function candidate */
4353 if (SUCCEED != zbx_function_validate(ptr, par_l, par_r, error, max_error_len))
4354 {
4355 if (*par_l > *par_r)
4356 return FAIL;
4357
4358 continue;
4359 }
4360
4361 *func_pos = ptr - expr;
4362 *par_l += *func_pos;
4363 *par_r += *func_pos;
4364 return SUCCEED;
4365 }
4366
4367 zbx_snprintf(error, max_error_len, "Incorrect function expression: %s", expr);
4368
4369 return FAIL;
4370 }
4371
4372 /******************************************************************************
4373 * *
4374 * Function: zbx_strmatch_condition *
4375 * *
4376 * Purpose: check if pattern matches the specified value *
4377 * *
4378 * Parameters: value - [IN] the value to match *
4379 * pattern - [IN] the pattern to match *
4380 * op - [IN] the matching operator *
4381 * *
4382 * Return value: SUCCEED - matches, FAIL - otherwise *
4383 * *
4384 ******************************************************************************/
zbx_strmatch_condition(const char * value,const char * pattern,unsigned char op)4385 int zbx_strmatch_condition(const char *value, const char *pattern, unsigned char op)
4386 {
4387 int ret = FAIL;
4388
4389 switch (op)
4390 {
4391 case CONDITION_OPERATOR_EQUAL:
4392 if (0 == strcmp(value, pattern))
4393 ret = SUCCEED;
4394 break;
4395 case CONDITION_OPERATOR_NOT_EQUAL:
4396 if (0 != strcmp(value, pattern))
4397 ret = SUCCEED;
4398 break;
4399 case CONDITION_OPERATOR_LIKE:
4400 if (NULL != strstr(value, pattern))
4401 ret = SUCCEED;
4402 break;
4403 case CONDITION_OPERATOR_NOT_LIKE:
4404 if (NULL == strstr(value, pattern))
4405 ret = SUCCEED;
4406 break;
4407 }
4408
4409 return ret;
4410 }
4411
4412 /******************************************************************************
4413 * *
4414 * Function: zbx_number_parse *
4415 * *
4416 * Purpose: parse a number like "12.345" *
4417 * *
4418 * Parameters: number - [IN] start of number *
4419 * len - [OUT] length of parsed number *
4420 * *
4421 * Return value: SUCCEED - the number was parsed successfully *
4422 * FAIL - invalid number *
4423 * *
4424 * Comments: !!! Don't forget to sync the code with PHP !!! *
4425 * The token field locations are specified as offsets from the *
4426 * beginning of the expression. *
4427 * *
4428 ******************************************************************************/
zbx_number_parse(const char * number,int * len)4429 int zbx_number_parse(const char *number, int *len)
4430 {
4431 int digits = 0, dots = 0;
4432
4433 *len = 0;
4434
4435 while (1)
4436 {
4437 if (0 != isdigit(number[*len]))
4438 {
4439 (*len)++;
4440 digits++;
4441 continue;
4442 }
4443
4444 if ('.' == number[*len])
4445 {
4446 (*len)++;
4447 dots++;
4448 continue;
4449 }
4450
4451 if ('e' == number[*len] || 'E' == number[*len])
4452 {
4453 (*len)++;
4454
4455 if ('-' == number[*len] || '+' == number[*len])
4456 (*len)++;
4457
4458 if (0 == isdigit(number[*len]))
4459 return FAIL;
4460
4461 while (0 != isdigit(number[++(*len)]));
4462
4463 if ('.' == number[*len] ||'e' == number[*len] || 'E' == number[*len])
4464 return FAIL;
4465 }
4466
4467 if (1 > digits || 1 < dots)
4468 return FAIL;
4469
4470 return SUCCEED;
4471 }
4472 }
4473
4474 /******************************************************************************
4475 * *
4476 * Function: zbx_suffixed_number_parse *
4477 * *
4478 * Purpose: parse a suffixed number like "12.345K" *
4479 * *
4480 * Parameters: number - [IN] start of number *
4481 * len - [OUT] length of parsed number *
4482 * *
4483 * Return value: SUCCEED - the number was parsed successfully *
4484 * FAIL - invalid number *
4485 * *
4486 * Comments: !!! Don't forget to sync the code with PHP !!! *
4487 * The token field locations are specified as offsets from the *
4488 * beginning of the expression. *
4489 * *
4490 ******************************************************************************/
zbx_suffixed_number_parse(const char * number,int * len)4491 int zbx_suffixed_number_parse(const char *number, int *len)
4492 {
4493 if (FAIL == zbx_number_parse(number, len))
4494 return FAIL;
4495
4496 if (0 != isalpha(number[*len]) && NULL != strchr(ZBX_UNIT_SYMBOLS, number[*len]))
4497 (*len)++;
4498
4499 return SUCCEED;
4500 }
4501
4502 /******************************************************************************
4503 * *
4504 * Function: zbx_expression_next_constant *
4505 * *
4506 * Purpose: gets next constant (numeric/string value or unresolved user *
4507 * macro) from trigger expression. *
4508 * *
4509 * Parameters: src - [IN] the expression *
4510 * pos - [IN] the starting position *
4511 * loc - [IN] the substring location *
4512 * *
4513 * Return value: SUCCEED - the next constant was located. *
4514 * FAIL - otherwise. *
4515 * *
4516 ******************************************************************************/
zbx_expression_next_constant(const char * str,size_t pos,zbx_strloc_t * loc)4517 int zbx_expression_next_constant(const char *str, size_t pos, zbx_strloc_t *loc)
4518 {
4519 const char *s;
4520 zbx_token_t token;
4521 int offset = 0, len;
4522
4523 for (s = str + pos; '\0' != *s; s++)
4524 {
4525 switch (*s)
4526 {
4527 case '"':
4528 loc->l = s - str;
4529
4530 for (++s;'\0' != *s; s++)
4531 {
4532 if ('"' == *s)
4533 {
4534 loc->r = s - str;
4535 return SUCCEED;
4536 }
4537 if ('\\' == *s)
4538 {
4539 if ('\\' != s[1] && '"' != s[1])
4540 return FAIL;
4541 s++;
4542 }
4543 }
4544 return FAIL;
4545 case '{':
4546 if (SUCCEED == zbx_token_find(str, s - str, &token, ZBX_TOKEN_SEARCH_BASIC))
4547 {
4548 if (ZBX_TOKEN_USER_MACRO == token.type)
4549 {
4550 *loc = token.loc;
4551 return SUCCEED;
4552 }
4553 /* Skip all other tokens. Currently it can be only {TRIGGER.VALUE} macro. */
4554 s = str + token.loc.r;
4555 }
4556 continue;
4557 case '-':
4558 offset = 1;
4559 continue;
4560 case '0':
4561 case '1':
4562 case '2':
4563 case '3':
4564 case '4':
4565 case '5':
4566 case '6':
4567 case '7':
4568 case '8':
4569 case '9':
4570 case '.':
4571 if (SUCCEED != zbx_suffixed_number_parse(s, &len))
4572 return FAIL;
4573
4574 loc->l = s - str - offset;
4575 loc->r = s - str + len - 1;
4576 return SUCCEED;
4577 default:
4578 offset = 0;
4579 }
4580 }
4581 return FAIL;
4582 }
4583
4584 /******************************************************************************
4585 * *
4586 * Function: zbx_expression_extract_constant *
4587 * *
4588 * Purpose: extracts constant from trigger expression unquoting/escaping if *
4589 * necessary *
4590 * *
4591 * Parameters: src - [IN] the source string *
4592 * loc - [IN] the substring location *
4593 * *
4594 * Return value: The constant. *
4595 * *
4596 ******************************************************************************/
zbx_expression_extract_constant(const char * src,const zbx_strloc_t * loc)4597 char *zbx_expression_extract_constant(const char *src, const zbx_strloc_t *loc)
4598 {
4599 char *str, *pout;
4600 const char *pin;
4601 size_t len;
4602
4603 len = loc->r - loc->l + 1;
4604 str = zbx_malloc(NULL, len + 1);
4605
4606 if ('"' == src[loc->l])
4607 {
4608 for (pout = str, pin = src + loc->l + 1; pin <= src + loc->r - 1; pin++)
4609 {
4610 if ('\\' == *pin)
4611 {
4612 pin++;
4613 switch (*pin)
4614 {
4615 case '\\':
4616 *pout++ = '\\';
4617 break;
4618 case '"':
4619 *pout++ = '"';
4620 break;
4621 default:
4622 THIS_SHOULD_NEVER_HAPPEN;
4623 *pout++ = '?';
4624 }
4625 }
4626 else
4627 *pout++ = *pin;
4628 }
4629 *pout++ ='\0';
4630 }
4631 else
4632 {
4633 memcpy(str, src + loc->l, len);
4634 str[len] = '\0';
4635 }
4636
4637 return str;
4638 }
4639
4640 /******************************************************************************
4641 * *
4642 * Function: num_param *
4643 * *
4644 * Purpose: find number of parameters in parameter list *
4645 * *
4646 * Parameters: *
4647 * p - [IN] parameter list *
4648 * *
4649 * Return value: number of parameters (starting from 1) or *
4650 * 0 if syntax error *
4651 * *
4652 * Author: Alexei Vladishev *
4653 * *
4654 * Comments: delimiter for parameters is ','. Empty parameter list or a list *
4655 * containing only spaces is handled as having one empty parameter *
4656 * and 1 is returned. *
4657 * *
4658 ******************************************************************************/
num_param(const char * p)4659 int num_param(const char *p)
4660 {
4661 /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
4662 int ret = 1, state, array;
4663
4664 if (p == NULL)
4665 return 0;
4666
4667 for (state = 0, array = 0; '\0' != *p; p++)
4668 {
4669 switch (state) {
4670 /* Init state */
4671 case 0:
4672 if (',' == *p)
4673 {
4674 if (0 == array)
4675 ret++;
4676 }
4677 else if ('"' == *p)
4678 state = 1;
4679 else if ('[' == *p)
4680 {
4681 if (0 == array)
4682 array = 1;
4683 else
4684 return 0; /* incorrect syntax: multi-level array */
4685 }
4686 else if (']' == *p && 0 != array)
4687 {
4688 array = 0;
4689
4690 while (' ' == p[1]) /* skip trailing spaces after closing ']' */
4691 p++;
4692
4693 if (',' != p[1] && '\0' != p[1])
4694 return 0; /* incorrect syntax */
4695 }
4696 else if (']' == *p && 0 == array)
4697 return 0; /* incorrect syntax */
4698 else if (' ' != *p)
4699 state = 2;
4700 break;
4701 /* Quoted */
4702 case 1:
4703 if ('"' == *p)
4704 {
4705 while (' ' == p[1]) /* skip trailing spaces after closing quotes */
4706 p++;
4707
4708 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4709 return 0; /* incorrect syntax */
4710
4711 state = 0;
4712 }
4713 else if ('\\' == *p && '"' == p[1])
4714 p++;
4715 break;
4716 /* Unquoted */
4717 case 2:
4718 if (',' == *p || (']' == *p && 0 != array))
4719 {
4720 p--;
4721 state = 0;
4722 }
4723 else if (']' == *p && 0 == array)
4724 return 0; /* incorrect syntax */
4725 break;
4726 }
4727 }
4728
4729 /* missing terminating '"' character */
4730 if (state == 1)
4731 return 0;
4732
4733 /* missing terminating ']' character */
4734 if (array != 0)
4735 return 0;
4736
4737 return ret;
4738 }
4739
4740 /******************************************************************************
4741 * *
4742 * Function: get_param *
4743 * *
4744 * Purpose: return parameter by index (num) from parameter list (param) *
4745 * *
4746 * Parameters: *
4747 * p - [IN] parameter list *
4748 * num - [IN] requested parameter index *
4749 * buf - [OUT] pointer of output buffer *
4750 * max_len - [IN] size of output buffer *
4751 * type - [OUT] parameter type (may be NULL) *
4752 * *
4753 * Return value: *
4754 * 1 - requested parameter missing or buffer overflow *
4755 * 0 - requested parameter found (value - 'buf' can be empty string) *
4756 * *
4757 * Author: Eugene Grigorjev, rewritten by Alexei Vladishev *
4758 * *
4759 * Comments: delimiter for parameters is ',' *
4760 * *
4761 ******************************************************************************/
get_param(const char * p,int num,char * buf,size_t max_len,zbx_request_parameter_type_t * type)4762 int get_param(const char *p, int num, char *buf, size_t max_len, zbx_request_parameter_type_t *type)
4763 {
4764 #define ZBX_ASSIGN_PARAM \
4765 { \
4766 if (buf_i == max_len) \
4767 return 1; /* buffer overflow */ \
4768 buf[buf_i++] = *p; \
4769 }
4770
4771 int state; /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
4772 int array, idx = 1;
4773 size_t buf_i = 0;
4774
4775 if (NULL != type)
4776 *type = REQUEST_PARAMETER_TYPE_UNDEFINED;
4777
4778 if (0 == max_len)
4779 return 1; /* buffer overflow */
4780
4781 max_len--; /* '\0' */
4782
4783 for (state = 0, array = 0; '\0' != *p && idx <= num; p++)
4784 {
4785 switch (state)
4786 {
4787 /* init state */
4788 case 0:
4789 if (',' == *p)
4790 {
4791 if (0 == array)
4792 idx++;
4793 else if (idx == num)
4794 ZBX_ASSIGN_PARAM;
4795 }
4796 else if ('"' == *p)
4797 {
4798 state = 1;
4799
4800 if (idx == num)
4801 {
4802 if (NULL != type && REQUEST_PARAMETER_TYPE_UNDEFINED == *type)
4803 *type = REQUEST_PARAMETER_TYPE_STRING;
4804
4805 if (0 != array)
4806 ZBX_ASSIGN_PARAM;
4807 }
4808 }
4809 else if ('[' == *p)
4810 {
4811 if (idx == num)
4812 {
4813 if (NULL != type && REQUEST_PARAMETER_TYPE_UNDEFINED == *type)
4814 *type = REQUEST_PARAMETER_TYPE_ARRAY;
4815
4816 if (0 != array)
4817 ZBX_ASSIGN_PARAM;
4818 }
4819 array++;
4820 }
4821 else if (']' == *p && 0 != array)
4822 {
4823 array--;
4824 if (0 != array && idx == num)
4825 ZBX_ASSIGN_PARAM;
4826
4827 /* skip spaces */
4828 while (' ' == p[1])
4829 p++;
4830
4831 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4832 return 1; /* incorrect syntax */
4833 }
4834 else if (' ' != *p)
4835 {
4836 if (idx == num)
4837 {
4838 if (NULL != type && REQUEST_PARAMETER_TYPE_UNDEFINED == *type)
4839 *type = REQUEST_PARAMETER_TYPE_STRING;
4840
4841 ZBX_ASSIGN_PARAM;
4842 }
4843
4844 state = 2;
4845 }
4846 break;
4847 case 1:
4848 /* quoted */
4849
4850 if ('"' == *p)
4851 {
4852 if (0 != array && idx == num)
4853 ZBX_ASSIGN_PARAM;
4854
4855 /* skip spaces */
4856 while (' ' == p[1])
4857 p++;
4858
4859 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4860 return 1; /* incorrect syntax */
4861
4862 state = 0;
4863 }
4864 else if ('\\' == *p && '"' == p[1])
4865 {
4866 if (idx == num && 0 != array)
4867 ZBX_ASSIGN_PARAM;
4868
4869 p++;
4870
4871 if (idx == num)
4872 ZBX_ASSIGN_PARAM;
4873 }
4874 else if (idx == num)
4875 ZBX_ASSIGN_PARAM;
4876 break;
4877 case 2:
4878 /* unquoted */
4879
4880 if (',' == *p || (']' == *p && 0 != array))
4881 {
4882 p--;
4883 state = 0;
4884 }
4885 else if (idx == num)
4886 ZBX_ASSIGN_PARAM;
4887 break;
4888 }
4889
4890 if (idx > num)
4891 break;
4892 }
4893 #undef ZBX_ASSIGN_PARAM
4894
4895 /* missing terminating '"' character */
4896 if (1 == state)
4897 return 1;
4898
4899 /* missing terminating ']' character */
4900 if (0 != array)
4901 return 1;
4902
4903 buf[buf_i] = '\0';
4904
4905 if (idx >= num)
4906 return 0;
4907
4908 return 1;
4909 }
4910
4911 /******************************************************************************
4912 * *
4913 * Function: get_param_len *
4914 * *
4915 * Purpose: return length of the parameter by index (num) *
4916 * from parameter list (param) *
4917 * *
4918 * Parameters: *
4919 * p - [IN] parameter list *
4920 * num - [IN] requested parameter index *
4921 * sz - [OUT] length of requested parameter *
4922 * *
4923 * Return value: *
4924 * 1 - requested parameter missing *
4925 * 0 - requested parameter found *
4926 * (for first parameter result is always 0) *
4927 * *
4928 * Author: Alexander Vladishev *
4929 * *
4930 * Comments: delimiter for parameters is ',' *
4931 * *
4932 ******************************************************************************/
get_param_len(const char * p,int num,size_t * sz)4933 static int get_param_len(const char *p, int num, size_t *sz)
4934 {
4935 /* 0 - init, 1 - inside quoted param, 2 - inside unquoted param */
4936 int state, array, idx = 1;
4937
4938 *sz = 0;
4939
4940 for (state = 0, array = 0; '\0' != *p && idx <= num; p++)
4941 {
4942 switch (state) {
4943 /* Init state */
4944 case 0:
4945 if (',' == *p)
4946 {
4947 if (0 == array)
4948 idx++;
4949 else if (idx == num)
4950 (*sz)++;
4951 }
4952 else if ('"' == *p)
4953 {
4954 state = 1;
4955 if (0 != array && idx == num)
4956 (*sz)++;
4957 }
4958 else if ('[' == *p)
4959 {
4960 if (0 != array && idx == num)
4961 (*sz)++;
4962 array++;
4963 }
4964 else if (']' == *p && 0 != array)
4965 {
4966 array--;
4967 if (0 != array && idx == num)
4968 (*sz)++;
4969
4970 /* skip spaces */
4971 while (' ' == p[1])
4972 p++;
4973
4974 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4975 return 1; /* incorrect syntax */
4976 }
4977 else if (' ' != *p)
4978 {
4979 if (idx == num)
4980 (*sz)++;
4981 state = 2;
4982 }
4983 break;
4984 /* Quoted */
4985 case 1:
4986 if ('"' == *p)
4987 {
4988 if (0 != array && idx == num)
4989 (*sz)++;
4990
4991 /* skip spaces */
4992 while (' ' == p[1])
4993 p++;
4994
4995 if (',' != p[1] && '\0' != p[1] && (0 == array || ']' != p[1]))
4996 return 1; /* incorrect syntax */
4997
4998 state = 0;
4999 }
5000 else if ('\\' == *p && '"' == p[1])
5001 {
5002 if (idx == num && 0 != array)
5003 (*sz)++;
5004
5005 p++;
5006
5007 if (idx == num)
5008 (*sz)++;
5009 }
5010 else if (idx == num)
5011 (*sz)++;
5012 break;
5013 /* Unquoted */
5014 case 2:
5015 if (',' == *p || (']' == *p && 0 != array))
5016 {
5017 p--;
5018 state = 0;
5019 }
5020 else if (idx == num)
5021 (*sz)++;
5022 break;
5023 }
5024
5025 if (idx > num)
5026 break;
5027 }
5028
5029 /* missing terminating '"' character */
5030 if (state == 1)
5031 return 1;
5032
5033 /* missing terminating ']' character */
5034 if (array != 0)
5035 return 1;
5036
5037 if (idx >= num)
5038 return 0;
5039
5040 return 1;
5041 }
5042
5043 /******************************************************************************
5044 * *
5045 * Function: get_param_dyn *
5046 * *
5047 * Purpose: return parameter by index (num) from parameter list (param) *
5048 * *
5049 * Parameters: *
5050 * p - [IN] parameter list *
5051 * num - [IN] requested parameter index *
5052 * type - [OUT] parameter type (may be NULL) *
5053 * *
5054 * Return value: *
5055 * NULL - requested parameter missing *
5056 * otherwise - requested parameter *
5057 * (for first parameter result is not NULL) *
5058 * *
5059 * Author: Alexander Vladishev *
5060 * *
5061 * Comments: delimiter for parameters is ',' *
5062 * *
5063 ******************************************************************************/
get_param_dyn(const char * p,int num,zbx_request_parameter_type_t * type)5064 char *get_param_dyn(const char *p, int num, zbx_request_parameter_type_t *type)
5065 {
5066 char *buf = NULL;
5067 size_t sz;
5068
5069 if (0 != get_param_len(p, num, &sz))
5070 return buf;
5071
5072 buf = (char *)zbx_malloc(buf, sz + 1);
5073
5074 if (0 != get_param(p, num, buf, sz + 1, type))
5075 zbx_free(buf);
5076
5077 return buf;
5078 }
5079
5080 /******************************************************************************
5081 * *
5082 * Function: replace_key_param *
5083 * *
5084 * Purpose: replaces an item key, SNMP OID or their parameters when callback *
5085 * function returns a new string *
5086 * *
5087 * Comments: auxiliary function for replace_key_params_dyn() *
5088 * *
5089 ******************************************************************************/
replace_key_param(char ** data,int key_type,size_t l,size_t * r,int level,int num,int quoted,replace_key_param_f cb,void * cb_data)5090 static int replace_key_param(char **data, int key_type, size_t l, size_t *r, int level, int num, int quoted,
5091 replace_key_param_f cb, void *cb_data)
5092 {
5093 char c = (*data)[*r], *param = NULL;
5094 int ret;
5095
5096 (*data)[*r] = '\0';
5097 ret = cb(*data + l, key_type, level, num, quoted, cb_data, ¶m);
5098 (*data)[*r] = c;
5099
5100 if (NULL != param)
5101 {
5102 (*r)--;
5103 zbx_replace_string(data, l, r, param);
5104 (*r)++;
5105
5106 zbx_free(param);
5107 }
5108
5109 return ret;
5110 }
5111
5112 /******************************************************************************
5113 * *
5114 * Function: replace_key_params_dyn *
5115 * *
5116 * Purpose: replaces an item key, SNMP OID or their parameters by using *
5117 * callback function *
5118 * *
5119 * Parameters: *
5120 * data - [IN/OUT] item key or SNMP OID *
5121 * key_type - [IN] ZBX_KEY_TYPE_* *
5122 * cb - [IN] callback function *
5123 * cb_data - [IN] callback function custom data *
5124 * error - [OUT] error message *
5125 * maxerrlen - [IN] error size *
5126 * *
5127 * Return value: SUCCEED - function executed successfully *
5128 * FAIL - otherwise, error will contain error message *
5129 * *
5130 ******************************************************************************/
replace_key_params_dyn(char ** data,int key_type,replace_key_param_f cb,void * cb_data,char * error,size_t maxerrlen)5131 int replace_key_params_dyn(char **data, int key_type, replace_key_param_f cb, void *cb_data, char *error,
5132 size_t maxerrlen)
5133 {
5134 typedef enum
5135 {
5136 ZBX_STATE_NEW,
5137 ZBX_STATE_END,
5138 ZBX_STATE_UNQUOTED,
5139 ZBX_STATE_QUOTED
5140 }
5141 zbx_parser_state_t;
5142
5143 size_t i = 0, l = 0;
5144 int level = 0, num = 0, ret = SUCCEED;
5145 zbx_parser_state_t state = ZBX_STATE_NEW;
5146
5147 if (ZBX_KEY_TYPE_ITEM == key_type)
5148 {
5149 for (; SUCCEED == is_key_char((*data)[i]) && '\0' != (*data)[i]; i++)
5150 ;
5151
5152 if (0 == i)
5153 goto clean;
5154
5155 if ('[' != (*data)[i] && '\0' != (*data)[i])
5156 goto clean;
5157 }
5158 else
5159 {
5160 zbx_token_t token;
5161 int len, c_l, c_r;
5162
5163 while ('\0' != (*data)[i])
5164 {
5165 if ('{' == (*data)[i] && '$' == (*data)[i + 1] &&
5166 SUCCEED == zbx_user_macro_parse(&(*data)[i], &len, &c_l, &c_r, NULL))
5167 {
5168 i += len + 1; /* skip to the position after user macro */
5169 }
5170 else if ('{' == (*data)[i] && '{' == (*data)[i + 1] && '#' == (*data)[i + 2] &&
5171 SUCCEED == zbx_token_parse_nested_macro(&(*data)[i], &(*data)[i], &token))
5172 {
5173 i += token.loc.r - token.loc.l + 1;
5174 }
5175 else if ('[' != (*data)[i])
5176 {
5177 i++;
5178 }
5179 else
5180 break;
5181 }
5182 }
5183
5184 ret = replace_key_param(data, key_type, 0, &i, level, num, 0, cb, cb_data);
5185
5186 for (; '\0' != (*data)[i] && FAIL != ret; i++)
5187 {
5188 switch (state)
5189 {
5190 case ZBX_STATE_NEW: /* a new parameter started */
5191 switch ((*data)[i])
5192 {
5193 case ' ':
5194 break;
5195 case ',':
5196 ret = replace_key_param(data, key_type, i, &i, level, num, 0, cb,
5197 cb_data);
5198 if (1 == level)
5199 num++;
5200 break;
5201 case '[':
5202 if (2 == level)
5203 goto clean; /* incorrect syntax: multi-level array */
5204 level++;
5205 if (1 == level)
5206 num++;
5207 break;
5208 case ']':
5209 ret = replace_key_param(data, key_type, i, &i, level, num, 0, cb,
5210 cb_data);
5211 level--;
5212 state = ZBX_STATE_END;
5213 break;
5214 case '"':
5215 state = ZBX_STATE_QUOTED;
5216 l = i;
5217 break;
5218 default:
5219 state = ZBX_STATE_UNQUOTED;
5220 l = i;
5221 }
5222 break;
5223 case ZBX_STATE_END: /* end of parameter */
5224 switch ((*data)[i])
5225 {
5226 case ' ':
5227 break;
5228 case ',':
5229 state = ZBX_STATE_NEW;
5230 if (1 == level)
5231 num++;
5232 break;
5233 case ']':
5234 if (0 == level)
5235 goto clean; /* incorrect syntax: redundant ']' */
5236 level--;
5237 break;
5238 default:
5239 goto clean;
5240 }
5241 break;
5242 case ZBX_STATE_UNQUOTED: /* an unquoted parameter */
5243 if (']' == (*data)[i] || ',' == (*data)[i])
5244 {
5245 ret = replace_key_param(data, key_type, l, &i, level, num, 0, cb, cb_data);
5246
5247 i--;
5248 state = ZBX_STATE_END;
5249 }
5250 break;
5251 case ZBX_STATE_QUOTED: /* a quoted parameter */
5252 if ('"' == (*data)[i] && '\\' != (*data)[i - 1])
5253 {
5254 i++;
5255 ret = replace_key_param(data, key_type, l, &i, level, num, 1, cb, cb_data);
5256 i--;
5257
5258 state = ZBX_STATE_END;
5259 }
5260 break;
5261 }
5262 }
5263 clean:
5264 if (0 == i || '\0' != (*data)[i] || 0 != level)
5265 {
5266 if (NULL != error)
5267 {
5268 zbx_snprintf(error, maxerrlen, "Invalid %s at position " ZBX_FS_SIZE_T,
5269 (ZBX_KEY_TYPE_ITEM == key_type ? "item key" : "SNMP OID"), (zbx_fs_size_t)i);
5270 }
5271 ret = FAIL;
5272 }
5273
5274 return ret;
5275 }
5276
5277 /******************************************************************************
5278 * *
5279 * Function: remove_param *
5280 * *
5281 * Purpose: remove parameter by index (num) from parameter list (param) *
5282 * *
5283 * Parameters: *
5284 * param - parameter list *
5285 * num - requested parameter index *
5286 * *
5287 * Return value: *
5288 * *
5289 * Comments: delimiter for parameters is ',' *
5290 * *
5291 ******************************************************************************/
remove_param(char * param,int num)5292 void remove_param(char *param, int num)
5293 {
5294 int state = 0; /* 0 - unquoted parameter, 1 - quoted parameter */
5295 int idx = 1, skip_char = 0;
5296 char *p;
5297
5298 for (p = param; '\0' != *p; p++)
5299 {
5300 switch (state)
5301 {
5302 case 0: /* in unquoted parameter */
5303 if (',' == *p)
5304 {
5305 if (1 == idx && 1 == num)
5306 skip_char = 1;
5307 idx++;
5308 }
5309 else if ('"' == *p)
5310 state = 1;
5311 break;
5312 case 1: /* in quoted parameter */
5313 if ('"' == *p && '\\' != *(p - 1))
5314 state = 0;
5315 break;
5316 }
5317 if (idx != num && 0 == skip_char)
5318 *param++ = *p;
5319
5320 skip_char = 0;
5321 }
5322
5323 *param = '\0';
5324 }
5325
5326 /******************************************************************************
5327 * *
5328 * Function: str_in_list *
5329 * *
5330 * Purpose: check if string is contained in a list of delimited strings *
5331 * *
5332 * Parameters: list - strings a,b,ccc,ddd *
5333 * value - value *
5334 * delimiter - delimiter *
5335 * *
5336 * Return value: SUCCEED - string is in the list, FAIL - otherwise *
5337 * *
5338 * Author: Alexei Vladishev, Aleksandrs Saveljevs *
5339 * *
5340 ******************************************************************************/
str_in_list(const char * list,const char * value,char delimiter)5341 int str_in_list(const char *list, const char *value, char delimiter)
5342 {
5343 const char *end;
5344 int ret = FAIL;
5345 size_t len;
5346
5347 len = strlen(value);
5348
5349 while (SUCCEED != ret)
5350 {
5351 if (NULL != (end = strchr(list, delimiter)))
5352 {
5353 ret = (len == (size_t)(end - list) && 0 == strncmp(list, value, len) ? SUCCEED : FAIL);
5354 list = end + 1;
5355 }
5356 else
5357 {
5358 ret = (0 == strcmp(list, value) ? SUCCEED : FAIL);
5359 break;
5360 }
5361 }
5362
5363 return ret;
5364 }
5365
5366 /******************************************************************************
5367 * *
5368 * Function: get_key_param *
5369 * *
5370 * Purpose: return parameter by index (num) from parameter list (param) *
5371 * to be used for keys: key[param1,param2] *
5372 * *
5373 * Parameters: *
5374 * param - parameter list *
5375 * num - requested parameter index *
5376 * buf - pointer of output buffer *
5377 * max_len - size of output buffer *
5378 * *
5379 * Return value: *
5380 * 1 - requested parameter missing *
5381 * 0 - requested parameter found (value - 'buf' can be empty string) *
5382 * *
5383 * Author: Alexei Vladishev *
5384 * *
5385 * Comments: delimiter for parameters is ',' *
5386 * *
5387 ******************************************************************************/
get_key_param(char * param,int num,char * buf,size_t max_len)5388 int get_key_param(char *param, int num, char *buf, size_t max_len)
5389 {
5390 int ret;
5391 char *pl, *pr;
5392
5393 pl = strchr(param, '[');
5394 pr = strrchr(param, ']');
5395
5396 if (NULL == pl || NULL == pr || pl > pr)
5397 return 1;
5398
5399 *pr = '\0';
5400 ret = get_param(pl + 1, num, buf, max_len, NULL);
5401 *pr = ']';
5402
5403 return ret;
5404 }
5405
5406 /******************************************************************************
5407 * *
5408 * Function: num_key_param *
5409 * *
5410 * Purpose: calculate count of parameters from parameter list (param) *
5411 * to be used for keys: key[param1,param2] *
5412 * *
5413 * Parameters: *
5414 * param - parameter list *
5415 * *
5416 * Return value: count of parameters *
5417 * *
5418 * Author: Alexei Vladishev *
5419 * *
5420 * Comments: delimiter for parameters is ',' *
5421 * *
5422 ******************************************************************************/
num_key_param(char * param)5423 int num_key_param(char *param)
5424 {
5425 int ret;
5426 char *pl, *pr;
5427
5428 if (NULL == param)
5429 return 0;
5430
5431 pl = strchr(param, '[');
5432 pr = strrchr(param, ']');
5433
5434 if (NULL == pl || NULL == pr || pl > pr)
5435 return 0;
5436
5437 *pr = '\0';
5438 ret = num_param(pl + 1);
5439 *pr = ']';
5440
5441 return ret;
5442 }
5443
5444 /******************************************************************************
5445 * *
5446 * Function: zbx_replace_mem_dyn *
5447 * *
5448 * Purpose: to replace memory block and allocate more memory if needed *
5449 * *
5450 * Parameters: data - [IN/OUT] allocated memory *
5451 * data_alloc - [IN/OUT] allocated memory size *
5452 * data_len - [IN/OUT] used memory size *
5453 * offset - [IN] offset of memory block to be replaced *
5454 * sz_to - [IN] size of block that need to be replaced *
5455 * from - [IN] what to replace with *
5456 * sz_from - [IN] size of new block *
5457 * *
5458 * Return value: once data is replaced offset can become less, bigger or *
5459 * remain unchanged *
5460 ******************************************************************************/
zbx_replace_mem_dyn(char ** data,size_t * data_alloc,size_t * data_len,size_t offset,size_t sz_to,const char * from,size_t sz_from)5461 int zbx_replace_mem_dyn(char **data, size_t *data_alloc, size_t *data_len, size_t offset, size_t sz_to,
5462 const char *from, size_t sz_from)
5463 {
5464 size_t sz_changed = sz_from - sz_to;
5465
5466 if (0 != sz_changed)
5467 {
5468 char *to;
5469
5470 *data_len += sz_changed;
5471
5472 if (*data_len > *data_alloc)
5473 {
5474 while (*data_len > *data_alloc)
5475 *data_alloc *= 2;
5476
5477 *data = (char *)zbx_realloc(*data, *data_alloc);
5478 }
5479
5480 to = *data + offset;
5481 memmove(to + sz_from, to + sz_to, *data_len - (to - *data) - sz_from);
5482 }
5483
5484 memcpy(*data + offset, from, sz_from);
5485
5486 return (int)sz_changed;
5487 }
5488
5489 /******************************************************************************
5490 * *
5491 * Function: zbx_strsplit *
5492 * *
5493 * Purpose: splits string *
5494 * *
5495 * Parameters: src - [IN] source string *
5496 * delimiter - [IN] delimiter *
5497 * left - [IN/OUT] first part of the string *
5498 * right - [IN/OUT] second part of the string or NULL, if *
5499 * delimiter was not found *
5500 * *
5501 ******************************************************************************/
zbx_strsplit(const char * src,char delimiter,char ** left,char ** right)5502 void zbx_strsplit(const char *src, char delimiter, char **left, char **right)
5503 {
5504 char *delimiter_ptr;
5505
5506 if (NULL == (delimiter_ptr = strchr(src, delimiter)))
5507 {
5508 *left = zbx_strdup(NULL, src);
5509 *right = NULL;
5510 }
5511 else
5512 {
5513 size_t left_size;
5514 size_t right_size;
5515
5516 left_size = (size_t)(delimiter_ptr - src) + 1;
5517 right_size = strlen(src) - (size_t)(delimiter_ptr - src);
5518
5519 *left = zbx_malloc(NULL, left_size);
5520 *right = zbx_malloc(NULL, right_size);
5521
5522 memcpy(*left, src, left_size - 1);
5523 (*left)[left_size - 1] = '\0';
5524 memcpy(*right, delimiter_ptr + 1, right_size);
5525 }
5526 }
5527
5528 /******************************************************************************
5529 * *
5530 * Function: zbx_trim_number *
5531 * *
5532 * Purpose: Removes spaces from both ends of the string, then unquotes it if *
5533 * double quotation mark is present on both ends of the string. If *
5534 * strip_plus_sign is non-zero, then removes single "+" sign from *
5535 * the beginning of the trimmed and unquoted string. *
5536 * *
5537 * This function does not guarantee that the resulting string *
5538 * contains numeric value. It is meant to be used for removing *
5539 * "valid" characters from the value that is expected to be numeric *
5540 * before checking if value is numeric. *
5541 * *
5542 * Parameters: str - [IN/OUT] string for processing *
5543 * strip_plus_sign - [IN] non-zero if "+" should be stripped *
5544 * *
5545 ******************************************************************************/
zbx_trim_number(char * str,int strip_plus_sign)5546 static void zbx_trim_number(char *str, int strip_plus_sign)
5547 {
5548 char *left = str; /* pointer to the first character */
5549 char *right = strchr(str, '\0') - 1; /* pointer to the last character, not including terminating null-char */
5550
5551 if (left > right)
5552 {
5553 /* string is empty before any trimming */
5554 return;
5555 }
5556
5557 while (' ' == *left)
5558 {
5559 left++;
5560 }
5561
5562 while (' ' == *right && left < right)
5563 {
5564 right--;
5565 }
5566
5567 if ('"' == *left && '"' == *right && left < right)
5568 {
5569 left++;
5570 right--;
5571 }
5572
5573 if (0 != strip_plus_sign && '+' == *left)
5574 {
5575 left++;
5576 }
5577
5578 if (left > right)
5579 {
5580 /* string is empty after trimming */
5581 *str = '\0';
5582 return;
5583 }
5584
5585 if (str < left)
5586 {
5587 while (left <= right)
5588 {
5589 *str++ = *left++;
5590 }
5591 *str = '\0';
5592 }
5593 else
5594 {
5595 *(right + 1) = '\0';
5596 }
5597 }
5598
5599 /******************************************************************************
5600 * *
5601 * Function: zbx_trim_integer *
5602 * *
5603 * Purpose: Removes spaces from both ends of the string, then unquotes it if *
5604 * double quotation mark is present on both ends of the string, then *
5605 * removes single "+" sign from the beginning of the trimmed and *
5606 * unquoted string. *
5607 * *
5608 * This function does not guarantee that the resulting string *
5609 * contains integer value. It is meant to be used for removing *
5610 * "valid" characters from the value that is expected to be numeric *
5611 * before checking if value is numeric. *
5612 * *
5613 * Parameters: str - [IN/OUT] string for processing *
5614 * *
5615 ******************************************************************************/
zbx_trim_integer(char * str)5616 void zbx_trim_integer(char *str)
5617 {
5618 zbx_trim_number(str, 1);
5619 }
5620
5621 /******************************************************************************
5622 * *
5623 * Function: zbx_trim_float *
5624 * *
5625 * Purpose: Removes spaces from both ends of the string, then unquotes it if *
5626 * double quotation mark is present on both ends of the string. *
5627 * *
5628 * This function does not guarantee that the resulting string *
5629 * contains floating-point number. It is meant to be used for *
5630 * removing "valid" characters from the value that is expected to be *
5631 * numeric before checking if value is numeric. *
5632 * *
5633 * Parameters: str - [IN/OUT] string for processing *
5634 * *
5635 ******************************************************************************/
zbx_trim_float(char * str)5636 void zbx_trim_float(char *str)
5637 {
5638 zbx_trim_number(str, 0);
5639 }
5640
5641 /******************************************************************************
5642 * *
5643 * Function: zbx_get_component_version *
5644 * *
5645 * Purpose: extracts protocol version from value *
5646 * *
5647 * Parameters: *
5648 * value - [IN] textual representation of version *
5649 * *
5650 * Return value: The protocol version if it was successfully extracted, *
5651 * otherwise -1 *
5652 * *
5653 ******************************************************************************/
zbx_get_component_version(char * value)5654 int zbx_get_component_version(char *value)
5655 {
5656 char *pminor, *ptr;
5657
5658 if (NULL == (pminor = strchr(value, '.')))
5659 return FAIL;
5660
5661 *pminor++ = '\0';
5662
5663 if (NULL != (ptr = strchr(pminor, '.')))
5664 *ptr = '\0';
5665
5666 return ZBX_COMPONENT_VERSION(atoi(value), atoi(pminor));
5667 }
5668
5669 /******************************************************************************
5670 * *
5671 * Function: zbx_str_extract *
5672 * *
5673 * Purpose: extracts value from a string, unquoting if necessary *
5674 * *
5675 * Parameters: *
5676 * text - [IN] the text containing value to extract *
5677 * len - [IN] length (in bytes) of the value to extract. *
5678 * It can be 0. It must not exceed length of 'text' string. *
5679 * value - [OUT] the extracted value *
5680 * *
5681 * Return value: SUCCEED - the value was extracted successfully *
5682 * FAIL - otherwise *
5683 * *
5684 * Comments: When unquoting value only " and \ character escapes are accepted.*
5685 * *
5686 ******************************************************************************/
zbx_str_extract(const char * text,size_t len,char ** value)5687 int zbx_str_extract(const char *text, size_t len, char **value)
5688 {
5689 char *tmp, *out;
5690 const char *in;
5691
5692 tmp = zbx_malloc(NULL, len + 1);
5693
5694 if (0 == len)
5695 {
5696 *tmp = '\0';
5697 *value = tmp;
5698 return SUCCEED;
5699 }
5700
5701 if ('"' != *text)
5702 {
5703 memcpy(tmp, text, len);
5704 tmp[len] = '\0';
5705 *value = tmp;
5706 return SUCCEED;
5707 }
5708
5709 if (2 > len)
5710 goto fail;
5711
5712 for (out = tmp, in = text + 1; '"' != *in; in++)
5713 {
5714 if ((size_t)(in - text) >= len - 1)
5715 goto fail;
5716
5717 if ('\\' == *in)
5718 {
5719 if ((size_t)(++in - text) >= len - 1)
5720 goto fail;
5721
5722 if ('"' != *in && '\\' != *in)
5723 goto fail;
5724 }
5725 *out++ = *in;
5726 }
5727
5728 if ((size_t)(in - text) != len - 1)
5729 goto fail;
5730
5731 *out = '\0';
5732 *value = tmp;
5733 return SUCCEED;
5734 fail:
5735 zbx_free(tmp);
5736 return FAIL;
5737 }
5738
5739 /******************************************************************************
5740 * *
5741 * Function: zbx_truncate_itemkey *
5742 * *
5743 * Purpose: check the item key characters length and, if the length exceeds *
5744 * max allowable characters length, truncate the item key, while *
5745 * maintaining the right square bracket *
5746 * *
5747 * Parameters: key - [IN] item key for processing *
5748 * char_max - [IN] item key max characters length *
5749 * buf - [IN/OUT] buffer for short version of item key *
5750 * buf_len - [IN] buffer size for short version of item key *
5751 * *
5752 * Return value: The item key that does not exceed passed length *
5753 * *
5754 ******************************************************************************/
zbx_truncate_itemkey(const char * key,const size_t char_max,char * buf,const size_t buf_len)5755 const char *zbx_truncate_itemkey(const char *key, const size_t char_max, char *buf, const size_t buf_len)
5756 {
5757 # define ZBX_SUFFIX "..."
5758 # define ZBX_BSUFFIX "[...]"
5759
5760 size_t key_byte_count, key_char_total;
5761 int is_bracket = 0;
5762 char *bracket_l;
5763
5764 if (char_max >= (key_char_total = zbx_strlen_utf8(key)))
5765 return key;
5766
5767 if (NULL != (bracket_l = strchr(key, '[')))
5768 is_bracket = 1;
5769
5770 if (char_max < ZBX_CONST_STRLEN(ZBX_SUFFIX) + 2 * is_bracket) /* [...] or ... */
5771 return key;
5772
5773 if (0 != is_bracket)
5774 {
5775 size_t key_char_count, param_char_count, param_byte_count;
5776
5777 key_char_count = zbx_charcount_utf8_nbytes(key, bracket_l - key);
5778 param_char_count = key_char_total - key_char_count;
5779
5780 if (param_char_count <= ZBX_CONST_STRLEN(ZBX_BSUFFIX))
5781 {
5782 if (char_max < param_char_count + ZBX_CONST_STRLEN(ZBX_SUFFIX))
5783 return key;
5784
5785 key_byte_count = 1 + zbx_strlen_utf8_nchars(key, char_max - param_char_count -
5786 ZBX_CONST_STRLEN(ZBX_SUFFIX));
5787 param_byte_count = 1 + zbx_strlen_utf8_nchars(bracket_l, key_char_count);
5788
5789 if (buf_len < key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX) + param_byte_count - 1)
5790 return key;
5791
5792 key_byte_count = zbx_strlcpy_utf8(buf, key, key_byte_count);
5793 key_byte_count += zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_SUFFIX, sizeof(ZBX_SUFFIX));
5794 zbx_strlcpy_utf8(&buf[key_byte_count], bracket_l, param_byte_count);
5795
5796 return buf;
5797 }
5798
5799 if (key_char_count + ZBX_CONST_STRLEN(ZBX_BSUFFIX) > char_max)
5800 {
5801 if (char_max <= ZBX_CONST_STRLEN(ZBX_SUFFIX) + ZBX_CONST_STRLEN(ZBX_BSUFFIX))
5802 return key;
5803
5804 key_byte_count = 1 + zbx_strlen_utf8_nchars(key, char_max - ZBX_CONST_STRLEN(ZBX_SUFFIX) -
5805 ZBX_CONST_STRLEN(ZBX_BSUFFIX));
5806
5807 if (buf_len < key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX) + ZBX_CONST_STRLEN(ZBX_BSUFFIX))
5808 return key;
5809
5810 key_byte_count = zbx_strlcpy_utf8(buf, key, key_byte_count);
5811 key_byte_count += zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_SUFFIX, sizeof(ZBX_SUFFIX));
5812 zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_BSUFFIX, sizeof(ZBX_BSUFFIX));
5813
5814 return buf;
5815 }
5816 }
5817
5818 key_byte_count = 1 + zbx_strlen_utf8_nchars(key, char_max - (ZBX_CONST_STRLEN(ZBX_SUFFIX) + is_bracket));
5819
5820 if (buf_len < key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX) + is_bracket)
5821 return key;
5822
5823 key_byte_count = zbx_strlcpy_utf8(buf, key, key_byte_count);
5824 zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_SUFFIX, sizeof(ZBX_SUFFIX));
5825
5826 if (0 != is_bracket)
5827 zbx_strlcpy_utf8(&buf[key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX)], "]", sizeof("]"));
5828
5829 return buf;
5830
5831 # undef ZBX_SUFFIX
5832 # undef ZBX_BSUFFIX
5833 }
5834
5835 /******************************************************************************
5836 * *
5837 * Function: zbx_truncate_value *
5838 * *
5839 * Purpose: check the value characters length and, if the length exceeds *
5840 * max allowable characters length, truncate the value *
5841 * *
5842 * Parameters: val - [IN] value for processing *
5843 * char_max - [IN] value max characters length *
5844 * buf - [IN/OUT] buffer for short version of value *
5845 * buf_len - [IN] buffer size for short version of value *
5846 * *
5847 * Return value: The value that does not exceed passed length *
5848 * *
5849 ******************************************************************************/
zbx_truncate_value(const char * val,const size_t char_max,char * buf,const size_t buf_len)5850 const char *zbx_truncate_value(const char *val, const size_t char_max, char *buf, const size_t buf_len)
5851 {
5852 # define ZBX_SUFFIX "..."
5853
5854 size_t key_byte_count;
5855
5856 if (char_max >= zbx_strlen_utf8(val))
5857 return val;
5858
5859 key_byte_count = 1 + zbx_strlen_utf8_nchars(val, char_max - ZBX_CONST_STRLEN(ZBX_SUFFIX));
5860
5861 if (buf_len < key_byte_count + ZBX_CONST_STRLEN(ZBX_SUFFIX))
5862 return val;
5863
5864 key_byte_count = zbx_strlcpy_utf8(buf, val, key_byte_count);
5865 zbx_strlcpy_utf8(&buf[key_byte_count], ZBX_SUFFIX, sizeof(ZBX_SUFFIX));
5866
5867 return buf;
5868
5869 # undef ZBX_SUFFIX
5870 }
5871
5872 /******************************************************************************
5873 * *
5874 * Function: zbx_print_double *
5875 * *
5876 * Purpose: converts double value to string and truncates insignificant *
5877 * precision *
5878 * *
5879 * Parameters: buffer - [OUT] the output buffer *
5880 * size - [IN] the output buffer size *
5881 * val - [IN] double value to be converted *
5882 * *
5883 * Return value: the oputput buffer with printed value *
5884 * *
5885 ******************************************************************************/
zbx_print_double(char * buffer,size_t size,double val)5886 const char *zbx_print_double(char *buffer, size_t size, double val)
5887 {
5888 zbx_snprintf(buffer, size, "%.15G", val);
5889
5890 if (atof(buffer) != val)
5891 zbx_snprintf(buffer, size, ZBX_FS_DBL64, val);
5892
5893 return buffer;
5894 }
5895