TD 3

Vendredi 31 janvier 2003

Dans ce td nous allons écrire les primitives de bases nécessaires pour écrire un mini shell. Toutes les fonctions seront écrites dans un fichier shell.ml. À partir de le deuxième question, vous pourrez utiliser le toplevel de OCaml pour tester vos commandes comme dans un shell.

1  Execution inconditionelle

Écrire une fonction mon_exec: string -> string array -> unit telle que mon_exec "truc" [|"b";"c"|] exécute la commande truc, en la cherchant dans le path par défaut, avec les arguments b et c. Notez que le tableau passé en argument à mon_exec ne contient pas la commande en case d'indice 0, contrairement à la fonction Unix.execvp.
(corrigé)
Écrivez un programme mon_exec dans un fichier mon_exec.ml qui appelle la fonction précédente en lui passant sa ligne de commande.
(corrigé)

2  Exécution avec attente

Écrire une commande command_wait: string -> string array -> int telle que command_wait "truc" [|"b";"c"|] exécute la commande truc avec les arguments b et c, attend qu'elle termine, renvoie son code de retour et indique la raison de la terminaison.
(corrigé)
Remarquez que les erreurs au lancement sont traitées par mon_exec et les erreurs au retour par mon_waitpid. Une erreur de fork sera considérée comme fatale et n'est pas rattrapée.

3  Execution en arrière plan

Écrire une commande command_bg: string -> string array -> int telle que command_bg "truc" [|"b";"c"|] exécute la commande truc avec les arguments b et c et renvoie immédiatement son pid sans attendre.
(corrigé)

4  Combinaison de commandes

Écrire une commande command_seq: (string * string array) list -> int qui exécute l'une après l'autre les commandes avec leurs arguments, puis renvoie le code de retour de la dernière commande exécutée.
(corrigé)
De même, écrivez la fonction de même type command_and (respectivement command_or) qui exécute les arguments en séquence jusqu'à ce qu'une des commandes échoue (respectivement réussisse) et renvoie le code de retour de la dernière commande exécutée.
(corrigé)

5  Gestion de jobs

Écrivez la fonction jobs : unit -> unit qui affiche la liste des pid des processus fils qui s'exécutent en arrière plan. Vous pourrez par exemple modifier la fonction commande_bg et rattraper les signaux sigchld. Pour tester votre fonction, vous pouvez tuer depuis un autre shell les processus en cours avec sigint.
(corrigé)
Écrivez une fonction fg : unit -> int qui met en avant plan le dernier processus mis en arrière plan et renvoie le code de retour de ce processsus quand il termine.
(corrigé)
On désire simuler l'envoie l'interruption d'un processus en premier plan comme le fait Ctrl-Z dans un shell. Pour cela, on envoie un signal SIGTSTP, ce qui a pour effet de stopper le processus et qui le place en arrière plan. Réécrire la fonction command_wait en conséquence et une fonction fg utilisant le signal SIGCONT pour replacer la commande en premier plan.
(corrigé)

6  Analyse des commandes

Écrire une commande command: string -> int qui décompose son argument (à l'aide de la bibliothèque d'expressions régulière Str) en une suite de mots séparés par des espaces, exécute la commande correspondante et renvoie son code de retour.

Si vous êtes en avance complétez cette fonction en permettant de construire des séquences (;), des conjonctions (&&) et disjonctions (||) de commandes dans son argument (pour simplifier, on prendra tous les opérateurs associatifs à droite). Par exemple command "ls -l ; ls toto && cat toto || echo Not_found" doit exécuter ls -l puis ls toto. Si cette dernière est un succès alors elle exécute cat toto sinon elle exécute echo Not_found.
(corrigé)
Nous ne pouvons pas traiter l'exemple proposé qui est une commande complexe dont les sous commandes sont elles mêmes complexes. Pour cela, il faudrait généraliser les fonction command_seq, command_and et command_or pour qu'elles puissent elles-mêmes prendre des commandes complexes en argument.

7  Démonisation

Écrivez une commande daemonize: string -> string array -> bool qui exécute une commande avec ses arguments et lui donne le statut de démon, c'est-à-dire lui donne comme père le processus init. De plus un démon ne doit pas pouvoir être exécuté simultanément plus d'une fois. Pour cela, vous utiliserez un fichier verrou (O_EXCL) de nom /tmp/<cmd>_lock et contenant le pid du démon. En cas d'échec cette fonction renvoie false.
(corrigé)

8  Tuer un démon

Écrivez une fonction kill_daemon: string -> unit qui tue le démon dont le nom est passé en argument en utilisant le fichier verrou créé précédemment.
(corrigé)



This document was translated from LATEX by HEVEA and HACHA.