all repos — cgit @ 4bb87cbf17588ec91b46bf0ef0be01672e9be787

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