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