1 /******************************************************************************/
2 /* pfixtools: a collection of postfix related tools */
4 /* ________________________________________________________________________ */
6 /* Redistribution and use in source and binary forms, with or without */
7 /* modification, are permitted provided that the following conditions */
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 */
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. */
31 /* Copyright (c) 2006-2008 the Authors */
32 /* see AUTHORS and source files for details */
33 /******************************************************************************/
36 * Copyright © 2008 Florent Bruneau
43 typedef struct server_io_t {
59 delete_client_t clear_data;
64 static PA(listener_t) listeners = ARRAY_INIT;
65 static PA(client_t) client_pool = ARRAY_INIT;
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;
75 /* Server io structure methods.
78 static inline void server_io_wipe(server_io_t *io)
81 ev_io_stop(gl_loop, &io->io);
91 /* 1 - managing clients */
93 static client_t* client_new(void)
95 client_t* server = p_new(client_t, 1);
100 static void client_wipe(client_t *server)
102 server_io_wipe(&server->io);
103 if (server->data && server->clear_data) {
104 server->clear_data(&server->data);
106 server->obuf.len = 0;
107 server->ibuf.len = 0;
109 server->clear_data = NULL;
113 void client_delete(client_t **server)
116 buffer_wipe(&(*server)->ibuf);
117 buffer_wipe(&(*server)->obuf);
118 client_wipe(*server);
123 static client_t* client_acquire(void)
125 if (client_pool.len != 0) {
126 return array_pop_last(client_pool);
132 void client_release(client_t *server)
135 array_add(client_pool, server);
140 void client_io_none(client_t *server)
142 ev_io_stop(gl_loop, &server->io.io);
145 void client_io_rw(client_t *server)
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);
152 void client_io_ro(client_t *server)
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);
159 ssize_t client_read(client_t *client)
161 return buffer_read(&client->ibuf, client->io.fd, -1);
164 buffer_t *client_input_buffer(client_t *client)
166 return &client->ibuf;
169 buffer_t *client_output_buffer(client_t *client)
171 return &client->obuf;
174 void *client_data(client_t *client)
180 static void client_cb(EV_P_ struct ev_io *w, int events)
182 client_t *server = (client_t*)w;
184 if (events & EV_WRITE && server->obuf.len) {
185 if (buffer_write(&server->obuf, server->io.fd) < 0) {
186 client_release(server);
189 if (!server->obuf.len) {
190 client_io_ro(server);
194 if (events & EV_READ) {
195 if (server->run(server, gl_config) < 0) {
196 client_release(server);
202 client_t *client_register(int fd, run_client_t runner, void *data)
208 client_t *tmp = client_acquire();
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);
219 /* Listeners management.
224 static listener_t *listener_new(void)
226 listener_t *io = p_new(listener_t, 1);
231 static inline void listener_wipe(listener_t *io)
233 server_io_wipe(&io->io);
236 static inline void listener_delete(listener_t **io)
247 static void listener_cb(EV_P_ struct ev_io *w, int events)
249 listener_t *server = (listener_t*)w;
254 sock = accept_nonblock(server->io.fd);
257 ev_unloop(EV_A_ EVUNLOOP_ALL);
261 if (gl_client_start) {
262 data = gl_client_start(server);
265 ev_unloop(EV_A_ EVUNLOOP_ALL);
270 tmp = client_acquire();
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);
279 listener_t *start_listener(int port)
281 struct sockaddr_in addr = {
282 .sin_family = AF_INET,
283 .sin_addr = { htonl(INADDR_LOOPBACK) },
288 addr.sin_port = htons(port);
289 sock = tcp_listen_nonblock((const struct sockaddr *)&addr, sizeof(addr));
294 tmp = listener_new();
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);
305 /* Server runtime stuff.
308 static int server_init(void)
310 gl_loop = ev_default_loop(0);
314 static void server_shutdown(void)
316 array_deep_wipe(listeners, listener_delete);
317 array_deep_wipe(client_pool, client_delete);
319 module_init(server_init);
320 module_exit(server_shutdown);
323 static void refresh_cb(EV_P_ struct ev_signal *w, int event)
325 log_state = "refreshing ";
326 if (!gl_config_refresh(gl_config)) {
327 ev_unloop(EV_A_ EVUNLOOP_ALL);
335 static void exit_cb(EV_P_ struct ev_signal *w, int event)
337 ev_unloop(EV_A_ EVUNLOOP_ALL);
340 int server_loop(start_client_t starter, delete_client_t deleter,
341 run_client_t runner, refresh_t refresh, void* config)
343 struct ev_signal ev_sighup;
344 struct ev_signal ev_sigint;
345 struct ev_signal ev_sigterm;
347 gl_client_start = starter;
348 gl_client_delete = deleter;
349 gl_client_run = runner;
350 gl_config_refresh = refresh;
353 if (refresh != NULL) {
354 ev_signal_init(&ev_sighup, refresh_cb, SIGHUP);
355 ev_signal_start(gl_loop, &ev_sighup);
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);
363 info("entering processing loop");
365 info("exit requested");