a chkconfig clone that works with upstart
Eric S. Raymond
esr at thyrsus.com
Wed May 30 19:47:31 BST 2007
Enclosed please find code and man page of a bash+sed script that uses
update-rc.d to emulate the behavior of the IRIX/Red-Hat chkconfig tool
Try a few invocations with --dryrun to see exactly what it does --
that switch makes it print the generated commands rather than
executing them.
Upconversion of the chkconfig.8 page to XML-DocBook is available on
request, if you prefer that format.
This will be useful because some third-party packages (especially in
the Red Hat world) rely on chkconfig in their installation scripts.
I wrote this because as a Red Hat refugee I missed chkconfig.
I'm contributing this to upstart for the usual reason, which is that
I'd rather not have to maintain and separately distribute it myself.
Also, BTW, because I think upstart is pretty cool. I've been
administering boxes with System V runlevels for literally decades,
and they were OK, but I'm not sorry to see an elegant new approach
displace them. Congrats on getting picked up by Debian and Ubuntu,
and best wishes for total world domination.
--
<a href="http://www.catb.org/~esr/">Eric S. Raymond</a>
-------------- next part --------------
#!/bin/bash
#
# chkconfig -- list, enable, and disable system services by runlevel
#
# By Eric S. Raymond, May 2007. Released under BSD license.
#
# This is an emulation of the IRIX chkconfig tool to run over Debian/Ubuntu
# (it calls update-rc.d, the native tool, to do the actual work). Also
# requires sed(1) and runlevel(8).
#
# I wrote this because (a) a significant number of third-party packages
# depend on chkconfig, and (b) as an ex-Red-Hat user, I'm used to this
# interface and want it handy.
# Things one might want to configure
RUNLEVELS="1 2 3 4 5 6 S"
ONOFFDEFAULT=2345
START=20
STOP=80
currentlevel () {
# Get the current run level
set -- `runlevel`
echo $2
}
explode () {
# Explode a sequence of digits into a space-separated list
echo "$1" | sed -e 's/[0-9A-Z]/ &/g'
}
checkroot(){
# Privileged operation coming up, require caller to be superuser.
if [ "$prefix" != echo -a "$EUID" -ne 0 ]
then
echo "chkconfig: you must be root to use this feature."
exit 1
fi
}
parse_init_header() {
# Parse an LSB INIT INFO header for default start and stop levels.
{
state=0
startlevels=''
stoplevels=''
status=1
while read line
do
#echo "Looking at" $line
if expr "$line" : ".*BEGIN INIT INFO" >/dev/null
then
state=1
status=0
continue
fi
if expr "$line" : ".*END INIT INFO" >/dev/null
then
state=0
continue
fi
if [ $state = 1 ]
then
t1=`expr "$line" : '.*Default-Start:[ \t]*\(.*\)'`
if [ "$t1" != "" ]
then
startlevels="$t1"
fi
t2=`expr "$line" : '.*Default-Stop:[ \t]*\(.*\)'`
if [ "$t2" != "" ]
then
stoplevels="$t2"
fi
fi
done
} </etc/init.d/$1
return $status
}
clearlevels () {
# Clear init scripts for $name at runlevels $*
name=$1
shift
for level in $*; do
$prefix rm -f /etc/rc[${level}].d/[SK][0-9][0-9]$name
done
}
chkconfig_list() {
if [ $# == 0 ]
then
for service in /etc/init.d/*
do
# Ignoring files that aren't executable is a cheap way
# to skip README and other similar cruft like .dpkg-dist.
if [ -x $service ]
then
chkconfig_list `basename $service`
fi
done
else
printf "%-16s\t" $1
for i in $RUNLEVELS
do
status="none" # Other chkconfigs report 'off' here
if [ -f /etc/rc$i.d/K[0-9][0-9]$1 ]
then
status=off
fi
if [ -f /etc/rc$i.d/S[0-9][0-9]$1 ]
then
status=on
fi
printf "$i:$status\t"
done
echo ""
fi
}
chkconfig_add() {
if parse_init_header $1
then
exec $prefix update-rc.d $1 start $START $startlevels . stop $STOP $stoplevels .
else
echo "chkconfig: can't --add a service with no INIT INFO header."
exit 1
fi
}
chkconfig_del() {
exec $prefix update-rc.d -f $1 remove
}
chkconfig_level() {
levels=$1
name=$2
directive=$3
priority=$4
if [ "$name" = "" ]
then
echo "chkconfig: requires a service name"
exit 1
fi
# Query the state of a service (doesn't require root).
if [ "$directive" = "" ]
then
levels=${levels:-`currentlevel`}
if [ -f /etc/rc${level}.d/S[0-9][0-9]${1} ]
then
exit 0
else
exit 1
fi
fi
# Turn on, turn off, or reset a service.
checkroot
case $directive in
on) levels=${levels:-$ONOFFDEFAULT}
clearlevels $name $levels
$prefix update-rc.d $name start ${priority:-$START} `explode $levels` .
;;
off) levels=${levels:-$ONOFFDEFAULT}
clearlevels $name $levels
$prefix update-rc.d $name stop ${priority:-$STOP} `explode $levels` .
;;
reset) if parse_init_header $name
then
startlevels=${startlevels:-$RESETDEFAULT}
stoplevels=${stoplevels:-$RESETDEFAULT}
clearlevels $name ${startlevels}${stoplevels}
$prefix update-rc.d $name \
start ${priority:-$START} `explode $startlevels` . \
stop ${priority:-$STOP} `explode $stoplevels` .
else
echo "chkconfig: no INIT INFO header found"
exit 1
fi
;;
*) echo "chkconfig: unknown operation '$directive'"; exit 1 ;;
esac
}
if [ "$#" = 0 ]
then
echo -en "usage: chkconfig --list [name]\n\
chkconfig [--dryrun] --add <name>\n\
chkconfig [--dryrun] --del <name>\n\
chkconfig [--dryrun] [--level <levels>] <name> [<on|off|reset>] [priority]\n"
exit 0
fi
prefix=""
if [ "$1" = "--dryrun" ]
then
prefix=echo
shift
fi
case $1 in
--list) shift; chkconfig_list $1 ;;
--add) checkroot; shift; chkconfig_add $1 ;;
--del) checkroot; shift; chkconfig_del $1 ;;
--level) shift; chkconfig_level $* ;;
*) chkconfig_level "" $* ;;
esac
-------------- next part --------------
.TH CHKCONFIG 8 "Wed Oct 8 1997"
.UC 4
.SH NAME
chkconfig \- updates and queries runlevel information for system services
.SH SYNOPSIS
\fBchkconfig\fR \fB--list\fR [\fIname\fR]
.br
\fBchkconfig\fR [\fB--dryrun\fR] \fB--add\fR \fIname\fR
.br
\fBchkconfig\fR [\fB--dryrun\fR] \fB--del\fR \fIname\fR
.br
\fBchkconfig\fR [\fB--dryrun\fR] [\fB--level\fR \fIlevels\fR]
\fIname\fR {\fBon\fR|\fBoff\fR||\fBreset\fR\fR} [priority]
.br
\fBchkconfig\fR [\fB--level\fR \fIlevels\fR] \fIname\fR
.br
.SH DESCRIPTION
\fBchkconfig\fR provides a simple command-line tool for maintaining the
/etc/rc[0-6].d directory hierarchy by relieving system administrators of
the task of directly manipulating the numerous symbolic links in those
directories.
This implementation of \fBchkconfig\fR was inspired by the
\fBchkconfig\fR command present in the IRIX operating system and Red
Hat Linux. Rather than maintaining configuration information outside
of the /etc/rc[0-6].d hierarchy, however, this version uses
update-rc.d to directly manages the symlinks in /etc/rc[0-6].d. This
leaves all of the configuration information regarding what services
\fIinit\fR starts in a single location.
\fBchkconfig\fR has five distinct functions: adding new services for
management, removing services from management, listing the current
startup information for services, changing the startup information
for services, and checking the startup state of a particular service.
When \fBchkconfig\fR is run without any options, it displays usage
information. If only a service name is given, it checks to see if the
service is configured to be started in the current runlevel. If it is,
\fBchkconfig\fR returns true (0); otherwise it returns false (1). The
\fB--level\fR option may be used to have \fBchkconfig\fR query an
alternative runlevel rather than the current one.
If one of \fBon\fR, \fBoff\fR, or \fRreset\fR is specified after the
service name, \fBchkconfig\fR changes the startup information for the
specified service. The \fBon\fR and \fBoff\fR flags cause the service
to be started or stopped, respectively, in the runlevels being changed.
The \fBreset\fR flag resets the startup information for the service to
whatever is specified in the init script in question.
The optional priority argument, used only if the operation is \fBon\fR
or\fBoff\fR, sets the start or stop priority of the link.
By default, the \fBon\fR and \fBoff\fR options affect only runlevels 2, 3, 4,
and 5, while \fBreset\fR affects all of the runlevels. The \fB--level\fR
option may be used to specify which runlevels are affected.
Note that for every service, each runlevel has either a start script
or a stop script. When switching runlevels, init will not re-start
an already-started service, and will not re-stop a service that is
not running.
.SH OPTIONS
.TP
\fB--dryrun\fR
Show what the rest of the command line would do, but don't actually do it.
.TP
\fB-\-level \fIlevels\fR
Specifies the run levels an operation should pertain to. It is given as
a string of numbers from 0 to 7. For example, \fB--level 35\fR specifies
runlevels 3 and 5.
.TP
\fB-\-add\fR \fIname\fR
This option adds a new service for management by \fBchkconfig\fR.
When a new service is added, \fBchkconfig\fR ensures that the service
has either a start or a kill entry in every runlevel. If any runlevel
is missing such an entry, \fBchkconfig\fR creates the appropriate entry
as specified by the default values in the init script. Note that
default entries in LSB-delimited 'INIT INFO' sections take precedence
over the default runlevels in the initscript.
.TP
\fB-\-del\fR \fIname\fR
The service is removed from \fBchkconfig\fR management, and any symbolic
links in /etc/rc[0-6].d which pertain to it are removed.
Note that future package installs for this service may run
\fBchkconfig \-\-add\fR, which will re-add such links. To disable a
service, run \fBchkconfig \fIname\fR \fBoff\fR.
.TP
\fB-\-list\fR \fIname\fR
This option lists all of the services which \fBchkconfig\fR knows about,
and whether they are stopped or started in each runlevel. If \fIname\fR
is specified, information in only display about service \fIname\fR.
.SH LIMITATIONS
Red Hat \fBchkconfig\fR could manage xinetd scripts by manipulating
xinetd.d configuration files (only the \fBon\fR, \fBoff\fR, and
\fB-\-list\fR commands were supported for xinetd.d services). This
Debian/Ubuntu port doesn't do that.
Red Hat \fBchkconfig\fR had a special syntax for embedding
metainformation in init-script headers which this script doesn't
support. Instead, this script interprets LSB INIT INFO blocks, which are
functionally equivalent except that they don't specify priority
numbers. To compensate, this implementation adds the priority argument;
if that is not given, the script uses a fixed startup priority
of 20 and a fixed stop priority of 80, relying on processing of
the dependencies in the LSIB INIT INFO blocks to get ordering right.
On the other hand, the IRIX and Red Hat version didn't have --dryrun.
.SH SEE ALSO
.IR init (8).
.IR runlevel (8).
.SH AUTHOR
.nf
Eric S. Raymond. Portions of the manual page are from the Red Hat
implementation by Erik Troan <ewt at redhat.com>.
.fi
More information about the upstart-devel
mailing list