]> git.proxmox.com Git - pve-installer.git/blame - proxinstall
write final package list to final.pkglist
[pve-installer.git] / proxinstall
CommitLineData
6981164e 1#!/usr/bin/perl
89a12446 2
7becc472
DM
3$ENV{DEBIAN_FRONTEND} = 'noninteractive';
4$ENV{LC_ALL} = 'C';
5
89a12446 6use strict;
6981164e
DM
7use warnings;
8
89a12446 9use Getopt::Long;
62c05878 10use IPC::Open2;
89a12446
DM
11use IPC::Open3;
12use IO::File;
89a12446
DM
13use IO::Select;
14use Cwd 'abs_path';
7becc472
DM
15use Gtk3 '-init';
16use Gtk3::WebKit;
89a12446 17use Encode;
84761f93 18use String::ShellQuote;
7becc472 19use Data::Dumper;
a5af22f5 20use File::Basename;
121ebc59 21use Time::HiRes;
89a12446 22
b9075af2
DM
23use ProxmoxInstallerSetup;
24
25my $setup = ProxmoxInstallerSetup::setup();
26
89a12446
DM
27
28my $kapi = `uname -r`;
29chomp $kapi;
30
31my $opt_testmode;
32
7becc472
DM
33if (!$ENV{G_SLICE} || $ENV{G_SLICE} ne "always-malloc") {
34 die "do not use slice allocator (run with 'G_SLICE=always-malloc ./proxinstall ...')\n";
35}
36
89a12446
DM
37if (!GetOptions ('testmode=s' => \$opt_testmode)) {
38 die "usage error\n";
39 exit (-1);
40}
41
6b900321
DM
42my $zfstestpool = "test_rpool";
43my $zfspoolname = $opt_testmode ? $zfstestpool : 'rpool';
5fd81672
DM
44
45my $storage_cfg_zfs = <<__EOD__;
46dir: local
47 path /var/lib/vz
48 content iso,vztmpl,backup
49
239398be 50zfspool: local-zfs
5fd81672
DM
51 pool $zfspoolname/data
52 sparse
53 content images,rootdir
54__EOD__
55
121ebc59
DM
56my $storage_cfg_btrfs = <<__EOD__;
57dir: local
58 path /var/lib/vz
59 content iso,vztmpl,backup
60 disabled
61
62btrfs: local-btrfs
63 path /var/lib/pve/local-btrfs
64 content iso,vztmpl,backup,images,rootdir
65__EOD__
66
5fd81672
DM
67my $storage_cfg_lvmthin = <<__EOD__;
68dir: local
69 path /var/lib/vz
70 content iso,vztmpl,backup
71
239398be 72lvmthin: local-lvm
5fd81672
DM
73 thinpool data
74 vgname pve
75 content rootdir,images
76__EOD__
77
78
d2120e51
DM
79sub file_read_firstline {
80 my ($filename) = @_;
81
82 my $fh = IO::File->new ($filename, "r");
83 return undef if !$fh;
84 my $res = <$fh>;
85 chomp $res if $res;
86 $fh->close;
87 return $res;
88}
89
89a12446
DM
90my $logfd = IO::File->new (">/tmp/install.log");
91
c7429f77
DM
92my $proxmox_libdir = $opt_testmode ?
93 Cwd::cwd() . "/testdir/var/lib/pve-installer" : "/var/lib/pve-installer";
97980bf2
DM
94my $proxmox_cddir = $opt_testmode ? "../pve-cd-builder/tmp/data-gz/" : "/cdrom";
95my $proxmox_pkgdir = "${proxmox_cddir}/proxmox/packages/";
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
c437cef5
DM
1265 my $files = file_read_firstline("${proxmox_cddir}/proxmox/$setup->{product}-base.cnt") ||
1266 die "unable to read base file count\n";
89a12446
DM
1267
1268 my $per = 0;
1269 my $count = 0;
1270
fafc616c 1271 run_command ("unsquashfs -f -dest $targetdir -i $basefile", sub {
89a12446 1272 my $line = shift;
fafc616c 1273 return if $line !~ m/^$targetdir/;
89a12446
DM
1274 $count++;
1275 my $nper = int (($count *100)/$files);
1276 if ($nper != $per) {
1277 $per = $nper;
0f3d1edd 1278 my $frac = $per > 100 ? 1 : $per/100;
89a12446
DM
1279 update_progress ($frac, $maxper, 0.5);
1280 }
1281 });
1282
1283 syscmd ("mount -n -t tmpfs tmpfs $targetdir/tmp") == 0 ||
1284 die "unable to mount tmpfs on $targetdir/tmp\n";
1285 syscmd ("mount -n -t proc proc $targetdir/proc") == 0 ||
1286 die "unable to mount proc on $targetdir/proc\n";
1287 syscmd ("mount -n -t sysfs sysfs $targetdir/sys") == 0 ||
1288 die "unable to mount sysfs on $targetdir/sys\n";
1289
1290 display_html ("extract3-spam.htm");
1291 update_progress (1, $maxper, 0.5, "configuring base system");
1292
1293 # configure hosts
1294
968fa90b 1295 my $hosts =
89a12446 1296 "127.0.0.1 localhost.localdomain localhost\n" .
968fa90b 1297 "$ipaddress $hostname.$domain $hostname pvelocalhost\n\n" .
89a12446
DM
1298 "# The following lines are desirable for IPv6 capable hosts\n\n" .
1299 "::1 ip6-localhost ip6-loopback\n" .
1300 "fe00::0 ip6-localnet\n" .
1301 "ff00::0 ip6-mcastprefix\n" .
1302 "ff02::1 ip6-allnodes\n" .
1303 "ff02::2 ip6-allrouters\n" .
1304 "ff02::3 ip6-allhosts\n";
1305
968fa90b 1306 write_config ($hosts, "$targetdir/etc/hosts");
89a12446 1307
968fa90b 1308 write_config ("$hostname\n", "$targetdir/etc/hostname");
89a12446
DM
1309
1310 syscmd ("/bin/hostname $hostname") if !$opt_testmode;
1311
1312 # configure interfaces
1313
b6200603
DM
1314 my $ifaces = "auto lo\niface lo inet loopback\n\n";
1315
1316 my $ntype = $ipversion == 4 ? 'inet' : 'inet6';
1317
4a0331ab
DM
1318 my $ethdev = $ipconf->{ifaces}->{$ipconf->{selected}}->{name};
1319
1320 if ($setup->{bridged_network}) {
1321 $ifaces .= "iface $ethdev $ntype manual\n";
1322
1323 $ifaces .=
1324 "\nauto vmbr0\niface vmbr0 $ntype static\n" .
1325 "\taddress $ipaddress\n" .
1326 "\tnetmask $netmask\n" .
1327 "\tgateway $gateway\n" .
1328 "\tbridge_ports $ethdev\n" .
1329 "\tbridge_stp off\n" .
1330 "\tbridge_fd 0\n";
1331 } else {
1332 $ifaces .= "auto $ethdev\n" .
1333 "iface $ethdev $ntype static\n" .
1334 "\taddress $ipaddress\n" .
1335 "\tnetmask $netmask\n" .
1336 "\tgateway $gateway\n";
1337 }
89a12446 1338
fe44bd92
FG
1339 foreach my $iface (sort keys %{$ipconf->{ifaces}}) {
1340 my $name = $ipconf->{ifaces}->{$iface}->{name};
4a0331ab 1341 next if $name eq $ethdev;
fe44bd92
FG
1342
1343 $ifaces .= "\niface $name $ntype manual\n";
1344 }
1345
89a12446
DM
1346 write_config ($ifaces, "$targetdir/etc/network/interfaces");
1347
1348 # configure dns
1349
39a0f44b
FG
1350 my $resolvconf = "search $domain\nnameserver $dnsserver\n";
1351 write_config ($resolvconf, "$targetdir/etc/resolv.conf");
89a12446 1352
5c06ced5
DM
1353 # configure fstab
1354
1355 my $fstab = "# <file system> <mount point> <type> <options> <dump> <pass>\n";
1356
121ebc59
DM
1357 if ($use_zfs) {
1358 # do nothing
1359 } elsif ($use_btrfs) {
1360 my $fsuuid;
1361 my $cmd = "blkid -u filesystem -t TYPE=btrfs -o export $rootdev";
1362 run_command($cmd, sub {
1363 my $line = shift;
1364
1365 if ($line =~ m/^UUID=([A-Fa-f0-9\-]+)$/) {
1366 $fsuuid = $1;
1367 }
1368 });
1369
1370 die "unable to detect FS UUID" if !defined($fsuuid);
1371
1372 $fstab .= "UUID=$fsuuid / btrfs defaults 0 1\n";
1373 } else {
80090926
DM
1374 my $root_mountopt = $fssetup->{$filesys}->{root_mountopt} || 'defaults';
1375 $fstab .= "$rootdev / $filesys ${root_mountopt} 0 1\n";
7bc4f6bd 1376 }
a84ea010
DM
1377
1378 # mount /boot/efi
1379 # Note: this is required by current grub, but really dangerous, because
1380 # vfat does not have journaling, so it triggers manual fsck after each crash
1381 # so we only mount /boot/efi if really required (efi systems).
1382 if ($grub_plattform =~ m/^efi-/) {
1383 if (scalar(@$bootdevinfo)) {
f810f5d0
DM
1384 my $di = @$bootdevinfo[0]; # simply use first disk
1385 if ($di->{esp}) {
1386 my $efi_boot_uuid = $di->{esp};
1387 if (my $uuid = find_dev_by_uuid ($di->{esp})) {
1388 $efi_boot_uuid = "UUID=$uuid";
1389 }
84761f93 1390
f810f5d0
DM
1391 $fstab .= "${efi_boot_uuid} /boot/efi vfat defaults 0 1\n";
1392 }
a84ea010 1393 }
84761f93
DM
1394 }
1395
a84ea010 1396
89a12446
DM
1397 $fstab .= "$swapfile none swap sw 0 0\n" if $swapfile;
1398
1399 $fstab .= "proc /proc proc defaults 0 0\n";
1400
1401 write_config ($fstab, "$targetdir/etc/fstab");
1402 write_config ("", "$targetdir/etc/mtab");
968fa90b 1403
a04ac176 1404 syscmd ("cp ${proxmox_libdir}/policy-disable-rc.d " .
968fa90b 1405 "$targetdir/usr/sbin/policy-rc.d") == 0 ||
89a12446 1406 die "unable to copy policy-rc.d\n";
a04ac176 1407 syscmd ("cp ${proxmox_libdir}/fake-start-stop-daemon " .
968fa90b 1408 "$targetdir/sbin/") == 0 ||
89a12446
DM
1409 die "unable to copy start-stop-daemon\n";
1410
1411 diversion_add ($targetdir, "/sbin/start-stop-daemon", "/sbin/fake-start-stop-daemon");
1412 diversion_add ($targetdir, "/usr/sbin/update-grub", "/bin/true");
1413 diversion_add ($targetdir, "/usr/sbin/update-initramfs", "/bin/true");
1414
1415 syscmd ("touch $targetdir/proxmox_install_mode");
1416
e35d5efb 1417 my $grub_install_devices_txt = '';
3573c046 1418 foreach my $di (@$bootdevinfo) {
e35d5efb 1419 $grub_install_devices_txt .= ', ' if $grub_install_devices_txt;
ff863262 1420 $grub_install_devices_txt .= $di->{by_id} || $di->{devname};
3573c046
DM
1421 }
1422
b1293fcb
FG
1423 # Note: keyboard-configuration/xbkb-keymap is used by console-setup
1424 my $xkmap = $cmap->{kmap}->{$keymap}->{x11} // 'us';
814f5c39 1425
89a12446
DM
1426 debconfig_set ($targetdir, <<_EOD);
1427locales locales/default_environment_locale select en_US.UTF-8
1428locales locales/locales_to_be_generated select en_US.UTF-8 UTF-8
1429samba-common samba-common/dhcp boolean false
1430samba-common samba-common/workgroup string WORKGROUP
e953719f 1431postfix postfix/main_mailer_type select No configuration
b1293fcb 1432keyboard-configuration keyboard-configuration/xkb-keymap select $xkmap
814f5c39 1433d-i debian-installer/locale select en_US.UTF-8
3573c046 1434grub-pc grub-pc/install_devices select $grub_install_devices_txt
89a12446
DM
1435_EOD
1436
89a12446 1437 my $pkg_count = 0;
97980bf2 1438 while (<${proxmox_pkgdir}/*.deb>) { $pkg_count++ };
89a12446 1439
121ebc59
DM
1440 # btrfs/dpkg is extremely slow without --force-unsafe-io
1441 my $dpkg_opts = $use_btrfs ? "--force-unsafe-io" : "";
1442
89a12446 1443 $count = 0;
97980bf2 1444 while (<${proxmox_pkgdir}/*.deb>) {
89a12446
DM
1445 chomp;
1446 my $path = $_;
97980bf2 1447 my ($deb) = $path =~ m/${proxmox_pkgdir}\/(.*\.deb)/;
1e61f3d8
DM
1448# if ($deb =~ m/^grub-efi-/ && $deb !~ m/^grub-${grub_plattform}/) {
1449# $count++;
1450# next;
1451# }
89a12446
DM
1452 update_progress ($count/$pkg_count, 0.5, 0.75, "extracting $deb");
1453 print "extracting: $deb\n";
1454 syscmd ("cp $path $targetdir/tmp/$deb") == 0 ||
1455 die "installation of package $deb failed\n";
121ebc59 1456 syscmd ("chroot $targetdir dpkg $dpkg_opts --force-depends --no-triggers --unpack /tmp/$deb") == 0 ||
968fa90b 1457 die "installation of package $deb failed\n";
89a12446
DM
1458 update_progress ((++$count)/$pkg_count, 0.5, 0.75);
1459 }
1460
1461 display_html ("extract4-virus.htm");
1462
3b11dce4
FG
1463 # needed for postfix postinst in case no other NIC is active
1464 syscmd("chroot $targetdir ifup lo");
1465
121ebc59 1466 my $cmd = "chroot $targetdir dpkg $dpkg_opts --force-confold --configure -a";
89a12446
DM
1467 $count = 0;
1468 run_command ($cmd, sub {
1469 my $line = shift;
1470 if ($line =~ m/Setting up\s+(\S+)/) {
1471 update_progress ((++$count)/$pkg_count, 0.75, 0.95,
1472 "configuring $1");
1473 }
1474 });
968fa90b 1475
89a12446
DM
1476 unlink "$targetdir/etc/mailname";
1477 $postfix_main_cf =~ s/__FQDN__/${hostname}.${domain}/;
1478 write_config ($postfix_main_cf, "$targetdir/etc/postfix/main.cf");
1479
1480 # make sure we have all postfix directories
1481 syscmd ("chroot $targetdir /usr/sbin/postfix check");
1482 # cleanup mail queue
1483 syscmd ("chroot $targetdir /usr/sbin/postsuper -d ALL");
1484
6b5dc3d0
DM
1485 # enable NTP (timedatectl set-ntp true does not work without DBUS)
1486 syscmd ("chroot $targetdir /bin/systemctl enable systemd-timesyncd.service");
1487
89a12446
DM
1488 unlink "$targetdir/proxmox_install_mode";
1489
968fa90b 1490 # set timezone
89a12446
DM
1491 unlink ("$targetdir/etc/localtime");
1492 symlink ("/usr/share/zoneinfo/$timezone", "$targetdir/etc/localtime");
1493 write_config ("$timezone\n", "$targetdir/etc/timezone");
1494
89a12446
DM
1495 # set apt mirror
1496 if (my $mirror = $cmap->{country}->{$country}->{mirror}) {
1497 my $fn = "$targetdir/etc/apt/sources.list";
968fa90b 1498 syscmd ("sed -i 's/ftp\\.debian\\.org/$mirror/' '$fn'");
89a12446
DM
1499 }
1500
19edf8b7
DM
1501 # create extended_states for apt (avoid cron job warning if that
1502 # file does not exist)
1503 write_config ('', "$targetdir/var/lib/apt/extended_states");
1504
c2657b8b
DM
1505 # allow ssh root login
1506 syscmd ("sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' '$targetdir/etc/ssh/sshd_config'");
1507
58a09baa
DM
1508 if ($setup->{product} eq 'pve') {
1509 # save installer settings
1510 my $ucc = uc ($country);
1511 debconfig_set($targetdir, "pve-manager pve-manager/country string $ucc\n");
1512 }
89a12446
DM
1513
1514 update_progress (0.8, 0.95, 1, "make system bootable");
1515
5c06ced5 1516 if ($use_zfs) {
52f45831 1517 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
1518 die "unable to update /etc/default/grub\n";
1519
1520 }
23c337f5 1521
89a12446
DM
1522 diversion_remove ($targetdir, "/usr/sbin/update-grub");
1523 diversion_remove ($targetdir, "/usr/sbin/update-initramfs");
1524
c6ed3b24 1525 if (!$opt_testmode) {
89a12446
DM
1526
1527 unlink ("$targetdir/etc/mtab");
1528 symlink ("/proc/mounts", "$targetdir/etc/mtab");
1529 syscmd ("mount -n --bind /dev $targetdir/dev");
1530
1531 syscmd ("chroot $targetdir /usr/sbin/update-initramfs -c -k $kapi") == 0 ||
1532 die "unable to install initramfs\n";
1533
c6ed3b24
DM
1534 foreach my $di (@$bootdevinfo) {
1535 my $dev = $di->{devname};
f810f5d0
DM
1536 syscmd ("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
1537 die "unable to install the i386-pc boot loader on '$dev'\n";
1538
1539 if ($di->{esp}) {
83250a74 1540 syscmd ("mount -n $di->{esp} -t vfat $targetdir/boot/efi") == 0 ||
f810f5d0 1541 die "unable to mount $di->{esp}\n";
5e0d6dce
FG
1542 my $rc = syscmd ("chroot $targetdir /usr/sbin/grub-install --target x86_64-efi --no-floppy --bootloader-id='proxmox' $dev");
1543 if ($rc != 0) {
1544 if (-d '/sys/firmware/efi') {
1545 die "unable to install the EFI boot loader on '$dev'\n";
1546 } else {
1547 warn "unable to install the EFI boot loader on '$dev', ignoring (not booted using UEFI)\n";
1548 }
1549 }
fd3ec787
DM
1550 # also install fallback boot file (OVMF does not boot without)
1551 mkdir("$targetdir/boot/efi/EFI/BOOT");
1552 syscmd("cp $targetdir/boot/efi/EFI/proxmox/grubx64.efi $targetdir/boot/efi/EFI/BOOT/BOOTx64.EFI") == 0 ||
1553 die "unable to copy efi boot loader\n";
1554
f810f5d0
DM
1555 syscmd ("umount $targetdir/boot/efi") == 0 ||
1556 die "unable to umount $targetdir/boot/efi\n";
1e61f3d8 1557 }
c6ed3b24 1558 }
89a12446
DM
1559
1560 syscmd ("chroot $targetdir /usr/sbin/update-grub") == 0 ||
c6ed3b24 1561 die "unable to update boot loader config\n";
89a12446
DM
1562
1563 syscmd ("umount $targetdir/dev");
1564 }
1565
968fa90b 1566 # cleanup
89a12446 1567
968fa90b 1568 # hack: remove dead.letter from sshd installation
89a12446
DM
1569 syscmd ("rm -rf $targetdir/dead.letter");
1570
89a12446
DM
1571 unlink "$targetdir/usr/sbin/policy-rc.d";
1572
1573 diversion_remove ($targetdir, "/sbin/start-stop-daemon");
1574
1575 # set root password
968fa90b 1576 my $octets = encode("utf-8", $password);
89a12446
DM
1577 run_command ("chroot $targetdir /usr/sbin/chpasswd", undef,
1578 "root:$octets\n");
7053f98b 1579
038552a1
DM
1580 if ($setup->{product} eq 'pmg') {
1581 mkdir "/etc/pmg";
1582 # save admin email
1583 write_config ("#Unix Superuser\nroot:1:0::root:${mailto}:::\n",
1584 "/etc/pmg/user.conf");
1585
1586 } elsif ($setup->{product} eq 'pve') {
7053f98b 1587
8acc47b5 1588 # create pmxcfs DB
7053f98b 1589
8acc47b5
DM
1590 my $tmpdir = "$targetdir/tmp/pve";
1591 mkdir $tmpdir;
7053f98b 1592
8acc47b5
DM
1593 # write vnc keymap to datacenter.cfg
1594 my $vnckmap = $cmap->{kmap}->{$keymap}->{kvm} || 'en-us';
1595 write_config ("keyboard: $vnckmap\n",
1596 "$tmpdir/datacenter.cfg");
968fa90b 1597
8acc47b5
DM
1598 # save admin email
1599 write_config ("user:root\@pam:1:0:::${mailto}::\n",
1600 "$tmpdir/user.cfg");
5fd81672 1601
8acc47b5
DM
1602 # write storage.cfg
1603 my $strorage_cfg_fn = "$tmpdir/storage.cfg";
1604 if ($use_zfs) {
1605 write_config ($storage_cfg_zfs, $strorage_cfg_fn);
1606 } elsif ($use_btrfs) {
1607 write_config ($storage_cfg_btrfs, $strorage_cfg_fn);
1608 } else {
1609 write_config ($storage_cfg_lvmthin, $strorage_cfg_fn);
1610 }
7053f98b 1611
8acc47b5
DM
1612 run_command("chroot $targetdir /usr/bin/create_pmxcfs_db /tmp/pve /var/lib/pve-cluster/config.db");
1613
1614 syscmd ("rm -rf $tmpdir");
1615 }
89a12446
DM
1616 };
1617
1618 my $err = $@;
1619
1620 update_progress (1, 0, 1, "");
1621
1622 print $err if $err;
1623
1624 if ($opt_testmode) {
121ebc59
DM
1625 my $elapsed = Time::HiRes::tv_interval($starttime);
1626 print "Elapsed extract time: $elapsed\n";
1627
ef5f4f86 1628 syscmd ("chroot $targetdir /usr/bin/dpkg-query -W --showformat='\${package}\n'> final.pkglist");
89a12446
DM
1629 }
1630
89a12446
DM
1631 syscmd ("umount $targetdir/tmp");
1632 syscmd ("umount $targetdir/proc");
1633 syscmd ("umount $targetdir/sys");
6fbd1fb1
DM
1634
1635 if ($use_zfs) {
1636 syscmd ("zfs umount -a") == 0 ||
1637 die "unable to unmount zfs\n";
1638 } else {
1639 syscmd ("umount -d $targetdir");
1640 }
89a12446 1641
5c06ced5 1642 if (!$err && $use_zfs) {
481671c3
DM
1643 syscmd ("zfs set sync=standard $zfspoolname") == 0 ||
1644 die "unable to set zfs properties\n";
1645
5c06ced5
DM
1646 syscmd ("zfs set mountpoint=/ $zfspoolname/ROOT/pve-1") == 0 ||
1647 die "zfs set mountpoint failed\n";
1648
1649 syscmd ("zpool set bootfs=$zfspoolname/ROOT/pve-1 $zfspoolname") == 0 ||
1650 die "zfs set bootfs failed\n";
016679a2 1651 syscmd ("zpool export $zfspoolname");
5c06ced5
DM
1652 }
1653
89a12446
DM
1654 die $err if $err;
1655}
1656
1657sub display_html {
1658 my ($filename) = @_;
1659
a04ac176 1660 my $path = "${proxmox_libdir}/html/$filename";
c437cef5 1661
8a50920c
DM
1662 my $url = "file://$path";
1663
1664 my $data = file_get_contents($path);
1665
1666 if ($filename eq 'license.htm') {
3c866639
DM
1667 my $license = decode('utf8', file_get_contents("${proxmox_cddir}/EULA"));
1668 my $title = "END USER LICENSE AGREEMENT (EULA)";
f91c161b 1669 $data =~ s/__LICENSE__/$license/;
8a50920c
DM
1670 $data =~ s/__LICENSE_TITLE__/$title/;
1671 }
1672
1673 $htmlview->load_html_string($data, $url);
7becc472
DM
1674}
1675
89a12446
DM
1676sub set_next {
1677 my ($text, $fctn) = @_;
1678
1679 $next_fctn = $fctn;
1680 $text = "_Next" if !$text;
1681 $next->set_label ($text);
968fa90b 1682
89a12446
DM
1683 $next->grab_focus ();
1684}
89a12446
DM
1685
1686sub create_main_window {
1687
7becc472 1688 $window = Gtk3::Window->new ();
89a12446 1689 $window->set_default_size (1024, 768);
84761f93 1690 $window->set_has_resize_grip(0);
89a12446
DM
1691 $window->set_decorated (0) if !$opt_testmode;
1692
7becc472 1693 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 1694
a04ac176 1695 my $image = Gtk3::Image->new_from_file ("${proxmox_libdir}/proxlogo.png");
89a12446
DM
1696 $vbox->pack_start ($image, 0, 0, 0);
1697
7becc472 1698 my $hbox = Gtk3::HBox->new (0, 0);
89a12446
DM
1699 $vbox->pack_start ($hbox, 1, 1, 0);
1700
7becc472
DM
1701 # my $f1 = Gtk3::Frame->new ('test');
1702 # $f1->set_shadow_type ('none');
1703 # $hbox->pack_start ($f1, 1, 1, 0);
89a12446 1704
7becc472 1705 my $sep1 = Gtk3::HSeparator->new;
89a12446
DM
1706 $vbox->pack_start ($sep1, 0, 0, 0);
1707
7becc472 1708 $cmdbox = Gtk3::HBox->new ();
89a12446
DM
1709 $vbox->pack_start ($cmdbox, 0, 0, 10);
1710
7becc472 1711 $next = Gtk3::Button->new ('_Next');
89a12446
DM
1712 $next->signal_connect (clicked => sub { &$next_fctn (); });
1713 $cmdbox->pack_end ($next, 0, 0, 10);
7becc472
DM
1714 my $abort = Gtk3::Button->new ('_Abort');
1715 $abort->set_can_focus (0);
89a12446
DM
1716 $cmdbox->pack_start ($abort, 0, 0, 10);
1717 $abort->signal_connect (clicked => sub { exit (-1); });
1718
7becc472
DM
1719 my $vbox2 = Gtk3::VBox->new (0, 0);
1720 $hbox->add ($vbox2);
89a12446 1721
7becc472
DM
1722 $htmlview = Gtk3::WebKit::WebView->new();
1723 my $scrolls = Gtk3::ScrolledWindow->new();
1724 $scrolls->add($htmlview);
1725
1726 my $hbox2 = Gtk3::HBox->new (0, 0);
1727 $hbox2->pack_start ($scrolls, 1, 1, 0);
89a12446
DM
1728
1729 $vbox2->pack_start ($hbox2, 1, 1, 0);
1730
7becc472 1731 my $vbox3 = Gtk3::VBox->new (0, 0);
89a12446
DM
1732 $vbox2->pack_start ($vbox3, 0, 0, 0);
1733
7becc472 1734 my $sep2 = Gtk3::HSeparator->new;
89a12446
DM
1735 $vbox3->pack_start ($sep2, 0, 0, 0);
1736
7becc472 1737 $inbox = Gtk3::HBox->new (0, 0);
89a12446
DM
1738 $vbox3->pack_start ($inbox, 0, 0, 0);
1739
1740 $window->add ($vbox);
1741
1742 $window->show_all;
1743 $window->realize ();
1744}
1745
d2120e51
DM
1746sub cleanup_view {
1747 $inbox->foreach(sub {
1748 my $child = shift;
1749 $inbox->remove ($child);
1750 });
89a12446
DM
1751}
1752
aed81ff0
DM
1753# fixme: newer GTK3 has special properties to handle numbers with Entry
1754# only allow floating point numbers with Gtk3::Entry
e73c5fcf 1755
aed81ff0
DM
1756sub check_float {
1757 my ($entry, $event) = @_;
1758
e73c5fcf
FG
1759 return check_number($entry, $event, 1);
1760}
1761
1762sub check_int {
1763 my ($entry, $event) = @_;
1764
1765 return check_number($entry, $event, 0);
1766}
1767
1768sub check_number {
1769 my ($entry, $event, $float) = @_;
aed81ff0
DM
1770
1771 my $val = $event->get_keyval;
1772
e73c5fcf 1773 if (($float && $val == ord '.') ||
aed81ff0
DM
1774 $val == Gtk3::Gdk::KEY_ISO_Left_Tab ||
1775 $val == Gtk3::Gdk::KEY_Shift_L ||
1776 $val == Gtk3::Gdk::KEY_Tab ||
1777 $val == Gtk3::Gdk::KEY_Left ||
1778 $val == Gtk3::Gdk::KEY_Right ||
1779 $val == Gtk3::Gdk::KEY_BackSpace ||
1780 $val == Gtk3::Gdk::KEY_Delete ||
1781 ($val >= ord '0' && $val <= ord '9') ||
1782 ($val >= Gtk3::Gdk::KEY_KP_0 &&
1783 $val <= Gtk3::Gdk::KEY_KP_9)) {
1784 return undef;
1785 }
1786
1787 return 1;
1788}
1789
d2120e51 1790sub create_text_input {
89a12446
DM
1791 my ($default, $text) = @_;
1792
7becc472 1793 my $hbox = Gtk3::HBox->new (0, 0);
89a12446 1794
7becc472 1795 my $label = Gtk3::Label->new ($text);
89a12446
DM
1796 $label->set_size_request (150, -1);
1797 $label->set_alignment (1, 0.5);
1798 $hbox->pack_start ($label, 0, 0, 10);
7becc472 1799 my $e1 = Gtk3::Entry->new ();
89a12446
DM
1800 $e1->set_width_chars (30);
1801 $hbox->pack_start ($e1, 0, 0, 0);
1802 $e1->set_text ($default);
1803
1804 return ($hbox, $e1);
1805}
1806
89a12446
DM
1807sub get_ip_config {
1808
fe44bd92
FG
1809 my $ifaces = {};
1810 my $default;
89a12446 1811
fe44bd92
FG
1812 my $links = `ip -o l`;
1813 foreach my $l (split /\n/,$links) {
1814 my ($index, $name, $flags, $state, $mac) = $l =~ m/^(\d+):\s+(\S+):\s+<(\S+)>.*\s+state\s+(\S+)\s+.*\s+link\/ether\s+(\S+)\s+/;
1815 next if !$name || $name eq 'lo';
89a12446 1816
fe44bd92
FG
1817 my $driver = readlink "/sys/class/net/$name/device/driver" || 'unknown';
1818 $driver =~ s!^.*/!!;
1819
1820 $ifaces->{"$index"} = {
1821 name => $name,
1822 driver => $driver,
1823 flags => $flags,
1824 state => $state,
1825 mac => $mac,
1826 };
1827
1828 my $addresses = `ip -o a s $name`;
1829 foreach my $a (split /\n/,$addresses) {
1830 my ($family, $ip, $prefix) = $a =~ m/^\Q$index\E:\s+\Q$name\E\s+(inet|inet6)\s+($IPRE)\/(\d+)\s+/;
1831 next if !$ip;
32b6fbcf 1832 next if $a =~ /scope\s+link/; # ignore link local
fe44bd92
FG
1833
1834 my $mask = $prefix;
1835
1836 if ($family eq 'inet') {
1837 next if !$ip =~ /$IPV4RE/;
1838 next if $prefix < 8 || $prefix > 32;
1839 $mask = @$ipv4_reverse_mask[$prefix];
1840 } else {
1841 next if !$ip =~ /$IPV6RE/;
1842 }
1843
1844 $default = $index if !$default;
1845
1846 $ifaces->{"$index"}->{"$family"} = {
1847 mask => $mask,
1848 addr => $ip,
1849 };
1850 }
1851 }
1852
1853
1854 my $route = `ip route`;
1855 my ($gateway) = $route =~ m/^default\s+via\s+(\S+)\s+/m;
89a12446
DM
1856
1857 my $resolvconf = `cat /etc/resolv.conf`;
1858 my ($dnsserver) = $resolvconf =~ m/^nameserver\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/m;
713790a4 1859 my ($domain) = $resolvconf =~ m/^domain\s+(\S+)$/m;
89a12446
DM
1860
1861 return {
fe44bd92
FG
1862 default => $default,
1863 ifaces => $ifaces,
89a12446
DM
1864 gateway => $gateway,
1865 dnsserver => $dnsserver,
713790a4 1866 domain => $domain,
89a12446
DM
1867 }
1868}
1869
1870sub display_message {
1871 my ($msg) = @_;
1872
7becc472 1873 my $dialog = Gtk3::MessageDialog->new ($window, 'modal',
89a12446
DM
1874 'info', 'ok', $msg);
1875 $dialog->run();
1876 $dialog->destroy();
1877}
1878
1879sub display_error {
1880 my ($msg) = @_;
1881
7becc472 1882 my $dialog = Gtk3::MessageDialog->new ($window, 'modal',
89a12446
DM
1883 'error', 'ok', $msg);
1884 $dialog->run();
1885 $dialog->destroy();
1886}
1887
fe44bd92
FG
1888my $ipconf_first_view = 1;
1889
89a12446
DM
1890sub create_ipconf_view {
1891
1892 cleanup_view ();
1893 display_html ("ipconf.htm");
1894
7becc472 1895 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 1896 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 1897 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 1898 $vbox->pack_start ($hbox, 0, 0, 10);
7becc472 1899 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446
DM
1900 $hbox->add ($vbox2);
1901
fe44bd92
FG
1902 my $ipbox;
1903 ($ipbox, $ipconf_entry_addr) =
1904 create_text_input ("192.168.100.2", 'IP Address:');
1905
1906 my $maskbox;
1907 ($maskbox, $ipconf_entry_mask) =
1908 create_text_input ("255.255.255.0", 'Netmask:');
1909
1910 my $device_cb = Gtk3::ComboBoxText->new();
1911 $device_cb->set_active(0);
1912 $device_cb->set_visible(1);
1913
1914 my $get_device_desc = sub {
1915 my $iface = shift;
1916 return "$iface->{name} - $iface->{mac} ($iface->{driver})";
1917 };
1918
1919 my $device_active_map = {};
5b6ba737
FG
1920
1921 my $device_change_handler = sub {
1922 my $current = shift;
1923 $ipconf->{selected} = $device_active_map->{$current->get_active()};
1924 my $iface = $ipconf->{ifaces}->{$ipconf->{selected}};
1925 $ipconf_entry_addr->set_text($iface->{inet}->{addr} || $iface->{inet6}->{addr})
1926 if $iface->{inet}->{addr} || $iface->{inet6}->{addr};
1927 $ipconf_entry_mask->set_text($iface->{inet}->{mask} || $iface->{inet6}->{mask})
1928 if $iface->{inet}->{mask} || $iface->{inet6}->{mask};
1929 };
1930
fe44bd92
FG
1931 my $i = 0;
1932 foreach my $index (sort keys %{$ipconf->{ifaces}}) {
1933 $device_cb->append_text(&$get_device_desc($ipconf->{ifaces}->{$index}));
1934 $device_active_map->{$i} = $index;
1935 if ($ipconf_first_view && $index == $ipconf->{default}) {
1936 $device_cb->set_active($i);
5b6ba737 1937 &$device_change_handler($device_cb);
fe44bd92
FG
1938 $ipconf_first_view = 0;
1939 }
5b6ba737 1940 $device_cb->signal_connect ('changed' => $device_change_handler);
fe44bd92
FG
1941 $i++;
1942 }
1943
5b6ba737
FG
1944 $device_cb->set_active(0)
1945 if !($ipconf->{selected});
1946
fe44bd92
FG
1947 my $devicebox = Gtk3::HBox->new (0, 0);
1948 my $label = Gtk3::Label->new ("Management Interface:");
1949 $label->set_size_request (150, -1);
1950 $label->set_alignment (1, 0.5);
1951 $devicebox->pack_start ($label, 0, 0, 10);
1952 $devicebox->pack_start ($device_cb, 0, 0, 0);
1953
1954 $vbox2->pack_start ($devicebox, 0, 0, 2);
968fa90b 1955
a39bc1f2 1956 my $hn = $ipconf->{domain} ? "pve.$ipconf->{domain}" : 'pve.example.invalid';
713790a4 1957
968fa90b 1958 my ($hostbox, $hostentry) =
d2120e51 1959 create_text_input ($hn, 'Hostname (FQDN):');
89a12446
DM
1960 $vbox2->pack_start ($hostbox, 0, 0, 2);
1961
89a12446
DM
1962 $vbox2->pack_start ($ipbox, 0, 0, 2);
1963
89a12446
DM
1964 $vbox2->pack_start ($maskbox, 0, 0, 2);
1965
1966 $gateway = $ipconf->{gateway} || '192.168.100.1';
1967
1968 my $gwbox;
d2120e51
DM
1969 ($gwbox, $ipconf_entry_gw) =
1970 create_text_input ($gateway, 'Gateway:');
89a12446 1971
53986d77 1972 $vbox2->pack_start ($gwbox, 0, 0, 2);
89a12446
DM
1973
1974 $dnsserver = $ipconf->{dnsserver} || $gateway;
1975
1976 my $dnsbox;
d2120e51
DM
1977 ($dnsbox, $ipconf_entry_dns) =
1978 create_text_input ($dnsserver, 'DNS Server:');
89a12446
DM
1979
1980 $vbox2->pack_start ($dnsbox, 0, 0, 0);
1981
1982 $inbox->show_all;
968fa90b 1983 set_next (undef, sub {
d2120e51
DM
1984
1985 # verify hostname
1986
89a12446 1987 my $text = $hostentry->get_text();
968fa90b 1988
89a12446
DM
1989 $text =~ s/^\s+//;
1990 $text =~ s/\s+$//;
1991
ac3757a9 1992 my $namere = "([a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)";
968fa90b 1993
24973868
WB
1994 # Debian does not support purely numeric hostnames
1995 if ($text && $text =~ /^[0-9]+(?:\.|$)/) {
1996 display_message("Purely numeric hostnames are not allowed.");
1997 $hostentry->grab_focus();
1998 return;
1999 }
2000
a39bc1f2 2001 if ($text && $text =~ m/^(${namere}\.)*${namere}$/ && $text !~ m/.example.invalid$/ &&
89a12446
DM
2002 $text =~ m/^([^\.]+)\.(\S+)$/) {
2003 $hostname = $1;
2004 $domain = $2;
d2120e51
DM
2005 } else {
2006 display_message ("Hostname does not look like a fully qualified domain name.");
2007 $hostentry->grab_focus();
89a12446
DM
2008 return;
2009 }
d2120e51
DM
2010
2011 # verify ip address
2012
2013 $text = $ipconf_entry_addr->get_text();
2014 $text =~ s/^\s+//;
2015 $text =~ s/\s+$//;
2016 if ($text =~ m!^($IPV4RE)$!) {
2017 $ipaddress = $text;
b6200603
DM
2018 $ipversion = 4;
2019 } elsif ($text =~ m!^($IPV6RE)$!) {
2020 $ipaddress = $text;
2021 $ipversion = 6;
d2120e51
DM
2022 } else {
2023 display_message ("IP address is not valid.");
2024 $ipconf_entry_addr->grab_focus();
2025 return;
2026 }
2027
2028 $text = $ipconf_entry_mask->get_text();
2029 $text =~ s/^\s+//;
2030 $text =~ s/\s+$//;
b6200603
DM
2031 if (($ipversion == 6) && ($text =~ m/^(\d+)$/) && ($1 >= 8) && ($1 <= 126)) {
2032 $netmask = $text;
2033 } elsif (($ipversion == 4) && defined($ipv4_mask_hash->{$text})) {
d2120e51
DM
2034 $netmask = $text;
2035 } else {
2036 display_message ("Netmask is not valid.");
2037 $ipconf_entry_mask->grab_focus();
2038 return;
2039 }
2040
2041 $text = $ipconf_entry_gw->get_text();
2042 $text =~ s/^\s+//;
2043 $text =~ s/\s+$//;
b6200603
DM
2044 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2045 $gateway = $text;
2046 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2047 $gateway = $text;
2048 } else {
2049 display_message ("Gateway is not valid.");
2050 $ipconf_entry_gw->grab_focus();
2051 return;
2052 }
2053
2054 $text = $ipconf_entry_dns->get_text();
2055 $text =~ s/^\s+//;
2056 $text =~ s/\s+$//;
b6200603
DM
2057 if (($ipversion == 4) && ($text =~ m!^($IPV4RE)$!)) {
2058 $dnsserver = $text;
2059 } elsif (($ipversion == 6) && ($text =~ m!^($IPV6RE)$!)) {
d2120e51
DM
2060 $dnsserver = $text;
2061 } else {
2062 display_message ("DNS server is not valid.");
2063 $ipconf_entry_dns->grab_focus();
2064 return;
2065 }
2066
2067 #print "TEST $ipaddress $netmask $gateway $dnsserver\n";
2068
2069 create_extract_view ();
89a12446
DM
2070 });
2071
2072 $hostentry->grab_focus();
2073}
2074
2075sub get_device_desc {
2076 my ($devname, $size, $model) = @_;
2077
d2120e51 2078 if ($size && ($size > 0)) {
1bd457bb 2079 $size = int($size/2048); # size in MB, from 512B "sectors"
89a12446 2080
d2120e51 2081 my $text = "$devname (";
89a12446
DM
2082 if ($size >= 1024) {
2083 $size = int($size/1024); # size in GB
d2120e51 2084 $text .= "${size}GB";
89a12446 2085 } else {
d2120e51 2086 $text .= "${size}MB";
89a12446
DM
2087 }
2088
d2120e51
DM
2089 $text .= ", $model" if $model;
2090 $text .= ")";
2091
89a12446
DM
2092 } else {
2093 return $devname;
2094 }
2095}
2096
2097sub update_layout {
2098 my ($cb, $kmap) = @_;
2099
2100 my $ind;
2101 my $def;
2102 my $i = 0;
2103 my $kmaphash = $cmap->{kmaphash};
2104 foreach my $layout (sort keys %$kmaphash) {
2105 $def = $i if $kmaphash->{$layout} eq 'en-us';
2106 $ind = $i if $kmap && $kmaphash->{$layout} eq $kmap;
2107 $i++;
2108 }
2109
2110 $cb->set_active ($ind || $def || 0);
2111}
2112
2113my $lastzonecb;
2114sub update_zonelist {
2115 my ($box, $cc) = @_;
2116
2117 my $cczones = $cmap->{cczones};
2118 my $zones = $cmap->{zones};
2119
2120 my $sel;
2121 if ($lastzonecb) {
2122 $sel = $lastzonecb->get_active_text();
2123 $box->remove ($lastzonecb);
2124 } else {
2125 $sel = $timezone; # used once to select default
2126 }
2127
bcbfab6b 2128 my $cb = $lastzonecb = Gtk3::ComboBoxText->new();
89a12446
DM
2129 $cb->set_size_request (200, -1);
2130
2131 $cb->signal_connect ('changed' => sub {
2132 $timezone = $cb->get_active_text();
2133 });
2134
2135 my @za;
2136 if ($cc && defined ($cczones->{$cc})) {
2137 @za = keys %{$cczones->{$cc}};
2138 } else {
2139 @za = keys %$zones;
2140 }
2141 my $ind;
2142 my $i = 0;
2143 foreach my $zone (sort @za) {
2144 $ind = $i if $sel && $zone eq $sel;
2145 $cb->append_text ($zone);
2146 $i++;
2147 }
2148
2149 $cb->set_active ($ind || 0);
2150
2151 $cb->show;
2152 $box->pack_start ($cb, 0, 0, 0);
2153}
2154
2155sub create_password_view {
2156
2157 cleanup_view ();
2158
7becc472 2159 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446 2160 $inbox->pack_start ($vbox2, 1, 0, 0);
7becc472 2161 my $vbox = Gtk3::VBox->new (0, 0);
53986d77 2162 $vbox2->pack_start ($vbox, 0, 0, 10);
89a12446 2163
7becc472
DM
2164 my $hbox1 = Gtk3::HBox->new (0, 0);
2165 my $label = Gtk3::Label->new ("Password");
89a12446
DM
2166 $label->set_size_request (150, -1);
2167 $label->set_alignment (1, 0.5);
2168 $hbox1->pack_start ($label, 0, 0, 10);
7becc472 2169 my $pwe1 = Gtk3::Entry->new ();
89a12446
DM
2170 $pwe1->set_visibility (0);
2171 $pwe1->set_size_request (200, -1);
2172 $hbox1->pack_start ($pwe1, 0, 0, 0);
2173
7becc472
DM
2174 my $hbox2 = Gtk3::HBox->new (0, 0);
2175 $label = Gtk3::Label->new ("Confirm");
89a12446
DM
2176 $label->set_size_request (150, -1);
2177 $label->set_alignment (1, 0.5);
2178 $hbox2->pack_start ($label, 0, 0, 10);
7becc472 2179 my $pwe2 = Gtk3::Entry->new ();
89a12446
DM
2180 $pwe2->set_visibility (0);
2181 $pwe2->set_size_request (200, -1);
2182 $hbox2->pack_start ($pwe2, 0, 0, 0);
2183
7becc472
DM
2184 my $hbox3 = Gtk3::HBox->new (0, 0);
2185 $label = Gtk3::Label->new ("E-Mail");
89a12446
DM
2186 $label->set_size_request (150, -1);
2187 $label->set_alignment (1, 0.5);
2188 $hbox3->pack_start ($label, 0, 0, 10);
7becc472 2189 my $eme = Gtk3::Entry->new ();
89a12446 2190 $eme->set_size_request (200, -1);
a39bc1f2 2191 $eme->set_text('mail@example.invalid');
89a12446
DM
2192 $hbox3->pack_start ($eme, 0, 0, 0);
2193
2194
2195 $vbox->pack_start ($hbox1, 0, 0, 5);
2196 $vbox->pack_start ($hbox2, 0, 0, 5);
2197 $vbox->pack_start ($hbox3, 0, 0, 15);
2198
2199 $inbox->show_all;
2200
2201 display_html ("passwd.htm");
2202
2203 set_next (undef, sub {
2204
2205 my $t1 = $pwe1->get_text;
2206 my $t2 = $pwe2->get_text;
2207
2208 if (length ($t1) < 5) {
2209 display_message ("Password is too short.");
2210 $pwe1->grab_focus();
2211 return;
2212 }
2213
2214 if ($t1 ne $t2) {
2215 display_message ("Password does not match.");
2216 $pwe1->grab_focus();
2217 return;
2218 }
2219
2220 my $t3 = $eme->get_text;
2221 if ($t3 !~ m/^\S+\@\S+\.\S+$/) {
034f75e4 2222 display_message ("E-Mail does not look like a valid address" .
89a12446
DM
2223 " (user\@domain.tld)");
2224 $eme->grab_focus();
2225 return;
a39bc1f2 2226 }
89a12446 2227
a39bc1f2
FG
2228 if ($t3 eq 'mail@example.invalid') {
2229 display_message ("Please enter a valid E-Mail address");
2230 $eme->grab_focus();
2231 return;
89a12446
DM
2232 }
2233
2234 $password = $t1;
2235 $mailto = $t3;
2236
2237 create_ipconf_view();
2238 });
2239
2240 $pwe1->grab_focus();
2241
2242}
2243
2244sub create_country_view {
2245
2246 cleanup_view ();
2247
2248 my $countryhash = $cmap->{countryhash};
2249 my $ctr = $cmap->{country};
2250
7becc472 2251 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446 2252 $inbox->pack_start ($vbox2, 1, 0, 0);
7becc472 2253 my $vbox = Gtk3::VBox->new (0, 0);
53986d77 2254 $vbox2->pack_start ($vbox, 0, 0, 10);
89a12446 2255
7becc472 2256 my $w = Gtk3::Entry->new ();
89a12446
DM
2257 $w->set_size_request (200, -1);
2258
7becc472 2259 my $c = Gtk3::EntryCompletion->new ();
89a12446
DM
2260 $c->set_text_column (0);
2261 $c->set_minimum_key_length(0);
2262 $c->set_popup_set_width (1);
4443aa27 2263 $c->set_inline_completion (1);
89a12446 2264
7becc472
DM
2265 my $hbox2 = Gtk3::HBox->new (0, 0);
2266 my $label = Gtk3::Label->new ("Time zone");
89a12446
DM
2267 $label->set_size_request (150, -1);
2268 $label->set_alignment (1, 0.5);
2269 $hbox2->pack_start ($label, 0, 0, 10);
2270 update_zonelist ($hbox2);
2271
7becc472
DM
2272 my $hbox3 = Gtk3::HBox->new (0, 0);
2273 $label = Gtk3::Label->new ("Keyboard Layout");
89a12446
DM
2274 $label->set_size_request (150, -1);
2275 $label->set_alignment (1, 0.5);
2276 $hbox3->pack_start ($label, 0, 0, 10);
2277
bcbfab6b 2278 my $kmapcb = Gtk3::ComboBoxText->new();
89a12446
DM
2279 $kmapcb->set_size_request (200, -1);
2280 foreach my $layout (sort keys %{$cmap->{kmaphash}}) {
2281 $kmapcb->append_text ($layout);
2282 }
2283
2284 update_layout ($kmapcb);
2285 $hbox3->pack_start ($kmapcb, 0, 0, 0);
2286
2287 $kmapcb->signal_connect ('changed' => sub {
2288 my $sel = $kmapcb->get_active_text();
2289 if (my $kmap = $cmap->{kmaphash}->{$sel}) {
2290 my $xkmap = $cmap->{kmap}->{$kmap}->{x11};
2291 my $xvar = $cmap->{kmap}->{$kmap}->{x11var};
2292 syscmd ("setxkbmap $xkmap $xvar") if !$opt_testmode;
2293 $keymap = $kmap;
2294 }
2295 });
2296
2297 $w->signal_connect ('changed' => sub {
2298 my ($entry, $event) = @_;
2299 my $text = $entry->get_text;
2300
2301 if (my $cc = $countryhash->{lc($text)}) {
2302 update_zonelist ($hbox2, $cc);
2303 my $kmap = $ctr->{$cc}->{kmap} || 'en-us';
2304 update_layout ($kmapcb, $kmap);
2305 }
2306 });
2307
2308 $w->signal_connect (key_press_event => sub {
2309 my ($entry, $event) = @_;
2310 my $text = $entry->get_text;
2311
7becc472
DM
2312 my $val = $event->get_keyval;
2313
2314 if ($val == Gtk3::Gdk::KEY_Tab) {
89a12446 2315 my $cc = $countryhash->{lc($text)};
4443aa27 2316
89a12446
DM
2317 my $found = 0;
2318 my $compl;
7becc472 2319
4443aa27
DM
2320 if ($cc) {
2321 $found = 1;
2322 $compl = $ctr->{$cc}->{name};
2323 } else {
2324 foreach my $cc (keys %$ctr) {
2325 my $ct = $ctr->{$cc}->{name};
2326 if ($ct =~ m/^\Q$text\E.*$/i) {
2327 $found++;
2328 $compl = $ct;
2329 }
2330 last if $found > 1;
89a12446 2331 }
89a12446 2332 }
4443aa27 2333
89a12446 2334 if ($found == 1) {
7becc472 2335 $entry->set_text($compl);
3df718ea 2336 $c->complete();
89a12446
DM
2337 return undef;
2338 } else {
7becc472
DM
2339 #Gtk3::Gdk::beep();
2340 print chr(7); # beep ?
89a12446
DM
2341 }
2342
3df718ea
DM
2343 $c->complete();
2344
7becc472
DM
2345 my $buf = $w->get_buffer();
2346 $buf->insert_text(-1, '', -1); # popup selection
2347
89a12446
DM
2348 return 1;
2349 }
2350
2351 return undef;
2352 });
7becc472
DM
2353
2354 my $ls = Gtk3::ListStore->new('Glib::String');
89a12446
DM
2355 foreach my $cc (sort {$ctr->{$a}->{name} cmp $ctr->{$b}->{name} } keys %$ctr) {
2356 my $iter = $ls->append();
2357 $ls->set ($iter, 0, $ctr->{$cc}->{name});
2358 }
2359 $c->set_model ($ls);
2360
968fa90b 2361 $w->set_completion ($c);
89a12446 2362
7becc472 2363 my $hbox = Gtk3::HBox->new (0, 0);
89a12446 2364
7becc472 2365 $label = Gtk3::Label->new ("Country");
89a12446
DM
2366 $label->set_alignment (1, 0.5);
2367 $label->set_size_request (150, -1);
2368 $hbox->pack_start ($label, 0, 0, 10);
2369 $hbox->pack_start ($w, 0, 0, 0);
2370
2371 $vbox->pack_start ($hbox, 0, 0, 5);
2372 $vbox->pack_start ($hbox2, 0, 0, 5);
2373 $vbox->pack_start ($hbox3, 0, 0, 5);
2374
9d1f1ee3 2375 if ($country && $ctr->{$country}) {
89a12446
DM
2376 $w->set_text ($ctr->{$country}->{name});
2377 }
2378
2379 $inbox->show_all;
2380
2381 display_html ("country.htm");
2382 set_next (undef, sub {
2383
2384 my $text = $w->get_text;
2385
2386 if (my $cc = $countryhash->{lc($text)}) {
2387 $country = $cc;
2388 create_password_view();
2389 return;
2390 } else {
2391 display_message ("Please select a country first.");
2392 $w->grab_focus();
2393 }
2394 });
2395
2396 $w->grab_focus();
2397}
2398
c6ed3b24
DM
2399my $target_hd_combo;
2400my $target_hd_label;
2401
2402my $hdopion_first_setup = 1;
2403
c7779156
FG
2404my $create_basic_grid = sub {
2405 my $grid = Gtk3::Grid->new();
2406 $grid->set_visible(1);
2407 $grid->set_column_spacing(10);
2408 $grid->set_row_spacing(10);
2409 $grid->set_hexpand(1);
2410
2411 $grid->set_margin_start(5);
2412 $grid->set_margin_end(5);
2413 $grid->set_margin_top(5);
2414 $grid->set_margin_bottom(5);
2415
2416 return $grid;
2417};
2418
2419my $create_label_widget_grid = sub {
2420 my ($labeled_widgets) = @_;
2421
2422 my $grid = &$create_basic_grid();
2423 my $row = 0;
2424
2425 for (my $i = 0; $i < @$labeled_widgets; $i += 2) {
2426 my $widget = @$labeled_widgets[$i+1];
2427 my $label = Gtk3::Label->new(@$labeled_widgets[$i]);
2428 $label->set_visible(1);
2429 $label->set_alignment (1, 0.5);
2430 $grid->attach($label, 0, $row, 1, 1);
2431 $widget->set_visible(1);
2432 $grid->attach($widget, 1, $row, 1, 1);
2433 $row++;
2434 }
2435
2436 return $grid;
2437};
2438
2439my $create_raid_disk_grid = sub {
2440 my $disk_labeled_widgets = [];
2441 for (my $i = 0; $i < @$hds; $i++) {
2442 my $disk_selector = Gtk3::ComboBoxText->new();
2443 $disk_selector->append_text("-- do not use --");
2444 $disk_selector->set_active(0);
2445 $disk_selector->set_visible(1);
2446 foreach my $hd (@$hds) {
2447 my ($disk, $devname, $size, $model) = @$hd;
2448 $disk_selector->append_text(get_device_desc ($devname, $size, $model));
2449 $disk_selector->{pve_disk_id} = $i;
2450 $disk_selector->signal_connect (changed => sub {
2451 my $w = shift;
2452 my $diskid = $w->{pve_disk_id};
2453 my $a = $w->get_active - 1;
2454 $config_options->{"disksel${diskid}"} = ($a >= 0) ? $hds->[$a] : undef;
2455 });
2456 }
2457
2458 if ($hdopion_first_setup) {
2459 $disk_selector->set_active ($i+1) if $hds->[$i];
2460 } else {
2461 my $hdind = 0;
2462 if (my $cur_hd = $config_options->{"disksel$i"}) {
2463 foreach my $hd (@$hds) {
2464 if (@$hd[1] eq @$cur_hd[1]) {
2465 $disk_selector->set_active($hdind+1);
2466 last;
2467 }
2468 $hdind++;
2469 }
2470 }
2471 }
2472
2473 push @$disk_labeled_widgets, "Harddisk $i", $disk_selector;
2474 }
2475
2476 my $scrolled_window = Gtk3::ScrolledWindow->new();
2477 $scrolled_window->set_hexpand(1);
650a9aab 2478 $scrolled_window->set_propagate_natural_height(1) if @$hds > 4;
c7779156
FG
2479 $scrolled_window->add(&$create_label_widget_grid($disk_labeled_widgets));
2480 $scrolled_window->set_policy('never', 'automatic');
2481
2482 return $scrolled_window;
2483# &$create_label_widget_grid($disk_labeled_widgets)
2484};
2485
2486my $create_raid_advanced_grid = sub {
2487 my $labeled_widgets = [];
6c99667a
FG
2488 my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9,13,1);
2489 $spinbutton_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
2490 $spinbutton_ashift->signal_connect ("value-changed" => sub {
2491 my $w = shift;
2492 $config_options->{ashift} = $w->get_value_as_int();
c7779156
FG
2493 });
2494 $config_options->{ashift} = 12 if ! defined($config_options->{ashift});
6c99667a 2495 $spinbutton_ashift->set_value($config_options->{ashift});
c7779156 2496 push @$labeled_widgets, "ashift";
6c99667a 2497 push @$labeled_widgets, $spinbutton_ashift;
c7779156
FG
2498
2499 my $combo_compress = Gtk3::ComboBoxText->new();
2500 $combo_compress->set_tooltip_text("zfs compression algorithm for rpool dataset");
2501 # note: gzip / lze not allowed for bootfs vdevs
2502 my $comp_opts = ["on","off","lzjb","lz4"];
2503 foreach my $opt (@$comp_opts) {
2504 $combo_compress->append($opt, $opt);
2505 }
2506 $config_options->{compress} = "on" if !defined($config_options->{compress});
2507 $combo_compress->set_active_id($config_options->{compress});
2508 $combo_compress->signal_connect (changed => sub {
2509 my $w = shift;
2510 $config_options->{compress} = $w->get_active_text();
2511 });
2512 push @$labeled_widgets, "compress";
2513 push @$labeled_widgets, $combo_compress;
2514
2515 my $combo_checksum = Gtk3::ComboBoxText->new();
2516 $combo_checksum->set_tooltip_text("zfs checksum algorithm for rpool dataset");
2517 my $csum_opts = ["on", "off","fletcher2", "fletcher4", "sha256"];
2518 foreach my $opt (@$csum_opts) {
2519 $combo_checksum->append($opt, $opt);
2520 }
2521 $config_options->{checksum} = "on" if !($config_options->{checksum});
2522 $combo_checksum->set_active_id($config_options->{checksum});
2523 $combo_checksum->signal_connect (changed => sub {
2524 my $w = shift;
2525 $config_options->{checksum} = $w->get_active_text();
2526 });
2527 push @$labeled_widgets, "checksum";
2528 push @$labeled_widgets, $combo_checksum;
2529
2530 my $spinbutton_copies = Gtk3::SpinButton->new_with_range(1,3,1);
2531 $spinbutton_copies->set_tooltip_text("zfs copies property for rpool dataset (in addition to RAID redundancy!)");
2532 $spinbutton_copies->signal_connect ("value-changed" => sub {
2533 my $w = shift;
2534 $config_options->{copies} = $w->get_value_as_int();
c7779156
FG
2535 });
2536 $config_options->{copies} = 1 if !defined($config_options->{copies});
2537 $spinbutton_copies->set_value($config_options->{copies});
2538 push @$labeled_widgets, "copies", $spinbutton_copies;
2539
2540 return &$create_label_widget_grid($labeled_widgets);;
2541};
2542
aed81ff0
DM
2543sub create_hdoption_view {
2544
2545 my $dialog = Gtk3::Dialog->new();
2546
2547 $dialog->set_title("Harddisk options");
2548
2549 $dialog->add_button("_OK", 1);
2550
2551 my $contarea = $dialog->get_content_area();
2552
2553 my $hbox2 = Gtk3::Box->new('horizontal', 0);
2554 $contarea->pack_start($hbox2, 1, 1, 10);
2555
2556 my $grid = Gtk3::Grid->new();
2557 $grid->set_column_spacing(10);
2558 $grid->set_row_spacing(10);
c6ed3b24 2559
aed81ff0 2560 $hbox2->pack_start($grid, 1, 0, 10);
c6ed3b24
DM
2561
2562 my $row = 0;
2563
aed81ff0
DM
2564 # Filesystem type
2565
2566 my $label0 = Gtk3::Label->new ("Filesystem");
2567 $label0->set_alignment (1, 0.5);
c6ed3b24
DM
2568 $grid->attach($label0, 0, $row, 1, 1);
2569
bcbfab6b 2570 my $fstypecb = Gtk3::ComboBoxText->new();
aed81ff0 2571
121ebc59
DM
2572 my $fstype = ['ext3', 'ext4', 'xfs',
2573 'zfs (RAID0)', 'zfs (RAID1)',
2574 'zfs (RAID10)', 'zfs (RAIDZ-1)',
6f52fc3d
DM
2575 'zfs (RAIDZ-2)', 'zfs (RAIDZ-3)'];
2576
2577 push @$fstype, 'btrfs (RAID0)', 'btrfs (RAID1)', 'btrfs (RAID10)'
c20d6ab0 2578 if $setup->{enable_btrfs};
aed81ff0 2579
c6ed3b24
DM
2580 my $tcount = 0;
2581 foreach my $tmp (@$fstype) {
2582 $fstypecb->append_text($tmp);
2583 $fstypecb->set_active ($tcount)
2584 if $config_options->{filesys} eq $tmp;
2585 $tcount++;
2586 }
2587
2588 $grid->attach($fstypecb, 1, $row, 1, 1);
2589
2590 $hbox2->show_all();
2591
2592 $row++;
2593
c7779156
FG
2594 my $sep = Gtk3::HSeparator->new();
2595 $sep->set_visible(1);
2596 $grid->attach($sep, 0, $row, 2, 1);
2597 $row++;
aed81ff0 2598
c7779156 2599 my $hdsize_labeled_widgets = [];
aed81ff0 2600
c7779156 2601 # size compute
c6ed3b24 2602 my $hdsize = 0;
aed81ff0
DM
2603 if ( -b $target_hd) {
2604 $hdsize = int(hd_size ($target_hd) / (1024*1024.0)); # size in GB
c6ed3b24 2605 } elsif ($target_hd) {
aed81ff0
DM
2606 $hdsize = int((-s $target_hd) / (1024*1024*1024.0));
2607 }
2608
2609 my $hdsize_size_adj = Gtk3::Adjustment->new($config_options->{hdsize} || $hdsize, 0, $hdsize+1, 1, 1, 1);
2610 my $spinbutton_hdsize = Gtk3::SpinButton->new($hdsize_size_adj, 1, 1);
2611 $spinbutton_hdsize->set_tooltip_text("only use specified size (GB) of the harddisk (rest left unpartitioned)");
c7779156 2612 push @$hdsize_labeled_widgets, "hdsize", $spinbutton_hdsize;
aed81ff0
DM
2613
2614 my $entry_swapsize = Gtk3::Entry->new();
2615 $entry_swapsize->set_tooltip_text("maximum SWAP size (GB)");
2616 $entry_swapsize->signal_connect (key_press_event => \&check_float);
2617 $entry_swapsize->set_text($config_options->{swapsize}) if $config_options->{swapsize};
c7779156 2618 push @$hdsize_labeled_widgets, "swapsize", $entry_swapsize;
aed81ff0
DM
2619
2620 my $entry_maxroot = Gtk3::Entry->new();
2621 $entry_maxroot->set_tooltip_text("maximum size (GB) for LVM root volume");
2622 $entry_maxroot->signal_connect (key_press_event => \&check_float);
2623 $entry_maxroot->set_text($config_options->{maxroot}) if $config_options->{maxroot};
c7779156 2624 push @$hdsize_labeled_widgets, "maxroot", $entry_maxroot;
aed81ff0
DM
2625
2626 my $entry_minfree = Gtk3::Entry->new();
034f75e4 2627 $entry_minfree->set_tooltip_text("minimum free LVM space (GB, required for LVM snapshots)");
aed81ff0
DM
2628 $entry_minfree->signal_connect (key_press_event => \&check_float);
2629 $entry_minfree->set_text($config_options->{minfree}) if $config_options->{minfree};
c7779156 2630 push @$hdsize_labeled_widgets, "minfree", $entry_minfree;
aed81ff0
DM
2631
2632 my $entry_maxvz = Gtk3::Entry->new();
2633 $entry_maxvz->set_tooltip_text("maximum size (GB) for LVM data volume");
2634 $entry_maxvz->signal_connect (key_press_event => \&check_float);
2635 $entry_maxvz->set_text($config_options->{maxvz}) if $config_options->{maxvz};
c7779156
FG
2636 push @$hdsize_labeled_widgets, "maxvz", $entry_maxvz;
2637
2638 my $options_stack = Gtk3::Stack->new();
2639 $options_stack->set_visible(1);
2640 $options_stack->set_hexpand(1);
2641 $options_stack->set_vexpand(1);
2642 $options_stack->add_titled(&$create_raid_disk_grid(), "raiddisk", "Disk Setup");
2643 $options_stack->add_titled(&$create_label_widget_grid($hdsize_labeled_widgets), "hdsize", "Size Options");
2644 $options_stack->add_titled(&$create_raid_advanced_grid("zfs"), "raidzfsadvanced", "Advanced Options");
2645 $options_stack->set_visible_child_name("raiddisk");
2646 my $options_stack_switcher = Gtk3::StackSwitcher->new();
2647 $options_stack_switcher->set_halign('center');
2648 $options_stack_switcher->set_stack($options_stack);
2649 $grid->attach($options_stack_switcher, 0, $row, 2, 1);
2650 $row++;
2651 $grid->attach($options_stack, 0, $row, 2, 1);
c6ed3b24 2652 $row++;
aed81ff0 2653
c7779156
FG
2654 $hdopion_first_setup = 0;
2655
2656 my $switch_view = sub {
2657 my $raid = $config_options->{filesys} =~ m/zfs|btrfs/;
2658 my $enable_zfs_opts = $config_options->{filesys} =~ m/zfs/;
c6ed3b24 2659
c7779156
FG
2660 $target_hd_combo->set_visible(!$raid);
2661 $options_stack->get_child_by_name("hdsize")->set_visible(!$raid);
2662 $options_stack->get_child_by_name("raiddisk")->set_visible($raid);
2663 $options_stack_switcher->set_visible($enable_zfs_opts);
2664 $options_stack->get_child_by_name("raidzfsadvanced")->set_visible($enable_zfs_opts);
2665 if ($raid) {
c6ed3b24 2666 $target_hd_label->set_text("Target: $config_options->{filesys} ");
c7779156 2667 $options_stack->set_visible_child_name("raiddisk");
c6ed3b24 2668 } else {
c6ed3b24
DM
2669 $target_hd_label->set_text("Target Harddisk: ");
2670 }
c7779156
FG
2671 my (undef, $pref_width) = $dialog->get_preferred_width();
2672 my (undef, $pref_height) = $dialog->get_preferred_height();
650a9aab 2673 $pref_height = 750 if $pref_height > 750;
c7779156 2674 $dialog->resize($pref_width, $pref_height);
f7b853d1
DM
2675 };
2676
c7779156 2677 &$switch_view();
f7b853d1
DM
2678
2679 $fstypecb->signal_connect (changed => sub {
2680 $config_options->{filesys} = $fstypecb->get_active_text();
c7779156 2681 &$switch_view();
f7b853d1
DM
2682 });
2683
c6ed3b24 2684 $dialog->show();
aed81ff0
DM
2685
2686 $dialog->run();
2687
2688 my $get_float = sub {
2689 my ($entry) = @_;
2690
2691 my $text = $entry->get_text();
2692 return undef if !defined($text);
2693
2694 $text =~ s/^\s+//;
2695 $text =~ s/\s+$//;
2696
2697 return undef if $text !~ m/^\d+(\.\d+)?$/;
2698
2699 return $text;
2700 };
2701
2702 my $tmp;
2703
2704 if (($tmp = &$get_float($spinbutton_hdsize)) && ($tmp != $hdsize)) {
2705 $config_options->{hdsize} = $tmp;
2706 } else {
2707 delete $config_options->{hdsize};
2708 }
2709
2710 if (defined($tmp = &$get_float($entry_swapsize))) {
2711 $config_options->{swapsize} = $tmp;
2712 } else {
2713 delete $config_options->{swapsize};
2714 }
2715
2716 if (defined($tmp = &$get_float($entry_maxroot))) {
2717 $config_options->{maxroot} = $tmp;
2718 } else {
2719 delete $config_options->{maxroot};
2720 }
2721
2722 if (defined($tmp = &$get_float($entry_minfree))) {
2723 $config_options->{minfree} = $tmp;
2724 } else {
2725 delete $config_options->{minfree};
2726 }
2727
2728 if (defined($tmp = &$get_float($entry_maxvz))) {
2729 $config_options->{maxvz} = $tmp;
2730 } else {
2731 delete $config_options->{maxvz};
2732 }
2733
2734 $dialog->destroy();
2735}
2736
121ebc59 2737my $get_raid_devlist = sub {
c6ed3b24
DM
2738
2739 my $dev_name_hash = {};
2740
2741 my $devlist = [];
5f8e86d5 2742 for (my $i = 0; $i < @$hds; $i++) {
c6ed3b24
DM
2743 if (my $hd = $config_options->{"disksel$i"}) {
2744 my ($disk, $devname, $size, $model) = @$hd;
2745 die "device '$devname' is used more than once\n"
2746 if $dev_name_hash->{$devname};
2747 $dev_name_hash->{$devname} = $hd;
2748 push @$devlist, $hd;
2749 }
2750 }
2751
121ebc59
DM
2752 return $devlist;
2753};
2754
14aacec8
FG
2755sub zfs_mirror_size_check {
2756 my ($expected, $actual) = @_;
2757
2758 die "mirrored disks must have same size\n"
2759 if abs($expected - $actual) > $expected / 10;
2760}
2761
121ebc59
DM
2762sub get_zfs_raid_setup {
2763
2764 my $filesys = $config_options->{filesys};
2765
2766 my $devlist = &$get_raid_devlist();
2767
224bb7b0 2768 my $diskcount = scalar(@$devlist);
0cfa502c 2769 die "$filesys needs at least one device\n" if $diskcount < 1;
c6ed3b24 2770
121ebc59
DM
2771 my $bootdevlist = [];
2772
c6ed3b24
DM
2773 my $cmd= '';
2774 if ($filesys eq 'zfs (RAID0)') {
2775 push @$bootdevlist, @$devlist[0];
2776 foreach my $hd (@$devlist) {
2777 $cmd .= " @$hd[1]";
2778 }
2779 } elsif ($filesys eq 'zfs (RAID1)') {
0cfa502c 2780 die "zfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
c6ed3b24 2781 $cmd .= ' mirror ';
269c66a6 2782 my $hd = @$devlist[0];
14aacec8 2783 my $expected_size = @$hd[2]; # all disks need approximately same size
269c66a6 2784 foreach $hd (@$devlist) {
14aacec8 2785 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
2786 $cmd .= " @$hd[1]";
2787 push @$bootdevlist, $hd;
2788 }
2789 } elsif ($filesys eq 'zfs (RAID10)') {
0cfa502c 2790 die "zfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
b8f4f0f9 2791 die "zfs (RAID10) needs an even number of devices\n" if $diskcount & 1;
c6ed3b24
DM
2792
2793 push @$bootdevlist, @$devlist[0], @$devlist[1];
2794
224bb7b0 2795 for (my $i = 0; $i < $diskcount; $i+=2) {
c6ed3b24
DM
2796 my $hd1 = @$devlist[$i];
2797 my $hd2 = @$devlist[$i+1];
14aacec8 2798 zfs_mirror_size_check(@$hd1[2], @$hd2[2]); # pairs need approximately same size
c6ed3b24
DM
2799 $cmd .= ' mirror ' . @$hd1[1] . ' ' . @$hd2[1];
2800 }
2801
2802 } elsif ($filesys =~ m/^zfs \(RAIDZ-([123])\)$/) {
2803 my $level = $1;
2804 my $mindisks = 2 + $level;
0cfa502c 2805 die "zfs (RAIDZ-$level) needs at least $mindisks devices\n" if scalar(@$devlist) < $mindisks;
269c66a6 2806 my $hd = @$devlist[0];
14aacec8 2807 my $expected_size = @$hd[2]; # all disks need approximately same size
097ecf8f 2808 $cmd .= " raidz$level";
269c66a6 2809 foreach $hd (@$devlist) {
14aacec8 2810 zfs_mirror_size_check($expected_size, @$hd[2]);
c6ed3b24
DM
2811 $cmd .= " @$hd[1]";
2812 push @$bootdevlist, $hd;
2813 }
2814 } else {
2815 die "unknown zfs mode '$filesys'\n";
2816 }
2817
2818 return ($devlist, $bootdevlist, $cmd);
2819}
2820
121ebc59
DM
2821sub get_btrfs_raid_setup {
2822
2823 my $filesys = $config_options->{filesys};
2824
2825 my $devlist = &$get_raid_devlist();
2826
2827 my $diskcount = scalar(@$devlist);
0cfa502c 2828 die "$filesys needs at least one device\n" if $diskcount < 1;
121ebc59
DM
2829
2830 my $mode;
2831
2832 if ($diskcount == 1) {
2833 $mode = 'single';
2834 } else {
2835 if ($filesys eq 'btrfs (RAID0)') {
2836 $mode = 'raid0';
2837 } elsif ($filesys eq 'btrfs (RAID1)') {
0cfa502c 2838 die "btrfs (RAID1) needs at least 2 device\n" if $diskcount < 2;
121ebc59
DM
2839 $mode = 'raid1';
2840 } elsif ($filesys eq 'btrfs (RAID10)') {
0cfa502c 2841 die "btrfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
121ebc59
DM
2842 $mode = 'raid10';
2843 } else {
9d69f3d3 2844 die "unknown btrfs mode '$filesys'\n";
121ebc59
DM
2845 }
2846 }
2847
2848 return ($devlist, $mode);
2849}
2850
89a12446
DM
2851sub create_hdsel_view {
2852
2853 cleanup_view ();
2854
7becc472 2855 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 2856 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 2857 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 2858 $vbox->pack_start ($hbox, 0, 0, 10);
968fa90b 2859
89a12446
DM
2860 my ($disk, $devname, $size, $model) = @{@$hds[0]};
2861 $target_hd = $devname;
89a12446 2862
c6ed3b24
DM
2863 $target_hd_label = Gtk3::Label->new ("Target Harddisk: ");
2864 $hbox->pack_start ($target_hd_label, 0, 0, 0);
89a12446 2865
bcbfab6b 2866 $target_hd_combo = Gtk3::ComboBoxText->new();
89a12446 2867
1aa5bd02
DM
2868 foreach my $hd (@$hds) {
2869 ($disk, $devname, $size, $model) = @$hd;
c6ed3b24 2870 $target_hd_combo->append_text (get_device_desc ($devname, $size, $model));
1aa5bd02 2871 }
89a12446 2872
c6ed3b24
DM
2873 $target_hd_combo->set_active (0);
2874 $target_hd_combo->signal_connect (changed => sub {
1aa5bd02
DM
2875 $a = shift->get_active;
2876 my ($disk, $devname) = @{@$hds[$a]};
2877 $target_hd = $devname;
1aa5bd02 2878 });
c6ed3b24
DM
2879
2880 $hbox->pack_start ($target_hd_combo, 0, 0, 10);
aed81ff0
DM
2881
2882 my $options = Gtk3::Button->new ('_Options');
2883 $options->signal_connect (clicked => \&create_hdoption_view);
2884 $hbox->pack_start ($options, 0, 0, 0);
2885
89a12446
DM
2886
2887 $inbox->show_all;
2888
2889 display_html ("page1.htm");
c6ed3b24
DM
2890
2891 set_next (undef, sub {
2892
2893 if ($config_options->{filesys} =~ m/zfs/) {
2894 eval { get_zfs_raid_setup(); };
2895 if (my $err = $@) {
2896 display_message ("Warning: $err\n" .
269c66a6 2897 "Please fix ZFS setup first.");
c6ed3b24
DM
2898 } else {
2899 create_country_view();
2900 }
121ebc59
DM
2901 } elsif ($config_options->{filesys} =~ m/btrfs/) {
2902 eval { get_btrfs_raid_setup(); };
2903 if (my $err = $@) {
2904 display_message ("Warning: $err\n" .
2905 "Please fix BTRFS setup first.");
2906 } else {
2907 create_country_view();
2908 }
c6ed3b24
DM
2909 } else {
2910 create_country_view();
2911 }
2912 });
89a12446
DM
2913}
2914
2915sub create_extract_view {
2916
89a12446
DM
2917 cleanup_view ();
2918
2919 display_html ("extract1-license.htm");
2920 $next->set_sensitive (0);
2921
7becc472 2922 my $vbox = Gtk3::VBox->new (0, 0);
89a12446 2923 $inbox->pack_start ($vbox, 1, 0, 0);
7becc472 2924 my $hbox = Gtk3::HBox->new (0, 0);
53986d77 2925 $vbox->pack_start ($hbox, 0, 0, 10);
89a12446 2926
7becc472 2927 my $vbox2 = Gtk3::VBox->new (0, 0);
89a12446
DM
2928 $hbox->pack_start ($vbox2, 0, 0, 0);
2929
7becc472 2930 $progress_status = Gtk3::Label->new ('');
89a12446 2931 $vbox2->pack_start ($progress_status, 1, 1, 0);
968fa90b 2932
7becc472 2933 $progress = Gtk3::ProgressBar->new;
45feca6f 2934 $progress->set_show_text(1);
7becc472 2935 $progress->set_size_request (600, -1);
89a12446
DM
2936
2937 $vbox2->pack_start ($progress, 0, 0, 0);
2938
2939 $inbox->show_all;
2940
2941 my $tdir = $opt_testmode ? "target" : "/target";
2942 mkdir $tdir;
97980bf2 2943 my $base = "${proxmox_cddir}/$setup->{product}-base.squashfs";
89a12446
DM
2944
2945 eval { extract_data ($base, $tdir); };
2946 my $err = $@;
2947
2948 $next->set_sensitive (1);
2949
2950 set_next ("_Reboot", sub { exit (0); } );
2951
296cf41f
DM
2952 if ($err) {
2953 display_html ("fail.htm");
2954 display_error ($err);
2955 } else {
2956 cleanup_view ();
2957 display_html ("success.htm");
2958 }
89a12446
DM
2959}
2960
89a12446
DM
2961sub create_intro_view {
2962
2963 cleanup_view ();
2964
bdeca872
DM
2965 if ($setup->{product} eq 'pve') {
2966 eval {
2967 my $cpuinfo = file_get_contents('/proc/cpuinfo');
2968 if ($cpuinfo && !($cpuinfo =~ /^flags\s*:.*(vmx|svm)/m)) {
2969 display_error("No support for KVM virtualisation detected.\n\n" .
2970 "Check BIOS settings for Intel VT / AMD-V / SVM.")
2971 }
2972 };
2973 }
7fff0d85 2974
89a12446
DM
2975 display_html ("license.htm");
2976
2977 set_next ("I a_gree", \&create_hdsel_view);
2978}
2979
2980$ipconf = get_ip_config ();
2981
9d1f1ee3 2982$country = detect_country() if $ipconf->{default} || $opt_testmode;
89a12446
DM
2983
2984# read country, kmap and timezone infos
2985$cmap = read_cmap ();
2986
9d1f1ee3
FG
2987if (!defined($cmap->{country}->{$country})) {
2988 print $logfd "ignoring detected country '$country', invalid or unknown\n";
2989 $country = undef;
2990}
2991
89a12446
DM
2992create_main_window ();
2993
ff2ce71c
FG
2994my $initial_error = 0;
2995
89a12446
DM
2996if (!defined ($hds) || (scalar (@$hds) <= 0)) {
2997 print "no hardisks found\n";
ff2ce71c 2998 $initial_error = 1;
89a12446
DM
2999 display_html ("nohds.htm");
3000 set_next ("Reboot", sub { exit (0); } );
3001} else {
89a12446
DM
3002 foreach my $hd (@$hds) {
3003 my ($disk, $devname) = @$hd;
3004 next if $devname =~ m|^/dev/md\d+$|;
3005 print "found Disk$disk N:$devname\n";
3006 }
89a12446
DM
3007}
3008
72836708
FG
3009if (!$initial_error && (scalar keys %{ $ipconf->{ifaces} } == 0)) {
3010 print "no network interfaces found\n";
3011 $initial_error = 1;
3012 display_html ("nonics.htm");
3013 set_next ("Reboot", sub { exit (0); } );
3014}
3015
ff2ce71c
FG
3016create_intro_view () if !$initial_error;
3017
7becc472 3018Gtk3->main;
89a12446
DM
3019
3020exit 0;