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