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 | |
| parent | 4f1f932f6ef6d6c9770266775c2db072964d7a62 (diff) | |
| download | blogc-3fff4bb3172f77b292b0c913749e81bedd3545f3.tar.gz blogc-3fff4bb3172f77b292b0c913749e81bedd3545f3.tar.bz2 blogc-3fff4bb3172f77b292b0c913749e81bedd3545f3.zip | |
git-receiver: splitted code
| -rw-r--r-- | Makefile.am | 21 | ||||
| -rw-r--r-- | src/blogc-git-receiver/main.c | 481 | ||||
| -rw-r--r-- | src/blogc-git-receiver/post-receive.c | 29 | ||||
| -rw-r--r-- | src/blogc-git-receiver/post-receive.h | 14 | ||||
| -rw-r--r-- | src/blogc-git-receiver/pre-receive.c | 281 | ||||
| -rw-r--r-- | src/blogc-git-receiver/pre-receive.h | 14 | ||||
| -rw-r--r-- | src/blogc-git-receiver/shell.c | 206 | ||||
| -rw-r--r-- | src/blogc-git-receiver/shell.h | 14 | 
8 files changed, 585 insertions, 475 deletions
| diff --git a/Makefile.am b/Makefile.am index 12cfad8..ef4e282 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,6 +39,9 @@ noinst_HEADERS = \  	src/blogc/renderer.h \  	src/blogc/source-parser.h \  	src/blogc/template-parser.h \ +	src/blogc-git-receiver/post-receive.h \ +	src/blogc-git-receiver/pre-receive.h \ +	src/blogc-git-receiver/shell.h \  	src/blogc-runserver/httpd.h \  	src/blogc-runserver/httpd-utils.h \  	src/blogc-runserver/mime.h \ @@ -65,6 +68,10 @@ if BUILD_GIT_RECEIVER  bin_PROGRAMS += \  	blogc-git-receiver \  	$(NULL) + +noinst_LTLIBRARIES += \ +	libblogc_git_receiver.la \ +	$(NULL)  endif  if BUILD_RUNSERVER @@ -137,8 +144,22 @@ blogc_git_receiver_CFLAGS = \  	$(NULL)  blogc_git_receiver_LDADD = \ +	libblogc_git_receiver.la \  	libblogc_common.la \  	$(NULL) + +libblogc_git_receiver_la_SOURCES = \ +	src/blogc-git-receiver/post-receive.c \ +	src/blogc-git-receiver/pre-receive.c \ +	src/blogc-git-receiver/shell.c \ +	$(NULL) + +libblogc_git_receiver_la_CFLAGS = \ +	$(AM_CFLAGS) \ +	$(NULL) + +libblogc_git_receiver_la_LIBADD = \ +	$(NULL)  endif 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; diff --git a/src/blogc-git-receiver/post-receive.c b/src/blogc-git-receiver/post-receive.c new file mode 100644 index 0000000..8310939 --- /dev/null +++ b/src/blogc-git-receiver/post-receive.c @@ -0,0 +1,29 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#include <stdio.h> +#include <stdlib.h> + + +int +bgr_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; +} diff --git a/src/blogc-git-receiver/post-receive.h b/src/blogc-git-receiver/post-receive.h new file mode 100644 index 0000000..a28dd5a --- /dev/null +++ b/src/blogc-git-receiver/post-receive.h @@ -0,0 +1,14 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#ifndef _POST_RECEIVE_H +#define _POST_RECEIVE_H + +int bgr_post_receive_hook(int argc, char *argv[]); + +#endif /* _POST_RECEIVE_H */ diff --git a/src/blogc-git-receiver/pre-receive.c b/src/blogc-git-receiver/pre-receive.c new file mode 100644 index 0000000..8c35537 --- /dev/null +++ b/src/blogc-git-receiver/pre-receive.c @@ -0,0 +1,281 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * 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); +    } +} + + +typedef enum { +    START_OLD = 1, +    OLD, +    START_NEW, +    NEW, +    START_REF, +    REF +} input_state_t; + + +int +bgr_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; +} diff --git a/src/blogc-git-receiver/pre-receive.h b/src/blogc-git-receiver/pre-receive.h new file mode 100644 index 0000000..5606e21 --- /dev/null +++ b/src/blogc-git-receiver/pre-receive.h @@ -0,0 +1,14 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#ifndef _PRE_RECEIVE_H +#define _PRE_RECEIVE_H + +int bgr_pre_receive_hook(int argc, char *argv[]); + +#endif /* _PRE_RECEIVE_H */ diff --git a/src/blogc-git-receiver/shell.c b/src/blogc-git-receiver/shell.c new file mode 100644 index 0000000..290526b --- /dev/null +++ b/src/blogc-git-receiver/shell.c @@ -0,0 +1,206 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include "../common/utils.h" +#include "shell.h" + +#ifndef BUFFER_SIZE +#define BUFFER_SIZE 4096 +#endif + + +int +bgr_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; +} diff --git a/src/blogc-git-receiver/shell.h b/src/blogc-git-receiver/shell.h new file mode 100644 index 0000000..c05e4e1 --- /dev/null +++ b/src/blogc-git-receiver/shell.h @@ -0,0 +1,14 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#ifndef _SHELL_H +#define _SHELL_H + +int bgr_shell(int argc, char *argv[]); + +#endif /* _SHELL_H */ | 
