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