#include "config.h" #include #include #include #include // strcasecmp #include #include #include #include #include #include #include #include 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; } /* ---- 字符串转枚举 ---- */ 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 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"; } } /* ---- 主函数:从 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); } } /* 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 \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; }