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 sub query_total_memory
: prototype() {
22 return $mem_total if defined($mem_total);
24 open (my $MEMINFO, '<', '/proc/meminfo');
26 my $res = 512; # default to 512 if something goes wrong
27 while (my $line = <$MEMINFO>) {
28 if ($line =~ m/^MemTotal:\s+(\d+)\s*kB/i) {
29 $res = int ($1 / 1024);
38 my $cpu_hvm_support = undef;
39 sub query_cpu_hvm_support
: prototype() {
40 return $cpu_hvm_support if defined($cpu_hvm_support);
42 open (my $CPUINFO, '<', '/proc/cpuinfo');
45 while (my $line = <$CPUINFO>) {
46 if ($line =~ /^flags\s*:.*(vmx|svm)/m) {
53 $cpu_hvm_support = $res;
54 return $cpu_hvm_support;
61 # mac => <mac address>,
65 # family => <inet|inet6>,
66 # address => <mac address>,
71 my sub query_netdevs
: prototype() {
75 # FIXME: not the same as the battle proven way we used in the installer for years?
76 my $interfaces = fromjs
(qx
/ip --json address show/);
78 for my $if (@$interfaces) {
79 my ($index, $name, $state, $mac, $addresses) =
80 $if->@{qw(ifindex ifname operstate address addr_info)};
82 next if !$name || $name eq 'lo'; # could also check flags for LOOPBACK..
85 if (uc($state) eq 'UP') {
86 for my $addr (@$addresses) {
87 next if $addr->{scope
} eq 'link';
89 my ($family, $addr, $prefix) = $addr->@{qw(family local prefixlen)};
105 $ifs->{$name}->{addresses
} = \
@valid_addrs if @valid_addrs;
125 my sub query_routes
: prototype() {
126 my ($gateway4, $gateway6);
128 log_info
("query routes");
129 my $route4 = fromjs
(qx
/ip -4 --json route show/);
130 for my $route (@$route4) {
131 if ($route->{dst
} eq 'default') {
133 dev
=> $route->{dev
},
134 gateway
=> $route->{gateway
},
140 my $route6 = fromjs
(qx
/ip -6 --json route show/);
141 for my $route (@$route6) {
142 if ($route->{dst
} eq 'default') {
144 dev
=> $route->{dev
},
145 gateway
=> $route->{gateway
},
152 $routes->{gateway4
} = $gateway4 if $gateway4;
153 $routes->{gateway6
} = $gateway6 if $gateway6;
158 # If `/etc/resolv.conf` fails to open this returns nothing.
159 # Otherwise it returns a hash:
161 # dns => <first dns entry>,
163 my sub query_dns
: prototype() {
164 log_info
("query DNS from resolv.conf (managed by DHCP client)");
165 open my $fh , '<', '/etc/resolv.conf' or return;
169 while (defined(my $line = <$fh>)) {
170 if ($line =~ /^nameserver\s+(\S+)/) {
172 } elsif (!defined($domain) && $line =~ /^domain\s+(\S+)/) {
179 @dns ?
(dns
=> \
@dns) : (),
183 # Uses `traceroute` and `geoiplookup`/`geoiplookup6` to figure out the current country.
184 # Has a 10s timeout and uses the stops at the first entry found in the geoip database.
185 my sub detect_country_tracing_to
: prototype($$) {
186 my ($ipver, $destination) = @_;
188 print STDERR
"trying to detect country...\n";
189 open(my $TRACEROUTE_FH, '-|', 'traceroute', "-$ipver", '-N', '1', '-q', '1', '-n', $destination)
192 my $geoip_bin = ($ipver == 6) ?
'geoiplookup6' : 'geoiplookup';
196 my $previous_alarm = alarm (10);
198 local $SIG{ALRM
} = sub { die "timed out!\n" };
200 while (defined ($line = <$TRACEROUTE_FH>)) {
201 log_debug
("DC TRACEROUTE: $line");
202 if ($line =~ m/^\s*\d+\s+(\S+)\s/) {
203 my $geoip = qx
/$geoip_bin $1/;
204 log_debug
("DC GEOIP: $geoip");
205 if ($geoip =~ m/GeoIP Country Edition:\s*([A-Z]+),/) {
207 log_info
("DC FOUND: $country\n");
214 alarm ($previous_alarm);
216 close($TRACEROUTE_FH);
219 die "unable to detect country - $err\n";
221 print STDERR
"detected country: " . uc($country) . "\n";
227 # Returns the entire environment as a hash.
229 # country => <short country>,
230 # ipconf = <see Proxmox::Sys::Net::get_ip_config()>,
231 # kernel_cmdline = <contents of /proc/cmdline>,
232 # total_memory = <memory size in MiB>,
233 # hvm_supported = <1 if the CPU supports hardware-accelerated virtualization>,
234 # boot_type = <either 'efi' or 'bios'>,
235 # disks => <see Proxmox::Sys::Block::hd_list()>,
237 # interfaces => <see query_netdevs()>,
238 # routes => <see query_routes()>,
239 # dns => <see query_dns()>,
242 sub query_installation_environment
: prototype() {
243 # check first if somebody already cached this for us and re-use that
244 my $run_env_file = Proxmox
::Install
::ISOEnv
::get
('run-env-cache-file');
245 if (-f
"$run_env_file") {
246 log_info
("re-using cached runtime env from $run_env_file");
247 my $cached_env = eval {
248 my $run_env_raw = Proxmox
::Sys
::File
::file_read_all
($run_env_file);
249 return fromjs
($run_env_raw); # returns from eval
251 log_error
("failed to parse cached runtime env - $@") if $@;
252 return $cached_env if defined($cached_env) && scalar keys $cached_env->%*;
253 log_warn
("cached runtime env seems empty, query everything (again)");
255 # else re-query everything
258 my $routes = query_routes
();
260 log_info
("query block devices");
261 $output->{disks
} = Proxmox
::Sys
::Block
::get_cached_disks
();
262 $output->{network
} = {
263 interfaces
=> query_netdevs
(),
268 # Cannot be put directly in the above hash as it might return undef ..
269 $output->{network
}->{hostname
} = Proxmox
::Sys
::Net
::get_dhcp_hostname
();
271 # FIXME: move whatever makes sense over to Proxmox::Sys::Net:: and keep that as single source,
272 # it can then use some different structure just fine (after adapting the GTK GUI to that) but
273 # **never** to (slightly different!) things for the same stuff...
274 $output->{ipconf
} = Proxmox
::Sys
::Net
::get_ip_config
();
276 $output->{kernel_cmdline
} = file_read_firstline
("/proc/cmdline");
277 $output->{total_memory
} = query_total_memory
();
278 $output->{hvm_supported
} = query_cpu_hvm_support
();
279 $output->{boot_type
} = -d
'/sys/firmware/efi' ?
'efi' : 'bios';
283 if ($routes->{gateway4
}) {
284 log_info
("trace country via IPv4");
285 $country = eval { detect_country_tracing_to
(4 => '8.8.8.8') };
286 $err = $@ if !$country;
289 if (!$country && $routes->{gateway6
}) {
290 log_info
("trace country via IPv6");
291 $country = eval { detect_country_tracing_to
(6 => '2001:4860:4860::8888') };
292 $err = $@ if !$country;
295 if (defined($country)) {
296 $output->{country
} = $country;
298 warn ($err // "unable to detect country\n");
307 $_env = query_installation_environment
() if !defined($_env);
308 return defined($k) ?
$_env->{$k} : $_env;