You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tde-guidance/grubconfig/grubconfig.py

698 lines
25 KiB

#!/usr/bin/python
# -*- coding: UTF-8 -*-
###########################################################################
# grubconfig.py - description #
# ------------------------------ #
# begin : Sun Dec 10 2006 #
# copyright : (C) 2006-2007 by Martin Böhm #
# email : martin.bohm@kubuntu.org #
# #
###########################################################################
# #
# 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. #
# #
###########################################################################
from qt import *
from tdeui import *
from tdecore import *
from kfile import *
import sys, os, string, re
import os.path
import shutil
import locale
import tempfile
programname = "Boot Loader Configuration"
version = "0.0.2"
standalone = __name__=='__main__'
if standalone:
programbase = KDialogBase
else:
programbase = KCModule
parsable = ["default","menu","color","timeout","hiddenmenu","title","root","kernel","initrd"]
cat1 = ["default","menu","color","timeout","hiddenmenu","root","initrd"]
cat2 = ["savedefault","makeactive","chainloader"]
cat3 = ["kernel"]
class GreyListViewItem(KListViewItem):
def paintCell(self, p, cg, column, width, align ):
cgGrey = cg
cgGrey.setColor(QColorGroup.Text,QColor("grey"))
KListViewItem.paintCell(self, p, cgGrey, column, width, align)
cg.restore()
class BoldListViewItem(KListViewItem):
def paintCell(self, p, cg, column, width, align ):
p.save()
f = p.font()
f.setBold(True)
KListViewItem.paintCell(self, p, cg, column, width, align)
p.restore()
class GrubConfigAppClass(programbase):
def __init__(self,parent=None,name=None):
if standalone:
KDialogBase.__init__(self,KJanusWidget.Tabbed,i18n("Boot Loader Configuration"),
KDialogBase.Help|KDialogBase.Ok|KDialogBase.Close, KDialogBase.Close)
# no need to include About button yet
self.menulstlocation = "/boot/grub/menu.lst"
self.readfilename = self.menulstlocation
self.globalvars = {}
self.itemslist = []
#--- Load menu.lst using the load_menulst() method
self.load_menulst()
print self.globalvars
print self.itemslist
# - GRUB Options Tab -
if standalone:
usershbox = self.addHBoxPage(i18n("Grub Options"))
vbox = QVBox(usershbox)
else:
vbox = QVBox(tabcontrol)
vbox.setMargin(KDialog.marginHint())
# -- Operating Systems List & MakeDefault Button --
horizontalbox = QHBox(vbox)
self.itemslistview = KListView(horizontalbox)
self.itemslistview.addColumn("")
self.itemslistview.setSorting(-1)
self.itemslistview.header().hide()
self.itemslistviewitems = []
arrowsbox = QVBox(horizontalbox)
self.upbutton = KPushButton(i18n("Move Up"),arrowsbox)
self.connect(self.upbutton,SIGNAL("clicked()"),self.slotUpButtonClicked)
self.downbutton = KPushButton(i18n("Move Down"),arrowsbox)
self.connect(self.downbutton,SIGNAL("clicked()"),self.slotDownButtonClicked)
self.defaultbutton = KPushButton(i18n("Make Default"),vbox)
self.connect(self.defaultbutton,SIGNAL("clicked()"),self.slotSetDefaultButtonClicked)
# -- Boot Options Group Box --
bootoptionsbasebox = QVGroupBox(vbox,"Boot Options")
bootoptionsbasebox.setTitle(i18n("Boot Options"));
bootoptionsbasevbox = QWidget(bootoptionsbasebox)
infogrid = QGridLayout(bootoptionsbasevbox,3,2)
infogrid.setSpacing(KDialog.spacingHint())
# --- Timeout ---
label = QLabel(i18n("Timeout:"),bootoptionsbasevbox)
infogrid.addWidget(label,0,0)
timeoutbox = QHBox(bootoptionsbasevbox)
timeoutbox.setSpacing(KDialog.spacingHint())
self.timeout = KIntSpinBox(timeoutbox,"Timeout")
if "timeout" in self.globalvars:
self.timeout.setValue(int(self.globalvars['timeout'][0]))
label = QLabel(i18n("seconds"),timeoutbox)
infogrid.addWidget(timeoutbox,0,1)
infogrid.addWidget(self.timeout,0,1)
# --- Hide Menu on Boot ---
self.hidemenuonboot = QCheckBox(i18n("Hide Menu on Boot"),bootoptionsbasevbox)
if 'hiddenmenu' in self.globalvars:
self.hidemenuonboot.setChecked(int(self.globalvars['hiddenmenu'][0]))
infogrid.addWidget(self.hidemenuonboot,1,1)
# --- Make Last OS Default ---
self.lastdefault = QCheckBox(i18n("Make Last Operating System Default"),bootoptionsbasevbox)
infogrid.addWidget(self.lastdefault,2,1)
# -- Security Group Box --
securitybox = QVGroupBox(vbox,"Security")
securitybox.setTitle(i18n("Security"));
securityvbox = QWidget(securitybox)
infogrid = QGridLayout(securityvbox,2,2)
infogrid.setSpacing(KDialog.spacingHint())
# --- Password ---
label = QLabel(i18n("Password:"),securityvbox)
infogrid.addWidget(label,0,0)
self.userpassword = KPasswordEdit(securityvbox)
infogrid.addWidget(self.userpassword,0,1)
# --- Repeat Password ---
label = QLabel(i18n("Repeat Password:"),securityvbox)
infogrid.addWidget(label,1,0)
self.userrepeatpassword = KPasswordEdit(securityvbox)
infogrid.addWidget(self.userrepeatpassword,1,1)
# -- Splash Screen Group Box --
splashbox = QVGroupBox(vbox,"Splash screen")
splashbox.setTitle(i18n("Splash screen"));
# --- Background Color ---
labelandeditbox = QHBox(splashbox)
label = QLabel(i18n("Background Color:"),labelandeditbox)
self.backgroundcolor = QComboBox(labelandeditbox)
# --- Highlight Color ---
labelandeditbox = QHBox(splashbox)
label = QLabel(i18n("Highlight Color:"),labelandeditbox)
self.highlightcolor = QComboBox(labelandeditbox)
# - Operating Systems Tab -
if standalone:
groupsvbox = self.addVBoxPage(i18n("Operating Systems"))
vb = QVBox(groupsvbox)
else:
groupsvbox = QVBox(tabcontrol)
roupsvbox.setMargin(KDialog.marginHint())
vb = QVBox(groupsvbox)
# -- Operating Systems List --
horizontalbox = QHBox(vb)
self.oslistview = KListView(horizontalbox)
self.oslistview.addColumn("")
self.oslistview.addColumn("")
self.oslistview.setSorting(-1)
self.oslistview.header().hide()
self.oslistviewitems = []
self.connect(self.oslistview,SIGNAL("selectionChanged()"),self.oslistviewitemSelected)
# -- Operating Systems Details Box --
osdetailsbox = QVGroupBox(vb,"Operating System Details")
# label = QLabel(i18n("Security"),securitybox)
detailsvbox = QWidget(osdetailsbox)
infogrid = QGridLayout(detailsvbox,7,2)
infogrid.setSpacing(KDialog.spacingHint())
osdetailsbox.setTitle(i18n("Operating System Details"));
# --- List in GRUB Menu ---
self.listingrub = QCheckBox(i18n("List in GRUB Menu"),detailsvbox)
infogrid.addWidget(self.listingrub,0,1)
# --- Display Name ---
label = QLabel(i18n("Display Name:"),detailsvbox)
infogrid.addWidget(label,1,0)
self.displaynamelabel = QLineEdit("",detailsvbox)
infogrid.addWidget(self.displaynamelabel,1,1)
self.connect(self.displaynamelabel,SIGNAL("textChanged(const QString &)"),self.slotDisplayNameLabelChanged)
# --- Operating System ---
label = QLabel(i18n("Operating System:"),detailsvbox)
infogrid.addWidget(label,2,0)
self.operatingsystem = QComboBox(detailsvbox)
infogrid.addWidget(self.operatingsystem,2,1)
# --- Kernel ---
label = QLabel(i18n("Kernel:"),detailsvbox)
infogrid.addWidget(label,3,0)
self.kernel = KURLRequester(detailsvbox)
infogrid.addWidget(self.kernel,3,1)
# --- Failsafe Kernel ---
# not sure if that is possible - requested by seele
label = QLabel(i18n("Failsafe Kernel:"),detailsvbox)
infogrid.addWidget(label,4,0)
self.failsafekernel = QComboBox(detailsvbox)
infogrid.addWidget(self.failsafekernel,4,1)
# --- Initial RAM Disk ---
label = QLabel(i18n("Initial RAM Disk:"),detailsvbox)
infogrid.addWidget(label,5,0)
self.initrd = KURLRequester(detailsvbox)
infogrid.addWidget(self.initrd,5,1)
# --- Root Filesystem ---
label = QLabel(i18n("Root Filesystem:"),detailsvbox)
infogrid.addWidget(label,6,0)
self.rootfilesystem = QComboBox(detailsvbox)
infogrid.addWidget(self.rootfilesystem,6,1)
# -- Boot Options Box --
bootoptionsbox = QVGroupBox(vb,"Boot Options")
# label = QLabel(i18n("Security"),securitybox)
bootoptionsbox.setTitle(i18n("Boot Options"));
self.acpibox = QCheckBox(i18n("Power Management (ACPI) "),bootoptionsbox)
self.debugbox = QCheckBox(i18n("Debugging Messages "),bootoptionsbox)
self.selinuxbox = QCheckBox(i18n("SELinux Support "),bootoptionsbox)
self.splashbox = QCheckBox(i18n("Splash Screen"),bootoptionsbox)
labelandeditbox = QHBox(bootoptionsbox)
label = QLabel(i18n("Custom Options:"),labelandeditbox)
self.customoptions = KLineEdit("",labelandeditbox)
# -- (static) UI finished --
self.reloadListViews("oslist")
self.reloadListViews("itemslist")
try:
self.oslistview.setSelected(self.oslistviewitems[int(self.globalvars['default'][0])],True)
except ValueError:
self.oslistview.setSelected(self.oslistviewitems[0],True)
ops_list = self.load_osprobe()
print ops_list # mhb debug
#######################################################################
# reload listviews, because they have changed
def reloadListViews(self,name):
print "reloaded"
# you should repaint the one that is not changed on screen
if name == "oslist":
self.oslistview.clear()
for item in self.itemslist:
try:
if self.itemslist.index(item) == int(self.globalvars['default'][0]):
self.oslistviewitems.append(BoldListViewItem(self.oslistview,self.oslistview.lastItem(),item['title'][0]))
else:
self.oslistviewitems.append(KListViewItem(self.oslistview,self.oslistview.lastItem(),item['title'][0]))
except ValueError:
self.oslistviewitems.append(KListViewItem(self.oslistview,self.oslistview.lastItem(),item['title'][0]))
# if it has a root option (other than 1 which means only root by itself), it is an OS
else:
self.itemslistview.clear()
#repaint main list
for item in self.itemslist:
try:
if self.itemslist.index(item) == int(self.globalvars['default'][0]):
print "bam!"
self.itemslistviewitems.append(BoldListViewItem(self.itemslistview,self.itemslistview.lastItem(),item['title'][0]))
else:
self.itemslistviewitems.append(KListViewItem(self.itemslistview,self.itemslistview.lastItem(),item['title'][0]))
except ValueError:
self.itemslistviewitems.append(KListViewItem(self.itemslistview,self.itemslistview.lastItem(),item['title'][0]))
#######################################################################
def slotUser1(self):
self.aboutus.show()
#######################################################################
# def slotClose(self):
# self.close()
#######################################################################
def slotOk(self):
self.save_menulst()
# mhb TODO: catching exceptions here would be useful
self.close()
#######################################################################
def slotCheckOsClicked(self):
self.OsProbedList = self.load_osprobe()
#######################################################################
def oslistviewitemSelected(self):
# save current item changes & select another one
i = self.oslistviewitems.index(self.oslistview.selectedItem())
self.updatingGUI = True
self.displaynamelabel.setText(self.itemslist[i]["title"][0])
# visible in GRUB reload
# kernel reload
try:
self.kernel.setURL(self.itemslist[i]["kernel"][0])
except KeyError:
self.initrd.setURL("unavailable") # mhb debug
# initrd reload
try:
self.initrd.setURL(self.itemslist[i]["initrd"][0])
except KeyError:
self.initrd.setURL("unavailable") # mhb debug
# custom options reload
customoptions = ""
for word in self.itemslist[i]["kernel"][1:-1]:
customoptions += word + " "
self.customoptions.setText(customoptions[:-1])
self.updatingGUI = False
print "oslistview item selected" #mhb debug
pass
#######################################################################
def slotDisplayNameLabelChanged(self, string):
if(self.updatingGUI == False):
print "display name changed" #mhb debug
i = self.oslistviewitems.index(self.oslistview.selectedItem())
self.itemslist[i]["title"][0] = string
self.oslistview.selectedItem().setText(0,string)
self.reloadListViews("itemslist")
pass
#######################################################################
def slotUpButtonClicked(self):
print "UpButton clicked" #mhb debug
i = self.itemslistviewitems.index(self.itemslistview.selectedItem())
self.itemslistview.selectedItem().itemAbove().moveItem(self.itemslistview.selectedItem())
# itemslist should have the same i for the same option
if(i != 0):
container = self.itemslist[i]
self.itemslist[i] = self.itemslist[i-1]
self.itemslist[i-1] = container
self.reloadListViews("oslist")
return "not working yet"
#######################################################################
def slotDownButtonClicked(self):
print "DownButton clicked" #mhb debug
i = self.itemslistviewitems.index(self.itemslistview.selectedItem())
self.itemslistview.selectedItem().moveItem(self.itemslistview.selectedItem().itemBelow())
if(i != len(self.itemslist)-1):
container = self.itemslist[i]
self.itemslist[i] = self.itemslist[i+1]
self.itemslist[i+1] = container
self.reloadListViews("oslist")
return "not working yet"
#######################################################################
def slotSetDefaultButtonClicked(self):
print "SetDefaultButton cliicked" #mhb debug
try:
defaultn = int(self.globalvars["default"][0])
except ValueError:
pass
else:
container = self.itemslistviewitems[defaultn]
self.itemslistviewitems[defaultn] = KListViewItem(self.itemslistview,container,self.itemslist[defaultn]['title'][0])
self.itemslistview.takeItem(container)
indexn = self.itemslistviewitems.index(self.itemslistview.selectedItem())
self.globalvars["default"] = str(indexn)
self.itemslistviewitems[indexn] = BoldListViewItem(self.itemslistview,self.itemslistview.selectedItem(),self.itemslist[indexn]['title'][0])
self.itemslistview.takeItem(self.itemslistview.selectedItem())
self.reloadListViews("oslist")
return "not working yet"
#######################################################################
# loop
def exec_loop(self):
global programbase
# self.__loadOptions()
self.updatingGUI = True
#self.__updateUserList()
#self.__updateGroupList()
self.updatingGUI = False
programbase.exec_loop(self)
print "done"
#######################################################################
# loads menu.lst
# NOT YET parsing:
#  fallback
# currently parsing: (type of the location number)
# default <int>
# timeout <int>
# hiddenmenu <int>
# color <int>
#  password (consider an md5 sum?) <int>
#  AUTOMAGIC KERNELS LIST <interval - two ints>
# GRUBCONFIG DISABLED ITEMS <interval - two ints>
# IMPORTANT note:
# any value that is not parsed MUST not be ommited at the end!
#  any value that is commented (parsed or not) MUST not be omitted at the end!
# not so important note:
# if a value we parse is not defined:
# apply defaults (how do find them out?)
# but specify it in the menu.lst when saving
# mhb TODO: somehow handle automagic kernel list (target: feisty)
# mhb TODO: adapt to more distributions, find menu.lst on different locations
def load_menulst(self):
self.modifiedlines = []
menufd = open(self.menulstlocation,"r")
linenum = 0
lock = 0
itemlock = 0
currentitem = 0
for line in menufd:
# Checks if the first non-white char in a line is a #
if re.search(r'^(/s)*#',line) or re.search(r'^(/s)*$',line):
print "a commented line" # mhb debug
if itemlock == 1:
itemlock = 0
currentitem += 1
# if it is a start of an area we parse, use a lock
if re.search(r'sumthin',line) and (lock == 0):
lock = 1
elif re.search(r'sumthin_other',line) and (lock == 0):
lock = 2
# else if it is an end of an area we parse, close the lock
elif re.search(r'sumthin_other_end',line) and (lock == 2):
lock = 0
elif re.search(r'sumthin_end',line) and (lock == 1):
lock = 0
# errors
# mhb TODO: exception catching
elif re.search(r'sumthin',line) and (lock == 0):
raise IdentationError
elif re.search(r'sumthin_other',line) and (lock == 1):
raise EndOfNotOpenedError
# if it is in the lock, do the mumbo-jumbo
# find out what kind of lock it is
# AUTOCONFIG
if lock == 1:
# automagic kernels list?
if re.search(r'sumthin',line):
pass
# or the other one (grubconfig disabled kernel's list )?
else:
self.modifiedlines.append(linenum)
# else save it as a commented line (does not save the locks)
# GRUBCONFiG commented item
elif lock == 2:
# remove leading spaces and one #
# the parse as a normal menu item
pass
# it's a commented, no need to do anything
else:
pass
# okay, it's not commented
else:
print "a not commented line" # mhb debug
self.modifiedlines.append(linenum)
# we presume the first character is already a name
var_name = line.split()[0]
#print "variable name is " + var_name # mhb debug
# var_value's last item is always the line that has to be changed
var_value = []
if var_name in parsable:
# cat 0 - a title - triggers itemlock, has a name and a value, which should be stored as text
if var_name == "title":
print line.split(None,1)
var_value.append(line.split(None,1)[1][:-1])
itemlock = 1
self.itemslist.append({})
# cat 1 - has a name and 1 value
elif var_name in cat1:
try:
var_value.append(line.split()[1])
except IndexError:
var_value.append(1)
# cat 2 - has a name, but no value ( implicit 1 )
elif var_name in cat2:
var_value.append(1)
# cat 3 - has a name, has multiple values, should be saved as list
elif var_name in cat3:
var_value = line.split()[1:]
# now, append the number
var_value.append(linenum)
if itemlock == 1:
self.itemslist[currentitem][var_name] = var_value
else:
self.globalvars[var_name] = var_value
#if var_name in parsable:
#print "variable name " + var_name + " is parsable " # mhb debug
#if var_name == "title":
#itemlock = 1
#self.itemslist.append({})
#if(len(line.split()) > 1):
#var_value = line.split()[1]
#if itemlock == 1:
#else:
#print "variable value is " + var_value # mhb debug
#else:
#if itemlock == 1:
#self.itemslist[currentitem][var_name] = var_value
#else:
#self.globalvars[var_name] = 1
#else:
#print "variable name " + var_name + " is currently not parsable" # mhb debug
#print "it has no value" # mhb debug
# print "parsed another line" # mhb debug
linenum += 1;
print "load_menulst() called" # mhb debug
return "not working yet"
#######################################################################
# writes menu.lst
def save_menulst(self):
delimeter = " "
# phase 1: preparing the values
lines = []
linecontent = []
# this consists of:
#  1. concatenating the list of lines that were modified
# 2. writing the lines in another list (or something more efficient
output = {}
# the globals first
for unit, value in self.globalvars.items():
lines.append(value[-1])
temp_str=""
temp_str+=(str(unit)+" ")
for index in range(len(value)-1):
temp_str+=(str(value[index])+" ")
linecontent.append(temp_str)
# itemslist next (abattoir)
for item in self.itemslist:
for unit, value in reversed(item.items()):
lines.append(value[-1])
temp_str=""
temp_str+=(str(unit)+" ")
for index in range(len(value)-1):
temp_str+=(str(value[index])+" ")
linecontent.append(temp_str)
# phase 2: writing the file
# by now we have a list of numbers (let's call it lines[])
# and a list of mofified lines (let's call it linecontent[] )
# lines[i] corresponds with linecontent[]
trfile = open(self.readfilename, "r" )
twfilename = tempfile.mkstemp("menulst")[1]
twfile = open(twfilename,"w")
# the current solution is:
# read the menu.lst again (or rather its copy, to prevent the file being changed)
# line by line write it in the output file (to be exact, to a file in /tmp)
linenum = 0
print linecontent
print lines
# foreach file as line:
for originalline in trfile:
# if its number isn't in the location list, simply write it
if linenum in lines:
twfile.writelines(linecontent[linenum])
else:
twfile.writelines(originalline)
linenum += 1;
# if there are any more lines to be written (newly detected options)
# write them at the end (now)
# when that process works out fine do a quick rewrite to /boot/grub/menu.lst
# mhb TODO: declare self.menulstlocation
twfile.close()
shutil.move(twfilename,self.menulstlocation)
# mhb TODO: Exception handling
os.remove(self.readfilename)
print "save_menulst() called" # mhb debug
return "not working yet"
#######################################################################
# loads output from os-probe
def load_osprobe(self):
detected = os.popen('os-prober').readlines()
ops_list = []
for ops in detected:
ops = string.replace(ops,"\n","")
temp_list = ops.split(':')
partition = temp_list[0]
temp_list[0] = string.replace(temp_list[0],"/dev/","")
re_obj = re.search(r'([sh]d)([a-z])([0-9])*$',temp_list[0])
disk = ord(re_obj.group(2))%97
part = int(re_obj.group(3))-1
if re_obj == None:
re_obj = re.search(r'(fd[0-9]*)$',temp_list[0])
if re_obj:
disk = temp_list[0]
part = ""
else : re_obj = re.search(r'(part[0-9]*$', temp_list[0])
if re_obj:
disk = '/disc'
part = temp_list[0]
temp_list[0] = '('+re_obj.group(1)+str(disk)+','+str(part)+')'
if temp_list[3].lower() == "linux":
mounted = os.popen('mount | grep '+partition).readlines()
if mounted:
linux_os = os.popen('linux-boot-prober --mounted '+partition).readlines()
else:
linux_os = os.popen('linux-boot-prober '+partition).readlines()
linux_list = []
for lops in linux_os:
lops = string.replace(lops,"\n","")
temp_linux_list = lops.split(':')
linux_list.append(temp_linux_list)
temp_list.append(linux_list)
ops_list.append(temp_list)
temp_list = []
return ops_list
############################################################################
# Factory function for KControl
def create_grubconfig(parent,name):
return GrubConfigAppClass(parent, name)
##########################################################################
def MakeAboutData():
aboutdata = KAboutData("guidance", programname, version,
unicode(i18n("Boot Loader Configuration Tool")).encode(locale.getpreferredencoding()),
KAboutData.License_GPL, "Copyright (C) 2006-2007 Martin Böhm")
aboutdata.addAuthor("Martin Böhm", "Developer", "martin.bohm@kubuntu.org", "http://mhb.ath.cx/")
aboutdata.addAuthor("Simon Edwards", "Developer", "simon@simonzone.com", "http://www.simonzone.com/software/")
aboutdata.addAuthor("Sebastian Kügler", "Developer", "sebas@kde.org", "http://vizZzion.org")
return aboutdata
if standalone:
aboutdata = MakeAboutData()
TDECmdLineArgs.init(sys.argv,aboutdata)
kapp = TDEApplication()
grubconfigapp = GrubConfigAppClass()
grubconfigapp.exec_loop()