initial commit
[project/firewall3.git] / utils.c
1 /*
2 * firewall3 - 3rd OpenWrt UCI firewall implementation
3 *
4 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "utils.h"
20 #include "options.h"
21
22 static int lock_fd = -1;
23 static pid_t pipe_pid = -1;
24 static FILE *pipe_fd = NULL;
25
26 static void
27 warn_elem_section_name(struct uci_section *s, bool find_name)
28 {
29 int i = 0;
30 struct uci_option *o;
31 struct uci_element *tmp;
32
33 if (s->anonymous)
34 {
35 uci_foreach_element(&s->package->sections, tmp)
36 {
37 if (strcmp(uci_to_section(tmp)->type, s->type))
38 continue;
39
40 if (&s->e == tmp)
41 break;
42
43 i++;
44 }
45
46 fprintf(stderr, "@%s[%d]", s->type, i);
47
48 if (find_name)
49 {
50 uci_foreach_element(&s->options, tmp)
51 {
52 o = uci_to_option(tmp);
53
54 if (!strcmp(tmp->name, "name") && (o->type == UCI_TYPE_STRING))
55 {
56 fprintf(stderr, " (%s)", o->v.string);
57 break;
58 }
59 }
60 }
61 }
62 else
63 {
64 fprintf(stderr, "'%s'", s->e.name);
65 }
66
67 if (find_name)
68 fprintf(stderr, " ");
69 }
70
71 void
72 warn_elem(struct uci_element *e, const char *format, ...)
73 {
74 if (e->type == UCI_TYPE_SECTION)
75 {
76 fprintf(stderr, "Warning: Section ");
77 warn_elem_section_name(uci_to_section(e), true);
78 }
79 else if (e->type == UCI_TYPE_OPTION)
80 {
81 fprintf(stderr, "Warning: Option ");
82 warn_elem_section_name(uci_to_option(e)->section, false);
83 fprintf(stderr, ".%s ", e->name);
84 }
85
86 va_list argptr;
87 va_start(argptr, format);
88 vfprintf(stderr, format, argptr);
89 va_end(argptr);
90
91 fprintf(stderr, "\n");
92 }
93
94 void
95 warn(const char* format, ...)
96 {
97 fprintf(stderr, "Warning: ");
98 va_list argptr;
99 va_start(argptr, format);
100 vfprintf(stderr, format, argptr);
101 va_end(argptr);
102 fprintf(stderr, "\n");
103 }
104
105 void
106 error(const char* format, ...)
107 {
108 fprintf(stderr, "Error: ");
109 va_list argptr;
110 va_start(argptr, format);
111 vfprintf(stderr, format, argptr);
112 va_end(argptr);
113 fprintf(stderr, "\n");
114
115 exit(1);
116 }
117
118 void
119 info(const char* format, ...)
120 {
121 va_list argptr;
122 va_start(argptr, format);
123 vfprintf(stderr, format, argptr);
124 va_end(argptr);
125 fprintf(stderr, "\n");
126 }
127
128 const char *
129 fw3_find_command(const char *cmd)
130 {
131 struct stat s;
132 int plen = 0, clen = strlen(cmd) + 1;
133 char *search, *p;
134 static char path[PATH_MAX];
135
136 if (!stat(cmd, &s) && S_ISREG(s.st_mode))
137 return cmd;
138
139 search = getenv("PATH");
140
141 if (!search)
142 search = "/bin:/usr/bin:/sbin:/usr/sbin";
143
144 p = search;
145
146 do
147 {
148 if (*p != ':' && *p != '\0')
149 continue;
150
151 plen = p - search;
152
153 if ((plen + clen) >= sizeof(path))
154 continue;
155
156 strncpy(path, search, plen);
157 sprintf(path + plen, "/%s", cmd);
158
159 if (!stat(path, &s) && S_ISREG(s.st_mode))
160 return path;
161
162 search = p + 1;
163 }
164 while (*p++);
165
166 return NULL;
167 }
168
169 bool
170 fw3_stdout_pipe(void)
171 {
172 pipe_fd = stdout;
173 return true;
174 }
175
176 bool
177 __fw3_command_pipe(bool silent, const char *command, ...)
178 {
179 pid_t pid;
180 va_list argp;
181 int pfds[2];
182 int argn;
183 char *arg, **args, **tmp;
184
185 command = fw3_find_command(command);
186
187 if (!command)
188 return false;
189
190 if (pipe(pfds))
191 return false;
192
193 argn = 2;
194 args = malloc(argn * sizeof(arg));
195
196 if (!args)
197 return false;
198
199 args[0] = (char *)command;
200 args[1] = NULL;
201
202 va_start(argp, command);
203
204 while ((arg = va_arg(argp, char *)) != NULL)
205 {
206 tmp = realloc(args, ++argn * sizeof(arg));
207
208 if (!tmp)
209 break;
210
211 args = tmp;
212 args[argn-2] = arg;
213 args[argn-1] = NULL;
214 }
215
216 va_end(argp);
217
218 switch ((pid = fork()))
219 {
220 case -1:
221 return false;
222
223 case 0:
224 dup2(pfds[0], 0);
225
226 close(pfds[0]);
227 close(pfds[1]);
228
229 close(1);
230
231 if (silent)
232 close(2);
233
234 execv(command, args);
235
236 default:
237 signal(SIGPIPE, SIG_IGN);
238 pipe_pid = pid;
239 close(pfds[0]);
240 }
241
242 pipe_fd = fdopen(pfds[1], "w");
243 return true;
244 }
245
246 void
247 fw3_pr(const char *fmt, ...)
248 {
249 va_list args;
250 va_start(args, fmt);
251 vfprintf(pipe_fd, fmt, args);
252 va_end(args);
253 }
254
255 void
256 fw3_command_close(void)
257 {
258 if (pipe_fd && pipe_fd != stdout)
259 fclose(pipe_fd);
260
261 if (pipe_pid > -1)
262 waitpid(pipe_pid, NULL, 0);
263
264 signal(SIGPIPE, SIG_DFL);
265
266 pipe_fd = NULL;
267 pipe_pid = -1;
268 }
269
270 bool
271 fw3_has_table(bool ipv6, const char *table)
272 {
273 FILE *f;
274
275 char line[12];
276 bool seen = false;
277
278 const char *path = ipv6
279 ? "/proc/net/ip6_tables_names" : "/proc/net/ip_tables_names";
280
281 if (!(f = fopen(path, "r")))
282 return false;
283
284 while (fgets(line, sizeof(line), f))
285 {
286 if (!strncmp(line, table, strlen(table)))
287 {
288 seen = true;
289 break;
290 }
291 }
292
293 fclose(f);
294
295 return seen;
296 }
297
298 bool
299 fw3_check_statefile(bool test_exists)
300 {
301 struct stat s;
302
303 if (!stat(FW3_STATEFILE, &s))
304 {
305 if (test_exists)
306 return true;
307
308 warn("The firewall appears to be started already. "
309 "If it is indeed empty, remove the %s file and retry.",
310 FW3_STATEFILE);
311
312 return false;
313 }
314 else if (test_exists)
315 {
316 warn("The firewall appears to stopped already.");
317 return false;
318 }
319
320 lock_fd = open(FW3_STATEFILE, O_CREAT | O_RDWR);
321
322 if (lock_fd < 0)
323 {
324 warn("Unable to create %s file", FW3_STATEFILE);
325 goto fail;
326 }
327
328 if (flock(lock_fd, LOCK_EX))
329 {
330 warn("Unable to acquire exclusive lock on %s file", FW3_STATEFILE);
331 goto fail;
332
333 }
334
335 return true;
336
337 fail:
338 if (lock_fd > -1)
339 {
340 close(lock_fd);
341 lock_fd = -1;
342 }
343
344 return false;
345 }
346
347 void
348 fw3_remove_statefile(void)
349 {
350 if (lock_fd > -1)
351 fw3_close_statefile();
352
353 if (unlink(FW3_STATEFILE))
354 warn("Unable to delete %s file", FW3_STATEFILE);
355 }
356
357 void
358 fw3_close_statefile(void)
359 {
360 flock(lock_fd, LOCK_UN);
361 close(lock_fd);
362
363 lock_fd = -1;
364 }