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

# Simple fixed media configurator. Designed to be architecture- and distribution independent.
#
# Copyright (C) 2000-2001 Ximian, Inc.
#
# Authors: Hans Petter Jansson <hpj@ximian.com>
#
# 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.

# Configuration files affected:
#
# /etc/fstab

# Running programs affected/used:
#
# fdisk
# mount


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/util.pl$DOTIN";
  require "$SCRIPTSDIR/file.pl$DOTIN";
  require "$SCRIPTSDIR/xml.pl$DOTIN";
  require "$SCRIPTSDIR/filesys.pl$DOTIN";
}


# --- Tool information --- #

$name = "disks";
$version = "0.11.0";
@platforms = ("redhat-5.2", "redhat-6.0", "redhat-6.1", "redhat-6.2", "redhat-7.0",
              "redhat-7.1",  "mandrake-7.2", "debian-2.2", "debian-woody", 
	      "suse-7.0", "turbolinux-7.0");

$description =<<"end_of_description;";
       Configures locally mounted partitioned media.
end_of_description;

$progress_max = 16;


# --- System config file locations --- #

# We list each config file type with as many alternate locations as possible.
# They are tried in array order. First found = used.

# Right now there's only one entry per array, as I couldn't find any
# typical deviations.

@fstab_names = ( "/etc/fstab" );


# --- Internal configuration variables --- #

# Configuration is parsed/read to, and printed/written from, these temporary variables.

@cf_disks = ();


# --- Backend-specific helper subs --- #

sub update_partition
{
  my ($disk, $device, $point, $fs, $options, $check);
  my ($listed, $bootable, $detected);
  my ($disk_found, $point_found) = (0, 0);
  my $label;

  ($disk, $device, $point, $fs, $options, $check, $size, $listed,
   $bootable, $detected, $label) = @_;

  if ($fs eq "auto") { $fs = ""; }
  if ($label eq "")
  {
    if ($device eq "") { return; }
    $label = xst_filesys_ext2_device_to_label ($device);
  }

  for ($i = 0; $cf_disks[$i]; $i++)
  {
    if ($disk eq "" || ($cf_disks[$i])->{device} eq $disk)
    {
      # Found disk. Now look for partition.
          
      for ($j = 0; $cf_disks[$i]->{partitions}[$j]; $j++)
      {
        if ($cf_disks[$i]->{partitions}[$j]->{device} eq $device ||
            ($label ne "") && ($cf_disks[$i]->{partitions}[$j]->{label} eq $label))
        {
          # Found partition.
          
          if ($options ne "")
          {
            if ($options =~ /noauto/)
            {
              $cf_disks[$i]->{partitions}[$j]->{mounted} = 0;
            }
            else
            {
              $cf_disks[$i]->{partitions}[$j]->{mounted} = 1;
            }
          }

          if ($point ne "") { $cf_disks[$i]->{partitions}[$j]->{point} = $point; }
          if ($fs) { $cf_disks[$i]->{partitions}[$j]->{type} = $fs; }
          if ($listed) { $cf_disks[$i]->{partitions}[$j]->{listed} = 1; }
          if ($bootable) { $cf_disks[$i]->{partitions}[$j]->{bootable} = 1; }
          if ($detected) { $cf_disks[$i]->{partitions}[$j]->{detected} = 1; }
          if ($check) { $cf_disks[$i]->{partitions}[$j]->{check} = 1; }
          if ($size) { $cf_disks[$i]->{partitions}[$j]->{size} = $size; }
          if ($label) { $cf_disks[$i]->{partitions}[$j]->{label} = $label; }

          $disk_found = 1;
          $point_found = 1; last;
        }
      }

      if (!$point_found && $device)
      {
        # Make new partition entry.

        my %partition;

        if ($options =~ /noauto/) { %partition->{mounted} = 0; }
        else                      { %partition->{mounted} = 1; }

        %partition->{device} = $device;
        %partition->{point} = $point;
        %partition->{type} = $fs;
        %partition->{listed} = $listed;
        if ($bootable) { %partition->{bootable} = 1; }
        if ($detected) { %partition->{detected} = 1; }
        if ($check) { %partition->{check} = 1; }
        %partition->{size} = $size;
        %partition->{label} = $label;

        $bleh = $cf_disks[$i]->{partitions};
        push(@$bleh, \%partition);

        $disk_found = 1; last;
      }
    }
  }

  if (!$disk_found)
  {
    # Make new disk entry containing this partition.

    my (%disk, %partition);

    if ($options =~ /noauto/) { %partition->{mounted} = 0; }
    else                      { %partition->{mounted} = 1; }

    %partition->{device} = $device;
    %partition->{point} = $point;
    %partition->{type} = $fs;
    %partition->{listed} = $listed;
    if ($bootable) { %partition->{bootable} = 1; }
    if ($detected) { %partition->{detected} = 1; }
    if ($check) { %partition->{check} = 1; }
    %partition->{size} = $size;
    %partition->{label} = $label;

    %disk->{device} = $disk;
    %disk->{partitions} = [];

    $bleh = %disk->{partitions};
    push(@$bleh, \%partition);
    push(@cf_disks, \%disk);
  }
}


sub get_partition
{
  my ($disk, $device, $label) = @_;
  my ($i, $j);

  for ($i = 0; $cf_disks[$i]; $i++)
  {
    if ($disk eq "" || ($cf_disks[$i])->{device} eq $disk)
    {
      # Found disk. Now look for partition.

      for ($j = 0; $cf_disks[$i]->{partitions}[$j]; $j++)
      {
        if ($cf_disks[$i]->{partitions}[$j]->{device} eq $device ||
            ($device eq "" && $cf_disks[$i]->{partitions}[$j]->{label} eq $label))
        {
          # Found partition.

          return ($cf_disks[$i]->{partitions}[$j]);
        }
      }
    }
  }
}


sub get_partition_data
{
  my ($disk, $device, $label);

  ($disk, $device, $label) = @_;

  for ($i = 0; $cf_disks[$i]; $i++)
  {
    if ($disk eq "" || ($cf_disks[$i])->{device} eq $disk)
    {
      # Found disk. Now look for partition.

      for ($j = 0; $cf_disks[$i]->{partitions}[$j]; $j++)
      {
        if ($cf_disks[$i]->{partitions}[$j]->{device} eq $device ||
            ($device eq "" && $cf_disks[$i]->{partitions}[$j]->{label} eq $label))
        {
          # Found partition.

          return ($cf_disks[$i]->{partitions}[$j]->{point},
                  $cf_disks[$i]->{partitions}[$j]->{type},
                  $cf_disks[$i]->{partitions}[$j]->{listed},
                  $cf_disks[$i]->{partitions}[$j]->{mounted},
                  $cf_disks[$i]->{partitions}[$j]->{bootable},
                  $cf_disks[$i]->{partitions}[$j]->{check},
                  $cf_disks[$i]->{partitions}[$j]->{label});
        }
      }
    }
  }
}


sub update_disk_size
{
  my ($disk, $size);

  ($disk, $size) = @_;

  for ($i = 0; $cf_disks[$i]; $i++)
  {
    if (($cf_disks[$i])->{device} eq $disk)
    {
      # Found disk.
      
      $cf_disks[$i]->{size} = $size;
      last;
    }
  }
}


# --- Configuration file manipulation --- #


# /etc/fstab
#
# <device> <mount point> <filesystem> <option,option,...> <dump> <fsck>
# <device> <mount point> <filesystem> <option,option,...> <dump> <fsck>
# ...
#
# Exists: (Presumably everywhere)
#
# Absent: (Presumably nowhere)

sub read_fstab
{
  my $fstab_file;
  local *FSTAB_FILE;

  # Find the file.

  $fstab_file = &xst_file_open_read_from_names(@fstab_names);
  if (not $fstab_file) { return; }  # We didn't find it.
  *FSTAB_FILE = $fstab_file;

  # Parse the file.

  while (<FSTAB_FILE>)
  {
    my ($disk, $device, $point, $fs, $options, $check, $label);

    @line = split(/[ \n\r\t]+/, $_);

    if ($line[0] eq "") { shift @line; }
    if ($line[0] eq "") { next; }
    if (&xst_ignore_line($line[0])) { next; }
    ($device, $point, $fs, $options, $dump, $check) = @line;

    if ($device =~ /$LABEL=(.*)/) { $label = $1; $device = ""; }
    else                          { $label = ""; }

    if ($fs eq "nfs" || $fs eq "smbfs" || $fs eq "proc" || $fs eq "devpts" ||
        $fs eq "iso9660" || $fs eq "swap")
    {
      next;  # We can skip these filesystems for sure.
    }

    if ($point eq "none") { $dir = ""; }
    ($disk) = ($device =~ /([a-zA-Z\/]+)/);

    if ($disk eq "/dev/fd" || ($disk ne "" && $disk eq $device))
    {
      next;  # Skip floppies and CD-ROMs.
    }

    # (Find and update) or (add) our internal disk/partition record.

    if ($disk ne "" || $label ne "")
    {
      &update_partition($disk, $device, $point, $fs, $options, $check, "", 1, 0, "", $label);
    }
  }

  close(FILE);
}


sub write_fstab
{
  my ($ifh, $ofh);
  local (*INFILE, *OUTFILE);

  ($ifh, $ofh) = &xst_file_open_filter_write_from_names(@fstab_names);
  if (not $ofh) { return; }  # No point if we can't write.
  *INFILE = $ifh; *OUTFILE = $ofh;

  while (<INFILE>)
  {
    my ($disk, $device, $point, $fs, $options, $dump, $check, $label);
    my ($ipoint, $itype, $icheck, $ilisted, $imounted, $ibootable, $ilabel);

    @line = split(/[ \n\r\t]+/, $_);

    if ($line[0] eq "") { shift @line; }
    if ($line[0] eq "") { print OUTFILE; next; }
    if (&xst_ignore_line($line[0])) { print OUTFILE; next; }
    ($device, $point, $fs, $options, $dump, $check) = @line;

    if ($fs eq "nfs" || $fs eq "smbfs" || $fs eq "proc" || $fs eq "devpts" ||
        $fs eq "iso9660" || $fs eq "swap" || $device =~ /$\/dev\/fd.*/)
    {
      print OUTFILE; next;  # We can skip these filesystems for sure.
    }

    # By now, we know that the "entry" is "interesting". Check if known.

    if ($device =~ /$LABEL=(.*)/)
    {
      $label = $1;
      $device = "";
      $disk = "";
    }
    else
    {
      $label = "";
      ($disk) = ($device =~ /([a-zA-Z\/]+)/);
    }

    if ($disk eq "/dev/fd") { print OUTFILE; next; }

    ($ipoint, $itype, $ilisted, $imounted, $ibootable, $icheck, $ilabel) =
      &get_partition_data($disk, $device, $label);

    if ($ilisted)
    {
      # Write record if listedness requested.

      if ($ilabel ne "")
      {
        print OUTFILE "LABEL=" . $ilabel . " ";
      }
      else
      {
        print OUTFILE $device . " ";
      }

      if ($ipoint eq "") { print OUTFILE "none "; }
      else { print OUTFILE $ipoint . " "; }

      if ($itype eq "") { print OUTFILE "auto "; }
      else              { print OUTFILE $itype . " "; }

      # Options merging and printing.

      my $prev = 0;
      if (!$imounted) { print OUTFILE "noauto"; $prev = 1; }
      my @options = ($options =~ /([a-zA-Z0-9=-]+),?/mg);
      for $option (@options)
      {
        # Strip options we handle, keep the rest.

        if ($option eq "auto" || $option eq "noauto" ||
            $option eq "defaults") { next; }
        if ($prev) { print OUTFILE ","; }
        print OUTFILE $option;
        $prev = 1;
      }
      
      if (!$prev) { print OUTFILE "defaults"; }
      
      # Leave dump alone.
      
      print OUTFILE " $dump ";
      
      # Fsck onboot priority.

      if ($icheck eq "") { $icheck = 0; }
      if ($icheck == 1)
      {
        if ($ipoint eq "/") { print OUTFILE "1\n"; }
        else { print OUTFILE "2\n"; }
      }
      else { print OUTFILE "0\n"; }
      
      # Indicate that parameters for this partition have been stored.
      
      my $partition = &get_partition($disk, $device, $label);
      %$partition->{stored} = 1;
    }
    
    # Unknown or unlisted-by-request partitions are not written.
  }
  
  # Print the remaining partitions from our internal list. These are
  # newly added, and didn't exist in the fstab previously.
  
  for ($i = 0; $cf_disks[$i]; $i++)
  {
    for ($j = 0; $cf_disks[$i]->{partitions}[$j]; $j++)
    {
      if ($cf_disks[$i]->{partitions}[$j]->{listed} &&
          !$cf_disks[$i]->{partitions}[$j]->{stored})
      {
        my $part = $cf_disks[$i]->{partitions}[$j];

        &xst_report ("disks_fstab_add", $cf_disks[$i]->{partitions}[$j]->{device});
        
        # Write record.

        print OUTFILE %$part->{device} . " ";
        if (%$part->{point} eq "") { print OUTFILE "none "; }
        else { print OUTFILE %$part->{point} . " "; }

        if (%$part->{type} eq "") { print OUTFILE "auto "; }
        else                     { print OUTFILE %$part->{type} . " "; }

        # Options printing.

        if (!%$part->{mounted}) { print OUTFILE "noauto "; }
        else { print OUTFILE "defaults "; }

        # No dumping by default.

        print OUTFILE " 0 ";
      
        # Fsck onboot priority.

        if (%$part->{check} == 1)
        {
          if (%$part->{point} eq "/") { print OUTFILE "1\n"; }
          else { print OUTFILE "2\n"; }
        }
        else { print OUTFILE "0\n"; }
      }
    }
  }
  
  close OUTFILE;
}


# fdisk -l <disk device>
#
# <&filtered lines>
#
# <partition device> [*] <start cluster> <end cluster> <blocks> <id> <verbose id>
# <partition device> [*] <start cluster> <end cluster> <blocks> <id> <verbose id>
# ...
#
# Exists: Red Hat 6.2 (Presumably all Linux)
#
# Absent:
#
# The star is optional, and means that the partition is bootable. We could've
# used /proc/partitions to get this information, but it lists CD-ROM devices
# and whatnot as well, without a disambiguating identifier. Then there is
# portability, forward-compatibility, people without /proc, etc.

# fdisk -s <disk device>
#
# <blocksize>

sub get_fdisk
{
  my $fdisk_tool;

  # Okay, so this is strictly not portable either. Patches welcome.

  my @check_devs = ( "/dev/hda", "/dev/hdb", "/dev/hdc", "/dev/hdd",
                     "/dev/hde", "/dev/hdf", "/dev/hdg", "/dev/hdh",

                     "/dev/sda", "/dev/sdb", "/dev/sdc", "/dev/sdd",
                     "/dev/sde", "/dev/sdf",
                     
                     "/dev/eda", "/dev/edb", "/dev/edc", "/dev/edd",
                     
                     "/dev/xda", "/dev/xdb" );

  $fdisk_tool = &xst_file_locate_tool("fdisk");

  for $dev (@check_devs)
  {
    my ($disk, $device, $point, $fs, $options, $check, $size, $bootable, $fd);
    
    &xst_report ("disks_partition_probe", $dev);

    $fd = &xst_file_run_pipe_read ("fdisk -l $dev");
    while (<$fd>)
    {
      if (/^\/dev/)
      {
        @line = split(/[ \n\r\t]+/, $_);

        $device = $line[0]; shift @line;
        ($disk) = ($device =~ /([a-zA-Z\/]+)/);
        if ($line[0] eq "\*")
        {
          # NOTE: Currently unused.
          $bootable = 1; shift @line;
        }
        else { $bootable = 0; }

        shift @line; shift @line;  # Start and end clusters.

        ($size) = ($line[0] =~ /([0-9]+)/);
        shift @line;

        # FIXME: add new popular ones, such as reiser and xfs and add
        # those documented by fdisk.
        if    ($line[0] eq "82" || $line[0] eq "5"  ||
               $line[0] eq "f"  || $line[0] eq "85")  { next; }  # Swap or extended.
        elsif ($line[0] eq "83")                     { $type = "ext2"; }
        elsif ($line[0] eq "e")                      { $type = "vfat"; }
        elsif ($line[0] eq "c")                      { $type = "fat32"; }
        elsif ($line[0] eq "b")                      { $type = "fat32"; }
        elsif ($line[0] eq "6")                      { $type = "msdos"; }
        elsif ($line[0] eq "4")                      { $type = "msdos"; }
        elsif ($line[0] eq "1")                      { $type = "msdos"; }
        elsif ($line[0] eq "7")                      { $type = "hpfs"; }
        else { $type = ""; }

        &update_partition($disk, $device, "", $type, "noauto", 0, $size, 0,
                          $bootable, 1);
      }
    }
    &xst_file_close ($fd);

    &xst_report ("disks_size_query", $dev);

    $fd = &xst_file_run_pipe_read ("fdisk -s $de");
    ($size) = (<$fd> =~ /([0-9]+)/);
    if ($size ne "") { &update_disk_size($dev, $size); }
    &xst_file_close ($fd);
    
    &xst_print_progress();
  }
}


# --- XML parsing --- #


sub xml_parse
{
  # Scan XML to tree.

  $tree = &xst_xml_scan;

  # Walk the tree recursively and extract configuration parameters.
  # This is the top level - find and enter the toplevel tag.

  while (@$tree)
  {
    if ($$tree[0] eq "disks") { &xml_parse_toplevel($$tree[1]); }

    shift @$tree;
    shift @$tree;
  }

  return($tree);
}


sub xml_parse_toplevel
{
  my $tree = $_[0];

  shift @$tree;  # Skip attributes.

  while (@$tree)
  {
    if ($$tree[0] eq "disk") { &xml_parse_disk($$tree[1]); }

    shift @$tree;
    shift @$tree;
  }
}


sub xml_parse_disk
{
  my %disk;

  my $tree = $_[0];
  shift @$tree;  # Skip attributes.

  %disk->{partitions} = [];  # Init partition list.

  while (@$tree)
  {
    if ($$tree[0] eq "device") { %disk->{device} = &xst_xml_get_word($$tree[1]); }
    elsif ($$tree[0] eq "size") { %disk->{size} = &xst_xml_get_word($$tree[1]); }
    elsif ($$tree[0] eq "partition")
    {
      my %partition = &xml_parse_partition($$tree[1]);
      $bleh = %disk->{partitions};
      push(@$bleh, \%partition);
    }
    
    shift @$tree; shift @$tree;
  }

  push(@cf_disks, \%disk);
}


sub xml_parse_partition
{
  my %partition;

  my $tree = $_[0];
  shift @$tree;  # Skip attributes.

  while (@$tree)
  {
    if ($$tree[0] eq "device") { %partition->{device} = &xst_xml_get_word($$tree[1]); }
    elsif ($$tree[0] eq "type") { %partition->{type} = &xst_xml_get_word($$tree[1]); }
    elsif ($$tree[0] eq "point") { %partition->{point} = &xst_xml_get_word($$tree[1]); }
    elsif ($$tree[0] eq "label") { %partition->{label} = &xst_xml_get_word($$tree[1]); }
    elsif ($$tree[0] eq "size") { %partition->{size} = &xst_xml_get_word($$tree[1]); }
    elsif ($$tree[0] eq "bootable") { %partition->{bootable} = &xml_parse_state($$tree[1]); }
    elsif ($$tree[0] eq "integritycheck") { %partition->{check} = &xml_parse_state($$tree[1]); }
    elsif ($$tree[0] eq "mounted") { %partition->{mounted} = &xml_parse_state($$tree[1]); }
    elsif ($$tree[0] eq "listed") { %partition->{listed} = &xml_parse_state($$tree[1]); }
    elsif ($$tree[0] eq "detected") { %partition->{detected} = &xml_parse_state($$tree[1]); }
    
    shift @$tree; shift @$tree;
  }

  return(%partition);
}


sub xml_parse_state
{
  my $tree = $_[0];

  # Check attribute; 'yes', 'true', 'no', 'false'.

  return(&xst_util_read_boolean($$tree[0]->{state}));
}


# --- XML printing --- #


sub xml_print
{
  &xst_xml_print_begin ();

  &xst_xml_print_line ("<!-- Local fixed media and partitions -->\n");
  &xst_xml_print_vspace ();

  my @disks = @cf_disks;
  
  while (@disks)
  {
    if ($disks[0])
    {
      my $disk = $disks[0];
      
      &xst_xml_print_vspace ();
      &xst_xml_print_line ("<disk>\n");
      &xst_xml_enter ();

      &xst_xml_print_line ("<device>" . %$disk->{device} . "</device>\n");
      if (%$disk->{size}) { &xst_xml_print_line ("<size>" . %$disk->{size} . "</size>\n"); }
      
      my $partitions = %$disk->{partitions};
      
      while (@$partitions)
      {
        my $partition = $$partitions[0];

        &xst_xml_print_vspace ();
        &xst_xml_print_line ("<partition>\n");
        &xst_xml_enter ();

        &xst_xml_print_line ("<device>" . %$partition->{device} . "</device>\n");
        if (%$partition->{type}) { &xst_xml_print_line ("<type>" . %$partition->{type} . "</type>\n"); }
        if (%$partition->{point}) { &xst_xml_print_line ("<point>" . %$partition->{point} . "</point>\n"); }
        if (%$partition->{label}) { &xst_xml_print_line ("<label>" . %$partition->{label} . "</label>\n"); }
        if (%$partition->{size})
        {
          &xst_xml_print_line ("<size>" . %$partition->{size} . "</size>\n");
        }

        &xst_xml_print_state_tag ("bootable", %$partition->{bootable});
        &xst_xml_print_state_tag ("integritycheck", %$partition->{check});
        &xst_xml_print_state_tag ("mounted", %$partition->{mounted});
        &xst_xml_print_state_tag ("listed", %$partition->{listed});
        &xst_xml_print_state_tag ("detected", %$partition->{detected});

        &xst_xml_leave ();
        &xst_xml_print_line ("</partition>\n");
        
        shift @$partitions;
      }

      &xst_xml_leave ();
      &xst_xml_print_line ("</disk>\n");
      &xst_xml_print_vspace ();
    }
  
    shift @disks;
  }

  &xst_xml_print_end ();
}


# --- Get (read) config --- #


sub get
{
  &get_fdisk;
  &read_fstab;

  &xst_report_end ();
  &xml_print ();
}


# --- Set (write) config --- #


sub set_immediate
{
  my $mount_tool;
  my $umount_tool;

  $mount_tool = &xst_file_locate_tool("mount");
  $umount_tool = &xst_file_locate_tool("umount");

  # Count partitions.

  my $i; my $j;
  my $num_partitions = 0;
  my $num_done = 0;
  
  for ($i = 0; $cf_disks[$i]; $i++)
  {
    for ($j = 0; $cf_disks[$i]->{partitions}[$j]; $j++)
    {
      $num_partitions++;
    }
  }

  # Update mount status.

  if (($mount_tool ne "") && ($umount_tool ne ""))
  {
    my $i; my $j;

    for ($i = 0; $cf_disks[$i]; $i++)
    {
      for ($j = 0; $cf_disks[$i]->{partitions}[$j]; $j++)
      {
        if ($cf_disks[$i]->{partitions}[$j]->{mounted})
        {
          &xst_report ("disks_mount", $cf_disks[$i]->{partitions}[$j]->{device});
          system "$mount_tool " . $cf_disks[$i]->{partitions}[$j]->{device} . " " .
                 $cf_disks[$i]->{partitions}[$j]->{point} . " >/dev/null 2>/dev/null";
        }
        else
        {
          &xst_report ("disks_umount", $cf_disks[$i]->{partitions}[$j]->{device});
          system "$umount_tool " . $cf_disks[$i]->{partitions}[$j]->{device} . " >/dev/null 2>/dev/null";
        }
	
        $num_done++;
        &xst_progress(10 + (80 / ($num_partitions - $num_done + 1)));
      }
    }
  }
  else
  {
    &xst_report ("disks_mount_error");
  }
  
  &xst_progress(90);
}


sub set
{
  &xml_parse ();

  &write_fstab; &xst_progress(10);

  if ($xst_do_immediate)
  {
    &set_immediate;
  }
  
  &xst_report_end ();
}


# --- Filter config: XML in, XML out --- #


sub filter
{
  &xml_parse ();
  &xst_report_end ();
  &xml_print ();
}


# --- Main --- #

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

$directives = {
  "get"    => [ \&get,    [], "" ],
  "set"    => [ \&set,    [], "" ],
  "filter" => [ \&filter, [], "" ]
    };

$tool = &xst_init ($name, $version, $description, $directives, @ARGV);
&xst_platform_ensure_supported ($tool, @platforms);
&xst_run ($tool);
