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, §ion);
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