Skip to content
Permalink
2b40bec0a6
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
executable file 430 lines (351 sloc) 11.1 KB
#! /usr/local/system/perl/bin/perl
use strict;
use warnings;
use Getopt::Long;
sub USAGE { return <<"EOF" }
usage:
$0 default : select default kernel for next boot
$0 {label} : select this kernel for next boot
$0 --list : list available labels
$0 --update : only scan /boot and rewrite grub.cfg
$0 --initramfs : update /boot/grub/initramfs.igz from /project/admin/initramfs
$0 --test : perform miscellaneous tests (VX50 board)
$0 --reboot : attempt kexec reboot of selected kernel
$0 --set-default {label} : select this kernel as the default kernel
EOF
sub sys {
my @cmd=@_;
print join(' ',@cmd),"\n";
system @cmd and exit 1;
}
sub read_block {
my ($dev)=@_;
my $buf;
open my $in,'<',$dev or die "$dev: $!\n";
my $l=sysread($in,$buf,512);
$l<0 and die "$dev: $!\n";
$l<512 and die "$dev: invalid\n";
return $buf;
}
sub check_mbr {
my ($dev)=@_;
my $mbr=read_block($dev);
if (unpack('x384a4',$mbr) eq 'GRUB') {
return 'GRUB';
} elsif (unpack('x6a4',$mbr) eq 'LILO') {
return 'LILO'
} else {
die "$dev: unknown MBR signature\n";
}
}
sub check_grub_menu_and_boot_dir {
my $distmaster=`distmaster`; chop $distmaster;
my $missing_in_menu=0;
my $missing_in_boot=0;
my $inconsistent = 1;
my @kernels_menu = grep {/menuentry.+?mariux-\d/} split m/\n/, read_file('/boot/grub/grub.cfg');
@kernels_menu = map {/["']mariux-.+-(\d+)['"]/; "mariux.$1"} @kernels_menu;
my @tmp = grep {/mariux\.\d+/} scandir('/boot');
my %installed;
for (@tmp) {
$installed{$_} = 0; # filesystem data (should have no dupes).
}
for my $k (@kernels_menu) {
if (defined $installed{$k}) {
$installed{$k}+=1; # mind, the top menu entry occurs twice.
} else {
$missing_in_boot++;
}
}
for my $k (keys %installed) {
$missing_in_menu++ if $installed{$k} < 1;
}
if ($missing_in_boot == 0 and $missing_in_menu == 0) {
$inconsistent = 0;
}
warn << "EOF" if $inconsistent;
Note: GRUB menu and installed kernels differ.
$missing_in_boot Kernel(s) from the menu are missing in '/boot'.
$missing_in_menu Kernel(s) installed aren't listed in the menu.
Consider a rewrite of grub.cfg (and mind $distmaster):
$0 --update
EOF
}
sub check_grub_installation {
my $fingerprint='[ab]*.mod'; # this 'scans' just a dozen, and not 200 files.
my $sum_lib = ` cat /usr/lib/grub/i386-pc/$fingerprint | md5sum `;
my $sum_boot = ` cat /boot/grub/i386-pc/$fingerprint | md5sum `;
my @root_dev = root_dev();
warn << "EOF" if $sum_lib ne $sum_boot;
Note: GRUB version installed in /usr differs from GRUB version used to boot.
Consider updating the boot-loader with:
grub-install --target=i386-pc --boot-directory=/boot $root_dev[1]
EOF
}
# /usr/include/sys/sysmacros.h
# MMMM.MMMM MMMM.MMMM MMMM.MMMM MMMM.MMMM mmmm.mmmm mmmm.MMMM MMMM.MMMM mmmm.mmmm
sub major { my ($st_dev)=@_ ; return ( (($st_dev>>8) & 0xfff ) | (($st_dev>>32) & ~0xfff) ); }
sub minor { my ($st_dev)=@_ ; return ( (($st_dev ) & 0xff ) | (($st_dev>>12) & ~0xff) ); }
sub image_to_label { # 'bzImage-3.14.51.mx64.69' -> 'mariux-3.14.51-69'
my ($in)=@_;
$in=~s/bzImage-/mariux-/;
$in=~s/\.mx64\./-/;
return $in;
}
sub label_to_image { # 'mariux-3.14.51-69' -> 'bzImage-3.14.51.mx64.69'
my ($in)=@_;
$in=~s/^mariux-/bzImage-/;
$in=~s/-(\d+)$/.mx64.$1/;
return $in;
}
sub read_file {
my ($fn)=@_;
open my $in,'<',$fn or die "$fn: $!\n";
return join('',<$in>);
}
sub write_file {
my ($fn,$data)=@_;
open my $out,'>',$fn or die "$fn: $!\n";
print $out $data;
close $out or die "$fn: $!\n";
}
sub write_file_atomic {
my ($fn,$data)=@_;
write_file("$fn.$$.tmp",$data);
rename("$fn.$$.tmp",$fn) or die "$fn: $!\n";
}
sub is_VX50 {
-e '/sys/devices/virtual/dmi/id/board_name' or return 0;
my $data=read_file('/sys/devices/virtual/dmi/id/board_name');
return $data=~/VX50/;
}
sub is_uefi_booted {
# If Linux is built with `CONFIG_EFI=y`, and it detects an UEFI
# installation, then it’ll create the directory `/sys/firmware/efi`.
# So, depend on Linux and check for the existence of the directory.
return -d '/sys/firmware/efi';
}
# Check if system has an EFI System Partition (ESP)
sub has_esp {
return !system('blkid -L ESP>/dev/null');
}
our $submenu="all-other-kernel";
sub get_chosen {
my $chosen;
open my $p,'-|','grub-editenv','-','list' or die "$!\n";
while (<$p>) {
/^chosen=(?:$submenu>)?(.+)/ and return $1;
}
close $p or die "$!\n";
$? and exit 1;
return undef;
}
sub root_dev {
my ($st_dev)=stat('/') or die "/: $!\n";
my ($major,$minor)=(major($st_dev),minor ($st_dev));
my $path=readlink("/sys/dev/block/$major:$minor") or die "/sys/dev/block/$major:$minor: $!\n";
my ($root_disk,$root_device)=$path=~m"/([^/]+)/([^/]+)$";
return ("/dev/$root_device","/dev/$root_disk");
}
sub scandir {
my ($dir)=@_;
opendir my $d,$dir or die "$dir: $!\n";
return grep !/^\.\.?$/,readdir $d;
}
our @MARIUX; # ( 'mariux-3.14.51-69','mariux-3.14.51-68',...)
our $MARIUX_DEFAULT; # 'mariux-3.14.51-69'
our $found_default;
our $found_chosen;
sub scan_mariux {
my ($verbose,$chosen)=@_;
@MARIUX=();
my $default_image=readlink "/boot/bzImage.x86_64" or die "/boot/bzImage.x86_64 : $!\n";
$MARIUX_DEFAULT=image_to_label($default_image);
opendir my $d,'/boot' or die "/boot: $!\n";
$verbose and print "available kernels:\n\n";
for my $num (sort {$b <=> $a} map { /^mariux\.(\d+)/ ? $1 : () } readdir $d) {
my $f="/boot/mariux.$num";
my $kernel=readlink $f or die "$f: $!\n";
my $label=image_to_label($kernel);
my $note='';
if ($label eq $MARIUX_DEFAULT) {
$note.=' <---- DEFAULT';
$found_default++;
}
if (defined $chosen and $label eq $chosen) {
$note.=' <---- CHOSEN';
$found_chosen++;
}
$verbose and print " $label$note\n";
push @MARIUX,$label;
}
if (-e '/boot/bzImage.test') {
my $label='bzImage.test';
my $note='';
if (defined $chosen and $label eq $chosen) {
$note.=' <---- CHOSEN';
$found_chosen++;
}
$verbose and print " $label$note\n";
push @MARIUX,$label;
}
if (!$found_default) {
warn "WARNING: default kernel labeled $MARIUX_DEFAULT not available\n";
}
if (defined $chosen and !$found_chosen) {
warn "WARNING: chosen kernel labeled $chosen not available\n";
}
}
our $KERNEL_PARAMETER="ro crashkernel=64G-:256M console=ttyS0,115200n8 console=tty0 init=/bin/systemd audit=0 random.trust_cpu=on systemd.unified_cgroup_hierarchy";
sub update_grub_cfg {
my $kernellist='';
@MARIUX or die "internal error: scan_mariux() not called\n";
for my $label (@MARIUX) {
my $image=label_to_image($label);
$kernellist.="\tmenuentry \"$label\" --unrestricted { save_env chosen ; linux /boot/$image root=LABEL=root $KERNEL_PARAMETER ; initrd /boot/grub/initramfs.igz }\n";
}
my $GRUB_CFG_NEW=<<"EOF";
set timeout=5
set superusers="root"
export superusers
password_pbkdf2 root grub.pbkdf2.sha512.10000.A1168F03CC3CD47F79848E949584EA624FF531B53611F61218DC5BAD760E767063A96E6A5CE6B3506BAC28C5A2F9A7D491BDA770CC233BC9F464F33F8524FC79.EE4EF2EB7B2B8C2DD313300396179F5484CAA011D7CBF2A3FF886A372A6FF2DBC3B1AE59E05687B294D4B8F4674DA55CCE18EDBEB2635E4B6F2BE7C4EBC8916A
set default="$MARIUX_DEFAULT"
load_env
insmod all_video
if [ -e /etc/local/USB.usb ]; then
set default="mariuxUSB"
menuentry "mariuxUSB" --unrestricted { save_env chosen ; linux /boot/bzImage.x86_64 root=LABEL=rootusb rootdelay=5 $KERNEL_PARAMETER ; initrd /boot/grub/initramfs.igz }
else
menuentry "$MARIUX_DEFAULT" --unrestricted { set chosen="$submenu>$MARIUX_DEFAULT" ; save_env chosen ; linux /boot/bzImage.x86_64 root=LABEL=root $KERNEL_PARAMETER ; initrd /boot/grub/initramfs.igz }
submenu "$submenu" --unrestricted {
$kernellist
}
if [ \$chosen ]; then
set default=\$chosen
unset chosen # must be clear to get a clean value from menuentry
fi
fi
EOF
if (-e '/boot/grub/grub.cfg') {
my $GRUB_CFG_OLD=read_file('/boot/grub/grub.cfg');
if ($GRUB_CFG_NEW eq $GRUB_CFG_OLD) {
# warn "/boot/grub/grub.cfg: no change\n";
return;
}
}
write_file_atomic('/boot/grub/grub.cfg',$GRUB_CFG_NEW);
warn "/boot/grub/grub.cfg: updated\n";
}
sub cmd_list {
my $chosen=get_chosen();
scan_mariux(1,$chosen);
my ($root_device,$root_disk)=root_dev();
my $mbr_type=check_mbr($root_disk);
$mbr_type eq 'GRUB' or warn "WARNING: $root_disk MBR type is $mbr_type\n";
is_VX50() && $mbr_type ne 'GRUB' and warn "WARNING: This is a VX50, I refuse to install grub here\n";
}
sub cmd_install {
my ($label)=@_; #
my ($root_device,$root_disk)=root_dev();
my $mbr_type=check_mbr($root_disk);
is_VX50() && $mbr_type ne 'GRUB' and die "This is a VX50, I refuse to install grub here\n";
if ($label eq 'default') {
scan_mariux(0);
$found_default or exit 1;
} else {
scan_mariux(0,$label);
$found_chosen or exit 1;
}
update_grub_cfg();
if (has_esp) {
sys 'mount','-L','ESP','/boot/efi';
if (-e '/boot/efi/EFI/grub/grubx64.efi') {
print "GRUB for UEFI is already installed.\n";
}
else {
sys 'grub-install','--target=x86_64-efi','--no-nvram',$root_disk;
}
sys 'umount','/boot/efi';
}
if ($mbr_type ne 'GRUB') {
sys 'grub-install','--target=i386-pc',$root_disk;
} else {
print "GRUB is already installed in MBR.\n";
}
if ($label eq 'default') {
sys('grub-editenv','-','set',"chosen=$MARIUX_DEFAULT");
} else {
sys('grub-editenv','-','set',"chosen=$submenu>$label");
}
}
sub cmd_update {
scan_mariux(0);
$found_default or exit 1;
update_grub_cfg();
}
sub cmd_initramfs {
sys('bash','-c','cd /project/admin/initramfs ; find . -name ".git*" -prune -or -print | cpio -H newc -o | gzip > /boot/grub/initramfs.igz');
}
sub cmd_reboot {
my $chosen=get_chosen();
my $image=label_to_image($chosen);
-e "/boot/$image" or die "/boot/$image: no such file\n";
sys('sync');
sys('kexec',"/boot/$image",'--initrd=/boot/grub/initramfs.igz',"--command-line=root=LABEL=root $KERNEL_PARAMETER");
}
sub cmd_set_default {
my ($label)=@_;
my $image=label_to_image($label);
-e "/boot/$image" or die "/boot/$image: no such file\n";
sys 'ln','-sf',$image,'/boot/bzImage.x86_64';
scan_mariux(0);
update_grub_cfg();
}
umask 022;
check_grub_installation();
our ($opt_list,$opt_update,$opt_initramfs,$opt_test,$opt_reboot,$opt_set_default);
GetOptions(
'list' => \$opt_list,
'update' => \$opt_update,
'initramfs' => \$opt_initramfs,
'test' => \$opt_test,
'reboot' => \$opt_reboot,
'set-default' => \$opt_set_default,
) or die USAGE;
$opt_list && $opt_update and die USAGE;
if ($opt_list) {
@ARGV==0 or die USAGE;
cmd_list();
} elsif ($opt_update) {
@ARGV==0 or die USAGE;
cmd_update();
} elsif ($opt_initramfs) {
@ARGV==0 or die USAGE;
cmd_initramfs();
} elsif ($opt_test) {
if (is_VX50) {
print "XV50: yes\n";
} else {
print "XV50: no\n";
}
if (has_esp) {
print "System has an ESP: yes\n";
} else {
print "System has an ESP: no\n";
}
if (is_uefi_booted) {
print "System booted using UEFI: yes\n";
} else {
print "System booted using UEFI: no\n";
}
} elsif ($opt_reboot) {
@ARGV==0 or die USAGE;
cmd_reboot();
} elsif ($opt_set_default) {
@ARGV==1 or die USAGE;
cmd_set_default($ARGV[0]);
} else {
@ARGV==1 or die USAGE;
cmd_install($ARGV[0]);
}
check_grub_menu_and_boot_dir();