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