1 package Proxmox
::Install
::RunEnv
;
7 use JSON
qw(from_json to_json);
11 my sub fromjs
: prototype($) {
12 return from_json
($_[0], { utf8
=> 1 });
21 my sub query_blockdevs
: prototype() {
24 my $lsblk = fromjs
(qx
/lsblk -e 230 --bytes --json/);
25 for my $disk ($lsblk->{blockdevices
}->@*) {
26 my ($name, $ro, $size, $type, $mountpoints) = $disk->@{qw(name ro size type mountpoints)};
28 next if $type ne 'disk' || $ro;
29 next if grep { defined($_) } @$mountpoints;
31 $disks->{$name} = { size
=> $size };
41 # mac => <mac address>,
45 # family => <inet|inet6>,
46 # address => <mac address>,
51 my sub query_netdevs
: prototype() {
55 my $interfaces = fromjs
(qx
/ip --json address show/);
57 for my $if (@$interfaces) {
58 my ($index, $name, $state, $mac, $addresses) =
59 $if->@{qw(ifindex ifname operstate address addr_info)};
61 next if $state ne 'UP';
64 for my $addr (@$addresses) {
65 next if $addr->{scope
} eq 'link';
67 my ($family, $addr, $prefix) = $addr->@{qw(family local prefixlen)};
81 addresses
=> \
@valid_addrs,
103 my sub query_routes
: prototype() {
104 my ($gateway4, $gateway6);
106 my $route4 = fromjs
(qx
/ip -4 --json route show/);
107 for my $route (@$route4) {
108 if ($route->{dst
} eq 'default') {
110 dev
=> $route->{dev
},
111 gateway
=> $route->{gateway
},
117 my $route6 = fromjs
(qx
/ip -6 --json route show/);
118 for my $route (@$route6) {
119 if ($route->{dst
} eq 'default') {
121 dev
=> $route->{dev
},
122 gateway
=> $route->{gateway
},
129 $routes->{gateway4
} = $gateway4 if $gateway4;
130 $routes->{gateway6
} = $gateway6 if $gateway6;
135 # If `/etc/resolv.conf` fails to open this returns nothing.
136 # Otherwise it returns a hash:
138 # dns => <first dns entry>,
140 my sub query_dns
: prototype() {
141 open my $fh , '<', '/etc/resolv.conf' or return;
145 while (defined(my $line = <$fh>)) {
146 if ($line =~ /^nameserver\s+(\S+)/) {
148 } elsif (!defined($domain) && $line =~ /^domain\s+(\S+)/) {
155 @dns ?
(dns
=> \
@dns) : (),
159 # Uses `traceroute` and `geoiplookup`/`geoiplookup6` to figure out the current country.
160 # Has a 10s timeout and uses the stops at the first entry found in the geoip database.
161 my sub detect_country_tracing_to
: prototype($$) {
162 my ($ipver, $destination) = @_;
164 print "trying to detect country...\n";
165 open(my $TRACEROUTE_FH, '-|',
166 'traceroute', "-$ipver", '-N', '1', '-q', '1', '-n', $destination)
169 my $geoip_bin = ($ipver == 6) ?
'geoiplookup6' : 'geoiplookup';
173 my $previous_alarm = alarm (10);
175 local $SIG{ALRM
} = sub { die "timed out!\n" };
177 while (defined ($line = <$TRACEROUTE_FH>)) {
178 log_debug
("DC TRACEROUTE: $line");
179 if ($line =~ m/^\s*\d+\s+(\S+)\s/) {
180 my $geoip = qx
/$geoip_bin $1/;
181 log_debug
("DC GEOIP: $geoip");
182 if ($geoip =~ m/GeoIP Country Edition:\s*([A-Z]+),/) {
184 log_info
("DC FOUND: $country\n");
191 alarm ($previous_alarm);
193 close($TRACEROUTE_FH);
196 die "unable to detect country - $err\n";
198 print "detected country: " . uc($country) . "\n";
204 # Returns the entire environment as a hash.
206 # country => <short country>,
207 # disks => <see query_blockdevs()>,
209 # interfaces => <see query_netdevs()>,
210 # routes => <see query_routes()>,
211 # dns => <see query_dns()>,
214 sub query_installation_environment
: prototype() {
217 my $routes = query_routes
();
219 $output->{disks
} = query_blockdevs
();
220 $output->{network
} = {
221 interfaces
=> query_netdevs
(),
228 if ($routes->{gateway4
}) {
229 $country = eval { detect_country_tracing_to
(4 => '8.8.8.8') };
230 $err = $@ if !$country;
233 if (!$country && $routes->{gateway6
}) {
234 $country = eval { detect_country_tracing_to
(6 => '2001:4860:4860::8888') };
235 $err = $@ if !$country;
238 if (defined($country)) {
239 $output->{country
} = $country;
241 warn ($err // "unable to detect country\n");