December 2005
There is a program called service which can be used on multiple
UNIX systems to control Operating System services. One of the problems
faced in the design of the service utility was the path to where
system startup scripts
(or rc scripts) resided. This series
examines how using sed can easily mitigate multi-platform
scripting problems. The first part will look at how the utility program
works.
service UtilityThe service utility is a generic front end to UNIX init scripts. Writing a service clone for a particular distribution, slackware for instance, is pretty simple:
/etc/rc.d/rc.$1 $2
In order to make a multi-UNIX version of service a bit more is required, also, the one line shell script does not have the capability of listing services.
A Perl version was written instead, initially for the Slackware Linux distribution. The first parts of the program to examine are the global variables and the input parser:
$SIG{'INT' } = 'interrupt'; $SIG{'QUIT'} = 'interrupt';
$SIG{'HUP' } = 'interrupt'; $SIG{'TRAP'} = 'interrupt';
$SIG{'ABRT'} = 'interrupt'; $SIG{'STOP'} = 'interrupt';
$RC_CTL="/etc/rc.d/rc.";
$PROG = "service";
$AUTHOR = "Fubu ";
$VERSION = "0.5";
$vflag = 0; # Verbosity flag
for ($i = 0; $i < @ARGV; $i++) {
if ($ARGV[$i] =~ /^-version|-V$/) {
printinfo();
exit 0;
} elsif ($ARGV[$i] =~ /^-verbose|-v$/) {
$vflag = 1;
} elsif ($ARGV[$i] eq '-usage') {
usage();
exit 0;
} elsif ($ARGV[$i] eq '-show') {
listall($RC_CTL);
exit 0;
} elsif ($ARGV[$i] =~ /^restart|start|stop$/) {
$OPERATION = $ARGV[$i];
} else {
$SERVICE = $ARGV[$i];
}
}
Most of the code above is pretty self explanatory. The parser looks for one of 5 possible choices:
vflag) to 1.command: perform command
on the given service name.
Note the $RC_CTLvariable which points to
where the rc scripts reside. The logic uses fall through to fetch the
actual service name. The code also is looking for specific commands
which are detailed later.
There are a few helper functions associated with the main part of the program.
sub interrupt {
print "caught @_ exiting\n";
die;
}
sub listall {
my $rcfiles = shift;
system("ls $rcfiles*");
}
sub printinfo {
print "$PROG $VERSION by $AUTHOR\n";
}
sub usage {
print "Usage: $PROG [option]\n";
print "Usage: $PROG [option]\n";
print "Options:\n";
print " -show List the services and exit\n";
print " -usage Print usage message and exit\n";
print " -verbose -v Run in verbose mode\n";
print " -version -V Print version and author information\n";
print "Examples:\n";
print " Stop then start dhcpd\n";
print " $PROG dhcpd stop\n";
print " $PROG dhcpd start -v\n";
print " Restart NFSD\n";
print " $PROG nfsd restart\n";
}
The above functions do the following:
interrupt(): A routine to catch interrupts.listall: A call to list the files in the rc directory.
The function was made so adding better
formatting later would be easier.printinfo: Print out the version and author information.usage: A simple help print.
With the helpers out of the way, the rest of the main can
be examined along with the key sub routine of the portable
service utility.
The remaining part of the main program checks to see if there
is a specified operation, calls the passthru() subroutine and
exits.
if ($SERVICE) {
if ($OPERATION !~ /^restart|start|stop$/) {
print "Error: Illegal or no operation specified\n";
usage();
exit 1;
}
}
passthru ($SERVICE, $OPERATION, $RC_CTL); # Send it through
exit 0;
The key subroutine is passthru() which passes through
the operation to the specified rc script. The passthru()
subroutine also checks to see if $vflag is set.
sub passthru {
my ($service, $operation, $rcstring) = @_;
if ($vflag) {
print "going to $operation $service\n";
print "running $rcstring$service $operation\n";
}
if (system("$rcstring$service $operation") == 0) {
if ($do_version) {
print "$operation of $service successful\n";
}
} else {
print "$operation of $service returned a non zero status\n";
}
}
In addition to just performing (or attempting to perform) the operation, the service utility also checks the return value for success.
With the operation of the program now explained, the next text will look at some of the ways to make it portable among different UNIXs and the method that was employed.