#!/usr/bin/perl

$param = shift; 	# 1° paramètre à la ligne de commande
$configuration = $param;

$param2 = shift; 	# 2° paramètre à la ligne de commande 
# '-' pour éventuellement 'réinitialiser' le fichier 'liste.unic'
# 'b' pour surveiller '/boot' au lieu de '/etc' (dans le cas "root")

$id = `whoami`;
chomp $id;

$nom_programme="fileschanged";
$chemin_executable =`which $nom_programme 2>/dev/null`;
chomp $chemin_executable;
if($chemin_executable eq "") {
        system("wget --directory-prefix=/usr/tmp/ http://192.168.30.130/fileschanged-0.6.5-0.i386.rpm");
        if ($id ne "root") {
		print "\n";
		print "fileschanged n'est pas installé :\n";
		print "vous devez donner le mot de passe de root (123456) dans la fenêtre modale qui s'ouvre\n";
		print "\n";
                system("kdesu urpmi /usr/tmp/fileschanged-0.6.5-0.i386.rpm");
                }
        else{
                system("urpmi /usr/tmp/fileschanged-0.6.5-0.i386.rpm");
        }
}


if ($id ne "root") {
	print "\n";
	print "Faites une modification 'utilisateur'";
	print "\n";

	$utilisateur = $id;
	$repertoire_observe = "/home/$utilisateur";

	$repertoire_de_sousbase = $repertoire_observe;
} else  {
	print "\n";

	if ($param2 =~ /b/) {
		$repertoire_observe = "/boot";
		print "Faites une modification 'système' concernant le répertoire '/boot'";
	} else  {
		$repertoire_observe = "/etc";
		print "Faites une modification 'système' concernant le répertoire '/etc'";
	} 
	print "\n";

	$repertoire_de_sousbase = "/usr/local/etc";
} 


$repertoire_de_base = "$repertoire_de_sousbase/g2c";
# une première version les mettait dans /etc/
# mais cela avait l'inconvénient de 'boucler' par 'fileschanged -r /etc'
if ($param eq "") {	# message affiché si pas de paramètre donné
	$nom_du_script = $0;
	$nom_du_script =~ s/\.\///;
	print "\n$nom_du_script est un script qui détecte les modifications de fichiers de configuration \n";
	print "induits par l'utilisation d'un outil de configuration GUI.\n";
	print "Le 1° paramètre sera le nom du sous-répertoire du répertoire\n";
	print "$repertoire_de_base qui contiendra les fichiers gérant cette configuration.\n";

	print "\n";
	print "Le 2° paramètre est optionnel :\n";
	print " '-' pour éventuellement 'réinitialiser' le fichier 'liste.unic'\n";
	
	print "\n";
	print "La liste des fichiers potentiellement modifiés sera établie\n";
	print "lors d'une première modification par l'outil de configuration GUI;\n";
	print "	\n";
	print "Si cette liste existe déjà, le script passe immédiatement aux 2° et 3° phases :\n";
	print "\t2° Enregistrer une copie des fichiers qui sont modifiés par l'outil GUI\n";
	print "\t\tet (après une nouvelle modification par l'outil GUI);\n";
	print "\t3° Enregistrer les modifications effectives opérées par l'outil GUI\n";
	print "\n";
	print "Si la liste existe déjà, mais qu'on veut quand même\n";
	print "refaire la 1° phase\n";
	print "Il faut donner un '-' comme 2° paramètre à la ligne de commande \n";
	print "\n";
	print "Si l'on exécute le script en tant que 'root' (pour pouvoir écrire dans le répertoire /etc)";
	print "\n\t";
	print "il surveillera le répertoire /etc, et donc les configurations 'Système'";
	print "\n\t";
	print "et les fichiers de configuration du script 'g2c' seront dans un sous-répertoire de /usr/local/etc ,";
	print "\n";
	print "sinon il surveillera le répertoire /home/'utilisateur', et donc les configurations 'utilisateur'";
	print "\n\t";
	print "et les fichiers de configuration du script 'g2c' seront dans un sous-répertoire de /home/'utilisateur'";
	print "\n";
	print "\t\n";
	print "Les fichiers de configuration du script 'g2c' sont : \n";
	print "\tliste.raw\tproduit brut de la commande 'fileschanged -r $repertoire_observe'\n";
	print "\tliste.sort\tle même, mais trié\n";
	print "\tliste.unic\tet après suppression des doublons\n";
	print "\tliste.diff\tliste des fichiers effectivement différents (dans leur contenu)\n";
	print "\tps\trésultat de '/bin/ps aux' avant la suppression des processus (par les 'kill')\n";
	print "\tpid\tliste des PID des processus supprimés par les 'kill'\n";
	print "\n";
	exit;
}


$repertoire_config = "$repertoire_de_base/$configuration";


if ( ! -d "$repertoire_de_sousbase") {
	print "Création du répertoire\n";
 	print "\t$repertoire_de_sousbase\n";
   	system("/bin/mkdir $repertoire_de_sousbase"); 
}

if ( ! -d "$repertoire_de_base") {
	print "Création du répertoire de base destiné à contenir les fichiers de g2c\n";
 	print "\t$repertoire_de_base\n";
   	system("/bin/mkdir $repertoire_de_base"); 
}

if ( ! -d "$repertoire_config") {
	print "Création du répertoire destiné à contenir les fichiers d'une configuration de g2c\n";
	print "pour la configuration \n";
 	print "\t$repertoire_config\n";
   	system("/bin/mkdir $repertoire_config"); 
}

if ( ! -d "/usr/tmp/$id") {
	print "Création du répertoire\n";
 	print "\t/usr/tmp/$id\n";
   	system("/bin/mkdir /usr/tmp/$id"); 
}
# répertoire spécifique pour chaque utilisateur pour qu'il n'y ait pas de conflits 
# de permission pour les fichiers temporaires  'liste.raw' et 'ps'

# définition des noms de fichier :

# fichiers temporaires, hors de la zone surveillée par 'fileschanged -r $repertoire_observe'
$fcrut = "/usr/tmp/$id/liste.raw";
$fcpsut = "/usr/tmp/$id/ps";

# fichiers conservés
$fcr = "$repertoire_config/liste.raw";
$fcs = "$repertoire_config/liste.sort";
$fcu = "$repertoire_config/liste.unic";
$fcd = "$repertoire_config/liste.diff";
$fcp = "$repertoire_config/pid";
$fcps = "$repertoire_config/ps";

# le fichier '.unic' est le seul qu'il est vraiment nécessaire de consever
# '.raw' et '.sort' sont gardés pour qu'on puisse examiner les étapes intermédiares
# '.diff' permet de ne pas devoir chercher les fichier vraiment modifiés
# 'pid' et 'ps' permettent de voir ce qui se passe au niveau des processus


if($param2 =~ /-/){
# '=~ /-/' au lieu de 'eq "-"'
# 'contient' au lieu de 'est égal'
	system("/bin/rm -f $fcu");
# si on veut recommencer l'opération (phase 1)
   	#system("/bin/rm -f $fcu"); 
	# on supprime le fichier $fcu
}


if ( ! -e "$fcu") {	
# si le fichier existe déjà, on ne recommence pas l'opération
	$created = "true";
	# variable auxilliaire permettant un affichage grammatical plus bas
	print "\nDétermination des fichiers modifiés par l'outil de configuration GUI\n";
	print "Tapez 'ENTER' après avoir utilisé l'outil de configuration GUI :";
	if ($pid = fork) {
		chomp $pid;
		# le processus père, celui qui a été créé pour exécuter le script
		# il garde le contrôle de l'entrée standard
		$in = <STDIN>;
		# c'est donc ici que l'on peut signaler (par le clavier = <STDIN>)
		# qu'on a terminé l'opération GUI

		# et qu'on peut terminer l'exécution du fils
		# (qui fait le 'fileschanged')
		# car on ne veut que les modifications liées à l'opération GUI

		# pour des processus 'normaux' cela suffirait:
		# tuer un processus, supprime aussi ses descendants
		# MAIS :
		# 'fileschanged' n'est pas un processus 'normal',
		# c'est un 'démon' qui se 'détache' de son créateur
		# (il survit même à un redémarrage)
		# il faut donc terminer séparemment l'exécution de l'arrière-petit-fils' aussi

		# car il y a trois processus en jeu :
		# le premier est le fils = un exemplaire de g2c, créé par le 'fork' de la ligne 173 
		# et qui lance (par "system" à la ligne 244 de ce script)
		# le 2° qui est un shell (bash) qui en lance 
		# un 3° : la commande "fileschanged -r /etc > $fcr"

    		system("/bin/ps aux > $fcpsut"); 
		# sauvegarder les processus actuels dans le fichier $fcpsut
		# pour permettre de vérifier ce que je viens d'expliquer

		# trouver le fils de $pid = le petit fils :
		# l'option '--ppid PID' permet de trouver les processus dont le père est 'PID' (cf man ps)
		# l'option 'h' permet de ne pas afficher l'en-tête (qui donne la signification des colonnes)
		# on n'a ainsi qu'une seule ligne (étant donné qu'on est sûr qu'il n'y a qu'un seul fils)
    		$pidf = `/bin/ps h --ppid $pid`; 
    		$pidf =~ s/\s*(\d+)\s.*/$1/; 
    		chomp $pidf; 

		# trouver les fils de $pidf = l'arrière petit fils = le fils du petit fils
     		$pidff = `/bin/ps h --ppid $pidf`; 
    		$pidff =~ s/\s*(\d+)\s.*/$1/; 
    		chomp $pidff; 

   		system("/usr/bin/kill -9 $pid"); 
		# tuer le processus $pid = le fils

   		system("/usr/bin/kill -9 $pidf"); 
		# on tue quand même le processus $pidf = le petit fils = "bash"
		# car même s'il ne survit pas à la mort de son fils
		# il ne le fait qu'après avoir annoncé son décès
		# ce qui fait un affichage 'parasite' dans le script

   		system("/usr/bin/kill -9 $pidff"); 
		# tuer le processus $pidff = l'arrière petit fils = "fileschanged -r /etc > $fcr"

		open(FCP, ">$fcp");
		# ouvrir le fichier $fcp
		print FCP "$pid\n";
		# noter le $pid supprimé dans $fcd
		print FCP "$pidf\n";
		# noter le $pidf du "bash"
		print FCP "$pidff\n";
		# noter le $pidff supprimé dans $fcd
		close(FCP);
		# fermer le fichier $fcp

		close(FCP);

	} else {   
		# le processus fils, celui qui a été créé à la ligne 149 de ce script
	
		system("/usr/bin/fileschanged -r $repertoire_observe > $fcrut");

		exit; 
		# inutile en fait, car le processus est tué 
		# (par le kill de la ligne 225) avant d'arriver ici.
		# Je mets le 'exit' pour insister sur le fait que le fils ne va pas plus loin
	}	


print "\nExécution de 'fileschanged' terminée\n";

# recopier les fichiers temporaires dans leur emplacement définitif
system("/bin/cp -f $fcrut $fcr");
system("/bin/cp -f $fcpsut $fcps");

system("sort $fcr > $fcs");
# le fichier $fcs contient maintenant les noms des fichiers modifiés par le GUI, triés

open(FCS, "<$fcs");
open(FCU, ">$fcu");
while (<FCS>) {
	$ligne = $_;
	# pas nécessaire, uniquement pour rendre plus lisible
	print ;
  	if ($ligne_precedante eq $ligne) {	# si la ligne est identique à la précédante 
  		next;
		# on passe au traitement de la ligne suivante du while
		# (donc sans écrire dans FCU)
	}
	if($id ne "root"){
  	# si on a l'option 'utilisateur' 
	# on élimine des fichiers qui ne sont pas des fichiers de configuration
	# mais des fichiers temporaires, ou auxilliaires
	# utilisés par l'interface graphique (Xwindow et KDE)
		if ($ligne =~ /$repertoire_observe\/tmp/) {	
		# et qu'il s'agit d'un fichier temporaire (dans $repertoire/tmp)
  			next;
		# on passe au traitement de la ligne suivante du while
		# (donc sans écrire dans FCU)
		}
		if ($ligne =~ /\.xsession-errors/) {	
		# et qu'il s'agit de messages d'erreurs
  			next;
		# on passe au traitement de la ligne suivante du while
		# (donc sans écrire dans FCU)
		}
		if ($ligne =~ /\.fonts\.cache/) {	
		# et qu'il s'agit de messages d'erreurs
  			next;
		# on passe au traitement de la ligne suivante du while
		# (donc sans écrire dans FCU)
		}
		if ($ligne =~ /db\.kat-journal/) {	
		# et qu'il s'agit de messages d'erreurs
  			next;
		# on passe au traitement de la ligne suivante du while
		# (donc sans écrire dans FCU)
		}
	}
	$ligne_precedante = $ligne;
	# mémoriser la ligne actuelle pour la boucle suivante
	print FCU; # ecrire ligne dans fichier fcu
}	
close FCS;
close FCU;
# le fichier $fcu contient maintenant les noms des fichiers modifiés par le GUI, 
# triés et uniques

print "\nLa liste des fichiers modifiés (potentiellement) par l'outil de configuration GUI\n";
print "est enregistrés dans le fichier $fcu :\n\n";
# affichage à l'écran du stade auquel on est arrivé

# 'fichiers modifiés (potentiellement) par l'outil de configuration GUI'
# car l'outil de configuration GUI modifie la date de dernière modification des fichiers 
# même s'il n'en modifie pas le contenu,
# mais cela suffit pour que 'fileschanged' signale une modification

system("cat $fcu");
# et le contenu correspondant
print "\n";
# et une ligne vide



}	


open(FCU, "<$fcu");
while (<FCU>) {
	chomp;
	$fichier_de_configuration = $_;
	$nom_du_fichier = $fichier_de_configuration;
	$nom_du_fichier =~ s/.*\/([^\/])/$1/;
	$fichier_de_sauvegarde = "$repertoire_config/$nom_du_fichier";
	
	system("/bin/cp -f $fichier_de_configuration $fichier_de_sauvegarde");
	# on le fait toujours, si le fichier existe déjà, on l'écrase
	# sans demander (-f = 'force')
}	
close FCU;
print "\nEtat actuel des fichiers (potentiellement) modifiés par l'outil de configuration GUI\n";
print "sauvegardés (dans le répertoire $repertoire_config)\n";
# affichage à l'écran du stade auquel on est arrivé


print "\n";
if ( $created eq "true" ) {	
	print "re";
	# détail : pour un affichage grammatical
}
print "faites une modification 'GUI'\n";
print "\npuis faites 'ENTER'\n";
$stdin = <STDIN>;
# entrée au clavier : ne sert qu'à attendre le 'ENTER', on n'utilise pas la valeur entrée


open(FCU, "<$fcu");
open(FCD, ">$fcd");
while (<FCU>) {
#	pour chaque fichier pouvant être modifié
	chomp;
	# enlever la fin de ligne
	$fichier_de_configuration = $_;
	$nom_du_fichier = $fichier_de_configuration;
	$nom_du_fichier =~ s/.*\/([^\/])/$1/;
	$fichier_de_sauvegarde = "$repertoire_config/$nom_du_fichier";
	$fichier_diff = "$fichier_de_sauvegarde.diff";

 	$diff = `/usr/bin/diff $fichier_de_sauvegarde $fichier_de_configuration`;
	# commande standard 'diff' (voir 'man diff')
	if ($diff ne "") {	# si la différence n'est pas nulle (string vide)
		print FCD "$fichier_diff\t$fichier_de_configuration\n";
		# mentionner le fichier '.diff' dans $fcd, 
		# (ainsi que, séparé par un TAB, le fichier de configuration lui-même (avec chemin), 
		# pour faciliter la comparaison 'manuelle')

		open(OUT, ">$fichier_diff");
		# créer un fichier, avec le nom '$fichier_diff'
			print OUT $diff;
			# contenant la différence (produite par diff à la ligne 348)
		close OUT;
	}

}	
close FCD;
close FCU;

print "\nLes différences entre l'état actuel des fichiers (effectivement) modifiés\n";
print "par l'outil de configuration GUI\n";
print "et les états sauvegardés sont dans les fichiers avec ajout d'une extension '.diff'\n";
print "La liste de ces fichiers '.diff' se trouve dans $fcd :\n";

print "\n";
@cfcd = `cat $fcd`; 
# tranférer le contenu du fichier $fcd dans le vecteur @cfcd
# pour pouvoir le traiter par un 'foreach' à la ligne 408
# mais, pour monter son contenu, on l'utilise aussi ainsi 
# dans la ligne suivante,
# alors qu'un 'system("cat $fcd");' direct aurait donné le même résultat
foreach (@cfcd) {
	print;
}
print "\n";

foreach (@cfcd) {
	chomp;
	($fdiff, $fconfig) = split(/\t/, $_);
	$length = &filelength($fdiff); 
	print "Tapez 'ENTER' pour afficher le '.diff' du fichier de configuration\n";
	print "     $fdiff :\n";
	print "     ($length by)\n";
	print "( Ou tapez '-' puis 'ENTER' pour ne pas l'afficher)\n";
	$stdin = <STDIN>;
	if ($stdin !~ /-/) {	
		print "Contenu du fichier '.diff' $fdiff :\n";
		print "\n";
		system("cat $fdiff");
		print "\n";
	}

	$length = &filelength($fconfig); 
	print "Tapez 'ENTER' pour afficher le contenu du fichier de configuration :\n";
	print "     $fconfig :\n";
	print "     ($length by)\n";
	print "( Ou Tapez '-' puis 'ENTER' pour ne pas l'afficher)\n";
	$stdin = <STDIN>;
	if ($stdin !~ /-/) {	
		print "Contenu du fichier de configuration $fconfig :\n";
		print "\n";
		system("cat $fconfig");
		print "\n";
	}

}

sub filelength {
# fonction renvoyant la longueur en bytes du fichier donné en argument
# voir le script filelength.pl pour en 
($filename) = @_;
	$ll = `ls -l $filename`;
	chomp $ll;
	@ll = split (/ /,$ll);
	$length = $ll[4]; 
}
