Dernière version: le 29/07/2004.
umsignal
- umsignal est un mini-projet de la spécialisation Systèmes,
Réseaux et Sécurité de l'EPITA.
- Le responsable du projet pour la promo 2005 est Laurent Bercot.
Organisation
Groupes
- Le projet doit être fait par groupes de 3 ou 4 étudiants.
- Les groupes sont formés à la discrétion des étudiants, ou
arbitrairement par le responsable de projet si des groupes ne sont pas
prêts à la date limite.
- Chaque groupe constitué doit envoyer avant la date limite un mail
au responsable de projet, précisant :
- La liste de tous les membres du groupe
- Le nom d'un membre du groupe qui sera le chef de projet,
c'est-à-dire l'interlocuteur unique du groupe avec le responsable de
projet.
Ce courrier électronique devra avoir pour sujet : Groupe de projet umsignal
- Tous les membres du groupe doivent bien sûr fournir un travail équivalent,
mais le chef de projet assure seul l'interface avec l'équipe pédagogique.
Calendrier
Le projet dure environ deux mois. Les étudiants sont, pendant
cette période, complètement autonomes ; leur seule contrainte est
la date de rendu du livrable.
- jeudi 29 juillet 2004: date de publication du sujet.
- mardi 7 septembre 2004: date limite de
formation des groupes
- samedi 2 octobre 2004: rendu du livrable par le chef de projet
Livrable
- Le sujet consiste en une certaine spécification
d'une bibliothèque utilisateur C/Unix, dont certaines parties sont
volontairement difficiles à implémenter en espace utilisateur, ainsi
que certaines indications.
- Le travail demandé consiste en :
- L'implémentation complète, en espace utilisateur, de la spécification
donnée dans le sujet, de façon portable sur un maximum de systèmes
- Une discussion sur les difficultés encontrées lors de cette implémentation,
les points de la spécification qui ne sont pas respectés, et des propositions
pour résoudre ces problèmes
Il s'agit donc d'un travail de codage, de portabilité, et aussi
d'analyse et de réflexion.
- Le livrable consiste en :
- Un rapport imprimé, en français ou en anglais au choix du groupe,
qui entre autres :
- décrit les parties de la spécification implémentées de façon
exacte et parfaite
- décrit les parties de la spécification difficiles à
implémenter correctement, explique quels sont les problèmes, quels sont
les choix qui ont été faits, dans quelle mesure l'implémentation se
conforme à la spécification et quelles libertés ont été prises, quelles
primitives ont été ajoutées à la spécification initiale et pourquoi, etc.
- discute des moyens d'obtenir une implémentation exacte des
spécifications données
Ce rapport ne doit aucunement être une série de listings. Il
ne doit pas paraphraser votre code. Il doit s'agir de français ou
d'anglais correct, lisible, montrant de réelles capacités de réflexion
et d'expression écrite.
- Un support informatique - disquette ou CD-ROM - contenant :
- Une version électronique du rapport imprimé, sous un format
libre (de préférence texte, HTML, PDF ou Postscript)
- Le code source de l'implémentation de umsignal
Détails
Utilisation de votre implémentation
- Votre implémentation doit se présenter sous la forme d'un
.tar.gz, s'extrayant dans un répertoire ayant pour nom
umsignal.
- L'exécution de la commande umsignal/compile, sur
tout système supporté, doit provoquer la configuration et la
compilation sans erreur de votre code sur ce système.
- Le résultat doit être la présence d'un fichier
umsignal/lib/libumsignal.a, valide sur le système sur
lequel la compilation a été effectuée. L'en-tête
portable fourni avec le sujet doit, au moins après compilation, figurer
dans umsignal/include/umsignal.h. Ce fichier peut être
complété par l'ajout de vos propres déclarations de fonctions.
- Un programme C utilisant les interfaces définies dans
umsignal/include/umsignal.h doit pouvoir être lié avec la
bibliothèque umsignal/lib/libumsignal.a, sans erreur, et
s'exécuter correctement avec les résultats attendus.
- Tout ajout de fonctionnalité par rapport à ce qui est mentionné
ci-dessus est autorisé, pourvu que les fonctionnalités supplémentaires
soient documentées dans votre rapport. Vous avez par exemple le droit
de fournir une bibliothèque dynamique sur les systèmes qui les
supportent. Aucun bonus ne sera toutefois accordé pour cela.
- Une légère modification des fonctionnalités ci-dessus pourra être
tolérée, à condition qu'elle soit justifiée de façon convaincante, et
que le mode opératoire de votre implémentation soit soigneusement
documenté dans votre rapport. Par exemple, si vous avez besoin de
symboles définis par une bibliothèque extérieure, vous pouvez demander
à l'utilisateur de lier son exécutable avec ladite bibliothèque en plus
de libumsignal.a. Mais vous devez, bien entendu, justifier
l'utilisation de la bibliothèque extérieure.
Portabilité
- Votre implémentation doit fonctionner sur les
systèmes suivants, pour avoir une chance d'être examinée :
- Linux 2.x.x sur i386 avec glibc 2.3.2
- NetBSD 1.6.1 sur i386
Ces systèmes sont disponibles sur le PIE, et sur bertha
(srs.epita.fr) où vous avez un compte.
- Votre implémentation sera considérée comme raisonnablement
portable, et sera regardée avec un oeil bienveillant, si elle
fonctionne en outre sur la plupart des systèmes suivants :
- Linux 2.x.x sur i386 avec dietlibc 0.26
- OpenBSD 3.x sur i386
- FreeBSD 5.x sur i386
- Solaris 8 ou 9 sur Sun Ultrasparc
- Solaris 8 ou 9 sur i386
- Digital Unix 4.0 sur Alpha
Certains de ces systèmes sont disponibles au lab SRS.
Vous pouvez joindre le responsable du lab SRS, ou un étudiant
chargé du lab, en cas de besoin.
- Votre implémentation sera considérée comme très portable, et
obtiendra des bonus, si elle fonctionne sur tous les systèmes
précédents et certains des systèmes suivants :
- NetBSD 1.6.x sur Alpha
- FreeBSD 4.10 sur Alpha
- Linux 2.x.x sur Sun Ultrasparc
- MacOS X
- Un Unix sur m68k
- AIX
- Plan 9
- MS-Windows avec le pack "Services For Unix"
- MS-Windows en environnement Cygwin
- Votre implémentation sera considérée comme extrêmement portable, et
obtiendra de gros bonus, si elle fonctionne sur tous les systèmes précédents
et certains des systèmes suivants :
- IRIX
- HP-UX
- Un Unix sur ARM ou autre processeur sans MMU
- N'importe quelle combinaison tordue de système de type Unix et
de matériel exotique
- GNU Hurd
- MS-Windows sans pack de compatibilité Unix
- BeOS
- MacOS 9
- etc. etc.
- Votre rapport devra mentionner sur quels systèmes votre implémentation
a été testée, et sur quels systèmes elle devrait marcher même si vous
n'avez pas pu la tester.
- Votre rapport devra mentionner quels sont exactement les
prérequis de votre système de compilation. Par exemple, vous pouvez
supposer l'existence d'un shell compatible Single Unix v3 dans
/bin/sh, ou d'un utilitaire make traditionnel
dans /bin/make, etc. etc. Il va de soi que l'importance
de vos suppositions sera prise en compte : vous serez pénalisé si
vous dépendez de l'existence d'un environnement complet de type Perl
ou Java, car vous n'aurez fait que déplacer le problème de portabilité.
- Vous avez le droit d'utiliser des logiciels tiers tels que
GNU Autoconf ou
skalibs. Vous
devez mentionner dans votre rapport quelles sont vos dépendances.
Conseils
- La maîtrise de la langue est fondamentale. Quelle que soit la langue
choisie, faites attention à votre orthographe, votre grammaire et votre
style - cela peut vous rapporter des points, ou vous en enlever beaucoup
(beaucoup) si votre rédaction est pénible à lire.
- Votre rapport est vital. C'est lui qui sera
utilisé pour faire la différence entre les "pisseurs de code" et les
gens qui réfléchissent à ce qu'ils font.
- Examinez bien toutes les implications de vos choix. Certaines
parties de la spécification de umsignal sont volontairement
impossibles à implémenter proprement ; c'est à vous de décider sur
quoi vous allez faire l'impasse (atomicité, portabilité, concision du
code, etc. etc.), et de justifier vos décisions.
- Pensez à finaliser votre travail, en particulier
prévoyez une large phase de tests. La notation ne sera pas
indulgente sur les parties de la spécification qu'il est
possible d'implémenter sans erreur : assurez-vous que tout marche.
- Plutôt que les mans du système sur lequel vous travaillez,
la norme Single Unix v3 est votre amie en matière de portabilité. Elle est
disponible
en ligne, et un exemplaire papier est disponible pour consultation au
lab SRS. Un autre exemplaire papier est disponible à l'emprunt à la
bibliothèque de Villejuif.
- Le responsable de projet est là pour vous aider si vous avez des
difficultés. N'hésitez pas à le contacter.
Technique
umsignal
umsignal, pour User-Mode Signal ou encore
Userland MSignal, est une bibliothèque qui implémente la
transmission de messages d'un processus à un autre par une interface
proche de celle utilisée pour l'émission et la réception de signaux.
- Il existe 255 umsignaux, numérotés de 1 à 255,
sans signification particulière. Le umsignal numéro 0, comme le
signal numéro 0, n'existe pas.
- Par défaut, tout umsignal est ignoré.
- Un processus récepteur peut choisir de recevoir un umsignal
particulier, en lui affectant un handler. Ce handler sera alors
appelé dès que le processus recevra le umsignal qu'il écoute,
interrompant le cours normal de l'exécution et la reprenant une fois
le handler terminé. Le handler s'exécutant en contexte d'interruption,
il est normal de limiter les fonctions qui peuvent y être utilisées.
Votre rapport devra préciser ce qu'il est autorisé de mettre dans un
handler et ce qui ne l'est pas.
- À la différence des signaux traditionnels, les umsignaux peuvent
s'empiler et ne sont jamais perdus. Si un processus reçoit quatre fois
consécutivement le umsignal numéro 3, le handler de numéro 3 sera appelé
quatre fois. De même, la chronologie est respectée : si un
umsignal 4 arrive avant un umsignal 5, le handler 4 sera appelé avant
le handler 5. Les umsignaux bloqués font exception à cette
chronologie : ils sont délivrés au moment où ils sont débloqués.
- Un umsignal s'accompagne d'un message, qui sera passé en argument
au handler. Ce message est une chaîne de caractères de taille variable
limitée à une très grande valeur.
- Un processus peut recevoir des umsignaux provenant de processus du
même utilisateur, mais aussi de processus ayant les mêmes privilèges de
groupe, ou encore de processus sans privilèges particuliers. Il peut
contrôler qui (utilisateur, groupe, autres) a le droit de lui envoyer
quel umsignal.
- L'implémentation doit bien entendu être sécurisée : la partie
"réception de signal" ne doit pas faire l'hypothèse que tout ce qu'elle
reçoit provient d'une utilisation correcte et honnête de la partie
"émission de signal". Un programme mal intentionné ne doit pas pouvoir
causer de tort à un programme utilisant la libumsignal.
- Vous pouvez faire les hypothèses qu'un programme utilisant la
libumsignal s'interdit de gérer à la main les signaux
traditionnels SIGUSR1 et SIGUSR2, et ne fait pas de multithreading.
Vous ne pouvez pas faire l'hypothèse que le programme s'interdit de
gérer à la main un autre signal.
Interface
L'implémentation doit scrupuleusement respecter cette interface, dont
le header est fourni ici :
umsignal.h
Vous pouvez compléter la spécification, par exemple en ajoutant des
valeurs de errno pour des cas d'erreur qui peuvent se produire
et qui ne sont pas mentionnés ici ; mais vous ne pouvez en aucun
cas modifier ce qui est décrit.
Vous pouvez ajouter vos propres définitions de fonctions, constantes, etc.
au fichier umsignal.h ; votre rapport devra justifier
chacun de ces ajouts, et documenter comment un programme utilisateur doit
se servir des interfaces supplémentaires que vous fournissez.
Constantes
-
#define UMSIGNAL_MSGLEN_MAX ((size_t)32768)
La taille maximale de message autorisée. Vous pouvez modifier sa valeur,
mais vous ne pouvez pas la rendre inférieure à PIPE_BUF+1.
-
typedef void umsignal_handler_t (unsigned char, char const *, size_t, pid_t, uid_t, gid_t)
typedef umsignal_handler_t *umsignal_handler_t_ref ;
Le type d'un handler de umsignal. Si f est une fonction de
ce type, installée dans le processus dest pour le umsignal
n, et que le processus pid tournant sous l'identité
uid:gid envoie à dest un umsignal n
avec le message s de longueur len, alors dest
appellera aussitôt f(n, s, len, pid, uid, gid).
-
umsignal_handler_t umsignal_ignore
umsignal_handler_t umsignal_error
Deux constantes semblables à SIG_IGN et SIG_ERR, pour les umsignaux.
Émission
-
int umsignal_send1 (pid_t dest, unsigned char n, char const *s, size_t len)
L'équivalent de kill() avec un pid positif.
Le umsignal n, avec le message s de taille len,
est envoyé au processus numéro dest. Si n vaut 0, la
vérification d'erreur est faite, mais aucun signal n'est envoyé.
La fonction renvoie 0 en cas de succès, même si dest ignore le
signal, et -1 en cas d'erreur, avec le errno suivant :
- EMSGSIZE - longueur de message trop grande
- ESRCH - pas de processus destination trouvé
- EPERM - droits insuffisants
-
int umsignal_sendpg (pid_t dest, unsigned char n, char const *s, size_t len)
L'équivalent de killpg().
Le umsignal n, avec le message s de taille len,
est envoyé au process group numéro dest, i.e. à tous les processus
du process group dest à qui le processus courant a le droit
d'envoyer le umsignal. Si n vaut 1 ou moins, le comportement est
indéfini.
La fonction renvoie 0 en cas de succès et -1 en cas d'erreur, avec le
errno suivant :
- EMSGSIZE - longueur de message trop grande
- ESRCH - pas de processus destination trouvé
- EPERM - droits insuffisants
-
int umsignal_send (pid_t dest, unsigned char n, char const *s, size_t len)
L'équivalent de kill().
La sémantique de dest est la même que pour l'envoi d'un signal
traditionnel avec kill().
- EMSGSIZE - longueur de message trop grande
- ESRCH - pas de processus destination trouvé
- EPERM - droits insuffisants
Réception
-
umsignal_handler_t_ref umsignal_gethandler (unsigned char n)
Renvoie un pointeur sur le handler du umsignal n. Par défaut,
il s'agit de &umsignal_ignore. En cas d'erreur,
&umsignal_error est renvoyé et errno contient une valeur
appropriée (par exemple, EINVAL si n vaut 0).
-
int umsignal_getmode (unsigned char n)
Renvoie les droits acceptés par le processus courant pour le umsignal
n. 4 signifie que seul l'utilisateur a le droit d'envoyer
des umsignaux au processus, 2 signifie que seul le groupe a le droit,
1 signifie que seuls les autres ont le droit. 0, 3, 5, 6, 7 sont des
combinaisons de ces valeurs. -1 signifie une erreur, avec un errno
approprié au type d'erreur.
-
int umsignal_sethandlermode (unsigned char n, umsignal_handler_t_ref f, unsigned int mode)
Installe le handler *f pour le umsignal n avec le
mode mode tel que défini par umsignal_getmode().
La fonction renvoie 0 en cas de succès, et -1 en cas d'erreur avec un
errno approprié au type d'erreur (en particulier EINVAL si n ou
mode ont des valeurs incorrectes).
Si f vaut &umsignal_ignore, alors la bibliothèque
considère que le processus n'écoute pas du tout le umsignal n
et arrête de le gérer. En particulier, les notions de mode et de
blocage/non-blocage deviennent non-pertinentes pour ce umsignal, et les
"signaux en attente" n'existent pas. Le comportement des fonctions
suivantes est donc non spécifié, et laissé au choix de l'implémentation,
quand le umsignal est ignoré.
-
int umsignal_sethandler (unsigned char n, umsignal_handler_t_ref f)
Installe le handler *f pour le umsignal n sans
changer le mode courant. Par défaut, le mode est 4.
La fonction renvoie 0 en cas de succès, et -1 en cas d'erreur avec un
errno approprié au type d'erreur.
Même commentaires que pour umsignal_sethandlermode() si
f vaut &umsignal_ignore.
-
int umsignal_setmode (unsigned char n, unsigned int mode)
Change les droits acceptés par le processus courant pour le umsignal
n, sans modifier le handler courant. La fonction renvoie 0 en
cas de succès, et -1 en cas d'erreur avec un errno approprié au type
d'erreur.
-
int umsignal_block (unsigned char n)
Bloque le traitement du umsignal n. Les umsignaux bloqués
s'empilent et seront traités quand ils seront débloqués. La fonction
renvoie 0 en cas de succès, et -1 en cas d'erreur avec un errno
approprié.
-
int umsignal_isblocked (unsigned char n)
Renvoie l'état du umsignal n : 1 si bloqué, 0
si non bloqué, -1 en cas d'erreur avec un errno approprié.
-
int umsignal_pending (unsigned char n)
Compte le nombre de umsignaux n en attente. Cette fonction
n'est normalement utilisée que sur des umsignaux bloqués. La fonction
renvoie le nombre de umsignaux n en attente, ou -1 en cas
d'erreur avec un errno approprié.
-
int umsignal_purge (unsigned char n)
Efface tous les umsignaux n en attente.
La fonction renvoie le nombre de umsignaux n effacés, ou -1 en cas
d'erreur avec un errno approprié.
-
int umsignal_unblock (unsigned char n)
Débloque le traitement du umsignal n. Tous les umsignaux n
en attente sont traités à ce moment-là, avant le retour de la fonction.
La fonction renvoie 0 en cas de succès, et -1 en cas d'erreur avec un errno
approprié.