aboutsummaryrefslogtreecommitdiff
path: root/git_hooks_client
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2025-03-18 19:52:00 +0800
committerRunxi Yu <me@runxiyu.org>2025-03-18 19:52:00 +0800
commitcbf280f54ced411020e4526aa2be21cd50aff529 (patch)
treea403cc384a92fdb225680be9cac025874133759d /git_hooks_client
parentRevert "Comic Sans" (diff)
downloadforge-cbf280f54ced411020e4526aa2be21cd50aff529.tar.gz
forge-cbf280f54ced411020e4526aa2be21cd50aff529.tar.zst
forge-cbf280f54ced411020e4526aa2be21cd50aff529.zip
git_hooks_client -> hookc
Diffstat (limited to 'git_hooks_client')
-rw-r--r--git_hooks_client/.gitignore1
-rw-r--r--git_hooks_client/git_hooks_client.c250
2 files changed, 0 insertions, 251 deletions
diff --git a/git_hooks_client/.gitignore b/git_hooks_client/.gitignore
deleted file mode 100644
index 129c0b4..0000000
--- a/git_hooks_client/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/git_hooks_client
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 <https://runxiyu.org>
- * SPDX-FileContributor: Test_User <hax@runxiyu.org>
- */
-
-#include <errno.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <fcntl.h>
-#include <signal.h>
-
-/*
- * 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;
-}