1 /*
2  * This file is part of the zlog Library.
3  *
4  * Copyright (C) 2011 by Hardy Simpson <HardySimpson1984@gmail.com>
5  *
6  * Licensed under the LGPL v2.1, see the file COPYING in base directory.
7  */
8 
9 #include "fmacros.h"
10 #include <ctype.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <time.h>
19 
20 #include "conf.h"
21 #include "rule.h"
22 #include "format.h"
23 #include "level_list.h"
24 #include "rotater.h"
25 #include "zc_defs.h"
26 
27 /*******************************************************************************/
28 #define ZLOG_CONF_DEFAULT_FORMAT "default = \"%D %V [%p:%F:%L] %m%n\""
29 #define ZLOG_CONF_DEFAULT_RULE "*.*        >stdout"
30 #define ZLOG_CONF_DEFAULT_BUF_SIZE_MIN 1024
31 #define ZLOG_CONF_DEFAULT_BUF_SIZE_MAX (2 * 1024 * 1024)
32 #define ZLOG_CONF_DEFAULT_FILE_PERMS 0600
33 #define ZLOG_CONF_DEFAULT_RELOAD_CONF_PERIOD 0
34 #define ZLOG_CONF_DEFAULT_FSYNC_PERIOD 0
35 #define ZLOG_CONF_BACKUP_ROTATE_LOCK_FILE "/tmp/zlog.lock"
36 /*******************************************************************************/
37 
zlog_conf_profile(zlog_conf_t * a_conf,int flag)38 void zlog_conf_profile(zlog_conf_t * a_conf, int flag)
39 {
40 	int i;
41 	zlog_rule_t *a_rule;
42 	zlog_format_t *a_format;
43 
44 	zc_assert(a_conf,);
45 	zc_profile(flag, "-conf[%p]-", a_conf);
46 	zc_profile(flag, "--global--");
47 	zc_profile(flag, "---file[%s],mtime[%s]---", a_conf->file, a_conf->mtime);
48 	zc_profile(flag, "---strict init[%d]---", a_conf->strict_init);
49 	zc_profile(flag, "---buffer min[%ld]---", a_conf->buf_size_min);
50 	zc_profile(flag, "---buffer max[%ld]---", a_conf->buf_size_max);
51 	if (a_conf->default_format) {
52 		zc_profile(flag, "---default_format---");
53 		zlog_format_profile(a_conf->default_format, flag);
54 	}
55 	zc_profile(flag, "---file perms[0%o]---", a_conf->file_perms);
56 	zc_profile(flag, "---reload conf period[%ld]---", a_conf->reload_conf_period);
57 	zc_profile(flag, "---fsync period[%ld]---", a_conf->fsync_period);
58 
59 	zc_profile(flag, "---rotate lock file[%s]---", a_conf->rotate_lock_file);
60 	if (a_conf->rotater) zlog_rotater_profile(a_conf->rotater, flag);
61 
62 	if (a_conf->levels) zlog_level_list_profile(a_conf->levels, flag);
63 
64 	if (a_conf->formats) {
65 		zc_profile(flag, "--format list[%p]--", a_conf->formats);
66 		zc_arraylist_foreach(a_conf->formats, i, a_format) {
67 			zlog_format_profile(a_format, flag);
68 		}
69 	}
70 
71 	if (a_conf->rules) {
72 		zc_profile(flag, "--rule_list[%p]--", a_conf->rules);
73 		zc_arraylist_foreach(a_conf->rules, i, a_rule) {
74 			zlog_rule_profile(a_rule, flag);
75 		}
76 	}
77 
78 	return;
79 }
80 /*******************************************************************************/
zlog_conf_del(zlog_conf_t * a_conf)81 void zlog_conf_del(zlog_conf_t * a_conf)
82 {
83 	zc_assert(a_conf,);
84 	if (a_conf->rotater) zlog_rotater_del(a_conf->rotater);
85 	if (a_conf->levels) zlog_level_list_del(a_conf->levels);
86 	if (a_conf->default_format) zlog_format_del(a_conf->default_format);
87 	if (a_conf->formats) zc_arraylist_del(a_conf->formats);
88 	if (a_conf->rules) zc_arraylist_del(a_conf->rules);
89 	free(a_conf);
90 	zc_debug("zlog_conf_del[%p]");
91 	return;
92 }
93 
94 static int zlog_conf_build_without_file(zlog_conf_t * a_conf);
95 static int zlog_conf_build_with_file(zlog_conf_t * a_conf);
96 
zlog_conf_new(const char * confpath)97 zlog_conf_t *zlog_conf_new(const char *confpath)
98 {
99 	int nwrite = 0;
100 	int has_conf_file = 0;
101 	zlog_conf_t *a_conf = NULL;
102 
103 	a_conf = calloc(1, sizeof(zlog_conf_t));
104 	if (!a_conf) {
105 		zc_error("calloc fail, errno[%d]", errno);
106 		return NULL;
107 	}
108 
109 	if (confpath && confpath[0] != '\0') {
110 		nwrite = snprintf(a_conf->file, sizeof(a_conf->file), "%s", confpath);
111 		has_conf_file = 1;
112 	} else if (getenv("ZLOG_CONF_PATH") != NULL) {
113 		nwrite = snprintf(a_conf->file, sizeof(a_conf->file), "%s", getenv("ZLOG_CONF_PATH"));
114 		has_conf_file = 1;
115 	} else {
116 		memset(a_conf->file, 0x00, sizeof(a_conf->file));
117 		has_conf_file = 0;
118 	}
119 	if (nwrite < 0 || nwrite >= sizeof(a_conf->file)) {
120 		zc_error("not enough space for path name, nwrite=[%d], errno[%d]", nwrite, errno);
121 		goto err;
122 	}
123 
124 	/* set default configuration start */
125 	a_conf->strict_init = 1;
126 	a_conf->buf_size_min = ZLOG_CONF_DEFAULT_BUF_SIZE_MIN;
127 	a_conf->buf_size_max = ZLOG_CONF_DEFAULT_BUF_SIZE_MAX;
128 	if (has_conf_file) {
129 		/* configure file as default lock file */
130 		strcpy(a_conf->rotate_lock_file, a_conf->file);
131 	} else {
132 		strcpy(a_conf->rotate_lock_file, ZLOG_CONF_BACKUP_ROTATE_LOCK_FILE);
133 	}
134 	strcpy(a_conf->default_format_line, ZLOG_CONF_DEFAULT_FORMAT);
135 	a_conf->file_perms = ZLOG_CONF_DEFAULT_FILE_PERMS;
136 	a_conf->reload_conf_period = ZLOG_CONF_DEFAULT_RELOAD_CONF_PERIOD;
137 	a_conf->fsync_period = ZLOG_CONF_DEFAULT_FSYNC_PERIOD;
138 	/* set default configuration end */
139 
140 	a_conf->levels = zlog_level_list_new();
141 	if (!a_conf->levels) {
142 		zc_error("zlog_level_list_new fail");
143 		goto err;
144 	}
145 
146 	a_conf->formats = zc_arraylist_new((zc_arraylist_del_fn) zlog_format_del);
147 	if (!a_conf->formats) {
148 		zc_error("zc_arraylist_new fail");
149 		goto err;
150 	}
151 
152 	a_conf->rules = zc_arraylist_new((zc_arraylist_del_fn) zlog_rule_del);
153 	if (!a_conf->rules) {
154 		zc_error("init rule_list fail");
155 		goto err;
156 	}
157 
158 	if (has_conf_file) {
159 		if (zlog_conf_build_with_file(a_conf)) {
160 			zc_error("zlog_conf_build_with_file fail");
161 			goto err;
162 		}
163 	} else {
164 		if (zlog_conf_build_without_file(a_conf)) {
165 			zc_error("zlog_conf_build_without_file fail");
166 			goto err;
167 		}
168 	}
169 
170 	zlog_conf_profile(a_conf, ZC_DEBUG);
171 	return a_conf;
172 err:
173 	zlog_conf_del(a_conf);
174 	return NULL;
175 }
176 /*******************************************************************************/
zlog_conf_build_without_file(zlog_conf_t * a_conf)177 static int zlog_conf_build_without_file(zlog_conf_t * a_conf)
178 {
179 	zlog_rule_t *default_rule;
180 
181 	a_conf->default_format = zlog_format_new(a_conf->default_format_line, &(a_conf->time_cache_count));
182 	if (!a_conf->default_format) {
183 		zc_error("zlog_format_new fail");
184 		return -1;
185 	}
186 
187 	a_conf->rotater = zlog_rotater_new(a_conf->rotate_lock_file);
188 	if (!a_conf->rotater) {
189 		zc_error("zlog_rotater_new fail");
190 		return -1;
191 	}
192 
193 	default_rule = zlog_rule_new(
194 			ZLOG_CONF_DEFAULT_RULE,
195 			a_conf->levels,
196 			a_conf->default_format,
197 			a_conf->formats,
198 			a_conf->file_perms,
199 			a_conf->fsync_period,
200 			&(a_conf->time_cache_count));
201 	if (!default_rule) {
202 		zc_error("zlog_rule_new fail");
203 		return -1;
204 	}
205 
206 	/* add default rule */
207 	if (zc_arraylist_add(a_conf->rules, default_rule)) {
208 		zlog_rule_del(default_rule);
209 		zc_error("zc_arraylist_add fail");
210 		return -1;
211 	}
212 
213 	return 0;
214 }
215 /*******************************************************************************/
216 static int zlog_conf_parse_line(zlog_conf_t * a_conf, char *line, int *section);
217 
zlog_conf_build_with_file(zlog_conf_t * a_conf)218 static int zlog_conf_build_with_file(zlog_conf_t * a_conf)
219 {
220 	int rc = 0;
221 	struct zlog_stat a_stat;
222 	struct tm local_time;
223 	FILE *fp = NULL;
224 
225 	char line[MAXLEN_CFG_LINE + 1];
226 	size_t line_len;
227 	char *pline = NULL;
228 	char *p = NULL;
229 	int line_no = 0;
230 	int i = 0;
231 	int in_quotation = 0;
232 
233 	int section = 0;
234 	/* [global:1] [levels:2] [formats:3] [rules:4] */
235 
236 	if (lstat(a_conf->file, &a_stat)) {
237 		zc_error("lstat conf file[%s] fail, errno[%d]", a_conf->file,
238 			 errno);
239 		return -1;
240 	}
241 	localtime_r(&(a_stat.st_mtime), &local_time);
242 	strftime(a_conf->mtime, sizeof(a_conf->mtime), "%F %T", &local_time);
243 
244 	if ((fp = fopen(a_conf->file, "r")) == NULL) {
245 		zc_error("open configure file[%s] fail", a_conf->file);
246 		return -1;
247 	}
248 
249 	/* Now process the file.
250 	 */
251 	pline = line;
252 	memset(&line, 0x00, sizeof(line));
253 	while (fgets((char *)pline, sizeof(line) - (pline - line), fp) != NULL) {
254 		++line_no;
255 		line_len = strlen(pline);
256 		if (pline[line_len - 1] == '\n') {
257 			pline[line_len - 1] = '\0';
258 		}
259 
260 		/* check for end-of-section, comments, strip off trailing
261 		 * spaces and newline character.
262 		 */
263 		p = pline;
264 		while (*p && isspace((int)*p))
265 			++p;
266 		if (*p == '\0' || *p == '#')
267 			continue;
268 
269 		for (i = 0; p[i] != '\0'; ++i) {
270 			pline[i] = p[i];
271 		}
272 		pline[i] = '\0';
273 
274 		for (p = pline + strlen(pline) - 1; isspace((int)*p); --p)
275 			/*EMPTY*/;
276 
277 		if (*p == '\\') {
278 			if ((p - line) > MAXLEN_CFG_LINE - 30) {
279 				/* Oops the buffer is full - what now? */
280 				pline = line;
281 			} else {
282 				for (p--; isspace((int)*p); --p)
283 					/*EMPTY*/;
284 				p++;
285 				*p = 0;
286 				pline = p;
287 				continue;
288 			}
289 		} else
290 			pline = line;
291 
292 		*++p = '\0';
293 
294 		/* clean the tail comments start from # and not in quotation */
295 		in_quotation = 0;
296 		for (p = line; *p != '\0'; p++) {
297 			if (*p == '"') {
298 				in_quotation ^= 1;
299 				continue;
300 			}
301 
302 			if (*p == '#' && !in_quotation) {
303 				*p = '\0';
304 				break;
305 			}
306 		}
307 
308 		/* we now have the complete line,
309 		 * and are positioned at the first non-whitespace
310 		 * character. So let's process it
311 		 */
312 		rc = zlog_conf_parse_line(a_conf, line, &section);
313 		if (rc < 0) {
314 			zc_error("parse configure file[%s]line_no[%ld] fail", a_conf->file, line_no);
315 			zc_error("line[%s]", line);
316 			goto exit;
317 		} else if (rc > 0) {
318 			zc_warn("parse configure file[%s]line_no[%ld] fail", a_conf->file, line_no);
319 			zc_warn("line[%s]", line);
320 			zc_warn("as strict init is set to false, ignore and go on");
321 			rc = 0;
322 			continue;
323 		}
324 	}
325 
326 exit:
327 	fclose(fp);
328 	return rc;
329 }
330 
331 /* section [global:1] [levels:2] [formats:3] [rules:4] */
zlog_conf_parse_line(zlog_conf_t * a_conf,char * line,int * section)332 static int zlog_conf_parse_line(zlog_conf_t * a_conf, char *line, int *section)
333 {
334 	int nscan;
335 	int nread;
336 	char name[MAXLEN_CFG_LINE + 1];
337 	char word_1[MAXLEN_CFG_LINE + 1];
338 	char word_2[MAXLEN_CFG_LINE + 1];
339 	char word_3[MAXLEN_CFG_LINE + 1];
340 	char value[MAXLEN_CFG_LINE + 1];
341 	zlog_format_t *a_format = NULL;
342 	zlog_rule_t *a_rule = NULL;
343 
344 	if (strlen(line) > MAXLEN_CFG_LINE) {
345 		zc_error ("line_len[%ld] > MAXLEN_CFG_LINE[%ld], may cause overflow",
346 		     strlen(line), MAXLEN_CFG_LINE);
347 		return -1;
348 	}
349 
350 	/* get and set outer section flag, so it is a closure? haha */
351 	if (line[0] == '[') {
352 		int last_section = *section;
353 		nscan = sscanf(line, "[ %[^] \t]", name);
354 		if (STRCMP(name, ==, "global")) {
355 			*section = 1;
356 		} else if (STRCMP(name, ==, "levels")) {
357 			*section = 2;
358 		} else if (STRCMP(name, ==, "formats")) {
359 			*section = 3;
360 		} else if (STRCMP(name, ==, "rules")) {
361 			*section = 4;
362 		} else {
363 			zc_error("wrong section name[%s]", name);
364 			return -1;
365 		}
366 		/* check the sequence of section, must increase */
367 		if (last_section >= *section) {
368 			zc_error("wrong sequence of section, must follow global->levels->formats->rules");
369 			return -1;
370 		}
371 
372 		if (*section == 4) {
373 			if (a_conf->reload_conf_period != 0
374 				&& a_conf->fsync_period >= a_conf->reload_conf_period) {
375 				/* as all rule will be rebuilt when conf is reload,
376 				 * so fsync_period > reload_conf_period will never
377 				 * cause rule to fsync it's file.
378 				 * fsync_period will be meaningless and down speed,
379 				 * so make it zero.
380 				 */
381 				zc_warn("fsync_period[%ld] >= reload_conf_period[%ld],"
382 					"set fsync_period to zero");
383 				a_conf->fsync_period = 0;
384 			}
385 
386 			/* now build rotater and default_format
387 			 * from the unchanging global setting,
388 			 * for zlog_rule_new() */
389 			a_conf->rotater = zlog_rotater_new(a_conf->rotate_lock_file);
390 			if (!a_conf->rotater) {
391 				zc_error("zlog_rotater_new fail");
392 				return -1;
393 			}
394 
395 			a_conf->default_format = zlog_format_new(a_conf->default_format_line,
396 							&(a_conf->time_cache_count));
397 			if (!a_conf->default_format) {
398 				zc_error("zlog_format_new fail");
399 				return -1;
400 			}
401 		}
402 		return 0;
403 	}
404 
405 	/* process detail */
406 	switch (*section) {
407 	case 1:
408 		memset(name, 0x00, sizeof(name));
409 		memset(value, 0x00, sizeof(value));
410 		nscan = sscanf(line, " %[^=]= %s ", name, value);
411 		if (nscan != 2) {
412 			zc_error("sscanf [%s] fail, name or value is null", line);
413 			return -1;
414 		}
415 
416 		memset(word_1, 0x00, sizeof(word_1));
417 		memset(word_2, 0x00, sizeof(word_2));
418 		memset(word_3, 0x00, sizeof(word_3));
419 		nread = 0;
420 		nscan = sscanf(name, "%s%n%s%s", word_1, &nread, word_2, word_3);
421 
422 		if (STRCMP(word_1, ==, "strict") && STRCMP(word_2, ==, "init")) {
423 			/* if environment variable ZLOG_STRICT_INIT is set
424 			 * then always make it strict
425 			 */
426 			if (STRICMP(value, ==, "false") && !getenv("ZLOG_STRICT_INIT")) {
427 				a_conf->strict_init = 0;
428 			} else {
429 				a_conf->strict_init = 1;
430 			}
431 		} else if (STRCMP(word_1, ==, "buffer") && STRCMP(word_2, ==, "min")) {
432 			a_conf->buf_size_min = zc_parse_byte_size(value);
433 		} else if (STRCMP(word_1, ==, "buffer") && STRCMP(word_2, ==, "max")) {
434 			a_conf->buf_size_max = zc_parse_byte_size(value);
435 		} else if (STRCMP(word_1, ==, "file") && STRCMP(word_2, ==, "perms")) {
436 			sscanf(value, "%o", &(a_conf->file_perms));
437 		} else if (STRCMP(word_1, ==, "rotate") &&
438 				STRCMP(word_2, ==, "lock") && STRCMP(word_3, ==, "file")) {
439 			/* may overwrite the inner default value, or last value */
440 			if (STRCMP(value, ==, "self")) {
441 				strcpy(a_conf->rotate_lock_file, a_conf->file);
442 			} else {
443 				strcpy(a_conf->rotate_lock_file, value);
444 			}
445 		} else if (STRCMP(word_1, ==, "default") && STRCMP(word_2, ==, "format")) {
446 			/* so the input now is [format = "xxyy"], fit format's style */
447 			strcpy(a_conf->default_format_line, line + nread);
448 		} else if (STRCMP(word_1, ==, "reload") &&
449 				STRCMP(word_2, ==, "conf") && STRCMP(word_3, ==, "period")) {
450 			a_conf->reload_conf_period = zc_parse_byte_size(value);
451 		} else if (STRCMP(word_1, ==, "fsync") && STRCMP(word_2, ==, "period")) {
452 			a_conf->fsync_period = zc_parse_byte_size(value);
453 		} else {
454 			zc_error("name[%s] is not any one of global options", name);
455 			if (a_conf->strict_init) return -1;
456 		}
457 		break;
458 	case 2:
459 		if (zlog_level_list_set(a_conf->levels, line)) {
460 			zc_error("zlog_level_list_set fail");
461 			if (a_conf->strict_init) return -1;
462 		}
463 		break;
464 	case 3:
465 		a_format = zlog_format_new(line, &(a_conf->time_cache_count));
466 		if (!a_format) {
467 			zc_error("zlog_format_new fail [%s]", line);
468 			if (a_conf->strict_init) return -1;
469 			else break;
470 		}
471 		if (zc_arraylist_add(a_conf->formats, a_format)) {
472 			zlog_format_del(a_format);
473 			zc_error("zc_arraylist_add fail");
474 			return -1;
475 		}
476 		break;
477 	case 4:
478 		a_rule = zlog_rule_new(line,
479 			a_conf->levels,
480 			a_conf->default_format,
481 			a_conf->formats,
482 			a_conf->file_perms,
483 			a_conf->fsync_period,
484 			&(a_conf->time_cache_count));
485 
486 		if (!a_rule) {
487 			zc_error("zlog_rule_new fail [%s]", line);
488 			if (a_conf->strict_init) return -1;
489 			else break;
490 		}
491 		if (zc_arraylist_add(a_conf->rules, a_rule)) {
492 			zlog_rule_del(a_rule);
493 			zc_error("zc_arraylist_add fail");
494 			return -1;
495 		}
496 		break;
497 	default:
498 		zc_error("not in any section");
499 		return -1;
500 		break;
501 	}
502 
503 	return 0;
504 }
505 /*******************************************************************************/
506