Snuggledash

awkwrapper.c

Sep 3rd, 2026 (edited)
16,490
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 2.35 KB | Source Code | 0 0
  1. /* awkwrapper: minimal accept-and-fork loop for smtpd.awk when it's not
  2.  * run directly through systemd (with StandardInput=socket & Accept=true) or
  3.  * inetd, but using socket activation
  4.  */
  5.  
  6. // only really needed for strdupa()
  7. #define _GNU_SOURCE
  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include <errno.h>
  11. #include <signal.h>
  12. #include <sys/types.h>
  13. #include <sys/socket.h>
  14. #include <sys/uio.h>
  15. #include <unistd.h>
  16. #include <fcntl.h>
  17.  
  18. #define LISTEN_FD 3
  19. static const char SCRIPT_NAME[] = "/usr/local/bin/smtpd.awk";
  20.  
  21. static int punt(const char *) __attribute__ ((noreturn));
  22.  
  23. static int punt(const char *msg) {
  24.     struct iovec vec[] = {
  25.         // surely writev doesn't write to the segment
  26.         { (char *) msg, strlen(msg) },
  27.         { "\n", 1 }
  28.     };
  29.     writev(2, vec, 2);
  30.     exit(1);
  31. }
  32.  
  33. static void setup_socket_activation() {
  34.     char buf[20];
  35.     char *expected;
  36.     pid_t how_long_is_this = getpid();
  37.     if (!getenv("LISTEN_FDS")) punt("No socket activation");
  38.     if ((expected = getenv("LISTEN_PID")) != NULL) {
  39.         buf[19] = 0;
  40.         int i;
  41.         for (i = 18; i >= 0; --i) {
  42.             buf[i] = '0' + how_long_is_this % 10;
  43.             how_long_is_this /= 10;
  44.             if (!how_long_is_this) break;
  45.         }
  46.         if (strcmp(expected, &buf[i]) != 0) punt("Wrong process");
  47.     } else {
  48.         punt("No socket activation");
  49.     }
  50.     unsetenv("LISTEN_FDS");
  51.     unsetenv("LISTEN_FDNAMES");
  52.     unsetenv("LISTEN_PID");
  53.     unsetenv("LISTEN_PIDFDID");
  54. }
  55.  
  56. static int fork_connection(int fd) {
  57.     switch (fork()) {
  58.     case -1:
  59.         return -1;
  60.     case 0:
  61.         if ((dup2(fd, 0) == -1) || (dup2(fd, 1) == -1)) punt("child: dup2 failed");
  62.         close(fd);
  63.         char *argv[] = { strdupa(SCRIPT_NAME), (char *) NULL };
  64.         execv(SCRIPT_NAME, argv);
  65.         punt("child: execv failed");
  66.     default:
  67.         close(fd);
  68.         return 0;
  69.     }
  70. }
  71.  
  72. int main() {
  73.     struct sigaction ignore_child = { .sa_handler = SIG_IGN, .sa_flags = SA_NOCLDSTOP };
  74.     setup_socket_activation();
  75.     if (sigaction(SIGCHLD, &ignore_child, NULL) == -1) punt("sigaction failed");
  76.     if (fcntl(LISTEN_FD, F_SETFD, FD_CLOEXEC) == -1) punt("close-on-exec failed, probably not a socket");
  77.     while (1) {
  78.         int fd = accept(LISTEN_FD, NULL, NULL);
  79.         if (fd == -1) {
  80.             if (errno != EINTR) punt("accept failed");
  81.             else continue;
  82.         }
  83.         if (fork_connection(fd) == -1) {
  84.             punt("fork failed, better exit before something terrible happens");
  85.         }
  86.     }
  87.     // just in case we somehow break out of the loop
  88.     return 0;
  89. }
Advertisement
Add Comment
Please, Sign In to add comment