all repos — cgit @ d750c7a2c9bb83cfc47c0d74fcee61f0a5042aa9

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 <sys/types.h>
 11#include <sys/wait.h>
 12#include <unistd.h>
 13#include <string.h>
 14#include <stdlib.h>
 15
 16static inline void reap_filter(struct cgit_filter *filter)
 17{
 18	if (filter && filter->cleanup)
 19		filter->cleanup(filter);
 20}
 21
 22void cgit_cleanup_filters(void)
 23{
 24	int i;
 25	reap_filter(ctx.cfg.about_filter);
 26	reap_filter(ctx.cfg.commit_filter);
 27	reap_filter(ctx.cfg.source_filter);
 28	for (i = 0; i < cgit_repolist.count; ++i) {
 29		reap_filter(cgit_repolist.repos[i].about_filter);
 30		reap_filter(cgit_repolist.repos[i].commit_filter);
 31		reap_filter(cgit_repolist.repos[i].source_filter);
 32	}
 33}
 34
 35static int open_exec_filter(struct cgit_filter *base, va_list ap)
 36{
 37	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
 38	int i;
 39
 40	for (i = 0; i < filter->extra_args; i++)
 41		filter->argv[i+1] = va_arg(ap, char *);
 42
 43	filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
 44		"Unable to duplicate STDOUT");
 45	chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
 46	filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
 47	if (filter->pid == 0) {
 48		close(filter->pipe_fh[1]);
 49		chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
 50			"Unable to use pipe as STDIN");
 51		execvp(filter->cmd, filter->argv);
 52		die_errno("Unable to exec subprocess %s", filter->cmd);
 53	}
 54	close(filter->pipe_fh[0]);
 55	chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
 56		"Unable to use pipe as STDOUT");
 57	close(filter->pipe_fh[1]);
 58	return 0;
 59}
 60
 61static int close_exec_filter(struct cgit_filter *base)
 62{
 63	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
 64	int i, exit_status;
 65
 66	chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
 67		"Unable to restore STDOUT");
 68	close(filter->old_stdout);
 69	if (filter->pid < 0)
 70		goto done;
 71	waitpid(filter->pid, &exit_status, 0);
 72	if (WIFEXITED(exit_status) && !WEXITSTATUS(exit_status))
 73		goto done;
 74	die("Subprocess %s exited abnormally", filter->cmd);
 75
 76done:
 77	for (i = 0; i < filter->extra_args; i++)
 78		filter->argv[i+1] = NULL;
 79	return 0;
 80
 81}
 82
 83static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *prefix)
 84{
 85	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
 86	fprintf(f, "%sexec:%s\n", prefix, filter->cmd);
 87}
 88
 89static void cleanup_exec_filter(struct cgit_filter *base)
 90{
 91	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
 92	if (filter->argv) {
 93		free(filter->argv);
 94		filter->argv = NULL;
 95	}
 96	if (filter->cmd) {
 97		free(filter->cmd);
 98		filter->cmd = NULL;
 99	}
100}
101
102static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filtertype)
103{
104	struct cgit_exec_filter *f;
105	int args_size = 0;
106
107	f = xmalloc(sizeof(*f));
108	/* We leave argv for now and assign it below. */
109	cgit_exec_filter_init(f, xstrdup(cmd), NULL);
110
111	switch (filtertype) {
112		case SOURCE:
113		case ABOUT:
114			f->extra_args = 1;
115			break;
116
117		case COMMIT:
118		default:
119			f->extra_args = 0;
120			break;
121	}
122
123	args_size = (2 + f->extra_args) * sizeof(char *);
124	f->argv = xmalloc(args_size);
125	memset(f->argv, 0, args_size);
126	f->argv[0] = f->cmd;
127	return &f->base;
128}
129
130void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv)
131{
132	memset(filter, 0, sizeof(*filter));
133	filter->base.open = open_exec_filter;
134	filter->base.close = close_exec_filter;
135	filter->base.fprintf = fprintf_exec_filter;
136	filter->base.cleanup = cleanup_exec_filter;
137	filter->cmd = cmd;
138	filter->argv = argv;
139}
140
141int cgit_open_filter(struct cgit_filter *filter, ...)
142{
143	int result;
144	va_list ap;
145	va_start(ap, filter);
146	result = filter->open(filter, ap);
147	va_end(ap);
148	return result;
149}
150
151int cgit_close_filter(struct cgit_filter *filter)
152{
153	return filter->close(filter);
154}
155
156void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
157{
158	filter->fprintf(filter, f, prefix);
159}
160
161
162
163static const struct {
164	const char *prefix;
165	struct cgit_filter *(*ctor)(const char *cmd, filter_type filtertype);
166} filter_specs[] = {
167	{ "exec", new_exec_filter },
168};
169
170struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
171{
172	char *colon;
173	int i;
174	size_t len;
175	if (!cmd || !cmd[0])
176		return NULL;
177
178	colon = strchr(cmd, ':');
179	len = colon - cmd;
180	/*
181	 * In case we're running on Windows, don't allow a single letter before
182	 * the colon.
183	 */
184	if (len == 1)
185		colon = NULL;
186
187	/* If no prefix is given, exec filter is the default. */
188	if (!colon)
189		return new_exec_filter(cmd, filtertype);
190
191	for (i = 0; i < ARRAY_SIZE(filter_specs); i++) {
192		if (len == strlen(filter_specs[i].prefix) &&
193		    !strncmp(filter_specs[i].prefix, cmd, len))
194			return filter_specs[i].ctor(colon + 1, filtertype);
195	}
196
197	die("Invalid filter type: %.*s", (int) len, cmd);
198}
199