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/serviceconfig/serviceconfig.py

1482 lines
60 KiB

#!/usr/bin/python
# -*- coding: UTF-8 -*-
###########################################################################
# serviceconfig.py - description #
# ------------------------------ #
# begin : Wed Apr 30 2003 #
# copyright : (C) 2003-2006 by Simon Edwards #
# email : simon@simonzone.com #
# #
###########################################################################
# #
# 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 *
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
TDEGlobal.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")
TDEGlobal.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 TDEAboutData 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 = TDEApplication.kApplication()
return SysVInitApp(parent, name)
############################################################################
def MakeAboutData():
aboutdata = TDEAboutData("guidance",programname,version, \
"Services Configuration Tool", TDEAboutData.License_GPL, \
"Copyright (C) 2003-2007 Simon Edwards", \
"Thanks go to Phil Thompson, Jim Bublitz and David Boddie.")
aboutdata.addAuthor("Simon Edwards","Developer","simon@simonzone.com","http://www.simonzone.com/software/")
aboutdata.addAuthor("Sebastian Kügler","Developer","sebas@kde.nl","http://vizZzion.org");
return aboutdata
if standalone:
aboutdata = MakeAboutData()
TDECmdLineArgs.init(sys.argv,aboutdata)
kapp = TDEApplication()
sysvapp = SysVInitApp()
sysvapp.exec_loop()