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