]> git.proxmox.com Git - pve-installer.git/blame - proxinstall
pmg: write admin email to /etc/pmg/pmg.conf
[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 1602 if ($setup->{product} eq 'pmg') {
038552a1 1603 # save admin email
fe172016
DM
1604 write_config ("section: admin\n\temail ${mailto}\n",
1605 "$targetdir/etc/pmg/pmg.conf");
038552a1
DM
1606
1607 } elsif ($setup->{product} eq 'pve') {
7053f98b 1608
8acc47b5 1609 # create pmxcfs DB
7053f98b 1610
8acc47b5
DM
1611 my $tmpdir = "$targetdir/tmp/pve";
1612 mkdir $tmpdir;
7053f98b 1613
8acc47b5
DM
1614 # write vnc keymap to datacenter.cfg
1615 my $vnckmap = $cmap->{kmap}->{$keymap}->{kvm} || 'en-us';
1616 write_config ("keyboard: $vnckmap\n",
1617 "$tmpdir/datacenter.cfg");
968fa90b 1618
8acc47b5
DM
1619 # save admin email
1620 write_config ("user:root\@pam:1:0:::${mailto}::\n",
1621 "$tmpdir/user.cfg");
5fd81672 1622
8acc47b5
DM
1623 # write storage.cfg
1624 my $strorage_cfg_fn = "$tmpdir/storage.cfg";
1625 if ($use_zfs) {
1626 write_config ($storage_cfg_zfs, $strorage_cfg_fn);
1627 } elsif ($use_btrfs) {
1628 write_config ($storage_cfg_btrfs, $strorage_cfg_fn);
1629 } else {
1630 write_config ($storage_cfg_lvmthin, $strorage_cfg_fn);
1631 }
7053f98b 1632
8acc47b5
DM
1633 run_command("chroot $targetdir /usr/bin/create_pmxcfs_db /tmp/pve /var/lib/pve-cluster/config.db");
1634
1635 syscmd ("rm -rf $tmpdir");
1636 }
89a12446
DM
1637 };
1638
1639 my $err = $@;
1640
1641 update_progress (1, 0, 1, "");
1642
1643 print $err if $err;
1644
1645 if ($opt_testmode) {
121ebc59
DM
1646 my $elapsed = Time::HiRes::tv_interval($starttime);
1647 print "Elapsed extract time: $elapsed\n";
1648
ef5f4f86 1649 syscmd ("chroot $targetdir /usr/bin/dpkg-query -W --showformat='\${package}\n'> final.pkglist");
89a12446
DM
1650 }
1651
89a12446
DM
1652 syscmd ("umount $targetdir/tmp");
1653 syscmd ("umount $targetdir/proc");
1654 syscmd ("umount $targetdir/sys");
6fbd1fb1
DM
1655
1656 if ($use_zfs) {
1657 syscmd ("zfs umount -a") == 0 ||
1658 die "unable to unmount zfs\n";
1659 } else {
1660 syscmd ("umount -d $targetdir");
1661 }
89a12446 1662
5c06ced5 1663 if (!$err && $use_zfs) {
481671c3
DM
1664 syscmd ("zfs set sync=standard $zfspoolname") == 0 ||
1665 die "unable to set zfs properties\n";
1666
5772392c 1667 syscmd ("zfs set mountpoint=/ $zfspoolname/ROOT/$zfsrootvolname") == 0 ||
5c06ced5
DM
1668 die "zfs set mountpoint failed\n";
1669
5772392c 1670 syscmd ("zpool set bootfs=$zfspoolname/ROOT/$zfsrootvolname $zfspoolname") == 0 ||
5c06ced5 1671 die "zfs set bootfs failed\n";
016679a2 1672 syscmd ("zpool export $zfspoolname");
5c06ced5
DM
1673 }
1674
89a12446
DM
1675 die $err if $err;
1676}
1677
1678sub display_html {
1679 my ($filename) = @_;
1680
a04ac176 1681 my $path = "${proxmox_libdir}/html/$filename";
c437cef5 1682
8a50920c
DM
1683 my $url = "file://$path";
1684
1685 my $data = file_get_contents($path);
1686
1687 if ($filename eq 'license.htm') {
3c866639
DM
1688 my $license = decode('utf8', file_get_contents("${proxmox_cddir}/EULA"));
1689 my $title = "END USER LICENSE AGREEMENT (EULA)";
f91c161b 1690 $data =~ s/__LICENSE__/$license/;
8a50920c
DM
1691 $data =~ s/__LICENSE_TITLE__/$title/;
1692 }
1693
1694 $htmlview->load_html_string($data, $url);
7becc472
DM
1695}
1696
89a12446
DM
1697sub set_next {
1698 my ($text, $fctn) = @_;
1699
1700 $next_fctn = $fctn;
1701 $text = "_Next" if !$text;
1702 $next->set_label ($text);
968fa90b 1703
89a12446
DM
1704 $next->grab_focus ();
1705}
89a12446
DM
1706
1707sub create_main_window {
1708
7becc472 1709 $window = Gtk3::Window->new ();
89a12446 1710 $window->set_default_size (1024, 768);
84761f93 1711 $window->set_has_resize_grip(0);
89a12446
DM
1712 $window->set_decorated (0) if !$opt_testmode;
1713
7becc472 1714 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 1715
a04ac176 1716 my $image = Gtk3::Image->new_from_file ("${proxmox_libdir}/proxlogo.png");
89a12446
DM
1717 $vbox->pack_start ($image, 0, 0, 0);
1718
7becc472 1719 my $hbox = Gtk3::HBox->new (0, 0);
89a12446
DM
1720 $vbox->pack_start ($hbox, 1, 1, 0);
1721
7becc472
DM
1722 # my $f1 = Gtk3::Frame->new ('test');
1723 # $f1->set_shadow_type ('none');
1724 # $hbox->pack_start ($f1, 1, 1, 0);
89a12446 1725
7becc472 1726 my $sep1 = Gtk3::HSeparator->new;
89a12446
DM
1727 $vbox->pack_start ($sep1, 0, 0, 0);
1728
7becc472 1729 $cmdbox = Gtk3::HBox->new ();
89a12446
DM
1730 $vbox->pack_start ($cmdbox, 0, 0, 10);
1731
7becc472 1732 $next = Gtk3::Button->new ('_Next');
89a12446
DM
1733 $next->signal_connect (clicked => sub { &$next_fctn (); });
1734 $cmdbox->pack_end ($next, 0, 0, 10);
7becc472
DM
1735 my $abort = Gtk3::Button->new ('_Abort');
1736 $abort->set_can_focus (0);
89a12446
DM
1737 $cmdbox->pack_start ($abort, 0, 0, 10);
1738 $abort->signal_connect (clicked => sub { exit (-1); });
1739
7becc472
DM
1740 my $vbox2 = Gtk3::VBox->new (0, 0);
1741 $hbox->add ($vbox2);
89a12446 1742
7becc472
DM
1743 $htmlview = Gtk3::WebKit::WebView->new();
1744 my $scrolls = Gtk3::ScrolledWindow->new();
1745 $scrolls->add($htmlview);
1746
1747 my $hbox2 = Gtk3::HBox->new (0, 0);
1748 $hbox2->pack_start ($scrolls, 1, 1, 0);
89a12446
DM
1749
1750 $vbox2->pack_start ($hbox2, 1, 1, 0);
1751
7becc472 1752 my $vbox3 = Gtk3::VBox->new (0, 0);
89a12446
DM
1753 $vbox2->pack_start ($vbox3, 0, 0, 0);
1754
7becc472 1755 my $sep2 = Gtk3::HSeparator->new;
89a12446
DM
1756 $vbox3->pack_start ($sep2, 0, 0, 0);
1757
7becc472 1758 $inbox = Gtk3::HBox->new (0, 0);
89a12446
DM
1759 $vbox3->pack_start ($inbox, 0, 0, 0);
1760
1761 $window->add ($vbox);
1762
1763 $window->show_all;
1764 $window->realize ();
1765}
1766
d2120e51
DM
1767sub cleanup_view {
1768 $inbox->foreach(sub {
1769 my $child = shift;
1770 $inbox->remove ($child);
1771 });
89a12446
DM
1772}
1773
aed81ff0
DM
1774# fixme: newer GTK3 has special properties to handle numbers with Entry
1775# only allow floating point numbers with Gtk3::Entry
e73c5fcf 1776
aed81ff0
DM
1777sub check_float {
1778 my ($entry, $event) = @_;
1779
e73c5fcf
FG
1780 return check_number($entry, $event, 1);
1781}
1782
1783sub check_int {
1784 my ($entry, $event) = @_;
1785
1786 return check_number($entry, $event, 0);
1787}
1788
1789sub check_number {
1790 my ($entry, $event, $float) = @_;
aed81ff0
DM
1791
1792 my $val = $event->get_keyval;
1793
e73c5fcf 1794 if (($float && $val == ord '.') ||
aed81ff0
DM
1795 $val == Gtk3::Gdk::KEY_ISO_Left_Tab ||
1796 $val == Gtk3::Gdk::KEY_Shift_L ||
1797 $val == Gtk3::Gdk::KEY_Tab ||
1798 $val == Gtk3::Gdk::KEY_Left ||
1799 $val == Gtk3::Gdk::KEY_Right ||
1800 $val == Gtk3::Gdk::KEY_BackSpace ||
1801 $val == Gtk3::Gdk::KEY_Delete ||
1802 ($val >= ord '0' && $val <= ord '9') ||
1803 ($val >= Gtk3::Gdk::KEY_KP_0 &&
1804 $val <= Gtk3::Gdk::KEY_KP_9)) {
1805 return undef;
1806 }
1807
1808 return 1;
1809}
1810
d2120e51 1811sub create_text_input {
89a12446
DM
1812 my ($default, $text) = @_;
1813
7becc472 1814 my $hbox = Gtk3::HBox->new (0, 0);
89a12446 1815
7becc472 1816 my $label = Gtk3::Label->new ($text);
89a12446
DM
1817 $label->set_size_request (150, -1);
1818 $label->set_alignment (1, 0.5);
1819 $hbox->pack_start ($label, 0, 0, 10);
7becc472 1820 my $e1 = Gtk3::Entry->new ();
89a12446
DM
1821 $e1->set_width_chars (30);
1822 $hbox->pack_start ($e1, 0, 0, 0);
1823 $e1->set_text ($default);
1824
1825 return ($hbox, $e1);
1826}
1827
89a12446
DM
1828sub get_ip_config {
1829
fe44bd92
FG
1830 my $ifaces = {};
1831 my $default;
89a12446 1832
fe44bd92
FG
1833 my $links = `ip -o l`;
1834 foreach my $l (split /\n/,$links) {
1835 my ($index, $name, $flags, $state, $mac) = $l =~ m/^(\d+):\s+(\S+):\s+<(\S+)>.*\s+state\s+(\S+)\s+.*\s+link\/ether\s+(\S+)\s+/;
1836 next if !$name || $name eq 'lo';
89a12446 1837
fe44bd92
FG
1838 my $driver = readlink "/sys/class/net/$name/device/driver" || 'unknown';
1839 $driver =~ s!^.*/!!;
1840
1841 $ifaces->{"$index"} = {
1842 name => $name,
1843 driver => $driver,
1844 flags => $flags,
1845 state => $state,
1846 mac => $mac,
1847 };
1848
1849 my $addresses = `ip -o a s $name`;
1850 foreach my $a (split /\n/,$addresses) {
1851 my ($family, $ip, $prefix) = $a =~ m/^\Q$index\E:\s+\Q$name\E\s+(inet|inet6)\s+($IPRE)\/(\d+)\s+/;
1852 next if !$ip;
32b6fbcf 1853 next if $a =~ /scope\s+link/; # ignore link local
fe44bd92
FG
1854
1855 my $mask = $prefix;
1856
1857 if ($family eq 'inet') {
1858 next if !$ip =~ /$IPV4RE/;
1859 next if $prefix < 8 || $prefix > 32;
1860 $mask = @$ipv4_reverse_mask[$prefix];
1861 } else {
1862 next if !$ip =~ /$IPV6RE/;
1863 }
1864
1865 $default = $index if !$default;
1866
1867 $ifaces->{"$index"}->{"$family"} = {
1868 mask => $mask,
1869 addr => $ip,
1870 };
1871 }
1872 }
1873
1874
1875 my $route = `ip route`;
1876 my ($gateway) = $route =~ m/^default\s+via\s+(\S+)\s+/m;
89a12446
DM
1877
1878 my $resolvconf = `cat /etc/resolv.conf`;
1879 my ($dnsserver) = $resolvconf =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/m;
713790a4 1880 my ($domain) = $resolvconf =~ m/^domain\s+(\S+)$/m;
89a12446
DM
1881
1882 return {
fe44bd92
FG
1883 default => $default,
1884 ifaces => $ifaces,
89a12446
DM
1885 gateway => $gateway,
1886 dnsserver => $dnsserver,
713790a4 1887 domain => $domain,
89a12446
DM
1888 }
1889}
1890
1891sub display_message {
1892 my ($msg) = @_;
1893
7becc472 1894 my $dialog = Gtk3::MessageDialog->new ($window, 'modal',
89a12446
DM
1895 'info', 'ok', $msg);
1896 $dialog->run();
1897 $dialog->destroy();
1898}
1899
1900sub display_error {
1901 my ($msg) = @_;
1902
7becc472 1903 my $dialog = Gtk3::MessageDialog->new ($window, 'modal',
89a12446
DM
1904 'error', 'ok', $msg);
1905 $dialog->run();
1906 $dialog->destroy();
1907}
1908
fe44bd92
FG
1909my $ipconf_first_view = 1;
1910
89a12446
DM
1911sub create_ipconf_view {
1912
1913 cleanup_view ();
1914 display_html ("ipconf.htm");
1915
7becc472 1916 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 1917 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 1918 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 1919 $vbox->pack_start ($hbox, 0, 0, 10);
7becc472 1920 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446
DM
1921 $hbox->add ($vbox2);
1922
fe44bd92
FG
1923 my $ipbox;
1924 ($ipbox, $ipconf_entry_addr) =
1925 create_text_input ("192.168.100.2", 'IP Address:');
1926
1927 my $maskbox;
1928 ($maskbox, $ipconf_entry_mask) =
1929 create_text_input ("255.255.255.0", 'Netmask:');
1930
1931 my $device_cb = Gtk3::ComboBoxText->new();
1932 $device_cb->set_active(0);
1933 $device_cb->set_visible(1);
1934
1935 my $get_device_desc = sub {
1936 my $iface = shift;
1937 return "$iface->{name} - $iface->{mac} ($iface->{driver})";
1938 };
1939
1940 my $device_active_map = {};
5b6ba737
FG
1941
1942 my $device_change_handler = sub {
1943 my $current = shift;
1944 $ipconf->{selected} = $device_active_map->{$current->get_active()};
1945 my $iface = $ipconf->{ifaces}->{$ipconf->{selected}};
1946 $ipconf_entry_addr->set_text($iface->{inet}->{addr} || $iface->{inet6}->{addr})
1947 if $iface->{inet}->{addr} || $iface->{inet6}->{addr};
1948 $ipconf_entry_mask->set_text($iface->{inet}->{mask} || $iface->{inet6}->{mask})
1949 if $iface->{inet}->{mask} || $iface->{inet6}->{mask};
1950 };
1951
fe44bd92
FG
1952 my $i = 0;
1953 foreach my $index (sort keys %{$ipconf->{ifaces}}) {
1954 $device_cb->append_text(&$get_device_desc($ipconf->{ifaces}->{$index}));
1955 $device_active_map->{$i} = $index;
1956 if ($ipconf_first_view && $index == $ipconf->{default}) {
1957 $device_cb->set_active($i);
5b6ba737 1958 &$device_change_handler($device_cb);
fe44bd92
FG
1959 $ipconf_first_view = 0;
1960 }
5b6ba737 1961 $device_cb->signal_connect ('changed' => $device_change_handler);
fe44bd92
FG
1962 $i++;
1963 }
1964
5b6ba737
FG
1965 $device_cb->set_active(0)
1966 if !($ipconf->{selected});
1967
fe44bd92
FG
1968 my $devicebox = Gtk3::HBox->new (0, 0);
1969 my $label = Gtk3::Label->new ("Management Interface:");
1970 $label->set_size_request (150, -1);
1971 $label->set_alignment (1, 0.5);
1972 $devicebox->pack_start ($label, 0, 0, 10);
1973 $devicebox->pack_start ($device_cb, 0, 0, 0);
1974
1975 $vbox2->pack_start ($devicebox, 0, 0, 2);
968fa90b 1976
232309b6
DM
1977 my $hn = $ipconf->{domain} ?
1978 "$setup->{product}.$ipconf->{domain}" : "$setup->{product}.example.invalid";
713790a4 1979
968fa90b 1980 my ($hostbox, $hostentry) =
d2120e51 1981 create_text_input ($hn, 'Hostname (FQDN):');
89a12446
DM
1982 $vbox2->pack_start ($hostbox, 0, 0, 2);
1983
89a12446
DM
1984 $vbox2->pack_start ($ipbox, 0, 0, 2);
1985
89a12446
DM
1986 $vbox2->pack_start ($maskbox, 0, 0, 2);
1987
1988 $gateway = $ipconf->{gateway} || '192.168.100.1';
1989
1990 my $gwbox;
d2120e51
DM
1991 ($gwbox, $ipconf_entry_gw) =
1992 create_text_input ($gateway, 'Gateway:');
89a12446 1993
53986d77 1994 $vbox2->pack_start ($gwbox, 0, 0, 2);
89a12446
DM
1995
1996 $dnsserver = $ipconf->{dnsserver} || $gateway;
1997
1998 my $dnsbox;
d2120e51
DM
1999 ($dnsbox, $ipconf_entry_dns) =
2000 create_text_input ($dnsserver, 'DNS Server:');
89a12446
DM
2001
2002 $vbox2->pack_start ($dnsbox, 0, 0, 0);
2003
2004 $inbox->show_all;
968fa90b 2005 set_next (undef, sub {
d2120e51
DM
2006
2007 # verify hostname
2008
89a12446 2009 my $text = $hostentry->get_text();
968fa90b 2010
89a12446
DM
2011 $text =~ s/^\s+//;
2012 $text =~ s/\s+$//;
2013
ac3757a9 2014 my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
968fa90b 2015
24973868
WB
2016 # Debian does not support purely numeric hostnames
2017 if ($text && $text =~ /^[0-9]+(?:\.|$)/) {
2018 display_message("Purely numeric hostnames are not allowed.");
2019 $hostentry->grab_focus();
2020 return;
2021 }
2022
a39bc1f2 2023 if ($text && $text =~ m/^(${namere}\.)*${namere}$/ && $text !~ m/.example.invalid$/ &&
89a12446
DM
2024 $text =~ m/^([^\.]+)\.(\S+)$/) {
2025 $hostname = $1;
2026 $domain = $2;
d2120e51
DM
2027 } else {
2028 display_message ("Hostname does not look like a fully qualified domain name.");
2029 $hostentry->grab_focus();
89a12446
DM
2030 return;
2031 }
d2120e51
DM
2032
2033 # verify ip address
2034
2035 $text = $ipconf_entry_addr->get_text();
2036 $text =~ s/^\s+//;
2037 $text =~ s/\s+$//;
2038 if ($text =~ m!^($IPV4RE)$!) {
2039 $ipaddress = $text;
b6200603
DM
2040 $ipversion = 4;
2041 } elsif ($text =~ m!^($IPV6RE)$!) {
2042 $ipaddress = $text;
2043 $ipversion = 6;
d2120e51
DM
2044 } else {
2045 display_message ("IP address is not valid.");
2046 $ipconf_entry_addr->grab_focus();
2047 return;
2048 }
2049
2050 $text = $ipconf_entry_mask->get_text();
2051 $text =~ s/^\s+//;
2052 $text =~ s/\s+$//;
b6200603
DM
2053 if (($ipversion == 6) && ($text =~ m/^(\d+)$/) && ($1 >= 8) && ($1 <= 126)) {
2054 $netmask = $text;
2055 } elsif (($ipversion == 4) && defined($ipv4_mask_hash->{$text})) {
d2120e51
DM
2056 $netmask = $text;
2057 } else {
2058 display_message ("Netmask is not valid.");
2059 $ipconf_entry_mask->grab_focus();
2060 return;
2061 }
2062
2063 $text = $ipconf_entry_gw->get_text();
2064 $text =~ s/^\s+//;
2065 $text =~ s/\s+$//;
b6200603
DM
2066 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2067 $gateway = $text;
2068 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2069 $gateway = $text;
2070 } else {
2071 display_message ("Gateway is not valid.");
2072 $ipconf_entry_gw->grab_focus();
2073 return;
2074 }
2075
2076 $text = $ipconf_entry_dns->get_text();
2077 $text =~ s/^\s+//;
2078 $text =~ s/\s+$//;
b6200603
DM
2079 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2080 $dnsserver = $text;
2081 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2082 $dnsserver = $text;
2083 } else {
2084 display_message ("DNS server is not valid.");
2085 $ipconf_entry_dns->grab_focus();
2086 return;
2087 }
2088
2089 #print "TEST $ipaddress $netmask $gateway $dnsserver\n";
2090
2091 create_extract_view ();
89a12446
DM
2092 });
2093
2094 $hostentry->grab_focus();
2095}
2096
2097sub get_device_desc {
2098 my ($devname, $size, $model) = @_;
2099
d2120e51 2100 if ($size && ($size > 0)) {
1bd457bb 2101 $size = int($size/2048); # size in MB, from 512B "sectors"
89a12446 2102
d2120e51 2103 my $text = "$devname (";
89a12446
DM
2104 if ($size >= 1024) {
2105 $size = int($size/1024); # size in GB
d2120e51 2106 $text .= "${size}GB";
89a12446 2107 } else {
d2120e51 2108 $text .= "${size}MB";
89a12446
DM
2109 }
2110
d2120e51
DM
2111 $text .= ", $model" if $model;
2112 $text .= ")";
2113
89a12446
DM
2114 } else {
2115 return $devname;
2116 }
2117}
2118
2119sub update_layout {
2120 my ($cb, $kmap) = @_;
2121
2122 my $ind;
2123 my $def;
2124 my $i = 0;
2125 my $kmaphash = $cmap->{kmaphash};
2126 foreach my $layout (sort keys %$kmaphash) {
2127 $def = $i if $kmaphash->{$layout} eq 'en-us';
2128 $ind = $i if $kmap && $kmaphash->{$layout} eq $kmap;
2129 $i++;
2130 }
2131
2132 $cb->set_active ($ind || $def || 0);
2133}
2134
2135my $lastzonecb;
2136sub update_zonelist {
2137 my ($box, $cc) = @_;
2138
2139 my $cczones = $cmap->{cczones};
2140 my $zones = $cmap->{zones};
2141
2142 my $sel;
2143 if ($lastzonecb) {
2144 $sel = $lastzonecb->get_active_text();
2145 $box->remove ($lastzonecb);
2146 } else {
2147 $sel = $timezone; # used once to select default
2148 }
2149
bcbfab6b 2150 my $cb = $lastzonecb = Gtk3::ComboBoxText->new();
89a12446
DM
2151 $cb->set_size_request (200, -1);
2152
2153 $cb->signal_connect ('changed' => sub {
2154 $timezone = $cb->get_active_text();
2155 });
2156
2157 my @za;
2158 if ($cc && defined ($cczones->{$cc})) {
2159 @za = keys %{$cczones->{$cc}};
2160 } else {
2161 @za = keys %$zones;
2162 }
2163 my $ind;
2164 my $i = 0;
2165 foreach my $zone (sort @za) {
2166 $ind = $i if $sel && $zone eq $sel;
2167 $cb->append_text ($zone);
2168 $i++;
2169 }
2170
2171 $cb->set_active ($ind || 0);
2172
2173 $cb->show;
2174 $box->pack_start ($cb, 0, 0, 0);
2175}
2176
2177sub create_password_view {
2178
2179 cleanup_view ();
2180
7becc472 2181 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446 2182 $inbox->pack_start ($vbox2, 1, 0, 0);
7becc472 2183 my $vbox = Gtk3::VBox->new (0, 0);
53986d77 2184 $vbox2->pack_start ($vbox, 0, 0, 10);
89a12446 2185
7becc472
DM
2186 my $hbox1 = Gtk3::HBox->new (0, 0);
2187 my $label = Gtk3::Label->new ("Password");
89a12446
DM
2188 $label->set_size_request (150, -1);
2189 $label->set_alignment (1, 0.5);
2190 $hbox1->pack_start ($label, 0, 0, 10);
7becc472 2191 my $pwe1 = Gtk3::Entry->new ();
89a12446
DM
2192 $pwe1->set_visibility (0);
2193 $pwe1->set_size_request (200, -1);
2194 $hbox1->pack_start ($pwe1, 0, 0, 0);
2195
7becc472
DM
2196 my $hbox2 = Gtk3::HBox->new (0, 0);
2197 $label = Gtk3::Label->new ("Confirm");
89a12446
DM
2198 $label->set_size_request (150, -1);
2199 $label->set_alignment (1, 0.5);
2200 $hbox2->pack_start ($label, 0, 0, 10);
7becc472 2201 my $pwe2 = Gtk3::Entry->new ();
89a12446
DM
2202 $pwe2->set_visibility (0);
2203 $pwe2->set_size_request (200, -1);
2204 $hbox2->pack_start ($pwe2, 0, 0, 0);
2205
7becc472
DM
2206 my $hbox3 = Gtk3::HBox->new (0, 0);
2207 $label = Gtk3::Label->new ("E-Mail");
89a12446
DM
2208 $label->set_size_request (150, -1);
2209 $label->set_alignment (1, 0.5);
2210 $hbox3->pack_start ($label, 0, 0, 10);
7becc472 2211 my $eme = Gtk3::Entry->new ();
89a12446 2212 $eme->set_size_request (200, -1);
a39bc1f2 2213 $eme->set_text('mail@example.invalid');
89a12446
DM
2214 $hbox3->pack_start ($eme, 0, 0, 0);
2215
2216
2217 $vbox->pack_start ($hbox1, 0, 0, 5);
2218 $vbox->pack_start ($hbox2, 0, 0, 5);
2219 $vbox->pack_start ($hbox3, 0, 0, 15);
2220
2221 $inbox->show_all;
2222
2223 display_html ("passwd.htm");
2224
2225 set_next (undef, sub {
2226
2227 my $t1 = $pwe1->get_text;
2228 my $t2 = $pwe2->get_text;
2229
2230 if (length ($t1) < 5) {
2231 display_message ("Password is too short.");
2232 $pwe1->grab_focus();
2233 return;
2234 }
2235
2236 if ($t1 ne $t2) {
2237 display_message ("Password does not match.");
2238 $pwe1->grab_focus();
2239 return;
2240 }
2241
2242 my $t3 = $eme->get_text;
2243 if ($t3 !~ m/^\S+\@\S+\.\S+$/) {
034f75e4 2244 display_message ("E-Mail does not look like a valid address" .
89a12446
DM
2245 " (user\@domain.tld)");
2246 $eme->grab_focus();
2247 return;
a39bc1f2 2248 }
89a12446 2249
a39bc1f2
FG
2250 if ($t3 eq 'mail@example.invalid') {
2251 display_message ("Please enter a valid E-Mail address");
2252 $eme->grab_focus();
2253 return;
89a12446
DM
2254 }
2255
2256 $password = $t1;
2257 $mailto = $t3;
2258
2259 create_ipconf_view();
2260 });
2261
2262 $pwe1->grab_focus();
2263
2264}
2265
2266sub create_country_view {
2267
2268 cleanup_view ();
2269
2270 my $countryhash = $cmap->{countryhash};
2271 my $ctr = $cmap->{country};
2272
7becc472 2273 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446 2274 $inbox->pack_start ($vbox2, 1, 0, 0);
7becc472 2275 my $vbox = Gtk3::VBox->new (0, 0);
53986d77 2276 $vbox2->pack_start ($vbox, 0, 0, 10);
89a12446 2277
7becc472 2278 my $w = Gtk3::Entry->new ();
89a12446
DM
2279 $w->set_size_request (200, -1);
2280
7becc472 2281 my $c = Gtk3::EntryCompletion->new ();
89a12446
DM
2282 $c->set_text_column (0);
2283 $c->set_minimum_key_length(0);
2284 $c->set_popup_set_width (1);
4443aa27 2285 $c->set_inline_completion (1);
89a12446 2286
7becc472
DM
2287 my $hbox2 = Gtk3::HBox->new (0, 0);
2288 my $label = Gtk3::Label->new ("Time zone");
89a12446
DM
2289 $label->set_size_request (150, -1);
2290 $label->set_alignment (1, 0.5);
2291 $hbox2->pack_start ($label, 0, 0, 10);
2292 update_zonelist ($hbox2);
2293
7becc472
DM
2294 my $hbox3 = Gtk3::HBox->new (0, 0);
2295 $label = Gtk3::Label->new ("Keyboard Layout");
89a12446
DM
2296 $label->set_size_request (150, -1);
2297 $label->set_alignment (1, 0.5);
2298 $hbox3->pack_start ($label, 0, 0, 10);
2299
bcbfab6b 2300 my $kmapcb = Gtk3::ComboBoxText->new();
89a12446
DM
2301 $kmapcb->set_size_request (200, -1);
2302 foreach my $layout (sort keys %{$cmap->{kmaphash}}) {
2303 $kmapcb->append_text ($layout);
2304 }
2305
2306 update_layout ($kmapcb);
2307 $hbox3->pack_start ($kmapcb, 0, 0, 0);
2308
2309 $kmapcb->signal_connect ('changed' => sub {
2310 my $sel = $kmapcb->get_active_text();
2311 if (my $kmap = $cmap->{kmaphash}->{$sel}) {
2312 my $xkmap = $cmap->{kmap}->{$kmap}->{x11};
2313 my $xvar = $cmap->{kmap}->{$kmap}->{x11var};
2314 syscmd ("setxkbmap $xkmap $xvar") if !$opt_testmode;
2315 $keymap = $kmap;
2316 }
2317 });
2318
2319 $w->signal_connect ('changed' => sub {
2320 my ($entry, $event) = @_;
2321 my $text = $entry->get_text;
2322
2323 if (my $cc = $countryhash->{lc($text)}) {
2324 update_zonelist ($hbox2, $cc);
2325 my $kmap = $ctr->{$cc}->{kmap} || 'en-us';
2326 update_layout ($kmapcb, $kmap);
2327 }
2328 });
2329
2330 $w->signal_connect (key_press_event => sub {
2331 my ($entry, $event) = @_;
2332 my $text = $entry->get_text;
2333
7becc472
DM
2334 my $val = $event->get_keyval;
2335
2336 if ($val == Gtk3::Gdk::KEY_Tab) {
89a12446 2337 my $cc = $countryhash->{lc($text)};
4443aa27 2338
89a12446
DM
2339 my $found = 0;
2340 my $compl;
7becc472 2341
4443aa27
DM
2342 if ($cc) {
2343 $found = 1;
2344 $compl = $ctr->{$cc}->{name};
2345 } else {
2346 foreach my $cc (keys %$ctr) {
2347 my $ct = $ctr->{$cc}->{name};
2348 if ($ct =~ m/^\Q$text\E.*$/i) {
2349 $found++;
2350 $compl = $ct;
2351 }
2352 last if $found > 1;
89a12446 2353 }
89a12446 2354 }
4443aa27 2355
89a12446 2356 if ($found == 1) {
7becc472 2357 $entry->set_text($compl);
3df718ea 2358 $c->complete();
89a12446
DM
2359 return undef;
2360 } else {
7becc472
DM
2361 #Gtk3::Gdk::beep();
2362 print chr(7); # beep ?
89a12446
DM
2363 }
2364
3df718ea
DM
2365 $c->complete();
2366
7becc472
DM
2367 my $buf = $w->get_buffer();
2368 $buf->insert_text(-1, '', -1); # popup selection
2369
89a12446
DM
2370 return 1;
2371 }
2372
2373 return undef;
2374 });
7becc472
DM
2375
2376 my $ls = Gtk3::ListStore->new('Glib::String');
89a12446
DM
2377 foreach my $cc (sort {$ctr->{$a}->{name} cmp $ctr->{$b}->{name} } keys %$ctr) {
2378 my $iter = $ls->append();
2379 $ls->set ($iter, 0, $ctr->{$cc}->{name});
2380 }
2381 $c->set_model ($ls);
2382
968fa90b 2383 $w->set_completion ($c);
89a12446 2384
7becc472 2385 my $hbox = Gtk3::HBox->new (0, 0);
89a12446 2386
7becc472 2387 $label = Gtk3::Label->new ("Country");
89a12446
DM
2388 $label->set_alignment (1, 0.5);
2389 $label->set_size_request (150, -1);
2390 $hbox->pack_start ($label, 0, 0, 10);
2391 $hbox->pack_start ($w, 0, 0, 0);
2392
2393 $vbox->pack_start ($hbox, 0, 0, 5);
2394 $vbox->pack_start ($hbox2, 0, 0, 5);
2395 $vbox->pack_start ($hbox3, 0, 0, 5);
2396
9d1f1ee3 2397 if ($country && $ctr->{$country}) {
89a12446
DM
2398 $w->set_text ($ctr->{$country}->{name});
2399 }
2400
2401 $inbox->show_all;
2402
2403 display_html ("country.htm");
2404 set_next (undef, sub {
2405
2406 my $text = $w->get_text;
2407
2408 if (my $cc = $countryhash->{lc($text)}) {
2409 $country = $cc;
2410 create_password_view();
2411 return;
2412 } else {
2413 display_message ("Please select a country first.");
2414 $w->grab_focus();
2415 }
2416 });
2417
2418 $w->grab_focus();
2419}
2420
c6ed3b24
DM
2421my $target_hd_combo;
2422my $target_hd_label;
2423
2424my $hdopion_first_setup = 1;
2425
c7779156
FG
2426my $create_basic_grid = sub {
2427 my $grid = Gtk3::Grid->new();
2428 $grid->set_visible(1);
2429 $grid->set_column_spacing(10);
2430 $grid->set_row_spacing(10);
2431 $grid->set_hexpand(1);
2432
2433 $grid->set_margin_start(5);
2434 $grid->set_margin_end(5);
2435 $grid->set_margin_top(5);
2436 $grid->set_margin_bottom(5);
2437
2438 return $grid;
2439};
2440
2441my $create_label_widget_grid = sub {
2442 my ($labeled_widgets) = @_;
2443
2444 my $grid = &$create_basic_grid();
2445 my $row = 0;
2446
2447 for (my $i = 0; $i < @$labeled_widgets; $i += 2) {
2448 my $widget = @$labeled_widgets[$i+1];
2449 my $label = Gtk3::Label->new(@$labeled_widgets[$i]);
2450 $label->set_visible(1);
2451 $label->set_alignment (1, 0.5);
2452 $grid->attach($label, 0, $row, 1, 1);
2453 $widget->set_visible(1);
2454 $grid->attach($widget, 1, $row, 1, 1);
2455 $row++;
2456 }
2457
2458 return $grid;
2459};
2460
2461my $create_raid_disk_grid = sub {
2462 my $disk_labeled_widgets = [];
2463 for (my $i = 0; $i < @$hds; $i++) {
2464 my $disk_selector = Gtk3::ComboBoxText->new();
2465 $disk_selector->append_text("-- do not use --");
2466 $disk_selector->set_active(0);
2467 $disk_selector->set_visible(1);
2468 foreach my $hd (@$hds) {
2469 my ($disk, $devname, $size, $model) = @$hd;
2470 $disk_selector->append_text(get_device_desc ($devname, $size, $model));
2471 $disk_selector->{pve_disk_id} = $i;
2472 $disk_selector->signal_connect (changed => sub {
2473 my $w = shift;
2474 my $diskid = $w->{pve_disk_id};
2475 my $a = $w->get_active - 1;
2476 $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
2477 });
2478 }
2479
2480 if ($hdopion_first_setup) {
2481 $disk_selector->set_active ($i+1) if $hds->[$i];
2482 } else {
2483 my $hdind = 0;
2484 if (my $cur_hd = $config_options->{"disksel$i"}) {
2485 foreach my $hd (@$hds) {
2486 if (@$hd[1] eq @$cur_hd[1]) {
2487 $disk_selector->set_active($hdind+1);
2488 last;
2489 }
2490 $hdind++;
2491 }
2492 }
2493 }
2494
2495 push @$disk_labeled_widgets, "Harddisk $i", $disk_selector;
2496 }
2497
2498 my $scrolled_window = Gtk3::ScrolledWindow->new();
2499 $scrolled_window->set_hexpand(1);
650a9aab 2500 $scrolled_window->set_propagate_natural_height(1) if @$hds > 4;
c7779156
FG
2501 $scrolled_window->add(&$create_label_widget_grid($disk_labeled_widgets));
2502 $scrolled_window->set_policy('never', 'automatic');
2503
2504 return $scrolled_window;
2505# &$create_label_widget_grid($disk_labeled_widgets)
2506};
2507
2508my $create_raid_advanced_grid = sub {
2509 my $labeled_widgets = [];
6c99667a
FG
2510 my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9,13,1);
2511 $spinbutton_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
2512 $spinbutton_ashift->signal_connect ("value-changed" => sub {
2513 my $w = shift;
2514 $config_options->{ashift} = $w->get_value_as_int();
c7779156
FG
2515 });
2516 $config_options->{ashift} = 12 if ! defined($config_options->{ashift});
6c99667a 2517 $spinbutton_ashift->set_value($config_options->{ashift});
c7779156 2518 push @$labeled_widgets, "ashift";
6c99667a 2519 push @$labeled_widgets, $spinbutton_ashift;
c7779156
FG
2520
2521 my $combo_compress = Gtk3::ComboBoxText->new();
2522 $combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset");
2523 # note: gzip / lze not allowed for bootfs vdevs
2524 my $comp_opts = ["on","off","lzjb","lz4"];
2525 foreach my $opt (@$comp_opts) {
2526 $combo_compress->append($opt, $opt);
2527 }
2528 $config_options->{compress} = "on" if !defined($config_options->{compress});
2529 $combo_compress->set_active_id($config_options->{compress});
2530 $combo_compress->signal_connect (changed => sub {
2531 my $w = shift;
2532 $config_options->{compress} = $w->get_active_text();
2533 });
2534 push @$labeled_widgets, "compress";
2535 push @$labeled_widgets, $combo_compress;
2536
2537 my $combo_checksum = Gtk3::ComboBoxText->new();
2538 $combo_checksum->set_tooltip_text("zfs checksum algorithm for rpool dataset");
2539 my $csum_opts = ["on", "off","fletcher2", "fletcher4", "sha256"];
2540 foreach my $opt (@$csum_opts) {
2541 $combo_checksum->append($opt, $opt);
2542 }
2543 $config_options->{checksum} = "on" if !($config_options->{checksum});
2544 $combo_checksum->set_active_id($config_options->{checksum});
2545 $combo_checksum->signal_connect (changed => sub {
2546 my $w = shift;
2547 $config_options->{checksum} = $w->get_active_text();
2548 });
2549 push @$labeled_widgets, "checksum";
2550 push @$labeled_widgets, $combo_checksum;
2551
2552 my $spinbutton_copies = Gtk3::SpinButton->new_with_range(1,3,1);
2553 $spinbutton_copies->set_tooltip_text("zfs copies property for rpool dataset (in addition to RAID redundancy!)");
2554 $spinbutton_copies->signal_connect ("value-changed" => sub {
2555 my $w = shift;
2556 $config_options->{copies} = $w->get_value_as_int();
c7779156
FG
2557 });
2558 $config_options->{copies} = 1 if !defined($config_options->{copies});
2559 $spinbutton_copies->set_value($config_options->{copies});
2560 push @$labeled_widgets, "copies", $spinbutton_copies;
2561
2562 return &$create_label_widget_grid($labeled_widgets);;
2563};
2564
aed81ff0
DM
2565sub create_hdoption_view {
2566
2567 my $dialog = Gtk3::Dialog->new();
2568
2569 $dialog->set_title("Harddisk options");
2570
2571 $dialog->add_button("_OK", 1);
2572
2573 my $contarea = $dialog->get_content_area();
2574
2575 my $hbox2 = Gtk3::Box->new('horizontal', 0);
2576 $contarea->pack_start($hbox2, 1, 1, 10);
2577
2578 my $grid = Gtk3::Grid->new();
2579 $grid->set_column_spacing(10);
2580 $grid->set_row_spacing(10);
c6ed3b24 2581
aed81ff0 2582 $hbox2->pack_start($grid, 1, 0, 10);
c6ed3b24
DM
2583
2584 my $row = 0;
2585
aed81ff0
DM
2586 # Filesystem type
2587
2588 my $label0 = Gtk3::Label->new ("Filesystem");
2589 $label0->set_alignment (1, 0.5);
c6ed3b24
DM
2590 $grid->attach($label0, 0, $row, 1, 1);
2591
bcbfab6b 2592 my $fstypecb = Gtk3::ComboBoxText->new();
aed81ff0 2593
121ebc59
DM
2594 my $fstype = ['ext3', 'ext4', 'xfs',
2595 'zfs (RAID0)', 'zfs (RAID1)',
2596 'zfs (RAID10)', 'zfs (RAIDZ-1)',
6f52fc3d
DM
2597 'zfs (RAIDZ-2)', 'zfs (RAIDZ-3)'];
2598
2599 push @$fstype, 'btrfs (RAID0)', 'btrfs (RAID1)', 'btrfs (RAID10)'
c20d6ab0 2600 if $setup->{enable_btrfs};
aed81ff0 2601
c6ed3b24
DM
2602 my $tcount = 0;
2603 foreach my $tmp (@$fstype) {
2604 $fstypecb->append_text($tmp);
2605 $fstypecb->set_active ($tcount)
2606 if $config_options->{filesys} eq $tmp;
2607 $tcount++;
2608 }
2609
2610 $grid->attach($fstypecb, 1, $row, 1, 1);
2611
2612 $hbox2->show_all();
2613
2614 $row++;
2615
c7779156
FG
2616 my $sep = Gtk3::HSeparator->new();
2617 $sep->set_visible(1);
2618 $grid->attach($sep, 0, $row, 2, 1);
2619 $row++;
aed81ff0 2620
c7779156 2621 my $hdsize_labeled_widgets = [];
aed81ff0 2622
c7779156 2623 # size compute
c6ed3b24 2624 my $hdsize = 0;
aed81ff0
DM
2625 if ( -b $target_hd) {
2626 $hdsize = int(hd_size ($target_hd) / (1024*1024.0)); # size in GB
c6ed3b24 2627 } elsif ($target_hd) {
aed81ff0
DM
2628 $hdsize = int((-s $target_hd) / (1024*1024*1024.0));
2629 }
2630
2631 my $hdsize_size_adj = Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
2632 my $spinbutton_hdsize = Gtk3::SpinButton->new($hdsize_size_adj, 1, 1);
2633 $spinbutton_hdsize->set_tooltip_text("only use specified size (GB) of the harddisk (rest left unpartitioned)");
c7779156 2634 push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize;
aed81ff0
DM
2635
2636 my $entry_swapsize = Gtk3::Entry->new();
2637 $entry_swapsize->set_tooltip_text("maximum SWAP size (GB)");
2638 $entry_swapsize->signal_connect (key_press_event => \&check_float);
2639 $entry_swapsize->set_text($config_options->{swapsize}) if $config_options->{swapsize};
c7779156 2640 push @$hdsize_labeled_widgets, "swapsize", $entry_swapsize;
aed81ff0
DM
2641
2642 my $entry_maxroot = Gtk3::Entry->new();
2643 $entry_maxroot->set_tooltip_text("maximum size (GB) for LVM root volume");
2644 $entry_maxroot->signal_connect (key_press_event => \&check_float);
2645 $entry_maxroot->set_text($config_options->{maxroot}) if $config_options->{maxroot};
c7779156 2646 push @$hdsize_labeled_widgets, "maxroot", $entry_maxroot;
aed81ff0
DM
2647
2648 my $entry_minfree = Gtk3::Entry->new();
034f75e4 2649 $entry_minfree->set_tooltip_text("minimum free LVM space (GB, required for LVM snapshots)");
aed81ff0
DM
2650 $entry_minfree->signal_connect (key_press_event => \&check_float);
2651 $entry_minfree->set_text($config_options->{minfree}) if $config_options->{minfree};
c7779156 2652 push @$hdsize_labeled_widgets, "minfree", $entry_minfree;
aed81ff0
DM
2653
2654 my $entry_maxvz = Gtk3::Entry->new();
2655 $entry_maxvz->set_tooltip_text("maximum size (GB) for LVM data volume");
2656 $entry_maxvz->signal_connect (key_press_event => \&check_float);
2657 $entry_maxvz->set_text($config_options->{maxvz}) if $config_options->{maxvz};
c7779156
FG
2658 push @$hdsize_labeled_widgets, "maxvz", $entry_maxvz;
2659
2660 my $options_stack = Gtk3::Stack->new();
2661 $options_stack->set_visible(1);
2662 $options_stack->set_hexpand(1);
2663 $options_stack->set_vexpand(1);
2664 $options_stack->add_titled(&$create_raid_disk_grid(), "raiddisk", "Disk Setup");
2665 $options_stack->add_titled(&$create_label_widget_grid($hdsize_labeled_widgets), "hdsize", "Size Options");
2666 $options_stack->add_titled(&$create_raid_advanced_grid("zfs"), "raidzfsadvanced", "Advanced Options");
2667 $options_stack->set_visible_child_name("raiddisk");
2668 my $options_stack_switcher = Gtk3::StackSwitcher->new();
2669 $options_stack_switcher->set_halign('center');
2670 $options_stack_switcher->set_stack($options_stack);
2671 $grid->attach($options_stack_switcher, 0, $row, 2, 1);
2672 $row++;
2673 $grid->attach($options_stack, 0, $row, 2, 1);
c6ed3b24 2674 $row++;
aed81ff0 2675
c7779156
FG
2676 $hdopion_first_setup = 0;
2677
2678 my $switch_view = sub {
2679 my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
2680 my $enable_zfs_opts = $config_options->{filesys} =~ m/zfs/;
c6ed3b24 2681
c7779156
FG
2682 $target_hd_combo->set_visible(!$raid);
2683 $options_stack->get_child_by_name("hdsize")->set_visible(!$raid);
2684 $options_stack->get_child_by_name("raiddisk")->set_visible($raid);
2685 $options_stack_switcher->set_visible($enable_zfs_opts);
2686 $options_stack->get_child_by_name("raidzfsadvanced")->set_visible($enable_zfs_opts);
2687 if ($raid) {
c6ed3b24 2688 $target_hd_label->set_text("Target: $config_options->{filesys} ");
c7779156 2689 $options_stack->set_visible_child_name("raiddisk");
c6ed3b24 2690 } else {
c6ed3b24
DM
2691 $target_hd_label->set_text("Target Harddisk: ");
2692 }
c7779156
FG
2693 my (undef, $pref_width) = $dialog->get_preferred_width();
2694 my (undef, $pref_height) = $dialog->get_preferred_height();
650a9aab 2695 $pref_height = 750 if $pref_height > 750;
c7779156 2696 $dialog->resize($pref_width, $pref_height);
f7b853d1
DM
2697 };
2698
c7779156 2699 &$switch_view();
f7b853d1
DM
2700
2701 $fstypecb->signal_connect (changed => sub {
2702 $config_options->{filesys} = $fstypecb->get_active_text();
c7779156 2703 &$switch_view();
f7b853d1
DM
2704 });
2705
c6ed3b24 2706 $dialog->show();
aed81ff0
DM
2707
2708 $dialog->run();
2709
2710 my $get_float = sub {
2711 my ($entry) = @_;
2712
2713 my $text = $entry->get_text();
2714 return undef if !defined($text);
2715
2716 $text =~ s/^\s+//;
2717 $text =~ s/\s+$//;
2718
2719 return undef if $text !~ m/^\d+(\.\d+)?$/;
2720
2721 return $text;
2722 };
2723
2724 my $tmp;
2725
2726 if (($tmp = &$get_float($spinbutton_hdsize)) && ($tmp != $hdsize)) {
2727 $config_options->{hdsize} = $tmp;
2728 } else {
2729 delete $config_options->{hdsize};
2730 }
2731
2732 if (defined($tmp = &$get_float($entry_swapsize))) {
2733 $config_options->{swapsize} = $tmp;
2734 } else {
2735 delete $config_options->{swapsize};
2736 }
2737
2738 if (defined($tmp = &$get_float($entry_maxroot))) {
2739 $config_options->{maxroot} = $tmp;
2740 } else {
2741 delete $config_options->{maxroot};
2742 }
2743
2744 if (defined($tmp = &$get_float($entry_minfree))) {
2745 $config_options->{minfree} = $tmp;
2746 } else {
2747 delete $config_options->{minfree};
2748 }
2749
2750 if (defined($tmp = &$get_float($entry_maxvz))) {
2751 $config_options->{maxvz} = $tmp;
2752 } else {
2753 delete $config_options->{maxvz};
2754 }
2755
2756 $dialog->destroy();
2757}
2758
121ebc59 2759my $get_raid_devlist = sub {
c6ed3b24
DM
2760
2761 my $dev_name_hash = {};
2762
2763 my $devlist = [];
5f8e86d5 2764 for (my $i = 0; $i < @$hds; $i++) {
c6ed3b24
DM
2765 if (my $hd = $config_options->{"disksel$i"}) {
2766 my ($disk, $devname, $size, $model) = @$hd;
2767 die "device '$devname' is used more than once\n"
2768 if $dev_name_hash->{$devname};
2769 $dev_name_hash->{$devname} = $hd;
2770 push @$devlist, $hd;
2771 }
2772 }
2773
121ebc59
DM
2774 return $devlist;
2775};
2776
14aacec8
FG
2777sub zfs_mirror_size_check {
2778 my ($expected, $actual) = @_;
2779
2780 die "mirrored disks must have same size\n"
2781 if abs($expected - $actual) > $expected / 10;
2782}
2783
121ebc59
DM
2784sub get_zfs_raid_setup {
2785
2786 my $filesys = $config_options->{filesys};
2787
2788 my $devlist = &$get_raid_devlist();
2789
224bb7b0 2790 my $diskcount = scalar(@$devlist);
0cfa502c 2791 die "$filesys needs at least one device\n" if $diskcount < 1;
c6ed3b24 2792
121ebc59
DM
2793 my $bootdevlist = [];
2794
c6ed3b24
DM
2795 my $cmd= '';
2796 if ($filesys eq 'zfs (RAID0)') {
2797 push @$bootdevlist, @$devlist[0];
2798 foreach my $hd (@$devlist) {
2799 $cmd .= " @$hd[1]";
2800 }
2801 } elsif ($filesys eq 'zfs (RAID1)') {
0cfa502c 2802 die "zfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
c6ed3b24 2803 $cmd .= ' mirror ';
269c66a6 2804 my $hd = @$devlist[0];
14aacec8 2805 my $expected_size = @$hd[2]; # all disks need approximately same size
269c66a6 2806 foreach $hd (@$devlist) {
14aacec8 2807 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
2808 $cmd .= " @$hd[1]";
2809 push @$bootdevlist, $hd;
2810 }
2811 } elsif ($filesys eq 'zfs (RAID10)') {
0cfa502c 2812 die "zfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
b8f4f0f9 2813 die "zfs (RAID10) needs an even number of devices\n" if $diskcount & 1;
c6ed3b24
DM
2814
2815 push @$bootdevlist, @$devlist[0], @$devlist[1];
2816
224bb7b0 2817 for (my $i = 0; $i < $diskcount; $i+=2) {
c6ed3b24
DM
2818 my $hd1 = @$devlist[$i];
2819 my $hd2 = @$devlist[$i+1];
14aacec8 2820 zfs_mirror_size_check(@$hd1[2], @$hd2[2]); # pairs need approximately same size
c6ed3b24
DM
2821 $cmd .= ' mirror ' . @$hd1[1] . ' ' . @$hd2[1];
2822 }
2823
2824 } elsif ($filesys =~ m/^zfs \(RAIDZ-([123])\)$/) {
2825 my $level = $1;
2826 my $mindisks = 2 + $level;
0cfa502c 2827 die "zfs (RAIDZ-$level) needs at least $mindisks devices\n" if scalar(@$devlist) < $mindisks;
269c66a6 2828 my $hd = @$devlist[0];
14aacec8 2829 my $expected_size = @$hd[2]; # all disks need approximately same size
097ecf8f 2830 $cmd .= " raidz$level";
269c66a6 2831 foreach $hd (@$devlist) {
14aacec8 2832 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
2833 $cmd .= " @$hd[1]";
2834 push @$bootdevlist, $hd;
2835 }
2836 } else {
2837 die "unknown zfs mode '$filesys'\n";
2838 }
2839
2840 return ($devlist, $bootdevlist, $cmd);
2841}
2842
121ebc59
DM
2843sub get_btrfs_raid_setup {
2844
2845 my $filesys = $config_options->{filesys};
2846
2847 my $devlist = &$get_raid_devlist();
2848
2849 my $diskcount = scalar(@$devlist);
0cfa502c 2850 die "$filesys needs at least one device\n" if $diskcount < 1;
121ebc59
DM
2851
2852 my $mode;
2853
2854 if ($diskcount == 1) {
2855 $mode = 'single';
2856 } else {
2857 if ($filesys eq 'btrfs (RAID0)') {
2858 $mode = 'raid0';
2859 } elsif ($filesys eq 'btrfs (RAID1)') {
0cfa502c 2860 die "btrfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
121ebc59
DM
2861 $mode = 'raid1';
2862 } elsif ($filesys eq 'btrfs (RAID10)') {
0cfa502c 2863 die "btrfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
121ebc59
DM
2864 $mode = 'raid10';
2865 } else {
9d69f3d3 2866 die "unknown btrfs mode '$filesys'\n";
121ebc59
DM
2867 }
2868 }
2869
2870 return ($devlist, $mode);
2871}
2872
89a12446
DM
2873sub create_hdsel_view {
2874
2875 cleanup_view ();
2876
7becc472 2877 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 2878 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 2879 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 2880 $vbox->pack_start ($hbox, 0, 0, 10);
968fa90b 2881
89a12446
DM
2882 my ($disk, $devname, $size, $model) = @{@$hds[0]};
2883 $target_hd = $devname;
89a12446 2884
c6ed3b24
DM
2885 $target_hd_label = Gtk3::Label->new ("Target Harddisk: ");
2886 $hbox->pack_start ($target_hd_label, 0, 0, 0);
89a12446 2887
bcbfab6b 2888 $target_hd_combo = Gtk3::ComboBoxText->new();
89a12446 2889
1aa5bd02
DM
2890 foreach my $hd (@$hds) {
2891 ($disk, $devname, $size, $model) = @$hd;
c6ed3b24 2892 $target_hd_combo->append_text (get_device_desc ($devname, $size, $model));
1aa5bd02 2893 }
89a12446 2894
c6ed3b24
DM
2895 $target_hd_combo->set_active (0);
2896 $target_hd_combo->signal_connect (changed => sub {
1aa5bd02
DM
2897 $a = shift->get_active;
2898 my ($disk, $devname) = @{@$hds[$a]};
2899 $target_hd = $devname;
1aa5bd02 2900 });
c6ed3b24
DM
2901
2902 $hbox->pack_start ($target_hd_combo, 0, 0, 10);
aed81ff0
DM
2903
2904 my $options = Gtk3::Button->new ('_Options');
2905 $options->signal_connect (clicked => \&create_hdoption_view);
2906 $hbox->pack_start ($options, 0, 0, 0);
2907
89a12446
DM
2908
2909 $inbox->show_all;
2910
2911 display_html ("page1.htm");
c6ed3b24
DM
2912
2913 set_next (undef, sub {
2914
2915 if ($config_options->{filesys} =~ m/zfs/) {
2916 eval { get_zfs_raid_setup(); };
2917 if (my $err = $@) {
2918 display_message ("Warning: $err\n" .
269c66a6 2919 "Please fix ZFS setup first.");
c6ed3b24
DM
2920 } else {
2921 create_country_view();
2922 }
121ebc59
DM
2923 } elsif ($config_options->{filesys} =~ m/btrfs/) {
2924 eval { get_btrfs_raid_setup(); };
2925 if (my $err = $@) {
2926 display_message ("Warning: $err\n" .
2927 "Please fix BTRFS setup first.");
2928 } else {
2929 create_country_view();
2930 }
c6ed3b24
DM
2931 } else {
2932 create_country_view();
2933 }
2934 });
89a12446
DM
2935}
2936
2937sub create_extract_view {
2938
89a12446
DM
2939 cleanup_view ();
2940
2941 display_html ("extract1-license.htm");
2942 $next->set_sensitive (0);
2943
7becc472 2944 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 2945 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 2946 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 2947 $vbox->pack_start ($hbox, 0, 0, 10);
89a12446 2948
7becc472 2949 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446
DM
2950 $hbox->pack_start ($vbox2, 0, 0, 0);
2951
7becc472 2952 $progress_status = Gtk3::Label->new ('');
89a12446 2953 $vbox2->pack_start ($progress_status, 1, 1, 0);
968fa90b 2954
7becc472 2955 $progress = Gtk3::ProgressBar->new;
45feca6f 2956 $progress->set_show_text(1);
7becc472 2957 $progress->set_size_request (600, -1);
89a12446
DM
2958
2959 $vbox2->pack_start ($progress, 0, 0, 0);
2960
2961 $inbox->show_all;
2962
2963 my $tdir = $opt_testmode ? "target" : "/target";
2964 mkdir $tdir;
97980bf2 2965 my $base = "${proxmox_cddir}/$setup->{product}-base.squashfs";
89a12446
DM
2966
2967 eval { extract_data ($base, $tdir); };
2968 my $err = $@;
2969
2970 $next->set_sensitive (1);
2971
2972 set_next ("_Reboot", sub { exit (0); } );
2973
296cf41f
DM
2974 if ($err) {
2975 display_html ("fail.htm");
2976 display_error ($err);
2977 } else {
2978 cleanup_view ();
2979 display_html ("success.htm");
2980 }
89a12446
DM
2981}
2982
89a12446
DM
2983sub create_intro_view {
2984
2985 cleanup_view ();
2986
bdeca872
DM
2987 if ($setup->{product} eq 'pve') {
2988 eval {
2989 my $cpuinfo = file_get_contents('/proc/cpuinfo');
2990 if ($cpuinfo && !($cpuinfo =~ /^flags\s*:.*(vmx|svm)/m)) {
2991 display_error("No support for KVM virtualisation detected.\n\n" .
2992 "Check BIOS settings for Intel VT / AMD-V / SVM.")
2993 }
2994 };
2995 }
7fff0d85 2996
89a12446
DM
2997 display_html ("license.htm");
2998
2999 set_next ("I a_gree", \&create_hdsel_view);
3000}
3001
3002$ipconf = get_ip_config ();
3003
9d1f1ee3 3004$country = detect_country() if $ipconf->{default} || $opt_testmode;
89a12446
DM
3005
3006# read country, kmap and timezone infos
3007$cmap = read_cmap ();
3008
9d1f1ee3
FG
3009if (!defined($cmap->{country}->{$country})) {
3010 print $logfd "ignoring detected country '$country', invalid or unknown\n";
3011 $country = undef;
3012}
3013
89a12446
DM
3014create_main_window ();
3015
ff2ce71c
FG
3016my $initial_error = 0;
3017
89a12446
DM
3018if (!defined ($hds) || (scalar (@$hds) <= 0)) {
3019 print "no hardisks found\n";
ff2ce71c 3020 $initial_error = 1;
89a12446
DM
3021 display_html ("nohds.htm");
3022 set_next ("Reboot", sub { exit (0); } );
3023} else {
89a12446
DM
3024 foreach my $hd (@$hds) {
3025 my ($disk, $devname) = @$hd;
3026 next if $devname =~ m|^/dev/md\d+$|;
3027 print "found Disk$disk N:$devname\n";
3028 }
89a12446
DM
3029}
3030
72836708
FG
3031if (!$initial_error && (scalar keys %{ $ipconf->{ifaces} } == 0)) {
3032 print "no network interfaces found\n";
3033 $initial_error = 1;
3034 display_html ("nonics.htm");
3035 set_next ("Reboot", sub { exit (0); } );
3036}
3037
ff2ce71c
FG
3038create_intro_view () if !$initial_error;
3039
7becc472 3040Gtk3->main;
89a12446
DM
3041
3042exit 0;