/* Correction du TP de programmation système UNIX

   TP exécution de commandes en parallèle

   Question 4

   -
   Antoine Miné
   24/03/2007
 */

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

int n = 0; /* nombre de processus actifs */

/* tue tout le groupe si le process s'est terminé anormalement */
void mon_wait()
{
  pid_t pid;
  int status;
  /* boucle jusqu'à ce que wait réussisse */
  while ( (pid=wait(&status)) == -1 ) { 
    if (errno != EINTR) {
      /* vrai erreur */
      perror("wait"); 
      exit(1); 
    }
  }
  n--;
  if (WIFSIGNALED(status)) {
    printf("le fils %i a reçu un signal %i\n",pid,WTERMSIG(status));
    /* tue tout le groupe, y compris le processus courant */
    kill(0,SIGTERM);
  }
}

int main(int argc, char* argv[])
{
  int i; 
  int max; /* nom d'arguments maximum */

  if (argc < 4) { fprintf(stderr, "%s prog nb fichiers\n", argv[0]); return 1; }

  max = atoi( argv[2] );
  if ( max <=0 ) { fprintf(stderr, "il faut nb>0\n"); return 1; }

  /* créé un nouveau groupe de processus dont on est le leader;
     tous les fils crés par fork appardiendront au meme groupe     
   */
  setpgrp();

  /* lancement des processus */
  for ( i=3; i<argc; i++ ) {
    pid_t p;

    /* on s'assure qu'il y strictement moins de max fils actifs */
    while (n >= max) mon_wait();
    n++;

    p = fork();

    if ( p < 0 ) { perror("fork"); return 1; } /* erreur */

    if ( !p ) {
      /* fils */
      execlp( argv[1], argv[1], argv[i], NULL);
      
      /* on n'arrive ici que si execlp a échoué */
      fprintf( stderr, "echec de execlp(%s)\n", argv[1] );
      exit(1);
    }

  }

  /* attente de la terminaison des fils restants */
  while (n > 0) mon_wait();
  return 0;
}
