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/
signature.asc
Description: Digital signature