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