#!/usr/bin/perl # Must be done as early as possible to avoid issues when displaying translated # strings BEGIN { push @::textdomains, 'draklive-install'; $::isLiveInstall = 1; } use lib qw(/usr/lib/libDrakX); use standalone; use interactive; use fs; use fs::any; use fs::type; use fs::partitioning; use fs::partitioning_wizard; use partition_table; use MDK::Common; use common; use feature qw(state); use File::Basename; ($::real_windowwidth, $::real_windowheight) = (600, 400); { use diskdrake::interactive; package diskdrake::interactive; my $old = \&hd_possible_actions_base; undef *hd_possible_actions_base; *hd_possible_actions_base = sub { #- for the partition wizard to show the auto-allocate option local $::isInstall = 1; &$old; }; undef *Done; #- skip the fstab/reboot checks (see below FIXME note) *Done = \&diskdrake_interactive_Done; #- don't ask whether to Move/Hide old files (FIXME: add a method to skip that to diskdrake::interactive) undef *need_migration; *need_migration = sub { 'hide' }; } install_live(); sub install_live() { my $in = 'interactive'->vnew('su'); $in->{pop_wait_messages} = 0; $::isWizard = 1; $::Wizard_no_previous = 1; $::Wizard_pix_up = "draklive-install"; any::set_wm_hints_if_needed($in); my $all_hds = {}; my $fstab = []; $::prefix = '/mnt/install'; my $system_file = '/etc/sysconfig/draklive-install'; my %settings = getVarsFromSh($system_file); my $copy_source = $settings{SOURCE} || '/'; my $live_media = 'Mageia*Live*'; # match disk label for all Mageia Live ISOS display_start_message(); init_hds($in, $all_hds, $fstab, $live_media); ask_partitions_loop($in, $all_hds, $fstab, $copy_source); remove_unused_packages($in, $copy_source); prepare_root($in); copy_root($in, $copy_source); complete_install($in, $all_hds); setup_bootloader($in, $all_hds, $fstab); finish_installation($fstab); display_end_message($in); $in->exit(0); } sub umount_all { my ($fstab, $o_except_swap) = @_; #- make sure nothing is mounted in the new root foreach (sort { $b cmp $a } grep { /^$::prefix/ } map { (split)[1] } cat_('/proc/mounts')) { system('umount', $_); } #- make sure selected devices aren't mounted, and swap isn't used if (!$o_except_swap) { foreach (grep { isSwap($_) } @$fstab) { eval { fs::mount::swapoff($_->{device}) }; } } foreach (map { $_->{isMounted} && !isSwap($_) ? "/dev/$_->{device}" : () } @$fstab) { system('umount', $_); } } sub on_reboot_needed { my ($in) = @_; fs::partitioning_wizard::warn_reboot_needed($in); $in->exit(0); } sub display_start_message() { require any; my $has_running_wm = to_bool(any::running_window_manager()); local $::isStandalone = $has_running_wm; # center me if run in xsetup.d script mygtk3::enable_quit_popup(1); my $w = ugtk3->new(N("Mageia Live")); ugtk3::gtkadd($w->{window}, ugtk3::gtkcreate_img("MageiaLive-install"), ugtk3::gtknew('Label', height => 5), N("This wizard will help you to install the live distribution."), ugtk3::create_okcancel($w)); $w->{ok}->grab_focus; $w->main; } sub umount_first_pass() { local $::prefix = undef; my $all_hds = fsedit::get_hds(); fs::get_raw_hds('', $all_hds); fs::get_info_from_fstab($all_hds); my $fstab = [ fs::get::fstab($all_hds) ]; fs::merge_info_from_mtab($fstab); #- inlined from fs::mount::umount_all to go on when one umount fail #- (maybe the sort function could be shared) log::l("unmounting all filesystems"); foreach (sort { $b->{mntpoint} cmp $a->{mntpoint} } grep { $_->{mntpoint} && !$_->{real_mntpoint} } @$fstab) { eval { fs::mount::umount_part($_) }; log::l("error unmounting $_->{mntpoint}: $@") if $@; } } sub init_hds { my ($in, $all_hds, $fstab, $live_media) = @_; my $wait = $in->wait_message('', N("Please wait")); umount_first_pass(); eval { fs::any::get_hds($all_hds, $fstab, [], {}, 'skip_mtab', $in) }; #- fs::any::get_hds does not return mounts that are not in fstab my @mounted = fs::read_fstab('', '/proc/mounts'); my $live_device = basename(readlink(glob "/dev/disk/by-label/$live_media")); #- remove live device from the detected hds, so that bootloader is not installed on it: #- bootloader installation uses first device from detect_devices::get, which tries to list devices #- by booting order, and our live system is likely to be here in first position #- it can be either a partition (USB) or the full disk (Hybrid on USB) @{$all_hds->{hds}} = grep { $_->{device} ne $live_device && !member($live_device, map { $_->{device} } partition_table::get_normal_parts_and_holes($_)); } @{$all_hds->{hds}} if $live_device; #- some of the selected devices may have been manually mounted or auto-mounted, and we need to #- ensure they will be unmounted before we start partitioning (mga#20268) foreach my $p (@$fstab) { $p->{isMounted} = 1 if grep { $_->{device} eq $p->{device} } @mounted; } my $err = $@; umount_all($fstab); if ($err) { undef $wait; $in->ask_warn(N("Error"), [ formatError($err) ]); $in->exit(1); } } sub ask_partitions_loop { my ($in, $all_hds, $fstab, $copy_source) = @_; while (1) { eval { ask_partitions($in, $all_hds, $fstab, $copy_source) }; my $err = $@ or last; $in->exit(1) if $err =~ /wizcancel/ || !$in->ask_warn(N("Error"), [ N("An error occurred"), formatError($err) ]); } } sub ask_partitions { my ($in, $all_hds, $fstab, $copy_source) = @_; fs::partitioning_wizard::main($in, $all_hds, $fstab, [], undef, {}, 'skip_mtab'); mkdir_p($::prefix) or die "unable to create $::prefix"; # There is an unidentified condition which stops udevd from calling the # BLKRRPART ioctl when the "use entire disk" solution is selected. Adding # this udevadm settle' call appears to fix it (although it may just be the # extra delay that does the job). Look at removing this once we switch to # using libparted for all partition table types. system('udevadm', 'settle'); fs::any::write_hds($all_hds, $fstab, undef, sub { on_reboot_needed($in) }, {}); fs::any::check_hds_boot_and_root($all_hds, $fstab); fs::partitioning::choose_partitions_to_format($in, $fstab); my $total = get_total_size($in, $copy_source); my $available = fs::any::getAvailableSpace($fstab, 'skip_mounted', 1); $total += fs::any::getNeededMinSpace($available); die N("Not enough space available (%s available while %s are needed)", formatXiB($available), formatXiB($total)) . "\n" if $total > $available; umount_all($fstab); fs::partitioning::format_mount_partitions($in, $all_hds, $fstab); } sub remove_unused_packages { my ($in, $o_prefix) = @_; require pkgs; #in remove_unused_packages, we want to get the locale from the currently #running system, but we want to remove unused packages from the #system based in $o_prefix, that's why we use an extra arg instead of #directly using $::prefix local $::prefix; pkgs::remove_unused_packages($in, $in->do_pkgs, $o_prefix); } sub prepare_root { my ($in) = @_; #- create required directories and devices (early to have a consistent root before calling other programs) my $_wait = $in->wait_message('', N("Please wait")); fs::any::prepare_minimal_root(); } sub build_copy_command { my ($source, $dest) = @_; join(' ', 'tar', 'c', '--one-file-system', '-C', $source, '.', '|', 'tar', 'xvv', '-C', $dest, ); } sub get_total_size { my ($in, $source) = @_; state %total; return $total{$source} if $total{$source}; my $_wait = $in->wait_message('', N("Computing total size")); $total{$source} = first(split(/\s+/, `du -sbx /run/mgalive/ovlsize 2>/dev/null`)); } sub sync_logs() { cp_af('/var/log', $::prefix . '/var'); } sub copy_root { my ($in, $copy_source) = @_; my $total = get_total_size($in, $copy_source); my ($wait, $update_progress) = copying_message_with_progress_bar($in, N("Copying in progress")); open(my $OUTPUT, '-|', build_copy_command($copy_source, $::prefix)); { local $_; my $current = my $previous = 0; while (<$OUTPUT>) { (undef, undef, my $size) = split; $current += $size; if ($current <= $total && $current/$total > $previous/$total + 0.001) { $update_progress->('', $current, $total); $previous = $current; } } } if (!close($OUTPUT)) { undef $wait; undef $update_progress; $in->ask_warn(N("Error"), N("Unable to copy files to new root")); $in->exit(1); } sync_logs(); } sub clean_harddrake_hds { my ($prefix) = @_; #- remove harddisks from harddrake's config file, so that hardddisks #- are automatically rediscovered at first boot require Storable; my $harddrake_file = $prefix . "/etc/sysconfig/harddrake2/previous_hw"; my $harddrake_conf = eval { Storable::retrieve($harddrake_file) }; if ($harddrake_conf) { delete $harddrake_conf->{HARDDISK}; Storable::store($harddrake_conf, $harddrake_file); } } sub complete_install { my ($in, $all_hds) = @_; my $_wait = $in->wait_message('', N("Please wait")); my $real_rpm_dir = "/tmp/rpm/real"; cp_f(glob($real_rpm_dir . "/*"), $::prefix . "/var/lib/rpm") if -d $real_rpm_dir; #- FIXME: maybe factorize with draklive, using draklive --clean-chroot ? #- remove unwanted files and packages my $live_user = chomp_(cat_('/etc/draklive-install.d/user')); my $live_user_desktop = $live_user && chomp_(run_program::rooted_get_stdout($::prefix, "su - $live_user -c 'xdg-user-dir DESKTOP'")); unlink(map { $::prefix . $_ } '/.autofsck', chomp_(cat_(glob('/etc/draklive-install.d/remove.d/*'))), if_($live_user_desktop, $live_user_desktop . '/draklive-copy-wizard.desktop', $live_user_desktop . '/draklive-install.desktop'), ); { #- do not allow update-menus to create home directory with invalid perms local $ENV{HOME} = '/root'; system('chroot', $::prefix, 'rpm', '-e', 'draklive-install'); } foreach (glob('/etc/draklive-install.d/run.d/*')) { run_program::rooted($::prefix, $_); } #- copy sysconfig files for first boot cp_f(glob('/etc/draklive-install.d/sysconfig/*'), $::prefix . '/etc/sysconfig'); #- unselect live user in kdm my $kdm_cfg = common::read_alternative('kdm4-config'); update_gnomekderc($::prefix . $kdm_cfg, 'X-:0-Greeter' => (PreselectUser => 'None', DefaultUser => '')) if -f $kdm_cfg; my $autologin = any::get_autologin(); delete $autologin->{user}; any::set_autologin($in->do_pkgs, $autologin); #- allow to install doc in disk install substInFile { undef $_ if /^\%_excludedocs/ } $::prefix . '/etc/rpm/macros'; fs::write_fstab($all_hds, $::prefix); clean_harddrake_hds($::prefix); # enable back some disabled services require services; services::start_service_on_boot($_) foreach chomp_(cat_('/etc/draklive-install.d/services')); sync_logs(); } sub setup_bootloader { my ($in, $all_hds, $fstab) = @_; use bootloader; my $bootloader = {}; my $_wait = $in->wait_message('', N("Preparing initial startup program...")); any::setupBootloaderBeforeStandalone($in->do_pkgs, $bootloader, $all_hds, $fstab); undef $_wait; local $::Wizard_no_previous = 0; any::setupBootloaderUntilInstalled($in, $bootloader, $all_hds, $fstab, $ENV{SECURE_LEVEL}); sync_logs(); } sub clean_live_system_hds() { #- clean fstab and harddrake config in the live system #- since partitions UUIDs of the installed system have been modified #- (useful for persistent live systems) local $::prefix = undef; clean_harddrake_hds($::prefix); my $all_hds = fs::get::empty_all_hds(); #- skip real harddisks fs::get_raw_hds('', $all_hds); fs::get_info_from_fstab($all_hds); fs::write_fstab($all_hds, $::prefix); } sub finish_installation { my ($fstab) = @_; #- remove the extra authorisation draklive added run_program::rooted($::prefix, 'sed', '-i', '/for draklive-install/d', '/etc/pam.d/polkit-1'); sync_logs(); #- cleanly umount here, it will avoid fs journals to be corrupted after a hackish reboot #- but leave swap space, to avoid out-of-memory error on machines with little memory #- draklive-install-lock-storage will unmount swap after we exit and free up some memory umount_all($fstab, 1); clean_live_system_hds(); } sub display_end_message { my ($in) = @_; $::Wizard_finished = 1; $in->ask_okcancel(N("Congratulations"), N("Please halt your computer, remove your live system, and restart your computer.")); } ### ### duplicate code ### # FIXME: code begins to fork # FIXME: => in mga4 add diskdrake::interactive::set_skip_fstab_reboot_steps() and use it instead #- from disdrake::interactive { package diskdrake::interactive; sub diskdrake_interactive_Done { my ($in, $all_hds) = @_; eval { raid::verify($all_hds->{raids}) }; if (my $err = $@) { $::expert or die; $in->ask_okcancel('', [ formatError($err), N("Continue anyway?") ]) or return; } foreach (@{$all_hds->{hds}}) { if (!write_partitions($in, $_, 'skip_check_rebootNeeded')) { return if !$::isStandalone; $in->ask_yesorno(N("Quit without saving"), N("Quit without writing the partition table?"), 1) or return; } } #- skip that fstab/reboot steps if (!$::isInstall && 0) { my $new = fs::fstab_to_string($all_hds); if ($new ne $all_hds->{current_fstab} && $in->ask_yesorno('', N("Do you want to save /etc/fstab modifications?"), 1)) { $all_hds->{current_fstab} = $new; fs::write_fstab($all_hds); } update_bootloader_for_renumbered_partitions($in, $all_hds); if (any { $_->{rebootNeeded} } @{$all_hds->{hds}}) { $in->ask_warn('', N("You need to reboot for the partition table modifications to take place")); tell_wm_and_reboot(); } } if (my $part = find { $_->{mntpoint} && !maybeFormatted($_) } fs::get::fstab($all_hds)) { $in->ask_okcancel('', N("You should format partition %s. Otherwise no entry for mount point %s will be written in fstab. Quit anyway?", $part->{device}, $part->{mntpoint})) or return if $::isStandalone && 0; #- no, please } 1; } } # forked from interactive::wait_message sub copying_message { my ($o, $title, $message, $b_temp) = @_; my $w = $o->wait_messageW($title, N("Copying in progress"), ugtk3::gtknew('VBox', padding => 5, children_tight => [ ugtk3::gtkcreate_img("MageiaLive-advert"), $message, ])); push @tempory::objects, $w if $b_temp; my $b = before_leaving { $o->wait_message_endW($w) }; #- enable access through set MDK::Common::Func::add_f4before_leaving(sub { $o->wait_message_nextW($_[1], $w) }, $b, 'set'); $b; } # forked from interactive::gtk::wait_message_with_progress_bar sub copying_message_with_progress_bar { my ($in, $o_title) = @_; my $progress = Gtk3::ProgressBar->new; my $w = copying_message($in, $o_title, $progress); my $displayed; $progress->signal_connect(draw => sub { $displayed = 1; 0 }); $w, sub { my ($msg, $current, $total) = @_; if ($msg) { $w->set($msg); } if ($total) { $progress or internal_error('You must first give some text to display'); my $fraction = min(1, $current / $total); if ($fraction != $progress->get_fraction) { $progress->set_fraction($fraction); $progress->show; $displayed = 0; mygtk3::flush() while !$displayed; } } else { $progress->hide; mygtk3::flush(); } }; }