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