Implement lightweight getaddrinfo_a wrappers.
[apps/pfixtools.git] / gai.c
1 /******************************************************************************/
2 /*          postlicyd: a postfix policy daemon with a lot of features         */
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 © 2007 Pierre Habouzit
34  */
35
36 #include <assert.h>
37 #include <signal.h>
38 #include <stdlib.h>
39 #include <sysexits.h>
40 #include <syslog.h>
41
42 #include "gai.h"
43
44 #define GAI_SIGNO  (SIGRTMIN+0)
45
46 static struct {
47     gai_req **ring;
48     int start, len, size;
49 } fifo = {
50     .ring = NULL,
51     .start = 0,
52     .len   = 0,
53     .size  = 0,
54 };
55
56 static int gai_fifo_end(void)
57 {
58     int res = fifo.start + fifo.len;
59     return res >= fifo.size ? res - fifo.size : fifo.size;
60 }
61
62 static void gai_fifo_append(gai_req *rq)
63 {
64     if (fifo.len >= fifo.size) {
65         p_realloc(&fifo.ring, fifo.size += 16);
66     }
67
68     fifo.ring[gai_fifo_end()] = rq;
69     fifo.len++;
70 }
71
72 static gai_req *gai_fifo_pop(void)
73 {
74     gai_req *res = NULL;
75
76     while (!res && fifo.len) {
77         res = fifo.ring[fifo.start];
78         fifo.start++, fifo.len--;
79         if (fifo.start >= fifo.size)
80             fifo.start = 0;
81     }
82
83     return res;
84 }
85
86 static void gai_fifo_remove(gai_req *rq)
87 {
88     int i, end = gai_fifo_end();
89
90     if (fifo.start + fifo.len <= fifo.size) {
91         for (i = fifo.start; i < end; i++) {
92             if (fifo.ring[i] == rq)
93                 fifo.ring[i] = NULL;
94         }
95     } else {
96         for (i = fifo.start; i < fifo.size; i++) {
97             if (fifo.ring[i] == rq)
98                 fifo.ring[i] = NULL;
99         }
100         for (i = 0; i < end; i++) {
101             if (fifo.ring[i] == rq)
102                 fifo.ring[i] = NULL;
103         }
104     }
105 }
106
107 static gai_req *gai_req_new(void)
108 {
109     gai_req *rq = p_new(gai_req, 1);
110     rq->cbp = &rq->cb;
111     return rq;
112 }
113 static void gai_req_delete(gai_req **rqp)
114 {
115     if (*rqp) {
116         gai_req *rq = *rqp;
117
118         switch (gai_error(rq->cbp)) {
119           case EAI_INPROGRESS:
120             if (gai_cancel(rq->cbp) == EAI_NOTCANCELED) {
121                 rq->caller = NULL;
122                 *rqp = NULL;
123                 return;
124             }
125             break;
126
127           case EAI_CANCELED:
128             break;
129
130           default: /* we are likely in the notify list remove us ! */
131             if (rq->caller) {
132                 gai_fifo_remove(rq);
133             }
134         }
135
136         p_delete((char **)&rq->cb.ar_name);
137         freeaddrinfo(rq->cb.ar_result);
138         rq->cb.ar_result = NULL;
139         p_delete(rqp);
140     }
141 }
142
143 gai_req *gai_query(job_t *caller, const char *lookup)
144 {
145     struct sigevent se = {
146         .sigev_signo  = GAI_SIGNO,
147         .sigev_notify = SIGEV_SIGNAL,
148     };
149     gai_req *res;
150
151     se.sigev_value.sival_ptr = res = gai_req_new();
152     res->cb.ar_name = strdup(lookup);
153     res->caller     = caller;
154     getaddrinfo_a(GAI_NOWAIT, &res->cbp, 1, &se);
155
156     return res;
157 }
158
159 void gai_abort(gai_req **rqp)
160 {
161     gai_req_delete(rqp);
162 }
163
164
165 static void gai_sigaction(int sig, siginfo_t *si, void *ctx)
166 {
167     gai_req *req = si->_sifields._rt.si_sigval.sival_ptr;
168
169     assert (sig == GAI_SIGNO);
170     assert (req && gai_error(req->cbp) != EAI_INPROGRESS);
171
172     if (req->caller) {
173         gai_fifo_append(req);
174     } else {
175         gai_req_delete(&req);
176     }
177 }
178
179 void gai_initialize(void)
180 {
181     struct sigaction sa = {
182         .sa_sigaction = &gai_sigaction,
183         .sa_flags     = SA_SIGINFO,
184     };
185     if (sigaction(GAI_SIGNO, &sa, NULL) < 0) {
186         syslog(LOG_ERR, "cannot hook SIGRTMIN+0: %m");
187         exit(EX_OSERR);
188     }
189 }
190
191 void gai_process(void)
192 {
193     gai_req *req;
194
195     while ((req = gai_fifo_pop())) {
196         assert (req->caller && req->caller->process);
197         req->caller->process(req->caller);
198         req->caller = NULL; /* make delete faster: avoid gai_fifo_remove() */
199     }
200 }
201
202 void gai_shutdown(void)
203 {
204     /* TODO: deallocate the fifo properly */
205 }