238 lines
6.7 KiB
C
238 lines
6.7 KiB
C
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <strings.h> // strcasecmp
|
|
#include <libxml/parser.h>
|
|
#include <libxml/tree.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';
|
|
|
|
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;
|
|
}
|
|
|
|
/* ---- 字符串转枚举 ---- */
|
|
|
|
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 *out = ALLOC_OTHER;
|
|
}
|
|
|
|
/* ---- 对外的枚举转字符串工具 ---- */
|
|
|
|
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_OTHER: return "other";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
/* ---- 主函数:从 XML 加载配置 ---- */
|
|
|
|
int config_load(const char *filename, AppConfig *out_cfg)
|
|
{
|
|
if (!filename || !out_cfg) return -1;
|
|
|
|
set_default_config(out_cfg);
|
|
|
|
xmlDocPtr doc = xmlReadFile(filename, "UTF-8", XML_PARSE_NOBLANKS);
|
|
if (!doc) {
|
|
fprintf(stderr, "config_load: failed to read file %s\n", filename);
|
|
return -1;
|
|
}
|
|
|
|
xmlNodePtr root = xmlDocGetRootElement(doc);
|
|
if (!root || xmlStrcmp(root->name, BAD_CAST "config") != 0) {
|
|
fprintf(stderr, "config_load: root element is not <config>\n");
|
|
xmlFreeDoc(doc);
|
|
return -1;
|
|
}
|
|
|
|
/* server 部分 */
|
|
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(port_node);
|
|
if (txt) {
|
|
out_cfg->master_port = atoi((char *)txt);
|
|
xmlFree(txt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* log 部分 */
|
|
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 部分 */
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* memory 部分 */
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
xmlFreeDoc(doc);
|
|
/* xmlCleanupParser() 建议在程序退出时 main 里统一调用 */
|
|
|
|
return 0;
|
|
}
|