#!/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]", "", "",) 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)