1 |
#!/usr/bin/perl |
2 |
|
3 |
use lib qw(/usr/lib/libDrakX); |
4 |
|
5 |
# prevent firing up drakbug (doesn't work w/o X11): |
6 |
BEGIN { $ENV{DISABLE_DRAKBUG} = 1 } |
7 |
|
8 |
use strict; |
9 |
use diagnostics; |
10 |
|
11 |
# fix translating reasons for 2nd stage warning |
12 |
use lang; |
13 |
BEGIN { |
14 |
my $locale = lang::read($>); |
15 |
lang::set($locale); |
16 |
} |
17 |
|
18 |
use standalone; #- warning, standalone must be loaded very first, for 'explanations' |
19 |
use c; |
20 |
use common; |
21 |
use interactive; |
22 |
use detect_devices; |
23 |
use harddrake::data; |
24 |
use harddrake::autoconf; |
25 |
use harddrake::sound; |
26 |
use Xconfig::card; |
27 |
use modules; |
28 |
use Storable qw(store retrieve); |
29 |
|
30 |
|
31 |
my $force = member('--force', @ARGV); |
32 |
|
33 |
my $invert_do_it = $ARGV[0] eq 'X11' ? 1 : 0; |
34 |
my ($hw_sysconfdir, $timeout) = ("/etc/sysconfig/harddrake2", $invert_do_it ? 600 : 25); |
35 |
my $last_boot_config = "$hw_sysconfdir/previous_hw"; |
36 |
|
37 |
$last_boot_config .= '_X11' if $invert_do_it; |
38 |
|
39 |
# first run ? if not read old hw config |
40 |
my $previous_config; |
41 |
if (-f $last_boot_config && -s $last_boot_config) { |
42 |
eval { $previous_config = Storable::retrieve($last_boot_config) }; |
43 |
log::explanations("resetting previous hardware file ($@)") if $@; |
44 |
} |
45 |
$previous_config ||= {}; |
46 |
$previous_config = $$previous_config if ref($previous_config) !~ /HASH/; |
47 |
my $first_run = is_empty_hash_ref($previous_config); |
48 |
|
49 |
my $modules_conf = modules::any_conf->read; |
50 |
|
51 |
my $isLaptop = detect_devices::isLaptop(); |
52 |
my $curr_kernel = c::kernel_version(); |
53 |
my ($kernel_major) = $curr_kernel =~ /^(\d+\.\d+)/; |
54 |
|
55 |
my %previous_kernel_config = getVarsFromSh("$hw_sysconfdir/kernel"); |
56 |
my %previous_xorg_config = getVarsFromSh("$hw_sysconfdir/xorg"); |
57 |
setVarsInSh("$hw_sysconfdir/kernel", { KERNEL => $kernel_major, IS_LAPTOP => bool2text($isLaptop) }); |
58 |
my %cfg = getVarsFromSh("$hw_sysconfdir/service.conf"); |
59 |
|
60 |
# default to 'yes' on upgrade: |
61 |
$cfg{AUTORECONFIGURE_RIGHT_XORG_DRIVER} = 'yes' if !exists $cfg{AUTORECONFIGURE_RIGHT_XORG_DRIVER}; |
62 |
|
63 |
# autoreconfigure laptop-dependent services when switching from laptop to desktop, and vice versa |
64 |
if (!exists $previous_kernel_config{IS_LAPTOP} || $force || $isLaptop != text2bool($previous_kernel_config{IS_LAPTOP})) { |
65 |
log::explanations("Autoconfiguring laptop tools since we switched between laptop and desktop systems"); |
66 |
harddrake::autoconf::laptop($isLaptop); |
67 |
} |
68 |
|
69 |
my $known_kernels = "$hw_sysconfdir/kernels"; |
70 |
if (!member($curr_kernel, chomp_(cat_($known_kernels)))) { |
71 |
harddrake::autoconf::fix_aliases($modules_conf) if !$first_run; |
72 |
append_to_file($known_kernels, "$curr_kernel\n"); |
73 |
} |
74 |
|
75 |
sub get_xorg_driver() { |
76 |
my $x = Xconfig::xfree->read; |
77 |
if ($x) { |
78 |
my ($dev_section) = grep { $_->{name} eq 'Device' } @{$x->{raw}}; |
79 |
$dev_section && $dev_section->{l}{Driver}{val}; |
80 |
} |
81 |
} |
82 |
|
83 |
sub schedule_warn_about_switch { |
84 |
my ($reason) = @_; |
85 |
output('/var/run/harddrake-notify-x11-free-driver-switch', $reason); |
86 |
} |
87 |
|
88 |
my $lib = arch() =~ /x86_64/ ? "lib64" : "lib"; |
89 |
|
90 |
sub find_xorg_driver { |
91 |
my ($new_driver) = @_; |
92 |
# nvidia driver has special place: |
93 |
-e "/usr/$lib/xorg/modules/drivers/${new_driver}_drv.so" |
94 |
|| -e "/usr/$lib/xorg/extra-modules/${new_driver}_drv.so"; |
95 |
} |
96 |
|
97 |
sub switch_x_driver { |
98 |
my ($old_driver, $new_driver, $reason) = @_; |
99 |
if (!find_xorg_driver($new_driver)) { |
100 |
log::explanations("would switch X.org driver from '$old_driver' to '$new_driver' ($reason); but new driver is not installed"); |
101 |
return; |
102 |
} |
103 |
cp_af('/etc/X11/xorg.conf', "/etc/X11/xorg.conf.mga$^T"); |
104 |
substInFile { s!Driver "($old_driver)"!Driver "$new_driver"!g } '/etc/X11/xorg.conf'; |
105 |
log::explanations("switch X.org driver from '$old_driver' to '$new_driver' ($reason)"); |
106 |
Xconfig::card::libgl_config_and_more({ Driver => $new_driver }); |
107 |
} |
108 |
|
109 |
sub should_reconfigure_x_driver { |
110 |
my ($card_data, $device, $current_driver) = @_; |
111 |
my $reason; |
112 |
my $reconfigure; |
113 |
my $new_key = $card_data->{Driver} . $card_data->{Driver2}; |
114 |
setVarsInSh("$hw_sysconfdir/xorg", { XORG_DRV => $new_key }); |
115 |
# auto reconfigure x11 only on first time default driver have changed: |
116 |
if ($previous_xorg_config{XORG_DRV} ne $new_key) { |
117 |
if (!member($current_driver, $card_data->{Driver}, $card_data->{Driver2}, 'fbdev', 'vesa')) { |
118 |
$reason = N("The graphic card '%s' is no more supported by the '%s' driver", |
119 |
$device->{description}, $current_driver); |
120 |
$reconfigure = 1; |
121 |
} |
122 |
} elsif ((stat('/etc/X11/xorg.conf'))[9] < (stat('/etc/product.id'))[9]) { |
123 |
# when switching to a new release (product.id is newer than xorg.conf), |
124 |
# regenerate xorg.conf even if the driver used is vesa or fbdev, this |
125 |
# way we handle switches like "no driver for the card in older releases |
126 |
# but good driver in new release", see bug #53753 |
127 |
if (!member($current_driver, $card_data->{Driver}, $card_data->{Driver2})) { |
128 |
$reason = N("New release, reconfiguring X for %s", $device->{description}); |
129 |
$reconfigure = 1; |
130 |
} |
131 |
} |
132 |
|
133 |
($reconfigure, $reason); |
134 |
} |
135 |
|
136 |
my @cards = ( |
137 |
{ |
138 |
ldetect_driver_regexp => 'Card:NVIDIA', |
139 |
xorg_driver_regexp => 'nv.+', |
140 |
module_names => [ qw(NVdriver nvidia.o nvidia.ko nvidia71xx.ko nvidia96xx.ko nvidia97xx.ko nvidia173.ko nvidia-current.ko) ] |
141 |
}, |
142 |
{ |
143 |
ldetect_driver_regexp => 'Card:ATI Radeon', |
144 |
xorg_driver_regexp => 'fglrx', |
145 |
module_names => [ qw(fglrx.ko fglrx-hd2000.ko) ] |
146 |
} |
147 |
); |
148 |
|
149 |
|
150 |
my @devices; |
151 |
@devices = grep { $_->{driver} =~ /^Card:/ } detect_devices::probeall() |
152 |
if -f '/etc/X11/xorg.conf'; |
153 |
|
154 |
# do not auto reconfigure if more than one graphic card: |
155 |
$cfg{AUTORECONFIGURE_RIGHT_XORG_DRIVER} = 'no' if scalar(@devices) > 1; |
156 |
|
157 |
foreach my $device (@devices) { |
158 |
next if !text2bool($cfg{AUTORECONFIGURE_RIGHT_XORG_DRIVER}); |
159 |
|
160 |
my $id = $device->{driver} =~ /Card:(.*)/ && $1; |
161 |
my $card_data = Xconfig::card::readCardsDB("/usr/share/ldetect-lst/Cards+")->{$id}; |
162 |
my $current_driver = get_xorg_driver(); |
163 |
|
164 |
# nvidia proprietary driver in ldetect-lst can be 'nvidia173', 'nvidia-current', ... |
165 |
# but really is just 'nvidia' in xorg.conf: |
166 |
$card_data->{Driver2} =~ s/(nvidia).*/$1/; |
167 |
|
168 |
# auto reconfigure x11 only on first time default driver have changed: |
169 |
my ($should_reconfigure, $reason) = should_reconfigure_x_driver($card_data, $device, $current_driver); |
170 |
if ($should_reconfigure) { |
171 |
switch_x_driver($current_driver, $card_data->{Driver}, $reason); |
172 |
schedule_warn_about_switch($reason) if any { $current_driver =~ $_->{xorg_driver_regexp} } @cards; |
173 |
# Update $current_driver with the new one |
174 |
$current_driver = $card_data->{Driver}; |
175 |
} |
176 |
|
177 |
# nv->nouveau or non_kms_nouveau->kms_nouveau can't have "Disable dri"! |
178 |
if ($current_driver eq "nouveau") { |
179 |
my $raw_x = Xconfig::xfree->read; |
180 |
if ($raw_x) { |
181 |
if (member("dri", $raw_x->get_disabled_modules)) { |
182 |
$raw_x->remove_disable_module("dri"); |
183 |
$raw_x->write; |
184 |
} |
185 |
} |
186 |
} |
187 |
} |
188 |
|
189 |
|
190 |
foreach my $card (@cards) { |
191 |
my $device = find { $_->{driver} =~ /$card->{ldetect_driver_regexp}/ } @devices; |
192 |
next if !$device; |
193 |
|
194 |
if (find { -e join('', "/lib/modules/", c::kernel_version(), $_) } |
195 |
map { ("/dkms/$_", "/dkms-binary/$_", "/kernel/$_") } map { "/drivers/$_" } map { ("extra/$_", "video/$_", "char/$_", "char/drm/$_") } map { $_, "$_.gz" } @{$card->{module_names}}) { |
196 |
|
197 |
# do not automatically switch from nv to nvidia (in order to handle |
198 |
# cases where nvidia module crashes the system): |
199 |
# |
200 |
# substInFile { |
201 |
# log::explanations("switch XFree86 driver from nv to nvidia") if /Driver "nv"/; |
202 |
# s!Driver "nv.*"!Driver "nvidia"!g; |
203 |
# s!#*( Load.*glx)!\1!g; |
204 |
# } $_ foreach "/etc/X11/XF86Config-4", "/etc/X11/XF86Config"; |
205 |
} else { |
206 |
my @cards = Xconfig::card::probe(); |
207 |
my $driver = $cards[0]{Driver}; |
208 |
my $old_driver = cat_('/etc/X11/xorg.conf') =~ /Driver "($card->{xorg_driver_regexp})"/ && $1; |
209 |
if ($old_driver) { |
210 |
my $reason = N("The proprietary kernel driver was not found for '%s' X.org driver", |
211 |
$old_driver); |
212 |
switch_x_driver($card->{xorg_driver_regexp}, $driver, $reason); |
213 |
schedule_warn_about_switch($reason); |
214 |
} |
215 |
} |
216 |
} |
217 |
|
218 |
my $is_globetrotter = -f '/usr/sbin/mdkmove'; |
219 |
|
220 |
my (%config, $wait); |
221 |
my $in; |
222 |
my $plymouth = -x '/bin/plymouth'; |
223 |
|
224 |
# For each hw, class, detect device, compare and offer to reconfigure if needed |
225 |
foreach my $hw_class (@harddrake::data::tree) { |
226 |
my ($Ident, $item, $configurator, $detector, $do_it) = @$hw_class{qw(class string configurator detector checked_on_boot)}; |
227 |
next if member($cfg{"DETECT_$Ident"}, qw(NO no)); |
228 |
|
229 |
$configurator ||= $hw_class->{configurator}; |
230 |
|
231 |
next unless $do_it ^ $invert_do_it; |
232 |
# No detector ? (should never happen but who know ?) |
233 |
ref($detector) eq 'CODE' or next; |
234 |
|
235 |
my %ID = map { |
236 |
my $i = $_; |
237 |
my $id = defined $i->{device} ? $i->{device} : join(':', map { $i->{$_} } qw(vendor id subvendor subid)); |
238 |
$id => $i; |
239 |
} eval { $detector->({}) }; |
240 |
$config{$Ident} = \%ID; |
241 |
next if !$is_globetrotter && !$force && $first_run; # do not fsck on first run but if --force |
242 |
|
243 |
my $oldconfig = $force ? {} : $previous_config->{$Ident}; |
244 |
|
245 |
my $msg; |
246 |
my @was_removed = difference2([ keys %$oldconfig ], [ keys %ID ]); |
247 |
if (@was_removed) { |
248 |
$msg .= N("Some devices in the \"%s\" hardware class were removed:\n", $item) . |
249 |
join('', map { N("- %s was removed\n", harddrake::data::custom_id($oldconfig->{$_}, $item)) } @was_removed) . "\n"; |
250 |
} |
251 |
my @added = difference2([ keys %ID ], [ keys %$oldconfig ]); |
252 |
$msg .= N("Some devices were added: %s\n", $item) if @added; |
253 |
$msg .= N("- %s was added\n", harddrake::data::custom_id($ID{$_}, $item)) foreach @added; |
254 |
log::explanations("removed $Ident: " . harddrake::data::custom_id($oldconfig->{$_}, $item)) foreach @was_removed; |
255 |
log::explanations("added $Ident: " . harddrake::data::custom_id($ID{$_}, $item)) foreach @added; |
256 |
|
257 |
if ($Ident eq 'FIREWIRE_CONTROLLER' && any { $_->{driver} eq 'ohci1394' } @ID{@added}) { |
258 |
modules::load_and_configure($modules_conf, 'ohci1394'); |
259 |
$modules_conf->write; |
260 |
} |
261 |
|
262 |
@added || @was_removed or $cfg{"DETECT_$Ident"} ne 'force' and next; |
263 |
|
264 |
next if $Ident eq 'MOUSE' && $kernel_major ne $previous_kernel_config{KERNEL} && $cfg{"DETECT_$Ident"} ne 'force'; |
265 |
|
266 |
my @configurator_pool = $configurator; |
267 |
|
268 |
if ($Ident eq "AUDIO") { |
269 |
# automatic sound slots configuration |
270 |
rm_rf("/etc/asound.state") if -e "/etc/asound.state"; |
271 |
harddrake::sound::configure_sound_slots($modules_conf); |
272 |
next; |
273 |
} elsif ($Ident eq "ETHERNET") { |
274 |
$modules_conf->remove_alias_regexp('^(wlan|eth)[0-9]*$'); |
275 |
modules::load_category($modules_conf, 'network/main|gigabit|usb|wireless|firewire|pcmcia'); |
276 |
require network::connection::ethernet; |
277 |
network::connection::ethernet::configure_eth_aliases($modules_conf); |
278 |
require network::rfswitch; |
279 |
network::rfswitch::configure(); |
280 |
require network::shorewall; |
281 |
network::shorewall::update_interfaces_list(); |
282 |
$modules_conf->write; |
283 |
next; |
284 |
} elsif (member($Ident, qw(ATA_STORAGE CARD_READER RAID_STORAGE SATA_STORAGE SCSI_CONTROLLER))) { |
285 |
# set scsi_hostadapter in modprobe.conf: |
286 |
modules::load_category($modules_conf, 'disk/' . { |
287 |
ATA_STORAGE => 'ide', |
288 |
SATA_STORAGE => 'sata', |
289 |
SCSI_CONTROLLER => 'scsi', |
290 |
RAID_STORAGE => 'hardware_raid', |
291 |
CARD_READER => 'card_reader' |
292 |
}->{$Ident}); |
293 |
$modules_conf->write; |
294 |
next; |
295 |
} elsif (member($Ident, qw(AGP DVB TV))) { |
296 |
my @old_drivers = uniq(map { $_->{driver} } values %$oldconfig); |
297 |
my @new_drivers = uniq(map { $_->{driver} } values %ID); |
298 |
# load DVB & TV drivers (eg: for One), not for AGP (done by X): |
299 |
modules::load_category($modules_conf, 'multimedia/' . lc($Ident)) if member($Ident, qw(DVB TV)); |
300 |
$modules_conf->remove_module(difference2(\@old_drivers, \@new_drivers)); |
301 |
# add agpgart and the like modules to modprobe.preload if needed: |
302 |
$modules_conf->write; |
303 |
foreach (difference2(\@new_drivers, \@old_drivers)) { |
304 |
eval { modules::load($_) }; |
305 |
warn "warning: $@" if $@; |
306 |
} |
307 |
next; |
308 |
} elsif ($Ident eq "BLUETOOTH") { |
309 |
harddrake::autoconf::bluetooth(scalar keys %ID); |
310 |
} elsif ($Ident eq "PCMCIA_CONTROLLER") { |
311 |
harddrake::autoconf::pcmcia(keys %ID ? first(values(%ID))->{driver} : ''); |
312 |
} elsif ($Ident eq "USB_CONTROLLER") { |
313 |
# nearly useless (only mkinitrd uses it): |
314 |
modules::load_category($modules_conf, 'bus/usb'); |
315 |
$modules_conf->write; |
316 |
} elsif ($Ident eq "VIDEO") { |
317 |
# explicitely NOT read the existing config (eg: new profile with globetrotter) |
318 |
harddrake::autoconf::xconf($modules_conf, {}, member($cfg{SETUP_FB}, qw(NO no)), $cfg{RESOLUTION_WANTED}); |
319 |
next; |
320 |
} elsif ($Ident eq "MOUSE") { |
321 |
harddrake::autoconf::mouse_conf($modules_conf); |
322 |
next; |
323 |
} elsif ($Ident eq "CPU") { |
324 |
harddrake::autoconf::cpufreq(); |
325 |
} elsif ($Ident eq "FLOPPY") { |
326 |
harddrake::autoconf::floppy(); |
327 |
} |
328 |
|
329 |
next if $is_globetrotter && !$hw_class->{automatic}; |
330 |
next unless $configurator_pool[0]; |
331 |
if (ref($configurator) ne 'CODE' && !-x first(split /\s+/, $configurator_pool[0])) { |
332 |
log::explanations(qw(skip $Ident configuration since "$configurator" is not executable)); |
333 |
next; |
334 |
} |
335 |
my ($pid, $no, $res); |
336 |
$hw_class->{automatic} ||= ref($configurator) eq 'CODE'; |
337 |
|
338 |
if (!$hw_class->{automatic}) { |
339 |
$SIG{ALRM} = sub { $no = 1; kill 15, $pid }; |
340 |
unless ($pid = fork()) { |
341 |
$plymouth and system('plymouth', 'hide-splash'); |
342 |
exec("/usr/share/harddrake/confirm", $Ident, $timeout, $msg); |
343 |
} |
344 |
alarm($timeout); |
345 |
wait(); |
346 |
$res = $?; |
347 |
alarm(0); |
348 |
} else { |
349 |
$res = 1; |
350 |
} |
351 |
if (ref($configurator) eq 'CODE') { |
352 |
eval { $configurator->() }; |
353 |
log::explanations(qw(cannot run "$configurator": $@)) if $@; |
354 |
} elsif (!$no && $res) { |
355 |
foreach my $program (@configurator_pool) { |
356 |
if (fork()) { |
357 |
wait(); |
358 |
} else { |
359 |
log::explanations(qq(run "$program")); |
360 |
exec("$program 2>/dev/null") or do { |
361 |
log::explanations(qq(cannot run "$program")); |
362 |
require POSIX; |
363 |
POSIX::_exit(); |
364 |
}; |
365 |
} |
366 |
} |
367 |
} |
368 |
if (!$hw_class->{automatic}) { |
369 |
require interactive; |
370 |
undef $wait; |
371 |
$in ||= interactive->vnew; |
372 |
$wait = $in->wait_message(N("Please wait"), N("Hardware probing in progress")); |
373 |
} |
374 |
|
375 |
} |
376 |
|
377 |
# output new hw config |
378 |
log::explanations("created file $last_boot_config"); |
379 |
Storable::store(\%config, $last_boot_config); |
380 |
|
381 |
$in->exit(0) if $in; |