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