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 });
15 my $mem_total = undef;
16 sub query_total_memory
: prototype() {
17 return $mem_total if defined($mem_total);
19 open (my $MEMINFO, '<', '/proc/meminfo');
21 my $res = 512; # default to 512 if something goes wrong
22 while (my $line = <$MEMINFO>) {
23 if ($line =~ m/^MemTotal:\s+(\d+)\s*kB/i) {
24 $res = int ($1 / 1024);
39 my sub query_blockdevs
: prototype() {
42 my $lsblk = fromjs
(qx
/lsblk -e 230 --bytes --json/);
43 for my $disk ($lsblk->{blockdevices
}->@*) {
44 my ($name, $ro, $size, $type, $mountpoints) = $disk->@{qw(name ro size type mountpoints)};
46 next if $type ne 'disk' || $ro;
47 next if grep { defined($_) } @$mountpoints;
49 $disks->{$name} = { size
=> $size };
59 # mac => <mac address>,
63 # family => <inet|inet6>,
64 # address => <mac address>,
69 my sub query_netdevs
: prototype() {
73 my $interfaces = fromjs
(qx
/ip --json address show/);
75 for my $if (@$interfaces) {
76 my ($index, $name, $state, $mac, $addresses) =
77 $if->@{qw(ifindex ifname operstate address addr_info)};
79 next if $state ne 'UP';
82 for my $addr (@$addresses) {
83 next if $addr->{scope
} eq 'link';
85 my ($family, $addr, $prefix) = $addr->@{qw(family local prefixlen)};
99 addresses
=> \
@valid_addrs,
121 my sub query_routes
: prototype() {
122 my ($gateway4, $gateway6);
124 my $route4 = fromjs
(qx
/ip -4 --json route show/);
125 for my $route (@$route4) {
126 if ($route->{dst
} eq 'default') {
128 dev
=> $route->{dev
},
129 gateway
=> $route->{gateway
},
135 my $route6 = fromjs
(qx
/ip -6 --json route show/);
136 for my $route (@$route6) {
137 if ($route->{dst
} eq 'default') {
139 dev
=> $route->{dev
},
140 gateway
=> $route->{gateway
},
147 $routes->{gateway4
} = $gateway4 if $gateway4;
148 $routes->{gateway6
} = $gateway6 if $gateway6;
153 # If `/etc/resolv.conf` fails to open this returns nothing.
154 # Otherwise it returns a hash:
156 # dns => <first dns entry>,
158 my sub query_dns
: prototype() {
159 open my $fh , '<', '/etc/resolv.conf' or return;
163 while (defined(my $line = <$fh>)) {
164 if ($line =~ /^nameserver\s+(\S+)/) {
166 } elsif (!defined($domain) && $line =~ /^domain\s+(\S+)/) {
173 @dns ?
(dns
=> \
@dns) : (),
177 # Uses `traceroute` and `geoiplookup`/`geoiplookup6` to figure out the current country.
178 # Has a 10s timeout and uses the stops at the first entry found in the geoip database.
179 my sub detect_country_tracing_to
: prototype($$) {
180 my ($ipver, $destination) = @_;
182 print "trying to detect country...\n";
183 open(my $TRACEROUTE_FH, '-|',
184 'traceroute', "-$ipver", '-N', '1', '-q', '1', '-n', $destination)
187 my $geoip_bin = ($ipver == 6) ?
'geoiplookup6' : 'geoiplookup';
191 my $previous_alarm = alarm (10);
193 local $SIG{ALRM
} = sub { die "timed out!\n" };
195 while (defined ($line = <$TRACEROUTE_FH>)) {
196 log_debug
("DC TRACEROUTE: $line");
197 if ($line =~ m/^\s*\d+\s+(\S+)\s/) {
198 my $geoip = qx
/$geoip_bin $1/;
199 log_debug
("DC GEOIP: $geoip");
200 if ($geoip =~ m/GeoIP Country Edition:\s*([A-Z]+),/) {
202 log_info
("DC FOUND: $country\n");
209 alarm ($previous_alarm);
211 close($TRACEROUTE_FH);
214 die "unable to detect country - $err\n";
216 print "detected country: " . uc($country) . "\n";
222 # Returns the entire environment as a hash.
224 # country => <short country>,
225 # disks => <see query_blockdevs()>,
227 # interfaces => <see query_netdevs()>,
228 # routes => <see query_routes()>,
229 # dns => <see query_dns()>,
232 sub query_installation_environment
: prototype() {
235 my $routes = query_routes
();
237 $output->{disks
} = query_blockdevs
();
238 $output->{network
} = {
239 interfaces
=> query_netdevs
(),
244 $output->{total_memory
} = query_total_memory
();
245 $output->{boot_type
} = -d
'/sys/firmware/efi' ?
'efi' : 'bios';
249 if ($routes->{gateway4
}) {
250 $country = eval { detect_country_tracing_to
(4 => '8.8.8.8') };
251 $err = $@ if !$country;
254 if (!$country && $routes->{gateway6
}) {
255 $country = eval { detect_country_tracing_to
(6 => '2001:4860:4860::8888') };
256 $err = $@ if !$country;
259 if (defined($country)) {
260 $output->{country
} = $country;
262 warn ($err // "unable to detect country\n");