/***** ATTENZIONE ******
 *  Questa e' una versione semplificata della soluzione, che funziona solo se il client e' telent!
 *   
 *    Il problema sta nella read alla riga 187: con un generico client potrei inviare tutti i dati in unica stringa
 *    (es.sort\n1 2 3\n4 5 6\n7 8 9\nSTOP), il che renderebbe non funzionante il ciclo indicato sotto.
 *    Per fortuna telnet invia subito la riga quando premete invio editandola, quindi ci semplifica abbastanza le cose,
 *    ma non e' la soluzione piu' ortodossa.
 *    
 */


/* 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>


//una porta a caso
#define PORT 9001

//variabile che indica se dobbiamo o meno stampare l'indirizzo del client
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 buffer[256]="",nomeFile[256]="",comando[256]="";
  struct sigaction sig;
  struct sockaddr_in server,client;
  int sock,msgsock;
  int lenght;
  int pid,f,i=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);

	  //leggo la stringa che mi manda il client contenente il comando da eseguire
	  if(read(msgsock,comando,sizeof(comando))<0)
	    {
	      perror("reading comando");
	      exit(-1);
	    }

	  //continua a leggere le stringhe del client salvandole in un file, fino a quando non riceve "STOP"
	  do
	    {
	      //resetto i buffer
	      memset(buffer,0,sizeof(buffer));

	      //leggo la stringa che mi manda il client con il comando da eseguire
	      //FUNZIONE SOLO SE IL CLIENT E' TELNET!!
	      if(read(msgsock,buffer,sizeof(buffer))<0)
		{
		  perror("reading argomenti");
		  exit(-1);
		}

	      //se ho ricevuto la stringa STOP esco
	      if(!strcmp(buffer,"STOP\r\n"))
		break;

	      //scrivo l'argomento nel file
	      if(write(f,buffer,strlen(buffer))<0)
		{
		  perror("writing message");
		  exit(-1);
		}
	    }
	  while(++i<9); //controllo quante linee ho ricevuto, alla decima esco

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

	  //elimino i caratteri aggiungi da telnet (\r\n), che non piaciono a execv
	  comando[strlen(comando)-2]=0x0;

          //preparo la stringa con il comando piu' argomento
          sprintf(comando,"%s %s",comando,nomeFile);

	  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);

	  //in questo modo torna nel programma e posso cancellare il file tmp
	  if(system(comando)<0)
	    perror("sistem");

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

	  //faccio uscire il figlio
	  //questo fara' uscire anche telnet, come richiesto nel testo
	  exit(0);
	  ////////////////////////////
	}
      close(msgsock);
    }
  while(1);


  return 0;
}
