Permalink
Cannot retrieve contributors at this time
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?
mxtools/mxgrub/mxgrub
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
executable file
430 lines (351 sloc)
11.1 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /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(); |