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);
103 server->obuf.len = 0;
104 server->ibuf.len = 0;
106 server->clear_data = NULL;
110 void client_delete(client_t **server)
113 buffer_wipe(&(*server)->ibuf);
114 buffer_wipe(&(*server)->obuf);
115 client_wipe(*server);
120 static client_t* client_acquire(void)
122 if (client_pool.len != 0) {
123 return array_pop_last(client_pool);
129 void client_release(client_t *server)
132 array_add(client_pool, server);
137 void client_io_none(client_t *server)
139 ev_io_stop(gl_loop, &server->io.io);
142 void client_io_rw(client_t *server)
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);
149 void client_io_ro(client_t *server)
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);
156 ssize_t client_read(client_t *client)
158 return buffer_read(&client->ibuf, client->io.fd, -1);
161 buffer_t *client_input_buffer(client_t *client)
163 return &client->ibuf;
166 buffer_t *client_output_buffer(client_t *client)
168 return &client->obuf;
171 void *client_data(client_t *client)
177 static void client_cb(EV_P_ struct ev_io *w, int events)
179 client_t *server = (client_t*)w;
181 if (events & EV_WRITE && server->obuf.len) {
182 if (buffer_write(&server->obuf, server->io.fd) < 0) {
183 client_release(server);
186 if (!server->obuf.len) {
187 client_io_ro(server);
191 if (events & EV_READ) {
192 if (server->run(server, gl_config) < 0) {
193 client_release(server);
199 client_t *client_register(int fd, run_client_t runner, void *data)
205 client_t *tmp = client_acquire();
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);
216 /* Listeners management.
221 static listener_t *listener_new(void)
223 listener_t *io = p_new(listener_t, 1);
228 static inline void listener_wipe(listener_t *io)
230 server_io_wipe(&io->io);
233 static inline void listener_delete(listener_t **io)
244 static void listener_cb(EV_P_ struct ev_io *w, int events)
246 listener_t *server = (listener_t*)w;
251 sock = accept_nonblock(server->io.fd);
254 ev_unloop(EV_A_ EVUNLOOP_ALL);
258 if (gl_client_start) {
259 data = gl_client_start(server);
262 ev_unloop(EV_A_ EVUNLOOP_ALL);
267 tmp = client_acquire();
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);
276 listener_t *start_listener(int port)
278 struct sockaddr_in addr = {
279 .sin_family = AF_INET,
280 .sin_addr = { htonl(INADDR_LOOPBACK) },
285 addr.sin_port = htons(port);
286 sock = tcp_listen_nonblock((const struct sockaddr *)&addr, sizeof(addr));
291 tmp = listener_new();
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);
302 /* Server runtime stuff.
305 static int server_init(void)
307 gl_loop = ev_default_loop(0);
311 static void server_shutdown(void)
313 array_deep_wipe(listeners, listener_delete);
314 array_deep_wipe(client_pool, client_delete);
316 module_init(server_init);
317 module_exit(server_shutdown);
320 static void refresh_cb(EV_P_ struct ev_signal *w, int event)
322 log_state = "refreshing ";
323 if (!gl_config_refresh(gl_config)) {
324 ev_unloop(EV_A_ EVUNLOOP_ALL);
332 static void exit_cb(EV_P_ struct ev_signal *w, int event)
334 ev_unloop(EV_A_ EVUNLOOP_ALL);
337 int server_loop(start_client_t starter, delete_client_t deleter,
338 run_client_t runner, refresh_t refresh, void* config)
340 struct ev_signal ev_sighup;
341 struct ev_signal ev_sigint;
342 struct ev_signal ev_sigterm;
344 gl_client_start = starter;
345 gl_client_delete = deleter;
346 gl_client_run = runner;
347 gl_config_refresh = refresh;
350 if (refresh != NULL) {
351 ev_signal_init(&ev_sighup, refresh_cb, SIGHUP);
352 ev_signal_start(gl_loop, &ev_sighup);
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);
360 info("entering processing loop");
362 info("exit requested");