use File::Basename;
use File::Path;
use Time::HiRes;
+use POSIX ":sys_wait_h";
use ProxmoxInstallerSetup;
-my $setup = ProxmoxInstallerSetup::setup();
-
-my $opt_testmode;
-
if (!$ENV{G_SLICE} || $ENV{G_SLICE} ne "always-malloc") {
die "do not use slice allocator (run with 'G_SLICE=always-malloc ./proxinstall ...')\n";
}
+my $opt_testmode;
if (!GetOptions('testmode=s' => \$opt_testmode)) {
die "usage error\n";
exit (-1);
}
+my ($setup, $cd_info) = ProxmoxInstallerSetup::setup();
+
my $zfstestpool = "test_rpool";
my $zfspoolname = $opt_testmode ? $zfstestpool : 'rpool';
my $zfsrootvolname = "$setup->{product}-1";
-my $product_cfg = {
- pve => {
- fullname => 'Proxmox VE',
- port => '8006',
- },
- pmg => {
- fullname => 'Proxmox Mail Gateway',
- port => '8006',
- },
- pbs => {
- fullname => 'Proxmox Backup Server',
- port => '8007',
- },
-};
-
my $storage_cfg_zfs = <<__EOD__;
dir: local
path /var/lib/vz
my $logfd = IO::File->new(">/tmp/install.log");
-my $proxmox_libdir = $opt_testmode ?
- Cwd::cwd() . "/testdir/var/lib/pve-installer" : "/var/lib/pve-installer";
+my $proxmox_libdir = $opt_testmode
+ ? Cwd::cwd() . "/testdir/var/lib/proxmox-installer"
+ : "/var/lib/proxmox-installer"
+ ;
my $proxmox_cddir = $opt_testmode ? "../pve-cd-builder/tmp/data-gz/" : "/cdrom";
my $proxmox_pkgdir = "${proxmox_cddir}/proxmox/packages/";
return $ostream;
}
+# forks and runs the provided coderef in the child
+# do not use syscmd or run_command as both confuse the GTK mainloop if
+# run from a child process
+sub run_in_background {
+ my ($cmd) = @_;
+
+ my $pid = fork() // die "fork failed: $!\n";
+ if (!$pid) {
+ eval { $cmd->(); };
+ if (my $err = $@) {
+ warn "run_in_background error: $err\n";
+ POSIX::_exit(1);
+ }
+ POSIX::_exit(0);
+ }
+}
+
sub detect_country {
print "trying to detect country...\n";
die "chroot: unable to change permission mode for '$path'\n";
}
-sub prepare_systemd_boot_esp {
+sub prepare_proxmox_boot_esp {
my ($espdev, $targetdir) = @_;
- syscmd("chroot $targetdir pve-efiboot-tool init $espdev") == 0 ||
- die "unable to init ESP and install systemd-boot loader on '$espdev'\n";
+ syscmd("chroot $targetdir proxmox-boot-tool init $espdev") == 0 ||
+ die "unable to init ESP and install proxmox-boot loader on '$espdev'\n";
}
sub prepare_grub_efi_boot_esp {
$disksize = $size;
}
- &$udevadm_trigger_block();
+ $udevadm_trigger_block->();
btrfs_create($btrfs_partitions, $btrfs_mode);
} elsif ($use_zfs) {
- my ($devlist, $bootdevlist, $vdev) = get_zfs_raid_setup();
+ my ($devlist, $vdev) = get_zfs_raid_setup();
foreach my $hd (@$devlist) {
- &$clean_disk(@$hd[1]);
+ $clean_disk->(@$hd[1]);
}
+ # install esp/boot part on all, we can only win!
my $disksize;
- foreach my $hd (@$bootdevlist) {
+ for my $hd (@$devlist) {
my $devname = @$hd[1];
my $logical_bsize = @$hd[4];
$disksize = $size;
}
- &$udevadm_trigger_block();
+ $udevadm_trigger_block->();
foreach my $di (@$bootdevinfo) {
my $devname = $di->{devname};
foreach my $di (@$bootdevinfo) {
my $dev = $di->{devname};
- if (!$native_4k_disk_bootable) {
- eval {
- syscmd("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
- die "unable to install the i386-pc boot loader on '$dev'\n";
- };
- push @$bootloader_err_list, $@ if $@;
- }
+ if ($use_zfs) {
+ prepare_proxmox_boot_esp($di->{esp}, $targetdir);
+ } else {
+ if (!$native_4k_disk_bootable) {
+ eval {
+ syscmd("chroot $targetdir /usr/sbin/grub-install --target i386-pc --no-floppy --bootloader-id='proxmox' $dev") == 0 ||
+ die "unable to install the i386-pc boot loader on '$dev'\n";
+ };
+ push @$bootloader_err_list, $@ if $@;
+ }
eval {
if (my $esp = $di->{esp}) {
- if ($use_zfs) {
- prepare_systemd_boot_esp($esp, $targetdir);
- } else {
prepare_grub_efi_boot_esp($dev, $esp, $targetdir);
}
}
$filename = $steps[$step_number]->{html} if !$filename;
- my $path = "${proxmox_libdir}/html/$filename";
-
- my $url = "file://$path";
+ my $htmldir = "${proxmox_libdir}/html";
+ my $path;
+ if (-f "$htmldir/$setup->{product}/$filename") {
+ $path = "$htmldir/$setup->{product}/$filename";
+ } elsif (-f "$htmldir/common/$filename") {
+ $path = "$htmldir/common/$filename";
+ } else {
+ # FIXME: die now already?
+ }
my $data = file_get_contents($path);
- my $product = $product_cfg->{$setup->{product}};
-
if ($filename eq 'license.htm') {
my $license = eval { decode('utf8', file_get_contents("${proxmox_cddir}/EULA")) };
if (my $err = $@) {
} elsif ($filename eq 'success.htm') {
my $addr = $ipversion == 6 ? "[${ipaddress}]" : "$ipaddress";
$data =~ s/__IPADDR__/$addr/g;
- $data =~ s/__PORT__/$product->{port}/g;
+ $data =~ s/__PORT__/$setup->{port}/g;
my $autoreboot_msg = $config_options->{autoreboot}
? "Automatic reboot scheduled in $autoreboot_seconds seconds."
: '';
$data =~ s/__AUTOREBOOT_MSG__/$autoreboot_msg/;
}
- $data =~ s/__FULL_PRODUCT_NAME__/$product->{fullname}/g;
+ $data =~ s/__FULL_PRODUCT_NAME__/$setup->{fullname}/g;
- $htmlview->load_html($data, $url);
+ # HACK: always set base-path to common path, all resources are there.
+ # NOTE: we could also use an overlayfs with lower=common upper=$product & work=/run/$tmp
+ $htmlview->load_html($data, "file://$htmldir/common/");
$last_display_change = time();
}
});
$vbox->pack_start($reboot_checkbox, 0, 0, 2);
- my $ack_template = "${proxmox_libdir}/html/ack_template.htm";
- my $ack_html = "${proxmox_libdir}/html/$steps[$step_number]->{html}";
+ my $ack_template = "${proxmox_libdir}/html/common/ack_template.htm";
+ my $ack_html = "${proxmox_libdir}/html/$setup->{product}/$steps[$step_number]->{html}";
my $html_data = file_get_contents($ack_template);
my %config_values = (
my $text = "$devname (";
if ($size >= 1024) {
$size = int($size/1024); # size in GB
- $text .= "${size}GB";
+ if ($size >= 1024) {
+ $size = int($size/1024); # size in GB
+ $text .= "${size}TiB";
+ } else {
+ $text .= "${size}GiB";
+ }
} else {
- $text .= "${size}MB";
+ $text .= "${size}MiB";
}
$text .= ", $model" if $model;
}
}
+my $last_layout;
+my $country_layout;
sub update_layout {
my ($cb, $kmap) = @_;
$i++;
}
- $cb->set_active($ind || $def || 0);
+ my $val = $ind || $def || 0;
+
+ if (!defined($kmap)) {
+ $last_layout //= $val;
+ } elsif (!defined($country_layout) || $country_layout != $val) {
+ $last_layout = $country_layout = $val;
+ }
+ $cb->set_active($last_layout);
}
my $lastzonecb;
$hbox2->pack_start($pwe2, 0, 0, 0);
my $hbox3 = Gtk3::HBox->new(0, 0);
- $label = Gtk3::Label->new("E-Mail");
+ $label = Gtk3::Label->new("Email");
$label->set_size_request(150, -1);
$label->set_alignment(1, 0.5);
$hbox3->pack_start($label, 0, 0, 10);
my $t3 = $eme->get_text;
if ($t3 !~ m/^[\w\+\-\~]+(\.[\w\+\-\~]+)*@[a-zA-Z0-9\-]+(\.[a-zA-Z0-9\-]+)*$/) {
- display_message("E-Mail does not look like a valid address" .
+ display_message("Email does not look like a valid address" .
" (user\@domain.tld)");
$eme->grab_focus();
return;
}
if ($t3 eq 'mail@example.invalid') {
- display_message("Please enter a valid E-Mail address");
+ display_message("Please enter a valid Email address");
$eme->grab_focus();
return;
}
}
+my $installer_kmap;
sub create_country_view {
cleanup_view();
$kmapcb->signal_connect ('changed' => sub {
my $sel = $kmapcb->get_active_text();
+ $last_layout = $kmapcb->get_active();
if (my $kmap = $cmap->{kmaphash}->{$sel}) {
my $xkmap = $cmap->{kmap}->{$kmap}->{x11};
my $xvar = $cmap->{kmap}->{$kmap}->{x11var};
$keymap = $kmap;
+ return if (defined($installer_kmap) && $installer_kmap eq $kmap);
+
+ $installer_kmap = $keymap;
+
if (! $opt_testmode) {
syscmd ("setxkbmap $xkmap $xvar");
+
+ my $kbd_config = qq{
+ XKBLAYOUT="$xkmap"
+ XKBVARIANT="$xvar"
+ BACKSPACE="guess"
+ };
+ $kbd_config =~ s/^\s+//gm;
+
+ run_in_background( sub {
+ write_config($kbd_config, '/etc/default/keyboard');
+ system("setupcon");
+ });
}
}
});
$grid->set_row_spacing(10);
$grid->set_hexpand(1);
- $grid->set_margin_start(5);
- $grid->set_margin_end(5);
+ $grid->set_margin_start(10);
+ $grid->set_margin_end(20);
$grid->set_margin_top(5);
$grid->set_margin_bottom(5);
};
my $create_raid_disk_grid = sub {
+ my $hd_count = scalar(@$hds);
my $disk_labeled_widgets = [];
- for (my $i = 0; $i < @$hds; $i++) {
+ for (my $i = 0; $i < $hd_count; $i++) {
my $disk_selector = Gtk3::ComboBoxText->new();
$disk_selector->append_text("-- do not use --");
$disk_selector->set_active(0);
push @$disk_labeled_widgets, "Harddisk $i", $disk_selector;
}
+ my $clear_all_button = Gtk3::Button->new('_Deselect All');
+ if ($hd_count > 3) {
+ $clear_all_button->signal_connect('clicked', sub {
+ my $is_widget = 0;
+ for my $disk_selector (@$disk_labeled_widgets) {
+ $disk_selector->set_active(0) if $is_widget;
+ $is_widget ^= 1;
+ }
+ });
+ $clear_all_button->set_visible(1);
+ }
+
my $scrolled_window = Gtk3::ScrolledWindow->new();
$scrolled_window->set_hexpand(1);
- $scrolled_window->set_propagate_natural_height(1) if @$hds > 4;
- $scrolled_window->add(&$create_label_widget_grid($disk_labeled_widgets));
+ $scrolled_window->set_propagate_natural_height(1) if $hd_count > 4;
+
+ my $diskgrid = $create_label_widget_grid->($disk_labeled_widgets);
+
+ $scrolled_window->add($diskgrid);
$scrolled_window->set_policy('never', 'automatic');
+ $scrolled_window->set_visible(1);
+ $scrolled_window->set_min_content_height(190);
+
+ my $vbox = Gtk3::Box->new('vertical', 0);
+ $vbox->pack_start($scrolled_window, 1, 1, 10);
- return $scrolled_window;
-# &$create_label_widget_grid($disk_labeled_widgets)
+ my $hbox = Gtk3::Box->new('horizontal', 0);
+ $hbox->pack_end($clear_all_button, 0, 0, 20);
+ $hbox->set_visible(1);
+ $vbox->pack_end($hbox, 0, 0, 0);
+
+ return $vbox;
};
# shared between different ui parts (e.g., ZFS and "normal" single disk FS)
my $create_raid_advanced_grid = sub {
my $labeled_widgets = [];
- my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9,13,1);
+ my $spinbutton_ashift = Gtk3::SpinButton->new_with_range(9, 13, 1);
$spinbutton_ashift->set_tooltip_text("zpool ashift property (pool sector size, default 2^12)");
$spinbutton_ashift->signal_connect ("value-changed" => sub {
my $w = shift;
push @$labeled_widgets, "copies", $spinbutton_copies;
push @$labeled_widgets, "hdsize", $get_hdsize_spinbtn->();
- return &$create_label_widget_grid($labeled_widgets);;
+ return $create_label_widget_grid->($labeled_widgets);;
};
sub create_hdoption_view {
my $contarea = $dialog->get_content_area();
my $hbox2 = Gtk3::Box->new('horizontal', 0);
- $contarea->pack_start($hbox2, 1, 1, 10);
+ $contarea->pack_start($hbox2, 1, 1, 5);
my $grid = Gtk3::Grid->new();
$grid->set_column_spacing(10);
$grid->set_row_spacing(10);
- $hbox2->pack_start($grid, 1, 0, 10);
+ $hbox2->pack_start($grid, 1, 0, 5);
my $row = 0;
# Filesystem type
-
my $label0 = Gtk3::Label->new("Filesystem");
$label0->set_alignment (1, 0.5);
$grid->attach($label0, 0, $row, 1, 1);
my $fstypecb = Gtk3::ComboBoxText->new();
-
- my $fstype = ['ext4', 'xfs',
- 'zfs (RAID0)', 'zfs (RAID1)',
- 'zfs (RAID10)', 'zfs (RAIDZ-1)',
- 'zfs (RAIDZ-2)', 'zfs (RAIDZ-3)'];
-
+ my $fstype = [
+ 'ext4',
+ 'xfs',
+ 'zfs (RAID0)',
+ 'zfs (RAID1)',
+ 'zfs (RAID10)',
+ 'zfs (RAIDZ-1)',
+ 'zfs (RAIDZ-2)',
+ 'zfs (RAIDZ-3)',
+ ];
push @$fstype, 'btrfs (RAID0)', 'btrfs (RAID1)', 'btrfs (RAID10)'
if $setup->{enable_btrfs};
my $tcount = 0;
foreach my $tmp (@$fstype) {
$fstypecb->append_text($tmp);
- $fstypecb->set_active ($tcount)
- if $config_options->{filesys} eq $tmp;
+ $fstypecb->set_active ($tcount) if $config_options->{filesys} eq $tmp;
$tcount++;
}
$grid->attach($sep, 0, $row, 2, 1);
$row++;
- my $hw_raid_note = Gtk3::Label->new("Note: ZFS is not compatible with disks backed by a hardware RAID controller. For details see the reference documentation.");
+ my $hw_raid_note = Gtk3::Label->new(
+ "Note: ZFS is not compatible with hardware RAID controllers, for details see the documentation."
+ );
$hw_raid_note->set_line_wrap(1);
$hw_raid_note->set_max_width_chars(30);
$hw_raid_note->set_visible(0);
}
sub get_zfs_raid_setup {
-
my $filesys = $config_options->{filesys};
my $devlist = &$get_raid_devlist();
my $diskcount = scalar(@$devlist);
die "$filesys needs at least one device\n" if $diskcount < 1;
- my $bootdevlist = [];
-
my $cmd= '';
if ($filesys eq 'zfs (RAID0)') {
- push @$bootdevlist, @$devlist[0];
foreach my $hd (@$devlist) {
legacy_bios_4k_check(@$hd[4]);
$cmd .= " @$hd[1]";
zfs_mirror_size_check($expected_size, @$hd[2]);
legacy_bios_4k_check(@$hd[4]);
$cmd .= " @$hd[1]";
- push @$bootdevlist, $hd;
}
} elsif ($filesys eq 'zfs (RAID10)') {
die "zfs (RAID10) needs at least 4 device\n" if $diskcount < 4;
die "zfs (RAID10) needs an even number of devices\n" if $diskcount & 1;
- push @$bootdevlist, @$devlist[0], @$devlist[1];
-
for (my $i = 0; $i < $diskcount; $i+=2) {
my $hd1 = @$devlist[$i];
my $hd2 = @$devlist[$i+1];
zfs_mirror_size_check($expected_size, @$hd[2]);
legacy_bios_4k_check(@$hd[4]);
$cmd .= " @$hd[1]";
- push @$bootdevlist, $hd;
}
} else {
die "unknown zfs mode '$filesys'\n";
}
- return ($devlist, $bootdevlist, $cmd);
+ return ($devlist, $cmd);
}
sub get_btrfs_raid_setup {
cleanup_view();
if (int($total_memory) < 1024) {
- my $product = $product_cfg->{$setup->{product}};
-
display_error("Less than 1 GiB of usable memory detected, installation will probably fail.\n\n".
- "See 'System Requirements' in the $product->{fullname} documentation.");
+ "See 'System Requirements' in the $setup->{fullname} documentation.");
}
if ($setup->{product} eq 'pve') {
Gtk3->main;
+# reap left over zombie processes
+while ((my $child = waitpid(-1, POSIX::WNOHANG)) > 0) {
+ print "reaped child $child\n";
+}
+
exit 0;