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