#! /bin/bash
# NE PAS ÉDITER CE FICHIER !!!
# Voici les appels qui ont un sens pour ce script :
# logon xxx yyy zzz
#
# Avec :
# - xxx = "initialisation" ou "ouverture" ou "fermeture"
# - yyy = "true" ou "false" pour indiquer s'il doit y avoir tentative
# de mise à jour avoir le script distant.
# - zzz = "true" ou "false" pour indiquer si le script s'exécute lors d'une
# d'une phase d'initialisation qui correspond au démarrage du
# système (true) ou non (false).
# Le plus tôt possible dans le script, on procède aux redirections
# de stdout et de stderr.
PHASE="$1"
REP_SE3_LOCAL="/etc/se3"
REP_LOG_LOCAL="$REP_SE3_LOCAL/log"
# En fonction de l'argument PHASE, on redirige différemment
# la sortie standard et la sortie des erreurs du script.
case "$PHASE" in
"initialisation")
# On cherche d'abord à récupérer le nom absolu du script exécuté.
if echo "$0" | grep -q '^/'; then
# L'appel du script s'est fait via son nom absolu.
nom_absolu_script="$0"
else
rep=$(dirname "$0")
nom=$(basename "$0")
nom_absolu_script=$(cd "$rep"; pwd)"/$nom"
fi
# Ici on fait appel à un grep classique car aucune fonction
# n'est encore définie à ce stade du script. Mais ce n'est
# pas très grave car le motif ici est une chaîne de caractères
# sans surprise.
if echo "$nom_absolu_script" | grep -q -- "^$REP_SE3_LOCAL"; then
# Le script exécuté se trouve dans le répertoire local.
exec 1> "$REP_LOG_LOCAL/1.initialisation.log" 2>&1
else
# Sinon, c'est la version distante qui est exécutée
# et il vaut mieux rediriger la sortie dans un autre
# fichier, sans quoi le script distant va réécrire
# par dessus le log du script local.
exec 1> "$REP_LOG_LOCAL/1.initialisation_distant.log" 2>&1
fi
;;
"ouverture")
exec 1> "$REP_LOG_LOCAL/2.ouverture.log" 2>&1
;;
"fermeture")
exec 1> "$REP_LOG_LOCAL/3.fermeture.log" 2>&1
;;
*)
# L'argument PHASE est incorrect, on arrête tout.
exit 1
;;
esac
unset -v nom_absolu_script rep nom
### LOGON_PERSO ###
### LOGON_PARAM_FOND_ECRAN ###
##############################################################
##############################################################
### ###
### On passe à la définition de quelques fonctions utiles. ###
### ###
##############################################################
##############################################################
# Fonction qui affiche la date du moment.
function afficher_date ()
{
LC_ALL="$LOCALE_FRANCAISE" date '+%Y/%m/%d %Hh%Mmn%Ss (%A %d %b)'
}
# Fonction pour afficher des messages.
function afficher ()
{
# On insère la date au niveau de chaque affichage.
echo "[$(afficher_date)] $@" # | fmt -w 65
}
# Cette fonction prend une chaîne en argument et teste si
# le nom en argument correspond à un login d'utilisateur local
# (c'est-à-dire qui se trouve dans /etc/passwd). Si c'est
# le cas la fonction s'exécute correctement en renvoyant 0,
# sinon elle renvoie 1.
function est_utilisateur_local ()
{
if cat "/etc/passwd" | grep_debut_verbatim "$1:"; then
return 0
else
return 1
fi
}
# Donne la liste des répertoires qui sont point de montage
# sur le système. Attention, avec cette sortie, les caractères
# un peu spéciaux dans les noms sont exprimés sous la forme
# \NNN avec NNN le numéro ASCII du caractère sous la forme
# octale.
function donner_liste_montages ()
{
cat "/proc/mounts" | cut -d ' ' -f 2
}
# Fonction qui permet de savoir si un répertoire, avec son chemin absolu,
# est bien un point de montage. Si c'est le cas la fonction s'exécute
# correctement en renvoyant 0 sinon elle renvoie 1.
# Le chemin absolu ne doit pas avoir de « / » à la fin.
# Par ailleurs cette commande ne fonctionnera que pour des noms de
# répertoires sans espace et sans caractère « spécial » au yeux du
# fichier /proc/mounts (par exemple « \ » est un caractère spécial,
# qui est représenté par \134 dans le fichier /proc/mounts sachant
# que 134 est le numéro ASCII du caractère « \ »).
# Mais en vérité je ne saurais donner une définition précise de ce qui
# est considéré comme caractère « spécial » dans ce fichier.
# Mais ce n'est pas grave car cette fonction devra être appelée avec
# en argument un nom de répertoire sans caractère « exotique »,
# ça fait partie du contrat. Par exemple, la classe de caractères
# [-_a-z0-9] est constitué de caractères non spéciaux, il faudra
# se limiter à cette classe.
#
# Remarque : la commande mountpoint aurait pu sembler plus naturelle
# pour remplir cette tâche. Mais le souci, c'est que :
# 1. Si c'est un montage réseau et si le serveur est inaccessible
# alors la commande peut durer un certain temps.
# 2. On pourrait ajouter un timeout, mais dans ce cas si la commande
# dépasse ce timeout (parce que le serveur est inaccessible), la
# valeur de retour est autre que 0 et cela signifie que le
# répertoire n'est pas un point de montage alors que c'est
# en fait le cas.
function est_point_montage ()
{
# $1 est le chemin absolu du répertoire.
if donner_liste_montages | grep_debut_fin_verbatim "$1"; then
return 0
else
return 1
fi
}
# Fonction qui supprime le répertoire de montage donné
# en argument sous la forme d'un chemin absolu.
# Si le répertoire n'existe pas alors la fonction ne fera
# rien, et ne provoquera pas d'erreur.
function nettoyer_un_rep_montage ()
{
local f
local ff
f="$1"
shopt -s dotglob
for ff in "$f/"*; do
[ "$ff" = "$f/*" ] && continue
# Si c'est un point de montage (et donc aussi un répertoire),
# il faut d'abord procéder au démontage.
if est_point_montage "$ff"; then
umount "$ff" && rm -rf --one-file-system "$ff"
else
# Dans tous les autres cas, on peut supprimer directement.
rm -rf --one-file-system "$ff"
fi
done
# On peut ensuite effacer "$f" qui doit être vide en principe.
rm -rf --one-file-system "$f"
shopt -u dotglob
}
# Fonction qui démonte tous les répertoires (qui se trouvent dans REP_MONTAGE)
# de partages des utilisateurs non connectés au système, sans toucher
# à REP_NETLOGON.
function nettoyer_rep_montage ()
{
local f
shopt -s dotglob
for f in "$REP_MONTAGE/_"*; do
# On itère sur tous les fichiers de la forme "$REP_MONTAGE/_xxx".
# On passe à l'itération suivante si f est égal à REP_NETLOGON
# (aucune chance à cause du caractère underscore mais on ne sait
# jamais) ou bien si f ne correspond à aucun fichier ce qui est
# possible si le * n'attrape rien, ou bien si ce n'est pas un
# répertoire.
[ "$f" = "$REP_NETLOGON" ] && continue
[ "$f" = "$REP_MONTAGE/*" ] && continue
[ ! -d "$f" ] && continue
local n
local nom
n=$(basename "$f") # "$n" sera de la forme "_xxx"
nom="${n#_}" # "$nom" sera de la forme "xxx"
# Si l'utilisateur est connecté, on passe.
est_connecte "$nom" && continue
# Enfin on efface le contenu du répertoire "$f".
nettoyer_un_rep_montage "$f"
done
shopt -u dotglob
}
# Fonction qui vide REP_TMP_LOCAL de son contenu.
function nettoyer_rep_tmp_local ()
{
local f
shopt -s dotglob
for f in "$REP_TMP_LOCAL/"*; do
[ "$f" = "$REP_TMP_LOCAL/*" ] && continue
rm -rf "$f"
done
shopt -u dotglob
}
# Fonction qui efface les homes des utilisateurs non locaux et qui
# ne sont pas connectés au système.
function nettoyer_rep_home ()
{
# On efface tous les home des utilisateurs non locaux (non connectés).
local f
shopt -s dotglob
for f in "/home/"*; do
[ "$f" = "/home/*" ] && continue
nom=$(basename "$f")
if est_utilisateur_local "$nom"; then
# Là, on garde le répertoire car c'est un utilisateur local.
true
else
# Sinon on supprime le home si celui-ci ne correspond pas
# à un utilisateur déjà connecté. Par ailleurs, on évite
# la suppression de "lost+found" qui est présent quand le
# répertoire /home est monté sur une partition séparée.
if [ "$nom" != "lost+found" ] && ! est_connecte "$nom"; then
# L'option --one-file-system est capitale, car sans cette
# option, si un lien symbolique se trouve dans le « /home »
# et s'il pointe vers un partage alors le contenu du
# partage est tout simplement effacé.
rm -fr --one-file-system "$f"
fi
fi
done
shopt -u dotglob
}
# Cette fonction va mettre à jour le profil local afin de
# copier le profil distant dans REP_SKEL_LOCAL. A priori,
# cette fonction sera appelée si et seulement si le profil
# distant a changé de version.
function mettre_a_jour_profil ()
{
local version version_local exit_code
version=$(cat "$VERSION_SKEL")
version_local=$(cat "$VERSION_SKEL_LOCAL")
afficher "Mise à jour du profil par défaut local." \
"Le contenu du fichier .VERSION du profil distant est" \
" $version ."
if [ ! -d "$REP_SKEL_LOCAL" ]; then
mkdir -p "$REP_SKEL_LOCAL"
chown "root:" "$REP_SKEL_LOCAL"
fi
# On oublie cp qui, apparemment, peut planter quand il
# fait des copies qui passent par le réseau (ce qui est
# le cas ici). Et perso, j'ai constaté ce bug avec un
# message du genre « cp: skipping file ... as it was
# replaced while being copied ». Ceci étant, je n'ai
# pas réussi à reproduire le bug, alors je ne sais plus.
#rm -fr --one-file-system "$REP_SKEL_LOCAL"
#cp -r "$REP_SKEL" "$REP_SE3_LOCAL"
# Pas de chose dans le genre « --modify-window=20 » car il
# vaut mieux que des copies en trop soient faites plutôt
# qu'il y ait certaines copies non faites.
# L'option --bwlimit indique une limite de téléchargement
# en KiloBytes/s. Ici on a 2048 KB/s soit 2 Mo/s.
if rsync -rtxh --links --delete --bwlimit=2048 "$REP_SKEL" "$REP_SE3_LOCAL"
then
# La commande rsync s'est bien passée.
exit_code=0
else
sleep 0.5
# La commande rsync a planté, il faut remettre le fichier
# .VERISON local dans son état initial.
exit_code=1
cat "$version_local" > "$VERSION_SKEL_LOCAL"
fi
chown -R "root:" "$REP_SKEL_LOCAL"
# On met des droits cohérents sur les fichiers et les répertoires.
find "$REP_SKEL_LOCAL" -type f -exec chmod u=rw,g=rw,o='',u-s,g-s,o-t '{}' \;
find "$REP_SKEL_LOCAL" -type d -exec chmod u=rwx,g=rwx,o='',u-s,g-s,o-t '{}' \;
sync
return "$exit_code"
}
# Met à jour le script de logon local. A priori cette fonction sera
# appelée si et seulement le script de logon local est différent (au
# sens de la commande diff) de la version distante sur le serveur.
function mettre_a_jour_logon ()
{
if [ ! -d "$REP_BIN_LOCAL" ]; then
mkdir -p "$REP_BIN_LOCAL"
chown "root:" "$REP_BIN_LOCAL"
chmod "700" "$REP_BIN_LOCAL"
fi
# On met à jour le script de logon en lançant une tâche en arrière plan.
afficher "Le déroulement de la mise à jour du script de logon sera écrit" \
"dans le fichier \"0.maj_logon.log\"."
{
afficher_date
# On attend que le script local ne soit lu par aucun processus
# afin qu'il soit disponible pour être réécrit.
local compteur
compteur="1"
while fuser "$LOGON_SCRIPT_LOCAL" >/dev/null 2>&1; do
sleep 0.5
if [ "$compteur" -ge "40" ]; then
# Au bout de 40 x 0.5 = 20 secondes on abandonne la mise à jour.
afficher "Mise à jour du script de logon local abandonnée" \
"car celui-ci semble toujours lu par un processus" \
"alors que le temps d'attente maximum est dépassé."
return 1
fi
compteur=$((compteur+1))
done
# On crée une sauvegarde de la version locale du script de logon.
if cp -a "$LOGON_SCRIPT_LOCAL" "$LOGON_SCRIPT_LOCAL.SAVE" && \
diff -q "$LOGON_SCRIPT_LOCAL" "$LOGON_SCRIPT_LOCAL.SAVE" >/dev/null 2>&1
then
# On a une sauvegarde de la version locale qui est fiable.
# On peut alors tenter la mise à jour du script de logon local.
# Une dernière fois, on vérifie que le script local est
# bien disponible, juste avant de le réécrire.
if ! fuser "$LOGON_SCRIPT_LOCAL" >/dev/null 2>&1 && \
cp "$LOGON_SCRIPT" "$LOGON_SCRIPT_LOCAL" && \
diff -q "$LOGON_SCRIPT" "$LOGON_SCRIPT_LOCAL" >/dev/null 2>&1
then
# La mise à jour a fonctionné.
chown "root:" "$LOGON_SCRIPT_LOCAL"
chmod "700" "$LOGON_SCRIPT_LOCAL"
afficher "Mise à jour du script de logon local réussie."
[ -e "$LOGON_SCRIPT_LOCAL.SAVE" ] && rm -f "$LOGON_SCRIPT_LOCAL.SAVE"
return 0
else
# La mise à jour n'a pas fonctionné, il faut alors
# restaurer la sauvegarde.
[ -e "$LOGON_SCRIPT_LOCAL" ] && rm -f "$LOGON_SCRIPT_LOCAL"
mv "$LOGON_SCRIPT_LOCAL.SAVE" "$LOGON_SCRIPT_LOCAL"
chown "root:" "$LOGON_SCRIPT_LOCAL"
chmod "700" "$LOGON_SCRIPT_LOCAL"
afficher "La mise à jour du script de logon local a échoué." \
"La version locale actuelle a été restaurée en attendant" \
"une prochaine tentative de mise à jour."
return 1
fi
else
# Si la création de la sauvegarde de la version locale a échoué
# alors on supprime la sauvegarde si elle existe et on ne fait
# plus rien.
[ -e "$LOGON_SCRIPT_LOCAL.SAVE" ] && rm -f "$LOGON_SCRIPT_LOCAL.SAVE"
afficher "Erreur lors de sauvegarde de la version locale du" \
"script de logon. Pas de mise à jour de script."
return 1
fi
} > "$REP_LOG_LOCAL/0.maj_logon.log" 2>&1 &
# Tout ça se fait en arrière plan pour que le script continue son exécution.
# On ne sait pas vraiment à quel moment cette tâche en arrière
# plan va se lancer donc il vaut mieux rediriger la sortie
# standard et la sortie des erreurs vers un fichier à part.
}
# Cette fonction crée un lien symbolique "/le/lien_symb" qui pointe vers un
# répertoire "/le/rep" et fait de "user" le propriétaire de ce fichier lien
# symbolique.
# creer_lien_symb "/le/rep" "/le/lien_symb" "user"
function creer_lien_symb ()
{
# $1 est le répertoire vers lequel pointe le lien.
# $2 est le chemin absolu du fichier lien symbolique.
# $3 est le propriétaire du fichier lien symbolique.
ln -s "$1" "$2"
# L'option permet de changer les propriétaires
# sur le lien symbolique lui-même car par défaut
# sinon c'est un changement sur la cible qui s'opère.
chown --no-dereference "$3:" "$2"
}
# Cette fonction tente de monter REP_NETLOGON s'il ne l'est
# pas déjà et renvoie 0 si ça marche, 1 sinon.
function monter_netlogon ()
{
local n
local c
n=$(donner_liste_montages | grep -c -- "^$REP_NETLOGON$")
c="1"
# Si n >= 1, le montage est fait, mais ce n'est pas pour autant qu'il
# est valide. J'ai déjà vu des cas comme ça. On va donc vérifier
# que le script de logon distant (par exemple) est bien lisible.
if [ "$n" -ge 1 ]; then
if ! timeout "--signal=SIGTERM" 2s test -r "$LOGON_SCRIPT"; then
# Le montage est incorrect car le fichier de logon distant
# n'est pas accessible. Du coup, on démonte netlogon pour
# repartir sur de nouvelles bases et retenter un montage
# ci-dessous.
while [ ! "$n" -eq 0 ]; do
# Pas de timeout nécessaire car en principe un umount est
# instantané quel que soit l'état du serveur de partages.
umount "$REP_NETLOGON"
sleep 0.25 # On ne sait jamais.
n=$(donner_liste_montages | grep -c -- "^$REP_NETLOGON$")
done
fi
fi
while [ ! "$n" -eq 1 ] && [ "$c" -le 4 ] ; do
if [ "$n" -eq 0 ]; then
timeout "--signal=SIGTERM" 2s \
mount -t cifs "$CHEMIN_PARTAGE_NETLOGON" "$REP_NETLOGON" -o ro,guest,"$OPTIONS_MOUNT_CIFS_BASE"
else
# différent de 1 et de 0, ça veut dire que n > 1.
# Cas qui ne devrait pas se produire, mais sait-on jamais.
# Ici il faut démonter une fois REP_NETLOGON.
umount "$REP_NETLOGON"
fi
sleep 0.5
n=$(donner_liste_montages | grep -c -- "^$REP_NETLOGON$")
c=$((c+1))
done
if [ "$n" -eq 1 ]; then
# Montage réussi.
return 0
else
# Montage pas réussi
return 1
fi
}
# Cette fonction tente de monter un partage CIFS (comme par exemple
# le partage « Classes » du Se3). Le premier paramètre est le chemin
# du partage CIFS et le deuxième paramètre est le répertoire servant
# de point de montage. Le troisième paramètre, s'il existe, permet
# d'ajouter des options de montage en plus de celles par défaut.
function monter_partage_cifs ()
{
# 10 secondes avant le timeout du montage, ça fait beaucoup
# mais pour l'instant, le Se3 n'étant pas bien adapté aux
# clients Linux (aux montages CIFS pour être plus précis),
# lors par exemple d'un montage d'un /home via CIFS,
# le script connexion.sh sur le Se3 est exécuté avant que
# le montage ne soit accepté (preexec dans smb.conf) et
# il a tendance à durer un peu hélas.
local options
# Les options par défaut.
options="$OPTIONS_MOUNT_CIFS_BASE"
if [ -n "$3" ]; then
# Des options supplémentaires sont spécifiées,
# on les ajoute à la liste.
options="$3,$options"
fi
timeout "--signal=SIGTERM" 10s mount -t cifs "$1" "$2" -o "$options"
}
# Affiche une petite fenêtre signalant une erreur.
# Le premier argument correspond au titre de la fenêtre et
# les suivants sont concaténés (avec un espace pour faire la jointure)
# afin de former le message d'erreur contenu dans la fenêtre.
function afficher_fenetre_erreur ()
{
local titre
local message
titre="$1"
# On mange le premier argument.
shift 1
# Avec les arguments restants, on forme le message.
local m
for m in "$@"; do
message="$message $m"
done
# On enlève l'espace au début.
message="${message# }"
# Attention d'appeler zenity avec une locale adaptée.
LC_ALL="$LOCALE_FRANCAISE" zenity --error --title "$titre" --text "$message"
}
# Fonction qui retourne la valeur 0 si le login donné en deuxième argument
# appartient au groupe donné en premier argument et retourne la valeur 1 sinon.
# Exemples d'appels :
# appartient_au_groupe Eleves hugov
# appartient_au groupe Profs hugov
# La recherche est insensible à la casse.
function appartient_au_groupe ()
{
# $1 = le groupe
# $2 = le login
local n
# On cherche à afficher la liste des DN des entrées de l'OU Groups
# qui possèdent un attribut memberUid égal à "$2".
n=$(timeout "--signal=SIGTERM" 4s ldapsearch -xLLL -h "$SE3" -b "cn=$1,ou=Groups,$BASE_DN" "(memberUid=$2)" "dn" | wc -l)
if [ "$n" -eq "0" ]; then
# n = 0 signifie que la recherche n'a donné aucun résultat et
# donc le compte n'appartient pas au groupe.
return 1
else
# Sinon, la recherche a donné un résultat et donc le compte
# appartient au groupe.
return 0
fi
}
# Fonction qui retourne la valeur 0 si le nom de machine donné en
# deuxième argument appartient au nom de parc donné en premier argument
# et retourne la valeur 1 sinon.
# Exemple d'appel :
# appartient_au_parc CDI S121-PC04
# La recherche est insensible à la casse.
function appartient_au_parc ()
{
# $1 = le parc
# $2 = le nom de machine
local n
n=$(timeout "--signal=SIGTERM" 4s ldapsearch -xLLL -h "$SE3" -b "cn=$1,ou=Parcs,$BASE_DN" "(member=cn=$2,ou=Computers,$BASE_DN)" "dn" | wc -l)
if [ "$n" -eq "0" ]; then
# n = 0 signifie que la recherche n'a donné aucun résultat et
# donc la machine n'appartient pas au parc.
return 1
else
# Sinon, la recherche a donné un résultat et donc la machine
# appartient au parc.
return 0
fi
}
# Cette fonction affiche la liste des groupes qui contiennent le
# compte dont le login est donné en paramètre. Le format d'affichage
# est de la forme « une ligne par nom de groupe ». Un exemple
# typique d'appel de la fonction :
# liste_groupes=$(afficher_liste_groupes "hugov")
function afficher_liste_groupes ()
{
# $1 = le login
# Recherche dans l'OU Groups de tous les dn des entrées qui
# possèdent un attribut memberUid égal "$1".
timeout "--signal=SIGTERM" 4s ldapsearch -xLLL -h "$SE3" -b "ou=Groups,$BASE_DN" "(memberUid=$1)" "dn" \
| awk 'BEGIN { RS="\0" } { gsub("\n ", ""); print $0 }' \
| awk '$0 ~ /^dn: / { print $2 }' \
| sed -r 's/^cn=([^,]+),.*$/\1/g'
# Avec le premier awk, on prend la sortie de la recherche dans son
# ensemble (RS="\0") et on remplace "\n " par "" car, dans une
# recherche LDAP, une ligne trop longue est cassée et la suite de
# la ligne est indentée sur un espace. Avec cette substitution,
# les lignes trop longues ne sont pas cassées.
#
# Avec le second awk, étant donné qu'on a des lignes soit vides,
# soit de la forme "dn: ". On affiche alors seulement
# les lignes non vides et on affiche uniquement la partie située
# après l'espace.
#
# Avec sed, on récupère uniquement le nom du groupe.
}
# Cette fonction affiche la liste des parcs qui contiennent la
# machine dont le nom est donné en paramètre. Le format d'affichage
# est de la forme « une ligne par nom de machine ». Un exemple
# typique d'appel de la fonction :
# liste_parcs=$(afficher_liste_parcs "S121-HP-04")
function afficher_liste_parcs ()
{
# $1 = le nom de machine
# Recherche dans l'OU Parcs de tous les dn des entrées qui
# possèdent un attribut member égal "cn=$1,ou=Computers,$BASE_DN".
timeout "--signal=SIGTERM" 4s ldapsearch -xLLL -h "$SE3" -b "ou=Parcs,$BASE_DN" "(member=cn=$1,ou=Computers,$BASE_DN)" "dn" \
| awk 'BEGIN { RS="\0" } { gsub("\n ", ""); print $0 }' \
| awk '$0 ~ /^dn: / { print $2 }' \
| sed -r 's/^cn=([^,]+),.*$/\1/g'
# Avec le premier awk, on prend la sortie de la recherche dans son
# ensemble (RS="\0") et on remplace "\n " par "" car, dans une
# recherche LDAP, une ligne trop longue est cassée et la suite de
# la ligne est indentée sur un espace. Avec cette substitution,
# les lignes trop longues ne sont pas cassées.
#
# Avec le second awk, étant donné qu'on a des lignes soit vides,
# soit de la forme "dn: ". On affiche alors seulement
# les lignes non vides et on affiche uniquement la partie située
# après l'espace.
#
# Avec sed, on récupère uniquement le nom du parc.
}
# Une liste (de la forme un item par ligne) étant donnée, la fonction
# renvoie 0 si le paramètre est dedans, 1 sinon. La recherche est
# sensible à la casse, c'est-à-dire que
# « est_dans_liste "$liste" "un_item" », et
# « est_dans_liste "$liste" "UN_ITEM" » ne renverront pas
# forcément la même valeur.
function est_dans_liste ()
{
# $1 = la liste
# $2 = le nom à tester
if echo "$1" | grep_debut_fin_verbatim "$2"; then
return 0
else
return 1
fi
}
# Fonction qui se charge de lancer les exécutables « unefois ».
function lancer_unefois ()
{
if test -e "$REP_UNEFOIS/PAUSE"; then
# Si le fichier PAUSE est présent alors on ne fait rien.
# et on s'arrête.
return 0
fi
if test -e "$REP_UNEFOIS/BLACKOUT"; then
# Si le fichier BLACKOUT est présent alors on supprime
# tous les exécutables dans REP_UNEFOIS_LOCAL.
local f
for f in "$REP_UNEFOIS_LOCAL/"*; do
[ "$f" = "$REP_UNEFOIS_LOCAL/*" ] && continue
rm -rf "$f"
done
# Et on arrête tout.
return 0
fi
# Dans cette situation (pas de fichier PAUSE ni de fichier
# BLACKOUT), il faut copier les « unefois » distants
# dont le nom n'existe pas en local et les exécuter.
local d
local regex
shopt -s dotglob
for d in "$REP_UNEFOIS/"*; do
[ "$d" = "$REP_UNEFOIS/*" ] && continue
if [ -d "$d" ]; then
parc="$(echo ${d##*/})"
if appartient_au_parc $parc $NOM_HOTE; then
lancer_unefois_dans_repertoire "$d"
fi
regex=$(basename "$d")
if echo "$NOM_HOTE" | grep -Eiq -- "$regex"; then
lancer_unefois_dans_repertoire "$d"
fi
fi
done
shopt -u dotglob
}
# Fonction qui se charge de l'installation des pilotes d'imprimantes
function lancer_parc ()
{
for p in "$REP_LANCEPARC/"*; do
[ "$p" = "$REP_LANCEPARC/*" ] && continue
parc="$(echo ${p##*/})"
echo "$p"
echo "$parc"
if [ "$parc" = "_TousLesPostes" ]; then
for script in $p/*.sh; do
[ "$script" = "$p/*.sh" ] && continue
echo "on lance $script"
/bin/bash $script
done
fi
if appartient_au_parc $parc $NOM_HOTE; then
# [ -e $REP_LANCEPARC/$parc.sh ] && $REP_LANCEPARC/$parc.sh
for script in $p/*.sh; do
[ "$script" = "$p/*.sh" ] && continue
echo "on lance $script"
/bin/bash $script
done
fi
done
}
# Cette fonction lance tous les scripts unefois situés dans le
# répertoire dont le chemin absolu doit être donné en paramètre
# (sans / à la fin).
function lancer_unefois_dans_repertoire ()
{
# $1 = le chemin absolu du répertoire.
local nom_local
local nom_distant
for nom_distant in "$1/"*".unefois"; do
[ "$nom_distant" = "$1/*.unefois" ] && continue
[ ! -f "$nom_distant" ] && continue
nom_local="$REP_UNEFOIS_LOCAL/"$(basename "$nom_distant")
if ! test -e "$nom_local"; then
# Si le « unefois » distant n'existe pas en local
# alors on le copie en local et on le lance.
# À ce stade, j'ai vu des plantages de la commande cp du
# genre « cp: ignore le fichier « /xxx/yyy » car il a été
# remplacé durant la copie », alors que ce n'est pas le
# cas. Ça semble être plus ou moins un bug...
if cp "$nom_distant" "$nom_local" && \
diff -q "$nom_distant" "$nom_local" >/dev/null 2>&1
then
chmod u+x "$nom_local"
# L'exécutable « unefois » est lancé en arrière plan.
"$nom_local" > "$nom_local.log" 2>&1 &
else
# Si la copie en local n'a pas bien fonctionné,
# on ne lance pas l'exécutable et on supprime
# sa version locale si elle existe afin qu'il y
# ait tentative d'exécution la prochaine fois.
test -e "$nom_local" && rm -rf "$nom_local"
afficher "Échec de la copie de $nom_distant en local." \
"L'exécutable ne sera pas lancé. Il y aura" \
"tentative au prochain démarrage du système."
fi
fi
done
}
# Fonction qui permet d'activer le pavé numérique. Cette fonction
# s'exécute à la fin du script et peut prendre un argument optionnel
# qui est le délai d'attente entre la fin du script et le lancement
# de l'activation du pavé numérique. Si l'argument est absent, par
# défaut le délai est de 1 seconde.
function activer_pave_numerique ()
{
local commande
if [ "$ARCHITECTURE" = "x86_64" ]; then
# On est sur du 64 bits.
commande="$REP_BIN_LOCAL/activer_pave_numerique_x86_64"
else
# Sinon, on est a priori sur du 32 bits.
commande="$REP_BIN_LOCAL/activer_pave_numerique_i386"
fi
local delai
if echo "$1" | grep -Eq -- '^[0-9]+$'; then
delai="$1"
else
delai="1"
fi
executer_a_la_fin "$delai" "$commande"
}
# Fonction qui renvoie 0 si l'argument correspond à un login
# d'un compte connecté au système, et renvoie 1 sinon.
function est_connecte ()
{
### ---%<------%<------%<------%<------%<------%<------%<------%<---
### Sans doute depuis une mise à jour, ce problème ne semble plus
### être d'actualité.
# Sur Precise, pour des raisons que j'ignore, la commande who ne
# liste aucune session graphique ouverte. Du coup, je n'ai pas
# trouvé mieux que de bricoler un truc basé sur la commande ps
# avec la recherche de la chaîne unity. C'est bien dommage,
# mais je n'ai pas trouvé mieux.
###if [ "$NOM_DE_CODE" = "precise" ]; then
### if ps -u "$1" -U "$1" | awk '{ if (NR>0) print $4 }' | grep -q '^unity'; then
### return 0
### else
### return 1
### fi
###fi
### ---%<------%<------%<------%<------%<------%<------%<------%<---
# Sinon, dans le cas général, on va utiliser la commande who.
# $1 est une chaîne représentant un login.
if who | cut -d ' ' -f 1 | grep_debut_fin_verbatim "$1"; then
return 0
else
return 1
fi
}
# Fonction qui affiche le nombre d'utilisateurs différents
# sont sont connectés au système.
function afficher_nombre_utilisateurs_connectes ()
{
### ---%<------%<------%<------%<------%<------%<------%<------%<---
### Sans doute depuis une mise à jour, ce problème ne semble plus
### être d'actualité.
# Pour les mêmes raisons que celles expliquées dans la fonction
# est_connecte, dans le cas de la distribution Precise, je n'ai
# rien trouvé de mieux que cet immonde bricolage avec ps.
###if [ "$NOM_DE_CODE" = "precise" ]; then
### ps aux | awk '/ [u]nity/ { print $1, $11 }' | cut -d' ' -f'1' | sort | uniq | wc -l
### return 0
###fi
### ---%<------%<------%<------%<------%<------%<------%<------%<---
# Sinon, dans le cas général, on va utiliser la commande who.
who | cut -d' ' -f'1' | sort | uniq | wc -l
}
# Fonction qui permet de monter le partage avec les droits de l'utilisateur
# qui se connecte. Le premier argument est le chemin UNC du partage. Le
# deuxième argument est le nom du répertoire qui sera créé dans
# "$REP_MONTAGE_UTILISATEUR/" et qui sera le point de montage du partage.
# Les arguments suivants correspondent aux chemins absolus des liens
# symboliques qui seront créés et qui pointeront vers le point de
# montage.
# Le montage se fait via une authentification
# sachant que c'est le fichier CREDENTIALS qui contient le login
# et le mot de passe sous la forme :
# --%<----%<----%<----%<----%<----%<----%<--
# username=toto
# password=le-mot-de-passe
# --%<----%<----%<----%<----%<----%<----%<--
function monter_partage ()
{
# Si on n'est pas pendant la phase d'ouverture, on abandonne.
[ "$PHASE" != "ouverture" ] && return 1
local partage
local nom_repertoire
partage="$1"
nom_repertoire="$2"
if [ -z "$partage" ] || [ -z "$nom_repertoire" ]; then
# Les arguments ne sont pas correctement renseignés.
afficher_fenetre_erreur "Problème" \
"Erreur lors de l'appel de la fonction \"monter_partage\". Au moins" \
"un des deux arguments est vide."
return 1
fi
if ! echo "$nom_repertoire" | grep -Eiq '^[-_a-z0-9]+$'; then
# Le nom du répertoire futur point de montage comporte des
# caractères illicites.
afficher_fenetre_erreur "Problème" \
"Erreur lors de l'appel de la fonction \"monter_partage\". Le" \
"nom du répertoire de montage (le deuxième argument de la fonction)" \
"contient des caractères illicites. Le nom de ce répertoire doit" \
"être constitué uniquement des caractères a-z, A-Z, du tiret (-)" \
"et du tiret-bas (_)."
return 2
fi
local point_de_montage
point_de_montage="$REP_MONTAGE_UTILISATEUR/$nom_repertoire"
if [ -e "$point_de_montage" ]; then
# Le répertoire de point de montage correspond à un nom
# de fichier déjà existant.
afficher_fenetre_erreur "Problème" \
"Erreur lors de l'appel de la fonction \"monter_partage\". Le" \
"répertoire de montage \"$nom_repertoire\" correspond à un nom" \
"de fichier déjà existant."
return 3
fi
mkdir "$point_de_montage"
# Par défaut, la commande mount.cifs tente d'utiliser le port 445 d'abord
# pour contacter le serveur. Mais dans ce cas la variable de substitution
# %m utilisée dans les fichiers smb*.conf du serveur et qui est censée
# être remplacée par le nom (netbios) du client est alors remplacée par
# son adresse IP. Avec l'option « port=139 », la variable %m sera bien
# remplacée par le nom (netbios) du client. En principe, l'option suivante
# « netbiosname=... », qui indique le nom (netbios) du client à envoyer au
# serveur, n'est pas nécessaire mais on la met quand même histoire
# d'enfoncer le clou encore un peu.
monter_partage_cifs "$partage" "$point_de_montage" \
"credentials=$CREDENTIALS,uid=$LOGIN,gid=lcs-users,port=139,netbiosname=$NOM_HOTE"
if [ "$?" != "0" ]; then
# Ici, il y a un problème réseau.
afficher_fenetre_erreur "Problème" \
"Erreur lors de l'appel de la fonction \"monter_partage\"." \
"Impossible de monter le partage \"$partage\"."
return 4
fi
# On « mange » les arguments $1 et $2.
shift 2
# S'il n'y a pas d'autre argument...
if [ "$#" = "0" ]; then
# ... alors pas de création de lien symbolique.
return 0
fi
# Création des liens symboliques.
local lien
for lien in "$@"; do
# Si le lien correspond à un fichier qui existe déjà, on passe.
[ -e "$lien" ] && continue
# Si le lien ne se trouve pas dans le home de l'utilisateur, on passe.
! echo "$lien" | grep_debut_verbatim "$REP_HOME/" && continue
creer_lien_symb "$point_de_montage" "$lien" "$LOGIN"
done
}
# Fonction qui permet de créer des liens symboliques appartenant
# à l'utilisateur qui se connecte. Le premier argument est le
# fichier (au sens large, ça peut être un répertoire) vers lequel
# pointent les liens et les arguments suivants (autant qu'on veut)
# sont les chemins absolus des liens symboliques à créer.
# Si jamais la cible n'est pas un chemin absolu, alors la
# fonction considère que le chemin absolu de la cible est
# "$REP_MONTAGE_UTILISATEUR/$cible".
function creer_lien ()
{
# Si on n'est pas pendant la phase d'ouverture, on abandonne.
[ "$PHASE" != "ouverture" ] && return 1
local cible
cible="$1"
# On teste si la cible est un chemin absolu (ie commence par un /).
if ! echo "$cible" | grep -q '^/'; then
# La cible n'est pas un chemin absolu, on complète le chemin
# pour en faire un chemin absolu.
cible="$REP_MONTAGE_UTILISATEUR/$cible"
fi
# On « mange » l'argument $1.
shift 1
# S'il n'y a pas d'autre argument, c'est qu'il y a une erreur...
if [ "$#" = "0" ]; then
# ... alors pas de création de lien symbolique.
return 1
fi
# Création des liens symboliques.
local lien
for lien in "$@"; do
# Si le lien correspond à un fichier qui existe déjà, on passe.
[ -e "$lien" ] && continue
# Si le lien ne se trouve pas dans le home de l'utilisateur, on passe.
! echo "$lien" | grep_debut_verbatim "$REP_HOME/" && continue
creer_lien_symb "$cible" "$lien" "$LOGIN"
done
}
# Fonction qui lit son entrée standard et qui attend un unique argument
# (une chaîne de caractères). Elle renvoie 0 si la chaîne donnée en argument
# se trouve en début de ligne d'au moins une ligne de l'entrée standard et
# renvoie 1 sinon. Attention, la chaîne donnée en argument n'est pas vue
# comme une regex mais comme une « chaîne brute », ie les caractères
# habituellement spéciaux pour une regex ne le sont pas et représentent
# eux-même (le point représente le point et pas un caractère quelconque).
grep_debut_verbatim ()
{
local motif n debut
motif="$1"
# Le nombre de caractères du motif.
n=${#motif};
# L'option -r est indispensable car sans elle la chaîne 'a\aa'
# deviendrait 'aaa' une fois passée dans le filtre read.
while read -r; do
# On coupe la ligne à n caractères maximum.
debut=${REPLY:0:$n}
# On compare le début de ligne avec le motif.
if [ "$debut" = "$motif" ]; then
return 0
fi
done
# Si on arrive ici, cela veut dire que le motif n'a jamais
# été trouvé en début de ligne. Du coup, on renvoie 1.
return 1
}
# Fonction identique à la précédente sauf qu'elle renvoie 0 uniquement
# si la chaîne donnée en argument correspond exactement à au moins une
# ligne complète de l'entrée standard.
grep_debut_fin_verbatim ()
{
local motif
motif="$1"
# L'option -r est indispensable car sans elle la chaîne 'a\aa'
# deviendrait 'aaa' une fois passée dans le filtre read.
while read -r; do
# On compare la ligne avec le motif.
if [ "$REPLY" = "$motif" ]; then
return 0
fi
done
# Si on arrive ici, cela veut dire que le motif n'a jamais
# été trouvé dans une ligne. Du coup, on renvoie 1.
return 1
}
# Fonction qui permet d'afficher la variable d'environnement
# DBUS_SESSION_BUS_ADDRESS de l'utilisateur toto qui ouvre une session afin
# de pouvoir ensuite exécuter en tant que toto certaines commandes
# impossibles à exécuter sans cette variable d'environnement.
# On lancera alors la commande ainsi :
# DBUS_SESSION_BUS_ADDRESS="$(afficher_adresse_bus)" sudo -Eu "$LOGIN" commande
# Attention, le temps pour exécuter cette fonction est indéterminé et surtout
# la fonction ne se terminera qu'une fois l'ouverture de session achevée (car
# c'est apparemment seulement une fois l'ouverture de session achevée que le
# fichier dans lequel on récupère la variable d'environnement est créé). Donc
# il faudra TOUJOURS lancer la fonction dans un sous-shell ainsi « { ... } & »,
# sans quoi on risque de bloquer le script de logon.
function afficher_adresse_bus ()
{
local n compteur
n=$(\ls "$REP_HOME/.dbus/session-bus/" 2> /dev/null | wc -l)
compteur=1
# On attend que le fichier dans "$REP_HOME/.dbus/session-bus/ soit
# créé lors de l'ouverture de session.
while [ "$n" != "1" ]; do
sleep 0.2
n=$(\ls "$REP_HOME/.dbus/session-bus/" 2> /dev/null | wc -l)
compteur=$((compteur+1))
if [ "$compteur" -ge "100" ]; then
# Au bout d'un certain nombre de tentatives on abandonne.
# Important pour éviter une boucle infinie.
return 1
fi
done
# On récupère et on affiche la valeur de la variable d'environnement
# DBUS_SESSION_BUS_ADDRESS trouvée dans le fichier qui vient d'être créé.
grep "^DBUS_SESSION_BUS_ADDRESS=" "$REP_HOME/.dbus/session-bus/"* | cut -d"=" -f"2-"
}
# Fonction, utilisable uniquement lors de l'ouverture de session, qui
# prend comme premier argument le chemin absolu d'un fichier et
# comme deuxième argument le chemin absolu d'une image qui sera
# l'icône associé au fichier. Le chemin du fichier dont on veut changer
# l'icône doit forcément se trouver dans "$REP_HOME".
#
# Rq: bien que la commande gvfs-set-attribute existe sous XUbuntu,
# celle-ci semble sans effet. Apparemment, sous XUbuntu, impossible
# de faire de changement d'icône.
function changer_icone ()
{
# Cette partie sera lancée via un « & » et donc dans un sous-shell d'après
# la page man de bash.
{
# Si on n'est pas pendant la phase d'ouverture, on abandonne.
[ "$PHASE" != "ouverture" ] && return 1
# Si le fichier cible ne se trouve pas dans le home de l'utilisateur,
# on abandonne.
! echo "$1" | grep_debut_verbatim "$REP_HOME/" && return 1
# Si le fichier image n'existe pas, on abandonne.
[ ! -f "$2" ] && return 1
# On récupère la variable d'environnement « adresse bus ».
local valeur
valeur=$(afficher_adresse_bus)
if [ "$valeur" = "" ]; then
# Si la valeur récupérée est vide, on abandonne.
exit 1
fi
# On change l'attribut concernant l'icône du fichier.
DBUS_SESSION_BUS_ADDRESS="$valeur" sudo -Eu "$LOGIN" \
gvfs-set-attribute -t string "$1" "metadata::custom-icon" "file://$2"
# Sur le bureau, pour que l'affichage se mette à jour, il faut faire
# un touch sur le fichier avec l'option « --no-dereference », sinon
# c'est la cible du lien symbolique qui est visée et non le lien
# symbolique lui-même.
touch --no-dereference "$1"
} > "/dev/null" 2>&1 &
# Ce sous-processus pourra être lancé à plusieurs reprises et,
# sans la redirection ci-dessus, tous les sous-processus écriraient
# plus ou moins en même temps sur un même fichier de log ce
# qui pourrait donner un résultat imprévisible sur le fichier
# de log. Le plus sage est de rediriger les deux sorties de
# ce bout de code dans /dev/null. Ce n'est pas très grave, ce
# n'est pas une partie critique.
}
# Cette fonction change le papier peint de l'utilisateur qui se
# connecte lors de l'ouverture de session. Elle prend 1 argument qui doit
# être le chemin absolu du fichier image (un accès à ce fichier en lecture
# pour l'utilisateur suffit).
function changer_papier_peint ()
{
# Cette partie sera lancée via un « & » et donc dans un sous-shell d'après
# la page man de bash.
{
# Si on n'est pas pendant la phase d'ouverture, on abandonne.
[ "$PHASE" != "ouverture" ] && return 1
# Si le fichier image n'existe pas, on abandonne.
[ ! -f "$1" ] && return 1
# On récupère la variable d'environnement « adresse bus ».
local valeur
valeur=$(afficher_adresse_bus)
if [ "$valeur" = "" ]; then
# Si la valeur récupérée est vide, on abandonne.
exit 1
fi
# La commande pour changer de papier peint dépend de l'environnement
# de bureau utilisé. Comment connaître l'environnement de bureau
# utilisé par le compte qui se connecte ? Je pensais utiliser des
# choses comme « if ps -u "$LOGIN" | grep -q unity; then » mais hélas
# pour des raisons de timing ça ne fonctionne pas. La commande ps
# ne renvoie pas (encore) de processus unity* alors que l'utilisateur
# courant se connecte pourtant avec Unity. On pourrait résoudre ce
# problème de timing avec la commande sleep mais ça n'est pas 100%
# fiable : quel argument donner à sleep dans ce cas ? Du coup, on
# va se contenter de lancer la commande pour chacun des
# environnements de bureau pris en charge, pour peu que la commande
# en question existe sur le système. Le résultat sera garanti et je
# pense que la charge processeur supplémentaire due aux commandes
# inutiles sera négligeable.
# Sur Unity.
if which gsettings > /dev/null; then
# Il est hautement probable que l'utilisateur soit sur Unity.
DBUS_SESSION_BUS_ADDRESS="$valeur" sudo -Eu "$LOGIN" \
gsettings set org.gnome.desktop.background picture-options stretched
DBUS_SESSION_BUS_ADDRESS="$valeur" sudo -Eu "$LOGIN" \
gsettings set org.gnome.desktop.background picture-uri "file://$1"
fi
# Sur Xfce.
if which xfconf-query > /dev/null; then
# Il est hautement probable que l'utilisateur soit sur Xfce.
DBUS_SESSION_BUS_ADDRESS="$valeur" sudo -Eu "$LOGIN" \
xfconf-query --create -c xfce4-desktop -p /backdrop/screen0/monitor0/image-style -s "3" -t int # la valeur correspond à "stretched"
DBUS_SESSION_BUS_ADDRESS="$valeur" sudo -Eu "$LOGIN" \
xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitor0/image-path -s "$1"
fi
# Sur Gnome.
if which gconftool-2 > /dev/null; then
DBUS_SESSION_BUS_ADDRESS="$valeur" sudo -Eu "$LOGIN" \
gconftool-2 --set /desktop/gnome/background/picture_options --type string stretched
DBUS_SESSION_BUS_ADDRESS="$valeur" sudo -Eu "$LOGIN" \
gconftool-2 --set /desktop/gnome/background/picture_filename --type string "$1"
fi
# Sur LXDE.
if which pcmanfm > /dev/null; then
DBUS_SESSION_BUS_ADDRESS="$valeur" sudo -Eu "$LOGIN" pcmanfm --set-wallpaper="$1" --wallpaper-mode=center
fi
} > "/dev/null" 2>&1 &
}
# Fonction qui lance, à la fin de l'exécution du script, une commande
# quelconque. Le premier argument est le délai en secondes entre la fin
# de l'exécution du script et le début du lancement de la commande.
# Les arguments suivants correspondent à la commande à lancer avec ses
# paramètres. Si le script est trop long à se terminer (plus précisément,
# si la fonction executer_a_la_fin doit attendre que le script se termine
# plus de 30 secondes) alors la commande ne sera pas lancée.
# Attention, la sortie standard et la sortie des erreurs seront
# redirigées vers /dev/null afin qu'il n'y ait pas d'écritures
# simultanées sur les fichiers de log.
function executer_a_la_fin ()
{
# Tout sera lancé dans un sous-shell à cause du « & » à la fin.
{
local compteur
compteur="1"
# Tant que le script de logon local est lu par un processus
# et donc qu'a priori il est en cours d'exécution, on attend.
while fuser "$LOGON_SCRIPT_LOCAL" >/dev/null 2>&1; do
sleep 0.5
if [ "$compteur" -ge "60" ]; then
# Au bout de 30 secondes, si ce n'est toujours
# pas fini, on abandonne.
return 1
fi
compteur=$((compteur+1))
done
sleep "$1" # petit délai avant de commencer.
# On « mange » $1.
shift 1
# Et on lance la commande avec ses arguments.
"$@"
} > "/dev/null" 2>&1 &
}
######################################
######################################
### ###
### Les arguments passés au script ###
### ###
######################################
######################################
# Variable déjà affectée afin de rediriger les sorties stdout stderr
# dans des fichiers, dès le début du script.
#PHASE="$1"
if [ -z "$2" ]; then
# Si "$2" est vide alors par défaut il y a tentative de MAJ.
TENTATIVE_DE_MAJ="true"
else
TENTATIVE_DE_MAJ="$2" # avec "$2" qui doit valoir true ou false.
fi
DEMARRAGE="$3"
if [ "$DEMARRAGE" != "true" ]; then
# Par défaut, cette variable, qui indique si le script s'exécute
# lors du démarrage, est sur false. La fonction "initialisation"
# se chargera de mettre cette variable sur true le cas échéant (en
# se basant sur le montage ou non de REP_TMP_LOCAL).
DEMARRAGE=false
fi
# On met dès le départ et une bonne fois pour toute
# la date du moment dans le fichier de log.
afficher_date
set -x
#############################################
#############################################
### ###
### Réglage des variables d'environnement ###
### ###
#############################################
#############################################
# Pour avoir des sorties les plus simples possibles, c'est-à-dire
# en anglais avec des caractères 100% ASCII.
export LC_ALL="C"
# Du coup, on utilisera la locale française au coup par coup.
# Celle-ci, il n'est pas nécessaire de l'exporter.
LOCALE_FRANCAISE="fr_FR.utf8"
# Réglage du PATH.
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
########################################################
########################################################
### ###
### Définitions des variables globales et exports de ###
### certaines variables et fonctions ###
### ###
########################################################
########################################################
#############################################
### Définitions de variables « globales » ###
#############################################
# Variables liées au Se3.
SE3="__SE3__"
BASE_DN="__BASE_DN__"
SERVEUR_NTP="__SERVEUR_NTP__"
NOM_PARTAGE_NETLOGON="netlogon-linux"
CHEMIN_PARTAGE_NETLOGON="//$SE3/$NOM_PARTAGE_NETLOGON"
# Variables liées à l'hôte.
NOM_HOTE=$(hostname)
NOM_DE_CODE=$(lsb_release --codename | cut -f 2)
ARCHITECTURE=$(uname -m)
MAX_UTILISATEURS_CONNECTES="4"
# Apparemment, l'option serverino permet d'éviter des erreurs obscures
# lors de copies avec la commande cp (notamment avec les exécutables
# *.unfois), alors qu'avec l'option noserverino j'avais rencontré
# ces erreurs obscures.
OPTIONS_MOUNT_CIFS_BASE="nobrl,serverino,iocharset=utf8,sec=ntlmv2"
# Noms de fichiers ou de répertoires locaux au client.
#REP_SE3_LOCAL="/etc/se3" # Déjà affectée en début de script.
REP_BIN_LOCAL="$REP_SE3_LOCAL/bin"
REP_TMP_LOCAL="$REP_SE3_LOCAL/tmp"
#REP_LOG_LOCAL="$REP_SE3_LOCAL/log" # Déjà affectée en début de script.
REP_SKEL_LOCAL="$REP_SE3_LOCAL/skel"
REP_UNEFOIS_LOCAL="$REP_SE3_LOCAL/unefois"
LOGON_SCRIPT_LOCAL="$REP_BIN_LOCAL/logon"
VERSION_SKEL_LOCAL="$REP_SKEL_LOCAL/.VERSION"
CREDENTIALS="$REP_TMP_LOCAL/credentials"
REP_MONTAGE="/mnt"
# Noms de fichiers ou de répertoires du Se3 accessibles par le client
# via le montage du partage NOM_PARTAGE_NETLOGON du Se3.
REP_NETLOGON="$REP_MONTAGE/netlogon"
LOGON_SCRIPT="$REP_NETLOGON/bin/logon"
REP_SKEL="$REP_NETLOGON/distribs/$NOM_DE_CODE/skel"
VERSION_SKEL="$REP_SKEL/.VERSION"
REP_UNEFOIS="$REP_NETLOGON/unefois"
REP_LANCEPARC="$REP_NETLOGON/lanceparc"
####################################################################################
### Définitions de variables « globales » valables uniquement lors d'une session ###
####################################################################################
if [ "$PHASE" = "ouverture" ] || [ "$PHASE" = "fermeture" ]; then
LOGIN="$LOGNAME"
REP_MONTAGE_UTILISATEUR="$REP_MONTAGE/_$LOGIN"
REP_HOME="/home/$LOGIN"
NOM_COMPLET_LOGIN=$(getent passwd "$LOGIN" | cut -d':' -f5 | cut -d',' -f1)
if [ "$PHASE" = "ouverture" ]; then
# La requête LDAP ne sera faite qu'au moment de l'ouverture de session.
if ! est_utilisateur_local "$LOGIN" ; then
LISTE_GROUPES_LOGIN=$(afficher_liste_groupes "$LOGIN")
fi
fi
fi
############################################################################
### On exporte certaines variables « globales » pour les scripts unefois ###
############################################################################
export SE3
export BASE_DN
export NOM_HOTE
export NOM_DE_CODE
export ARCHITECTURE
export REP_BIN_LOCAL # Seulement parce que la fonction activer_pave_numerique l'utilise.
# Les variables LOGIN, NOM_COMPLET_LOGIN, LISTE_GROUPES_LOGIN seront utilisables
# dans les fonctions ouverture_perso et fermeture_perso, mais un export
# ne sert à rien pour ces variables car elles n'ont pas de sens dans
# les scripts unefois.
# Quant à la variable DEMARRAGE, pas d'export non plus car dans un script
# unefois elle vaudra toujours true, puisque les scripts unefois ne sont
# lancés qu'au démarrage du système.
############################################################################
### On exporte certaines fonctions « globales » pour les scripts unefois ###
############################################################################
export -f appartient_au_groupe
export -f appartient_au_parc
export -f afficher_liste_groupes
export -f afficher_liste_parcs
export -f est_dans_liste
export -f activer_pave_numerique
export -f est_utilisateur_local
export -f est_connecte
################################################################################
################################################################################
### ###
### Définitions des trois fonctions de base appelées à des instants ###
### différents par la machine clientes : ###
### ###
### - "initialisation" qui sera appelée juste avant l'affichage de ###
### la fenêtre de connexion, c'est-à-dire au moment du ###
### démarrage et après chaque fermeture de session. ###
### ###
### - "ouverture" qui sera appelée au moment de ###
### l'ouverture de session de l'utilisateur, juste après ###
### l'authentification de celui-ci. ###
### ###
### - "fermeture" qui sera appelée au moment de la fermeture ###
### de session de l'utilisateur. ###
### ###
################################################################################
################################################################################
function initialisation ()
{
nettoyer_rep_tmp_local
# On attend un petit peu car, sur Precise par exemple, les processus de
# l'utilisateur qui vient de fermer sa session sont encore en vie si
# bien que, aux yeux du script de logon, l'utilisateur est encore
# connecté et le nettoyage est alors incomplet.
sleep 1
nettoyer_rep_montage
nettoyer_rep_home
local n
local message
n=$(afficher_nombre_utilisateurs_connectes)
if [ "$n" -ge "$MAX_UTILISATEURS_CONNECTES" ]; then
message="Trop de sessions n'ont pas été fermées. Le système va"
message="$message redémarrer ce qui provoquera la fermeture"
message="$message de toutes les sessions en suspens."
LC_ALL="$LOCALE_FRANCAISE" zenity \
--info "Trop de sessions non fermées" \
--text "$message" --timeout 10
reboot && exit
fi
if ! mountpoint -q "$REP_TMP_LOCAL"; then
# REP_TMP_LOCAL n'est pas monté, donc c'est le démarrage du système,
# il faut donc modifier la variable DEMARRAGE.
DEMARRAGE=true
mount -t tmpfs -o size=100k tmpfs "$REP_TMP_LOCAL"
fi
if monter_netlogon; then
# Montage réussi ou bien il avait été effectué auparavant.
# Donc le contenu n'est pas forcément lisible (le service
# Samba entre temps est peut être tombé en panne).
# Tentative de MAJ de LOGON_SCRIPT.
if $TENTATIVE_DE_MAJ \
&& timeout "--signal=SIGTERM" 2s test -r "$LOGON_SCRIPT" \
&& ! diff -q "$LOGON_SCRIPT_LOCAL" "$LOGON_SCRIPT" >/dev/null 2>&1
then
# On exécute le script distant avec l'argument empêchant une MAJ
# et en signalant, avec la variable DEMARRAGE, si l'on est pendant
# le démarrage du système ou non. En effet, si c'est le script
# de logon distant qui est lancé, alors REP_TMP_LOCAL est
# sans doute déjà monté cela ne sera plus un critère pour savoir
# si l'on est pendant le démarrage du système.
"$LOGON_SCRIPT" "initialisation" false "$DEMARRAGE"
# Si le script distant s'est effectué sans erreur (typiquement il
# ne possède pas d'erreur de syntaxe), alors on lance la mise à
# jour.
if [ "$?" = "0" ]; then
mettre_a_jour_logon
else
afficher "Attention, a priori le script de logon distant n'est pas correct." \
"Pas de mise à jour effectuée en local."
fi
# Inutile de continuer le script, on vient d'exécuter une fois
# sa version à jour (celle du serveur) au complet. Alors on sort.
exit 0
fi
# Tentative de MAJ du profil et lancements des « unefois ».
if timeout "--signal=SIGTERM" 2s test -r "$VERSION_SKEL"; then
if ! diff -q "$VERSION_SKEL_LOCAL" "$VERSION_SKEL" >/dev/null 2>&1; then
mettre_a_jour_profil
fi
# Puisque le serveur semble accessible, on tente d'accéder à
# REP_UNEFOIS, dans le cas où l'on est dans une phase d'initialisation
# qui correspond à un démarrage.
if "$DEMARRAGE" && timeout "--signal=SIGTERM" 2s test -d "$REP_UNEFOIS"; then
lancer_unefois
fi
# Idem ci-dessus pour le répertoire lance-parc
if timeout "--signal=SIGTERM" 2s test -d "$REP_LANCEPARC"; then
lancer_parc
fi
fi
fi
# Si jamais le répertoire ".dbus" existe déjà dans le profil par défaut
# local, il faut le supprimer car il devra être généré à la volée au
# moment de l'ouverture de session dans le home de l'utilisateur qui
# se connecte. En effet, ce répertoire contiendra alors un fichier
# qui permettra de connaître la valeur de la variable
# DBUS_SESSION_BUS_ADDRESS de la session en cours. Si on ne prend pas
# la précaution de supprimer ce répertoire dans le profil local maintenant,
# on risque de récupérer alors la valeur de cette variable pour une
# ancienne session, et toutes les fonctions qui dépendent de cette
# variable risqueront de ne pas marcher.
if [ -e "$REP_SKEL_LOCAL/.dbus" ]; then
afficher "Suppression du répertoire .dbus/ dans le profil local."
rm -fr --one-file-system "$REP_SKEL_LOCAL/.dbus"
fi
initialisation_perso 1> "$REP_LOG_LOCAL/1.initialisation_perso.log" 2>&1
exit 0
}
function ouverture ()
{
# Quoi qu'il arrive, à la fin du script lors de l'ouverture, on
# nettoie le répertoire temporaire local.
trap nettoyer_rep_tmp_local EXIT
# Dans cette fonction, on peut faire usage de $LOGIN
# qui est le login de l'utilisateur courant.
if est_utilisateur_local "$LOGIN" ; then
# On ne fait rien et on arrête le script.
exit 0
fi
# Mise en place du home de l'utilisateur qui n'est pas un
# utilisateur local ici.
if [ -e "$REP_HOME" ]; then
rm -fr --one-file-system "$REP_HOME"
fi
mkdir -p "$REP_HOME/Bureau"
# On copie tout le contenu de REP_SKEL_LOCAL dans le HOME_LOCAL.
shopt -s dotglob
local f
for f in "$REP_SKEL_LOCAL/"*; do
cp -r "$f" "$REP_HOME"
done
shopt -u dotglob
# Ajustement des droits sur le home.
chown -R "$LOGIN:" "$REP_HOME"
# On rend les fichiers *.desktop exécutables pour l'utilisateur au cas où...
for f in "$REP_HOME/Bureau/"*".desktop"; do
[ "$f" = "$REP_HOME/Bureau/*.desktop" ] && continue
chmod u+x "$f"
done
chmod 700 "$REP_HOME"
sync
# Après la création du home de l'utilisateur, on attend un peu.
# En effet, j'ai constaté empiriquement que, sur Precise par exemple,
# sans ce laps de temps les réglages inscrits dans le profil
# (notamment dans le répertoire "~/.config/") n'étaient pas
# systématiquement pris en compte par l'environnement de bureau.
sleep 1
# Maintenant, il faut procéder aux montages des partages CIFS.
# Pour commencer, il faut attendre la création du fichier CREDENTIALS.
local c
c="1"
while ! test -r "$CREDENTIALS" && [ "$c" -le 4 ]; do
# Tant que le fichier n'est pas accessible en lecture, on attend un peu.
sleep 0.5
c=$((c+1)) # pour éviter une boucle infinie, après 4 tentatives, on sort de la boucle.
done
# Si c vaut 5, c'est qu'il a été impossible de lire le fichier CREDENTIALS
# et donc que les montages seront impossibles. Dans ce cas, il vaut mieux
# sortir et ne pas tenter les-dits montages.
if [ "$c" -eq "5" ]; then
afficher_fenetre_erreur "Problème" \
"Le montage des partages de l'utilisateur sont impossibles car" \
"le fichier \"CREDENTIALS\" est inaccessible."
exit 1
fi
if [ -e "$REP_MONTAGE_UTILISATEUR" ]; then
# Le répertoire existe déjà ce qui n'est pas normal.
# Du coup, on nettoie le répertoire de montage, étant
# donné qu'il sera créé juste après.
nettoyer_un_rep_montage "$REP_MONTAGE_UTILISATEUR"
fi
# On crée le répertoire où seul LOGIN pourra se rendre.
mkdir "$REP_MONTAGE_UTILISATEUR"
chown "$LOGIN:" "$REP_MONTAGE_UTILISATEUR"
chmod "700" "$REP_MONTAGE_UTILISATEUR"
# C'est dans la fonction ouverture_perso que les montages seront
# effectués pour que l'administrateur puisse les gérer comme
# bon lui semble.
ouverture_perso 1> "$REP_LOG_LOCAL/2.ouverture_perso.log" 2>&1
# Surtout, on détruit immédiatement le fichier CREDENTIALS contenu
# dans le répertoire temporaire local. En principe, c'est fait
# automatiquement grâce à la fonction trap ci-dessus, mais on
# rajoute une couche au cas où...
nettoyer_rep_tmp_local
exit 0
}
function fermeture ()
{
# Dans cette fonction, on peut faire usage de $LOGIN
# qui est le login de l'utilisateur courant.
if est_utilisateur_local "$LOGIN" ; then
# On ne fait rien et on arrête le script.
exit 0
fi
# Par mesure de sécurité, avant le nettoyage, on débarrasse le
# /home des liens symboliques qui pointent vers des partages.
# Il y en a à la racine du /home et sur le « Bureau ».
# Le « ! -name '.gvfs' » permet d'éviter une petite erreur lors
# de la commande « find » quand on est sous Ubuntu.
find "$REP_HOME" -maxdepth 1 ! -name '.gvfs' -type l -exec rm -f '{}' \;
find "$REP_HOME/Bureau" -maxdepth 1 -type l -exec rm -f '{}' \;
fermeture_perso 1> "$REP_LOG_LOCAL/3.fermeture_perso.log" 2>&1
exit 0
}
# Dans logon, les variables SE3 et BASE_DN sont definies
function genere_fond_ecran() {
if [ -z "$LOGIN" ]
then
# La variable LOGIN n'est pas définie. On arrête là.
return 1
fi
# Quelques parametres
dossier_base_fond="$REP_NETLOGON/fond_ecran"
dossier_dest_fond=/tmp
ext=jpg
t=$(which convert)
if [ -z "$t" ]; then
echo "La generation de fond d'ecran necessite l'installation d'imagemagick cote client."
else
# Menage
if [ -e "${dossier_dest_fond}/fond_ecran_$LOGIN.$ext" ]; then
rm -f "${dossier_dest_fond}/fond_ecran_$LOGIN.$ext"
fi
# Recuperation des parametres generaux
parametres_generation_fonds
if [ -n "$prefixe" ]; then
if [ "$LOGIN" = "admin" ]; then
t=$(grep "function parametres_fond_ecran_admin()" $REP_BIN_LOCAL/logon)
if [ -n "$t" ]; then
parametres_fond_ecran_admin
if [ "$generation_fonds_ecran" = "actif" ]; then
annotation_fond_ecran_admin
temoin="admin"
classe="Admins"
fi
fi
else
if est_dans_liste "$LISTE_GROUPES_LOGIN" "overfill"; then
t=$(grep "function parametres_fond_ecran_overfill()" $REP_BIN_LOCAL/logon)
if [ -n "$t" ]; then
parametres_fond_ecran_overfill
if [ "$generation_fonds_ecran" = "actif" ]; then
annotation_fond_ecran_overfill
temoin="overfill"
classe=""
fi
fi
fi
# Pour les profs on outrepasse les parametres overfill
if est_dans_liste "$LISTE_GROUPES_LOGIN" "Profs"; then
t=$(grep "function parametres_fond_ecran_Profs()" $REP_BIN_LOCAL/logon)
if [ -n "$t" ]; then
parametres_fond_ecran_Profs
if [ "$generation_fonds_ecran" = "actif" ]; then
annotation_fond_ecran_Profs
temoin="Profs"
classe="Profs"
fi
fi
fi
if [ -z "$temoin" ]; then
# Utilisateur non prof... -> eleves ou administratifs?
if est_dans_liste "$LISTE_GROUPES_LOGIN" "Eleves"; then
# Utilisateur eleve
# Dans le cas d'un eleve, le groupe Classe est prioritaire (pour l'image) sur le groupe eleves.
#classe=$(ldapsearch -xLLL -h "$SE3" -b "ou=Groups,$BASE_DN" "(&(memberuid=$LOGIN)(cn=Classe*))" cn | grep "^cn: " | sed -e "s/^cn: //"|head -n1)
#classe=$(echo "$LISTE_GROUPES_LOGIN" | sed -rn 's/^Classe_(.*)$/\1/p')
# Les fonctions sont formatees
# en annotation_fond_ecran_admin,
# annotation_fond_ecran_,...
# mais si un admin cree une classe 'Classe_admin', et qu'on vire le prefixe 'Classe_', on va avoir une collision de noms.
classe=$(echo "$LISTE_GROUPES_LOGIN" | grep "^Classe_" |head -n1)
if [ ! -z "$classe" ]; then
# PROBLEME AVEC CA... IL NE DOIT PAS ETRE POSSIBLE D APPELER UNE TELLE FONCTION
# Il semble que si...
t=$(grep "function parametres_fond_ecran_$classe()" $REP_BIN_LOCAL/logon)
if [ -n "$t" ]; then
parametres_fond_ecran_$classe
if [ "$generation_fonds_ecran" = "actif" ]; then
annotation_fond_ecran_$classe
temoin="$classe"
classe="$classe"
fi
fi
fi
if [ -z "$temoin" ]; then
t=$(grep "function parametres_fond_ecran_Eleves()" $REP_BIN_LOCAL/logon)
if [ -n "$t" ]; then
parametres_fond_ecran_Eleves
if [ "$generation_fonds_ecran" = "actif" ]; then
annotation_fond_ecran_Eleves
temoin="Eleves"
fi
fi
fi
else
if est_dans_liste "$LISTE_GROUPES_LOGIN" "Administratifs"; then
# Utilisateur membre de: Administratifs
t=$(grep "function parametres_fond_ecran_Administratifs()" $REP_BIN_LOCAL/logon)
if [ -n "$t" ]; then
parametres_fond_ecran_Administratifs
if [ "$generation_fonds_ecran" = "actif" ]; then
annotation_fond_ecran_Administratifs
temoin="Administratifs"
classe="Administratifs"
fi
fi
fi
fi
fi
fi
if [ -n "$temoin" ]; then
# Generation avec les parametres...
# Passage de variable:
base=$temoin
if [ "$base" == "admin" ]; then
orig="Adminse3"
else
orig="$base"
fi
# Generation du fond commun s'il n'existe pas:
if [ ! -e "${dossier_base_fond}/$orig.jpg" ]; then
convert -size ${largeur}x${hauteur} gradient:${couleur1}-${couleur2} jpeg:${dossier_dest_fond}/$orig.jpg
else
cp ${dossier_base_fond}/$orig.jpg ${dossier_dest_fond}/
fi
#===============================================================
# Generation de la chaine des infos a afficher:
chaine=""
if [ "$annotation_nom" = "1" ]; then
nom_prenom=$NOM_COMPLET_LOGIN
chaine=$(echo "$nom_prenom" | tr "'ÂÄÀÁÃÄÅÇÊËÈÉÎÏÌÍÑÔÖÒÓÕ¦ÛÜÙÚݾ´áàâäãåçéèêëîïìíñôöðòóõ¨ûüùúýÿ¸" "_AAAAAAACEEEEIIIINOOOOOSUUUUYYZaaaaaaceeeeiiiinoooooosuuuuyyz" | sed -e "s|[^A-Za-z_ -]||g" | sed -e "s|Æ|AE|g" | sed -e "s|¼|OE|g" | sed -e "s|æ|ae|g" | sed -e "s|½|oe|g")
fi
if [ "$annotation_classe" = "1" ]; then
if [ -z "$classe" ]; then
# Cas d'un eleve dans le groupe overfill:
#classe=$(ldapsearch -xLLL -h "$SE3" -b "ou=Groups,$BASE_DN" "(&(memberUid=$LOGIN)(cn=Classe_*))" cn | grep "^cn: " | sed -e "s/^cn: //"|head -n1)
#classe=$(echo "$LISTE_GROUPES_LOGIN" | sed -rn 's/^Classe_(.*)$/\1/p')
classe=$(echo "$LISTE_GROUPES_LOGIN" | grep "^Classe_" |head -n1)
fi
if [ -z "$classe" ]; then
if est_dans_liste "$LISTE_GROUPES_LOGIN" "Profs"; then
classe="Profs"
elif est_dans_liste "$LISTE_GROUPES_LOGIN" "Administratifs"; then
classe="Administratifs"
fi
fi
if [ ! -z "$classe" ]; then
if [ -n "${chaine}" ]; then
chaine="$chaine ($classe)"
else
chaine="$classe"
fi
fi
fi
# Generation de l'image:
if [ -n "$couleur_txt" ]; then
convert -fill ${couleur_txt} -pointsize $taille_police -draw "gravity North text 0,0 '$chaine'" ${dossier_dest_fond}/$orig.jpg ${prefix}${dossier_dest_fond}/fond_ecran_$LOGIN.$ext
else
cp ${dossier_dest_fond}/$orig.jpg ${dossier_dest_fond}/fond_ecran_$LOGIN.$ext
fi
fi
fi
if [ -e /tmp/fond_ecran_${LOGIN}.jpg ]; then
changer_papier_peint /tmp/fond_ecran_${LOGIN}.jpg
fi
fi
}
function supprimer_fond_ecran() {
if [ -z "$LOGIN" ]
then
# La variable LOGIN n'est pas définie. On arrête là.
return 1
fi
if [ -e /tmp/fond_ecran_${LOGIN}.jpg ]; then
rm -f /tmp/fond_ecran_${LOGIN}.jpg
fi
}
################################################################################
################################################################################
### ###
### Fin des définitions de fonctions et autres variables. ###
### Suivant le paramètre $1 (qui s'appelle $PHASE dans le script) qui est ###
### passé à ce script, c'est une des quatre fonctions de base qui sera ###
### appelée. ###
### ###
################################################################################
################################################################################
case "$PHASE" in
"initialisation")
initialisation
;;
"ouverture")
ouverture
;;
"fermeture")
fermeture
;;
*)
# On ne fait rien de spécial
true
;;
esac
exit 0
# Fin du script
################################################################################