about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJason A. Donenfeld2014-01-13 14:16:18 +0100
committerJason A. Donenfeld2014-01-14 02:00:07 +0100
commite83b51b4f6bd53efea0c772e6ecdf1c5605ca611 (patch)
tree695797e4de7c7e413730f7ab66246327e6fa0046
parentfilter: allow for cleanup hook for filter types (diff)
downloadcgit-e83b51b4f6bd53efea0c772e6ecdf1c5605ca611.tar.gz
cgit-e83b51b4f6bd53efea0c772e6ecdf1c5605ca611.zip
filter: basic write hooking infrastructure
Filters can now call hook_write and unhook_write if they want to
redirect writing to stdout to a different function. This saves us from
potential file descriptor pipes and other less efficient mechanisms.

We do this instead of replacing the call in html_raw because some places
stdlib's printf functions are used (ui-patch or within git itself),
which has its own internal buffering, which makes it difficult to
interlace our function calls. So, we dlsym libc's write and then
override it in the link stage.

While we're at it, we move considerations of argument count into the
generic new filter handler.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--cgit.c2
-rw-r--r--cgit.h3
-rw-r--r--cgit.mk4
-rw-r--r--filter.c81
4 files changed, 67 insertions, 23 deletions
diff --git a/cgit.c b/cgit.c index 4f31e58..725fd65 100644 --- a/cgit.c +++ b/cgit.c
@@ -904,6 +904,8 @@ int main(int argc, const char **argv)
904 const char *path; 904 const char *path;
905 int err, ttl; 905 int err, ttl;
906 906
907 cgit_init_filters();
908
907 prepare_context(&ctx); 909 prepare_context(&ctx);
908 cgit_repolist.length = 0; 910 cgit_repolist.length = 0;
909 cgit_repolist.count = 0; 911 cgit_repolist.count = 0;
diff --git a/cgit.h b/cgit.h index 893c38f..519d2af 100644 --- a/cgit.h +++ b/cgit.h
@@ -61,13 +61,13 @@ struct cgit_filter {
61 int (*close)(struct cgit_filter *); 61 int (*close)(struct cgit_filter *);
62 void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix); 62 void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix);
63 void (*cleanup)(struct cgit_filter *); 63 void (*cleanup)(struct cgit_filter *);
64 int argument_count;
64}; 65};
65 66
66struct cgit_exec_filter { 67struct cgit_exec_filter {
67 struct cgit_filter base; 68 struct cgit_filter base;
68 char *cmd; 69 char *cmd;
69 char **argv; 70 char **argv;
70 int extra_args;
71 int old_stdout; 71 int old_stdout;
72 int pipe_fh[2]; 72 int pipe_fh[2];
73 int pid; 73 int pid;
@@ -357,6 +357,7 @@ extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char
357extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv); 357extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv);
358extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype); 358extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype);
359extern void cgit_cleanup_filters(void); 359extern void cgit_cleanup_filters(void);
360extern void cgit_init_filters(void);
360 361
361extern void cgit_prepare_repo_env(struct cgit_repo * repo); 362extern void cgit_prepare_repo_env(struct cgit_repo * repo);
362 363
diff --git a/cgit.mk b/cgit.mk index 19a76e7..9d6dea8 100644 --- a/cgit.mk +++ b/cgit.mk
@@ -61,6 +61,8 @@ $(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
61$(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \ 61$(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
62 -DCGIT_VERSION='"$(CGIT_VERSION)"' 62 -DCGIT_VERSION='"$(CGIT_VERSION)"'
63 63
64CGIT_LIBS += -ldl
65
64 66
65# Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not 67# Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not
66# handled by that and we must handle them ourselves. 68# handled by that and we must handle them ourselves.
@@ -88,4 +90,4 @@ $(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs)
88 $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $< 90 $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $<
89 91
90$(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS) 92$(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS)
91 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) 93 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS)
diff --git a/filter.c b/filter.c index 30bc74b..f5a5992 100644 --- a/filter.c +++ b/filter.c
@@ -12,6 +12,11 @@
12#include <unistd.h> 12#include <unistd.h>
13#include <string.h> 13#include <string.h>
14#include <stdlib.h> 14#include <stdlib.h>
15#include <dlfcn.h>
16
17static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
18static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL;
19static struct cgit_filter *current_write_filter = NULL;
15 20
16static inline void reap_filter(struct cgit_filter *filter) 21static inline void reap_filter(struct cgit_filter *filter)
17{ 22{
@@ -32,12 +37,43 @@ void cgit_cleanup_filters(void)
32 } 37 }
33} 38}
34 39
40void cgit_init_filters(void)
41{
42 libc_write = dlsym(RTLD_NEXT, "write");
43 if (!libc_write)
44 die("Could not locate libc's write function");
45}
46
47ssize_t write(int fd, const void *buf, size_t count)
48{
49 if (fd != STDOUT_FILENO || !filter_write)
50 return libc_write(fd, buf, count);
51 return filter_write(current_write_filter, buf, count);
52}
53
54static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count))
55{
56 /* We want to avoid buggy nested patterns. */
57 assert(filter_write == NULL);
58 assert(current_write_filter == NULL);
59 current_write_filter = filter;
60 filter_write = new_write;
61}
62
63static inline void unhook_write()
64{
65 assert(filter_write != NULL);
66 assert(current_write_filter != NULL);
67 filter_write = NULL;
68 current_write_filter = NULL;
69}
70
35static int open_exec_filter(struct cgit_filter *base, va_list ap) 71static int open_exec_filter(struct cgit_filter *base, va_list ap)
36{ 72{
37 struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base; 73 struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
38 int i; 74 int i;
39 75
40 for (i = 0; i < filter->extra_args; i++) 76 for (i = 0; i < filter->base.argument_count; i++)
41 filter->argv[i+1] = va_arg(ap, char *); 77 filter->argv[i+1] = va_arg(ap, char *);
42 78
43 filter->old_stdout = chk_positive(dup(STDOUT_FILENO), 79 filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
@@ -74,7 +110,7 @@ static int close_exec_filter(struct cgit_filter *base)
74 die("Subprocess %s exited abnormally", filter->cmd); 110 die("Subprocess %s exited abnormally", filter->cmd);
75 111
76done: 112done:
77 for (i = 0; i < filter->extra_args; i++) 113 for (i = 0; i < filter->base.argument_count; i++)
78 filter->argv[i+1] = NULL; 114 filter->argv[i+1] = NULL;
79 return 0; 115 return 0;
80 116
@@ -99,7 +135,7 @@ static void cleanup_exec_filter(struct cgit_filter *base)
99 } 135 }
100} 136}
101 137
102static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filtertype) 138static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count)
103{ 139{
104 struct cgit_exec_filter *f; 140 struct cgit_exec_filter *f;
105 int args_size = 0; 141 int args_size = 0;
@@ -107,20 +143,8 @@ static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filterty
107 f = xmalloc(sizeof(*f)); 143 f = xmalloc(sizeof(*f));
108 /* We leave argv for now and assign it below. */ 144 /* We leave argv for now and assign it below. */
109 cgit_exec_filter_init(f, xstrdup(cmd), NULL); 145 cgit_exec_filter_init(f, xstrdup(cmd), NULL);
110 146 f->base.argument_count = argument_count;
111 switch (filtertype) { 147 args_size = (2 + argument_count) * sizeof(char *);
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); 148 f->argv = xmalloc(args_size);
125 memset(f->argv, 0, args_size); 149 memset(f->argv, 0, args_size);
126 f->argv[0] = f->cmd; 150 f->argv[0] = f->cmd;
@@ -136,6 +160,8 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar
136 filter->base.cleanup = cleanup_exec_filter; 160 filter->base.cleanup = cleanup_exec_filter;
137 filter->cmd = cmd; 161 filter->cmd = cmd;
138 filter->argv = argv; 162 filter->argv = argv;
163 /* The argument count for open_filter is zero by default, unless called from new_filter, above. */
164 filter->base.argument_count = 0;
139} 165}
140 166
141int cgit_open_filter(struct cgit_filter *filter, ...) 167int cgit_open_filter(struct cgit_filter *filter, ...)
@@ -162,7 +188,7 @@ void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix
162 188
163static const struct { 189static const struct {
164 const char *prefix; 190 const char *prefix;
165 struct cgit_filter *(*ctor)(const char *cmd, filter_type filtertype); 191 struct cgit_filter *(*ctor)(const char *cmd, int argument_count);
166} filter_specs[] = { 192} filter_specs[] = {
167 { "exec", new_exec_filter }, 193 { "exec", new_exec_filter },
168}; 194};
@@ -172,6 +198,8 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
172 char *colon; 198 char *colon;
173 int i; 199 int i;
174 size_t len; 200 size_t len;
201 int argument_count;
202
175 if (!cmd || !cmd[0]) 203 if (!cmd || !cmd[0])
176 return NULL; 204 return NULL;
177 205
@@ -184,16 +212,27 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
184 if (len == 1) 212 if (len == 1)
185 colon = NULL; 213 colon = NULL;
186 214
215 switch (filtertype) {
216 case SOURCE:
217 case ABOUT:
218 argument_count = 1;
219 break;
220
221 case COMMIT:
222 default:
223 argument_count = 0;
224 break;
225 }
226
187 /* If no prefix is given, exec filter is the default. */ 227 /* If no prefix is given, exec filter is the default. */
188 if (!colon) 228 if (!colon)
189 return new_exec_filter(cmd, filtertype); 229 return new_exec_filter(cmd, argument_count);
190 230
191 for (i = 0; i < ARRAY_SIZE(filter_specs); i++) { 231 for (i = 0; i < ARRAY_SIZE(filter_specs); i++) {
192 if (len == strlen(filter_specs[i].prefix) && 232 if (len == strlen(filter_specs[i].prefix) &&
193 !strncmp(filter_specs[i].prefix, cmd, len)) 233 !strncmp(filter_specs[i].prefix, cmd, len))
194 return filter_specs[i].ctor(colon + 1, filtertype); 234 return filter_specs[i].ctor(colon + 1, argument_count);
195 } 235 }
196 236
197 die("Invalid filter type: %.*s", (int) len, cmd); 237 die("Invalid filter type: %.*s", (int) len, cmd);
198} 238}
199