diff options
Diffstat (limited to 'src/blogc-make')
| -rw-r--r-- | src/blogc-make/httpd.c | 81 | ||||
| -rw-r--r-- | src/blogc-make/httpd.h | 19 | ||||
| -rw-r--r-- | src/blogc-make/reloader.c | 117 | ||||
| -rw-r--r-- | src/blogc-make/reloader.h | 13 | ||||
| -rw-r--r-- | src/blogc-make/rules.c | 27 | 
5 files changed, 186 insertions, 71 deletions
| diff --git a/src/blogc-make/httpd.c b/src/blogc-make/httpd.c new file mode 100644 index 0000000..7d67b57 --- /dev/null +++ b/src/blogc-make/httpd.c @@ -0,0 +1,81 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2014-2017 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> +#include <string.h> +#include <pthread.h> +#include "../common/utils.h" +#include "ctx.h" +#include "exec.h" +#include "reloader.h" +#include "httpd.h" + +// we are not going to unit-test these functions, then printing errors +// directly is not a big issue + + +typedef struct { +    bm_ctx_t *ctx; +    bc_trie_t *args; +} bm_httpd_t; + + +static void* +httpd_thread(void *arg) +{ +    bm_httpd_t *httpd = arg; + +    int rv = bm_exec_blogc_runserver(httpd->ctx, bc_trie_lookup(httpd->args, "host"), +        bc_trie_lookup(httpd->args, "port"), bc_trie_lookup(httpd->args, "threads")); + +    free(httpd); + +    // stop the reloader +    bm_reloader_stop(rv); + +    return NULL; +} + + +int +bm_httpd_run(bm_ctx_t **ctx, bm_rule_exec_func_t rule_exec, bc_slist_t *outputs, +    bc_trie_t *args) +{ +    int err; + +    pthread_attr_t attr; +    if (0 != (err = pthread_attr_init(&attr))) { +        fprintf(stderr, "blogc-make: error: failed to initialize httpd " +            "thread attributes: %s\n", strerror(err)); +        return 3; +    } + +    // we run the thread detached, because we don't want to wait it to join +    // before exiting. the OS can clean it properly +    if (0 != (err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))) { +        fprintf(stderr, "blogc-make: error: failed to mark httpd thread as " +            "detached: %s\n", strerror(err)); +        return 3; +    } + +    bm_httpd_t *rv = bc_malloc(sizeof(bm_httpd_t)); +    rv->ctx = *ctx; +    rv->args = args; + +    pthread_t thread; +    if (0 != (err = pthread_create(&thread, &attr, httpd_thread, rv))) { +        fprintf(stderr, "blogc-make: error: failed to create httpd " +            "thread: %s\n", strerror(err)); +        free(rv); +        return 3; +    } + +    // run the reloader +    return bm_reloader_run(ctx, rule_exec, outputs, args); +} diff --git a/src/blogc-make/httpd.h b/src/blogc-make/httpd.h new file mode 100644 index 0000000..97e3b86 --- /dev/null +++ b/src/blogc-make/httpd.h @@ -0,0 +1,19 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2014-2017 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#ifndef _MAKE_HTTPD_H +#define _MAKE_HTTPD_H + +#include "../common/utils.h" +#include "ctx.h" +#include "rules.h" + +int bm_httpd_run(bm_ctx_t **ctx, bm_rule_exec_func_t rule_exec, bc_slist_t *outputs, +    bc_trie_t *args); + +#endif /* _MAKE_HTTPD_H */ diff --git a/src/blogc-make/reloader.c b/src/blogc-make/reloader.c index 6b30974..1bc7726 100644 --- a/src/blogc-make/reloader.c +++ b/src/blogc-make/reloader.c @@ -6,11 +6,14 @@   * See the file LICENSE.   */ +#include <stdbool.h>  #include <stdio.h>  #include <stdlib.h> +#include <signal.h>  #include <string.h>  #include <unistd.h>  #include <pthread.h> +#include <errno.h>  #include "../common/utils.h"  #include "ctx.h"  #include "rules.h" @@ -19,19 +22,52 @@  // we are not going to unit-test these functions, then printing errors  // directly is not a big issue +static pthread_mutex_t mutex_running = PTHREAD_MUTEX_INITIALIZER; +static bool running = false; +static int handler_signum = 0; +static void (*handler_func)(int) = NULL; -static void* -bm_reloader_thread(void *arg) + +int +bm_reloader_run(bm_ctx_t **ctx, bm_rule_exec_func_t rule_exec, +    bc_slist_t *outputs, bc_trie_t *args)  { -    bm_reloader_t *reloader = arg; -    while (reloader->running) { -        if (!bm_ctx_reload(&(reloader->ctx))) { +    // install ^C handler +    struct sigaction current_action; +    if (sigaction(SIGINT, NULL, ¤t_action) < 0) { +        fprintf(stderr, "blogc-make: failed to run reloader: %s\n", strerror(errno)); +        return 3; +    } +    if (current_action.sa_handler != bm_reloader_stop) {  // not installed yet +        // backup current handler +        pthread_mutex_lock(&mutex_running); +        handler_func = current_action.sa_handler; +        pthread_mutex_unlock(&mutex_running); + +        // set new handler +        struct sigaction new_action; +        new_action.sa_handler = bm_reloader_stop; +        sigemptyset(&new_action.sa_mask); +        new_action.sa_flags = 0; +        if (sigaction(SIGINT, &new_action, NULL) < 0) { +            fprintf(stderr, "blogc-make: failed to run reloader: %s\n", +                strerror(errno)); +            return 3; +        } +    } + +    pthread_mutex_lock(&mutex_running); +    running = true; +    pthread_mutex_unlock(&mutex_running); + +    while (running) { +        if (!bm_ctx_reload(ctx)) {              fprintf(stderr, "blogc-make: warning: failed to reload context. "                  "retrying in 5 seconds ...\n\n");              sleep(5);              continue;          } -        if (0 != reloader->rule_exec(reloader->ctx, reloader->outputs, reloader->args)) { +        if (0 != rule_exec(*ctx, outputs, args)) {              fprintf(stderr, "blogc-make: warning: failed to rebuild website. "                  "retrying in 5 seconds ...\n\n");              sleep(5); @@ -40,59 +76,38 @@ bm_reloader_thread(void *arg)          sleep(1);      } -    free(reloader); -    return NULL; +    if (handler_signum > 0 && handler_signum <= SIGRTMAX) +        return 128 + handler_signum; + +    return handler_signum;  } -bm_reloader_t* -bm_reloader_new(bm_ctx_t *ctx, bm_rule_exec_func_t rule_exec, -    bc_slist_t *outputs, bc_trie_t *args) +void +bm_reloader_stop(int signum)  { -    // first rule_exec call is syncronous, to do a 'sanity check' -    if (0 != rule_exec(ctx, outputs, args)) -        return NULL; +    pthread_mutex_lock(&mutex_running); -    int err; +    handler_signum = signum > 128 ? signum - 128 : signum; +    running = false; -    pthread_attr_t attr; -    if (0 != (err = pthread_attr_init(&attr))) { -        fprintf(stderr, "blogc-make: error: failed to initialize reloader " -            "thread attributes: %s\n", strerror(err)); -        return NULL; -    } +    // reraise if SIGINT +    if (handler_signum == SIGINT) { -    // we run the thread detached, because we don't want to wait it to join -    // before exiting. the OS can clean it properly -    if (0 != (err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED))) { -        fprintf(stderr, "blogc-make: error: failed to mark reloader thread as " -            "detached: %s\n", strerror(err)); -        return NULL; -    } +        // reinstall old ^C handler +        struct sigaction new_action; +        new_action.sa_handler = handler_func; +        sigemptyset(&new_action.sa_mask); +        new_action.sa_flags = 0; +        sigaction(SIGINT, &new_action, NULL); -    bm_reloader_t *rv = bc_malloc(sizeof(bm_reloader_t)); -    rv->ctx = ctx; -    rv->rule_exec = rule_exec; -    rv->outputs = outputs; -    rv->args = args; -    rv->running = true; - -    pthread_t thread; -    if (0 != (err = pthread_create(&thread, &attr, bm_reloader_thread, rv))) { -        fprintf(stderr, "blogc-make: error: failed to create reloader " -            "thread: %s\n", strerror(err)); -        free(rv); -        return NULL; -    } - -    return rv; -} +        // run it +        raise(SIGINT); +        // SIGINT will usually kill the process, but in the case that the +        // `handler_func` prevents it, our custom handler will be reinstalled +        // by `bm_reloader_run`. +    } -void -bm_reloader_stop(bm_reloader_t *reloader) -{ -    if (reloader == NULL) -        return; -    reloader->running = false; +    pthread_mutex_unlock(&mutex_running);  } diff --git a/src/blogc-make/reloader.h b/src/blogc-make/reloader.h index 40d2fe9..cafd67c 100644 --- a/src/blogc-make/reloader.h +++ b/src/blogc-make/reloader.h @@ -9,21 +9,12 @@  #ifndef _MAKE_RELOADER_H  #define _MAKE_RELOADER_H -#include <stdbool.h>  #include "../common/utils.h"  #include "ctx.h"  #include "rules.h" -typedef struct { -    bm_ctx_t *ctx; -    bm_rule_exec_func_t rule_exec; -    bc_slist_t *outputs; -    bc_trie_t *args; -    bool running; -} bm_reloader_t; - -bm_reloader_t* bm_reloader_new(bm_ctx_t *ctx, bm_rule_exec_func_t rule_exec, +int bm_reloader_run(bm_ctx_t **ctx, bm_rule_exec_func_t rule_exec,      bc_slist_t *outputs, bc_trie_t *args); -void bm_reloader_stop(bm_reloader_t *reloader); +void bm_reloader_stop(int signum);  #endif /* _MAKE_RELOADER_H */ diff --git a/src/blogc-make/rules.c b/src/blogc-make/rules.c index 8464bd2..5a23de7 100644 --- a/src/blogc-make/rules.c +++ b/src/blogc-make/rules.c @@ -16,6 +16,7 @@  #include "ctx.h"  #include "exec.h"  #include "exec-native.h" +#include "httpd.h"  #include "reloader.h"  #include "settings.h"  #include "rules.h" @@ -581,16 +582,16 @@ static int all_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args);  static int  runserver_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args)  { -    bm_reloader_t *reloader = bm_reloader_new(ctx, all_exec, outputs, args); -    if (reloader == NULL) { -        return 3; -    } +    return bm_httpd_run(&ctx, all_exec, outputs, args); +} -    int rv = bm_exec_blogc_runserver(ctx, bc_trie_lookup(args, "host"), -        bc_trie_lookup(args, "port"), bc_trie_lookup(args, "threads")); -    bm_reloader_stop(reloader); -    return rv; +// WATCH RULE + +static int +watch_exec(bm_ctx_t *ctx, bc_slist_t *outputs, bc_trie_t *args) +{ +    return bm_reloader_run(&ctx, all_exec, outputs, args);  } @@ -667,12 +668,20 @@ const bm_rule_t rules[] = {      },      {          .name = "runserver", -        .help = "run blogc-runserver pointing to output directory, if available\n" +        .help = "run blogc-runserver pointing to output directory, if available,\n" +            "                  rebuilding as needed\n"              "                  arguments: host (127.0.0.1), port (8080) and threads (20)",          .outputlist_func = NULL,          .exec_func = runserver_exec,          .generate_files = false,      }, +    { +        .name = "watch", +        .help = "watch for changes in the source files, rebuilding as needed", +        .outputlist_func = NULL, +        .exec_func = watch_exec, +        .generate_files = false, +    },      {NULL, NULL, NULL, NULL, false},  }; | 
