]> git.proxmox.com Git - pve-installer.git/blame - Proxmox/Install/RunEnv.pm
tui: fix clippy warning
[pve-installer.git] / Proxmox / Install / RunEnv.pm
CommitLineData
2231d226
WB
1package Proxmox::Install::RunEnv;
2
3use strict;
4use warnings;
5
6use Carp;
7use JSON qw(from_json to_json);
2231d226 8
0d27321b 9use Proxmox::Log;
88adf315 10use Proxmox::Sys::File qw(file_read_firstline);
ceec3def 11use Proxmox::Sys::Block;
fe06d7e9 12use Proxmox::Sys::Net;
2231d226 13
e1b74696
TL
14use Proxmox::Install::ISOEnv;
15
2231d226
WB
16my sub fromjs : prototype($) {
17 return from_json($_[0], { utf8 => 1 });
18}
19
b91f9cad
TL
20my $mem_total = undef;
21sub query_total_memory : prototype() {
22 return $mem_total if defined($mem_total);
23
24 open (my $MEMINFO, '<', '/proc/meminfo');
25
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);
30 }
31 }
32 close($MEMINFO);
33
34 $mem_total = $res;
35 return $mem_total;
36}
37
4d8aec8a
CH
38my $cpu_hvm_support = undef;
39sub query_cpu_hvm_support : prototype() {
40 return $cpu_hvm_support if defined($cpu_hvm_support);
41
42 open (my $CPUINFO, '<', '/proc/cpuinfo');
43
44 my $res = 0;
45 while (my $line = <$CPUINFO>) {
46 if ($line =~ /^flags\s*:.*(vmx|svm)/m) {
47 $res = 1;
48 last;
49 }
50 }
51 close($CPUINFO);
52
53 $cpu_hvm_support = $res;
54 return $cpu_hvm_support;
55}
56
2231d226
WB
57# Returns a hash.
58#
59# {
60# <ifname> => {
61# mac => <mac address>,
62# index => <index>,
63# name => <ifname>,
64# addresses => [
65# family => <inet|inet6>,
66# address => <mac address>,
67# prefix => <length>,
68# ],
69# },
70# }
71my sub query_netdevs : prototype() {
72 my $ifs = {};
73 my $default;
74
88adf315 75 # FIXME: not the same as the battle proven way we used in the installer for years?
2231d226
WB
76 my $interfaces = fromjs(qx/ip --json address show/);
77
78 for my $if (@$interfaces) {
79 my ($index, $name, $state, $mac, $addresses) =
80 $if->@{qw(ifindex ifname operstate address addr_info)};
81
5ea718f6 82 next if !$name || $name eq 'lo'; # could also check flags for LOOPBACK..
2231d226
WB
83
84 my @valid_addrs;
5ea718f6
TL
85 if (uc($state) eq 'UP') {
86 for my $addr (@$addresses) {
87 next if $addr->{scope} eq 'link';
88
89 my ($family, $addr, $prefix) = $addr->@{qw(family local prefixlen)};
90
91 push @valid_addrs, {
92 family => $family,
93 address => $addr,
94 prefix => $prefix,
95 };
96 }
2231d226
WB
97 }
98
5ea718f6
TL
99 $ifs->{$name} = {
100 index => $index,
101 name => $name,
102 mac => $mac,
5c450165 103 state => uc($state),
5ea718f6
TL
104 };
105 $ifs->{$name}->{addresses} = \@valid_addrs if @valid_addrs;
2231d226
WB
106 }
107
108 return $ifs;
109}
110
111# Returns a hash.
112#
113# {
114# gateway4 => {
115# dst => "default",
116# gateway => <ipv4>,
117# dev => <ifname>,
118# },
119# gateway6 => {
120# dst => "default",
121# gateway => <ipv6>,
122# dev => <ifname>,
123# },
124# }
125my sub query_routes : prototype() {
126 my ($gateway4, $gateway6);
127
a467ec47 128 log_info("query routes");
2231d226
WB
129 my $route4 = fromjs(qx/ip -4 --json route show/);
130 for my $route (@$route4) {
131 if ($route->{dst} eq 'default') {
132 $gateway4 = {
133 dev => $route->{dev},
134 gateway => $route->{gateway},
135 };
136 last;
137 }
138 }
139
140 my $route6 = fromjs(qx/ip -6 --json route show/);
141 for my $route (@$route6) {
142 if ($route->{dst} eq 'default') {
143 $gateway6 = {
144 dev => $route->{dev},
145 gateway => $route->{gateway},
146 };
147 last;
148 }
149 }
150
151 my $routes;
152 $routes->{gateway4} = $gateway4 if $gateway4;
153 $routes->{gateway6} = $gateway6 if $gateway6;
154
155 return $routes;
156}
157
158# If `/etc/resolv.conf` fails to open this returns nothing.
159# Otherwise it returns a hash:
160# {
161# dns => <first dns entry>,
162#
163my sub query_dns : prototype() {
a467ec47 164 log_info("query DNS from resolv.conf (managed by DHCP client)");
2231d226
WB
165 open my $fh , '<', '/etc/resolv.conf' or return;
166
167 my @dns;
168 my $domain;
169 while (defined(my $line = <$fh>)) {
170 if ($line =~ /^nameserver\s+(\S+)/) {
171 push @dns, $1;
172 } elsif (!defined($domain) && $line =~ /^domain\s+(\S+)/) {
173 $domain = $1;
174 }
175 }
176
177 my $output = {
178 domain => $domain,
179 @dns ? (dns => \@dns) : (),
180 };
181};
182
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.
185my sub detect_country_tracing_to : prototype($$) {
186 my ($ipver, $destination) = @_;
187
a467ec47
TL
188 print STDERR "trying to detect country...\n";
189 open(my $TRACEROUTE_FH, '-|', 'traceroute', "-$ipver", '-N', '1', '-q', '1', '-n', $destination)
2231d226
WB
190 or return undef;
191
192 my $geoip_bin = ($ipver == 6) ? 'geoiplookup6' : 'geoiplookup';
193
194 my $country;
195
196 my $previous_alarm = alarm (10);
197 eval {
198 local $SIG{ALRM} = sub { die "timed out!\n" };
199 my $line;
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]+),/) {
206 $country = lc ($1);
207 log_info("DC FOUND: $country\n");
208 last;
209 }
210 }
211 }
212 };
213 my $err = $@;
214 alarm ($previous_alarm);
215
216 close($TRACEROUTE_FH);
217
218 if ($err) {
219 die "unable to detect country - $err\n";
220 } elsif ($country) {
a467ec47 221 print STDERR "detected country: " . uc($country) . "\n";
2231d226
WB
222 }
223
224 return $country;
225}
226
227# Returns the entire environment as a hash.
228# {
229# country => <short country>,
4d8aec8a
CH
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'>,
ceec3def 235# disks => <see Proxmox::Sys::Block::hd_list()>,
2231d226
WB
236# network => {
237# interfaces => <see query_netdevs()>,
238# routes => <see query_routes()>,
1c5e103a 239# dns => <see query_dns()>,
2231d226 240# },
2231d226
WB
241# }
242sub query_installation_environment : prototype() {
e1b74696
TL
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
250 };
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)");
254 }
255 # else re-query everything
2231d226
WB
256 my $output = {};
257
258 my $routes = query_routes();
259
a467ec47 260 log_info("query block devices");
ceec3def 261 $output->{disks} = Proxmox::Sys::Block::get_cached_disks();
2231d226
WB
262 $output->{network} = {
263 interfaces => query_netdevs(),
264 routes => $routes,
1c5e103a 265 dns => query_dns(),
2231d226 266 };
bda1cdf6
CH
267
268 # Cannot be put directly in the above hash as it might return undef ..
269 $output->{network}->{hostname} = Proxmox::Sys::Net::get_dhcp_hostname();
270
fe06d7e9
TL
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();
2231d226 275
88adf315 276 $output->{kernel_cmdline} = file_read_firstline("/proc/cmdline");
b91f9cad 277 $output->{total_memory} = query_total_memory();
4d8aec8a 278 $output->{hvm_supported} = query_cpu_hvm_support();
71583761 279 $output->{boot_type} = -d '/sys/firmware/efi' ? 'efi' : 'bios';
b91f9cad 280
2231d226
WB
281 my $err;
282 my $country;
283 if ($routes->{gateway4}) {
a467ec47 284 log_info("trace country via IPv4");
2231d226
WB
285 $country = eval { detect_country_tracing_to(4 => '8.8.8.8') };
286 $err = $@ if !$country;
287 }
288
289 if (!$country && $routes->{gateway6}) {
a467ec47 290 log_info("trace country via IPv6");
2231d226
WB
291 $country = eval { detect_country_tracing_to(6 => '2001:4860:4860::8888') };
292 $err = $@ if !$country;
293 }
294
295 if (defined($country)) {
296 $output->{country} = $country;
297 } else {
298 warn ($err // "unable to detect country\n");
299 }
300
301 return $output;
302}
303
ea3b7cae
TL
304my $_env = undef;
305sub get {
306 my ($k) = @_;
307 $_env = query_installation_environment() if !defined($_env);
308 return defined($k) ? $_env->{$k} : $_env;
309}
310
2231d226 3111;