Examen de Systèmes I -- Licence -- Janvier 2005

Exercice

Soit l'extrait de code suivant:
d = open("f",ouverture,0);
c = 'a';
write(d,&c,1);
lseek(d,(off_t)100000,SEEK_END);
write(d,&c,1);
lseek(d,(off_t)50000,SEEK_SET);
read(d,&c,1);
printf("J'ai lu le caractère ASCII %d\n",c);
close(d);
On supposera qu'avant la première exécution il n'existe pas dans le répertoire courant de lien de nom f.
  1. quel mode d'ouverture doit être spécifié dans l'appel à open() pour que le programme puisse fonctionner comme attendu ?
  2. à quelles conditions l'appel à open() réalisera t-il la création du fichier ?
  3. quelle sera la longueur du fichier après exécution (les conditions d'une exécution normale étant réunies) ?
  4. que produira l'appel à printf() ?
  5. que contiendra le fichier ?
  6. que se passera t-il lors d'une seconde exécution ?

Exercice

Soit le fragment de programme suivant:
#define L 10
total = 0;
d = open("g",O_RDONLY);
while ((lu=read(d,buf,L))>0) total += lu;
printf("%d\n",total);
if (lu==-1) { close(d); exit(EXIT_FAILURE); }
sleep(10); // 10 secondes d'attente...
lu = read(d,buf,L);
printf("%d\n",lu);
close(d);
On suppose qu'avant l'exécution le fichier g contient 103 caractères:
  1. que produira le premier appel à printf() ?
  2. si rien ne se passe dans le système pendant les 10 secondes d'attente que produira le second appel à printf() ? et pourquoi ?
  3. si pendant les 10 secondes d'attente 7 caractères ont été ajoutés par ailleurs au fichier g, que produit le second appel à printf() ? et pourquoi ?
  4. si pendant les 10 secondes d'attente on supprime g (rm g) que se passe t-il pour chacun des deux cas précédents ?

Exercice

Soient:
$ ls -ail h
3427142 prw-r--r--  1 yunes  staff    0 16 Jan 13:05 h
$
// programme a.c
#define L 1000
int d; char buf[L]; int lu; int total;
d = open(argv[1],O_RDONLY);
printf("%s: %s ouvert\n",argv[0],argv[1]);
while ((lu=read(d,buf,L))>0) {
        buf[lu] = 0;
        printf("J'ai lu %s\n",buf);
}
return EXIT_SUCCESS;
// programme b.c
#define L 10
int d; char buf[L]; int lu; int total;
d = open(argv[1],O_WRONLY);
printf("%s: %s ouvert\n",argv[0],argv[1]);
while ((lu=read(STDIN_FILENO,buf,L))>0)
        write(d,buf,lu);
close(d);
return EXIT_SUCCESS;
  1. que se passe t-il si l'on exécute la commande a h depuis un terminal ?
  2. que se passe t-il si l'on exécute la commande b h depuis un autre terminal mais sur la même machine ?
  3. que se passe t-il lorsque on tape abcdef suivi d'un retour à la ligne sur le terminal depuis lequel on a lancé b ?
  4. que se passe t-il si l'on tape <CTRL>-D sur le terminal depuis lequel on a lancé b ?

Exercice

Soit le fragment de code:
void truc() {
  while ((lu=read(STDIN_FILENO,buf,L))>0) {
    write(STDOUT_FILENO,buf,lu);
    write(STDERR_FILENO,buf,lu);
  }
  return;
}
  1. que réalise la fonction truc() ?
  2. on souhaite réutiliser la fonction truc() afin de recopier le contenu du fichier f1 à la fois sur la sortie standard et dans un fichier de nom f2:
    1. quelle(s) fonctionnalité(s) POSIX peut-on utiliser ?
    2. écrire le code précédant un appel à truc() et permettant de réaliser ce que l'on désire.
    3. décrire sous la forme d'un graphique et pour chaque étape l'évolution des différentes tables du système.
  3. est-il possible de faire en sorte qu'il soit possible de rétablir l'état des descripteurs STDIN_FILENO, STDOUT_FILENO et STDERR_FILENO ? si oui écrivez le code correspondant, si non expliquez pourquoi.

Problème

On vous demande de réaliser le prototype d'une application client/serveur permettant de manipuler des fichiers dont on ignore la véritable localisation.

Le serveur ftpd reçoit sur un tube d'entrée de nom ftp des demandes de connexion. Chaque demande correspond à un message contenant deux références $r_c$ et $r_r$ de tubes qui seront utilisés pour réaliser le service de manipulation. Sur le tube $r_c$ le serveur doit s'attendre à recevoir trois types de messages:

Le serveur sera lancé avec un paramètre correspondant à la racine des fichiers visibles: si on lance le serveur en utilisant la commande ftpd /a/b, toute demande de la part d'un client concernant une référence /c sera donc traitée par le serveur comme concernant en réalité l'objet de référence /a/b/c (le client ignore donc la localisation exacte de la référence /c puisqu'elle est «traduite» dynamiquement par le serveur).

Le client est une simple application interactive qui créé deux tubes uniques ($r_c$ et $r_r$), se connecte sur le serveur en envoyant un message de type $r_c$,$r_r$, réalise une lecture sur le terminal, l'interprète et envoie le message correspondant sur le tube $r_c$ puis lit la réponse et recommence une lecture sur le terminal jusqu'à la saisie d'une commande QUIT laquelle doit terminer le client après la lecture de la réponse et suppression des objets devenus inutiles.

La création d'une référence unique pourra être réalisée par un appel à la fonction POSIX tmpnam(char *ref);.

  1. décrire, soigneusement, le format des messages échangés sur les différents tubes.
  2. écrire le code du serveur,
  3. écrire le code de la partie cliente.
Note: on pourra éviter les détails inutiles (comme l'analyse syntaxique des saisies) et la gestion exhaustive des erreurs.