From 631a3056f48bcd4d87adf6e720bfc41667bee498 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Mon, 17 Feb 2025 12:46:10 +0800 Subject: [PATCH] git_hooks_client.c: Splice socket to stderr This allows the daemon to send status messages to the hook handler. --- git_hooks_client/git_hooks_client.c | 57 +++++++++++++++++++++++++++++++++++++++-------------- diff --git a/git_hooks_client/git_hooks_client.c b/git_hooks_client/git_hooks_client.c index 98269ad2d5d904207f98b2c87226dbaefc0ab2a9..4d62c910beee8beeb8fc5f609e5889f47afc539d 100644 --- a/git_hooks_client/git_hooks_client.c +++ b/git_hooks_client/git_hooks_client.c @@ -10,36 +10,54 @@ int main(void) { const char *socket_path = getenv("LINDENII_FORGE_HOOKS_SOCKET_PATH"); if (socket_path == NULL) { - dprintf(STDERR_FILENO, "environment variable LINDENII_FORGE_HOOKS_SOCKET_PATH undefined\n"); + dprintf(STDERR_FILENO, "environment variable LINDENII_FORGE_HOOKS_SOCKET_PATH undefined\n"); return EXIT_FAILURE; } - /* - * All hooks in git (see builtin/receive-pack.c) use a pipe by setting - * .in = -1 on the child_process struct, which enables us to use - * splice(2) to move the data to the UNIX domain socket. Just to be + /* + * All hooks in git (see builtin/receive-pack.c) use a pipe by setting + * .in = -1 on the child_process struct, which enables us to use + * splice(2) to move the data to the UNIX domain socket. Just to be * safe, we check that stdin is a pipe; and additionally we fetch the * buffer size of the pipe to use as the maximum size for the splice. * * We connect to the UNIX domain socket after ensuring that standard * input matches our expectations. - */ + */ - struct stat stdin_stat; + struct stat stdin_stat; if (fstat(STDIN_FILENO, &stdin_stat) == -1) { perror("fstat on stdin"); return EXIT_FAILURE; } if (!S_ISFIFO(stdin_stat.st_mode)) { - dprintf(STDERR_FILENO, "stdin must be a pipe\n"); - return EXIT_FAILURE; + dprintf(STDERR_FILENO, "stdin must be a pipe\n"); + return EXIT_FAILURE; } - int pipe_size = fcntl(STDIN_FILENO, F_GETPIPE_SZ); - if (pipe_size == -1) { + int stdin_pipe_size = fcntl(STDIN_FILENO, F_GETPIPE_SZ); + if (stdin_pipe_size == -1) { perror("fcntl on stdin"); return EXIT_FAILURE; } + /* ... And we do the same for stderr */ + + struct stat stderr_stat; + if (fstat(STDERR_FILENO, &stderr_stat) == -1) { + perror("fstat on stderr"); + return EXIT_FAILURE; + } + if (!S_ISFIFO(stderr_stat.st_mode)) { + dprintf(STDERR_FILENO, "stderr must be a pipe\n"); + return EXIT_FAILURE; + } + int stderr_pipe_size = fcntl(STDERR_FILENO, F_GETPIPE_SZ); + if (stderr_pipe_size == -1) { + perror("fcntl on stderr"); + return EXIT_FAILURE; + } + + int sock; struct sockaddr_un addr; sock = socket(AF_UNIX, SOCK_STREAM, 0); @@ -57,12 +75,11 @@ close(sock); return EXIT_FAILURE; } - - ssize_t bytes_spliced; - while ((bytes_spliced = splice(STDIN_FILENO, NULL, sock, NULL, pipe_size, SPLICE_F_MORE)) > 0) { + ssize_t stdin_bytes_spliced; + while ((stdin_bytes_spliced = splice(STDIN_FILENO, NULL, sock, NULL, stdin_pipe_size, SPLICE_F_MORE)) > 0) { } - if (bytes_spliced == -1) { + if (stdin_bytes_spliced == -1) { perror("splice stdin to internal socket"); close(sock); return EXIT_FAILURE; @@ -83,6 +100,16 @@ case 1: break; default: dprintf(STDERR_FILENO, "read returned unexpected value on internal socket\n"); + close(sock); + return EXIT_FAILURE; + } + + ssize_t stderr_bytes_spliced; + while ((stderr_bytes_spliced = splice(sock, NULL, STDERR_FILENO, NULL, stderr_pipe_size, SPLICE_F_MORE)) > 0) { + } + + if (stdin_bytes_spliced == -1) { + perror("splice internal socket to stderr"); close(sock); return EXIT_FAILURE; } -- 2.48.1