/[soft]/drakx/trunk/perl-install/bootloader.pm
ViewVC logotype

Contents of /drakx/trunk/perl-install/bootloader.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3571 - (show annotations) (download)
Mon Mar 19 18:33:40 2012 UTC (12 years, 5 months ago) by colin
File size: 67728 byte(s)
- use "splash" on the kernel command line vs. "splash=silent" as per upstream
  code (e.g. plymouth, systemd and others)
- support the "quiet" kernel command line argument to hide kernel text


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;

  ViewVC Help
Powered by ViewVC 1.1.30