#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "live.h" #include "livebus.h" typedef struct Test Test; struct Test { Live l; Daemon first; Daemon second; int ipcfirst; int ipcnew; DBusConnection *busfirst; DBusConnection *busnew; char address[512]; }; static int setup(Test *t) { memset(t, 0, sizeof *t); daemoninit(&t->first, "first daemon"); daemoninit(&t->second, "second daemon"); t->ipcfirst = -1; t->ipcnew = -1; return livesetup(&t->l, "collision"); } static int addressunchanged(Test *t) { char contents[2048], address[512]; size_t ncontents; pid_t declared; if(!readfile(t->l.addrfile, contents, sizeof contents, &ncontents) || !parseaddress(contents, ncontents, address, sizeof address, &declared)) return 0; if(declared != t->first.pid) return fail("IBus address PID %ld, expected %ld", (long)declared, (long)t->first.pid); return strcmp(address, t->address) == 0 || fail("first private IBus address changed after collision"); } static int waitsecond(Test *t) { struct pollfd pfd; int n, status, timeout; int64_t deadline; deadline = nowms() + Starttimeout; for(;;){ do n = waitpid(t->second.pid, &status, WNOHANG); while(n < 0 && errno == EINTR); if(n == t->second.pid){ t->second.pid = -1; readerrors(&t->second); if(!WIFEXITED(status) || WEXITSTATUS(status) == 0) return fail("second daemon did not exit unsuccessfully: %#x", status); if(strstr(t->second.err, "IPC endpoint is already in use") == NULL) return fail("second daemon did not report the IPC collision"); return 1; } if(n < 0){ fail("waitpid second daemon %ld: %s", (long)t->second.pid, strerror(errno)); if(errno == ECHILD) t->second.pid = -1; return 0; } if(!daemonalive(&t->first)) return 0; timeout = leftms(deadline); if(timeout == 0){ fail("second daemon did not exit after the endpoint collision"); killdaemon(&t->second, NULL); return 0; } pfd.fd = t->second.errfd; pfd.events = POLLIN|POLLHUP; pfd.revents = 0; n = poll(&pfd, 1, timeout > 20 ? 20 : timeout); if(n < 0 && errno == EINTR) continue; if(n < 0) return fail("poll second daemon: %s", strerror(errno)); if(pfd.revents != 0) readerrors(&t->second); } } static int runcollision(Test *t, char *program, char *mapdir) { if(!startdaemon(&t->l, &t->first, program, mapdir) || !waitready(&t->l, &t->first, t->address, sizeof t->address)) return 0; t->ipcfirst = connectsocket(t->l.socket, nowms() + Calltimeout); if(t->ipcfirst < 0) return fail("connect persistent IPC client: %s", strerror(errno)); if(!ipcprobe(t->ipcfirst, "persistent pre-collision")) return 0; t->busfirst = openbus(t->address, "persistent pre-collision"); if(t->busfirst == NULL || !hello(t->busfirst, NULL, 0, "persistent pre-collision Hello")) return 0; if(!startdaemon(&t->l, &t->second, program, mapdir) || !waitsecond(t) || !daemonalive(&t->first) || !addressunchanged(t)) return 0; if(!ipcprobe(t->ipcfirst, "persistent post-collision")) return 0; if(!createcontext(t->busfirst, NULL, 0, "persistent post-collision context")) return 0; t->ipcnew = connectsocket(t->l.socket, nowms() + Calltimeout); if(t->ipcnew < 0) return fail("connect new IPC client after collision: %s", strerror(errno)); if(!ipcprobe(t->ipcnew, "new post-collision")) return 0; t->busnew = openbus(t->address, "new post-collision"); if(t->busnew == NULL || !hello(t->busnew, NULL, 0, "new post-collision Hello") || !createcontext(t->busnew, NULL, 0, "new post-collision context")) return 0; return daemonalive(&t->first); } static int cleanup(Test *t) { int n, ok, status; ok = 1; closebus(&t->busnew); closebus(&t->busfirst); if(t->ipcnew >= 0){ if(close(t->ipcnew) < 0) ok = fail("close new IPC client: %s", strerror(errno)); t->ipcnew = -1; } if(t->ipcfirst >= 0){ if(close(t->ipcfirst) < 0) ok = fail("close persistent IPC client: %s", strerror(errno)); t->ipcfirst = -1; } if(t->second.pid > 0){ do n = waitpid(t->second.pid, &status, WNOHANG); while(n < 0 && errno == EINTR); if(n == t->second.pid) t->second.pid = -1; else if(n == 0){ ok = fail("second daemon still running during cleanup"); if(!killdaemon(&t->second, NULL)) ok = 0; }else{ fail("check second daemon during cleanup: %s", strerror(errno)); ok = 0; if(errno == ECHILD) t->second.pid = -1; else if(!killdaemon(&t->second, NULL)) ok = 0; } } if(!stopdaemon(&t->first)) ok = 0; if(!closeerrors(&t->first)) ok = 0; if(!closeerrors(&t->second)) ok = 0; if(!liveclean(&t->l)) ok = 0; return ok; } int main(int argc, char **argv) { Test test; int ok; testname = "daemon_collision_test"; if(argc != 3){ fprintf(stderr, "usage: daemon_collision_test strans mapdir\n"); return 2; } ok = setup(&test); if(ok) ok = runcollision(&test, argv[1], argv[2]); if(!cleanup(&test)) ok = 0; if(!ok){ showerrors(&test.first); showerrors(&test.second); return 1; } printf("daemon endpoint collision ownership: ok\n"); return 0; }