Mercurial > syslog2iptables
comparison src/syslog2iptables.cpp @ 36:6a2f26976898
shutdown removes iptables entries that we added
author | carl |
---|---|
date | Thu, 08 Nov 2007 10:52:56 -0800 |
parents | 00bd0b0ef015 |
children | 26c29da3fbdf |
comparison
equal
deleted
inserted
replaced
35:d2ceebcf6595 | 36:6a2f26976898 |
---|---|
1 /*************************************************************************** | 1 /* |
2 * Copyright (C) 2005 by 510 Software Group * | 2 |
3 * * | 3 Copyright (c) 2007 Carl Byington - 510 Software Group, released under |
4 * * | 4 the GPL version 3 or any later version at your choice available at |
5 * This program is free software; you can redistribute it and/or modify * | 5 http://www.gnu.org/licenses/gpl-3.0.txt |
6 * it under the terms of the GNU General Public License as published by * | 6 |
7 * the Free Software Foundation; either version 2 of the License, or * | 7 */ |
8 * (at your option) any later version. * | 8 |
9 * * | |
10 * This program is distributed in the hope that it will be useful, * | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
13 * GNU General Public License for more details. * | |
14 * * | |
15 * You should have received a copy of the GNU General Public License * | |
16 * along with this program; if not, write to the * | |
17 * Free Software Foundation, Inc., * | |
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |
19 ***************************************************************************/ | |
20 | 9 |
21 // debug levels: | 10 // debug levels: |
22 // 4 - show syslog lines that match regex | 11 // 4 - show syslog lines that match regex |
23 // 3 - show addresses being dropped/released from the filter | 12 // 3 - show addresses being dropped/released from the filter |
24 // 2 - show files open/close | 13 // 2 - show files open/close |
29 #include <cstdlib> | 18 #include <cstdlib> |
30 #include <errno.h> | 19 #include <errno.h> |
31 #include <sysexits.h> | 20 #include <sysexits.h> |
32 #include <pthread.h> | 21 #include <pthread.h> |
33 #include <syslog.h> | 22 #include <syslog.h> |
34 #include <sys/wait.h> /* header for waitpid() and various macros */ | 23 #include <sys/wait.h> /* header for waitpid() and various macros */ |
35 #include <signal.h> /* header for signal functions */ | 24 #include <signal.h> /* header for signal functions */ |
36 | 25 |
37 static char* syslog2iptables_version = "$Id$"; | 26 static char* syslog2iptables_version = "$Id$"; |
38 | 27 |
39 extern "C" { | 28 extern "C" { |
40 void sig_chld(int signo); | 29 void sigchld(int sig); |
30 void sigterm(int sig); | |
41 } | 31 } |
42 int debug_syslog = 0; | 32 int debug_syslog = 0; |
43 bool syslog_opened = false; | 33 bool syslog_opened = false; |
44 bool use_syslog = true; // false to printf | 34 bool use_syslog = true; // false to printf |
45 bool loader_run = true; // used to stop the config loader thread | 35 bool loader_run = true; // used to stop the config loader thread |
46 CONFIG *config = NULL; // protected by the config_mutex | 36 CONFIG *config = NULL; // protected by the config_mutex |
47 int generation = 0; // protected by the config_mutex | 37 int generation = 0; // protected by the config_mutex |
48 const int maxlen = 1000; // used for snprintf buffers | 38 const int maxlen = 1000; // used for snprintf buffers |
49 | 39 |
50 pthread_mutex_t config_mutex; | 40 pthread_mutex_t config_mutex; |
51 pthread_mutex_t syslog_mutex; | 41 pthread_mutex_t syslog_mutex; |
52 | 42 |
53 | 43 |
54 //////////////////////////////////////////////// | 44 //////////////////////////////////////////////// |
55 // syslog a message | 45 // syslog a message |
56 // | 46 // |
57 void my_syslog(char *text) { | 47 void my_syslog(char *text) { |
58 if (use_syslog) { | 48 if (use_syslog) { |
59 pthread_mutex_lock(&syslog_mutex); | 49 pthread_mutex_lock(&syslog_mutex); |
60 if (!syslog_opened) { | 50 if (!syslog_opened) { |
61 openlog("syslog2iptables", LOG_PID, LOG_AUTHPRIV); | 51 openlog("syslog2iptables", LOG_PID, LOG_AUTHPRIV); |
62 syslog_opened = true; | 52 syslog_opened = true; |
63 } | 53 } |
64 syslog(LOG_NOTICE, "%s", text); | 54 syslog(LOG_NOTICE, "%s", text); |
65 pthread_mutex_unlock(&syslog_mutex); | 55 pthread_mutex_unlock(&syslog_mutex); |
66 } | 56 } |
67 else { | 57 else { |
68 printf("%s\n", text); | 58 printf("%s\n", text); |
69 } | 59 } |
70 } | 60 } |
71 | 61 |
72 //////////////////////////////////////////////// | 62 //////////////////////////////////////////////// |
73 // reload the config | 63 // reload the config |
74 // | 64 // |
75 CONFIG* new_conf(); | 65 CONFIG* new_conf(); |
76 CONFIG* new_conf() { | 66 CONFIG* new_conf() { |
77 CONFIG *newc = new CONFIG; | 67 CONFIG *newc = new CONFIG; |
78 pthread_mutex_lock(&config_mutex); | 68 pthread_mutex_lock(&config_mutex); |
79 newc->generation = generation++; | 69 newc->generation = generation++; |
80 pthread_mutex_unlock(&config_mutex); | 70 pthread_mutex_unlock(&config_mutex); |
81 if (debug_syslog) { | 71 if (debug_syslog) { |
82 char buf[maxlen]; | 72 char buf[maxlen]; |
83 snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation); | 73 snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation); |
84 my_syslog(buf); | 74 my_syslog(buf); |
85 } | 75 } |
86 if (load_conf(*newc, "syslog2iptables.conf")) { | 76 if (load_conf(*newc, "syslog2iptables.conf")) { |
87 newc->load_time = time(NULL); | 77 newc->load_time = time(NULL); |
88 return newc; | 78 return newc; |
89 } | 79 } |
90 delete newc; | 80 delete newc; |
91 return NULL; | 81 return NULL; |
92 } | 82 } |
93 | 83 |
94 | 84 |
95 //////////////////////////////////////////////// | 85 //////////////////////////////////////////////// |
96 // thread to watch the old config files for changes | 86 // thread to watch the old config files for changes |
97 // and reload when needed. we also cleanup old | 87 // and reload when needed. |
98 // configs whose reference count has gone to zero. | |
99 // | 88 // |
100 void* config_loader(void *arg); | 89 void* config_loader(void *arg); |
101 void* config_loader(void *arg) { | 90 void* config_loader(void *arg) { |
102 typedef set<CONFIG *> configp_set; | 91 typedef set<CONFIG *> configp_set; |
103 configp_set old_configs; | 92 while (loader_run) { |
104 while (loader_run) { | 93 sleep(180); // look for modifications every 3 minutes |
105 sleep(180); // look for modifications every 3 minutes | 94 if (!loader_run) break; |
106 if (!loader_run) break; | 95 CONFIG &dc = *config; |
107 CONFIG &dc = *config; | 96 time_t then = dc.load_time; |
108 time_t then = dc.load_time; | 97 struct stat st; |
109 struct stat st; | 98 bool reload = false; |
110 bool reload = false; | 99 for (string_set::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { |
111 for (string_set::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { | 100 char *fn = *i; |
112 char *fn = *i; | 101 if (stat(fn, &st)) reload = true; // file disappeared |
113 if (stat(fn, &st)) reload = true; // file disappeared | 102 else if (st.st_mtime > then) reload = true; // file modified |
114 else if (st.st_mtime > then) reload = true; // file modified | 103 if (reload) break; |
115 if (reload) break; | 104 } |
116 } | 105 if (reload) { |
117 if (reload) { | 106 CONFIG *newc = new_conf(); |
118 CONFIG *newc = new_conf(); | 107 if (newc) { |
119 if (newc) { | 108 // replace the global config pointer |
120 // replace the global config pointer | 109 pthread_mutex_lock(&config_mutex); |
121 pthread_mutex_lock(&config_mutex); | 110 config = newc; |
122 CONFIG *old = config; | 111 pthread_mutex_unlock(&config_mutex); |
123 config = newc; | 112 } |
124 pthread_mutex_unlock(&config_mutex); | 113 else { |
125 if (old) old_configs.insert(old); | 114 // failed to load new config |
126 } | 115 my_syslog("failed to load new configuration"); |
127 else { | 116 system("echo 'failed to load new /etc/syslog2iptables.conf' | mail -s 'error in /etc/syslog2iptables.conf' root"); |
128 // failed to load new config | 117 // update the load time on the current config to prevent complaining every 3 minutes |
129 my_syslog("failed to load new configuration"); | 118 dc.load_time = time(NULL); |
130 system("echo 'failed to load new /etc/syslog2iptables.conf' | mail -s 'error in /etc/syslog2iptables.conf' root"); | 119 } |
131 // update the load time on the current config to prevent complaining every 3 minutes | 120 } |
132 dc.load_time = time(NULL); | 121 } |
133 } | 122 return NULL; |
134 } | 123 } |
135 // now look for old configs with zero ref counts | 124 |
136 for (configp_set::iterator i=old_configs.begin(); i!=old_configs.end(); ) { | 125 |
137 CONFIG *old = *i; | 126 //////////////////////////////////////////////// |
138 if (!old->reference_count) { | 127 // The signal handler function for child process terminations, |
139 if (debug_syslog) { | 128 // called when a child terminates. |
140 char buf[maxlen]; | 129 // |
141 snprintf(buf, sizeof(buf), "freeing memory for old configuration generation %d", old->generation); | 130 void sigchld(int sig) |
142 my_syslog(buf); | 131 { |
143 } | 132 int status; |
144 delete old; // destructor does all the work | 133 /* Wait for any child without blocking */ |
145 old_configs.erase(i++); | 134 while (waitpid(-1, &status, WNOHANG) > 0) { |
146 } | 135 // ignore child exit status, we only do this to cleanup zombies |
147 else i++; | 136 } |
148 } | 137 } |
149 } | 138 |
150 return NULL; | 139 |
151 } | 140 //////////////////////////////////////////////// |
152 | 141 // The termination signal handler function, called to |
153 | 142 // request termination of this process. |
154 //////////////////////////////////////////////// | 143 // |
155 // The signal handler function -- only gets called when a SIGCHLD | 144 void sigterm(int sig) |
156 // is received, ie when a child terminates | 145 { |
157 // | 146 loader_run = false; |
158 void sig_chld(int signo) | 147 signal(sig, SIG_DFL); // quit on repeated signals |
159 { | |
160 int status; | |
161 /* Wait for any child without blocking */ | |
162 while (waitpid(-1, &status, WNOHANG) > 0) { | |
163 // ignore child exit status, we only do this to cleanup zombies | |
164 } | |
165 } | 148 } |
166 | 149 |
167 | 150 |
168 void usage(char *prog); | 151 void usage(char *prog); |
169 void usage(char *prog) | 152 void usage(char *prog) |
170 { | 153 { |
171 fprintf(stderr, "Usage: %s [-d [level]] [-c]\n", prog); | 154 fprintf(stderr, "Usage: %s [-d [level]] [-c]\n", prog); |
172 fprintf(stderr, "-c will load and dump the config to stdout\n"); | 155 fprintf(stderr, "-c will load and dump the config to stdout\n"); |
173 fprintf(stderr, "-d will set the syslog message level, currently 0 to 3\n"); | 156 fprintf(stderr, "-d will set the syslog message level, currently 0 to 3\n"); |
174 } | 157 } |
175 | 158 |
176 | 159 |
177 void worker(); | 160 void worker(); |
178 void worker() | 161 void worker() |
179 { | 162 { |
180 time_t t = time(NULL); | 163 time_t t = time(NULL); |
181 CONFIG *c; | 164 CONFIG *c; |
182 pthread_mutex_lock(&config_mutex); | 165 pthread_mutex_lock(&config_mutex); |
183 c = config; | 166 c = config; |
184 c->reference_count++; | 167 c->reference_count++; |
185 pthread_mutex_unlock(&config_mutex); | 168 pthread_mutex_unlock(&config_mutex); |
186 while (true) { | 169 while (loader_run) { |
187 if (c != config) { | 170 if (c != config) { |
188 pthread_mutex_lock(&config_mutex); | 171 pthread_mutex_lock(&config_mutex); |
189 c->reference_count--; | 172 CONFIG *old = c; old->reference_count--; |
190 c = config; | 173 c = config; c->reference_count++; |
191 c->reference_count++; | 174 pthread_mutex_unlock(&config_mutex); |
192 pthread_mutex_unlock(&config_mutex); | 175 if (!old->reference_count) { |
193 } | 176 if (debug_syslog) { |
194 c->read(); | 177 char buf[maxlen]; |
195 c->sleep(2, t); | 178 snprintf(buf, sizeof(buf), "freeing memory for old configuration generation %d", old->generation); |
196 } | 179 my_syslog(buf); |
180 } | |
181 delete old; // destructor does all the work | |
182 } | |
183 } | |
184 c->read(); | |
185 c->sleep(2, t); | |
186 } | |
187 // worker shutting down, free all ip addresses | |
188 c->free_all(); | |
197 } | 189 } |
198 | 190 |
199 | 191 |
200 int main(int argc, char *argv[]) | 192 int main(int argc, char *argv[]) |
201 { | 193 { |
202 token_init(); | 194 token_init(); |
203 bool check = false; | 195 bool check = false; |
204 int c; | 196 int c; |
205 const char *args = "d:ch"; | 197 const char *args = "d:ch"; |
206 extern char *optarg; | 198 extern char *optarg; |
207 | 199 |
208 // Process command line options | 200 // Process command line options |
209 while ((c = getopt(argc, argv, args)) != -1) { | 201 while ((c = getopt(argc, argv, args)) != -1) { |
210 switch (c) { | 202 switch (c) { |
211 case 'c': | 203 case 'c': |
212 check = true; | 204 check = true; |
213 break; | 205 break; |
214 | 206 |
215 case 'd': | 207 case 'd': |
216 if (optarg == NULL || *optarg == '\0') debug_syslog = 1; | 208 if (optarg == NULL || *optarg == '\0') debug_syslog = 1; |
217 else debug_syslog = atoi(optarg); | 209 else debug_syslog = atoi(optarg); |
218 break; | 210 break; |
219 | 211 |
220 case 'h': | 212 case 'h': |
221 default: | 213 default: |
222 usage(argv[0]); | 214 usage(argv[0]); |
223 exit(EX_USAGE); | 215 exit(EX_USAGE); |
224 } | 216 } |
225 } | 217 } |
226 | 218 |
227 if (check) { | 219 if (check) { |
228 use_syslog = false; | 220 use_syslog = false; |
229 debug_syslog = 10; | 221 debug_syslog = 10; |
230 config = new_conf(); | 222 config = new_conf(); |
231 if (config) { | 223 if (config) { |
232 config->dump(); | 224 config->dump(); |
233 delete config; | 225 delete config; |
234 return 0; | 226 return 0; |
235 } | 227 } |
236 else { | 228 else { |
237 return 1; // config failed to load | 229 return 1; // config failed to load |
238 } | 230 } |
239 } | 231 } |
240 | 232 |
241 // switch to background mode | 233 // switch to background mode |
242 if (daemon(1,0) < 0) { | 234 if (daemon(1,0) < 0) { |
243 fprintf(stderr, "daemon() call failed\n"); | 235 fprintf(stderr, "daemon() call failed\n"); |
244 exit(EX_UNAVAILABLE); | 236 exit(EX_UNAVAILABLE); |
245 } | 237 } |
246 | 238 |
247 // write the pid | 239 // write the pid |
248 const char *pidpath = "/var/run/syslog2iptables.pid"; | 240 const char *pidpath = "/var/run/syslog2iptables.pid"; |
249 unlink(pidpath); | 241 unlink(pidpath); |
250 FILE *f = fopen(pidpath, "w"); | 242 FILE *f = fopen(pidpath, "w"); |
251 if (f) { | 243 if (f) { |
252 #ifdef linux | 244 #ifdef linux |
253 // from a comment in the DCC source code: | 245 // from a comment in the DCC source code: |
254 // Linux threads are broken. Signals given the | 246 // Linux threads are broken. Signals given the |
255 // original process are delivered to only the | 247 // original process are delivered to only the |
256 // thread that happens to have that PID. The | 248 // thread that happens to have that PID. The |
257 // sendmail libmilter thread that needs to hear | 249 // sendmail libmilter thread that needs to hear |
258 // SIGINT and other signals does not, and that breaks | 250 // SIGINT and other signals does not, and that breaks |
259 // scripts that need to stop milters. | 251 // scripts that need to stop milters. |
260 // However, signaling the process group works. | 252 // However, signaling the process group works. |
261 fprintf(f, "-%d\n", (u_int)getpgrp()); | 253 fprintf(f, "-%d\n", (u_int)getpgrp()); |
262 #else | 254 #else |
263 fprintf(f, "%d\n", (u_int)getpid()); | 255 fprintf(f, "%d\n", (u_int)getpid()); |
264 #endif | 256 #endif |
265 fclose(f); | 257 fclose(f); |
266 } | 258 } |
267 | 259 |
268 // initialize the thread sync objects | 260 // setup signal handler for termination signals |
269 pthread_mutex_init(&config_mutex, 0); | 261 signal(SIGHUP, sigterm); |
270 pthread_mutex_init(&syslog_mutex, 0); | 262 signal(SIGTERM, sigterm); |
271 | 263 signal(SIGINT, sigterm); |
272 // load the initial config | 264 |
273 config = new_conf(); | 265 // initialize the thread sync objects |
274 if (!config) { | 266 pthread_mutex_init(&config_mutex, 0); |
275 my_syslog("failed to load initial configuration, quitting"); | 267 pthread_mutex_init(&syslog_mutex, 0); |
276 exit(1); | 268 |
277 } | 269 // load the initial config |
278 | 270 config = new_conf(); |
279 // setup sigchld handler to prevent zombies | 271 if (!config) { |
280 struct sigaction act; | 272 my_syslog("failed to load initial configuration, quitting"); |
281 act.sa_handler = sig_chld; // Assign sig_chld as our SIGCHLD handler | 273 exit(1); |
282 sigemptyset(&act.sa_mask); // We don't want to block any other signals in this example | 274 } |
283 act.sa_flags = SA_NOCLDSTOP; // only want children that have terminated | 275 |
284 if (sigaction(SIGCHLD, &act, NULL) < 0) { | 276 // setup sigchld handler to prevent zombies |
285 my_syslog("failed to setup SIGCHLD handler"); | 277 struct sigaction act; |
286 exit(1); | 278 act.sa_handler = sigchld; // Assign sig_chld as our SIGCHLD handler |
287 } | 279 sigemptyset(&act.sa_mask); // We don't want to block any other signals in this example |
288 | 280 act.sa_flags = SA_NOCLDSTOP; // only want children that have terminated |
289 // only create threads after the fork() in daemon | 281 if (sigaction(SIGCHLD, &act, NULL) < 0) { |
290 pthread_t tid; | 282 my_syslog("failed to setup SIGCHLD handler"); |
291 if (pthread_create(&tid, 0, config_loader, 0)) | 283 exit(1); |
292 my_syslog("failed to create config loader thread"); | 284 } |
293 if (pthread_detach(tid)) | 285 |
294 my_syslog("failed to detach config loader thread"); | 286 // only create threads after the fork() in daemon |
295 | 287 pthread_t tid; |
296 worker(); | 288 if (pthread_create(&tid, 0, config_loader, 0)) |
297 | 289 my_syslog("failed to create config loader thread"); |
298 return EXIT_SUCCESS; | 290 if (pthread_detach(tid)) |
299 } | 291 my_syslog("failed to detach config loader thread"); |
292 | |
293 worker(); | |
294 | |
295 return EXIT_SUCCESS; | |
296 } |