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