1 /*
2 * Copyright (c) 2007-2012, Vsevolod Stakhov
3 * All rights reserved.
4
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer. Redistributions in binary form
9 * must reproduce the above copyright notice, this list of conditions and the
10 * following disclaimer in the documentation and/or other materials provided with
11 * the distribution. Neither the name of the author nor the names of its
12 * contributors may be used to endorse or promote products derived from this
13 * software without specific prior written permission.
14
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include <stdbool.h>
29 #include "cfg_file.h"
30 #include "rmilter.h"
31 #include "util.h"
32 #include "mfapi.h"
33
34 /* config options here... */
35
36 struct config_file *cfg;
37 bool daemonize;
38 extern struct smfiDesc smfilter;
39 const char *_rmilter_progname;
40 extern int yydebug;
41
42 pthread_cond_t cfg_cond = PTHREAD_COND_INITIALIZER;
43 pthread_mutex_t cfg_reload_mtx = PTHREAD_MUTEX_INITIALIZER;
44 /* R/W lock for reconfiguring milter */
45 pthread_rwlock_t cfg_mtx = PTHREAD_RWLOCK_INITIALIZER;
46 struct rmilter_rng_state *rng_state = NULL;
47
48 int
my_strcmp(const void * s1,const void * s2)49 my_strcmp (const void *s1, const void *s2)
50 {
51 return strcmp (*(const char **)s1, *(const char **)s2);
52 }
53
54 static void
usage(void)55 usage (void)
56 {
57 printf ("Rapid Milter Version " MVERSION "\n"
58 "Usage: rmilter [-h] [-n] [-d] [-c <config_file>]\n"
59 "-n - do not daemonize on startup\n"
60 "-d - debug parsing\n"
61 "-h - this help message\n"
62 "-c - path to config file\n"
63 "-v - show version information\n");
64 exit (0);
65 }
66
67 static void
version(void)68 version (void)
69 {
70 printf ("Rapid Milter Version " MVERSION "\n");
71 exit (0);
72 }
73
74 static void
sig_usr1_handler(int signo)75 sig_usr1_handler (int signo)
76 {
77 pthread_cond_signal(&cfg_cond);
78 }
79
80 static void *
reload_thread(void * unused)81 reload_thread (void *unused)
82 {
83 extern int yynerrs;
84 extern FILE *yyin;
85 FILE *f;
86 struct config_file *new_cfg = NULL, *tmp;
87 struct sigaction signals;
88
89 /* Initialize signals and start reload thread */
90 bzero (&signals, sizeof (struct sigaction));
91 sigemptyset(&signals.sa_mask);
92 sigaddset(&signals.sa_mask, SIGUSR1);
93 signals.sa_handler = sig_usr1_handler;
94 sigaction (SIGUSR1, &signals, NULL);
95
96 msg_info ("reload_thread: starting...");
97
98 /* lock on mutex until we got SIGUSR1 that unlocks mutex */
99 while (1) {
100 pthread_mutex_lock(&cfg_reload_mtx);
101 pthread_cond_wait(&cfg_cond, &cfg_reload_mtx);
102 pthread_mutex_unlock(&cfg_reload_mtx);
103 msg_warn ("reload_thread: reloading, rmilter version %s", MVERSION);
104 /* lock for writing */
105 CFG_WLOCK();
106 f = fopen (cfg->cfg_name, "r");
107
108 if (f == NULL) {
109 CFG_UNLOCK();
110 msg_warn ("reload_thread: cannot open file %s, %m", cfg->cfg_name);
111 continue;
112 }
113
114 new_cfg = (struct config_file*) malloc (sizeof (struct config_file));
115 if (new_cfg == NULL) {
116 CFG_UNLOCK();
117 fclose (f);
118 msg_warn ("reload_thread: malloc, %s", strerror (errno));
119 continue;
120 }
121
122 bzero (new_cfg, sizeof (struct config_file));
123 init_defaults (new_cfg);
124 new_cfg->cfg_name = cfg->cfg_name;
125 tmp = cfg;
126 cfg = new_cfg;
127
128 yyin = f;
129 yyrestart (yyin);
130
131 if (yyparse() != 0 || yynerrs > 0) {
132 CFG_UNLOCK();
133 fclose (f);
134 msg_warn ("reload_thread: cannot parse config file %s", cfg->cfg_name);
135 free_config (new_cfg);
136 free (new_cfg);
137 cfg = tmp;
138 continue;
139 }
140
141 fclose (f);
142 new_cfg->cfg_name = tmp->cfg_name;
143 new_cfg->serial = tmp->serial + 1;
144
145 /* Strictly set temp dir */
146 if (!cfg->temp_dir) {
147 msg_warn ("tempdir is not set, trying to use $TMPDIR");
148 cfg->temp_dir = getenv("TMPDIR");
149
150 if (!cfg->temp_dir) {
151 cfg->temp_dir = strdup("/tmp");
152 }
153 }
154 #ifdef HAVE_SRANDOMDEV
155 srandomdev();
156 #else
157 srand (time (NULL));
158 #endif
159 /* Free old config */
160 free_config (tmp);
161 free (tmp);
162
163 CFG_UNLOCK();
164 }
165 return NULL;
166 }
167
168 static struct rmilter_rng_state*
get_prng_state(void)169 get_prng_state (void)
170 {
171 static const char *rng_dev = "/dev/urandom";
172 struct rmilter_rng_state *st;
173 int fd;
174 struct timeval tv;
175
176 st = malloc (sizeof (*st));
177
178 if (st == NULL) {
179 abort ();
180 }
181
182 fd = open (rng_dev, O_RDONLY);
183
184 if (fd == -1) {
185 msg_warn ("cannot open %s to seed prng, use current time",
186 rng_dev);
187 gettimeofday (&tv, NULL);
188 memcpy (st->s, &tv, sizeof (tv));
189 }
190 else {
191 if (read (fd, st->s, sizeof (st->s)) == -1) {
192 msg_warn ("cannot read %d bytes from %s to seed prng, use current time",
193 (int)sizeof (*st), rng_dev);
194 gettimeofday (&tv, NULL);
195 memcpy (st->s, &tv, sizeof (tv));
196 }
197
198 close (fd);
199 }
200
201 st->p = 0;
202 pthread_mutex_init (&st->mtx, NULL);
203
204 return st;
205 }
206
207 int
main(int argc,char * argv[])208 main(int argc, char *argv[])
209 {
210 int c, r;
211 extern int yynerrs;
212 extern FILE *yyin;
213 const char *args = "c:hndv";
214 char *cfg_file = NULL;
215 FILE *f;
216 pthread_t reload_thr;
217 rmilter_pidfh_t *pfh = NULL;
218 pid_t pid;
219
220 daemonize = 1;
221
222 /* Process command line options */
223 while ((c = getopt(argc, argv, args)) != -1) {
224 switch (c) {
225 case 'c':
226 if (optarg == NULL || *optarg == '\0') {
227 fprintf(stderr, "Illegal config_file: %s\n",
228 optarg);
229 exit(EX_USAGE);
230 }
231 else {
232 cfg_file = strdup (optarg);
233 }
234 break;
235 case 'n':
236 daemonize = 0;
237 break;
238 case 'd':
239 yydebug = 1;
240 break;
241 case 'v':
242 version ();
243 break;
244 case 'h':
245 default:
246 usage ();
247 break;
248 }
249 }
250
251 openlog("rmilter", LOG_PID, LOG_MAIL);
252
253 cfg = (struct config_file*) malloc (sizeof (struct config_file));
254 if (cfg == NULL) {
255 msg_warn ("malloc: %s", strerror (errno));
256 return -1;
257 }
258 bzero (cfg, sizeof (struct config_file));
259 init_defaults (cfg);
260
261 if (cfg_file == NULL) {
262 cfg_file = strdup ("/usr/local/etc/rmilter.conf");
263 }
264
265 f = fopen (cfg_file, "r");
266 if (f == NULL) {
267 msg_warn ("cannot open file: %s", cfg_file);
268 return EBADF;
269 }
270 yyin = f;
271
272 yyrestart (yyin);
273
274 if (yyparse() != 0 || yynerrs > 0) {
275 msg_warn ("yyparse: cannot parse config file, %d errors", yynerrs);
276 return EBADF;
277 }
278
279 if (!cfg->cache_use_redis) {
280 msg_warn ("rmilter is configured to work with legacy memcached cache,"
281 " please consider switching to redis by adding "
282 "'use_redis = true;' into configuration");
283 }
284
285 fclose (f);
286
287 if (argv[0] && strrchr (argv[0], '/') != NULL) {
288 _rmilter_progname = strrchr (argv[0], '/') + 1;
289 }
290 else {
291 _rmilter_progname = argv[0];
292 }
293
294 cfg->cfg_name = strdup (cfg_file);
295
296 /* Strictly set temp dir */
297 if (!cfg->temp_dir) {
298 msg_warn ("tempdir is not set, trying to use $TMPDIR");
299 cfg->temp_dir = getenv("TMPDIR");
300
301 if (!cfg->temp_dir) {
302 cfg->temp_dir = strdup("/tmp");
303 }
304 }
305 if (cfg->sizelimit == 0) {
306 msg_warn ("maxsize is not set, no limits on size of scanned mail");
307 }
308
309 #ifdef HAVE_SRANDOMDEV
310 srandomdev();
311 #else
312 srand (time (NULL));
313 #endif
314
315 umask (0);
316 rng_state = get_prng_state ();
317
318 smfi_setconn(cfg->sock_cred);
319 if (smfi_register(smfilter) == MI_FAILURE) {
320 msg_err ("smfi_register failed");
321 exit(EX_UNAVAILABLE);
322 }
323
324 if (smfi_opensocket(true) == MI_FAILURE) {
325 msg_err("Unable to open listening socket");
326 exit(EX_UNAVAILABLE);
327 }
328
329 if (daemonize && daemon (0, 0) == -1) {
330 msg_err("Unable to daemonize");
331 exit(EX_UNAVAILABLE);
332 }
333
334 msg_info ("main: starting rmilter version %s, listen on %s", MVERSION,
335 cfg->sock_cred);
336
337 if (pthread_create (&reload_thr, NULL, reload_thread, NULL)) {
338 msg_warn ("main: cannot start reload thread, ignoring error");
339 }
340
341 if (cfg->pid_file) {
342 pfh = rmilter_pidfile_open (cfg->pid_file, 0644, &pid);
343
344 if (pfh == NULL) {
345 msg_err("Unable to open pidfile %s", cfg->pid_file);
346 exit (EX_UNAVAILABLE);
347 }
348
349 rmilter_pidfile_write (pfh);
350 }
351
352 r = smfi_main();
353
354 if (cfg_file != NULL) free (cfg_file);
355
356 if (pfh) {
357 rmilter_pidfile_close (pfh);
358 }
359
360 return r;
361 }
362
363 /*
364 * vi:ts=4
365 */
366