mobley

C Git HTTP server
Log | Files | Refs | Submodules | README | git clone https://git.ne02ptzero.me/git/mobley

commit 5c19003462247a6c8df2c0ed28d0d87eed9ce057
parent 4330c3d2718d73074a1f4ea8a59d99218ab354fc
Author: Louis Solofrizzo <lsolofrizzo@online.net>
Date:   Sat, 15 Dec 2018 12:35:16 +0100

Add configuration file parsing

Now parsing information from a YAML configuration file.

Signed-off-by: Louis Solofrizzo <lsolofrizzo@online.net>

Diffstat:
MCMakeLists.txt | 3++-
Aconfig.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.h | 23+++++++++++++++++++++++
Aconfig.yaml | 10++++++++++
Mhtml.c | 7++++++-
Mhtml.h | 2+-
Mmain.c | 56+++++++++++++++++++++++++++++---------------------------
Mmobley.h | 25++++++++++++++++++++-----
Mstr.h | 38++++++++++++++++++++++++++------------
Mtemplate.c | 24++++++++++++++++--------
10 files changed, 264 insertions(+), 55 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -28,7 +28,7 @@ add_definitions(-DMOBLEY_MINOR="${MOBLEY_MINOR}") include_directories(".") -link_libraries(pthread event event_openssl crypto ssl evhtp) +link_libraries(pthread event event_openssl crypto ssl evhtp yaml) add_executable(${MOBLEY_NAME} main.c args.c @@ -38,6 +38,7 @@ add_executable(${MOBLEY_NAME} main.c html.c index.c template.c + config.c ) install(TARGETS ${MOBLEY_NAME} DESTINATION bin) diff --git a/config.c b/config.c @@ -0,0 +1,131 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#include <yaml.h> + +#include <config.h> + +# define COPY_OR_FAIL(dst, src) do { \ + dst = strdup((char *)src); \ + if (dst == NULL) \ + return false; \ +} while (0) + +static bool config_read_token(const char *tok, unsigned char *value, mobley_t *ctx) +{ + if (strcmp(tok, "static_dir") == 0) + COPY_OR_FAIL(ctx->conf.static_dir, value); + else if (strcmp(tok, "logo") == 0) + COPY_OR_FAIL(ctx->conf.logo, value); + else if (strcmp(tok, "desc") == 0) + COPY_OR_FAIL(ctx->conf.root_desc, value); + else if (strcmp(tok, "title") == 0) + COPY_OR_FAIL(ctx->conf.title, value); + else if (strcmp(tok, "address") == 0) + COPY_OR_FAIL(ctx->ip, value); + else if (strcmp(tok, "port") == 0) + return str_to_int32((char *)value, (int *)&ctx->port, 10); + else if (strcmp(tok, "styles") == 0) + string_array_node_add(&ctx->conf.styles, (char *)value); + else + { + printf("Unknown token = %s\n", tok); + return false; + } + + return true; +} + +bool config_read_file(const char *path, mobley_t *ctx) +{ + FILE *fd = fopen(path, "r"); + yaml_parser_t parser = { 0 }; + yaml_event_t event; + bool is_token = true; + bool is_sequence = false; + char *token = NULL; + bool ret = false; + + if (fd == NULL) + return false; + + if (!yaml_parser_initialize(&parser)) + return false; + + yaml_parser_set_input_file(&parser, fd); + + do { + if (!yaml_parser_parse(&parser, &event)) + goto end; + + switch(event.type) + { + case YAML_NO_EVENT: + case YAML_STREAM_START_EVENT: + case YAML_STREAM_END_EVENT: + case YAML_DOCUMENT_START_EVENT: + case YAML_DOCUMENT_END_EVENT: + case YAML_MAPPING_START_EVENT: + case YAML_MAPPING_END_EVENT: + case YAML_ALIAS_EVENT: + /* Nothing */ + break; + + case YAML_SEQUENCE_START_EVENT: + is_sequence = true; + break; + + case YAML_SEQUENCE_END_EVENT: + is_sequence = false; + is_token = true; + free(token); + break; + + case YAML_SCALAR_EVENT: + if (is_token) + { + token = strdup((const char *)event.data.scalar.value); + if (token == NULL) + goto end; + } + else + { + if (!config_read_token(token, event.data.scalar.value, ctx)) + goto end; + } + + if (!is_sequence) + { + if (!is_token) + free(token); + + is_token = (is_token ? false : true); + } + + break; + } + + if (event.type != YAML_STREAM_END_EVENT) + yaml_event_delete(&event); + + } while(event.type != YAML_STREAM_END_EVENT); + + ret = true; +end: + yaml_event_delete(&event); + yaml_parser_delete(&parser); + fclose(fd); + return ret; +} diff --git a/config.h b/config.h @@ -0,0 +1,23 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#ifndef CONFIG_H +# define CONFIG_H + +#include <mobley.h> + +bool config_read_file(const char *path, mobley_t *ctx); + +#endif /* CONFIG_H */ diff --git a/config.yaml b/config.yaml @@ -0,0 +1,10 @@ +address: 127.0.0.1 +port: 4242 + +static_dir: /usr/local/share/mobley/ +logo: logo.png +desc: Software Stuff +title: git:// + +styles: + - main.css diff --git a/html.c b/html.c @@ -22,7 +22,12 @@ void _html(server_req_t *r, const mobley_t *ctx, const char *b, const char *d, if (a->href != NULL) { - if (a->href[0] == '/') + if (a->is_static) + { + evbuffer_add_printf(r->req->buffer_out, + " href=\"http://%s:%d/static/%s\"", ctx->ip, ctx->port, a->href); + } + else if (a->href[0] == '/') { evbuffer_add_printf(r->req->buffer_out, " href=\"http://%s:%d%s\"", ctx->ip, ctx->port, a->href); diff --git a/html.h b/html.h @@ -40,7 +40,7 @@ void _html(server_req_t *r, const mobley_t *ctx, # define html_nd(b, ...) _html(r, ctx, b, NULL, &(html_args_t){__VA_ARGS__}); # define html_raw(s, ...) evbuffer_add_printf((r)->req->buffer_out, s, ##__VA_ARGS__) -# define html_open(s) evbuffer_add_printf((r)->req->buffer_out, "<" s ">"); +# define html_open(s, ...) _html(r, ctx, s, NULL, &(html_args_t){__VA_ARGS__}); # define html_close(s) evbuffer_add_printf((r)->req->buffer_out, "</" s ">") # define html_simple(s) html_open(s) diff --git a/main.c b/main.c @@ -22,9 +22,11 @@ #include <event2/util.h> #include <mobley.h> +#include <config.h> enum { OPT_VERSION = 0, + OPT_CONFIG_PATH, OPT_IP, OPT_PORT }; @@ -34,30 +36,23 @@ static opts_t options[] = { LONG_OPT("version"), SINGLE_OPT('v') }, - [OPT_IP] = { - LONG_OPT("addr"), - SINGLE_OPT('a'), - ARG_TYPE(OPT_TYPE_STRING) -#define DEFAULT_IP "127.0.0.1" - }, - [OPT_PORT] = { - LONG_OPT("port"), - SINGLE_OPT('p'), - ARG_TYPE(OPT_TYPE_INT) -#define DEFAULT_PORT 4242 + [OPT_CONFIG_PATH] = { + LONG_OPT("config"), + SINGLE_OPT('c'), + ARG_TYPE(OPT_TYPE_STRING), + REQUIRED } }; #define usage(str, ...) fprintf(stdout, str "\n", ##__VA_ARGS__) static void help(void) { - usage("Usage: " MOBLEY_NAME " [-a IP] [-p PORT] -[v]"); + usage("Usage: " MOBLEY_NAME " --config /etc/mobley.yaml -[v]"); usage("HTTP Git server"); usage(""); usage("Options:"); usage(" -v, --version Print software version"); - usage(" -a, --addr=IP IP Address to bind on. Default is " DEFAULT_IP); - usage(" -p, --port=PORT Port to bind on. Default is " STR(DEFAULT_PORT)); + usage(" -c, --config Configuration file path"); usage(""); usage("Sources are available at <https://git.ne02ptzero.me/mobley>"); usage("Found a bug? Please shoot me an email!"); @@ -86,19 +81,10 @@ static bool mobley_options(mobley_t *ctx) return false; } - if (IS_ARG_HERE(options, OPT_IP)) - ctx->ip = GET_STR_ARG(options, OPT_IP); - else - ctx->ip = DEFAULT_IP; - - if (IS_ARG_HERE(options, OPT_PORT)) - ctx->port = GET_INT_ARG(options, OPT_PORT); - else - ctx->port = DEFAULT_PORT; + INIT_LIST_HEAD(&ctx->conf.styles); - ctx->conf.static_dir = "/usr/local/share/mobley/"; - ctx->conf.logo = "logo.png"; - ctx->conf.root_desc = "Software Stuff"; + if (!config_read_file(GET_STR_ARG(options, OPT_CONFIG_PATH), ctx)) + return false; return true; } @@ -108,11 +94,21 @@ static void sighandler(int signal, short events, void *base) event_base_loopbreak(base); } +static void sig_reload(int signal, short events, void *mobley) +{ + mobley_t *ctx = mobley; + + mobley_conf_dtr(ctx); + INIT_LIST_HEAD(&ctx->conf.styles); + config_read_file(GET_STR_ARG(options, OPT_CONFIG_PATH), ctx); +} + int main(int ac, const char **av) { int ret = 1; mobley_t ctx = { 0 }; struct event *ev_sigint = NULL; + struct event *ev_sighup = NULL; if (!parse_args(ac, av, options, COUNT_OF(options), &help)) return 1; @@ -125,13 +121,19 @@ int main(int ac, const char **av) ev_sigint = evsignal_new(ctx.evbase, SIGINT, sighandler, ctx.evbase); evsignal_add(ev_sigint, NULL); + ev_sighup = evsignal_new(ctx.evbase, SIGHUP, sig_reload, &ctx); + evsignal_add(ev_sighup, NULL); if (!mobley_run(&ctx)) goto end; ret = 0; end: - event_free(ev_sigint); + if (ev_sigint != NULL) + event_free(ev_sigint); + if (ev_sighup != NULL) + event_free(ev_sighup); + mobley_dtr(&ctx); free_args(options, COUNT_OF(options)); return ret; diff --git a/mobley.h b/mobley.h @@ -24,24 +24,39 @@ # include <server.h> typedef struct { - const char *ip; + char *ip; uint16_t port; struct { - const char *static_dir; - const char *logo; - const char *root_desc; + char *static_dir; + char *logo; + char *root_desc; + char *title; + list_head_t styles; } conf; struct event_base *evbase; struct evhtp *htp; } mobley_t; +static inline void mobley_conf_dtr(mobley_t *ctx) +{ + free(ctx->ip); + free(ctx->conf.static_dir); + free(ctx->conf.logo); + free(ctx->conf.root_desc); + free(ctx->conf.title); + string_array_free(&ctx->conf.styles); +} + static inline void mobley_dtr(mobley_t *ctx) { + mobley_conf_dtr(ctx); + evhtp_unbind_socket(ctx->htp); evhtp_free(ctx->htp); - event_base_free(ctx->evbase); + if (ctx->evbase) + event_base_free(ctx->evbase); } static inline void mobley_config(mobley_t *ctx) diff --git a/str.h b/str.h @@ -163,6 +163,9 @@ static inline void string_array_free(list_head_t *head) { string_array_t *node, *tmp; + if (head->next == NULL) + return ; + list_for_each_entry_safe(node, tmp, head, node) { free(node->val); @@ -177,9 +180,30 @@ static inline void string_array_node_del(string_array_t *node) free(node); } -static inline bool str_to_string_array(const char *in, list_head_t *out, const char *delims) +static inline bool string_array_node_add(list_head_t *head, const char *val) { string_array_t *node; + + node = calloc(1, sizeof(*node)); + if (node == NULL) + goto fail; + + node->val = strdup(val); + if (node->val == NULL) + { + free(node); + goto fail; + } + + list_add_tail(&node->node, head); + + return true; +fail: + return false; +} + +static inline bool str_to_string_array(const char *in, list_head_t *out, const char *delims) +{ char *data = strdup(in), *ptr = data; char *line; @@ -191,18 +215,8 @@ static inline bool str_to_string_array(const char *in, list_head_t *out, const c if (line == NULL || strlen(line) == 0) continue; - node = calloc(1, sizeof(*node)); - if (node == NULL) - goto fail; - - node->val = strdup(line); - if (node->val == NULL) - { - free(node); + if (!string_array_node_add(out, line)) goto fail; - } - - list_add_tail(&node->node, out); } free(ptr); diff --git a/template.c b/template.c @@ -45,7 +45,7 @@ static void template_meta(const mobley_t *ctx, server_req_t *r) void template_header(const mobley_t *ctx, server_req_t *r) { html_open("ul") { - html_open("li class='logo'") { + html_open("li", .class = "logo") { html_nd("img", .src = ctx->conf.logo, .closing_slash = true, @@ -85,6 +85,7 @@ static void template_menu(const mobley_t *ctx, server_req_t *r) menu_entry_t global_menu[] = { { "Git", "/" }, { "Builds", "/~builds" }, + { "Reviews", "/~reviews" }, { "Issues", "/~issues" }, { "Documentation", "/~man" }, { "Meta", "/~meta" }, @@ -96,6 +97,8 @@ static void template_menu(const mobley_t *ctx, server_req_t *r) { "Branches", "branches" }, { "Releases", "releases" }, { "Issues", "issues" }, + { "Reviews", "reviews" }, + { "Builds", "builds" }, { "LICENSE", "license" } }; @@ -137,14 +140,19 @@ void template_begin(const mobley_t *ctx, server_req_t *r) template_meta(ctx, r); - html("title", "git://"); + html("title", ctx->conf.title); - html_nd("link", - .rel = "stylesheet", - .type = "text/css", - .href = "/static/main.css", - .closing_slash = true - ); + string_array_t *node; + list_for_each_entry(node, &ctx->conf.styles, node) + { + html_nd("link", + .rel = "stylesheet", + .type = "text/css", + .href = node->val, + .closing_slash = true, + .is_static = true + ); + } } html_close("head");