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