From cbf280f54ced411020e4526aa2be21cd50aff529 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Tue, 18 Mar 2025 19:52:00 +0800 Subject: git_hooks_client -> hookc --- git_hooks_client/git_hooks_client.c | 250 ------------------------------------ 1 file changed, 250 deletions(-) delete mode 100644 git_hooks_client/git_hooks_client.c (limited to 'git_hooks_client/git_hooks_client.c') diff --git a/git_hooks_client/git_hooks_client.c b/git_hooks_client/git_hooks_client.c deleted file mode 100644 index 9921edf..0000000 --- a/git_hooks_client/git_hooks_client.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * SPDX-License-Identifier: AGPL-3.0-only - * SPDX-FileContributor: Runxi Yu - * SPDX-FileContributor: Test_User - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * FIXME: splice(2) is not portable and will only work on Linux. Alternative - * implementations should be supplied for other environments. - */ - -int main(int argc, char *argv[]) { - if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { - perror("signal"); - return EXIT_FAILURE; - } - - 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"); - return EXIT_FAILURE; - } - const char *cookie = getenv("LINDENII_FORGE_HOOKS_COOKIE"); - if (cookie == NULL) { - dprintf(STDERR_FILENO, "environment variable LINDENII_FORGE_HOOKS_COOKIE undefined\n"); - return EXIT_FAILURE; - } - if (strlen(cookie) != 64) { - dprintf(STDERR_FILENO, "environment variable LINDENII_FORGE_HOOKS_COOKIE is not 64 characters long\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. - */ - 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; - } - int stdin_pipe_size = fcntl(STDIN_FILENO, F_GETPIPE_SZ); - if (stdin_pipe_size == -1) { - perror("fcntl on stdin"); - return EXIT_FAILURE; - } - - /* - * 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; - } - - /* Connecting back to the main daemon */ - int sock; - struct sockaddr_un addr; - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock == -1) { - perror("internal socket creation"); - return EXIT_FAILURE; - } - memset(&addr, 0, sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); - if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) { - perror("internal socket connect"); - close(sock); - return EXIT_FAILURE; - } - - /* - * Send the 64-byte cookit back. - */ - ssize_t cookie_bytes_sent = send(sock, cookie, 64, 0); - switch (cookie_bytes_sent) { - case -1: - perror("send cookie"); - close(sock); - return EXIT_FAILURE; - case 64: - break; - default: - dprintf(STDERR_FILENO, "send returned unexpected value on internal socket\n"); - close(sock); - return EXIT_FAILURE; - } - - /* - * Report arguments. - */ - uint64_t argc64 = (uint64_t)argc; - ssize_t bytes_sent = send(sock, &argc64, sizeof(argc64), 0); - switch (bytes_sent) { - case -1: - perror("send argc"); - close(sock); - return EXIT_FAILURE; - case sizeof(argc64): - break; - default: - dprintf(STDERR_FILENO, "send returned unexpected value on internal socket\n"); - close(sock); - return EXIT_FAILURE; - } - for (int i = 0; i < argc; i++) { - unsigned long len = strlen(argv[i]) + 1; - bytes_sent = send(sock, argv[i], len, 0); - if (bytes_sent == -1) { - perror("send argv"); - close(sock); - exit(EXIT_FAILURE); - } else if ((unsigned long)bytes_sent == len) { - } else { - dprintf(STDERR_FILENO, "send returned unexpected value on internal socket\n"); - close(sock); - exit(EXIT_FAILURE); - } - } - - /* - * Report GIT_* environment. - */ - extern char **environ; - for (char **env = environ; *env != NULL; env++) { - if (strncmp(*env, "GIT_", 4) == 0) { - unsigned long len = strlen(*env) + 1; - bytes_sent = send(sock, *env, len, 0); - if (bytes_sent == -1) { - perror("send env"); - close(sock); - exit(EXIT_FAILURE); - } else if ((unsigned long)bytes_sent == len) { - } else { - dprintf(STDERR_FILENO, "send returned unexpected value on internal socket\n"); - close(sock); - exit(EXIT_FAILURE); - } - } - } - bytes_sent = send(sock, "", 1, 0); - if (bytes_sent == -1) { - perror("send env terminator"); - close(sock); - exit(EXIT_FAILURE); - } else if (bytes_sent == 1) { - } else { - dprintf(STDERR_FILENO, "send returned unexpected value on internal socket\n"); - close(sock); - exit(EXIT_FAILURE); - } - - /* - * Splice stdin to the daemon. For pre-receive it's just old/new/ref. - */ - ssize_t stdin_bytes_spliced; - while ((stdin_bytes_spliced = splice(STDIN_FILENO, NULL, sock, NULL, stdin_pipe_size, SPLICE_F_MORE)) > 0) { - } - if (stdin_bytes_spliced == -1) { - perror("splice stdin to internal socket"); - close(sock); - return EXIT_FAILURE; - } - - /* - * The sending part of the UNIX socket should be shut down, to let - * io.Copy on the Go side return. - */ - if (shutdown(sock, SHUT_WR) == -1) { - perror("shutdown internal socket"); - close(sock); - return EXIT_FAILURE; - } - - /* - * The first byte of the response from the UNIX domain socket is the - * status code to return. - * - * FIXME: It doesn't make sense to require the return value to be - * sent before the log message. However, if we were to keep splicing, - * it's difficult to get the last byte before EOF. Perhaps we could - * hack together some sort of OOB message or ancillary data, or perhaps - * even use signals. - */ - char status_buf[1]; - ssize_t bytes_read = read(sock, status_buf, 1); - switch (bytes_read) { - case -1: - perror("read status code from internal socket"); - close(sock); - return EXIT_FAILURE; - case 0: - dprintf(STDERR_FILENO, "unexpected EOF on internal socket\n"); - close(sock); - return EXIT_FAILURE; - case 1: - break; - default: - dprintf(STDERR_FILENO, "read returned unexpected value on internal socket\n"); - close(sock); - return EXIT_FAILURE; - } - - /* - * Now we can splice data from the UNIX domain socket to stderr. - * This data is directly passed to the user (with "remote: " prepended). - * - * We usually don't actually use this as the daemon could easily write - * to the SSH connection's stderr directly anyway. - */ - 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 && errno != ECONNRESET) { - perror("splice internal socket to stderr"); - close(sock); - return EXIT_FAILURE; - } - - close(sock); - return *status_buf; -} -- cgit v1.2.3