/* * This file is part of the zlog Library. * * Copyright (C) 2011 by Hardy Simpson * * Licensed under the LGPL v2.1, see the file COPYING in base directory. */ #include "fmacros.h" #include #include #include #include #include #include #include #include #include #include "conf.h" #include "rule.h" #include "format.h" #include "level_list.h" #include "rotater.h" #include "zc_defs.h" /*******************************************************************************/ #define ZLOG_CONF_DEFAULT_FORMAT "default = \"%D %V [%p:%F:%L] %m%n\"" #define ZLOG_CONF_DEFAULT_RULE "*.* >stdout" #define ZLOG_CONF_DEFAULT_BUF_SIZE_MIN 1024 #define ZLOG_CONF_DEFAULT_BUF_SIZE_MAX (2 * 1024 * 1024) #define ZLOG_CONF_DEFAULT_FILE_PERMS 0600 #define ZLOG_CONF_DEFAULT_RELOAD_CONF_PERIOD 0 #define ZLOG_CONF_DEFAULT_FSYNC_PERIOD 0 #define ZLOG_CONF_BACKUP_ROTATE_LOCK_FILE "/tmp/zlog.lock" /*******************************************************************************/ void zlog_conf_profile(zlog_conf_t * a_conf, int flag) { int i; zlog_rule_t *a_rule; zlog_format_t *a_format; zc_assert(a_conf,); zc_profile(flag, "-conf[%p]-", a_conf); zc_profile(flag, "--global--"); zc_profile(flag, "---file[%s],mtime[%s]---", a_conf->file, a_conf->mtime); zc_profile(flag, "---in-memory conf[%s]---", a_conf->cfg_ptr); zc_profile(flag, "---strict init[%d]---", a_conf->strict_init); zc_profile(flag, "---buffer min[%ld]---", a_conf->buf_size_min); zc_profile(flag, "---buffer max[%ld]---", a_conf->buf_size_max); if (a_conf->default_format) { zc_profile(flag, "---default_format---"); zlog_format_profile(a_conf->default_format, flag); } zc_profile(flag, "---file perms[0%o]---", a_conf->file_perms); zc_profile(flag, "---reload conf period[%ld]---", a_conf->reload_conf_period); zc_profile(flag, "---fsync period[%ld]---", a_conf->fsync_period); zc_profile(flag, "---rotate lock file[%s]---", a_conf->rotate_lock_file); if (a_conf->rotater) zlog_rotater_profile(a_conf->rotater, flag); if (a_conf->levels) zlog_level_list_profile(a_conf->levels, flag); if (a_conf->formats) { zc_profile(flag, "--format list[%p]--", a_conf->formats); zc_arraylist_foreach(a_conf->formats, i, a_format) { zlog_format_profile(a_format, flag); } } if (a_conf->rules) { zc_profile(flag, "--rule_list[%p]--", a_conf->rules); zc_arraylist_foreach(a_conf->rules, i, a_rule) { zlog_rule_profile(a_rule, flag); } } return; } /*******************************************************************************/ void zlog_conf_del(zlog_conf_t * a_conf) { zc_assert(a_conf,); if (a_conf->rotater) zlog_rotater_del(a_conf->rotater); if (a_conf->levels) zlog_level_list_del(a_conf->levels); if (a_conf->default_format) zlog_format_del(a_conf->default_format); if (a_conf->formats) zc_arraylist_del(a_conf->formats); if (a_conf->rules) zc_arraylist_del(a_conf->rules); free(a_conf); zc_debug("zlog_conf_del[%p]"); return; } static int zlog_conf_build_without_file(zlog_conf_t * a_conf); static int zlog_conf_build_with_file(zlog_conf_t * a_conf); static int zlog_conf_build_with_in_memory(zlog_conf_t * a_conf); enum{ NO_CFG, FILE_CFG, IN_MEMORY_CFG }; zlog_conf_t *zlog_conf_new(const char *config) { int nwrite = 0; int cfg_source = 0; zlog_conf_t *a_conf = NULL; a_conf = calloc(1, sizeof(zlog_conf_t)); if (!a_conf) { zc_error("calloc fail, errno[%d]", errno); return NULL; } // Find content of pointer. If it starts with '[' then content are configurations. if (config && config[0] != '\0' && config[0] != '[') { nwrite = snprintf(a_conf->file, sizeof(a_conf->file), "%s", config); cfg_source = FILE_CFG; } else if (getenv("ZLOG_CONF_PATH") != NULL) { nwrite = snprintf(a_conf->file, sizeof(a_conf->file), "%s", getenv("ZLOG_CONF_PATH")); cfg_source = FILE_CFG; } else if (config[0]=='[') { memset(a_conf->file, 0x00, sizeof(a_conf->file)); nwrite = snprintf(a_conf->cfg_ptr, sizeof(a_conf->cfg_ptr), "%s", config); cfg_source = IN_MEMORY_CFG; if (nwrite < 0 || nwrite >= sizeof(a_conf->file)) { zc_error("not enough space for configurations, nwrite=[%d], errno[%d]", nwrite, errno); goto err; } } else { memset(a_conf->file, 0x00, sizeof(a_conf->file)); cfg_source = NO_CFG; } if (nwrite < 0 || nwrite >= sizeof(a_conf->file) && cfg_source == FILE_CFG) { zc_error("not enough space for path name, nwrite=[%d], errno[%d]", nwrite, errno); goto err; } /* set default configuration start */ a_conf->strict_init = 1; a_conf->buf_size_min = ZLOG_CONF_DEFAULT_BUF_SIZE_MIN; a_conf->buf_size_max = ZLOG_CONF_DEFAULT_BUF_SIZE_MAX; if (cfg_source == FILE_CFG) { /* configure file as default lock file */ strcpy(a_conf->rotate_lock_file, a_conf->file); } else { strcpy(a_conf->rotate_lock_file, ZLOG_CONF_BACKUP_ROTATE_LOCK_FILE); } strcpy(a_conf->default_format_line, ZLOG_CONF_DEFAULT_FORMAT); a_conf->file_perms = ZLOG_CONF_DEFAULT_FILE_PERMS; a_conf->reload_conf_period = ZLOG_CONF_DEFAULT_RELOAD_CONF_PERIOD; a_conf->fsync_period = ZLOG_CONF_DEFAULT_FSYNC_PERIOD; /* set default configuration end */ a_conf->levels = zlog_level_list_new(); if (!a_conf->levels) { zc_error("zlog_level_list_new fail"); goto err; } a_conf->formats = zc_arraylist_new((zc_arraylist_del_fn) zlog_format_del); if (!a_conf->formats) { zc_error("zc_arraylist_new fail"); goto err; } a_conf->rules = zc_arraylist_new((zc_arraylist_del_fn) zlog_rule_del); if (!a_conf->rules) { zc_error("init rule_list fail"); goto err; } if (cfg_source == FILE_CFG) { if (zlog_conf_build_with_file(a_conf)) { zc_error("zlog_conf_build_with_file fail"); goto err; } } else if (cfg_source == IN_MEMORY_CFG) { if(zlog_conf_build_with_in_memory(a_conf)){ zc_error("zlog_conf_build_with_in_memory fail"); goto err; } } else { if (zlog_conf_build_without_file(a_conf)) { zc_error("zlog_conf_build_without_file fail"); goto err; } } zlog_conf_profile(a_conf, ZC_DEBUG); return a_conf; err: zlog_conf_del(a_conf); return NULL; } /*******************************************************************************/ static int zlog_conf_build_without_file(zlog_conf_t * a_conf) { zlog_rule_t *default_rule; a_conf->default_format = zlog_format_new(a_conf->default_format_line, &(a_conf->time_cache_count)); if (!a_conf->default_format) { zc_error("zlog_format_new fail"); return -1; } a_conf->rotater = zlog_rotater_new(a_conf->rotate_lock_file); if (!a_conf->rotater) { zc_error("zlog_rotater_new fail"); return -1; } default_rule = zlog_rule_new( ZLOG_CONF_DEFAULT_RULE, a_conf->levels, a_conf->default_format, a_conf->formats, a_conf->file_perms, a_conf->fsync_period, &(a_conf->time_cache_count)); if (!default_rule) { zc_error("zlog_rule_new fail"); return -1; } /* add default rule */ if (zc_arraylist_add(a_conf->rules, default_rule)) { zlog_rule_del(default_rule); zc_error("zc_arraylist_add fail"); return -1; } return 0; } /*******************************************************************************/ static int zlog_conf_parse_line(zlog_conf_t * a_conf, char *line, int *section); static int zlog_conf_build_with_file(zlog_conf_t * a_conf) { int rc = 0; struct zlog_stat a_stat; struct tm local_time; FILE *fp = NULL; char line[MAXLEN_CFG_LINE + 1]; size_t line_len; char *pline = NULL; char *p = NULL; int line_no = 0; int i = 0; int in_quotation = 0; int section = 0; /* [global:1] [levels:2] [formats:3] [rules:4] */ if (lstat(a_conf->file, &a_stat)) { zc_error("lstat conf file[%s] fail, errno[%d]", a_conf->file, errno); return -1; } localtime_r(&(a_stat.st_mtime), &local_time); strftime(a_conf->mtime, sizeof(a_conf->mtime), "%F %T", &local_time); if ((fp = fopen(a_conf->file, "r")) == NULL) { zc_error("open configure file[%s] fail", a_conf->file); return -1; } /* Now process the file. */ pline = line; memset(&line, 0x00, sizeof(line)); while (fgets((char *)pline, sizeof(line) - (pline - line), fp) != NULL) { ++line_no; line_len = strlen(pline); if (0 == line_len) { continue; } if (pline[line_len - 1] == '\n') { pline[line_len - 1] = '\0'; } /* check for end-of-section, comments, strip off trailing * spaces and newline character. */ p = pline; while (*p && isspace((int)*p)) ++p; if (*p == '\0' || *p == '#') continue; for (i = 0; p[i] != '\0'; ++i) { pline[i] = p[i]; } pline[i] = '\0'; for (p = pline + strlen(pline) - 1; isspace((int)*p); --p) /*EMPTY*/; if (*p == '\\') { if ((p - line) > MAXLEN_CFG_LINE - 30) { /* Oops the buffer is full - what now? */ pline = line; } else { for (p--; isspace((int)*p); --p) /*EMPTY*/; p++; *p = 0; pline = p; continue; } } else pline = line; *++p = '\0'; /* clean the tail comments start from # and not in quotation */ in_quotation = 0; for (p = line; *p != '\0'; p++) { if (*p == '"') { in_quotation ^= 1; continue; } if (*p == '#' && !in_quotation) { *p = '\0'; break; } } /* we now have the complete line, * and are positioned at the first non-whitespace * character. So let's process it */ rc = zlog_conf_parse_line(a_conf, line, §ion); if (rc < 0) { zc_error("parse configure file[%s]line_no[%ld] fail", a_conf->file, line_no); zc_error("line[%s]", line); goto exit; } else if (rc > 0) { zc_warn("parse configure file[%s]line_no[%ld] fail", a_conf->file, line_no); zc_warn("line[%s]", line); zc_warn("as strict init is set to false, ignore and go on"); rc = 0; continue; } } exit: fclose(fp); return rc; } /**********************************************************************/ static int zlog_conf_build_with_in_memory(zlog_conf_t * a_conf) { int rc = 0; char line[MAXLEN_CFG_LINE + 1]; char *pline = NULL; int section = 0; pline = line; memset(&line, 0x00, sizeof(line)); pline = strtok((char *)a_conf->cfg_ptr, "\n"); while (pline != NULL) { rc = zlog_conf_parse_line(a_conf, pline, §ion); if (rc < 0) { zc_error("parse in-memory configurations[%s] line [%s] fail", a_conf->cfg_ptr, pline); break; } else if (rc > 0) { zc_error("parse in-memory configurations[%s] line [%s] fail", a_conf->cfg_ptr, pline); zc_warn("as strict init is set to false, ignore and go on"); rc = 0; continue; } pline = strtok(NULL, "\n"); } return rc; } /* section [global:1] [levels:2] [formats:3] [rules:4] */ static int zlog_conf_parse_line(zlog_conf_t * a_conf, char *line, int *section) { int nscan; int nread; char name[MAXLEN_CFG_LINE + 1]; char word_1[MAXLEN_CFG_LINE + 1]; char word_2[MAXLEN_CFG_LINE + 1]; char word_3[MAXLEN_CFG_LINE + 1]; char value[MAXLEN_CFG_LINE + 1]; zlog_format_t *a_format = NULL; zlog_rule_t *a_rule = NULL; if (strlen(line) > MAXLEN_CFG_LINE) { zc_error ("line_len[%ld] > MAXLEN_CFG_LINE[%ld], may cause overflow", strlen(line), MAXLEN_CFG_LINE); return -1; } /* get and set outer section flag, so it is a closure? haha */ if (line[0] == '[') { int last_section = *section; nscan = sscanf(line, "[ %[^] \t]", name); if (STRCMP(name, ==, "global")) { *section = 1; } else if (STRCMP(name, ==, "levels")) { *section = 2; } else if (STRCMP(name, ==, "formats")) { *section = 3; } else if (STRCMP(name, ==, "rules")) { *section = 4; } else { zc_error("wrong section name[%s]", name); return -1; } /* check the sequence of section, must increase */ if (last_section >= *section) { zc_error("wrong sequence of section, must follow global->levels->formats->rules"); return -1; } if (*section == 4) { if (a_conf->reload_conf_period != 0 && a_conf->fsync_period >= a_conf->reload_conf_period) { /* as all rule will be rebuilt when conf is reload, * so fsync_period > reload_conf_period will never * cause rule to fsync it's file. * fsync_period will be meaningless and down speed, * so make it zero. */ zc_warn("fsync_period[%ld] >= reload_conf_period[%ld]," "set fsync_period to zero"); a_conf->fsync_period = 0; } /* now build rotater and default_format * from the unchanging global setting, * for zlog_rule_new() */ a_conf->rotater = zlog_rotater_new(a_conf->rotate_lock_file); if (!a_conf->rotater) { zc_error("zlog_rotater_new fail"); return -1; } a_conf->default_format = zlog_format_new(a_conf->default_format_line, &(a_conf->time_cache_count)); if (!a_conf->default_format) { zc_error("zlog_format_new fail"); return -1; } } return 0; } /* process detail */ switch (*section) { case 1: memset(name, 0x00, sizeof(name)); memset(value, 0x00, sizeof(value)); nscan = sscanf(line, " %[^=]= %s ", name, value); if (nscan != 2) { zc_error("sscanf [%s] fail, name or value is null", line); return -1; } memset(word_1, 0x00, sizeof(word_1)); memset(word_2, 0x00, sizeof(word_2)); memset(word_3, 0x00, sizeof(word_3)); nread = 0; nscan = sscanf(name, "%s%n%s%s", word_1, &nread, word_2, word_3); if (STRCMP(word_1, ==, "strict") && STRCMP(word_2, ==, "init")) { /* if environment variable ZLOG_STRICT_INIT is set * then always make it strict */ if (STRICMP(value, ==, "false") && !getenv("ZLOG_STRICT_INIT")) { a_conf->strict_init = 0; } else { a_conf->strict_init = 1; } } else if (STRCMP(word_1, ==, "buffer") && STRCMP(word_2, ==, "min")) { a_conf->buf_size_min = zc_parse_byte_size(value); } else if (STRCMP(word_1, ==, "buffer") && STRCMP(word_2, ==, "max")) { a_conf->buf_size_max = zc_parse_byte_size(value); } else if (STRCMP(word_1, ==, "file") && STRCMP(word_2, ==, "perms")) { sscanf(value, "%o", &(a_conf->file_perms)); } else if (STRCMP(word_1, ==, "rotate") && STRCMP(word_2, ==, "lock") && STRCMP(word_3, ==, "file")) { /* may overwrite the inner default value, or last value */ if (STRCMP(value, ==, "self")) { strcpy(a_conf->rotate_lock_file, a_conf->file); } else { strcpy(a_conf->rotate_lock_file, value); } } else if (STRCMP(word_1, ==, "default") && STRCMP(word_2, ==, "format")) { /* so the input now is [format = "xxyy"], fit format's style */ strcpy(a_conf->default_format_line, line + nread); } else if (STRCMP(word_1, ==, "reload") && STRCMP(word_2, ==, "conf") && STRCMP(word_3, ==, "period")) { a_conf->reload_conf_period = zc_parse_byte_size(value); } else if (STRCMP(word_1, ==, "fsync") && STRCMP(word_2, ==, "period")) { a_conf->fsync_period = zc_parse_byte_size(value); } else { zc_error("name[%s] is not any one of global options", name); if (a_conf->strict_init) return -1; } break; case 2: if (zlog_level_list_set(a_conf->levels, line)) { zc_error("zlog_level_list_set fail"); if (a_conf->strict_init) return -1; } break; case 3: a_format = zlog_format_new(line, &(a_conf->time_cache_count)); if (!a_format) { zc_error("zlog_format_new fail [%s]", line); if (a_conf->strict_init) return -1; else break; } if (zc_arraylist_add(a_conf->formats, a_format)) { zlog_format_del(a_format); zc_error("zc_arraylist_add fail"); return -1; } break; case 4: a_rule = zlog_rule_new(line, a_conf->levels, a_conf->default_format, a_conf->formats, a_conf->file_perms, a_conf->fsync_period, &(a_conf->time_cache_count)); if (!a_rule) { zc_error("zlog_rule_new fail [%s]", line); if (a_conf->strict_init) return -1; else break; } if (zc_arraylist_add(a_conf->rules, a_rule)) { zlog_rule_del(a_rule); zc_error("zc_arraylist_add fail"); return -1; } break; default: zc_error("not in any section"); return -1; } return 0; } /*******************************************************************************/