Asynchronous DNS queries on iplist.
[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 "server.h"
37 #include "epoll.h"
38 #include "common.h"
39
40 static PA(server_t) listeners   = ARRAY_INIT;
41 static PA(server_t) server_pool = ARRAY_INIT;
42
43 static server_t* server_new(void)
44 {
45     server_t* server = p_new(server_t, 1);
46     server->fd  = -1;
47     return server;
48 }
49
50 static void server_wipe(server_t *server)
51 {
52     server->listener = false;
53     if (server->fd >= 0) {
54         epoll_unregister(server->fd);
55         close(server->fd);
56         server->fd = -1;
57     }
58     if (server->data && server->clear_data) {
59         server->clear_data(&server->data);
60     }
61 }
62
63 static void server_delete(server_t **server)
64 {
65     if (*server) {
66         buffer_wipe(&(*server)->ibuf);
67         buffer_wipe(&(*server)->obuf);
68         server_wipe(*server);
69         p_delete(server);
70     }
71 }
72
73 static server_t* server_acquire(void)
74 {
75     if (server_pool.len != 0) {
76         return array_elt(server_pool, --server_pool.len);
77     } else {
78         return server_new();
79     }
80 }
81
82 void server_release(server_t *server)
83 {
84     server_wipe(server);
85     array_add(server_pool, server);
86 }
87
88 static void server_shutdown(void)
89 {
90     printf("Server shutdown");
91     array_deep_wipe(listeners, server_delete);
92     array_deep_wipe(server_pool, server_delete);
93 }
94
95 module_exit(server_shutdown);
96
97 int start_server(int port, start_listener_t starter, delete_client_t deleter)
98 {
99     struct sockaddr_in addr = {
100         .sin_family = AF_INET,
101         .sin_addr   = { htonl(INADDR_LOOPBACK) },
102     };
103     server_t *tmp;
104     void* data = NULL;
105     int sock;
106
107     addr.sin_port = htons(port);
108     sock = tcp_listen_nonblock((const struct sockaddr *)&addr, sizeof(addr));
109     if (sock < 0) {
110         return -1;
111     }
112
113     if (starter) {
114       data = starter();
115       if (data == NULL) {
116         close(sock);
117         return -1;
118       }
119     }
120
121     tmp             = server_acquire();
122     tmp->fd         = sock;
123     tmp->listener   = true;
124     tmp->data       = data;
125     tmp->run        = NULL;
126     tmp->clear_data = deleter;
127     epoll_register(sock, EPOLLIN, tmp);
128     array_add(listeners, tmp);
129     return 0;
130 }
131
132 static int start_client(server_t *server, start_client_t starter,
133                         run_client_t runner, delete_client_t deleter)
134 {
135     server_t *tmp;
136     void* data = NULL;
137     int sock;
138
139     sock = accept_nonblock(server->fd);
140     if (sock < 0) {
141         UNIXERR("accept");
142         return -1;
143     }
144
145     if (starter) {
146         data = starter(server);
147         if (data == NULL) {
148             close(sock);
149             return -1;
150         }
151     }
152
153     tmp             = server_acquire();
154     tmp->listener   = false;
155     tmp->fd         = sock;
156     tmp->data       = data;
157     tmp->run        = runner;
158     tmp->clear_data = deleter;
159     epoll_register(sock, EPOLLIN, tmp);
160     return 0;
161 }
162
163 server_t *server_register(int fd, run_client_t runner, void *data)
164 {
165     if (fd < 0) {
166         return NULL;
167     }
168
169     server_t *tmp   = server_acquire();
170     tmp->listener   = false;
171     tmp->fd         = fd;
172     tmp->data       = data;
173     tmp->run        = runner;
174     tmp->clear_data = NULL;
175     epoll_register(fd, EPOLLIN, tmp);
176     return tmp;
177 }
178
179 int server_loop(start_client_t starter, delete_client_t deleter,
180                 run_client_t runner, refresh_t refresh, void* config)
181 {
182     info("entering processing loop");
183     while (!sigint) {
184         struct epoll_event evts[1024];
185         int n;
186
187         if (sighup && refresh) {
188             sighup = false;
189             info("refreshing...");
190             if (!refresh(config)) {
191                 crit("error while refreshing configuration");
192                 return EXIT_FAILURE;
193             }
194             info("refresh done, processing loop restarts");
195         }
196
197         n = epoll_select(evts, countof(evts), -1);
198         if (n < 0) {
199             if (errno != EAGAIN && errno != EINTR) {
200                 UNIXERR("epoll_wait");
201                 return EXIT_FAILURE;
202             }
203             continue;
204         }
205
206         while (--n >= 0) {
207             server_t *d = evts[n].data.ptr;
208
209             if (d->listener) {
210                 (void)start_client(d, starter, runner, deleter);
211                 continue;
212             }
213
214             if (evts[n].events & EPOLLIN) {
215                 if (d->run(d, config) < 0) {
216                     server_release(d);
217                     continue;
218                 }
219             }
220
221             if ((evts[n].events & EPOLLOUT) && d->obuf.len) {
222                 if (buffer_write(&d->obuf, d->fd) < 0) {
223                     server_release(d);
224                     continue;
225                 }
226                 if (!d->obuf.len) {
227                     epoll_modify(d->fd, EPOLLIN, d);
228                 }
229             }
230         }
231     }
232     info("exit requested");
233     return EXIT_SUCCESS;
234 }