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 "cfg.h"
22 #include "log.h"
23 #include "comms.h"
24
25 extern unsigned char program_type;
26
27 char *CONFIG_FILE = NULL;
28
29 char *CONFIG_LOG_TYPE_STR = NULL;
30 int CONFIG_LOG_TYPE = LOG_TYPE_UNDEFINED;
31 char *CONFIG_LOG_FILE = NULL;
32 int CONFIG_LOG_FILE_SIZE = 1;
33 int CONFIG_ALLOW_ROOT = 0;
34 int CONFIG_TIMEOUT = 3;
35
36 static int __parse_cfg_file(const char *cfg_file, struct cfg_line *cfg, int level, int optional, int strict);
37
38 /******************************************************************************
39 * *
40 * Function: match_glob *
41 * *
42 * Purpose: see whether a file (e.g., "parameter.conf") *
43 * matches a pattern (e.g., "p*.conf") *
44 * *
45 * Return value: SUCCEED - file matches a pattern *
46 * FAIL - otherwise *
47 * *
48 ******************************************************************************/
match_glob(const char * file,const char * pattern)49 static int match_glob(const char *file, const char *pattern)
50 {
51 const char *f, *g, *p, *q;
52
53 f = file;
54 p = pattern;
55
56 while (1)
57 {
58 /* corner case */
59
60 if ('\0' == *p)
61 return '\0' == *f ? SUCCEED : FAIL;
62
63 /* find a set of literal characters */
64
65 while ('*' == *p)
66 p++;
67
68 for (q = p; '\0' != *q && '*' != *q; q++)
69 ;
70
71 /* if literal characters are at the beginning... */
72
73 if (pattern == p)
74 {
75 #ifdef _WINDOWS
76 if (0 != zbx_strncasecmp(f, p, q - p))
77 #else
78 if (0 != strncmp(f, p, q - p))
79 #endif
80 return FAIL;
81
82 f += q - p;
83 p = q;
84
85 continue;
86 }
87
88 /* if literal characters are at the end... */
89
90 if ('\0' == *q)
91 {
92 for (g = f; '\0' != *g; g++)
93 ;
94
95 if (g - f < q - p)
96 return FAIL;
97 #ifdef _WINDOWS
98 return 0 == strcasecmp(g - (q - p), p) ? SUCCEED : FAIL;
99 #else
100 return 0 == strcmp(g - (q - p), p) ? SUCCEED : FAIL;
101 #endif
102 }
103
104 /* if literal characters are in the middle... */
105
106 while (1)
107 {
108 if ('\0' == *f)
109 return FAIL;
110 #ifdef _WINDOWS
111 if (0 == zbx_strncasecmp(f, p, q - p))
112 #else
113 if (0 == strncmp(f, p, q - p))
114 #endif
115 {
116 f += q - p;
117 p = q;
118
119 break;
120 }
121
122 f++;
123 }
124 }
125 }
126
127 /******************************************************************************
128 * *
129 * Function: parse_glob *
130 * *
131 * Purpose: parse a glob like "/usr/local/etc/zabbix_agentd.conf.d/p*.conf" *
132 * into "/usr/local/etc/zabbix_agentd.conf.d" and "p*.conf" parts *
133 * *
134 * Parameters: glob - [IN] glob as specified in Include directive *
135 * path - [OUT] parsed path, either directory or file *
136 * pattern - [OUT] parsed pattern, if path is directory *
137 * *
138 * Return value: SUCCEED - glob is valid and was parsed successfully *
139 * FAIL - otherwise *
140 * *
141 ******************************************************************************/
parse_glob(const char * glob,char ** path,char ** pattern)142 static int parse_glob(const char *glob, char **path, char **pattern)
143 {
144 const char *p;
145
146 if (NULL == (p = strchr(glob, '*')))
147 {
148 *path = zbx_strdup(NULL, glob);
149 *pattern = NULL;
150
151 goto trim;
152 }
153
154 if (NULL != strchr(p + 1, PATH_SEPARATOR))
155 {
156 zbx_error("%s: glob pattern should be the last component of the path", glob);
157 return FAIL;
158 }
159
160 do
161 {
162 if (glob == p)
163 {
164 zbx_error("%s: path should be absolute", glob);
165 return FAIL;
166 }
167
168 p--;
169 }
170 while (PATH_SEPARATOR != *p);
171
172 *path = zbx_strdup(NULL, glob);
173 (*path)[p - glob] = '\0';
174
175 *pattern = zbx_strdup(NULL, p + 1);
176 trim:
177 #ifdef _WINDOWS
178 if (0 != zbx_rtrim(*path, "\\") && NULL == *pattern)
179 *pattern = zbx_strdup(NULL, "*"); /* make sure path is a directory */
180
181 if (':' == (*path)[1] && '\0' == (*path)[2] && '\\' == glob[2]) /* retain backslash for "C:\" */
182 {
183 (*path)[2] = '\\';
184 (*path)[3] = '\0';
185 }
186 #else
187 if (0 != zbx_rtrim(*path, "/") && NULL == *pattern)
188 *pattern = zbx_strdup(NULL, "*"); /* make sure path is a directory */
189
190 if ('\0' == (*path)[0] && '/' == glob[0]) /* retain forward slash for "/" */
191 {
192 (*path)[0] = '/';
193 (*path)[1] = '\0';
194 }
195 #endif
196 return SUCCEED;
197 }
198
199 /******************************************************************************
200 * *
201 * Function: parse_cfg_dir *
202 * *
203 * Purpose: parse directory with configuration files *
204 * *
205 * Parameters: path - full path to directory *
206 * pattern - pattern that files in the directory should match *
207 * cfg - pointer to configuration parameter structure *
208 * level - a level of included file *
209 * strict - treat unknown parameters as error *
210 * *
211 * Return value: SUCCEED - parsed successfully *
212 * FAIL - error processing directory *
213 * *
214 ******************************************************************************/
215 #ifdef _WINDOWS
parse_cfg_dir(const char * path,const char * pattern,struct cfg_line * cfg,int level,int strict)216 static int parse_cfg_dir(const char *path, const char *pattern, struct cfg_line *cfg, int level, int strict)
217 {
218 WIN32_FIND_DATAW find_file_data;
219 HANDLE h_find;
220 char *find_path = NULL, *file = NULL, *file_name;
221 wchar_t *wfind_path = NULL;
222 int ret = FAIL;
223
224 find_path = zbx_dsprintf(find_path, "%s\\*", path);
225 wfind_path = zbx_utf8_to_unicode(find_path);
226
227 if (INVALID_HANDLE_VALUE == (h_find = FindFirstFileW(wfind_path, &find_file_data)))
228 goto clean;
229
230 while (0 != FindNextFileW(h_find, &find_file_data))
231 {
232 if (0 != (find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
233 continue;
234
235 file_name = zbx_unicode_to_utf8(find_file_data.cFileName);
236
237 if (NULL != pattern && SUCCEED != match_glob(file_name, pattern))
238 {
239 zbx_free(file_name);
240 continue;
241 }
242
243 file = zbx_dsprintf(file, "%s\\%s", path, file_name);
244
245 zbx_free(file_name);
246
247 if (SUCCEED != __parse_cfg_file(file, cfg, level, ZBX_CFG_FILE_REQUIRED, strict))
248 goto close;
249 }
250
251 ret = SUCCEED;
252 close:
253 zbx_free(file);
254 FindClose(h_find);
255 clean:
256 zbx_free(wfind_path);
257 zbx_free(find_path);
258
259 return ret;
260 }
261 #else
parse_cfg_dir(const char * path,const char * pattern,struct cfg_line * cfg,int level,int strict)262 static int parse_cfg_dir(const char *path, const char *pattern, struct cfg_line *cfg, int level, int strict)
263 {
264 DIR *dir;
265 struct dirent *d;
266 zbx_stat_t sb;
267 char *file = NULL;
268 int ret = FAIL;
269
270 if (NULL == (dir = opendir(path)))
271 {
272 zbx_error("%s: %s", path, zbx_strerror(errno));
273 goto out;
274 }
275
276 while (NULL != (d = readdir(dir)))
277 {
278 file = zbx_dsprintf(file, "%s/%s", path, d->d_name);
279
280 if (0 != zbx_stat(file, &sb) || 0 == S_ISREG(sb.st_mode))
281 continue;
282
283 if (NULL != pattern && SUCCEED != match_glob(d->d_name, pattern))
284 continue;
285
286 if (SUCCEED != __parse_cfg_file(file, cfg, level, ZBX_CFG_FILE_REQUIRED, strict))
287 goto close;
288 }
289
290 ret = SUCCEED;
291 close:
292 if (0 != closedir(dir))
293 {
294 zbx_error("%s: %s", path, zbx_strerror(errno));
295 ret = FAIL;
296 }
297
298 zbx_free(file);
299 out:
300 return ret;
301 }
302 #endif
303
304 /******************************************************************************
305 * *
306 * Function: parse_cfg_object *
307 * *
308 * Purpose: parse "Include=..." line in configuration file *
309 * *
310 * Parameters: cfg_file - full name of config file *
311 * cfg - pointer to configuration parameter structure *
312 * level - a level of included file *
313 * strict - treat unknown parameters as error *
314 * *
315 * Return value: SUCCEED - parsed successfully *
316 * FAIL - error processing object *
317 * *
318 ******************************************************************************/
parse_cfg_object(const char * cfg_file,struct cfg_line * cfg,int level,int strict)319 static int parse_cfg_object(const char *cfg_file, struct cfg_line *cfg, int level, int strict)
320 {
321 int ret = FAIL;
322 char *path = NULL, *pattern = NULL;
323 zbx_stat_t sb;
324
325 if (SUCCEED != parse_glob(cfg_file, &path, &pattern))
326 goto clean;
327
328 if (0 != zbx_stat(path, &sb))
329 {
330 zbx_error("%s: %s", path, zbx_strerror(errno));
331 goto clean;
332 }
333
334 if (0 == S_ISDIR(sb.st_mode))
335 {
336 if (NULL == pattern)
337 {
338 ret = __parse_cfg_file(path, cfg, level, ZBX_CFG_FILE_REQUIRED, strict);
339 goto clean;
340 }
341
342 zbx_error("%s: base path is not a directory", cfg_file);
343 goto clean;
344 }
345
346 ret = parse_cfg_dir(path, pattern, cfg, level, strict);
347 clean:
348 zbx_free(pattern);
349 zbx_free(path);
350
351 return ret;
352 }
353
354 /******************************************************************************
355 * *
356 * Function: parse_cfg_file *
357 * *
358 * Purpose: parse configuration file *
359 * *
360 * Parameters: cfg_file - full name of config file *
361 * cfg - pointer to configuration parameter structure *
362 * level - a level of included file *
363 * optional - do not treat missing configuration file as error *
364 * strict - treat unknown parameters as error *
365 * *
366 * Return value: SUCCEED - parsed successfully *
367 * FAIL - error processing config file *
368 * *
369 * Author: Alexei Vladishev, Eugene Grigorjev *
370 * *
371 ******************************************************************************/
__parse_cfg_file(const char * cfg_file,struct cfg_line * cfg,int level,int optional,int strict)372 static int __parse_cfg_file(const char *cfg_file, struct cfg_line *cfg, int level, int optional, int strict)
373 {
374 #define ZBX_MAX_INCLUDE_LEVEL 10
375
376 #define ZBX_CFG_LTRIM_CHARS "\t "
377 #define ZBX_CFG_RTRIM_CHARS ZBX_CFG_LTRIM_CHARS "\r\n"
378
379 FILE *file;
380 int i, lineno, param_valid;
381 char line[MAX_STRING_LEN + 3], *parameter, *value;
382 zbx_uint64_t var;
383 size_t len;
384 #ifdef _WINDOWS
385 wchar_t *wcfg_file;
386 #endif
387 if (++level > ZBX_MAX_INCLUDE_LEVEL)
388 {
389 zbx_error("Recursion detected! Skipped processing of '%s'.", cfg_file);
390 return FAIL;
391 }
392
393 if (NULL != cfg_file)
394 {
395 #ifdef _WINDOWS
396 wcfg_file = zbx_utf8_to_unicode(cfg_file);
397 file = _wfopen(wcfg_file, L"r");
398 zbx_free(wcfg_file);
399
400 if (NULL == file)
401 goto cannot_open;
402 #else
403 if (NULL == (file = fopen(cfg_file, "r")))
404 goto cannot_open;
405 #endif
406 for (lineno = 1; NULL != fgets(line, sizeof(line), file); lineno++)
407 {
408 /* check if line length exceeds limit (max. 2048 bytes) */
409 len = strlen(line);
410 if (MAX_STRING_LEN < len && NULL == strchr("\r\n", line[MAX_STRING_LEN]))
411 goto line_too_long;
412
413 zbx_ltrim(line, ZBX_CFG_LTRIM_CHARS);
414 zbx_rtrim(line, ZBX_CFG_RTRIM_CHARS);
415
416 if ('#' == *line || '\0' == *line)
417 continue;
418
419 /* we only support UTF-8 characters in the config file */
420 if (SUCCEED != zbx_is_utf8(line))
421 goto non_utf8;
422
423 parameter = line;
424 if (NULL == (value = strchr(line, '=')))
425 goto non_key_value;
426
427 *value++ = '\0';
428
429 zbx_rtrim(parameter, ZBX_CFG_RTRIM_CHARS);
430 zbx_ltrim(value, ZBX_CFG_LTRIM_CHARS);
431
432 zabbix_log(LOG_LEVEL_DEBUG, "cfg: para: [%s] val [%s]", parameter, value);
433
434 if (0 == strcmp(parameter, "Include"))
435 {
436 if (FAIL == parse_cfg_object(value, cfg, level, strict))
437 {
438 fclose(file);
439 goto error;
440 }
441
442 continue;
443 }
444
445 param_valid = 0;
446
447 for (i = 0; NULL != cfg[i].parameter; i++)
448 {
449 if (0 != strcmp(cfg[i].parameter, parameter))
450 continue;
451
452 param_valid = 1;
453
454 zabbix_log(LOG_LEVEL_DEBUG, "accepted configuration parameter: '%s' = '%s'",
455 parameter, value);
456
457 switch (cfg[i].type)
458 {
459 case TYPE_INT:
460 if (FAIL == str2uint64(value, "KMGT", &var))
461 goto incorrect_config;
462
463 if (cfg[i].min > var || (0 != cfg[i].max && var > cfg[i].max))
464 goto incorrect_config;
465
466 *((int *)cfg[i].variable) = (int)var;
467 break;
468 case TYPE_STRING_LIST:
469 zbx_trim_str_list(value, ',');
470 ZBX_FALLTHROUGH;
471 case TYPE_STRING:
472 *((char **)cfg[i].variable) =
473 zbx_strdup(*((char **)cfg[i].variable), value);
474 break;
475 case TYPE_MULTISTRING:
476 zbx_strarr_add((char ***)cfg[i].variable, value);
477 break;
478 case TYPE_UINT64:
479 if (FAIL == str2uint64(value, "KMGT", &var))
480 goto incorrect_config;
481
482 if (cfg[i].min > var || (0 != cfg[i].max && var > cfg[i].max))
483 goto incorrect_config;
484
485 *((zbx_uint64_t *)cfg[i].variable) = var;
486 break;
487 case TYPE_CUSTOM:
488 if (NULL != cfg[i].variable)
489 {
490 cfg_custom_parameter_parser_t custom_parser =
491 (cfg_custom_parameter_parser_t)cfg[i].variable;
492
493 if (SUCCEED != custom_parser(value, &cfg[i]))
494 goto incorrect_config;
495
496 continue;
497 }
498 break;
499 default:
500 assert(0);
501 }
502 }
503
504 if (0 == param_valid && ZBX_CFG_STRICT == strict)
505 goto unknown_parameter;
506 }
507 fclose(file);
508 }
509
510 if (1 != level) /* skip mandatory parameters check for included files */
511 return SUCCEED;
512
513 for (i = 0; NULL != cfg[i].parameter; i++) /* check for mandatory parameters */
514 {
515 if (PARM_MAND != cfg[i].mandatory)
516 continue;
517
518 switch (cfg[i].type)
519 {
520 case TYPE_INT:
521 if (0 == *((int *)cfg[i].variable))
522 goto missing_mandatory;
523 break;
524 case TYPE_STRING:
525 case TYPE_STRING_LIST:
526 if (NULL == (*(char **)cfg[i].variable))
527 goto missing_mandatory;
528 break;
529 default:
530 assert(0);
531 }
532 }
533
534 return SUCCEED;
535 cannot_open:
536 if (ZBX_CFG_FILE_REQUIRED != optional)
537 return SUCCEED;
538 zbx_error("cannot open config file \"%s\": %s", cfg_file, zbx_strerror(errno));
539 goto error;
540 line_too_long:
541 fclose(file);
542 zbx_error("line %d exceeds %d byte length limit in config file \"%s\"", lineno, MAX_STRING_LEN, cfg_file);
543 goto error;
544 non_utf8:
545 fclose(file);
546 zbx_error("non-UTF-8 character at line %d \"%s\" in config file \"%s\"", lineno, line, cfg_file);
547 goto error;
548 non_key_value:
549 fclose(file);
550 zbx_error("invalid entry \"%s\" (not following \"parameter=value\" notation) in config file \"%s\", line %d",
551 line, cfg_file, lineno);
552 goto error;
553 incorrect_config:
554 fclose(file);
555 zbx_error("wrong value of \"%s\" in config file \"%s\", line %d", cfg[i].parameter, cfg_file, lineno);
556 goto error;
557 unknown_parameter:
558 fclose(file);
559 zbx_error("unknown parameter \"%s\" in config file \"%s\", line %d", parameter, cfg_file, lineno);
560 goto error;
561 missing_mandatory:
562 zbx_error("missing mandatory parameter \"%s\" in config file \"%s\"", cfg[i].parameter, cfg_file);
563 error:
564 exit(EXIT_FAILURE);
565 }
566
parse_cfg_file(const char * cfg_file,struct cfg_line * cfg,int optional,int strict)567 int parse_cfg_file(const char *cfg_file, struct cfg_line *cfg, int optional, int strict)
568 {
569 return __parse_cfg_file(cfg_file, cfg, 0, optional, strict);
570 }
571
check_cfg_feature_int(const char * parameter,int value,const char * feature)572 int check_cfg_feature_int(const char *parameter, int value, const char *feature)
573 {
574 if (0 != value)
575 {
576 zbx_error("\"%s\" configuration parameter cannot be used: Zabbix %s was compiled without %s",
577 parameter, get_program_type_string(program_type), feature);
578 return FAIL;
579 }
580
581 return SUCCEED;
582 }
583
check_cfg_feature_str(const char * parameter,const char * value,const char * feature)584 int check_cfg_feature_str(const char *parameter, const char *value, const char *feature)
585 {
586 if (NULL != value)
587 {
588 zbx_error("\"%s\" configuration parameter cannot be used: Zabbix %s was compiled without %s",
589 parameter, get_program_type_string(program_type), feature);
590 return FAIL;
591 }
592
593 return SUCCEED;
594 }
595
596 /******************************************************************************
597 * *
598 * Function: zbx_set_data_destination_hosts *
599 * *
600 * Purpose: parse "ServerActive' parameter value and set destination servers *
601 * using a callback function *
602 * *
603 ******************************************************************************/
zbx_set_data_destination_hosts(char * active_hosts,add_serveractive_host_f cb)604 void zbx_set_data_destination_hosts(char *active_hosts, add_serveractive_host_f cb)
605 {
606 char *l = active_hosts, *r;
607
608 do
609 {
610 char *host = NULL;
611 unsigned short port;
612
613 if (NULL != (r = strchr(l, ',')))
614 *r = '\0';
615
616 if (SUCCEED != parse_serveractive_element(l, &host, &port, (unsigned short)ZBX_DEFAULT_SERVER_PORT))
617 {
618 zbx_error("error parsing the \"ServerActive\" parameter: address \"%s\" is invalid", l);
619 exit(EXIT_FAILURE);
620 }
621
622 if (SUCCEED != cb(host, port))
623 {
624 zbx_error("error parsing the \"ServerActive\" parameter: address \"%s\" specified more than"
625 " once", l);
626 zbx_free(host);
627 exit(EXIT_FAILURE);
628 }
629
630 zbx_free(host);
631
632 if (NULL != r)
633 {
634 *r = ',';
635 l = r + 1;
636 }
637 }
638 while (NULL != r);
639 }
640