summaryrefslogtreecommitdiffstats
path: root/serviceconfig/serviceconfig.py
diff options
context:
space:
mode:
Diffstat (limited to 'serviceconfig/serviceconfig.py')
-rwxr-xr-xserviceconfig/serviceconfig.py1481
1 files changed, 1481 insertions, 0 deletions
diff --git a/serviceconfig/serviceconfig.py b/serviceconfig/serviceconfig.py
new file mode 100755
index 0000000..0da85a0
--- /dev/null
+++ b/serviceconfig/serviceconfig.py
@@ -0,0 +1,1481 @@
+#!/usr/bin/python
+# -*- coding: UTF-8 -*-
+###########################################################################
+# serviceconfig.py - description #
+# ------------------------------ #
+# begin : Wed Apr 30 2003 #
+# copyright : (C) 2003-2006 by Simon Edwards #
+# email : [email protected] #
+# #
+###########################################################################
+# #
+# 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 kdeui import *
+from kdecore import *
+import sys
+import os
+import os.path
+import posix
+import re
+from pickle import Pickler,Unpickler
+import locale
+
+programname = "Services Configuration"
+version = "0.8.1"
+
+# Holding the name of the distribution, defaults to ...
+DISTRO = "Mandrake"
+
+# These get overridden for Debian.
+initdir = "/etc/init.d"
+rcdir = "/etc/rc.d"
+
+chkconfigpath = "/sbin/chkconfig"
+statusblacklist = ['iptables']
+
+# Are we running as a separate standalone application or in KControl?
+standalone = __name__=='__main__'
+
+# Running as the root user or not?
+isroot = os.getuid()==0
+
+#print "Warning:: root check is disabled"
+#isroot = True
+
+############################################################################
+class DescriptionCache(object):
+
+ proto = 2 ## Choose latest available version of Pickler
+ _lines = []
+ data = {}
+
+ def __init__(self,filename,path="/tmp"):
+ if not os.path.isdir(path):
+ print path, "is not a valid directory, can't cache."
+
+ self.filename = os.path.join(path,filename)
+
+ def loadCache(self):
+ try:
+ fhandle = open(self.filename, "r")
+ pickler = Unpickler(fhandle)
+ self.data = pickler.load()
+ fhandle.close()
+ except IOError, e:
+ print "Couldn't load file:", e
+ print "Not reading description cache data"
+
+ def saveCache(self):
+ """ Save pickled dataobject to the file. We don't want the user to be able to save a datastructure that root
+ will read in since that would have implications for security."""
+ if not isroot:
+ print "Not saving description cache data for security reasons, we're not root"
+ return
+
+ try:
+ fhandle = open(self.filename, "w")
+ pickler = Pickler(fhandle, self.proto)
+ pickler.dump(self.data)
+ fhandle.close()
+ #print "Saved description cache data to ", self.filename
+ except IOError, e:
+ print "Can't cache:", e
+
+ def add(self,filename,packagename,description):
+ self.data[filename] = (packagename,description)
+
+ def readDescription(self,filename):
+ """ Tries to look up the package description, returns None if it can't find it."""
+ try:
+ return self.data[filename][1]
+ except KeyError:
+ return None
+
+ def readPackagename(self,filename):
+ """ Tries to look up the Packagename, returns None if it can't find it."""
+ try:
+ return self.data[filename][0]
+ except KeyError:
+ return None
+
+############################################################################
+# Holds all of the info about a single service.
+class Service(object):
+ ########################################################################
+ def __init__(self,context,filename):
+ self.context = context
+ self.filename = filename
+ self.longname = ""
+ self.description = ""
+ self.runlevels = []
+ self.gotStatus = False
+ self.statusable = False
+ self.status = unicode(i18n("?"))
+ self.startpriority = "50"
+ self.killpriority = "50"
+
+ try:
+ fhandle = open(os.path.join(initdir,self.filename))
+ place = 0
+ for line in fhandle.readlines():
+ line = line.strip()
+ if place==0:
+ if line.startswith("#!/bin/sh") or line.startswith("#! /bin/sh") or \
+ line.startswith("#!/bin/bash") or line.startswith("#! /bin/bash"):
+ place = 1
+ elif place==1: # Grab the short description line.
+ if line!="#":
+ if line.startswith("# chkconfig:"):
+ parts = line[12:].split()
+ if len(parts)>=1:
+ for c in parts[0]:
+ try:
+ rl = self.context.getRunLevelByNumber(int(c))
+ if rl is not None:
+ self.runlevels.append(rl)
+ rl.availableservices.append(self)
+ except ValueError:
+ pass
+ if len(parts)>=2:
+ self.startpriority = parts[1]
+ if len(parts)>=3:
+ self.killpriority = parts[2]
+ else:
+ self.longname = line[2:]
+ place = 2
+ elif place==2: # Look for the description line
+ if line.startswith("# chkconfig:"):
+ parts = line[12:].split()
+ if len(parts)>=1:
+ for c in parts[0]:
+ try:
+ rl = self.context.getRunLevelByNumber(int(c))
+ if rl is not None:
+ self.runlevels.append(rl)
+ rl.availableservices.append(self)
+ except ValueError:
+ pass
+ if len(parts)>=2:
+ self.startpriority = parts[1]
+ if len(parts)>=3:
+ self.killpriority = parts[2]
+ elif line.startswith("# description:"):
+ self.description = line[15:]
+ if self.description[-1:]=="\\":
+ self.description = self.description[:-1]
+ place = 3
+ else:
+ place = 2
+ elif place==3: # Grab a description continuation line.
+ if line[0]=="#":
+ self.description += line[1:].strip()
+ if self.description[-1:]=="\\":
+ self.description = self.description[:-1]
+ else:
+ place = 2
+ if line.startswith("status)"):
+ self.statusable = True
+
+ fhandle.close()
+ if self.filename in statusblacklist:
+ self.statusable = False
+ except IOError:
+ pass
+
+ if self.longname=="":
+ self.longname = self.filename
+
+ if len(self.runlevels)==0:
+ self.runlevels = self.context.runlevels[:]
+ for rl in self.context.runlevels:
+ rl.availableservices.append(self)
+
+ ########################################################################
+ def isAvailableInRunlevel(self,level):
+ return level in self.runlevels
+
+ ########################################################################
+ def fetchStatus(self):
+ global initdir
+ if self.statusable:
+ self.status = os.popen(os.path.join(initdir,self.filename) + " status").read()
+ self.gotStatus = True
+
+ ########################################################################
+ def doStartCommand(self):
+ self.gotStatus = False
+ return "export CONSOLETYPE=serial && " + os.path.join(self.context.initdir,self.filename) + " start"
+
+ ########################################################################
+ def doStopCommand(self):
+ self.gotStatus = False
+ return "export CONSOLETYPE=serial && " + os.path.join(self.context.initdir,self.filename) + " stop"
+
+ ########################################################################
+ def doRestartCommand(self):
+ self.gotStatus = False
+ return "export CONSOLETYPE=serial && " + os.path.join(self.context.initdir,self.filename) + " restart"
+
+############################################################################
+class ServiceContext(object):
+ def __init__(self):
+ self.runlevels = []
+ self.services = {}
+
+ def loadInfo(self): pass
+ def currentRunLevel(self): pass
+ def newRunLevel(self): pass
+ def newService(self,file): pass
+ def getRunLevelByNumber(self,num):
+ for rl in self.runlevels:
+ if rl.levelnum==num:
+ return rl
+ return None
+
+############################################################################
+def getServiceContext():
+ global DISTRO
+ # Detect if we are running on Debian, Mandrake or what.
+
+ # Uncomment here to test Gentoo compatibility.
+ #DISTRO = "Gentoo"; return GentooServiceContext()
+
+ # Check for Debian - is this the 'good' way?
+ etc_issue = '/etc/issue'
+
+ # Knoppix and Kanotix have a symlink called /etc/issue
+ if os.path.islink(etc_issue):
+ etc_issue = posix.readlink(etc_issue)
+
+ if os.path.isfile(etc_issue):
+ etc_issue = open(etc_issue)
+ system_name = etc_issue.readline()
+ etc_issue.close()
+ if system_name.startswith("Debian") or system_name.startswith("Ubuntu"):
+ DISTRO = "Debian"
+ return DebianServiceContext()
+
+ # Might this be Gentoo Linux?
+ if os.path.isfile('/etc/gentoo-release'):
+ DISTRO = "Gentoo"
+ return GentooServiceContext()
+
+ # Mandrake is default.
+ return MandrakeServiceContext()
+
+############################################################################
+class MandrakeServiceContext(ServiceContext):
+ ########################################################################
+ def __init__(self):
+ ServiceContext.__init__(self)
+ self.initdir = "/etc/init.d"
+ self.rcdir = "/etc/rc.d"
+ self.runlevels.append(SysVRunLevel(self,0,i18n("Halt (0)")))
+ self.runlevels.append(SysVRunLevel(self,1,i18n("Single User Mode (1)")))
+ self.runlevels.append(SysVRunLevel(self,2,i18n("Multiuser mode with Networking (2)")))
+ self.runlevels.append(SysVRunLevel(self,3,i18n("Multiuser mode (3)")))
+ self.__currentrunlevel = SysVRunLevel(self,5,i18n("GUI Multiuser mode (5)"))
+ self.runlevels.append(self.__currentrunlevel)
+ self.runlevels.append(SysVRunLevel(self,6,i18n("Reboot (6)")))
+ self._filenametoservice = {}
+
+ ########################################################################
+ def currentRunLevel(self):
+ return self.__currentrunlevel
+
+ ########################################################################
+ def loadInfo(self):
+ # Load in all of the service info.
+ for filename in os.listdir(self.initdir):
+ newservice = self.newService(filename)
+ self.services[filename] = newservice
+ self._filenametoservice[filename] = newservice
+
+ # Now load in which services are active in which run level.
+ for rl in self.runlevels:
+ rl.loadInfo()
+
+ ########################################################################
+ def newService(self,file):
+ return Service(self,file)
+
+############################################################################
+class SysVRunLevel(object):
+ ########################################################################
+ def __init__(self,context,levelnum,name):
+ self.context = context
+ self.levelnum = levelnum
+ self.name = name
+ self.availableservices = []
+ self.activeservices = []
+ self.leveldir = os.path.join(self.context.rcdir,"rc"+str(self.levelnum)+".d")
+
+ ########################################################################
+ def loadInfo(self):
+ #print "SysVRunLevel(object).loadInfo",self.leveldir
+ for filename in os.listdir(self.leveldir):
+ if filename.startswith("S") and os.path.islink(self.leveldir+"/"+filename):
+ target = os.path.basename(posix.readlink(self.leveldir+"/"+filename))
+ if target in self.context._filenametoservice:
+ self.activeservices.append(self.context._filenametoservice[target])
+ #else:
+ # print "Couldn't find service '%s' in runlevel %i." % (target, self.levelnum)
+
+ ########################################################################
+ def isActiveAtBoot(self,service):
+ return service in self.activeservices
+
+ ########################################################################
+ def setActiveAtBoot(self,service,activeflag):
+ leveldir = self.leveldir
+
+ # Remove any existing links to the service script
+ for filename in os.listdir(leveldir):
+ link = os.path.join(leveldir,filename)
+ if os.path.islink(link):
+ target = os.path.basename(posix.readlink(os.path.join(leveldir,filename)))
+ if target==service.filename:
+ # Kill target.
+ #print "Killing link",link
+ os.remove(link)
+
+ if activeflag:
+ #print "symlink(",leveldir+"/S"+service.startpriority+service.filename,",",self.context.initdir+"/"+service.filename,")"
+ posix.symlink( os.path.join(self.context.initdir,service.filename),
+ os.path.join(leveldir,"S"+service.startpriority+service.filename))
+ self.activeservices.append(service)
+ else:
+ #print "symlink(", leveldir+"/K"+service.killpriority+service.filename,",",self.context.initdir+"/"+service.filename,")"
+ posix.symlink( os.path.join(self.context.initdir,service.filename),
+ os.path.join(leveldir,"K"+service.killpriority+service.filename))
+ self.activeservices.remove(service)
+
+############################################################################
+#
+# Here's the Debian specific stuff, a Service, a ServiceContext and a RunLevel
+#
+# Enjoy. ;-)
+
+class DebianService(Service):
+ """ Mapping for services which don't use a pidfile like /var/run/<service>.pid
+ Services not in here are lookup up "normally" """
+ pidfiles = { 'acpid':'acpid.socket',
+ 'spamassassin':'spamd.pid',
+ 'dbus':'dbus/pid',
+ 'klogd':'klogd/klogd.pid',
+ 'samba':'samba/smbd.pid',
+ 'zope':'zope/default/Z2.pid',
+ 'mysql':'mysqld/mysqld.pid',
+ 'hald':'hal/hald.pid',
+ 'sysklogd':'syslogd.pid',
+ 'ssh':'sshd.pid',
+ 'cron':'crond.pid',
+ 'slapd':'slapd/slapd.pid',
+ 'laptop-mode':'laptop-mode-enabled',
+ 'cupsys':'cups/cupsd.pid'
+ }
+
+ ########################################################################
+ def __init__(self,context,filename):
+ self.context = context
+ self.filename = filename
+ self.path_and_filename = os.path.join(context.initdir, self.filename)
+ self.packagename = False
+ self.description = i18n("Description is being loaded.")
+ self.runlevels = []
+ self.gotStatus = False
+ self.statusable = False
+ self.status = unicode(i18n("not running"))
+ self.startpriority = "50"
+ self.killpriority = "50"
+ self.getStatusFrom = "pidfile"
+
+ self.fetchStatus()
+
+ if len(self.runlevels)==0:
+ self.runlevels = self.context.runlevels[:]
+ for rl in self.context.runlevels:
+ rl.availableservices.append(self)
+
+ def fetchDescription(self):
+ self.description = self.context.descriptioncache.readDescription(self.filename)
+ self.packagename = self.context.descriptioncache.readPackagename(self.filename)
+ if not self.description:
+ if not self.packagename:
+ self.fetchPackageName()
+ #print " packagename", self.packagename
+ if self.packagename:
+ # FIXME: don't assume english output!
+ command = "apt-cache show " + self.packagename
+
+ self.description = ""
+ description_label = "Description:"
+ for line in os.popen(command).readlines():
+ if line.startswith(description_label):
+ self.description = line.strip()[len(description_label):]
+
+ self.context.descriptioncache.add(self.filename, self.packagename, self.description, )
+ else:
+ self.description = i18n("Couldn't fetch a description from apt.")
+
+ def fetchPackageName(self):
+ if os.path.isfile(self.path_and_filename):
+ command = "dpkg -S " + self.path_and_filename
+ self.packagename = None # as opposed to False( = not yet fetched)
+ for line in os.popen(command).readlines():
+ if ":" in line:
+ self.packagename = line.strip().split(":")[0]
+ else:
+ print self.path_and_filename + " is no file or does not exist!"
+
+ ########################################################################
+ def fetchStatus(self):
+ if self.getStatusFrom == "pidfile":
+ self.fetchStatusFromPidFile()
+ elif self.getStatusFrom == "top":
+ # FIXME: not yet implemented
+ self.fetchStatusFromTop()
+
+ ########################################################################
+ def fetchStatusFromTop(self):
+ # FIXME, incomplete.
+ top = os.popen("ps -aux")
+
+ ########################################################################
+ def fetchStatusFromPidFile(self):
+ try:
+ if os.path.isfile(os.path.join('/var/run',self.pidfiles[self.filename])):
+ self.status = unicode(i18n("running"))
+ else:
+ self.status = unicode(i18n("not running"))
+ except KeyError:
+ if os.path.isfile(os.path.join('/var/run',self.filename + '.pid')):
+ self.status = unicode(i18n("running"))
+ else:
+ self.status = unicode(i18n("not running"))
+ self.gotStatus = True
+
+############################################################################
+class DebianServiceContext(ServiceContext):
+ """ bootscripts are scripts that are only running once at boot and where starting,
+ stopping and restarting does not really make sense, generally exclude these from
+ serviceconfig list."""
+ bootscripts = ( 'README',
+ 'acpi-support',
+ 'xorg-common',
+ 'binfmt-support',
+ 'bootclean.sh',
+ 'bootmisc.sh',
+ 'checkfs.sh',
+ 'checkroot.sh',
+ 'console-screen.sh',
+ 'dns-clean',
+ 'glibc.sh',
+ 'halt',
+ 'hostname.sh',
+ 'hwclock.sh',
+ 'hwclockfirst.sh',
+ 'initrd-tools.sh',
+ 'keymap.sh',
+ 'makedev',
+ 'module-init-tools',
+ 'mountall.sh',
+ 'mountvirtfs',
+ 'mountnfs.sh',
+ 'nvidia-kernel',
+ 'procps.sh',
+ 'pppd-dns',
+ 'powernowd.early',
+ 'rc',
+ 'rc.local',
+ 'rcS',
+ 'readahead',
+ 'readahead-desktop',
+ 'reboot',
+ 'rmnologin',
+ 'screen-cleanup',
+ 'screen',
+ 'sendsigs',
+ 'single',
+ 'skeleton',
+ 'stop-bootlogd',
+ 'stop-readahead',
+ 'umountfs',
+ 'umountnfs.sh',
+ 'urandom'
+ )
+
+ def __init__(self):
+ """
+ Debian uses the following runlevels:
+
+ 1 (single-user mode),
+ 2 through 5 (multiuser modes), and
+ 0 (halt the system),
+ 6 (reboot the system).
+
+ Runlevels 7, 8, and 9 can also be used but their rc directories are not populated
+ when packages are installed. They are intentionally left out here, but should be
+ easy to add.
+ """
+ ServiceContext.__init__(self)
+
+ self.initdir = "/etc/init.d"
+ self.rcdir = "/etc"
+ self.relative_initdir = "../init.d"
+
+ deb_runlevels = { 0 : i18n("Halt (0)"),
+ 1 : i18n("Single User Mode (1)"),
+ "S" : i18n("Single User Mode (S)"),
+ 2 : i18n("Multiuser Mode (2)"),
+ 3 : i18n("Multiuser Mode (3)"),
+ 4 : i18n("Multiuser Mode (4)"),
+ 5 : i18n("Multiuser Mode (5)"),
+ 6 : i18n("Reboot (6)") }
+
+ # Lookup what runlevel we're in.
+ shell_output = os.popen('/sbin/runlevel')
+ raw_runlevel = shell_output.readline()
+ shell_output.close()
+ cur_runlevel = raw_runlevel[2:-1]
+
+ for num in deb_runlevels.keys():
+ if cur_runlevel.isdigit():
+ if num == int(cur_runlevel):
+ self.__currentrunlevel = DebianRunLevel(self, num, deb_runlevels[num])
+ self.runlevels.append(self.__currentrunlevel)
+ else:
+ self.runlevels.append(DebianRunLevel(self, num, deb_runlevels[num]))
+ else:
+ if num == cur_runlevel:
+ self.__currentrunlevel = DebianRunLevel(self, num, deb_runlevels[num])
+ self.runlevels.append(self.__currentrunlevel)
+ else:
+ self.runlevels.append(DebianRunLevel(self, num, deb_runlevels[num]))
+ self._filenametoservice = {}
+
+ ########################################################################
+ def currentRunLevel(self):
+ return self.__currentrunlevel
+
+ ########################################################################
+ def loadInfo(self):
+ # Load in all of the service info.
+ initscripts = os.listdir(self.initdir)
+ # Remove "bootscripts" from our list.
+ servicefiles = []
+ self.services = []
+ for script in initscripts:
+ if script not in self.bootscripts:
+ try:
+ # Exclude backup copies.
+ if script.split(".")[1] not in ("orig","dpkg-dist"):
+ servicefiles.append(script)
+ except IndexError:
+ servicefiles.append(script)
+
+ for filename in servicefiles:
+ if filename not in self.bootscripts:
+ newservice = self.newService(filename)
+ self.services.append(newservice)
+ self._filenametoservice[filename] = newservice
+
+ # Now load in which services are active in which run level.
+ for rl in self.runlevels:
+ rl.loadInfo()
+
+ ########################################################################
+ def newService(self,file):
+ return DebianService(self,file)
+
+############################################################################
+class DebianRunLevel(SysVRunLevel):
+ ########################################################################
+ def setActiveAtBoot(self,service,activeflag):
+ """ Adds a Service to a runlevel.
+
+ Activating a service adds start symlinks in the respective levels, and
+ maintains symlinks in levels 0, 1 and 6 (halt, single user and reboot).
+ """
+ leveldir = self.context.rcdir
+
+ def createSymlink(target, linkname):
+ """ Creates a symlink after having checked if it makes sense to do so.
+ We first change to the rcdir, then create a relative symlink and then
+ change back, sounds weird, but Debian's own scripts break when the
+ symlinks are not relative.
+
+ Returns True or False and prints debugging message.
+ """
+ odir = os.getcwd()
+ tmpdir = "/".join(linkname.split("/")[0:-1]) # FIXME use os.path
+ os.chdir(tmpdir)
+
+ if not os.path.isfile(target) or os.path.islink(target):
+ #print target + " is not a valid filename. Can't create symlink."
+ os.chdir(odir)
+ return False
+
+ if os.path.islink(linkname) and posix.readlink(linkname) == target:
+ #print "Symlink " + linkname + " -> " + target + " already exists."
+ os.chdir(odir)
+ return True
+
+ if os.path.islink(linkname) and posix.readlink(linkname) != target:
+ #print "Removing symlink, " + linkname + ", the target does not match."
+ try:
+ posix.unlink(linkname)
+ except OSError, e:
+ print "Couldn't remove symlink " + linkname + " :: " + str(e)
+
+ try:
+ posix.symlink(target, linkname)
+ #print "Created symlink " + linkname + " -> " + target + " successfully."
+ os.chdir(odir)
+ return True
+ except OSError, e:
+ #print "Creating symlink " + linkname + " -> " + target + " failed: " + str(e)
+ os.chdir(odir)
+ return False
+
+ def removeSymlink(servicename, runleveldir, KorS):
+ if KorS not in ('K','S'):
+ print "OUCH, symlinks have to start with S or K!"
+ return
+ for link in os.listdir(runleveldir):
+ if (link[0] == KorS) and (link[3:] == servicename):
+ #print "Killing ...", runleveldir+link
+ posix.unlink(os.path.join(runleveldir,link))
+
+ # In these levels, the K symlinks are created.
+ stop_levels = (0,1,6)
+ l_num = str(self.levelnum)
+
+ if activeflag:
+ target = os.path.join(self.context.relative_initdir,service.filename)
+ createSymlink(target, os.path.join(self.context.rcdir,"rc"+l_num+".d","S"+service.startpriority+service.filename))
+ # Kill links:
+ for i in stop_levels:
+ createSymlink(target, os.path.join(self.context.rcdir,"rc"+str(i)+".d","K"+service.killpriority+service.filename))
+ self.activeservices.append(service)
+ else:
+
+ try:
+ s_link = os.path.join(leveldir,"rc"+l_num+".d","S"+service.startpriority+service.filename)
+ runleveldir = os.path.join(leveldir,"rc"+l_num+".d")
+ #print "Removing symlink " + s_link
+ removeSymlink(service.filename, runleveldir, "S")
+ except OSError, e:
+ print "Could not remove symlink " + s_link + " :: " + str(e)
+
+ self.activeservices.remove(service)
+
+ # check if service has to be started in other runlevels:
+ # Y: Don't touch links
+ # N: Remove symlinks
+
+ #print "Should remove symlinks here."
+ for rl in self.context.runlevels:
+ if service in rl.activeservices:
+ #print "Service " + service.filename + " is still used in runlevel " + \
+ # str(rl.levelnum) + ", not removing K-Links."
+ break
+ else:
+ # The service is not being used anywhere. We can remove it now.
+
+ #print "Service completely inactive, removing K-links."
+ for i in stop_levels:
+ k_link = os.path.join(leveldir,"rc"+str(i)+".d","K"+service.killpriority+service.filename)
+ runleveldir = os.path.join(leveldir,"rc"+str(i)+".d")
+
+ try:
+ #print "Removing " + k_link
+ removeSymlink(service.filename, runleveldir, "K")
+ except OSError, e:
+ print "Could not remove " + k_link + " :: " + str(e)
+
+
+############################################################################
+#
+# Here come all the Gentoo specific pieces.
+# Gentoo Linux has a special way of organizing the init and runlevels stuff.
+
+class GentooService(DebianService):
+ """ GentooService
+
+ Services in Gentoo are handled very much like the ones in Debian, except
+ that there is rc-status to check whether a service is running or not.
+ """
+ ########################################################################
+ def fetchStatus(self):
+ # rc-status is run everytime we check a service, this might be some
+ # more efficiently solved.
+ # FIXME: add check if 'rc-status' is in current PATH
+ rc_status_fhandle = os.popen('rc-status')
+ for line in rc_status_fhandle.readlines():
+ parts = line.split()
+ if parts[0] == self.filename:
+ # Who needs Perl? ;-)
+ # FIXME: set the terminal type to serial when running rc-status.
+ self.status = line.split(';01m')[2].split('\x1b[')[0].strip()
+ rc_status_fhandle.close()
+ self.gotStatus = True
+
+ def fetchDescription(self):
+ # Temporary vars.
+ description_lines = []
+ first_block = True
+
+ if os.path.isfile(self.path_and_filename):
+ fhandle = open(self.path_and_filename)
+ for line in fhandle.readlines():
+ # Ignore blank lines and CVS Headers:
+ if len(line.strip()) > 1 and line[:2] != '#!' and line[:10] != '# $Header:':
+ # Cut off newline at the end.
+ line = line[:-1]
+ # The first commencted block might be the description.
+ if first_block:
+ if line[0] != '#': first_block = False
+ else: description_lines.append(line[1:].strip())
+ fhandle.close()
+ else:
+ print self.path_and_filename + " is no file or does not exist!"
+
+ if len(description_lines):
+ self.description = "\n".join(description_lines)
+ else:
+ self.description = i18n("Could not extract description.")
+
+ ########################################################################
+ def doZapCommand(self):
+ self.gotStatus = False
+ return "export CONSOLETYPE=serial && rc-status"
+ return "export CONSOLETYPE=serial && "+os.path.join(self.context.initdir,self.filename)+" zap"
+
+############################################################################
+class GentooServiceContext(ServiceContext):
+ ########################################################################
+ def __init__(self):
+ """
+ Gentoo uses customized runlevels, see the Gentoo Documentation for
+ the dirty details. The runlevels are defined in inittab, so we have
+ a look there.
+
+ - Runlevel links reside in /etc/runlevels/${RUNLEVEL}/.
+ - Default existing runlevels are boot/, nonetwork/ and default/.
+ - Custom runlevels and default are defined in /etc/inittab.
+
+ Dependencies between runscripts / initscrips are not handled here,
+ this is responsibility of rc-update. Also, after rc-update has run,
+ some items might be necessary to refresh.
+
+ """
+ ServiceContext.__init__(self)
+
+ # Here comes the Gentoo specific stuff.
+ # First off, parsing inittab for runlevels available.
+ def parseInittab():
+ if os.path.isfile(self.inittab):
+ inittab_fhandle = open(self.inittab, 'r')
+ rl_appended = []
+ for line in inittab_fhandle.readlines():
+ line = line[:-1]
+ # Ignore blank and commented lines.
+ if len(line.strip()) > 1 and line.strip()[0] != '#':
+ parts = line.split(':')
+ if len(parts) == 4 and parts[2] == 'wait':
+ rl_num, rl_label = parts[1], parts[3].split()[1] +' ('+parts[1]+')'
+ rl_name = parts[3].split()[1]
+ #print "Num: " + rl_num + " Label: " + rl_label + " Name: " + parts[3].split()[1]
+ # This is a runlevel in Gentoo, is it the current one?
+ if parts[1] == self.current_runlevelnum:
+ self.__currentrunlevel = GentooRunLevel(self, rl_num, rl_name, rl_label)
+ self.runlevels.append(self.__currentrunlevel)
+ rl_appended.append(rl_name)
+ else:
+ if rl_name not in rl_appended:
+ self.runlevels.append(GentooRunLevel(self, rl_num, rl_name, rl_label))
+ rl_appended.append(rl_name)
+ elif len(parts) == 4 and parts[2] == 'bootwait':
+ # The boot runlevel does not have a 'real' runlevel number, so we use 0.
+ self.runlevels.append(GentooRunLevel(self, 0, parts[3].split()[1], 'boot'))
+ rl_appended.append('boot')
+ inittab_fhandle.close()
+
+ def currentRunLevelNum():
+ runlevelbin = "/sbin/runlevel"
+ if not os.path.isfile(runlevelbin):
+ print "Couldn't find %s, that sucks. :o" % runlevelbin
+ sys.exit(1)
+ shell_output = os.popen(runlevelbin)
+ raw_runlevel = shell_output.readline()
+ shell_output.close()
+ return raw_runlevel[2:-1]
+
+ self.initdir = "/etc/init.d"
+ self.rcdir = "/etc/runlevels"
+ self.inittab = "/etc/inittab"
+
+ #self.initdir = "/home/sebas/gentooinit/init.d"
+ #self.rcdir = "/home/sebas/gentooinit/runlevels"
+ #self.inittab = "/home/sebas/gentooinit/inittab"
+
+ self.current_runlevelnum = currentRunLevelNum()
+ parseInittab()
+ self._filenametoservice = {}
+
+ ########################################################################
+ def currentRunLevel(self):
+ return self.__currentrunlevel
+
+ ########################################################################
+ def loadInfo(self):
+ """ Load in all of the service info for every file in the init.d directory. """
+ for filename in os.listdir(self.initdir):
+ # Exclude backup files from portage and .sh files like shutdown, depscan, etc.
+ if (filename.find('._cfg')<0) and not filename.endswith(".sh"):
+ newservice = self.newService(filename)
+ self.services[filename] = newservice
+ self._filenametoservice[filename] = newservice
+
+ # Now load in which services are active in which run level.
+ for rl in self.runlevels:
+ rl.loadInfo()
+
+ ########################################################################
+ def newService(self,file):
+ return GentooService(self,file)
+
+
+############################################################################
+class GentooRunLevel(SysVRunLevel):
+ """ Gentoo Runlevel
+
+ GentooRunLevel has an additional parameter. the 'label' gets displayed in the
+ 'Run level:' drop-down menu, the 'name' is used. 'name' is internally handled
+ in very much the same way as SysVRunLevel.levelnum. It corresponds to the
+ actual runlevels Gentoo uses, such as /etc/runlevels/default/, rather than
+ using SysVRunLevel.levelnum, as it would be in a SysV init. """
+
+ ########################################################################
+ def __init__(self,context,levelnum,dirname,label):
+ SysVRunLevel.__init__(self,context,levelnum,label)
+
+ self.dirname = dirname
+
+ self.leveldir = self.context.rcdir+'/'+self.dirname
+ # Not all runlevels in Gentoo correspond to a runlevel directory.
+ self.no_dirs = []
+ if not os.path.isdir(self.leveldir):
+ #self.no_dirs = ('reboot', 'shutdown', 'single')
+ if self.dirname not in self.no_dirs:
+ self.no_dirs.append(self.dirname)
+ print "Runlevel " + self.leveldir + " is not a valid path. '" + self.dirname + "'"
+ self.leveldir = False
+ return
+
+ ########################################################################
+ def loadInfo(self):
+ """ Only look up active services if runlevel path exists, else leave empty. """
+ if self.leveldir:
+ print "GentooRunLevel.loadInfo() from " + self.leveldir
+ for filename in os.listdir(self.leveldir):
+ # Exclude backup files from portage and .sh files like shutdown, depscan, etc.
+ if (filename.find('._cfg')<0) and not filename.endswith('.sh'):
+ linkname = self.leveldir+"/"+filename
+ if os.path.islink(linkname):
+ target = os.path.basename(posix.readlink(linkname))
+ if target in self.context._filenametoservice:
+ self.activeservices.append(self.context._filenametoservice[target])
+ else:
+ print "Couldn't find service '%s'. " % target
+ else:
+ print "%s is not a valid symlink." % linkname
+
+ ########################################################################
+ def setActiveAtBoot(self,service,activeflag):
+ """ Runs rc-update to add and remove Services from RunLevels.
+
+ Dependencies are checked from within runscript.sh, so we don't handle them here. """
+
+ # FIXME :: "Start at Boot" column does not properly get updated once it's "False".
+ # The commands issued might better be passed through via CommandRunner.
+ if self.name in self.no_dirs:
+ print "Runlevel has no corresponding path, running rc-update anyway."
+ if activeflag:
+ if not service in self.activeservices:
+ rc_add_cmd = "rc-update add %s %s" % (service.filename, self.dirname)
+ print rc_add_cmd
+ # The brave really run it yet.
+ os.system(rc_add_cmd)
+ self.activeservices.append(service)
+ else:
+ if service in self.activeservices:
+ rc_del_cmd = "rc-update del %s %s" % (service.filename, self.dirname)
+ print rc_del_cmd
+ # The brave really run it yet.
+ os.system(rc_dell_cmd)
+ self.activeservices.remove(service)
+
+############################################################################
+
+# Try translating this code to C++. I dare ya!
+if standalone:
+ programbase = KDialogBase
+else:
+ programbase = KCModule
+
+# is_shown exists to prevent loadDescriptions from running two times, which is
+# the case when we're running inside kcontrol. Yes, this is an ugly hack. :(
+# It's set to True after show has finished once. It doesn't play a role when
+# we're running standalone.
+is_shown = False
+
+class SysVInitApp(programbase):
+ ########################################################################
+
+ def __init__(self,parent=None,name=None):
+ global standalone,isroot, DISTRO, is_shown
+ KGlobal.locale().insertCatalogue("guidance")
+
+ if standalone:
+ KDialogBase.__init__(self,KJanusWidget.Plain,i18n("Service Configuration"), \
+ KDialogBase.User1|KDialogBase.Close, KDialogBase.Close)
+ self.setButtonText(KDialogBase.User1,i18n("About"))
+ else:
+ KCModule.__init__(self,parent,name)
+ self.setButtons(1)
+ self.aboutdata = MakeAboutData()
+
+ # Create a configuration object.
+ self.config = KConfig("serviceconfigrc")
+ KGlobal.iconLoader().addAppDir("guidance")
+
+ self.updatingGUI = False
+ self.context = getServiceContext()
+ self.servicestolistitems = {} # Map service names to QListViewItems
+ self.currentrunlevel = self.context.currentRunLevel()
+
+ self.context.loadInfo()
+
+ self.aboutus = KAboutApplication(self)
+
+ if standalone:
+ toplayout = QVBoxLayout( self.plainPage(), 0, KDialog.spacingHint() )
+ tophb = QSplitter(Qt.Horizontal, self.plainPage())
+ else:
+ toplayout = QVBoxLayout( self, 0, KDialog.spacingHint() )
+ tophb = QSplitter(Qt.Horizontal, self)
+
+ toplayout.addWidget(tophb)
+
+ vb = QVBox(tophb)
+ vb.setSpacing(KDialog.spacingHint())
+
+ hb = QHBox(vb)
+ hb.setSpacing(KDialog.spacingHint())
+ vb.setStretchFactor(hb,0)
+
+ label = QLabel(hb)
+ label.setPixmap(UserIcon("hi32-app-daemons"))
+ hb.setStretchFactor(label,0)
+
+ label = QLabel(i18n("Run level:"),hb)
+ hb.setStretchFactor(label,0)
+ self.runlevelcombo = QComboBox(hb)
+
+ # Load up the runlevel combo box.
+ i = 0
+ for runlevel in self.context.runlevels:
+ self.runlevelcombo.insertItem(runlevel.name)
+ if self.context.currentRunLevel() is runlevel:
+ self.runlevelcombo.setCurrentItem(i)
+ i += 1
+
+ hb.setStretchFactor(self.runlevelcombo,0)
+
+ self.connect(self.runlevelcombo, SIGNAL("activated(int)"), self.slotRunLevelChanged)
+
+ widget = QWidget(hb)
+ hb.setStretchFactor(widget,1)
+
+ self.servicelistview = KListView(vb)
+ self.servicelistview.addColumn(i18n("Service"))
+ self.servicelistview.addColumn(i18n("Start at Boot"))
+ self.servicelistview.addColumn(i18n("Status"))
+ self.servicelistview.setAllColumnsShowFocus(True)
+ self.servicelistview.setSelectionMode(QListView.Single)
+ self.connect(self.servicelistview, SIGNAL("selectionChanged(QListViewItem *)"), self.slotListClicked)
+
+ # Right hand side of the dialog.
+ vb = QVBox(tophb)
+ vb.setSpacing(KDialog.spacingHint())
+
+ hgb = QHGroupBox(i18n("Service Details"),vb)
+ vb.setStretchFactor(hgb,1)
+ vb2 = QVBox(hgb)
+ vb2.setSpacing(KDialog.spacingHint())
+
+ label = QLabel(i18n("Description:"),vb2)
+ vb2.setStretchFactor(label,0)
+ self.descriptiontextedit = QTextEdit(vb2)
+ vb2.setStretchFactor(self.descriptiontextedit,2)
+ self.descriptiontextedit.setReadOnly(True)
+ self.startatbootcheckbox = QCheckBox(i18n("Start during boot"),vb2)
+ vb2.setStretchFactor(self.startatbootcheckbox,0)
+ self.connect(self.startatbootcheckbox, SIGNAL("toggled(bool)"), self.slotBootChanged)
+
+ label = QLabel(i18n("Status:"),vb2)
+ vb2.setStretchFactor(label,0)
+ self.statustext = QTextEdit(vb2)
+ self.statustext.setReadOnly(True)
+ vb2.setStretchFactor(self.statustext,1)
+
+ hb2 = QHBox(vb2)
+ hb2.setSpacing(KDialog.spacingHint())
+ vb2.setStretchFactor(hb2,0)
+ self.startbutton = QPushButton(i18n("Start"),hb2)
+ hb2.setStretchFactor(self.startbutton,1)
+ self.connect(self.startbutton, SIGNAL("clicked()"), self.slotStartButton)
+ self.stopbutton = QPushButton(i18n("Stop"),hb2)
+ hb2.setStretchFactor(self.stopbutton,1)
+ self.connect(self.stopbutton, SIGNAL("clicked()"), self.slotStopButton)
+ self.restartbutton = QPushButton(i18n("Restart"),hb2)
+ hb2.setStretchFactor(self.restartbutton,1)
+ self.connect(self.restartbutton, SIGNAL("clicked()"), self.slotRestartButton)
+
+ if DISTRO == "Gentoo":
+ # Gentoo Linux gets an extra button.
+ self.zapbutton = QPushButton(i18n("Zap"),hb2)
+ hb2.setStretchFactor(self.zapbutton,1)
+ self.connect(self.zapbutton, SIGNAL("clicked()"), self.slotZapButton)
+
+ if not isroot:
+ self.disableStuff()
+ else:
+ self.connect(self.servicelistview, SIGNAL("contextMenu(KListView*,QListViewItem*,const QPoint&)"),
+ self.slotServiceContextMenu)
+
+ self.__fillListView(self.currentrunlevel)
+ self.__selectFirstService()
+
+ self.cr = CommandRunner(None,"title")
+ self.timerid = None
+
+ ########################################################################
+ def __del__(self):
+ pass
+
+ def disableStuff(self):
+ """Disable a couple of widgets when not running as root"""
+ self.startatbootcheckbox.setDisabled(True)
+ self.startbutton.setDisabled(True)
+ self.restartbutton.setDisabled(True)
+ self.stopbutton.setDisabled(True)
+ if DISTRO == "Gentoo":
+ self.zapbutton.setDisabled(True)
+
+ def slotServiceContextMenu(self,l,v,p):
+ self.cmenu = KPopupMenu(self,"MyActions")
+ self.cmenu.insertItem(i18n("Start..."), self.slotStartButton)
+ self.cmenu.insertItem(i18n("Stop..."), self.slotStopButton)
+ self.cmenu.insertItem(i18n("Restart..."), self.slotRestartButton)
+ self.cmenu.insertItem(i18n("Toggle start during boot..."), self.slotBootChangedAndToggle)
+
+ self.cmenu.exec_loop(p)
+
+ def slotBootChangedAndToggle(self):
+ """Wrap slotBootChanged in order to pass the status of the checkbox, used from contextmenu."""
+ self.startatbootcheckbox.toggle()
+
+ ########################################################################
+ # KDialogBase method
+ def exec_loop(self):
+ global programbase
+
+ self.__loadOptions()
+ programbase.exec_loop(self)
+ self.__saveOptions()
+
+ ########################################################################
+ def __selectFirstService(self):
+ # Grab the first service in the list and select it.
+ services = self.currentrunlevel.availableservices[:]
+ services.sort(lambda x,y: cmp(x.filename,y.filename))
+ self.selectedservice = None
+ try:
+ self.selectedservice = services[0]
+ lvi = self.servicestolistitems[self.selectedservice]
+ self.servicelistview.setSelected(lvi,True)
+ except IndexError:
+ pass
+ self.__selectService(self.selectedservice)
+
+ ########################################################################
+ def show(self):
+ global standalone,isroot, is_shown
+
+ programbase.show(self)
+ self.updatingGUI = True
+ if isroot:
+ self.__selectFirstService()
+ self.updatingGUI = False
+ self.__checkServiceStatus()
+
+ if DISTRO == "Debian" and (standalone or (not standalone and is_shown)):
+ QTimer.singleShot(0,self.__startLoadDescriptions)
+ is_shown = True
+
+
+ ########################################################################
+ # KDialogBase method
+ def slotUser1(self):
+ self.aboutus.show()
+
+ ########################################################################
+ def __fillListView(self,runlevelobj):
+ self.servicelistview.clear()
+ self.servicestolistitems = {}
+
+ services = self.currentrunlevel.availableservices[:]
+ services.sort(lambda x,y: cmp(x.filename,y.filename))
+
+ for item in services:
+ if item.isAvailableInRunlevel(runlevelobj):
+ status = item.status.strip().replace("\n",", ")[:32]
+ if item in runlevelobj.activeservices:
+ lvi = QListViewItem(self.servicelistview,item.filename,i18n("Yes"),status)
+ else:
+ lvi = QListViewItem(self.servicelistview,item.filename,i18n("No"),status)
+ self.servicestolistitems[item] = lvi
+
+ ########################################################################
+ def __selectService(self,service):
+ if service!=None:
+ self.descriptiontextedit.setEnabled(True)
+ self.descriptiontextedit.setText(service.description)
+
+ if isroot:
+ self.startatbootcheckbox.setEnabled(True)
+
+ self.startatbootcheckbox.setChecked(self.currentrunlevel.isActiveAtBoot(service))
+ self.statustext.setEnabled(True)
+ self.statustext.setText(service.status)
+ else:
+ self.disableStuff()
+ self.descriptiontextedit.setText("")
+ self.descriptiontextedit.setEnabled(False)
+ self.statustext.setText("")
+ self.statustext.setEnabled(False)
+
+ ########################################################################
+ def slotRunLevelChanged(self,levelnum):
+ if self.updatingGUI:
+ return
+ self.updatingGUI = True
+
+ self.currentrunlevel = self.context.runlevels[levelnum]
+ self.__fillListView(self.currentrunlevel)
+ self.__selectFirstService()
+ self.__checkServiceStatus()
+
+ self.updatingGUI = False
+
+ ########################################################################
+ def slotListClicked(self,item):
+ if self.updatingGUI:
+ return
+ self.updatingGUI = True
+
+ for service in self.servicestolistitems.keys():
+ if self.servicestolistitems[service] is item:
+ self.selectedservice = service
+ self.__selectService(self.selectedservice)
+ break
+ self.updatingGUI = False
+
+ ########################################################################
+ def slotBootChanged(self,state):
+ if self.updatingGUI:
+ return
+ self.updatingGUI = True
+ level = self.currentrunlevel
+ level.setActiveAtBoot(self.selectedservice,state)
+ if level.isActiveAtBoot(self.selectedservice):
+ self.servicestolistitems[self.selectedservice].setText(1,i18n("Yes"))
+ else:
+ self.servicestolistitems[self.selectedservice].setText(1,i18n("No"))
+ self.updatingGUI = False
+
+ ########################################################################
+ def slotCloseButton(self):
+ self.close()
+
+ ########################################################################
+ def slotStartButton(self):
+ self.__runInitScript(i18n("Starting %1").arg(self.selectedservice.filename), \
+ self.selectedservice,"start")
+
+ ########################################################################
+ def slotStopButton(self):
+ self.__runInitScript(i18n("Stopping %1").arg(self.selectedservice.filename), \
+ self.selectedservice,"stop")
+
+ ########################################################################
+ def slotRestartButton(self):
+ self.__runInitScript(i18n("Restarting %1").arg(self.selectedservice.filename), \
+ self.selectedservice,"restart")
+
+ ########################################################################
+ def slotZapButton(self):
+ """This button lets the Gentoo user use the zap command, if process
+ information has not properly been cleaned up by the init script.
+ """
+ if DISTRO == "Gentoo":
+ self.__runInitScript(i18n("Zapping %1").arg(self.selectedservice.filename), \
+ self.selectedservice,"zap")
+
+
+ ########################################################################
+ def __startLoadDescriptions(self):
+ if DISTRO=="Debian":
+ cachepath = "/var/tmp"
+ cachefile = "guidance-packagedescriptioncache"
+ self.context.descriptioncache = DescriptionCache(cachefile,cachepath)
+ self.context.descriptioncache.loadCache()
+ self.__loadDescriptions()
+
+ ########################################################################
+ def __loadDescriptions(self):
+ """
+ Loads the description of all services showing a progressbar if it takes longer,
+ tries to get the descriptions from cache first.
+ """
+ for service in self.context.services:
+ if service.packagename is None:
+ continue
+ # Check if we want to fetch a description for the currently selected item
+ # before we go on fetching other descriptions.
+ if not self.selectedservice.packagename:
+ self.selectedservice.fetchDescription()
+ self.slotListClicked(self.servicelistview.currentItem())
+ break
+ if not service.packagename:
+ service.fetchDescription()
+ break
+ else:
+ self.slotListClicked(self.servicelistview.currentItem())
+ if DISTRO=="Debian":
+ self.context.descriptioncache.saveCache()
+ return
+
+ QTimer.singleShot(0,self.__loadDescriptions)
+
+ ########################################################################
+ def __runInitScript(self,title,service,command):
+ global DISTRO
+ self.cr.setCaption(title)
+ self.cr.setHeading(title)
+ if command=="start":
+ cmd = service.doStartCommand()
+ elif command=="stop":
+ cmd = service.doStopCommand()
+ elif command=="restart":
+ cmd = service.doRestartCommand()
+ elif DISTRO == "Gentoo" and command=="zap":
+ cmd = service.doZapCommand()
+
+ self.cr.run(["/bin/bash","-c",cmd])
+
+ # Does not seem to properly update ...
+ self.__checkServiceStatus()
+
+ ########################################################################
+ def __checkServiceStatus(self):
+ global kapp
+ global progcount
+
+ # Put up the progress dialog. (User pacifier).
+ dialog = KProgressDialog(self,"statusprogress",
+ i18n("Querying System Service Status"),
+ i18n("Querying system service status"))
+ dialog.setLabel(i18n("Querying system service status"))
+ dialog.setAutoClose(True)
+ dialog.showCancelButton(False)
+
+ services = self.currentrunlevel.availableservices[:]
+ services.sort(lambda x,y: cmp(x.filename,y.filename))
+ dialog.progressBar().setTotalSteps(len(services))
+
+ kapp.processEvents()
+ self.updatingGUI = True
+
+ for item in services:
+ if item.gotStatus==False:
+ item.fetchStatus()
+ lvi = self.servicestolistitems[item]
+ lvi.setText(2,item.status.strip().replace("\n",", ")[:32])
+ if self.selectedservice is item:
+ self.statustext.setText(item.status)
+ dialog.progressBar().advance(1)
+ kapp.processEvents()
+ dialog.setMinimumDuration(2000000000)
+ dialog.hide()
+ self.updatingGUI = False
+
+ ########################################################################
+ def __loadOptions(self):
+ self.config.setGroup("General")
+ size = self.config.readSizeEntry("Geometry")
+ if size.isEmpty()==False:
+ self.resize(size)
+ size = self.config.readSizeEntry("CommandRunnerGeometry")
+ if size.isEmpty()==False:
+ self.cr.resize(size)
+
+ #######################################################################
+ def __saveOptions(self):
+ global isroot
+ if isroot:
+ return
+ self.config.setGroup("General")
+ self.config.writeEntry("Geometry", self.size())
+ self.config.writeEntry("CommandRunnerGeometry",self.cr.size())
+ self.config.sync()
+
+ #######################################################################
+ # KControl virtual void methods
+ def load(self):
+ pass
+ def save(self):
+ pass
+ def defaults(self):
+ pass
+ def sysdefaults(self):
+ pass
+
+ def aboutData(self):
+ # Return the KAboutData object which we created during initialisation.
+ return self.aboutdata
+
+ def buttons(self):
+ # Only supply a Help button. Other choices are Default and Apply.
+ return KCModule.Help
+
+############################################################################
+class CommandRunner(KDialogBase):
+ ########################################################################
+ def __init__(self,parent,name):
+ KDialogBase.__init__(self,parent,name,1,"",KDialogBase.Ok)
+ self.output = ""
+ self.running = False
+
+ self.resize(400,200)
+ vbox = self.makeVBoxMainWidget()
+
+ hbox = QHBox(vbox)
+ hbox.setSpacing(self.spacingHint())
+
+ tmplabel = QLabel(hbox)
+ tmplabel.setPixmap(UserIcon("laserwarn"))
+ hbox.setStretchFactor(tmplabel,0)
+
+ self.headinglabel = QLabel(hbox)
+ hbox.setStretchFactor(self.headinglabel,1)
+
+ self.outputtextview = QTextView(vbox)
+ self.outputtextview.setTextFormat(QTextView.PlainText)
+
+ self.kid = QProcess()
+ self.kid.setCommunication(QProcess.Stdout|QProcess.Stderr)
+ self.connect(self.kid,SIGNAL("processExited()"),self.slotProcessExited)
+ self.connect(self.kid,SIGNAL("readyReadStdout()"),self.slotReadyReadStdout)
+ self.connect(self.kid,SIGNAL("readyReadStderr()"),self.slotReadyReadStderr)
+
+ ########################################################################
+ def run(self,argslist):
+ self.kid.clearArguments()
+ for arg in argslist:
+ self.kid.addArgument(arg)
+ self.output = ""
+ self.outputtextview.setText(self.output)
+ self.bootstraptimer = self.startTimer(0)
+ self.running = True
+ self.enableButtonOK(False)
+ self.exec_loop()
+
+ ########################################################################
+ def timerEvent(self,timer):
+ self.killTimer(self.bootstraptimer)
+
+ # Create a slightly new environment where TERM is vt100
+ new_env = QStringList()
+ for key in os.environ:
+ if key=="TERM":
+ new_env.append("TERM=vt100")
+ else:
+ new_env.append(key + "=" + os.environ[key])
+
+ self.kid.launch("",new_env)
+
+ ########################################################################
+ def setHeading(self,heading):
+ self.headinglabel.setText(heading)
+
+ ########################################################################
+ def slotOKButton(self):
+ self.accept()
+
+ ########################################################################
+ def slotReadyReadStdout(self):
+ # Remove the colors used by some programs.
+ # FIXME: this probably isn't neccessary anymore.
+ uncolor = lambda text: re.compile('\\x1b\[[0-9]+;01m').sub("", \
+ re.compile('\\x1b\[0m').sub("", re.compile('\\033\[1;[0-9]+m').sub("", \
+ re.compile('\\033\[0m').sub("", text))))
+ self.output += uncolor(unicode(self.kid.readStdout()))
+ self.outputtextview.setText(self.output)
+ self.outputtextview.ensureVisible(0,self.outputtextview.contentsHeight())
+
+ ########################################################################
+ def slotReadyReadStderr(self):
+ self.output += unicode(self.kid.readStderr())
+ self.outputtextview.setText(self.output)
+ self.outputtextview.ensureVisible(0,self.outputtextview.contentsHeight())
+
+ ########################################################################
+ def slotProcessExited(self):
+ self.running = False
+ self.enableButtonOK(True)
+
+############################################################################
+# Factory function for KControl
+def create_serviceconfig(parent,name):
+ global kapp
+ kapp = KApplication.kApplication()
+ return SysVInitApp(parent, name)
+
+############################################################################
+def MakeAboutData():
+ aboutdata = KAboutData("guidance",programname,version, \
+ "Services Configuration Tool", KAboutData.License_GPL, \
+ "Copyright (C) 2003-2007 Simon Edwards", \
+ "Thanks go to Phil Thompson, Jim Bublitz and David Boddie.")
+ aboutdata.addAuthor("Simon Edwards","Developer","[email protected]","http://www.simonzone.com/software/")
+ aboutdata.addAuthor("Sebastian Kügler","Developer","[email protected]","http://vizZzion.org");
+ return aboutdata
+
+if standalone:
+ aboutdata = MakeAboutData()
+ KCmdLineArgs.init(sys.argv,aboutdata)
+
+ kapp = KApplication()
+ sysvapp = SysVInitApp()
+ sysvapp.exec_loop()