test: probe the dead IBus address in-process

rejectold forked and re-execed the test binary with --reject-address
just to call dbus_connection_open_private once against the crashed
daemon's abstract address, then waited on the helper, drained its
stderr pipe and checked its exit status. An abstract socket with no
listener refuses the connection immediately, so the call cannot block
and needs no separate process.

Open the address in-process and require a null connection with the
D-Bus error set, freeing the error afterwards. This drops the helper
process, its stderr capture, the Test.helper fields and the argv
dispatch in main. killdaemon stays; the daemons still need it.
This commit is contained in:
2026-08-16 16:43:26 +09:00
parent 7220286ab5
commit dde9fb431b

View File

@@ -1,7 +1,5 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h> #include <poll.h>
#include <signal.h> #include <signal.h>
#include <stdint.h> #include <stdint.h>
@@ -29,7 +27,6 @@ struct Test
Live l; Live l;
Daemon first; Daemon first;
Daemon second; Daemon second;
Daemon helper;
pid_t firstpid; pid_t firstpid;
int ipcfirst; int ipcfirst;
int ipcnew; int ipcnew;
@@ -48,7 +45,6 @@ setup(Test *t)
memset(t, 0, sizeof *t); memset(t, 0, sizeof *t);
daemoninit(&t->first, "first daemon"); daemoninit(&t->first, "first daemon");
daemoninit(&t->second, "second daemon"); daemoninit(&t->second, "second daemon");
daemoninit(&t->helper, "old-address helper");
t->firstpid = -1; t->firstpid = -1;
t->ipcfirst = -1; t->ipcfirst = -1;
t->ipcnew = -1; t->ipcnew = -1;
@@ -245,114 +241,25 @@ checkstale(Test *t)
return 1; return 1;
} }
/* The dead daemon's abstract socket has no listener, so this must fail. */
static int static int
helpermain(char *address) rejectold(Test *t, char *where)
{ {
DBusConnection *conn; DBusConnection *conn;
DBusError err; DBusError err;
int ok;
dbus_error_init(&err); dbus_error_init(&err);
conn = dbus_connection_open_private(address, &err); conn = dbus_connection_open_private(t->firstaddress, &err);
if(conn != NULL){ if(conn != NULL){
dbus_connection_set_exit_on_disconnect(conn, FALSE); dbus_connection_set_exit_on_disconnect(conn, FALSE);
closebus(&conn); closebus(&conn);
dbus_error_free(&err); ok = fail("old private address accepted a connection %s", where);
fprintf(stderr, "old private address unexpectedly accepted a connection\n"); }else
return 1; ok = dbus_error_is_set(&err) ||
} fail("old private address failed without a D-Bus error %s", where);
if(!dbus_error_is_set(&err)){
fprintf(stderr, "old private address failed without a D-Bus error\n");
dbus_error_free(&err);
return 2;
}
dbus_error_free(&err); dbus_error_free(&err);
return 0; return ok;
}
static int
rejectold(Test *t, char *self, char *where)
{
struct pollfd pfd;
int errpipe[2], fd, n, status, timeout;
long maxfd;
pid_t pid;
int64_t deadline;
if(pipe2(errpipe, O_CLOEXEC|O_NONBLOCK) < 0)
return fail("pipe old-address helper: %s", strerror(errno));
pid = fork();
if(pid < 0){
close(errpipe[0]);
close(errpipe[1]);
return fail("fork old-address helper: %s", strerror(errno));
}
if(pid == 0){
close(errpipe[0]);
if(dup2(errpipe[1], STDERR_FILENO) < 0)
_exit(126);
close(errpipe[1]);
if(close_range(3, UINT_MAX, 0) < 0){
maxfd = sysconf(_SC_OPEN_MAX);
if(maxfd < 0)
maxfd = 1024;
for(fd = 3; fd < maxfd; fd++)
close(fd);
}
execl(self, self, "--reject-address", t->firstaddress, (char*)0);
dprintf(STDERR_FILENO, "exec %s: %s\n", self, strerror(errno));
_exit(127);
}
close(errpipe[1]);
t->helper.pid = pid;
t->helper.errfd = errpipe[0];
deadline = nowms() + Calltimeout;
for(;;){
do
n = waitpid(pid, &status, WNOHANG);
while(n < 0 && errno == EINTR);
if(n == pid){
t->helper.pid = -1;
break;
}
if(n < 0){
fail("wait old-address helper %ld: %s", (long)pid, strerror(errno));
if(errno == ECHILD)
t->helper.pid = -1;
else
killdaemon(&t->helper, NULL);
return 0;
}
timeout = leftms(deadline);
if(timeout == 0){
fail("old private address open did not fail promptly %s", where);
killdaemon(&t->helper, NULL);
return 0;
}
pfd.fd = t->helper.errfd;
pfd.events = POLLIN|POLLHUP;
pfd.revents = 0;
n = poll(&pfd, 1, timeout);
if(n < 0 && errno == EINTR)
continue;
if(n < 0){
fail("poll old-address helper: %s", strerror(errno));
killdaemon(&t->helper, NULL);
return 0;
}
if(pfd.revents & (POLLERR|POLLNVAL)){
fail("old-address helper pipe became unusable: %#x", pfd.revents);
killdaemon(&t->helper, NULL);
return 0;
}
if(pfd.revents & (POLLIN|POLLHUP))
readerrors(&t->helper);
}
if(!closeerrors(&t->helper))
return 0;
if(!WIFEXITED(status) || WEXITSTATUS(status) != 0)
return fail("old private address helper failed %s with status %#x",
where, status);
return 1;
} }
static int static int
@@ -400,8 +307,6 @@ cleanup(Test *t)
ok = fail("close persistent IPC client: %s", strerror(errno)); ok = fail("close persistent IPC client: %s", strerror(errno));
t->ipcfirst = -1; t->ipcfirst = -1;
} }
if(!killdaemon(&t->helper, NULL))
ok = 0;
if(!killdaemon(&t->first, NULL)) if(!killdaemon(&t->first, NULL))
ok = 0; ok = 0;
if(!stopdaemon(&t->second)) if(!stopdaemon(&t->second))
@@ -410,15 +315,13 @@ cleanup(Test *t)
ok = 0; ok = 0;
if(!closeerrors(&t->second)) if(!closeerrors(&t->second))
ok = 0; ok = 0;
if(!closeerrors(&t->helper))
ok = 0;
if(!liveclean(&t->l)) if(!liveclean(&t->l))
ok = 0; ok = 0;
return ok; return ok;
} }
static int static int
runrestart(Test *t, char *self, char *program, char *mapdir) runrestart(Test *t, char *program, char *mapdir)
{ {
if(!startdaemon(&t->l, &t->first, program, mapdir)) if(!startdaemon(&t->l, &t->first, program, mapdir))
return 0; return 0;
@@ -427,7 +330,7 @@ runrestart(Test *t, char *self, char *program, char *mapdir)
!privateaddress(t->firstaddress, t->firstpid) || !openpersistent(t)) !privateaddress(t->firstaddress, t->firstpid) || !openpersistent(t))
return 0; return 0;
if(!hardcrash(t) || !waitipcclosed(t) || !waitbusclosed(t) || if(!hardcrash(t) || !waitipcclosed(t) || !waitbusclosed(t) ||
!checkstale(t) || !rejectold(t, self, "after hard crash")) !checkstale(t) || !rejectold(t, "after hard crash"))
return 0; return 0;
closebus(&t->busfirst); closebus(&t->busfirst);
if(close(t->ipcfirst) < 0) if(close(t->ipcfirst) < 0)
@@ -450,21 +353,18 @@ main(int argc, char **argv)
int ok; int ok;
testname = "daemon_restart_test"; testname = "daemon_restart_test";
if(argc == 3 && strcmp(argv[1], "--reject-address") == 0)
return helpermain(argv[2]);
if(argc != 3){ if(argc != 3){
fprintf(stderr, "usage: daemon_restart_test strans mapdir\n"); fprintf(stderr, "usage: daemon_restart_test strans mapdir\n");
return 2; return 2;
} }
ok = setup(&test); ok = setup(&test);
if(ok) if(ok)
ok = runrestart(&test, argv[0], argv[1], argv[2]); ok = runrestart(&test, argv[1], argv[2]);
if(!cleanup(&test)) if(!cleanup(&test))
ok = 0; ok = 0;
if(!ok){ if(!ok){
showerrors(&test.first); showerrors(&test.first);
showerrors(&test.second); showerrors(&test.second);
showerrors(&test.helper);
return 1; return 1;
} }
printf("daemon hard-crash endpoint recovery: ok\n"); printf("daemon hard-crash endpoint recovery: ok\n");