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