L3 Informatique - Système
Il est important de bien lire le sujet jusqu'au bout et de bien y réfléchir avant de se lancer dans la programmation du projet.
Le but du projet est de programmer un interpréteur de commandes (aka
shell) interactif reprenant quelques fonctionnalités classiques des
shells usuels, en particulier le job control, c'est-à-dire la gestion
des tâches lancées depuis le shell. Outre la possibilité d'exécuter
toutes les commandes externes, jsh
devra proposer quelques commandes
internes, permettre la redirection des flots standard ainsi que les
combinaisons par tube, et adapter le prompt à la situation.
Plus précisément, jsh
doit respecter les prescriptions suivantes :
Un terminal peut contrôler de nombreux processus simultanément, mais ne
peut avoir, à un instant donné, qu'un seul groupe de processus à
l'avant-plan. jsh
permet le contrôle de l'exécution simultanée de
plusieurs tâches, ainsi que leur bascule entre l'avant- et
l'arrière-plan, en gardant en mémoire toutes les informations nécessaires
à la surveillance, à l'interruption et à la reprise des processus
concernés.
Pour chaque ligne de commande nécessitant la création d'un ou plusieurs
processus supplémentaires, jsh
crée un nouveau groupe de processus,
qui constitue un job. Ce groupe reçoit alors un numéro de job, qui
l'identifie de manière unique parmi les jobs en cours dépendant de
l'instance courante de jsh
.
jsh
peut lancer de nouveaux jobs à l'arrière-plan ou à l'avant-plan
du terminal, selon que la ligne de commande termine ou non par le symbole
&
. Il peut basculer un job de l'arrière-plan vers l'avant-plan, ou
relancer l'exécution d'un job suspendu, vers l'avant- ou
l'arrière-plan. Il surveille les jobs à l'arrière-plan et annonce leur
terminaison. Il peut lister les jobs surveillés en précisant leur état :
-
un job dont tous les processus sont terminés, et tel que tous les processus directement lancés par le shell ont correctement terminé (avec
exit
), est dit achevé (done); -
un job dont tous les processus sont terminés, et tel qu'au moins un processus directement lancé par le shell a terminé suite à la réception d'un signal, est dit tué (killed);
-
un job dont au moins un processus n'a pas terminé, mais dont tous les processus directement lancés par le shell ont terminé, est détaché (detached) et quitte la surveillance de
jsh
; -
un job dont tous les processus non terminés directement lancés par le shell sont suspendus est lui-même dit suspendu (stopped);
-
les autres jobs sont dits en cours d'exécution (running).
Un job sort de la liste des jobs surveillés lorsque sa terminaison a été annoncée, ou qu'il est détaché.
Un job est désigné par son numéro, précédé du symbole %
pour lever
toute ambiguïté avec un pid.
jsh
peut exécuter toutes les commandes externes, avec ou sans
arguments, en tenant compte de la variable d'environnement PATH
.
jsh
possède (au moins) les commandes internes listées ci-dessous.
Elles ont comme valeur de retour 0 en cas de succès (à l'exception
d'exit
, naturellement), et 1 en cas d'échec.
Affiche la référence physique absolue du répertoire de travail courant.
Change de répertoire de travail courant en le répertoire ref
(s'il
s'agit d'une référence valide), le précédent répertoire de travail si le
paramètre est -
, ou $HOME
en l'absence de paramètre.
Affiche la valeur de retour de la dernière commande exécutée.
Si un ou plusieurs jobs sont en cours d'exécution ou suspendus, affiche
un message d'avertissement; dans ce cas, la valeur de retour est 1, et
jsh
affiche une nouvelle invite de commande.
Sinon, termine le processus jsh
avec comme valeur de retour val
(si
un argument est fourni), ou par défaut la valeur de retour de la dernière
commande exécutée.
Lorsqu'il atteint la fin de son entrée standard (ie si l'utilisateur
saisit ctrl-D
en mode interactif), jsh
se comporte comme si la
commande interne exit
(sans argument) avait été saisie.
Sans option ni argument, affiche la liste des jobs en cours, en précisant pour chacun :
- le numéro de job, entre crochets;
- l'identifiant du groupe de processus;
- l'état du job (Running, Stopped, Detached, Killed ou Done);
- la ligne de commande qu'il exécute.
Par exemple :
[1] 590622 Done sleep 1000
[2] 590643 Running sleep 500
[3] 590664 Stopped vim toto.c
[4] 591141 Running ./a.out | wc -l > /tmp/tutu
Avec l'option -t
, liste l'arborescence des processus de chaque job,
en indiquant pour chacun son pid, son état et la commande qu'il exécute.
Par exemple :
[2] 590643 Done sleep 500
[3] 590664 Stopped vim toto.c
[4] 591141 Running ./a.out
| 591143 Running ./a.out
591142 Running wc -l > /tmp/tutu
Si un numéro de job est passé en argument à jobs
, la liste est
restreinte au job concerné.
Relance à l'arrière-plan l'exécution du job spécifié en argument.
Relance à l'avant-plan l'exécution du job spécifié en argument.
Envoie le signal sig
(ou SIGTERM
par défaut) à tous les processus du
job de numéro job
, ou au processus d'identifiant pid
.
Par exemple, kill -9 5312
envoie SIGKILL
(9) au processus de pid 5312,
tandis que kill %2
envoie SIGTERM
à tous les processus du job numéro 2.
jsh
fonctionne en mode interactif : il affiche une invite de commande
(prompt), lit la ligne de commande saisie par l'utilisateur, la découpe
en mots selon les (blocs d')espaces, interprète les éventuels caractères
spéciaux (&
, <
, >
, |
, cf ci-dessous), puis exécute la ou les
commandes correspondantes.
Avant chaque nouvel affichage de l'invite de commande, jsh
effectue un
tour des jobs en cours, et affiche le cas échéant, sur la sortie d'erreur,
la liste des jobs ayant changé de statut (job créé, stoppé, terminé ou détaché).
Exception : jsh
n'affiche rien pour un job qui était à l'avant-plan et dont le
nouveau statut est achevé, tué ou détaché.
jsh
ne fournit aucun mécanisme d'échappement; les arguments de la
ligne de commande ne peuvent donc pas contenir de caractères spéciaux
(espaces en particulier).
Il est fortement recommandé d'utiliser la bibliothèque
readline
pour
simplifier la lecture de la ligne de commande : cette bibliothèque offre
de nombreuses possibilités d'édition pendant la saisie de la ligne de
commande, de gestion de l'historique, de complétion automatique... Sans
entrer dans les détails, un usage basique de la fonction char *readline (const char *prompt)
fournit déjà un résultat très satisfaisant :
char * ligne = readline("mon joli prompt $ ");
/* alloue et remplit ligne avec une version "propre" de la ligne saisie,
* ie nettoyée des éventuelles erreurs et corrections (et du '\n' final) */
add_history(ligne);
/* ajoute la ligne à l'historique, permettant sa réutilisation avec les
* flèches du clavier */
L'affichage du prompt de jsh
est réalisé sur sa sortie erreur.
Avec la bibliothèque readline
, ce comportement s'obtient en indiquant
rl_outstream = stderr;
avant le premier appel à la fonction
readline()
.
Le prompt est limité à 30 caractères (apparents, ie. sans compter les indications de couleur), et est formé des éléments suivants :
- un premier code de bascule de couleur;
- entre crochets, le nombre de jobs actuellement surveillés;
- une deuxième bascule de couleur;
- la référence du répertoire courant, éventuellement
tronquée (par la gauche) pour respecter la limite de 30 caractères;
dans ce cas la référence commencera par trois points (
"..."
); - la bascule
"\033[00m"
(retour à la normale); - un dollar puis un espace (
"$ "
).
Exemples de codes de bascule de couleur : "\033[32m"
(vert),
"\033[33m"
(jaune), "\033[34m"
(bleu), "\033[36m"
(cyan),
"\033[91m"
(rouge). Pour que l'affichage s'adapte correctement à la
géométrie de la fenêtre, chaque bascule de couleur doit être entourée des
caractères-balises '\001'
et '\002'
(qui indiquent que la portion de
chaîne qu'ils délimitent est formée de caractères non imprimables et
doit donc être ignorée dans le calcul de la longueur du texte à
afficher).
Par exemple (sans coloration) :
[0]/home/titi$ cd pas/trop/long
[0]/home/titi/pas/trop/long$ cd mais/la/ca/depasse
[0]...ong/mais/la/ca/depasse$ pwd
/home/titi/pas/trop/long/mais/la/ca/depasse
[0]...ong/mais/la/ca/depasse$ sleep 1000 &
[1] 590522 Running sleep 1000
[1]...ong/mais/la/ca/depasse$
jsh
gère les redirections suivantes :
-
cmd < fic
: redirection de l'entrée standard de la commandecmd
sur le fichier (ordinaire, tube nommé...)fic
-
cmd > fic
: redirection de la sortie standard de la commandecmd
sur le fichier (ordinaire, tube nommé...)fic
sans écrasement (ie, échoue sific
existe déjà) -
cmd >| fic
: redirection de la sortie standard de la commandecmd
sur le fichier (ordinaire, tube nommé...)fic
avec écrasement éventuel -
cmd >> fic
: redirection de la sortie standard de la commandecmd
sur le fichier (ordinaire, tube nommé...)fic
en concaténation -
cmd 2> fic
: redirection de la sortie erreur standard de la commandecmd
sur le fichier (ordinaire, tube nommé...)fic
sans écrasement (ie, échoue sific
existe déjà) -
cmd 2>| fic
: redirection de la sortie erreur standard de la commandecmd
sur le fichier (ordinaire, tube nommé...)fic
avec écrasement éventuel -
cmd 2>> fic
: redirection de la sortie erreur standard de la commandecmd
sur le fichier (ordinaire, tube nommé...)fic
en concaténation -
cmd1 | cmd2
: redirection de la sortie standard decmd1
et de l'entrée standard decmd2
sur un même tube anonyme -
cmd <( cmd1 ) ... <( cmdn )
(substitution de processus) : redirection de la sortie standard de chaque commandecmd1
...cmdn
vers un flot dont la référence est utilisée comme paramètre de la commandecmd
.
Les espaces de part et d'autre des symboles de redirection sont requis.
Par ailleurs, dans un pipeline cmd1 | cmd2 | ... | cmdn
, les
redirections additionnelles sont autorisées :
- redirection de l'entrée standard de
cmd1
- redirection de la sortie standard de
cmdn
- redirection des sorties erreur des
n
commandes
En cas d'échec d'une redirection, la ligne de commande saisie n'est pas exécutée, et la valeur de retour est 1.
jsh
ignore les signaux SIGINT
, SIGTERM
, SIGTTIN
, SIGQUIT
,
SIGTTOU
et SIGTSTP
, contrairement aux processus exécutant des
commandes externes.
(optionnel) Si vous le désirez, vous pouvez améliorer le comportement
d'exit
de la manière suivante : si jsh
a des jobs en cours
d'exécution ou suspendus, après un premier appel à exit
sans effet, un
deuxième appel de confirmation, immédiatement après le premier,
provoque cette fois la terminaison de jsh
; jsh
doit cependant en
informer tous les processus des jobs en cours par l'envoi d'un signal
SIGHUP
, après avoir relancé les éventuels processus stoppés.
Le projet est à faire par équipes de 3 étudiants, exceptionnellement 2.
La composition de chaque équipe devra être envoyée par mail à Dominique
Poulalhon avec pour sujet [SY5] équipe projet
au plus tard le 20
novembre 2023, avec copie à chaque membre de l'équipe.
Chaque équipe doit créer un dépôt git
privé sur le gitlab de
l'UFR dès la
constitution de l'équipe et y donner accès en tant que Reporter
à
tous les enseignants du cours de Système : Raphaël Cosson, Isabelle
Fagnot, Guillaume Geoffroy, Anne Micheli et Dominique Poulalhon. Le dépôt
devra contenir un fichier AUTHORS.md
donnant la liste des membres de
l'équipe (un par ligne, en précisant pour chacun, dans l'ordre : nom,
prénom, numéro étudiant et pseudo(s) sur le gitlab).
En plus du code source de votre programme, vous devez fournir un
Makefile
tel que :
- l'exécution de
make
à la racine du dépôt crée (dans ce même répertoire) l'exécutablejsh
, make clean
efface tous les fichiers compilés,
ainsi qu'un fichier ARCHITECTURE.md
expliquant la stratégie adoptée
pour répondre au sujet (notamment l'architecture logicielle, les
structures de données et les algorithmes implémentés).
Le projet doit s'exécuter correctement sur lulu.
Il n'est pas exigé que le projet soit portable sur d'autres systèmes.
Certaines fonctionnalités demandées (jobs -t
et la substitution de
processus, au moins) sont en effet beaucoup plus simples à réaliser sous
linux grâce au pseudo-SGF /proc
.
Les seules interdictions strictes sont les suivantes : plagiat (d'un
autre projet ou d'une source extérieure à la licence), utilisation de
la fonction system
de la stdlib
et des fonctions de stdio.h
manipulant le type FILE
pour les redirections.
Pour rappel, l'emprunt de code sans citer sa source est un plagiat. L'emprunt de code en citant sa source est autorisé, mais bien sûr vous n'êtes notés que sur ce que vous avez effectivement produit. Donc si vous copiez l'intégralité de votre projet en le spécifiant clairement, vous aurez quand même 0 (mais vous éviterez une demande de convocation de la section disciplinaire).
Le projet sera évalué en 3 phases : deux jalons intermédiaires, sans soutenance, et un rendu final avec soutenance. Les deux jalons intermédiaires seront évalués par des tests automatiques.
La version à évaluer devra être spécifiée à l'aide du tag jalon-1
.
Pour créer le tag : se placer sur le commit à étiqueter et faire :
git tag jalon-1
git push origin --tags
Points testés :
- existence du dépôt git
- présence du fichier
AUTHORS.md
- compilation sans erreur avec
make
à la racine du dépôt - exécution de
jsh
à la racine du dépôt - exécution des commandes externes simples à l'avant-plan (avec arguments mais sans redirection ni possibilité de basculer à l'arrière-plan)
- bon fonctionnement de
cd
,pwd
,?
,exit
(en l'absence de jobs) - conformité du prompt (en l'absence de jobs)
La version à évaluer devra être spécifiée à l'aide du tag jalon-2
.
Points testés : ceux du premier jalon, plus :
- exécution des commandes externes simples à l'arrière-plan avec
&
(sans possibilité de basculer à l'avant-plan, et seulement pour les commandes qui ne cherchent pas à lire ou écrire sur le terminal) - bon fonctionnement de
jobs
(sans option),kill
etexit
- adaptation du prompt selon le nombre de jobs en cours
- redirections autres que
|
et<( )
La version à évaluer devra être spécifiée à l'aide du tag rendu-final
.
Le projet final devra être rendu à une date encore à définir mais au plus tard le 12 janvier, pour des soutenances entre le 15 et le 19 janvier.
Les projets sont des travaux de groupe, pas des travaux à répartir entre vous ("je fais le projet de Prog fonctionnelle, tu fais celui de Systèmes"). La participation effective d'un étudiant au projet de son groupe sera évaluée grâce, entre autres, aux statistiques du dépôt git et aux réponses aux questions pendant la soutenance, et les notes des étudiants d'un même groupe pourront être modulées en cas de participation déséquilibrée.
En particulier, un étudiant n'ayant aucun commit sur les aspects réellement "système" du code et incapable de répondre de manière satisfaisante sera considéré comme défaillant (DEF).