#include #include #include #include #include #include "myconf.h" #include "utils.h" #include "xce/cluster.h" extern int myapp_run(myconf_t*); static char doc[] = "xce_controller"; static char args_doc[] = ""; const char* argp_program_version = MK_VERSION("xce v0.0.1-", STR(VERSION_COMMIT_ID)); const char* argp_program_bug_address = BUG_EMAIL; /* Long-option key (above ASCII to avoid collisions with short opts) */ #define OPT_PEERS 256 struct arguments { char* myname; char* logfile; char* workdir; int verbose; char* peers_str; }; static struct argp_option options[] = { { "workdir", 'w', "DIR", 0, "The work directory of xce controller" }, { "logfile", 'l', "FILE", 0, "The full path of log file" }, { "verbose", 'v', 0, 0, "Produce verbose logs" }, { "myname", 'n', "STR", 0, "The name of controller" }, { "peers", OPT_PEERS, "PEERS", 0, "HA peer list: comma-separated host:port pairs ordered by priority " "(e.g. ctrl01:9990,ctrl02:9990). Index 0 = highest priority (primary)." }, { 0 } }; /* parse_peers — split "host:port,host:port,..." into cfg->candidates[]. * Accepts optional "name=host:port" prefix (name is ignored for controller). * Returns 0 on success, -1 on empty input or parse error. */ static int parse_peers(const char* peers_str, xce_cluster_config_t* cfg) { cfg->candidates_count = 0; if (!peers_str || peers_str[0] == '\0') return -1; char buf[2048]; strncpy(buf, peers_str, sizeof(buf) - 1); buf[sizeof(buf) - 1] = '\0'; char* saveptr; char* tok = strtok_r(buf, ",", &saveptr); while (tok && cfg->candidates_count < MAX_HA_CANDIDATES) { char* eq = strchr(tok, '='); char* addr = eq ? eq + 1 : tok; char* colon = strrchr(addr, ':'); if (!colon) return -1; *colon = '\0'; xce_candidate_t* cand = &cfg->candidates[cfg->candidates_count]; if (eq) { *eq = '\0'; strncpy(cand->name, tok, XCE_MAXNAME_LEN - 1); cand->name[XCE_MAXNAME_LEN - 1] = '\0'; } else { cand->name[0] = '\0'; } strncpy(cand->hostname, addr, XCE_MAXHOSTNAME_LEN - 1); cand->hostname[XCE_MAXHOSTNAME_LEN - 1] = '\0'; cand->port = atoi(colon + 1); cfg->candidates_count++; tok = strtok_r(NULL, ",", &saveptr); } return cfg->candidates_count > 0 ? 0 : -1; } static error_t parse_opt(int key, char* arg, struct argp_state* state) { struct arguments* arguments = state->input; switch (key) { case 'l': arguments->logfile = arg; break; case 'v': arguments->verbose++; break; case 'w': arguments->workdir = arg; break; case 'n': arguments->myname = arg; break; case OPT_PEERS: arguments->peers_str = arg; break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, args_doc, doc }; int main(int argc, char* argv[]) { struct arguments arguments; myconf_t myconf; char* logfile = "/tmp/xce_controller.log"; char* loglevel = "info"; int logsize = 64 * 1024 * 1024; int logcount = 10; char conf_file[XCE_MAXPATH_LEN + 1]; char key_file[XCE_MAXPATH_LEN + 1]; char cert_file[XCE_MAXPATH_LEN + 1]; char myname[XCE_MAXNAME_LEN + 1]; memset(&arguments, 0x0, sizeof(arguments)); argp_parse(&argp, argc, argv, 0, 0, &arguments); if (arguments.logfile != NULL) { logfile = arguments.logfile; } if (arguments.verbose != 0) { loglevel = verbose_to_loglevel(arguments.verbose); } mylog_init(logfile, loglevel, logsize, logcount); if (arguments.workdir == NULL) { INFO("no cluster work directory specified. The default work directory " "`" DEFAULT_WORKDIR "' will be used."); arguments.workdir = DEFAULT_WORKDIR; } if (arguments.myname == NULL) { if (get_myname(myname, sizeof(myname) - 1) < 0) { ERROR("get_myname"); return -1; } arguments.myname = myname; } INFO("My name is %s", myname); memset(&conf_file, 0x0, sizeof(conf_file)); snprintf(conf_file, XCE_MAXPATH_LEN, "%s/%s/conf/current_config", arguments.workdir, myname); snprintf(key_file, XCE_MAXPATH_LEN, "%s/certs/xce.key", arguments.workdir); snprintf(cert_file, XCE_MAXPATH_LEN, "%s/certs/xce.crt", arguments.workdir); myconf.cluster_config = NULL; myconf.conffile = conf_file; myconf.certfile = cert_file; myconf.keyfile = key_file; myconf.workdir = arguments.workdir; myconf.name = arguments.myname; memset(&myconf.peers_cfg, 0, sizeof(myconf.peers_cfg)); if (arguments.peers_str) { if (parse_peers(arguments.peers_str, &myconf.peers_cfg) != 0) { fprintf(stderr, "xce_controller: --peers parse error: expected " "host:port,... got: %s\n", arguments.peers_str); return 1; } /* CA cert derived from workdir — same infrastructure as certfile/keyfile */ snprintf(myconf.peers_cfg.ca_cert_path, sizeof(myconf.peers_cfg.ca_cert_path), "%s/certs/xce.crt", arguments.workdir); strncpy(myconf.peers_cfg.identity, arguments.myname, sizeof(myconf.peers_cfg.identity) - 1); INFO("HA mode: %d controller candidates, identity=%s", myconf.peers_cfg.candidates_count, myconf.peers_cfg.identity); } else { INFO("No --peers flag; running as single-node primary"); } myapp_run(&myconf); mylog_fini(); return 0; }