#!/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 # timeout # hiddenmenu # color #  password (consider an md5 sum?) #  AUTOMAGIC KERNELS LIST # GRUBCONFIG DISABLED ITEMS # 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 = TDEAboutData("guidance", programname, version, unicode(i18n("Boot Loader Configuration Tool")).encode(locale.getpreferredencoding()), TDEAboutData.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()