server_release didn't clear the buffers.
[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->obuf.len = 0;
104     server->ibuf.len = 0;
105     server->data = NULL;
106     server->clear_data = NULL;
107     server->run = NULL;
108 }
109
110 void client_delete(client_t **server)
111 {
112     if (*server) {
113         buffer_wipe(&(*server)->ibuf);
114         buffer_wipe(&(*server)->obuf);
115         client_wipe(*server);
116         p_delete(server);
117     }
118 }
119
120 static client_t* client_acquire(void)
121 {
122     if (client_pool.len != 0) {
123         return array_pop_last(client_pool);
124     } else {
125         return client_new();
126     }
127 }
128
129 void client_release(client_t *server)
130 {
131     client_wipe(server);
132     array_add(client_pool, server);
133 }
134
135 /* 2 - Doing I/O */
136
137 void client_io_none(client_t *server)
138 {
139     ev_io_stop(gl_loop, &server->io.io);
140 }
141
142 void client_io_rw(client_t *server)
143 {
144     ev_io_stop(gl_loop, &server->io.io);
145     ev_io_set(&server->io.io, server->io.fd, EV_READ | EV_WRITE);
146     ev_io_start(gl_loop, &server->io.io);
147 }
148
149 void client_io_ro(client_t *server)
150 {
151     ev_io_stop(gl_loop, &server->io.io);
152     ev_io_set(&server->io.io, server->io.fd, EV_READ);
153     ev_io_start(gl_loop, &server->io.io);
154 }
155
156 ssize_t client_read(client_t *client)
157 {
158     return buffer_read(&client->ibuf, client->io.fd, -1);
159 }
160
161 buffer_t *client_input_buffer(client_t *client)
162 {
163     return &client->ibuf;
164 }
165
166 buffer_t *client_output_buffer(client_t *client)
167 {
168     return &client->obuf;
169 }
170
171 void *client_data(client_t *client)
172 {
173     return client->data;
174 }
175
176
177 static void client_cb(EV_P_ struct ev_io *w, int events)
178 {
179     client_t *server = (client_t*)w;
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     log_state = "refreshing ";
323     if (!gl_config_refresh(gl_config)) {
324         ev_unloop(EV_A_ EVUNLOOP_ALL);
325         info("failed");
326     } else {
327         info("done");
328     }
329     log_state = "";
330 }
331
332 static void exit_cb(EV_P_ struct ev_signal *w, int event)
333 {
334     ev_unloop(EV_A_ EVUNLOOP_ALL);
335 }
336
337 int server_loop(start_client_t starter, delete_client_t deleter,
338                 run_client_t runner, refresh_t refresh, void* config)
339 {
340     struct ev_signal ev_sighup;
341     struct ev_signal ev_sigint;
342     struct ev_signal ev_sigterm;
343
344     gl_client_start   = starter;
345     gl_client_delete  = deleter;
346     gl_client_run     = runner;
347     gl_config_refresh = refresh;
348     gl_config         = config;
349
350     if (refresh != NULL) {
351         ev_signal_init(&ev_sighup, refresh_cb, SIGHUP);
352         ev_signal_start(gl_loop, &ev_sighup);
353     }
354     ev_signal_init(&ev_sigint, exit_cb, SIGINT);
355     ev_signal_start(gl_loop, &ev_sigint);
356     ev_signal_init(&ev_sigterm, exit_cb, SIGTERM);
357     ev_signal_start(gl_loop, &ev_sigterm);
358
359     log_state = "";
360     info("entering processing loop");
361     ev_loop(gl_loop, 0);
362     info("exit requested");
363     return EXIT_SUCCESS;
364 }