/*  Esercizio UNIX-1 del 14/6/2005 */

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


#define N      5


int segnale_ricevuto=0 ;
int pipepf[2];
sigset_t zeromask;

void sig_handler(int signo)
{

  segnale_ricevuto= signo ;

}


int body_proc()
{
  int pid,exitstatus;

/* Per ricevere comunque un segnale SIGALRM dopo 10 secondi  */
  alarm(10);


  /* Attende un segnale */

  /* Se il segnale (SIGUSR1 era stato bloccato) e' gia' pendente, mettendo una maschera vuota viene sbloccato e gestito dall'handler */
  
  sigsuspend(&zeromask);

  if(segnale_ricevuto == SIGUSR1)
    printf("Processo figlio %d : segnale SIGSUR1 ricevuto (%d)\n", getpid(), segnale_ricevuto);
  else
    if(segnale_ricevuto == SIGALRM)
      printf("Processo figlio %d : segnale SIGALRM ricevuto (%d)\n", getpid(), segnale_ricevuto) ;
    else
      printf("Il processo figlio %d ha ricevuto un segnale %d non previsto\n",getpid(),segnale_ricevuto);


  if(segnale_ricevuto == SIGUSR1)
    {
      /* Annulla il countdown del timer  */
      alarm(0);

      close(pipepf[0]);		/* Questo figlio non e' lettore sulla pipe */

      pid= getpid();

      if(write(pipepf[1],&pid,sizeof(pid))<0)
	{
	  perror("errore write sulla pipe: ");
	  exitstatus = errno ;
	}

      exitstatus = 0;
    }


  if(segnale_ricevuto == SIGALRM)
    {
      close(pipepf[0]);
      close(pipepf[1]);

      exitstatus = 0 ;
    }


  if(segnale_ricevuto != SIGUSR1  && segnale_ricevuto != SIGALRM)
    {
  /* Per tutti gli altri segnali */

      exitstatus = -1;
    }

  return exitstatus ;
}




int main(int argc, char *argv[])
{
  int n, i;
  struct sigaction act ;
  sigset_t sigmask, oldmask ;
  pid_t tabpid[10], pid, terminato;
  int status, attesa, destinatario  ;


/* 1) Controllo degli argomenti  */

  if(argc != 2)
    {
      fprintf(stderr,"Uso: %s N        (con N intero tra 1 e 10)\n",argv[0]);
      exit(1);
    }
  else
    {
      /* Converte l'argomento (stringa) in un intero */
      n = atoi(argv[1]);

      /* Sanity check del valore dell'argomento */
      if(n<1 || n>10)
	{
	  n= N ;
	}
    }

/* 2) Gestione segnali (ereditata anche dai figli) */

  act.sa_handler= sig_handler ;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0 ;


  /* Per il segnale SIGUSR1 che solo un processo figlio ricevera' */
  if(sigaction(SIGUSR1,&act,NULL)<0)
    {
      perror("errore sigaction: ");
      exit(2);
    }


  /* Per il segnale SIGALRM che gli altri n-1 processi figli riceveranno */
  if(sigaction(SIGALRM,&act,NULL)<0)
    {
      perror("errore sigaction: ");
      exit(2);
    }

/* In questo esercizio lo stesso gestore sara' invocato sia per SIGUSR1 che per SIGALRM */


  /* Blocco del segnale SIGUSR1 che i figli attenderanno con la sigsuspend */

  sigemptyset( &sigmask);
  sigaddset(&sigmask, SIGUSR1);
  sigprocmask(SIG_BLOCK, &sigmask, &oldmask);


  /* Per l'invocazione di sigsuspend da parte dei figli occorre una maschera che non blocchi SIGUSR1: una maschera vuota andra' benissimo */

  sigemptyset( &zeromask);


/* 3) Creazione pipe  */

  if(pipe(pipepf)<0)
    {
      perror("errore pipe: ");
      exit(3);
    }


/* 4) Creazione degli n figli  */

  for(i=0;i<n;i++)
    {

      if((tabpid[i]=fork())<0)
	{
	  perror("errore fork: ");
	  exit(4);
	}
      else
	{
	  if(tabpid[i]==0)
	    {
	      /* Codice eseguito dai figli */
	      
	      /* 5) Comportamento dei figli definito da un'apposita funzione */

	      status= body_proc();
	      exit(status);
	    }
	  else
	    printf("Padre : ho creato il processo figlio n. %d cha ha PID=%d\n",i,tabpid[i]);
	}
    }

  /* Codice eseguito dal padre */

  srand(getpid());

  attesa = (rand()%4) + 3 ;


  printf("Padre: attesa di %d secondi \n",attesa); 
  sleep(attesa);


  destinatario = rand() % n ;

  if(kill(tabpid[destinatario], SIGUSR1)<0)
    {
      perror("errore kill: ");
      exit(5);
    }

  printf("Padre: inviato segnale SIGUSR1 al processo figlio con PID %d (ordine di creazione = %d)\n\n", tabpid[destinatario], destinatario); 



  close(pipepf[1]); 		/* il padre non e' scrittore sulla pipe  */

  /* Legge dalla pipe cio' che il figlio ha inviato */
  if(read(pipepf[0],&pid, sizeof(pid))<0)
    {
      perror("errore read: ");
      exit(6);
    }

  printf("Padre: il processo figlio con PID %d ha ricevuto il segnale \n",pid); 

/* Attesa della terminazione degli n figli */
  for(i=0;i<n;i++)
    {
      terminato = wait(&status);
      
      printf("Padre: il processo figlio con PID %d e' terminato con %d\n",terminato, WEXITSTATUS(status));
    }

  printf("Padre: tutti i figli sono terminati, quindi termino.");
  exit(0);
}






