!c99Shell v. 1.0 pre-release build #16!

Software: Apache/2.2.3 (CentOS). PHP/5.1.6 

uname -a: Linux mx-ll-110-164-51-230.static.3bb.co.th 2.6.18-194.el5PAE #1 SMP Fri Apr 2 15:37:44
EDT 2010 i686
 

uid=48(apache) gid=48(apache) groups=48(apache) 

Safe-mode: OFF (not secure)

/usr/libexec/webmin/init/   drwxr-xr-x
Free 53.79 GB of 127.8 GB (42.09%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     init-lib.pl (51.4 KB)      -rwxr-xr-x
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
=head1 init-lib.pl

Common functions for SYSV-style boot/shutdown sequences, MacOS, FreeBSD
and Windows. Because each system uses a different format and semantics for
bootup actions, there are separate functions for listing and managing each
type. However, some functions like enable_at_boot and disable_at_boot can 
creation actions regardless of the underlying boot system.

Example code :

 foreign_require('init', 'init-lib.pl');
 $ok = init::action_status('foo');
 if ($ok == 0) {
   init::enable_at_boot('foo', 'Start or stop the Foo server',
                        '/etc/foo/start', '/etc/foo/stop');
 }

=cut

BEGIN { push(@INC, ".."); };
use WebminCore;
&init_config();
@action_buttons = ( 'start', 'restart', 'condrestart', 'reload', 'status',
            'stop' );
%access = &get_module_acl();

=head2 init_mode

This variable is set based on the bootup system in use. Possible values are :

=item osx - MacOSX hostconfig files

=item rc - FreeBSD 6+ RC files

=item init - System V init.d files, seen on Linux and Solaris

=item local - A single rc.local file

=item win32 - Windows services

=item upstart - Upstart, seend on Ubuntu 11

=item systemd - SystemD, as seen on Fedora 16

=cut
if ($config{'init_mode'}) {
    $init_mode = $config{'init_mode'};
    }
elsif ($config{'hostconfig'}) {
    $init_mode = "osx";
    }
elsif ($config{'rc_dir'}) {
    $init_mode = "rc";
    }
elsif ($config{'init_base'} && -d "/etc/init" && &has_command("initctl")) {
    $init_mode = "upstart";
    }
elsif ($config{'init_base'} && -d "/etc/systemd" &&
       &has_command("systemctl") &&
       &execute_command("systemctl list-units") == 0) {
    $init_mode = "systemd";
    }
elsif ($config{'init_base'}) {
    $init_mode = "init";
    }
elsif ($config{'local_script'}) {
    $init_mode = "local";
    }
elsif ($gconfig{'os_type'} eq 'windows') {
    $init_mode = "win32";
    }

=head2 runlevel_actions(level, S|K)

Return a list of init.d actions started or stopped in some run-level, each of
which is a space-separated string in the format : number name inode

=cut
sub runlevel_actions
{
local($dir, $f, @stbuf, @rv);
$dir = &runlevel_dir($_[0]);
opendir(DIR, $dir);
foreach $f (readdir(DIR)) {
    if ($f !~ /^([A-Z])(\d+)(.*)$/ || $1 ne $_[1]) { next; }
    if (!(@stbuf = stat("$dir/$f"))) { next; }
    push(@rv, "$2 $3 $stbuf[1]");
    }
closedir(DIR);
@rv = sort { @a = split(/\s/,$a); @b = split(/\s/,$b); $a[0] <=> $b[0]; } @rv;
return $_[1] eq "S" ? @rv : reverse(@rv);
}


=head2 list_runlevels

Returns a list of known runlevels, such as : 2 3 5.

=cut
sub list_runlevels
{
local(@rv);
opendir(DIR, $config{init_base});
foreach (readdir(DIR)) {
    if (/^rc([A-z0-9])\.d$/ || /^(boot)\.d$/) {
        #if (!$config{show_opts} && $1 < 1) { next; }
        push(@rv, $1);
        }
    }
closedir(DIR);
return sort(@rv);
}


=head2 list_actions

List boot time action names from init.d, such as httpd and cron.

=cut
sub list_actions
{
local($dir, $f, @stbuf, @rv);
$dir = $config{init_dir};
opendir(DIR, $dir);
foreach $f (sort { lc($a) cmp lc($b) } readdir(DIR)) {
    if ($f eq "." || $f eq ".." || $f =~ /\.bak$/ || $f eq "functions" ||
        $f eq "core" || $f eq "README" || $f eq "rc" || $f eq "rcS" ||
        -d "$dir/$f" || $f =~ /\.swp$/ || $f eq "skeleton" ||
        $f =~ /\.lock$/ || $f =~ /\.dpkg-(old|dist)$/ ||
        $f =~ /^\.depend\./ || $f eq '.legacy-bootordering' ||
        $f =~ /^mandrake/) { next; }
    if (@stbuf = stat("$dir/$f")) {
        push(@rv, "$f $stbuf[1]");
        }
    }
closedir(DIR);
foreach $f (split(/\s+/, $config{'extra_init'})) {
    if (@stbuf = stat($f)) {
        push(@rv, "$f $stbuf[1]");
        }
    }
return @rv;
}


=head2 action_levels(S|K, action)

Return a list of run levels in which some action (from init.d) is started
or stopped. Each item is a space-separated string in the format : level order name

=cut
sub action_levels
{
local(@stbuf, $rl, $dir, $f, @stbuf2, @rv);
@stbuf = stat(&action_filename($_[1]));
foreach $rl (&list_runlevels()) {
    $dir = &runlevel_dir($rl);
    opendir(DIR, $dir);
    foreach $f (readdir(DIR)) {
        if ($f =~ /^([A-Z])(\d+)(.*)$/ && $1 eq $_[0]) {
            @stbuf2 = stat("$dir/$f");
            if ($stbuf[1] == $stbuf2[1]) {
                push(@rv, "$rl $2 $3");
                last;
                }
            }
        }
    closedir(DIR);
    }
return @rv;
}


=head2 action_filename(name)

Returns the path to the file in init.d for some action, such as /etc/init.d/foo.

=cut
sub action_filename
{
return $_[0] =~ /^\// ? $_[0] : "$config{init_dir}/$_[0]";
}

=head2 runlevel_filename(level, S|K, order, name)

Returns the path to the actual script run at boot for some action, such as
/etc/rc3.d/S99foo.

=cut
sub runlevel_filename
{
local $n = $_[3];
$n =~ s/^(.*)\///;
return &runlevel_dir($_[0])."/$_[1]$_[2]$n";
}


=head2 add_rl_action(action, runlevel, S|K, order)

Add some existing action to a runlevel. The parameters are :

=item action - Name of the action, like foo

=item runlevel - A runlevel number, like 3

=item S|K - Either S for an action to run at boot, or K for shutdown

=item order - Numeric boot order, like 99

=cut
sub add_rl_action
{
$file = &runlevel_filename($_[1], $_[2], $_[3], $_[0]);
while(-r $file) {
    if ($file =~ /^(.*)_(\d+)$/) { $file = "$1_".($2+1); }
    else { $file = $file."_1"; }
    }
&lock_file($file);
if ($config{soft_links}) {
    &symlink_file(&action_filename($_[0]), $file);
    }
else {
    &link_file(&action_filename($_[0]), $file);
    }
&unlock_file($file);
}


=head2 delete_rl_action(name, runlevel, S|K)

Delete some action from a runlevel. The parameters are :

=item action - Name of the action, like foo.

=item runlevel - A runlevel number, like 3.

=item S|K - Either S for an action to run at boot, or K for shutdown.

=cut
sub delete_rl_action
{
local(@stbuf, $dir, $f, @stbuf2);
@stbuf = stat(&action_filename($_[0]));
$dir = &runlevel_dir($_[1]);
opendir(DIR, $dir);
foreach $f (readdir(DIR)) {
    if ($f =~ /^([A-Z])(\d+)(.+)$/ && $1 eq $_[2]) {
        @stbuf2 = stat("$dir/$f");
        if ($stbuf[1] == $stbuf2[1]) {
            # found file to delete.. unlink
            &unlink_logged("$dir/$f");
            last;
            }
        }
    }
closedir(DIR);
}


=head2 reorder_rl_action(name, runlevel, S|K, new_order)

Change the boot order of some existing runlevel action. The parameters are :

=item action - Name of the action, like foo.

=item runlevel - A runlevel number, like 3.

=item S|K - Either S for an action to run at boot, or K for shutdown.

=item new_order - New numeric boot order to use, like 99.

=cut
sub reorder_rl_action
{
local(@stbuf, $dir, $f, @stbuf2);
@stbuf = stat(&action_filename($_[0]));
$dir = &runlevel_dir($_[1]);
opendir(DIR, $dir);
foreach $f (readdir(DIR)) {
    if ($f =~ /^([A-Z])(\d+)(.+)$/ && $1 eq $_[2]) {
        @stbuf2 = stat("$dir/$f");
        if ($stbuf[1] == $stbuf2[1]) {
            # Found file that needs renaming
            $file = &runlevel_dir($_[1])."/$1$_[3]$3";
            while(-r $file) {
                if ($file =~ /^(.*)_(\d+)$/)
                    { $file = "$1_".($2+1); }
                else { $file = $file."_1"; }
                }
            &rename_logged("$dir/$f", $file);
            last;
            }
        }
    }
closedir(DIR);
}


=head2 rename_action(old, new)

Change the name of an action in init.d, and re-direct all soft links
to it from the runlevel directories. Parameters are :

=item old - Old action name.

=item new - New action name.

=cut
sub rename_action
{
local($file, $idx, $old);
foreach (&action_levels('S', $_[0])) {
    /^(\S+)\s+(\S+)\s+(\S+)$/;
    $file = &runlevel_dir($1)."/S$2$3";
    if (readlink($file)) {
        # File is a symbolic link.. change it
        &lock_file($file);
        &unlink_file($file);
        &symlink_file("$config{init_dir}/$_[1]", $file);
        &unlock_file($file);
        }
    if (($idx = index($file, $_[0])) != -1) {
        $old = $file;
        substr($file, $idx, length($_[0])) = $_[1];
        &rename_logged($old, $file);
        }
    }
foreach (&action_levels('K', $_[0])) {
    /^(\S+)\s+(\S+)\s+(\S+)$/;
    $file = &runlevel_dir($1)."/K$2$3";
    if (readlink($file)) {
        # File is a symbolic link.. change it
        &lock_file($file);
        &unlink_file($file);
        &symlink_file("$config{init_dir}/$_[1]", $file);
        &unlock_file($file);
        }
    if (($idx = index($file, $_[0])) != -1) {
        $old = $file;
        substr($file, $idx, length($_[0])) = $_[1];
        &rename_logged($old, $file);
        }
    }
&rename_logged("$config{init_dir}/$_[0]", "$config{init_dir}/$_[1]");
}


=head2 rename_rl_action(runlevel, S|K, order, old, new)

Change the name of a runlevel file. For internal use only.

=cut
sub rename_rl_action
{
&rename_logged(&runlevel_dir($_[0])."/$_[1]$_[2]$_[3]",
               &runlevel_dir($_[0])."/$_[1]$_[2]$_[4]");
}

=head2 get_inittab_runlevel

Returns the runlevels entered at boot time. If more than one is returned,
actions from all of them are used.

=cut
sub get_inittab_runlevel
{
local %iconfig = &foreign_config("inittab");
local @rv;
local $id = $config{'inittab_id'};
if (open(TAB, $iconfig{'inittab_file'})) {
    # Read the inittab file
    while(<TAB>) {
        if (/^$id:(\d+):/ && $1) { @rv = ( $1 ); }
        }
    close(TAB);
    }

if (&has_command("runlevel")) {
    # Use runlevel command to get current level
    local $out = &backquote_command("runlevel");
    if ($out =~ /^(\S+)\s+(\S+)/) {
        push(@rv, $2);
        }
    }
elsif (&has_command("who")) {
    # Use who -r command to get runlevel
    local $out = &backquote_command("who -r 2>/dev/null");
    if (!$? && $out =~ /run-level\s+(\d+)/) {
        push(@rv, $1);
        }
    }

# Add statically configured runlevels
if ($config{"inittab_rl_$rv[0]"}) {
    @rv = split(/,/, $config{"inittab_rl_$rv[0]"});
    }
push(@rv, $config{'inittab_extra'});
return &unique(@rv);
}

=head2 init_description(file, [&hasargs])

Given a full path to an init.d file, returns a description from the comments
about what it does. If the hasargs hash ref parameter is given, it is filled
in with supported parameters to the action, like 'start' and 'stop'.

=cut
sub init_description
{
# Read contents of script, extract start/stop commands
open(FILE, $_[0]);
local @lines = <FILE>;
close(FILE);
local $data = join("", @lines);
if ($_[1]) {
    foreach (@lines) {
        if (/^\s*(['"]?)([a-z]+)\1\)/i) {
            $_[1]->{$2}++;
            }
        }
    }

local $desc;
if ($config{'daemons_dir'}) {
    # First try the daemons file
    local %daemon;
    if ($_[0] =~ /\/([^\/]+)$/ &&
        &read_env_file("$config{'daemons_dir'}/$1", \%daemon) &&
        $daemon{'DESCRIPTIVE'}) {
        return $daemon{'DESCRIPTIVE'};
        }
    }
if ($config{'chkconfig'}) {
    # Find the redhat-style description: section
    foreach (@lines) {
        s/\r|\n//g;
        if (/^#+\s*description:(.*?)(\\?$)/) {
            $desc = $1;
            }
        elsif (/^#+\s*(.*?)(\\?$)/ && $desc && $1) {
            $desc .= "\n".$1;
            }
        if ($desc && !$2) {
            last;
            }
        }
    }
elsif ($config{'init_info'} || $data =~ /BEGIN INIT INFO/) {
    # Find the suse-style Description: line
    foreach (@lines) {
        s/\r|\n//g;
        if (/^#\s*(Description|Short-Description):\s*(.*)/) {
            $desc = $2;
            }
        }
    }
else {
    # Use the first comments
    foreach (@lines) {
        s/\r|\n//g;
        next if (/^#!\s*\/(bin|sbin|usr)\// || /\$id/i || /^#+\s+@/ ||
             /source function library/i || /^#+\s*copyright/i);
        if (/^#+\s*(.*)/) {
            last if ($desc && !$1);
            $desc .= $1."\n" if ($1);
            }
        elsif (/\S/) { last; }
        }
    $_[0] =~ /\/([^\/]+)$/;
    $desc =~ s/^Tag\s+(\S+)\s*//i;
    $desc =~ s/^\s*$1\s+//;
    }
return $desc;
}

=head2 chkconfig_info(file)

If a file has a chkconfig: section specifying the runlevels to start in and
the orders to use, return an array containing the levels (as array ref),
start order, stop order and description.

=cut
sub chkconfig_info
{
local @rv;
local $desc;
open(FILE, $_[0]);
while(<FILE>) {
    if (/^#\s*chkconfig:\s+(\S+)\s+(\d+)\s+(\d+)/) {
        @rv = ( $1 eq '-' ? [ ] : [ split(//, $1) ], $2, $3 );
        }
    elsif (/^#\s*description:\s*(.*)/) {
        $desc = $1;
        }
    }
close(FILE);
$rv[3] = $desc if ($desc && @rv);
return @rv;
}

=head2 action_status(action)

Returns 0 if some action doesn't exist, 1 if it does but is not enabled,
or 2 if it exists and is enabled. This works for all supported boot systems,
such as init.d, OSX and FreeBSD.

=cut
sub action_status
{
if ($init_mode eq "upstart") {
    # Check upstart service status
    local $out = &backquote_command("initctl status ".
                    quotemeta($_[0])." 2>&1");
    if (!$?) {
        my $cfile = "/etc/init/$_[0].conf";
        open(CONF, $cfile);
        while(<CONF>) {
            if (/^(#*)\s*start/) {
                return $1 ? 1 : 2;
                }
            }
        close(CONF);
        return 1;    # Should never happen
        }
    }
elsif ($init_mode eq "systemd") {
    # Check systemd service status
    local $unit = $_[0];
    $unit .= ".service" if ($unit !~ /\.service$/);
    local $out = &backquote_command("systemctl show ".
                    quotemeta($unit)." 2>&1");
    if ($out =~ /UnitFileState=(\S+)/ &&
        $out !~ /Description=LSB:\s/) {
        # Exists .. but is it started at boot?
        return lc($1) eq 'enabled' ? 2 : 1;
        }
    }
if ($init_mode eq "init" || $init_mode eq "upstart" ||
    $init_mode eq "systemd") {
    # Look for init script
    local ($a, $exists, $starting, %daemon);
    foreach $a (&list_actions()) {
        local @a = split(/\s+/, $a);
        if ($a[0] eq $_[0]) {
            $exists++;
            local @boot = &get_inittab_runlevel();
            foreach $s (&action_levels("S", $a[0])) {
                local ($l, $p) = split(/\s+/, $s);
                $starting++ if (&indexof($l, @boot) >= 0);
                }
            }
        }
    if ($starting && $config{'daemons_dir'} &&
        &read_env_file("$config{'daemons_dir'}/$_[0]", \%daemon)) {
        $starting = lc($daemon{'ONBOOT'}) eq 'yes' ? 1 : 0;
        }
    return !$exists ? 0 : $starting ? 2 : 1;
    }
elsif ($init_mode eq "local") {
    # Look for entry in rc.local
    local $fn = "$module_config_directory/$_[0].sh";
    local $cmd = "$fn start";
    open(LOCAL, $config{'local_script'});
    while(<LOCAL>) {
        s/\r|\n//g;
        $found++ if ($_ eq $cmd);
        }
    close(LOCAL);
    return $found && -r $fn ? 2 : -r $fn ? 1 : 0;
    }
elsif ($init_mode eq "win32") {
    # Look for a win32 service, enabled at boot
    local ($svc) = &list_win32_services($_[0]);
    return !$svc ? 0 :
           $svc->{'boot'} == 2 ? 2 : 1;
    }
elsif ($init_mode eq "rc") {
    # Look for an RC script
    local @rcs = &list_rc_scripts();
    local ($rc) = grep { $_->{'name'} eq $_[0] } @rcs;
    return !$rc ? 0 :
           $rc->{'enabled'} ? 2 : 1;
    }
elsif ($init_mode eq "osx") {
    # Look for a hostconfig entry
    local $ucname = uc($_[0]);
    local %hc;
    &read_env_file($config{'hostconfig'}, \%hc);
    return $hc{$ucname} eq '-YES-' ? 2 :
           $hc{$ucname} eq '-NO-' ? 1 : 0;
    }
}

=head2 enable_at_boot(action, description, startcode, stopcode, statuscode, &opts)

Makes some action start at boot time, creating the script by copying the
specified file if necessary. The parameters are :

=item action - Name of the action to create or enable.

=item description - A human-readable description for the action.

=item startcode - Shell commands to run at boot time.

=item stopcode - Shell commands to run at shutdown time.

=item statuscode - Shell code to output the action's status.

=item opts - Hash ref of additional options, like : fork -> server will fork into background

If this is called for a named action that already exists (even if it isn't
enabled), only the first parameter needs to be given.

=cut
sub enable_at_boot
{
local $st = &action_status($_[0]);
return if ($st == 2);    # already starting!
local ($daemon, %daemon);
local $unit = $_[0];
$unit .= ".service" if ($unit !~ /\.service$/);

if ($init_mode eq "upstart" && (!-r "$config{'init_dir'}/$_[0]" ||
                -r "/etc/init/$_[0].conf")) {
    # Create upstart action if missing, as long as this isn't an old-style
    # init script
    my $cfile = "/etc/init/$_[0].conf";
    if (-r $cfile) {
        # Config file exists, make sure it is enabled
        if (&has_command("insserv")) {
            &system_logged(
                "insserv ".quotemeta($_[0])." >/dev/null 2>&1");
            }
        my $lref = &read_file_lines($cfile);
        my $foundstart;
        foreach my $l (@$lref) {
            if ($l =~ /^#+start/) {
                # Start of start block
                $l =~ s/^#+//;
                $foundstart = 1;
                }
            elsif ($l =~ /^#+\s+\S/ && $foundstart) {
                # Continuation line for start
                $l =~ s/^#+//;
                }
            elsif ($l =~ /^\S/ && $foundstart) {
                # Some other directive after start
                last;
                }
            }
        &flush_file_lines($cfile);
        }
    else {
        # Need to create config
        $_[2] || &error("Upstart service $_[0] cannot be created ".
                "unless a command is given");
        &create_upstart_service($_[0], $_[1], $_[2], undef,
                    $_[5]->{'fork'});
        if (&has_command("insserv")) {
            &system_logged(
                "insserv ".quotemeta($_[0])." >/dev/null 2>&1");
            }
        }
    return;
    }
if ($init_mode eq "systemd" && (!-r "$config{'init_dir'}/$_[0]" ||
                &is_systemd_service($unit))) {
    # Create systemd unit if missing, as long as this isn't an old-style
    # init script
    my $cfile = &get_systemd_root($_[0])."/".$unit;
    if (!-r $cfile) {
        # Need to create config
        $_[2] || &error("Systemd service $_[0] cannot be created ".
                "unless a command is given");
        &create_systemd_service($unit, $_[1], $_[2], $_[3], undef,
                    $_[5]->{'fork'}, $_[5]->{'pidfile'});
        }
    &system_logged("systemctl enable ".
               quotemeta($unit)." >/dev/null 2>&1");
    return;
    }
if ($init_mode eq "init" || $init_mode eq "local" || $init_mode eq "upstart" ||
    $init_mode eq "systemd") {
    # In these modes, we create a script to run
    if ($config{'daemons_dir'} &&
        &read_env_file("$config{'daemons_dir'}/$_[0]", \%daemon)) {
        $daemon++;
        }
    local $fn;
    if ($init_mode eq "init" || $init_mode eq "upstart" ||
            $init_mode eq "systemd") {
        # Normal init.d system
        $fn = &action_filename($_[0]);
        }
    else {
        # Need to create hack init script
        $fn = "$module_config_directory/$_[0].sh";
        }
    local @chk = &chkconfig_info($fn);
    local @start = @{$chk[0]} ? @{$chk[0]} : &get_start_runlevels();
    local $start_order = $chk[1] || "9" x $config{'order_digits'};
    local $stop_order = $chk[2] || "9" x $config{'order_digits'};
    local @stop;
    if (@chk) {
        local %starting = map { $_, 1 } @start;
        @stop = grep { !$starting{$_} && /^\d+$/ } &list_runlevels();
        }

    local $need_links = 0;
    if ($st == 1 && $daemon) {
        # Just update daemons file
        $daemon{'ONBOOT'} = 'yes';
        &lock_file("$config{'daemons_dir'}/$_[0]");
        &write_env_file("$config{'daemons_dir'}/$_[0]", \%daemon);
        &unlock_file("$config{'daemons_dir'}/$_[0]");
        }
    elsif ($st == 1) {
        # Just need to create links (later)
        $need_links++;
        }
    elsif ($_[1]) {
        # Need to create the init script
        &lock_file($fn);
        &open_tempfile(ACTION, ">$fn");
        &print_tempfile(ACTION, "#!/bin/sh\n");
        if ($config{'chkconfig'}) {
            # Redhat-style description: and chkconfig: lines
            &print_tempfile(ACTION, "# description: $_[1]\n");
            &print_tempfile(ACTION, "# chkconfig: $config{'chkconfig'} ",
                     "$start_order $stop_order\n");
            }
        elsif ($config{'init_info'}) {
            # Suse-style init info section
            &print_tempfile(ACTION, "### BEGIN INIT INFO\n",
                     "# Provides: $_[0]\n",
                     "# Required-Start: \$network \$syslog\n",
                     "# Required-Stop: \$network\n",
                     "# Default-Start: ",join(" ", @start),"\n",
                     "# Default-Stop:\n",
                     "# Description: $_[1]\n",
                     "### END INIT INFO\n");
            }
        else {
            &print_tempfile(ACTION, "# $_[1]\n");
            }
        &print_tempfile(ACTION, "\n");
        &print_tempfile(ACTION, "case \"\$1\" in\n");

        if ($_[2]) {
            &print_tempfile(ACTION, "'start')\n");
            &print_tempfile(ACTION, &tab_indent($_[2]));
            &print_tempfile(ACTION, "\tRETVAL=\$?\n");
            if ($config{'subsys'}) {
                &print_tempfile(ACTION, "\tif [ \"\$RETVAL\" = \"0\" ]; then\n");
                &print_tempfile(ACTION, "\t\ttouch $config{'subsys'}/$_[0]\n");
                &print_tempfile(ACTION, "\tfi\n");
                }
            &print_tempfile(ACTION, "\t;;\n");
            }

        if ($_[3]) {
            &print_tempfile(ACTION, "'stop')\n");
            &print_tempfile(ACTION, &tab_indent($_[3]));
            &print_tempfile(ACTION, "\tRETVAL=\$?\n");
            if ($config{'subsys'}) {
                &print_tempfile(ACTION, "\tif [ \"\$RETVAL\" = \"0\" ]; then\n");
                &print_tempfile(ACTION, "\t\trm -f $config{'subsys'}/$_[0]\n");
                &print_tempfile(ACTION, "\tfi\n");
                }
            &print_tempfile(ACTION, "\t;;\n");
            }

        if ($_[4]) {
            &print_tempfile(ACTION, "'status')\n");
            &print_tempfile(ACTION, &tab_indent($_[4]));
            &print_tempfile(ACTION, "\t;;\n");
            }

        if ($_[2] && $_[3]) {
            &print_tempfile(ACTION, "'restart')\n");
            &print_tempfile(ACTION, "\t\$0 stop ; \$0 start\n");
            &print_tempfile(ACTION, "\tRETVAL=\$?\n");
            &print_tempfile(ACTION, "\t;;\n");
            }

        &print_tempfile(ACTION, "*)\n");
        &print_tempfile(ACTION, "\techo \"Usage: \$0 { start | stop }\"\n");
        &print_tempfile(ACTION, "\tRETVAL=1\n");
        &print_tempfile(ACTION, "\t;;\n");
        &print_tempfile(ACTION, "esac\n");
        &print_tempfile(ACTION, "exit \$RETVAL\n");
        &close_tempfile(ACTION);
        chmod(0755, $fn);
        &unlock_file($fn);
        $need_links++;
        }

    if ($need_links && ($init_mode eq "init" ||
                $init_mode eq "upstart" ||
                $init_mode eq "systemd")) {
        local $data = &read_file_contents($fn);
        my $done = 0;
        if (&has_command("chkconfig") && !$config{'no_chkconfig'} &&
            (@chk && $chk[3] || $data =~ /Default-Start:/i)) {
            # Call the chkconfig command to link up
            &system_logged("chkconfig --add ".quotemeta($_[0]));
            my $ex = &system_logged(
                "chkconfig ".quotemeta($_[0])." on");
            if (!$ex) {
                $done = 1;
                }
            }
        elsif (&has_command("insserv") && !$config{'no_chkconfig'} &&
               $data =~ /Default-Start:/i) {
            # Call the insserv command to enable
            my $ex = &system_logged("insserv ".quotemeta($_[0]).
                       " >/dev/null 2>&1");
            $done = 1 if (!$ex && &action_status($_[0]) == 2);
            }
        if (!$done) {
            # Just link up the init script
            local $s;
            foreach $s (@start) {
                &add_rl_action($_[0], $s, "S", $start_order);
                }
            local @klevels = &action_levels("K", $_[0]);
            if (!@klevels) {
                # Only add K scripts if none exist
                foreach $s (@stop) {
                    &add_rl_action($_[0], $s, "K", $stop_order);
                    }
                }
            }
        }
    elsif ($need_links) {
        # Just add rc.local entry
        local $lref = &read_file_lines($config{'local_script'});
        local $i;
        for($i=0; $i<@$lref && $lref->[$i] !~ /^exit\s/; $i++) { }
        splice(@$lref, $i, 0, "$fn start");
        if ($config{'local_down'}) {
            # Also add to shutdown script
            $lref = &read_file_lines($config{'local_down'});
            for($i=0; $i<@$lref &&
                  $lref->[$i] !~ /^exit\s/; $i++) { }
            splice(@$lref, $i, 0, "$fn stop");
            }
        &flush_file_lines();
        }
    }
elsif ($init_mode eq "win32") {
    # Enable and/or create a win32 service
    if ($st == 1) {
        # Just enable
        &enable_win32_service($_[0]);
        }
    else {
        # Need to create service, which calls wrapper program
        eval "use Win32::Daemon";

            # modify the string handed over
            # so it does not contain backslashes ...
            $_[2] =~ s/\\/\//g;

        local $perl_path = &get_perl_path();
        local %svc = ( 'name' => $_[0],
             'display' => $_[1],
             'path' => $perl_path,
             'user' => '',
             'description' => "OCM Webmin Pro Service",
             'pwd' => $module_root_directory,
             'parameters' => "\"$module_root_directory/win32.pl\" $_[2]",
            );
        if (!Win32::Daemon::CreateService(\%svc)) {
            print STDERR "Failed to create Win32 service : ",
                 Win32::FormatMessage(Win32::Daemon::GetLastError()),"\n";
            }
        }
    }
elsif ($init_mode eq "rc") {
    # Enable and/or create an RC script
    &lock_rc_files();
    if ($st == 1) {
        # Just enable
        &enable_rc_script($_[0]);
        }
    else {
        # Need to create a local rc script, and enable
        local @dirs = split(/\s+/, $config{'rc_dir'});
        local $file = $dirs[$#dirs]."/".$_[0].".sh";
        local $name = $_[0];
        $name =~ s/-/_/g;
        &open_lock_tempfile(SCRIPT, ">$file");
        &print_tempfile(SCRIPT, "#!/bin/sh\n");
        &print_tempfile(SCRIPT, "#\n");
        &print_tempfile(SCRIPT, "# PROVIDE: $_[0]\n");
        &print_tempfile(SCRIPT, "# REQUIRE: LOGIN\n");
        &print_tempfile(SCRIPT, "\n");
        &print_tempfile(SCRIPT, ". /etc/rc.subr\n");
        &print_tempfile(SCRIPT, "\n");
        &print_tempfile(SCRIPT, "name=$name\n");
        &print_tempfile(SCRIPT, "rcvar=`set_rcvar`\n");
        &print_tempfile(SCRIPT, "start_cmd=\"$_[2]\"\n");
        if ($_[3]) {
            &print_tempfile(SCRIPT, "stop_cmd=\"$_[3]\"\n")
            }
        if ($_[4] && $_[4] !~ /\n/) {
            &print_tempfile(SCRIPT, "status_cmd=\"$_[4]\"\n")
            }
        &print_tempfile(SCRIPT, "\n");
        &print_tempfile(SCRIPT, "load_rc_config \${name}\n");
        &print_tempfile(SCRIPT, "run_rc_command \"\$1\"\n");
        &close_tempfile(SCRIPT);
        &set_ownership_permissions(undef, undef, 0755, $file);
        &enable_rc_script($_[0]);
        }
    &unlock_rc_files();
    }
elsif ($init_mode eq "osx") {
    # Add hostconfig file entry
    local $ucname = uc($_[0]);
    local %hc;
    &lock_file($config{'hostconfig'});
    &read_env_file($config{'hostconfig'}, \%hc);
    if (!$hc{$ucname}) {
        # Need to create action
        local $ucfirst = ucfirst($_[0]);
        local $dir = "$config{'darwin_setup'}/$ucfirst";
        local $paramlist = "$dir/$config{'plist'}";
        local $scriptfile = "$dir/$ucfirst";

        # Create dirs if missing
        if (!-d $config{'darwin_setup'}) {
            &make_dir($config{'darwin_setup'}, 0755);
            }
        if (!-d $dir) {
            &make_dir($dir, 0755);
            }

        # Make params list file
        &open_lock_tempfile(PLIST, ">$paramlist");
        &print_tempfile(PLIST, "{\n");
        &print_tempfile(PLIST, "\t\tDescription\t\t= \"$_[1]\";\n");
        &print_tempfile(PLIST, "\t\tProvides\t\t= (\"$ucfirst\");\n");
        &print_tempfile(PLIST, "\t\tRequires\t\t= (\"Resolver\");\n");
        &print_tempfile(PLIST, "\t\tOrderPreference\t\t= \"None\";\n");
        &print_tempfile(PLIST, "\t\tMessages =\n");
        &print_tempfile(PLIST, "\t\t{\n");
        &print_tempfile(PLIST, "\t\t\tstart\t= \"Starting $ucfirst\";\n");
        &print_tempfile(PLIST, "\t\t\tstop\t= \"Stopping $ucfirst\";\n");
        &print_tempfile(PLIST, "\t\t};\n");
        &print_tempfile(PLIST, "}\n");
        &close_tempfile(PLIST);

        # Create Bootup Script
        &open_lock_tempfile(STARTUP, ">$scriptfile");
        &print_tempfile(STARTUP, "#!/bin/sh\n\n");
        &print_tempfile(STARTUP, ". /etc/rc.common\n\n");
        &print_tempfile(STARTUP, "if [ \"\${$ucname:=-NO-}\" = \"-YES-\" ]; then\n");
        &print_tempfile(STARTUP, "\tConsoleMessage \"Starting $ucfirst\"\n");
        &print_tempfile(STARTUP, "\t$_[2]\n");
        &print_tempfile(STARTUP, "fi\n");
        &close_tempfile(STARTUP);
        &set_ownership_permissions(undef, undef, 0750, $scriptfile);
        }

    # Update hostconfig file
    $hc{$ucname} = '-YES-';
    &write_env_file($config{'hostconfig'}, \%hc);
    &unlock_file($config{'hostconfig'});
    }
}

=head2 disable_at_boot(action)

Disabled some action from starting at boot, identified by the action
parameter. The config files that define what commands the action runs are not
touched, so it can be re-enabled with the enable_at_boot function.

=cut
sub disable_at_boot
{
local $st = &action_status($_[0]);
return if ($st != 2);    # not currently starting
local $unit = $_[0];
$unit .= ".service" if ($unit !~ /\.service$/);

if ($init_mode eq "upstart") {
    # Just use insserv to disable, and comment out start line in .conf file
    if (&has_command("insserv")) {
        &system_logged(
            "insserv -r ".quotemeta($_[0])." >/dev/null 2>&1");
        }
    my $cfile = "/etc/init/$_[0].conf";
    if (-r $cfile) {
        my $lref = &read_file_lines($cfile);
        my $foundstart;
        foreach my $l (@$lref) {
            if ($l =~ /^start\s/) {
                # Start of start block
                $l = "#".$l;
                $foundstart = 1;
                }
            elsif ($l =~ /^\s+\S/ && $foundstart) {
                # Continuation line for start
                $l = "#".$l;
                }
            elsif ($l =~ /^\S/ && $foundstart) {
                # Some other directive after start
                last;
                }
            }
        &flush_file_lines($cfile);
        }
    }
elsif ($init_mode eq "systemd") {
    # Use systemctl to disable at boot
    &system_logged("systemctl disable ".quotemeta($unit).
               " >/dev/null 2>&1");
    }
if ($init_mode eq "init" || $init_mode eq "upstart" ||
    $init_mode eq "systemd") {
    # Unlink or disable init script
    local ($daemon, %daemon);
    local $file = &action_filename($_[0]);
    local @chk = &chkconfig_info($file);
    local $data = &read_file_contents($file);

    if ($config{'daemons_dir'} &&
        &read_env_file("$config{'daemons_dir'}/$_[0]", \%daemon)) {
        # Update daemons file
        $daemon{'ONBOOT'} = 'no';
        &lock_file("$config{'daemons_dir'}/$_[0]");
        &write_env_file("$config{'daemons_dir'}/$_[0]", \%daemon);
        &unlock_file("$config{'daemons_dir'}/$_[0]");
        }
    elsif (&has_command("chkconfig") && !$config{'no_chkconfig'} && @chk) {
        # Call chkconfig to remove the links
        &system_logged("chkconfig ".quotemeta($_[0])." off");
        }
    else {
        # Just unlink the S links
        foreach my $a (&action_levels('S', $_[0])) {
            $a =~ /^(\S+)\s+(\S+)\s+(\S+)$/;
            &delete_rl_action($_[0], $1, 'S');
            }

        if (@chk) {
            # Take out the K links as well, since we know how to put
            # them back from the chkconfig info
            foreach my $a (&action_levels('K', $_[0])) {
                $a =~ /^(\S+)\s+(\S+)\s+(\S+)$/;
                &delete_rl_action($_[0], $1, 'K');
                }
            }
        }
    }
elsif ($init_mode eq "local") {
    # Take out of rc.local file
    local $lref = &read_file_lines($config{'local_script'});
    local $cmd = "$module_config_directory/$_[0].sh start";
    local $i;
    for($i=0; $i<@$lref; $i++) {
        if ($lref->[$i] eq $cmd) {
            splice(@$lref, $i, 1);
            last;
            }
        }
    if ($config{'local_down'}) {
        # Take out of shutdown script
        $lref = &read_file_lines($config{'local_down'});
        local $cmd = "$module_config_directory/$_[0].sh stop";
        for($i=0; $i<@$lref; $i++) {
            if ($lref->[$i] eq $cmd) {
                splice(@$lref, $i, 1);
                last;
                }
            }
        }
    &flush_file_lines();
    }
elsif ($init_mode eq "win32") {
    # Disable the service
    &disable_win32_service($_[0]);
    }
elsif ($init_mode eq "rc") {
    # Disable an RC script
    &lock_rc_files();
    &disable_rc_script($_[0]);
    &unlock_rc_files();
    }
elsif ($init_mode eq "osx") {
    # Disable in hostconfig
    local $ucname = uc($_[0]);
    local %hc;
    &lock_file($config{'hostconfig'});
    &read_env_file($config{'hostconfig'}, \%hc);
    if ($hc{$ucname} eq '-YES-' || $hc{$ucname} eq '-AUTOMATIC-') {
        $hc{$ucname} = '-NO-';
        &write_env_file($config{'hostconfig'}, \%hc);
        }
    &unlock_file($config{'hostconfig'});
    }
}

=head2 start_action(name)

Start the action with the given name, using whatever method is appropriate
for this operating system. Returns a status code (0 or 1 for failure or 
success) and all output from the action script.

=cut
sub start_action
{
local ($name) = @_;
local $action_mode = &get_action_mode($name);
if ($action_mode eq "init" || $action_mode eq "local") {
    # Run the init script or Webmin-created wrapper
    local $fn = $action_mode eq "init" ? &action_filename($name) :
            "$module_config_directory/$name.sh";
    if (!-x $fn) {
        return (0, "$fn does not exist");
        }
    &clean_environment();
    local $out = &backquote_logged("$fn start 2>&1 </dev/null");
    &reset_environment();
    local $ex = $?;
    return (!$ex, $out);
    }
elsif ($action_mode eq "rc") {
    # Run FreeBSD RC script
    return &start_rc_script($name);
    }
elsif ($action_mode eq "win32") {
    # Start Windows service
    local $err = &start_win32_service($name);
    return (!$err, $err);
    }
elsif ($action_mode eq "upstart") {
    # Run upstart action
    return &start_upstart_service($name);
    }
elsif ($action_mode eq "systemd") {
    # Start systemd service
    return &start_systemd_service($name);
    }
else {
    return (0, "Bootup mode $action_mode not supported");
    }
}

=head2 stop_action(name)

Stop the action with the given name, using whatever method is appropriate
for this operating system. Returns a status code (0 or 1 for failure or
success) and all output from the action script.

=cut
sub stop_action
{
local ($name) = @_;
local $action_mode = &get_action_mode($name);
if ($action_mode eq "init" || $action_mode eq "local") {
    # Run the init script or Webmin-created wrapper
    local $fn = $action_mode eq "init" ? &action_filename($name) :
            "$module_config_directory/$name.sh";
    if (!-x $fn) {
        return (0, "$fn does not exist");
        }
    local $out = &backquote_logged("$fn stop 2>&1 </dev/null");
    local $ex = $?;
    return (!$ex, $out);
    }
elsif ($action_mode eq "rc") {
    # Run FreeBSD RC script
    return &stop_rc_script($name);
    }
elsif ($action_mode eq "win32") {
    # Start Windows service
    local $err = &stop_win32_service($name);
    return (!$err, $err);
    }
elsif ($action_mode eq "upstart") {
    # Stop upstart action
    return &stop_upstart_service($name);
    }
elsif ($action_mode eq "systemd") {
    # Stop systemd service
    return &stop_systemd_service($name);
    }
else {
    return (0, "Bootup mode $action_mode not supported");
    }
}

=head2 restart_action(action)

Calls a stop then a start for some named action.

=cut
sub restart_action
{
local ($name) = @_;
local $action_mode = &get_action_mode($name);
if ($action_mode eq "upstart") {
    return &restart_upstart_service($name);
    }
elsif ($action_mode eq "systemd") {
    return &restart_systemd_service($name);
    }
else {
    &stop_action($name);
    return &start_action($name);
    }
}

=head2 get_action_mode(name)

Returns the init mode used by some action. May be different from the global
default on systems with mixed modes

=cut
sub get_action_mode
{
local ($name) = @_;
if ($init_mode eq "systemd") {
    # If classic init script exists but no systemd unit, assume init
    if (-r "$config{'init_dir'}/$name" && !&is_systemd_service($name)) {
        return "init";
        }
    }
elsif ($init_mode eq "upstart") {
    # If classic init script exists but not upstart config, assume init
    if (-r "$config{'init_dir'}/$name" && !-r "/etc/init/$name.conf") {
        return "init";
        }
    }
return $init_mode;
}

=head2 tab_indent(lines)

Given a string with multiple \n separated lines, returns the same string
with lines prefixed by tabs.

=cut
sub tab_indent
{
local ($rv, $l);
foreach $l (split(/\n/, $_[0])) {
    $rv .= "\t$l\n";
    }
return $rv;
}

=head2 get_start_runlevels

Returns a list of runlevels that actions should be started in, either based
on the module configuration or /etc/inittab.

=cut
sub get_start_runlevels
{
if ($config{'boot_levels'}) {
    return split(/[ ,]+/, $config{'boot_levels'});
    }
else {
    local @boot = &get_inittab_runlevel();
    return ( $boot[0] );
    }
}

=head2 runlevel_dir(runlevel)

Given a runlevel like 3, returns the directory containing symlinks for it,
like /etc/rc2.d.

=cut
sub runlevel_dir
{
if ($_[0] eq "boot") {
    return "$config{init_base}/boot.d";
    }
else {
    return "$config{init_base}/rc$_[0].d";
    }
}

=head2 list_win32_services([name])

Returns a list of known Win32 services, each of which is a hash ref. If the
name parameter is given, only details of that service are returned. Useful
keys for each hash are :

=item name - A unique name for the service.

=item desc - A human-readable description.

=item boot - Set to 2 if started at boot, 3 if not, 4 if disabled.

=item state -Set to 4 if running now, 1 if stopped.

=cut
sub list_win32_services
{
local ($name) = @_;
local @rv;
local $svc;

# Get the current statuses
if ($name) {
    &open_execute_command(SC, "sc query $name", 1, 1);
    }
else {
    &open_execute_command(SC, "sc query type= service state= all", 1, 1);
    }
while(<SC>) {
    s/\r|\n//g;
    if (/^SERVICE_NAME:\s+(\S.*\S)/) {
        $svc = { 'name' => $1 };
        push(@rv, $svc);
        }
    elsif (/^DISPLAY_NAME:\s+(\S.*)/ && $svc) {
        $svc->{'desc'} = $1;
        }
    elsif (/^\s+TYPE\s+:\s+(\d+)\s+(\S+)/ && $svc) {
        $svc->{'type'} = $1;
        $svc->{'type_desc'} = $2;
        }
    elsif (/^\s+STATE\s+:\s+(\d+)\s+(\S+)/ && $svc) {
        $svc->{'state'} = $1;
        $svc->{'state_desc'} = $2;
        }
    }
close(SC);

# For each service, see if it starts at boot or not
foreach $svc (@rv) {
    &open_execute_command(SC, "sc qc \"$svc->{'name'}\"", 1, 1);
    while(<SC>) {
        s/\r|\n//g;
        if (/^\s+START_TYPE\s+:\s+(\d+)\s+(\S+)/) {
            $svc->{'boot'} = $1;
            $svc->{'boot_desc'} = $2;
            }
        }
    close(SC);
    }

return @rv;
}

=head2 start_win32_service(name)

Attempts to start a service, returning undef on success, or some error message.

=cut
sub start_win32_service
{
local ($name) = @_;
local $out = &backquote_command("sc start \"$name\" 2>&1");
return $? ? $out : undef;
}

=head2 stop_win32_service(name)

Attempts to stop a service, returning undef on success, or some error message.

=cut
sub stop_win32_service
{
local ($name) = @_;
local $out = &backquote_command("sc stop \"$name\" 2>&1");
return $? ? $out : undef;
}

=head2 enable_win32_service(name)

Marks some service as starting at boot time. Returns undef on success or an
error message on failure.

=cut
sub enable_win32_service
{
local ($name) = @_;
local $out = &backquote_command("sc config \"$name\" start= auto 2>&1");
return $? ? $out : undef;
}

=head2 disable_win32_service(name)

Marks some service as disabled at boot time. Returns undef on success or an
error message on failure.

=cut
sub disable_win32_service
{
local ($name) = @_;
local $out = &backquote_command("sc config \"$name\" start= demand 2>&1");
return $? ? $out : undef;
}

=head2 create_win32_service(name, command, desc)

Creates a new win32 service, enabled at boot time. The required parameters are:
name - A unique name for the service
command - The DOS command to run at boot time
desc - A human-readable description.

=cut
sub create_win32_service
{
local ($name, $cmd, $desc) = @_;
local $out = &backquote_command("sc create \"$name\" DisplayName= \"$desc\" type= share start= auto binPath= \"$cmd\" 2>&1");
return $? ? $out : undef;
}

=head2 delete_win32_service(name)

Delete some existing service, identified by some name. Returns undef on
success or an error message on failure.

=cut
sub delete_win32_service
{
local ($name) = @_;
local $out = &backquote_command("sc delete \"$name\" 2>&1");
return $? ? $out : undef;
}

=head2 list_rc_scripts

Returns a list of known BSD RC scripts, and their enabled statuses. Each
element of the return list is a hash ref, with the following keys :

=item name - A unique name for the script.

=item desc - A human-readable description.

=item enabled - Set to 1 if enabled, 0 if not, 2 if unknown.

=item file - Full path to the action script file.

=item standard - Set to 0 for user-defined actions, 1 for those supplied with FreeBSD.

=cut
sub list_rc_scripts
{
# Build a list of those that are enabled in the rc.conf files
local @rc = &get_rc_conf();
local (%enabled, %cmt);
foreach my $r (@rc) {
    if ($r->{'name'} =~ /^(\S+)_enable$/) {
        local $name = $1;
        if (lc($r->{'value'}) eq 'yes') {
            $enabled{$name} = 1;
            }
        $r->{'cmt'} =~ s/\s*\(\s*or\s+NO\)//i;
        $r->{'cmt'} =~ s/\s*\(YES.*NO\)//i;
        $cmt{$name} ||= $r->{'cmt'};
        }
    }

# Scan the script dirs
local @rv;
foreach my $dir (split(/\s+/, $config{'rc_dir'})) {
    opendir(DIR, $dir);
    foreach my $f (readdir(DIR)) {
        next if ($f =~ /^\./ || $f =~ /\.(bak|tmp)/i);
        next if (uc($f) eq $f);        # Dummy actions are upper-case
        local $name = $f;
        $name =~ s/\.sh$//;
        local $data = &read_file_contents("$dir/$f");
        local $ename = $name;
        $ename =~ s/-/_/g;
        push(@rv, { 'name' => $name,
                'file' => "$dir/$f",
                'enabled' => $data !~ /rc\.subr/ ? 2 :
                     $enabled{$ename},
                'startstop' => $data =~ /rc\.subr/ ||
                       $data =~ /start\)/,
                'desc' => $cmt{$name},
                'standard' => ($dir !~ /local/)
              });
        }
    closedir(DIR);
    }
return sort { $a->{'name'} cmp $b->{'name'} } @rv;
}

=head2 save_rc_conf(name, value)

Internal function to modify the value of a single entry in the FreeBSD
rc.conf file.

=cut
sub save_rc_conf
{
local $found;
local @rcs = split(/\s+/, $config{'rc_conf'});
local $rcfile = $rcs[$#rcs];
&open_readfile(CONF, $rcfile);
local @conf = <CONF>;
close(CONF);
&open_tempfile(CONF, ">$rcfile");
foreach (@conf) {
    if (/^\s*([^=]+)\s*=\s*(.*)/ && $1 eq $_[0]) {
        &print_tempfile(CONF, "$_[0]=\"$_[1]\"\n") if (@_ > 1);
        $found++;
        }
    else {
        &print_tempfile(CONF, $_);
        }
    }
if (!$found && @_ > 1) {
    &print_tempfile(CONF, "$_[0]=\"$_[1]\"\n");
    }
&close_tempfile(CONF);
}

=head2 get_rc_conf

Reads the default and system-specific FreeBSD rc.conf files, and parses
them into a list of hash refs. Each element in the list has the following keys:

=item name - Name of this configuration parameter. May appear more than once, with the later one taking precedence.

=item value - Current value.

=item cmt - A human-readable comment about the parameter.

=cut
sub get_rc_conf
{
local ($file, @rv);
foreach $file (map { glob($_) } split(/\s+/, $config{'rc_conf'})) {
    local $lnum = 0;
    &open_readfile(FILE, $file);
    while(<FILE>) {
        local $cmt;
        s/\r|\n//g;
        if (s/#(.*)$//) {
            $cmt = $1;
            }
        if (/^\s*([^=\s]+)\s*=\s*"(.*)"/ ||
            /^\s*([^=\s]+)\s*=\s*'(.*)'/ ||
            /^\s*([^=\s]+)\s*=\s*(\S+)/) {
            push(@rv, { 'name' => $1,
                    'value' => $2,
                    'line' => $lnum,
                    'file' => $file,
                    'cmt' => $cmt });
            }
        $lnum++;
        }
    close(FILE);
    }
return @rv;
}

=head2 enable_rc_script(name)

Mark some RC script as enabled at boot.

=cut
sub enable_rc_script
{
local ($name) = @_;
$name =~ s/-/_/g;
&save_rc_conf($name."_enable", "YES");
}

=head2 disable_rc_script(name)

Mark some RC script as disabled at boot.

=cut
sub disable_rc_script
{
local ($name) = @_;
$name =~ s/-/_/g;
local $enabled;
foreach my $r (&get_rc_conf()) {
    if ($r->{'name'} eq $name."_enable" &&
        lc($r->{'value'}) eq 'yes') {
        $enabled = 1;
        }
    }
&save_rc_conf($name."_enable", "NO") if ($enabled);
}

=head2 start_rc_script(name)

Attempt to start some RC script, and returns 1 or 0 (for success or failure)
and the output.

=cut
sub start_rc_script
{
local ($name) = @_;
local @rcs = &list_rc_scripts();
local ($rc) = grep { $_->{'name'} eq $name } @rcs;
$rc || return "No script found for $name";
local $out = &backquote_logged("$rc->{'file'} forcestart 2>&1 </dev/null");
return (!$?, $out);
}

=head2 stop_rc_script(name)

Attempts to stop some RC script, and returns 1 or 0 (for success or failure)
and the output.

=cut
sub stop_rc_script
{
local ($name) = @_;
local @rcs = &list_rc_scripts();
local ($rc) = grep { $_->{'name'} eq $name } @rcs;
$rc || return "No script found for $name";
local $out = &backquote_logged("$rc->{'file'} forcestop 2>&1 </dev/null");
return (!$?, $out);
}

=head2 delete_rc_script(name)

Delete the FreeBSD RC script with some name

=cut
sub delete_rc_script
{
local ($name) = @_;
my @rcs = &list_rc_scripts();
my ($rc) = grep { $_->{'name'} eq $name } @rcs;
if ($rc) {
    &lock_rc_files();
    &disable_rc_script($in{'name'});
    &unlock_rc_files();
    &unlink_logged($rc->{'file'});
    }
}

=head2 lock_rc_files

Internal function to lock all FreeBSD rc.conf files.

=cut
sub lock_rc_files
{
foreach my $f (split(/\s+/, $config{'rc_conf'})) {
    &lock_file($f);
    }
}

=head2 unlock_rc_files

Internal function to un-lock all FreeBSD rc.conf files.

=cut
sub unlock_rc_files
{
foreach my $f (split(/\s+/, $config{'rc_conf'})) {
    &unlock_file($f);
    }
}

=head2 list_upstart_services

Returns a list of all known upstart services, each of which is a hash ref
with 'name', 'desc', 'boot', 'status' and 'pid' keys.

=cut
sub list_upstart_services
{
# Start with native upstart services
my @rv;
my $out = &backquote_command("initctl list");
my %done;
foreach my $l (split(/\r?\n/, $out)) {
    if ($l =~ /^(\S+)\s+(start|stop)\/([a-z]+)/) {
        my $s = { 'name' => $1,
              'goal' => $2,
              'status' => $3 };
        if ($l =~ /process\s+(\d+)/) {
            $s->{'pid'} = $1;
            }
        open(CONF, "/etc/init/$s->{'name'}.conf");
        while(<CONF>) {
            if (/^description\s+"([^"]+)"/ && !$s->{'desc'}) {
                $s->{'desc'} = $1;
                }
            elsif (/^(#*)\s*start/ && !$s->{'boot'}) {
                $s->{'boot'} = $1 ? 'stop' : 'start';
                }
            }
        close(CONF);
        push(@rv, $s);
        $done{$s->{'name'}} = 1;
        }
    }

# Also add legacy init scripts
my @rls = &get_inittab_runlevel();
foreach my $a (&list_actions()) {
    $a =~ s/\s+\d+$//;
    next if ($done{$a});
    my $f = &action_filename($a);
    my $s = { 'name' => $a,
          'legacy' => 1 };
    $s->{'boot'} = 'stop';
    foreach my $rl (@rls) {
        my $l = glob("/etc/rc$rl.d/S*$a");
        $s->{'boot'} = 'start' if ($l);
        }
    $s->{'desc'} = &init_description($f);
    my $hasarg = &get_action_args($f);
    if ($hasarg->{'status'}) {
        my $r = &action_running($f);
        if ($r == 0) {
            $s->{'status'} = 'waiting';
            }
        elsif ($r == 1) {
            $s->{'status'} = 'running';
            }
        }
    push(@rv, $s);
    }

return sort { $a->{'name'} cmp $b->{'name'} } @rv;
}

=head2 start_upstart_service(name)

Run the upstart service with some name, and return an OK flag and output

=cut
sub start_upstart_service
{
my ($name) = @_;
my $out = &backquote_logged(
    "initctl start ".quotemeta($name)." 2>&1 </dev/null");
return (!$?, $out);
}

=head2 stop_upstart_service(name)

Shut down the upstart service with some name, and return an OK flag and output

=cut
sub stop_upstart_service
{
my ($name) = @_;
my $out = &backquote_logged(
    "initctl stop ".quotemeta($name)." 2>&1 </dev/null");
return (!$?, $out);
}

=head2 restart_upstart_service(name)

Restart the upstart service with some name, and return an OK flag and output

=cut
sub restart_upstart_service
{
my ($name) = @_;
my $out = &backquote_logged(
    "service ".quotemeta($name)." restart 2>&1 </dev/null");
return (!$?, $out);
}

=head2 create_upstart_service(name, description, command, [pre-script], [fork])

Create a new upstart service with the given details.

=cut
sub create_upstart_service
{
my ($name, $desc, $server, $prestart, $forks) = @_;
my $cfile = "/etc/init/$name.conf";
&open_lock_tempfile(CFILE, ">$cfile");
&print_tempfile(CFILE,
  "# $name\n".
  "#\n".
  "# $desc\n".
  "\n".
  "description  \"$desc\"\n".
  "\n".
  "start on runlevel [2345]\n".
  "stop on runlevel [!2345]\n".
  "\n"
  );
if ($forks) {
    &print_tempfile(CFILE,
      "expect fork\n".
      "\n"
      );
    }
if ($prestart) {
    &print_tempfile(CFILE,
      "pre-start script\n".
      join("\n",
        map { "    ".$_."\n" }
        split(/\n/, $prestart))."\n".
      "end script\n".
      "\n");
    }
&print_tempfile(CFILE, "exec ".$server."\n");
&close_tempfile(CFILE);
}

=head2 delete_upstart_service(name)

Delete all traces of some upstart service

=cut
sub delete_upstart_service
{
my ($name) = @_;
if (&has_command("insserv")) {
    &system_logged("insserv -r ".quotemeta($name)." >/dev/null 2>&1");
    }
my $cfile = "/etc/init/$name.conf";
my $ifile = "/etc/init.d/$name";
&unlink_logged($cfile, $ifile);
}

=head2 list_systemd_services

Returns a list of all known systemd services, each of which is a hash ref
with 'name', 'desc', 'boot', 'status' and 'pid' keys.

=cut
sub list_systemd_services
{
# Get all systemd unit names
my $out = &backquote_command("systemctl list-units --full --all");
&error("Failed to list systemd units : $out") if ($?);
foreach my $l (split(/\r?\n/, $out)) {
    my ($unit, $loaded, $active, $sub, $desc) = split(/\s+/, $l, 5);
    if ($unit ne "UNIT" && $loaded eq "loaded") {
        push(@units, $unit);
        }
    }

# Also find unit files for units that may be disabled at boot and not running,
# and so don't show up in systemctl list-units
opendir(UNITS, &get_systemd_root());
push(@units, grep { !/\.wants$/ && !/^\./ && !/\@/ } readdir(UNITS));
closedir(UNITS);

# Skip useless units
@units = grep { !/^sys-devices-/ &&
            !/^\-\.mount/ &&
        !/^dev-/ &&
        !/^systemd-/ } @units;
@units = &unique(@units);

# Dump state of all of them, 100 at a time
my %info;
while(@units) {
    my @args;
    while(@args < 100 && @units) {
        push(@args, shift(@units));
        }
    $out = &backquote_command("systemctl show -- ".join(" ", @args));
    my @lines = split(/\r?\n/, $out);
    my $curr;
    foreach my $l (@lines) {
        my ($n, $v) = split(/=/, $l, 2);
        next if (!$n);
        if (lc($n) eq 'id') {
            $curr = $v;
            $info{$curr} ||= { };
            }
        if ($curr) {
            $info{$curr}->{$n} = $v;
            }
        }
    if ($? && keys(%info) < 2) {
        &error("Failed to read systemd units : $out");
        }
    }

# Extract info we want
my @rv;
foreach my $name (keys %info) {
    my $root = &get_systemd_root($name);
    my $i = $info{$name};
    next if ($i->{'Description'} =~ /^LSB:\s/);
    push(@rv, { 'name' => $name,
            'desc' => $i->{'Description'},
            'legacy' => 0,
            'boot' => $i->{'UnitFileState'} eq 'enabled' ? 1 :
                  $i->{'UnitFileState'} eq 'static' ? 2 : 0,
            'status' => $i->{'ActiveState'} eq 'active',
            'start' => $i->{'ExecStart'},
            'stop' => $i->{'ExecStop'},
            'reload' => $i->{'ExecReload'},
            'pid' => $i->{'ExecMainPID'},
            'file' => $root."/".$name,
          });
    }

# Also add legacy init scripts
my @rls = &get_inittab_runlevel();
foreach my $a (&list_actions()) {
    $a =~ s/\s+\d+$//;
    next if ($done{$a});
    my $f = &action_filename($a);
    my $s = { 'name' => $a,
          'legacy' => 1 };
    $s->{'boot'} = 0;
    foreach my $rl (@rls) {
        my $l = glob("/etc/rc$rl.d/S*$a");
        $s->{'boot'} = 1 if ($l);
        }
    $s->{'desc'} = &init_description($f);
    my $hasarg = &get_action_args($f);
    if ($hasarg->{'status'}) {
        my $r = &action_running($f);
        $s->{'status'} = $r == 1 ? 1 : 0;
        }
    push(@rv, $s);
    }

return sort { $a->{'name'} cmp $b->{'name'} } @rv;
}

=head2 start_systemd_service(name)

Run the systemd service with some name, and return an OK flag and output

=cut
sub start_systemd_service
{
my ($name) = @_;
my $out = &backquote_logged(
    "systemctl start ".quotemeta($name)." 2>&1 </dev/null");
return (!$?, $out);
}

=head2 stop_systemd_service(name)

Shut down the systemctl service with some name, and return an OK flag and output

=cut
sub stop_systemd_service
{
my ($name) = @_;
my $out = &backquote_logged(
    "systemctl stop ".quotemeta($name)." 2>&1 </dev/null");
return (!$?, $out);
}

=head2 restart_systemd_service(name)

Restart the systemd service with some name, and return an OK flag and output

=cut
sub restart_systemd_service
{
my ($name) = @_;
my $out = &backquote_logged(
    "systemctl restart ".quotemeta($name)." 2>&1 </dev/null");
return (!$?, $out);
}

=head2 create_systemd_service(name, description, start-script, stop-script,
                  restart-script, [forks], [pidfile])

Create a new systemd service with the given details.

=cut
sub create_systemd_service
{
my ($name, $desc, $start, $stop, $restart, $forks, $pidfile) = @_;
$start =~ s/\r?\n/ ; /g;
$stop =~ s/\r?\n/ ; /g;
$restart =~ s/\r?\n/ ; /g;
if ($start =~ /<|>/) {
    $start = "sh -c '$start'";
    }
if ($restart =~ /<|>/) {
    $restart = "sh -c '$restart'";
    }
if ($stop =~ /<|>/) {
    $stop = "sh -c '$stop'";
    }
my $cfile = &get_systemd_root($name)."/".$name;
&open_lock_tempfile(CFILE, ">$cfile");
&print_tempfile(CFILE, "[Unit]\n");
&print_tempfile(CFILE, "Description=$desc\n") if ($desc);
&print_tempfile(CFILE, "\n");
&print_tempfile(CFILE, "[Service]\n");
&print_tempfile(CFILE, "ExecStart=$start\n");
&print_tempfile(CFILE, "ExecStop=$stop\n") if ($stop);
&print_tempfile(CFILE, "ExecReload=$restart\n") if ($restart);
&print_tempfile(CFILE, "Type=forking\n") if ($forks);
&print_tempfile(CFILE, "PIDFile=$pidfile\n") if ($pidfile);
&print_tempfile(CFILE, "\n");
&print_tempfile(CFILE, "[Install]\n");
&print_tempfile(CFILE, "WantedBy=multi-user.target\n");
&close_tempfile(CFILE);
&restart_systemd();
}

=head2 delete_systemd_service(name)

Delete all traces of some systemd service

=cut
sub delete_systemd_service
{
my ($name) = @_;
&unlink_logged(&get_systemd_root($name)."/".$name);
&unlink_logged(&get_systemd_root($name)."/".$name.".service");
&restart_systemd();
}

=head2 is_systemd_service(name)

Returns 1 if some service is managed by systemd

=cut
sub is_systemd_service
{
my ($name) = @_;
foreach my $s (&list_systemd_services()) {
    if ($s->{'name'} eq $name && !$s->{'legacy'}) {
        return 1;
        }
    }
return 0;
}

=head2 get_systemd_root([name])

Returns the base directory for systemd unit config files

=cut
sub get_systemd_root
{
my ($name) = @_;
if ($name && (-r "/etc/systemd/system/$name.service" ||
          -r "/etc/systemd/system/$name")) {
    return "/etc/systemd/system";
    }
return "/lib/systemd/system";
}

=head2 restart_systemd()

Tell the systemd daemon to re-read its config

=cut
sub restart_systemd
{
my @pids = &find_byname("systemd");
if (@pids) {
    &kill_logged('HUP', @pids);
    &system_logged("systemctl --system daemon-reload >/dev/null 2>&1");
    }
}

=head2 reboot_system

Immediately reboots the system.

=cut
sub reboot_system
{
&system_logged("$config{'reboot_command'} >$null_file 2>$null_file");
}

=head2 shutdown_system

Immediately shuts down the system.

=cut
sub shutdown_system
{
&system_logged("$config{'shutdown_command'} >$null_file 2>$null_file");
}

# get_action_args(filename)
# Returns the args that this action script appears to support, like stop, start
# and status.
sub get_action_args
{
my ($file) = @_;
my %hasarg;
open(FILE, $file);
while(<FILE>) {
    if (/^\s*(['"]?)([a-z]+)\1\)/i) {
        $hasarg{$2}++;
        }
    }
close(FILE);
return \%hasarg;
}

# action_running(filename)
# Assuming some init.d action supports the status parameter, returns a 1 if
# running, 0 if not, or -1 if unknown
sub action_running
{
my ($file) = @_;
&clean_language();
my ($out, $timedout) = &backquote_with_timeout("$file status", 2);
&reset_environment();
if ($timedout) {
    return -1;
    }
elsif ($out =~ /not\s+running/i ||
       $out =~ /no\s+server\s+running/i) {
    return 0;
    }
elsif ($out =~ /running/i) {
    return 1;
    }
elsif ($out =~ /stopped/i) {
    return 0;
    }
return -1;
}

1;

:: Command execute ::

Enter:
 
Select:
 

:: Shadow's tricks :D ::

Useful Commands
 
Warning. Kernel may be alerted using higher levels
Kernel Info:

:: Preddy's tricks :D ::

Php Safe-Mode Bypass (Read Files)

File:

eg: /etc/passwd

Php Safe-Mode Bypass (List Directories):

Dir:

eg: /etc/

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c999shell v. 1.0 pre-release build #16 Modded by Shadow & Preddy | RootShell Security Group | r57 c99 shell | Generation time: 0.0125 ]--