tests: verify live daemon cleanup

This commit is contained in:
2026-08-14 00:11:03 +09:00
parent 685d78bab5
commit b44663096f
2 changed files with 160 additions and 46 deletions

View File

@@ -51,7 +51,8 @@ struct Siglog
int invalid;
int npreedit;
int ncommit;
char path[96];
char preeditpath[96];
char commitpath[96];
char preedit[256];
char commit[256];
dbus_uint32_t cursor;
@@ -279,12 +280,15 @@ startdaemon(Daemon *d, char *program, char *mapdir)
dup2(errpipe[1], STDERR_FILENO) < 0)
_exit(126);
close(errpipe[1]);
setenv("XDG_RUNTIME_DIR", d->runtime, 1);
setenv("XDG_CONFIG_HOME", d->config, 1);
setenv("HOME", d->home, 1);
unsetenv("DISPLAY");
unsetenv("DBUS_SESSION_BUS_ADDRESS");
unsetenv("IBUS_ADDRESS");
if(setenv("XDG_RUNTIME_DIR", d->runtime, 1) < 0 ||
setenv("XDG_CONFIG_HOME", d->config, 1) < 0 ||
setenv("HOME", d->home, 1) < 0 || unsetenv("DISPLAY") < 0 ||
unsetenv("DBUS_SESSION_BUS_ADDRESS") < 0 ||
unsetenv("IBUS_ADDRESS") < 0){
dprintf(STDERR_FILENO, "set daemon environment: %s\n",
strerror(errno));
_exit(126);
}
execl(program, program, mapdir, missing, (char*)0);
dprintf(STDERR_FILENO, "exec %s: %s\n", program, strerror(errno));
_exit(127);
@@ -350,36 +354,82 @@ rmdirknown(char *path)
return fail("rmdir %s: %s", path, strerror(errno));
}
static int
killdaemon(Daemon *d, int *status)
{
int n, ok;
ok = 1;
if(kill(d->pid, SIGKILL) < 0 && errno != ESRCH){
fail("kill -9 %ld: %s", (long)d->pid, strerror(errno));
ok = 0;
}
do
n = waitpid(d->pid, status, 0);
while(n < 0 && errno == EINTR);
if(n != d->pid){
fail("reap %ld after SIGKILL: %s", (long)d->pid,
n < 0 ? strerror(errno) : "wrong child");
ok = 0;
}else
d->pid = -1;
return ok;
}
static int
stopdaemon(Daemon *d)
{
struct pollfd pfd;
int ok, status, n;
int ok, status, n, reaped;
int64_t deadline;
ok = 1;
if(d->pid > 0){
if(kill(d->pid, SIGTERM) < 0 && errno != ESRCH){
do
n = waitpid(d->pid, &status, WNOHANG);
while(n < 0 && errno == EINTR);
if(n == d->pid){
fail("daemon exited before test termination with wait status %#x",
status);
d->pid = -1;
ok = 0;
}else if(n < 0){
fail("check daemon %ld before termination: %s",
(long)d->pid, strerror(errno));
if(errno == ECHILD)
d->pid = -1;
ok = 0;
}
}
if(d->pid > 0){
if(kill(d->pid, SIGTERM) < 0){
fail("kill %ld: %s", (long)d->pid, strerror(errno));
ok = 0;
}
reaped = 0;
deadline = nowms() + Stoptimeout;
for(;;){
n = waitpid(d->pid, &status, WNOHANG);
if(n == d->pid)
if(n == d->pid){
reaped = 1;
d->pid = -1;
break;
if(n < 0 && errno != EINTR){
}
if(n < 0 && errno == EINTR)
continue;
if(n < 0){
fail("waitpid %ld: %s", (long)d->pid, strerror(errno));
ok = 0;
if(errno == ECHILD)
d->pid = -1;
break;
}
n = leftms(deadline);
if(n == 0){
fail("daemon %ld did not stop after SIGTERM", (long)d->pid);
ok = 0;
kill(d->pid, SIGKILL);
while(waitpid(d->pid, &status, 0) < 0 && errno == EINTR)
;
if(!killdaemon(d, &status))
ok = 0;
break;
}
pfd.fd = d->errfd;
@@ -388,14 +438,19 @@ stopdaemon(Daemon *d)
if(poll(&pfd, 1, n) < 0 && errno != EINTR){
fail("poll daemon %ld: %s", (long)d->pid, strerror(errno));
ok = 0;
kill(d->pid, SIGKILL);
while(waitpid(d->pid, &status, 0) < 0 && errno == EINTR)
;
if(!killdaemon(d, &status))
ok = 0;
break;
}
readerrors(d);
}
d->pid = -1;
/* plan9port turns a caught termination note into exit status 1. */
if(reaped &&
!((WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM) ||
(WIFEXITED(status) && WEXITSTATUS(status) == 1))){
fail("daemon exited with unexpected wait status %#x", status);
ok = 0;
}
}
readerrors(d);
if(d->errfd >= 0){
@@ -490,15 +545,13 @@ onsignal(DBusConnection *conn, DBusMessage *m, void *arg)
if(dbus_message_get_type(m) != DBUS_MESSAGE_TYPE_SIGNAL ||
!dbus_message_has_interface(m, "org.freedesktop.IBus.InputContext"))
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
path = dbus_message_get_path(m);
if(path == NULL || snprintf(log->path, sizeof log->path, "%s", path)
>= (int)sizeof log->path){
log->invalid = 1;
return DBUS_HANDLER_RESULT_HANDLED;
}
if(dbus_message_has_member(m, "UpdatePreeditText")){
log->npreedit++;
if(!dbus_message_has_signature(m, "vub") ||
path = dbus_message_get_path(m);
if(path == NULL || snprintf(log->preeditpath,
sizeof log->preeditpath, "%s", path) >=
(int)sizeof log->preeditpath ||
!dbus_message_has_signature(m, "vub") ||
!dbus_message_iter_init(m, &it) ||
!ibustext(&it, log->preedit, sizeof log->preedit) ||
!dbus_message_iter_next(&it) ||
@@ -519,7 +572,11 @@ onsignal(DBusConnection *conn, DBusMessage *m, void *arg)
}
if(dbus_message_has_member(m, "CommitText")){
log->ncommit++;
if(!dbus_message_has_signature(m, "v") ||
path = dbus_message_get_path(m);
if(path == NULL || snprintf(log->commitpath,
sizeof log->commitpath, "%s", path) >=
(int)sizeof log->commitpath ||
!dbus_message_has_signature(m, "v") ||
!dbus_message_iter_init(m, &it) ||
!ibustext(&it, log->commit, sizeof log->commit) ||
dbus_message_iter_next(&it))
@@ -834,10 +891,11 @@ preedit(Siglog *log, char *path, char *text, unsigned int cursor, int visible,
char *where)
{
if(log->invalid || log->npreedit != 1 || log->ncommit != 0 ||
strcmp(log->path, path) != 0 || strcmp(log->preedit, text) != 0 ||
strcmp(log->preeditpath, path) != 0 ||
strcmp(log->preedit, text) != 0 ||
log->cursor != cursor || (log->visible != FALSE) != (visible != 0))
return fail("%s preedit: count=%d commit=%d path=%s text=%s cursor=%u visible=%d",
where, log->npreedit, log->ncommit, log->path, log->preedit,
where, log->npreedit, log->ncommit, log->preeditpath, log->preedit,
(unsigned)log->cursor, log->visible != FALSE);
return 1;
}
@@ -846,7 +904,9 @@ static int
committed(Siglog *log, char *path, char *text, char *where)
{
if(log->invalid || log->npreedit != 1 || log->ncommit != 1 ||
strcmp(log->path, path) != 0 || strcmp(log->commit, text) != 0 ||
strcmp(log->preeditpath, path) != 0 ||
strcmp(log->commitpath, path) != 0 ||
strcmp(log->commit, text) != 0 ||
strcmp(log->preedit, "") != 0 || log->cursor != 0 ||
log->visible != FALSE)
return fail("%s signals: preedit=%d commit=%d text=%s/%s",

View File

@@ -228,12 +228,15 @@ startdaemon(Daemon *d, char *program, char *mapdir)
dup2(errpipe[1], STDERR_FILENO) < 0)
_exit(126);
close(errpipe[1]);
setenv("XDG_RUNTIME_DIR", d->runtime, 1);
setenv("XDG_CONFIG_HOME", d->config, 1);
setenv("HOME", d->home, 1);
unsetenv("DISPLAY");
unsetenv("DBUS_SESSION_BUS_ADDRESS");
unsetenv("IBUS_ADDRESS");
if(setenv("XDG_RUNTIME_DIR", d->runtime, 1) < 0 ||
setenv("XDG_CONFIG_HOME", d->config, 1) < 0 ||
setenv("HOME", d->home, 1) < 0 || unsetenv("DISPLAY") < 0 ||
unsetenv("DBUS_SESSION_BUS_ADDRESS") < 0 ||
unsetenv("IBUS_ADDRESS") < 0){
dprintf(STDERR_FILENO, "set daemon environment: %s\n",
strerror(errno));
_exit(126);
}
execl(program, program, mapdir, missing, (char*)0);
dprintf(STDERR_FILENO, "exec %s: %s\n", program, strerror(errno));
_exit(127);
@@ -297,36 +300,82 @@ rmdirknown(char *path)
return fail("rmdir %s: %s", path, strerror(errno));
}
static int
killdaemon(Daemon *d, int *status)
{
int n, ok;
ok = 1;
if(kill(d->pid, SIGKILL) < 0 && errno != ESRCH){
fail("kill -9 %ld: %s", (long)d->pid, strerror(errno));
ok = 0;
}
do
n = waitpid(d->pid, status, 0);
while(n < 0 && errno == EINTR);
if(n != d->pid){
fail("reap %ld after SIGKILL: %s", (long)d->pid,
n < 0 ? strerror(errno) : "wrong child");
ok = 0;
}else
d->pid = -1;
return ok;
}
static int
stopdaemon(Daemon *d)
{
struct pollfd pfd;
int ok, status, n;
int ok, status, n, reaped;
int64_t deadline;
ok = 1;
if(d->pid > 0){
if(kill(d->pid, SIGTERM) < 0 && errno != ESRCH){
do
n = waitpid(d->pid, &status, WNOHANG);
while(n < 0 && errno == EINTR);
if(n == d->pid){
fail("daemon exited before test termination with wait status %#x",
status);
d->pid = -1;
ok = 0;
}else if(n < 0){
fail("check daemon %ld before termination: %s",
(long)d->pid, strerror(errno));
if(errno == ECHILD)
d->pid = -1;
ok = 0;
}
}
if(d->pid > 0){
if(kill(d->pid, SIGTERM) < 0){
fail("kill %ld: %s", (long)d->pid, strerror(errno));
ok = 0;
}
reaped = 0;
deadline = nowms() + Stoptimeout;
for(;;){
n = waitpid(d->pid, &status, WNOHANG);
if(n == d->pid)
if(n == d->pid){
reaped = 1;
d->pid = -1;
break;
if(n < 0 && errno != EINTR){
}
if(n < 0 && errno == EINTR)
continue;
if(n < 0){
fail("waitpid %ld: %s", (long)d->pid, strerror(errno));
ok = 0;
if(errno == ECHILD)
d->pid = -1;
break;
}
n = leftms(deadline);
if(n == 0){
fail("daemon %ld did not stop after SIGTERM", (long)d->pid);
ok = 0;
kill(d->pid, SIGKILL);
while(waitpid(d->pid, &status, 0) < 0 && errno == EINTR)
;
if(!killdaemon(d, &status))
ok = 0;
break;
}
pfd.fd = d->errfd;
@@ -335,14 +384,19 @@ stopdaemon(Daemon *d)
if(poll(&pfd, 1, n) < 0 && errno != EINTR){
fail("poll daemon %ld: %s", (long)d->pid, strerror(errno));
ok = 0;
kill(d->pid, SIGKILL);
while(waitpid(d->pid, &status, 0) < 0 && errno == EINTR)
;
if(!killdaemon(d, &status))
ok = 0;
break;
}
readerrors(d);
}
d->pid = -1;
/* plan9port turns a caught termination note into exit status 1. */
if(reaped &&
!((WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM) ||
(WIFEXITED(status) && WEXITSTATUS(status) == 1))){
fail("daemon exited with unexpected wait status %#x", status);
ok = 0;
}
}
readerrors(d);
if(d->errfd >= 0){