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 }