]> git.proxmox.com Git - pve-installer.git/blame - proxinstall
country.pl: use warnings instead of -w
[pve-installer.git] / proxinstall
CommitLineData
6981164e 1#!/usr/bin/perl
89a12446 2
7becc472
DM
3$ENV{DEBIAN_FRONTEND} = 'noninteractive';
4$ENV{LC_ALL} = 'C';
5
89a12446 6use strict;
6981164e
DM
7use warnings;
8
89a12446 9use Getopt::Long;
62c05878 10use IPC::Open2;
89a12446
DM
11use IPC::Open3;
12use IO::File;
89a12446
DM
13use IO::Select;
14use Cwd 'abs_path';
7becc472
DM
15use Gtk3 '-init';
16use Gtk3::WebKit;
89a12446 17use Encode;
84761f93 18use String::ShellQuote;
7becc472 19use Data::Dumper;
a5af22f5 20use File::Basename;
121ebc59 21use Time::HiRes;
89a12446 22
b9075af2
DM
23use ProxmoxInstallerSetup;
24
25my $setup = ProxmoxInstallerSetup::setup();
26
27my $release = $setup->{release};
89a12446
DM
28
29my $kapi = `uname -r`;
30chomp $kapi;
31
b9075af2 32my $enable_btrfs = $setup->{enable_btrfs};
6f52fc3d 33
89a12446
DM
34my $opt_testmode;
35
7becc472
DM
36if (!$ENV{G_SLICE} || $ENV{G_SLICE} ne "always-malloc") {
37 die "do not use slice allocator (run with 'G_SLICE=always-malloc ./proxinstall ...')\n";
38}
39
89a12446
DM
40if (!GetOptions ('testmode=s' => \$opt_testmode)) {
41 die "usage error\n";
42 exit (-1);
43}
44
6b900321
DM
45my $zfstestpool = "test_rpool";
46my $zfspoolname = $opt_testmode ? $zfstestpool : 'rpool';
5fd81672
DM
47
48my $storage_cfg_zfs = <<__EOD__;
49dir: local
50 path /var/lib/vz
51 content iso,vztmpl,backup
52
239398be 53zfspool: local-zfs
5fd81672
DM
54 pool $zfspoolname/data
55 sparse
56 content images,rootdir
57__EOD__
58
121ebc59
DM
59my $storage_cfg_btrfs = <<__EOD__;
60dir: local
61 path /var/lib/vz
62 content iso,vztmpl,backup
63 disabled
64
65btrfs: local-btrfs
66 path /var/lib/pve/local-btrfs
67 content iso,vztmpl,backup,images,rootdir
68__EOD__
69
5fd81672
DM
70my $storage_cfg_lvmthin = <<__EOD__;
71dir: local
72 path /var/lib/vz
73 content iso,vztmpl,backup
74
239398be 75lvmthin: local-lvm
5fd81672
DM
76 thinpool data
77 vgname pve
78 content rootdir,images
79__EOD__
80
81
d2120e51
DM
82sub file_read_firstline {
83 my ($filename) = @_;
84
85 my $fh = IO::File->new ($filename, "r");
86 return undef if !$fh;
87 my $res = <$fh>;
88 chomp $res if $res;
89 $fh->close;
90 return $res;
91}
92
89a12446
DM
93my $logfd = IO::File->new (">/tmp/install.log");
94
c7429f77
DM
95my $proxmox_libdir = $opt_testmode ?
96 Cwd::cwd() . "/testdir/var/lib/pve-installer" : "/var/lib/pve-installer";
97980bf2
DM
97my $proxmox_cddir = $opt_testmode ? "../pve-cd-builder/tmp/data-gz/" : "/cdrom";
98my $proxmox_pkgdir = "${proxmox_cddir}/proxmox/packages/";
89a12446 99
84761f93
DM
100my $grub_plattform = "pc"; # pc, efi-amd64 or efi-ia32
101
102$grub_plattform = "efi-amd64" if -d "/sys/firmware/efi";
89a12446 103
32300628 104my $IPV4OCTET = "(?:25[0-5]|(?:2[0-4]|1[0-9]|[1-9])?[0-9])";
d2120e51 105my $IPV4RE = "(?:(?:$IPV4OCTET\\.){3}$IPV4OCTET)";
b6200603
DM
106my $IPV6H16 = "(?:[0-9a-fA-F]{1,4})";
107my $IPV6LS32 = "(?:(?:$IPV4RE|$IPV6H16:$IPV6H16))";
108
109my $IPV6RE = "(?:" .
110 "(?:(?:" . "(?:$IPV6H16:){6})$IPV6LS32)|" .
111 "(?:(?:" . "::(?:$IPV6H16:){5})$IPV6LS32)|" .
112 "(?:(?:(?:" . "$IPV6H16)?::(?:$IPV6H16:){4})$IPV6LS32)|" .
113 "(?:(?:(?:(?:$IPV6H16:){0,1}$IPV6H16)?::(?:$IPV6H16:){3})$IPV6LS32)|" .
114 "(?:(?:(?:(?:$IPV6H16:){0,2}$IPV6H16)?::(?:$IPV6H16:){2})$IPV6LS32)|" .
115 "(?:(?:(?:(?:$IPV6H16:){0,3}$IPV6H16)?::(?:$IPV6H16:){1})$IPV6LS32)|" .
116 "(?:(?:(?:(?:$IPV6H16:){0,4}$IPV6H16)?::" . ")$IPV6LS32)|" .
117 "(?:(?:(?:(?:$IPV6H16:){0,5}$IPV6H16)?::" . ")$IPV6H16)|" .
118 "(?:(?:(?:(?:$IPV6H16:){0,6}$IPV6H16)?::" . ")))";
119
120my $IPRE = "(?:$IPV4RE|$IPV6RE)";
121
122
d2120e51
DM
123my $ipv4_mask_hash = {
124 '128.0.0.0' => 1,
125 '192.0.0.0' => 2,
126 '224.0.0.0' => 3,
127 '240.0.0.0' => 4,
128 '248.0.0.0' => 5,
129 '252.0.0.0' => 6,
130 '254.0.0.0' => 7,
131 '255.0.0.0' => 8,
132 '255.128.0.0' => 9,
133 '255.192.0.0' => 10,
134 '255.224.0.0' => 11,
135 '255.240.0.0' => 12,
136 '255.248.0.0' => 13,
137 '255.252.0.0' => 14,
138 '255.254.0.0' => 15,
139 '255.255.0.0' => 16,
140 '255.255.128.0' => 17,
141 '255.255.192.0' => 18,
142 '255.255.224.0' => 19,
143 '255.255.240.0' => 20,
144 '255.255.248.0' => 21,
145 '255.255.252.0' => 22,
146 '255.255.254.0' => 23,
147 '255.255.255.0' => 24,
148 '255.255.255.128' => 25,
149 '255.255.255.192' => 26,
150 '255.255.255.224' => 27,
151 '255.255.255.240' => 28,
152 '255.255.255.248' => 29,
153 '255.255.255.252' => 30
154};
155
fe44bd92
FG
156my $ipv4_reverse_mask = [
157 '0.0.0.0',
158 '128.0.0.0',
159 '192.0.0.0',
160 '224.0.0.0',
161 '240.0.0.0',
162 '248.0.0.0',
163 '252.0.0.0',
164 '254.0.0.0',
165 '255.0.0.0',
166 '255.128.0.0',
167 '255.192.0.0',
168 '255.224.0.0',
169 '255.240.0.0',
170 '255.248.0.0',
171 '255.252.0.0',
172 '255.254.0.0',
173 '255.255.0.0',
174 '255.255.128.0',
175 '255.255.192.0',
176 '255.255.224.0',
177 '255.255.240.0',
178 '255.255.248.0',
179 '255.255.252.0',
180 '255.255.254.0',
181 '255.255.255.0',
182 '255.255.255.128',
183 '255.255.255.192',
184 '255.255.255.224',
185 '255.255.255.240',
186 '255.255.255.248',
187 '255.255.255.252',
188 '255.255.255.254',
189 '255.255.255.255',
190];
191
7becc472 192my ($window, $cmdbox, $inbox, $htmlview);
c6ed3b24 193my ($next, $next_fctn, $target_hd);
89a12446 194my ($progress, $progress_status);
b6200603 195my ($ipversion, $ipaddress, $ipconf_entry_addr);
d2120e51
DM
196my ($netmask, $ipconf_entry_mask);
197my ($gateway, $ipconf_entry_gw);
198my ($dnsserver, $ipconf_entry_dns);
89a12446
DM
199my $hostname = 'proxmox';
200my $domain = 'domain.tld';
d2120e51 201my $cmdline = file_read_firstline("/proc/cmdline");
89a12446
DM
202my $ipconf;
203my $country;
204my $timezone = 'Europe/Vienna';
205my $password;
206my $mailto;
207my $keymap = 'en-us';
208my $cmap;
209
aed81ff0
DM
210# parse command line args
211
212my $config_options = {};
213
121ebc59 214if ($cmdline =~ m/\s(ext3|ext4|xfs)(\s.*)?$/) {
5c06ced5
DM
215 $config_options->{filesys} = $1;
216} else {
aeb3d07f 217 $config_options->{filesys} = 'ext4';
5c06ced5 218}
aed81ff0
DM
219
220if ($cmdline =~ m/hdsize=(\d+(\.\d+)?)[\s\n]/i) {
221 $config_options->{hdsize} = $1;
222}
223
224if ($cmdline =~ m/swapsize=(\d+(\.\d+)?)[\s\n]/i) {
225 $config_options->{swapsize} = $1;
226}
227
228if ($cmdline =~ m/maxroot=(\d+(\.\d+)?)[\s\n]/i) {
229 $config_options->{maxroot} = $1;
230}
231
232if ($cmdline =~ m/minfree=(\d+(\.\d+)?)[\s\n]/i) {
233 $config_options->{minfree} = $1;
234}
235
236if ($cmdline =~ m/maxvz=(\d+(\.\d+)?)[\s\n]/i) {
237 $config_options->{maxvz} = $1;
238}
89a12446
DM
239
240my $postfix_main_cf = <<_EOD;
241# See /usr/share/postfix/main.cf.dist for a commented, more complete version
242
243myhostname=__FQDN__
244
245smtpd_banner = \$myhostname ESMTP \$mail_name (Debian/GNU)
246biff = no
247
248# appending .domain is the MUA's job.
249append_dot_mydomain = no
250
251# Uncomment the next line to generate "delayed mail" warnings
252#delay_warning_time = 4h
253
254alias_maps = hash:/etc/aliases
255alias_database = hash:/etc/aliases
256mydestination = \$myhostname, localhost.\$mydomain, localhost
968fa90b 257relayhost =
89a12446
DM
258mynetworks = 127.0.0.0/8
259inet_interfaces = loopback-only
260recipient_delimiter = +
261
262_EOD
263
84761f93
DM
264sub shellquote {
265 my $str = shift;
266
267 return String::ShellQuote::shell_quote($str);
268}
269
270sub cmd2string {
271 my ($cmd) = @_;
272
273 die "no arguments" if !$cmd;
274
275 return $cmd if !ref($cmd);
276
277 my @qa = ();
278 foreach my $arg (@$cmd) { push @qa, shellquote($arg); }
279
280 return join (' ', @qa);
281}
282
968fa90b 283sub syscmd {
89a12446
DM
284 my ($cmd) = @_;
285
286 return run_command ($cmd, undef, undef, 1);
287}
968fa90b 288
89a12446
DM
289sub run_command {
290 my ($cmd, $func, $input, $noout) = @_;
291
84761f93
DM
292 my $cmdstr;
293 if (!ref($cmd)) {
294 $cmdstr = $cmd;
295 if ($cmd =~ m/|/) {
296 # see 'man bash' for option pipefail
297 $cmd = [ '/bin/bash', '-c', "set -o pipefail && $cmd" ];
298 } else {
299 $cmd = [ $cmd ];
300 }
301 } else {
302 $cmdstr = cmd2string($cmd);
303 }
304
89a12446 305 my $cmdtxt;
84761f93
DM
306 if ($input && ($cmdstr !~ m/chpasswd/)) {
307 $cmdtxt = "# $cmdstr <<EOD\n$input";
89a12446
DM
308 chomp $cmdtxt;
309 $cmdtxt .= "\nEOD\n";
310 } else {
84761f93 311 $cmdtxt = "# $cmdstr\n";
89a12446 312 }
4a5dbe69
DM
313
314 if ($opt_testmode) {
315 print $cmdtxt;
316 STDOUT->flush();
317 }
318
89a12446
DM
319 print $logfd $cmdtxt;
320
321 my $reader = IO::File->new();
322 my $writer = IO::File->new();
323 my $error = IO::File->new();
324
325 my $orig_pid = $$;
326
327 my $pid;
328 eval {
84761f93 329 $pid = open3 ($writer, $reader, $error, @$cmd) || die $!;
89a12446
DM
330 };
331
332 my $err = $@;
333
334 # catch exec errors
335 if ($orig_pid != $$) {
968fa90b
DM
336 POSIX::_exit (1);
337 kill ('KILL', $$);
89a12446
DM
338 }
339
340 die $err if $err;
341
342 print $writer $input if defined $input;
343 close $writer;
344
345 my $select = new IO::Select;
346 $select->add ($reader);
347 $select->add ($error);
348
349 my ($ostream, $logout) = ('', '', '');
350
351 while ($select->count) {
352 my @handles = $select->can_read (0.2);
353
d2120e51 354 Gtk3::main_iteration() while Gtk3::events_pending();
89a12446
DM
355
356 next if !scalar (@handles); # timeout
357
358 foreach my $h (@handles) {
359 my $buf = '';
360 my $count = sysread ($h, $buf, 4096);
361 if (!defined ($count)) {
362 my $err = $!;
363 kill (9, $pid);
364 waitpid ($pid, 0);
365 die "command '$cmd' failed: $err";
366 }
367 $select->remove ($h) if !$count;
368 if ($h eq $reader) {
369 $ostream .= $buf if !($noout || $func);
370 $logout .= $buf;
371 while ($logout =~ s/^([^\010\r\n]*)(\r|\n|(\010)+|\r\n)//s) {
372 my $line = $1;
373 &$func($line) if $func;
374 }
375
376 } elsif ($h eq $error) {
377 $ostream .= $buf if !($noout || $func);
378 }
379 print $buf;
380 STDOUT->flush();
381 print $logfd $buf;
382 }
383 }
384
385 &$func($logout) if $func;
386
387 my $rv = waitpid ($pid, 0);
388
389 return $? if $noout; # behave like standard system();
390
556b1466
DM
391 if ($? == -1) {
392 die "command '$cmdstr' failed to execute\n";
393 } elsif (my $sig = ($? & 127)) {
394 die "command '$cmdstr' failed - got signal $sig\n";
395 } elsif (my $exitcode = ($? >> 8)) {
396 die "command '$cmdstr' failed with exit code $exitcode";
89a12446
DM
397 }
398
399 return $ostream;
400}
401
402sub detect_country {
403
404 print "trying to detect country...\n";
62c05878
DM
405 my $cpid = open2(\*TMP, undef, "traceroute -N 1 -q 1 -n 8.8.8.8");
406 return undef if !$cpid;
968fa90b 407
89a12446
DM
408 my $country;
409
410 my $previous_alarm = alarm (10);
411 eval {
412 local $SIG{ALRM} = sub { die "timed out!\n" };
413 my $line;
414 while (defined ($line = <TMP>)) {
415 print $logfd "DC TRACEROUTE: $line";
416 if ($line =~ m/\s*\d+\s+(\d+\.\d+\.\d+\.\d+)\s/) {
417 my $geoip = `geoiplookup $1`;
418 print $logfd "DC GEOIP: $geoip";
419 if ($geoip =~ m/GeoIP Country Edition:\s*([A-Z]+),/) {
420 $country = lc ($1);
62c05878 421 print $logfd "DC FOUND: $country\n";
89a12446
DM
422 last;
423 }
424 }
425 }
426 };
427
428 my $err = $@;
429
430 alarm ($previous_alarm);
431
432 close (TMP);
433
434 if ($err) {
435 print "unable to detect country - $err\n";
436 } elsif ($country) {
437 print "detected country: " . uc($country) . "\n";
438 } else {
439 print "unable to detect country\n";
440 }
441
442 return $country;
443}
444
445sub get_memtotal {
446
447 open (MEMINFO, "/proc/meminfo");
448
449 my $res = 512; # default to 512 if something goes wrong
450 while (my $line = <MEMINFO>) {
451 if ($line =~ m/^MemTotal:\s+(\d+)\s*kB/i) {
452 $res = int ($1 / 1024);
968fa90b 453 }
89a12446
DM
454 }
455
456 close (MEMINFO);
457
458 return $res;
459}
460
461my $total_memory = get_memtotal();
462
463sub link_points_to {
464 my ($src, $dest) = @_;
465
466 my ($dev1,$ino1) = stat ($src);
467 my ($dev2,$ino2) = stat ($dest);
468
469 return 0 if !($dev1 && $dev2 && $ino1 && $ino2);
470
471 return $ino1 == $ino2 && $dev1 == $dev2;
472}
473
474sub find_stable_path {
475 my ($stabledir, $bdev) = @_;
476
a5af22f5
DM
477 foreach my $path (<$stabledir/*>) {
478 if (link_points_to ($path, $bdev)) {
479 return wantarray ? ($path, basename($path)) : $path;
89a12446 480 }
89a12446 481 }
89a12446
DM
482}
483
484sub find_dev_by_uuid {
485 my $bdev = shift;
486
487 my ($full_path, $name) = find_stable_path ("/dev/disk/by-uuid", $bdev);
488
489 return $name;
490}
491
492sub hd_list {
493
494 my $res = ();
495
496 if ($opt_testmode) {
497 push @$res, [-1, $opt_testmode, int((-s $opt_testmode)/512), "TESTDISK"];
121ebc59 498 return $res;
89a12446
DM
499 }
500
501 my $count = 0;
502
503 foreach my $bd (</sys/block/*>) {
504 next if $bd =~ m|^/sys/block/ram\d+$|;
505 next if $bd =~ m|^/sys/block/loop\d+$|;
506 next if $bd =~ m|^/sys/block/md\d+$|;
507 next if $bd =~ m|^/sys/block/dm-.*$|;
508 next if $bd =~ m|^/sys/block/fd\d+$|;
509 next if $bd =~ m|^/sys/block/sr\d+$|;
510
d2120e51 511 my $dev = file_read_firstline("$bd/dev");
89a12446 512 chomp $dev;
968fa90b 513
89a12446
DM
514 next if !$dev;
515
516 my $info = `udevadm info --path $bd --query all`;
517 next if !$info;
518
519 next if $info !~ m/^E: DEVTYPE=disk$/m;
520
521 next if $info =~ m/^E: ID_CDROM/m;
522
523 my ($name) = $info =~ m/^N: (\S+)$/m;
524
968fa90b 525 if ($name) {
89a12446
DM
526 my $real_name = "/dev/$name";
527
d2120e51 528 my $size = file_read_firstline("$bd/size");
89a12446
DM
529 chomp $size;
530 $size = undef if !($size && $size =~ m/^\d+$/);
531
d2120e51 532 my $model = file_read_firstline("$bd/device/model") || '';
89a12446
DM
533 $model =~ s/^\s+//;
534 $model =~ s/\s+$//;
535 if (length ($model) > 30) {
536 $model = substr ($model, 0, 30);
537 }
538 push @$res, [$count++, $real_name, $size, $model] if $size;
539 } else {
540 print STDERR "ERROR: unable to map device $dev ($bd)\n";
541 }
542 }
543
544 return $res;
545}
546
547sub read_cmap {
8c094410 548 my $countryfn = "${proxmox_libdir}/country.dat";
89a12446
DM
549 open (TMP, "<$countryfn") || die "unable to open '$countryfn' - $!\n";
550 my $line;
551 my $country = {};
552 my $countryhash = {};
553 my $kmap = {};
554 my $kmaphash = {};
555 while (defined ($line = <TMP>)) {
556 if ($line =~ m|^map:([^\s:]+):([^:]+):([^:]+):([^:]+):([^:]+):([^:]*):$|) {
557 $kmap->{$1} = {
558 name => $2,
559 kvm => $3,
560 console => $4,
561 x11 => $5,
562 x11var => $6,
563 };
564 $kmaphash->{$2} = $1;
565 } elsif ($line =~ m|^([a-z]{2}):([^:]+):([^:]*):([^:]*):$|) {
566 $country->{$1} = {
567 name => $2,
568 kmap => $3,
569 mirror => $4,
570 };
571 $countryhash->{lc($2)} = $1;
572 } else {
573 warn "unable to parse 'country.dat' line: $line";
574 }
575 }
576 close (TMP);
577
578 my $zones = {};
579 my $cczones = {};
580 my $zonefn = "/usr/share/zoneinfo/zone.tab";
581 open (TMP, "<$zonefn") || die "unable to open '$zonefn' - $!\n";
582 while (defined ($line = <TMP>)) {
583 next if $line =~ m/^\#/;
584 next if $line =~ m/^\s*$/;
585 if ($line =~ m|^([A-Z][A-Z])\s+\S+\s+(([^/]+)/\S+)\s|) {
586 my $cc = lc($1);
587 $cczones->{$cc}->{$2} = 1;
588 $country->{$cc}->{zone} = $2 if !defined ($country->{$cc}->{zone});
589 $zones->{$2} = 1;
590
591 }
592 }
593 close (TMP);
594
595 return {
596 zones => $zones,
597 cczones => $cczones,
598 country => $country,
599 countryhash => $countryhash,
600 kmap => $kmap,
601 kmaphash => $kmaphash,
602 }
603}
604
605# search for Harddisks
606my $hds = hd_list ();
607
608sub hd_size {
609 my ($dev) = @_;
610
611 foreach my $hd (@$hds) {
612 my ($disk, $devname, $size, $model) = @$hd;
1bd457bb 613 # size is always in 512B "sectors"! convert to KB
89a12446
DM
614 return int($size/2) if $devname eq $dev;
615 }
616
eb4b1e56 617 die "no such device '$dev'\n";
89a12446
DM
618}
619
89a12446 620sub get_partition_dev {
c6ed3b24
DM
621 my ($dev, $partnum) = @_;
622
623 if ($dev =~ m|^/dev/[hxsev]d[a-z]$|) {
624 return "${dev}$partnum";
625 } elsif ($dev =~ m|^/dev/[^/]+/c\d+d\d+$|) {
626 return "${dev}p$partnum";
627 } elsif ($dev =~ m|^/dev/[^/]+/d\d+$|) {
628 return "${dev}p$partnum";
629 } elsif ($dev =~ m|^/dev/[^/]+/hd[a-z]$|) {
630 return "${dev}$partnum";
ed32cc83
WL
631 } elsif ($dev =~ m|^/dev/nvme\d+n\d+$|) {
632 return "${dev}p$partnum";
89a12446 633 } else {
c6ed3b24 634 die "unable to get device for partition $partnum on device $dev\n";
89a12446
DM
635 }
636
637}
638
8a50920c
DM
639sub file_get_contents {
640 my ($filename, $max) = @_;
641
642 my $fh = IO::File->new($filename, "r") ||
643 die "can't open '$filename' - $!\n";
644
645 local $/; # slurp mode
646
647 my $content = <$fh>;
648
649 close $fh;
650
651 return $content;
652}
653
89a12446
DM
654sub write_config {
655 my ($text, $filename) = @_;
656
657 my $fd = IO::File->new (">$filename") ||
eb4b1e56 658 die "unable to open file '$filename' - $!\n";
89a12446
DM
659 print $fd $text;
660 $fd->close();
661}
662
663sub update_progress {
664 my ($frac, $start, $end, $text) = @_;
665
666 my $part = $end - $start;
667 my $res = $start + $frac*$part;
668
669 $progress->set_fraction ($res);
670 $progress->set_text (sprintf ("%d%%", int ($res*100)));
671 $progress_status->set_text ($text) if defined ($text);
672
d2120e51 673 Gtk3::main_iteration() while Gtk3::events_pending();
89a12446
DM
674}
675
80090926
DM
676my $fssetup = {
677 ext3 => {
678 mkfs => 'mkfs.ext3 -F',
1f8d0104
DM
679 mkfs_root_opt => '',
680 mkfs_data_opt => '-m 0',
80090926
DM
681 root_mountopt => 'errors=remount-ro',
682 },
683 ext4 => {
684 mkfs => 'mkfs.ext4 -F',
1f8d0104
DM
685 mkfs_root_opt => '',
686 mkfs_data_opt => '-m 0',
80090926
DM
687 root_mountopt => 'errors=remount-ro',
688 },
689 xfs => {
690 mkfs => 'mkfs.xfs -f',
1f8d0104
DM
691 mkfs_root_opt => '',
692 mkfs_data_opt => '',
80090926
DM
693 root_mountopt => '',
694 },
695};
696
89a12446 697sub create_filesystem {
1f8d0104 698 my ($dev, $name, $type, $start, $end, $fs, $fe) = @_;
89a12446
DM
699
700 my $range = $end - $start;
701 my $rs = $start + $range*$fs;
702 my $re = $start + $range*$fe;
703 my $max = 0;
704
80090926 705 my $fsdata = $fssetup->{$type} || die "internal error - unknown file system '$type'";
1f8d0104 706 my $opts = $name eq 'root' ? $fsdata->{mkfs_root_opt} : $fsdata->{mkfs_data_opt};
80090926 707
89a12446
DM
708 update_progress (0, $rs, $re, "creating $name filesystem");
709
80090926 710 run_command ("$fsdata->{mkfs} $opts $dev", sub {
89a12446
DM
711 my $line = shift;
712
713 if ($line =~ m/Writing inode tables:\s+(\d+)\/(\d+)/) {
714 $max = $2;
715 } elsif ($max && $line =~ m/(\d+)\/$max/) {
716 update_progress (($1/$max)*0.9, $rs, $re);
717 } elsif ($line =~ m/Creating journal.*done/) {
718 update_progress (0.95, $rs, $re);
719 } elsif ($line =~ m/Writing superblocks and filesystem.*done/) {
720 update_progress (1, $rs, $re);
968fa90b 721 }
89a12446
DM
722 });
723}
724
725sub debconfig_set {
726 my ($targetdir, $dcdata) = @_;
727
728 my $cfgfile = "/tmp/debconf.txt";
729 write_config ($dcdata, "$targetdir/$cfgfile");
968fa90b
DM
730 syscmd ("chroot $targetdir debconf-set-selections $cfgfile");
731 unlink "$targetdir/$cfgfile";
89a12446
DM
732}
733
734sub diversion_add {
735 my ($targetdir, $cmd, $new_cmd) = @_;
736
737 syscmd ("chroot $targetdir dpkg-divert --package proxmox " .
738 "--add --rename $cmd") == 0 ||
739 die "unable to exec dpkg-divert\n";
740
741 syscmd ("ln -sf ${new_cmd} $targetdir/$cmd") == 0 ||
968fa90b 742 die "unable to link diversion to ${new_cmd}\n";
89a12446
DM
743}
744
745sub diversion_remove {
746 my ($targetdir, $cmd) = @_;
747
748 syscmd ("mv $targetdir/${cmd}.distrib $targetdir/${cmd};") == 0 ||
749 die "unable to remove $cmd diversion\n";
968fa90b 750
89a12446
DM
751 syscmd ("chroot $targetdir dpkg-divert --remove $cmd") == 0 ||
752 die "unable to remove $cmd diversion\n";
753}
754
121ebc59
DM
755sub btrfs_create {
756 my ($partitions, $mode) = @_;
757
758 die "unknown btrfs mode '$mode'"
759 if !($mode eq 'single' || $mode eq 'raid0' ||
760 $mode eq 'raid1' || $mode eq 'raid10');
761
762 my $cmd = ['mkfs.btrfs', '-f'];
763
764 push @$cmd, '-d', $mode, '-m', $mode;
765
766 push @$cmd, @$partitions;
767
768 syscmd($cmd);
769}
770
5c06ced5 771sub zfs_create_rpool {
5fd81672 772 my ($vdev) = @_;
486c490d 773
c7779156
FG
774 my $cmd = "zpool create -f -o cachefile=none";
775
776 $cmd .= " -o ashift=$config_options->{ashift}"
777 if defined($config_options->{ashift});
778
779 syscmd ("$cmd $zfspoolname $vdev") == 0 ||
5c06ced5
DM
780 die "unable to create zfs root pool\n";
781
782 syscmd ("zfs create $zfspoolname/ROOT") == 0 ||
7bc4f6bd 783 die "unable to create zfs $zfspoolname/ROOT volume\n";
5c06ced5 784
5fd81672
DM
785 syscmd ("zfs create $zfspoolname/data") == 0 ||
786 die "unable to create zfs $zfspoolname/data volume\n";
787
5c06ced5 788 syscmd ("zfs create $zfspoolname/ROOT/pve-1") == 0 ||
7bc4f6bd 789 die "unable to create zfs $zfspoolname/ROOT/pve-1 volume\n";
5c06ced5 790
2df572ae 791 # disable atime during install
5c06ced5
DM
792 syscmd ("zfs set atime=off $zfspoolname") == 0 ||
793 die "unable to set zfs properties\n";
c7779156
FG
794
795 my $value = $config_options->{compress};
796 syscmd ("zfs set compression=$value $zfspoolname")
797 if defined($value) && $value ne 'off';
798
799 $value = $config_options->{checksum};
800 syscmd ("zfs set checksum=$value $zfspoolname")
801 if defined($value) && $value ne 'on';
802
803 $value = $config_options->{copies};
804 syscmd ("zfs set copies=$value $zfspoolname")
805 if defined($value) && $value != 1;
5c06ced5
DM
806}
807
7bc4f6bd 808sub zfs_create_swap {
5fd81672 809 my ($swapsize) = @_;
7bc4f6bd 810
991ec37f 811 my $cmd = "zfs create -V ${swapsize}K -b 4K";
7bc4f6bd 812
991ec37f
FG
813 $cmd .= " -o com.sun:auto-snapshot=false";
814
815 # copies for swap does not make sense
816 $cmd .= " -o copies=1";
7bc4f6bd
DM
817
818 # reduces memory pressure
991ec37f 819 $cmd .= " -o sync=always";
7bc4f6bd 820
991ec37f
FG
821 # cheapest compression to drop zero pages
822 $cmd .= " -o compression=zle";
823
824 # skip log devices
825 $cmd .= " -o logbias=throughput";
826 # only cache metadata in RAM (caching swap content does not make sense)
827 $cmd .= " -o primarycache=metadata";
828 # don't cache anything in L2ARC
829 $cmd .= " -o secondarycache=none";
830
831 $cmd .= " $zfspoolname/swap";
832 syscmd ($cmd) == 0 ||
833 die "unable to create zfs swap device\n";
c7779156 834
7bc4f6bd
DM
835 return "/dev/zvol/$zfspoolname/swap";
836}
837
dc4ad419
FG
838my $udevadm_trigger_block = sub {
839 my ($nowait) = @_;
840
841 sleep(1) if !$nowait; # give kernel time to reread part table
842
843 # trigger udev to create /dev/disk/by-uuid
844 syscmd ("udevadm trigger --subsystem-match block");
845 syscmd ("udevadm settle --timeout 10");
846};
847
857c43a9
FG
848my $clean_disk = sub {
849 my ($disk) = @_;
850
851 my $partitions = `lsblk --output kname --noheadings --path --list $disk`;
852 foreach my $part (split "\n", $partitions) {
853 next if $part eq $disk;
854 next if $part !~ /^\Q$disk\E/;
855 eval { syscmd("pvremove -ff -y $part"); };
856 eval { syscmd("dd if=/dev/zero of=$part bs=1M count=16"); };
857 }
858};
859
c6ed3b24 860sub partition_bootable_disk {
121ebc59 861 my ($target_dev, $maxhdsize, $ptype) = @_;
89a12446 862
c6ed3b24 863 die "too dangerous" if $opt_testmode;
89a12446 864
121ebc59
DM
865 die "unknown partition type '$ptype'"
866 if !($ptype eq '8E00' || $ptype eq '8300');
867
6ab785ef 868 syscmd("sgdisk -Z ${target_dev}");
1bd457bb 869 my $hdsize = hd_size($target_dev); # size in KB (1024 bytes)
c6ed3b24 870
9b4dc6e8 871 my $restricted_hdsize_mb = 0; # 0 ==> end of partition
c6ed3b24
DM
872 if ($maxhdsize && ($maxhdsize < $hdsize)) {
873 $hdsize = $maxhdsize;
0cf4b512 874 $restricted_hdsize_mb = int($hdsize/1024) . 'M';
c6ed3b24
DM
875 }
876
877 my $hdgb = int($hdsize/(1024*1024));
eb4b1e56 878 die "hardisk '$target_dev' too small (${hdsize}GB)\n" if $hdgb < 8;
c6ed3b24 879
43b5216c 880 # 1 - BIOS boot partition (Grub Stage2): first free 1M
b15f2bdf 881 # 2 - EFI ESP: next free 256M
43b5216c 882 # 3 - OS/Data partition: rest, up to $maxhdsize in MB
c6ed3b24
DM
883
884 my $grubbootdev = get_partition_dev($target_dev, 1);
885 my $efibootdev = get_partition_dev($target_dev, 2);
a2876e48 886 my $osdev = get_partition_dev ($target_dev, 3);
aed81ff0 887
43b5216c 888 my $pcmd = ['sgdisk'];
89a12446 889
f810f5d0 890 my $pnum = 1;
d8505895 891 push @$pcmd, "-n${pnum}:1M:+1M", "-t$pnum:EF02";
b282cfe8 892
35be9ba7 893 $pnum = 2;
d8505895 894 push @$pcmd, "-n${pnum}:2M:+256M", "-t$pnum:EF00";
35be9ba7 895
f810f5d0 896 $pnum = 3;
d8505895 897 push @$pcmd, "-n${pnum}:258M:${restricted_hdsize_mb}", "-t$pnum:$ptype";
35be9ba7 898
f810f5d0 899 push @$pcmd, $target_dev;
b282cfe8 900
d8505895 901 my $os_size = $hdsize - 258*1024; # 256M + 1M + 1M alignment
89a12446 902
f810f5d0
DM
903 syscmd($pcmd) == 0 ||
904 die "unable to partition harddisk '${target_dev}'\n";
89a12446 905
dc4ad419
FG
906 &$udevadm_trigger_block();
907
908 foreach my $part ($efibootdev, $osdev) {
909 syscmd("dd if=/dev/zero of=$part bs=1M count=256") if -b $part;
910 }
911
f810f5d0
DM
912 return ($os_size, $osdev, $efibootdev);
913}
5c06ced5 914
f810f5d0
DM
915# ZFS has this use_whole_disk concept, so we try to partition the same
916# way as zfs does by default. There is room at start of disk to insert
917# a grub boot partition. But adding a EFI ESP is not possible.
918#
919# Note: zfs people think this is just a waste of space an not
920# required. Instead, you should put the ESP on another disk (log,
921# ..).
89a12446 922
f810f5d0
DM
923sub partition_bootable_zfs_disk {
924 my ($target_dev) = @_;
925
926 die "too dangerous" if $opt_testmode;
927
649d65b6 928 syscmd("sgdisk -Z ${target_dev}");
f810f5d0
DM
929 my $hdsize = hd_size($target_dev); # size in blocks (1024 bytes)
930
931 my $hdgb = int($hdsize/(1024*1024));
eb4b1e56 932 die "hardisk '$target_dev' too small (${hdsize}GB)\n" if $hdgb < 8;
f810f5d0
DM
933
934 # 1 - GRUB boot partition: 1M
935 # 2 - OS/Data partition
936 # 9 - ZFS reserved partition
937
938 my $grubbootdev = get_partition_dev($target_dev, 1);
939 my $osdev = get_partition_dev ($target_dev, 2);
940
941 my $pcmd = ['sgdisk', '-a1'];
942
943 my $pnum = 1;
944 push @$pcmd, "-n$pnum:34:2047", "-t$pnum:EF02";
945
946 $pnum = 9;
947 push @$pcmd, "-n$pnum:-8M:0", "-t$pnum:BF07";
948
949 $pnum = 2;
950 push @$pcmd, "-n$pnum:2048:0", "-t$pnum:BF01", '-c', "$pnum:zfs";
951
952 push @$pcmd, $target_dev;
c6ed3b24 953
f810f5d0 954 my $os_size = $hdsize - 1024 - 1024*8;
968fa90b 955
c6ed3b24
DM
956 syscmd($pcmd) == 0 ||
957 die "unable to partition harddisk '${target_dev}'\n";
968fa90b 958
dc4ad419
FG
959 &$udevadm_trigger_block();
960
961 syscmd("dd if=/dev/zero of=$osdev bs=1M count=16") if -b $osdev;
962
f810f5d0 963 return ($os_size, $osdev);
c6ed3b24 964}
84761f93 965
c6ed3b24
DM
966sub create_lvm_volumes {
967 my ($lvmdev, $os_size, $swap_size) = @_;
7bc4f6bd 968
c6ed3b24
DM
969 my $rootdev = '/dev/pve/root';
970 my $datadev = '/dev/pve/data';
971 my $swapfile = '/dev/pve/swap';
84761f93 972
2df572ae 973 # we use --metadatasize 250k, which results in "pe_start = 512"
c6ed3b24
DM
974 # so pe_start is aligned on a 128k boundary (advantage for SSDs)
975 syscmd ("/sbin/pvcreate --metadatasize 250k -y -ff $lvmdev") == 0 ||
eb4b1e56 976 die "unable to initialize physical volume $lvmdev\n";
c6ed3b24 977 syscmd ("/sbin/vgcreate pve $lvmdev") == 0 ||
eb4b1e56 978 die "unable to create volume group 'pve'\n";
89a12446 979
c6ed3b24
DM
980 my $hdgb = int($os_size/(1024*1024));
981 my $space = (($hdgb > 128) ? 16 : ($hdgb/8))*1024*1024;
89a12446 982
c6ed3b24
DM
983 my $maxroot;
984 if ($config_options->{maxroot}) {
985 $maxroot = $config_options->{maxroot};
986 } else {
987 $maxroot = 96;
988 }
89a12446 989
c6ed3b24 990 my $rootsize = (($hdgb > ($maxroot*4)) ? $maxroot : $hdgb/4)*1024*1024;
89a12446 991
c6ed3b24 992 my $rest = $os_size - $swap_size - $rootsize; # in KB
7bc4f6bd 993
c6ed3b24
DM
994 my $minfree;
995 if ($config_options->{minfree}) {
996 $minfree = (($config_options->{minfree}*1024*1024) >= $rest ) ? $space :
997 $config_options->{minfree}*1024*1024 ;
998 } else {
999 $minfree = $space;
1000 }
7bc4f6bd 1001
c6ed3b24 1002 $rest = $rest - $minfree;
7bc4f6bd 1003
c6ed3b24
DM
1004 if ($config_options->{maxvz}) {
1005 $rest = (($config_options->{maxvz}*1024*1024) <= $rest) ?
1006 $config_options->{maxvz}*1024*1024 : $rest;
1007 }
7bc4f6bd 1008
c6ed3b24 1009 syscmd ("/sbin/lvcreate -L${swap_size}K -nswap pve") == 0 ||
eb4b1e56 1010 die "unable to create swap volume\n";
89a12446 1011
c6ed3b24 1012 syscmd ("/sbin/lvcreate -L${rootsize}K -nroot pve") == 0 ||
eb4b1e56 1013 die "unable to create root volume\n";
89a12446 1014
c6ed3b24 1015 syscmd ("/sbin/lvcreate -L${rest}K -ndata pve") == 0 ||
eb4b1e56 1016 die "unable to create data volume\n";
89a12446 1017
5fd81672 1018 syscmd ("/sbin/lvconvert --yes --type thin-pool pve/data") == 0 ||
eb4b1e56 1019 die "unable to create data thin-pool\n";
5fd81672 1020
c6ed3b24 1021 syscmd ("/sbin/vgchange -a y pve") == 0 ||
eb4b1e56 1022 die "unable to activate volume group\n";
7bc4f6bd 1023
c6ed3b24
DM
1024 return ($rootdev, $datadev, $swapfile);
1025}
7bc4f6bd 1026
c6ed3b24
DM
1027sub compute_swapsize {
1028 my ($hdsize) = @_;
89a12446 1029
c6ed3b24 1030 my $hdgb = int($hdsize/(1024*1024));
5c06ced5 1031
c6ed3b24
DM
1032 my $swapsize;
1033 if ($config_options->{swapsize}) {
1034 $swapsize = $config_options->{swapsize}*1024*1024;
1035 } else {
1036 my $ss = int ($total_memory / 1024);
1037 $ss = 4 if $ss < 4;
1038 $ss = ($hdgb/8) if $ss > ($hdgb/8);
cbdfeb36 1039 $ss = 8 if $ss > 8;
c6ed3b24
DM
1040 $swapsize = $ss*1024*1024;
1041 }
d0d8ce3f
DM
1042
1043 return $swapsize;
c6ed3b24 1044}
5c06ced5 1045
121ebc59 1046
c6ed3b24 1047sub extract_data {
fafc616c 1048 my ($basefile, $targetdir) = @_;
89a12446 1049
c6ed3b24 1050 die "target '$targetdir' does not exist\n" if ! -d $targetdir;
89a12446 1051
121ebc59
DM
1052 my $starttime = [Time::HiRes::gettimeofday];
1053
c6ed3b24 1054 my $bootdevinfo = [];
84761f93 1055
c6ed3b24
DM
1056 my $datadev;
1057 my $swapfile;
1058 my $rootdev;
84761f93 1059
121ebc59
DM
1060 my $use_zfs = 0;
1061 my $use_btrfs = 0;
89092156 1062
c6ed3b24 1063 my $filesys = $config_options->{filesys};
89092156 1064
c6ed3b24
DM
1065 if ($filesys =~ m/zfs/) {
1066 $target_hd = undef; # do not use this config
1067 $use_zfs = 1;
1068 $targetdir = "/$zfspoolname/ROOT/pve-1";
121ebc59
DM
1069 } elsif ($filesys =~ m/btrfs/) {
1070 $target_hd = undef; # do not use this config
1071 $use_btrfs = 1;
c6ed3b24
DM
1072 }
1073
1074 if ($use_zfs) {
1075 my $i;
1076 for ($i = 5; $i > 0; $i--) {
1077 syscmd("modprobe zfs");
1078 last if -c "/dev/zfs";
1079 sleep(1);
1080 }
89092156 1081
c6ed3b24
DM
1082 die "unable to load zfs kernel module\n" if !$i;
1083 }
89092156 1084
c6ed3b24 1085 eval {
89a12446 1086
89a12446 1087
c6ed3b24 1088 my $maxper = 0.25;
89a12446 1089
c6ed3b24
DM
1090 update_progress (0, 0, $maxper, "create partitions");
1091
857c43a9
FG
1092 syscmd("vgchange -an") if !$opt_testmode; # deactivate all detected VGs
1093
c6ed3b24 1094 if ($opt_testmode) {
89a12446 1095
6b900321
DM
1096 $rootdev = abs_path($opt_testmode);
1097 syscmd("umount $rootdev");
121ebc59 1098
6b900321 1099 if ($use_btrfs) {
121ebc59
DM
1100
1101 die "unsupported btrfs mode (for testing environment)\n"
1102 if $filesys ne 'btrfs (RAID0)';
1103
1104 btrfs_create([$rootdev], 'single');
5c06ced5 1105
121ebc59 1106 } elsif ($use_zfs) {
5c06ced5 1107
121ebc59 1108 die "unsupported zfs mode (for testing environment)\n"
c6ed3b24
DM
1109 if $filesys ne 'zfs (RAID0)';
1110
6b900321 1111 syscmd ("zpool destroy $zfstestpool");
5c06ced5 1112
5fd81672 1113 zfs_create_rpool($rootdev);
121ebc59
DM
1114
1115 } else {
1116
6b900321 1117 # nothing to do
121ebc59
DM
1118 }
1119
1120 } elsif ($use_btrfs) {
1121
1122 my ($devlist, $btrfs_mode) = get_btrfs_raid_setup();
1123 my $btrfs_partitions = [];
1124 my $disksize;
1125 foreach my $hd (@$devlist) {
1126 my $devname = @$hd[1];
857c43a9 1127 &$clean_disk($devname);
121ebc59
DM
1128 my ($size, $osdev, $efidev) =
1129 partition_bootable_disk($devname, undef, '8300');
1130 $rootdev = $osdev if !defined($rootdev); # simply point to first disk
1131 my $by_id = find_stable_path("/dev/disk/by-id", $devname);
1132 push @$bootdevinfo, { esp => $efidev, devname => $devname,
1133 osdev => $osdev, by_id => $by_id };
1134 push @$btrfs_partitions, $osdev;
1135 $disksize = $size;
5c06ced5 1136 }
c6ed3b24 1137
121ebc59
DM
1138 &$udevadm_trigger_block();
1139
1140 btrfs_create($btrfs_partitions, $btrfs_mode);
1141
c6ed3b24
DM
1142 } elsif ($use_zfs) {
1143
c6ed3b24
DM
1144 my ($devlist, $bootdevlist, $vdev) = get_zfs_raid_setup();
1145
1146 my $disksize;
857c43a9
FG
1147 foreach my $hd (@$devlist) {
1148 &$clean_disk(@$hd[1]);
1149 }
c6ed3b24
DM
1150 foreach my $hd (@$bootdevlist) {
1151 my $devname = @$hd[1];
f810f5d0
DM
1152 my ($size, $osdev) =
1153 partition_bootable_zfs_disk($devname);
14aacec8 1154 zfs_mirror_size_check($disksize, $size) if $disksize;
f810f5d0 1155 push @$bootdevinfo, { devname => $devname, osdev => $osdev};
c6ed3b24 1156 $disksize = $size;
c6ed3b24
DM
1157 }
1158
121ebc59 1159 &$udevadm_trigger_block();
c6ed3b24 1160
35c6f89c
DM
1161 foreach my $di (@$bootdevinfo) {
1162 my $devname = $di->{devname};
1163 $di->{by_id} = find_stable_path ("/dev/disk/by-id", $devname);
1164
1165 # Note: using /dev/disk/by-id/ does not work for unknown reason, we get
1166 # cannot create 'rpool': no such pool or dataset
1167 #my $osdev = find_stable_path ("/dev/disk/by-id", $di->{osdev}) || $di->{osdev};
c6ed3b24 1168
35c6f89c
DM
1169 my $osdev = $di->{osdev};
1170 $vdev =~ s/ $devname/ $osdev/;
1171 }
1172
5fd81672 1173 zfs_create_rpool($vdev);
35c6f89c 1174
c6ed3b24 1175 my $swap_size = compute_swapsize($disksize);
5fd81672 1176 $swapfile = zfs_create_swap($swap_size);
c6ed3b24
DM
1177
1178 } else {
1179
1180 die "target '$target_hd' is not a valid block device\n" if ! -b $target_hd;
1181
1182 my $maxhdsize;
1183 if ($config_options->{hdsize}) {
1184 # max hdsize passed on cmdline (GB)
1185 $maxhdsize = $config_options->{hdsize}*1024*1024;
1186 }
1187
857c43a9
FG
1188 &$clean_disk($target_hd);
1189
c6ed3b24 1190 my ($os_size, $osdev, $efidev);
a2876e48 1191 ($os_size, $osdev, $efidev) =
121ebc59 1192 partition_bootable_disk($target_hd, $maxhdsize, '8E00');
c6ed3b24 1193
121ebc59 1194 &$udevadm_trigger_block();
c6ed3b24 1195
35c6f89c
DM
1196 my $by_id = find_stable_path ("/dev/disk/by-id", $target_hd);
1197 push @$bootdevinfo, { esp => $efidev, devname => $target_hd,
1198 osdev => $osdev, by_id => $by_id };
c6ed3b24 1199
35c6f89c
DM
1200 my $swap_size = compute_swapsize($os_size);
1201 ($rootdev, $datadev, $swapfile) =
1202 create_lvm_volumes($osdev, $os_size, $swap_size);
c6ed3b24 1203
35c6f89c 1204 # trigger udev to create /dev/disk/by-uuid
121ebc59 1205 &$udevadm_trigger_block(1);
89a12446
DM
1206 }
1207
481671c3
DM
1208 if ($use_zfs) {
1209 # to be fast during installation
1210 syscmd ("zfs set sync=disabled $zfspoolname") == 0 ||
1211 die "unable to set zfs properties\n";
1212 }
1213
89a12446
DM
1214 update_progress (0.03, 0, $maxper, "create swap space");
1215 if ($swapfile) {
7bc4f6bd 1216 syscmd ("mkswap -f $swapfile") == 0 ||
89a12446
DM
1217 die "unable to create swap space\n";
1218 }
1219
1220 update_progress (0.05, 0, $maxper, "creating filesystems");
1221
c6ed3b24 1222 foreach my $di (@$bootdevinfo) {
f810f5d0 1223 next if !$di->{esp};
c6ed3b24
DM
1224 syscmd ("mkfs.vfat -F32 $di->{esp}") == 0 ||
1225 die "unable to initialize EFI ESP on device $di->{esp}\n";
1226 }
1227
121ebc59
DM
1228 if ($use_zfs) {
1229 # do nothing
1230 } elsif ($use_btrfs) {
1231 # do nothing
1232 } else {
1233 create_filesystem ($rootdev, 'root', $filesys, 0.05, $maxper, 0, 1);
89a12446
DM
1234 }
1235
1236 update_progress (1, 0.05, $maxper, "mounting target $rootdev");
1237
121ebc59
DM
1238 if ($use_zfs) {
1239 # do nothing
121ebc59 1240 } else {
6e56032e
FG
1241 my $mount_opts = 'noatime';
1242 $mount_opts .= ',nobarrier'
1243 if $use_btrfs || $filesys =~ /^ext\d$/;
1244
1245 syscmd("mount -n $rootdev -o $mount_opts $targetdir") == 0 ||
35c6f89c
DM
1246 die "unable to mount $rootdev\n";
1247 }
89a12446 1248
35c6f89c
DM
1249 mkdir "$targetdir/boot";
1250 mkdir "$targetdir/boot/efi";
89a12446 1251
5fd81672
DM
1252 mkdir "$targetdir/var";
1253 mkdir "$targetdir/var/lib";
1254 mkdir "$targetdir/var/lib/vz";
121ebc59
DM
1255 mkdir "$targetdir/var/lib/pve";
1256
1257 if ($use_btrfs) {
1258 syscmd("btrfs subvolume create $targetdir/var/lib/pve/local-btrfs") == 0 ||
1259 die "unable to create btrfs subvolume\n";
1260 }
89a12446
DM
1261
1262 display_html ("extract2-rulesystem.htm");
1263 update_progress (1, 0.05, $maxper, "extracting base system");
1264
fafc616c
DM
1265 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size) = stat ($basefile);
1266 $ino || die "unable to open file '$basefile' - $!\n";
968fa90b 1267
c437cef5
DM
1268 my $files = file_read_firstline("${proxmox_cddir}/proxmox/$setup->{product}-base.cnt") ||
1269 die "unable to read base file count\n";
89a12446
DM
1270
1271 my $per = 0;
1272 my $count = 0;
1273
fafc616c 1274 run_command ("unsquashfs -f -dest $targetdir -i $basefile", sub {
89a12446 1275 my $line = shift;
fafc616c 1276 return if $line !~ m/^$targetdir/;
89a12446
DM
1277 $count++;
1278 my $nper = int (($count *100)/$files);
1279 if ($nper != $per) {
1280 $per = $nper;
0f3d1edd 1281 my $frac = $per > 100 ? 1 : $per/100;
89a12446
DM
1282 update_progress ($frac, $maxper, 0.5);
1283 }
1284 });
1285
1286 syscmd ("mount -n -t tmpfs tmpfs $targetdir/tmp") == 0 ||
1287 die "unable to mount tmpfs on $targetdir/tmp\n";
1288 syscmd ("mount -n -t proc proc $targetdir/proc") == 0 ||
1289 die "unable to mount proc on $targetdir/proc\n";
1290 syscmd ("mount -n -t sysfs sysfs $targetdir/sys") == 0 ||
1291 die "unable to mount sysfs on $targetdir/sys\n";
1292
1293 display_html ("extract3-spam.htm");
1294 update_progress (1, $maxper, 0.5, "configuring base system");
1295
1296 # configure hosts
1297
968fa90b 1298 my $hosts =
89a12446 1299 "127.0.0.1 localhost.localdomain localhost\n" .
968fa90b 1300 "$ipaddress $hostname.$domain $hostname pvelocalhost\n\n" .
89a12446
DM
1301 "# The following lines are desirable for IPv6 capable hosts\n\n" .
1302 "::1 ip6-localhost ip6-loopback\n" .
1303 "fe00::0 ip6-localnet\n" .
1304 "ff00::0 ip6-mcastprefix\n" .
1305 "ff02::1 ip6-allnodes\n" .
1306 "ff02::2 ip6-allrouters\n" .
1307 "ff02::3 ip6-allhosts\n";
1308
968fa90b 1309 write_config ($hosts, "$targetdir/etc/hosts");
89a12446 1310
968fa90b 1311 write_config ("$hostname\n", "$targetdir/etc/hostname");
89a12446
DM
1312
1313 syscmd ("/bin/hostname $hostname") if !$opt_testmode;
1314
1315 # configure interfaces
1316
b6200603
DM
1317 my $ifaces = "auto lo\niface lo inet loopback\n\n";
1318
1319 my $ntype = $ipversion == 4 ? 'inet' : 'inet6';
1320
fe44bd92
FG
1321 my $bridge_port = $ipconf->{ifaces}->{$ipconf->{selected}}->{name};
1322
1323 $ifaces .= "iface $bridge_port $ntype manual\n";
1324
b6200603 1325 $ifaces .=
fe44bd92 1326 "\nauto vmbr0\niface vmbr0 $ntype static\n" .
b6200603
DM
1327 "\taddress $ipaddress\n" .
1328 "\tnetmask $netmask\n" .
1329 "\tgateway $gateway\n" .
fe44bd92 1330 "\tbridge_ports $bridge_port\n" .
b6200603
DM
1331 "\tbridge_stp off\n" .
1332 "\tbridge_fd 0\n";
89a12446 1333
fe44bd92
FG
1334 foreach my $iface (sort keys %{$ipconf->{ifaces}}) {
1335 my $name = $ipconf->{ifaces}->{$iface}->{name};
1336 next if $name eq $bridge_port;
1337
1338 $ifaces .= "\niface $name $ntype manual\n";
1339 }
1340
89a12446
DM
1341 write_config ($ifaces, "$targetdir/etc/network/interfaces");
1342
1343 # configure dns
1344
39a0f44b
FG
1345 my $resolvconf = "search $domain\nnameserver $dnsserver\n";
1346 write_config ($resolvconf, "$targetdir/etc/resolv.conf");
89a12446 1347
5c06ced5
DM
1348 # configure fstab
1349
1350 my $fstab = "# <file system> <mount point> <type> <options> <dump> <pass>\n";
1351
121ebc59
DM
1352 if ($use_zfs) {
1353 # do nothing
1354 } elsif ($use_btrfs) {
1355 my $fsuuid;
1356 my $cmd = "blkid -u filesystem -t TYPE=btrfs -o export $rootdev";
1357 run_command($cmd, sub {
1358 my $line = shift;
1359
1360 if ($line =~ m/^UUID=([A-Fa-f0-9\-]+)$/) {
1361 $fsuuid = $1;
1362 }
1363 });
1364
1365 die "unable to detect FS UUID" if !defined($fsuuid);
1366
1367 $fstab .= "UUID=$fsuuid / btrfs defaults 0 1\n";
1368 } else {
80090926
DM
1369 my $root_mountopt = $fssetup->{$filesys}->{root_mountopt} || 'defaults';
1370 $fstab .= "$rootdev / $filesys ${root_mountopt} 0 1\n";
7bc4f6bd 1371 }
a84ea010
DM
1372
1373 # mount /boot/efi
1374 # Note: this is required by current grub, but really dangerous, because
1375 # vfat does not have journaling, so it triggers manual fsck after each crash
1376 # so we only mount /boot/efi if really required (efi systems).
1377 if ($grub_plattform =~ m/^efi-/) {
1378 if (scalar(@$bootdevinfo)) {
f810f5d0
DM
1379 my $di = @$bootdevinfo[0]; # simply use first disk
1380 if ($di->{esp}) {
1381 my $efi_boot_uuid = $di->{esp};
1382 if (my $uuid = find_dev_by_uuid ($di->{esp})) {
1383 $efi_boot_uuid = "UUID=$uuid";
1384 }
84761f93 1385
f810f5d0
DM
1386 $fstab .= "${efi_boot_uuid} /boot/efi vfat defaults 0 1\n";
1387 }
a84ea010 1388 }
84761f93
DM
1389 }
1390
a84ea010 1391
89a12446
DM
1392 $fstab .= "$swapfile none swap sw 0 0\n" if $swapfile;
1393
1394 $fstab .= "proc /proc proc defaults 0 0\n";
1395
1396 write_config ($fstab, "$targetdir/etc/fstab");
1397 write_config ("", "$targetdir/etc/mtab");
968fa90b 1398
a04ac176 1399 syscmd ("cp ${proxmox_libdir}/policy-disable-rc.d " .
968fa90b 1400 "$targetdir/usr/sbin/policy-rc.d") == 0 ||
89a12446 1401 die "unable to copy policy-rc.d\n";
a04ac176 1402 syscmd ("cp ${proxmox_libdir}/fake-start-stop-daemon " .
968fa90b 1403 "$targetdir/sbin/") == 0 ||
89a12446
DM
1404 die "unable to copy start-stop-daemon\n";
1405
1406 diversion_add ($targetdir, "/sbin/start-stop-daemon", "/sbin/fake-start-stop-daemon");
1407 diversion_add ($targetdir, "/usr/sbin/update-grub", "/bin/true");
1408 diversion_add ($targetdir, "/usr/sbin/update-initramfs", "/bin/true");
1409
1410 syscmd ("touch $targetdir/proxmox_install_mode");
1411
e35d5efb 1412 my $grub_install_devices_txt = '';
3573c046 1413 foreach my $di (@$bootdevinfo) {
e35d5efb 1414 $grub_install_devices_txt .= ', ' if $grub_install_devices_txt;
ff863262 1415 $grub_install_devices_txt .= $di->{by_id} || $di->{devname};
3573c046
DM
1416 }
1417
b1293fcb
FG
1418 # Note: keyboard-configuration/xbkb-keymap is used by console-setup
1419 my $xkmap = $cmap->{kmap}->{$keymap}->{x11} // 'us';
814f5c39 1420
89a12446
DM
1421 debconfig_set ($targetdir, <<_EOD);
1422locales locales/default_environment_locale select en_US.UTF-8
1423locales locales/locales_to_be_generated select en_US.UTF-8 UTF-8
1424samba-common samba-common/dhcp boolean false
1425samba-common samba-common/workgroup string WORKGROUP
1426postfix postfix/main_mailer_type select Local only
b1293fcb 1427keyboard-configuration keyboard-configuration/xkb-keymap select $xkmap
814f5c39 1428d-i debian-installer/locale select en_US.UTF-8
3573c046 1429grub-pc grub-pc/install_devices select $grub_install_devices_txt
89a12446
DM
1430_EOD
1431
89a12446 1432 my $pkg_count = 0;
97980bf2 1433 while (<${proxmox_pkgdir}/*.deb>) { $pkg_count++ };
89a12446 1434
121ebc59
DM
1435 # btrfs/dpkg is extremely slow without --force-unsafe-io
1436 my $dpkg_opts = $use_btrfs ? "--force-unsafe-io" : "";
1437
89a12446 1438 $count = 0;
97980bf2 1439 while (<${proxmox_pkgdir}/*.deb>) {
89a12446
DM
1440 chomp;
1441 my $path = $_;
97980bf2 1442 my ($deb) = $path =~ m/${proxmox_pkgdir}\/(.*\.deb)/;
1e61f3d8
DM
1443# if ($deb =~ m/^grub-efi-/ && $deb !~ m/^grub-${grub_plattform}/) {
1444# $count++;
1445# next;
1446# }
89a12446
DM
1447 update_progress ($count/$pkg_count, 0.5, 0.75, "extracting $deb");
1448 print "extracting: $deb\n";
1449 syscmd ("cp $path $targetdir/tmp/$deb") == 0 ||
1450 die "installation of package $deb failed\n";
121ebc59 1451 syscmd ("chroot $targetdir dpkg $dpkg_opts --force-depends --no-triggers --unpack /tmp/$deb") == 0 ||
968fa90b 1452 die "installation of package $deb failed\n";
89a12446
DM
1453 update_progress ((++$count)/$pkg_count, 0.5, 0.75);
1454 }
1455
1456 display_html ("extract4-virus.htm");
1457
3b11dce4
FG
1458 # needed for postfix postinst in case no other NIC is active
1459 syscmd("chroot $targetdir ifup lo");
1460
121ebc59 1461 my $cmd = "chroot $targetdir dpkg $dpkg_opts --force-confold --configure -a";
89a12446
DM
1462 $count = 0;
1463 run_command ($cmd, sub {
1464 my $line = shift;
1465 if ($line =~ m/Setting up\s+(\S+)/) {
1466 update_progress ((++$count)/$pkg_count, 0.75, 0.95,
1467 "configuring $1");
1468 }
1469 });
968fa90b
DM
1470
1471 debconfig_set ($targetdir, <<_EOD);
89a12446
DM
1472postfix postfix/main_mailer_type select No configuration
1473_EOD
1474
1475 unlink "$targetdir/etc/mailname";
1476 $postfix_main_cf =~ s/__FQDN__/${hostname}.${domain}/;
1477 write_config ($postfix_main_cf, "$targetdir/etc/postfix/main.cf");
1478
1479 # make sure we have all postfix directories
1480 syscmd ("chroot $targetdir /usr/sbin/postfix check");
1481 # cleanup mail queue
1482 syscmd ("chroot $targetdir /usr/sbin/postsuper -d ALL");
1483
6b5dc3d0
DM
1484 # enable NTP (timedatectl set-ntp true does not work without DBUS)
1485 syscmd ("chroot $targetdir /bin/systemctl enable systemd-timesyncd.service");
1486
89a12446
DM
1487 unlink "$targetdir/proxmox_install_mode";
1488
968fa90b 1489 # set timezone
89a12446
DM
1490 unlink ("$targetdir/etc/localtime");
1491 symlink ("/usr/share/zoneinfo/$timezone", "$targetdir/etc/localtime");
1492 write_config ("$timezone\n", "$targetdir/etc/timezone");
1493
89a12446
DM
1494 # set apt mirror
1495 if (my $mirror = $cmap->{country}->{$country}->{mirror}) {
1496 my $fn = "$targetdir/etc/apt/sources.list";
968fa90b 1497 syscmd ("sed -i 's/ftp\\.debian\\.org/$mirror/' '$fn'");
89a12446
DM
1498 }
1499
19edf8b7
DM
1500 # create extended_states for apt (avoid cron job warning if that
1501 # file does not exist)
1502 write_config ('', "$targetdir/var/lib/apt/extended_states");
1503
c2657b8b
DM
1504 # allow ssh root login
1505 syscmd ("sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' '$targetdir/etc/ssh/sshd_config'");
1506
89a12446
DM
1507 # save installer settings
1508 my $ucc = uc ($country);
1509 debconfig_set ($targetdir, <<_EOD);
1510pve-manager pve-manager/country string $ucc
1511_EOD
1512
1513 update_progress (0.8, 0.95, 1, "make system bootable");
1514
5c06ced5 1515 if ($use_zfs) {
52f45831 1516 syscmd ("sed -i -e 's/^GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX=\"root=ZFS=$zfspoolname\\/ROOT\\/pve-1 boot=zfs\"/' $targetdir/etc/default/grub") == 0 ||
5c06ced5
DM
1517 die "unable to update /etc/default/grub\n";
1518
1519 }
23c337f5 1520
89a12446
DM
1521 diversion_remove ($targetdir, "/usr/sbin/update-grub");
1522 diversion_remove ($targetdir, "/usr/sbin/update-initramfs");
1523
c6ed3b24 1524 if (!$opt_testmode) {
89a12446
DM
1525
1526 unlink ("$targetdir/etc/mtab");
1527 symlink ("/proc/mounts", "$targetdir/etc/mtab");
1528 syscmd ("mount -n --bind /dev $targetdir/dev");
1529
1530 syscmd ("chroot $targetdir /usr/sbin/update-initramfs -c -k $kapi") == 0 ||
1531 die "unable to install initramfs\n";
1532
c6ed3b24
DM
1533 foreach my $di (@$bootdevinfo) {
1534 my $dev = $di->{devname};
f810f5d0
DM
1535 syscmd ("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
1536 die "unable to install the i386-pc boot loader on '$dev'\n";
1537
1538 if ($di->{esp}) {
83250a74 1539 syscmd ("mount -n $di->{esp} -t vfat $targetdir/boot/efi") == 0 ||
f810f5d0 1540 die "unable to mount $di->{esp}\n";
5e0d6dce
FG
1541 my $rc = syscmd ("chroot $targetdir /usr/sbin/grub-install --target x86_64-efi --no-floppy --bootloader-id='proxmox' $dev");
1542 if ($rc != 0) {
1543 if (-d '/sys/firmware/efi') {
1544 die "unable to install the EFI boot loader on '$dev'\n";
1545 } else {
1546 warn "unable to install the EFI boot loader on '$dev', ignoring (not booted using UEFI)\n";
1547 }
1548 }
fd3ec787
DM
1549 # also install fallback boot file (OVMF does not boot without)
1550 mkdir("$targetdir/boot/efi/EFI/BOOT");
1551 syscmd("cp $targetdir/boot/efi/EFI/proxmox/grubx64.efi $targetdir/boot/efi/EFI/BOOT/BOOTx64.EFI") == 0 ||
1552 die "unable to copy efi boot loader\n";
1553
f810f5d0
DM
1554 syscmd ("umount $targetdir/boot/efi") == 0 ||
1555 die "unable to umount $targetdir/boot/efi\n";
1e61f3d8 1556 }
c6ed3b24 1557 }
89a12446
DM
1558
1559 syscmd ("chroot $targetdir /usr/sbin/update-grub") == 0 ||
c6ed3b24 1560 die "unable to update boot loader config\n";
89a12446
DM
1561
1562 syscmd ("umount $targetdir/dev");
1563 }
1564
968fa90b 1565 # cleanup
89a12446 1566
968fa90b 1567 # hack: remove dead.letter from sshd installation
89a12446
DM
1568 syscmd ("rm -rf $targetdir/dead.letter");
1569
89a12446
DM
1570 unlink "$targetdir/usr/sbin/policy-rc.d";
1571
1572 diversion_remove ($targetdir, "/sbin/start-stop-daemon");
1573
1574 # set root password
968fa90b 1575 my $octets = encode("utf-8", $password);
89a12446
DM
1576 run_command ("chroot $targetdir /usr/sbin/chpasswd", undef,
1577 "root:$octets\n");
7053f98b
DM
1578
1579 # create pmxcfs DB
1580
1581 my $tmpdir = "$targetdir/tmp/pve";
1582 mkdir $tmpdir;
1583
1584 # write vnc keymap to datacenter.cfg
1585 my $vnckmap = $cmap->{kmap}->{$keymap}->{kvm} || 'en-us';
968fa90b 1586 write_config ("keyboard: $vnckmap\n",
7053f98b
DM
1587 "$tmpdir/datacenter.cfg");
1588
1589 # save admin email
968fa90b 1590 write_config ("user:root\@pam:1:0:::${mailto}::\n",
7053f98b 1591 "$tmpdir/user.cfg");
968fa90b 1592
5fd81672
DM
1593 # write storage.cfg
1594 my $strorage_cfg_fn = "$tmpdir/storage.cfg";
1595 if ($use_zfs) {
1596 write_config ($storage_cfg_zfs, $strorage_cfg_fn);
121ebc59
DM
1597 } elsif ($use_btrfs) {
1598 write_config ($storage_cfg_btrfs, $strorage_cfg_fn);
5fd81672
DM
1599 } else {
1600 write_config ($storage_cfg_lvmthin, $strorage_cfg_fn);
1601 }
1602
7053f98b
DM
1603 run_command("chroot $targetdir /usr/bin/create_pmxcfs_db /tmp/pve /var/lib/pve-cluster/config.db");
1604
1605 syscmd ("rm -rf $tmpdir");
89a12446
DM
1606 };
1607
1608 my $err = $@;
1609
1610 update_progress (1, 0, 1, "");
1611
1612 print $err if $err;
1613
1614 if ($opt_testmode) {
121ebc59
DM
1615 my $elapsed = Time::HiRes::tv_interval($starttime);
1616 print "Elapsed extract time: $elapsed\n";
1617
89a12446
DM
1618 syscmd ("chroot $targetdir /usr/bin/dpkg-query -W --showformat='\${package}\n'> pve-final.pkglist");
1619 }
1620
89a12446
DM
1621 syscmd ("umount $targetdir/var/lib/vz");
1622 syscmd ("umount $targetdir/tmp");
1623 syscmd ("umount $targetdir/proc");
1624 syscmd ("umount $targetdir/sys");
6fbd1fb1
DM
1625
1626 if ($use_zfs) {
1627 syscmd ("zfs umount -a") == 0 ||
1628 die "unable to unmount zfs\n";
1629 } else {
1630 syscmd ("umount -d $targetdir");
1631 }
89a12446 1632
5c06ced5 1633 if (!$err && $use_zfs) {
481671c3
DM
1634 syscmd ("zfs set sync=standard $zfspoolname") == 0 ||
1635 die "unable to set zfs properties\n";
1636
5c06ced5
DM
1637 syscmd ("zfs set mountpoint=/ $zfspoolname/ROOT/pve-1") == 0 ||
1638 die "zfs set mountpoint failed\n";
1639
1640 syscmd ("zpool set bootfs=$zfspoolname/ROOT/pve-1 $zfspoolname") == 0 ||
1641 die "zfs set bootfs failed\n";
016679a2 1642 syscmd ("zpool export $zfspoolname");
5c06ced5
DM
1643 }
1644
89a12446
DM
1645 die $err if $err;
1646}
1647
1648sub display_html {
1649 my ($filename) = @_;
1650
a04ac176 1651 my $path = "${proxmox_libdir}/html/$filename";
c437cef5 1652
8a50920c
DM
1653 my $url = "file://$path";
1654
1655 my $data = file_get_contents($path);
1656
1657 if ($filename eq 'license.htm') {
3c866639
DM
1658 my $license = decode('utf8', file_get_contents("${proxmox_cddir}/EULA"));
1659 my $title = "END USER LICENSE AGREEMENT (EULA)";
f91c161b 1660 $data =~ s/__LICENSE__/$license/;
8a50920c
DM
1661 $data =~ s/__LICENSE_TITLE__/$title/;
1662 }
1663
1664 $htmlview->load_html_string($data, $url);
7becc472
DM
1665}
1666
89a12446
DM
1667sub set_next {
1668 my ($text, $fctn) = @_;
1669
1670 $next_fctn = $fctn;
1671 $text = "_Next" if !$text;
1672 $next->set_label ($text);
968fa90b 1673
89a12446
DM
1674 $next->grab_focus ();
1675}
89a12446
DM
1676
1677sub create_main_window {
1678
7becc472 1679 $window = Gtk3::Window->new ();
89a12446 1680 $window->set_default_size (1024, 768);
84761f93 1681 $window->set_has_resize_grip(0);
89a12446
DM
1682 $window->set_decorated (0) if !$opt_testmode;
1683
7becc472 1684 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 1685
a04ac176 1686 my $image = Gtk3::Image->new_from_file ("${proxmox_libdir}/proxlogo.png");
89a12446
DM
1687 $vbox->pack_start ($image, 0, 0, 0);
1688
7becc472 1689 my $hbox = Gtk3::HBox->new (0, 0);
89a12446
DM
1690 $vbox->pack_start ($hbox, 1, 1, 0);
1691
7becc472
DM
1692 # my $f1 = Gtk3::Frame->new ('test');
1693 # $f1->set_shadow_type ('none');
1694 # $hbox->pack_start ($f1, 1, 1, 0);
89a12446 1695
7becc472 1696 my $sep1 = Gtk3::HSeparator->new;
89a12446
DM
1697 $vbox->pack_start ($sep1, 0, 0, 0);
1698
7becc472 1699 $cmdbox = Gtk3::HBox->new ();
89a12446
DM
1700 $vbox->pack_start ($cmdbox, 0, 0, 10);
1701
7becc472 1702 $next = Gtk3::Button->new ('_Next');
89a12446
DM
1703 $next->signal_connect (clicked => sub { &$next_fctn (); });
1704 $cmdbox->pack_end ($next, 0, 0, 10);
7becc472
DM
1705 my $abort = Gtk3::Button->new ('_Abort');
1706 $abort->set_can_focus (0);
89a12446
DM
1707 $cmdbox->pack_start ($abort, 0, 0, 10);
1708 $abort->signal_connect (clicked => sub { exit (-1); });
1709
7becc472
DM
1710 my $vbox2 = Gtk3::VBox->new (0, 0);
1711 $hbox->add ($vbox2);
89a12446 1712
7becc472
DM
1713 $htmlview = Gtk3::WebKit::WebView->new();
1714 my $scrolls = Gtk3::ScrolledWindow->new();
1715 $scrolls->add($htmlview);
1716
1717 my $hbox2 = Gtk3::HBox->new (0, 0);
1718 $hbox2->pack_start ($scrolls, 1, 1, 0);
89a12446
DM
1719
1720 $vbox2->pack_start ($hbox2, 1, 1, 0);
1721
7becc472 1722 my $vbox3 = Gtk3::VBox->new (0, 0);
89a12446
DM
1723 $vbox2->pack_start ($vbox3, 0, 0, 0);
1724
7becc472 1725 my $sep2 = Gtk3::HSeparator->new;
89a12446
DM
1726 $vbox3->pack_start ($sep2, 0, 0, 0);
1727
7becc472 1728 $inbox = Gtk3::HBox->new (0, 0);
89a12446
DM
1729 $vbox3->pack_start ($inbox, 0, 0, 0);
1730
1731 $window->add ($vbox);
1732
1733 $window->show_all;
1734 $window->realize ();
1735}
1736
d2120e51
DM
1737sub cleanup_view {
1738 $inbox->foreach(sub {
1739 my $child = shift;
1740 $inbox->remove ($child);
1741 });
89a12446
DM
1742}
1743
aed81ff0
DM
1744# fixme: newer GTK3 has special properties to handle numbers with Entry
1745# only allow floating point numbers with Gtk3::Entry
e73c5fcf 1746
aed81ff0
DM
1747sub check_float {
1748 my ($entry, $event) = @_;
1749
e73c5fcf
FG
1750 return check_number($entry, $event, 1);
1751}
1752
1753sub check_int {
1754 my ($entry, $event) = @_;
1755
1756 return check_number($entry, $event, 0);
1757}
1758
1759sub check_number {
1760 my ($entry, $event, $float) = @_;
aed81ff0
DM
1761
1762 my $val = $event->get_keyval;
1763
e73c5fcf 1764 if (($float && $val == ord '.') ||
aed81ff0
DM
1765 $val == Gtk3::Gdk::KEY_ISO_Left_Tab ||
1766 $val == Gtk3::Gdk::KEY_Shift_L ||
1767 $val == Gtk3::Gdk::KEY_Tab ||
1768 $val == Gtk3::Gdk::KEY_Left ||
1769 $val == Gtk3::Gdk::KEY_Right ||
1770 $val == Gtk3::Gdk::KEY_BackSpace ||
1771 $val == Gtk3::Gdk::KEY_Delete ||
1772 ($val >= ord '0' && $val <= ord '9') ||
1773 ($val >= Gtk3::Gdk::KEY_KP_0 &&
1774 $val <= Gtk3::Gdk::KEY_KP_9)) {
1775 return undef;
1776 }
1777
1778 return 1;
1779}
1780
d2120e51 1781sub create_text_input {
89a12446
DM
1782 my ($default, $text) = @_;
1783
7becc472 1784 my $hbox = Gtk3::HBox->new (0, 0);
89a12446 1785
7becc472 1786 my $label = Gtk3::Label->new ($text);
89a12446
DM
1787 $label->set_size_request (150, -1);
1788 $label->set_alignment (1, 0.5);
1789 $hbox->pack_start ($label, 0, 0, 10);
7becc472 1790 my $e1 = Gtk3::Entry->new ();
89a12446
DM
1791 $e1->set_width_chars (30);
1792 $hbox->pack_start ($e1, 0, 0, 0);
1793 $e1->set_text ($default);
1794
1795 return ($hbox, $e1);
1796}
1797
89a12446
DM
1798sub get_ip_config {
1799
fe44bd92
FG
1800 my $ifaces = {};
1801 my $default;
89a12446 1802
fe44bd92
FG
1803 my $links = `ip -o l`;
1804 foreach my $l (split /\n/,$links) {
1805 my ($index, $name, $flags, $state, $mac) = $l =~ m/^(\d+):\s+(\S+):\s+<(\S+)>.*\s+state\s+(\S+)\s+.*\s+link\/ether\s+(\S+)\s+/;
1806 next if !$name || $name eq 'lo';
89a12446 1807
fe44bd92
FG
1808 my $driver = readlink "/sys/class/net/$name/device/driver" || 'unknown';
1809 $driver =~ s!^.*/!!;
1810
1811 $ifaces->{"$index"} = {
1812 name => $name,
1813 driver => $driver,
1814 flags => $flags,
1815 state => $state,
1816 mac => $mac,
1817 };
1818
1819 my $addresses = `ip -o a s $name`;
1820 foreach my $a (split /\n/,$addresses) {
1821 my ($family, $ip, $prefix) = $a =~ m/^\Q$index\E:\s+\Q$name\E\s+(inet|inet6)\s+($IPRE)\/(\d+)\s+/;
1822 next if !$ip;
32b6fbcf 1823 next if $a =~ /scope\s+link/; # ignore link local
fe44bd92
FG
1824
1825 my $mask = $prefix;
1826
1827 if ($family eq 'inet') {
1828 next if !$ip =~ /$IPV4RE/;
1829 next if $prefix < 8 || $prefix > 32;
1830 $mask = @$ipv4_reverse_mask[$prefix];
1831 } else {
1832 next if !$ip =~ /$IPV6RE/;
1833 }
1834
1835 $default = $index if !$default;
1836
1837 $ifaces->{"$index"}->{"$family"} = {
1838 mask => $mask,
1839 addr => $ip,
1840 };
1841 }
1842 }
1843
1844
1845 my $route = `ip route`;
1846 my ($gateway) = $route =~ m/^default\s+via\s+(\S+)\s+/m;
89a12446
DM
1847
1848 my $resolvconf = `cat /etc/resolv.conf`;
1849 my ($dnsserver) = $resolvconf =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/m;
713790a4 1850 my ($domain) = $resolvconf =~ m/^domain\s+(\S+)$/m;
89a12446
DM
1851
1852 return {
fe44bd92
FG
1853 default => $default,
1854 ifaces => $ifaces,
89a12446
DM
1855 gateway => $gateway,
1856 dnsserver => $dnsserver,
713790a4 1857 domain => $domain,
89a12446
DM
1858 }
1859}
1860
1861sub display_message {
1862 my ($msg) = @_;
1863
7becc472 1864 my $dialog = Gtk3::MessageDialog->new ($window, 'modal',
89a12446
DM
1865 'info', 'ok', $msg);
1866 $dialog->run();
1867 $dialog->destroy();
1868}
1869
1870sub display_error {
1871 my ($msg) = @_;
1872
7becc472 1873 my $dialog = Gtk3::MessageDialog->new ($window, 'modal',
89a12446
DM
1874 'error', 'ok', $msg);
1875 $dialog->run();
1876 $dialog->destroy();
1877}
1878
fe44bd92
FG
1879my $ipconf_first_view = 1;
1880
89a12446
DM
1881sub create_ipconf_view {
1882
1883 cleanup_view ();
1884 display_html ("ipconf.htm");
1885
7becc472 1886 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 1887 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 1888 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 1889 $vbox->pack_start ($hbox, 0, 0, 10);
7becc472 1890 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446
DM
1891 $hbox->add ($vbox2);
1892
fe44bd92
FG
1893 my $ipbox;
1894 ($ipbox, $ipconf_entry_addr) =
1895 create_text_input ("192.168.100.2", 'IP Address:');
1896
1897 my $maskbox;
1898 ($maskbox, $ipconf_entry_mask) =
1899 create_text_input ("255.255.255.0", 'Netmask:');
1900
1901 my $device_cb = Gtk3::ComboBoxText->new();
1902 $device_cb->set_active(0);
1903 $device_cb->set_visible(1);
1904
1905 my $get_device_desc = sub {
1906 my $iface = shift;
1907 return "$iface->{name} - $iface->{mac} ($iface->{driver})";
1908 };
1909
1910 my $device_active_map = {};
5b6ba737
FG
1911
1912 my $device_change_handler = sub {
1913 my $current = shift;
1914 $ipconf->{selected} = $device_active_map->{$current->get_active()};
1915 my $iface = $ipconf->{ifaces}->{$ipconf->{selected}};
1916 $ipconf_entry_addr->set_text($iface->{inet}->{addr} || $iface->{inet6}->{addr})
1917 if $iface->{inet}->{addr} || $iface->{inet6}->{addr};
1918 $ipconf_entry_mask->set_text($iface->{inet}->{mask} || $iface->{inet6}->{mask})
1919 if $iface->{inet}->{mask} || $iface->{inet6}->{mask};
1920 };
1921
fe44bd92
FG
1922 my $i = 0;
1923 foreach my $index (sort keys %{$ipconf->{ifaces}}) {
1924 $device_cb->append_text(&$get_device_desc($ipconf->{ifaces}->{$index}));
1925 $device_active_map->{$i} = $index;
1926 if ($ipconf_first_view && $index == $ipconf->{default}) {
1927 $device_cb->set_active($i);
5b6ba737 1928 &$device_change_handler($device_cb);
fe44bd92
FG
1929 $ipconf_first_view = 0;
1930 }
5b6ba737 1931 $device_cb->signal_connect ('changed' => $device_change_handler);
fe44bd92
FG
1932 $i++;
1933 }
1934
5b6ba737
FG
1935 $device_cb->set_active(0)
1936 if !($ipconf->{selected});
1937
fe44bd92
FG
1938 my $devicebox = Gtk3::HBox->new (0, 0);
1939 my $label = Gtk3::Label->new ("Management Interface:");
1940 $label->set_size_request (150, -1);
1941 $label->set_alignment (1, 0.5);
1942 $devicebox->pack_start ($label, 0, 0, 10);
1943 $devicebox->pack_start ($device_cb, 0, 0, 0);
1944
1945 $vbox2->pack_start ($devicebox, 0, 0, 2);
968fa90b 1946
a39bc1f2 1947 my $hn = $ipconf->{domain} ? "pve.$ipconf->{domain}" : 'pve.example.invalid';
713790a4 1948
968fa90b 1949 my ($hostbox, $hostentry) =
d2120e51 1950 create_text_input ($hn, 'Hostname (FQDN):');
89a12446
DM
1951 $vbox2->pack_start ($hostbox, 0, 0, 2);
1952
89a12446
DM
1953 $vbox2->pack_start ($ipbox, 0, 0, 2);
1954
89a12446
DM
1955 $vbox2->pack_start ($maskbox, 0, 0, 2);
1956
1957 $gateway = $ipconf->{gateway} || '192.168.100.1';
1958
1959 my $gwbox;
d2120e51
DM
1960 ($gwbox, $ipconf_entry_gw) =
1961 create_text_input ($gateway, 'Gateway:');
89a12446 1962
53986d77 1963 $vbox2->pack_start ($gwbox, 0, 0, 2);
89a12446
DM
1964
1965 $dnsserver = $ipconf->{dnsserver} || $gateway;
1966
1967 my $dnsbox;
d2120e51
DM
1968 ($dnsbox, $ipconf_entry_dns) =
1969 create_text_input ($dnsserver, 'DNS Server:');
89a12446
DM
1970
1971 $vbox2->pack_start ($dnsbox, 0, 0, 0);
1972
1973 $inbox->show_all;
968fa90b 1974 set_next (undef, sub {
d2120e51
DM
1975
1976 # verify hostname
1977
89a12446 1978 my $text = $hostentry->get_text();
968fa90b 1979
89a12446
DM
1980 $text =~ s/^\s+//;
1981 $text =~ s/\s+$//;
1982
ac3757a9 1983 my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
968fa90b 1984
24973868
WB
1985 # Debian does not support purely numeric hostnames
1986 if ($text && $text =~ /^[0-9]+(?:\.|$)/) {
1987 display_message("Purely numeric hostnames are not allowed.");
1988 $hostentry->grab_focus();
1989 return;
1990 }
1991
a39bc1f2 1992 if ($text && $text =~ m/^(${namere}\.)*${namere}$/ && $text !~ m/.example.invalid$/ &&
89a12446
DM
1993 $text =~ m/^([^\.]+)\.(\S+)$/) {
1994 $hostname = $1;
1995 $domain = $2;
d2120e51
DM
1996 } else {
1997 display_message ("Hostname does not look like a fully qualified domain name.");
1998 $hostentry->grab_focus();
89a12446
DM
1999 return;
2000 }
d2120e51
DM
2001
2002 # verify ip address
2003
2004 $text = $ipconf_entry_addr->get_text();
2005 $text =~ s/^\s+//;
2006 $text =~ s/\s+$//;
2007 if ($text =~ m!^($IPV4RE)$!) {
2008 $ipaddress = $text;
b6200603
DM
2009 $ipversion = 4;
2010 } elsif ($text =~ m!^($IPV6RE)$!) {
2011 $ipaddress = $text;
2012 $ipversion = 6;
d2120e51
DM
2013 } else {
2014 display_message ("IP address is not valid.");
2015 $ipconf_entry_addr->grab_focus();
2016 return;
2017 }
2018
2019 $text = $ipconf_entry_mask->get_text();
2020 $text =~ s/^\s+//;
2021 $text =~ s/\s+$//;
b6200603
DM
2022 if (($ipversion == 6) && ($text =~ m/^(\d+)$/) && ($1 >= 8) && ($1 <= 126)) {
2023 $netmask = $text;
2024 } elsif (($ipversion == 4) && defined($ipv4_mask_hash->{$text})) {
d2120e51
DM
2025 $netmask = $text;
2026 } else {
2027 display_message ("Netmask is not valid.");
2028 $ipconf_entry_mask->grab_focus();
2029 return;
2030 }
2031
2032 $text = $ipconf_entry_gw->get_text();
2033 $text =~ s/^\s+//;
2034 $text =~ s/\s+$//;
b6200603
DM
2035 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2036 $gateway = $text;
2037 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2038 $gateway = $text;
2039 } else {
2040 display_message ("Gateway is not valid.");
2041 $ipconf_entry_gw->grab_focus();
2042 return;
2043 }
2044
2045 $text = $ipconf_entry_dns->get_text();
2046 $text =~ s/^\s+//;
2047 $text =~ s/\s+$//;
b6200603
DM
2048 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2049 $dnsserver = $text;
2050 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2051 $dnsserver = $text;
2052 } else {
2053 display_message ("DNS server is not valid.");
2054 $ipconf_entry_dns->grab_focus();
2055 return;
2056 }
2057
2058 #print "TEST $ipaddress $netmask $gateway $dnsserver\n";
2059
2060 create_extract_view ();
89a12446
DM
2061 });
2062
2063 $hostentry->grab_focus();
2064}
2065
2066sub get_device_desc {
2067 my ($devname, $size, $model) = @_;
2068
d2120e51 2069 if ($size && ($size > 0)) {
1bd457bb 2070 $size = int($size/2048); # size in MB, from 512B "sectors"
89a12446 2071
d2120e51 2072 my $text = "$devname (";
89a12446
DM
2073 if ($size >= 1024) {
2074 $size = int($size/1024); # size in GB
d2120e51 2075 $text .= "${size}GB";
89a12446 2076 } else {
d2120e51 2077 $text .= "${size}MB";
89a12446
DM
2078 }
2079
d2120e51
DM
2080 $text .= ", $model" if $model;
2081 $text .= ")";
2082
89a12446
DM
2083 } else {
2084 return $devname;
2085 }
2086}
2087
2088sub update_layout {
2089 my ($cb, $kmap) = @_;
2090
2091 my $ind;
2092 my $def;
2093 my $i = 0;
2094 my $kmaphash = $cmap->{kmaphash};
2095 foreach my $layout (sort keys %$kmaphash) {
2096 $def = $i if $kmaphash->{$layout} eq 'en-us';
2097 $ind = $i if $kmap && $kmaphash->{$layout} eq $kmap;
2098 $i++;
2099 }
2100
2101 $cb->set_active ($ind || $def || 0);
2102}
2103
2104my $lastzonecb;
2105sub update_zonelist {
2106 my ($box, $cc) = @_;
2107
2108 my $cczones = $cmap->{cczones};
2109 my $zones = $cmap->{zones};
2110
2111 my $sel;
2112 if ($lastzonecb) {
2113 $sel = $lastzonecb->get_active_text();
2114 $box->remove ($lastzonecb);
2115 } else {
2116 $sel = $timezone; # used once to select default
2117 }
2118
bcbfab6b 2119 my $cb = $lastzonecb = Gtk3::ComboBoxText->new();
89a12446
DM
2120 $cb->set_size_request (200, -1);
2121
2122 $cb->signal_connect ('changed' => sub {
2123 $timezone = $cb->get_active_text();
2124 });
2125
2126 my @za;
2127 if ($cc && defined ($cczones->{$cc})) {
2128 @za = keys %{$cczones->{$cc}};
2129 } else {
2130 @za = keys %$zones;
2131 }
2132 my $ind;
2133 my $i = 0;
2134 foreach my $zone (sort @za) {
2135 $ind = $i if $sel && $zone eq $sel;
2136 $cb->append_text ($zone);
2137 $i++;
2138 }
2139
2140 $cb->set_active ($ind || 0);
2141
2142 $cb->show;
2143 $box->pack_start ($cb, 0, 0, 0);
2144}
2145
2146sub create_password_view {
2147
2148 cleanup_view ();
2149
7becc472 2150 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446 2151 $inbox->pack_start ($vbox2, 1, 0, 0);
7becc472 2152 my $vbox = Gtk3::VBox->new (0, 0);
53986d77 2153 $vbox2->pack_start ($vbox, 0, 0, 10);
89a12446 2154
7becc472
DM
2155 my $hbox1 = Gtk3::HBox->new (0, 0);
2156 my $label = Gtk3::Label->new ("Password");
89a12446
DM
2157 $label->set_size_request (150, -1);
2158 $label->set_alignment (1, 0.5);
2159 $hbox1->pack_start ($label, 0, 0, 10);
7becc472 2160 my $pwe1 = Gtk3::Entry->new ();
89a12446
DM
2161 $pwe1->set_visibility (0);
2162 $pwe1->set_size_request (200, -1);
2163 $hbox1->pack_start ($pwe1, 0, 0, 0);
2164
7becc472
DM
2165 my $hbox2 = Gtk3::HBox->new (0, 0);
2166 $label = Gtk3::Label->new ("Confirm");
89a12446
DM
2167 $label->set_size_request (150, -1);
2168 $label->set_alignment (1, 0.5);
2169 $hbox2->pack_start ($label, 0, 0, 10);
7becc472 2170 my $pwe2 = Gtk3::Entry->new ();
89a12446
DM
2171 $pwe2->set_visibility (0);
2172 $pwe2->set_size_request (200, -1);
2173 $hbox2->pack_start ($pwe2, 0, 0, 0);
2174
7becc472
DM
2175 my $hbox3 = Gtk3::HBox->new (0, 0);
2176 $label = Gtk3::Label->new ("E-Mail");
89a12446
DM
2177 $label->set_size_request (150, -1);
2178 $label->set_alignment (1, 0.5);
2179 $hbox3->pack_start ($label, 0, 0, 10);
7becc472 2180 my $eme = Gtk3::Entry->new ();
89a12446 2181 $eme->set_size_request (200, -1);
a39bc1f2 2182 $eme->set_text('mail@example.invalid');
89a12446
DM
2183 $hbox3->pack_start ($eme, 0, 0, 0);
2184
2185
2186 $vbox->pack_start ($hbox1, 0, 0, 5);
2187 $vbox->pack_start ($hbox2, 0, 0, 5);
2188 $vbox->pack_start ($hbox3, 0, 0, 15);
2189
2190 $inbox->show_all;
2191
2192 display_html ("passwd.htm");
2193
2194 set_next (undef, sub {
2195
2196 my $t1 = $pwe1->get_text;
2197 my $t2 = $pwe2->get_text;
2198
2199 if (length ($t1) < 5) {
2200 display_message ("Password is too short.");
2201 $pwe1->grab_focus();
2202 return;
2203 }
2204
2205 if ($t1 ne $t2) {
2206 display_message ("Password does not match.");
2207 $pwe1->grab_focus();
2208 return;
2209 }
2210
2211 my $t3 = $eme->get_text;
2212 if ($t3 !~ m/^\S+\@\S+\.\S+$/) {
034f75e4 2213 display_message ("E-Mail does not look like a valid address" .
89a12446
DM
2214 " (user\@domain.tld)");
2215 $eme->grab_focus();
2216 return;
a39bc1f2 2217 }
89a12446 2218
a39bc1f2
FG
2219 if ($t3 eq 'mail@example.invalid') {
2220 display_message ("Please enter a valid E-Mail address");
2221 $eme->grab_focus();
2222 return;
89a12446
DM
2223 }
2224
2225 $password = $t1;
2226 $mailto = $t3;
2227
2228 create_ipconf_view();
2229 });
2230
2231 $pwe1->grab_focus();
2232
2233}
2234
2235sub create_country_view {
2236
2237 cleanup_view ();
2238
2239 my $countryhash = $cmap->{countryhash};
2240 my $ctr = $cmap->{country};
2241
7becc472 2242 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446 2243 $inbox->pack_start ($vbox2, 1, 0, 0);
7becc472 2244 my $vbox = Gtk3::VBox->new (0, 0);
53986d77 2245 $vbox2->pack_start ($vbox, 0, 0, 10);
89a12446 2246
7becc472 2247 my $w = Gtk3::Entry->new ();
89a12446
DM
2248 $w->set_size_request (200, -1);
2249
7becc472 2250 my $c = Gtk3::EntryCompletion->new ();
89a12446
DM
2251 $c->set_text_column (0);
2252 $c->set_minimum_key_length(0);
2253 $c->set_popup_set_width (1);
4443aa27 2254 $c->set_inline_completion (1);
89a12446 2255
7becc472
DM
2256 my $hbox2 = Gtk3::HBox->new (0, 0);
2257 my $label = Gtk3::Label->new ("Time zone");
89a12446
DM
2258 $label->set_size_request (150, -1);
2259 $label->set_alignment (1, 0.5);
2260 $hbox2->pack_start ($label, 0, 0, 10);
2261 update_zonelist ($hbox2);
2262
7becc472
DM
2263 my $hbox3 = Gtk3::HBox->new (0, 0);
2264 $label = Gtk3::Label->new ("Keyboard Layout");
89a12446
DM
2265 $label->set_size_request (150, -1);
2266 $label->set_alignment (1, 0.5);
2267 $hbox3->pack_start ($label, 0, 0, 10);
2268
bcbfab6b 2269 my $kmapcb = Gtk3::ComboBoxText->new();
89a12446
DM
2270 $kmapcb->set_size_request (200, -1);
2271 foreach my $layout (sort keys %{$cmap->{kmaphash}}) {
2272 $kmapcb->append_text ($layout);
2273 }
2274
2275 update_layout ($kmapcb);
2276 $hbox3->pack_start ($kmapcb, 0, 0, 0);
2277
2278 $kmapcb->signal_connect ('changed' => sub {
2279 my $sel = $kmapcb->get_active_text();
2280 if (my $kmap = $cmap->{kmaphash}->{$sel}) {
2281 my $xkmap = $cmap->{kmap}->{$kmap}->{x11};
2282 my $xvar = $cmap->{kmap}->{$kmap}->{x11var};
2283 syscmd ("setxkbmap $xkmap $xvar") if !$opt_testmode;
2284 $keymap = $kmap;
2285 }
2286 });
2287
2288 $w->signal_connect ('changed' => sub {
2289 my ($entry, $event) = @_;
2290 my $text = $entry->get_text;
2291
2292 if (my $cc = $countryhash->{lc($text)}) {
2293 update_zonelist ($hbox2, $cc);
2294 my $kmap = $ctr->{$cc}->{kmap} || 'en-us';
2295 update_layout ($kmapcb, $kmap);
2296 }
2297 });
2298
2299 $w->signal_connect (key_press_event => sub {
2300 my ($entry, $event) = @_;
2301 my $text = $entry->get_text;
2302
7becc472
DM
2303 my $val = $event->get_keyval;
2304
2305 if ($val == Gtk3::Gdk::KEY_Tab) {
89a12446 2306 my $cc = $countryhash->{lc($text)};
4443aa27 2307
89a12446
DM
2308 my $found = 0;
2309 my $compl;
7becc472 2310
4443aa27
DM
2311 if ($cc) {
2312 $found = 1;
2313 $compl = $ctr->{$cc}->{name};
2314 } else {
2315 foreach my $cc (keys %$ctr) {
2316 my $ct = $ctr->{$cc}->{name};
2317 if ($ct =~ m/^\Q$text\E.*$/i) {
2318 $found++;
2319 $compl = $ct;
2320 }
2321 last if $found > 1;
89a12446 2322 }
89a12446 2323 }
4443aa27 2324
89a12446 2325 if ($found == 1) {
7becc472 2326 $entry->set_text($compl);
3df718ea 2327 $c->complete();
89a12446
DM
2328 return undef;
2329 } else {
7becc472
DM
2330 #Gtk3::Gdk::beep();
2331 print chr(7); # beep ?
89a12446
DM
2332 }
2333
3df718ea
DM
2334 $c->complete();
2335
7becc472
DM
2336 my $buf = $w->get_buffer();
2337 $buf->insert_text(-1, '', -1); # popup selection
2338
89a12446
DM
2339 return 1;
2340 }
2341
2342 return undef;
2343 });
7becc472
DM
2344
2345 my $ls = Gtk3::ListStore->new('Glib::String');
89a12446
DM
2346 foreach my $cc (sort {$ctr->{$a}->{name} cmp $ctr->{$b}->{name} } keys %$ctr) {
2347 my $iter = $ls->append();
2348 $ls->set ($iter, 0, $ctr->{$cc}->{name});
2349 }
2350 $c->set_model ($ls);
2351
968fa90b 2352 $w->set_completion ($c);
89a12446 2353
7becc472 2354 my $hbox = Gtk3::HBox->new (0, 0);
89a12446 2355
7becc472 2356 $label = Gtk3::Label->new ("Country");
89a12446
DM
2357 $label->set_alignment (1, 0.5);
2358 $label->set_size_request (150, -1);
2359 $hbox->pack_start ($label, 0, 0, 10);
2360 $hbox->pack_start ($w, 0, 0, 0);
2361
2362 $vbox->pack_start ($hbox, 0, 0, 5);
2363 $vbox->pack_start ($hbox2, 0, 0, 5);
2364 $vbox->pack_start ($hbox3, 0, 0, 5);
2365
9d1f1ee3 2366 if ($country && $ctr->{$country}) {
89a12446
DM
2367 $w->set_text ($ctr->{$country}->{name});
2368 }
2369
2370 $inbox->show_all;
2371
2372 display_html ("country.htm");
2373 set_next (undef, sub {
2374
2375 my $text = $w->get_text;
2376
2377 if (my $cc = $countryhash->{lc($text)}) {
2378 $country = $cc;
2379 create_password_view();
2380 return;
2381 } else {
2382 display_message ("Please select a country first.");
2383 $w->grab_focus();
2384 }
2385 });
2386
2387 $w->grab_focus();
2388}
2389
c6ed3b24
DM
2390my $target_hd_combo;
2391my $target_hd_label;
2392
2393my $hdopion_first_setup = 1;
2394
c7779156
FG
2395my $create_basic_grid = sub {
2396 my $grid = Gtk3::Grid->new();
2397 $grid->set_visible(1);
2398 $grid->set_column_spacing(10);
2399 $grid->set_row_spacing(10);
2400 $grid->set_hexpand(1);
2401
2402 $grid->set_margin_start(5);
2403 $grid->set_margin_end(5);
2404 $grid->set_margin_top(5);
2405 $grid->set_margin_bottom(5);
2406
2407 return $grid;
2408};
2409
2410my $create_label_widget_grid = sub {
2411 my ($labeled_widgets) = @_;
2412
2413 my $grid = &$create_basic_grid();
2414 my $row = 0;
2415
2416 for (my $i = 0; $i < @$labeled_widgets; $i += 2) {
2417 my $widget = @$labeled_widgets[$i+1];
2418 my $label = Gtk3::Label->new(@$labeled_widgets[$i]);
2419 $label->set_visible(1);
2420 $label->set_alignment (1, 0.5);
2421 $grid->attach($label, 0, $row, 1, 1);
2422 $widget->set_visible(1);
2423 $grid->attach($widget, 1, $row, 1, 1);
2424 $row++;
2425 }
2426
2427 return $grid;
2428};
2429
2430my $create_raid_disk_grid = sub {
2431 my $disk_labeled_widgets = [];
2432 for (my $i = 0; $i < @$hds; $i++) {
2433 my $disk_selector = Gtk3::ComboBoxText->new();
2434 $disk_selector->append_text("-- do not use --");
2435 $disk_selector->set_active(0);
2436 $disk_selector->set_visible(1);
2437 foreach my $hd (@$hds) {
2438 my ($disk, $devname, $size, $model) = @$hd;
2439 $disk_selector->append_text(get_device_desc ($devname, $size, $model));
2440 $disk_selector->{pve_disk_id} = $i;
2441 $disk_selector->signal_connect (changed => sub {
2442 my $w = shift;
2443 my $diskid = $w->{pve_disk_id};
2444 my $a = $w->get_active - 1;
2445 $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
2446 });
2447 }
2448
2449 if ($hdopion_first_setup) {
2450 $disk_selector->set_active ($i+1) if $hds->[$i];
2451 } else {
2452 my $hdind = 0;
2453 if (my $cur_hd = $config_options->{"disksel$i"}) {
2454 foreach my $hd (@$hds) {
2455 if (@$hd[1] eq @$cur_hd[1]) {
2456 $disk_selector->set_active($hdind+1);
2457 last;
2458 }
2459 $hdind++;
2460 }
2461 }
2462 }
2463
2464 push @$disk_labeled_widgets, "Harddisk $i", $disk_selector;
2465 }
2466
2467 my $scrolled_window = Gtk3::ScrolledWindow->new();
2468 $scrolled_window->set_hexpand(1);
650a9aab 2469 $scrolled_window->set_propagate_natural_height(1) if @$hds > 4;
c7779156
FG
2470 $scrolled_window->add(&$create_label_widget_grid($disk_labeled_widgets));
2471 $scrolled_window->set_policy('never', 'automatic');
2472
2473 return $scrolled_window;
2474# &$create_label_widget_grid($disk_labeled_widgets)
2475};
2476
2477my $create_raid_advanced_grid = sub {
2478 my $labeled_widgets = [];
6c99667a
FG
2479 my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9,13,1);
2480 $spinbutton_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
2481 $spinbutton_ashift->signal_connect ("value-changed" => sub {
2482 my $w = shift;
2483 $config_options->{ashift} = $w->get_value_as_int();
c7779156
FG
2484 });
2485 $config_options->{ashift} = 12 if ! defined($config_options->{ashift});
6c99667a 2486 $spinbutton_ashift->set_value($config_options->{ashift});
c7779156 2487 push @$labeled_widgets, "ashift";
6c99667a 2488 push @$labeled_widgets, $spinbutton_ashift;
c7779156
FG
2489
2490 my $combo_compress = Gtk3::ComboBoxText->new();
2491 $combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset");
2492 # note: gzip / lze not allowed for bootfs vdevs
2493 my $comp_opts = ["on","off","lzjb","lz4"];
2494 foreach my $opt (@$comp_opts) {
2495 $combo_compress->append($opt, $opt);
2496 }
2497 $config_options->{compress} = "on" if !defined($config_options->{compress});
2498 $combo_compress->set_active_id($config_options->{compress});
2499 $combo_compress->signal_connect (changed => sub {
2500 my $w = shift;
2501 $config_options->{compress} = $w->get_active_text();
2502 });
2503 push @$labeled_widgets, "compress";
2504 push @$labeled_widgets, $combo_compress;
2505
2506 my $combo_checksum = Gtk3::ComboBoxText->new();
2507 $combo_checksum->set_tooltip_text("zfs checksum algorithm for rpool dataset");
2508 my $csum_opts = ["on", "off","fletcher2", "fletcher4", "sha256"];
2509 foreach my $opt (@$csum_opts) {
2510 $combo_checksum->append($opt, $opt);
2511 }
2512 $config_options->{checksum} = "on" if !($config_options->{checksum});
2513 $combo_checksum->set_active_id($config_options->{checksum});
2514 $combo_checksum->signal_connect (changed => sub {
2515 my $w = shift;
2516 $config_options->{checksum} = $w->get_active_text();
2517 });
2518 push @$labeled_widgets, "checksum";
2519 push @$labeled_widgets, $combo_checksum;
2520
2521 my $spinbutton_copies = Gtk3::SpinButton->new_with_range(1,3,1);
2522 $spinbutton_copies->set_tooltip_text("zfs copies property for rpool dataset (in addition to RAID redundancy!)");
2523 $spinbutton_copies->signal_connect ("value-changed" => sub {
2524 my $w = shift;
2525 $config_options->{copies} = $w->get_value_as_int();
c7779156
FG
2526 });
2527 $config_options->{copies} = 1 if !defined($config_options->{copies});
2528 $spinbutton_copies->set_value($config_options->{copies});
2529 push @$labeled_widgets, "copies", $spinbutton_copies;
2530
2531 return &$create_label_widget_grid($labeled_widgets);;
2532};
2533
aed81ff0
DM
2534sub create_hdoption_view {
2535
2536 my $dialog = Gtk3::Dialog->new();
2537
2538 $dialog->set_title("Harddisk options");
2539
2540 $dialog->add_button("_OK", 1);
2541
2542 my $contarea = $dialog->get_content_area();
2543
2544 my $hbox2 = Gtk3::Box->new('horizontal', 0);
2545 $contarea->pack_start($hbox2, 1, 1, 10);
2546
2547 my $grid = Gtk3::Grid->new();
2548 $grid->set_column_spacing(10);
2549 $grid->set_row_spacing(10);
c6ed3b24 2550
aed81ff0 2551 $hbox2->pack_start($grid, 1, 0, 10);
c6ed3b24
DM
2552
2553 my $row = 0;
2554
aed81ff0
DM
2555 # Filesystem type
2556
2557 my $label0 = Gtk3::Label->new ("Filesystem");
2558 $label0->set_alignment (1, 0.5);
c6ed3b24
DM
2559 $grid->attach($label0, 0, $row, 1, 1);
2560
bcbfab6b 2561 my $fstypecb = Gtk3::ComboBoxText->new();
aed81ff0 2562
121ebc59
DM
2563 my $fstype = ['ext3', 'ext4', 'xfs',
2564 'zfs (RAID0)', 'zfs (RAID1)',
2565 'zfs (RAID10)', 'zfs (RAIDZ-1)',
6f52fc3d
DM
2566 'zfs (RAIDZ-2)', 'zfs (RAIDZ-3)'];
2567
2568 push @$fstype, 'btrfs (RAID0)', 'btrfs (RAID1)', 'btrfs (RAID10)'
2569 if $enable_btrfs;
aed81ff0 2570
c6ed3b24
DM
2571 my $tcount = 0;
2572 foreach my $tmp (@$fstype) {
2573 $fstypecb->append_text($tmp);
2574 $fstypecb->set_active ($tcount)
2575 if $config_options->{filesys} eq $tmp;
2576 $tcount++;
2577 }
2578
2579 $grid->attach($fstypecb, 1, $row, 1, 1);
2580
2581 $hbox2->show_all();
2582
2583 $row++;
2584
c7779156
FG
2585 my $sep = Gtk3::HSeparator->new();
2586 $sep->set_visible(1);
2587 $grid->attach($sep, 0, $row, 2, 1);
2588 $row++;
aed81ff0 2589
c7779156 2590 my $hdsize_labeled_widgets = [];
aed81ff0 2591
c7779156 2592 # size compute
c6ed3b24 2593 my $hdsize = 0;
aed81ff0
DM
2594 if ( -b $target_hd) {
2595 $hdsize = int(hd_size ($target_hd) / (1024*1024.0)); # size in GB
c6ed3b24 2596 } elsif ($target_hd) {
aed81ff0
DM
2597 $hdsize = int((-s $target_hd) / (1024*1024*1024.0));
2598 }
2599
2600 my $hdsize_size_adj = Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
2601 my $spinbutton_hdsize = Gtk3::SpinButton->new($hdsize_size_adj, 1, 1);
2602 $spinbutton_hdsize->set_tooltip_text("only use specified size (GB) of the harddisk (rest left unpartitioned)");
c7779156 2603 push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize;
aed81ff0
DM
2604
2605 my $entry_swapsize = Gtk3::Entry->new();
2606 $entry_swapsize->set_tooltip_text("maximum SWAP size (GB)");
2607 $entry_swapsize->signal_connect (key_press_event => \&check_float);
2608 $entry_swapsize->set_text($config_options->{swapsize}) if $config_options->{swapsize};
c7779156 2609 push @$hdsize_labeled_widgets, "swapsize", $entry_swapsize;
aed81ff0
DM
2610
2611 my $entry_maxroot = Gtk3::Entry->new();
2612 $entry_maxroot->set_tooltip_text("maximum size (GB) for LVM root volume");
2613 $entry_maxroot->signal_connect (key_press_event => \&check_float);
2614 $entry_maxroot->set_text($config_options->{maxroot}) if $config_options->{maxroot};
c7779156 2615 push @$hdsize_labeled_widgets, "maxroot", $entry_maxroot;
aed81ff0
DM
2616
2617 my $entry_minfree = Gtk3::Entry->new();
034f75e4 2618 $entry_minfree->set_tooltip_text("minimum free LVM space (GB, required for LVM snapshots)");
aed81ff0
DM
2619 $entry_minfree->signal_connect (key_press_event => \&check_float);
2620 $entry_minfree->set_text($config_options->{minfree}) if $config_options->{minfree};
c7779156 2621 push @$hdsize_labeled_widgets, "minfree", $entry_minfree;
aed81ff0
DM
2622
2623 my $entry_maxvz = Gtk3::Entry->new();
2624 $entry_maxvz->set_tooltip_text("maximum size (GB) for LVM data volume");
2625 $entry_maxvz->signal_connect (key_press_event => \&check_float);
2626 $entry_maxvz->set_text($config_options->{maxvz}) if $config_options->{maxvz};
c7779156
FG
2627 push @$hdsize_labeled_widgets, "maxvz", $entry_maxvz;
2628
2629 my $options_stack = Gtk3::Stack->new();
2630 $options_stack->set_visible(1);
2631 $options_stack->set_hexpand(1);
2632 $options_stack->set_vexpand(1);
2633 $options_stack->add_titled(&$create_raid_disk_grid(), "raiddisk", "Disk Setup");
2634 $options_stack->add_titled(&$create_label_widget_grid($hdsize_labeled_widgets), "hdsize", "Size Options");
2635 $options_stack->add_titled(&$create_raid_advanced_grid("zfs"), "raidzfsadvanced", "Advanced Options");
2636 $options_stack->set_visible_child_name("raiddisk");
2637 my $options_stack_switcher = Gtk3::StackSwitcher->new();
2638 $options_stack_switcher->set_halign('center');
2639 $options_stack_switcher->set_stack($options_stack);
2640 $grid->attach($options_stack_switcher, 0, $row, 2, 1);
2641 $row++;
2642 $grid->attach($options_stack, 0, $row, 2, 1);
c6ed3b24 2643 $row++;
aed81ff0 2644
c7779156
FG
2645 $hdopion_first_setup = 0;
2646
2647 my $switch_view = sub {
2648 my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
2649 my $enable_zfs_opts = $config_options->{filesys} =~ m/zfs/;
c6ed3b24 2650
c7779156
FG
2651 $target_hd_combo->set_visible(!$raid);
2652 $options_stack->get_child_by_name("hdsize")->set_visible(!$raid);
2653 $options_stack->get_child_by_name("raiddisk")->set_visible($raid);
2654 $options_stack_switcher->set_visible($enable_zfs_opts);
2655 $options_stack->get_child_by_name("raidzfsadvanced")->set_visible($enable_zfs_opts);
2656 if ($raid) {
c6ed3b24 2657 $target_hd_label->set_text("Target: $config_options->{filesys} ");
c7779156 2658 $options_stack->set_visible_child_name("raiddisk");
c6ed3b24 2659 } else {
c6ed3b24
DM
2660 $target_hd_label->set_text("Target Harddisk: ");
2661 }
c7779156
FG
2662 my (undef, $pref_width) = $dialog->get_preferred_width();
2663 my (undef, $pref_height) = $dialog->get_preferred_height();
650a9aab 2664 $pref_height = 750 if $pref_height > 750;
c7779156 2665 $dialog->resize($pref_width, $pref_height);
f7b853d1
DM
2666 };
2667
c7779156 2668 &$switch_view();
f7b853d1
DM
2669
2670 $fstypecb->signal_connect (changed => sub {
2671 $config_options->{filesys} = $fstypecb->get_active_text();
c7779156 2672 &$switch_view();
f7b853d1
DM
2673 });
2674
c6ed3b24 2675 $dialog->show();
aed81ff0
DM
2676
2677 $dialog->run();
2678
2679 my $get_float = sub {
2680 my ($entry) = @_;
2681
2682 my $text = $entry->get_text();
2683 return undef if !defined($text);
2684
2685 $text =~ s/^\s+//;
2686 $text =~ s/\s+$//;
2687
2688 return undef if $text !~ m/^\d+(\.\d+)?$/;
2689
2690 return $text;
2691 };
2692
2693 my $tmp;
2694
2695 if (($tmp = &$get_float($spinbutton_hdsize)) && ($tmp != $hdsize)) {
2696 $config_options->{hdsize} = $tmp;
2697 } else {
2698 delete $config_options->{hdsize};
2699 }
2700
2701 if (defined($tmp = &$get_float($entry_swapsize))) {
2702 $config_options->{swapsize} = $tmp;
2703 } else {
2704 delete $config_options->{swapsize};
2705 }
2706
2707 if (defined($tmp = &$get_float($entry_maxroot))) {
2708 $config_options->{maxroot} = $tmp;
2709 } else {
2710 delete $config_options->{maxroot};
2711 }
2712
2713 if (defined($tmp = &$get_float($entry_minfree))) {
2714 $config_options->{minfree} = $tmp;
2715 } else {
2716 delete $config_options->{minfree};
2717 }
2718
2719 if (defined($tmp = &$get_float($entry_maxvz))) {
2720 $config_options->{maxvz} = $tmp;
2721 } else {
2722 delete $config_options->{maxvz};
2723 }
2724
2725 $dialog->destroy();
2726}
2727
121ebc59 2728my $get_raid_devlist = sub {
c6ed3b24
DM
2729
2730 my $dev_name_hash = {};
2731
2732 my $devlist = [];
5f8e86d5 2733 for (my $i = 0; $i < @$hds; $i++) {
c6ed3b24
DM
2734 if (my $hd = $config_options->{"disksel$i"}) {
2735 my ($disk, $devname, $size, $model) = @$hd;
2736 die "device '$devname' is used more than once\n"
2737 if $dev_name_hash->{$devname};
2738 $dev_name_hash->{$devname} = $hd;
2739 push @$devlist, $hd;
2740 }
2741 }
2742
121ebc59
DM
2743 return $devlist;
2744};
2745
14aacec8
FG
2746sub zfs_mirror_size_check {
2747 my ($expected, $actual) = @_;
2748
2749 die "mirrored disks must have same size\n"
2750 if abs($expected - $actual) > $expected / 10;
2751}
2752
121ebc59
DM
2753sub get_zfs_raid_setup {
2754
2755 my $filesys = $config_options->{filesys};
2756
2757 my $devlist = &$get_raid_devlist();
2758
224bb7b0 2759 my $diskcount = scalar(@$devlist);
0cfa502c 2760 die "$filesys needs at least one device\n" if $diskcount < 1;
c6ed3b24 2761
121ebc59
DM
2762 my $bootdevlist = [];
2763
c6ed3b24
DM
2764 my $cmd= '';
2765 if ($filesys eq 'zfs (RAID0)') {
2766 push @$bootdevlist, @$devlist[0];
2767 foreach my $hd (@$devlist) {
2768 $cmd .= " @$hd[1]";
2769 }
2770 } elsif ($filesys eq 'zfs (RAID1)') {
0cfa502c 2771 die "zfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
c6ed3b24 2772 $cmd .= ' mirror ';
269c66a6 2773 my $hd = @$devlist[0];
14aacec8 2774 my $expected_size = @$hd[2]; # all disks need approximately same size
269c66a6 2775 foreach $hd (@$devlist) {
14aacec8 2776 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
2777 $cmd .= " @$hd[1]";
2778 push @$bootdevlist, $hd;
2779 }
2780 } elsif ($filesys eq 'zfs (RAID10)') {
0cfa502c 2781 die "zfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
b8f4f0f9 2782 die "zfs (RAID10) needs an even number of devices\n" if $diskcount & 1;
c6ed3b24
DM
2783
2784 push @$bootdevlist, @$devlist[0], @$devlist[1];
2785
224bb7b0 2786 for (my $i = 0; $i < $diskcount; $i+=2) {
c6ed3b24
DM
2787 my $hd1 = @$devlist[$i];
2788 my $hd2 = @$devlist[$i+1];
14aacec8 2789 zfs_mirror_size_check(@$hd1[2], @$hd2[2]); # pairs need approximately same size
c6ed3b24
DM
2790 $cmd .= ' mirror ' . @$hd1[1] . ' ' . @$hd2[1];
2791 }
2792
2793 } elsif ($filesys =~ m/^zfs \(RAIDZ-([123])\)$/) {
2794 my $level = $1;
2795 my $mindisks = 2 + $level;
0cfa502c 2796 die "zfs (RAIDZ-$level) needs at least $mindisks devices\n" if scalar(@$devlist) < $mindisks;
269c66a6 2797 my $hd = @$devlist[0];
14aacec8 2798 my $expected_size = @$hd[2]; # all disks need approximately same size
097ecf8f 2799 $cmd .= " raidz$level";
269c66a6 2800 foreach $hd (@$devlist) {
14aacec8 2801 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
2802 $cmd .= " @$hd[1]";
2803 push @$bootdevlist, $hd;
2804 }
2805 } else {
2806 die "unknown zfs mode '$filesys'\n";
2807 }
2808
2809 return ($devlist, $bootdevlist, $cmd);
2810}
2811
121ebc59
DM
2812sub get_btrfs_raid_setup {
2813
2814 my $filesys = $config_options->{filesys};
2815
2816 my $devlist = &$get_raid_devlist();
2817
2818 my $diskcount = scalar(@$devlist);
0cfa502c 2819 die "$filesys needs at least one device\n" if $diskcount < 1;
121ebc59
DM
2820
2821 my $mode;
2822
2823 if ($diskcount == 1) {
2824 $mode = 'single';
2825 } else {
2826 if ($filesys eq 'btrfs (RAID0)') {
2827 $mode = 'raid0';
2828 } elsif ($filesys eq 'btrfs (RAID1)') {
0cfa502c 2829 die "btrfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
121ebc59
DM
2830 $mode = 'raid1';
2831 } elsif ($filesys eq 'btrfs (RAID10)') {
0cfa502c 2832 die "btrfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
121ebc59
DM
2833 $mode = 'raid10';
2834 } else {
9d69f3d3 2835 die "unknown btrfs mode '$filesys'\n";
121ebc59
DM
2836 }
2837 }
2838
2839 return ($devlist, $mode);
2840}
2841
89a12446
DM
2842sub create_hdsel_view {
2843
2844 cleanup_view ();
2845
7becc472 2846 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 2847 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 2848 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 2849 $vbox->pack_start ($hbox, 0, 0, 10);
968fa90b 2850
89a12446
DM
2851 my ($disk, $devname, $size, $model) = @{@$hds[0]};
2852 $target_hd = $devname;
89a12446 2853
c6ed3b24
DM
2854 $target_hd_label = Gtk3::Label->new ("Target Harddisk: ");
2855 $hbox->pack_start ($target_hd_label, 0, 0, 0);
89a12446 2856
bcbfab6b 2857 $target_hd_combo = Gtk3::ComboBoxText->new();
89a12446 2858
1aa5bd02
DM
2859 foreach my $hd (@$hds) {
2860 ($disk, $devname, $size, $model) = @$hd;
c6ed3b24 2861 $target_hd_combo->append_text (get_device_desc ($devname, $size, $model));
1aa5bd02 2862 }
89a12446 2863
c6ed3b24
DM
2864 $target_hd_combo->set_active (0);
2865 $target_hd_combo->signal_connect (changed => sub {
1aa5bd02
DM
2866 $a = shift->get_active;
2867 my ($disk, $devname) = @{@$hds[$a]};
2868 $target_hd = $devname;
1aa5bd02 2869 });
c6ed3b24
DM
2870
2871 $hbox->pack_start ($target_hd_combo, 0, 0, 10);
aed81ff0
DM
2872
2873 my $options = Gtk3::Button->new ('_Options');
2874 $options->signal_connect (clicked => \&create_hdoption_view);
2875 $hbox->pack_start ($options, 0, 0, 0);
2876
89a12446
DM
2877
2878 $inbox->show_all;
2879
2880 display_html ("page1.htm");
c6ed3b24
DM
2881
2882 set_next (undef, sub {
2883
2884 if ($config_options->{filesys} =~ m/zfs/) {
2885 eval { get_zfs_raid_setup(); };
2886 if (my $err = $@) {
2887 display_message ("Warning: $err\n" .
269c66a6 2888 "Please fix ZFS setup first.");
c6ed3b24
DM
2889 } else {
2890 create_country_view();
2891 }
121ebc59
DM
2892 } elsif ($config_options->{filesys} =~ m/btrfs/) {
2893 eval { get_btrfs_raid_setup(); };
2894 if (my $err = $@) {
2895 display_message ("Warning: $err\n" .
2896 "Please fix BTRFS setup first.");
2897 } else {
2898 create_country_view();
2899 }
c6ed3b24
DM
2900 } else {
2901 create_country_view();
2902 }
2903 });
89a12446
DM
2904}
2905
2906sub create_extract_view {
2907
89a12446
DM
2908 cleanup_view ();
2909
2910 display_html ("extract1-license.htm");
2911 $next->set_sensitive (0);
2912
7becc472 2913 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 2914 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 2915 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 2916 $vbox->pack_start ($hbox, 0, 0, 10);
89a12446 2917
7becc472 2918 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446
DM
2919 $hbox->pack_start ($vbox2, 0, 0, 0);
2920
7becc472 2921 $progress_status = Gtk3::Label->new ('');
89a12446 2922 $vbox2->pack_start ($progress_status, 1, 1, 0);
968fa90b 2923
7becc472 2924 $progress = Gtk3::ProgressBar->new;
45feca6f 2925 $progress->set_show_text(1);
7becc472 2926 $progress->set_size_request (600, -1);
89a12446
DM
2927
2928 $vbox2->pack_start ($progress, 0, 0, 0);
2929
2930 $inbox->show_all;
2931
2932 my $tdir = $opt_testmode ? "target" : "/target";
2933 mkdir $tdir;
97980bf2 2934 my $base = "${proxmox_cddir}/$setup->{product}-base.squashfs";
89a12446
DM
2935
2936 eval { extract_data ($base, $tdir); };
2937 my $err = $@;
2938
2939 $next->set_sensitive (1);
2940
2941 set_next ("_Reboot", sub { exit (0); } );
2942
296cf41f
DM
2943 if ($err) {
2944 display_html ("fail.htm");
2945 display_error ($err);
2946 } else {
2947 cleanup_view ();
2948 display_html ("success.htm");
2949 }
89a12446
DM
2950}
2951
89a12446
DM
2952sub create_intro_view {
2953
2954 cleanup_view ();
2955
7fff0d85
FG
2956 eval {
2957 my $cpuinfo = file_get_contents('/proc/cpuinfo');
2958 if ($cpuinfo && !($cpuinfo =~ /^flags\s*:.*(vmx|svm)/m)) {
2959 display_error("No support for KVM virtualisation detected.\n\n" .
2960 "Check BIOS settings for Intel VT / AMD-V / SVM.")
2961 }
2962 };
2963
89a12446
DM
2964 display_html ("license.htm");
2965
2966 set_next ("I a_gree", \&create_hdsel_view);
2967}
2968
2969$ipconf = get_ip_config ();
2970
9d1f1ee3 2971$country = detect_country() if $ipconf->{default} || $opt_testmode;
89a12446
DM
2972
2973# read country, kmap and timezone infos
2974$cmap = read_cmap ();
2975
9d1f1ee3
FG
2976if (!defined($cmap->{country}->{$country})) {
2977 print $logfd "ignoring detected country '$country', invalid or unknown\n";
2978 $country = undef;
2979}
2980
89a12446
DM
2981create_main_window ();
2982
ff2ce71c
FG
2983my $initial_error = 0;
2984
89a12446
DM
2985if (!defined ($hds) || (scalar (@$hds) <= 0)) {
2986 print "no hardisks found\n";
ff2ce71c 2987 $initial_error = 1;
89a12446
DM
2988 display_html ("nohds.htm");
2989 set_next ("Reboot", sub { exit (0); } );
2990} else {
89a12446
DM
2991 foreach my $hd (@$hds) {
2992 my ($disk, $devname) = @$hd;
2993 next if $devname =~ m|^/dev/md\d+$|;
2994 print "found Disk$disk N:$devname\n";
2995 }
89a12446
DM
2996}
2997
72836708
FG
2998if (!$initial_error && (scalar keys %{ $ipconf->{ifaces} } == 0)) {
2999 print "no network interfaces found\n";
3000 $initial_error = 1;
3001 display_html ("nonics.htm");
3002 set_next ("Reboot", sub { exit (0); } );
3003}
3004
ff2ce71c
FG
3005create_intro_view () if !$initial_error;
3006
7becc472 3007Gtk3->main;
89a12446
DM
3008
3009exit 0;