]> granicus.if.org Git - pgbouncer/blob - src/pooler.c
make failure from event_del() non-fatal
[pgbouncer] / src / pooler.c
1 /*
2  * PgBouncer - Lightweight connection pooler for PostgreSQL.
3  * 
4  * Copyright (c) 2007 Marko Kreen, Skype Technologies OÜ
5  * 
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  * 
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 /*
20  * Handling of pooler listening sockets
21  */
22
23 #include "bouncer.h"
24
25 static int fd_net = 0;
26 static int fd_unix = 0;
27
28 static struct event ev_net;
29 static struct event ev_unix;
30
31 /* if sockets are registered in libevent */
32 static bool reg_net = false;
33 static bool reg_unix = false;
34
35 /* should listening sockets be active or suspended? */
36 static bool pooler_active = false;
37
38 /* on accept() failure sleep 5 seconds */
39 static struct event ev_err;
40 static struct timeval err_timeout = {5, 0};
41
42 /* atexit() cleanup func */
43 static void cleanup_unix_socket(void)
44 {
45         char fn[256];
46
47         /* avoid cleanup if exit() while suspended */
48         if (!reg_unix)
49                 return;
50
51         snprintf(fn, sizeof(fn), "%s/.s.PGSQL.%d",
52                         cf_unix_socket_dir, cf_listen_port);
53         unlink(fn);
54 }
55
56 void get_pooler_fds(int *p_net, int *p_unix)
57 {
58         *p_net = fd_net;
59         *p_unix = fd_unix;
60 }
61
62 static int create_unix_socket(const char *socket_dir, int listen_port)
63 {
64         struct sockaddr_un un;
65         int res, sock;
66         char lockfile[256];
67         struct stat st;
68
69         /* fill sockaddr struct */
70         memset(&un, 0, sizeof(un));
71         un.sun_family = AF_UNIX;
72         snprintf(un.sun_path, sizeof(un.sun_path),
73                 "%s/.s.PGSQL.%d", socket_dir, listen_port);
74
75         /* check for lockfile */
76         snprintf(lockfile, sizeof(lockfile), "%s.lock", un.sun_path);
77         res = lstat(lockfile, &st);
78         if (res == 0)
79                 fatal("unix port %d is in use", listen_port);
80
81         /* expect old bouncer gone */
82         unlink(un.sun_path);
83
84         /* create socket */
85         sock = socket(PF_UNIX, SOCK_STREAM, 0);
86         if (sock < 0)
87                 fatal_perror("socket");
88
89         /* bind it */
90         res = bind(sock, (const struct sockaddr *)&un, sizeof(un));
91         if (res < 0)
92                 fatal_perror("bind");
93
94         /* remove socket on shutdown */
95         atexit(cleanup_unix_socket);
96
97         /* set common options */
98         tune_socket(sock, true);
99
100         /* finally, accept connections */
101         res = listen(sock, 100);
102         if (res < 0)
103                 fatal_perror("listen");
104
105         res = chmod(un.sun_path, 0777);
106         if (res < 0)
107                 fatal_perror("chmod");
108
109         log_info("listening on unix:%s", un.sun_path);
110
111         return sock;
112 }
113
114 static int create_net_socket(const char *listen_addr, int listen_port)
115 {
116         int sock;
117         struct sockaddr_in sa;
118         int res;
119         int val;
120
121         /* create socket */
122         sock = socket(AF_INET, SOCK_STREAM, 0);
123         if (sock < 0)
124                 fatal_perror("socket");
125
126         /* parse address */
127         memset(&sa, 0, sizeof(sa));
128         sa.sin_family = AF_INET;
129         sa.sin_port = htons(cf_listen_port);
130         if (strcmp(listen_addr, "*") == 0) {
131                 sa.sin_addr.s_addr = htonl(INADDR_ANY);
132         } else {
133                 sa.sin_addr.s_addr = inet_addr(listen_addr);
134                 if (sa.sin_addr.s_addr == INADDR_NONE)
135                         fatal("cannot parse addr: '%s'", listen_addr);
136         }
137
138         /* relaxed binding */
139         val = 1;
140         res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
141         if (res < 0)
142                 fatal_perror("setsockopt");
143
144         /* bind to address */
145         res = bind(sock, (struct sockaddr *)&sa, sizeof(sa));
146         if (res < 0)
147                 fatal_perror("bind");
148
149         /* set common options */
150         tune_socket(sock, false);
151
152 #ifdef TCP_DEFER_ACCEPT
153         /*
154          * Notify pooler only when also data is arrived.
155          *
156          * optval specifies how long after connection attempt to wait for data.
157          *
158          * Related to tcp_synack_retries sysctl, default 5 (corresponds 180 secs).
159          */
160         if (cf_tcp_defer_accept > 0) {
161                 val = cf_tcp_defer_accept;
162                 res = setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val));
163                 if (res < 0)
164                         fatal_perror("setsockopt TCP_DEFER_ACCEPT");
165         }
166 #endif
167
168         /* finally, accept connections */
169         res = listen(sock, 100);
170         if (res < 0)
171                 fatal_perror("listen");
172
173         log_info("listening on %s:%d", cf_listen_addr, cf_listen_port);
174
175         return sock;
176 }
177
178 static void err_wait_func(int sock, short flags, void *arg)
179 {
180         if (cf_pause_mode != P_SUSPEND)
181                 resume_pooler();
182 }
183
184 /* got new connection, associate it with client struct */
185 static void pool_accept(int sock, short flags, void *is_unix)
186 {
187         int fd;
188         PgSocket *client;
189         union {
190                 struct sockaddr_in in;
191                 struct sockaddr_un un;
192                 struct sockaddr sa;
193         } addr;
194         socklen_t len = sizeof(addr);
195
196 loop:
197         /* get fd */
198         fd = safe_accept(sock, &addr.sa, &len);
199         if (fd < 0) {
200                 if (errno == EAGAIN)
201                         return;
202                 else if (errno == ECONNABORTED)
203                         return;
204
205                 /*
206                  * probably fd limit, pointless to try often
207                  * wait a bit, hope that admin resolves somehow
208                  */
209                 log_error("accept() failed: %s", strerror(errno));
210                 evtimer_set(&ev_err, err_wait_func, NULL);
211                 safe_evtimer_add(&ev_err, &err_timeout);
212                 suspend_pooler();
213                 return;
214         }
215
216         log_noise("new fd from accept=%d", fd);
217         if (is_unix) {
218                 log_debug("P: new unix client");
219                 {
220                         uid_t uid;
221                         gid_t gid;
222                         log_noise("getuid(): %d", (int)getuid());
223                         if (getpeereid(fd, &uid, &gid) >= 0)
224                                 log_noise("unix peer uid: %d", (int)uid);
225                         else
226                                 log_warning("unix peer uid failed: %s", strerror(errno));
227                 }
228                 client = accept_client(fd, NULL, true);
229         } else {
230                 log_debug("P: new tcp client");
231                 client = accept_client(fd, &addr.in, false);
232         }
233
234         if (!client) {
235                 log_warning("P: no mem for client struct");
236                 return;
237         }
238
239         /*
240          * there may be several clients waiting,
241          * avoid context switch by looping
242          */
243         goto loop;
244 }
245
246 bool use_pooler_socket(int sock, bool is_unix)
247 {
248         tune_socket(sock, is_unix);
249
250         if (is_unix)
251                 fd_unix = sock;
252         else
253                 fd_net = sock;
254         return true;
255 }
256
257 void suspend_pooler(void)
258 {
259         pooler_active = false;
260
261         if (fd_net && reg_net) {
262                 if (event_del(&ev_net) < 0) {
263                         log_warning("suspend_pooler, event_del: %s", strerror(errno));
264                         return;
265                 }
266                 reg_net = false;
267         }
268         if (fd_unix && reg_unix) {
269                 if (event_del(&ev_unix) < 0) {
270                         log_warning("suspend_pooler, event_del: %s", strerror(errno));
271                         return;
272                 }
273                 reg_unix = false;
274         }
275 }
276
277 void resume_pooler(void)
278 {
279         pooler_active = true;
280
281         if (fd_unix && !reg_unix) {
282                 event_set(&ev_unix, fd_unix, EV_READ | EV_PERSIST, pool_accept, "1");
283                 if (event_add(&ev_unix, NULL) < 0) {
284                         log_warning("event_add failed: %s", strerror(errno));
285                         return;
286                 }
287                 reg_unix = true;
288         }
289
290         if (fd_net && !reg_net) {
291                 event_set(&ev_net, fd_net, EV_READ | EV_PERSIST, pool_accept, NULL);
292                 if (event_add(&ev_net, NULL) < 0) {
293                         log_warning("event_add failed: %s", strerror(errno));
294                 }
295                 reg_net = true;
296         }
297 }
298
299 /* listen on socket - should happen after all other initializations */
300 void pooler_setup(void)
301 {
302         if (cf_listen_addr && !fd_net)
303                 fd_net = create_net_socket(cf_listen_addr, cf_listen_port);
304
305         if (cf_unix_socket_dir && !fd_unix)
306                 fd_unix = create_unix_socket(cf_unix_socket_dir, cf_listen_port);
307
308         if (!fd_net && !fd_unix)
309                 fatal("nowhere to listen on");
310
311         resume_pooler();
312 }
313
314 /* retry previously failed suspend_pooler() / resume_pooler() */
315 void per_loop_pooler_maint(void)
316 {
317         if (pooler_active) {
318                 if ((fd_unix && !reg_unix) || (fd_net && !reg_net))
319                         resume_pooler();
320         } else {
321                 if ((fd_unix && reg_unix) || (fd_net && reg_net))
322                         suspend_pooler();
323         }
324 }
325