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