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