|
13 | 13 | *
|
14 | 14 | * Copyright (c) 2001-2003, PostgreSQL Global Development Group
|
15 | 15 | *
|
16 |
| - * $Header: /cvsroot/pgsql/src/backend/postmaster/pgstat.c,v 1.46 2003/11/07 21:55:50 tgl Exp $ |
| 16 | + * $Header: /cvsroot/pgsql/src/backend/postmaster/pgstat.c,v 1.47 2003/11/15 17:24:07 tgl Exp $ |
17 | 17 | * ----------
|
18 | 18 | */
|
19 | 19 | #include "postgres.h"
|
@@ -203,60 +203,83 @@ pgstat_init(void)
|
203 | 203 | goto startup_failed;
|
204 | 204 | }
|
205 | 205 |
|
| 206 | + /* |
| 207 | + * On some platforms, getaddrinfo_all() may return multiple addresses |
| 208 | + * only one of which will actually work (eg, both IPv6 and IPv4 addresses |
| 209 | + * when kernel will reject IPv6). Worse, the failure may occur at the |
| 210 | + * bind() or perhaps even connect() stage. So we must loop through the |
| 211 | + * results till we find a working combination. We will generate LOG |
| 212 | + * messages, but no error, for bogus combinations. |
| 213 | + */ |
206 | 214 | for (addr = addrs; addr; addr = addr->ai_next)
|
207 | 215 | {
|
208 | 216 | #ifdef HAVE_UNIX_SOCKETS
|
209 | 217 | /* Ignore AF_UNIX sockets, if any are returned. */
|
210 | 218 | if (addr->ai_family == AF_UNIX)
|
211 | 219 | continue;
|
212 | 220 | #endif
|
213 |
| - if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) >= 0) |
214 |
| - break; |
215 |
| - } |
| 221 | + /* |
| 222 | + * Create the socket. |
| 223 | + */ |
| 224 | + if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) < 0) |
| 225 | + { |
| 226 | + ereport(LOG, |
| 227 | + (errcode_for_socket_access(), |
| 228 | + errmsg("could not create socket for statistics collector: %m"))); |
| 229 | + continue; |
| 230 | + } |
216 | 231 |
|
217 |
| - if (!addr || pgStatSock < 0) |
218 |
| - { |
219 |
| - ereport(LOG, |
220 |
| - (errcode_for_socket_access(), |
221 |
| - errmsg("could not create socket for statistics collector: %m"))); |
222 |
| - goto startup_failed; |
223 |
| - } |
| 232 | + /* |
| 233 | + * Bind it to a kernel assigned port on localhost and get the assigned |
| 234 | + * port via getsockname(). |
| 235 | + */ |
| 236 | + if (bind(pgStatSock, addr->ai_addr, addr->ai_addrlen) < 0) |
| 237 | + { |
| 238 | + ereport(LOG, |
| 239 | + (errcode_for_socket_access(), |
| 240 | + errmsg("could not bind socket for statistics collector: %m"))); |
| 241 | + closesocket(pgStatSock); |
| 242 | + pgStatSock = -1; |
| 243 | + continue; |
| 244 | + } |
224 | 245 |
|
225 |
| - /* |
226 |
| - * Bind it to a kernel assigned port on localhost and get the assigned |
227 |
| - * port via getsockname(). |
228 |
| - */ |
229 |
| - if (bind(pgStatSock, addr->ai_addr, addr->ai_addrlen) < 0) |
230 |
| - { |
231 |
| - ereport(LOG, |
232 |
| - (errcode_for_socket_access(), |
233 |
| - errmsg("could not bind socket for statistics collector: %m"))); |
234 |
| - goto startup_failed; |
235 |
| - } |
| 246 | + alen = sizeof(pgStatAddr); |
| 247 | + if (getsockname(pgStatSock, (struct sockaddr *) &pgStatAddr, &alen) < 0) |
| 248 | + { |
| 249 | + ereport(LOG, |
| 250 | + (errcode_for_socket_access(), |
| 251 | + errmsg("could not get address of socket for statistics collector: %m"))); |
| 252 | + closesocket(pgStatSock); |
| 253 | + pgStatSock = -1; |
| 254 | + continue; |
| 255 | + } |
236 | 256 |
|
237 |
| - freeaddrinfo_all(hints.ai_family, addrs); |
238 |
| - addrs = NULL; |
| 257 | + /* |
| 258 | + * Connect the socket to its own address. This saves a few cycles by |
| 259 | + * not having to respecify the target address on every send. This also |
| 260 | + * provides a kernel-level check that only packets from this same |
| 261 | + * address will be received. |
| 262 | + */ |
| 263 | + if (connect(pgStatSock, (struct sockaddr *) &pgStatAddr, alen) < 0) |
| 264 | + { |
| 265 | + ereport(LOG, |
| 266 | + (errcode_for_socket_access(), |
| 267 | + errmsg("could not connect socket for statistics collector: %m"))); |
| 268 | + closesocket(pgStatSock); |
| 269 | + pgStatSock = -1; |
| 270 | + continue; |
| 271 | + } |
239 | 272 |
|
240 |
| - alen = sizeof(pgStatAddr); |
241 |
| - if (getsockname(pgStatSock, (struct sockaddr *) & pgStatAddr, &alen) < 0) |
242 |
| - { |
243 |
| - ereport(LOG, |
244 |
| - (errcode_for_socket_access(), |
245 |
| - errmsg("could not get address of socket for statistics collector: %m"))); |
246 |
| - goto startup_failed; |
| 273 | + /* If we get here, we have a working socket */ |
| 274 | + break; |
247 | 275 | }
|
248 | 276 |
|
249 |
| - /* |
250 |
| - * Connect the socket to its own address. This saves a few cycles by |
251 |
| - * not having to respecify the target address on every send. This also |
252 |
| - * provides a kernel-level check that only packets from this same |
253 |
| - * address will be received. |
254 |
| - */ |
255 |
| - if (connect(pgStatSock, (struct sockaddr *) & pgStatAddr, alen) < 0) |
| 277 | + /* Did we find a working address? */ |
| 278 | + if (!addr || pgStatSock < 0) |
256 | 279 | {
|
257 | 280 | ereport(LOG,
|
258 | 281 | (errcode_for_socket_access(),
|
259 |
| - errmsg("could not connect socket for statistics collector: %m"))); |
| 282 | + errmsg("disabling statistics collector for lack of working socket"))); |
260 | 283 | goto startup_failed;
|
261 | 284 | }
|
262 | 285 |
|
@@ -285,6 +308,8 @@ pgstat_init(void)
|
285 | 308 | goto startup_failed;
|
286 | 309 | }
|
287 | 310 |
|
| 311 | + freeaddrinfo_all(hints.ai_family, addrs); |
| 312 | + |
288 | 313 | return;
|
289 | 314 |
|
290 | 315 | startup_failed:
|
|
0 commit comments