#!/usr/bin/perl -w
# $Revision: 1.17 $
# $Date: 2007-03-10 22:30:11 $
# Luis Mondesi < lemsx1@gmail.com >
#
# DESCRIPTION: Advanced drop-in replacement for "service" command line utility found on Redhat systems. Use to start/stop or removes services in Linux. Works on all Linux distributions.
# USAGE: service <start|stop|restart|remove|reload|status|on|off> <service1 [service2 ... serviceN]>
# LICENSE: GPL
# BUGS:
# - A service cannot be called: start, stop, restart, remove, reload, off, on, status
# - script will do first action found in list (start stop reload...) all other actions are removed
=pod
=head1 NAME
service - service script for starting, stoping adding and removing services in Linux
=head1 SYNOPSIS
B<service> [-v,--version]
[-D,--debug]
[-h,--help]
<[--list-all] |
[--list-running] |
[--list-runlevel] |
[--list-on] |
[--list-off]> | <start|stop|restart|reset|remove|off|on|reload>
<service1 service2 ... serviceN>
=head1 EXAMPLES
# new style like:
service stop apache2 squid gnump3d shorewall
# or classic Redhat style:
service apache2 stop
service off fingerd telnetd wu-ftpd
service --list-all
service --list-running
service --list-runlevel # is the same as service --list-on --list-off
=head1 DESCRIPTION
This script is used for sending commands to a given service or to remove/add a service to the SysV style startup script directories. This script should work well for all Linux distributions. Tested on: Redhat/Fedora, SuSE, Ubuntu and Debian.
This is a drop-in replacement for the "service" command line utility on Redhat-based systems.
Adding more distributions is as simple as:
* take the distribution id from the command: lsb_release -i
* add the distribution to the %distros HASH
=cut
use strict;
use File::Basename qw/ basename /;
$|++;
$ENV{'PATH'} .= ":/sbin:/usr/sbin";
my $revision = '1.17'; # version
# service is the template for the command to execute
# /etc/init.d/service_name action
my %redhat_commands = (
'on' => 'chkconfig %s on',
'off' => 'chkconfig %s off',
'service' => '/etc/init.d/%s %s'
);
my %debian_commands = (
'on' => 'update-rc.d %s defaults',
'off' => 'update-rc.d -f %s remove',
'service' => '/etc/init.d/%s %s'
);
# now assign commands accordingly
# NOTE: key 1 is output of lsb_release -i
my %distros = (
'redhat' => \%redhat_commands,
'suselinux' => \%redhat_commands,
'fedora' => \%redhat_commands,
'fedoracore' => \%redhat_commands,
'debian' => \%debian_commands,
'ubuntu' => \%debian_commands,
);
#---------------------------------------------------------------------------#
# standard Perl modules
use Getopt::Long;
Getopt::Long::Configure('bundling');
use File::Spec::Functions; # abs2rel() and other dir/filename specific
=pod
=head1 OPTIONS
=over 8
=item -v,--version
prints version and exits
=item -D,--debug
enables debug mode
=item -h,--help
prints this help and exits
=item start,stop,restart,status,reload
sends these arguments to the given services
=item remove,off
removes the initrc scripts from the system using chkconfig or update-rc.d depending on the distribution
=item add,on
adds the initrc scripts to the system using chkconfig or update-rc.d depending on the distribution
=item reset
same as remove and add. This ensures that the proper links are created for a given run level
=item --list-all
List all services in /etc/init.d/*
=item --list-on
List all services set to start in our current run level
=item --list-off
List all services set to stop in our current run level
=item --list-running
List all processes running which have startup scripts in /etc/init.d/*
=item --list-runlevel
Same as --list-off --list-on
=cut
# Args:
my $PVERSION = 0;
my $HELP = 0;
my $DEBUG = 0;
my $ACTION = undef;
my $LIST_ALL = undef;
my $LIST_RUN = undef;
my $LIST_LEV = undef;
my $LIST_ON = undef;
my $LIST_OFF = undef;
# get options
GetOptions(
# flags
'v|version' => \$PVERSION,
'h|help' => \$HELP,
'D|debug' => \$DEBUG,
'list-all' => \$LIST_ALL,
'list-running' => \$LIST_RUN,
'list-runlevel' => sub { $LIST_LEV++; $LIST_ON++; $LIST_OFF++; },
'list-on' => sub { $LIST_LEV++; $LIST_ON++; },
'list-off' => sub { $LIST_LEV++; $LIST_OFF++; },
);# and $ACTION = shift;
if ($HELP)
{
use Pod::Text;
my $parser = Pod::Text->new(sentence => 0, width => 78);
$parser->parse_from_file($0, \*STDOUT);
exit 0;
}
if ($PVERSION) { print STDOUT ($revision, "
"); exit 0; }
# what distro are we?
open(LSB,"</etc/lsb-release") or open(LSB,"lsb_release -i|")
or die("Cannot get distributor id ",$!,"
");
my $release_info = undef;
while(<LSB>)
{
chomp;
if (/\s*distrib(utor)*[[:blank:]\_]*ID\s*[\:\=]{1}\s*([^[:blank:]]+)/i)
{
$release_info = lc($2);
$release_info =~ s/\s+//;
last;
}
}
print "Distributor ID: ",$release_info, "
" if ($DEBUG);
if ($DEBUG)
{
use Data::Dumper;
print Dumper(%distros);
}
# main()
if ($LIST_ALL or $LIST_RUN or $LIST_LEV or $LIST_ON or $LIST_OFF)
{
if ($LIST_ALL)
{
print "All services
";
# TODO use distro specific
my $DIR = "/etc/init.d"; # LSB
# TODO use distro specific
opendir(DIR, $DIR) or die("Cannot open $DIR. $!
");
while (my $file = readdir(DIR))
{
next if (-d "$DIR/$file");
next if (!-x "$DIR/$file");
next if ($file =~ /\.dpkg-old$/);
next if ($file =~ /\.rpmsave$/);
print "$DIR/", $file, "
";
}
}
if ($LIST_RUN)
{
print "All services running
";
# TODO use distro specific
my $DIR = "/etc/init.d"; # LSB
# TODO use distro specific
# TODO attempt to find some names by reading the initrc files...
# will take a lot of time to finish.
open(PROC,"ps ax --format \%c | sort | uniq|") or die($!,"
");
while (my $file = <PROC>)
{
chomp($file);
next if (!-x "$DIR/$file");
print "$DIR/$file
";
}
}
if ($LIST_LEV)
{
my $RUNLEVEL = undef;
# parse inittab
open(INITTAB, "</etc/inittab") or die($! . "
");
while (<INITTAB>)
{
if (/^\s*id\s*:\s*(\d+)\s*:\s*initdefault\s*:\s*$/)
{
$RUNLEVEL = $1;
last;
}
}
print "All services for this runlevel [$RUNLEVEL]
";
my $DIR = "/etc/rc$RUNLEVEL.d"; # LSB
# TODO use distro specific
opendir(DIR, $DIR) or die("Cannot open $DIR. $!
");
print '-'x64,"
";
print sprintf("\%s\t%32s\t%s
","Pri","Service Name","Status");
print '-'x64,"
";
while (my $file = readdir(DIR))
{
next if (-d "$DIR/$file");
next if (!-x "$DIR/$file");
next if ($file =~ /\.dpkg-old$/);
next if ($file =~ /\.rpmsave$/);
if ($LIST_ON and $file =~ /^S(\d+)([^[:blank:]]+)/)
{
print sprintf("\%s\t%32s\t%s
",$1,$2,"On");
}
elsif ($LIST_OFF and $file =~ /^K(\d+)([^[:blank:]]+)/)
{
print sprintf("\%s\t%32s\t%s
",$1,$2,"Off");
}
}
}
exit(0);
}
my $_args = join(",",@ARGV);
if ( $_args =~ /(\bstart\b|\bstop\b|\breload\b|\bstatus\b|\boff\b|\bon\b|\breset\b|\bremove\b|\badd\b)/)
{
$ACTION = $1;
}
# sanity checks before action is taken
if (not defined($ACTION)
or $ACTION !~ /^(start|stop|reload|status|off|on|reset|remove|add)$/)
{
use Pod::Usage;
pod2usage(1);
exit 0;
}
# remove all other actions from $_args
$_args =~ s/,?(\bstart\b|\bstop\b|\breload\b|\bstatus\b|\boff\b|\bon\b|\breset\b|\bremove\b|\badd\b)//g;
print "Remaining arguments: ",$_args,"
" if ($DEBUG);
my $KEY = $ACTION;
# deal with synonyms
# we allow as our first argument: remove, off, on, add, and all the service conventions: start, stop, restart, reload, condrestart etc..
if ($ACTION =~ /start|stop|reload|status/)
{
$KEY = "service";
}
elsif ($ACTION =~ /remove/)
{
$KEY = "off";
}
elsif ($ACTION =~ /add/)
{
$KEY = "on";
}
foreach (split(",",$_args))
{
print "_argument: '$_'
" if ($DEBUG);
next if (/^[[:blank:]]*$/);
if ($ACTION =~ /\breset\b/
) # reset means to make sure the service has the proper initrc links
{
my $error = 0;
system(sprintf($distros{$release_info}->{'off'}, basename($_), $ACTION));
$error++ if ($? != 0);
system(sprintf($distros{$release_info}->{'on'}, basename($_), $ACTION));
$error++ if ($? != 0);
if (!$error)
{
print "$_ service was reset
";
}
else
{
print "ERROR: $_ service was not reset
";
}
next;
}
elsif ($ACTION =~ /\bremove\b/ or $ACTION =~ /\boff\b/)
{
# when removing a service we turn it off as well
print "Turning off service $_
" if ($DEBUG);
system(sprintf($distros{$release_info}->{$KEY}, basename($_), 'stop'));
}
# TODO sanity check: see if command exists
# my $service_cmd = sprintf($distros{$release_info}->{$KEY}, basename($_), $ACTION);
# $service_cmd =~ s/^(\S+).*$/$1/;
# if ( ! -x $service_cmd )
# {
# print STDERR "No such command $service_cmd
";
# next;
# }
my $system_cmd = sprintf($distros{$release_info}->{$KEY}, basename($_), $ACTION);
print "Executing cmd: $system_cmd
" if ($DEBUG);
system($system_cmd);
if ($? != 0)
{
print "ERROR: $_ service was not $ACTION" . "ed
";
}
}
=pod
=back
=head1 BUGS
* Service names cannot be any of: start stop reload status off on reset remove add
* First action found in list (start stop reload...) will be executed and all other actions are removed
=head1 AUTHORS
Luis Mondesi <lemsx1@gmail.com>
=cut
Advertisement