package Xconfigurator; # $Id$ use diagnostics; use strict; use vars qw($in $install $isLaptop @window_managers @depths @monitorSize2resolution @hsyncranges %min_hsync4wres @vsyncranges %depths @resolutions %serversdriver @svgaservers @accelservers @allbutfbservers @allservers %vgamodes %videomemory @ramdac_name @ramdac_id @clockchip_name @clockchip_id %keymap_translate %standard_monitors $XF86firstchunk_text $XF86firstchunk_text2 $keyboardsection_start $keyboardsection_start_v4 $keyboardsection_part2 $keyboardsection_part3 $keyboardsection_part3_v4 $keyboardsection_end $pointersection_text $pointersection_text_v4 $monitorsection_text1 $monitorsection_text2 $monitorsection_text3 $monitorsection_text4 $modelines_text_Trident_TG_96xx $modelines_text $devicesection_text $devicesection_text_v4 $screensection_text1 %lines @options %xkb_options $default_monitor $layoutsection_v4); use common qw(:common :file :functional :system); use log; use detect_devices; use run_program; use Xconfigurator_consts; use any; use modules; use my_gtk qw(:wrappers); my $tmpconfig = "/tmp/Xconfig"; my ($prefix, %monitors); 1; sub getVGAMode($) { $_[0]->{card}{vga_mode} || $vgamodes{"640x480x16"}; } sub readCardsDB { my ($file) = @_; my ($card, %cards); local *F; open F, $file or die "file $file not found"; my ($lineno, $cmd, $val) = 0; my $fs = { LINE => sub { push @{$card->{lines}}, $val unless $val eq "VideoRam" }, NAME => sub { $cards{$card->{type}} = $card if $card; $card = { type => $val }; }, SEE => sub { my $c = $cards{$val} or die "Error in database, invalid reference $val at line $lineno"; push @{$card->{lines}}, @{$c->{lines} || []}; add2hash($card->{flags}, $c->{flags}); add2hash($card, $c); }, CHIPSET => sub { $card->{chipset} = $val; $card->{flags}{needChipset} = 1 if $val eq 'GeForce DDR'; $card->{flags}{needVideoRam} = 1 if member($val, qw(mgag10 mgag200 RIVA128 SiS6326)); }, SERVER => sub { $card->{server} = $val; }, DRIVER => sub { $card->{driver} = $val; }, RAMDAC => sub { $card->{ramdac} = $val; }, DACSPEED => sub { $card->{dacspeed} = $val; }, CLOCKCHIP => sub { $card->{clockchip} = $val; $card->{flags}{noclockprobe} = 1; }, NOCLOCKPROBE => sub { $card->{flags}{noclockprobe} = 1 }, UNSUPPORTED => sub { $card->{flags}{unsupported} = 1 }, COMMENT => sub {}, }; local $_; while () { $lineno++; s/\s+$//; /^#/ and next; /^$/ and next; /^END/ and last; ($cmd, $val) = /(\S+)\s*(.*)/ or next; #log::l("bad line $lineno ($_)"), next; my $f = $fs->{$cmd}; $f ? $f->() : log::l("unknown line $lineno ($_)"); } \%cards; } sub readCardsNames { my $file = "$ENV{SHARE_PATH}/ldetect-lst/CardsNames"; local *F; open F, $file or die "can't find $file\n"; map { (split '=>')[0] } grep { !/^#/ } ; } sub cardName2RealName { my $file = "$ENV{SHARE_PATH}/ldetect-lst/CardsNames"; my ($name) = @_; local *F; open F, $file or die "can't find $file\n"; local $_; while () { chop; next if /^#/; my ($name_, $real) = split '=>'; return $real if $name eq $name_; } $name; } sub updateCardAccordingName { my ($card, $name) = @_; my $cards = readCardsDB("$ENV{SHARE_PATH}/ldetect-lst/Cards+"); add2hash($card->{flags}, $cards->{$name}{flags}); add2hash($card, $cards->{$name}); $card; } sub readMonitorsDB { my ($file) = @_; %monitors and return; local *F; open F, $file or die "can't open monitors database ($file): $!"; local $_; my $lineno = 0; while () { $lineno++; s/\s+$//; /^#/ and next; /^$/ and next; my @fields = qw(vendor type eisa hsyncrange vsyncrange); my @l = split /\s*;\s*/; @l == @fields or log::l("bad line $lineno ($_)"), next; my %l; @l{@fields} = @l; if ($monitors{$l{type}}) { my $i; for ($i = 0; $monitors{"$l{type} ($i)"}; $i++) {} $l{type} = "$l{type} ($i)"; } $monitors{"$l{vendor}|$l{type}"} = \%l; } while (my ($k, $v) = each %standard_monitors) { $monitors{'Generic|' . translate($k)} = { hsyncrange => $v->[1], vsyncrange => $v->[2] }; } } sub rewriteInittab { my ($runlevel) = @_; my $f = "$prefix/etc/inittab"; -r $f or log::l("missing inittab!!!"), return; substInFile { s/^(id:)[35](:initdefault:)\s*$/$1$runlevel$2\n/ } $f; } sub keepOnlyLegalModes { my ($card, $monitor) = @_; my $mem = 1024 * ($card->{memory} || ($card->{server} eq 'FBDev' ? 2048 : 99999)); my $hsync = max(split(/[,-]/, $monitor->{hsyncrange})); while (my ($depth, $res) = each %{$card->{depth}}) { @$res = grep { $mem >= product(@$_, $depth / 8) && $hsync >= ($min_hsync4wres{$_->[0]} || 0) && ($card->{server} ne 'FBDev' || $vgamodes{"$_->[0]x$_->[1]x$depth"}) } @$res; delete $card->{depth}{$depth} if @$res == 0; } } sub cardConfigurationAuto() { my $card; if (my ($c) = grep { $_->{driver} =~ /(Card|Server):/ } detect_devices::probeall()) { local $_ = $c->{driver}; $card->{type} = $1 if /Card:(.*)/; $card->{server} = $1 if /Server:(.*)/; $card->{flags}{needVideoRam} &&= /86c368/; $card->{identifier} = $c->{description}; push @{$card->{lines}}, @{$lines{$card->{identifier}} || []}; } #- take a default on sparc if nothing has been found. if (arch() =~ /^sparc/ && !$card->{server} && !$card->{type}) { log::l("Using probe with /proc/fb as nothing has been found!"); local $_ = cat_("/proc/fb"); if (/Mach64/) { $card->{server} = "Mach64" } elsif (/Permedia2/) { $card->{server} = "3DLabs" } else { $card->{server} = "Sun24" } } $card; } sub cardConfiguration(;$$$) { my ($card, $noauto, $allowFB) = @_; $card ||= {}; updateCardAccordingName($card, $card->{type}) if $card->{type}; #- try to get info from given type undef $card->{type} unless $card->{server}; #- bad type as we can't find the server add2hash($card, cardConfigurationAuto()) unless $card->{server} || $noauto; $card->{server} = 'FBDev' unless !$allowFB || $card->{server} || $card->{type} || $noauto; $card->{type} = cardName2RealName($in->ask_from_treelist(_("Graphic card"), _("Select a graphic card"), '|', ['Other|Unlisted', readCardsNames()])) unless $card->{type} || $card->{server}; undef $card->{type}, $card->{server} = $in->ask_from_list(_("X server"), _("Choose a X server"), $allowFB ? \@allservers : \@allbutfbservers ) if $card->{type} eq 'Other|Unlisted'; updateCardAccordingName($card, $card->{type}) if $card->{type}; add2hash($card, { vendor => "Unknown", board => "Unknown" }); $card->{memory} = 4096, delete $card->{depth} if $card->{driver} eq 'i810'; $card->{memory} = 16384, delete $card->{depth} if $card->{chipset} =~ /PERMEDIA/ && $card->{memory} <= 1024; #- 3D acceleration configuration for XFree 3.3 using Utah-GLX. $card->{Utah_glx} = ($card->{identifier} =~ /Matrox.* G[24]00/ || #- 8bpp does not work. $card->{identifier} =~ /Riva.*128/ || $card->{identifier} =~ /Rage X[CL]/ || $card->{identifier} =~ /Rage Mobility (?:P\/M|L) / || $card->{identifier} =~ /3D Rage (?:LT|Pro)/); #- NOT WORKING $card->{type} =~ /Intel 810/); #- 3D acceleration configuration for XFree 3.3 using Utah-GLX but EXPERIMENTAL that may freeze the machine (FOR INFO NOT USED). $card->{Utah_glx_EXPERIMENTAL} = ($card->{type} =~ /RIVA TNT/ || #- all RIVA/GeForce comes from NVIDIA and may freeze (gltron). #$card->{type} =~ /RIVA128/ || $card->{type} =~ /GeForce 256/ || $card->{type} =~ /S3 Savage3D/ || #- only this one is evoluting (expect a stable release ?) #- $card->{type} =~ /S3 ViRGE/ || #- 15bits only $card->{type} =~ /SiS /); #- 3D acceleration configuration for XFree 4.0 using DRI. $card->{DRI_glx} = ($card->{identifier} =~ /Voodoo [35]/ || $card->{identifier} =~ /Voodoo Banshee/ || #- 16bit only $card->{identifier} =~ /Matrox.* G[24]00/ || #- prefer 16bit (24bit not well tested according to DRI) $card->{identifier} =~ /8281[05].* CGC/ || #- 16bits (Intel 810 & 815). $card->{identifier} =~ /Radeon / || #- 16bits preferable ? $card->{identifier} =~ /Rage 128/); #- 16 and 32 bits, prefer 16bit as no DMA. #- check to use XFree 4.0 or XFree 3.3. $card->{use_xf4} = $card->{driver} && !$card->{flags}{unsupported}; $card->{prefer_xf3} = ($card->{type} =~ /RIVA TNT/ || $card->{type} =~ /RIVA128/ || $card->{type} =~ /GeForce/ || $card->{type} =~ /NeoMagic /); #- basic installation, use of XFree 4.0 or XFree 3.3. my ($xf4_ver, $xf3_ver) = ("4.0.1", "3.3.6"); my $xf3_tc = { text => _("XFree %s", $xf3_ver), code => sub { $card->{Utah_glx} = $card->{DRI_glx} = ''; $card->{use_xf4} = ''; log::l("Using XFree $xf3_ver") } }; my $msg = _("Which configuration of XFree do you want to have?"); my @choices = $card->{use_xf4} ? (if_($card->{prefer_xf3}, $xf3_tc), if_(!$card->{prefer_xf3} || $::expert, { text => _("XFree %s", $xf4_ver), code => sub { $card->{Utah_glx} = $card->{DRI_glx} = ''; log::l("Using XFree $xf4_ver") } }), if_(!$card->{prefer_xf3} && $::expert, $xf3_tc)) : $xf3_tc; #- try to figure if 3D acceleration is supported #- by XFree 3.3 but not XFree 4.0 then ask user to keep XFree 3.3 ? if ($card->{Utah_glx}) { $msg = ($card->{use_xf4} && !$card->{DRI_glx} && !$card->{prefer_xf3} ? _("Your card can have 3D hardware acceleration support but only with XFree %s. Your card is supported by XFree %s which may have a better support in 2D.", $xf3_ver, $xf4_ver) : _("Your card can have 3D hardware acceleration support with XFree %s.", $xf3_ver)) . "\n\n\n" . $msg; $::beginner and @choices = (); #- keep it by default here as it is the only choice available. unshift @choices, { text => _("XFree %s with 3D hardware acceleration", $xf3_ver), code => sub { $card->{use_xf4} = ''; log::l("Using XFree $xf3_ver with 3D hardware acceleration") } }; } #- an expert user may want to try to use an EXPERIMENTAL 3D acceleration, currenlty #- this is with Utah GLX and so, it can provide a way of testing. if ($::expert && $card->{Utah_glx_EXPERIMENTAL}) { $msg = ($card->{use_xf4} && !$card->{DRI_glx} && !$card->{prefer_xf3} ? _("Your card can have 3D hardware acceleration support but only with XFree %s, NOTE THIS IS EXPERIMENTAL SUPPORT AND MAY FREEZE YOUR COMPUTER. Your card is supported by XFree %s which may have a better support in 2D.", $xf3_ver, $xf4_ver) : _("Your card can have 3D hardware acceleration support with XFree %s, NOTE THIS IS EXPERIMENTAL SUPPORT AND MAY FREEZE YOUR COMPUTER.", $xf3_ver)) . "\n\n\n" . $msg; push @choices, { text => _("XFree %s with EXPERIMENTAL 3D hardware acceleration", $xf3_ver), code => sub { $card->{use_xf4} = ''; $card->{Utah_glx} = 'EXPERIMENTAL'; log::l("Using XFree $xf3_ver with EXPERIMENTAL 3D hardware acceleration") } }; } #- ask the expert user to enable or not hardware acceleration support. if ($card->{use_xf4} && $card->{DRI_glx}) { $msg = _("Your card can have 3D hardware acceleration support with XFree %s.", $xf4_ver) . "\n\n\n" . $msg; $::expert or @choices = (); #- keep all user by default with XFree 4.0 including 3D acceleration. unshift @choices, { text => _("XFree %s with 3D hardware acceleration", $xf4_ver), code => sub { log::l("Using XFree $xf4_ver with 3D hardware acceleration") } }; } #- examine choice of user, beware the list MUST NOT BE REORDERED AS the first item should be the #- proposed one by DrakX. my $tc = $in->ask_from_listf(_("XFree configuration"), formatAlaTeX($msg), sub { translate($_[0]{text}) }, \@choices); #- in case of class discarding, this can help ... $tc or $tc = $choices[0]; $tc->{code} and $tc->{code}(); $card->{prog} = "/usr/X11R6/bin/" . ($card->{use_xf4} ? 'XFree86' : $card->{server} =~ /Sun (.*)/x ? "Xsun$1" : "XF86_$card->{server}"); #- additional packages to install according available card. #- add XFree86-libs-DRI here if using DRI (future split of XFree86 TODO) my @l = (); if ($card->{DRI_glx}) { push @l, 'Glide_V5' if $card->{identifier} =~ /Voodoo 5/; push @l, 'Glide_V3-DRI' if $card->{identifier} =~ /Voodoo (3|Banshee)/; push @l, 'Device3Dfx', 'XFree86-glide-module' if $card->{identifier} =~ /Voodoo/; } if ($card->{Utah_glx}) { push @l, 'Mesa' if !$card->{use_xf4}; } -x "$prefix$card->{prog}" or $install && do { $in->suspend; &$install('server', @l) if $card->{use_xf4}; &$install($card->{server}, @l) if !$card->{use_xf4}; $in->resume; }; -x "$prefix$card->{prog}" or die "server $card->{server} is not available (should be in $prefix$card->{prog})"; $card->{options}{sw_cursor} = 1 if $card->{type} =~ /S3 Trio3D|SiS 6326/; unless ($card->{type}) { $card->{flags}{noclockprobe} = member($card->{server}, qw(I128 S3 S3V Mach64)); } $card->{options_xf3}{power_saver} = 1; $card->{options_xf4}{DPMS} = 1; $card->{flags}{needVideoRam} and $card->{memory} ||= $videomemory{$in->ask_from_list_('', _("Select the memory size of your graphic card"), [ sort { $videomemory{$a} <=> $videomemory{$b} } keys %videomemory])}; #- hack for ATI Mach64 card where two options should be used if using Utah-GLX. if ($card->{identifier} =~ /Rage X[CL]/ || $card->{identifier} =~ /Rage Mobility (?:P\/M|L) / || $card->{identifier} =~ /3D Rage (?:LT|Pro)/) { $card->{options_xf3}{no_font_cache} = $card->{Utah_glx}; $card->{options_xf3}{no_pixmap_cache} = $card->{Utah_glx}; } #- 3D acceleration configuration for XFree 4.0 using DRI, this is enabled by default #- but for some there is a need to specify VideoRam (else it won't run). if ($card->{DRI_glx}) { $card->{identifier} =~ /Matrox.* G[24]00/ and $card->{flags}{needVideoRam} = 'fakeVideoRam'; $card->{identifier} =~ /8281[05].* CGC/ and ($card->{flags}{needVideoRam}, $card->{memory}) = ('fakeVideoRam', 16384); } if (!$::isStandalone && $card->{driver} eq 'i810') { require modules; eval { modules::load("agpgart"); }; } $card; } sub optionsConfiguration($) { my ($o) = @_; my @l; my %l; foreach (@options) { if ($o->{card}{server} eq $_->[1] && $o->{card}{identifier} =~ /$_->[2]/) { my $options = 'options_' . ($o->{card}{server} eq 'XFree86' ? 'xf4' : 'xf3'); $o->{card}{$options}{$_->[0]} ||= 0; unless ($l{$_->[0]}) { push @l, { label => $_->[0], val => \$o->{card}{$options}{$_->[0]}, type => 'bool' }; $l{$_->[0]} = 1; } } } @l = @l[0..9] if @l > 9; #- reduce list size to 10 for display $in->ask_from_entries_refH('', _("Choose options for server"), \@l); } sub monitorConfiguration(;$$) { my $monitor = shift || {}; my $useFB = shift || 0; $monitor->{hsyncrange} && $monitor->{vsyncrange} and return $monitor; readMonitorsDB("$ENV{SHARE_PATH}/ldetect-lst/MonitorsDB"); add2hash($monitor, { type => $in->ask_from_treelist(_("Monitor"), _("Choose a monitor"), '|', ['Custom', keys %monitors], 'Generic|' . translate($default_monitor)) }) unless $monitor->{type}; if ($monitor->{type} eq 'Custom') { $in->ask_from_entries_refH('', _("The two critical parameters are the vertical refresh rate, which is the rate at which the whole screen is refreshed, and most importantly the horizontal sync rate, which is the rate at which scanlines are displayed. It is VERY IMPORTANT that you do not specify a monitor type with a sync range that is beyond the capabilities of your monitor: you may damage your monitor. If in doubt, choose a conservative setting."), [ { val => \$monitor->{hsyncrange}, list => \@hsyncranges, label => _("Horizontal refresh rate") }, { val => \$monitor->{vsyncrange}, list => \@vsyncranges, label => _("Vertical refresh rate") }]); } else { add2hash($monitor, $monitors{$monitor->{type}}); } add2hash($monitor, { type => "Unknown", vendor => "Unknown", model => "Unknown", manual => 1 }); } sub testConfig($) { my ($o) = @_; my ($resolutions, $clocklines); write_XF86Config($o, $tmpconfig); unlink "/tmp/.X9-lock"; #- restart_xfs; my $f = $tmpconfig . ($o->{card}{use_xf4} && "-4"); local *F; open F, "$prefix$o->{card}{prog} :9 -probeonly -pn -xf86config $f 2>&1 |"; local $_; while () { $o->{card}{memory} ||= $2 if /(videoram|Video RAM):\s*(\d*)/; # look for clocks push @$clocklines, $1 if /clocks: (.*)/ && !/(pixel |num)clocks:/; push @$resolutions, [ $1, $2 ] if /: Mode "(\d+)x(\d+)": mode clock/; print; } close F or die "X probeonly failed"; ($resolutions, $clocklines); } sub testFinalConfig { my ($o, $auto, $skiptest, $skip_badcard) = @_; $o->{monitor}{hsyncrange} && $o->{monitor}{vsyncrange} or $in->ask_warn('', _("Monitor not configured")), return; $o->{card}{server} || $o->{card}{driver} or $in->ask_warn('', _("Graphic card not configured yet")), return; $o->{card}{depth} or $in->ask_warn('', _("Resolutions not chosen yet")), return; my $f = "/etc/X11/XF86Config.test"; write_XF86Config($o, $::testing ? $tmpconfig : "$prefix/$f"); $skiptest || $o->{card}{server} =~ 'FBDev|Sun' and return 1; #- avoid testing with these. #- needed for bad cards not restoring cleanly framebuffer my $bad_card = $o->{card}{identifier} =~ /i740|ViRGE/; $bad_card ||= $o->{card}{identifier} =~ /Rage Mobility (?:P\/M|L) / || $o->{card}{identifier} =~ /3D Rage LT/; $bad_card ||= $o->{card}{use_xf4}; #- TODO obsoleted to check, when using fbdev of XFree 4.0! log::l("the graphic card does not like X in framebuffer") if $bad_card; my $verybad_card = $o->{card}{driver} eq 'i810'; $verybad_card and return 1; my $mesg = _("Do you want to test the configuration?"); my $def = 1; if ($bad_card && !$::isStandalone) { $skip_badcard and return 1; $mesg = $mesg . "\n" . _("Warning: testing this graphic card may freeze your computer"); $def = 0; } $auto && $def or $in->ask_yesorno(_("Test of the configuration"), $mesg, $def) or return 1; unlink "$prefix/tmp/.X9-lock"; #- create a link from the non-prefixed /tmp/.X11-unix/X9 to the prefixed one #- that way, you can talk to :9 without doing a chroot #- but take care of non X11 install :-) if (-d "/tmp/.X11-unix") { symlinkf "$prefix/tmp/.X11-unix/X9", "/tmp/.X11-unix/X9" if $prefix; } else { symlinkf "$prefix/tmp/.X11-unix", "/tmp/.X11-unix" if $prefix; } #- restart_xfs; my $f_err = "$prefix/tmp/Xoutput"; my $pid; unless ($pid = fork) { open STDERR, ">$f_err"; chroot $prefix if $prefix; exec $o->{card}{prog}, if_($o->{card}{prog} !~ /Xsun/, "-xf86config", ($::testing ? $tmpconfig : $f) . ($o->{card}{use_xf4} && "-4")), ":9" or c::_exit(0); } do { sleep 1 } until c::Xtest(":9") || waitpid($pid, c::WNOHANG()); my $b = before_leaving { unlink $f_err }; unless (c::Xtest(":9")) { local $_; local *F; open F, $f_err; i: while () { if (/\b(error|not supported)\b/i) { my @msg = !/error/ && $_ ; while () { /not fatal/ and last i; /^$/ and last; push @msg, $_; } $in->ask_warn('', [ _("An error has occurred:"), " ", @msg, _("\ntry to change some parameters") ]); return 0; } } } local *F; open F, "|perl" or die ''; print F "use lib qw(", join(' ', @INC), ");\n"; print F q{ use interactive_gtk; use my_gtk qw(:wrappers); $ENV{DISPLAY} = ":9"; gtkset_background(200 * 257, 210 * 257, 210 * 257); my ($h, $w) = Gtk::Gdk::Window->new_foreign(Gtk::Gdk->ROOT_WINDOW)->get_size; $my_gtk::force_position = [ $w / 3, $h / 2.4 ]; $my_gtk::force_focus = 1; my $text = Gtk::Label->new; my $time = 8; Gtk->timeout_add(1000, sub { $text->set(_("Leaving in %d seconds", $time)); $time-- or Gtk->main_quit; 1; }); my $background = "/usr/share/pixmaps/backgrounds/linux-mandrake/XFdrake-image-test.jpg"; my $qiv = "/usr/bin/qiv"; -r "} . $prefix . q{/$background" && -x "} . $prefix . q{/$qiv" and system(($::testing ? "} . $prefix . q{" : "chroot } . $prefix . q{/ ") . "$qiv -y $background"); exit (interactive_gtk->new->ask_yesorno('', [ _("Is this the correct setting?"), $text ], 0) ? 0 : 222); }; my $rc = close F; my $err = $?; unlink "/tmp/.X11-unix/X9" if $prefix; kill 2, $pid; $rc || $err == 222 << 8 or $in->ask_warn('', _("An error has occurred, try to change some parameters")); $rc; } sub autoDefaultDepth($$) { my ($card, $wres_wanted) = @_; my ($best, $depth); return 24 if $card->{identifier} =~ /SiS/; #- assume 24 bit even for 3D acceleration (not enabled currently). return 16 if $card->{Utah_glx} || $card->{DRI_glx}; #- assume 16bit as most of them need 16. for ($card->{server}) { /FBDev/ and return 16; #- this should work by default, FBDev is allowed only if install currently uses it at 16bpp. /Sun24/ and return 24; /SunMono/ and return 2; /Sun/ and return 8; } while (my ($d, $r) = each %{$card->{depth}}) { $depth = max($depth || 0, $d); #- try to have resolution_wanted $best = max($best || 0, $d) if $r->[0][0] >= $wres_wanted; $best = $card->{suggest_depth}, last if $r->[0][0] >= $wres_wanted || $r->[0][0] >= $card->{suggest_wres}; } $best || $depth or die "no valid modes"; } sub autoDefaultResolution { return "1024x768" if $isLaptop; my ($size) = @_; $monitorSize2resolution[round($size || 14)] || #- assume a small monitor (size is in inch) $monitorSize2resolution[-1]; #- no corresponding resolution for this size. It means a big monitor, take biggest we have } sub chooseResolutionsGtk($$;$) { my ($card, $chosen_depth, $chosen_w) = @_; my $W = my_gtk->new(_("Resolution")); my %txt2depth = reverse %depths; my ($r, $depth_combo, %w2depth, %w2h, %w2widget); my $best_w; while (my ($depth, $res) = each %{$card->{depth}}) { foreach (@$res) { $w2h{$_->[0]} = $_->[1]; push @{$w2depth{$_->[0]}}, $depth; $best_w = max($_->[0], $best_w) if $_->[0] <= $chosen_w; } } $chosen_w = $best_w; my $set_depth = sub { $depth_combo->entry->set_text(translate($depths{$chosen_depth})) }; #- the set function is usefull to toggle the CheckButton with the callback being ignored my $ignore; my $set = sub { $ignore = 1; $_[0]->set_active(1); $ignore = 0; }; while (my ($w, $h) = each %w2h) { my $V = $w . "x" . $h; $w2widget{$w} = $r = new Gtk::RadioButton($r ? ($V, $r) : $V); &$set($r) if $chosen_w == $w; $r->signal_connect("clicked" => sub { $ignore and return; $chosen_w = $w; unless (member($chosen_depth, @{$w2depth{$w}})) { $chosen_depth = max(@{$w2depth{$w}}); &$set_depth(); } }); } gtkadd($W->{window}, gtkpack_($W->create_box_with_title(_("Choose the resolution and the color depth"), "(" . ($card->{type} ? _("Graphic card: %s", $card->{type}) : _("XFree86 server: %s", $card->{server})) . ")" ), 1, gtkpack(new Gtk::HBox(0,20), $depth_combo = new Gtk::Combo, gtkpack_(new Gtk::VBox(0,0), map {; 0, $w2widget{$_} } ikeys(%w2widget), ), ), 0, gtkadd($W->create_okcancel, gtksignal_connect(new Gtk::Button(_("Show all")), clicked => sub { $W->{retval} = 1; $chosen_w = 0; Gtk->main_quit })), )); $depth_combo->disable_activate; $depth_combo->set_use_arrows_always(1); $depth_combo->entry->set_editable(0); $depth_combo->set_popdown_strings(map { translate($depths{$_}) } ikeys(%{$card->{depth}})); $depth_combo->entry->signal_connect(changed => sub { $chosen_depth = $txt2depth{untranslate($depth_combo->entry->get_text, keys %txt2depth)}; my $w = $card->{depth}{$chosen_depth}[0][0]; $chosen_w > $w and &$set($w2widget{$chosen_w = $w}); }); &$set_depth(); $W->{ok}->grab_focus; $W->main or return; ($chosen_depth, $chosen_w); } sub chooseResolutions($$;$) { goto &chooseResolutionsGtk if ref($in) =~ /gtk/; my ($card, $chosen_depth, $chosen_w) = @_; my $best_w; local $_ = $in->ask_from_list(_("Resolutions"), "", [ map_each { map { "$_->[0]x$_->[1] ${main::a}bpp" } @$::b } %{$card->{depth}} ]) or return; reverse /(\d+)x\S+ (\d+)/; } sub resolutionsConfiguration { my ($o, $auto) = @_; my $card = $o->{card}; #- For the mono and vga16 server, no further configuration is required. if (member($card->{server}, "Mono", "VGA16")) { $card->{depth}{8} = [[ 640, 480 ]]; return; } elsif ($card->{server} =~ /Sun/) { $card->{depth}{2} = [[ 1152, 864 ]] if $card->{server} =~ /^(SunMono)$/; $card->{depth}{8} = [[ 1152, 864 ]] if $card->{server} =~ /^(SunMono|Sun)$/; $card->{depth}{24} = [[ 1152, 864 ]] if $card->{server} =~ /^(SunMono|Sun|Sun24)$/; $card->{default_wres} = 1152; $o->{default_depth} = max(keys %{$card->{depth}}); return 1; #- aka we cannot test, assumed as good (should be). } if (is_empty_hash_ref($card->{depth})) { $card->{depth}{$_} = [ map { [ split "x" ] } @resolutions ] foreach @depths; } #- sort resolutions in each depth foreach (values %{$card->{depth}}) { my $i = 0; @$_ = grep { first($i != $_->[0], $i = $_->[0]) } sort { $b->[0] <=> $a->[0] } @$_; } #- remove unusable resolutions (based on the video memory size and the monitor hsync rate) keepOnlyLegalModes($card, $o->{monitor}); my $res = $o->{resolution_wanted} || $card->{suggest_wres} || autoDefaultResolution($o->{monitor}{size}); my $wres = first(split 'x', $res); #- take the first available resolution <= the wanted resolution $wres = max map { first(grep { $_->[0] <= $wres } @$_)->[0] } values %{$card->{depth}}; my $depth = eval { $o->{default_depth} || autoDefaultDepth($card, $wres) }; $auto or ($depth, $wres) = chooseResolutions($card, $depth, $wres) or return; unless ($wres) { delete $card->{depth}; return resolutionsConfiguration($o); } #- needed in auto mode when all has been provided by the user $card->{depth}{$depth} or die "you selected an unusable depth"; #- remove all biggest resolution (keep the small ones for ctl-alt-+) #- otherwise there'll be a virtual screen :( $_ = [ grep { $_->[0] <= $wres } @$_ ] foreach values %{$card->{depth}}; $card->{default_wres} = $wres; $card->{vga_mode} = $vgamodes{"${wres}xx$depth"} || $vgamodes{"${res}x$depth"}; #- for use with frame buffer. $o->{default_depth} = $depth; 1; } #- Create the XF86Config file. sub write_XF86Config { my ($o, $file) = @_; my $O; local (*F, *G); open F, ">$file" or die "can't write XF86Config in $file: $!"; open G, ">$file-4" or die "can't write XF86Config in $file-4: $!"; print F $XF86firstchunk_text, $XF86firstchunk_text2; print G $XF86firstchunk_text; print G qq( Option "Pixmap" "24"\n) if $o->{card}{type} eq "SiS 6326"; print G $XF86firstchunk_text2; #- Write keyboard section. $O = $o->{keyboard}; print F $keyboardsection_start; print G $keyboardsection_start_v4; print F qq( XkbDisable\n) unless $O->{xkb_keymap}; print G qq( Option "XkbDisable"\n) unless $O->{xkb_keymap}; print F $keyboardsection_part3; print G $keyboardsection_part3_v4; print F qq( XkbLayout "$O->{xkb_keymap}"\n); print G qq( Option "XkbLayout" "$O->{xkb_keymap}"\n); print F join '', map { " $_\n" } @{$xkb_options{$O->{xkb_keymap}} || []}; print G join '', map { /(\S+)(.*)/; qq( Option "$1" $2\n) } @{$xkb_options{$O->{xkb_keymap}} || []}; print F $keyboardsection_end; print G $keyboardsection_end; #- Write pointer section. $O = $o->{mouse}; print F $pointersection_text; print G $pointersection_text_v4; print F qq( Protocol "$O->{XMOUSETYPE}"\n); print G qq( Option "Protocol" "$O->{XMOUSETYPE}"\n); print F qq( Device "/dev/$O->{device}"\n); print G qq( Option "Device" "/dev/$O->{device}"\n); #- this will enable the "wheel" or "knob" functionality if the mouse supports it print F " ZAxisMapping 4 5\n" if $O->{nbuttons} > 3; print F " ZAxisMapping 6 7\n" if $O->{nbuttons} > 5; print G qq( Option "ZAxisMapping" "4 5"\n) if $O->{nbuttons} > 3; print G qq( Option "ZAxisMapping" "6 7"\n) if $O->{nbuttons} > 5; print F "#" unless $O->{nbuttons} < 3; print G "#" unless $O->{nbuttons} < 3; print F qq( Emulate3Buttons\n); print G qq( Option "Emulate3Buttons"\n); print F "#" unless $O->{nbuttons} < 3; print G "#" unless $O->{nbuttons} < 3; print F qq( Emulate3Timeout 50\n\n); print G qq( Option "Emulate3Timeout" "50"\n\n); print F "# ChordMiddle is an option for some 3-button Logitech mice\n\n"; print G "# ChordMiddle is an option for some 3-button Logitech mice\n\n"; print F "#" unless $O->{chordmiddle}; print G "#" unless $O->{chordmiddle}; print F qq( ChordMiddle\n\n); print G qq( Option "ChordMiddle"\n\n); print F " ClearDTR\n" if $O->{cleardtrrts}; print F " ClearRTS\n\n" if $O->{cleardtrrts}; print F "EndSection\n\n\n"; print G "EndSection\n\n\n"; #- write module section for version 3. if ($o->{wacom} || $o->{card}{Utah_glx}) { print F qq(Section "Module" ); print F qq( Load "xf86Wacom.so"\n) if $o->{wacom}; print F qq( Load "glx-3.so"\n) if $o->{card}{Utah_glx}; #- glx.so may clash with server version 4. print F qq(EndSection ); } #- write wacom device support. print F qq( Section "XInput" SubSection "WacomStylus" Port "/dev/$o->{wacom}" AlwaysCore Mode Absolute EndSubSection SubSection "WacomCursor" Port "/dev/$o->{wacom}" AlwaysCore Mode Relative EndSubSection SubSection "WacomEraser" Port "/dev/$o->{wacom}" AlwaysCore Mode Absolute EndSubSection EndSection ) if $o->{wacom}; print G qq( Section "InputDevice" Identifier "stylus" Driver "wacom" Option "Type" "stylus" Option "Device" "/dev/$o->{wacom}" Option "Mode" "Absolute" EndSection Section "InputDevice" Identifier "eraser" Driver "wacom" Option "Type" "eraser" Option "Device" "/dev/$o->{wacom}" Option "Mode" "Absolute" EndSection Section "InputDevice" Identifier "cursor" Driver "wacom" Option "Type" "cursor" Option "Device" "/dev/$o->{wacom}" Option "Mode" "Relative" EndSection ) if $o->{wacom}; #- write modules section for version 4. print G qq( Section "Module" # This loads the DBE extension module. Load "dbe" ); print G qq( Load "glx" Load "dri" ) if $o->{card}{DRI_glx}; print G qq( # This loads the miscellaneous extensions module, and disables # initialisation of the XFree86-DGA extension within that module. SubSection "extmod" #Option "omit xfree86-dga" EndSubSection # This loads the Type1 and FreeType font modules Load "type1" Load "freetype" EndSection ); print G qq( Section "DRI" Mode 0666 EndSection ) if $o->{card}{DRI_glx}; #- Write monitor section. $O = $o->{monitor}; print F $monitorsection_text1; print G $monitorsection_text1; print F qq( Identifier "$O->{type}"\n); print G qq( Identifier "$O->{type}"\n); print F qq( VendorName "$O->{vendor}"\n); print G qq( VendorName "$O->{vendor}"\n); print F qq( ModelName "$O->{model}"\n\n); print G qq( ModelName "$O->{model}"\n\n); print F $monitorsection_text2; print G $monitorsection_text2; print F qq( HorizSync $O->{hsyncrange}\n\n); print G qq( HorizSync $O->{hsyncrange}\n\n); print F $monitorsection_text3; print G $monitorsection_text3; print F qq( VertRefresh $O->{vsyncrange}\n\n); print G qq( VertRefresh $O->{vsyncrange}\n\n); print F $monitorsection_text4; print F ($O->{modelines} || '') . ($o->{card}{type} eq "TG 96" ? $modelines_text_Trident_TG_96xx : $modelines_text); print F "\nEndSection\n\n\n"; print G "\nEndSection\n\n\n"; #- Write Device section. $O = $o->{card}; print F $devicesection_text; print G $devicesection_text_v4; print F qq(Section "Device"\n); print G qq(Section "Device"\n); print F qq( Identifier "$O->{type}"\n); print G qq( Identifier "$O->{type}"\n); print F qq( VendorName "$O->{vendor}"\n); print G qq( VendorName "$O->{vendor}"\n); print F qq( BoardName "$O->{board}"\n); print G qq( BoardName "$O->{board}"\n); print F "#" if $O->{chipset} && !$O->{flags}{needChipset}; print F qq( Chipset "$O->{chipset}"\n) if $O->{chipset}; print G qq( Driver "$O->{driver}"\n); print F "#" if $O->{memory} && !$O->{flags}{needVideoRam}; print G "#" if $O->{memory} && !$O->{flags}{needVideoRam}; print F " VideoRam $O->{memory}\n" if $O->{memory}; print G " VideoRam $O->{memory}\n" if $O->{memory}; print F map { " $_\n" } @{$O->{lines} || []}; print G map { " $_\n" } @{$O->{lines} || []}; print F qq( Ramdac "$O->{ramdac}"\n) if $O->{ramdac}; print G qq( Ramdac "$O->{ramdac}"\n) if $O->{ramdac}; print F qq( Dacspeed "$O->{dacspeed}"\n) if $O->{dacspeed}; print G qq( Dacspeed "$O->{dacspeed}"\n) if $O->{dacspeed}; if ($O->{clockchip}) { print F qq( Clockchip "$O->{clockchip}"\n); print G qq( Clockchip "$O->{clockchip}"\n); } else { print F " # Clock lines\n"; print G " # Clock lines\n"; print F " Clocks $_\n" foreach (@{$O->{clocklines}}); print G " Clocks $_\n" foreach (@{$O->{clocklines}}); } do { print F; print G } for qq( # Uncomment following option if you see a big white block # instead of the cursor! # Option "sw_cursor" ); my $p = sub { my $l = $O->{$_[0]}; map { (!$l->{$_} && '#') . qq( Option "$_"\n) } keys %{$l || {}}; }; print F $p->('options'); print F $p->('options_xf3'); print G $p->('options'); print G $p->('options_xf4'); print F "EndSection\n\n\n"; print G "EndSection\n\n\n"; #- Write Screen sections. print F $screensection_text1, "\n"; print G $screensection_text1, "\n"; my $subscreen = sub { my ($f, $server, $defdepth, $depths) = @_; print $f " DefaultColorDepth $defdepth\n" if $defdepth; foreach (ikeys(%$depths)) { my $m = $server ne "fbdev" ? join(" ", map { qq("$_->[0]x$_->[1]") } @{$depths->{$_}}) : qq("default"); #-" print $f qq( Subsection "Display"\n); print $f qq( Depth $_\n) if $_; print $f qq( Modes $m\n); print $f qq( ViewPort 0 0\n); print $f qq( EndSubsection\n); } print $f "EndSection\n"; }; my $screen = sub { my ($server, $defdepth, $device, $depths) = @_; print F qq( Section "Screen" Driver "$server" Device "$device" Monitor "$o->{monitor}{type}" ); #-" $subscreen->(*F, $server, $defdepth, $depths); }; #- SVGA screen section. print F qq( # The Colour SVGA server ); if (member($O->{server}, @svgaservers)) { &$screen("svga", $o->{default_depth}, $O->{type}, $O->{depth}); } else { &$screen("svga", '', "Generic VGA", { 8 => [[ 320, 200 ]] }); } &$screen("vga16", '', (member($O->{server}, "Mono", "VGA16") ? $O->{type} : "Generic VGA"), { '' => [[ 640, 480 ], [ 800, 600 ]]}); &$screen("vga2", '', (member($O->{server}, "Mono", "VGA16") ? $O->{type} : "Generic VGA"), { '' => [[ 640, 480 ], [ 800, 600 ]]}); &$screen("accel", $o->{default_depth}, $O->{type}, $O->{depth}); &$screen("fbdev", $o->{default_depth}, $O->{type}, $O->{depth}); print G qq( Section "Screen" Identifier "screen1" Device "$O->{type}" Monitor "$o->{monitor}{type}" ); #- bpp 32 not handled by XF4 $subscreen->(*G, "svga", min($o->{default_depth}, 24), $O->{depth}); print G ' Section "ServerLayout" Identifier "layout1" Screen "screen1" InputDevice "Mouse1" "CorePointer" '; print G ' InputDevice "stylus" "AlwaysCore" InputDevice "eraser" "AlwaysCore" InputDevice "cursor" "AlwaysCore" ' if $o->{wacom}; print G ' InputDevice "Keyboard1" "CoreKeyboard" EndSection '; #-" close F; close G; } sub XF86check_link { my ($ext) = @_; my $f = "$prefix/etc/X11/XF86Config$ext"; touch($f); my $l = "$prefix/usr/X11R6/lib/X11/XF86Config$ext"; if (-e $l && (stat($f))[1] != (stat($l))[1]) { #- compare the inode, must be the sames -e $l and unlink($l) || die "can't remove bad $l"; symlinkf "../../../../etc/X11/XF86Config$ext", $l; } } sub info { my ($o) = @_; my $info; $info .= _("Keyboard layout: %s\n", $o->{keyboard}{xkb_keymap}); $info .= _("Mouse type: %s\n", $o->{mouse}{XMOUSETYPE}); $info .= _("Mouse device: %s\n", $o->{mouse}{device}) if $::expert; $info .= _("Monitor: %s\n", $o->{monitor}{type}); $info .= _("Monitor HorizSync: %s\n", $o->{monitor}{hsyncrange}) if $::expert; $info .= _("Monitor VertRefresh: %s\n", $o->{monitor}{vsyncrange}) if $::expert; $info .= _("Graphic card: %s\n", $o->{card}{type}); $info .= _("Graphic memory: %s kB\n", $o->{card}{memory}) if $o->{card}{memory}; if ($o->{default_depth} and my $depth = $o->{card}{depth}{$o->{default_depth}}) { $info .= _("Color depth: %s\n", translate($depths{$o->{default_depth}})); $info .= _("Resolution: %s\n", join "x", @{$depth->[0]}) if $depth && !is_empty_array_ref($depth->[0]); } $info .= _("XFree86 server: %s\n", $o->{card}{server}) if $o->{card}{server}; $info .= _("XFree86 driver: %s\n", $o->{card}{driver}) if $o->{card}{driver}; $info; } sub show_info { my ($o) = @_; $in->ask_warn('', info($o)); } #- Program entry point. sub main { my ($o, $allowFB); ($prefix, $o, $in, $allowFB, $isLaptop, $install) = @_; $o ||= {}; XF86check_link(''); XF86check_link('-4'); { my $w = $in->wait_message('', _("Preparing X-Window configuration"), 1); $o->{card} = cardConfiguration($o->{card}, $::noauto, $allowFB); $o->{monitor} = monitorConfiguration($o->{monitor}, $o->{card}{server} eq 'FBDev'); } my $ok = resolutionsConfiguration($o, $::auto); $ok &&= testFinalConfig($o, $::auto, $o->{skiptest}, $::auto); my $quit; until ($ok || $quit) { my %c = my @c = ( __("Change Monitor") => sub { $o->{monitor} = monitorConfiguration() }, __("Change Graphic card") => sub { $o->{card} = cardConfiguration('', 'noauto', $allowFB) }, if_($::expert, __("Change Server options") => sub { optionsConfiguration($o) }), __("Change Resolution") => sub { resolutionsConfiguration($o) }, __("Show information") => sub { show_info($o) }, __("Test again") => sub { $ok = testFinalConfig($o, 1) }, __("Quit") => sub { $quit = 1 }, ); $in->set_help('configureXmain') unless $::isStandalone; my $f = $in->ask_from_list_(['XFdrake'], _("What do you want to do?"), [ grep { !ref } @c ]); eval { &{$c{$f}} }; !$@ || $@ =~ /ask_from_list cancel/ or die; $in->kill; } if (!$ok) { $ok = $in->ask_yesorno('', _("Keep the changes? Current configuration is: %s", info($o))); } if ($ok) { unless ($::testing) { my $f = "$prefix/etc/X11/XF86Config"; if (-e "$f.test") { rename $f, "$f.old" or die "unable to make a backup of XF86Config"; rename "$f-4", "$f-4.old"; rename "$f.test", $f; rename "$f.test-4", "$f-4"; symlinkf "../..$o->{card}{prog}", "$prefix/etc/X11/X"; } } if ($::isStandalone && $0 =~ /Xdrakres/) { my $found; foreach (@window_managers) { if (`pidof "$_"` > 0) { if ($in->ask_okcancel('', _("Please relog into %s to activate the changes", ucfirst $_), 1)) { fork and $in->exit; system("kwmcom logout") if /kwm/; system("dcop kdesktop default logout") if /kwin/; system("save-session --kill") if /gnome-session/; system("killall -QUIT icewm") if /icewm/; open STDIN, "/dev/null"; open STDERR, ">&STDERR"; c::setsid(); exec qw(perl -e), q{ my $wm = shift; for (my $nb = 30; $nb && `pidof "$wm"` > 0; $nb--) { sleep 1 } system("killall X ; killall -15 xdm gdm kdm prefdm") unless `pidof "$wm"` > 0; }, $_; } $found = 1; last; } } $in->ask_warn('', _("Please log out and then use Ctrl-Alt-BackSpace")) unless $found; } else { $in->set_help('configureXxdm') unless $::isStandalone; my $run = exists $o->{xdm} ? $o->{xdm} : $::auto || $in->ask_yesorno(_("X at startup"), _("I can set up your computer to automatically start X upon booting. Would you like X to start when you reboot?"), 1); rewriteInittab($run ? 5 : 3) unless $::testing; } autologin($prefix, $o, $in, $install); } } sub autologin { my ($prefix, $o, $in, $install) = @_; my $cmd = $prefix ? "chroot $prefix" : ""; my @wm = (split (' ', `$cmd /usr/sbin/chksession -l`)); my @etc_pass_fields = qw(name pw uid gid realname home shell); my @users = mapgrep { my %l; @l{@etc_pass_fields} = split ':'; $l{uid} > 500, $l{name}; } cat_("$prefix/etc/passwd"); my @runlevel = mapgrep { /^id:([35]):initdefault:\s*$/ > 0, $1; } cat_("$prefix/etc/inittab"); if (!($::isStandalone && $0 =~ /Xdrakres/) && !($::auto && $o->{skiptest}) && first(@runlevel, 0) == 5 && @wm && @users && !$o->{authentication}{NIS} && $ENV{SECURE_LEVEL} <= 3) { my %l = getVarsFromSh("$prefix/etc/sysconfig/autologin"); $o->{autologin} ||= $l{USER}; $in->ask_from_entries_refH(_("Autologin"), _("I can set up your computer to automatically log on one user. If you don't want to use this feature, click on the cancel button."), [ { label => _("Choose the default user:"), val => \$o->{autologin}, list => [ '', @users ] }, { label => _("Choose the window manager to run:"), val => \$o->{desktop}, list => \@wm }, ]) or delete $o->{autologin}; } $o->{autologin} and $::isStandalone ? do { $in->suspend; system("urpmi --auto --best-output autologin"); $in->resume; } : $in->pkg_install("autologin"); any::setAutologin($prefix, $o->{autologin}, $o->{desktop}); run_program::rooted($prefix, "chkconfig", "--del", "gpm") if $o->{mouse}{device} =~ /ttyS/ && !$::isStandalone; } 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795
package install_any; # $Id$

use strict;

use vars qw(@ISA %EXPORT_TAGS @EXPORT_OK $boot_medium $current_medium $asked_medium @advertising_images);

@ISA = qw(Exporter);
%EXPORT_TAGS = (
    all => [ qw(getNextStep spawnShell addToBeDone) ],
);
@EXPORT_OK = map { @$_ } values %EXPORT_TAGS;

#-######################################################################################
#- misc imports
#-######################################################################################
use MDK::Common::System;
use common;
use run_program;
use fs::type;
use fs::format;
use partition_table;
use devices;
use fsedit;
use modules;
use detect_devices;
use lang;
use any;
use log;

#- boot medium (the first medium to take into account).
$boot_medium = 1;
$current_medium = $boot_medium;
$asked_medium = $boot_medium;

our $global_ftp_prefix;

sub drakx_version() { 
    $::move ? sprintf "DrakX-move v%s", cat_('/usr/bin/stage2/move.pm') =~ /move\.pm,v (\S+ \S+ \S+)/
	    : sprintf "DrakX v%s built %s", $::testing ? ('TEST', scalar gmtime()) : (split('/', cat__(getFile("install/stage2/VERSION"))))[2,3];
}

#-######################################################################################
#- Media change variables&functions
#-######################################################################################
my $postinstall_rpms = '';
my $cdrom;
my %iso_images;

sub mountCdrom {
    my ($mountpoint, $o_cdrom) = @_;
    $o_cdrom = $cdrom if !defined $o_cdrom;
    eval { fs::mount($o_cdrom, $mountpoint, "iso9660", 'readonly') };
}

sub useMedium($) {
    #- before ejecting the first CD, there are some files to copy!
    #- does nothing if the function has already been called.
    $_[0] > 1 and method_allows_medium_change($::o->{method}) and setup_postinstall_rpms($::prefix, $::o->{packages});

    $asked_medium eq $_[0] or log::l("selecting new medium '$_[0]'");
    $asked_medium = $_[0];
}
sub changeMedium($$) {
    my ($method, $medium_name) = @_;
    log::l("change to medium $medium_name for method $method (refused by default)");
    0;
}
sub relGetFile($) {
    local $_ = $_[0];
    if (my ($arch) = m|\.([^\.]*)\.rpm$|) {
	$_ = "$::o->{packages}{mediums}{$asked_medium}{rpmsdir}/$_";
	s/%{ARCH}/$arch/g;
	s,^/+,,g;
    }
    $_;
}
sub askChangeMedium($$) {
    my ($method, $medium_name) = @_;
    my $allow;
    do {
	local $::o->{method} = $method = 'cdrom' if $medium_name =~ /^\d+s$/; #- Suppl CD
	eval { $allow = changeMedium($method, $medium_name) };
    } while $@; #- really it is not allowed to die in changeMedium!!! or install will core with rpmlib!!!
    log::l($allow ? "accepting medium $medium_name" : "refusing medium $medium_name");
    $allow;
}

sub method_is_from_ISO_images($) {
    my ($method) = @_;
    $method eq "disk-iso" || $method eq "nfs-iso";
}
sub method_allows_medium_change($) {
    my ($method) = @_;
    $method eq "cdrom" || method_is_from_ISO_images($method);
}

sub look_for_ISO_images() {
    $iso_images{media} = [];

    ($iso_images{loopdev}, $iso_images{mountpoint}) = cat_("/proc/mounts") =~ m|(/dev/loop\d+)\s+(/tmp/image) iso9660| or return;

    my $get_iso_ids = sub {
	my ($F) = @_;
	my ($vol_id, $app_id) = c::get_iso_volume_ids(fileno $F);
	#- the ISO volume names must end in -Disc\d+
	my ($cd_set) = $vol_id =~ /^(.*)-Disc\d+$/;
	$cd_set && { cd_set => $cd_set, app_id => $app_id };
    };

    sysopen(my $F, $iso_images{loopdev}, 0) or return;
    put_in_hash(\%iso_images, $get_iso_ids->($F));

    my $iso_dir = $ENV{ISOPATH};
    #- strip old root and remove iso file from path if present
    $iso_dir =~ s!^/sysroot!!; $iso_dir =~ s![^/]*\.iso$!!;

    foreach my $iso_file (glob("$iso_dir/*.iso")) {
	my $iso_dev = devices::set_loop($iso_file) or return;
	if (sysopen($F, $iso_dev, 0)) {
	    my $iso_ids = $get_iso_ids->($F);
	    push @{$iso_images{media}}, { file => $iso_file, %$iso_ids } if $iso_ids;
	    close($F); #- needed to delete loop device
	}
	devices::del_loop($iso_dev);
    }
    1;
}

sub find_ISO_image_labelled($) {
    %iso_images or look_for_ISO_images() or return;
    my ($iso_label) = @_;
    find { $_->{app_id} eq $iso_label && $_->{cd_set} eq $iso_images{cd_set} } @{$iso_images{media}};
}

sub changeIso($) {
    my ($iso_label) = @_;
    my $iso_info = find_ISO_image_labelled($iso_label) or return;

    eval { fs::umount($iso_images{mountpoint}) };
    $@ and warnAboutFilesStillOpen();
    devices::del_loop($iso_images{loopdev});

    $iso_images{loopdev} = devices::set_loop($iso_info->{file});
    eval { 
	fs::mount($iso_images{loopdev}, $iso_images{mountpoint}, "iso9660", 'readonly');
	log::l("using ISO image '$iso_label'");
	1;
    };
}

sub errorOpeningFile($) {
    my ($file) = @_;
    $file eq 'XXX' and return; #- special case to force closing file after rpmlib transaction.
    $current_medium eq $asked_medium and log::l("errorOpeningFile $file"), return; #- nothing to do in such case.
    $::o->{packages}{mediums}{$asked_medium}{selected} or return; #- not selected means no need to worry about.
    my $current_method = $::o->{packages}{mediums}{$asked_medium}{method} || $::o->{method};

    my $max = 32; #- always refuse after $max tries.
    if ($current_method eq "cdrom") {
	cat_("/proc/mounts") =~ m,(/dev/\S+)\s+(/mnt/cdrom|/tmp/image),
	    and ($cdrom, my $mountpoint) = ($1, $2);
	return unless $cdrom;
	ejectCdrom($cdrom, $mountpoint);
	while ($max > 0 && askChangeMedium($current_method, $asked_medium)) {
	    $current_medium = $asked_medium;
	    mountCdrom("/tmp/image");
	    my $getFile = getFile($file); 
	    $getFile && @advertising_images and copy_advertising($::o);
	    $getFile and return $getFile;
	    $current_medium = 'unknown'; #- do not know what CD is inserted now.
	    ejectCdrom($cdrom, $mountpoint);
	    --$max;
	}
    } else {
	while ($max > 0 && askChangeMedium($current_method, $asked_medium)) {
	    $current_medium = $asked_medium;
	    my $getFile = getFile($file); $getFile and return $getFile;
	    $current_medium = 'unknown'; #- do not know what CD image has been copied.
	    --$max;
	}
    }

    #- Do not unselect supplementary CDs.
    return if $asked_medium =~ /^\d+s$/;

    #- keep in mind the asked medium has been refused on this way.
    #- this means it is no more selected.
    $::o->{packages}{mediums}{$asked_medium}{selected} = undef;

    #- on cancel, we can expect the current medium to be undefined too,
    #- this enables remounting if selecting a package back.
    $current_medium = 'unknown';

    return;
}
sub getFile {
    my ($f, $o_method, $o_altroot) = @_;
    my $current_method = ($asked_medium ? $::o->{packages}{mediums}{$asked_medium}{method} : '') || $::o->{method};
    log::l("getFile $f:$o_method ($asked_medium:$current_method)");
    my $rel = relGetFile($f);
    do {
	if ($f =~ m|^http://|) {
	    require http;
	    http::getFile($f);
	} elsif ($o_method =~ /crypto|update/i) {
	    require crypto;
	    crypto::getFile($f);
	} elsif ($current_method eq "ftp") {
	    require ftp;
	    ftp::getFile($rel, @{ $::o->{packages}{mediums}{$asked_medium}{ftp_prefix} || $global_ftp_prefix || [] });
	} elsif ($current_method eq "http") {
	    require http;
	    http::getFile(($ENV{URLPREFIX} || $o_altroot) . "/$rel");
	} else {
	    #- try to open the file, but examine if it is present in the repository,
	    #- this allows handling changing a media when some of the files on the
	    #- first CD have been copied to other to avoid media change...
	    my $f2 = "$postinstall_rpms/$f";
	    $o_altroot ||= '/tmp/image';
	    $f2 = "$o_altroot/$rel" if $rel !~ m,^/, && (!$postinstall_rpms || !-e $f2);
	    #- $f2 = "/$rel" if !$::o->{packages}{mediums}{$asked_medium}{rpmsdir} && !-e $f2; #- not a relative path, should not be necessary with new media layout
	    my $F; open($F, $f2) ? $F : do { $f2 !~ /XXX/ and log::l("Can not open $f2: $!"); undef };
	}
    } || errorOpeningFile($f);
}
sub getAndSaveFile {
    my ($file, $local) = @_ == 1 ? ("install/stage2/live$_[0]", $_[0]) : @_;
    local $/ = \ (16 * 1024);
    my $f = ref($file) ? $file : getFile($file) or return;
    open(my $F, ">$local") or log::l("getAndSaveFile(opening $local): $!"), return;
    local $_;
    while (<$f>) { syswrite($F, $_) or die("getAndSaveFile($local): $!") }
    1;
}


#-######################################################################################
#- Post installation RPMS from cdrom only, functions
#-######################################################################################
sub setup_postinstall_rpms($$) {
    my ($prefix, $packages) = @_;

    $postinstall_rpms and return;
    $postinstall_rpms = "$prefix/usr/postinstall-rpm";

    require pkgs;

    log::l("postinstall rpms directory set to $postinstall_rpms");
    clean_postinstall_rpms(); #- make sure in case of previous upgrade problem.
    mkdir_p($postinstall_rpms);

    my %toCopy;
    #- compute closure of package that may be copied, use INSTALL category
    #- in rpmsrate.
    $packages->{rpmdb} ||= pkgs::rpmDbOpen();
    foreach (@{$packages->{needToCopy} || []}) {
	my $p = pkgs::packageByName($packages, $_) or next;
	pkgs::selectPackage($packages, $p, 0, \%toCopy);
    }
    delete $packages->{rpmdb};

    my @toCopy = grep { $_ && !$_->flag_selected } map { $packages->{depslist}[$_] } keys %toCopy;

    #- extract headers of package, this is necessary for getting
    #- the complete filename of each package.
    #- copy the package files in the postinstall RPMS directory.
    #- last arg is default medium '' known as the CD#1.
    #- cp_af does not handle correctly a missing file.
    eval { cp_af((grep { -r $_ } map { "/tmp/image/" . relGetFile($_->filename) } @toCopy), $postinstall_rpms) };

    log::l("copying Auto Install Floppy");
    getAndSaveInstallFloppies($::o, $postinstall_rpms, 'auto_install');
}

sub clean_postinstall_rpms() {
    $postinstall_rpms and -d $postinstall_rpms and rm_rf($postinstall_rpms);
}


#-######################################################################################
#- Functions
#-######################################################################################
sub getNextStep {
    my ($o) = @_;
    find { !$o->{steps}{$_}{done} && $o->{steps}{$_}{reachable} } @{$o->{orderedSteps}};
}

sub dont_run_directly_stage2() {
    readlink("/usr/bin/runinstall2") eq "runinstall2.sh";
}

sub spawnShell() {
    return if $::o->{localInstall} || $::testing || dont_run_directly_stage2();

    if (my $shellpid = fork()) {
        output('/var/run/drakx_shell.pid', $shellpid);
        return;
    }

    $ENV{DISPLAY} ||= ":0"; #- why not :pp

    local *F;
    sysopen F, "/dev/tty2", 2 or log::l("cannot open /dev/tty2 -- no shell will be provided: $!"), goto cant_spawn;

    open STDIN, "<&F" or goto cant_spawn;
    open STDOUT, ">&F" or goto cant_spawn;
    open STDERR, ">&F" or goto cant_spawn;
    close F;

    print drakx_version(), "\n";

    c::setsid();

    ioctl(STDIN, c::TIOCSCTTY(), 0) or warn "could not set new controlling tty: $!";

    my @args; -e '/etc/bashrc' and @args = qw(--rcfile /etc/bashrc);
    foreach (qw(/bin/bash /usr/bin/busybox /bin/sh)) {
        -x $_ or next;
        my $program_name = /busybox/ ? "/bin/sh" : $_;  #- since perl_checker is too dumb
        exec { $_ } $program_name, @args or log::l("exec of $_ failed: $!");
    }

    log::l("cannot open any shell");
cant_spawn:
    c::_exit(1);
}

sub getAvailableSpace {
    my ($o) = @_;

    #- make sure of this place to be available for installation, this could help a lot.
    #- currently doing a very small install use 36Mb of postinstall-rpm, but installing
    #- these packages may eat up to 90Mb (of course not all the server may be installed!).
    #- 65mb may be a good choice to avoid almost all problem of insuficient space left...
    my $minAvailableSize = 65 * sqr(1024);

    my $n = !$::testing && getAvailableSpace_mounted($o->{prefix}) || 
            getAvailableSpace_raw($o->{fstab}) * 512 / 1.07;
    $n - max(0.1 * $n, $minAvailableSize);
}

sub getAvailableSpace_mounted {
    my ($prefix) = @_;
    my $dir = -d "$prefix/usr" ? "$prefix/usr" : $prefix;
    my (undef, $free) = MDK::Common::System::df($dir) or return;
    log::l("getAvailableSpace_mounted $free KB");
    $free * 1024 || 1;
}
sub getAvailableSpace_raw {
    my ($fstab) = @_;

    do { $_->{mntpoint} eq '/usr' and return $_->{size} } foreach @$fstab;
    do { $_->{mntpoint} eq '/'    and return $_->{size} } foreach @$fstab;

    if ($::testing) {
	my $nb = 450;
	log::l("taking ${nb}MB for testing");
	return $nb << 11;
    }
    die "missing root partition";
}

sub preConfigureTimezone {
    my ($o) = @_;
    require timezone;
   
    #- can not be done in install cuz' timeconfig %post creates funny things
    add2hash($o->{timezone}, timezone::read()) if $o->{isUpgrade};

    $o->{timezone}{timezone} ||= timezone::bestTimezone($o->{locale}{country});

    my $utc = every { !isFat_or_NTFS($_) } @{$o->{fstab}};
    my $ntp = timezone::ntp_server();
    add2hash_($o->{timezone}, { UTC => $utc, ntp => $ntp });
}

sub ask_if_suppl_media {
    my ($o) = @_;
    our $suppl_already_asked;
    my $msg = $suppl_already_asked
      ? N("Do you have further supplementary media?")
      : formatAlaTeX(
#-PO: keep the double empty lines between sections, this is formatted a la LaTeX
	    N("The following media have been found and will be used during install: %s.


Do you have a supplementary installation media to configure?",
	    join ", ", sort uniq(map { $_->{descr} } values %{$o->{packages}{mediums}})));
    $o->ask_from(
	'', $msg,
	[ {
	    val => \my $suppl,
	    list => [ N_("None"), N_("CD-ROM"), N_("Network (http)"), N_("Network (ftp)") ],
	    type => 'list',
	    format => \&translate,
	} ],
    );
    $suppl_already_asked = 1;
    return $suppl;
}

#- if the supplementary media is networked, but not the main one, network
#- support must be installed and network started.
sub prep_net_suppl_media {
    return if our $net_suppl_media_configured;
    $net_suppl_media_configured = 1;
    my ($o) = @_;
    #- install basesystem now
    $::o->do_pkgs->ensure_is_installed('basesystem', undef, 1);
    #- from install_steps_interactive:
    local $::expert = $::expert;
    require network::netconnect;
    network::netconnect::main($o->{netcnx} ||= {}, $o, $o->{modules_conf}, $o->{netc}, $o->{mouse}, $o->{intf}, 0, 1);
    require install_interactive;
    install_interactive::upNetwork($o);
    sleep(3);
}

sub selectSupplMedia {
    my ($o, $suppl_method) = @_;
    #- ask whether there are supplementary media
    my $prev_asked_medium = $asked_medium;
    if ($suppl_method && (my $suppl = ask_if_suppl_media($o)) ne 'None') {
	#- translate to method name
	$suppl_method = {
	    'CD-ROM' => 'cdrom',
	    'Network (http)' => 'http',
	    'Network (ftp)' => 'ftp',
	}->{$suppl};
	#- by convention, the media names for suppl. CDs match /^\d+s$/
	my $medium_name = $suppl_method eq 'cdrom'
	    ? (max(map { $_->{medium} =~ /^(\d+)s$/ ? $1 : 0 } values %{$o->{packages}{mediums}}) + 1) . "s"
	    : int(keys %{$o->{packages}{mediums}}) + 1;
	#- configure network if needed
	prep_net_suppl_media($o) if !scalar keys %{$o->{intf}} && $suppl_method !~ /^(?:cdrom|disk)/;
	local $::isWizard = 0;
	my $main_method = $o->{method};
	local $o->{method} = $suppl_method;
	if ($suppl_method eq 'cdrom') {
	    (my $cdromdev) = detect_devices::cdroms();
	    $o->ask_warn('', N("No device found")), return 'error' if !$cdromdev;
	    $cdrom = $cdromdev->{device};
	    devices::make($cdrom);
	    ejectCdrom($cdrom);
	    if ($o->ask_okcancel('', N("Insert the CD"), 1)) {
		#- mount suppl CD in /mnt/cdrom to avoid umounting /tmp/image
		mountCdrom("/mnt/cdrom", $cdrom);
		if ($@) {
		    log::l($@);
		    $o->ask_warn('', N("Unable to mount CD-ROM"));
		    return 'error';
		}
		useMedium($medium_name);

		#- probe for an hdlists file and then look for all hdlists listed herein
		eval { pkgs::psUsingHdlists($o, $suppl_method, "/mnt/cdrom", $o->{packages}, $medium_name) };
		log::l("psUsingHdlists failed: $@") if $@;

		#- copy latest compssUsers.pl and rpmsrate somewhere locally
		getAndSaveFile("/mnt/cdrom/media/media_info/compssUsers.pl", "/tmp/compssUsers.pl");
		getAndSaveFile("/mnt/cdrom/media/media_info/rpmsrate", "/tmp/rpmsrate");

		#- umount supplementary CD. Will re-ask for it later
		getFile("XXX"); #- close still opened filehandles
		log::l("Umounting suppl. CD, back to medium 1");
		eval { fs::umount("/mnt/cdrom") };
		#- re-mount CD 1 if this was a cdrom install
		if ($main_method eq 'cdrom') {
		    eval { 
			my $dev = detect_devices::tryOpen($cdrom);	    
			ioctl($dev, c::CDROMEJECT(), 1);
		    };
		    $o->ask_warn('', N("Insert the CD 1 again"));
		    mountCdrom("/tmp/image", $cdrom);
		    log::l($@) if $@;
		    $asked_medium = 1;
		}
	    }
	} else {
	    my $url;
	    local $global_ftp_prefix;
	    if ($suppl_method eq 'ftp') { #- mirrors are ftp only (currently)
		$url = $o->askSupplMirror(N("URL of the mirror?")) or return 'error';
		$url =~ m!^ftp://(?:(.*?)(?::(.*?))?\@)?([^/]+)/(.*)!
		    and $global_ftp_prefix = [ $3, $4, $1, $2 ]; #- for getFile
	    } else {
		$url = $o->ask_from_entry('', N("URL of the mirror?")) or return 'error';
		$url =~ s!/+\z!!;
	    }
	    useMedium($medium_name);
	    require http if $suppl_method eq 'http';
	    require ftp if $suppl_method eq 'ftp';
	    #- first, try to find an hdlists file
	    eval { pkgs::psUsingHdlists($o, $suppl_method, $url, $o->{packages}, $medium_name, \&setup_suppl_medium) };
	    if ($@) {
		log::l("psUsingHdlists failed: $@");
	    } else {
		#- copy latest compssUsers.pl and rpmsrate somewhere locally
		if ($suppl_method eq 'ftp') {
		    getAndSaveFile("media/media_info/compssUsers.pl", "/tmp/compssUsers.pl");
		    getAndSaveFile("media/media_info/rpmsrate", "/tmp/rpmsrate");
		} else {
		    getAndSaveFile("$url/media/media_info/compssUsers.pl", "/tmp/compssUsers.pl");
		    getAndSaveFile("$url/media/media_info/rpmsrate", "/tmp/rpmsrate");
		}
		useMedium($prev_asked_medium); #- back to main medium
		return $suppl_method;
	    }
	    #- then probe for an hdlist.cz
	    my $f = eval {
		if ($suppl_method eq 'http') {
		    http::getFile("$url/media_info/hdlist.cz");
		} elsif ($suppl_method eq 'ftp') {
		    getFile("media_info/hdlist.cz");
		} else { undef }
	    };
	    if (!defined $f) {
		log::l($@) if $@;
		#- no hdlist found
		$o->ask_warn('', N("Can't find a package list file on this mirror. Make sure the location is correct."));
		useMedium($prev_asked_medium);
		return 'error';
	    }
	    my $supplmedium = pkgs::psUsingHdlist(
		$suppl_method,
		$o->{packages},
		"hdlist$medium_name.cz", #- hdlist
		$medium_name,
		'', #- rpmsdir
		"Supplementary media $medium_name", #- description
		1, # selected
		$f,
	    );
	    close $f;
	    if ($supplmedium) {
		log::l("read suppl hdlist (via $suppl_method)");
		setup_suppl_medium($supplmedium, $url, $suppl_method);
	    } else {
		log::l("no suppl hdlist");
		$suppl_method = 'error';
	    }
	}
    } else {
	$suppl_method = '';
    }
    useMedium($prev_asked_medium); #- back to main medium
    return $suppl_method;
}

sub setup_suppl_medium {
    my ($supplmedium, $url, $suppl_method) = @_;
    $supplmedium->{prefix} = $url; #- for install_urpmi
    if ($suppl_method eq 'ftp') {
	$url =~ m!^ftp://(?:(.*?)(?::(.*?))?\@)?([^/]+)/(.*)!
	    and $supplmedium->{ftp_prefix} = [ $3, $4, $1, $2 ]; #- for getFile
    }
    $supplmedium->{selected} = 1;
    $supplmedium->{method} = $suppl_method;
    $supplmedium->{with_hdlist} = 'media_info/hdlist.cz'; #- for install_urpmi
}

sub _media_rank {
    my ($x) = @_;
    my ($y, $s) = $x =~ /(\d+)(s?)\)\.cz/;
    $s and $y += 100;
    $y;
}

sub load_rate_files {
    my ($o) = @_;
    #- must be done after getProvides
    #- if there is a supplementary media, the rpmsrate/compssUsers are overridable
    pkgs::read_rpmsrate(
	$o->{packages},
	$o->{rpmsrate_flags_chosen},
	getFile(-e "/tmp/rpmsrate" ? "/tmp/rpmsrate" : "media/media_info/rpmsrate")
    );
    ($o->{compssUsers}, $o->{gtk_display_compssUsers}) = pkgs::readCompssUsers(
	-e '/tmp/compssUsers.pl' ? '/tmp/compssUsers.pl' : 'media/media_info/compssUsers.pl'
    );
    defined $o->{compssUsers} or die "Can't read compssUsers.pl file, aborting installation\n";
}

sub setPackages {
    my ($o, $rebuild_needed) = @_;

    require pkgs;
    if (!$o->{packages} || is_empty_array_ref($o->{packages}{depslist})) {
	($o->{packages}, my $suppl_method, my $copy_rpms_on_disk) = pkgs::psUsingHdlists($o, $o->{method});

	1 while $suppl_method = $o->selectSupplMedia($suppl_method);

	#- open rpm db according to right mode needed.
	$o->{packages}{rpmdb} ||= pkgs::rpmDbOpen($rebuild_needed);

	#- always try to select basic kernel (else on upgrade, kernel will never be updated provided a kernel is already
	#- installed and provides what is necessary).
	pkgs::selectPackage($o->{packages},
			    pkgs::bestKernelPackage($o->{packages}) || die("missing kernel package"), 1);

	pkgs::selectPackage($o->{packages},
			    pkgs::packageByName($o->{packages}, 'basesystem') || die("missing basesystem package"), 1);

	my $rpmsrate_flags_was_chosen = $o->{rpmsrate_flags_chosen};

	put_in_hash($o->{rpmsrate_flags_chosen} ||= {}, rpmsrate_always_flags($o)); #- must be done before pkgs::read_rpmsrate()
	load_rate_files($o);

	copy_rpms_on_disk($o) if $copy_rpms_on_disk;

	set_rpmsrate_default_category_flags($o, $rpmsrate_flags_was_chosen);

	push @{$o->{default_packages}}, default_packages($o);
	select_default_packages($o);
    } else {
	#- this has to be done to make sure necessary files for urpmi are
	#- present.
	pkgs::psUpdateHdlistsDeps($o->{packages});

	#- open rpm db (always without rebuilding db, it should be false at this point).
	$o->{packages}{rpmdb} ||= pkgs::rpmDbOpen();
    }
}

sub copy_rpms_on_disk {
    my ($o) = @_;
    mkdir "$o->{prefix}/$_", 0755 foreach qw(var var/ftp var/ftp/pub var/ftp/pub/Mandrakelinux var/ftp/pub/Mandrakelinux/media);
    my $wait_w;
    local *changeMedium = sub {
	my ($method, $medium) = @_;
	my $name = pkgs::mediumDescr($o->{packages}, $medium);
	if (method_allows_medium_change($method)) {
	    my $r;
	    if ($method =~ /-iso$/) {
		$r = install_any::changeIso($name);
	    } else {
		cat_("/proc/mounts") =~ m,(/dev/\S+)\s+(/mnt/cdrom|/tmp/image),
		    and ($cdrom, my $mountpoint) = ($1, $2);
		ejectCdrom($cdrom, $mountpoint);
		undef $wait_w;
		$r = $o->ask_okcancel('', N("Change your Cd-Rom!
Please insert the Cd-Rom labelled \"%s\" in your drive and press Ok when done.", $name), 1);
		$wait_w = $o->wait_message(N("Please wait"), N("Copying in progress"));
	    }
	    return $r;
	} else {
	    return 1;
	}
    };
    my $total = $o->{mediumsize};
    log::l("totalsize=$total");
    my $pid;
    #- will we show a progress bar?
    my $copy_has_progress_bar = !method_allows_medium_change($o->{method}) || $o->{method} =~ /-iso$/;
    if ($copy_has_progress_bar) {
	#- display the progress bar only for non-cdrom installation methods
	$pid = fork();
	if (!$pid && defined $pid) { #- child
	    ($wait_w, my $wait_message) = fs::format::wait_message($o); #- nb, this is only called when interactive
	    $wait_message->(N("Copying in progress"));
	    #- from commands.pm. TODO: factorize, possibly in MDK::Common.
	    my $f; $f = sub {
		my ($e) = @_;
		my $s = (lstat($e))[12];
		$s += sum(map { &$f($_) } glob_("$e/*")) if !-l _ && -d _;
		$s;
	    };
	    while (1) {
		my $s = $f->("$o->{prefix}/var/ftp/pub/Mandrakelinux/media") / 1024;
		$wait_message->('', $s, $total);
		sleep 1;
		last if $s > $total - 100;
	    }
	    undef $wait_w;
	    c::_exit(0);
	}
    }
    foreach my $k (pkgs::allMediums($o->{packages})) {
	my $m = $o->{packages}{mediums}{$k};
	if ($k != $current_medium) {
	    do {
		askChangeMedium($o->{method}, $k)
		    or next;
		mountCdrom("/tmp/image", $cdrom);
	    } while !-d "/tmp/image/$m->{rpmsdir}";
	    $current_medium = $k;
	}
	log::l("copying /tmp/image/$m->{rpmsdir} to $o->{prefix}/var/ftp/pub/Mandrakelinux/media");
	unless ($copy_has_progress_bar) { $wait_w = $o->wait_message(N("Please wait"), N("Copying in progress")) }
	eval {
	    cp_af("/tmp/image/$m->{rpmsdir}", "$o->{prefix}/var/ftp/pub/Mandrakelinux/media");
	};
	undef $wait_w;
	log::l($@) if $@;
	$m->{prefix} = "$o->{prefix}/var/ftp/pub/Mandrakelinux";
	$m->{method} = 'disk';
	$m->{with_hdlist} = 'media_info/hdlist.cz'; #- for install_urpmi
    }
    kill 15, $pid if defined $pid;
    ejectCdrom() if $o->{method} eq "cdrom";
    #- now the install will continue as 'disk'
    $o->{method} = 'disk';
    #- shoud be enough to fool errorOpeningFile
    $current_medium = 1;
    our $copied_rpms_on_disk = 1;
}

sub set_rpmsrate_default_category_flags {
    my ($o, $rpmsrate_flags_was_chosen) = @_;

    #- if no cleaning needed, populate by default, clean is used for second or more call to this function.
    if ($::auto_install && ($o->{rpmsrate_flags_chosen} || {})->{CAT_ALL}) {
	$o->{rpmsrate_flags_chosen}{"CAT_$_"} = 1 foreach map { @{$_->{flags}} } @{$o->{compssUsers}};
    }
    if (!$rpmsrate_flags_was_chosen && !$o->{isUpgrade}) {
	#- use default selection seen in compssUsers directly.
	$_->{selected} = $_->{default_selected} foreach @{$o->{compssUsers}};
	set_rpmsrate_category_flags($o, $o->{compssUsers});
    }
}

sub set_rpmsrate_category_flags {
    my ($o, $compssUsers) = @_;

    $o->{rpmsrate_flags_chosen}{$_} = 0 foreach grep { /^CAT_/ } keys %{$o->{rpmsrate_flags_chosen}};
    $o->{rpmsrate_flags_chosen}{"CAT_$_"} = 1 foreach map { @{$_->{flags}} } grep { $_->{selected} } @$compssUsers;
    $o->{rpmsrate_flags_chosen}{CAT_SYSTEM} = 1;
}


sub rpmsrate_always_flags {
    my ($o) = @_;

    my $rpmsrate_flags_chosen = {};
    $rpmsrate_flags_chosen->{uc($_)} = 1 foreach grep { modules::probe_category("multimedia/$_") } modules::sub_categories('multimedia');
    $rpmsrate_flags_chosen->{uc($_)} = 1 foreach map { $_->{driver} =~ /Flag:(.*)/ } detect_devices::probeall();
    $rpmsrate_flags_chosen->{DOCS} = !$o->{excludedocs};
    $rpmsrate_flags_chosen->{UTF8} = $o->{locale}{utf8};
    $rpmsrate_flags_chosen->{BURNER} = 1 if detect_devices::burners();
    $rpmsrate_flags_chosen->{DVD} = 1 if detect_devices::dvdroms();
    $rpmsrate_flags_chosen->{USB} = 1 if $o->{modules_conf}->get_probeall("usb-interface");
    $rpmsrate_flags_chosen->{PCMCIA} = 1 if detect_devices::hasPCMCIA();
    $rpmsrate_flags_chosen->{HIGH_SECURITY} = 1 if $o->{security} > 3;
    $rpmsrate_flags_chosen->{BIGMEM} = 1 if detect_devices::BIGMEM();
    $rpmsrate_flags_chosen->{SMP} = 1 if detect_devices::hasSMP();
    $rpmsrate_flags_chosen->{CDCOM} = 1 if any { $_->{descr} =~ /commercial/i } values %{$o->{packages}{mediums}};
    $rpmsrate_flags_chosen->{TV} = 1 if detect_devices::getTVcards();
    $rpmsrate_flags_chosen->{'3D'} = 1 if 
      detect_devices::matching_desc__regexp('Matrox.* G[245][05]0') ||
      detect_devices::matching_desc__regexp('Rage X[CL]') ||
      detect_devices::matching_desc__regexp('3D Rage (?:LT|Pro)') ||
      detect_devices::matching_desc__regexp('Voodoo [35]') ||
      detect_devices::matching_desc__regexp('Voodoo Banshee') ||
      detect_devices::matching_desc__regexp('8281[05].* CGC') ||
      detect_devices::matching_desc__regexp('Rage 128') ||
      detect_devices::matching_desc__regexp('Radeon ') || #- all Radeon card are now 3D with 4.3.0
      detect_devices::matching_desc__regexp('[nN]Vidia.*T[nN]T2') || #- TNT2 cards
      detect_devices::matching_desc__regexp('[nN][vV]idia.*NV[56]') ||
      detect_devices::matching_desc__regexp('[nN][vV]idia.*Vanta') ||
      detect_devices::matching_desc__regexp('[nN][vV]idia.*[gG]e[fF]orce') || #- GeForce cards
      detect_devices::matching_desc__regexp('[nN][vV]idia.*NV1[15]') ||
      detect_devices::matching_desc__regexp('[nN][vV]idia.*Quadro');

    foreach (lang::langsLANGUAGE($o->{locale}{langs})) {
	$rpmsrate_flags_chosen->{qq(LOCALES"$_")} = 1;
    }
    $rpmsrate_flags_chosen->{'CHARSET"' . lang::l2charset($o->{locale}{lang}) . '"'} = 1;

    $rpmsrate_flags_chosen;
}

sub default_packages {
    my ($o) = @_;
    my @l;

    push @l, "brltty" if cat_("/proc/cmdline") =~ /brltty=/;
    push @l, "nfs-utils-clients" if $o->{method} eq "nfs";
    push @l, "numlock" if $o->{miscellaneous}{numlock};
    push @l, "mdadm" if !is_empty_array_ref($o->{all_hds}{raids});
    push @l, "lvm2" if !is_empty_array_ref($o->{all_hds}{lvms});
    push @l, "alsa", "alsa-utils" if any { $o->{modules_conf}->get_alias("sound-slot-$_") =~ /^snd-/ } 0 .. 4;
    my $dmi_System = detect_devices::dmidecode_category('System');
    my $dmi_BIOS = detect_devices::dmidecode_category('BIOS');
    my $dmi_Base_Board = detect_devices::dmidecode_category('Base Board');
    if ($dmi_System->{Manufacturer} =~ /Dell Computer/ && $dmi_System->{'Product Name'} =~ /Inspiron|Latitude/) {
        modules::append_to_modules_loaded_at_startup_for_all_kernels('i8k');
        push @l, "i8kutils";
    }
    if ($dmi_System->{Manufacturer} =~ /TOSHIBA/ && $dmi_BIOS->{Vendor} =~ /TOSHIBA/) {
        modules::append_to_modules_loaded_at_startup_for_all_kernels('toshiba');
        push @l, "toshutils";
    }
    if ($dmi_BIOS->{Vendor} eq 'COMPAL' && $dmi_BIOS->{Characteristics} =~ /Function key-initiated network boot is supported/
          || $dmi_Base_Board->{Manufacturer} =~ /^ACER/ && $dmi_Base_Board->{'Product Name'} =~ /TravelMate 610/) {
        modules::append_to_modules_loaded_at_startup_for_all_kernels('acerhk');
    }

    push @l, "grub" if isLoopback(fs::get::root($o->{fstab}));
    push @l, uniq(grep { $_ } map { fs::format::package_needed_for_partition_type($_) } @{$o->{fstab}});

    my @locale_pkgs = map { pkgs::packagesProviding($o->{packages}, 'locales-' . $_) } lang::langsLANGUAGE($o->{locale}{langs});
    unshift @l, uniq(map { $_->name } @locale_pkgs);

    @l;
}

sub select_default_packages {
    my ($o) = @_;
    pkgs::selectPackage($o->{packages}, pkgs::packageByName($o->{packages}, $_) || next) foreach @{$o->{default_packages}};
}

sub unselectMostPackages {
    my ($o) = @_;
    pkgs::unselectAllPackages($o->{packages});
    select_default_packages($o);
}

sub warnAboutNaughtyServers {
    my ($o) = @_;
    my @naughtyServers = pkgs::naughtyServers($o->{packages}) or return 1;
    my $r = $o->ask_from_list_('', 
formatAlaTeX(
             #-PO: keep the double empty lines between sections, this is formatted a la LaTeX
             N("You have selected the following server(s): %s


These servers are activated by default. They do not have any known security
issues, but some new ones could be found. In that case, you must make sure
to upgrade as soon as possible.


Do you really want to install these servers?
", join(", ", @naughtyServers))), [ N_("Yes"), N_("No") ], 'Yes') or return;
    if ($r ne 'Yes') {
	log::l("unselecting naughty servers");
	pkgs::unselectPackage($o->{packages}, pkgs::packageByName($o->{packages}, $_)) foreach @naughtyServers;
    }
    1;
}

sub warnAboutRemovedPackages {
    my ($o, $packages) = @_;
    my @removedPackages = keys %{$packages->{state}{ask_remove} || {}} or return;
    if (!$o->ask_yesorno('', 
formatAlaTeX(
             #-PO: keep the double empty lines between sections, this is formatted a la LaTeX
             N("The following packages will be removed to allow upgrading your system: %s


Do you really want to remove these packages?
", join(", ", @removedPackages))), 1)) {
	$packages->{state}{ask_remove} = {};
    }
}

sub addToBeDone(&$) {
    my ($f, $step) = @_;

    return &$f() if $::o->{steps}{$step}{done};

    push @{$::o->{steps}{$step}{toBeDone}}, $f;
}

sub set_authentication {
    my ($o) = @_;

    my $when_network_is_up = sub {
	my ($f) = @_;
	#- defer running xxx - no network yet
	addToBeDone {
	    require install_steps;
	    install_steps::upNetwork($o, 'pppAvoided');
	    $f->();
	} 'configureNetwork';
    };
    require authentication;
    authentication::set($o, $o->{netc}, $o->{authentication} ||= {}, $when_network_is_up);
}

sub killCardServices() {
    my $pid = chomp_(cat_("/tmp/cardmgr.pid"));
    $pid and kill(15, $pid); #- send SIGTERM
}

sub unlockCdrom() {
    my $cdrom = cat_("/proc/mounts") =~ m!(/dev/\S+)\s+(?:/mnt/cdrom|/tmp/image)! && $1 or return;
    eval { ioctl(detect_devices::tryOpen($cdrom), c::CDROM_LOCKDOOR(), 0) };
}

sub ejectCdrom {
    my ($o_cdrom, $o_mountpoint) = @_;
    getFile("XXX"); #- close still opened filehandle
    my $cdrom;
    if ($o_mountpoint) {
	$cdrom = $o_cdrom || cat_("/proc/mounts") =~ m!(/dev/\S+)\s+(/mnt/cdrom|/tmp/image)! && $1 or return;
    } else {
	$cdrom = cat_("/proc/mounts") =~ m!(/dev/$o_cdrom)\s+(/mnt/cdrom|/tmp/image)! && $1 or return;
	$o_mountpoint ||= $2 || '/tmp/image';
    }

    #- umount BEFORE opening the cdrom device otherwise the umount will
    #- D state if the cdrom is already removed
    eval { fs::umount($o_mountpoint) };
    $@ and warnAboutFilesStillOpen();
    eval { 
	my $dev = detect_devices::tryOpen($cdrom);
	ioctl($dev, c::CDROMEJECT(), 1) if ioctl($dev, c::CDROM_DRIVE_STATUS(), 0) == c::CDS_DISC_OK();
    };
}

sub warnAboutFilesStillOpen() {
    log::l("files still open: ", readlink($_)) foreach map { glob_("$_/fd/*") } glob_("/proc/*");
}

sub install_urpmi {
    my ($method, $packages, $mediums) = @_;

    my $hdInstallPath = any::hdInstallPath();

    #- rare case where urpmi cannot be installed (no hd install path).
    our $copied_rpms_on_disk;
    $method eq 'disk' && !$hdInstallPath && !$copied_rpms_on_disk and return;

    log::l("install_urpmi $method");
    #- clean to avoid opening twice the rpm db.
    delete $packages->{rpmdb};

    #- import pubkey in rpmdb.
    my $db = pkgs::rpmDbOpenForInstall();
    $packages->parse_pubkeys(db => $db);
    foreach my $medium (values %$mediums) {
	$packages->import_needed_pubkeys($medium->{pubkey}, db => $db, callback => sub {
					     my (undef, undef, $_k, $id, $imported) = @_;
					     if ($id) {
						 log::l(($imported ? "imported" : "found") . " key=$id for medium $medium->{descr}");
						 $medium->{key_ids}{$id} = undef;
					     }
					 });
    }

    my @cfg;
    foreach (sort { $a->{medium} <=> $b->{medium} } values %$mediums) {
	my $name = $_->{fakemedium};
	if ($_->{ignored} || $_->{selected}) {
	    my $curmethod = $_->{method} || $::o->{method};
	    my $dir = (($copied_rpms_on_disk ? "/var/ftp/pub/Mandrakelinux" : '')
		|| $_->{prefix} || ${{ nfs => "file://mnt/nfs", 
					   disk => "file:/" . $hdInstallPath,
					   ftp => $ENV{URLPREFIX},
					   http => $ENV{URLPREFIX},
					   cdrom => "removable://mnt/cdrom" }}{$curmethod} ||
		       #- for live_update or live_install script.
		       readlink("/tmp/image/media") =~ m,^(/.*)/media/*$, && "removable:/$1") . "/$_->{rpmsdir}";
	    #- use list file only if visible password or macro.
	    my $need_list = $dir =~ m,^(?:[^:]*://[^/:\@]*:[^/:\@]+\@|.*%{),; #- }

            my $removable_device;

            if ($curmethod eq 'disk-iso') {
                my $p = find { $_->{real_mntpoint} eq '/tmp/hdimage' } @{$::o->{fstab}} or
                  log::l("unable to find ISO image mountpoint, not adding urpmi media"), next;
                my $iso_info = find_ISO_image_labelled($_->{descr}) or
                  log::l("unable to find ISO image labelled $name, not adding urpmi media"), next;
                my ($iso_path) = $iso_info->{file} =~ m,^/tmp/hdimage/+(.*), or
                  log::l("unable to find ISO image file name ($iso_info->{file}), not adding urpmi media"), next;
                my $dest = "/mnt/inst_iso";
                $dir = "removable:/$dest/$_->{rpmsdir}";
                -d "$::prefix$dest" or mkdir_p("$::prefix$dest");
                $removable_device = "$p->{mntpoint}/$iso_path";
            } elsif ($curmethod eq 'cdrom') {
                $removable_device = '/dev/cdrom';
            }

	    #- build a list file if needed.
	    if ($need_list) {
		my $mask = umask 077;
		open(my $LIST, ">$::prefix/var/lib/urpmi/list.$name") or log::l("failed to write list.$name");
		umask $mask;

		#- build list file using internal data, synthesis file should exist.
		if ($_->{end} > $_->{start}) {
		    #- WARNING this method of build only works because synthesis (or hdlist)
		    #-         has been read.
		    foreach (@{$packages->{depslist}}[$_->{start} .. $_->{end}]) {
			my $arch = $_->arch;
			my $ldir = $dir;
			$ldir =~ s|/([^/]*)%{ARCH}|/./$1$arch|; $ldir =~ s|%{ARCH}|$arch|g;
			print $LIST "$ldir/" . $_->filename . "\n";
		    }
		} else {
		    #- need to use another method here to build list file.
		    open(my $F, "parsehdlist '$::prefix/var/lib/urpmi/hdlist.$name.cz' |");
		    local $_; 
		    while (<$F>) {
                        my ($arch) = /\.([^\.]+)\.rpm$/;
			my $ldir = $dir;
			$ldir =~ s|/([^/]*)%{ARCH}|/./$1$arch|; $ldir =~ s|%{ARCH}|$arch|g;
			print $LIST "$ldir/$_";
		    }
		    close $F;
		}
		close $LIST;
	    }

	    #- build a names file
	    if (open my $F, ">", "$::prefix/var/lib/urpmi/names.$name") {
		if (defined $_->{start} && defined $_->{end}) {
		    foreach ($_->{start} .. $_->{end}) {
			print $F $packages->{depslist}[$_]->name . "\n";
		    }
		}
		close $F;
	    }

	    #- touch a MD5SUM file
	    eval { output("$::prefix/var/lib/urpmi/MD5SUM", '') };

	    #- build synthesis file if there are still not existing (ie not copied from mirror).
	    if (-s "$::prefix/var/lib/urpmi/synthesis.hdlist.$name.cz" <= 32) {
		unlink "$::prefix/var/lib/urpmi/synthesis.hdlist.$name.cz";
		run_program::rooted($::prefix, "parsehdlist", ">", "/var/lib/urpmi/synthesis.hdlist.$name",
				    "--synthesis", "/var/lib/urpmi/hdlist.$name.cz");
		run_program::rooted($::prefix, "gzip", "-S", ".cz", "/var/lib/urpmi/synthesis.hdlist.$name");
	    }

	    my ($qname, $qdir) = ($name, $dir);
	    $qname =~ s/(\s)/\\$1/g; $qdir =~ s/(\s)/\\$1/g;

	    #- compute correctly reference to media/media_info
	    my $with;
	    if ($_->{update}) {
		$with = "media_info/hdlist.cz";
	    } elsif ($_->{with_hdlist}) {
		$with = $_->{with_hdlist};
	    } else {
		$with = $_->{rpmsdir};
		$with =~ s|/[^/]*%{ARCH}.*||;
		$with =~ s|/+|/|g; $with =~ s|/$||; $with =~ s|[^/]||g; $with =~ s!/!../!g;
		$with .= "../media/media_info/$_->{hdlist}";
	    }

	    #- output new urpmi.cfg format here.
	    push @cfg, "$qname " . ($need_list ? "" : $qdir) . " {
  hdlist: hdlist.$name.cz
  with_hdlist: $with" . ($need_list ? "
  list: list.$name" : "") . (keys(%{$_->{key_ids}}) ? "
  key-ids: " . join(',', keys(%{$_->{key_ids}})) : "") . (defined $removable_device && "
  removable: $removable_device") . ($_->{update} ? "
  update" : "") . "
}

";
	} else {
	    #- remove not selected media by removing hdlist and synthesis files copied.
	    log::l("removing media $name");
	    unlink "$::prefix/var/lib/urpmi/hdlist.$name.cz";
	    unlink "$::prefix/var/lib/urpmi/synthesis.hdlist.$name.cz";
	}
    }
    eval { output "$::prefix/etc/urpmi/urpmi.cfg", @cfg };
}


#-###############################################################################
#- kde stuff
#-###############################################################################
sub kderc_largedisplay {
    my ($prefix) = @_;

    update_gnomekderc($_, 'KDE', 
		     Contrast => 7,
		     kfmIconStyle => "Large",
		     kpanelIconStyle => "Normal", #- to change to Large when icons looks better
		     KDEIconStyle => "Large") foreach list_skels($prefix, '.kderc');

    substInFile {
	s/^(GridWidth)=85/$1=100/;
	s/^(GridHeight)=70/$1=75/;
    } $_ foreach list_skels($prefix, '.kde/share/config/kfmrc');
}

sub kdemove_desktop_file {
    my ($prefix) = @_;
    my @toMove = qw(doc.kdelnk news.kdelnk updates.kdelnk home.kdelnk printer.kdelnk floppy.kdelnk cdrom.kdelnk FLOPPY.kdelnk CDROM.kdelnk);

    #- remove any existing save in Trash of each user and
    #- move appropriate file there after an upgrade.
    foreach my $dir (grep { -d $_ } list_skels($prefix, 'Desktop')) {
	renamef("$dir/$_", "$dir/Trash/$_") 
	  foreach grep { -e "$dir/$_" } @toMove, grep { /\.rpmorig$/ } all($dir);
    }
}


#-###############################################################################
#- auto_install stuff
#-###############################################################################
sub auto_inst_file() { "$::prefix/root/drakx/auto_inst.cfg.pl" }

sub report_bug() {
    any::report_bug('auto_inst' => g_auto_install('', 1));
}

sub g_auto_install {
    my ($b_replay, $b_respect_privacy) = @_;
    my $o = {};

    require pkgs;
    $o->{default_packages} = pkgs::selected_leaves($::o->{packages});

    my @fields = qw(mntpoint fs_type size);
    $o->{partitions} = [ map { 
	my %l; @l{@fields} = @$_{@fields}; \%l;
    } grep { 
	$_->{mntpoint} && fs::format::known_type($_);
    } @{$::o->{fstab}} ];
    
    exists $::o->{$_} and $o->{$_} = $::o->{$_} foreach qw(locale authentication mouse netc timezone superuser intf keyboard users partitioning isUpgrade manualFstab nomouseprobe crypto security security_user libsafe netcnx useSupermount autoExitInstall X services); #- TODO modules bootloader 

    $o->{printer} = $::o->{printer} if $::o->{printer};

    local $o->{partitioning}{auto_allocate} = !$b_replay;
    $o->{autoExitInstall} = !$b_replay;
    $o->{interactiveSteps} = [ 'doPartitionDisks', 'formatPartitions' ] if $b_replay;

    #- deep copy because we're modifying it below
    $o->{users} = [ @{$o->{users} || []} ];

    my @user_info_to_remove = (
	if_($b_respect_privacy, qw(name realname home pw)), 
	qw(oldu oldg password password2),
    );
    $_ = { %{$_ || {}} }, delete @$_{@user_info_to_remove} foreach $o->{superuser}, @{$o->{users} || []};

    if ($b_respect_privacy && $o->{netcnx}) {
	if (my $type = $o->{netcnx}{type}) {
	    my @netcnx_type_to_remove = qw(passwd passwd2 login phone_in phone_out);
	    $_ = { %{$_ || {}} }, delete @$_{@netcnx_type_to_remove} foreach $o->{netcnx}{$type};
	}
    }
    
    require Data::Dumper;
    my $str = join('', 
"#!/usr/bin/perl -cw
#
# You should check the syntax of this file before using it in an auto-install.
# You can do this with 'perl -cw auto_inst.cfg.pl' or by executing this file
# (note the '#!/usr/bin/perl -cw' on the first line).
", Data::Dumper->Dump([$o], ['$o']), "\0");
    $str =~ s/ {8}/\t/g; #- replace all 8 space char by only one tabulation, this reduces file size so much :-)
    $str;
}

sub getAndSaveInstallFloppies {
    my ($o, $dest_dir, $name) = @_;    

    if ($postinstall_rpms && -d $postinstall_rpms && -r "$postinstall_rpms/auto_install.img") {
	log::l("getAndSaveInstallFloppies: using file saved as $postinstall_rpms/auto_install.img");
	cp_af("$postinstall_rpms/auto_install.img", "$dest_dir/$name.img");
	"$dest_dir/$name.img";
    } else {
	my $image = cat_("/proc/cmdline") =~ /pcmcia/ ? "pcmcia" :
	  arch() =~ /ia64|ppc/ ? "all"  : #- we only use all.img there
	  ${{ disk => 'hd_grub', 'disk-iso' => 'hd_grub', cdrom => 'cdrom', ftp => 'network', nfs => 'network', http => 'network' }}{$o->{method}};
	my $have_drivers = $image eq 'network';
	$image .= arch() =~ /sparc64/ && "64"; #- for sparc64 there are a specific set of image.

	if ($have_drivers) {
	    getAndSaveFile("install/images/${image}_drivers.img", "$dest_dir/${name}_drivers.img") or log::l("failed to write Install Floppy (${image}_drivers.img) to $dest_dir/${name}_drivers.img"), return;
	}
	getAndSaveFile("install/images/$image.img", "$dest_dir/$name.img") or log::l("failed to write Install Floppy ($image.img) to $dest_dir/$name.img"), return;

	"$dest_dir/$name.img", if_($have_drivers, "$dest_dir/${name}_drivers.img");
    }
}

sub getAndSaveAutoInstallFloppies {
    my ($o, $replay) = @_;
    my $name = ($replay ? 'replay' : 'auto') . '_install';
    my $dest_dir = "$o->{prefix}/root/drakx";

    eval { modules::load('loop') };

    if (arch() =~ /ia64/) {
	#- nothing yet
    } else {
	my $mountdir = "$o->{prefix}/root/aif-mount"; -d $mountdir or mkdir $mountdir, 0755;
	my $param = 'kickstart=floppy ' . generate_automatic_stage1_params($o);

	my @imgs = getAndSaveInstallFloppies($o, $dest_dir, $name) or return;

	foreach my $img (@imgs) {
	    my $dev = devices::set_loop($img) or log::l("couldn't set loopback device"), return;
	    find { eval { fs::mount($dev, $mountdir, $_, 0); 1 } } qw(ext2 vfat) or return;

	    if (-e "$mountdir/menu.lst") {
		# hd_grub boot disk is different than others
		substInFile {
		    s/^(\s*timeout.*)/timeout 1/;
		    s/\bautomatic=method:disk/$param/;
		} "$mountdir/menu.lst";
	    } elsif (-e "$mountdir/syslinux.cfg") {
		#- make room first
		unlink "$mountdir/help.msg", "$mountdir/boot.msg";

		substInFile { 
		    s/timeout.*/$replay ? 'timeout 1' : ''/e;
		    s/^(\s*append)/$1 $param/; 
		} "$mountdir/syslinux.cfg";

		output "$mountdir/boot.msg", $replay ? '' : "\n0c" .
"!! If you press enter, an auto-install is going to start.
   All data on this computer is going to be lost,
   including any Windows partitions !!
" . "07\n";
	    }

	    if (@imgs == 1 || $img =~ /drivers/) {
		local $o->{partitioning}{clearall} = !$replay;
		eval { output("$mountdir/auto_inst.cfg", g_auto_install($replay)) };
		$@ and log::l("Warning: <", formatError($@), ">");
	    }
	
	    fs::umount($mountdir);
	    devices::del_loop($dev);
	}
	rmdir $mountdir;
	@imgs;
    }
}


sub g_default_packages {
    my ($o) = @_;

    my ($_h, $fh) = media_browser($o, 'save', 'package_list.pl') or return;

    require Data::Dumper;
    my $str = Data::Dumper->Dump([ { default_packages => pkgs::selected_leaves($o->{packages}) } ], ['$o']);
    $str =~ s/ {8}/\t/g;
    print $fh
	   "# You should always check the syntax with 'perl -cw auto_inst.cfg.pl'\n" .
	   "# before testing.  To use it, boot with ``linux defcfg=floppy''\n" .
	   $str . "\0";
}

sub loadO {
    my ($O, $f) = @_; $f ||= auto_inst_file();
    my $o;
    if ($f =~ /^(floppy|patch)$/) {
	my $f = $f eq "floppy" ? 'auto_inst.cfg' : "patch";
	unless ($::testing) {
            my $dev = devices::make(detect_devices::floppy());
            foreach my $fs (arch() =~ /sparc/ ? 'romfs' : ('ext2', 'vfat')) {
                eval { fs::mount($dev, '/mnt', $fs, 'readonly'); 1 } and goto mount_ok;
            }
            die "Could not mount floppy [$dev]";
          mount_ok:
	    $f = "/mnt/$f";
	}
	-e $f or $f .= '.pl';

	my $_b = before_leaving {
	    fs::umount("/mnt") unless $::testing;
	    modules::unload(qw(vfat fat));
	};
	$o = loadO($O, $f);
    } else {
	my $fh;
	if (ref $f) {
	    $fh = $f;
	} else {
	    -e "$f.pl" and $f .= ".pl" unless -e $f;

	    if (-e $f) { open $fh, $f } else { $fh = getFile($f) or die N("Error reading file %s", $f) }
	}
	{
	    local $/ = "\0";
	    no strict;
	    eval <$fh>;
	    close $fh;
	    $@ and die;
	}
	$O and add2hash_($o ||= {}, $O);
    }
    $O and bless $o, ref $O;

    #- handle backward compatibility for things that changed
    foreach (@{$o->{partitions} || []}, @{$o->{manualFstab} || []}) {
	if (my $type = delete $_->{type}) {
	    if ($type =~ /^(0x)?(\d*)$/) {
		fs::type::set_pt_type($_, $type);
	    } else {
		fs::type::set_fs_type($_, $type);
	    }
	}
    }
    #- {rpmsrate_flags_chosen} was called {compssUsersChoice}
    if (my $rpmsrate_flags_chosen = delete $o->{compssUsersChoice}) {
	$o->{rpmsrate_flags_chosen} = $rpmsrate_flags_chosen;
    }
    #- compssUsers flags are now named CAT_XXX
    if ($o->{rpmsrate_flags_chosen} &&
	! any { /^CAT_/ } keys %{$o->{rpmsrate_flags_chosen}}) {
	#- we don't really know if this is needed for compatibility, but it won't hurt :)
	foreach (keys %{$o->{rpmsrate_flags_chosen}}) {
	    $o->{rpmsrate_flags_chosen}{"CAT_$_"} = $o->{rpmsrate_flags_chosen}{$_};
	}
	#- it used to be always selected
	$o->{rpmsrate_flags_chosen}{CAT_SYSTEM} = 1;
    }

    $o;
}

sub generate_automatic_stage1_params {
    my ($o) = @_;

    my $method = $o->{method};
    my @ks;

    if ($o->{method} eq 'http') {
	$ENV{URLPREFIX} =~ m!(http|ftp)://([^/:]+)(.*)! or die;
	$method = $1; #- in stage1, FTP via HTTP proxy is available through FTP config, not HTTP
	@ks = (server => $2, directory => $3);
    } elsif ($o->{method} eq 'ftp') {
	@ks = (server => $ENV{HOST}, directory => $ENV{PREFIX}, user => $ENV{LOGIN}, pass => $ENV{PASSWORD});
    } elsif ($o->{method} eq 'nfs') {
	cat_("/proc/mounts") =~ m|(\S+):(\S+)\s+/tmp/nfsimage| or internal_error("can not find nfsimage");
	@ks = (server => $1, directory => $2);
    }
    @ks = (method => $method, @ks);

    if (member($o->{method}, qw(http ftp nfs))) {
	if ($ENV{PROXY}) {
	    push @ks, proxy_host => $ENV{PROXY}, proxy_port => $ENV{PROXYPORT};
	}
	my ($intf) = values %{$o->{intf}};
	push @ks, interface => $intf->{DEVICE};
	if ($intf->{BOOTPROTO} eq 'dhcp') {
	    push @ks, network => 'dhcp';
	} else {
	    push @ks, network => 'static', ip => $intf->{IPADDR}, netmask => $intf->{NETMASK}, gateway => $o->{netc}{GATEWAY};
	    require network::network;
	    if (my @dnss = network::network::dnsServers($o->{netc})) {
		push @ks, dns => $dnss[0];
	    }
	}
    }

    #- sync it with ../mdk-stage1/automatic.c
    my %aliases = (method => 'met', network => 'netw', interface => 'int', gateway => 'gat', netmask => 'netm',
		   adsluser => 'adslu', adslpass => 'adslp', hostname => 'hos', domain => 'dom', server => 'ser',
		   directory => 'dir', user => 'use', pass => 'pas', disk => 'dis', partition => 'par');
    
    'automatic=' . join(',', map { ($aliases{$_->[0]} || $_->[0]) . ':' . $_->[1] } group_by2(@ks));
}

sub guess_mount_point {
    my ($part, $prefix, $user) = @_;

    my %l = (
	     '/'     => 'etc/fstab',
	     '/boot' => 'vmlinuz',
	     '/tmp'  => '.X11-unix',
	     '/usr'  => 'X11R6',
	     '/var'  => 'catman',
	    );

    my $handle = any::inspect($part, $prefix) or return;
    my $d = $handle->{dir};
    my $mnt = find { -e "$d/$l{$_}" } keys %l;
    $mnt ||= (stat("$d/.bashrc"))[4] ? '/root' : '/home/user' . ++$$user if -e "$d/.bashrc";
    $mnt ||= (any { -d $_ && (stat($_))[4] >= 500 && -e "$_/.bashrc" } glob_($d)) ? '/home' : '';
    ($mnt, $handle);
}

sub suggest_mount_points {
    my ($fstab, $prefix, $uniq) = @_;

    my $user;
    foreach my $part (grep { isTrueFS($_) } @$fstab) {
	$part->{mntpoint} && !$part->{unsafeMntpoint} and next; #- if already found via an fstab

	my ($mnt, $handle) = guess_mount_point($part, $prefix, \$user) or next;

	next if $uniq && fs::get::mntpoint2part($mnt, $fstab);
	$part->{mntpoint} = $mnt; delete $part->{unsafeMntpoint};

	#- try to find other mount points via fstab
	fs::merge_info_from_fstab($fstab, $handle->{dir}, $uniq, 'loose') if $mnt eq '/';
    }
    $_->{mntpoint} and log::l("suggest_mount_points: $_->{device} -> $_->{mntpoint}") foreach @$fstab;
}

sub find_root_parts {
    my ($fstab, $prefix) = @_;
    map { 
	my $handle = any::inspect($_, $prefix);
	my $f = $handle && (find { -f $_ } map { "$handle->{dir}/etc/$_" } 'mandrakelinux-release', 'mandrake-release', 'redhat-release');
	if ($f) {
	    my $s = cat_($f);
	    chomp($s);
	    $s =~ s/\s+for\s+\S+//;
	    log::l("find_root_parts found $_->{device}: $s");
	    { release => $s, part => $_, release_file => $f };
	} else { () }
    } @$fstab;
}

sub migrate_device_names {
    my ($all_hds, $from_fstab, $new_root, $root_from_fstab, $o_in) = @_;

    log::l("warning: fstab says root partition is $root_from_fstab->{device}, whereas we were reading fstab from $new_root->{device}");
    my ($old_prefix, $old_part_number) = devices::simple_partition_scan($root_from_fstab);
    my ($new_prefix, $new_part_number) = devices::simple_partition_scan($new_root);

    if ($old_part_number != $new_part_number) {
	log::l("argh, $root_from_fstab->{device} and $old_part_number->{device} are not the same partition number");
	return;
    }

    log::l("replacing $old_prefix with $new_prefix");
    
    my %h;
    foreach (@$from_fstab) {
	if ($_->{device} =~ s!^\Q$old_prefix!$new_prefix!) {
	    #- this is simple to handle, nothing more to do
	} elsif ($_->{part_number}) {
	    my $device_prefix = devices::part_prefix($_);
	    push @{$h{$device_prefix}}, $_;
	} else {
	    #- hopefully this does not need anything special
	}
    }
    my @from_fstab_per_hds = values %h or return;


    my @current_hds = grep { $new_root->{rootDevice} ne $_->{device} } fs::get::hds($all_hds);

    found_one:
    @from_fstab_per_hds or return;

    foreach my $from_fstab_per_hd (@from_fstab_per_hds) {
	my ($matching, $other) = partition { 
	    my $hd = $_;
	    every {
		my $wanted = $_;
		my $part = find { $_->{part_number} eq $wanted->{part_number} } partition_table::get_normal_parts($hd);
		$part && $part->{fs_type} && fs::type::can_be_this_fs_type($wanted, $part->{fs_type});
	    } @$from_fstab_per_hd;
	} @current_hds;
	@$matching == 1 or next;

	my ($hd) = @$matching;
	@current_hds = @$other;
	@from_fstab_per_hds = grep { $_ != $from_fstab_per_hd } @from_fstab_per_hds;

	log::l("$hd->{device} nicely corresponds to " . join(' ', map { $_->{device} } @$from_fstab_per_hd));
	foreach (@$from_fstab_per_hd) {
	    partition_table::compute_device_name($_, $hd);
	}
	goto found_one;
    }
	
    #- we can not find one and only one matching hd
    my @from_fstab_not_handled = map { @$_ } @from_fstab_per_hds;
    log::l("we still do not know what to do with: " . join(' ', map { $_->{device} } @from_fstab_not_handled));


    if (!$o_in) {
	die 'still have';
	log::l("well, ignoring them!");
	return;
    }

    my $propositions_valid = every {
	my $wanted = $_;
	my @parts = grep { $_->{part_number} eq $wanted->{part_number}
			     && $_->{fs_type} && fs::type::can_be_this_fs_type($wanted, $_->{fs_type}) } fs::get::hds_fstab(@current_hds);
	$wanted->{propositions} = \@parts;
	@parts > 0;
    } @from_fstab_not_handled;

    $o_in->ask_from('', 
		    N("The following disk(s) were renamed:"),
		    [ map {
			{ label => N("%s (previously named as %s)", $_->{mntpoint}, $_->{device}),
			  val => \$_->{device}, format => sub { $_[0] && $_->{device} },
			  list => [ '', 
				    $propositions_valid ? @{$_->{propositions}} : 
				    fs::get::hds_fstab(@current_hds) ] };
		    } @from_fstab_not_handled ]);
}

sub use_root_part {
    my ($all_hds, $part, $o_in) = @_;
    my $migrate_device_names;
    {
	my $handle = any::inspect($part, $::prefix) or die;

	my @from_fstab = fs::read_fstab($handle->{dir}, '/etc/fstab', 'keep_default');

	my $root_from_fstab = fs::get::root_(\@from_fstab);
	if (!fsedit::is_same_hd($root_from_fstab, $part)) {
	    $migrate_device_names = 1;
	    log::l("from_fstab contained: $_->{device} $_->{mntpoint}") foreach @from_fstab;
	    migrate_device_names($all_hds, \@from_fstab, $part, $root_from_fstab, $o_in);
	    log::l("from_fstab now contains: $_->{device} $_->{mntpoint}") foreach @from_fstab;
	}
	fs::add2all_hds($all_hds, @from_fstab);
	log::l("fstab is now: $_->{device} $_->{mntpoint}") foreach fs::get::fstab($all_hds);
    }
    isSwap($_) and $_->{mntpoint} = 'swap' foreach fs::get::really_all_fstab($all_hds); #- use all available swap.
    $migrate_device_names;
}

sub getHds {
    my ($o, $o_in) = @_;

  getHds: 
    my $all_hds = fsedit::get_hds($o->{partitioning}, $o_in);
    my $hds = $all_hds->{hds};

    if (is_empty_array_ref($hds) && !$::move) { #- no way
	die N("An error occurred - no valid devices were found on which to create new filesystems. Please check your hardware for the cause of this problem");
    }

    #- try to figure out if the same number of hds is available, use them if ok.
    @{$o->{all_hds}{hds} || []} == @$hds and return 1;

    fs::get_raw_hds('', $all_hds);
    fs::add2all_hds($all_hds, @{$o->{manualFstab}});

    $o->{all_hds} = $all_hds;
    $o->{fstab} = [ fs::get::really_all_fstab($all_hds) ];
    fs::merge_info_from_mtab($o->{fstab});

    my @win = grep { isFat_or_NTFS($_) && maybeFormatted($_) && !$_->{is_removable} } @{$o->{fstab}};
    log::l("win parts: ", join ",", map { $_->{device} } @win) if @win;
    if (@win == 1) {
	#- Suggest /boot/efi on ia64.
	$win[0]{mntpoint} = arch() =~ /ia64/ ? "/boot/efi" : "/mnt/windows";
    } else {
	my %w; foreach (@win) {
	    my $v = $w{$_->{device_windobe}}++;
	    $_->{mntpoint} = $_->{unsafeMntpoint} = "/mnt/win_" . lc($_->{device_windobe}) . ($v ? $v+1 : ''); #- lc cuz of StartOffice(!) cf dadou
	}
    }

    my @sunos = grep { $_->{pt_type} == 2 } @{$o->{fstab}}; #- take only into account root partitions.
    if (@sunos) {
	my $v = '';
	map { $_->{mntpoint} = $_->{unsafeMntpoint} = "/mnt/sunos" . ($v && ++$v) } @sunos;
    }
    #- a good job is to mount SunOS root partition, and to use mount point described here in /etc/vfstab.

    1;
}

my %media_browser;
sub media_browser {
    my ($in, $save, $o_suggested_name) = @_;

    my %media_type2text = (
	fd => N("Floppy"),
	hd => N("Hard Disk"),
	cdrom => N("CDROM"),
    );
    my @network_protocols = (if_(!$save, N_("HTTP")), N_("FTP"), N_("NFS"));

    my $to_text = sub {
	my ($hd) = @_;
	($media_type2text{$hd->{media_type}} || $hd->{media_type}) . ': ' . partition_table::description($hd);
    };

  ask_media:
    my $all_hds = fsedit::get_hds({}, $in);
    fs::get_raw_hds('', $all_hds);

    my @raw_hds = grep { !$save || $_->{media_type} ne 'cdrom' } @{$all_hds->{raw_hds}};
    my @dev_and_text = group_by2(
	(map { $_ => $to_text->($_) } @raw_hds),
	(map { 
	    my $hd = $to_text->($_);
	    map { $_ => join('\1', $hd, partition_table::description($_)) } grep { isTrueFS($_) || isOtherAvailableFS($_) } fs::get::hds_fstab($_);
	} fs::get::hds($all_hds)),
	if_(member($::o->{method}, qw(ftp http nfs)) || install_steps::hasNetwork($::o),
	    map { $_ => join('\1', N("Network"), translate($_)) } @network_protocols),
    );

    $in->ask_from_({
	messages => N("Please choose a media"),
    }, [ 
	{ val => \$media_browser{dev}, separator => '\1', list => [ map { $_->[1] } @dev_and_text ] },
    ]) or return;

    my $dev = (find { $_->[1] eq $media_browser{dev} } @dev_and_text)->[0];

    if (member($dev, @network_protocols)) {
	install_interactive::upNetwork($::o);
	if ($dev eq 'HTTP') {
	    require http;
	    $media_browser{network} ||= 'http://';
	} else {
	    $in->ask_warn('', 'todo');
	    goto ask_media;
	}
	while (1) {
	    $in->ask_from('', 'URL', [
		{ val => \$media_browser{network} }
	    ]) or last;
		    
	    if ($dev eq 'HTTP') {
		my $fh = http::getFile($media_browser{network});
		$fh and return '', $fh;
	    }
	}
    } else {
	if (!$dev->{fs_type} || $dev->{fs_type} eq 'auto' || $dev->{fs_type} =~ /:/) {
	    if (my $p = fs::type::type_subpart_from_magic($dev)) {
		add2hash($p, $dev);
		$dev = $p;
	    } else {
		$in->ask_warn(N("Error"), N("Bad media %s", partition_table::description($dev)));
		goto ask_media;
	    }
	}
	my $file;
	if (my $h = any::inspect($dev, $::prefix, $save)) {
	    while (1) {
		$file = $in->ask_filename({ save => $save, 
					    directory => $h->{dir}, 
					    if_($o_suggested_name, file => "$h->{dir}/$o_suggested_name"),
					}) or last;
		if (-e $file && $save) {
		    $in->ask_yesorno('', N("File already exists. Overwrite it?")) or next;
		}
		if ($save) {
		    return $h, $file;
		} else {
		    my $fh;
		    open($fh, $file) and return $h, $fh;
		}
	    }
	    undef $h; #- help perl
	} else {
	    $in->ask_warn(N("Error"), formatError($@));
	}
	goto ask_media;
    }
}

sub log_sizes {
    my ($o) = @_;
    my @df = MDK::Common::System::df($o->{prefix});
    log::l(sprintf "Installed: %s(df), %s(rpm)",
	   formatXiB($df[0] - $df[1], 1024),
	   formatXiB(sum(run_program::rooted_get_stdout($o->{prefix}, 'rpm', '-qa', '--queryformat', '%{size}\n')))) if -x "$o->{prefix}/bin/rpm";
}

sub X_options_from_o {
    my ($o) = @_;
    { 
	freedriver => $o->{freedriver},
	allowFB => $o->{allowFB},
    };
}

sub screenshot_dir__and_move() {
    my ($dir1, $dir2) = ("$::prefix/root", '/tmp');
    if (-e $dir1) {
	if (-e "$dir2/DrakX-screenshots") {
	    cp_af("$dir2/DrakX-screenshots", $dir1);
	    rm_rf("$dir2/DrakX-screenshots");
	}
	$dir1;
    } else {
	$dir2;
    }
}

sub take_screenshot {
    my ($in) = @_;
    my $dir = screenshot_dir__and_move() . '/DrakX-screenshots';
    my $warn;
    if (!-e $dir) {
	mkdir $dir or $in->ask_warn('', N("Can not make screenshots before partitioning")), return;
	$warn = 1;
    }
    my $nb = 1;
    $nb++ while -e "$dir/$nb.png";
    system("fb2png /dev/fb0 $dir/$nb.png 0");

    $in->ask_warn('', N("Screenshots will be available after install in %s", "/root/DrakX-screenshots")) if $warn;
}

sub copy_advertising {
    my ($o) = @_;

    return if $::rootwidth < 800;

    my $f;
    my $source_dir = "install/extra/advertising";
    foreach ("." . $o->{locale}{lang}, "." . substr($o->{locale}{lang},0,2), '') {
	$f = getFile("$source_dir$_/list") or next;
	$source_dir = "$source_dir$_";
    }
    if (my @files = <$f>) {
	my $dir = "$o->{prefix}/tmp/drakx-images";
	mkdir $dir;
	unlink glob_("$dir/*");
	foreach (@files) {
	    chomp;
	    getAndSaveFile("$source_dir/$_", "$dir/$_");
	    s/\.png/.pl/;
	    getAndSaveFile("$source_dir/$_", "$dir/$_");
	    s/\.pl/_icon.png/;
	    getAndSaveFile("$source_dir/$_", "$dir/$_");
	    s/_icon\.png/.png/;
	}
	@advertising_images = map { "$dir/$_" } @files;
    }
}

sub remove_advertising {
    my ($o) = @_;
    eval { rm_rf("$o->{prefix}/tmp/drakx-images") };
    @advertising_images = ();
}

sub disable_user_view() {
    substInFile { s/^UserView=.*/UserView=true/ } "$::prefix/usr/share/config/kdm/kdmrc";
    substInFile { s/^Browser=.*/Browser=0/ } "$::prefix/etc/X11/gdm/gdm.conf";
}

sub set_security {
    my ($o) = @_;
    {
	local $ENV{DRAKX_PASSWORD} = $o->{bootloader}{password};
	local $ENV{DURING_INSTALL} = 1;
	security::level::set($o->{security});
    }
    require security::various;
    security::various::config_libsafe($::prefix, $o->{libsafe});
    security::various::config_security_user($::prefix, $o->{security_user});
}

sub write_fstab {
    my ($o) = @_;
    fs::write_fstab($o->{all_hds}, $o->{prefix}) if !$o->{isUpgrade} || $o->{migrate_device_names};
}

my $clp_name = 'mdkinst.clp';
sub clp_on_disk() { "$::prefix/tmp/$clp_name" }

sub move_clp_to_disk() {
    return if -e clp_on_disk();

    my ($loop, $current_clp) = devices::find_clp_loop($clp_name) or return;
    log::l("move_clp_to_disk: copying $current_clp to ", clp_on_disk());
    cp_af($current_clp, clp_on_disk());
    run_program::run('losetup', '-r', $loop, clp_on_disk());

    #- in $current_clp eq "/tmp/$clp_name"
    unlink "/tmp/$clp_name";
}