Examen de Systèmes II
Licence - Septembre 2000

Exercice

/* Programme mon_cd */
int main(int argc,char *argv[]) {
  char *wd;

  if (argc!=2) {
    fprintf(stderr,"mon_cd <rep>\n");
    exit(EXIT_FAILURE);
  }
  chdir(argv[1]);
  wd = getcwd(NULL,100);
  printf("Je suis dans le répertoire %s\n",wd);
  free(wd);
  exit(EXIT_SUCCESS);
}
Pourquoi lorsque j'exécute la suite de commandes Unix suivante :
$ pwd
/ens/yunes
$ mon_cd ..
Je suis dans le répertoire /ens
$ pwd
/ens/yunes
$
le dernier pwd me donne-t'il le résultat indiqué (/ens/yunes) ?

Exercice

Pourquoi la commande /bin/passwd possède-t'elle les droits r-sr-xr-x ?

Exercice

Soit le programme suivant :
int main(int argc,char *argv[]) {
  int i, n, m, status, s;
  char buf[10];

  sscanf(argv[1],"%d",&n);
  if (n==1) exit(EXIT_SUCCESS);
  sprintf(buf,"%d",n-1);
  m = n;
  for (i=0; i<n; i++) {
    switch (fork()) {
    case 0:
      execlp(argv[0],argv[0],buf,NULL);
      exit(EXIT_FAILURE);
    case -1:
      m--;
      break;
    default:
      break;
    }
  }
  status = EXIT_SUCCESS;
  for (i=0; i<m; i++) {
    wait(&s);
    if ((WIFEXITED(s) && WEXITSTATUS(s)!=EXIT_SUCCESS) ||
        WIFSIGNALED(s) || WIFSTOPPED(s))
      status = EXIT_FAILURE;
  }
  if (m!=n) status = EXIT_FAILURE;
  exit(status);
}
1.
Combien de processus sont créés si j'appelle ce programme par les commandes suivantes : quoi 1, quoi 2, quoi 3 ? Généralisez avec quoi n.
2.
Comment s'effectue la terminaison de cette application (ordre de disparition de processus, synchronisation, etc) ?
3.
Dans quels cas le code de retour est-il égal à EXIT_FAILURE ?

Problème

On se propose de réaliser un système client/serveur permettant d'exécuter des applications à un instant donné dans le futur (fonctionnalité << similaire >> à la commande Unix at).

1.
En utilisant la définition suivante :
struct commande {
  time_t quand;
  int    argc;
  char *argv[];
};
Écrire les fonctions suivantes :
2.
Écrire une fonction dont le prototype est le suivant :
void lance(char *argv[]);
dont le rôle est d'exécuter, sans en attendre la fin, la commande décrite par le tableau d'arguments argv.
3.
Sachant qu'une requète circulant dans le tube est structurée ainsi : un objet de type size_t que l'on nommera taille dont le rôle est de donner la taille exprimée en octets des données à suivre, suivi d'un objet de type time_t que l'on nommera quand, suivi d'un objet de type int nommé argc dont le rôle est de donner le nombre de chaînes de caractères (chacune terminée par un 0) qui suivent. Une requête a donc la forme décrite par la figure 1 (si l'on prend le cas où les différents types sont de taille 4).
  
Figure 1: Un exemple de requête
\begin{figure}
\centerline{\psfig{file=tube.eps,width=12cm}}\end{figure}

Écrire une fonction dont le prototype est le suivant :

void cree_requete(int *tr,int quand,char **buffer,int argc,char *argv[])
qui << fabrique >> dans le paramètre buffer un tableau conforme à la spécification précédente en utilisant les valeurs contenues par quand, argc et argv. Le paramètre tr contiendra en retour d'un appel de la fonction la taille totale exprimée en octets de buffer (dans l'exemple de la figure ce n'est pas 22, mais 22+4).
4.
À l'aide de la fonction cree_requete écrivez le programme client.c prenant les arguments suivants :
client <sec> <cmd> [ <arg>...]
sec représente un delai exprimé en secondes. cmd une commande Unix à exécuter et arg ses arguments.

Cette commande doit vérifier la validité des arguments puis envoyer une requète au serveur dans un tube dont le nom est connu.

5.
À l'aide des fonctions ajoute_commande, enleve_premier et lance, écrivez le programme serveur.c ne prenant aucun argument mais qui : Vous pouvez utiliser la fonction unsigned int alarm(unsigned int nsecs) qui permet au processus appelant de recevoir le signal SIGALRM lorsque les nsecs secondes se sont écoulées. Il n'est pas possible d'enregistrer deux alarmes, à chaque appel à alarm() la précédente est annulée. Si nsecs vaut 0, l'alarme précédente est annulée. La valeur de retour de alarm() indique le temps qui restait pour épuiser l'alarme précédente.

Attention : pensez à ce qui doît se passer si au temps t le serveur reçoit une requête pour exécuter une commande dans 10 secondes, et qu'au temps t+3 une nouvelle requête arrive pour dans 5 secondes...



Jean-Baptiste Yunes
2000-09-18