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