perf: accept thread + dial backoff; join/peers.json (0.9.23)
Run inbound peer handling on its own thread so outbound dials cannot starve :9100. Failed peers cool down exponentially. Also land --join (member seat) and realm/peers.json seed discovery for Tailscale meshes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+12
-2
@@ -17,15 +17,24 @@ cst-agent --realm-password-init
|
|||||||
```bash
|
```bash
|
||||||
export CST_INSTANCE_ID=laptop-a # 可选;默认用本机 hostname
|
export CST_INSTANCE_ID=laptop-a # 可选;默认用本机 hostname
|
||||||
cst-agent --login --realm-label "thinkpad"
|
cst-agent --login --realm-label "thinkpad"
|
||||||
|
# 其他机器(同 salt/公钥 + 密码):
|
||||||
|
cst-agent --join --instance phone-b --realm-label "pixel"
|
||||||
# TUI: /realm login(夺主席)或 /realm join(不抢主席)
|
# TUI: /realm login(夺主席)或 /realm join(不抢主席)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
跨网段(如 Tailscale)多播发现不可用时,在 Realm 目录放 `peers.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"peers":[{"host":"100.64.0.13","port":9100,"iid":"labpc","name":"labpc"}]}
|
||||||
|
```
|
||||||
|
|
||||||
效果:
|
效果:
|
||||||
|
|
||||||
1. 用密码解锁 Realm;`--login` / `/realm login` 使本机成为**唯一主席**
|
1. 用密码解锁 Realm;`--login` / `/realm login` 使本机成为**唯一主席**
|
||||||
2. `/realm join` 只登记席位票:主席空缺时才夺椅,否则以 **member** 身份加入
|
2. `--join` / `/realm join` 只登记席位票:主席空缺时才夺椅,否则以 **member** 身份加入
|
||||||
3. 写入本地登录票(24h;成员票在主席易主后仍有效,除非本席被剥夺主席)
|
3. 写入本地登录票(24h;成员票在主席易主后仍有效,除非本席被剥夺主席)
|
||||||
4. 拉起后台议会服务(发现 + 密封通道按 revision 同步 `parliament.context`)
|
4. 拉起后台议会服务(发现 + 密封通道按 revision 同步 `parliament.context`;
|
||||||
|
Unix 下 accept 独立线程,dial 失败会指数退避,避免饿死入站)
|
||||||
|
|
||||||
查看状态:
|
查看状态:
|
||||||
|
|
||||||
@@ -94,6 +103,7 @@ Alpine 内构建,chair↔member 握手 + 密封通道同步(`sealed=1` / `pe
|
|||||||
|------|------|
|
|------|------|
|
||||||
| `--realm-password-init` | 创建密码 Realm |
|
| `--realm-password-init` | 创建密码 Realm |
|
||||||
| `--login` | 登录 + 夺主席 + 启服务 |
|
| `--login` | 登录 + 夺主席 + 启服务 |
|
||||||
|
| `--join` | 加入席位(不抢主席)+ 启服务 |
|
||||||
| `--service` / `--service-fg` | 仅启服务 |
|
| `--service` / `--service-fg` | 仅启服务 |
|
||||||
| `--service-stop` | 停服务 |
|
| `--service-stop` | 停服务 |
|
||||||
| `--new-project ID` | 清场换项目 |
|
| `--new-project ID` | 清场换项目 |
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ int cst_parliament_cli_login(const char *password, const char *instance_id,
|
|||||||
int cst_parliament_cli_login_and_serve(const char *password,
|
int cst_parliament_cli_login_and_serve(const char *password,
|
||||||
const char *instance_id,
|
const char *instance_id,
|
||||||
const char *device_label, int peer_port);
|
const char *device_label, int peer_port);
|
||||||
|
/* Join as member seat (no chair steal) then start background service. */
|
||||||
|
int cst_parliament_cli_join_and_serve(const char *password,
|
||||||
|
const char *instance_id,
|
||||||
|
const char *device_label, int peer_port);
|
||||||
int cst_parliament_cli_service(const char *password, const char *instance_id,
|
int cst_parliament_cli_service(const char *password, const char *instance_id,
|
||||||
int peer_port, int foreground);
|
int peer_port, int foreground);
|
||||||
int cst_parliament_cli_service_stop(void);
|
int cst_parliament_cli_service_stop(void);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#define CST_VERSION_MAJOR 0
|
#define CST_VERSION_MAJOR 0
|
||||||
#define CST_VERSION_MINOR 9
|
#define CST_VERSION_MINOR 9
|
||||||
#define CST_VERSION_PATCH 22
|
#define CST_VERSION_PATCH 23
|
||||||
|
|
||||||
#define CST_VERSION_STR_(x) #x
|
#define CST_VERSION_STR_(x) #x
|
||||||
#define CST_VERSION_STR(x) CST_VERSION_STR_(x)
|
#define CST_VERSION_STR(x) CST_VERSION_STR_(x)
|
||||||
|
|||||||
@@ -250,6 +250,60 @@ static void ingest_local(cst_discover_t *d) {
|
|||||||
remember(d, &list[i]);
|
remember(d, &list[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Optional realm/peers.json for meshes without multicast (e.g. Tailscale):
|
||||||
|
* {"peers":[{"host":"100.x.y.z","port":9100,"iid":"seat-a","name":"a"}]} */
|
||||||
|
static void ingest_seed_peers(cst_discover_t *d) {
|
||||||
|
char dir[OS_PATH_MAX], path[OS_PATH_MAX];
|
||||||
|
if (cst_realm_dir(dir, sizeof(dir)) != 0)
|
||||||
|
return;
|
||||||
|
if (os_path_join(path, sizeof(path), dir, "peers.json") != 0)
|
||||||
|
return;
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
if (!f)
|
||||||
|
return;
|
||||||
|
cst_buf_t b = {0};
|
||||||
|
char chunk[4096];
|
||||||
|
size_t n;
|
||||||
|
while ((n = fread(chunk, 1, sizeof(chunk), f)) && b.len < 64 * 1024)
|
||||||
|
cst_buf_append(&b, chunk, n);
|
||||||
|
fclose(f);
|
||||||
|
cJSON *root = cJSON_Parse(b.buf ? b.buf : "{}");
|
||||||
|
cst_buf_free(&b);
|
||||||
|
if (!root)
|
||||||
|
return;
|
||||||
|
cJSON *arr = cJSON_GetObjectItemCaseSensitive(root, "peers");
|
||||||
|
if (!cJSON_IsArray(arr)) {
|
||||||
|
cJSON_Delete(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const cJSON *it;
|
||||||
|
cJSON_ArrayForEach(it, arr) {
|
||||||
|
const cJSON *host = cJSON_GetObjectItemCaseSensitive(it, "host");
|
||||||
|
const cJSON *port = cJSON_GetObjectItemCaseSensitive(it, "port");
|
||||||
|
const cJSON *iid = cJSON_GetObjectItemCaseSensitive(it, "iid");
|
||||||
|
const cJSON *name = cJSON_GetObjectItemCaseSensitive(it, "name");
|
||||||
|
const cJSON *rid = cJSON_GetObjectItemCaseSensitive(it, "rid");
|
||||||
|
if (!cJSON_IsString(host) || !host->valuestring[0] || !cJSON_IsNumber(port) ||
|
||||||
|
port->valueint <= 0 || !cJSON_IsString(iid) || !iid->valuestring[0])
|
||||||
|
continue;
|
||||||
|
if (cJSON_IsString(rid) && rid->valuestring[0] &&
|
||||||
|
strcmp(rid->valuestring, d->realm_fp) != 0)
|
||||||
|
continue;
|
||||||
|
cst_peer_info_t p;
|
||||||
|
memset(&p, 0, sizeof(p));
|
||||||
|
snprintf(p.realm_fp, sizeof(p.realm_fp), "%s", d->realm_fp);
|
||||||
|
snprintf(p.instance_id, sizeof(p.instance_id), "%s", iid->valuestring);
|
||||||
|
snprintf(p.host, sizeof(p.host), "%s", host->valuestring);
|
||||||
|
if (cJSON_IsString(name) && name->valuestring[0])
|
||||||
|
snprintf(p.name, sizeof(p.name), "%s", name->valuestring);
|
||||||
|
else
|
||||||
|
snprintf(p.name, sizeof(p.name), "%s", iid->valuestring);
|
||||||
|
p.port = port->valueint;
|
||||||
|
remember(d, &p);
|
||||||
|
}
|
||||||
|
cJSON_Delete(root);
|
||||||
|
}
|
||||||
|
|
||||||
static int send_beacon(cst_discover_t *d) {
|
static int send_beacon(cst_discover_t *d) {
|
||||||
if (d->sock == DISC_BAD)
|
if (d->sock == DISC_BAD)
|
||||||
return -1;
|
return -1;
|
||||||
@@ -310,6 +364,7 @@ static void *discover_main(void *arg) {
|
|||||||
(void)cst_discover_local_put(d->realm_fp, d->instance_id, d->name,
|
(void)cst_discover_local_put(d->realm_fp, d->instance_id, d->name,
|
||||||
d->peer_port);
|
d->peer_port);
|
||||||
ingest_local(d);
|
ingest_local(d);
|
||||||
|
ingest_seed_peers(d);
|
||||||
}
|
}
|
||||||
ticks++;
|
ticks++;
|
||||||
if (d->sock == DISC_BAD) {
|
if (d->sock == DISC_BAD) {
|
||||||
|
|||||||
+20
-2
@@ -68,6 +68,7 @@ static void usage(const char *argv0) {
|
|||||||
" --realm-new create Realm file keys (legacy/dev)\n"
|
" --realm-new create Realm file keys (legacy/dev)\n"
|
||||||
" --realm-password-init create password-derived Realm (no sk on disk)\n"
|
" --realm-password-init create password-derived Realm (no sk on disk)\n"
|
||||||
" --login unlock (CST_REALM_PASSWORD or prompt); claim chair\n"
|
" --login unlock (CST_REALM_PASSWORD or prompt); claim chair\n"
|
||||||
|
" --join unlock; join as member seat (no chair steal)\n"
|
||||||
" --service run parliament service (discover+context sync)\n"
|
" --service run parliament service (discover+context sync)\n"
|
||||||
" --service-fg same as --service, stay in foreground\n"
|
" --service-fg same as --service, stay in foreground\n"
|
||||||
" --service-stop stop the parliament background service\n"
|
" --service-stop stop the parliament background service\n"
|
||||||
@@ -393,8 +394,8 @@ int main(int argc, char **argv) {
|
|||||||
int new_session = 0, do_list = 0, do_migrate = 0;
|
int new_session = 0, do_list = 0, do_migrate = 0;
|
||||||
const char *resume_id = NULL;
|
const char *resume_id = NULL;
|
||||||
int do_realm_new = 0, do_realm_enroll = 0, do_peer_listen = 0, do_peer_dial = 0,
|
int do_realm_new = 0, do_realm_enroll = 0, do_peer_listen = 0, do_peer_dial = 0,
|
||||||
do_discover = 0, do_realm_pw_init = 0, do_login = 0, do_new_project = 0,
|
do_discover = 0, do_realm_pw_init = 0, do_login = 0, do_join = 0,
|
||||||
do_service = 0, do_service_fg = 0, do_service_stop = 0;
|
do_new_project = 0, do_service = 0, do_service_fg = 0, do_service_stop = 0;
|
||||||
const char *realm_enroll_id = NULL, *realm_label = NULL;
|
const char *realm_enroll_id = NULL, *realm_label = NULL;
|
||||||
const char *peer_dial_spec = NULL, *parliament_instance = NULL;
|
const char *peer_dial_spec = NULL, *parliament_instance = NULL;
|
||||||
const char *new_project_id = NULL;
|
const char *new_project_id = NULL;
|
||||||
@@ -462,6 +463,7 @@ int main(int argc, char **argv) {
|
|||||||
{"service", no_argument, 0, 0x11b},
|
{"service", no_argument, 0, 0x11b},
|
||||||
{"service-fg", no_argument, 0, 0x11c},
|
{"service-fg", no_argument, 0, 0x11c},
|
||||||
{"service-stop", no_argument, 0, 0x11d},
|
{"service-stop", no_argument, 0, 0x11d},
|
||||||
|
{"join", no_argument, 0, 0x11e},
|
||||||
{0, 0, 0, 0}};
|
{0, 0, 0, 0}};
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long(argc, argv, "w:s:b:k:m:P:r:n:T:Hp:dahVNcR:LyA:C:e:M:",
|
while ((c = getopt_long(argc, argv, "w:s:b:k:m:P:r:n:T:Hp:dahVNcR:LyA:C:e:M:",
|
||||||
@@ -524,6 +526,9 @@ int main(int argc, char **argv) {
|
|||||||
case 0x11d:
|
case 0x11d:
|
||||||
do_service_stop = 1;
|
do_service_stop = 1;
|
||||||
break;
|
break;
|
||||||
|
case 0x11e:
|
||||||
|
do_join = 1;
|
||||||
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
snprintf(cfg.workspace_root, sizeof(cfg.workspace_root), "%s", optarg);
|
snprintf(cfg.workspace_root, sizeof(cfg.workspace_root), "%s", optarg);
|
||||||
break;
|
break;
|
||||||
@@ -705,6 +710,19 @@ int main(int argc, char **argv) {
|
|||||||
cst_curl_cleanup();
|
cst_curl_cleanup();
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
if (do_join) {
|
||||||
|
const char *pw = getenv("CST_REALM_PASSWORD");
|
||||||
|
char iidbuf[64];
|
||||||
|
const char *iid = parliament_instance;
|
||||||
|
if (!iid || !iid[0]) {
|
||||||
|
cst_parliament_default_instance(iidbuf, sizeof(iidbuf));
|
||||||
|
iid = iidbuf;
|
||||||
|
}
|
||||||
|
int rc = cst_parliament_cli_join_and_serve(pw, iid, realm_label,
|
||||||
|
discover_peer_port);
|
||||||
|
cst_curl_cleanup();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
if (do_new_project) {
|
if (do_new_project) {
|
||||||
int rc = cst_parliament_cli_new_project(new_project_id);
|
int rc = cst_parliament_cli_new_project(new_project_id);
|
||||||
cst_curl_cleanup();
|
cst_curl_cleanup();
|
||||||
|
|||||||
@@ -125,6 +125,40 @@ int cst_parliament_cli_login_and_serve(const char *password,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cst_parliament_cli_join_and_serve(const char *password,
|
||||||
|
const char *instance_id,
|
||||||
|
const char *device_label, int peer_port) {
|
||||||
|
char err[256], pwbuf[256];
|
||||||
|
const char *pw = password;
|
||||||
|
if ((!pw || !pw[0]) && resolve_password(pwbuf, sizeof(pwbuf), &pw) != 0)
|
||||||
|
return 1;
|
||||||
|
char *hand =
|
||||||
|
cst_parliament_seat_join(pw, instance_id, device_label, err, sizeof(err));
|
||||||
|
if (!hand) {
|
||||||
|
fprintf(stderr, "%s\n", err[0] ? err : "join failed");
|
||||||
|
if (pw == pwbuf)
|
||||||
|
memset(pwbuf, 0, sizeof(pwbuf));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fputs(hand, stdout);
|
||||||
|
free(hand);
|
||||||
|
if (cst_parliament_daemon_start(pw, instance_id,
|
||||||
|
peer_port > 0 ? peer_port : 9100, err,
|
||||||
|
sizeof(err)) != 0) {
|
||||||
|
fprintf(stderr, "warning: service: %s (seat join still valid)\n",
|
||||||
|
err[0] ? err : "start failed");
|
||||||
|
printf("service=failed\n");
|
||||||
|
if (pw == pwbuf)
|
||||||
|
memset(pwbuf, 0, sizeof(pwbuf));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (cst_parliament_daemon_running())
|
||||||
|
printf("service=running\n");
|
||||||
|
if (pw == pwbuf)
|
||||||
|
memset(pwbuf, 0, sizeof(pwbuf));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int cst_parliament_cli_service(const char *password, const char *instance_id,
|
int cst_parliament_cli_service(const char *password, const char *instance_id,
|
||||||
int peer_port, int foreground) {
|
int peer_port, int foreground) {
|
||||||
char err[256], pwbuf[256];
|
char err[256], pwbuf[256];
|
||||||
|
|||||||
+147
-32
@@ -20,8 +20,10 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#else
|
#else
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -72,7 +74,6 @@ static int read_meta(daemon_meta_t *m) {
|
|||||||
} else if (!strncmp(line, "port=", 5))
|
} else if (!strncmp(line, "port=", 5))
|
||||||
m->port = atoi(line + 5);
|
m->port = atoi(line + 5);
|
||||||
else if (m->pid <= 0) {
|
else if (m->pid <= 0) {
|
||||||
/* Legacy single-line pidfile. */
|
|
||||||
int pid = atoi(line);
|
int pid = atoi(line);
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
m->pid = pid;
|
m->pid = pid;
|
||||||
@@ -165,6 +166,20 @@ static void sleep_ms(int ms) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int64_t now_ms(void) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
return (int64_t)GetTickCount64();
|
||||||
|
#else
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return (int64_t)tv.tv_sec * 1000 + (int64_t)tv.tv_usec / 1000;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
static pthread_mutex_t g_sync_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
#endif
|
||||||
|
|
||||||
static int load_local_bundle(cst_peer_sync_bundle_t *b) {
|
static int load_local_bundle(cst_peer_sync_bundle_t *b) {
|
||||||
memset(b, 0, sizeof(*b));
|
memset(b, 0, sizeof(*b));
|
||||||
cst_parliament_meta_t m;
|
cst_parliament_meta_t m;
|
||||||
@@ -186,16 +201,27 @@ static int apply_if_newer(cst_peer_sync_bundle_t *b, unsigned long before) {
|
|||||||
b->chair_instance, b->context);
|
b->chair_instance, b->context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Short sync budget: accept thread must not stall the listen backlog. */
|
||||||
static int sync_on_session(cst_peer_session_t *sess) {
|
static int sync_on_session(cst_peer_session_t *sess) {
|
||||||
cst_peer_sync_bundle_t local;
|
cst_peer_sync_bundle_t local;
|
||||||
if (load_local_bundle(&local) != 0)
|
#ifndef _WIN32
|
||||||
|
pthread_mutex_lock(&g_sync_mu);
|
||||||
|
#endif
|
||||||
|
if (load_local_bundle(&local) != 0) {
|
||||||
|
#ifndef _WIN32
|
||||||
|
pthread_mutex_unlock(&g_sync_mu);
|
||||||
|
#endif
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
unsigned long before = local.revision;
|
unsigned long before = local.revision;
|
||||||
char err[128];
|
char err[128];
|
||||||
int rc = cst_peer_sync(sess, &local, 15000, err, sizeof(err));
|
int rc = cst_peer_sync(sess, &local, 3000, err, sizeof(err));
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
apply_if_newer(&local, before);
|
apply_if_newer(&local, before);
|
||||||
free(local.context);
|
free(local.context);
|
||||||
|
#ifndef _WIN32
|
||||||
|
pthread_mutex_unlock(&g_sync_mu);
|
||||||
|
#endif
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +234,72 @@ static int unlock_realm(const char *password, cst_realm_keys_t *keys) {
|
|||||||
return cst_realm_load(dir, keys);
|
return cst_realm_load(dir, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
os_socket_t ls;
|
||||||
|
cst_realm_cert_t cert;
|
||||||
|
uint8_t sk[64];
|
||||||
|
uint8_t realm_pk[32];
|
||||||
|
} accept_ctx_t;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
static void *accept_main(void *arg) {
|
||||||
|
accept_ctx_t *ac = arg;
|
||||||
|
while (!g_stop) {
|
||||||
|
cst_peer_session_t sess;
|
||||||
|
char herr[128];
|
||||||
|
if (cst_peer_accept((intptr_t)ac->ls, &ac->cert, ac->sk, ac->realm_pk, 400,
|
||||||
|
&sess, herr, sizeof(herr)) != 0)
|
||||||
|
continue;
|
||||||
|
sync_on_session(&sess);
|
||||||
|
cst_peer_close(&sess);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Per-peer dial cooldown so stuck remotes do not dominate the dial loop. */
|
||||||
|
typedef struct {
|
||||||
|
char iid[64];
|
||||||
|
int fails;
|
||||||
|
int64_t cool_until_ms;
|
||||||
|
} dial_cool_t;
|
||||||
|
|
||||||
|
static dial_cool_t *cool_find(dial_cool_t *tab, int *n, int cap,
|
||||||
|
const char *iid) {
|
||||||
|
for (int i = 0; i < *n; i++) {
|
||||||
|
if (!strcmp(tab[i].iid, iid))
|
||||||
|
return &tab[i];
|
||||||
|
}
|
||||||
|
if (*n >= cap)
|
||||||
|
return NULL;
|
||||||
|
dial_cool_t *c = &tab[(*n)++];
|
||||||
|
memset(c, 0, sizeof(*c));
|
||||||
|
snprintf(c->iid, sizeof(c->iid), "%s", iid);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cool_blocked(dial_cool_t *c, int64_t now) {
|
||||||
|
return c && c->cool_until_ms > now;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cool_fail(dial_cool_t *c, int64_t now) {
|
||||||
|
if (!c)
|
||||||
|
return;
|
||||||
|
c->fails = c->fails < 8 ? c->fails + 1 : 8;
|
||||||
|
/* 4s, 8s, 16s, … capped at 120s */
|
||||||
|
int64_t wait = 4000LL << (c->fails - 1);
|
||||||
|
if (wait > 120000)
|
||||||
|
wait = 120000;
|
||||||
|
c->cool_until_ms = now + wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cool_ok(dial_cool_t *c) {
|
||||||
|
if (!c)
|
||||||
|
return;
|
||||||
|
c->fails = 0;
|
||||||
|
c->cool_until_ms = 0;
|
||||||
|
}
|
||||||
|
|
||||||
int cst_parliament_daemon_run(const char *password, const char *instance_id,
|
int cst_parliament_daemon_run(const char *password, const char *instance_id,
|
||||||
int peer_port, char *err, size_t err_sz) {
|
int peer_port, char *err, size_t err_sz) {
|
||||||
if (err && err_sz)
|
if (err && err_sz)
|
||||||
@@ -272,65 +364,93 @@ int cst_parliament_daemon_run(const char *password, const char *instance_id,
|
|||||||
signal(SIGHUP, on_sig);
|
signal(SIGHUP, on_sig);
|
||||||
#endif
|
#endif
|
||||||
g_stop = 0;
|
g_stop = 0;
|
||||||
int tick = 0, idle = 0, dial_rr = 0;
|
|
||||||
|
accept_ctx_t ac;
|
||||||
|
memset(&ac, 0, sizeof(ac));
|
||||||
|
ac.ls = ls;
|
||||||
|
ac.cert = cert;
|
||||||
|
memcpy(ac.sk, sk, 64);
|
||||||
|
memcpy(ac.realm_pk, realm.pk, 32);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
pthread_t ath;
|
||||||
|
int have_ath = pthread_create(&ath, NULL, accept_main, &ac) == 0;
|
||||||
|
#else
|
||||||
|
int have_ath = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int tick = 0, dial_rr = 0;
|
||||||
|
dial_cool_t cool[24];
|
||||||
|
int ncool = 0;
|
||||||
|
|
||||||
while (!g_stop) {
|
while (!g_stop) {
|
||||||
cst_peer_session_t sess;
|
cst_peer_session_t sess;
|
||||||
char herr[128];
|
char herr[128];
|
||||||
/* Prefer accept: a blocking outbound dial used to starve inbound peers
|
|
||||||
* (CLOSE-WAIT + unread hello ~385B on :9100). Drain the backlog first. */
|
#ifdef _WIN32
|
||||||
int accepted = 0;
|
/* No accept thread: keep prefer-accept single-threaded path. */
|
||||||
for (;;) {
|
for (int a = 0; a < 4; a++) {
|
||||||
int accept_ms = accepted > 0 ? 50 : (idle > 20 ? 800 : idle > 5 ? 400 : 200);
|
if (cst_peer_accept((intptr_t)ls, &cert, sk, realm.pk, 200, &sess, herr,
|
||||||
if (cst_peer_accept((intptr_t)ls, &cert, sk, realm.pk, accept_ms, &sess,
|
sizeof(herr)) != 0)
|
||||||
herr, sizeof(herr)) != 0)
|
|
||||||
break;
|
break;
|
||||||
sync_on_session(&sess);
|
sync_on_session(&sess);
|
||||||
cst_peer_close(&sess);
|
cst_peer_close(&sess);
|
||||||
accepted++;
|
|
||||||
idle = 0;
|
|
||||||
if (accepted >= 8)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (accepted == 0)
|
#endif
|
||||||
idle++;
|
|
||||||
|
|
||||||
int push = cst_parliament_sync_pending();
|
int push = cst_parliament_sync_pending();
|
||||||
tick++;
|
tick++;
|
||||||
/* At most one outbound dial per loop turn, short wall budget, so accept
|
int period = push ? 1 : 8;
|
||||||
* keeps pace. Lexicographic rule avoids A↔B mutual-dial deadlock unless
|
|
||||||
* we have a local push to deliver. */
|
|
||||||
int period = idle > 20 ? 30 : idle > 5 ? 12 : 6;
|
|
||||||
if ((push || tick % period == 0) && disc) {
|
if ((push || tick % period == 0) && disc) {
|
||||||
cst_peer_info_t peers[16];
|
cst_peer_info_t peers[16];
|
||||||
int n = cst_discover_peers(disc, peers, 16);
|
int n = cst_discover_peers(disc, peers, 16);
|
||||||
|
int64_t now = now_ms();
|
||||||
int tried = 0;
|
int tried = 0;
|
||||||
for (int k = 0; k < n; k++) {
|
for (int k = 0; k < n; k++) {
|
||||||
int i = (dial_rr + k) % n;
|
int i = (dial_rr + k) % n;
|
||||||
if (peers[i].port <= 0)
|
if (peers[i].port <= 0)
|
||||||
continue;
|
continue;
|
||||||
if (!strcmp(peers[i].instance_id, instance_id))
|
if (!strcmp(peers[i].instance_id, instance_id))
|
||||||
continue; /* never dial ourselves */
|
continue;
|
||||||
if (!push && strcmp(instance_id, peers[i].instance_id) > 0)
|
if (!push && strcmp(instance_id, peers[i].instance_id) > 0)
|
||||||
continue; /* lower id initiates idle polls */
|
continue;
|
||||||
|
dial_cool_t *c = cool_find(cool, &ncool, 24, peers[i].instance_id);
|
||||||
|
if (!push && cool_blocked(c, now))
|
||||||
|
continue;
|
||||||
dial_rr = (i + 1) % n;
|
dial_rr = (i + 1) % n;
|
||||||
if (cst_peer_dial(peers[i].host, peers[i].port, &cert, sk, realm.pk,
|
/* Dial budget stays short; accept thread covers inbound meanwhile. */
|
||||||
2500, &sess, herr, sizeof(herr)) == 0) {
|
int ok = cst_peer_dial(peers[i].host, peers[i].port, &cert, sk,
|
||||||
|
realm.pk, 2000, &sess, herr, sizeof(herr)) == 0;
|
||||||
|
if (ok) {
|
||||||
sync_on_session(&sess);
|
sync_on_session(&sess);
|
||||||
cst_peer_close(&sess);
|
cst_peer_close(&sess);
|
||||||
idle = 0;
|
cool_ok(c);
|
||||||
|
} else {
|
||||||
|
cool_fail(c, now);
|
||||||
}
|
}
|
||||||
tried = 1;
|
tried = 1;
|
||||||
break; /* one dial then go back to accept */
|
break;
|
||||||
}
|
}
|
||||||
if (push && (tried || n == 0))
|
if (push && (tried || n == 0))
|
||||||
cst_parliament_sync_ack();
|
cst_parliament_sync_ack();
|
||||||
}
|
}
|
||||||
|
/* Pace dial loop; accept runs concurrently on Unix. */
|
||||||
|
sleep_ms(have_ath ? 150 : 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
if (have_ath) {
|
||||||
|
/* Wake accept's poll by shutting down listen sock after stop. */
|
||||||
|
os_net_shutdown(ls);
|
||||||
|
pthread_join(ath, NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (disc)
|
if (disc)
|
||||||
cst_discover_stop(disc);
|
cst_discover_stop(disc);
|
||||||
os_net_close(ls);
|
os_net_close(ls);
|
||||||
clear_pid();
|
clear_pid();
|
||||||
crypto_wipe(sk, sizeof(sk));
|
crypto_wipe(sk, sizeof(sk));
|
||||||
|
crypto_wipe(ac.sk, sizeof(ac.sk));
|
||||||
crypto_wipe(realm.seed, sizeof(realm.seed));
|
crypto_wipe(realm.seed, sizeof(realm.seed));
|
||||||
crypto_wipe(realm.sk, sizeof(realm.sk));
|
crypto_wipe(realm.sk, sizeof(realm.sk));
|
||||||
return 0;
|
return 0;
|
||||||
@@ -380,13 +500,10 @@ int cst_parliament_daemon_start(const char *password, const char *instance_id,
|
|||||||
(cur_port <= 0 || cur_port == want_port);
|
(cur_port <= 0 || cur_port == want_port);
|
||||||
if (same)
|
if (same)
|
||||||
return 0;
|
return 0;
|
||||||
/* Different seat or port: take over. */
|
|
||||||
(void)cst_parliament_daemon_stop();
|
(void)cst_parliament_daemon_stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
/* Windows: run in-process foreground loop under a detached console is
|
|
||||||
* deferred; callers should use --service in a service manager. */
|
|
||||||
if (err && err_sz)
|
if (err && err_sz)
|
||||||
snprintf(err, err_sz, "background daemon requires --service on Windows");
|
snprintf(err, err_sz, "background daemon requires --service on Windows");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -403,7 +520,6 @@ int cst_parliament_daemon_start(const char *password, const char *instance_id,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (pid > 0) {
|
if (pid > 0) {
|
||||||
/* Parent: wait briefly for matching pidfile. */
|
|
||||||
for (int i = 0; i < 50; i++) {
|
for (int i = 0; i < 50; i++) {
|
||||||
int cur_pid = 0, cur_port = 0;
|
int cur_pid = 0, cur_port = 0;
|
||||||
char cur_id[64] = {0};
|
char cur_id[64] = {0};
|
||||||
@@ -417,7 +533,6 @@ int cst_parliament_daemon_start(const char *password, const char *instance_id,
|
|||||||
snprintf(err, err_sz, "service did not start");
|
snprintf(err, err_sz, "service did not start");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* Child: detach fully so SSH/nohup callers are not held by open stdio. */
|
|
||||||
setsid();
|
setsid();
|
||||||
int fd = open("/dev/null", O_RDWR);
|
int fd = open("/dev/null", O_RDWR);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user