#!/usr/bin/perl -w

# $Id: savepermscvs.pl,v 1.1 2001/04/11 14:32:04 stephand Exp $

# 20040411 stephan.duehr@suse.de
# Funktion dieses Scripts:
# Sichern der Permissions von Konfigurationsdateien, die im CVS gepflegt werden
# im Format 
# 	FILEPATH OWNER.GROUP MODE
# wie es von SuSEconfig in /etc/permissions verwendet wird.
# (siehe man chkstat).
#
# Parameter: CVS-Arbeitsverzeichnis
#            (dort mu die Verzeichnisstruktur bezogen auf / abgebildet sein, 
#            z.B. etc, var usw.

use File::Find;
use File::stat;
use strict;

my $cvsworkdir = $ARGV[0];
usage() if (! defined $cvsworkdir);
# Mit "." als Pfadangabe funktionieren diverse Funktionen nicht
# => ersetzen durch vollstndigen absoluten Pfad
if ($cvsworkdir eq ".") {
  $cvsworkdir = $ENV{'PWD'};
}

# Pfad und Dateiname f. Sicherung der Permissions
my $savepath = "etc";
my $savefile = "permissions.cvssave";

# abschlieenden "/" entfernen, falls vorhanden
$cvsworkdir =~ s/\/$//g;

(-d $cvsworkdir) || die "Fehler: $cvsworkdir ist kein Verzeichnis!";

my %files = ();

# cvsfilescb ist die Callback-Funktion
File::Find::find(\&cvsfilescb, $cvsworkdir);

# Permissions sichern bzw. ausgeben, falls Dateien gefunden wurden
# Anzahl Elemente im Hash???
my @files = keys %files;

if ($#files > -1) {
  # generieren der Permissions
  my @permissions = genperm(\%files);
  my $OUTFILE;
  my $tofile = 0;   # Status fr schreiben in File
  if (-d "$cvsworkdir/$savepath") {
    # Verzeichnis existiert => schreiben in $savefile
    print "Sicherung der Permissions in\n$cvsworkdir/$savepath/$savefile\n";
    open(OUTFILE, ">$cvsworkdir/$savepath/$savefile") ||
             die ("kann $cvsworkdir/$savepath/$savefile nicht schreiben");
    $tofile=1;     # Merken, da in File geschrieben wird
  }
  else {
    # Pfad fr Sicherungsdatei existiert nicht => schreiben auf stdout
    # Alias Filehandle fr stdout erzeugen
    *OUTFILE=*STDOUT;
  }
  foreach my $line (@permissions) {
    print OUTFILE "$line\n";
  }
  close(OUTFILE) if ($tofile);

  
}

# --------------------- Funktionen ------------------------

sub cvsfilescb {
  # callbackfunktion fr File::Find
  return unless -f;	# keine Directories
  return if $File::Find::dir =~ /\/CVS$/;	# ignoriere CVS-Verzeichnisse
  return if $_ eq $savefile;  # ignoriere vorhandenes Permission-savefile
  my $cvsworkfile = "$File::Find::dir/$_";
  # Ursprungspfad ermitteln
  $cvsworkfile =~ /${cvsworkdir}\/(.+)/;
  my $realfile = "/" . $1;
  (-r $realfile) || die("Fehler: $realfile existiert nicht oder ist nicht lesbar");
  $files{"$realfile"} = $cvsworkfile;
}

sub genperm {
  # generieren der Zeilen fr Permission-Savefile
  my ($filesref) = @_;
  my @permlist = ();
  foreach my $key (keys %$filesref) {
    my $info = stat($key) || die "$key: stat error";
    my $mode = get_type($info->mode) & 07777;
    my $modestring = sprintf("%04o", $mode);
    my $uid = $info->uid;
    my $uidname = getpwuid($uid);
    my $gid = $info->gid;
    my $gidname = getgrgid($gid);
    #print "$key $uidname" . "." . "$gidname $modestring\n";
    push(@permlist, "$key $uidname" . "." . "$gidname $modestring");
  }
  return @permlist;
}

sub get_type {
    # Funktion bernommen aus /usr/bin/chkstat
    my $S_IFLNK  = 0120000;    # symbolic link
    my $S_IFREG  = 0100000;    # regular file
    my $S_IFDIR  = 0040000;    # directory
    my $S_IFCHAR = 0020000;    # character device
    my $S_IFBLK  = 0060000;    # block device
    my $S_IFFIFO = 0010000;    # fifo
    my $S_IFSOCK = 0140000;    # socket
    my $S_IFMT   = 0170000;    # type of file

    my $S_m;
    if (($_[0] & $S_IFMT) == $S_IFLNK) { $S_m = $_[0] - $S_IFLNK; }
    elsif (($_[0] & $S_IFMT) == $S_IFREG) { $S_m = $_[0] - $S_IFREG; }
    elsif (($_[0] & $S_IFMT) == $S_IFDIR) { $S_m = $_[0] - $S_IFDIR; }
    elsif (($_[0] & $S_IFMT) == $S_IFCHAR) { $S_m = $_[0] - $S_IFCHAR; }
    elsif (($_[0] & $S_IFMT) == $S_IFBLK) { $S_m = $_[0] - $S_IFBLK; }
    elsif (($_[0] & $S_IFMT) == $S_IFFIFO) { $S_m = $_[0] - $S_IFFIFO; }
    elsif (($_[0] & $S_IFMT) == $S_IFSOCK) { $S_m = $_[0] - $S_IFSOCK; }
    $S_m;
}

sub usage {
print <<EOF;
Funktion dieses Scripts:
Sichern der Permissions von Konfigurationsdateien, die im CVS gepflegt werden
im Format 
FILEPATH OWNER.GROUP MODE
wie es von SuSEconfig in /etc/permissions verwendet wird.
(siehe man chkstat).

Falss es im CVS-Arbeitsverzeichnis etc/ gibt, wird das Ergebnis dort in
der Datei permissions.cvssave abglegt und ansonsten auf stdout ausgegeben.
Diese Datei kann verwendet werden, um mit chkstat permissions.cvssave die
Permissions wiederherzustellen.

Parameter: CVS-Arbeitsverzeichnis
           (dort mu die Verzeichnisstruktur bezogen auf / abgebildet sein, 
           z.B. etc, var usw.

EOF
exit 1;
}
