/* awkwrapper: minimal accept-and-fork loop for smtpd.awk when it's not * run directly through systemd (with StandardInput=socket & Accept=true) or * inetd, but using socket activation */ // only really needed for strdupa() #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #define LISTEN_FD 3 static const char SCRIPT_NAME[] = "/usr/local/bin/smtpd.awk"; static int punt(const char *) __attribute__ ((noreturn)); static int punt(const char *msg) { struct iovec vec[] = { // surely writev doesn't write to the segment { (char *) msg, strlen(msg) }, { "\n", 1 } }; writev(2, vec, 2); exit(1); } static void setup_socket_activation() { char buf[20]; char *expected; pid_t how_long_is_this = getpid(); if (!getenv("LISTEN_FDS")) punt("No socket activation"); if ((expected = getenv("LISTEN_PID")) != NULL) { buf[19] = 0; int i; for (i = 18; i >= 0; --i) { buf[i] = '0' + how_long_is_this % 10; how_long_is_this /= 10; if (!how_long_is_this) break; } if (strcmp(expected, &buf[i]) != 0) punt("Wrong process"); } else { punt("No socket activation"); } unsetenv("LISTEN_FDS"); unsetenv("LISTEN_FDNAMES"); unsetenv("LISTEN_PID"); unsetenv("LISTEN_PIDFDID"); } static int fork_connection(int fd) { switch (fork()) { case -1: return -1; case 0: if ((dup2(fd, 0) == -1) || (dup2(fd, 1) == -1)) punt("child: dup2 failed"); close(fd); char *argv[] = { strdupa(SCRIPT_NAME), (char *) NULL }; execv(SCRIPT_NAME, argv); punt("child: execv failed"); default: close(fd); return 0; } } int main() { struct sigaction ignore_child = { .sa_handler = SIG_IGN, .sa_flags = SA_NOCLDSTOP }; setup_socket_activation(); if (sigaction(SIGCHLD, &ignore_child, NULL) == -1) punt("sigaction failed"); if (fcntl(LISTEN_FD, F_SETFD, FD_CLOEXEC) == -1) punt("close-on-exec failed, probably not a socket"); while (1) { int fd = accept(LISTEN_FD, NULL, NULL); if (fd == -1) { if (errno != EINTR) punt("accept failed"); else continue; } if (fork_connection(fd) == -1) { punt("fork failed, better exit before something terrible happens"); } } // just in case we somehow break out of the loop return 0; }