Lindenii Project Forge
Login

server

Lindenii Forge’s main backend daemon
Commit info
ID
00442d92d5340f3a60965b395e14a7ba785aa4e9
Author
Runxi Yu <me@runxiyu.org>
Author date
Thu, 03 Apr 2025 15:32:27 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Thu, 03 Apr 2025 15:32:27 +0800
Actions
git2d: Report recent commits too

Also reformat in a Linux-esque style. I can't really maintain KNF
without using BSD indent(1).
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

/*
 * TODO: Pool repositories (and take care of thread safety)
 * libgit2 has a nice builtin per-repo cache that we could utilize this way.
 */

#include <err.h>
#include <errno.h>
#include <git2.h>
#include <pthread.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "bare.h"

typedef struct {
	int fd;
} conn_io_t;

static bare_error
conn_read(void *buffer, void *dst, uint64_t sz)
{
	conn_io_t *io = buffer;
	ssize_t rsz = read(io->fd, dst, sz);
	return (rsz == (ssize_t)sz) ? BARE_ERROR_NONE : BARE_ERROR_READ_FAILED;
	return (rsz == (ssize_t) sz) ? BARE_ERROR_NONE : BARE_ERROR_READ_FAILED;
}

static bare_error
conn_write(void *buffer, const void *src, uint64_t sz)
{
	conn_io_t *io = buffer;
	const uint8_t *data = src;
	uint64_t total = 0;

	while (total < sz) {
		ssize_t written = write(io->fd, data + total, sz - total);
		if (written < 0) {
			if (errno == EINTR)
				continue;
			return BARE_ERROR_WRITE_FAILED;
		}
		if (written == 0)
			break;
		total += written;
	}

	return (total == sz) ? BARE_ERROR_NONE : BARE_ERROR_WRITE_FAILED;
}

void *
session(void *_conn)
{
	int		conn = *(int *)_conn;
	int conn = *(int *)_conn;
	free((int *)_conn);

	int		err;
	int err;
	git_repository *repo = NULL;

	char path[4096];
	conn_io_t io = {.fd = conn};
	conn_io_t io = {.fd = conn };
	struct bare_reader reader = {
		.buffer = &io,
		.read = conn_read,
	};
	struct bare_writer writer = {
		.buffer = &io,
		.write = conn_write,
	};

	err = bare_get_data(&reader, (uint8_t *)path, sizeof(path) - 1);
	err = bare_get_data(&reader, (uint8_t *) path, sizeof(path) - 1);
	if (err != BARE_ERROR_NONE) {
		goto close;
	}
	path[sizeof(path) - 1] = '\0';

	err = git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_NO_SEARCH | GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT, NULL);
	if (err != 0) {
		bare_put_uint(&writer, 1);
		goto close;
	}

	git_object     *obj = NULL;
	git_object *obj = NULL;
	err = git_revparse_single(&obj, repo, "HEAD^{tree}");
	if (err != 0) {
		bare_put_uint(&writer, 2);
		goto free_repo;
	}
	git_tree       *tree = (git_tree *) obj;
	git_tree *tree = (git_tree *) obj;

	/* README */

	git_tree_entry *entry = NULL;
	err = git_tree_entry_bypath(&entry, tree, "README.md");
	if (err != 0) {
		bare_put_uint(&writer, 3);
		goto free_tree;
	}

	git_otype	objtype = git_tree_entry_type(entry);
	git_otype objtype = git_tree_entry_type(entry);
	if (objtype != GIT_OBJECT_BLOB) {
		bare_put_uint(&writer, 4);
		goto free_tree_entry;
	}

	git_object     *obj2 = NULL;
	git_object *obj2 = NULL;
	err = git_tree_entry_to_object(&obj2, repo, entry);
	if (err != 0) {
		bare_put_uint(&writer, 5);
		goto free_tree_entry;
	}

	git_blob       *blob = (git_blob *) obj2;
	const void     *content = git_blob_rawcontent(blob);
	git_blob *blob = (git_blob *) obj2;
	const void *content = git_blob_rawcontent(blob);
	if (content == NULL) {
		bare_put_uint(&writer, 6);
		goto free_blob;
	}

	bare_put_uint(&writer, 0);
	bare_put_data(&writer, content, git_blob_rawsize(blob));

	/* Commits */

	git_revwalk *walker = NULL;
	if (git_revwalk_new(&walker, repo) != 0) {
		bare_put_uint(&writer, 7);
		goto free_blob;
	}

	if (git_revwalk_push_head(walker) != 0) {
		bare_put_uint(&writer, 8);
		goto free_blob;
	}

	int count = 0;
	git_oid oid;
	while (count < 5 && git_revwalk_next(&oid, walker) == 0) {
		git_commit *commit = NULL;
		if (git_commit_lookup(&commit, repo, &oid) != 0)
			break;

		const char *msg = git_commit_summary(commit);
		const git_signature *author = git_commit_author(commit);
		time_t time = git_commit_time(commit);
		char timebuf[64];
		struct tm *tm = localtime(&time);
		if (tm)
			strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
		else
			strcpy(timebuf, "unknown");

		/* ID */
		bare_put_data(&writer, oid.id, GIT_OID_RAWSZ);

		/* Title */
		size_t msg_len = msg ? strlen(msg) : 0;
		bare_put_data(&writer, (const uint8_t *)(msg ? msg : ""), msg_len);

		/* Author */
		const char *author_name = author ? author->name : "";
		size_t author_len = strlen(author_name);
		bare_put_data(&writer, (const uint8_t *)author_name, author_len);

		/* Author's date */
		size_t time_len = strlen(timebuf);
		bare_put_data(&writer, (const uint8_t *)timebuf, time_len);

		git_commit_free(commit);
		count++;
	}

	git_revwalk_free(walker);

free_blob:
	git_blob_free(blob);
free_tree_entry:
	git_tree_entry_free(entry);
free_tree:
	git_tree_free(tree);
free_repo:
	git_repository_free(repo);

close:
	close(conn);

	return NULL;
}

int
main(int argc, char **argv)
{
	if (argc != 2) {
		errx(1, "provide one argument: the socket path");
	}

	signal(SIGPIPE, SIG_IGN);

	git_libgit2_init();

	int		sock;
	int sock;
	if ((sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0)
		err(1, "socket");

	struct sockaddr_un addr;
	memset(&addr, 0, sizeof(addr));
	addr.sun_family = AF_UNIX;
	strcpy(addr.sun_path, argv[1]);

	umask(0077);

	if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un))) {
		if (errno == EADDRINUSE) {
			unlink(argv[1]);
			if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)))
				err(1, "bind");
		} else {
			err(1, "bind");
		}
	}

	listen(sock, 0);

	for (;;) {
		int		*conn = malloc(sizeof(int));
		int *conn = malloc(sizeof(int));
		if (conn == NULL)
			err(1, "malloc");

		*conn = accept(sock, 0, 0);
		if (*conn == -1)
			err(1, "accept");

		pthread_t	thread;
		pthread_t thread;

		pthread_create(&thread, NULL, session, (void *)conn);
	}

	close(sock);

	git_libgit2_shutdown();
}