all repos — cgit @ v1.2.1

a hyperfast web frontend for git written in c

filter.c (view raw)

  1/* filter.c: filter framework functions
  2 *
  3 * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
  4 *
  5 * Licensed under GNU General Public License v2
  6 *   (see COPYING for full license text)
  7 */
  8
  9#include "cgit.h"
 10#include "html.h"
 11#ifndef NO_LUA
 12#include <dlfcn.h>
 13#include <lua.h>
 14#include <lualib.h>
 15#include <lauxlib.h>
 16#endif
 17
 18static inline void reap_filter(struct cgit_filter *filter)
 19{
 20	if (filter && filter->cleanup)
 21		filter->cleanup(filter);
 22}
 23
 24void cgit_cleanup_filters(void)
 25{
 26	int i;
 27	reap_filter(ctx.cfg.about_filter);
 28	reap_filter(ctx.cfg.commit_filter);
 29	reap_filter(ctx.cfg.source_filter);
 30	reap_filter(ctx.cfg.email_filter);
 31	reap_filter(ctx.cfg.owner_filter);
 32	reap_filter(ctx.cfg.auth_filter);
 33	for (i = 0; i < cgit_repolist.count; ++i) {
 34		reap_filter(cgit_repolist.repos[i].about_filter);
 35		reap_filter(cgit_repolist.repos[i].commit_filter);
 36		reap_filter(cgit_repolist.repos[i].source_filter);
 37		reap_filter(cgit_repolist.repos[i].email_filter);
 38		reap_filter(cgit_repolist.repos[i].owner_filter);
 39	}
 40}
 41
 42static int open_exec_filter(struct cgit_filter *base, va_list ap)
 43{
 44	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
 45	int pipe_fh[2];
 46	int i;
 47
 48	for (i = 0; i < filter->base.argument_count; i++)
 49		filter->argv[i + 1] = va_arg(ap, char *);
 50
 51	filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
 52		"Unable to duplicate STDOUT");
 53	chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess");
 54	filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
 55	if (filter->pid == 0) {
 56		close(pipe_fh[1]);
 57		chk_non_negative(dup2(pipe_fh[0], STDIN_FILENO),
 58			"Unable to use pipe as STDIN");
 59		execvp(filter->cmd, filter->argv);
 60		die_errno("Unable to exec subprocess %s", filter->cmd);
 61	}
 62	close(pipe_fh[0]);
 63	chk_non_negative(dup2(pipe_fh[1], STDOUT_FILENO),
 64		"Unable to use pipe as STDOUT");
 65	close(pipe_fh[1]);
 66	return 0;
 67}
 68
 69static int close_exec_filter(struct cgit_filter *base)
 70{
 71	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
 72	int i, exit_status = 0;
 73
 74	chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
 75		"Unable to restore STDOUT");
 76	close(filter->old_stdout);
 77	if (filter->pid < 0)
 78		goto done;
 79	waitpid(filter->pid, &exit_status, 0);
 80	if (WIFEXITED(exit_status))
 81		goto done;
 82	die("Subprocess %s exited abnormally", filter->cmd);
 83
 84done:
 85	for (i = 0; i < filter->base.argument_count; i++)
 86		filter->argv[i + 1] = NULL;
 87	return WEXITSTATUS(exit_status);
 88
 89}
 90
 91static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *prefix)
 92{
 93	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
 94	fprintf(f, "%sexec:%s\n", prefix, filter->cmd);
 95}
 96
 97static void cleanup_exec_filter(struct cgit_filter *base)
 98{
 99	struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
100	if (filter->argv) {
101		free(filter->argv);
102		filter->argv = NULL;
103	}
104	if (filter->cmd) {
105		free(filter->cmd);
106		filter->cmd = NULL;
107	}
108}
109
110static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count)
111{
112	struct cgit_exec_filter *f;
113	int args_size = 0;
114
115	f = xmalloc(sizeof(*f));
116	/* We leave argv for now and assign it below. */
117	cgit_exec_filter_init(f, xstrdup(cmd), NULL);
118	f->base.argument_count = argument_count;
119	args_size = (2 + argument_count) * sizeof(char *);
120	f->argv = xmalloc(args_size);
121	memset(f->argv, 0, args_size);
122	f->argv[0] = f->cmd;
123	return &f->base;
124}
125
126void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv)
127{
128	memset(filter, 0, sizeof(*filter));
129	filter->base.open = open_exec_filter;
130	filter->base.close = close_exec_filter;
131	filter->base.fprintf = fprintf_exec_filter;
132	filter->base.cleanup = cleanup_exec_filter;
133	filter->cmd = cmd;
134	filter->argv = argv;
135	/* The argument count for open_filter is zero by default, unless called from new_filter, above. */
136	filter->base.argument_count = 0;
137}
138
139#ifdef NO_LUA
140void cgit_init_filters(void)
141{
142}
143#endif
144
145#ifndef NO_LUA
146static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
147static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL;
148static struct cgit_filter *current_write_filter = NULL;
149
150void cgit_init_filters(void)
151{
152	libc_write = dlsym(RTLD_NEXT, "write");
153	if (!libc_write)
154		die("Could not locate libc's write function");
155}
156
157ssize_t write(int fd, const void *buf, size_t count)
158{
159	if (fd != STDOUT_FILENO || !filter_write)
160		return libc_write(fd, buf, count);
161	return filter_write(current_write_filter, buf, count);
162}
163
164static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count))
165{
166	/* We want to avoid buggy nested patterns. */
167	assert(filter_write == NULL);
168	assert(current_write_filter == NULL);
169	current_write_filter = filter;
170	filter_write = new_write;
171}
172
173static inline void unhook_write(void)
174{
175	assert(filter_write != NULL);
176	assert(current_write_filter != NULL);
177	filter_write = NULL;
178	current_write_filter = NULL;
179}
180
181struct lua_filter {
182	struct cgit_filter base;
183	char *script_file;
184	lua_State *lua_state;
185};
186
187static void error_lua_filter(struct lua_filter *filter)
188{
189	die("Lua error in %s: %s", filter->script_file, lua_tostring(filter->lua_state, -1));
190	lua_pop(filter->lua_state, 1);
191}
192
193static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_t count)
194{
195	struct lua_filter *filter = (struct lua_filter *)base;
196
197	lua_getglobal(filter->lua_state, "filter_write");
198	lua_pushlstring(filter->lua_state, buf, count);
199	if (lua_pcall(filter->lua_state, 1, 0, 0)) {
200		error_lua_filter(filter);
201		errno = EIO;
202		return -1;
203	}
204	return count;
205}
206
207static inline int hook_lua_filter(lua_State *lua_state, void (*fn)(const char *txt))
208{
209	const char *str;
210	ssize_t (*save_filter_write)(struct cgit_filter *base, const void *buf, size_t count);
211	struct cgit_filter *save_filter;
212
213	str = lua_tostring(lua_state, 1);
214	if (!str)
215		return 0;
216
217	save_filter_write = filter_write;
218	save_filter = current_write_filter;
219	unhook_write();
220	fn(str);
221	hook_write(save_filter, save_filter_write);
222
223	return 0;
224}
225
226static int html_lua_filter(lua_State *lua_state)
227{
228	return hook_lua_filter(lua_state, html);
229}
230
231static int html_txt_lua_filter(lua_State *lua_state)
232{
233	return hook_lua_filter(lua_state, html_txt);
234}
235
236static int html_attr_lua_filter(lua_State *lua_state)
237{
238	return hook_lua_filter(lua_state, html_attr);
239}
240
241static int html_url_path_lua_filter(lua_State *lua_state)
242{
243	return hook_lua_filter(lua_state, html_url_path);
244}
245
246static int html_url_arg_lua_filter(lua_State *lua_state)
247{
248	return hook_lua_filter(lua_state, html_url_arg);
249}
250
251static int html_include_lua_filter(lua_State *lua_state)
252{
253	return hook_lua_filter(lua_state, (void (*)(const char *))html_include);
254}
255
256static void cleanup_lua_filter(struct cgit_filter *base)
257{
258	struct lua_filter *filter = (struct lua_filter *)base;
259
260	if (!filter->lua_state)
261		return;
262
263	lua_close(filter->lua_state);
264	filter->lua_state = NULL;
265	if (filter->script_file) {
266		free(filter->script_file);
267		filter->script_file = NULL;
268	}
269}
270
271static int init_lua_filter(struct lua_filter *filter)
272{
273	if (filter->lua_state)
274		return 0;
275
276	if (!(filter->lua_state = luaL_newstate()))
277		return 1;
278
279	luaL_openlibs(filter->lua_state);
280
281	lua_pushcfunction(filter->lua_state, html_lua_filter);
282	lua_setglobal(filter->lua_state, "html");
283	lua_pushcfunction(filter->lua_state, html_txt_lua_filter);
284	lua_setglobal(filter->lua_state, "html_txt");
285	lua_pushcfunction(filter->lua_state, html_attr_lua_filter);
286	lua_setglobal(filter->lua_state, "html_attr");
287	lua_pushcfunction(filter->lua_state, html_url_path_lua_filter);
288	lua_setglobal(filter->lua_state, "html_url_path");
289	lua_pushcfunction(filter->lua_state, html_url_arg_lua_filter);
290	lua_setglobal(filter->lua_state, "html_url_arg");
291	lua_pushcfunction(filter->lua_state, html_include_lua_filter);
292	lua_setglobal(filter->lua_state, "html_include");
293
294	if (luaL_dofile(filter->lua_state, filter->script_file)) {
295		error_lua_filter(filter);
296		lua_close(filter->lua_state);
297		filter->lua_state = NULL;
298		return 1;
299	}
300	return 0;
301}
302
303static int open_lua_filter(struct cgit_filter *base, va_list ap)
304{
305	struct lua_filter *filter = (struct lua_filter *)base;
306	int i;
307
308	if (init_lua_filter(filter))
309		return 1;
310
311	hook_write(base, write_lua_filter);
312
313	lua_getglobal(filter->lua_state, "filter_open");
314	for (i = 0; i < filter->base.argument_count; ++i)
315		lua_pushstring(filter->lua_state, va_arg(ap, char *));
316	if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0)) {
317		error_lua_filter(filter);
318		return 1;
319	}
320	return 0;
321}
322
323static int close_lua_filter(struct cgit_filter *base)
324{
325	struct lua_filter *filter = (struct lua_filter *)base;
326	int ret = 0;
327
328	lua_getglobal(filter->lua_state, "filter_close");
329	if (lua_pcall(filter->lua_state, 0, 1, 0)) {
330		error_lua_filter(filter);
331		ret = -1;
332	} else {
333		ret = lua_tonumber(filter->lua_state, -1);
334		lua_pop(filter->lua_state, 1);
335	}
336
337	unhook_write();
338	return ret;
339}
340
341static void fprintf_lua_filter(struct cgit_filter *base, FILE *f, const char *prefix)
342{
343	struct lua_filter *filter = (struct lua_filter *)base;
344	fprintf(f, "%slua:%s\n", prefix, filter->script_file);
345}
346
347
348static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count)
349{
350	struct lua_filter *filter;
351
352	filter = xmalloc(sizeof(*filter));
353	memset(filter, 0, sizeof(*filter));
354	filter->base.open = open_lua_filter;
355	filter->base.close = close_lua_filter;
356	filter->base.fprintf = fprintf_lua_filter;
357	filter->base.cleanup = cleanup_lua_filter;
358	filter->base.argument_count = argument_count;
359	filter->script_file = xstrdup(cmd);
360
361	return &filter->base;
362}
363
364#endif
365
366
367int cgit_open_filter(struct cgit_filter *filter, ...)
368{
369	int result;
370	va_list ap;
371	if (!filter)
372		return 0;
373	va_start(ap, filter);
374	result = filter->open(filter, ap);
375	va_end(ap);
376	return result;
377}
378
379int cgit_close_filter(struct cgit_filter *filter)
380{
381	if (!filter)
382		return 0;
383	return filter->close(filter);
384}
385
386void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
387{
388	filter->fprintf(filter, f, prefix);
389}
390
391
392
393static const struct {
394	const char *prefix;
395	struct cgit_filter *(*ctor)(const char *cmd, int argument_count);
396} filter_specs[] = {
397	{ "exec", new_exec_filter },
398#ifndef NO_LUA
399	{ "lua", new_lua_filter },
400#endif
401};
402
403struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
404{
405	char *colon;
406	int i;
407	size_t len;
408	int argument_count;
409
410	if (!cmd || !cmd[0])
411		return NULL;
412
413	colon = strchr(cmd, ':');
414	len = colon - cmd;
415	/*
416	 * In case we're running on Windows, don't allow a single letter before
417	 * the colon.
418	 */
419	if (len == 1)
420		colon = NULL;
421
422	switch (filtertype) {
423		case AUTH:
424			argument_count = 12;
425			break;
426
427		case EMAIL:
428			argument_count = 2;
429			break;
430
431		case OWNER:
432			argument_count = 0;
433			break;
434
435		case SOURCE:
436		case ABOUT:
437			argument_count = 1;
438			break;
439
440		case COMMIT:
441		default:
442			argument_count = 0;
443			break;
444	}
445
446	/* If no prefix is given, exec filter is the default. */
447	if (!colon)
448		return new_exec_filter(cmd, argument_count);
449
450	for (i = 0; i < ARRAY_SIZE(filter_specs); i++) {
451		if (len == strlen(filter_specs[i].prefix) &&
452		    !strncmp(filter_specs[i].prefix, cmd, len))
453			return filter_specs[i].ctor(colon + 1, argument_count);
454	}
455
456	die("Invalid filter type: %.*s", (int) len, cmd);
457}