Can remove the sender and/or the recipient from the key of the greylister.
[apps/pfixtools.git] / common / server.c
1 /******************************************************************************/
2 /*          pfixtools: a collection of postfix related tools                  */
3 /*          ~~~~~~~~~                                                         */
4 /*  ________________________________________________________________________  */
5 /*                                                                            */
6 /*  Redistribution and use in source and binary forms, with or without        */
7 /*  modification, are permitted provided that the following conditions        */
8 /*  are met:                                                                  */
9 /*                                                                            */
10 /*  1. Redistributions of source code must retain the above copyright         */
11 /*     notice, this list of conditions and the following disclaimer.          */
12 /*  2. Redistributions in binary form must reproduce the above copyright      */
13 /*     notice, this list of conditions and the following disclaimer in the    */
14 /*     documentation and/or other materials provided with the distribution.   */
15 /*  3. The names of its contributors may not be used to endorse or promote    */
16 /*     products derived from this software without specific prior written     */
17 /*     permission.                                                            */
18 /*                                                                            */
19 /*  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND   */
20 /*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE     */
21 /*  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR        */
22 /*  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS    */
23 /*  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR    */
24 /*  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF      */
25 /*  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS  */
26 /*  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN   */
27 /*  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)   */
28 /*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF    */
29 /*  THE POSSIBILITY OF SUCH DAMAGE.                                           */
30 /******************************************************************************/
31
32 /*
33  * Copyright © 2008 Florent Bruneau
34  */
35
36 #include <ev.h>
37 #include "server.h"
38 #include "common.h"
39
40 typedef struct server_io_t {
41     struct ev_io io;
42     int fd;
43 } server_io_t;
44
45 struct listener_t {
46     server_io_t io;
47 };
48
49 struct client_t {
50     server_io_t io;
51
52     buffer_t ibuf;
53     buffer_t obuf;
54
55     run_client_t run;
56     delete_client_t clear_data;
57     void* data;
58 };
59
60
61 static PA(listener_t) listeners = ARRAY_INIT;
62 static PA(client_t) client_pool = ARRAY_INIT;
63
64 static struct ev_loop *gl_loop           = NULL;
65 static start_client_t  gl_client_start   = NULL;
66 static delete_client_t gl_client_delete  = NULL;
67 static run_client_t    gl_client_run     = NULL;
68 static refresh_t       gl_config_refresh = NULL;
69 static void           *gl_config         = NULL;
70
71
72 /* Server io structure methods.
73  */
74
75 static inline void server_io_wipe(server_io_t *io)
76 {
77     if (io->fd >= 0) {
78         ev_io_stop(gl_loop, &io->io);
79         close(io->fd);
80         io->fd = -1;
81     }
82 }
83
84
85 /* Client methods.
86  */
87
88 /* 1 - managing clients */
89
90 static client_t* client_new(void)
91 {
92     client_t* server = p_new(client_t, 1);
93     server->io.fd  = -1;
94     return server;
95 }
96
97 static void client_wipe(client_t *server)
98 {
99     server_io_wipe(&server->io);
100     if (server->data && server->clear_data) {
101         server->clear_data(&server->data);
102     }
103     server->data = NULL;
104     server->clear_data = NULL;
105     server->run = NULL;
106 }
107
108 void client_delete(client_t **server)
109 {
110     if (*server) {
111         buffer_wipe(&(*server)->ibuf);
112         buffer_wipe(&(*server)->obuf);
113         client_wipe(*server);
114         p_delete(server);
115     }
116 }
117
118 static client_t* client_acquire(void)
119 {
120     if (client_pool.len != 0) {
121         return array_pop_last(client_pool);
122     } else {
123         return client_new();
124     }
125 }
126
127 void client_release(client_t *server)
128 {
129     client_wipe(server);
130     array_add(client_pool, server);
131 }
132
133 /* 2 - Doing I/O */
134
135 void client_io_none(client_t *server)
136 {
137     ev_io_stop(gl_loop, &server->io.io);
138 }
139
140 void client_io_rw(client_t *server)
141 {
142     ev_io_stop(gl_loop, &server->io.io);
143     ev_io_set(&server->io.io, server->io.fd, EV_READ | EV_WRITE);
144     ev_io_start(gl_loop, &server->io.io);
145 }
146
147 void client_io_ro(client_t *server)
148 {
149     ev_io_stop(gl_loop, &server->io.io);
150     ev_io_set(&server->io.io, server->io.fd, EV_READ);
151     ev_io_start(gl_loop, &server->io.io);
152 }
153
154 ssize_t client_read(client_t *client)
155 {
156     return buffer_read(&client->ibuf, client->io.fd, -1);
157 }
158
159 buffer_t *client_input_buffer(client_t *client)
160 {
161     return &client->ibuf;
162 }
163
164 buffer_t *client_output_buffer(client_t *client)
165 {
166     return &client->obuf;
167 }
168
169 void *client_data(client_t *client)
170 {
171     return client->data;
172 }
173
174
175 static void client_cb(EV_P_ struct ev_io *w, int events)
176 {
177     client_t *server = (client_t*)w;
178
179     debug("Entering client_cb for %p, %d (%d | %d)", w, events, EV_WRITE, EV_READ);
180
181     if (events & EV_WRITE && server->obuf.len) {
182         if (buffer_write(&server->obuf, server->io.fd) < 0) {
183             client_release(server);
184             return;
185         }
186         if (!server->obuf.len) {
187             client_io_ro(server);
188         }
189     }
190
191     if (events & EV_READ) {
192         if (server->run(server, gl_config) < 0) {
193             client_release(server);
194             return;
195         }
196     }
197 }
198
199 client_t *client_register(int fd, run_client_t runner, void *data)
200 {
201     if (fd < 0) {
202         return NULL;
203     }
204
205     client_t *tmp   = client_acquire();
206     tmp->io.fd      = fd;
207     tmp->data       = data;
208     tmp->run        = runner;
209     tmp->clear_data = NULL;
210     ev_io_init(&tmp->io.io, client_cb, tmp->io.fd, EV_READ);
211     ev_io_start(gl_loop, &tmp->io.io);
212     return tmp;
213 }
214
215
216 /* Listeners management.
217  */
218
219 /* 1 - Allocation */
220
221 static listener_t *listener_new(void)
222 {
223     listener_t *io = p_new(listener_t, 1);
224     io->io.fd = -1;
225     return io;
226 }
227
228 static inline void listener_wipe(listener_t *io)
229 {
230     server_io_wipe(&io->io);
231 }
232
233 static inline void listener_delete(listener_t **io)
234 {
235     if (*io) {
236         listener_wipe(*io);
237         p_delete(io);
238     }
239 }
240
241
242 /* 2 - Management */
243
244 static void listener_cb(EV_P_ struct ev_io *w, int events)
245 {
246     listener_t *server = (listener_t*)w;
247     client_t *tmp;
248     void* data = NULL;
249     int sock;
250
251     sock = accept_nonblock(server->io.fd);
252     if (sock < 0) {
253         UNIXERR("accept");
254         ev_unloop(EV_A_ EVUNLOOP_ALL);
255         return;
256     }
257
258     if (gl_client_start) {
259         data = gl_client_start(server);
260         if (data == NULL) {
261             close(sock);
262             ev_unloop(EV_A_ EVUNLOOP_ALL);
263             return;
264         }
265     }
266
267     tmp             = client_acquire();
268     tmp->io.fd      = sock;
269     tmp->data       = data;
270     tmp->run        = gl_client_run;
271     tmp->clear_data = gl_client_delete;
272     ev_io_init(&tmp->io.io, client_cb, tmp->io.fd, EV_READ);
273     ev_io_start(gl_loop, &tmp->io.io);
274 }
275
276 listener_t *start_listener(int port)
277 {
278     struct sockaddr_in addr = {
279         .sin_family = AF_INET,
280         .sin_addr   = { htonl(INADDR_LOOPBACK) },
281     };
282     listener_t *tmp;
283     int sock;
284
285     addr.sin_port = htons(port);
286     sock = tcp_listen_nonblock((const struct sockaddr *)&addr, sizeof(addr));
287     if (sock < 0) {
288         return NULL;
289     }
290
291     tmp             = listener_new();
292     tmp->io.fd      = sock;
293     ev_io_init(&tmp->io.io, listener_cb, tmp->io.fd, EV_READ);
294     ev_io_start(gl_loop, &tmp->io.io);
295     array_add(listeners, tmp);
296     return tmp;
297 }
298
299
300
301
302 /* Server runtime stuff.
303  */
304
305 static int server_init(void)
306 {
307     gl_loop = ev_default_loop(0);
308     return 0;
309 }
310
311 static void server_shutdown(void)
312 {
313     array_deep_wipe(listeners, listener_delete);
314     array_deep_wipe(client_pool, client_delete);
315 }
316 module_init(server_init);
317 module_exit(server_shutdown);
318
319
320 static void refresh_cb(EV_P_ struct ev_signal *w, int event)
321 {
322     if (!gl_config_refresh(gl_config)) {
323         ev_unloop(EV_A_ EVUNLOOP_ALL);
324     }
325 }
326
327 static void exit_cb(EV_P_ struct ev_signal *w, int event)
328 {
329     ev_unloop(EV_A_ EVUNLOOP_ALL);
330 }
331
332 int server_loop(start_client_t starter, delete_client_t deleter,
333                 run_client_t runner, refresh_t refresh, void* config)
334 {
335     struct ev_signal ev_sighup;
336     struct ev_signal ev_sigint;
337     struct ev_signal ev_sigterm;
338
339     gl_client_start   = starter;
340     gl_client_delete  = deleter;
341     gl_client_run     = runner;
342     gl_config_refresh = refresh;
343     gl_config         = config;
344
345     if (refresh != NULL) {
346         ev_signal_init(&ev_sighup, refresh_cb, SIGHUP);
347         ev_signal_start(gl_loop, &ev_sighup);
348     }
349     ev_signal_init(&ev_sigint, exit_cb, SIGINT);
350     ev_signal_start(gl_loop, &ev_sigint);
351     ev_signal_init(&ev_sigterm, exit_cb, SIGTERM);
352     ev_signal_start(gl_loop, &ev_sigterm);
353
354     info("entering processing loop");
355     ev_loop(gl_loop, 0);
356     info("exit requested");
357     return EXIT_SUCCESS;
358 }