Files
ldb/common/config.c

452 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h> // strcasecmp
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
static xmlNodePtr find_child(xmlNodePtr parent, const char *name)
{
if (!parent || !name) return NULL;
for (xmlNodePtr n = parent->children; n; n = n->next)
{
if (n->type == XML_ELEMENT_NODE &&
xmlStrcmp(n->name, BAD_CAST name) == 0)
{
return n;
}
}
return NULL;
}
static void set_default_config(AppConfig *cfg)
{
memset(cfg, 0, sizeof(*cfg));
strncpy(cfg->ip, "127.0.0.1", sizeof(cfg->ip) - 1);
cfg->ip[sizeof(cfg->ip) - 1] = '\0';
strncpy(cfg->master_ip, "127.0.0.1", sizeof(cfg->master_ip) - 1);
cfg->master_ip[sizeof(cfg->master_ip) - 1] = '\0';
strncpy(cfg->persist_dir, "data.default", sizeof(cfg->persist_dir) - 1);
cfg->persist_dir[sizeof(cfg->persist_dir) - 1] = '\0';
strncpy(cfg->oplog_file, "kvs_oplog.default.db", sizeof(cfg->oplog_file) - 1);
cfg->oplog_file[sizeof(cfg->oplog_file) - 1] = '\0';
strncpy(cfg->array_file, "kvs_array.default.db", sizeof(cfg->array_file) - 1);
cfg->array_file[sizeof(cfg->array_file) - 1] = '\0';
strncpy(cfg->rbtree_file, "kvs_rbtree.default.db", sizeof(cfg->rbtree_file) - 1);
cfg->rbtree_file[sizeof(cfg->rbtree_file) - 1] = '\0';
strncpy(cfg->hash_file, "kvs_hash.default.db", sizeof(cfg->hash_file) - 1);
cfg->hash_file[sizeof(cfg->hash_file) - 1] = '\0';
cfg->port = 8888;
cfg->log_level = LOG_LEVEL_INFO;
cfg->mode = MODE_MASTER;
cfg->master_port = 8888;
cfg->persistence = PERSIST_NONE;
cfg->allocator = ALLOC_JEMALLOC;
cfg->leak_mode = MEMLEAK_DETECT_OFF;
cfg->replica_mode = REPLICA_DISABLE;
}
/* ---- 字符串转枚举 ---- */
static void parse_log_level(const char *s, LogLevel *out)
{
if (!s || !out) return;
if (!strcasecmp(s, "DEBUG")) *out = LOG_LEVEL_DEBUG;
else if (!strcasecmp(s, "INFO")) *out = LOG_LEVEL_INFO;
else if (!strcasecmp(s, "ERROR")) *out = LOG_LEVEL_ERROR;
/* 不认识就保持默认 */
}
static void parse_server_mode(const char *s, ServerMode *out)
{
if (!s || !out) return;
if (!strcasecmp(s, "master")) *out = MODE_MASTER;
else if (!strcasecmp(s, "slave")) *out = MODE_SLAVE;
}
static void parse_persistence(const char *s, PersistenceType *out)
{
if (!s || !out) return;
if (!strcasecmp(s, "incremental")) *out = PERSIST_INCREMENTAL;
else if (!strcasecmp(s, "none")) *out = PERSIST_NONE;
}
static void parse_allocator(const char *s, AllocatorType *out)
{
if (!s || !out) return;
if (!strcasecmp(s, "jemalloc")) *out = ALLOC_JEMALLOC;
else if (!strcasecmp(s, "malloc")) *out = ALLOC_MALLOC;
else if (!strcasecmp(s, "mypool")) *out = ALLOC_MYPOOL;
else *out = ALLOC_OTHER;
}
static void parse_leakage(const char *s, MemLeakDetectMode *out)
{
if (!s || !out) return;
if (!strcasecmp(s, "enable")) *out = MEMLEAK_DETECT_ON;
else if (!strcasecmp(s, "disable")) *out = MEMLEAK_DETECT_OFF;
else *out = MEMLEAK_DETECT_OFF;
}
static void parse_replica(const char *s, ReplicaMode *out)
{
if (!s || !out) return;
if (!strcasecmp(s, "enable")) *out = REPLICA_ENABLE;
else if (!strcasecmp(s, "disable")) *out = REPLICA_DISABLE;
else *out = REPLICA_DISABLE;
}
static int read_file_mmap(const char *filename, void **out_addr, size_t *out_len, int *out_fd) {
if (!filename || !out_addr || !out_len || !out_fd) return -1;
*out_addr = NULL;
*out_len = 0;
*out_fd = -1;
int fd = open(filename, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "config_load: open(%s) failed: %s\n", filename, strerror(errno));
return -1;
}
struct stat st;
if (fstat(fd, &st) != 0) {
fprintf(stderr, "config_load: fstat(%s) failed: %s\n", filename, strerror(errno));
close(fd);
return -1;
}
if (!S_ISREG(st.st_mode)) {
fprintf(stderr, "config_load: %s is not a regular file\n", filename);
close(fd);
return -1;
}
if (st.st_size <= 0) {
fprintf(stderr, "config_load: %s is empty\n", filename);
close(fd);
return -1;
}
size_t len = (size_t)st.st_size;
void *addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) {
fprintf(stderr, "config_load: mmap(%s) failed: %s\n", filename, strerror(errno));
close(fd);
return -1;
}
*out_addr = addr;
*out_len = len;
*out_fd = fd;
return 0;
}
/* ---- 对外的枚举转字符串工具 ---- */
const char *log_level_to_string(LogLevel lvl)
{
switch (lvl) {
case LOG_LEVEL_DEBUG: return "DEBUG";
case LOG_LEVEL_INFO: return "INFO";
case LOG_LEVEL_ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
const char *server_mode_to_string(ServerMode mode)
{
switch (mode) {
case MODE_MASTER: return "master";
case MODE_SLAVE: return "slave";
default: return "unknown";
}
}
const char *persistence_to_string(PersistenceType p)
{
switch (p) {
case PERSIST_INCREMENTAL: return "incremental";
case PERSIST_NONE: return "none";
default: return "unknown";
}
}
const char *allocator_to_string(AllocatorType a)
{
switch (a) {
case ALLOC_JEMALLOC: return "jemalloc";
case ALLOC_MALLOC: return "malloc";
case ALLOC_MYPOOL: return "mypool";
case ALLOC_OTHER: return "other";
default: return "unknown";
}
}
const char *leakage_to_string(MemLeakDetectMode a)
{
switch (a) {
case MEMLEAK_DETECT_ON: return "enable";
case MEMLEAK_DETECT_OFF: return "disable";
default: return "unknown";
}
}
const char *replica_to_string(ReplicaMode a)
{
switch (a) {
case MEMLEAK_DETECT_ON: return "enable";
case MEMLEAK_DETECT_OFF: return "disable";
default: return "unknown";
}
}
/* ---- 主函数:从 XML 加载配置 ---- */
/* server 部分 */
void server_load(xmlNodePtr *root, AppConfig *out_cfg){
xmlNodePtr server = find_child(*root, "server");
if (server) {
/* ip */
xmlNodePtr ip_node = find_child(server, "ip");
if (ip_node) {
xmlChar *txt = xmlNodeGetContent(ip_node);
if (txt) {
strncpy(out_cfg->ip, (char *)txt, sizeof(out_cfg->ip) - 1);
out_cfg->ip[sizeof(out_cfg->ip) - 1] = '\0';
xmlFree(txt);
}
}
/* port */
xmlNodePtr port_node = find_child(server, "port");
if (port_node) {
xmlChar *txt = xmlNodeGetContent(port_node);
if (txt) {
out_cfg->port = atoi((char *)txt);
xmlFree(txt);
}
}
/* mode: master / slave */
xmlNodePtr mode_node = find_child(server, "mode");
if (mode_node) {
xmlChar *txt = xmlNodeGetContent(mode_node);
if (txt) {
parse_server_mode((char *)txt, &out_cfg->mode);
xmlFree(txt);
}
}
xmlNodePtr replica_node = find_child(server, "replica");
if (replica_node) {
xmlChar *txt = xmlNodeGetContent(replica_node);
if (txt) {
parse_replica((char *)txt, &out_cfg->replica_mode);
xmlFree(txt);
}
}
/* master (always read if present) */
xmlNodePtr master = find_child(server, "master");
if (master) {
xmlNodePtr mip_node = find_child(master, "ip");
if (mip_node) {
xmlChar *txt = xmlNodeGetContent(mip_node);
if (txt) {
strncpy(out_cfg->master_ip, (char *)txt, sizeof(out_cfg->master_ip) - 1);
out_cfg->master_ip[sizeof(out_cfg->master_ip) - 1] = '\0';
xmlFree(txt);
}
}
xmlNodePtr mport_node = find_child(master, "port");
if (mport_node) {
xmlChar *txt = xmlNodeGetContent(mport_node);
if (txt) {
out_cfg->master_port = atoi((char *)txt);
xmlFree(txt);
}
}
}
}
}
/* log 部分 */
void log_load(xmlNodePtr *root, AppConfig *out_cfg){
xmlNodePtr log = find_child(*root, "log");
if (log) {
xmlNodePtr lvl_node = find_child(log, "level");
if (lvl_node) {
xmlChar *txt = xmlNodeGetContent(lvl_node);
if (txt) {
parse_log_level((char *)txt, &out_cfg->log_level);
xmlFree(txt);
}
}
}
}
/* persistence 部分 */
void persist_load(xmlNodePtr *root, AppConfig *out_cfg){
xmlNodePtr pers = find_child(*root, "persistence");
if (pers) {
xmlNodePtr type_node = find_child(pers, "type");
if (type_node) {
xmlChar *txt = xmlNodeGetContent(type_node);
if (txt) {
parse_persistence((char *)txt, &out_cfg->persistence);
xmlFree(txt);
}
}
xmlNodePtr dir_node = find_child(pers, "dir");
if (dir_node) {
xmlChar *txt = xmlNodeGetContent(dir_node);
if (txt) {
strncpy(out_cfg->persist_dir, (char *)txt, sizeof(out_cfg->persist_dir) - 1);
out_cfg->persist_dir[sizeof(out_cfg->persist_dir) - 1] = '\0';
xmlFree(txt);
}
}
xmlNodePtr wal_node = find_child(pers, "wal");
if (wal_node) {
xmlChar *txt = xmlNodeGetContent(wal_node);
if (txt) {
strncpy(out_cfg->oplog_file, (char *)txt, sizeof(out_cfg->oplog_file) - 1);
out_cfg->oplog_file[sizeof(out_cfg->oplog_file) - 1] = '\0';
xmlFree(txt);
}
}
xmlNodePtr array_node = find_child(pers, "array");
if (array_node) {
xmlChar *txt = xmlNodeGetContent(array_node);
if (txt) {
strncpy(out_cfg->array_file, (char *)txt, sizeof(out_cfg->array_file) - 1);
out_cfg->array_file[sizeof(out_cfg->array_file) - 1] = '\0';
xmlFree(txt);
}
}
xmlNodePtr rbtree_node = find_child(pers, "rbtree");
if (rbtree_node) {
xmlChar *txt = xmlNodeGetContent(rbtree_node);
if (txt) {
strncpy(out_cfg->rbtree_file, (char *)txt, sizeof(out_cfg->rbtree_file) - 1);
out_cfg->rbtree_file[sizeof(out_cfg->rbtree_file) - 1] = '\0';
xmlFree(txt);
}
}
xmlNodePtr hash_node = find_child(pers, "hash");
if (hash_node) {
xmlChar *txt = xmlNodeGetContent(hash_node);
if (txt) {
strncpy(out_cfg->hash_file, (char *)txt, sizeof(out_cfg->hash_file) - 1);
out_cfg->hash_file[sizeof(out_cfg->hash_file) - 1] = '\0';
xmlFree(txt);
}
}
}
}
/* memory 部分 */
void memory_load(xmlNodePtr *root, AppConfig *out_cfg){
xmlNodePtr mem = find_child(*root, "memory");
if (mem) {
xmlNodePtr alloc_node = find_child(mem, "allocator");
if (alloc_node) {
xmlChar *txt = xmlNodeGetContent(alloc_node);
if (txt) {
parse_allocator((char *)txt, &out_cfg->allocator);
xmlFree(txt);
}
}
xmlNodePtr leakage_node = find_child(mem, "leakage");
if (leakage_node) {
xmlChar *txt = xmlNodeGetContent(leakage_node);
if (txt) {
parse_leakage((char *)txt, &out_cfg->leak_mode);
xmlFree(txt);
}
}
}
}
int config_load(const char *filename, AppConfig *out_cfg)
{
if (!filename || !out_cfg) return -1;
set_default_config(out_cfg);
int rc = -1;
xmlDocPtr doc = NULL;
int fd = -1;
void *addr = NULL;
size_t len = 0;
if (read_file_mmap(filename, &addr, &len, &fd) != 0) {
// read_file_mmap 已经打印错误
return -1;
}
/*
* 用 xmlReadMemory 从内存解析。
* - "UTF-8":你原来指定了 UTF-8如果希望自动探测可以传 NULL。
* - XML_PARSE_NONET禁用网络访问防 XXE/外部实体拉取)
* - XML_PARSE_NOBLANKS保持你原来的行为
* 你也可以加 XML_PARSE_NOERROR | XML_PARSE_NOWARNING 减少噪音,但调试阶段不建议。
*/
int parse_opts = XML_PARSE_NOBLANKS | XML_PARSE_NONET;
// xmlDocPtr doc = xmlReadFile(filename, "UTF-8", XML_PARSE_NOBLANKS);
doc = xmlReadMemory((const char *)addr, (int)len, filename, "UTF-8", parse_opts);
if (!doc) {
fprintf(stderr, "config_load: failed to read file %s\n", filename);
goto cleanup;
}
xmlNodePtr root = xmlDocGetRootElement(doc);
if (!root || xmlStrcmp(root->name, BAD_CAST "config") != 0) {
fprintf(stderr, "config_load: root element is not <config>\n");
goto cleanup;
}
server_load(&root, out_cfg);
log_load(&root, out_cfg);
persist_load(&root, out_cfg);
memory_load(&root, out_cfg);
rc = 0;
cleanup:
if (doc) xmlFreeDoc(doc);
if (addr && len) munmap(addr, len);
if (fd >= 0) close(fd);
return rc;
}