|
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 | gotostartup_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 | #ifdefHAVE_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 | | -gotostartup_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 | | -gotostartup_failed; |
235 | | -} |
| 246 | +alen=sizeof(pgStatAddr); |
| 247 | +if (getsockname(pgStatSock, (structsockaddr*)&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, (structsockaddr*)&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, (structsockaddr*)&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 | | -gotostartup_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, (structsockaddr*)&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 socketforstatistics collector: %m"))); |
| 282 | +errmsg("disabling statistics collectorforlack of working socket"))); |
260 | 283 | gotostartup_failed; |
261 | 284 | } |
262 | 285 |
|
@@ -285,6 +308,8 @@ pgstat_init(void) |
285 | 308 | gotostartup_failed; |
286 | 309 | } |
287 | 310 |
|
| 311 | +freeaddrinfo_all(hints.ai_family,addrs); |
| 312 | + |
288 | 313 | return; |
289 | 314 |
|
290 | 315 | startup_failed: |
|