diff options
author | Rafael G. Martins <rafael@rafaelmartins.eng.br> | 2016-09-29 00:49:36 +0200 |
---|---|---|
committer | Rafael G. Martins <rafael@rafaelmartins.eng.br> | 2016-09-29 00:49:36 +0200 |
commit | 3fff4bb3172f77b292b0c913749e81bedd3545f3 (patch) | |
tree | cf0445fac31f374445c7706bd7b4d62fa10101bc /src/blogc-git-receiver/main.c | |
parent | 4f1f932f6ef6d6c9770266775c2db072964d7a62 (diff) | |
download | blogc-3fff4bb3172f77b292b0c913749e81bedd3545f3.tar.gz blogc-3fff4bb3172f77b292b0c913749e81bedd3545f3.tar.bz2 blogc-3fff4bb3172f77b292b0c913749e81bedd3545f3.zip |
git-receiver: splitted code
Diffstat (limited to 'src/blogc-git-receiver/main.c')
-rw-r--r-- | src/blogc-git-receiver/main.c | 481 |
1 files changed, 6 insertions, 475 deletions
diff --git a/src/blogc-git-receiver/main.c b/src/blogc-git-receiver/main.c index 86e1027..fb6e724 100644 --- a/src/blogc-git-receiver/main.c +++ b/src/blogc-git-receiver/main.c @@ -6,481 +6,12 @@ * See the file LICENSE. */ -#include <stdbool.h> -#include <stddef.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> #include <libgen.h> -#include <unistd.h> -#include <errno.h> -#include <sys/stat.h> -#include <dirent.h> -#include <time.h> - -#include "../common/utils.h" - -#ifndef BUFFER_SIZE -#define BUFFER_SIZE 4096 -#endif - - -static unsigned int -cpu_count(void) -{ -#ifdef _SC_NPROCESSORS_ONLN - long num = sysconf(_SC_NPROCESSORS_ONLN); - if (num >= 1) - return (unsigned int) num; -#endif - return 1; -} - - -static void -rmdir_recursive(const char *dir) -{ - struct stat buf; - if (0 != stat(dir, &buf)) { - fprintf(stderr, "warning: failed to remove directory (%s): %s\n", dir, - strerror(errno)); - return; - } - if (!S_ISDIR(buf.st_mode)) { - fprintf(stderr, "error: trying to remove invalid directory: %s\n", dir); - exit(2); - } - DIR *d = opendir(dir); - if (d == NULL) { - fprintf(stderr, "error: failed to open directory: %s\n", - strerror(errno)); - exit(2); - } - struct dirent *e; - while (NULL != (e = readdir(d))) { - if ((0 == strcmp(e->d_name, ".")) || (0 == strcmp(e->d_name, ".."))) - continue; - char *f = bc_strdup_printf("%s/%s", dir, e->d_name); - if (0 != stat(f, &buf)) { - fprintf(stderr, "error: failed to stat directory entry (%s): %s\n", - e->d_name, strerror(errno)); - free(f); - exit(2); - } - if (S_ISDIR(buf.st_mode)) { - rmdir_recursive(f); - } - else if (0 != unlink(f)) { - fprintf(stderr, "error: failed to remove file (%s): %s\n", f, - strerror(errno)); - free(f); - exit(2); - } - free(f); - } - if (0 != closedir(d)) { - fprintf(stderr, "error: failed to close directory: %s\n", - strerror(errno)); - exit(2); - } - if (0 != rmdir(dir)) { - fprintf(stderr, "error: failed to remove directory: %s\n", - strerror(errno)); - exit(2); - } -} - - -static int -git_shell(int argc, char *argv[]) -{ - int rv = 0; - - char *repo = NULL; - char *command_orig = NULL; - char *command_name = NULL; - char command_new[BUFFER_SIZE]; - - bool exec_git = false; - - // validate git command - size_t len = strlen(argv[2]); - if (!((len > 17 && (0 == strncmp(argv[2], "git-receive-pack ", 17))) || - (len > 16 && (0 == strncmp(argv[2], "git-upload-pack ", 16))) || - (len > 19 && (0 == strncmp(argv[2], "git-upload-archive ", 19))))) - { - fprintf(stderr, "error: unsupported git command: %s\n", argv[2]); - rv = 1; - goto cleanup; - } - - // get shell path - char *self = getenv("SHELL"); - if (self == NULL) { - fprintf(stderr, "error: failed to find blogc-git-receiver path\n"); - rv = 1; - goto cleanup; - } - - // get home path - char *home = getenv("HOME"); - if (home == NULL) { - fprintf(stderr, "error: failed to find user home path\n"); - rv = 1; - goto cleanup; - } - - // get git repository - command_orig = bc_strdup(argv[2]); - char *p, *r; - for (p = command_orig; *p != ' ' && *p != '\0'; p++); - if (*p == ' ') - p++; - if (*p == '\'' || *p == '"') - p++; - if (*p == '/') - p++; - for (r = p; *p != '\'' && *p != '"' && *p != '\0'; p++); - if (*p == '\'' || *p == '"') - *p = '\0'; - if (*--p == '/') - *p = '\0'; - - repo = bc_strdup_printf("repos/%s", r); - - // check if repository is sane - if (0 == strlen(repo)) { - fprintf(stderr, "error: invalid repository\n"); - rv = 1; - goto cleanup; - } - - if (0 == strncmp(argv[2], "git-upload-", 11)) // no need to check len here - goto git_exec; - - if (0 != chdir(home)) { - fprintf(stderr, "error: failed to chdir (%s): %s\n", home, - strerror(errno)); - rv = 1; - goto cleanup; - } - - if (0 != access(repo, F_OK)) { - char *git_init_cmd = bc_strdup_printf( - "git init --bare \"%s\" > /dev/null", repo); - if (0 != system(git_init_cmd)) { - fprintf(stderr, "error: failed to create git repository: %s\n", - repo); - rv = 1; - free(git_init_cmd); - goto cleanup; - } - free(git_init_cmd); - } - - if (0 != chdir(repo)) { - fprintf(stderr, "error: failed to chdir (%s/%s): %s\n", home, repo, - strerror(errno)); - rv = 1; - goto cleanup; - } - - if (0 != access("hooks", F_OK)) { - // openwrt git package won't install git templates, then the git - // repositories created with it won't have the hooks/ directory. - if (0 != mkdir("hooks", 0777)) { // mkdir honors umask for us. - fprintf(stderr, "error: failed to create directory (%s/%s/hooks): " - "%s\n", home, repo, strerror(errno)); - rv = 1; - goto cleanup; - } - } - - if (0 != chdir("hooks")) { - fprintf(stderr, "error: failed to chdir (%s/%s/hooks): %s\n", home, - repo, strerror(errno)); - rv = 1; - goto cleanup; - } - - if (0 == access("pre-receive", F_OK)) { - if (0 != unlink("pre-receive")) { - fprintf(stderr, "error: failed to remove old symlink " - "(%s/%s/hooks/pre-receive): %s\n", home, repo, strerror(errno)); - rv = 1; - goto cleanup; - } - } - - if (0 != symlink(self, "pre-receive")) { - fprintf(stderr, "error: failed to create symlink " - "(%s/%s/hooks/pre-receive): %s\n", home, repo, strerror(errno)); - rv = 1; - goto cleanup; - } - - if (0 == access("post-receive", F_OK)) { - if (0 != unlink("post-receive")) { - fprintf(stderr, "error: failed to remove old symlink " - "(%s/%s/hooks/post-receive): %s\n", home, repo, strerror(errno)); - rv = 1; - goto cleanup; - } - } - - if (0 != symlink(self, "post-receive")) { - fprintf(stderr, "error: failed to create symlink " - "(%s/%s/hooks/post-receive): %s\n", home, repo, strerror(errno)); - rv = 1; - goto cleanup; - } - - if (0 != chdir(home)) { - fprintf(stderr, "error: failed to chdir (%s): %s\n", home, - strerror(errno)); - rv = 1; - goto cleanup; - } - -git_exec: - command_name = bc_strdup(argv[2]); - for (p = command_name; *p != ' ' && *p != '\0'; p++); - if (*p == ' ') - *p = '\0'; - - if (BUFFER_SIZE < (strlen(command_name) + strlen(repo) + 4)) { - fprintf(stderr, "error: git-shell command is too big\n"); - rv = 1; - goto cleanup; - } - - if (snprintf(command_new, BUFFER_SIZE, "%s '%s'", command_name, repo)) - exec_git = true; - -cleanup: - free(repo); - free(command_orig); - free(command_name); - - if (exec_git) { - execlp("git-shell", "git-shell", "-c", command_new, NULL); - - // execlp only returns on error, then something bad happened - fprintf(stderr, "error: failed to execute git-shell\n"); - rv = 1; - } - - return rv; -} - - -static int -git_post_receive_hook(int argc, char *argv[]) -{ - if (0 != system("git config --local remote.mirror.pushurl &> /dev/null")) { - if (0 != system("git config --local remote.mirror.url &> /dev/null")) { - fprintf(stderr, "warning: repository mirroring disabled\n"); - return 0; - } - } - - // at this point we know that we have a remote called mirror, we can just - // push to it. - if (0 != system("git push --mirror mirror")) - fprintf(stderr, "warning: failed push to git mirror\n"); - - return 0; -} - - -typedef enum { - START_OLD = 1, - OLD, - START_NEW, - NEW, - START_REF, - REF -} input_state_t; - - -static int -git_pre_receive_hook(int argc, char *argv[]) -{ - int c; - char buffer[BUFFER_SIZE]; - - input_state_t state = START_OLD; - size_t i = 0; - size_t start = 0; - - int rv = 0; - char *new = NULL; - char *master = NULL; - - while (EOF != (c = getc(stdin))) { - - buffer[i] = (char) c; - - switch (state) { - case START_OLD: - start = i; - state = OLD; - break; - case OLD: - if (c != ' ') - break; - // no need to store old - state = START_NEW; - break; - case START_NEW: - start = i; - state = NEW; - break; - case NEW: - if (c != ' ') - break; - state = START_REF; - new = strndup(buffer + start, i - start); - break; - case START_REF: - start = i; - state = REF; - break; - case REF: - if (c != '\n') - break; - state = START_OLD; - // we just care about a ref (refs/heads/master), everything - // else is disposable :) - if (!((i - start == 17) && - (0 == strncmp("refs/heads/master", buffer + start, 17)))) - { - free(new); - new = NULL; - break; - } - master = new; - break; - } - - if (++i >= BUFFER_SIZE) { - fprintf(stderr, "error: pre-receive hook payload is too big.\n"); - rv = 1; - goto cleanup2; - } - } - - if (master == NULL) { - fprintf(stderr, "warning: no reference to master branch found. " - "nothing to deploy.\n"); - goto cleanup2; - } - - char *repo_dir = NULL; - char *output_dir = NULL; - - if (NULL == getcwd(buffer, BUFFER_SIZE)) { - fprintf(stderr, "error: failed to get repository remote path: %s\n", - strerror(errno)); - rv = 1; - goto cleanup; - } - - repo_dir = bc_strdup(buffer); - - char dir[] = "/tmp/blogc_XXXXXX"; - if (NULL == mkdtemp(dir)) { - rv = 1; - goto cleanup; - } - - char *git_archive_cmd = bc_strdup_printf( - "git archive \"%s\" | tar -x -C \"%s\" -f -", master, dir); - if (0 != system(git_archive_cmd)) { - fprintf(stderr, "error: failed to extract git content to temporary " - "directory: %s\n", dir); - rv = 1; - free(git_archive_cmd); - goto cleanup; - } - free(git_archive_cmd); - - if (0 != chdir(dir)) { - fprintf(stderr, "error: failed to chdir (%s): %s\n", dir, - strerror(errno)); - rv = 1; - goto cleanup; - } - - if ((0 != access("Makefile", F_OK)) && (0 != access("GNUMakefile", F_OK))) { - fprintf(stderr, "warning: no makefile found. skipping ...\n"); - goto cleanup; - } - - char *home = getenv("HOME"); - if (home == NULL) { - fprintf(stderr, "error: failed to find user home path\n"); - rv = 1; - goto cleanup; - } - - unsigned long epoch = time(NULL); - output_dir = bc_strdup_printf("%s/builds/%s-%lu", home, master, epoch); - char *gmake_cmd = bc_strdup_printf( - "gmake -j%d OUTPUT_DIR=\"%s\" BLOGC_GIT_RECEIVER=1", - cpu_count(), output_dir); - fprintf(stdout, "running command: %s\n\n", gmake_cmd); - fflush(stdout); - if (0 != system(gmake_cmd)) { - fprintf(stderr, "error: failed to build website ...\n"); - rmdir_recursive(output_dir); - free(gmake_cmd); - rv = 1; - goto cleanup; - } - free(gmake_cmd); - - if (0 != chdir(repo_dir)) { - fprintf(stderr, "error: failed to chdir (%s): %s\n", repo_dir, - strerror(errno)); - rmdir_recursive(output_dir); - rv = 1; - goto cleanup; - } - - char *htdocs_sym = NULL; - ssize_t htdocs_sym_len = readlink("htdocs", buffer, BUFFER_SIZE); - if (0 < htdocs_sym_len) { - if (0 != unlink("htdocs")) { - fprintf(stderr, "error: failed to remove symlink (%s/htdocs): %s\n", - repo_dir, strerror(errno)); - rmdir_recursive(output_dir); - rv = 1; - goto cleanup; - } - buffer[htdocs_sym_len] = '\0'; - htdocs_sym = buffer; - } - - if (0 != symlink(output_dir, "htdocs")) { - fprintf(stderr, "error: failed to create symlink (%s/htdocs): %s\n", - repo_dir, strerror(errno)); - rmdir_recursive(output_dir); - rv = 1; - goto cleanup; - } - - if (htdocs_sym != NULL) - rmdir_recursive(htdocs_sym); - -cleanup: - free(output_dir); - rmdir_recursive(dir); - free(repo_dir); -cleanup2: - free(new); - return rv; -} +#include "shell.h" +#include "pre-receive.h" +#include "post-receive.h" int @@ -488,13 +19,13 @@ main(int argc, char *argv[]) { if (argc > 0) { if (0 == strcmp(basename(argv[0]), "pre-receive")) - return git_pre_receive_hook(argc, argv); + return bgr_pre_receive_hook(argc, argv); if (0 == strcmp(basename(argv[0]), "post-receive")) - return git_post_receive_hook(argc, argv); + return bgr_post_receive_hook(argc, argv); } if (argc == 3 && (0 == strcmp(argv[1], "-c"))) - return git_shell(argc, argv); + return bgr_shell(argc, argv); fprintf(stderr, "error: this is a special shell, go away!\n"); return 1; |