Index: wgsync/src/def.h ================================================================== --- wgsync/src/def.h +++ wgsync/src/def.h @@ -5,10 +5,12 @@ /* seteuid * getnameinfo */ #define _GNU_SOURCE /* IFNAMSIZ [linux] * getresuid [linux]*/ +#define _DEFAULT_SOURCE + /* daemon [glibc, BSD] */ /* arch headers */ #include #include Index: wgsync/src/wgsync.c ================================================================== --- wgsync/src/wgsync.c +++ wgsync/src/wgsync.c @@ -9,10 +9,16 @@ /* posix */ #include #include #include #include +#include + +#if __linux__ +# include +# include +#endif /* libs */ #include #include "wglist.h" /* wireguard uses messy linked lists but doesn't @@ -342,10 +348,86 @@ _fatalf("could not set wg device (error %i)", -e); } PQclear(rows); } + +void daemonmain(PGconn* db, const char* wgdev) { + PGresult* subscribe = PQexec(db, + "listen sync_vpn;" + "listen sync_priv;"); + if (PQresultStatus(subscribe) != PGRES_COMMAND_OK) + _warn("could not subscribe to DB notification channels"); + PQclear(subscribe); + + int pqfd = PQsocket(db); +#if __linux__ + sigset_t sigs; + sigemptyset(&sigs); + sigaddset(&sigs, SIGHUP); + sigaddset(&sigs, SIGTERM); + sigaddset(&sigs, SIGINT); + sigprocmask(SIG_BLOCK, &sigs, null); + int sigfd = signalfd(-1, &sigs, SFD_CLOEXEC); +#endif + + struct pollfd polls[] = { + { .fd = pqfd, .events = POLLIN, .revents = 0 }, +#if __linux__ + { .fd = sigfd, .events = POLLIN, .revents = 0 }, +#endif + }; + + for (;;) { + int p = poll(polls, _sz(polls), -1); + if (p > 0) { + bool didSync = false; + switch (polls[0].revents) { + case 0: break; + case POLLHUP: + _fatal("lost DB connection; terminating"); + case POLLIN: { + PQconsumeInput(db); + for (;;) { + PGnotify* n = PQnotifies(db); + if(n == null) break; + if(strcmp(n->relname, "sync_vpn") == 0 + || strcmp(n->relname, "sync_priv") == 0) { + if(!didSync) { + syncauth(db, wgdev); + didSync = true; + } + } + } + } + } +#if __linux__ + switch (polls[1].revents) { + case 0: break; + case POLLIN: { + struct signalfd_siginfo si; + read(sigfd, &si, sizeof si); + + if(si.ssi_signo == SIGHUP && !didSync) { + syncauth(db, wgdev); + didSync = true; + } else if (si.ssi_signo == SIGTERM || si.ssi_signo == SIGINT) { + goto poll_end; + } + }; + } +#endif + } + } + + poll_end :; + + _info("shutting down"); +#if __linux__ + close(sigfd); +#endif +} int main(int argc, char** argv) { setvbuf(stderr, null, _IONBF, 0); if (argc < 3) { _fatal("missing device name"); @@ -388,19 +470,31 @@ getresuid(null, null, &svuid); if (svuid == 0) setuid(0);} if(strcmp(arg_mode, "sync") == 0) { syncauth(db, arg_devname); - } else if(strcmp(arg_mode, "wait") == 0) { - /* foreground daemon */ - } else if(strcmp(arg_mode, "fork") == 0) { - /* background daemon */ + } else if(strcmp(arg_mode, "wait") == 0 || + strcmp(arg_mode, "syncwait") == 0 || + strcmp(arg_mode, "fork") == 0 || + strcmp(arg_mode, "syncfork") == 0) { + + if(strncmp(arg_mode, "sync", 4) == 0) + syncauth(db, arg_devname); + + /* maybe background daemon */ + if(strcmp(arg_mode, "fork") == 0 || + strcmp(arg_mode, "syncfork") == 0) { + if (daemon(1,1) == -1) + _fatal("cannot daemonize"); + } + + daemonmain(db, arg_devname); } else { - _fatal("valid modes are sync, wait, and fork"); + _fatal("valid modes are sync, wait, syncwait, fork, and syncfork"); } /* other possibilities: a mode that generates an eventfd * and provides it on fd4 to a subordinate process, or * sends it with SCM_RIGHTS */ PQfinish(db); return 0; }