1 |
package bootloader; # $Id: bootloader.pm 267511 2010-04-12 12:34:45Z cfergeau $ |
2 |
|
3 |
use diagnostics; |
4 |
use strict; |
5 |
|
6 |
#-###################################################################################### |
7 |
#- misc imports |
8 |
#-###################################################################################### |
9 |
use common; |
10 |
use fs::type; |
11 |
use fs::get; |
12 |
use fs::loopback; |
13 |
use fs::proc_partitions; |
14 |
use log; |
15 |
use any; |
16 |
use devices; |
17 |
use detect_devices; |
18 |
use partition_table::raw; |
19 |
use run_program; |
20 |
use modules; |
21 |
|
22 |
#-##################################################################################### |
23 |
#- Functions |
24 |
#-##################################################################################### |
25 |
my $vmlinuz_regexp = 'vmlinu[xz]|win4lin|uImage'; |
26 |
my $decompose_vmlinuz_name = qr/((?:$vmlinuz_regexp).*?)-(\d+\.\d+.*)/; |
27 |
|
28 |
sub expand_vmlinuz_symlink { |
29 |
my ($vmlinuz) = @_; |
30 |
my $f = $::prefix . ($vmlinuz =~ m!^/! ? $vmlinuz : "/boot/$vmlinuz"); |
31 |
-l $f ? readlink($f) : $vmlinuz; |
32 |
} |
33 |
|
34 |
sub installed_vmlinuz_raw() { grep { /^($vmlinuz_regexp)/ } all("$::prefix/boot") } |
35 |
sub installed_vmlinuz() { grep { ! -l "$::prefix/boot/$_" } installed_vmlinuz_raw() } |
36 |
sub vmlinuz2version { |
37 |
my ($vmlinuz) = @_; |
38 |
expand_vmlinuz_symlink($vmlinuz) =~ /$decompose_vmlinuz_name/ && $2; |
39 |
} |
40 |
sub vmlinuz2kernel_str { |
41 |
my ($vmlinuz) = @_; |
42 |
my ($basename, $version) = expand_vmlinuz_symlink($vmlinuz) =~ /$decompose_vmlinuz_name/ or return; |
43 |
{ |
44 |
basename => $basename, |
45 |
version => $version, |
46 |
$version =~ /([\d.]*)-(\D.*)-((\d+|0\.rc\d+.*)\.mga.*)$/ ? #- eg: 3.0.0-1.mga2 |
47 |
(ext => $2, version_no_ext => "$1-$3") : |
48 |
$version =~ /(.*mga)-?(.*)/ ? #- (old) eg: 2.6.17-13mdventerprise |
49 |
(ext => $2, version_no_ext => $1) : (version_no_ext => $version), |
50 |
}; |
51 |
} |
52 |
|
53 |
sub kernel_str2short_name { |
54 |
my ($kernel) = @_; |
55 |
$kernel->{basename}; |
56 |
} |
57 |
|
58 |
sub basename2initrd_basename { |
59 |
my ($basename) = @_; |
60 |
$basename =~ s!(vmlinu[zx]|uImage)-?!!; #- here we do not use $vmlinuz_regexp since we explictly want to keep all that is not "vmlinuz" |
61 |
'initrd' . ($basename ? "-$basename" : ''); |
62 |
} |
63 |
sub kernel_str2vmlinuz_long { |
64 |
my ($kernel) = @_; |
65 |
$kernel->{basename} . '-' . $kernel->{version}; |
66 |
} |
67 |
sub kernel_str2initrd_long { |
68 |
my ($kernel) = @_; |
69 |
basename2initrd_basename(kernel_str2short_name($kernel)) . '-' . $kernel->{version} . '.img'; |
70 |
} |
71 |
sub kernel_str2vmlinuz_short { |
72 |
my ($kernel) = @_; |
73 |
if ($kernel->{use_long_name}) { |
74 |
kernel_str2vmlinuz_long($kernel); |
75 |
} else { |
76 |
kernel_str2short_name($kernel); |
77 |
} |
78 |
} |
79 |
sub kernel_str2initrd_short { |
80 |
my ($kernel) = @_; |
81 |
if ($kernel->{use_long_name}) { |
82 |
kernel_str2initrd_long($kernel); |
83 |
} else { |
84 |
basename2initrd_basename(kernel_str2short_name($kernel)) . '.img'; |
85 |
} |
86 |
} |
87 |
|
88 |
sub kernel_str2label { |
89 |
my ($kernel, $o_use_long_name) = @_; |
90 |
if ($o_use_long_name || $kernel->{use_long_name}) { |
91 |
_sanitize_ver($kernel); |
92 |
} else { |
93 |
my $short_name = kernel_str2short_name($kernel); |
94 |
$kernel->{ext} =~ /^xen/ ? 'xen' : ($short_name eq 'vmlinuz' ? 'linux' : $short_name); |
95 |
} |
96 |
} |
97 |
|
98 |
sub get { |
99 |
my ($vmlinuz, $bootloader) = @_; |
100 |
$_->{kernel_or_dev} && $_->{kernel_or_dev} eq $vmlinuz and return $_ foreach @{$bootloader->{entries}}; |
101 |
undef; |
102 |
} |
103 |
sub get_label { |
104 |
my ($label, $bootloader) = @_; |
105 |
$_->{label} && lc(make_label_lilo_compatible($_->{label})) eq lc(make_label_lilo_compatible($label)) and return $_ foreach @{$bootloader->{entries}}; |
106 |
undef; |
107 |
} |
108 |
|
109 |
sub mkinitrd { |
110 |
my ($kernel_version, $bootloader, $entry, $initrd) = @_; |
111 |
|
112 |
$::testing || -e "$::prefix/$initrd" and return $initrd; |
113 |
|
114 |
# for /boot on dos partitions when installing on loopback file on dos partition |
115 |
my $loop_boot = fs::loopback::prepare_boot(); |
116 |
|
117 |
modules::load('loop'); |
118 |
my @options = ( |
119 |
if_($::isInstall, "-v"), "-f", $initrd, $kernel_version, |
120 |
if_($entry->{initrd_options}, split(' ', $entry->{initrd_options})), |
121 |
); |
122 |
if (!run_program::rooted($::prefix, 'mkinitrd', @options)) { |
123 |
unlink("$::prefix/$initrd"); |
124 |
die "mkinitrd failed:\n(mkinitrd @options)"; |
125 |
} |
126 |
add_boot_splash($initrd, $entry->{vga} || $bootloader->{vga}); |
127 |
|
128 |
fs::loopback::save_boot($loop_boot); |
129 |
|
130 |
-e "$::prefix/$initrd" && $initrd; |
131 |
} |
132 |
|
133 |
sub rebuild_initrd { |
134 |
my ($kernel_version, $bootloader, $entry, $initrd) = @_; |
135 |
|
136 |
my $old = $::prefix . $entry->{initrd} . '.old'; |
137 |
unlink $old; |
138 |
rename "$::prefix$initrd", $old; |
139 |
if (!mkinitrd($kernel_version, $bootloader, $entry, $initrd)) { |
140 |
log::l("rebuilding initrd failed, putting back the old one"); |
141 |
rename $old, "$::prefix$initrd"; |
142 |
} |
143 |
} |
144 |
|
145 |
sub remove_boot_splash { |
146 |
my ($initrd) = @_; |
147 |
run_program::rooted($::prefix, '/usr/share/bootsplash/scripts/remove-boot-splash', $initrd); |
148 |
} |
149 |
sub add_boot_splash { |
150 |
my ($initrd, $vga) = @_; |
151 |
|
152 |
$vga or return; |
153 |
|
154 |
eval { require Xconfig::resolution_and_depth } or return; |
155 |
if (my $res = Xconfig::resolution_and_depth::from_bios($vga)) { |
156 |
run_program::rooted($::prefix, '/usr/share/bootsplash/scripts/make-boot-splash', $initrd, $res->{X}); |
157 |
} else { |
158 |
log::l("unknown vga bios mode $vga"); |
159 |
} |
160 |
} |
161 |
sub update_splash { |
162 |
my ($bootloader) = @_; |
163 |
|
164 |
foreach (@{$bootloader->{entries}}) { |
165 |
add_boot_splash($_->{initrd}, $_->{vga} || $bootloader->{vga}) if $_->{initrd}; |
166 |
} |
167 |
} |
168 |
|
169 |
sub read { |
170 |
my ($all_hds) = @_; |
171 |
my $fstab = [ fs::get::fstab($all_hds) ]; |
172 |
foreach my $main_method (main_method_choices()) { |
173 |
my $f = $bootloader::{"read_$main_method"} or die "unknown bootloader method $main_method (read)"; |
174 |
my $bootloader = $f->($fstab); |
175 |
|
176 |
cleanup_entries($bootloader); |
177 |
|
178 |
# handle raid-extra-boot (lilo) |
179 |
my @devs = $bootloader->{boot}; |
180 |
if ($bootloader->{'raid-extra-boot'} =~ /mbr/ && |
181 |
(my $md = fs::get::device2part($bootloader->{boot}, $all_hds->{raids}))) { |
182 |
@devs = map { $_->{rootDevice} } @{$md->{disks}}; |
183 |
} elsif ($bootloader->{'raid-extra-boot'} =~ m!/dev/!) { |
184 |
@devs = split(',', $bootloader->{'raid-extra-boot'}); |
185 |
} |
186 |
|
187 |
my ($type) = map { |
188 |
if (m!/fd\d+$!) { |
189 |
warn "not checking the method on floppy, assuming $main_method is right\n"; |
190 |
$main_method; |
191 |
} elsif (member($main_method, qw(yaboot cromwell silo pmon2000 uboot))) { |
192 |
#- not checking, there's only one bootloader anyway :) |
193 |
$main_method; |
194 |
} elsif (my $type = partition_table::raw::typeOfMBR($_)) { |
195 |
warn "typeOfMBR $type on $_ for method $main_method\n" if $ENV{DEBUG}; |
196 |
$type; |
197 |
} else { () } |
198 |
} @devs; |
199 |
|
200 |
if ($type eq $main_method) { |
201 |
my @prefered_entries = map { get_label($_, $bootloader) } $bootloader->{default}, 'linux'; |
202 |
|
203 |
if (my $default = find { $_ && $_->{type} eq 'image' } (@prefered_entries, @{$bootloader->{entries}})) { |
204 |
$bootloader->{default_options} = $default; |
205 |
$bootloader->{perImageAppend} ||= $default->{append}; |
206 |
log::l("perImageAppend is now $bootloader->{perImageAppend}"); |
207 |
} else { |
208 |
$bootloader->{default_options} = {}; |
209 |
} |
210 |
return $bootloader; |
211 |
} |
212 |
} |
213 |
} |
214 |
|
215 |
sub read_grub { |
216 |
my ($fstab) = @_; |
217 |
|
218 |
my $grub2dev = read_grub_device_map(); |
219 |
my $boot_root = read_grub_install_sh(); |
220 |
_may_fix_grub2dev($fstab, $grub2dev, $boot_root->{boot_part}); |
221 |
|
222 |
my $bootloader = read_grub_menu_lst($fstab, $grub2dev) or return; |
223 |
|
224 |
if ($boot_root->{boot}) { |
225 |
$bootloader->{boot} = grub2dev($boot_root->{boot}, $grub2dev); |
226 |
} |
227 |
|
228 |
$bootloader; |
229 |
} |
230 |
|
231 |
# adapts device.map (aka $grub2dev) when for example hda is now sda |
232 |
# nb: |
233 |
# - $boot_part comes from /boot/grub/install.sh "root (hd...)" line |
234 |
# - $grub2dev is /boot/grub/device.map |
235 |
sub _may_fix_grub2dev { |
236 |
my ($fstab, $grub2dev, $boot_part) = @_; |
237 |
|
238 |
$boot_part or log::l("install.sh does not contain 'root (hd...)' line, no way to magically adapt device.map"), return; |
239 |
|
240 |
my $real_boot_part = fs::get::root_($fstab, 'boot') or |
241 |
log::l("argh... the fstab given is useless, it doesn't contain '/'"), return; |
242 |
|
243 |
my $real_boot_dev = $real_boot_part->{rootDevice} or return; # if /boot is on Linux RAID 1, hope things are all right... |
244 |
|
245 |
if (my $prev_boot_part = fs::get::device2part(grub2dev($boot_part, $grub2dev), $fstab)) { # the boot_device as far as grub config files say |
246 |
$real_boot_part == $prev_boot_part and return; |
247 |
} |
248 |
|
249 |
log::l("WARNING: we have detected that device.map is inconsistent with the system"); |
250 |
|
251 |
my ($hd_grub, undef, undef) = parse_grub_file($boot_part); # extract hdX |
252 |
if (my $prev_hd_grub = find { $grub2dev->{$_} eq $real_boot_dev } keys %$grub2dev) { |
253 |
$grub2dev->{$prev_hd_grub} = $grub2dev->{$hd_grub}; |
254 |
log::l("swapping result: $hd_grub/$real_boot_dev and $prev_hd_grub/$grub2dev->{$hd_grub}"); |
255 |
} else { |
256 |
log::l("argh... can't swap, setting $hd_grub to $real_boot_dev anyway"); |
257 |
} |
258 |
$grub2dev->{$hd_grub} = $real_boot_dev; |
259 |
} |
260 |
|
261 |
sub read_grub_install_sh() { |
262 |
my $s = cat_("$::prefix/boot/grub/install.sh"); |
263 |
my %h; |
264 |
|
265 |
#- matches either: |
266 |
#- setup (hd0) |
267 |
#- install (hd0,0)/boot/grub/stage1 d (hd0) (hd0,0)/boot/grub/stage2 p (hd0,0)/boot/grub/menu.lst |
268 |
if ($s =~ /^(?:setup.*|install\s.*\sd)\s+(\(.*?\))/m) { |
269 |
$h{boot} = $1; |
270 |
} |
271 |
if ($s =~ /^root\s+(\(.*?\))/m) { |
272 |
$h{boot_part} = $1; |
273 |
} |
274 |
\%h; |
275 |
} |
276 |
|
277 |
sub _parse_grub_menu_lst() { |
278 |
my $global = 1; |
279 |
my ($e, %b); |
280 |
|
281 |
my $menu_lst_file = "$::prefix/boot/grub/menu.lst"; |
282 |
-e $menu_lst_file or return; |
283 |
|
284 |
foreach (MDK::Common::File::cat_utf8($menu_lst_file)) { |
285 |
my $verbatim = $_; |
286 |
chomp; |
287 |
s/^\s*//; s/\s*$//; |
288 |
next if /^#/ || /^$/; |
289 |
my ($keyword, $v) = split('[ \t=]+', $_, 2) or |
290 |
warn qq(unknown line in /boot/grub/menu.lst: "$_"\n), next; |
291 |
|
292 |
if ($keyword eq 'root') { |
293 |
#- rename to avoid name conflict |
294 |
$keyword = 'grub_root'; |
295 |
} |
296 |
|
297 |
if ($keyword eq 'title') { |
298 |
push @{$b{entries}}, $e = { label => $v }; |
299 |
$global = 0; |
300 |
} elsif ($global) { |
301 |
$b{$keyword} = $v; |
302 |
} else { |
303 |
if ($keyword eq 'kernel') { |
304 |
$e->{type} = 'image'; |
305 |
$e->{kernel} = $v; |
306 |
} elsif ($keyword eq 'chainloader') { |
307 |
$e->{type} = 'other'; |
308 |
$e->{append} = ""; |
309 |
} elsif ($keyword eq 'configfile') { |
310 |
$e->{type} = 'grub_configfile'; |
311 |
$e->{configfile} = $v; |
312 |
} elsif ($keyword eq 'map') { |
313 |
$e->{mapdrive}{$2} = $1 if $v =~ m/\((.*)\) \((.*)\)/; |
314 |
} elsif ($keyword eq 'module') { |
315 |
push @{$e->{modules}}, $v; |
316 |
} else { |
317 |
$e->{$keyword} = $v eq '' ? 1 : $v; |
318 |
} |
319 |
} |
320 |
$e and $e->{verbatim} .= $verbatim; |
321 |
} |
322 |
|
323 |
%b; |
324 |
} |
325 |
|
326 |
sub is_already_crypted { |
327 |
my ($password) = @_; |
328 |
$password =~ /^--md5 (.*)/; |
329 |
} |
330 |
|
331 |
sub read_grub_menu_lst { |
332 |
my ($fstab, $grub2dev) = @_; |
333 |
|
334 |
my %b = _parse_grub_menu_lst(); |
335 |
|
336 |
foreach my $keyword (grep { $_ ne 'entries' } keys %b) { |
337 |
$b{$keyword} = $b{$keyword} eq '' ? 1 : grub2file($b{$keyword}, $grub2dev, $fstab, \%b); |
338 |
} |
339 |
|
340 |
#- sanitize |
341 |
foreach my $e (@{$b{entries}}) { |
342 |
if (member($e->{type}, 'other', 'grub_configfile')) { |
343 |
eval { $e->{kernel_or_dev} = grub2dev($e->{rootnoverify} || $e->{grub_root}, $grub2dev) }; |
344 |
$e->{keep_verbatim} = 1 unless $e->{kernel_or_dev}; |
345 |
} elsif ($e->{initrd}) { |
346 |
my $initrd; |
347 |
eval { $initrd = grub2file($e->{initrd}, $grub2dev, $fstab, $e) }; |
348 |
if ($initrd) { |
349 |
$e->{initrd} = $initrd; |
350 |
} else { |
351 |
$e->{keep_verbatim} = 1; |
352 |
} |
353 |
} |
354 |
|
355 |
if ($e->{kernel} =~ /xen/ && @{$e->{modules} || []} == 2 && $e->{modules}[1] =~ /initrd/) { |
356 |
(my $xen, $e->{xen_append}) = split(' ', $e->{kernel}, 2); |
357 |
($e->{kernel}, my $initrd) = @{delete $e->{modules}}; |
358 |
$e->{xen} = grub2file($xen, $grub2dev, $fstab, $e); |
359 |
$e->{initrd} = grub2file($initrd, $grub2dev, $fstab, $e); |
360 |
} |
361 |
if (my $v = delete $e->{kernel}) { |
362 |
(my $kernel, $e->{append}) = split(' ', $v, 2); |
363 |
$e->{append} = join(' ', grep { !/^BOOT_IMAGE=/ } split(' ', $e->{append})); |
364 |
$e->{root} = $1 if $e->{append} =~ s/root=(\S*)\s*//; |
365 |
eval { $e->{kernel_or_dev} = grub2file($kernel, $grub2dev, $fstab, $e) }; |
366 |
$e->{keep_verbatim} = 1 unless $e->{kernel_or_dev} && dirname($e->{kernel_or_dev}) eq '/boot'; |
367 |
} |
368 |
my ($vga, $other) = partition { /^vga=/ } split(' ', $e->{append}); |
369 |
if (@$vga) { |
370 |
$e->{vga} = $vga->[0] =~ /vga=(.*)/ && $1; |
371 |
$e->{append} = join(' ', @$other); |
372 |
} |
373 |
} |
374 |
|
375 |
$b{nowarn} = 1; |
376 |
# handle broken installkernel -r: |
377 |
if (@{$b{entries}}) { |
378 |
$b{default} = min($b{default}, scalar(@{$b{entries}}) - 1); |
379 |
$b{default} = $b{entries}[$b{default}]{label}; |
380 |
} |
381 |
$b{method} = $b{gfxmenu} ? 'grub-graphic' : 'grub-menu'; |
382 |
|
383 |
\%b; |
384 |
} |
385 |
|
386 |
sub yaboot2dev { |
387 |
my ($of_path) = @_; |
388 |
find { dev2yaboot($_) eq $of_path } map { "/dev/$_->{dev}" } fs::proc_partitions::read_raw(); |
389 |
} |
390 |
|
391 |
# assumes file is in /boot |
392 |
# to do: use yaboot2dev for files as well |
393 |
#- example of of_path: /pci@f4000000/ata-6@d/disk@0:3,/initrd-2.6.8.1-8mdk.img |
394 |
sub yaboot2file { |
395 |
my ($of_path) = @_; |
396 |
|
397 |
if ($of_path =~ /,/) { |
398 |
"$::prefix/boot/" . basename($of_path); |
399 |
} else { |
400 |
yaboot2dev($of_path); |
401 |
} |
402 |
} |
403 |
|
404 |
sub read_silo() { |
405 |
my $bootloader = read_lilo_like("/boot/silo.conf", sub { |
406 |
my ($f) = @_; |
407 |
"/boot$f"; |
408 |
}); |
409 |
$bootloader->{method} = 'silo'; |
410 |
$bootloader; |
411 |
} |
412 |
sub read_pmon2000() { |
413 |
my %b; |
414 |
$b{method} = 'pmon2000'; |
415 |
\%b; |
416 |
} |
417 |
sub read_uboot() { |
418 |
my %b; |
419 |
$b{method} = 'uboot'; |
420 |
\%b; |
421 |
} |
422 |
sub read_cromwell() { |
423 |
my %b; |
424 |
$b{method} = 'cromwell'; |
425 |
\%b; |
426 |
} |
427 |
sub read_yaboot() { |
428 |
my $bootloader = read_lilo_like("/etc/yaboot.conf", \&yaboot2file); |
429 |
$bootloader->{method} = 'yaboot'; |
430 |
$bootloader; |
431 |
} |
432 |
sub read_lilo() { |
433 |
my $bootloader = read_lilo_like("/etc/lilo.conf", sub { $_[0] }); |
434 |
|
435 |
delete $bootloader->{timeout} unless $bootloader->{prompt}; |
436 |
$bootloader->{timeout} = $bootloader->{timeout} / 10 if $bootloader->{timeout}; |
437 |
|
438 |
my $submethod = member($bootloader->{install}, 'text', 'menu') ? $bootloader->{install} : 'menu'; |
439 |
$bootloader->{method} = "lilo-$submethod"; |
440 |
|
441 |
$bootloader; |
442 |
} |
443 |
sub read_lilo_like { |
444 |
my ($file, $filter_file) = @_; |
445 |
|
446 |
my $global = 1; |
447 |
my ($e); |
448 |
my %b; |
449 |
-e "$::prefix$file" or return; |
450 |
foreach my $line (cat_("$::prefix$file")) { |
451 |
next if $line =~ /^\s*#/ || $line =~ /^\s*$/; |
452 |
my ($cmd, $v) = $line =~ /^\s*([^=\s]+)\s*(?:=\s*(.*?))?\s*$/ or log::l("unknown line in $file: $line"), next; |
453 |
|
454 |
if ($cmd =~ /^(?:image|other|macos|macosx|bsd|darwin)$/) { |
455 |
$v = $filter_file->($v); |
456 |
push @{$b{entries}}, $e = { type => $cmd, kernel_or_dev => $v }; |
457 |
$global = 0; |
458 |
} elsif ($global) { |
459 |
if ($cmd eq 'disk' && $v =~ /(\S+)\s+bios\s*=\s*(\S+)/) { |
460 |
$b{bios}{$1} = $2; |
461 |
} elsif ($cmd eq 'bios') { |
462 |
$b{bios}{$b{disk}} = $v; |
463 |
} elsif ($cmd eq 'init-message') { |
464 |
$v =~ s/\\n//g; |
465 |
$v =~ s/"//g; |
466 |
$b{'init-message'} = $v; |
467 |
} else { |
468 |
$b{$cmd} = $v eq '' ? 1 : $v; |
469 |
} |
470 |
} else { |
471 |
if (($cmd eq 'map-drive' .. $cmd eq 'to') && $cmd eq 'to') { |
472 |
$e->{mapdrive}{$e->{'map-drive'}} = $v; |
473 |
} else { |
474 |
if ($cmd eq 'initrd') { |
475 |
$v = $filter_file->($v); |
476 |
} |
477 |
$e->{$cmd} = $v || 1; |
478 |
} |
479 |
} |
480 |
} |
481 |
|
482 |
sub remove_quotes_and_spaces { |
483 |
local ($_) = @_; |
484 |
s/^\s*//; s/\s*$//; |
485 |
s/^"(.*?)"$/$1/; |
486 |
s/\\"/"/g; |
487 |
s/^\s*//; s/\s*$//; #- do it again for append=" foo" |
488 |
$_; |
489 |
} |
490 |
|
491 |
foreach ('append', 'root', 'default', 'raid-extra-boot') { |
492 |
$b{$_} = remove_quotes_and_spaces($b{$_}) if $b{$_}; |
493 |
} |
494 |
foreach my $entry (@{$b{entries}}) { |
495 |
foreach ('append', 'root', 'label') { |
496 |
$entry->{$_} = remove_quotes_and_spaces($entry->{$_}) if $entry->{$_}; |
497 |
} |
498 |
if ($entry->{kernel_or_dev} =~ /\bmbootpack\b/) { |
499 |
$entry->{initrd} = $entry->{kernel_or_dev}; |
500 |
$entry->{initrd} =~ s/\bmbootpack/initrd/; |
501 |
$entry->{kernel_or_dev} =~ s/\bmbootpack/vmlinuz/; |
502 |
$entry->{kernel_or_dev} =~ s/.img$//; |
503 |
#- assume only xen is configured with mbootpack |
504 |
$entry->{xen} = '/boot/xen.gz'; |
505 |
$entry->{root} = $1 if $entry->{append} =~ s/root=(\S*)\s*//; |
506 |
($entry->{xen_append}, $entry->{append}) = split '\s*--\s*', $entry->{append}, 2; |
507 |
} |
508 |
} |
509 |
|
510 |
# cleanup duplicate labels (in case file is corrupted) |
511 |
@{$b{entries}} = uniq_ { $_->{label} } @{$b{entries}}; |
512 |
|
513 |
\%b; |
514 |
} |
515 |
|
516 |
sub cleanup_entries { |
517 |
my ($bootloader) = @_; |
518 |
|
519 |
#- cleanup bad entries (in case file is corrupted) |
520 |
@{$bootloader->{entries}} = |
521 |
grep { |
522 |
my $pb = $_->{type} eq 'image' && !$_->{keep_verbatim} && ! -e "$::prefix$_->{kernel_or_dev}"; |
523 |
log::l("dropping bootloader entry $_->{label} since $_->{kernel_or_dev} doesn't exist") if $pb; |
524 |
!$pb; |
525 |
} @{$bootloader->{entries}}; |
526 |
} |
527 |
|
528 |
sub suggest_onmbr { |
529 |
my ($hd) = @_; |
530 |
|
531 |
my ($onmbr, $unsafe) = (1, 1); |
532 |
|
533 |
if (my $type = partition_table::raw::typeOfMBR($hd->{device})) { |
534 |
if (member($type, qw(dos dummy empty))) { |
535 |
$unsafe = 0; |
536 |
} elsif (!member($type, qw(lilo grub))) { |
537 |
$onmbr = 0; |
538 |
} |
539 |
log::l("bootloader::suggest_onmbr: type $type, onmbr $onmbr, unsafe $unsafe"); |
540 |
} |
541 |
($onmbr, $unsafe); |
542 |
} |
543 |
|
544 |
# list of places where we can install the bootloader |
545 |
sub allowed_boot_parts { |
546 |
my ($bootloader, $all_hds) = @_; |
547 |
( |
548 |
@{$all_hds->{hds}}, # MBR |
549 |
|
550 |
if_($bootloader->{method} =~ /lilo/, |
551 |
grep { $_->{level} eq '1' } @{$all_hds->{raids}} |
552 |
), |
553 |
(grep { !isFat_or_NTFS($_) } fs::get::fstab($all_hds)), # filesystems except those who do not leave space for our bootloaders |
554 |
detect_devices::floppies(), |
555 |
); |
556 |
} |
557 |
|
558 |
sub same_entries { |
559 |
my ($a, $b) = @_; |
560 |
|
561 |
foreach (uniq(keys %$a, keys %$b)) { |
562 |
if (member($_, 'label', 'append', 'mapdrive', 'readonly', 'makeactive', 'verbatim')) { |
563 |
next; |
564 |
} elsif ($_ eq 'grub_root' && (!$a->{$_} || !$b->{$_})) { |
565 |
#- grub_root is mostly internal stuff. if it misses, it's ok |
566 |
next; |
567 |
} else { |
568 |
next if $a->{$_} eq $b->{$_}; |
569 |
|
570 |
my ($inode_a, $inode_b) = map { (stat "$::prefix$_")[1] } ($a->{$_}, $b->{$_}); |
571 |
next if $inode_a && $inode_b && $inode_a == $inode_b; |
572 |
} |
573 |
|
574 |
log::l("entries $a->{label} do not have same $_: $a->{$_} ne $b->{$_}"); |
575 |
return; |
576 |
} |
577 |
1; |
578 |
} |
579 |
|
580 |
sub add_entry { |
581 |
my ($bootloader, $v) = @_; |
582 |
|
583 |
my $to_add = $v; |
584 |
my $label = $v->{label}; |
585 |
for (my $i = 0; $i < 10;) { |
586 |
my $conflicting = get_label($label, $bootloader); |
587 |
|
588 |
$to_add->{label} = $label; |
589 |
|
590 |
if ($conflicting) { |
591 |
#- replacing $conflicting with $to_add |
592 |
@{$bootloader->{entries}} = map { $_ == $conflicting ? $to_add : $_ } @{$bootloader->{entries}}; |
593 |
|
594 |
#- we will keep $conflicting, but not with same symlinks if used by the entry to add |
595 |
expand_entry_symlinks($bootloader, $conflicting); |
596 |
} else { |
597 |
#- we have found an unused label |
598 |
push @{$bootloader->{entries}}, $to_add; |
599 |
} |
600 |
|
601 |
if (!$conflicting || same_entries($conflicting, $to_add)) { |
602 |
log::l("current labels: " . join(" ", map { $_->{label} } @{$bootloader->{entries}})); |
603 |
return $v; |
604 |
} |
605 |
$to_add = $conflicting; |
606 |
|
607 |
if ($to_add->{label} eq 'linux') { |
608 |
$label = kernel_str2label(vmlinuz2kernel_str($to_add->{kernel_or_dev}), 'use_long_name'); |
609 |
} else { |
610 |
$label =~ s/^alt\d*_//; |
611 |
$label = 'alt' . ($i++ ? $i : '') . "_$label"; |
612 |
} |
613 |
} |
614 |
die 'add_entry'; |
615 |
} |
616 |
|
617 |
sub expand_entry_symlinks { |
618 |
my ($bootloader, $entry) = @_; |
619 |
|
620 |
foreach my $kind ('kernel_or_dev', 'initrd') { |
621 |
my $old_long_name = $bootloader->{old_long_names} && $bootloader->{old_long_names}{$entry->{$kind}} or next; |
622 |
|
623 |
#- replace all the {$kind} using this symlink to the real file |
624 |
log::l("replacing $entry->{$kind} with $old_long_name for bootloader label $entry->{label}"); |
625 |
$entry->{$kind} = $old_long_name; |
626 |
} |
627 |
} |
628 |
|
629 |
sub _do_the_symlink { |
630 |
my ($bootloader, $link, $long_name) = @_; |
631 |
|
632 |
my $existing_link = readlink("$::prefix$link"); |
633 |
if ($existing_link && $existing_link eq $long_name) { |
634 |
#- nothing to do :) |
635 |
return; |
636 |
} |
637 |
|
638 |
if ($existing_link) { |
639 |
#- the symlink is going to change! |
640 |
#- replace all the {$kind} using this symlink to the real file |
641 |
my $old_long_name = $existing_link =~ m!^/! ? $existing_link : "/boot/$existing_link"; |
642 |
if (-e "$::prefix$old_long_name") { |
643 |
$bootloader->{old_long_names}{$link} = $old_long_name; |
644 |
} else { |
645 |
log::l("ERROR: $link points to $old_long_name which does not exist"); |
646 |
} |
647 |
} elsif (-e "$::prefix$link") { |
648 |
log::l("ERROR: $link is not a symbolic link"); |
649 |
} |
650 |
|
651 |
#- changing the symlink |
652 |
symlinkf($long_name, "$::prefix$link") |
653 |
or cp_af("$::prefix/boot/$long_name", "$::prefix$link"); |
654 |
} |
655 |
|
656 |
# for lilo & xen |
657 |
sub get_mbootpack_filename { |
658 |
my ($entry) = @_; |
659 |
my $mbootpack_file = $entry->{initrd}; |
660 |
$mbootpack_file =~ s/\binitrd/mbootpack/; |
661 |
$entry->{xen} && $mbootpack_file; |
662 |
} |
663 |
|
664 |
# for lilo & xen |
665 |
sub build_mbootpack { |
666 |
my ($entry) = @_; |
667 |
|
668 |
my $mbootpack = '/usr/bin/mbootpack'; |
669 |
-f $::prefix . $entry->{kernel_or_dev} && -f $::prefix . $entry->{initrd} or return; |
670 |
|
671 |
my $mbootpack_file = get_mbootpack_filename($entry); |
672 |
-f ($::prefix . $mbootpack_file) and return 1; |
673 |
|
674 |
my $error; |
675 |
my $xen_kernel = '/tmp/xen_kernel'; |
676 |
my $xen_vmlinux = '/tmp/xen_vmlinux'; |
677 |
my $_b = before_leaving { unlink $::prefix . $_ foreach $xen_kernel, $xen_vmlinux }; |
678 |
run_program::rooted($::prefix, '/bin/gzip', '>', $xen_kernel, '2>', \$error, '-dc', $entry->{xen}) |
679 |
or die "unable to uncompress xen kernel"; |
680 |
run_program::rooted($::prefix, '/bin/gzip', '>', $xen_vmlinux, '2>', \$error, '-dc', $entry->{kernel_or_dev}) |
681 |
or die "unable to uncompress xen vmlinuz"; |
682 |
|
683 |
run_program::rooted($::prefix, $mbootpack, |
684 |
"2>", \$error, |
685 |
'-o', $mbootpack_file, |
686 |
'-m', $xen_vmlinux, |
687 |
'-m', $entry->{initrd}, |
688 |
$xen_kernel) |
689 |
or die "mbootpack failed: $error"; |
690 |
|
691 |
1; |
692 |
} |
693 |
|
694 |
sub add_kernel { |
695 |
my ($bootloader, $kernel_str, $v, $b_nolink, $b_no_initrd) = @_; |
696 |
|
697 |
#- eg: for /boot/vmlinuz-2.6.17-13mdvxen0 (pkg kernel-xen0-xxx) |
698 |
#- or /boot/vmlinuz-2.6.18-xen (pkg kernel-xen-uptodate) |
699 |
if ($kernel_str->{version} =~ /xen/ && -f '/boot/xen.gz') { |
700 |
$v->{xen} = '/boot/xen.gz'; |
701 |
} |
702 |
|
703 |
add2hash($v, |
704 |
{ |
705 |
type => 'image', |
706 |
label => kernel_str2label($kernel_str), |
707 |
}); |
708 |
|
709 |
#- normalize append and handle special options |
710 |
{ |
711 |
my ($simple, $dict) = unpack_append("$bootloader->{perImageAppend} $v->{append}"); |
712 |
if ($v->{label} eq 'failsafe') { |
713 |
#- perImageAppend contains resume=/dev/xxx which we don't want |
714 |
@$dict = grep { $_->[0] ne 'resume' } @$dict; |
715 |
} |
716 |
$v->{append} = pack_append($simple, $dict); |
717 |
} |
718 |
|
719 |
#- new versions of yaboot do not handle symlinks |
720 |
$b_nolink ||= arch() =~ /ppc/; |
721 |
$b_no_initrd //= (arch() =~ /mips|arm/) && !detect_devices::is_mips_gdium(); |
722 |
|
723 |
$b_nolink ||= $kernel_str->{use_long_name}; |
724 |
|
725 |
#- do not link /boot/vmlinuz to xen |
726 |
$b_nolink ||= $v->{xen}; |
727 |
|
728 |
my $vmlinuz_long = kernel_str2vmlinuz_long($kernel_str); |
729 |
my $initrd_long = kernel_str2initrd_long($kernel_str); |
730 |
$v->{kernel_or_dev} = "/boot/$vmlinuz_long"; |
731 |
-e "$::prefix$v->{kernel_or_dev}" or log::l("unable to find kernel image $::prefix$v->{kernel_or_dev}"), return; |
732 |
log::l("adding $v->{kernel_or_dev}"); |
733 |
|
734 |
if (!$b_no_initrd) { |
735 |
$v->{initrd} = mkinitrd($kernel_str->{version}, $bootloader, $v, "/boot/$initrd_long"); |
736 |
} |
737 |
|
738 |
if (!$b_nolink) { |
739 |
$v->{kernel_or_dev} = '/boot/' . kernel_str2vmlinuz_short($kernel_str); |
740 |
if (arch() =~ /mips/) { |
741 |
log::l("link $::prefix/boot/$vmlinuz_long -> $::prefix$v->{kernel_or_dev}"); |
742 |
linkf("$::prefix/boot/$vmlinuz_long", $::prefix . $v->{kernel_or_dev}); |
743 |
linkf("$::prefix/boot/$vmlinuz_long", $::prefix . $v->{kernel_or_dev} . ".32"); |
744 |
} else { |
745 |
_do_the_symlink($bootloader, $v->{kernel_or_dev}, $vmlinuz_long); |
746 |
} |
747 |
|
748 |
if ($v->{initrd}) { |
749 |
$v->{initrd} = '/boot/' . kernel_str2initrd_short($kernel_str); |
750 |
if (arch() =~ /mips/) { |
751 |
log::l("link $::prefix/boot/$initrd_long -> $::prefix$v->{initrd}"); |
752 |
linkf("$::prefix/boot/$initrd_long", $::prefix . $v->{initrd}); |
753 |
if ($v->{initrd} =~ s/.img$/.gz/) { |
754 |
linkf("$::prefix/boot/$initrd_long", $::prefix . $v->{initrd}); |
755 |
} |
756 |
} else { |
757 |
_do_the_symlink($bootloader, $v->{initrd}, $initrd_long); |
758 |
} |
759 |
} |
760 |
} |
761 |
|
762 |
add_entry($bootloader, $v); |
763 |
} |
764 |
|
765 |
sub rebuild_initrds { |
766 |
my ($bootloader) = @_; |
767 |
|
768 |
my %done; |
769 |
foreach my $v (grep { $_->{initrd} } @{$bootloader->{entries}}) { |
770 |
my $kernel_str = vmlinuz2kernel_str($v->{kernel_or_dev}) or next; |
771 |
my $initrd_long = '/boot/' . kernel_str2initrd_long($kernel_str); |
772 |
next if $done{$initrd_long}++; |
773 |
|
774 |
rebuild_initrd($kernel_str->{version}, $bootloader, $v, $initrd_long); |
775 |
} |
776 |
} |
777 |
|
778 |
# unused (?) |
779 |
sub duplicate_kernel_entry { |
780 |
my ($bootloader, $new_label) = @_; |
781 |
|
782 |
get_label($new_label, $bootloader) and return; |
783 |
|
784 |
my $entry = { %{ get_label('linux', $bootloader) }, label => $new_label }; |
785 |
add_entry($bootloader, $entry); |
786 |
} |
787 |
|
788 |
my $uniq_dict_appends = join('|', qw(acpi pci resume PROFILE XFree)); |
789 |
|
790 |
sub unpack_append { |
791 |
my ($s) = @_; |
792 |
my @l = "$s " =~ /((?:[^"\s]+|".*?")*)\s+/g; |
793 |
[ grep { !/=/ } @l ], [ map { if_(/(.*?)=(.*)/, [$1, $2]) } @l ]; |
794 |
} |
795 |
sub pack_append { |
796 |
my ($simple, $dict) = @_; |
797 |
|
798 |
#- normalize |
799 |
$simple = [ reverse(uniq(reverse @$simple)) ]; |
800 |
$dict = [ reverse(uniq_ { |
801 |
my ($k, $v) = @$_; |
802 |
$k =~ /^($uniq_dict_appends)$/ ? $k : "$k=$v"; |
803 |
} reverse @$dict) ]; |
804 |
|
805 |
join(' ', @$simple, map { "$_->[0]=$_->[1]" } @$dict); |
806 |
} |
807 |
|
808 |
sub modify_append { |
809 |
my ($b, $f) = @_; |
810 |
|
811 |
my @l = grep { $_->{type} eq 'image' && !$_->{keep_verbatim} && !($::isStandalone && $_->{label} eq 'failsafe') } @{$b->{entries}}; |
812 |
|
813 |
foreach (\$b->{perImageAppend}, map { \$_->{append} } @l) { |
814 |
my ($simple, $dict) = unpack_append($$_); |
815 |
$f->($simple, $dict); |
816 |
$$_ = pack_append($simple, $dict); |
817 |
log::l("modify_append: $$_"); |
818 |
} |
819 |
} |
820 |
|
821 |
sub get_append_simple { |
822 |
my ($b, $key) = @_; |
823 |
my ($simple, $_dict) = unpack_append($b->{perImageAppend}); |
824 |
member($key, @$simple); |
825 |
} |
826 |
sub get_append_with_key { |
827 |
my ($b, $key) = @_; |
828 |
my ($_simple, $dict) = unpack_append($b->{perImageAppend}); |
829 |
my @l = map { $_->[1] } grep { $_->[0] eq $key } @$dict; |
830 |
|
831 |
log::l("more than one $key in $b->{perImageAppend}") if @l > 1; |
832 |
$l[0]; |
833 |
} |
834 |
sub remove_append_simple { |
835 |
my ($b, $key) = @_; |
836 |
modify_append($b, sub { |
837 |
my ($simple, $_dict) = @_; |
838 |
@$simple = grep { $_ ne $key } @$simple; |
839 |
}); |
840 |
} |
841 |
sub set_append_with_key { |
842 |
my ($b, $key, $val) = @_; |
843 |
|
844 |
modify_append($b, sub { |
845 |
my ($_simple, $dict) = @_; |
846 |
|
847 |
if ($val eq '') { |
848 |
@$dict = grep { $_->[0] ne $key } @$dict; |
849 |
} else { |
850 |
push @$dict, [ $key, $val ]; |
851 |
} |
852 |
}); |
853 |
} |
854 |
sub set_append_simple { |
855 |
my ($b, $key) = @_; |
856 |
|
857 |
modify_append($b, sub { |
858 |
my ($simple, $_dict) = @_; |
859 |
@$simple = uniq(@$simple, $key); |
860 |
}); |
861 |
} |
862 |
sub may_append_with_key { |
863 |
my ($b, $key, $val) = @_; |
864 |
set_append_with_key($b, $key, $val) if !get_append_with_key($b, $key); |
865 |
} |
866 |
|
867 |
sub get_append_netprofile { |
868 |
my ($e) = @_; |
869 |
my ($simple, $dict) = unpack_append($e->{append}); |
870 |
my ($p, $dict_) = partition { $_->[0] eq 'PROFILE' } @$dict; |
871 |
pack_append($simple, $dict_), $p->[0][1]; |
872 |
} |
873 |
sub set_append_netprofile { |
874 |
my ($e, $append, $profile) = @_; |
875 |
my ($simple, $dict) = unpack_append($append); |
876 |
push @$dict, [ 'PROFILE', $profile ] if $profile; |
877 |
$e->{append} = pack_append($simple, $dict); |
878 |
} |
879 |
|
880 |
# used when a bootloader $entry has been modified (eg: $entry->{vga}) |
881 |
sub configure_entry { |
882 |
my ($bootloader, $entry) = @_; |
883 |
$entry->{type} eq 'image' or return; |
884 |
|
885 |
if (my $kernel_str = vmlinuz2kernel_str($entry->{kernel_or_dev})) { |
886 |
$entry->{initrd} = |
887 |
mkinitrd($kernel_str->{version}, $bootloader, $entry, |
888 |
$entry->{initrd} || '/boot/' . kernel_str2initrd_short($kernel_str)); |
889 |
} |
890 |
} |
891 |
|
892 |
sub get_kernels_and_labels_before_kernel_remove { |
893 |
my ($to_remove_kernel) = @_; |
894 |
my @kernels = grep { $_ ne $to_remove_kernel } installed_vmlinuz(); |
895 |
map { kernel_str2label($_) => $_ } get_kernel_labels(\@kernels); |
896 |
} |
897 |
|
898 |
sub get_kernels_and_labels { |
899 |
get_kernel_labels([ installed_vmlinuz() ]); |
900 |
} |
901 |
|
902 |
sub get_kernel_labels { |
903 |
my ($kernels) = @_; |
904 |
|
905 |
my @kernels_str = |
906 |
sort { common::cmp_kernel_versions($b->{version_no_ext}, $a->{version_no_ext}) } |
907 |
grep { -d "$::prefix/lib/modules/$_->{version}" } |
908 |
map { vmlinuz2kernel_str($_) } @$kernels; |
909 |
|
910 |
my %labels; |
911 |
foreach (@kernels_str) { |
912 |
if ($labels{$_->{ext}}) { |
913 |
$_->{use_long_name} = 1; |
914 |
} else { |
915 |
$labels{$_->{ext}} = 1; |
916 |
} |
917 |
} |
918 |
|
919 |
$kernels_str[0]{ext} = ''; |
920 |
|
921 |
@kernels_str; |
922 |
} |
923 |
|
924 |
sub short_ext { |
925 |
my ($kernel_str) = @_; |
926 |
|
927 |
my $short_ext = { |
928 |
'i586-up-1GB' => 'i586', |
929 |
'i686-up-4GB' => '4GB', |
930 |
'xen0' => 'xen', |
931 |
}->{$kernel_str->{ext}}; |
932 |
|
933 |
$short_ext || $kernel_str->{ext}; |
934 |
} |
935 |
|
936 |
# deprecated, only for compatibility (nov 2007) |
937 |
sub sanitize_ver { |
938 |
my ($_name, $kernel_str) = @_; |
939 |
_sanitize_ver($kernel_str); |
940 |
} |
941 |
|
942 |
sub _sanitize_ver { |
943 |
my ($kernel_str) = @_; |
944 |
|
945 |
my $name = $kernel_str->{basename}; |
946 |
$name = '' if $name eq 'vmlinuz'; |
947 |
|
948 |
my $v = $kernel_str->{version_no_ext}; |
949 |
if ($v =~ s/-\d+\.mm\././) { |
950 |
$name = join(' ', grep { $_ } $name, 'multimedia'); |
951 |
} |
952 |
|
953 |
$v =~ s!(md[kv]|mnb)$!!; |
954 |
$v =~ s!-0\.(pre|rc)(\d+)\.!$1$2-!; |
955 |
|
956 |
my $return = join(' ', grep { $_ } $name, short_ext($kernel_str), $v); |
957 |
|
958 |
length($return) < 30 or $return =~ s!secure!sec!; |
959 |
length($return) < 30 or $return =~ s!enterprise!ent!; |
960 |
length($return) < 30 or $return =~ s!multimedia!mm!; |
961 |
|
962 |
$return; |
963 |
} |
964 |
|
965 |
# for lilo |
966 |
sub suggest_message_text { |
967 |
my ($bootloader) = @_; |
968 |
|
969 |
if (!$bootloader->{message} && !$bootloader->{message_text} && arch() !~ /ia64/) { |
970 |
my $msg_en = |
971 |
#-PO: these messages will be displayed at boot time in the BIOS, use only ASCII (7bit) |
972 |
N_("Welcome to the operating system chooser! |
973 |
|
974 |
Choose an operating system from the list above or |
975 |
wait for default boot. |
976 |
|
977 |
"); |
978 |
my $msg = translate($msg_en); |
979 |
#- use the english version if more than 40% of 8bits chars |
980 |
#- else, use the translation but force a conversion to ascii |
981 |
#- to be sure there won't be undisplayable characters |
982 |
if (int(grep { $_ & 0x80 } unpack "c*", $msg) / length($msg) > 0.4) { |
983 |
$msg = $msg_en; |
984 |
} else { |
985 |
$msg = Locale::gettext::iconv($msg, "utf-8", "ascii//TRANSLIT"); |
986 |
} |
987 |
$bootloader->{message_text} = $msg; |
988 |
} |
989 |
} |
990 |
|
991 |
sub suggest { |
992 |
my ($bootloader, $all_hds, %options) = @_; |
993 |
my $fstab = [ fs::get::fstab($all_hds) ]; |
994 |
my $root_part = fs::get::root($fstab); |
995 |
my $root = isLoopback($root_part) ? '/dev/loop7' : fs::wild_device::from_part('', $root_part); |
996 |
my $boot = fs::get::root($fstab, 'boot')->{device}; |
997 |
#- PPC xfs module requires enlarged initrd |
998 |
my $xfsroot = $root_part->{fs_type} eq 'xfs'; |
999 |
my $mbr; |
1000 |
|
1001 |
# If installing onto an USB drive, put the mbr there, else on the first non removable drive |
1002 |
if ($root_part->{is_removable}) { |
1003 |
$mbr = fs::get::part2hd($root_part, $all_hds); |
1004 |
} else { |
1005 |
$mbr = find { !$_->{is_removable} } @{$all_hds->{hds}}; |
1006 |
} |
1007 |
|
1008 |
my ($onmbr, $unsafe) = $bootloader->{crushMbr} ? (1, 0) : suggest_onmbr($mbr); |
1009 |
add2hash_($bootloader, arch() =~ /ppc/ ? |
1010 |
{ |
1011 |
defaultos => "linux", |
1012 |
entries => [], |
1013 |
'init-message' => "Welcome to %s!", |
1014 |
delay => 30, #- OpenFirmware delay |
1015 |
timeout => 50, |
1016 |
enableofboot => 1, |
1017 |
enablecdboot => 1, |
1018 |
if_(detect_devices::get_mac_model() =~ /IBM/, |
1019 |
boot => "/dev/sda1", |
1020 |
), |
1021 |
xfsroot => $xfsroot, |
1022 |
} : |
1023 |
{ |
1024 |
bootUnsafe => $unsafe, |
1025 |
entries => [], |
1026 |
timeout => $onmbr && 10, |
1027 |
nowarn => 1, |
1028 |
if_(arch() !~ /ia64/, |
1029 |
boot => "/dev/" . ($onmbr ? $mbr->{device} : $boot), |
1030 |
map => "/boot/map", |
1031 |
compact => 1, |
1032 |
'large-memory' => 1, |
1033 |
color => 'black/cyan yellow/cyan', |
1034 |
'menu-scheme' => 'wb:bw:wb:bw' |
1035 |
), |
1036 |
}); |
1037 |
|
1038 |
suggest_message_text($bootloader); |
1039 |
|
1040 |
add2hash_($bootloader, { memsize => $1 }) if cat_("/proc/cmdline") =~ /\bmem=(\d+[KkMm]?)(?:\s.*)?$/; |
1041 |
if (my ($s, $port, $speed) = cat_("/proc/cmdline") =~ /console=(ttyS(\d),(\d+)\S*)/) { |
1042 |
log::l("serial console $s $port $speed"); |
1043 |
set_append_with_key($bootloader, console => $s); |
1044 |
any::set_login_serial_console($port, $speed); |
1045 |
} |
1046 |
|
1047 |
my @kernels = get_kernels_and_labels() or die "no kernel installed"; |
1048 |
|
1049 |
my %old_kernels = map { vmlinuz2version($_->{kernel_or_dev}) => 1 } @{$bootloader->{entries}}; |
1050 |
@kernels = grep { !$old_kernels{$_->{version}} } @kernels; |
1051 |
|
1052 |
#- remove existing failsafe and linux-nonfb, do not care if the previous one was modified by the user? |
1053 |
@{$bootloader->{entries}} = grep { !member($_->{label}, qw(failsafe linux-nonfb)) } @{$bootloader->{entries}}; |
1054 |
|
1055 |
foreach my $kernel (@kernels) { |
1056 |
my $e = add_kernel($bootloader, $kernel, |
1057 |
{ |
1058 |
root => $root, |
1059 |
if_($options{vga_fb}, vga => $options{vga_fb}), #- using framebuffer |
1060 |
if_($options{vga_fb} && $options{splash}, append => "splash"), |
1061 |
if_($options{quiet}, append => "quiet"), |
1062 |
}); |
1063 |
|
1064 |
if ($options{vga_fb} && $e->{label} eq 'linux') { |
1065 |
add_kernel($bootloader, $kernel, { root => $root, label => 'linux-nonfb' }); |
1066 |
} |
1067 |
} |
1068 |
|
1069 |
add_kernel($bootloader, $kernels[0], |
1070 |
{ root => $root, label => 'failsafe', append => 'failsafe' }) |
1071 |
if @kernels; |
1072 |
|
1073 |
if (arch() =~ /ppc/) { |
1074 |
#- if we identified a MacOS partition earlier - add it |
1075 |
if (defined $partition_table::mac::macos_part) { |
1076 |
add_entry($bootloader, |
1077 |
{ |
1078 |
type => "macos", |
1079 |
kernel_or_dev => $partition_table::mac::macos_part |
1080 |
}); |
1081 |
} |
1082 |
} elsif (arch() !~ /ia64/) { |
1083 |
#- search for dos (or windows) boot partition. Do not look in extended partitions! |
1084 |
my @windows_boot_parts = |
1085 |
grep { $_->{active} |
1086 |
&& isFat_or_NTFS($_) && member(fs::type::fs_type_from_magic($_), 'vfat', 'ntfs', 'ntfs-3g') |
1087 |
&& !$_->{is_removable} |
1088 |
&& !isRecovery($_); |
1089 |
} |
1090 |
map { @{$_->{primary}{normal}} } @{$all_hds->{hds}}; |
1091 |
each_index { |
1092 |
add_entry($bootloader, |
1093 |
{ |
1094 |
type => 'other', |
1095 |
kernel_or_dev => "/dev/$_->{device}", |
1096 |
label => 'windows' . ($::i || ''), |
1097 |
table => "/dev/$_->{rootDevice}", |
1098 |
makeactive => 1, |
1099 |
}); |
1100 |
} @windows_boot_parts; |
1101 |
} |
1102 |
|
1103 |
my @preferred = map { "linux-$_" } 'p3-smp-64GB', 'secure', 'enterprise', 'smp', 'i686-up-4GB'; |
1104 |
if (my $preferred = find { get_label($_, $bootloader) } @preferred) { |
1105 |
$bootloader->{default} ||= $preferred; |
1106 |
} |
1107 |
$bootloader->{default} ||= "linux"; |
1108 |
$bootloader->{method} ||= first(method_choices($all_hds, 1), # best installed |
1109 |
method_choices($all_hds, 0)); # or best if no valid one is installed |
1110 |
|
1111 |
if (main_method($bootloader->{method}) eq 'grub') { |
1112 |
foreach my $c (find_other_distros_grub_conf($fstab)) { |
1113 |
add_entry($bootloader, { |
1114 |
type => 'grub_configfile', |
1115 |
label => $c->{name}, |
1116 |
kernel_or_dev => "/dev/$c->{bootpart}{device}", |
1117 |
configfile => $c->{grub_conf}, |
1118 |
}); |
1119 |
} |
1120 |
} |
1121 |
} |
1122 |
|
1123 |
sub detect_main_method { |
1124 |
my ($all_hds) = @_; |
1125 |
my $bootloader = &read($all_hds); |
1126 |
$bootloader && main_method($bootloader->{method}); |
1127 |
} |
1128 |
|
1129 |
sub main_method { |
1130 |
my ($method) = @_; |
1131 |
$method =~ /(\w+)/ && $1; |
1132 |
} |
1133 |
|
1134 |
sub config_files() { |
1135 |
my %files = ( |
1136 |
lilo => '/etc/lilo.conf', |
1137 |
grub => '/boot/grub/menu.lst', |
1138 |
grub_install => '/boot/grub/install.sh', |
1139 |
); |
1140 |
|
1141 |
map_each { |
1142 |
my $content = cat_("$::prefix/$::b"); |
1143 |
{ main_method => main_method($::a), name => $::a, file => $::b, content => $content }; |
1144 |
} %files; |
1145 |
} |
1146 |
|
1147 |
sub method2text { |
1148 |
my ($method) = @_; |
1149 |
+{ |
1150 |
'lilo-menu' => N("LILO with text menu"), |
1151 |
'grub-graphic' => N("GRUB with graphical menu"), |
1152 |
'grub-menu' => N("GRUB with text menu"), |
1153 |
'yaboot' => N("Yaboot"), |
1154 |
'silo' => N("SILO"), |
1155 |
}->{$method}; |
1156 |
} |
1157 |
|
1158 |
sub method_choices_raw { |
1159 |
my ($b_prefix_mounted) = @_; |
1160 |
detect_devices::is_xbox() ? 'cromwell' : |
1161 |
arch() =~ /ppc/ ? 'yaboot' : |
1162 |
arch() =~ /ia64/ ? 'lilo' : |
1163 |
arch() =~ /sparc/ ? 'silo' : |
1164 |
arch() =~ /mips/ ? 'pmon2000' : |
1165 |
arch() =~ /arm/ ? 'uboot' : |
1166 |
( |
1167 |
if_(!$b_prefix_mounted || whereis_binary('grub', $::prefix), |
1168 |
'grub-graphic', 'grub-menu'), |
1169 |
if_(!$b_prefix_mounted || whereis_binary('lilo', $::prefix), |
1170 |
'lilo-menu'), |
1171 |
); |
1172 |
} |
1173 |
sub method_choices { |
1174 |
my ($all_hds, $b_prefix_mounted) = @_; |
1175 |
my $fstab = [ fs::get::fstab($all_hds) ]; |
1176 |
my $root_part = fs::get::root($fstab); |
1177 |
my $boot_part = fs::get::root($fstab, 'boot'); |
1178 |
my $have_dmraid = find { fs::type::is_dmraid($_) } @{$all_hds->{hds}}; |
1179 |
|
1180 |
grep { |
1181 |
!(/lilo/ && (isLoopback($root_part) || $have_dmraid)) |
1182 |
&& !(/grub/ && isRAID($boot_part)) |
1183 |
&& !(/grub-graphic/ && cat_("/proc/cmdline") =~ /console=ttyS/); |
1184 |
} method_choices_raw($b_prefix_mounted); |
1185 |
} |
1186 |
sub main_method_choices { |
1187 |
my ($b_prefix_mounted) = @_; |
1188 |
uniq(map { main_method($_) } method_choices_raw($b_prefix_mounted)); |
1189 |
} |
1190 |
sub configured_main_methods() { |
1191 |
my @bad_main_methods = map { if_(!$_->{content}, $_->{main_method}) } config_files(); |
1192 |
difference2([ main_method_choices(1) ], \@bad_main_methods); |
1193 |
} |
1194 |
|
1195 |
# for lilo |
1196 |
sub keytable { |
1197 |
my ($f) = @_; |
1198 |
$f or return; |
1199 |
|
1200 |
if ($f !~ /\.klt$/) { |
1201 |
my $file = "/boot/$f.klt"; |
1202 |
run_program::rooted($::prefix, "keytab-lilo.pl", ">", $file, $f) or return; |
1203 |
$f = $file; |
1204 |
} |
1205 |
-r "$::prefix/$f" && $f; |
1206 |
} |
1207 |
|
1208 |
|
1209 |
sub create_link_source() { |
1210 |
#- we simply do it for all kernels :) |
1211 |
#- so this can be used in %post of kernel and also of kernel-source |
1212 |
foreach (all("$::prefix/usr/src")) { |
1213 |
my ($version) = /^linux-(\d+\.\d+.*)/ or next; |
1214 |
foreach (glob("$::prefix/lib/modules/$version*")) { |
1215 |
-d $_ or next; |
1216 |
log::l("creating symlink $_/build"); |
1217 |
symlink "/usr/src/linux-$version", "$_/build"; |
1218 |
log::l("creating symlink $_/source"); |
1219 |
symlink "/usr/src/linux-$version", "$_/source"; |
1220 |
} |
1221 |
} |
1222 |
} |
1223 |
|
1224 |
sub dev2yaboot { |
1225 |
my ($dev) = @_; |
1226 |
|
1227 |
devices::make("$::prefix$dev"); #- create it in the chroot |
1228 |
|
1229 |
my $of_dev; |
1230 |
run_program::rooted_or_die($::prefix, "/usr/sbin/ofpath", ">", \$of_dev, $dev); |
1231 |
chomp($of_dev); |
1232 |
log::l("OF Device: $of_dev"); |
1233 |
$of_dev; |
1234 |
} |
1235 |
|
1236 |
sub check_enough_space() { |
1237 |
my $e = "$::prefix/boot/.enough_space"; |
1238 |
output $e, 1; -s $e or die N("not enough room in /boot"); |
1239 |
unlink $e; |
1240 |
} |
1241 |
|
1242 |
sub write_yaboot { |
1243 |
my ($bootloader, $all_hds) = @_; |
1244 |
|
1245 |
my $fstab = [ fs::get::fstab($all_hds) ]; |
1246 |
|
1247 |
my $file2yaboot = sub { |
1248 |
my ($part, $file) = fs::get::file2part($fstab, $_[0]); |
1249 |
dev2yaboot('/dev/' . $part->{device}) . "," . $file; |
1250 |
}; |
1251 |
|
1252 |
#- do not write yaboot.conf for old-world macs |
1253 |
my $mac_type = detect_devices::get_mac_model(); |
1254 |
return if $mac_type =~ /Power Macintosh/; |
1255 |
|
1256 |
$bootloader->{prompt} ||= $bootloader->{timeout}; |
1257 |
|
1258 |
if ($bootloader->{message_text}) { |
1259 |
eval { output("$::prefix/boot/message", $bootloader->{message_text}) } |
1260 |
and $bootloader->{message} = '/boot/message'; |
1261 |
} |
1262 |
|
1263 |
my @conf; |
1264 |
|
1265 |
if (!get_label($bootloader->{default}, $bootloader)) { |
1266 |
log::l("default bootloader entry $bootloader->{default} is invalid, choosing another one"); |
1267 |
$bootloader->{default} = $bootloader->{entries}[0]{label}; |
1268 |
} |
1269 |
push @conf, "# yaboot.conf - generated by DrakX/drakboot"; |
1270 |
push @conf, "# WARNING: do not forget to run ybin after modifying this file\n"; |
1271 |
push @conf, "default=" . make_label_lilo_compatible($bootloader->{default}) if $bootloader->{default}; |
1272 |
push @conf, sprintf('init-message="\n%s\n"', $bootloader->{'init-message'}) if $bootloader->{'init-message'}; |
1273 |
|
1274 |
if ($bootloader->{boot}) { |
1275 |
push @conf, "boot=$bootloader->{boot}"; |
1276 |
push @conf, "ofboot=" . dev2yaboot($bootloader->{boot}) if $mac_type !~ /IBM/; |
1277 |
} else { |
1278 |
die "no bootstrap partition defined."; |
1279 |
} |
1280 |
|
1281 |
push @conf, map { "$_=$bootloader->{$_}" } grep { $bootloader->{$_} } (qw(delay timeout), if_($mac_type !~ /IBM/, 'defaultos')); |
1282 |
push @conf, "install=/usr/lib/yaboot/yaboot"; |
1283 |
if ($mac_type =~ /IBM/) { |
1284 |
push @conf, 'nonvram'; |
1285 |
} else { |
1286 |
push @conf, 'magicboot=/usr/lib/yaboot/ofboot'; |
1287 |
push @conf, grep { $bootloader->{$_} } qw(enablecdboot enableofboot); |
1288 |
} |
1289 |
foreach my $entry (@{$bootloader->{entries}}) { |
1290 |
|
1291 |
if ($entry->{type} eq "image") { |
1292 |
push @conf, "$entry->{type}=" . $file2yaboot->($entry->{kernel_or_dev}); |
1293 |
my @entry_conf; |
1294 |
push @entry_conf, "label=" . make_label_lilo_compatible($entry->{label}); |
1295 |
push @entry_conf, "root=$entry->{root}"; |
1296 |
push @entry_conf, "initrd=" . $file2yaboot->($entry->{initrd}) if $entry->{initrd}; |
1297 |
#- xfs module on PPC requires larger initrd - say 6MB? |
1298 |
push @entry_conf, "initrd-size=6144" if $bootloader->{xfsroot}; |
1299 |
push @entry_conf, qq(append=" $entry->{append}") if $entry->{append}; |
1300 |
push @entry_conf, grep { $entry->{$_} } qw(read-write read-only); |
1301 |
push @conf, map { "\t$_" } @entry_conf; |
1302 |
} else { |
1303 |
my $of_dev = dev2yaboot($entry->{kernel_or_dev}); |
1304 |
push @conf, "$entry->{type}=$of_dev"; |
1305 |
} |
1306 |
} |
1307 |
my $f = "$::prefix/etc/yaboot.conf"; |
1308 |
log::l("writing yaboot config to $f"); |
1309 |
renamef($f, "$f.old"); |
1310 |
output($f, map { "$_\n" } @conf); |
1311 |
} |
1312 |
|
1313 |
sub install_yaboot { |
1314 |
my ($bootloader, $all_hds) = @_; |
1315 |
log::l("Installing boot loader..."); |
1316 |
write_yaboot($bootloader, $all_hds); |
1317 |
when_config_changed_yaboot($bootloader); |
1318 |
} |
1319 |
sub when_config_changed_yaboot { |
1320 |
my ($bootloader) = @_; |
1321 |
$::testing and return; |
1322 |
if (defined $partition_table::mac::new_bootstrap) { |
1323 |
run_program::run("hformat", $bootloader->{boot}) or die "hformat failed"; |
1324 |
} |
1325 |
my $error; |
1326 |
run_program::rooted($::prefix, "/usr/sbin/ybin", "2>", \$error) or die "ybin failed: $error"; |
1327 |
} |
1328 |
|
1329 |
sub install_pmon2000 { |
1330 |
my ($_bootloader, $_all_hds) = @_; |
1331 |
log::l("Mips/pmon2000 - nothing to install..."); |
1332 |
} |
1333 |
sub write_pmon2000 { |
1334 |
my ($_bootloader, $_all_hds) = @_; |
1335 |
log::l("Mips/pmon2000 - nothing to write..."); |
1336 |
} |
1337 |
sub when_config_changed_pmon2000 { |
1338 |
my ($_bootloader) = @_; |
1339 |
log::l("Mips/pmon2000 - nothing to do..."); |
1340 |
} |
1341 |
|
1342 |
sub install_uboot { |
1343 |
my ($_bootloader, $_all_hds) = @_; |
1344 |
log::l("uboot - nothing to install..."); |
1345 |
} |
1346 |
sub write_uboot { |
1347 |
my ($_bootloader, $_all_hds) = @_; |
1348 |
log::l("uboot - nothing to write..."); |
1349 |
} |
1350 |
sub when_config_changed_uboot { |
1351 |
my ($_bootloader) = @_; |
1352 |
log::l("uboot - nothing to do..."); |
1353 |
} |
1354 |
|
1355 |
sub install_cromwell { |
1356 |
my ($_bootloader, $_all_hds) = @_; |
1357 |
log::l("XBox/Cromwell - nothing to install..."); |
1358 |
} |
1359 |
sub write_cromwell { |
1360 |
my ($_bootloader, $_all_hds) = @_; |
1361 |
log::l("XBox/Cromwell - nothing to write..."); |
1362 |
} |
1363 |
sub when_config_changed_cromwell { |
1364 |
my ($_bootloader) = @_; |
1365 |
log::l("XBox/Cromwell - nothing to do..."); |
1366 |
} |
1367 |
|
1368 |
sub simplify_label { |
1369 |
my ($label) = @_; |
1370 |
|
1371 |
length($label) < 31 or $label =~ s/\.//g; |
1372 |
|
1373 |
$label = substr($label, 0, 31); #- lilo does not handle more than 31 char long labels |
1374 |
$label =~ s/ /_/g; #- lilo does not support blank character in image names, labels or aliases |
1375 |
$label; |
1376 |
} |
1377 |
|
1378 |
sub make_label_lilo_compatible { |
1379 |
my ($label) = @_; |
1380 |
'"' . simplify_label($label) . '"'; |
1381 |
} |
1382 |
|
1383 |
sub write_lilo { |
1384 |
my ($bootloader, $all_hds, $o_backup_extension) = @_; |
1385 |
$bootloader->{prompt} ||= $bootloader->{timeout}; |
1386 |
|
1387 |
my $file2fullname = sub { |
1388 |
my ($file) = @_; |
1389 |
if (arch() =~ /ia64/) { |
1390 |
my $fstab = [ fs::get::fstab($all_hds) ]; |
1391 |
(my $part, $file) = fs::get::file2part($fstab, $file); |
1392 |
my %hds = map_index { $_ => "hd$::i" } map { $_->{device} } |
1393 |
sort { |
1394 |
my ($a_is_fat, $b_is_fat) = ($a->{fs_type} eq 'vfat', $b->{fs_type} eq 'vfat'); |
1395 |
$a_is_fat <=> $b_is_fat || $a->{device} cmp $b->{device}; |
1396 |
} @$fstab; |
1397 |
$hds{$part->{device}} . ":" . $file; |
1398 |
} else { |
1399 |
$file; |
1400 |
} |
1401 |
}; |
1402 |
|
1403 |
my $quotes = sub { |
1404 |
my ($s) = @_; |
1405 |
$s =~ s/"/\\"/g; |
1406 |
qq("$s"); |
1407 |
}; |
1408 |
|
1409 |
my $quotes_if_needed = sub { |
1410 |
my ($s) = @_; |
1411 |
$s =~ /["=\s]/ ? $quotes->($s) : $s; |
1412 |
}; |
1413 |
|
1414 |
|
1415 |
my @sorted_hds = sort_hds_according_to_bios($bootloader, $all_hds); |
1416 |
|
1417 |
if (is_empty_hash_ref($bootloader->{bios} ||= {}) && $all_hds->{hds}[0] != $sorted_hds[0]) { |
1418 |
log::l("Since we're booting on $sorted_hds[0]{device}, make it bios=0x80"); |
1419 |
$bootloader->{bios} = { "/dev/$sorted_hds[0]{device}" => '0x80' }; |
1420 |
} |
1421 |
|
1422 |
my @conf; |
1423 |
|
1424 |
#- normalize: RESTRICTED and MANDATORY are only valid if PASSWORD is set |
1425 |
if ($bootloader->{password}) { |
1426 |
# lilo defaults to mandatory, use restricted by default to have |
1427 |
# the same behaviour as with grub |
1428 |
$bootloader->{restricted} = 1; |
1429 |
} else { |
1430 |
delete $bootloader->{mandatory} if !$bootloader->{password}; |
1431 |
delete $bootloader->{restricted} if !$bootloader->{password}; |
1432 |
} |
1433 |
foreach my $entry (@{$bootloader->{entries}}) { |
1434 |
delete $entry->{mandatory} if !$entry->{password} && !$bootloader->{password}; |
1435 |
delete $entry->{restricted} if !$entry->{password} && !$bootloader->{password}; |
1436 |
} |
1437 |
if (get_append_with_key($bootloader, 'console') =~ /ttyS(.*)/) { |
1438 |
$bootloader->{serial} ||= $1; |
1439 |
} |
1440 |
|
1441 |
if (!get_label($bootloader->{default}, $bootloader)) { |
1442 |
log::l("default bootloader entry $bootloader->{default} is invalid, choosing another one"); |
1443 |
$bootloader->{default} = $bootloader->{entries}[0]{label}; |
1444 |
} |
1445 |
push @conf, "# File generated by DrakX/drakboot"; |
1446 |
push @conf, "# WARNING: do not forget to run lilo after modifying this file\n"; |
1447 |
push @conf, "default=" . make_label_lilo_compatible($bootloader->{default}) if $bootloader->{default}; |
1448 |
push @conf, map { $_ . '=' . $quotes_if_needed->($bootloader->{$_}) } grep { $bootloader->{$_} } qw(boot root map install serial vga keytable raid-extra-boot menu-scheme vmdefault); |
1449 |
push @conf, grep { $bootloader->{$_} } qw(linear geometric compact prompt mandatory nowarn restricted static-bios-codes large-memory); |
1450 |
push @conf, "append=" . $quotes->($bootloader->{append}) if $bootloader->{append}; |
1451 |
push @conf, "password=" . $bootloader->{password} if $bootloader->{password}; #- also done by msec |
1452 |
push @conf, "timeout=" . round(10 * $bootloader->{timeout}) if $bootloader->{timeout}; |
1453 |
|
1454 |
push @conf, "message=$bootloader->{message}" if $bootloader->{message}; |
1455 |
|
1456 |
push @conf, "ignore-table" if any { $_->{unsafe} && $_->{table} } @{$bootloader->{entries}}; |
1457 |
|
1458 |
push @conf, map_each { "disk=$::a bios=$::b" } %{$bootloader->{bios}}; |
1459 |
|
1460 |
foreach my $entry (@{$bootloader->{entries}}) { |
1461 |
my $mbootpack_file = get_mbootpack_filename($entry); |
1462 |
if ($mbootpack_file && !build_mbootpack($entry)) { |
1463 |
warn "mbootpack is required for xen but unavailable, skipping\n"; |
1464 |
next; |
1465 |
} |
1466 |
if ($entry->{type} eq 'grub_configfile') { |
1467 |
next; |
1468 |
} |
1469 |
|
1470 |
push @conf, "$entry->{type}=" . $file2fullname->($mbootpack_file || $entry->{kernel_or_dev}); |
1471 |
my @entry_conf; |
1472 |
push @entry_conf, "label=" . make_label_lilo_compatible($entry->{label}) if $entry->{label}; |
1473 |
|
1474 |
if ($entry->{type} eq "image") { |
1475 |
push @entry_conf, 'root=' . $quotes_if_needed->($entry->{root}) if $entry->{root} && !$entry->{xen}; |
1476 |
push @entry_conf, "initrd=" . $file2fullname->($entry->{initrd}) if $entry->{initrd} && !$mbootpack_file; |
1477 |
my $append = join(' ', if_($entry->{xen_append}, $entry->{xen_append}), |
1478 |
if_($entry->{xen}, '--', 'root=' . $entry->{root}), |
1479 |
if_($entry->{append}, $entry->{append})); |
1480 |
push @entry_conf, "append=" . $quotes->($append) if $append; |
1481 |
push @entry_conf, "vga=$entry->{vga}" if $entry->{vga}; |
1482 |
push @entry_conf, grep { $entry->{$_} } qw(read-write read-only optional); |
1483 |
push @entry_conf, "mandatory" if $entry->{lock}; |
1484 |
} else { |
1485 |
delete $entry->{unsafe} if $entry->{table}; #- we can't have both |
1486 |
push @entry_conf, map { "$_=$entry->{$_}" } grep { $entry->{$_} } qw(table boot-as); |
1487 |
push @entry_conf, grep { $entry->{$_} } qw(unsafe master-boot); |
1488 |
|
1489 |
if ($entry->{table}) { |
1490 |
#- hum, things like table=c: are needed for some os2 cases, |
1491 |
#- in that case $hd below is undef |
1492 |
my $hd = fs::get::device2part($entry->{table}, $all_hds->{hds}); |
1493 |
if ($hd && $hd != $sorted_hds[0]) { |
1494 |
#- boot off the nth drive, so reverse the BIOS maps |
1495 |
my $nb = sprintf("0x%x", 0x80 + (find_index { $hd == $_ } @sorted_hds)); |
1496 |
$entry->{mapdrive} ||= { '0x80' => $nb, $nb => '0x80' }; |
1497 |
} |
1498 |
} |
1499 |
if ($entry->{mapdrive}) { |
1500 |
push @entry_conf, map_each { "map-drive=$::a", " to=$::b" } %{$entry->{mapdrive}}; |
1501 |
} |
1502 |
} |
1503 |
push @entry_conf, "password=$entry->{password}" if $entry->{password}; |
1504 |
push @entry_conf, grep { $entry->{$_} } qw(mandatory vmwarn vmdisable); |
1505 |
|
1506 |
push @conf, map { "\t$_" } @entry_conf; |
1507 |
} |
1508 |
my $f = arch() =~ /ia64/ ? "$::prefix/boot/efi/elilo.conf" : "$::prefix/etc/lilo.conf"; |
1509 |
|
1510 |
log::l("writing lilo config to $f"); |
1511 |
renamef($f, $f . ($o_backup_extension || '.old')); |
1512 |
output_with_perm($f, $bootloader->{password} ? 0600 : 0644, map { "$_\n" } @conf); |
1513 |
} |
1514 |
|
1515 |
sub install_lilo { |
1516 |
my ($bootloader, $all_hds) = @_; |
1517 |
|
1518 |
if (my ($install) = $bootloader->{method} =~ /lilo-(text|menu)/) { |
1519 |
$bootloader->{install} = $install; |
1520 |
} else { |
1521 |
delete $bootloader->{install}; |
1522 |
} |
1523 |
if ($bootloader->{message_text}) { |
1524 |
output("$::prefix/boot/message-text", $bootloader->{message_text}); |
1525 |
} |
1526 |
my $message = "message-text"; |
1527 |
if (-r "$::prefix/boot/$message") { |
1528 |
symlinkf $message, "$::prefix/boot/message"; |
1529 |
$bootloader->{message} = '/boot/message'; |
1530 |
} |
1531 |
|
1532 |
#- ensure message does not contain the old graphic format |
1533 |
if ($bootloader->{message} && -s "$::prefix$bootloader->{message}" > 65_000) { |
1534 |
output("$::prefix$bootloader->{message}", ''); |
1535 |
} |
1536 |
|
1537 |
write_lilo($bootloader, $all_hds); |
1538 |
|
1539 |
when_config_changed_lilo($bootloader); |
1540 |
|
1541 |
configure_kdm_BootManager('Lilo'); |
1542 |
} |
1543 |
|
1544 |
sub install_raw_lilo { |
1545 |
my ($o_force_answer) = @_; |
1546 |
|
1547 |
my $error; |
1548 |
my $answer = $o_force_answer || ''; |
1549 |
run_program::rooted($::prefix, "echo $answer | lilo", '2>', \$error) or die "lilo failed: $error"; |
1550 |
} |
1551 |
|
1552 |
sub when_config_changed_lilo { |
1553 |
my ($bootloader) = @_; |
1554 |
if (!$::testing && arch() !~ /ia64/ && $bootloader->{method} =~ /lilo/) { |
1555 |
log::l("Installing boot loader on $bootloader->{boot}..."); |
1556 |
install_raw_lilo($bootloader->{force_lilo_answer}); |
1557 |
} |
1558 |
} |
1559 |
|
1560 |
#- NB: ide is lower than scsi, this is important for sort_hds_according_to_bios() |
1561 |
sub hd2bios_kind { |
1562 |
my ($hd) = @_; |
1563 |
lc(join('_', $hd->{bus}, $hd->{host})); |
1564 |
} |
1565 |
|
1566 |
sub ensafe_first_bios_drive { |
1567 |
my ($hds) = @_; |
1568 |
mixed_kind_of_disks($hds) || @$hds > 1 && _not_first_bios_drive($hds->[0]); |
1569 |
} |
1570 |
sub mixed_kind_of_disks { |
1571 |
my ($hds) = @_; |
1572 |
(uniq_ { hd2bios_kind($_) } @$hds) > 1; |
1573 |
} |
1574 |
sub _not_first_bios_drive { |
1575 |
my ($hd) = @_; |
1576 |
my $bios = $hd && $hd->{bios_from_edd}; |
1577 |
$bios && $bios ne '80'; |
1578 |
} |
1579 |
|
1580 |
sub sort_hds_according_to_bios { |
1581 |
my ($bootloader, $all_hds) = @_; |
1582 |
my $boot_hd = fs::get::device2part($bootloader->{first_hd_device} || $bootloader->{boot}, $all_hds->{hds}); #- $boot_hd is undefined when installing on floppy |
1583 |
my $boot_kind = $boot_hd && hd2bios_kind($boot_hd); |
1584 |
|
1585 |
my $translate = sub { |
1586 |
my ($hd) = @_; |
1587 |
my $kind = hd2bios_kind($hd); |
1588 |
$boot_hd ? ($hd == $boot_hd ? 0 : $kind eq $boot_kind ? 1 : 2) . "_$kind" : $kind; |
1589 |
}; |
1590 |
sort { $translate->($a) cmp $translate->($b) } @{$all_hds->{hds}}; |
1591 |
} |
1592 |
|
1593 |
sub device_string2grub { |
1594 |
my ($dev, $legacy_floppies, $sorted_hds) = @_; |
1595 |
if (my $device = fs::get::device2part($dev, [ @$sorted_hds, fs::get::hds_fstab(@$sorted_hds) ])) { |
1596 |
device2grub($device, $sorted_hds); |
1597 |
} elsif (my $floppy = fs::get::device2part($dev, $legacy_floppies)) { |
1598 |
my $bios = find_index { $floppy eq $_ } @$legacy_floppies; |
1599 |
"(fd$bios)"; |
1600 |
} else { |
1601 |
internal_error("unknown device $dev"); |
1602 |
} |
1603 |
} |
1604 |
sub device2grub { |
1605 |
my ($device, $sorted_hds) = @_; |
1606 |
|
1607 |
if (isRAID($device) && $device->{level} == 1) { |
1608 |
#- we can take any disk |
1609 |
$device = $device->{disks}[0]; |
1610 |
} |
1611 |
my ($hd, $part_nb) = |
1612 |
$device->{rootDevice} ? |
1613 |
(fs::get::device2part($device->{rootDevice}, $sorted_hds), $device->{device} =~ /(\d+)$/) : |
1614 |
$device; |
1615 |
my $bios = eval { find_index { $hd eq $_ } @$sorted_hds }; |
1616 |
if (defined $bios) { |
1617 |
my $part_string = defined $part_nb ? ',' . ($part_nb - 1) : ''; |
1618 |
"(hd$bios$part_string)"; |
1619 |
} else { |
1620 |
undef; |
1621 |
} |
1622 |
} |
1623 |
|
1624 |
sub read_grub_device_map() { |
1625 |
my %grub2dev = map { m!\((.*)\)\s+/dev/(.*)$! } cat_("$::prefix/boot/grub/device.map"); |
1626 |
\%grub2dev; |
1627 |
} |
1628 |
sub write_grub_device_map { |
1629 |
my ($legacy_floppies, $sorted_hds) = @_; |
1630 |
my $f = "$::prefix/boot/grub/device.map"; |
1631 |
renamef($f, "$f.old"); |
1632 |
output($f, |
1633 |
(map_index { "(fd$::i) /dev/$_->{device}\n" } @$legacy_floppies), |
1634 |
(map_index { "(hd$::i) /dev/$_->{device}\n" } @$sorted_hds)); |
1635 |
} |
1636 |
|
1637 |
# parses things like "(hd0,4)/boot/vmlinuz" |
1638 |
# returns: ("hd0", 4, "boot/vmlinuz") |
1639 |
sub parse_grub_file { |
1640 |
my ($grub_file) = @_; |
1641 |
my ($grub_dev, $rel_file) = $grub_file =~ m!\((.*?)\)/?(.*)! or return; |
1642 |
my ($hd, $part) = split(',', $grub_dev); |
1643 |
($hd, $part, $rel_file); |
1644 |
} |
1645 |
|
1646 |
# takes things like "(hd0,4)/boot/vmlinuz" |
1647 |
# returns: ("/dev/sda5", "boot/vmlinuz") |
1648 |
sub grub2dev_and_file { |
1649 |
my ($grub_file, $grub2dev, $o_block_device) = @_; |
1650 |
my ($hd, $part, $rel_file) = parse_grub_file($grub_file) or return; |
1651 |
$grub2dev->{$hd} or internal_error("$hd has no mapping in device.map (when translating $grub_file)"); |
1652 |
$part = $o_block_device ? '' : defined $part && $part + 1; #- grub wants "(hdX,Y)" where lilo just want "hdY+1" |
1653 |
my $device = '/dev/' . ($part eq '' ? $grub2dev->{$hd} : devices::prefix_for_dev($grub2dev->{$hd}) . $part); |
1654 |
$device, $rel_file; |
1655 |
} |
1656 |
# takes things like "(hd0,4)/boot/vmlinuz" |
1657 |
# returns: "/dev/sda5" |
1658 |
sub grub2dev { |
1659 |
my ($grub_file, $grub2dev, $o_block_device) = @_; |
1660 |
first(grub2dev_and_file($grub_file, $grub2dev, $o_block_device)); |
1661 |
} |
1662 |
|
1663 |
# replaces |
1664 |
# - "/vmlinuz" with "/boot/vmlinuz" when "root" or "rootnoverify" is set for the entry |
1665 |
# - "(hdX,Y)" in "(hdX,Y)/boot/vmlinuz..." by appropriate path if possible/needed |
1666 |
sub grub2file { |
1667 |
my ($grub_file, $grub2dev, $fstab, $o_entry) = @_; |
1668 |
|
1669 |
if ($grub_file =~ m!^/!) { |
1670 |
my $root = $o_entry && ($o_entry->{rootnoverify} || $o_entry->{grub_root}); |
1671 |
$root and $grub_file = "$root$grub_file"; |
1672 |
} |
1673 |
|
1674 |
if (my ($device, $rel_file) = grub2dev_and_file($grub_file, $grub2dev)) { |
1675 |
my $part = fs::get::device2part($device, $fstab); |
1676 |
if (my $mntpoint = $part && $part->{mntpoint}) { |
1677 |
($mntpoint eq '/' ? '' : $mntpoint) . '/' . $rel_file; |
1678 |
} else { |
1679 |
log::l("ERROR: unknown device $device (computed from $grub_file)"); |
1680 |
$grub_file; |
1681 |
} |
1682 |
} else { |
1683 |
$grub_file; |
1684 |
} |
1685 |
} |
1686 |
|
1687 |
sub boot_copies_dir() { '/boot/copied' } |
1688 |
sub create_copy_in_boot { |
1689 |
my ($file) = @_; |
1690 |
|
1691 |
my $s = $file; |
1692 |
$s =~ s!/!_!g; |
1693 |
my $file2 = boot_copies_dir() . "/$s"; |
1694 |
|
1695 |
log::l("$file is not available at boot time, creating a copy ($file2)"); |
1696 |
mkdir_p(boot_copies_dir()); |
1697 |
output("$file2.link", $file . "\n"); |
1698 |
update_copy_in_boot("$file2.link"); |
1699 |
|
1700 |
$file2; |
1701 |
} |
1702 |
sub update_copy_in_boot { |
1703 |
my ($link) = @_; |
1704 |
my $orig = chomp_(cat_("$::prefix$link")); |
1705 |
(my $dest = $link) =~ s/\.link$// or internal_error("update_copy_in_boot: $link"); |
1706 |
if (-e "$::prefix$orig") { |
1707 |
log::l("updating $dest from $orig"); |
1708 |
cp_af("$::prefix$orig", "$::prefix$dest"); |
1709 |
} else { |
1710 |
log::l("removing $dest since $orig does not exist anymore"); |
1711 |
unlink "$::prefix$link", "$::prefix$orig"; |
1712 |
} |
1713 |
} |
1714 |
|
1715 |
sub crypt_grub_password { |
1716 |
my ($password) = @_; |
1717 |
require IPC::Open2; |
1718 |
local $ENV{LC_ALL} = 'C'; |
1719 |
my ($his_out, $his_in); |
1720 |
my $cmd = ($::prefix ? "chroot $::prefix " : "") . "/sbin/grub-md5-crypt"; |
1721 |
|
1722 |
my $pid = IPC::Open2::open2($his_out, $his_in, $cmd); |
1723 |
|
1724 |
my ($line, $res); |
1725 |
while (sysread($his_out, $line, 100)) { |
1726 |
if ($line =~ /Password/i) { |
1727 |
syswrite($his_in, "$password\n"); |
1728 |
} else { |
1729 |
$res = $line; |
1730 |
} |
1731 |
} |
1732 |
waitpid($pid, 0); |
1733 |
my $status = $? >> 8; |
1734 |
die "failed to encrypt password (status=$status)" if $status != 0; |
1735 |
chomp_($res); |
1736 |
} |
1737 |
|
1738 |
|
1739 |
sub write_grub { |
1740 |
my ($bootloader, $all_hds, $o_backup_extension) = @_; |
1741 |
|
1742 |
my $fstab = [ fs::get::fstab($all_hds) ]; |
1743 |
my @legacy_floppies = detect_devices::floppies(); |
1744 |
my @sorted_hds = sort_hds_according_to_bios($bootloader, $all_hds); |
1745 |
write_grub_device_map(\@legacy_floppies, \@sorted_hds); |
1746 |
|
1747 |
my $file2grub; $file2grub = sub { |
1748 |
my ($file) = @_; |
1749 |
if ($file =~ m!^\(.*\)/!) { |
1750 |
$file; #- it's already in grub format |
1751 |
} else { |
1752 |
my ($part, $rel_file) = fs::get::file2part($fstab, $file, 'keep_simple_symlinks'); |
1753 |
if (my $grub = device2grub($part, \@sorted_hds)) { |
1754 |
$grub . $rel_file; |
1755 |
} elsif (!begins_with($file, '/boot/')) { |
1756 |
log::l("$file is on device $part->{device} which is not available at boot time. Copying it"); |
1757 |
$file2grub->(create_copy_in_boot($file)); |
1758 |
} else { |
1759 |
log::l("ERROR: $file is on device $part->{device} which is not available at boot time. Defaulting to a dumb value"); |
1760 |
"(hd0,0)$file"; |
1761 |
} |
1762 |
} |
1763 |
}; |
1764 |
|
1765 |
if (get_append_with_key($bootloader, 'console') =~ /ttyS(\d),(\d+)/) { |
1766 |
$bootloader->{serial} ||= "--unit=$1 --speed=$2"; |
1767 |
$bootloader->{terminal} ||= "--timeout=" . ($bootloader->{timeout} || 0) . " console serial"; |
1768 |
} elsif ($bootloader->{method} eq 'grub-graphic') { |
1769 |
my $bin = '/usr/sbin/grub-gfxmenu'; |
1770 |
if ($bootloader->{gfxmenu} eq '' && -x "$::prefix$bin") { |
1771 |
my $locale = $::o->{locale} || do { require lang; lang::read() }; |
1772 |
run_program::rooted($::prefix, $bin, '--lang', $locale->{lang}, '--update-gfxmenu'); |
1773 |
$bootloader->{gfxmenu} ||= '/boot/gfxmenu'; |
1774 |
} |
1775 |
#- not handled anymore |
1776 |
delete $bootloader->{$_} foreach qw(splashimage viewport shade); |
1777 |
} else { |
1778 |
delete $bootloader->{gfxmenu}; |
1779 |
} |
1780 |
|
1781 |
my $format = sub { map { "$_ $bootloader->{$_}" } @_ }; |
1782 |
|
1783 |
{ |
1784 |
my @conf; |
1785 |
|
1786 |
if ($bootloader->{password}) { |
1787 |
if (!is_already_crypted($bootloader->{password})) { |
1788 |
my $encrypted = crypt_grub_password($bootloader->{password}); |
1789 |
$bootloader->{password} = "--md5 $encrypted"; |
1790 |
} |
1791 |
} |
1792 |
|
1793 |
push @conf, $format->(grep { defined $bootloader->{$_} } qw(timeout)); |
1794 |
push @conf, $format->(grep { $bootloader->{$_} } qw(color password serial shade terminal viewport background foreground)); |
1795 |
|
1796 |
push @conf, map { $_ . ' ' . $file2grub->($bootloader->{$_}) } grep { $bootloader->{$_} } qw(gfxmenu); |
1797 |
|
1798 |
eval { |
1799 |
push @conf, "default " . (find_index { $_->{label} eq $bootloader->{default} } @{$bootloader->{entries}}); |
1800 |
}; |
1801 |
|
1802 |
foreach my $entry (@{$bootloader->{entries}}) { |
1803 |
my $title = "\ntitle $entry->{label}"; |
1804 |
|
1805 |
if ($entry->{keep_verbatim}) { |
1806 |
push @conf, '', $entry->{verbatim}; |
1807 |
} elsif ($entry->{type} eq "image") { |
1808 |
push @conf, $title; |
1809 |
push @conf, grep { $entry->{$_} } 'lock'; |
1810 |
push @conf, join(' ', 'kernel', $file2grub->($entry->{xen}), $entry->{xen_append}) if $entry->{xen}; |
1811 |
|
1812 |
my $vga = $entry->{vga} || $bootloader->{vga}; |
1813 |
push @conf, join(' ', $entry->{xen} ? 'module' : 'kernel', |
1814 |
$file2grub->($entry->{kernel_or_dev}), |
1815 |
$entry->{xen} ? () : 'BOOT_IMAGE=' . simplify_label($entry->{label}), |
1816 |
if_($entry->{root}, $entry->{root} =~ /loop7/ ? "root=707" : "root=$entry->{root}"), #- special to workaround bug in kernel (see #ifdef CONFIG_BLK_DEV_LOOP) |
1817 |
$entry->{append}, |
1818 |
if_($entry->{'read-write'}, 'rw'), |
1819 |
if_($vga && $vga ne "normal", "vga=$vga")); |
1820 |
push @conf, "module " . $_ foreach @{$entry->{modules} || []}; |
1821 |
push @conf, join(' ', $entry->{xen} ? 'module' : 'initrd', $file2grub->($entry->{initrd})) if $entry->{initrd}; |
1822 |
} else { |
1823 |
my $dev = eval { device_string2grub($entry->{kernel_or_dev}, \@legacy_floppies, \@sorted_hds) }; |
1824 |
if (!$dev) { |
1825 |
log::l("dropping bad entry $entry->{label} for unknown device $entry->{kernel_or_dev}"); |
1826 |
next; |
1827 |
} |
1828 |
push @conf, $title; |
1829 |
push @conf, grep { $entry->{$_} } 'lock'; |
1830 |
push @conf, join(' ', $entry->{rootnoverify} ? 'rootnoverify' : 'root', $dev); |
1831 |
|
1832 |
if ($entry->{table}) { |
1833 |
if (my $hd = fs::get::device2part($entry->{table}, \@sorted_hds)) { |
1834 |
if (my $bios = find_index { $hd eq $_ } @sorted_hds) { |
1835 |
#- boot off the nth drive, so reverse the BIOS maps |
1836 |
my $nb = sprintf("0x%x", 0x80 + $bios); |
1837 |
$entry->{mapdrive} ||= { '0x80' => $nb, $nb => '0x80' }; |
1838 |
} |
1839 |
} |
1840 |
} |
1841 |
if ($entry->{mapdrive}) { |
1842 |
push @conf, map_each { "map ($::b) ($::a)" } %{$entry->{mapdrive}}; |
1843 |
} |
1844 |
push @conf, "makeactive" if $entry->{makeactive}; |
1845 |
if ($entry->{type} eq 'grub_configfile') { |
1846 |
push @conf, "configfile $entry->{configfile}"; |
1847 |
} else { |
1848 |
push @conf, "chainloader +1"; |
1849 |
} |
1850 |
} |
1851 |
} |
1852 |
my $f = "$::prefix/boot/grub/menu.lst"; |
1853 |
log::l("writing grub config to $f"); |
1854 |
renamef($f, $f . ($o_backup_extension || '.old')); |
1855 |
output_with_perm($f, 0600, map { "$_\n" } @conf); |
1856 |
} |
1857 |
{ |
1858 |
my $f = "$::prefix/boot/grub/install.sh"; |
1859 |
my $boot_dev = device_string2grub($bootloader->{boot}, \@legacy_floppies, \@sorted_hds); |
1860 |
my $files_dev = device2grub(fs::get::root_($fstab, 'boot'), \@sorted_hds); |
1861 |
renamef($f, $f . ($o_backup_extension || '.old')); |
1862 |
output_with_perm($f, 0755, |
1863 |
"grub --device-map=/boot/grub/device.map --batch <<EOF |
1864 |
root $files_dev |
1865 |
setup --stage2=/boot/grub/stage2 $boot_dev |
1866 |
quit |
1867 |
EOF |
1868 |
"); |
1869 |
} |
1870 |
|
1871 |
check_enough_space(); |
1872 |
} |
1873 |
|
1874 |
sub configure_kdm_BootManager { |
1875 |
my ($name) = @_; |
1876 |
eval { common::update_gnomekderc_no_create("$::prefix/etc/kde/kdm/kdmrc", 'Shutdown' => ( |
1877 |
BootManager => $name |
1878 |
)) }; |
1879 |
} |
1880 |
|
1881 |
sub sync_partition_data_to_disk { |
1882 |
my ($part) = @_; |
1883 |
|
1884 |
common::sync(); |
1885 |
|
1886 |
if ($part->{fs_type} eq 'xfs') { |
1887 |
run_program::rooted($::prefix, 'xfs_freeze', '-f', $part->{mntpoint}); |
1888 |
run_program::rooted($::prefix, 'xfs_freeze', '-u', $part->{mntpoint}); |
1889 |
} |
1890 |
} |
1891 |
|
1892 |
sub _dev_to_MBR_backup { |
1893 |
my ($dev) = @_; |
1894 |
$dev =~ s!/dev/!!; |
1895 |
$dev =~ s!/!_!g; |
1896 |
"$::prefix/boot/boot.backup.$dev"; |
1897 |
} |
1898 |
|
1899 |
sub save_previous_MBR_bootloader { |
1900 |
my ($dev) = @_; |
1901 |
my $t; |
1902 |
open(my $F, $dev); |
1903 |
CORE::read($F, $t, 0x1b8); #- up to disk magic |
1904 |
output(_dev_to_MBR_backup($dev), $t); |
1905 |
} |
1906 |
|
1907 |
sub restore_previous_MBR_bootloader { |
1908 |
my ($dev) = @_; |
1909 |
log::l("restoring previous bootloader on $dev"); |
1910 |
output($dev, scalar cat_(_dev_to_MBR_backup($dev))); |
1911 |
} |
1912 |
|
1913 |
sub install_grub { |
1914 |
my ($bootloader, $all_hds) = @_; |
1915 |
|
1916 |
write_grub($bootloader, $all_hds); |
1917 |
|
1918 |
if (!$::testing) { |
1919 |
if ($bootloader->{previous_boot} && $bootloader->{previous_boot} eq $bootloader->{boot}) { |
1920 |
# nothing to do (already installed in {boot}) |
1921 |
} else { |
1922 |
if ($bootloader->{previous_boot}) { |
1923 |
restore_previous_MBR_bootloader(delete $bootloader->{previous_boot}); |
1924 |
} |
1925 |
if (fs::get::device2part($bootloader->{boot}, [ fs::get::hds($all_hds) ])) { |
1926 |
save_previous_MBR_bootloader($bootloader->{boot}); |
1927 |
$bootloader->{previous_boot} = $bootloader->{boot}; |
1928 |
} |
1929 |
} |
1930 |
|
1931 |
my @files = grep { /(stage1|stage2|_stage1_5)$/ } glob("$::prefix/lib/grub/*/*"); |
1932 |
cp_af(@files, "$::prefix/boot/grub"); |
1933 |
sync_partition_data_to_disk(fs::get::root([ fs::get::fstab($all_hds) ], 'boot')); |
1934 |
install_raw_grub(); |
1935 |
} |
1936 |
|
1937 |
configure_kdm_BootManager('Grub'); |
1938 |
} |
1939 |
sub install_raw_grub() { |
1940 |
log::l("Installing boot loader..."); |
1941 |
my $error; |
1942 |
run_program::rooted($::prefix, "sh", "2>", \$error, '/boot/grub/install.sh') or die "grub failed: $error"; |
1943 |
} |
1944 |
|
1945 |
sub when_config_changed_grub { |
1946 |
my ($_bootloader) = @_; |
1947 |
#- do not do anything |
1948 |
|
1949 |
update_copy_in_boot($_) foreach glob($::prefix . boot_copies_dir() . '/*.link'); |
1950 |
} |
1951 |
|
1952 |
sub action { |
1953 |
my ($bootloader, $action, @para) = @_; |
1954 |
|
1955 |
my $main_method = main_method($bootloader->{method}); |
1956 |
my $f = $bootloader::{$action . '_' . $main_method} or die "unknown bootloader method $bootloader->{method} ($action)"; |
1957 |
$f->($bootloader, @para); |
1958 |
} |
1959 |
|
1960 |
sub install { |
1961 |
my ($bootloader, $all_hds) = @_; |
1962 |
|
1963 |
if (my $part = fs::get::device2part($bootloader->{boot}, [ fs::get::fstab($all_hds) ])) { |
1964 |
die N("You cannot install the bootloader on a %s partition\n", $part->{fs_type}) |
1965 |
if $part->{fs_type} eq 'xfs'; |
1966 |
} |
1967 |
$bootloader->{keytable} = keytable($bootloader->{keytable}); |
1968 |
action($bootloader, 'install', $all_hds); |
1969 |
} |
1970 |
|
1971 |
sub ensure_pkg_is_installed { |
1972 |
my ($do_pkgs, $bootloader) = @_; |
1973 |
|
1974 |
my $main_method = main_method($bootloader->{method}); |
1975 |
if ($main_method eq 'grub' || $main_method eq 'lilo') { |
1976 |
$do_pkgs->ensure_binary_is_installed($main_method, $main_method, 1) or return 0; |
1977 |
if ($bootloader->{method} eq 'grub-graphic') { |
1978 |
$do_pkgs->ensure_is_installed('mageia-gfxboot-theme', '/usr/share/gfxboot/themes/Mageia/boot/message', 1) or return 0; |
1979 |
} |
1980 |
} |
1981 |
1; |
1982 |
} |
1983 |
|
1984 |
sub find_other_distros_grub_conf { |
1985 |
my ($fstab) = @_; |
1986 |
|
1987 |
my @unknown_true_fs = |
1988 |
grep { isTrueLocalFS($_) && |
1989 |
(!$_->{mntpoint} || !member($_->{mntpoint}, '/home', fs::type::directories_needed_to_boot())); |
1990 |
} @$fstab; |
1991 |
|
1992 |
log::l("looking for configured grub on partitions " . join(' ', map { $_->{device} } @unknown_true_fs)); |
1993 |
|
1994 |
my @l; |
1995 |
foreach my $part (@unknown_true_fs) { |
1996 |
my $handle = any::inspect($part, $::prefix) or next; |
1997 |
|
1998 |
foreach my $bootdir ('', '/boot') { |
1999 |
my $f = find { -e "$handle->{dir}$bootdir/$_" } 'grub.conf', 'grub/menu.lst' or next; |
2000 |
push @l, { bootpart => $part, bootdir => $bootdir, grub_conf => "$bootdir/$f" }; |
2001 |
} |
2002 |
if (my $f = common::release_file($handle->{dir})) { |
2003 |
my $h = common::parse_release_file($handle->{dir}, $f, $part); |
2004 |
$h->{name} = $h->{release}; |
2005 |
push @l, $h; |
2006 |
} elsif ($handle && -e "$handle->{dir}/etc/issue") { |
2007 |
my ($s, $dropped) = cat_("$handle->{dir}/etc/issue") =~ /^([^\\\n]*)(.*)/; |
2008 |
log::l("found /etc/issue: $s (removed: $dropped)"); |
2009 |
push @l, { name => $s, part => $part }; |
2010 |
} |
2011 |
} |
2012 |
my $root; |
2013 |
my $set_root = sub { |
2014 |
my ($v) = @_; |
2015 |
$root and log::l("don't know what to do with $root->{name} ($root->{part}{device})"); |
2016 |
$root = $v; |
2017 |
}; |
2018 |
my @found; |
2019 |
while (my $e = shift @l) { |
2020 |
if ($e->{name}) { |
2021 |
$set_root->($e); |
2022 |
} else { |
2023 |
if (@l && $l[0]{name}) { |
2024 |
$set_root->(shift @l); |
2025 |
} |
2026 |
|
2027 |
my $ok; |
2028 |
if ($root && $root->{part} == $e->{bootpart} && $e->{bootdir}) { |
2029 |
# easy case: /boot is not a separate partition |
2030 |
$ok = 1; |
2031 |
} elsif ($root && $root->{part} != $e->{bootpart} && !$e->{bootdir}) { |
2032 |
log::l("associating '/' $root->{part}{device} with '/boot' $e->{bootpart}{device}"); |
2033 |
$ok = 1; |
2034 |
} |
2035 |
if ($ok) { |
2036 |
add2hash($e, $root); |
2037 |
undef $root; |
2038 |
} elsif ($root) { |
2039 |
log::l("weird case for grub conf in $e->{bootpart}{device}, keeping '/' from $root->{part}{device}"); |
2040 |
} else { |
2041 |
log::l("could not recognise the distribution for $e->{grub_conf} in $e->{bootpart}{device}"); |
2042 |
} |
2043 |
$e->{name} ||= "Linux $e->{bootpart}{device}"; |
2044 |
push @found, $e; |
2045 |
} |
2046 |
} |
2047 |
$set_root->(undef); |
2048 |
|
2049 |
@found; |
2050 |
} |
2051 |
|
2052 |
sub update_for_renumbered_partitions { |
2053 |
my ($in, $renumbering, $all_hds) = @_; |
2054 |
|
2055 |
my @configs = grep { $_->{content} } config_files(); |
2056 |
$_->{new} = $_->{orig} = $_->{content} foreach @configs; |
2057 |
|
2058 |
my @sorted_hds; { |
2059 |
my $grub2dev = read_grub_device_map(); |
2060 |
map_each { |
2061 |
$sorted_hds[$1] = fs::get::device2part($::b, $all_hds->{hds}) if $::a =~ /hd(\d+)/; |
2062 |
} %$grub2dev; |
2063 |
} |
2064 |
|
2065 |
#- NB: we make the changes with an added string inside so that hda5 is only renamed once to hda6 |
2066 |
|
2067 |
foreach (@$renumbering) { |
2068 |
my ($old, $new) = @$_; |
2069 |
log::l("renaming $old -> $new"); |
2070 |
(my $lnew = $new) =~ s/(\d+)$/__DRAKX_DONE__$1/; |
2071 |
$_->{new} =~ s/\b$old/$lnew/g foreach @configs; |
2072 |
|
2073 |
any { $_->{name} eq 'grub' } @configs or next; |
2074 |
|
2075 |
my ($old_grub, $new_grub) = map { device_string2grub($_, [], \@sorted_hds) } $old, $new; |
2076 |
log::l("renaming $old_grub -> $new_grub"); |
2077 |
(my $lnew_grub = $new_grub) =~ s/\)$/__DRAKX_DONE__)/; |
2078 |
$_->{new} =~ s/\Q$old_grub/$lnew_grub/g foreach @configs; |
2079 |
} |
2080 |
|
2081 |
$_->{new} =~ s/__DRAKX_DONE__//g foreach @configs; |
2082 |
|
2083 |
my @changed_configs = grep { $_->{orig} ne $_->{new} } @configs or return 1; # no need to update |
2084 |
|
2085 |
$in->ask_okcancel('', N("Your bootloader configuration must be updated because partition has been renumbered")) or return; |
2086 |
|
2087 |
foreach (@changed_configs) { |
2088 |
renamef("$::prefix/$_->{file}", "$::prefix/$_->{file}.old"); |
2089 |
output("$::prefix/$_->{file}", $_->{new}); |
2090 |
} |
2091 |
|
2092 |
my $main_method = detect_main_method($all_hds); |
2093 |
my @needed = map { |
2094 |
$_ eq 'grub' ? 'grub_install' : $_; |
2095 |
} $main_method ? $main_method : ('lilo', 'grub'); |
2096 |
|
2097 |
if (intersection(\@needed, [ map { $_->{name} } @changed_configs ])) { |
2098 |
$in->ask_warn('', N("The bootloader cannot be installed correctly. You have to boot rescue and choose \"%s\"", |
2099 |
N("Re-install Boot Loader"))); |
2100 |
} |
2101 |
1; |
2102 |
} |
2103 |
|
2104 |
1; |