/* systemd socket activation * * compile with: * gcc -shared -Wl,-soname,sockethack.so -o sockethack.so sockethack.c -ldl `pkg-config --cflags --libs libsystemd` -fPIC * usage: env LD_PRELOAD=libdivert.so LD_LIBRARY_PATH=. /path/to/program */ #include #include #include #include #include #include #include #include #define SD_LISTEN_FDS_START 3 pthread_mutex_t sockethack = PTHREAD_MUTEX_INITIALIZER; int systemd_fds; char** systemd_fds_names; extern int h_errno; typedef struct { int domain; int type; int protocol; int index; } mapping_t; mapping_t* mappings; __attribute__((destructor)) void hackdown () { } __attribute__((constructor)) void hackup1 () { pthread_mutex_lock(&sockethack); systemd_fds = sd_listen_fds_with_names(0, &systemd_fds_names); if (0) { fprintf(stderr, "Have %d systemd descriptors and want %ld memory for it\n", systemd_fds, sizeof(mapping_t)); } mappings = malloc(systemd_fds * sizeof(mapping_t)); for (int i=0; i < systemd_fds; i++) { if (mappings && sscanf(systemd_fds_names[i],"socket(%d,%d,%d)=%d\n", &mappings[i].domain, &mappings[i].type, &mappings[i].protocol, &mappings[i].index) == 4) { if (0) { fprintf(stderr, "match and stored selector %d_%d_%d_%d\n", mappings[i].domain, mappings[i].type, mappings[i].protocol, mappings[i].index); } } free(systemd_fds_names[i]); } free(systemd_fds_names); pthread_mutex_unlock(&sockethack); } __attribute__((constructor)) void gcc_wants_this_to_have_a_name() { } int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int (*real_bind)(int, const struct sockaddr*, socklen_t); int result; real_bind = dlsym(RTLD_NEXT, "bind"); if (sockfd > 2 + systemd_fds) { result = (*real_bind)(sockfd, addr, addrlen); return result; } fprintf(stderr, "throwing away bind() call for %d\n", sockfd); return 0; } /** * intercept calls to make sockets * if it is the one that service uses to listen, * pass in the already opened fd */ int socket(int domain, int type, int protocol) { int (*real_socket)(int, int, int); int sockfd, i; real_socket = dlsym(RTLD_NEXT, "socket"); if (mappings) { for (i=0; i < systemd_fds; i++) { if ( mappings[i].domain == domain && mappings[i].type == type && mappings[i].protocol == protocol ) { if (mappings[i].index == 0) { sockfd = 3+i; real_socket = NULL; } mappings[i].index--; } } for (i=0; i < systemd_fds; i++) { if (mappings[i].index > -1) { break; } } if (i == systemd_fds) { fprintf(stderr, "Every fd has been allocated \360\237\230\270\n"); free(mappings); mappings = NULL; } } if (real_socket) { sockfd = (*real_socket)(domain, type, protocol); } if (1) { fprintf(stderr, "App requested socket %d %d %d and got fd %d\n", domain, type, protocol, sockfd); } fflush(stdout); return sockfd; } /** * prevent the service shutting down the descriptors obtained off systemd, * for now presuming they are listening sockets. * Would exempt FDSTORE=1 descriptors from this */ int shutdown(int sockfd, int how) { int (*real_shutdown)(int, int); real_shutdown = dlsym(RTLD_NEXT, "shutdown"); if (sockfd <= 2 + systemd_fds) { return 0; } return (*real_shutdown)(sockfd, how); } int unlink(const char *pathname) { int (*real_unlink)(const char *); real_unlink = dlsym(RTLD_NEXT, "unlink"); if (!strcmp(pathname,"/var/run/slapd/ldapi")) { return 0; } return (*real_unlink)(pathname); }