From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Mon, 13 Jan 2014 05:11:13 +0100 Subject: [PATCH 06/12] filter: add preliminary lua support In-Reply-To: <1389586279-23724-1-git-send-email-Jason@zx2c4.com> References: <1389586279-23724-1-git-send-email-Jason@zx2c4.com> Message-ID: <1389586279-23724-7-git-send-email-Jason@zx2c4.com> Signed-off-by: Jason A. Donenfeld --- cgit.h | 2 +- cgit.mk | 13 ++- filter.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 230 insertions(+), 69 deletions(-) diff --git a/cgit.h b/cgit.h index db34493..519d2af 100644 --- a/cgit.h +++ b/cgit.h @@ -61,13 +61,13 @@ struct cgit_filter { int (*close)(struct cgit_filter *); void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix); void (*cleanup)(struct cgit_filter *); + int argument_count; }; struct cgit_exec_filter { struct cgit_filter base; char *cmd; char **argv; - int extra_args; int old_stdout; int pipe_fh[2]; int pid; diff --git a/cgit.mk b/cgit.mk index 9d6dea8..71bbe8b 100644 --- a/cgit.mk +++ b/cgit.mk @@ -25,6 +25,16 @@ ifdef NO_C99_FORMAT CFLAGS += -DNO_C99_FORMAT endif +ifdef NO_LUA + CFLAGS += -DNO_LUA +else + CGIT_LIBS += -llua +endif + +CGIT_LIBS += -ldl + + + CGIT_OBJ_NAMES += cgit.o CGIT_OBJ_NAMES += cache.o CGIT_OBJ_NAMES += cmd.o @@ -61,9 +71,6 @@ $(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION $(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \ -DCGIT_VERSION='"$(CGIT_VERSION)"' -CGIT_LIBS += -ldl - - # Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not # handled by that and we must handle them ourselves. cgit_dep_files := $(foreach f,$(CGIT_OBJS),$(dir $f).depend/$(notdir $f).d) diff --git a/filter.c b/filter.c index 8990575..6f0e298 100644 --- a/filter.c +++ b/filter.c @@ -13,16 +13,73 @@ #include #include #include +#include +#ifndef NO_LUA +#include +#include +#include +#endif static ssize_t (*libc_write)(int fd, const void *buf, size_t count); -static ssize_t (*filter_write)(const void *buf, size_t count) = NULL; +static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL; +static struct cgit_filter *current_write_filter = NULL; + +static inline void reap_filter(struct cgit_filter *filter) +{ + if (filter && filter->cleanup) + filter->cleanup(filter); +} + +void cgit_cleanup_filters(void) +{ + int i; + reap_filter(ctx.cfg.about_filter); + reap_filter(ctx.cfg.commit_filter); + reap_filter(ctx.cfg.source_filter); + for (i = 0; i < cgit_repolist.count; ++i) { + reap_filter(cgit_repolist.repos[i].about_filter); + reap_filter(cgit_repolist.repos[i].commit_filter); + reap_filter(cgit_repolist.repos[i].source_filter); + } +} + +void cgit_init_filters(void) +{ + libc_write = dlsym(RTLD_NEXT, "write"); + if (!libc_write) + die("Could not locate libc's write function"); +} + +ssize_t write(int fd, const void *buf, size_t count) +{ + if (fd != STDOUT_FILENO || !filter_write) + return libc_write(fd, buf, count); + return filter_write(current_write_filter, buf, count); +} + +static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count)) +{ + /* We want to avoid buggy nested patterns. */ + assert(filter_write == NULL); + assert(current_write_filter == NULL); + current_write_filter = filter; + filter_write = new_write; +} + +static inline void unhook_write() +{ + assert(filter_write != NULL); + assert(current_write_filter != NULL); + filter_write = NULL; + current_write_filter = NULL; +} static int open_exec_filter(struct cgit_filter *base, va_list ap) { struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base; int i; - for (i = 0; i < filter->extra_args; i++) + for (i = 0; i < filter->base.argument_count; i++) filter->argv[i+1] = va_arg(ap, char *); filter->old_stdout = chk_positive(dup(STDOUT_FILENO), @@ -59,7 +116,7 @@ static int close_exec_filter(struct cgit_filter *base) die("Subprocess %s exited abnormally", filter->cmd); done: - for (i = 0; i < filter->extra_args; i++) + for (i = 0; i < filter->base.argument_count; i++) filter->argv[i+1] = NULL; return 0; @@ -78,9 +135,18 @@ static void cleanup_exec_filter(struct cgit_filter *base) free(filter->argv); filter->argv = NULL; } + +/* We can't enable this code block because cgit_exec_filter_init sometimes takes + * non-malloc'd cmd. Refactor the callers of that before uncommenting this block */ +#if 0 + if (filter->cmd) { + free(filter->cmd); + filter->cmd = NULL; + } +#endif } -static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filtertype) +static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count) { struct cgit_exec_filter *f; int args_size = 0; @@ -88,20 +154,8 @@ static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filterty f = xmalloc(sizeof(*f)); /* We leave argv for now and assign it below. */ cgit_exec_filter_init(f, xstrdup(cmd), NULL); - - switch (filtertype) { - case SOURCE: - case ABOUT: - f->extra_args = 1; - break; - - case COMMIT: - default: - f->extra_args = 0; - break; - } - - args_size = (2 + f->extra_args) * sizeof(char *); + f->base.argument_count = argument_count; + args_size = (2 + argument_count) * sizeof(char *); f->argv = xmalloc(args_size); memset(f->argv, 0, args_size); f->argv[0] = f->cmd; @@ -117,8 +171,135 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar filter->base.cleanup = cleanup_exec_filter; filter->cmd = cmd; filter->argv = argv; + /* The argument count for open_filter is zero by default, unless called from new_filter, above. */ + filter->base.argument_count = 0; +} + +#ifndef NO_LUA +struct lua_filter { + struct cgit_filter base; + char *script_file; + lua_State *lua_state; +}; + +static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_t count) +{ + struct lua_filter *filter = (struct lua_filter *) base; + + lua_getglobal(filter->lua_state, "filter_write"); + lua_pushlstring(filter->lua_state, buf, count); + if (lua_pcall(filter->lua_state, 1, 0, 0)) { + errno = EPROTO; + return -1; + } + return count; +} + +static int html_lua_filter(lua_State *lua_state) +{ + size_t len; + const char *str; + + str = lua_tostring(lua_state, 1); + if (!str) + return 0; + len = strlen(str); + libc_write(STDOUT_FILENO, str, len); + return 0; +} + +static void cleanup_lua_filter(struct cgit_filter *base) +{ + struct lua_filter *filter = (struct lua_filter *) base; + + if (!filter->lua_state) + return; + + lua_close(filter->lua_state); + filter->lua_state = NULL; + if (filter->script_file) { + free(filter->script_file); + filter->script_file = NULL; + } +} + +static int init_lua_filter(struct lua_filter *filter) +{ + if (filter->lua_state) + return 0; + + if (!(filter->lua_state = luaL_newstate())) + return 1; + + luaL_openlibs(filter->lua_state); + + lua_pushcfunction(filter->lua_state, html_lua_filter); + lua_setglobal(filter->lua_state, "html"); + + if (luaL_dofile(filter->lua_state, filter->script_file)) { + lua_close(filter->lua_state); + filter->lua_state = NULL; + return 1; + } + return 0; +} + +static int open_lua_filter(struct cgit_filter *base, va_list ap) +{ + struct lua_filter *filter = (struct lua_filter *) base; + int i; + + if (init_lua_filter(filter)) + return 1; + + hook_write(base, write_lua_filter); + + lua_getglobal(filter->lua_state, "filter_open"); + for (i = 0; i < filter->base.argument_count; ++i) + lua_pushstring(filter->lua_state, va_arg(ap, char *)); + if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0)) + return 1; + return 0; +} + +static int close_lua_filter(struct cgit_filter *base) +{ + struct lua_filter *filter = (struct lua_filter *) base; + int ret = 0; + + lua_getglobal(filter->lua_state, "filter_close"); + if (lua_pcall(filter->lua_state, 0, 0, 0)) + ret = 1; + unhook_write(); + return ret; +} + +static void fprintf_lua_filter(struct cgit_filter *base, FILE *f, const char *prefix) +{ + struct lua_filter *filter = (struct lua_filter *) base; + fprintf(f, "%slua:%s\n", prefix, filter->script_file); +} + + +static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count) +{ + struct lua_filter *filter; + + filter = xmalloc(sizeof(*filter)); + memset(filter, 0, sizeof(*filter)); + filter->base.open = open_lua_filter; + filter->base.close = close_lua_filter; + filter->base.fprintf = fprintf_lua_filter; + filter->base.cleanup = cleanup_lua_filter; + filter->base.argument_count = argument_count; + filter->script_file = xstrdup(cmd); + + return &filter->base; } +#endif + + int cgit_open_filter(struct cgit_filter *filter, ...) { int result; @@ -143,9 +324,12 @@ void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix static const struct { const char *prefix; - struct cgit_filter *(*ctor)(const char *cmd, filter_type filtertype); + struct cgit_filter *(*ctor)(const char *cmd, int argument_count); } filter_specs[] = { { "exec", new_exec_filter }, +#ifndef NO_LUA + { "lua", new_lua_filter }, +#endif }; struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) @@ -153,6 +337,8 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) char *colon; int i; size_t len; + int argument_count; + if (!cmd || !cmd[0]) return NULL; @@ -165,60 +351,28 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) if (len == 1) colon = NULL; + switch (filtertype) { + case SOURCE: + case ABOUT: + argument_count = 1; + break; + + case COMMIT: + default: + argument_count = 0; + break; + } + /* If no prefix is given, exec filter is the default. */ if (!colon) - return new_exec_filter(cmd, filtertype); + return new_exec_filter(cmd, argument_count); for (i = 0; i < ARRAY_SIZE(filter_specs); i++) { if (len == strlen(filter_specs[i].prefix) && !strncmp(filter_specs[i].prefix, cmd, len)) - return filter_specs[i].ctor(colon + 1, filtertype); + return filter_specs[i].ctor(colon + 1, argument_count); } die("Invalid filter type: %.*s", (int) len, cmd); } -static inline void reap_filter(struct cgit_filter *filter) -{ - if (filter && filter->cleanup) - filter->cleanup(filter); -} - -void cgit_cleanup_filters(void) -{ - int i; - reap_filter(ctx.cfg.about_filter); - reap_filter(ctx.cfg.commit_filter); - reap_filter(ctx.cfg.source_filter); - for (i = 0; i < cgit_repolist.count; ++i) { - reap_filter(cgit_repolist.repos[i].about_filter); - reap_filter(cgit_repolist.repos[i].commit_filter); - reap_filter(cgit_repolist.repos[i].source_filter); - } -} - -void cgit_init_filters(void) -{ - libc_write = dlsym(RTLD_NEXT, "write"); - if (!libc_write) - die("Could not locate libc's write function"); -} - -ssize_t write(int fd, const void *buf, size_t count) -{ - if (fd != STDOUT_FILENO || !filter_write) - return libc_write(fd, buf, count); - return filter_write(buf, count); -} - -static inline void hook_write(ssize_t (*new_write)(const void *buf, size_t count)) -{ - /* We want to avoid buggy nested patterns. */ - assert(filter_write == NULL); - filter_write = new_write; -} -static inline void unhook_write() -{ - assert(filter_write != NULL); - filter_write = NULL; -} -- 1.8.5.2