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