/* TESTO

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

- il processo server ricevce dal client un singolo messaggio con un numero variabile n (2<= n <= 10) di linee di testo,
  in cui la prima contiene il nume di un comando UNIX, e terminate da una linea contenente  unicamente la parola chiave
  "STOP"; Esempio: (n=5)

  sort
  1 2 3
  4 5 6
  7 8 9
  STOP

- se n>2 il server provvede a memorizzare in un file temporaneo il contenuto delle righe intermedie (linee 2... n-1) del
  messaggio ricevuto per renderlo disponibilie come argomento al momento dell'esecuzione del comando indicato dal client;

- l'output (stdout e stderr) generato dal comendo deve essere inviato al client

- al termine dell'esecuzione del comando il file temporaneo deve essere rimosso

- dopo aver ricevuto un segnale SIGUSR1 il server deve iniziare  a visualizzare sul suo stdout l'indirizzo di ogni cliente
  che si connette, cessando tale comportamento alla notifica di un segnale SIGUSR2

Si siggerisce l'utilizzo di telnet come client.
*/




#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <string.h>


//una porta a caso
#define PORT 9001

//descrittore del file server-log.txt
int scriviIndirizzo=0;



//Funzione di gestione segnali SIGUSR1 e SIGUSR2
void handler(int signo)
{
  if(signo == SIGUSR1)
    scriviIndirizzo=1;
  else
    scriviIndirizzo=0;
}




int main()
{
  char * env[]={NULL,NULL,(char *)0};
  char buffer[256]="",nomeFile[256]="",comando[256]="";
  struct sigaction sig;
  struct sockaddr_in server,client;
  int sock,msgsock;
  int lenght;
  int pid,f,n=0,j=0;

  printf("Sono il server con pid %d\n",getpid());

  //SOCKET //////////////////////
  //
  //preparo la struttura sockaddr_in per settare i parametri della socket
  sock=socket(AF_INET,SOCK_STREAM,0); //creo la socket
  if(sock<0)
    {
      perror("creazione stream socket");
      exit(-1);
    }

  //preparo la struttura sockaddr_in per settare i parametri della socket
  server.sin_family=AF_INET;         //internet protocol
  server.sin_addr.s_addr=INADDR_ANY; //ascolto da tutte le interfacce
  server.sin_port=htons(PORT);       //e dalla porta specificata
  if(bind(sock,(struct sockaddr *)&server,sizeof(server))<0)
    {
      perror("binding stream socket");
      exit(-1);
    }

  //leggo la porta che mi e' stata effettivamente assegnata
  lenght=sizeof(server);
  if(getsockname(sock,(struct sockaddr *)&server,(socklen_t *)&lenght)<0)//leggo il nome della socket
    {
      perror("getting socket name");
      exit(-1);
    }

  printf("Socket port # %d\n",ntohs(server.sin_port));

  listen(sock,5); //massimo di 5 connesioni in attesa
  ////////////////////////////////


  //SEGNALI //////////////////////
  //
  sig.sa_handler = handler; //gestore del segnale SIGUSR1
  sigemptyset(&sig.sa_mask);
  sigaddset(&sig.sa_mask,SIGUSR2); //blocco SIGUSR2 durante l'esecuzione dell'handler
  sig.sa_flags=SA_RESTART;

  sigaction(SIGUSR1,&sig,NULL);

  sig.sa_handler = handler; //gestore del segnale SIGUSR2
  sigemptyset(&sig.sa_mask);
  sigaddset(&sig.sa_mask,SIGUSR1); //blocco SIGUSR1 durante l'esecuzione dell'handler

  sigaction(SIGUSR2,&sig,NULL);
  ////////////////////////////////

  //ciclo infinito del server in attesa di connessioni
  do
    {

      msgsock=accept(sock,(struct sockaddr *)&client,(socklen_t *)&lenght);//attendo connesioni...
      if(msgsock == -1)
	{
	  perror("accept");
	  exit(-1);
	}

      //se e' stato ricevuto il segnale SIGUSR1 allora stampo le info del client
      if(scriviIndirizzo)
	printf("Connection from %s, port %d\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));

      if((pid=fork())<0)  //creo il figlio che gestira' la connessione
	{
	  perror("fork figlio gestore connessione");
	  exit(-1);
	}
      if(pid==0) //sono il figlio?
	{
	  /////////////////////////////
	  //CODICE FIGLIO GESTORE

	  printf("Sono il gestore della connessione, ho pid %d\n",getpid());

	  //preparo il nome del file temporaneo.
	  //e' meglio che il nome del file sia sempre diverso, con un nome specifico per ogni gestore. 
	  //In questo modo evitiamo che diversi figli/gestori scrivano e leggano dallo stesso file. 
	  //Usiamo il pid
	  memset(nomeFile,0,sizeof(nomeFile));
	  sprintf(nomeFile,"%d.txt",getpid());

	  f=open(nomeFile,O_CREAT | O_RDWR | O_TRUNC,0666);

	  //continua a leggere le stringhe del client, fino a quando non riceve "STOP" o non ne ha lette 10
	  do
	    {
	      //resetto i buffer
	      memset(buffer,0,sizeof(buffer));
	      j=0;

	      //faccio un ciclo per leggere la riga che mi manda il client, carattere per carattere
	      do
		{
		  //leggo la stringa che mi manda il client caratte per carattere
		  if(read(msgsock,&buffer[j],1)<0)
		    {
		      perror("reading");
		      exit(-1);
		    }
		}
	      while(buffer[j++]!='\n');

	      //se ho ricevuto la stringa STOP e almeno un'altra stringa (n>=1), esco
	      if(!strcmp(buffer,"STOP\r\n") && n)
		break;

	      //se e' la prima riga che leggo allora la salvo in comando
	      if(!n)
		{
		  //elimino l'eventuale \r e \n, che non piaciono molto a execvp
		  if(strchr(buffer,'\r')) strchr(buffer,'\r')[0]=0x0;
		  if(strchr(buffer,'\n')) strchr(buffer,'\n')[0]=0x0;
		  strcpy(comando,buffer);
		}
	      else //altrimenti la salvo nel file
		if(write(f,buffer,strlen(buffer))<0)
		  {
		    perror("writing argomenti");
		    exit(-1);
		  }

	    }
	  while(++n<9); //controllo quante linee ho ricevuto, alla decima esco in ogni caso

	  //chiudo il file in modo che salvi le modifiche
	  close(f);

	  if((pid=fork())<0)  //creo il NIPOTE eseguira' il comando
	    {
	      perror("fork figlio gestore connessione");
	      exit(-1);
	    }
	  if(pid==0) //sono il figlio del gestore (nipote del padre)?
	    {
	      /////////////////////////////
	      //CODICE NIPOTE

	      printf("Eseguo il comando %s\n",comando);
	      fflush(NULL);

	      //chiudo stdout e stderr
	      close(1);
	      close(2);

	      //il nuovo stdout e' la socket
	      if(dup(msgsock)<0)
		perror("dup1");

	      //il nuovo stderr e' la socket
	      if(dup(msgsock)<0)
		perror("dup1");

	      close(msgsock);

	      env[0]=comando;
	      env[1]=nomeFile;
	      if(execvp(comando,env)<0)
		perror("execvp");
	      /////////////////
	    }

	  //il gestore attende che il nipote abbia finito di eseguire il comando
	  wait(NULL);

	  //elimino il file temporaneo
	  if(unlink(nomeFile)<0)
            perror("unlink");

	  close(msgsock);

	  //faccio uscire il figlio
	  exit(0);
	  ////////////////////////////
	}
      close(msgsock);
    }
  while(1);


  return 0;
}
