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