forked from lei.chen/myxce
- xce_candidate_t gains name[] field for logical peer identity - parse_peers stores name prefix before '=' (empty when omitted) - cluster_init matches own identity by name first, hostname as fallback enabling multiple controllers on the same dev host via --peers ctrl-1=10.0.1.1:7000,ctrl-2=10.0.1.2:7001
191 lines
5.9 KiB
C
191 lines
5.9 KiB
C
#include <argp.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <tlog.h>
|
|
|
|
#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;
|
|
}
|