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 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 /******************************************************************************/
33 * Copyright © 2008 Florent Bruneau
40 typedef struct server_io_t {
56 delete_client_t clear_data;
61 static PA(listener_t) listeners = ARRAY_INIT;
62 static PA(client_t) client_pool = ARRAY_INIT;
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;
72 /* Server io structure methods.
75 static inline void server_io_wipe(server_io_t *io)
78 ev_io_stop(gl_loop, &io->io);
88 /* 1 - managing clients */
90 static client_t* client_new(void)
92 client_t* server = p_new(client_t, 1);
97 static void client_wipe(client_t *server)
99 server_io_wipe(&server->io);
100 if (server->data && server->clear_data) {
101 server->clear_data(&server->data);
104 server->clear_data = NULL;
108 void client_delete(client_t **server)
111 buffer_wipe(&(*server)->ibuf);
112 buffer_wipe(&(*server)->obuf);
113 client_wipe(*server);
118 static client_t* client_acquire(void)
120 if (client_pool.len != 0) {
121 return array_pop_last(client_pool);
127 void client_release(client_t *server)
130 array_add(client_pool, server);
135 void client_io_none(client_t *server)
137 ev_io_stop(gl_loop, &server->io.io);
140 void client_io_rw(client_t *server)
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);
147 void client_io_ro(client_t *server)
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);
154 ssize_t client_read(client_t *client)
156 return buffer_read(&client->ibuf, client->io.fd, -1);
159 buffer_t *client_input_buffer(client_t *client)
161 return &client->ibuf;
164 buffer_t *client_output_buffer(client_t *client)
166 return &client->obuf;
169 void *client_data(client_t *client)
175 static void client_cb(EV_P_ struct ev_io *w, int events)
177 client_t *server = (client_t*)w;
179 if (events & EV_WRITE && server->obuf.len) {
180 if (buffer_write(&server->obuf, server->io.fd) < 0) {
181 client_release(server);
184 if (!server->obuf.len) {
185 client_io_ro(server);
189 if (events & EV_READ) {
190 if (server->run(server, gl_config) < 0) {
191 client_release(server);
197 client_t *client_register(int fd, run_client_t runner, void *data)
203 client_t *tmp = client_acquire();
207 tmp->clear_data = NULL;
208 ev_io_init(&tmp->io.io, client_cb, tmp->io.fd, EV_READ);
209 ev_io_start(gl_loop, &tmp->io.io);
214 /* Listeners management.
219 static listener_t *listener_new(void)
221 listener_t *io = p_new(listener_t, 1);
226 static inline void listener_wipe(listener_t *io)
228 server_io_wipe(&io->io);
231 static inline void listener_delete(listener_t **io)
242 static void listener_cb(EV_P_ struct ev_io *w, int events)
244 listener_t *server = (listener_t*)w;
249 sock = accept_nonblock(server->io.fd);
252 ev_unloop(EV_A_ EVUNLOOP_ALL);
256 if (gl_client_start) {
257 data = gl_client_start(server);
260 ev_unloop(EV_A_ EVUNLOOP_ALL);
265 tmp = client_acquire();
268 tmp->run = gl_client_run;
269 tmp->clear_data = gl_client_delete;
270 ev_io_init(&tmp->io.io, client_cb, tmp->io.fd, EV_READ);
271 ev_io_start(gl_loop, &tmp->io.io);
274 listener_t *start_listener(int port)
276 struct sockaddr_in addr = {
277 .sin_family = AF_INET,
278 .sin_addr = { htonl(INADDR_LOOPBACK) },
283 addr.sin_port = htons(port);
284 sock = tcp_listen_nonblock((const struct sockaddr *)&addr, sizeof(addr));
289 tmp = listener_new();
291 ev_io_init(&tmp->io.io, listener_cb, tmp->io.fd, EV_READ);
292 ev_io_start(gl_loop, &tmp->io.io);
293 array_add(listeners, tmp);
300 /* Server runtime stuff.
303 static int server_init(void)
305 gl_loop = ev_default_loop(0);
309 static void server_shutdown(void)
311 array_deep_wipe(listeners, listener_delete);
312 array_deep_wipe(client_pool, client_delete);
314 module_init(server_init);
315 module_exit(server_shutdown);
318 static void refresh_cb(EV_P_ struct ev_signal *w, int event)
320 log_state = "refreshing ";
321 if (!gl_config_refresh(gl_config)) {
322 ev_unloop(EV_A_ EVUNLOOP_ALL);
330 static void exit_cb(EV_P_ struct ev_signal *w, int event)
332 ev_unloop(EV_A_ EVUNLOOP_ALL);
335 int server_loop(start_client_t starter, delete_client_t deleter,
336 run_client_t runner, refresh_t refresh, void* config)
338 struct ev_signal ev_sighup;
339 struct ev_signal ev_sigint;
340 struct ev_signal ev_sigterm;
342 gl_client_start = starter;
343 gl_client_delete = deleter;
344 gl_client_run = runner;
345 gl_config_refresh = refresh;
348 if (refresh != NULL) {
349 ev_signal_init(&ev_sighup, refresh_cb, SIGHUP);
350 ev_signal_start(gl_loop, &ev_sighup);
352 ev_signal_init(&ev_sigint, exit_cb, SIGINT);
353 ev_signal_start(gl_loop, &ev_sigint);
354 ev_signal_init(&ev_sigterm, exit_cb, SIGTERM);
355 ev_signal_start(gl_loop, &ev_sigterm);
358 info("entering processing loop");
360 info("exit requested");