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:
+98
-15
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user