#!/usr/bin/env perl
#-*- Mode: perl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-

# SysV services configurator. Aims to be designed to be architecture- and distribution independent.
#
# Copyright (C) 2002 Ximian, Inc.
#
# Authors: Carlos Garnacho Parro <garparr@teleline.es>
#          Grzegorz Golawski <grzegol@pld-linux.org> (PLD Support)
#
# 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.

# Best viewed with 100 columns of width.

BEGIN {
  $SCRIPTSDIR = "/usr/share/setup-tool-backends/scripts";
  if ($SCRIPTSDIR =~ /^@scriptsdir[@]/)
  {
      $SCRIPTSDIR = ".";
      $DOTIN = ".in";
  }
  
  require "$SCRIPTSDIR/general.pl$DOTIN";
  require "$SCRIPTSDIR/platform.pl$DOTIN";
  require "$SCRIPTSDIR/service.pl$DOTIN";
  require "$SCRIPTSDIR/print.pl$DOTIN";
}

# --- Tool information --- #

$name = "services";
$version = "1.4.2";
@platforms = ("redhat-5.2", "redhat-6.0", "redhat-6.1", "redhat-6.2", "redhat-7.0", "redhat-7.1",
              "redhat-7.2", "redhat-7.3", "redhat-8.0", "redhat-9",
              "openna-1.0",
              
              "mandrake-7.1", "mandrake-7.2", "mandrake-9.0", "mandrake-9.1", "mandrake-9.2",
              "mandrake-10.0", "mandrake-10.1",
              
              "debian-3.0", "debian-3.1", "debian-4.0", "debian-testing",

              "suse-7.0", "turbolinux-7.0", "fedora-1", "fedora-2", "fedora-3", "rpath",
              "pld-1.0", "pld-1.1", "pld-1.99",
              "slackware-9.1.0", "slackware-10.0.0", "slackware-10.1.0", "slackware-10.2.0",
              "vine-3.0", "vine-3.1",
              "gentoo", "archlinux", "vlos-1.2", "freebsd-5", "freebsd-6", "suse-9.0", "suse-9.1");

$description =<<"end_of_description;";
       Configures which services are to be started or stopped at which runlevels
       and with what priority. System V, file-rc and bsd init, in short.
end_of_description;

# --- XML parsing --- #

sub xml_parse
{
  my ($tree, %hash, $elem);
  $tree = &gst_xml_scan ();

  # Walk the tree recursively and extract configuration parameters.
  while ($elem = shift @$tree)
  {
    if   ($elem eq "services") { &xml_parse_services (shift @$tree, \%hash); }
    else {&gst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }

  return (\%hash);
}

sub xml_parse_services
{
  my ($tree, $hash) = @_;
  my ($services, $elem);
  my ($runlevel);
	
  shift @$tree; #skip attributes

  while ($elem = shift @$tree)
  {
    if ($elem eq "runlevels") { $runlevel = &xml_parse_runlevel_list (shift @$tree)}
    elsif ($elem eq "services") { push @$services, &xml_parse_service_list (shift @$tree); }
    else { &gst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }

  $$hash{"services"} = $services;
  $$hash{"runlevel"} = $runlevel;
}

sub xml_parse_runlevel_list
{
  my ($tree) = @_;
  my ($runlevels, $elem, $runlevel);
  my ($rl);

  shift @$tree; #skip attributes

  while ($elem = shift @$tree)
  {
    if ($elem eq "runlevel")
    {
      $rl = &xml_parse_runlevel (shift @$tree);
      if ($rl ne undef)
      {
        $runlevel = $rl;
      }
    }
    else { &gst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }

  return $runlevel;
}

sub xml_parse_runlevel
{
  my ($tree) = @_;
  my ($elem, $default, $runlevel);

  shift @$tree; #skip attributes

  while ($elem = shift @$tree)
  {
    if ($elem eq "default") {  $default = 1; shift @$tree }
    elsif ($elem eq "name") { $runlevel = &gst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "role") { shift @$tree; }
    else { &gst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }

  if ($default eq 1)
  {
    return $runlevel;
  }
}

sub xml_parse_service_list
{
  my ($tree) = @_;
  my ($hash, $service, $elem);

  shift @$tree; #skip attributes

  while ($elem = shift @$tree)
  {
    if ($elem eq "service") { push @$service, &xml_parse_service (shift @$tree); }
    else { &gst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }

  $$hash{"service"} = $service unless scalar @$service == 0;
  return ($hash);
}

sub xml_parse_service
{
  my ($tree) = @_;
  my ($hash, $actions, $runlevels, $elem);

  shift @$tree; #skip attributes

  while ($elem = shift @$tree)
  {
    if ($elem eq "actions") { push @$actions, &xml_parse_actions (shift @$tree); }
    elsif ($elem eq "role") { shift @$tree }
    elsif ($elem eq "runlevels") { push @$runlevels, &xml_parse_runlevels (shift @$tree); }
    elsif ($elem eq "script") { $$hash{"script"} = &gst_xml_get_pcdata (shift @$tree); }
    else { &gst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }
  
  $$hash{"actions"} = $actions unless $actions eq undef;
  $$hash{"runlevels"} = $runlevels unless $runlevels eq undef;
  
  return ($hash);
}

sub xml_parse_actions
{
  my ($tree) = @_;
  my ($hash, $action, $elem);

  shift @$tree; #skip attributes

  while ($elem = shift @$tree)
  {
    if ($elem eq "action") { push @$action, &gst_xml_get_pcdata (shift @$tree); }
	else { &gst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }
  
  if (scalar @$action != 0)
  {
    $$hash{"action"} = $action;
    return ($hash);
  }
}

sub xml_parse_runlevels
{
  my ($tree) = @_;
  my ($hash, $runlevel, $elem);

  shift @$tree; #skip attributes

  while ($elem = shift @$tree)
  {
    if ($elem eq "runlevel") { push @$runlevel, &xml_parse_single_runlevel (shift @$tree); }
	else { &gst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }

  if (scalar @$runlevel != 0)
  {
    $$hash{"runlevel"} = $runlevel;
    return ($hash);
  }
}

sub xml_parse_single_runlevel
{
  my ($tree) = @_;
  my ($number, $action, $priority);
  my ($elem);
  
  shift @$tree; #skip attributes
  
  while ($elem = shift @$tree)
  {
    if ($elem eq "name") { $number = &gst_xml_get_pcdata (shift @$tree); }
  	elsif ($elem eq "action") { $action = &gst_xml_get_pcdata (shift @$tree); }
    elsif ($elem eq "priority") { $priority = &gst_xml_get_pcdata (shift @$tree); }
    else { &gst_report ("xml_unexp_tag", $elem); shift @$tree; }
  }
  
  return { "name"     => $number,
           "action"   => $action,
           "priority" => $priority };
}

# --- XML printing --- #
sub xml_print_services
{
  my ($h) = @_;

  &gst_xml_print_line ("<services>");
  &gst_xml_enter ();

  &gst_xml_print_hash_hash ($h, "service");

  &gst_xml_leave ();
  &gst_xml_print_line ("</services>");
}

sub xml_print_runlevels
{
  my ($h, $default) = @_;
  &gst_xml_print_line ("<runlevels>");
  &gst_xml_enter ();

  foreach $i (sort keys %$h)
  {
    &gst_xml_print_line ("<runlevel>");
    &gst_xml_enter ();

    &gst_xml_print_pcdata ("name", $i);
    &gst_xml_print_pcdata ("role", $$h{$i});

    if ($i eq $default)
    {
      # It's the default runlevel
      &gst_xml_print_pcdata ("default", "1");
    }
      
    &gst_xml_leave ();
    &gst_xml_print_line ("</runlevel>");
  }
  
  &gst_xml_leave ();
  &gst_xml_print_line ("</runlevels>");
  &gst_xml_print_vspace ();
}

sub xml_print
{
  my ($h) = @_;

  &gst_xml_print_begin ();
  &gst_xml_print_comment ("These are the runlevels available in the system");
  &xml_print_runlevels ($$h{"runlevels"}, $$h{"runlevel"});
  
  &gst_xml_print_comment ("These are the services");
  &xml_print_services ($$h{"services"});
  &gst_xml_print_end ();
}


# Main operations

sub get
{
  my %hash;

  $hash{"runlevels"} = &gst_service_get_runlevel_roles ();

  $hash{"services"} = &gst_service_get_services ();
  $hash{"runlevel"} = &gst_service_get_default_runlevel ();

  &gst_report_end ();
  &xml_print (\%hash);
}

sub set
{
  my %hash;
	
  $hash = &xml_parse ();

  if ($hash)
  {
    &gst_service_set_conf ($hash);
  }

  &gst_report_end ();
}

# --- Filter config: XML in, XML out --- #
sub filter
{
  my $hash = &xml_parse ();

  &gst_report_end ();
  &xml_print ($hash);
}

# --- throw_service: throws a service with a parameter --- #
sub throw_service
{
  my ($tool, $service, $parameter) = @_;

  &gst_service_run_script ($service, $parameter);
  &gst_report_end ();

  &gst_xml_print_begin ("throw_service");
  &gst_xml_print_end ("throw_service");
}

# --- get_status: gets the status of a service --- #
sub get_status
{
  my ($tool, $service) = @_;
  my ($active);

  $active = &gst_service_get_status ($service);

  &gst_report_end ();

  &gst_xml_print_begin ("get_status");
  &gst_xml_print_state_tag ("active", $active) if ($active ne undef);
  &gst_xml_print_end ("get_status");
}

# --- Main --- #

# get, set and filter are special cases that don't need more parameters than a ref to their function.
# Read general.pl.in:gst_run_directive to know about the format of this hash.

$directives = {
  "get"                => [ \&get,    [], "" ],
  "set"                => [ \&set,    [], "" ],
  "filter"             => [ \&filter, [], "" ],
  "throw_service"      => [ \&throw_service, ["service", "parameter"], "Throws a service with a parameter (start, stop...)" ],
  "get_status"         => [ \&get_status, ["service"], "Gets the status of a service"]
  };

$tool = &gst_init ($name, $version, $description, $directives, @ARGV);
&gst_platform_ensure_supported ($tool, @platforms);
&gst_run ($tool);
