#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include static void pause_tick(void) { struct timespec delay = {0, 100000000}; nanosleep(&delay, NULL); } int main(void) { const char *forbidden[] = {"/bin/sh", "/bin/bash", "/bin/busybox", "/sbin/apk", NULL}; for (int i = 0; forbidden[i]; ++i) { if (access(forbidden[i], F_OK) == 0) { fprintf(stderr, "Unexpected runtime tool: %s\n", forbidden[i]); return 1; } } const char *config = "/tmp/rtorrent-smoke.rc"; const char *lock = "/home/nfs_download/rsession/rtorrent.lock"; FILE *f = fopen(config, "w"); if (!f) return 1; fputs("system.daemon.set = true\nnetwork.bind_address.set = 0.0.0.0\n" "network.scgi.open_port = 127.0.0.1:5001\n", f); fclose(f); f = fopen(lock, "w"); if (!f) return 1; fputs("stale smoke-test lock", f); fclose(f); pid_t child = fork(); if (child < 0) return 1; if (child == 0) { execl("/usr/local/bin/rtorrent-entrypoint", "rtorrent-entrypoint", "-n", "-o", "import=/tmp/rtorrent-smoke.rc", (char *)NULL); _exit(127); } int result = 1, fd = -1, status; for (int attempt = 0; attempt < 300; ++attempt) { if (waitpid(child, &status, WNOHANG) == child) { fprintf(stderr, "rTorrent exited before SCGI became ready\n"); child = -1; goto cleanup; } fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) goto cleanup; struct sockaddr_in addr = {0}; addr.sin_family = AF_INET; addr.sin_port = htons(5001); if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) != 1) goto cleanup; if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) break; close(fd); fd = -1; pause_tick(); } if (fd < 0 || access(lock, F_OK) == 0) goto cleanup; struct timeval timeout = {5, 0}; setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); const char *body = "system.client_version"; char headers[128], request[512], response[8192]; int hlen = snprintf(headers, sizeof(headers), "CONTENT_LENGTH%c%zu%cSCGI%c1%c", 0, strlen(body), 0, 0, 0); int prefix = snprintf(request, sizeof(request), "%d:", hlen); memcpy(request + prefix, headers, (size_t)hlen); size_t length = (size_t)prefix + (size_t)hlen; request[length++] = ','; memcpy(request + length, body, strlen(body)); length += strlen(body); if (send(fd, request, length, 0) != (ssize_t)length) goto cleanup; size_t used = 0; ssize_t n; while (used < sizeof(response) - 1 && (n = recv(fd, response + used, sizeof(response) - 1 - used, 0)) > 0) used += (size_t)n; response[used] = '\0'; if (!strstr(response, "") || strstr(response, "")) { fprintf(stderr, "SCGI test failed: %s\n", response); goto cleanup; } pid_t health = fork(); if (health < 0) goto cleanup; if (health == 0) { execl("/usr/local/bin/rtorrent-entrypoint", "rtorrent-entrypoint", "--healthcheck", (char *)NULL); _exit(127); } if (waitpid(health, &status, 0) < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) goto cleanup; puts("Distroless rTorrent: startup, stale lock cleanup, SCGI and healthcheck passed"); result = 0; cleanup: if (fd >= 0) close(fd); if (child > 0) { kill(child, SIGTERM); for (int i = 0; i < 100; ++i) { if (waitpid(child, &status, WNOHANG) == child) { child = -1; break; } pause_tick(); } if (child > 0) { kill(child, SIGKILL); waitpid(child, &status, 0); } } unlink(config); unlink(lock); return result; }