feat: /realm in TUI, daemon instance takeover (0.9.16)

Pidfile records instance+port; login restarts the service when the seat
changes. TUI /realm status|login|stop; prompts discourage filler chatter.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WHC
2026-10-29 11:00:34 +08:00
co-authored by Cursor
parent e610d70c30
commit a3e115539e
8 changed files with 202 additions and 24 deletions
+98 -15
View File
@@ -41,21 +41,53 @@ int cst_parliament_daemon_pidfile(char *out, size_t n) {
return os_path_join(out, n, dir, "parliament.pid");
}
static int read_pid(void) {
typedef struct {
int pid;
char instance[64];
int port;
} daemon_meta_t;
static int read_meta(daemon_meta_t *m) {
memset(m, 0, sizeof(*m));
m->pid = -1;
char path[OS_PATH_MAX];
if (cst_parliament_daemon_pidfile(path, sizeof(path)) != 0)
return -1;
FILE *f = fopen(path, "rb");
if (!f)
return -1;
int pid = -1;
if (fscanf(f, "%d", &pid) != 1)
pid = -1;
char line[256];
while (fgets(line, sizeof(line), f)) {
if (!strncmp(line, "pid=", 4))
m->pid = atoi(line + 4);
else if (!strncmp(line, "instance=", 9)) {
const char *p = line + 9;
size_t i = 0;
while (p[i] && p[i] != '\n' && p[i] != '\r' &&
i + 1 < sizeof(m->instance)) {
m->instance[i] = p[i];
i++;
}
m->instance[i] = 0;
} else if (!strncmp(line, "port=", 5))
m->port = atoi(line + 5);
else if (m->pid <= 0) {
/* Legacy single-line pidfile. */
int pid = atoi(line);
if (pid > 0)
m->pid = pid;
}
}
fclose(f);
return pid;
return m->pid > 0 ? 0 : -1;
}
static int write_pid(int pid) {
static int read_pid(void) {
daemon_meta_t m;
return read_meta(&m) == 0 ? m.pid : -1;
}
static int write_pid(int pid, const char *instance_id, int peer_port) {
char path[OS_PATH_MAX], tmp[OS_PATH_MAX + 8];
if (cst_parliament_daemon_pidfile(path, sizeof(path)) != 0)
return -1;
@@ -64,7 +96,9 @@ static int write_pid(int pid) {
FILE *f = fopen(tmp, "wb");
if (!f)
return -1;
int ok = fprintf(f, "%d\n", pid) > 0;
int ok = fprintf(f, "pid=%d\ninstance=%s\nport=%d\n", pid,
instance_id ? instance_id : "",
peer_port > 0 ? peer_port : 0) > 0;
if (fclose(f) != 0)
ok = 0;
if (!ok) {
@@ -80,8 +114,7 @@ static void clear_pid(void) {
remove(path);
}
int cst_parliament_daemon_running(void) {
int pid = read_pid();
static int pid_alive(int pid) {
if (pid <= 0)
return 0;
#ifdef _WIN32
@@ -95,6 +128,34 @@ int cst_parliament_daemon_running(void) {
#endif
}
int cst_parliament_daemon_running(void) {
daemon_meta_t m;
if (read_meta(&m) != 0)
return 0;
if (!pid_alive(m.pid)) {
clear_pid();
return 0;
}
return 1;
}
int cst_parliament_daemon_info(int *pid_out, char *instance_out, size_t in_sz,
int *port_out) {
daemon_meta_t m;
if (read_meta(&m) != 0 || !pid_alive(m.pid)) {
if (read_meta(&m) == 0 && !pid_alive(m.pid))
clear_pid();
return -1;
}
if (pid_out)
*pid_out = m.pid;
if (instance_out && in_sz)
snprintf(instance_out, in_sz, "%s", m.instance);
if (port_out)
*port_out = m.port;
return 0;
}
static void sleep_ms(int ms) {
#ifdef _WIN32
Sleep((DWORD)ms);
@@ -194,7 +255,8 @@ int cst_parliament_daemon_run(const char *password, const char *instance_id,
#else
(int)getpid()
#endif
);
,
instance_id, bound);
fprintf(stderr, "parliament_service pid=%d port=%d id=%s realm=%s\n",
#ifdef _WIN32
(int)GetCurrentProcessId(),
@@ -273,8 +335,25 @@ int cst_parliament_daemon_start(const char *password, const char *instance_id,
int peer_port, char *err, size_t err_sz) {
if (err && err_sz)
err[0] = 0;
if (cst_parliament_daemon_running())
return 0;
if (!instance_id || !instance_id[0]) {
if (err && err_sz)
snprintf(err, err_sz, "instance id required");
return -1;
}
int want_port = peer_port > 0 ? peer_port : 9100;
{
int cur_pid = 0, cur_port = 0;
char cur_id[64] = {0};
if (cst_parliament_daemon_info(&cur_pid, cur_id, sizeof(cur_id),
&cur_port) == 0) {
int same = (!cur_id[0] || strcmp(cur_id, instance_id) == 0) &&
(cur_port <= 0 || cur_port == want_port);
if (same)
return 0;
/* Different seat or port: take over. */
(void)cst_parliament_daemon_stop();
}
}
#ifdef _WIN32
/* Windows: run in-process foreground loop under a detached console is
* deferred; callers should use --service in a service manager. */
@@ -294,9 +373,13 @@ int cst_parliament_daemon_start(const char *password, const char *instance_id,
return -1;
}
if (pid > 0) {
/* Parent: wait briefly for pidfile. */
/* Parent: wait briefly for matching pidfile. */
for (int i = 0; i < 50; i++) {
if (cst_parliament_daemon_running())
int cur_pid = 0, cur_port = 0;
char cur_id[64] = {0};
if (cst_parliament_daemon_info(&cur_pid, cur_id, sizeof(cur_id),
&cur_port) == 0 &&
(!cur_id[0] || strcmp(cur_id, instance_id) == 0))
return 0;
sleep_ms(50);
}
@@ -314,7 +397,7 @@ int cst_parliament_daemon_start(const char *password, const char *instance_id,
close(fd);
}
char derr[256];
(void)cst_parliament_daemon_run(password, instance_id, peer_port, derr,
(void)cst_parliament_daemon_run(password, instance_id, want_port, derr,
sizeof(derr));
_exit(0);
#endif