#-*- 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 , # Milan Bouchet-Valat # # 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. package Init::Services; my $SERVICE_START = 0; my $SERVICE_STOP = 1; use Init::ServicesList; use Utils::Report; sub get_runlevels { my (%dist_map, %runlevels); my ($desc, $distro); %dist_map = ( "redhat-6.2" => "redhat-6.2", "redhat-7.0" => "redhat-6.2", "redhat-7.1" => "redhat-6.2", "redhat-7.2" => "redhat-6.2", "redhat-7.3" => "redhat-6.2", "redhat-8.0" => "redhat-6.2", "mandrake-9.0" => "redhat-6.2", "conectiva-9" => "redhat-6.2", "debian" => "redhat-6.2", "suse-9.0" => "redhat-6.2", "pld-1.0" => "redhat-6.2", "vine-3.0" => "redhat-6.2", "slackware-9.1.0" => "freebsd-5", "gentoo" => "gentoo", "archlinux" => "freebsd-5", "freebsd-5" => "freebsd-5", "solaris-2.11" => "freebsd-5", ); %runlevels= ( "redhat-6.2" => [ "0", "1", "2", "3", "4", "5", "6" ], "gentoo" => &get_gentoo_runlevels (), "freebsd-5" => [ "default" ], ); $distro = $dist_map{$Utils::Backend::tool{"platform"}}; $desc = $runlevels{$distro}; return $desc; } # This function gets the runlevel that is in use sub get_sysv_default_runlevel { my (@arr); @arr = split / /, `/sbin/runlevel` ; chomp $arr[1]; return $arr[1]; } sub get_default_runlevel { my $type = &get_init_type (); return "default" if ($type eq "gentoo" || $type eq "rcng" || $type eq "bsd" || $type eq "smf"); return &get_sysv_default_runlevel (); } # Upstart support # TODO: Handle Upstart jobs, and not only traditional SystemV scripts inside an upstart system sub get_upstart_paths { my %dist_map = ( # gst_dist => [rc.X dirs location, init.d scripts location, relative path, upstart init jobs location] "debian" => ["$gst_prefix/etc", "$gst_prefix/etc/init.d", "../init.d", "$gst_prefix/etc/init"], ); my $res; $res = $dist_map{$Utils::Backend::tool{"platform"}}; &Utils::Report::do_report ("service_upstart_unsupported", $Utils::Backend::tool{"platform"}) if ($res eq undef); return @$res; } # we are going to extract the name of the script sub get_upstart_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 get_upstart_runlevels_status { my ($service) = @_; my ($link); my ($runlevel, $action, $priority); my (@arr, @ret); 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, [ $runlevel, $SERVICE_START, $priority ]; } elsif ($action eq "K") { push @arr, [ $runlevel, $SERVICE_STOP, $priority ]; } } return \@arr; } # We are going to extract the information of the service sub get_upstart_service_info { my ($service) = @_; my ($script, @actions, @runlevels, $role); # Return if it's a directory return if (-d $service); # We have to check if the service is executable return unless (-x $service); $script = &get_upstart_service_name ($service); $runlevels = &get_upstart_runlevels_status($script); return ($script, $runlevels); } # This function gets an ordered array of the available services from a upstart system sub get_upstart_services { my ($service); my (@arr); ($rcd_path, $initd_path, $relative_path, $init_path) = &get_upstart_paths (); return undef unless ($rcd_path && $initd_path && $init_path); foreach $service (<$initd_path/*>) { my (@info, $script); @info = &get_upstart_service_info ($service); # Only manage traditional init.d scripts, ignore services with jobs installed $script = $info[0]; if (!&Init::ServicesList::is_forbidden ($script) && !-e "$init_path/$script.conf") { push @arr, \@info if (scalar (@info)); } } return \@arr; } # These are the functions for storing the service settings in upstart sub remove_upstart_link { my ($rcd_path, $runlevel, $script) = @_; foreach $link (<$rcd_path/rc$runlevel.d/[SK][0-9][0-9]$script>) { &Utils::Report::enter (); &Utils::Report::do_report ("service_upstart_remove_link", "$link"); unlink ($link); &Utils::Report::leave (); } } sub add_upstart_link { my ($rcd_path, $relative_path, $runlevel, $action, $priority, $service) = @_; my ($prio) = sprintf ("%0.2d",$priority); symlink ("$relative_path/$service", "$rcd_path/rc$runlevel.d/$action$prio$service"); &Utils::Report::enter (); &Utils::Report::do_report ("service_upstart_add_link", "$rcd_path/rc$runlevel.d/$action$prio$service"); &Utils::Report::leave (); } sub run_upstart_initd_script { my ($service, $arg) = @_; my ($rc_path, $initd_path); my $str; &Utils::Report::enter (); if (&Utils::File::run ("service", $service, $arg) == 0) { &Utils::Report::do_report ("service_upstart_op_success", $service, $arg); &Utils::Report::leave (); return 0; } &Utils::Report::do_report ("service_upstart_op_failed", $service, $arg); &Utils::Report::leave (); return -1; } sub set_upstart_service { my ($service) = @_; my ($script, $priority, $runlevels, $default_runlevel); my ($runlevel, $action, %configured_runlevels); ($rcd_path, $initd_path, $relative_path) = &get_upstart_paths (); return unless ($rcd_path && $initd_path && $relative_path); $script = $$service[0]; $runlevels = $$service[1]; $default_runlevel = &get_default_runlevel (); foreach $r (@$runlevels) { $runlevel = $$r[0]; $action = ($$r[1] == $SERVICE_START) ? "S" : "K"; $priority = sprintf ("%0.2d", $$r[2]); $priority = "50" if ($$r[2] <= 0); $configured_runlevels{$runlevel} = 1; if (!-f "$rcd_path/rc$runlevel.d/$action$priority$script") { &remove_upstart_link ($rcd_path, $runlevel, $script); &add_upstart_link ($rcd_path, $relative_path, $runlevel, $action, $priority, $script); if ($runlevel eq $default_runlevel) { &run_upstart_initd_script ($script, ($$r[1] == $SERVICE_START) ? "start" : "stop"); } } } # remove unneeded links foreach $link (<$rcd_path/rc[0-6].d/[SK][0-9][0-9]$script>) { $link =~ /rc([0-6])\.d/; $runlevel = $1; if (!exists $configured_runlevels{$runlevel}) { &remove_upstart_link ($rcd_path, $runlevel, $script); if ($runlevel eq $default_runlevel) { &run_upstart_initd_script ($script, "stop"); } } } } sub set_upstart_services { my ($services) = @_; foreach $i (@$services) { &set_upstart_service($i); } } # This function gets the runlevel that is in use sub get_sysv_default_runlevel { my (@arr); @arr = split / /, `/sbin/runlevel` ; chomp $arr[1]; return $arr[1]; } sub get_default_runlevel { my $type = &get_init_type (); return "default" if ($type eq "gentoo" || $type eq "rcng" || $type eq "bsd" || $type eq "smf"); return &get_sysv_default_runlevel (); } sub get_sysv_paths { my %dist_map = ( # gst_dist => [rc.X dirs location, init.d scripts location, relative path location] "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"], "mandrake-9.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "yoper-2.2" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "conectiva-9" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "debian" => ["$gst_prefix/etc", "$gst_prefix/etc/init.d", "../init.d"], "suse-9.0" => ["$gst_prefix/etc/init.d", "$gst_prefix/etc/init.d", "../"], "pld-1.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "vine-3.0" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "ark" => ["$gst_prefix/etc/rc.d", "$gst_prefix/etc/rc.d/init.d", "../init.d"], "solaris-2.11" => ["$gst_prefix/etc", "$gst_prefix/etc/init.d", "../init.d"], ); my $res; $res = $dist_map{$Utils::Backend::tool{"platform"}}; &Utils::Report::do_report ("service_sysv_unsupported", $Utils::Backend::tool{"platform"}) if ($res eq undef); return @$res; } # we are going to extract the name of the script sub get_sysv_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 get_sysv_runlevels_status { my ($service) = @_; my ($link); my ($runlevel, $action, $priority); my (@arr, @ret); 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, [ $runlevel, $SERVICE_START, $priority ]; } elsif ($action eq "K") { push @arr, [ $runlevel, $SERVICE_STOP, $priority ]; } } return \@arr; } # We are going to extract the information of the service sub get_sysv_service_info { my ($service) = @_; my ($script, @actions, @runlevels, $role); # Return if it's a directory return if (-d $service); # We have to check if the service is executable return unless (-x $service); $script = &get_sysv_service_name ($service); $runlevels = &get_sysv_runlevels_status($script); return ($script, $runlevels); } # This function gets an ordered array of the available services from a SysV system sub get_sysv_services { my ($service); my (@arr); ($rcd_path, $initd_path) = &get_sysv_paths (); return undef unless ($rcd_path && $initd_path); foreach $service (<$initd_path/*>) { my (@info); @info = &get_sysv_service_info ($service); push @arr, \@info if (scalar (@info) && !&Init::ServicesList::is_forbidden ($info[0])); } return \@arr; } # These are the functions for storing the service settings in SysV sub remove_sysv_link { my ($rcd_path, $runlevel, $script) = @_; foreach $link (<$rcd_path/rc$runlevel.d/[SK][0-9][0-9]$script>) { &Utils::Report::enter (); &Utils::Report::do_report ("service_sysv_remove_link", "$link"); unlink ($link); &Utils::Report::leave (); } } sub add_sysv_link { my ($rcd_path, $relative_path, $runlevel, $action, $priority, $service) = @_; my ($prio) = sprintf ("%0.2d",$priority); symlink ("$relative_path/$service", "$rcd_path/rc$runlevel.d/$action$prio$service"); &Utils::Report::enter (); &Utils::Report::do_report ("service_sysv_add_link", "$rcd_path/rc$runlevel.d/$action$prio$service"); &Utils::Report::leave (); } sub run_sysv_initd_script { my ($service, $arg) = @_; my ($rc_path, $initd_path); my $str; &Utils::Report::enter (); ($rcd_path, $initd_path) = &get_sysv_paths (); return -1 unless ($rcd_path && $initd_path); if (-f "$initd_path/$service") { if (&Utils::File::run ("$initd_path/$service", $arg) == 0) { &Utils::Report::do_report ("service_sysv_op_success", $service, $arg); &Utils::Report::leave (); return 0; } } &Utils::Report::do_report ("service_sysv_op_failed", $service, $arg); &Utils::Report::leave (); return -1; } sub set_sysv_service { my ($service) = @_; my ($script, $priority, $runlevels, $default_runlevel); my ($runlevel, $action, %configured_runlevels); ($rcd_path, $initd_path, $relative_path) = &get_sysv_paths (); return unless ($rcd_path && $initd_path && $relative_path); $script = $$service[0]; $runlevels = $$service[1]; $default_runlevel = &get_sysv_default_runlevel (); foreach $r (@$runlevels) { $runlevel = $$r[0]; $action = ($$r[1] == $SERVICE_START) ? "S" : "K"; $priority = sprintf ("%0.2d", $$r[2]); $priority = "50" if ($$r[2] <= 0); $configured_runlevels{$runlevel} = 1; if (!-f "$rcd_path/rc$runlevel.d/$action$priority$script") { &remove_sysv_link ($rcd_path, $runlevel, $script); &add_sysv_link ($rcd_path, $relative_path, $runlevel, $action, $priority, $script); if ($runlevel eq $default_runlevel) { &run_sysv_initd_script ($script, ($$r[1] == $SERVICE_START) ? "start" : "stop"); } } } # remove unneeded links foreach $link (<$rcd_path/rc[0-6].d/[SK][0-9][0-9]$script>) { $link =~ /rc([0-6])\.d/; $runlevel = $1; if (!exists $configured_runlevels{$runlevel}) { &remove_sysv_link ($rcd_path, $runlevel, $script); if ($runlevel eq $default_runlevel) { &run_sysv_initd_script ($script, "stop"); } } } } sub set_sysv_services { my ($services) = @_; foreach $i (@$services) { &set_sysv_service($i); } } # This functions get an ordered array of the available services from a file-rc system sub get_filerc_runlevels_status { my ($start_service, $stop_service, $priority) = @_; my (@arr); # we start with the runlevels in which the service starts if ($start_service !~ /-/) { my (@runlevels); @runlevels = split /,/, $start_service; foreach $runlevel (@runlevels) { push @arr, [ $runlevel, $SERVICE_START, $priority ]; } } # 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, [ $runlevel, $SERVICE_STOP, $priority ]; } } return \@arr; } sub get_filerc_service_info { my ($line, %ret) = @_; my (@runlevels, $role); 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; $runlevels = &get_filerc_runlevels_status ($start_service, $stop_service, $priority); return ($script, $runlevels); } return; } sub get_filerc_services { my ($script); my ($script, @arr, %hash); open FILE, "$gst_prefix/etc/runlevel.conf" or return undef; while ($line = ) { next if ($line =~ /^\#.*/); my (@info); my ($start_service, $stop_service); @info = &get_filerc_service_info ($line); next if (!scalar (@info)); $script = $info[0]; if (!$hash{$script}) { $hash{$script} = \@info; } else { # We need to mix the runlevels foreach $runlevel (@{$info[2]}) { push @{$hash{$script}[2]}, $runlevel; } } } foreach $key (keys %hash) { push @arr, $hash{$key}; } return \@arr; } # These are the functions for storing the service settings in file-rc sub concat_filerc_runlevels { my (@runlevels) = @_; $str = join (",", sort (@runlevels)); return ($str) ? $str : "-"; } sub set_filerc_service { my ($buff, $initd_path, $service) = @_; my (%hash, $priority, $line, $str); my ($script, $default_runlevel, %configured_runlevels); $script = $$service[0]; $runlevels = $$service[1]; $default_runlevel = &get_sysv_default_runlevel (); foreach $i (@$runlevels) { $priority = 0 + $$i[2]; $priority = 50 if ($priority == 0); #very rough guess $configured_runlevels {$$i[0]} = 1; if ($$i[1] == $SERVICE_START) { $hash{$priority}{$SERVICE_START} = [] if (!$hash{$priority}{$SERVICE_START}); push @{$hash{$priority}{$SERVICE_START}}, $$i[0]; } else { $hash{$priority}{$SERVICE_STOP} = [] if (!$hash{$priority}{$SERVICE_STOP}); push @{$hash{$priority}{$SERVICE_STOP}}, $$i[0]; } if ($$i[0] eq $default_runlevel) { &run_sysv_initd_script ($script, ($$i[1] == $SERVICE_START) ? "start" : "stop"); } } foreach $priority (keys %hash) { $line = sprintf ("%0.2d", $priority) . "\t"; $line .= &concat_filerc_runlevels (@{$hash{$priority}{$SERVICE_STOP}}) . "\t"; $line .= &concat_filerc_runlevels (@{$hash{$priority}{$SERVICE_START}}) . "\t"; $line .= $initd_path . "/" . $script . "\n"; push @$buff, $line; } # stop the service if it's not configured if (!$configured_runlevels {$default_runlevel}) { &run_sysv_initd_script ($script, "stop"); } } sub set_filerc_services { my ($services) = @_; my ($buff, $lineno, $line, $file); my ($rcd_path, $initd_path, $relative_path) = &get_sysv_paths (); return unless ($rcd_path && $initd_path && $relative_path); $file = "/etc/runlevel.conf"; $buff = &Utils::File::load_buffer ($file); &Utils::File::join_buffer_lines ($buff); $lineno = 0; # We prepare the file for storing the configuration, save the initial comments # and delete the rest while ($$buff[$lineno] =~ /^#.*/) { $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 # FIXME: need to remove this call to is_forbidden if (!&Init::ServicesList::is_forbidden ($1)) { delete $$buff[$i]; } } # Now we append the services foreach $service (@$services) { &set_filerc_service ($buff, $initd_path, $service); } @$buff = sort @$buff; push @$buff, "\n"; &Utils::File::clean_buffer ($buff); &Utils::File::save_buffer ($buff, $file); } # this functions get a list of the services that run on a bsd init sub get_bsd_scripts_list { my ($files) = [ "rc.M", "rc.inet2", "rc.4" ]; my ($file, $i, %scripts); my ($service, $name); foreach $i (@$files) { $file = "/etc/rc.d/" . $i; $fd = &Utils::File::open_read_from_names ($file); if (!$fd) { &Utils::Report::do_report ("rc_file_read_failed", $file); next; } while (<$fd>) { $line = $_; if ($line =~ /^if[ \t]+\[[ \t]+\-x[ \t]([0-9a-zA-Z\/\.\-_]+) .*\]/) { $service = $1; $name = $service; $name =~ s/^.*\///; $name =~ s/^rc\.//; $scripts{$name} = $service; } } &Utils::File::close_file ($fd); } return \%scripts; } sub get_bsd_service_status { my ($service) = @_; return (-x $service) ? $SERVICE_START : $SERVICE_STOP; } sub get_bsd_service_info { my ($service, $name) = @_; my (@runlevels, $status); return if (! Utils::File::exists ($service)); $status = &get_bsd_service_status ($service); push @runlevels, [ "default", $status, 0 ]; return ($name, \@runlevels); } sub get_bsd_services { my (@arr, %scripts, $name); $scripts = &get_bsd_scripts_list (); foreach $name (keys %$scripts) { my (@info); @info = &get_bsd_service_info ($$scripts{$name}, $name); push @arr, \@info if (scalar (@info) && !&Init::ServicesList::is_forbidden ($info[0])); } return \@arr; } sub run_bsd_script { my ($service, $arg) = @_; my ($chmod) = 0; return if (!&Utils::File::exists ($service)); # if it's not executable then chmod it if (! -x $service) { $chmod = 1; &Utils::File::run ("chmod", "ugo+x", $service); } &Utils::File::run_bg ($service, $arg); # return it to it's normal state if ($chmod) { &Utils::File::run ("chmod", "ugo-x", $service); } } sub set_bsd_service { my ($service) = @_; my ($script, $runlevels, $status, %scripts); $scripts = &get_bsd_scripts_list (); $script = $$scripts{$$service[0]}; $runlevels = $$service[1]; $runlevel = $$runlevels[0]; next if ($script eq undef); $status = $$runlevel[1]; $status = $SERVICE_STOP if ($status eq undef); next if ($status == &get_bsd_service_status ($script)); if ($status == $SERVICE_START) { &Utils::File::run ("chmod", "ugo+x", $script); &run_bsd_script ($script, "start"); } else { &run_bsd_script ($script, "stop"); &Utils::File::run ("chmod", "ugo-x", $script); } } # This function stores the configuration in a bsd init sub set_bsd_services { my ($services) = @_; foreach $service (@$services) { &set_bsd_service ($service); } } # these functions get a list of the services that run on a gentoo init sub get_gentoo_service_status { my ($script, $runlevel) = @_; my ($services) = &get_gentoo_services_for_runlevel ($runlevel); return ($$services {$script}); } sub get_gentoo_runlevels { my($raw_output) = Utils::File::run_backtick("rc-status --nocolor -l"); my(@runlevels); return undef if (!$raw_output); @runlevels = split(/\n/,$raw_output); return \@runlevels; } sub get_gentoo_services_for_runlevel { my($runlevel) = @_; my($raw_output) = Utils::File::run_backtick("rc-status --nocolor $runlevel"); my(@raw_lines) = split(/\n/,$raw_output); my($line, $service); my(%services); foreach $line (@raw_lines) { if ($line !~ /^Runlevel/) { $service = (split(" ",$line))[0]; $services{$service} = 1; } } return \%services } sub get_gentoo_runlevels_services { my (%runlevels_services, $runlevels); $runlevels = &get_gentoo_runlevels (); return undef if (!$runlevels); foreach $runlevel (@$runlevels) { $runlevels_services{$runlevel} = &get_gentoo_services_for_runlevel ($runlevel); } return \%runlevels_services; } sub get_gentoo_services_list { my ($service, @services); foreach $service (<$gst_prefix/etc/init.d/*>) { if (-x $service) { $service =~ s/.*\///; push @services, $service; } } return \@services; } sub gentoo_service_exists { my($service) = @_; my($services) = &get_gentoo_services_list(); foreach $i (@$services) { return 1 if ($i =~ /$service/); } return 0; } sub get_gentoo_runlevels_status { my ($service, $runlevels_services) = @_; my (@arr, $services_in_runlevel); foreach $runlevel (keys %$runlevels_services) { $services_in_runlevel = $$runlevels_services {$runlevel}; if ($$services_in_runlevel{$service}) { push @arr, [ $runlevel, $SERVICE_START, 0 ]; } else { push @arr, [ $runlevel, $SERVICE_STOP, 0 ]; } } return \@arr; } sub get_gentoo_service_info { my ($service, $runlevels_services) = @_; my (@runlevels_info); $runlevels_info = &get_gentoo_runlevels_status ($service, $runlevels_services); return ($service, $runlevels_info); } sub get_gentoo_services { my ($service, @arr); my ($service_list) = &get_gentoo_services_list (); my ($runlevels_services) = &get_gentoo_runlevels_services (); foreach $service (@$service_list) { my (@info); @info = &get_gentoo_service_info ($service, $runlevels_services); push @arr, \@info if (scalar (@info) && !&Init::ServicesList::is_forbidden ($info[0])); } return \@arr; } #FIXME: almost equal to the sysv equivalent sub run_gentoo_script { my ($service, $arg) = @_; &Utils::Report::enter (); if (&gentoo_service_exists ($service)) { if (!&Utils::File::run ("/etc/init.d/$service", $arg)) { &Utils::Report::do_report ("service_sysv_op_success", $service, $arg); &Utils::Report::leave (); return 0; } } &Utils::Report::do_report ("service_sysv_op_failed", $service, $arg); &Utils::Report::leave (); return -1; } sub set_gentoo_service_status { my ($script, $rl, $status, $runlevels_services) = @_; my ($services_in_runlevel, $old_status); $services_in_runlevel = $$runlevels_services {$rl}; $old_status = ($$services_in_runlevel{$script}) ? $SERVICE_START : $SERVICE_STOP; return if ($status == $old_status); if ($status == $SERVICE_START) { &Utils::File::run ("rc-update", "add", $script, $rl); &run_gentoo_script ($script, "start"); } else { &run_gentoo_script ($script, "stop"); &Utils::File::run ("rc-update", "del", $script, $rl); } } sub set_gentoo_service { my ($service) = @_; my ($action, $rl, $script, $arr); my ($runlevels_services) = &get_gentoo_runlevels_services (); return if (!$runlevels_services); $script = $$service[0]; $arr = $$service[1]; foreach $i (@$arr) { $action = $$i[1]; $rl = $$i[0]; &set_gentoo_service_status ($script, $rl, $action, $runlevels_services); } } # This function stores the configuration in gentoo init sub set_gentoo_services { my ($services) = @_; foreach $service (@$services) { &set_gentoo_service ($service); } } # rcNG functions, mostly for FreeBSD sub get_rcng_status_by_service { my ($service) = @_; my ($fd, $line, $active); # This is the only difference between rcNG and archlinux if ($Utils::Backend::tool{"platform"} eq "archlinux") { return &Utils::File::exists ("/var/run/daemons/$service"); } else { $fd = &Utils::File::run_pipe_read ("/etc/rc.d/$service rcvar"); while (<$fd>) { $line = $_; if ($line =~ /^\$.*=YES$/) { $active = 1; last; } } &Utils::File::close_file ($fd); return $active; } } sub get_rcng_service_info { my ($script) = @_; my (@runlevels); if (get_rcng_status_by_service ($script)) { push @runlevels, [ "default", $SERVICE_START, 0 ]; } else { push @runlevels, [ "default", $SERVICE_STOP, 0 ]; } return ($script, \@runlevels); } sub get_rcng_services { my ($service); my (@arr); foreach $service (<$gst_prefix/etc/rc.d/*>) { my (@info); $service =~ s/.*\///; @info = &get_rcng_service_info ($service); push @arr, \@info if (scalar (@info) && !&Init::ServicesList::is_forbidden ($info[0])); } return \@arr; } sub run_rcng_script { my ($service, $arg) = @_; &Utils::Report::enter (); if (!&Utils::File::run ("/etc/rc.d/$service", $arg)) { &Utils::Report::do_report ("service_sysv_op_success", $service, $arg); &Utils::Report::leave (); return 0; } &Utils::Report::do_report ("service_sysv_op_failed", $service, $arg); &Utils::Report::leave (); return -1; } # These functions store the configuration of a rcng init sub set_rcng_service_status { my ($service, $action) = @_; my ($fd, $key, $res); my ($default_rcconf) = "/etc/defaults/rc.conf"; my ($rcconf) = "/etc/rc.conf"; if (&Utils::File::exists ("/etc/rc.d/$service")) { $fd = &Utils::File::run_pipe_read ("/etc/rc.d/$service rcvar"); while (<$fd>) { if (/^\$(.*)=.*$/) { # to avoid cluttering rc.conf with duplicated data, # we first look in the defaults/rc.conf for the key $key = $1; $res = &Utils::Parse::get_sh_bool ($default_rcconf, $key); if ($res == $action) { &Utils::Replace::set_sh ($rcconf, $key); } else { &Utils::Replace::set_sh_bool ($rcconf, $key, "YES", "NO", $action); } &run_rcng_script ($service, ($action) ? "forcestart" : "forcestop"); } } &Utils::File::close_file ($fd); } elsif (&Utils::File::exists ("/usr/local/etc/rc.d/$service.sh")) { if ($action) { &Utils::File::copy_file ("/usr/local/etc/rc.d/$service.sh.sample", "/usr/local/etc/rc.d/$service.sh"); &run_rcng_script ($service, "forcestart"); } else { &run_rcng_script ($service, "forcestop"); Utils::File::remove ("/usr/local/etc/rc.d/$service.sh"); } } } sub set_archlinux_service_status { my ($script, $active) = @_; my $rcconf = '/etc/rc.conf'; my ($daemons); $daemons = &Utils::Parse::get_sh ($rcconf, "DAEMONS"); $daemons =~ s/[\(\)]//g; # escape these chars $script =~ s/([\\\.\^\$\*\+\?\{\}\[\]\(\)\|])/\\\1/g; $notscript = "\!" . $script; if (($daemons =~ m/$notscript/) && $active) { # It was disabled, enable it $daemons =~ s/$notscript/$script/g; } elsif (($daemons =~ m/$script/) && !$active) { # It was enabled, disable it $daemons =~ s/$script/$notscript/g; } elsif (($daemons !~ m/$script/) && $active) { $daemons .= " ".$script; } $daemons = "(" . $daemons . ")"; &Utils::Replace::set_sh ($rcconf, "DAEMONS", $daemons, 1); &run_rcng_script ($service, ($active) ? "start" : "stop"); } sub set_rcng_service { my ($service) = @_; my ($action, $runlevels, $script, $func); # archlinux stores services differently if ($Utils::Backend::tool{"platform"} eq "archlinux") { $func = \&set_archlinux_service_status; } else { $func = \&set_rcng_service_status; } $script = $$service[0]; $runlevels = $$service[1]; $runlevel = $$runlevels[0]; $action = ($$runlevel[1] == $SERVICE_START)? 1 : 0; &$func ($script, $action); } sub set_rcng_services { my ($services) = @_; foreach $service (@$services) { &set_rcng_services ($service); } } # SuSE functions, quite similar to SysV, but not equal... sub get_suse_service_info ($service) { my ($service) = @_; my (@runlevels, $link, $runlevel); foreach $link (<$rcd_path/rc[0-9S].d/S[0-9][0-9]$service>) { $link =~ s/$rcd_path\///; $link =~ /rc([0-6])\.d\/S[0-9][0-9].*/; $runlevel = $1; push @runlevels, [ $runlevel, $SERVICE_START, 0 ]; } foreach $link (<$rcd_path/boot.d/S[0-9][0-9]$service>) { push @runlevels, [ "B", $SERVICE_START, 0 ]; } return ($service, $runlevels); } sub get_suse_services { my ($service, @arr); ($rcd_path, $initd_path) = &get_sysv_paths (); return undef unless ($rcd_path && $initd_path); foreach $service (<$gst_prefix/etc/init.d/*>) { my (@info); next if (-d $service || ! -x $service); $service =~ s/.*\///; @info = &get_suse_service_info ($service); push @arr, \@info if (scalar (@info) && !&Init::ServicesList::is_forbidden ($info[0])); } return \@arr; } sub set_suse_service { my ($service) = @_; my ($action, $runlevels, $script, $rllist); my (%configured_runlevelsl); $script = $$service[0]; $runlevels = $$service[1]; $rllist = ""; %configured_runlevels = {}; &Utils::File::run ("insserv", "-r", $script); foreach $rl (@$runlevels) { $configured_runlevels{$$rl[0]} = 1; if ($$rl[1] == $SERVICE_START) { $rllist .= $$rl[0] . ","; } &run_sysv_initd_script ($script, ($$rl[1] == $SERVICE_START) ? "start" : "stop"); } if ($rllist ne "") { $rllist =~ s/,$//; &Utils::File::run ("insserv", $script, "start=$rllist"); } if (!$configured_runlevels{$default_runlevel}) { &run_sysv_initd_script ($script, $$rl[1]); } } # This function stores the configuration in suse init sub set_suse_services { my ($services) = @_; my ($default_runlevel); $default_runlevel = &get_sysv_default_runlevel (); foreach $service (@$services) { &set_suse_service ($service); } } # functions to get/set services info in smf sub smf_service_exists { my($service) = @_; my($services) = &get_smf_services_list (); foreach $i (@$services) { return 1 if ($i =~ /$service/); } return 0; } sub run_smf_svcadm { my ($service, $arg) = @_; my ($option); my %op = ("stop" => "disable", "start" => "enable" ); if (&smf_service_exists ($service)) { if (!&Utils::File::run ("svcadm", $op{$arg}, $service)) { &Utils::Report::do_report ("service_sysv_op_success", $service, $arg); &Utils::Report::leave (); return 0; } } &Utils::Report::do_report ("service_sysv_op_failed", $service, $arg); &Utils::Report::leave (); return -1; } sub get_smf_runlevel_status_by_service { my ($service, $status) = @_; my (@arr); if ($status) { push @arr, [ "default", $SERVICE_START, 0 ]; } else { push @arr, [ "default", $SERVICE_STOP, 0 ]; } return \@arr; } sub get_smf_service_info { my ($service) = @_; my ($fd, @runlevels); my $status = 0; $fd = &Utils::File::run_pipe_read ("svcs -l $service"); while (<$fd>) { $status = 1 if (/^state.*online/); } &Utils::File::close_file ($fd); $runlevels = &get_smf_runlevel_status_by_service ($service, $status); $service =~ m/.*\/(.*)$/; return ($service, $runlevels); } sub get_smf_services_list { my ($fd, @list); $fd = &Utils::File::run_pipe_read ("svcs -H -a"); while (<$fd>) { next if (/svc:\/milestone/); if (/^.*\s*.*\s*svc:\/(.*):.*/) { push @list, $1; } } &Utils::File::close_file ($fd); return \@list; } sub get_smf_services { my ($service, @arr); my ($service_list) = &get_smf_services_list (); foreach $service (@$service_list) { my (@info); @info = &get_smf_service_info ($service); push @arr, \@info if scalar (@info && !&Init::ServicesList::is_forbidden ($info[0])); } return \@arr; } sub set_smf_service_status { my ($service, $rl, $active) = @_; my ($info); $info = &get_smf_service_info ($service); #return if service has not changed return if ($active == @{@$info[0]}[1]); if ($active == $SERVICE_START) { &Utils::File::run ("svcadm", "enable", "-s", $service); } else { &Utils::File::run ("svcadm", "disable", "-s", $service); } } sub set_smf_service { my ($service) = @_; my ($action, $rl, $script, $arr); $script = $$service[0]; $arr = $$service[1]; foreach $i (@$arr) { $action = $$i[1]; $rl = $$i[0]; &set_smf_service_status ($script, $rl, $action); } } sub set_smf_services { my ($services) = @_; foreach $service (@$services) { &set_smf_service ($service); } } # generic functions to get the available services sub get_init_type { my $gst_dist; $gst_dist = $Utils::Backend::tool{"platform"}; if (($gst_dist =~ /debian/)) { return "upstart"; } elsif ($gst_dist =~ /slackware/) { return "bsd"; } elsif (($gst_dist =~ /freebsd/) || ($gst_dist =~ /archlinux/)) { return "rcng"; } elsif ($gst_dist =~ /gentoo/) { return "gentoo"; } elsif ($gst_dist =~ /suse/) { return "suse"; } elsif ($gst_dist =~ /solaris/) { return "smf"; } else { return "sysv"; } } sub run_script { my ($service, $arg) = @_; my ($proc, $type); my %map = ( "upstart" => \&run_upstart_initd_script, "sysv" => \&run_sysv_initd_script, "file-rc" => \&run_sysv_initd_script, "bsd" => \&run_bsd_script, "gentoo" => \&run_gentoo_script, "rcng" => \&run_rcng_script, "suse" => \&run_sysv_initd_script, "smf" => \&run_smf_svcadm, ); $type = &get_init_type (); $proc = $map {$type}; &$proc ($service, $arg); } sub get { $type = &get_init_type (); return &get_upstart_services () if ($type eq "upstart"); return &get_sysv_services () if ($type eq "sysv"); return &get_filerc_services () if ($type eq "file-rc"); return &get_bsd_services () if ($type eq "bsd"); return &get_gentoo_services () if ($type eq "gentoo"); return &get_rcng_services () if ($type eq "rcng"); return &get_suse_services () if ($type eq "suse"); return &get_smf_services () if ($type eq "smf"); return undef; } sub set { my ($services) = @_; $type = &get_init_type (); &set_upstart_services ($services) if ($type eq "upstart"); &set_sysv_services ($services) if ($type eq "sysv"); &set_filerc_services ($services) if ($type eq "file-rc"); &set_bsd_services ($services) if ($type eq "bsd"); &set_gentoo_services ($services) if ($type eq "gentoo"); &set_rcng_services ($services) if ($type eq "rcng"); &set_suse_services ($services) if ($type eq "suse"); &set_smf_services ($services) if ($type eq "smf"); } sub get_service { my ($name) = @_; my ($services) = &get (); foreach $service (@$services) { return $service if ($service[0] eq $name); } return undef; } sub set_service { my ($service) = @_; $type = &get_init_type (); &set_upstart_service ($service) if ($type eq "upstart"); &set_sysv_service ($service) if ($type eq "sysv"); &set_filerc_service ($service) if ($type eq "file-rc"); &set_bsd_service ($service) if ($type eq "bsd"); &set_gentoo_service ($service) if ($type eq "gentoo"); &set_rcng_service ($service) if ($type eq "rcng"); &set_suse_service ($service) if ($type eq "suse"); &set_smf_service ($service) if ($type eq "smf"); } 1;