]> granicus.if.org Git - pgbouncer/blob - src/pooler.c
use gcc attributes to uncover few correctness bugs
[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 static struct event ev_net;
28 static struct event ev_unix;
29 static int suspended = 0;
30
31 /* on accept() failure sleep 5 seconds */
32 static struct event ev_err;
33 static struct timeval err_timeout = {5, 0};
34
35 static void cleanup_unix_socket(void)
36 {
37         char fn[256];
38         if (!cf_unix_socket_dir || suspended)
39                 return;
40         snprintf(fn, sizeof(fn), "%s/.s.PGSQL.%d",
41                         cf_unix_socket_dir, cf_listen_port);
42         unlink(fn);
43 }
44
45 void get_pooler_fds(int *p_net, int *p_unix)
46 {
47         *p_net = fd_net;
48         *p_unix = fd_unix;
49 }
50
51 static int create_unix_socket(const char *socket_dir, int listen_port)
52 {
53         struct sockaddr_un un;
54         int res, sock;
55         char lockfile[256];
56         struct stat st;
57
58         /* fill sockaddr struct */
59         memset(&un, 0, sizeof(un));
60         un.sun_family = AF_UNIX;
61         snprintf(un.sun_path, sizeof(un.sun_path),
62                 "%s/.s.PGSQL.%d", socket_dir, listen_port);
63
64         /* check for lockfile */
65         snprintf(lockfile, sizeof(lockfile), "%s.lock", un.sun_path);
66         res = lstat(lockfile, &st);
67         if (res == 0)
68                 fatal("unix port %d is in use", listen_port);
69
70         /* expect old bouncer gone */
71         unlink(un.sun_path);
72
73         /* create socket */
74         sock = socket(PF_UNIX, SOCK_STREAM, 0);
75         if (sock < 0)
76                 fatal_perror("socket");
77
78         /* bind it */
79         res = bind(sock, (const struct sockaddr *)&un, sizeof(un));
80         if (res < 0)
81                 fatal_perror("bind");
82
83         /* remove socket on shutdown */
84         atexit(cleanup_unix_socket);
85
86         /* set common options */
87         tune_socket(sock, true);
88
89         /* finally, accept connections */
90         res = listen(sock, 100);
91         if (res < 0)
92                 fatal_perror("listen");
93
94         res = chmod(un.sun_path, 0777);
95         if (res < 0)
96                 fatal_perror("chmod");
97
98         log_info("listening on unix:%s", un.sun_path);
99
100         return sock;
101 }
102
103 static int create_net_socket(const char *listen_addr, int listen_port)
104 {
105         int sock;
106         struct sockaddr_in sa;
107         int res;
108         int val;
109
110         /* create socket */
111         sock = socket(AF_INET, SOCK_STREAM, 0);
112         if (sock < 0)
113                 fatal_perror("socket");
114
115         /* parse address */
116         memset(&sa, 0, sizeof(sa));
117         sa.sin_family = AF_INET;
118         sa.sin_port = htons(cf_listen_port);
119         if (strcmp(listen_addr, "*") == 0) {
120                 sa.sin_addr.s_addr = htonl(INADDR_ANY);
121         } else {
122                 sa.sin_addr.s_addr = inet_addr(listen_addr);
123                 if (sa.sin_addr.s_addr == INADDR_NONE)
124                         fatal("cannot parse addr: '%s'", listen_addr);
125         }
126
127         /* relaxed binding */
128         val = 1;
129         res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
130         if (res < 0)
131                 fatal_perror("setsockopt");
132
133         /* bind to address */
134         res = bind(sock, (struct sockaddr *)&sa, sizeof(sa));
135         if (res < 0)
136                 fatal_perror("bind");
137
138         /* set common options */
139         tune_socket(sock, false);
140
141 #ifdef TCP_DEFER_ACCEPT
142         /*
143          * Notify pooler only when also data is arrived.
144          *
145          * optval specifies how long after connection attempt to wait for data.
146          *
147          * Related to tcp_synack_retries sysctl, default 5 (corresponds 180 secs).
148          */
149         if (cf_tcp_defer_accept > 0) {
150                 val = cf_tcp_defer_accept;
151                 res = setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val));
152                 if (res < 0)
153                         fatal_perror("setsockopt TCP_DEFER_ACCEPT");
154         }
155 #endif
156
157         /* finally, accept connections */
158         res = listen(sock, 100);
159         if (res < 0)
160                 fatal_perror("listen");
161
162         log_info("listening on %s:%d", cf_listen_addr, cf_listen_port);
163
164         return sock;
165 }
166
167 static void err_wait_func(int sock, short flags, void *arg)
168 {
169         resume_pooler();
170 }
171
172 /* got new connection, associate it with client struct */
173 static void pool_accept(int sock, short flags, void *is_unix)
174 {
175         int fd;
176         PgSocket *client;
177         union {
178                 struct sockaddr_in in;
179                 struct sockaddr_un un;
180                 struct sockaddr sa;
181         } addr;
182         socklen_t len = sizeof(addr);
183
184         /* get fd */
185         fd = accept(sock, &addr.sa, &len);
186         if (fd < 0) {
187                 /*
188                  * probably fd limit, pointless to try often
189                  * wait a bit, hope that admin resolves somehow
190                  */
191                 log_error("accept() failed: %s", strerror(errno));
192                 suspend_pooler();
193                 evtimer_set(&ev_err, err_wait_func, NULL);
194                 evtimer_add(&ev_err, &err_timeout);
195                 return;
196         }
197
198         log_noise("new fd from accept=%d", fd);
199         if (is_unix) {
200                 log_debug("P: new unix client");
201                 {
202                         uid_t uid;
203                         gid_t gid;
204                         log_noise("getuid(): %d", (int)getuid());
205                         if (getpeereid(fd, &uid, &gid) >= 0)
206                                 log_noise("unix peer uid: %d", (int)uid);
207                         else
208                                 log_warning("unix peer uid failed: %s", strerror(errno));
209                 }
210                 client = accept_client(fd, NULL, true);
211         } else {
212                 log_debug("P: new tcp client");
213                 client = accept_client(fd, &addr.in, false);
214         }
215
216         if (!client) {
217                 log_debug("P: no mem for client struct");
218                 safe_close(fd);
219         }
220 }
221
222 bool use_pooler_socket(int sock, bool is_unix)
223 {
224         tune_socket(sock, is_unix);
225
226         if (is_unix)
227                 fd_unix = sock;
228         else
229                 fd_net = sock;
230         return true;
231 }
232
233 void suspend_pooler(void)
234 {
235         suspended = 1;
236
237         if (fd_net)
238                 event_del(&ev_net);
239         if (fd_unix)
240                 event_del(&ev_unix);
241 }
242
243 void resume_pooler(void)
244 {
245         suspended = 0;
246
247         if (fd_unix) {
248                 event_set(&ev_unix, fd_unix, EV_READ | EV_PERSIST, pool_accept, "1");
249                 event_add(&ev_unix, NULL);
250         }
251
252         if (fd_net) {
253                 event_set(&ev_net, fd_net, EV_READ | EV_PERSIST, pool_accept, NULL);
254                 event_add(&ev_net, NULL);
255         }
256 }
257
258 /* listen on socket - should happen after all other initializations */
259 void pooler_setup(void)
260 {
261         if (cf_listen_addr && !fd_net)
262                 fd_net = create_net_socket(cf_listen_addr, cf_listen_port);
263
264         if (cf_unix_socket_dir && !fd_unix)
265                 fd_unix = create_unix_socket(cf_unix_socket_dir, cf_listen_port);
266
267         if (!fd_net && !fd_unix)
268                 fatal("nowhere to listen on");
269
270         resume_pooler();
271 }
272