319 lines
9.9 KiB
Python
Executable File
319 lines
9.9 KiB
Python
Executable File
#!/usr/bin/python2
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Kernel grub.conf configuration script
|
|
|
|
Copyright (C) 2009 Fabio Erculiani
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
"""
|
|
import os
|
|
import sys
|
|
import subprocess
|
|
import shutil
|
|
BOOT_MOUNT = False
|
|
NO_SYS_ROOT_BOOT_DIR = "/boot"
|
|
if os.path.ismount(NO_SYS_ROOT_BOOT_DIR):
|
|
BOOT_MOUNT = True
|
|
SYS_ROOT = os.getenv("ROOT","")
|
|
GRUB_CONF = SYS_ROOT+"/boot/grub/grub.conf"
|
|
FSTAB_CONF = SYS_ROOT+"/etc/fstab"
|
|
DISTRO_NAME = "Sabayon Linux"
|
|
|
|
def getstatusoutput(cmd):
|
|
"""Return (status, output) of executing cmd in a shell."""
|
|
pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r')
|
|
text = pipe.read()
|
|
sts = pipe.close()
|
|
if sts is None: sts = 0
|
|
if text[-1:] == '\n': text = text[:-1]
|
|
return sts, text
|
|
|
|
def get_kernel_grub_line(kernel):
|
|
return "title=%s (%s)\n" % (DISTRO_NAME, os.path.basename(kernel),)
|
|
|
|
def configure_boot_grub(kernel, initramfs):
|
|
|
|
grub_dir = os.path.dirname(GRUB_CONF)
|
|
if not os.path.isdir(grub_dir):
|
|
os.makedirs(grub_dir)
|
|
|
|
if os.access(GRUB_CONF, os.R_OK | os.F_OK):
|
|
|
|
# open in append
|
|
grub = open(GRUB_CONF,"aw")
|
|
shutil.copy2(GRUB_CONF, GRUB_CONF+".add")
|
|
# get boot dev
|
|
boot_dev = get_grub_boot_dev()
|
|
# test if entry has been already added
|
|
grubtest = open(GRUB_CONF,"r")
|
|
content = grubtest.readlines()
|
|
content = [unicode(x,'raw_unicode_escape') for x in content]
|
|
for line in content:
|
|
|
|
if line.find(get_kernel_grub_line(kernel)) != -1:
|
|
grubtest.close()
|
|
print "** Kernel already in configuration => ", line.strip()
|
|
return
|
|
|
|
# also check if we have the same kernel listed
|
|
if (line.find("kernel") != 1) and \
|
|
(line.find(os.path.basename(kernel)) != -1) and not \
|
|
line.strip().startswith("#") \
|
|
and (line.find("safe mode") == -1):
|
|
|
|
grubtest.close()
|
|
print "** Kernel already in configuration (2) => ", line.strip()
|
|
return
|
|
else:
|
|
|
|
# create
|
|
boot_dev = get_grub_boot_dev()
|
|
grub = open(GRUB_CONF,"w")
|
|
# write header - guess (hd0,0)... since it is weird
|
|
# having a running system without a bootloader, at least, grub.
|
|
grub.write("default=0\ntimeout=10\n")
|
|
|
|
cmdline = ''
|
|
if os.access("/proc/cmdline", os.R_OK):
|
|
cmdline_f = open("/proc/cmdline","r")
|
|
cmdline = " "+cmdline_f.readline().strip()
|
|
cmdline_f.close()
|
|
|
|
grub.write(get_kernel_grub_line(kernel))
|
|
grub.write("\troot "+boot_dev+"\n")
|
|
grub.write("\tkernel "+kernel+cmdline+"\n")
|
|
if initramfs:
|
|
grub.write("\tinitrd "+initramfs+"\n")
|
|
grub.write("\tsavedefault\n")
|
|
grub.write("\n")
|
|
grub.flush()
|
|
grub.close()
|
|
|
|
def remove_boot_grub(kernel, initramfs):
|
|
|
|
grub_dir = os.path.dirname(GRUB_CONF)
|
|
if not os.path.isdir(grub_dir):
|
|
os.makedirs(grub_dir)
|
|
|
|
if os.path.isdir(grub_dir) and os.access(GRUB_CONF, os.R_OK | os.F_OK):
|
|
|
|
shutil.copy2(GRUB_CONF, GRUB_CONF+".remove")
|
|
grub_f = open(GRUB_CONF,"r")
|
|
grub_conf = grub_f.readlines()
|
|
grub_f.close()
|
|
|
|
content = [unicode(x,'raw_unicode_escape') for x in grub_conf]
|
|
if not isinstance(kernel, unicode):
|
|
kernel = unicode(kernel,'raw_unicode_escape')
|
|
if not isinstance(initramfs, unicode):
|
|
initramfs = unicode(initramfs,'raw_unicode_escape')
|
|
|
|
new_conf = []
|
|
skip = False
|
|
for line in content:
|
|
|
|
kernel_grub_line = get_kernel_grub_line(kernel)
|
|
if (line.find(kernel_grub_line) != -1):
|
|
skip = True
|
|
continue
|
|
|
|
if line.strip().startswith("title"):
|
|
skip = False
|
|
|
|
if not skip or line.strip().startswith("#"):
|
|
new_conf.append(line)
|
|
|
|
grub_tmp_f = open(GRUB_CONF+".tmp","w")
|
|
for line in new_conf:
|
|
try:
|
|
grub_tmp_f.write(line)
|
|
except UnicodeEncodeError:
|
|
grub_tmp_f.write(line.encode('utf-8'))
|
|
grub_tmp_f.flush()
|
|
grub_tmp_f.close()
|
|
os.rename(GRUB_CONF+".tmp", GRUB_CONF)
|
|
|
|
def boot_device_translation(boot_dev):
|
|
|
|
# actually disabled due to buggy grub.conf entry
|
|
if os.access(GRUB_CONF, os.R_OK | os.F_OK) and 0:
|
|
|
|
f_grub = open(GRUB_CONF, "r")
|
|
stored_boot_dev = [x.strip() for x in f_grub.readlines() if \
|
|
x.strip().startswith("#boot=")]
|
|
f_grub.close()
|
|
if stored_boot_dev:
|
|
stored_boot_dev = stored_boot_dev[0]
|
|
boot_dev = "/dev/" + stored_boot_dev[len("#boot="):]
|
|
|
|
if boot_dev.startswith("/dev/md"):
|
|
|
|
boot_dev = os.path.realpath(boot_dev)
|
|
md_dev = os.path.basename(boot_dev)
|
|
|
|
if os.access("/proc/mdstat", os.R_OK | os.F_OK):
|
|
|
|
f_mdstat = open("/proc/mdstat","r")
|
|
stored_boot_dev = [x.split() for x in f_mdstat.readlines() if \
|
|
x.startswith(md_dev)]
|
|
f_mdstat.close()
|
|
|
|
if stored_boot_dev:
|
|
stored_boot_dev = stored_boot_dev[0]
|
|
for elem in stored_boot_dev:
|
|
if elem.endswith("[0]"):
|
|
boot_dev = "/dev/" + elem[:-len("[0]")]
|
|
break
|
|
|
|
return boot_dev
|
|
|
|
def resolve_device(device):
|
|
if device.startswith("/dev/"):
|
|
return device
|
|
if device.startswith("UUID=") or device.startswith("LABEL="):
|
|
print "resolving UUID/LABEL to device", device
|
|
rc, outstring = getstatusoutput("blkid -lt %s" % (device,))
|
|
if rc != 0:
|
|
print "cannot resolve UUID/LABEL for", device
|
|
return None # argh!
|
|
device = outstring.split(":")[0]
|
|
print "UUID/LABEL resolved to", device
|
|
return device
|
|
|
|
def get_grub_boot_dev():
|
|
|
|
grub_avail = subprocess.call("which grub &> /dev/null", shell = True)
|
|
if grub_avail != 0:
|
|
print "** Cannot find grub. Cannot properly configure kernel"
|
|
return "(hd0,0)"
|
|
|
|
# load currently mounted partitions
|
|
if not os.access(FSTAB_CONF, os.R_OK | os.F_OK):
|
|
print "** Cannot find %s. Cannot properly configure kernel" % (
|
|
FSTAB_CONF,)
|
|
return "(hd0,0)"
|
|
|
|
f_fstab = open(FSTAB_CONF, "r")
|
|
mount_data = [x.split() for x in f_fstab.readlines()]
|
|
f_fstab.close()
|
|
# filter out bogus devices
|
|
mount_data = [x for x in mount_data if x]
|
|
mount_data = [x for x in mount_data if x[0].startswith("/") or \
|
|
x[0].startswith("UUID=") or x[0].startswith('LABEL=')]
|
|
|
|
mount_hash = {}
|
|
for item in mount_data:
|
|
solved_dev = resolve_device(item[0])
|
|
if not solved_dev:
|
|
continue
|
|
mount_hash[item[1]] = solved_dev
|
|
boot_dev = mount_hash.get(NO_SYS_ROOT_BOOT_DIR, mount_hash.get("/"))
|
|
if boot_dev == None:
|
|
print "** Cannot determine boot device. Cannot properly configure" \
|
|
" kernel"
|
|
return "(hd0,0)"
|
|
|
|
# translate boot device, if needed
|
|
boot_dev = boot_device_translation(boot_dev)
|
|
|
|
# load grub map file
|
|
map_file = "grub.map"
|
|
subprocess.call('echo "quit" | grub --no-floppy --no-config-file ' \
|
|
'--no-curses --batch --device-map=grub.map &> /dev/null', shell = True)
|
|
if not os.access(map_file, os.R_OK | os.F_OK):
|
|
print "** Cannot find grub. Cannot properly configure kernel"
|
|
return "(hd0,0)"
|
|
|
|
f_map = open(map_file)
|
|
map_data = [x.split() for x in f_map.readlines()]
|
|
f_map.close()
|
|
os.remove(map_file)
|
|
map_data = dict(((y, x) for x, y in map_data))
|
|
|
|
map_data_devs = map_data.keys()
|
|
grub_dev = None
|
|
linux_dev = None
|
|
for dev in map_data_devs:
|
|
if boot_dev.startswith(dev):
|
|
grub_dev = map_data.get(dev)
|
|
linux_dev = dev
|
|
break
|
|
|
|
if grub_dev == None:
|
|
print "** Cannot match grub device. Cannot properly configure kernel"
|
|
return "(hd0,0)"
|
|
|
|
device_number = boot_dev.replace(linux_dev,'')
|
|
try:
|
|
device_number = int(device_number)
|
|
except ValueError:
|
|
print "** Cannot get device number for '%s' => '%s' | '%s'. Cannot properly configure kernel" % (
|
|
device_number, boot_dev, linux_dev,)
|
|
return "(hd0,0)"
|
|
|
|
device_number -= 1
|
|
grub_boot_dev = grub_dev.replace(')',',%s)' % (device_number,))
|
|
return grub_boot_dev
|
|
|
|
def print_help():
|
|
print "%s %s %s %s" % (sys.argv[0], "[add/remove]",
|
|
"<kernel>", "<initramfs or 'none'>",)
|
|
|
|
def add_kernel(kernel, initramfs):
|
|
|
|
boot_len = len(NO_SYS_ROOT_BOOT_DIR)
|
|
if BOOT_MOUNT:
|
|
kernel = kernel[boot_len:]
|
|
if initramfs:
|
|
initramfs = initramfs[boot_len:]
|
|
|
|
# configure GRUB
|
|
print "** Configuring GRUB bootloader. Adding the new kernel ..."
|
|
configure_boot_grub(kernel, initramfs)
|
|
|
|
def remove_kernel(kernel, initramfs):
|
|
|
|
boot_len = len(NO_SYS_ROOT_BOOT_DIR)
|
|
if BOOT_MOUNT:
|
|
kernel = kernel[boot_len:]
|
|
if initramfs:
|
|
initramfs = initramfs[boot_len:]
|
|
|
|
# configure GRUB
|
|
print "** Configuring GRUB bootloader. Removing the selected kernel ..."
|
|
remove_boot_grub(kernel, initramfs)
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
args = sys.argv[1:]
|
|
if len(args) < 3:
|
|
print_help()
|
|
raise SystemExit(1)
|
|
|
|
cmd = args[0]
|
|
if cmd not in ("add", "remove",):
|
|
print_help()
|
|
raise SystemExit(1)
|
|
|
|
kernel = args[1]
|
|
initramfs = args[2]
|
|
|
|
if initramfs == "none":
|
|
initramfs = ''
|
|
|
|
if cmd == "add":
|
|
print "** Adding kernel '%s' and initramfs '%s'" % (kernel, initramfs,)
|
|
add_kernel(kernel, initramfs)
|
|
elif cmd == "remove":
|
|
print "** Removing kernel '%s' and initramfs '%s'" % (kernel,
|
|
initramfs,)
|
|
remove_kernel(kernel, initramfs)
|
|
raise SystemExit(0)
|