#!/usr/bin/env perl #-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- # Functions for manipulating system services, like daemons and network. # # Copyright (C) 2002 Ximian, Inc. # # Authors: Carlos Garnacho Parro , # Hans Petter Jansson , # Arturo Espinosa # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Library General Public License as published # by the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU Library General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. $rcd_path; $initd_path; $relative_path; $SCRIPTSDIR = "@scriptsdir@"; $FILESDIR = "@filesdir@"; if ($SCRIPTSDIR =~ /^@scriptsdir[@]/) { $FILESDIR = "files"; $SCRIPTSDIR = "."; $DOTIN = ".in"; } use File::Copy; require "$SCRIPTSDIR/file.pl$DOTIN"; require "$SCRIPTSDIR/report.pl$DOTIN"; require "$SCRIPTSDIR/service-list.pl$DOTIN"; # Where is the SysV subsystem installed? sub gst_service_sysv_get_paths { my %dist_map = ( # gst_dist => [rc.X dirs location, init.d scripts location] "redhat-5.2" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "redhat-6.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "redhat-6.1" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "redhat-6.2" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "redhat-7.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "redhat-7.1" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "redhat-7.2" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "redhat-7.3" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "redhat-8.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "redhat-9" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "openna-1.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "mandrake-7.1" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "mandrake-7.2" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "mandrake-9.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "mandrake-9.1" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "debian-2.2" => ["$gst_prefix/etc", "$gst_prefix/etc/init.d", "../init.d"], "debian-3.0" => ["$gst_prefix/etc", "$gst_prefix/etc/init.d", "../init.d"], "debian-sarge" => ["$gst_prefix/etc", "$gst_prefix/etc/init.d", "../init.d"], "suse-7.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d", "../"], "turbolinux-7.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "pld-1.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"] ); my $res; $res = $dist_map{$gst_dist}; &gst_report ("service_sysv_unsupported", $gst_dist) if ($res eq undef); return @$res; } # Those runlevels that are usually used. Maybe we should add # the current running runlevel, using the "runlevel" command. sub gst_service_sysv_get_runlevels { my %dist_map = ( "redhat-5.2" => [3, 5], "redhat-6.0" => [3, 5], "redhat-6.1" => [3, 5], "redhat-6.2" => [3, 5], "redhat-7.0" => [3, 5], "redhat-7.1" => [3, 5], "redhat-7.2" => [3, 5], "redhat-7.3" => [3, 5], "redhat-8.0" => [3, 5], "redhat-9" => [3, 5], "openna-1.0" => [3, 5], "mandrake-7.1" => [3, 5], "mandrake-7.2" => [3, 5], "mandrake-9.0" => [3, 5], "mandrake-9.1" => [3, 5], "debian-2.2" => [2, 3], "debian-3.0" => [2, 3], "debian-sarge" => [2, 3], "suse-7.0" => [3, 5], "turbolinux-7.0" => [3, 5], "pld-1.0" => [3, 5] ); my $res; $res = $dist_map{$gst_dist}; &gst_report ("service_sysv_unsupported", $gst_dist) if ($res eq undef); return @$res; } sub gst_service_get_verbose_runlevels { my (%dist_map, %runlevels, $desc, $distro); %dist_map = ( "redhat-5.2" => "redhat-5.2", "redhat-6.0" => "redhat-5.2", "redhat-6.1" => "redhat-5.2", "redhat-6.2" => "redhat-5.2", "redhat-7.0" => "redhat-5.2", "redhat-7.1" => "redhat-5.2", "redhat-7.2" => "redhat-5.2", "redhat-7.3" => "redhat-5.2", "redhat-8.0" => "redhat-5.2", "redhat-9" => "redhat-5.2", "openna-1.0" => "redhat-5.2", "mandrake-7.1" => "redhat-5.2", "mandrake-7.2" => "redhat-5.2", "mandrake-9.0" => "redhat-5.2", "mandrake-9.1" => "redhat-5.2", "debian-2.2" => "debian-2.2", "debian-3.0" => "debian-2.2", "debian-sarge" => "debian-2.2", "suse-7.0" => "redhat-5.2", "turbolinux-7.0" => "redhat-5.2", "pld-1.0" => "redhat-5.2", ); %runlevels= ( "redhat-5.2" => {"0" => "Halting the system", "3" => "Text mode", "5" => "Graphical mode", "6" => "Rebooting the system" }, "debian-2.2" => {"0" => "Halting the system", "2" => "Graphical mode", "3" => "Text mode", "6" => "Rebooting the system" } ); $distro = $dist_map{$gst_dist}; $desc = $runlevels{$distro}; return $runlevels{$distro}; } # --- Plain process utilities --- # # Get owner (login) of named process. # FIXME: Works only on modern Linux, probably. # FIXME: Need a version of this that examines all instances of process name. sub gst_service_proc_get_owner { my ($service) = @_; my ($pid, $owner); my $res = 0; &gst_report_enter (); # Stolen from RedHat's /etc/rc.d/init.d/functions:status # FIXME: Portable to other UNIXen? $pid = &gst_file_run_backtick ("pidof -o %PPID -x $service"); chomp $pid; if ($pid) { $owner = &gst_file_run_backtick ("ps --no-headers -p $pid -o user"); } else { $owner = undef; } &gst_report_leave (); return $owner; } # FIXME: Need a version of this that stops all instances of process name. sub gst_service_proc_stop { my ($service) = @_; my ($pid); my $res = 0; &gst_report_enter (); # Stolen from RedHat's /etc/rc.d/init.d/functions:status # FIXME: Portable to other UNIXen? $pid = &gst_file_run_backtick ("pidof -o %PPID -x $service"); chomp $pid; if ($pid) { kill "SIGTERM", $pid; $res = 1; } &gst_report_leave (); return $res; } # FIXME: Need a version of this that starts all instances of process name. sub gst_service_proc_start { my ($cmd, $user) = @_; my ($fqsu, $fqcmd); $fqcmd = &gst_file_get_cmd_path ($cmd); $fqsu = &gst_file_get_cmd_path ("su"); # Can't use gst_file_run_bg here, since it clobbers the quotes. system ("$fqsu -c \"$fqcmd &\" $user >/dev/null 2>/dev/null"); } sub gst_service_sysv_installed { my ($service) = @_; my ($res, $rcd_path, $initd_path); &gst_report_enter (); ($rcd_path, $initd_path) = &gst_service_sysv_get_paths (); $res = 1; if (! -f "$initd_path/$service") { $res = 0; &gst_report ("service_sysv_not_found", $service); } &gst_report_leave (); return $res; } sub gst_service_sysv_list_any_installed { my @service = @_; my $res; $res = 0; foreach $serv (@service) { if (gst_service_sysv_installed ($serv)) { $res = 1; } } return $res; } sub gst_service_sysv_list_dir { my ($path) = @_; my ($service, @services); foreach $service (<$path/*>) { if (-x $service) { $service =~ s/.*\///; push @services, $service; } } return \@services; } sub gst_service_sysv_list_available { my ($rcd_path, $initd_path); ($rcd_path, $initd_path) = &gst_service_sysv_get_paths (); return &gst_service_sysv_list_dir ($initd_path); } # Return 1 or 0: is the service running? # Depends on the rc script to support the "status" arg. # Maybe we should do something more portable. sub gst_service_sysv_get_status { my ($service) = @_; my ($rc_path, $initd_path, $res); my ($pid); &gst_report_enter (); # Stolen from RedHat's /etc/rc.d/init.d/functions:status # FIXME: portable to other UNIXES? $pid = &gst_file_run_backtick ("pidof -o %PPID -x $service"); chomp $pid; if ($pid) { $res = 1; &gst_report ("service_status_running", $service); } else { $res = 0; &gst_report ("service_status_stopped", $service); } # ($rcd_path, $initd_path) = &gst_service_sysv_get_paths (); # $res = 0; # # if (-f "$initd_path/$service") # { # $res = &gst_file_run ("$initd_path/$service status")? 0 : 1; # &gst_report ("service_status_running", $service) if $res; # &gst_report ("service_status_stopped", $service) if !$res; # } &gst_report_leave (); return $res; } # If any of the passed services is running, return true. sub gst_service_sysv_get_status_any { my (@services) = @_; my $i; foreach $i (@services) { return 1 if &gst_service_sysv_get_status ($i); } return 0; } # Set start links and remove stop links at the usual runlevels. # Old start link is removed, in case the priority is different from $pri. sub gst_service_sysv_set_links_active { my ($pri, $service) = @_; foreach $runlevel (&gst_service_sysv_get_runlevels ()) { &gst_service_sysv_remove_link ($runlevel, $service); &gst_service_sysv_add_link ($runlevel, "S", $pri, $service); } } # Set stop links and remove start links at the usual runlevels. sub gst_service_sysv_set_links_inactive { my ($pri, $service) = @_; foreach $runlevel (&gst_service_sysv_get_runlevels ()) { &gst_service_sysv_remove_link ($runlevel, "$service"); &gst_service_sysv_add_link ($runlevel, "K", $pri, $service); } } # Set links for active/inactive service at the given priority. sub gst_service_sysv_set_links { my ($pri, $service, $active) = @_; if ($active) { &gst_service_sysv_set_links_active ($pri, $service); } else { &gst_service_sysv_set_links_inactive (100 - $pri, $service); } } # Run the init.d script for the corresponding service, with # arg, which can be "stop", "start" or "restart". sub gst_service_sysv_run_initd_script { my ($service, $arg) = @_; my ($rc_path, $initd_path); my $str; my %map = ("restart" => "restarted", "stop" => "stopped", "start" => "started"); &gst_report_enter (); if (!exists $map{$arg}) { &gst_report ("service_sysv_op_unk", $arg); &gst_report_leave (); return -1; } $str = $map{$arg}; ($rcd_path, $initd_path) = &gst_service_sysv_get_paths (); if (-f "$initd_path/$service") { if (!&gst_file_run ("$initd_path/$service $arg")) { &gst_report ("service_sysv_op_success", $service, $str); &gst_report_leave (); return 0; } } &gst_report ("service_sysv_op_failed", $service, $str); &gst_report_leave (); return -1; } # Start or stop the service, depending on $active. Set # links accordingly. $force makes this function use # start/stop only, without considerations for restart. # Not to be called from parse/replace tables, due to last $force # param: use the following two functions instead. sub gst_service_sysv_set_status_do { my ($priority, $service, $active, $force) = @_; my ($arg, $status); &gst_service_sysv_set_links ($priority, $service, $active); $status = &gst_service_sysv_get_status ($service); if ($status && !$force) { # if it's already active and you want it active, restart. $arg = $active? "restart" : "stop"; } else { # normal operation. $arg = $active? "start" : "stop"; } return &gst_service_sysv_run_initd_script ($service, $arg); } sub gst_service_sysv_set_status { my ($priority, $service, $active) = @_; return &gst_service_sysv_set_status_do ($priority, $service, $active, 0); } sub gst_service_sysv_force_status { my ($priority, $service, $active) = @_; return &gst_service_sysv_set_status_do ($priority, $service, $active, 1); } sub gst_service_sysv_install_script { my ($service, $file) = @_; my ($res, $rcd_path, $initd_path); ($rcd_path, $initd_path) = &gst_service_sysv_get_paths (); if (!copy ("$FILESDIR/$file", "$initd_path/$service")) { &gst_report ("file_copy_failed", "$FILESDIR/$file", "$initd_path/$service"); return -1; } chmod (0755, "$initd_path/$service"); return 0; } # THESE ARE THE FUNCTIONS WHICH EXTRACT THE CONFIGURATION FROM THE COMPUTER # we are going to extract the name of the script sub gst_service_sysv_get_service_name { my ($service) = @_; $service =~ s/$initd_path\///; return $service; } # This function gets the state of the service along the runlevels, # it also returns the average priority sub gst_service_sysv_get_runlevels_status { my ($service) = @_; my ($link); my ($runlevel, $action, $priority); my (@arr, @ret); my ($sum, $count); $sum = $count = 0; foreach $link (<$rcd_path/rc[0-6].d/[SK][0-9][0-9]$service>) { $link =~ s/$rcd_path\///; $link =~ /rc([0-6])\.d\/([SK])([0-9][0-9]).*/; ($runlevel,$action,$priority)=($1,$2,$3); if ($action eq "S") { push @arr, { "number" => $runlevel, "action" => "start" }; $sum += $priority; } elsif ($action eq "K") { push @arr, { "number" => $runlevel, "action" => "stop" }; $sum += (100 -$priority); } $count++; } return (undef,99) if (scalar(@arr) eq 0); push @ret, { "runlevel" => \@arr }; return (\@ret, int ($sum / $count)); } # We are going to extract the information of the service sub gst_service_sysv_get_service_info { my ($service) = @_; my ($script, $name, $description, @actions, @runlevels); my %hash; # We have to check if the service is executable return undef unless (-x $service); $script = &gst_service_sysv_get_service_name ($service); # We have to check out if the service is in the "forbidden" list return undef if (&gst_service_list_service_is_forbidden ($script)); ($name, $description) = &gst_service_list_get_info ($script); ($runlevels, $priority) = &gst_service_sysv_get_runlevels_status($script); $hash{"script"} = $script; $hash{"name"} = $name unless ($name eq undef); $hash{"description"} = $description unless ($description eq undef); $hash{"runlevels"} = $runlevels unless ($runlevels eq undef); $hash{"priority"} = $priority; return \%hash; } # This function gets an ordered array of the available services from a SysV system sub gst_service_sysv_get_services { my ($service); my (@arr,%ret); ($rcd_path, $initd_path) = &gst_service_sysv_get_paths (); foreach $service (<$initd_path/*>) { my (%hash); $hash = &gst_service_sysv_get_service_info ($service); if ($hash ne undef) { $ret{$service} = $hash; } } return \%ret; } # This functions get an ordered array of the available services from a file-rc system sub gst_service_filerc_get_runlevels_status { my ($start_service, $stop_service, @arr) = @_; my (@ret); # we start with the runlevels in which the service starts if ($start_service !~ /-/) { my (@runlevels); @runlevels = split /,/, $start_service; foreach $runlevel (@runlevels) { push @arr, { "number" => $runlevel, "action" => "start" }; } } # now let's go with the runlevels in which the service stops if ($stop_service !~ /-/) { my (@runlevels); @runlevels = split /,/, $stop_service; foreach $runlevel (@runlevels) { push @arr, { "number" => $runlevel, "action" => "stop" }; } } push @ret, {"runlevel" => \@arr}; return \@ret; } sub gst_service_filerc_get_service_info { my ($line, %ret) = @_; my %hash; my @runlevels; if ($line =~ /^([0-9][0-9])[\t ]+([0-9\-S,]+)[\t ]+([0-9\-S,]+)[\t ]+\/etc\/init\.d\/(.*)/) { $priority = $1; $stop_service = $2; $start_service = $3; $script = $4; return undef if (&gst_service_list_service_is_forbidden ($script)); $hash{"script"} = $script; $hash{"runlevels"} = &gst_service_filerc_get_runlevels_status ($start_service, $stop_service); if ($start_service eq "-") { $hash{"priority"} = 100 - $priority; $priority = 100 - $priority; } else { $hash{"priority"} = $priority; } return (\%hash); } return undef; } sub gst_service_filerc_get_services { my ($script); my %ret; open FILE, "$gst_prefix/etc/runlevel.conf" or return undef; while ($line = ) { if ($line !~ /^#.*/) { my (%hash); my ($start_service, $stop_service); $hash = &gst_service_filerc_get_service_info ($line); if ($hash ne undef) { $script = $$hash{"script"}; if ($ret{$script} eq undef) { ($name, $description) = &gst_service_list_get_info ($script); $$hash{"name"} = $name unless ($name eq undef); $$hash{"description"} = $description unless ($description eq undef); $$hash{"count"} = 1; $ret{$script} = $hash; } else { my (@runlevels); # We need to mix the runlevels @runlevels = $$hash{"runlevels"}[0]{"runlevel"}; foreach $runlevel (@runlevels) { push @{$ret{$script}{"runlevels"}[0]{"runlevel"}}, $runlevel; } $ret{$script}{"priority"} += $$hash{"priority"}; $ret{$script}{"count"}++; } } } } # we have to return the average priority foreach $i (sort keys %ret) { $ret{$i}{"priority"} = int ($ret{$i}{"priority"} / $ret{$i}{"count"}); delete $ret{$i}{"count"}; } return \%ret; } # generic functions to get the available services sub gst_get_init_type { if (($gst_dist =~ /debian/) && (stat ("$gst_prefix/etc/runlevel.conf"))) { return "file-rc"; } else { return "sysv"; } } sub gst_service_get_services { $type = &gst_get_init_type (); if ($type eq "sysv") { return &gst_service_sysv_get_services (); } elsif ($type eq "file-rc") { return &gst_service_filerc_get_services (); } return undef; } # This function gets the runlevel that is in use sub gst_service_sysv_get_default_runlevel { my (@arr); @arr = split / /, `/sbin/runlevel` ; $arr[1] =~ s/\n//; return $arr[1]; } # THESE ARE THE FUNCTIONS WHICH APPLY THE CHANGES MADE TO THE CONFIGURATION OF THE COMPUTER sub gst_service_sysv_add_link { my ($runlevel, $action, $priority, $service) = @_; my ($prio) = sprintf ("%0.2d",$priority); symlink ("$relative_path/$service", "$rcd_path/rc$runlevel.d/$action$prio$service"); &gst_report_enter (); &gst_report ("service_sysv_add_link", "$rcd_path/rc$runlevel.d/$action$prio$service"); &gst_report_leave (); } sub gst_service_sysv_remove_link { my ($runlevel, $script) = @_; foreach $link (<$rcd_path/rc$runlevel.d/[SK][0-9][0-9]$script>) { &gst_report ("service_sysv_remove_link", "$link"); unlink ("$link"); &gst_report_leave (); } } # These are the functions for storing the service settings from XML in SysV sub gst_service_sysv_set_service { my ($service) = @_; my ($script, $priority, $runlevels); my ($action); ($rcd_path, $initd_path, $relative_path) = &gst_service_sysv_get_paths (); $script = $$service{"script"}; $priority = $$service{"priority"}; $runlevels = $$service{"runlevels"}[0]{"runlevel"}; # pass though all the runlevels checking if the service must be started, stopped or removed for ($i = 0; $i <= 6; $i++) { &gst_service_sysv_remove_link ($i, $script); $action = undef; foreach $j (@$runlevels) { if ($i == $$j{"number"}) { $found = 1; $action = $$j{"action"}; } } if ($action ne undef) { if ($action eq "start") { &gst_service_sysv_add_link ($i, "S", $priority, $script); } else { &gst_service_sysv_add_link ($i, "K", 100 - $priority, $script); } } } } sub gst_service_sysv_set_services { my ($services, $runlevel) = @_; foreach $i (@$services) { &gst_service_sysv_set_service($i); } } # This is the function for storing the service settings from XML in file-rc sub gst_service_filerc_set_services { my ($services, $runlevel) = @_; my ($buff, $lineno, $line, $file); my ($rcd_path, $initd_path, $relative_path) = &gst_service_sysv_get_paths (); $file = "$gst_prefix/etc/runlevel.conf"; $buff = &gst_file_buffer_load ($file); &gst_file_buffer_join_lines ($buff); $lineno = 0; # We prepare the file for storing the configuration, save the initial comments # and delete the rest do { $lineno++; } while ($$buff[$lineno] =~ /^#.*/); for ($i = $lineno; $i < scalar (@$buff); $i++) { $$buff[$i] =~ /.*\/etc\/init\.d\/(.*)/; # we need to keep the forbidden services and the services that only start in rcS.d if (!gst_service_list_service_is_forbidden ($1)) { delete $$buff[$i]; } } # Now we append the services foreach $service (@$services) { my (@start, @stop, @arr); $arr = $$service{"runlevels"}[0]{"runlevel"}; # split the runlevels in two arrays foreach $i (@$arr) { if ($$i{"action"} eq "start") { push @start, $$i{"number"}; } else { push @stop, $$i{"number"}; } # find out if it's the current runlevel, in this case we should start/stop the service by hand # if ($$i{"number"} eq $runlevel) # { # if ($$i{"action"} eq "start") # { # &gst_file_run ("$initd_path/$$service{\"script\"} start >/dev/null 2>/dev/null"); # } # else # { # &gst_file_run ("$initd_path/$$service{\"script\"} stop >/dev/null 2>/dev/null"); # } # } } if ((scalar (@start) eq 0) && (scalar (@stop) eq 0)) { #print a empty line $line = sprintf ("%0.2d",$$service{"priority"}) . "\t"; $line .= "-\t-"; $line .= "\t\t". "/etc/init.d/" . $$service{"script"} . "\n"; push @$buff, $line; } else { if (scalar (@start) ne 0) { # print the line with the runlevels in which the service starts with priority = $priority $line = sprintf ("%0.2d",$$service{"priority"}) . "\t"; $line .= "-" . "\t"; $line .= join ",", sort @start; $line .= "\t\t". "/etc/init.d/" . $$service{"script"} . "\n"; push @$buff, $line; } if (scalar (@stop) ne 0) { # print the line with the runlevels in which the service stops with priority = 100 - $priority $line = sprintf ("%0.2d", 100 - $$service{"priority"}) . "\t"; $line .= join ",", sort @stop; $line .= "\t" . "-"; $line .= "\t\t". "/etc/init.d/" . $$service{"script"} . "\n"; push @$buff, $line; } } } @$buff = sort @$buff; push @$buff, "\n"; &gst_file_buffer_clean ($buff); &gst_file_buffer_save ($buff, $file); } sub gst_service_set_services { my ($services, $runlevel) = @_; $type = &gst_get_init_type (); if ($type eq "sysv") { &gst_service_sysv_set_services ($services, $runlevel); } elsif ($type eq "file-rc") { &gst_service_filerc_set_services ($services, $runlevel); } } sub gst_service_set_conf { my ($hash) = @_; my ($services, $runlevel); return unless $hash; $services = $$hash{"services"}[0]{"service"}; return unless $services; $runlevel = $$hash{"runlevel"}; return unless $runlevel; &gst_service_set_services($services, $runlevel); } # gets the valid parameters for a service sub gst_service_get_service_parameters { my ($service_name) = @_; my ($rcd_path, $initd_path, $relative_path) = &gst_service_sysv_get_paths (); my ($service) = "$initd_path/$service_name"; my (@arr, @actions, $output, $size); # We have to check if the service is executable return undef unless (-x $service); $output = `$service 2>/dev/null`; # if the output doesn't have the 'usage' word in it, then isn't a valid output return undef unless ($output =~ /usage/i); @arr = split / /, $output; $size = scalar (@arr); # if the array has 0 rows, the service has no actions return undef if ($size eq 0); $arr[$size-1] =~ s/[\{\}]//g; @actions = split /\|/, $arr[size-1]; return \@actions; }