fix(scheduler,cli): bug-1453/1454/1455/1456 remove dead CA-cert plumbing, fix scheduler HA config bugs

bug-1453: remove unused client-side CA-certificate plumbing. C-side:
drop ca_cert_path from xce_cluster_config_t, do_connect()'s ca_cert
param, --ca-cert flags on worker/scheduler, and controller's cert
population (TLS is server-side only, SSL_VERIFY_NONE everywhere on
the client side already, verified zero SSL_CTX_load_verify_locations
calls in the C tree). Go-side: delete xcecli's self-signed cert
TOFU-pinning flow (cmd/auth/cert/*), CACert field, SetRootCAs/
loadRootCAs, and every call site.

bug-1454: fix xcecli_ctx_t leak in scheduler.c sched_init() — the
unconditional initial ctx_init was never freed before the HA
candidate loop re-initialized it on the first iteration.

bug-1455: sched_init()'s memset(sched, 0, ...) was silently wiping
sched->cluster_cfg that main.c had populated from --peers just before
calling sched_init(), so --peers was always ignored and the scheduler
fell back to the hardcoded localhost:9990 single-host default.
Restructured sched_init() to take cluster_cfg as an explicit parameter
(assigned after the memset) instead of relying on caller-side
ordering with a separate sched_set_cluster_cfg() setter, which is now
removed.

bug-1456: revert an unsafe stack-local xce_client_config_t introduced
while fixing bug-1455 — xcecli_ctx_init() stores that pointer as
ctx->conf and client_failover.c dereferences it for the ctx's entire
life (runtime reconnect), so a per-loop-iteration stack copy would
dangle the moment sched_init() returned. Back to mutating the
caller-owned, process-lifetime config in place.

Verified: full ASAN build (all components), meson test suite 9/9 OK,
manual scheduler testsuite 15/15, Go build/vet/test all pass.
This commit is contained in:
Rongsong Shen 2026-07-20 12:01:10 +00:00
parent 619f41f70e
commit 94f50ce6e1
34 changed files with 1999 additions and 2274 deletions

View file

@ -122,8 +122,7 @@ typedef struct {
} xce_scheduler_t;
extern int sched_init(xce_scheduler_t*, xce_client_config_t*,
xce_request_handler_t);
extern void sched_set_cluster_cfg(xce_scheduler_t*, const xce_cluster_config_t*);
const xce_cluster_config_t*, xce_request_handler_t);
extern void sched_fini(xce_scheduler_t*);
extern int sched_init_config(xce_scheduler_t* sched, xce_conf4sched_t*);
extern int sched_update(xce_scheduler_t*);

View file

@ -27,15 +27,13 @@ const char* argp_program_bug_address = BUG_EMAIL;
struct arguments {
char* logfile;
int verbose;
char* peers; /* --peers value (optional) */
char* ca_cert; /* --ca-cert value (optional) */
char* peers; /* --peers value (optional) */
};
static struct argp_option options[] = {
{ "logfile", 'l', "FILE", 0, "The full path of log file" },
{ "verbose", 'v', 0, 0, "Produce verbose log" },
{ "peers", 'P', "LIST", 0, "HA candidate list: [name=]host:port,..." },
{ "ca-cert", 'C', "FILE", 0, "CA certificate path for TLS verification" },
{ "logfile", 'l', "FILE", 0, "The full path of log file" },
{ "verbose", 'v', 0, 0, "Produce verbose log" },
{ "peers", 'P', "LIST", 0, "HA candidate list: [name=]host:port,..." },
{ 0 }
};
@ -53,9 +51,6 @@ static error_t parse_opt(int key, char* arg, struct argp_state* state)
case 'P':
arguments->peers = arg;
break;
case 'C':
arguments->ca_cert = arg;
break;
default:
return ARGP_ERR_UNKNOWN;
}
@ -106,7 +101,9 @@ int main(int argc, char* argv[])
char* loglevel = "info";
int logsize = 64 * 1024 * 1024;
int logcount = 10;
xce_client_config_t config;
xce_client_config_t config;
xce_cluster_config_t cluster_cfg = {0};
const xce_cluster_config_t* cluster_cfg_ptr = NULL;
memset(&arguments, 0x0, sizeof(arguments));
@ -124,8 +121,6 @@ int main(int argc, char* argv[])
/* P2.5-6: populate and validate xce_cluster_config_t when --peers given */
if (arguments.peers) {
xce_cluster_config_t cluster_cfg = {0};
if (xce_parse_peers(arguments.peers, &cluster_cfg) != 0) {
fprintf(stderr,
"xce_scheduler: --peers parse error: expected "
@ -133,16 +128,12 @@ int main(int argc, char* argv[])
arguments.peers);
return 1;
}
if (arguments.ca_cert) {
strncpy(cluster_cfg.ca_cert_path, arguments.ca_cert,
sizeof(cluster_cfg.ca_cert_path) - 1);
}
strncpy(cluster_cfg.identity, "xce_scheduler",
sizeof(cluster_cfg.identity) - 1);
if (xce_cluster_config_validate(&cluster_cfg) != 0) {
fprintf(stderr,
"xce_scheduler: invalid cluster config (missing "
"--ca-cert?).\n");
"identity?).\n");
return 1;
}
if (cluster_cfg.candidates_count > 3) {
@ -150,17 +141,16 @@ int main(int argc, char* argv[])
"only <=3 validated for HA",
cluster_cfg.candidates_count);
}
INFO("HA cluster config: %d candidate(s), identity=%s, ca_cert=%s",
cluster_cfg.candidates_count, cluster_cfg.identity,
cluster_cfg.ca_cert_path);
sched_set_cluster_cfg(&sched, &cluster_cfg);
INFO("HA cluster config: %d candidate(s), identity=%s",
cluster_cfg.candidates_count, cluster_cfg.identity);
cluster_cfg_ptr = &cluster_cfg;
} else {
INFO("No --peers flag; using single-controller path");
}
init_config(&config);
sched_init(&sched, &config, scheduler_req_handler);
sched_init(&sched, &config, cluster_cfg_ptr, scheduler_req_handler);
signal(SIGINT, sig_handler);

View file

@ -372,6 +372,7 @@ static int sched_plugin_init(xce_scheduler_t* sched)
}
int sched_init(xce_scheduler_t* sched, xce_client_config_t* config,
const xce_cluster_config_t* cluster_cfg,
xce_request_handler_t handler)
{
int rc = 0;
@ -412,6 +413,10 @@ int sched_init(xce_scheduler_t* sched, xce_client_config_t* config,
memset(sched, 0x0, sizeof(*sched));
if (cluster_cfg != NULL) {
sched->cluster_cfg = *cluster_cfg;
}
resreq_init(1); /* enable CSE intern table for scheduler process */
sched->nextid = 1;
@ -461,10 +466,22 @@ int sched_init(xce_scheduler_t* sched, xce_client_config_t* config,
/* HA mode: iterate candidates until one accepts as primary */
if (sched->cluster_cfg.candidates_count > 0) {
int joined = 0;
/* loop below re-inits apictx per candidate; free the ctx from the
* unconditional init above before the first iteration replaces it */
xcecli_ctx_fini(sched->apictx);
for (int i = 0; i < sched->cluster_cfg.candidates_count; i++) {
const char* host = sched->cluster_cfg.candidates[i].hostname;
int port = sched->cluster_cfg.candidates[i].port;
/* xcecli_ctx_init() stores this pointer (ctx->conf), not a copy —
* client_failover.c dereferences it for the ctx's entire life
* (runtime reconnect), well past this loop returning. It MUST
* point at storage that outlives sched_init(): mutate the
* caller's config in place (main.c's `config` lives for the
* whole process) rather than a per-iteration stack local, which
* would leave ctx->conf dangling the moment this loop exits. */
config->nmasters = 1;
config->masters[0] = (char*)host;
config->master_ports[0] = port;
@ -518,15 +535,6 @@ int sched_init(xce_scheduler_t* sched, xce_client_config_t* config,
return rc;
}
void sched_set_cluster_cfg(xce_scheduler_t* sched,
const xce_cluster_config_t* cfg)
{
if (sched == NULL || cfg == NULL) {
return;
}
sched->cluster_cfg = *cfg;
}
void sched_fini(xce_scheduler_t* sched)
{
void* res;