Re: Daniel Kahn Gillmor 2012-07-08 <4ff9dd62.10...@fifthhorseman.net>
> On 07/08/2012 11:57 AM, Christoph Berg wrote:
> > I can't really test it here because the Debian (linux) kernels have
> > ipv4 compiled in.
> 
> You might be able to test it on a debian machine by not even bringing up
> the loopback interface with ipv4 -- i ran into this problem a while ago
> on a machine running stock debian (including the kernel), and subscribed
> to it, so i'm pretty sure it's replicable without a custom kernel.

I tried that when I realized I couldn't just "rmmod ipv4", but
"ifconfig lo down" will just make 127.0.0.1 disappear, but the
socket() call will still succeed. Later I realized I could test it in
the code by removing the ipv4 call.

You might have run into some other problem, postgres' stats collector
wants to open a UDP socket on localhost, but that will work for ipv6
as well, as long as there's some ipv6 "localhost" entry in /etc/hosts.
(I believe it is not fatal for that not to work, though.)

net.ipv6.bindv6only makes things even more interesting, so I rewrote
the test to try both protocols, and only allocate a port if it is free
for all protocols that are available.


--- PgCommon.pm 2012-04-10 09:19:24 +0000
+++ PgCommon.pm 2012-07-08 21:30:50 +0000
@@ -1,6 +1,7 @@
 # Common functions for the postgresql-common framework
 #
 # (C) 2008-2009 Martin Pitt <mp...@debian.org>
+# (C) 2012 Christoph Berg <m...@debian.org
 #
 #  This program is free software; you can redistribute it and/or modify
 #  it under the terms of the GNU General Public License as published by
@@ -15,6 +16,7 @@
 package PgCommon;
 use strict;
 use Socket;
+use Socket qw(IN6ADDR_ANY);
 use POSIX;
 
 use Exporter;
@@ -670,18 +672,30 @@
     }
 
     my $port;
-    for ($port = $defaultport; ; ++$port) {
+    for ($port = $defaultport; $port < 65536; ++$port) {
        next if grep { $_ == $port } @ports;
 
         # check if port is already in use
-        socket (SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or 
+       my ($have_ip4, $res4, $have_ip6, $res6);
+       if (socket (SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'))) { # IPv4
+           $have_ip4 = 1;
+           $res4 = bind (SOCK, sockaddr_in($port, INADDR_ANY));
+       }
+       if (socket (SOCK, PF_INET6, SOCK_STREAM, getprotobyname('tcp'))) { # 
IPv6
+           $have_ip6 = 1;
+           $res6 = bind (SOCK, sockaddr_in6($port, IN6ADDR_ANY));
+       }
+       unless ($have_ip4 or $have_ip6) {
+           # require at least one protocol to work (PostgreSQL needs it anyway
+           # for the stats collector)
             die "could not create socket: $!";
-        my $res = bind (SOCK, sockaddr_in($port, INADDR_ANY));
+       }
         close SOCK;
-        last if $res;
+       # return port if it is available on all supported protocols
+       return $port if ($have_ip4 ? $res4 : 1) and ($have_ip6 ? $res6 : 1);
     }
 
-    return $port;
+    die "no free port found";
 }
 
 # Return the PostgreSQL version, cluster, and database to connect to. version


Christoph
-- 
c...@df7cb.de | http://www.df7cb.de/

Attachment: signature.asc
Description: Digital signature

Reply via email to