]> gitweb.ps.run Git - ps-cgit/blob - filter.c
filter: allow for cleanup hook for filter types
[ps-cgit] / filter.c
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
16 static inline void reap_filter(struct cgit_filter *filter)
17 {
18         if (filter && filter->cleanup)
19                 filter->cleanup(filter);
20 }
21
22 void 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
35 static 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
61 static 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
76 done:
77         for (i = 0; i < filter->extra_args; i++)
78                 filter->argv[i+1] = NULL;
79         return 0;
80
81 }
82
83 static 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
89 static 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
102 static 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
130 void 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
141 int 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
151 int cgit_close_filter(struct cgit_filter *filter)
152 {
153         return filter->close(filter);
154 }
155
156 void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
157 {
158         filter->fprintf(filter, f, prefix);
159 }
160
161
162
163 static 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
170 struct 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