aboutsummaryrefslogtreecommitdiffstats
path: root/filter.c
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 /filter.c
parentd750c7a2c9bb83cfc47c0d74fcee61f0a5042aa9 (diff)
downloadcgit-e83b51b4f6bd53efea0c772e6ecdf1c5605ca611.tar.gz
cgit-e83b51b4f6bd53efea0c772e6ecdf1c5605ca611.tar.bz2
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>
Diffstat (limited to 'filter.c')
-rw-r--r--filter.c81
1 files changed, 60 insertions, 21 deletions
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