1 package Proxmox
::Install
::RunEnv
;
7 use JSON
qw(from_json to_json);
10 use Proxmox
::Sys
::File
qw(file_read_firstline);
11 use Proxmox
::Sys
::Block
;
12 use Proxmox
::Sys
::Net
;
14 use Proxmox
::Install
::ISOEnv
;
16 my sub fromjs
: prototype($) {
17 return from_json
($_[0], { utf8
=> 1 });
20 my $mem_total = undef;
21 # Returns the system memory size in MiB, and falls back to 512 MiB if it
22 # could not be determined.
23 sub query_total_memory
: prototype() {
24 return $mem_total if defined($mem_total);
26 open (my $MEMINFO, '<', '/proc/meminfo');
28 my $res = 512; # default to 512 if something goes wrong
29 while (my $line = <$MEMINFO>) {
30 if ($line =~ m/^MemTotal:\s+(\d+)\s*kB/i) {
31 $res = int ($1 / 1024);
40 my $cpu_hvm_support = undef;
41 sub query_cpu_hvm_support
: prototype() {
42 return $cpu_hvm_support if defined($cpu_hvm_support);
44 open (my $CPUINFO, '<', '/proc/cpuinfo');
47 while (my $line = <$CPUINFO>) {
48 if ($line =~ /^flags\s*:.*(vmx|svm)/m) {
55 $cpu_hvm_support = $res;
56 return $cpu_hvm_support;
63 # mac => <mac address>,
67 # family => <inet|inet6>,
68 # address => <mac address>,
73 my sub query_netdevs
: prototype() {
77 # FIXME: not the same as the battle proven way we used in the installer for years?
78 my $interfaces = fromjs
(qx
/ip --json address show/);
80 for my $if (@$interfaces) {
81 my ($index, $name, $state, $mac, $addresses) =
82 $if->@{qw(ifindex ifname operstate address addr_info)};
84 next if !$name || $name eq 'lo'; # could also check flags for LOOPBACK..
87 if (uc($state) eq 'UP') {
88 for my $addr (@$addresses) {
89 next if $addr->{scope
} eq 'link';
91 my ($family, $addr, $prefix) = $addr->@{qw(family local prefixlen)};
107 $ifs->{$name}->{addresses
} = \
@valid_addrs if @valid_addrs;
127 my sub query_routes
: prototype() {
128 my ($gateway4, $gateway6);
130 log_info
("query routes");
131 my $route4 = fromjs
(qx
/ip -4 --json route show/);
132 for my $route (@$route4) {
133 if ($route->{dst
} eq 'default') {
135 dev
=> $route->{dev
},
136 gateway
=> $route->{gateway
},
142 my $route6 = fromjs
(qx
/ip -6 --json route show/);
143 for my $route (@$route6) {
144 if ($route->{dst
} eq 'default') {
146 dev
=> $route->{dev
},
147 gateway
=> $route->{gateway
},
154 $routes->{gateway4
} = $gateway4 if $gateway4;
155 $routes->{gateway6
} = $gateway6 if $gateway6;
160 # If `/etc/resolv.conf` fails to open this returns nothing.
161 # Otherwise it returns a hash:
163 # dns => <first dns entry>,
165 my sub query_dns
: prototype() {
166 log_info
("query DNS from resolv.conf (managed by DHCP client)");
167 open my $fh , '<', '/etc/resolv.conf' or return;
171 while (defined(my $line = <$fh>)) {
172 if ($line =~ /^nameserver\s+(\S+)/) {
174 } elsif (!defined($domain) && $line =~ /^domain\s+(\S+)/) {
181 @dns ?
(dns
=> \
@dns) : (),
185 # Uses `traceroute` and `geoiplookup`/`geoiplookup6` to figure out the current country.
186 # Has a 10s timeout and uses the stops at the first entry found in the geoip database.
187 my sub detect_country_tracing_to
: prototype($$) {
188 my ($ipver, $destination) = @_;
190 print STDERR
"trying to detect country...\n";
191 open(my $TRACEROUTE_FH, '-|', 'traceroute', "-$ipver", '-N', '1', '-q', '1', '-n', $destination)
194 my $geoip_bin = ($ipver == 6) ?
'geoiplookup6' : 'geoiplookup';
198 my $previous_alarm = alarm (10);
200 local $SIG{ALRM
} = sub { die "timed out!\n" };
202 while (defined ($line = <$TRACEROUTE_FH>)) {
203 log_debug
("DC TRACEROUTE: $line");
204 if ($line =~ m/^\s*\d+\s+(\S+)\s/) {
205 my $geoip = qx
/$geoip_bin $1/;
206 log_debug
("DC GEOIP: $geoip");
207 if ($geoip =~ m/GeoIP Country Edition:\s*([A-Z]+),/) {
209 log_info
("DC FOUND: $country\n");
216 alarm ($previous_alarm);
218 close($TRACEROUTE_FH);
221 die "unable to detect country - $err\n";
223 print STDERR
"detected country: " . uc($country) . "\n";
229 # Returns the entire environment as a hash.
231 # country => <short country>,
232 # ipconf = <see Proxmox::Sys::Net::get_ip_config()>,
233 # kernel_cmdline = <contents of /proc/cmdline>,
234 # total_memory = <memory size in MiB>,
235 # hvm_supported = <1 if the CPU supports hardware-accelerated virtualization>,
236 # boot_type = <either 'efi' or 'bios'>,
237 # disks => <see Proxmox::Sys::Block::hd_list()>,
239 # interfaces => <see query_netdevs()>,
240 # routes => <see query_routes()>,
241 # dns => <see query_dns()>,
244 sub query_installation_environment
: prototype() {
245 # check first if somebody already cached this for us and re-use that
246 my $run_env_file = Proxmox
::Install
::ISOEnv
::get
('run-env-cache-file');
247 if (-f
"$run_env_file") {
248 log_info
("re-using cached runtime env from $run_env_file");
249 my $cached_env = eval {
250 my $run_env_raw = Proxmox
::Sys
::File
::file_read_all
($run_env_file);
251 return fromjs
($run_env_raw); # returns from eval
253 log_error
("failed to parse cached runtime env - $@") if $@;
254 return $cached_env if defined($cached_env) && scalar keys $cached_env->%*;
255 log_warn
("cached runtime env seems empty, query everything (again)");
257 # else re-query everything
260 my $routes = query_routes
();
262 log_info
("query block devices");
263 $output->{disks
} = Proxmox
::Sys
::Block
::get_cached_disks
();
264 $output->{network
} = {
265 interfaces
=> query_netdevs
(),
270 # Cannot be put directly in the above hash as it might return undef ..
271 $output->{network
}->{hostname
} = Proxmox
::Sys
::Net
::get_dhcp_hostname
();
273 # FIXME: move whatever makes sense over to Proxmox::Sys::Net:: and keep that as single source,
274 # it can then use some different structure just fine (after adapting the GTK GUI to that) but
275 # **never** to (slightly different!) things for the same stuff...
276 $output->{ipconf
} = Proxmox
::Sys
::Net
::get_ip_config
();
278 $output->{kernel_cmdline
} = file_read_firstline
("/proc/cmdline");
279 $output->{total_memory
} = query_total_memory
();
280 $output->{hvm_supported
} = query_cpu_hvm_support
();
281 $output->{boot_type
} = -d
'/sys/firmware/efi' ?
'efi' : 'bios';
285 if ($routes->{gateway4
}) {
286 log_info
("trace country via IPv4");
287 $country = eval { detect_country_tracing_to
(4 => '8.8.8.8') };
288 $err = $@ if !$country;
291 if (!$country && $routes->{gateway6
}) {
292 log_info
("trace country via IPv6");
293 $country = eval { detect_country_tracing_to
(6 => '2001:4860:4860::8888') };
294 $err = $@ if !$country;
297 if (defined($country)) {
298 $output->{country
} = $country;
300 warn ($err // "unable to detect country\n");
309 $_env = query_installation_environment
() if !defined($_env);
310 return defined($k) ?
$_env->{$k} : $_env;