/* Copyright (C) 2015 Luke Shumaker * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define _(s) s const char *xgetenv(const char *name, const char *unset) { const char *val = getenv(name); if (!val) val = unset; return val; } char *xasprintf(const char *format, ...) { va_list arg; int len; char *str = NULL; va_start(arg, format); len = vasprintf(&str, format, arg); va_end(arg); if (len < 0) error(EXIT_FAILURE, errno, _("Could not allocate memory in vasprintf")); return str; } #define xasprintfa(...) (__extension__ ({ char *heap = xasprintf(__VA_ARGS__); char *stack = strdupa(heap); free(heap); stack; })) int pid = -1; void sigchld_handler(int sig __attribute__((__unused__))) { int status; pid = waitpid(pid, &status, WNOHANG); int exited = WEXITSTATUS(status); error(exited, 0, "%ld exited with status %d", (long)pid, exited); exit(0); } union addr { struct sockaddr gen; struct sockaddr_un un; }; int new_unix_sock(const char *filename, int type) { union addr addr; addr.un.sun_family = AF_UNIX; strcpy(addr.un.sun_path, filename); int sock = socket(AF_UNIX, type, 0); if (sock < 0) error(EXIT_FAILURE, errno, "socket(%d, %d)", AF_UNIX, type); unlink(filename); if (bind(sock, &addr.gen, sizeof(addr))) error(EXIT_FAILURE, errno, "bind(%d, sockaddr(\"%s\"))", sock, filename); switch (type) { case SOCK_STREAM: case SOCK_SEQPACKET: if (listen(sock, 5)) error(EXIT_FAILURE, errno, "listen(%d /* \"%s\" */, %d)", sock, filename, 5); break; case SOCK_DGRAM: break; default: error(EXIT_FAILURE, errno, "new_unix_sock: Unrecognized type: %d", type); } return sock; } char *cmdname = "nshd_runner"; const char *notify_sockname = "notify.sock"; const char *nslcd_sockname = "nslcd.sock"; void cleanup(void) { if (nslcd_sockname) unlink(nslcd_sockname); if (notify_sockname) unlink(notify_sockname); fprintf(stderr, "%s: Exiting\n", cmdname); } int main(int argc, char *argv[]) { cmdname = argv[0]; if (argc != 2) { error(2, 0, _("Usage: %s NSHD_PROGRAM"), argv[0]); } atexit(&cleanup); int nslcd_sock = new_unix_sock(nslcd_sockname , SOCK_STREAM); int notify_sock = new_unix_sock(notify_sockname, SOCK_DGRAM ); struct sigaction act; sigemptyset(&act.sa_mask); act.sa_flags = SA_RESTART; act.sa_handler = sigchld_handler; if (sigaction(SIGCHLD, &act, 0)) error(EXIT_FAILURE, errno, "sigaction"); pid = fork(); switch (pid) { case -1: error(EXIT_FAILURE, errno, "fork"); case 0: close(notify_sock); dup2(nslcd_sock, 3); if (nslcd_sock != 3) close(nslcd_sock); pid = getpid(); setenv("LISTEN_PID", xasprintfa("%ld", (long)pid), 1); setenv("LISTEN_FDS", "1", 1); setenv("NOTIFY_SOCKET", notify_sockname, 1); execl(argv[1], argv[1], NULL); error(EXIT_FAILURE, errno, "execl"); } while (1) { union addr client_addr; socklen_t client_size; char buf[4097]; ssize_t bytes_read = recvfrom(notify_sock, buf, sizeof(buf)-1, 0, &client_addr.gen, &client_size); if (bytes_read < 1) error(EXIT_FAILURE, errno, "recvfrom"); if (buf[bytes_read-1] != '\n') { buf[bytes_read] = '\n'; bytes_read++; } ssize_t bytes_written = 0; while (bytes_written < bytes_read) { ssize_t n = write(2, &(buf[bytes_written]), bytes_read-bytes_written); if (n < 0) { bytes_written = -1; break; } bytes_written += n; } if (bytes_written < 0) error(EXIT_FAILURE, errno, "write"); } error(EXIT_FAILURE, 0, "not reached"); return EXIT_FAILURE; }