Mercurial > sm-archive
comparison src/sm-archive.cpp @ 27:9298f8b00db2 stable-1-0-10
patches from Takao Abe add switches for config and pid files
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Thu, 24 May 2018 10:35:45 -0700 |
parents | 09564d4acd9e |
children |
comparison
equal
deleted
inserted
replaced
26:c6d5b1658f61 | 27:9298f8b00db2 |
---|---|
6 | 6 |
7 Based on a sample milter Copyright (c) 2000-2003 Sendmail, Inc. and its | 7 Based on a sample milter Copyright (c) 2000-2003 Sendmail, Inc. and its |
8 suppliers. | 8 suppliers. |
9 | 9 |
10 -p port The port through which the MTA will connect to this milter. | 10 -p port The port through which the MTA will connect to this milter. |
11 -t sec The timeout value. | 11 -t sec The timeout value. |
12 -c Check the config, and print a copy to stdout. Don't start the | 12 -c Check the config, and print a copy to stdout. Don't start the |
13 milter or do anything with the socket. | 13 milter or do anything with the socket. |
14 -d increase debug level | 14 -d increase debug level |
15 | 15 |
16 */ | 16 */ |
17 | 17 |
18 | 18 |
19 // from sendmail sample | 19 // from sendmail sample |
33 | 33 |
34 // misc stuff needed here | 34 // misc stuff needed here |
35 #include <ctype.h> | 35 #include <ctype.h> |
36 #include <syslog.h> | 36 #include <syslog.h> |
37 #include <pwd.h> | 37 #include <pwd.h> |
38 #include <sys/wait.h> /* header for waitpid() and various macros */ | 38 #include <sys/wait.h> /* header for waitpid() and various macros */ |
39 #include <signal.h> /* header for signal functions */ | 39 #include <signal.h> /* header for signal functions */ |
40 | 40 |
41 #include "includes.h" | 41 #include "includes.h" |
42 | 42 |
43 #ifndef HAVE_DAEMON | 43 #ifndef HAVE_DAEMON |
44 #include "daemon.h" | 44 #include "daemon.h" |
45 #include "daemon.c" | 45 #include "daemon.c" |
46 #endif | 46 #endif |
47 | 47 |
48 extern "C" { | 48 extern "C" { |
49 #include <libmilter/mfapi.h> | 49 #include <libmilter/mfapi.h> |
50 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr); | 50 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr); |
51 sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv); | 51 sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv); |
52 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv); | 52 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv); |
53 sfsistat mlfi_eom(SMFICTX *ctx); | 53 sfsistat mlfi_eom(SMFICTX *ctx); |
54 sfsistat mlfi_abort(SMFICTX *ctx); | 54 sfsistat mlfi_abort(SMFICTX *ctx); |
55 sfsistat mlfi_close(SMFICTX *ctx); | 55 sfsistat mlfi_close(SMFICTX *ctx); |
56 void sig_chld(int signo); | 56 void sig_chld(int signo); |
57 } | 57 } |
58 | 58 |
59 // command line options | |
60 const char *optionConfigFile = "sm-archive.conf" ; | |
61 const char *optionPidFile = "/var/run/sm-archive.pid" ; | |
59 int debug_syslog = 0; | 62 int debug_syslog = 0; |
63 // | |
60 bool syslog_opened = false; | 64 bool syslog_opened = false; |
61 bool use_syslog = true; // false to printf | 65 bool use_syslog = true; // false to printf |
62 bool loader_run = true; // used to stop the config loader thread | 66 bool loader_run = true; // used to stop the config loader thread |
63 CONFIG *config = NULL; // protected by the config_mutex | 67 CONFIG *config = NULL; // protected by the config_mutex |
64 int generation = 0; // protected by the config_mutex | 68 int generation = 0; // protected by the config_mutex |
65 const int maxlen = 1000; // used for snprintf buffers | 69 const int maxlen = 1000; // used for snprintf buffers |
66 | 70 |
67 pthread_mutex_t config_mutex; | 71 pthread_mutex_t config_mutex; |
68 pthread_mutex_t syslog_mutex; | 72 pthread_mutex_t syslog_mutex; |
69 | 73 |
70 | 74 |
71 mlfiPriv::mlfiPriv() { | 75 mlfiPriv::mlfiPriv() { |
72 pthread_mutex_lock(&config_mutex); | 76 pthread_mutex_lock(&config_mutex); |
73 pc = config; | 77 pc = config; |
74 pc->reference_count++; | 78 pc->reference_count++; |
75 pthread_mutex_unlock(&config_mutex); | 79 pthread_mutex_unlock(&config_mutex); |
76 mailaddr = NULL; | 80 mailaddr = NULL; |
77 queueid = NULL; | 81 queueid = NULL; |
78 } | 82 } |
79 | 83 |
80 mlfiPriv::~mlfiPriv() { | 84 mlfiPriv::~mlfiPriv() { |
81 pthread_mutex_lock(&config_mutex); | 85 pthread_mutex_lock(&config_mutex); |
82 pc->reference_count--; | 86 pc->reference_count--; |
83 pthread_mutex_unlock(&config_mutex); | 87 pthread_mutex_unlock(&config_mutex); |
84 reset(true); | 88 reset(true); |
85 } | 89 } |
86 | 90 |
87 void mlfiPriv::reset(bool final) { | 91 void mlfiPriv::reset(bool final) { |
88 targets.clear(); | 92 targets.clear(); |
89 for (string_set::iterator i=removal.begin(); i!=removal.end(); i++) { | 93 for (string_set::iterator i=removal.begin(); i!=removal.end(); i++) { |
90 const char *remove = (*i); | 94 const char *remove = (*i); |
91 free((void*)remove); | 95 free((void*)remove); |
92 } | 96 } |
93 removal.clear(); | 97 removal.clear(); |
94 if (mailaddr) free((void*)mailaddr); | 98 if (mailaddr) free((void*)mailaddr); |
95 if (queueid) free((void*)queueid); | 99 if (queueid) free((void*)queueid); |
96 if (!final) { | 100 if (!final) { |
97 mailaddr = NULL; | 101 mailaddr = NULL; |
98 queueid = NULL; | 102 queueid = NULL; |
99 } | 103 } |
100 } | 104 } |
101 | 105 |
102 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) | 106 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) |
103 | 107 |
104 | 108 |
105 //////////////////////////////////////////////// | 109 //////////////////////////////////////////////// |
106 // syslog a message | 110 // syslog a message |
107 // | 111 // |
108 void my_syslog(mlfiPriv *priv, const char *text) { | 112 void my_syslog(mlfiPriv *priv, const char *text) { |
109 char buf[maxlen]; | 113 char buf[maxlen]; |
110 if (priv) { | 114 if (priv) { |
111 snprintf(buf, sizeof(buf), "%s: %s", | 115 snprintf(buf, sizeof(buf), "%s: %s", |
112 priv->queueid ? priv->queueid : "NOQUEUE", text); | 116 priv->queueid ? priv->queueid : "NOQUEUE", text); |
113 text = buf; | 117 text = buf; |
114 } | 118 } |
115 if (use_syslog) { | 119 if (use_syslog) { |
116 pthread_mutex_lock(&syslog_mutex); | 120 pthread_mutex_lock(&syslog_mutex); |
117 if (!syslog_opened) { | 121 if (!syslog_opened) { |
118 openlog("sm-archive", LOG_PID, LOG_MAIL); | 122 openlog("sm-archive", LOG_PID, LOG_MAIL); |
119 syslog_opened = true; | 123 syslog_opened = true; |
120 } | 124 } |
121 syslog(LOG_NOTICE, "%s", text); | 125 syslog(LOG_NOTICE, "%s", text); |
122 pthread_mutex_unlock(&syslog_mutex); | 126 pthread_mutex_unlock(&syslog_mutex); |
123 } | 127 } |
124 else { | 128 else { |
125 printf("%s \n", text); | 129 printf("%s \n", text); |
126 } | 130 } |
127 } | 131 } |
128 | 132 |
129 void my_syslog(const char *text) { | 133 void my_syslog(const char *text) { |
130 my_syslog(NULL, text); | 134 my_syslog(NULL, text); |
131 } | 135 } |
132 | 136 |
133 | 137 |
134 //////////////////////////////////////////////// | 138 //////////////////////////////////////////////// |
135 // this email address is passed in from sendmail, and will | 139 // this email address is passed in from sendmail, and will |
139 // Postfix will return addresses without <> if they have been provided | 143 // Postfix will return addresses without <> if they have been provided |
140 // this way in the SMTP dialog. | 144 // this way in the SMTP dialog. |
141 // | 145 // |
142 const char *to_lower_string(const char *email); | 146 const char *to_lower_string(const char *email); |
143 const char *to_lower_string(const char *email) { | 147 const char *to_lower_string(const char *email) { |
144 char *key, *p; | 148 char *key, *p; |
145 int i; | 149 int i; |
146 | 150 |
147 if (strcmp(email, "<>") == 0) return strdup(email); | 151 if (strcmp(email, "<>") == 0) return strdup(email); |
148 if (email[0] == '<') p = (char *) email + 1; | 152 if (email[0] == '<') p = (char *) email + 1; |
149 else p = (char *) email; | 153 else p = (char *) email; |
150 key = (char *) malloc(strlen(p) + 1); | 154 key = (char *) malloc(strlen(p) + 1); |
151 for (i = 0; p[i] != '\0'; i++) key[i] = tolower(p[i]); | 155 for (i = 0; p[i] != '\0'; i++) key[i] = tolower(p[i]); |
152 if (p[i - 1] == '>') i--; | 156 if (p[i - 1] == '>') i--; |
153 key[i] = '\0'; | 157 key[i] = '\0'; |
154 return key; | 158 return key; |
155 } | 159 } |
156 | 160 |
157 | 161 |
158 //////////////////////////////////////////////// | 162 //////////////////////////////////////////////// |
159 // start of sendmail milter interfaces | 163 // start of sendmail milter interfaces |
160 // | 164 // |
161 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) | 165 sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) |
162 { | 166 { |
163 // allocate some private memory | 167 // allocate some private memory |
164 mlfiPriv *priv = new mlfiPriv; | 168 mlfiPriv *priv = new mlfiPriv; |
165 // save the private data | 169 // save the private data |
166 smfi_setpriv(ctx, (void*)priv); | 170 smfi_setpriv(ctx, (void*)priv); |
167 // continue processing | 171 // continue processing |
168 return SMFIS_CONTINUE; | 172 return SMFIS_CONTINUE; |
169 } | 173 } |
170 | 174 |
171 sfsistat mlfi_envfrom(SMFICTX *ctx, char **from) | 175 sfsistat mlfi_envfrom(SMFICTX *ctx, char **from) |
172 { | 176 { |
173 mlfiPriv &priv = *MLFIPRIV; | 177 mlfiPriv &priv = *MLFIPRIV; |
174 priv.mailaddr = to_lower_string(from[0]); | 178 priv.mailaddr = to_lower_string(from[0]); |
175 return SMFIS_CONTINUE; | 179 return SMFIS_CONTINUE; |
176 } | 180 } |
177 | 181 |
178 void add_target(mlfiPriv &priv, const char *target); | 182 void add_target(mlfiPriv &priv, const char *target); |
179 void add_target(mlfiPriv &priv, const char *target) | 183 void add_target(mlfiPriv &priv, const char *target) |
180 { | 184 { |
181 if (target) { | 185 if (target) { |
182 string_set::iterator i = priv.targets.find(target); | 186 string_set::iterator i = priv.targets.find(target); |
183 if (i == priv.targets.end()) priv.targets.insert(target); | 187 if (i == priv.targets.end()) priv.targets.insert(target); |
184 } | 188 } |
185 } | 189 } |
186 | 190 |
187 void add_remove(mlfiPriv &priv, const char *remove); | 191 void add_remove(mlfiPriv &priv, const char *remove); |
188 void add_remove(mlfiPriv &priv, const char *remove) | 192 void add_remove(mlfiPriv &priv, const char *remove) |
189 { | 193 { |
190 if (remove) { | 194 if (remove) { |
191 string_set::iterator i = priv.removal.find(remove); | 195 string_set::iterator i = priv.removal.find(remove); |
192 if (i == priv.removal.end()) priv.removal.insert(remove); | 196 if (i == priv.removal.end()) priv.removal.insert(remove); |
193 } | 197 } |
194 } | 198 } |
195 | 199 |
196 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **rcpt) | 200 sfsistat mlfi_envrcpt(SMFICTX *ctx, char **rcpt) |
197 { | 201 { |
198 mlfiPriv &priv = *MLFIPRIV; | 202 mlfiPriv &priv = *MLFIPRIV; |
199 CONFIG &dc = *priv.pc; | 203 CONFIG &dc = *priv.pc; |
200 const char *i_macro = smfi_getsymval(ctx, (char*)"i"); | 204 const char *i_macro = smfi_getsymval(ctx, (char*)"i"); |
201 if (!priv.queueid && i_macro) priv.queueid = strdup(i_macro); | 205 if (!priv.queueid && i_macro) priv.queueid = strdup(i_macro); |
202 const char *rcptaddr = to_lower_string(rcpt[0]); | 206 const char *rcptaddr = to_lower_string(rcpt[0]); |
203 if (debug_syslog > 1) { | 207 if (debug_syslog > 1) { |
204 char msg[maxlen]; | 208 char msg[maxlen]; |
205 snprintf(msg, sizeof(msg), "from <%s> to <%s>", priv.mailaddr, rcptaddr); | 209 snprintf(msg, sizeof(msg), "from <%s> to <%s>", priv.mailaddr, rcptaddr); |
206 my_syslog(&priv, msg); | 210 my_syslog(&priv, msg); |
207 } | 211 } |
208 const char *target = dc.find_to(rcptaddr); | 212 const char *target = dc.find_to(rcptaddr); |
209 add_target(priv, target); | 213 add_target(priv, target); |
210 bool remove = dc.find_remove(rcptaddr); | 214 bool remove = dc.find_remove(rcptaddr); |
211 if (remove) add_remove(priv, strdup(rcptaddr)); | 215 if (remove) add_remove(priv, strdup(rcptaddr)); |
212 free((void*)rcptaddr); | 216 free((void*)rcptaddr); |
213 return SMFIS_CONTINUE; | 217 return SMFIS_CONTINUE; |
214 } | 218 } |
215 | 219 |
216 sfsistat mlfi_eom(SMFICTX *ctx) | 220 sfsistat mlfi_eom(SMFICTX *ctx) |
217 { | 221 { |
218 mlfiPriv &priv = *MLFIPRIV; | 222 mlfiPriv &priv = *MLFIPRIV; |
219 CONFIG &dc = *priv.pc; | 223 CONFIG &dc = *priv.pc; |
220 const char *i_macro = smfi_getsymval(ctx, (char*)"i"); | 224 const char *i_macro = smfi_getsymval(ctx, (char*)"i"); |
221 if (!priv.queueid && i_macro) priv.queueid = strdup(i_macro); | 225 if (!priv.queueid && i_macro) priv.queueid = strdup(i_macro); |
222 const char *target = dc.find_from(priv.mailaddr); | 226 const char *target = dc.find_from(priv.mailaddr); |
223 add_target(priv, target); | 227 add_target(priv, target); |
224 for (string_set::iterator i=priv.targets.begin(); i!=priv.targets.end(); i++) { | 228 for (string_set::iterator i=priv.targets.begin(); i!=priv.targets.end(); i++) { |
225 target = (*i); | 229 target = (*i); |
226 smfi_addrcpt(ctx, (char*)target); | 230 smfi_addrcpt(ctx, (char*)target); |
227 if (debug_syslog > 1) { | 231 if (debug_syslog > 1) { |
228 char msg[maxlen]; | 232 char msg[maxlen]; |
229 snprintf(msg, sizeof(msg), "adding recipient <%s>", target); | 233 snprintf(msg, sizeof(msg), "adding recipient <%s>", target); |
230 my_syslog(&priv, msg); | 234 my_syslog(&priv, msg); |
231 } | 235 } |
232 } | 236 } |
233 for (string_set::iterator i=priv.removal.begin(); i!=priv.removal.end(); i++) { | 237 for (string_set::iterator i=priv.removal.begin(); i!=priv.removal.end(); i++) { |
234 const char *remove = (*i); | 238 const char *remove = (*i); |
235 smfi_delrcpt(ctx, (char*)remove); | 239 smfi_delrcpt(ctx, (char*)remove); |
236 if (debug_syslog > 1) { | 240 if (debug_syslog > 1) { |
237 char msg[maxlen]; | 241 char msg[maxlen]; |
238 snprintf(msg, sizeof(msg), "removing recipient <%s>", remove); | 242 snprintf(msg, sizeof(msg), "removing recipient <%s>", remove); |
239 my_syslog(&priv, msg); | 243 my_syslog(&priv, msg); |
240 } | 244 } |
241 } | 245 } |
242 // reset for a new message on the same connection | 246 // reset for a new message on the same connection |
243 mlfi_abort(ctx); | 247 mlfi_abort(ctx); |
244 return SMFIS_CONTINUE; | 248 return SMFIS_CONTINUE; |
245 } | 249 } |
246 | 250 |
247 sfsistat mlfi_abort(SMFICTX *ctx) | 251 sfsistat mlfi_abort(SMFICTX *ctx) |
248 { | 252 { |
249 mlfiPriv &priv = *MLFIPRIV; | 253 mlfiPriv &priv = *MLFIPRIV; |
250 priv.reset(); | 254 priv.reset(); |
251 return SMFIS_CONTINUE; | 255 return SMFIS_CONTINUE; |
252 } | 256 } |
253 | 257 |
254 sfsistat mlfi_close(SMFICTX *ctx) | 258 sfsistat mlfi_close(SMFICTX *ctx) |
255 { | 259 { |
256 mlfiPriv *priv = MLFIPRIV; | 260 mlfiPriv *priv = MLFIPRIV; |
257 if (!priv) return SMFIS_CONTINUE; | 261 if (!priv) return SMFIS_CONTINUE; |
258 delete priv; | 262 delete priv; |
259 smfi_setpriv(ctx, NULL); | 263 smfi_setpriv(ctx, NULL); |
260 return SMFIS_CONTINUE; | 264 return SMFIS_CONTINUE; |
261 } | 265 } |
262 | 266 |
263 struct smfiDesc smfilter = | 267 struct smfiDesc smfilter = |
264 { | 268 { |
265 (char*)"SM-ARCHIVE",// filter name | 269 (char*)"SM-ARCHIVE",// filter name |
266 SMFI_VERSION, // version code -- do not change | 270 SMFI_VERSION, // version code -- do not change |
267 SMFIF_ADDRCPT | \ | 271 SMFIF_ADDRCPT | \ |
268 SMFIF_DELRCPT, // flags | 272 SMFIF_DELRCPT, // flags |
269 mlfi_connect, // connection info filter | 273 mlfi_connect, // connection info filter |
270 NULL, // SMTP HELO command filter | 274 NULL, // SMTP HELO command filter |
271 mlfi_envfrom, // envelope sender filter | 275 mlfi_envfrom, // envelope sender filter |
272 mlfi_envrcpt, // envelope recipient filter | 276 mlfi_envrcpt, // envelope recipient filter |
273 NULL, // header filter | 277 NULL, // header filter |
274 NULL, // end of header | 278 NULL, // end of header |
275 NULL, // body block filter | 279 NULL, // body block filter |
276 mlfi_eom, // end of message | 280 mlfi_eom, // end of message |
277 mlfi_abort, // message aborted | 281 mlfi_abort, // message aborted |
278 mlfi_close, // connection cleanup | 282 mlfi_close, // connection cleanup |
279 }; | 283 }; |
280 | 284 |
281 | 285 |
282 //////////////////////////////////////////////// | 286 //////////////////////////////////////////////// |
283 // reload the config | 287 // reload the config |
284 // | 288 // |
285 CONFIG* new_conf(); | 289 CONFIG* new_conf(); |
286 CONFIG* new_conf() { | 290 CONFIG* new_conf() { |
287 CONFIG *newc = new CONFIG; | 291 CONFIG *newc = new CONFIG; |
288 pthread_mutex_lock(&config_mutex); | 292 pthread_mutex_lock(&config_mutex); |
289 newc->generation = generation++; | 293 newc->generation = generation++; |
290 pthread_mutex_unlock(&config_mutex); | 294 pthread_mutex_unlock(&config_mutex); |
291 if (debug_syslog) { | 295 if (debug_syslog) { |
292 char buf[maxlen]; | 296 char buf[maxlen]; |
293 snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation); | 297 snprintf(buf, sizeof(buf), "loading configuration generation %d", newc->generation); |
294 my_syslog(buf); | 298 my_syslog(buf); |
295 } | 299 } |
296 if (load_conf(*newc, "sm-archive.conf")) { | 300 if (load_conf(*newc, optionConfigFile)) { |
297 newc->load_time = time(NULL); | 301 newc->load_time = time(NULL); |
298 return newc; | 302 return newc; |
299 } | 303 } |
300 delete newc; | 304 delete newc; |
301 return NULL; | 305 return NULL; |
302 } | 306 } |
303 | 307 |
304 | 308 |
305 //////////////////////////////////////////////// | 309 //////////////////////////////////////////////// |
306 // thread to watch the old config files for changes | 310 // thread to watch the old config files for changes |
307 // and reload when needed. we also cleanup old | 311 // and reload when needed. we also cleanup old |
308 // configs whose reference count has gone to zero. | 312 // configs whose reference count has gone to zero. |
309 // | 313 // |
310 extern "C" {void* config_loader(void *arg);} | 314 extern "C" {void* config_loader(void *arg);} |
311 void* config_loader(void *arg) { | 315 void* config_loader(void *arg) { |
312 while (loader_run) { | 316 while (loader_run) { |
313 sleep(180); // look for modifications every 3 minutes | 317 sleep(180); // look for modifications every 3 minutes |
314 if (!loader_run) break; | 318 if (!loader_run) break; |
315 CONFIG &dc = *config; | 319 CONFIG &dc = *config; |
316 time_t then = dc.load_time; | 320 time_t then = dc.load_time; |
317 struct stat st; | 321 struct stat st; |
318 bool reload = false; | 322 bool reload = false; |
319 for (string_set::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { | 323 for (string_set::iterator i=dc.config_files.begin(); i!=dc.config_files.end(); i++) { |
320 const char *fn = *i; | 324 const char *fn = *i; |
321 if (stat(fn, &st)) reload = true; // file disappeared | 325 if (stat(fn, &st)) reload = true; // file disappeared |
322 else if (st.st_mtime > then) reload = true; // file modified | 326 else if (st.st_mtime > then) reload = true; // file modified |
323 if (reload) break; | 327 if (reload) break; |
324 } | 328 } |
325 if (reload) { | 329 if (reload) { |
326 CONFIG *newc = new_conf(); | 330 CONFIG *newc = new_conf(); |
327 if (newc) { | 331 if (newc) { |
328 // replace the global config pointer | 332 // replace the global config pointer |
329 pthread_mutex_lock(&config_mutex); | 333 pthread_mutex_lock(&config_mutex); |
330 CONFIG *old = config; | 334 CONFIG *old = config; |
331 bool last = old && (!old->reference_count); | 335 bool last = old && (!old->reference_count); |
332 config = newc; | 336 config = newc; |
333 pthread_mutex_unlock(&config_mutex); | 337 pthread_mutex_unlock(&config_mutex); |
334 if (last) delete old; // there were no references to this config | 338 if (last) delete old; // there were no references to this config |
335 } | 339 } |
336 else { | 340 else { |
337 // failed to load new config | 341 // failed to load new config |
338 my_syslog("failed to load new configuration"); | 342 my_syslog("failed to load new configuration"); |
339 system("echo 'failed to load new sm-archive configuration from /etc/sm-archive' | mail -s 'error in /etc/sm-archive configuration' root"); | 343 system("echo 'failed to load new sm-archive configuration from /etc/sm-archive' | mail -s 'error in /etc/sm-archive configuration' root"); |
340 // update the load time on the current config to prevent complaining every 3 minutes | 344 // update the load time on the current config to prevent complaining every 3 minutes |
341 dc.load_time = time(NULL); | 345 dc.load_time = time(NULL); |
342 } | 346 } |
343 } | 347 } |
344 } | 348 } |
345 return NULL; | 349 return NULL; |
346 } | 350 } |
347 | 351 |
348 | 352 |
349 void usage(const char *prog); | 353 void usage(const char *prog); |
350 void usage(const char *prog) | 354 void usage(const char *prog) |
351 { | 355 { |
352 fprintf(stderr, "Usage: %s [-d [level]] [-c] -p sm-sock-addr [-t timeout]\n", prog); | 356 fprintf(stderr, "Usage: %s [-d [level]] [-c] -p sm-sock-addr [-t timeout] [-C config-file] [-P pid-file]\n", prog); |
353 fprintf(stderr, "where sm-sock-addr is for the connection to sendmail\n"); | 357 fprintf(stderr, "where sm-sock-addr is for the connection to sendmail\n"); |
354 fprintf(stderr, " and should be one of\n"); | 358 fprintf(stderr, " and should be one of\n"); |
355 fprintf(stderr, " inet:port@ip-address\n"); | 359 fprintf(stderr, " inet:port@ip-address\n"); |
356 fprintf(stderr, " local:local-domain-socket-file-name\n"); | 360 fprintf(stderr, " local:local-domain-socket-file-name\n"); |
357 fprintf(stderr, "-c will load and dump the config to stdout\n"); | 361 fprintf(stderr, "-c will load and dump the config to stdout\n"); |
358 fprintf(stderr, "-d will set the syslog message level, currently 0 to 3\n"); | 362 fprintf(stderr, "-d will set the syslog message level, currently 0 to 3\n"); |
363 fprintf(stderr, "-C specifies the config file, defaults to sm-archive.conf\n"); | |
364 fprintf(stderr, "-P specifies the pid file, defaults to /var/run/sm-archive.pid\n"); | |
359 } | 365 } |
360 | 366 |
361 | 367 |
362 | 368 |
363 void setup_socket(const char *sock); | 369 void setup_socket(const char *sock); |
364 void setup_socket(const char *sock) { | 370 void setup_socket(const char *sock) { |
365 unlink(sock); | 371 unlink(sock); |
366 // sockaddr_un addr; | 372 // sockaddr_un addr; |
367 // memset(&addr, '\0', sizeof addr); | 373 // memset(&addr, '\0', sizeof addr); |
368 // addr.sun_family = AF_UNIX; | 374 // addr.sun_family = AF_UNIX; |
369 // strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1); | 375 // strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1); |
370 // int s = socket(AF_UNIX, SOCK_STREAM, 0); | 376 // int s = socket(AF_UNIX, SOCK_STREAM, 0); |
371 // bind(s, (sockaddr*)&addr, sizeof(addr)); | 377 // bind(s, (sockaddr*)&addr, sizeof(addr)); |
372 // close(s); | 378 // close(s); |
373 } | 379 } |
374 | 380 |
375 | 381 |
376 /* | 382 /* |
377 * The signal handler function -- only gets called when a SIGCHLD | 383 * The signal handler function -- only gets called when a SIGCHLD |
378 * is received, ie when a child terminates | 384 * is received, ie when a child terminates |
379 */ | 385 */ |
380 void sig_chld(int signo) | 386 void sig_chld(int signo) |
381 { | 387 { |
382 int status; | 388 int status; |
383 /* Wait for any child without blocking */ | 389 /* Wait for any child without blocking */ |
384 while (waitpid(-1, &status, WNOHANG) > 0) { | 390 while (waitpid(-1, &status, WNOHANG) > 0) { |
385 // ignore child exit status, we only do this to cleanup zombies | 391 // ignore child exit status, we only do this to cleanup zombies |
386 } | 392 } |
387 } | 393 } |
388 | 394 |
389 | 395 |
390 int main(int argc, char**argv) | 396 int main(int argc, char**argv) |
391 { | 397 { |
392 token_init(); | 398 token_init(); |
393 bool check = false; | 399 bool check = false; |
394 bool setconn = false; | 400 bool setconn = false; |
395 int c; | 401 int c; |
396 const char *args = "p:t:d:ch"; | 402 const char *args = "p:t:d:chC:P:"; |
397 extern char *optarg; | 403 extern char *optarg; |
398 | 404 |
399 // Process command line options | 405 // Process command line options |
400 while ((c = getopt(argc, argv, args)) != -1) { | 406 while ((c = getopt(argc, argv, args)) != -1) { |
401 switch (c) { | 407 switch (c) { |
402 case 'p': | 408 case 'p': |
403 if (optarg == NULL || *optarg == '\0') { | 409 if (optarg == NULL || *optarg == '\0') { |
404 fprintf(stderr, "Illegal sendmail socket: %s\n", optarg); | 410 fprintf(stderr, "Illegal sendmail socket: %s\n", optarg); |
405 exit(EX_USAGE); | 411 exit(EX_USAGE); |
406 } | 412 } |
407 if (smfi_setconn(optarg) == MI_FAILURE) { | 413 if (smfi_setconn(optarg) == MI_FAILURE) { |
408 fprintf(stderr, "smfi_setconn failed\n"); | 414 fprintf(stderr, "smfi_setconn failed\n"); |
409 exit(EX_SOFTWARE); | 415 exit(EX_SOFTWARE); |
410 } | 416 } |
411 if (strncasecmp(optarg, "unix:", 5) == 0) setup_socket(optarg + 5); | 417 if (strncasecmp(optarg, "unix:", 5) == 0) setup_socket(optarg + 5); |
412 else if (strncasecmp(optarg, "local:", 6) == 0) setup_socket(optarg + 6); | 418 else if (strncasecmp(optarg, "local:", 6) == 0) setup_socket(optarg + 6); |
413 setconn = true; | 419 setconn = true; |
414 break; | 420 break; |
415 | 421 |
416 case 't': | 422 case 't': |
417 if (optarg == NULL || *optarg == '\0') { | 423 if (optarg == NULL || *optarg == '\0') { |
418 fprintf(stderr, "Illegal timeout: %s\n", optarg); | 424 fprintf(stderr, "Illegal timeout: %s\n", optarg); |
419 exit(EX_USAGE); | 425 exit(EX_USAGE); |
420 } | 426 } |
421 if (smfi_settimeout(atoi(optarg)) == MI_FAILURE) { | 427 if (smfi_settimeout(atoi(optarg)) == MI_FAILURE) { |
422 fprintf(stderr, "smfi_settimeout failed\n"); | 428 fprintf(stderr, "smfi_settimeout failed\n"); |
423 exit(EX_SOFTWARE); | 429 exit(EX_SOFTWARE); |
424 } | 430 } |
425 break; | 431 break; |
426 | 432 |
427 case 'c': | 433 case 'c': |
428 check = true; | 434 check = true; |
429 break; | 435 break; |
430 | 436 |
431 case 'd': | 437 case 'd': |
432 if (optarg == NULL || *optarg == '\0') debug_syslog = 1; | 438 if (optarg == NULL || *optarg == '\0') debug_syslog = 1; |
433 else debug_syslog = atoi(optarg); | 439 else debug_syslog = atoi(optarg); |
434 break; | 440 break; |
435 | 441 |
436 case 'h': | 442 case 'C': |
437 default: | 443 if (optarg == NULL || *optarg == '\0') { |
438 usage(argv[0]); | 444 fprintf( stderr, "Must specify the config file path: %s\n", optarg ); |
439 exit(EX_USAGE); | 445 exit(EX_USAGE); |
440 } | 446 } |
441 } | 447 optionConfigFile = optarg ; |
442 | 448 break ; |
443 if (check) { | 449 |
444 use_syslog = false; | 450 case 'P': |
445 debug_syslog = 10; | 451 if (optarg == NULL || *optarg == '\0') { |
446 CONFIG *conf = new_conf(); | 452 fprintf( stderr, "Must specify the pid file path: %s\n", optarg ); |
447 if (conf) { | 453 exit(EX_USAGE); |
448 conf->dump(); | 454 } |
449 delete conf; | 455 optionPidFile = optarg ; |
450 clear_strings(); // for valgrind checking | 456 break ; |
451 return 0; | 457 |
452 } | 458 case 'h': |
453 else { | 459 default: |
454 return 1; // config failed to load | 460 usage(argv[0]); |
455 } | 461 exit(EX_USAGE); |
456 } | 462 } |
457 | 463 } |
458 if (!setconn) { | 464 |
459 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); | 465 if (check) { |
460 usage(argv[0]); | 466 use_syslog = false; |
461 exit(EX_USAGE); | 467 debug_syslog = 10; |
462 } | 468 CONFIG *conf = new_conf(); |
463 | 469 if (conf) { |
464 if (smfi_register(smfilter) == MI_FAILURE) { | 470 conf->dump(); |
465 fprintf(stderr, "smfi_register failed\n"); | 471 delete conf; |
466 exit(EX_UNAVAILABLE); | 472 clear_strings(); // for valgrind checking |
467 } | 473 return 0; |
468 | 474 } |
469 // switch to background mode | 475 else { |
470 if (daemon(1,0) < 0) { | 476 return 1; // config failed to load |
471 fprintf(stderr, "daemon() call failed\n"); | 477 } |
472 exit(EX_UNAVAILABLE); | 478 } |
473 } | 479 |
474 | 480 if (!setconn) { |
475 // write the pid | 481 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); |
476 const char *pidpath = "/var/run/sm-archive.pid"; | 482 usage(argv[0]); |
477 unlink(pidpath); | 483 exit(EX_USAGE); |
478 FILE *f = fopen(pidpath, "w"); | 484 } |
479 if (f) { | 485 |
486 if (smfi_register(smfilter) == MI_FAILURE) { | |
487 fprintf(stderr, "smfi_register failed\n"); | |
488 exit(EX_UNAVAILABLE); | |
489 } | |
490 | |
491 // switch to background mode | |
492 if (daemon(1,0) < 0) { | |
493 fprintf(stderr, "daemon() call failed\n"); | |
494 exit(EX_UNAVAILABLE); | |
495 } | |
496 | |
497 // write the pid | |
498 const char *pidpath = optionPidFile; | |
499 unlink(pidpath); | |
500 FILE *f = fopen(pidpath, "w"); | |
501 if (f) { | |
480 #ifdef linux | 502 #ifdef linux |
481 // from a comment in the DCC source code: | 503 // from a comment in the DCC source code: |
482 // Linux threads are broken. Signals given the | 504 // Linux threads are broken. Signals given the |
483 // original process are delivered to only the | 505 // original process are delivered to only the |
484 // thread that happens to have that PID. The | 506 // thread that happens to have that PID. The |
485 // sendmail libmilter thread that needs to hear | 507 // sendmail libmilter thread that needs to hear |
486 // SIGINT and other signals does not, and that breaks | 508 // SIGINT and other signals does not, and that breaks |
487 // scripts that need to stop milters. | 509 // scripts that need to stop milters. |
488 // However, signaling the process group works. | 510 // However, signaling the process group works. |
489 fprintf(f, "-%d\n", (u_int)getpgrp()); | 511 fprintf(f, "-%d\n", (u_int)getpgrp()); |
490 #else | 512 #else |
491 fprintf(f, "%d\n", (u_int)getpid()); | 513 fprintf(f, "%d\n", (u_int)getpid()); |
492 #endif | 514 #endif |
493 fclose(f); | 515 fclose(f); |
494 } | 516 } |
495 | 517 |
496 // initialize the thread sync objects | 518 // initialize the thread sync objects |
497 pthread_mutex_init(&config_mutex, 0); | 519 pthread_mutex_init(&config_mutex, 0); |
498 pthread_mutex_init(&syslog_mutex, 0); | 520 pthread_mutex_init(&syslog_mutex, 0); |
499 | 521 |
500 // drop root privs | 522 // drop root privs |
501 struct passwd *pw = getpwnam("sm-archive"); | 523 struct passwd *pw = getpwnam("sm-archive"); |
502 if (pw) { | 524 if (pw) { |
503 if (setgid(pw->pw_gid) == -1) { | 525 if (setgid(pw->pw_gid) == -1) { |
504 my_syslog("failed to switch to group sm-archive"); | 526 my_syslog("failed to switch to group sm-archive"); |
505 } | 527 } |
506 if (setuid(pw->pw_uid) == -1) { | 528 if (setuid(pw->pw_uid) == -1) { |
507 my_syslog("failed to switch to user sm-archive"); | 529 my_syslog("failed to switch to user sm-archive"); |
508 } | 530 } |
509 } | 531 } |
510 | 532 |
511 // load the initial config | 533 // load the initial config |
512 config = new_conf(); | 534 config = new_conf(); |
513 if (!config) { | 535 if (!config) { |
514 my_syslog("failed to load initial configuration, quitting"); | 536 my_syslog("failed to load initial configuration, quitting"); |
515 exit(1); | 537 exit(1); |
516 } | 538 } |
517 | 539 |
518 // only create threads after the fork() in daemon | 540 // only create threads after the fork() in daemon |
519 pthread_t tid; | 541 pthread_t tid; |
520 if (pthread_create(&tid, 0, config_loader, 0)) | 542 if (pthread_create(&tid, 0, config_loader, 0)) |
521 my_syslog("failed to create config loader thread"); | 543 my_syslog("failed to create config loader thread"); |
522 if (pthread_detach(tid)) | 544 if (pthread_detach(tid)) |
523 my_syslog("failed to detach config loader thread"); | 545 my_syslog("failed to detach config loader thread"); |
524 | 546 |
525 time_t starting = time(NULL); | 547 time_t starting = time(NULL); |
526 int rc = smfi_main(); | 548 int rc = smfi_main(); |
527 if ((rc != MI_SUCCESS) && (time(NULL) > starting+5*60)) { | 549 if ((rc != MI_SUCCESS) && (time(NULL) > starting+5*60)) { |
528 my_syslog("trying to restart after smfi_main()"); | 550 my_syslog("trying to restart after smfi_main()"); |
529 loader_run = false; // eventually the config loader thread will terminate | 551 loader_run = false; // eventually the config loader thread will terminate |
530 execvp(argv[0], argv); | 552 execvp(argv[0], argv); |
531 } | 553 } |
532 exit((rc == MI_SUCCESS) ? 0 : EX_UNAVAILABLE); | 554 exit((rc == MI_SUCCESS) ? 0 : EX_UNAVAILABLE); |
533 } | 555 } |
534 | 556 |