/*TESTO

Si realizzi un server concorrente su socket STREAM che abbia il seguente comportamento:

- il processo server Ps riceve dal client un primo messaggio ?SETUP? e crea un processo figlio Pf che si mette inizialmente in attesa di un segnale;

- dopo aver ricevuto dal client un secondo messaggio ?START? il processo server invia un segnale SIGUSR1 a Pf;

- dopo aver rispettivamente inviato e ricevuto il segnale SIGUSR1, Ps e Pf devono attendere un intervallo di durata casuale tra 1 e 10 secondi,
  prima di inviare un segnale SIGKILL all?altro processo e terminare;

- al processo client dovra' essere trasmesso un messaggio ASCII contenente, sia per Ps che per Pf , il relativo PID e un?indicazione del fatto che abbia inviato o meno
  il segnale SIGKILL.

Si richiede l?utilizzo della gestione affidabile dei segnali.
Si suggerisce l?utilizzo del comando telnet come client per la verifica del funzionamento del server

*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>

#define PORT1 8080

int sock,msgsock,porta;
struct sockaddr_in server;

int pidPs, pidPf;
char buffer[256];
struct sockaddr_in client;
int lenght;

struct sigaction sig;

void handler (int signo)
{
    if(signo == SIGUSR1)
        printf("Processo figlio: sono il processo con PID %d e ho ricevuto il segnale SIGUSR1\n", getpid());
    else
        printf("Segnale non gestito\n");
}


/*
    NOTA: Per come e' formulato il testo resta sempre la possibilita' di una corsa critica, nel caso in cui entrambi i SIGKILL vengano inviati assieme;
    la conseguenza e' che nessuna delle due write viene eseguita, e al client non arriva nessun messaggio dopo l'invio del comando START
*/
int main()
{
    /*DEFINIZIONE DELLA GESTIONE "AFFIDABILE" DEI SEGNALI*/
    sig.sa_handler = handler; //installo il gestore del segnale SIGUSR1
    sigemptyset(&sig.sa_mask);
    sig.sa_flags=0;
    sigaction(SIGUSR1,&sig,NULL);

    pidPs=getpid();

    /*DEFINIZIONE DELLA COMUNICAZIONE ATTRAVERSO SOCKET STREAM*/

    /*Creo la SOCKET*/
    sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0) {

        perror("creazione stream socket");
        exit(-1);
    }

    /*Preparo la struttura sockaddr_in per settare i parametri della socket con PORT1*/
    server.sin_family=AF_INET;         /*internet protocol*/
    server.sin_addr.s_addr=INADDR_ANY; /*ascolto da tutte le interfacce*/
    server.sin_port=htons(PORT1);       /*e dalla porta specificata*/

    if(bind(sock,(struct sockaddr *)&server,sizeof(server)) < 0) {

        perror("binding stream socket");
        exit(-1);
    }

    /*Stampo il pid del processo server e il nome della porta di comunicazione*/
    printf("Server %d attivo sulla porta %d\n",getpid(),ntohs(server.sin_port));

    /*Dichiaro il numero massimo di connessioni*/
    listen(sock, 5);

    lenght=sizeof(client);
    msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght); /*Attendo connesione*/

    /*Leggo il primo messaggio ricevuto dal clienr*/
    if(read(msgsock,buffer,256)<0) 
        perror("Errore: lettura del messaggio\n");

    /*Controllo il messaggio ricevuto dal client*/
    if(!strncmp(buffer, "SETUP", 5)) {

        pidPf=fork();
        /*Creo il processo figlio Pf*/
        if(pidPf<0) {

            perror("Errore: creazione processo figlio\n");
            exit(-1);
        }

        if(pidPf==0) { /*Processo figlio Pf*/

            pidPf=getpid();

            srand(getpid());/*Inizializzo il generatore di numeri casuali*/
            sigsuspend(&(sig.sa_mask)); /*Rimango in attesa di un segnale valido*/

            int sec = rand()%10+1;
            printf("Figlio (%d): dormo %d s\n", getpid(), sec);
            sleep(sec);
            kill(pidPs, SIGKILL); /*Spedisco il segnale SIGKILL al processo server Ps*/
            sprintf(buffer, "Padre (%d): segnale non inviato\nFiglio (%d): segnale inviato\n", pidPs, pidPf);

            if(write(msgsock, buffer, strlen(buffer)) < 0) /*Spedisco il messaggio al client*/
                perror("Errore: scrittura del messaggio\n");

            exit(0);
        }
    } else {

        printf("Errore: comando non corretto %s\n");
        exit(-1);
    }

    /*Leggo il secondo messaggio ricevuto dal clienr*/
    if(read(msgsock,buffer,256)<0)
        perror("Errore: lettura del messaggio\n");

    /*Controllo il messaggio ricevuto dal client*/
    if(!strncmp(buffer, "START", 5)) {

        srand(getpid());/*Inizializzo il generatore di numeri casuali*/
        kill(pidPf, SIGUSR1); /*Spedisco il segnale SIGUSR1 al processo figlio Pf*/

        int sec = rand()%10+1;
        printf("Padre (%d): dormo %d s\n", getpid(), sec);
        sleep(sec);
        kill(pidPf, SIGKILL); /*Spedisco il segnale SIGKILL al processo figlio Pf*/

        sprintf(buffer, "Padre (%d): segnale inviato\nFiglio (%d): segnale non inviato\n", pidPs, pidPf);

        if(write(msgsock, buffer, strlen(buffer)) < 0) /*Spedisco il messaggio al client*/
            perror("Errore: scrittura del messaggio\n");

        exit(0);
    } else {

        printf("Errore: comando non corretto\n");
        exit(-1);
    }

    close(sock);
    close(msgsock);

    return 0;
}
