/* EDW ORIZETE O SERVER O OPOIOS EKTELEI TOUS ALGORITHMOUS SJF KAI RR.
GIA NA TREXEI O SJF STHN GRAMMH EVTOLWN DINW TO EXIS: ./server 2
GIA NA TREXEI O SJF STHN GRAMMH EVTOLWN DINW TO EXIS: ./server 1 100
opou 100 enai o xronos tou time slice se microseconds
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <errno.h>



typedef struct process{
int pidouras; //to pid pou dinetai apo ton server
pid_t pidleitourgikou; //to pid tis diergasias pou dinetai apo to leitourgiko sustima
char* onoma; //to onoma tou ektelesimou 
char* xaraktiristika; /*ta xaraktiristika tou ektelesimou dhl edw apothikeuete to tester-v3.x86 5. opou 5 einai o anamenomenos xronos ektelesis*/
float anamenomenos_xronos; //O xronos poy xreiazete gia na ektelestei i diergasia 
struct timeval xronos_upobolis;		//xronos pou staluike h aitisi
struct timeval enarxi;			//xronos ekinisis tis diergasias 
struct timeval lixi;			//xronos oloklirwsis tis diergasias
} process;

process ** oura; 	// edw orizete h oura me tis pros ektelesi diergasies 
static int megethos;		//edw orizete to megetos tis ouras

struct sockaddr_un serv_addr;

void error(char *);
void munuma_eisodou();
void prosthiki_diergasias(int,char *,char *,int);
void diagrafi_diergasias(int);
void emfanisi_listas();
void quicksort(int,int);
void sig_handler(int,siginfo_t*,void*);
void myalarm(int);

int sd,new_sd,servlen;

static int pid_ouras;
int oloklirwthike;
int oloklirwsi_xronou;

//edw orizontai oi structs tou myalarm
struct itimerval xronos_lixis, xronos_enarxis;
struct sigaction sa, my_alarm;


int main(int argc, char *argv[])
{
	int status;
	struct timeval xronos_apokrisis,xronos_ektelesis;
	int a=atoi(argv[1]);
	
	if(a==2)
	{	
		if(argc!=2)
		{
			printf("edwses lathos orosmata\n");
			printf("dwse ta orismata sti morfi: ./server 'politiki dromologisis' 'time slice(mono gia ton RR)'\n");
			printf("gia tin politiki dromologisis SJF dwse to 2 enw gia tin politiki dromologisis RR dwse to 1");
			exit(1);
		}
	}
	else if(a==1)
	{
		if(argc!=3)
		{
			printf("edwses lathos orosmata\n");
			printf("dwse ta orismata sti morfi: ./server 'politiki dromologisis' 'time slice(mono gia ton RR) se microseconds' \n\n");
			printf("gia tin politiki dromologisis SJF dwse to 2 enw gia tin politiki dromologisis RR dwse to 1\n");
			exit(1);
		}
	}
	
	char buf[80];
	
	remove("mysock");
	
	// dimiourgia tou socket gia tin epikoinonia me ton client
	if ((sd = socket(AF_UNIX,SOCK_STREAM,0)) < 0)
       	{	error("sfalma sti dimioyrgia tou socket");	}
	
	bzero((char *) &serv_addr, sizeof(serv_addr));
	
	//edw orizete h address Family tou server
	serv_addr.sun_family = AF_UNIX;
	strcpy(serv_addr.sun_path, "mysock");
	servlen=strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
	
	if(bind(sd,(struct sockaddr *)&serv_addr,servlen)<0)
	{	error("error binding socket");	} 
	
	//orismos tou megethous tis ouras(listas)
	if(listen(sd,10)<0)
	{	error("den mporese na dimiourghthei h oura");	}
	
	
	
	if(a==2)	/*andinete ws orisma to 2 tote xrisimopoihse tin politiki dromologisis SJF*/
	{
		printf("Xrisimopoieite o SJF algoritmos ws politiki dromologisis\n");
		pid_t pidfork; //edv apothikeuete to pid tou paidiou
		char *stoixeia[2];
		
		sa.sa_sigaction=sig_handler; /*H sunartisi pou kaleitai otan teleiwnei mia child process (dil edw orizete i sunartisi xeirismou simatwn kathos kai to filtro shmatwn pou ua xrisimopoihthei otan ektelestei i sunartish xeirismou)*/
		
		sigemptyset(&sa.sa_mask);
		
		sa.sa_flags = SA_RESTART | SA_NOCLDSTOP | SA_SIGINFO; // SA_NOCLDSTOP na mhn kaleitai h sig_handler otan ena
							// child ginetai stopped
							// SA_SIGINFO gia na apothkeuontai kapoies xrhsimes plhrofories
							// opws p.x. poios esteile to signal kai ti signal einai
		if(sigaction(SIGCHLD, &sa, NULL) == -1)
		{
			perror("Sigaction");
			exit(1);
		}
		
		int i;
		//diabazei tis 6 diergasies poy tou stelnei o client
		for(i=0; i<6; i++)
		{
			new_sd = accept(sd,(struct sockaddr *)&serv_addr,&servlen);
			
			if(new_sd <0)
			{
				error("server accept failure");
				exit(1);
			}
			
			munuma_eisodou();	//Diabazei to munima pou tou stelnai o client
		}
		
		//taxinomei tis diergasies
		if(megethos>1) 
		{	quicksort(0,megethos-1);	}
			
		while(megethos>0) //loop oso yparxoun diergasies stin oura
		{
			/*pairnei tin prwti diergasia(dil auti me ton mikrotero xrono ektelesis) apo thn oura*/
			if(oura[0])
			{
			if(oura[0]->pidleitourgikou==-1) //an den exei xanatre3ei mia diergasia (pidleitourgikou=-1)
			{	
					
				printf("H process %s me pid %d trexei\n",oura[0]->onoma,oura[0]->pidouras);
				gettimeofday(&oura[0]->enarxi,NULL);
				pidfork=fork();
					
				if(pidfork==0)		//an pidfork einai 0 tote h diergasia einai h child
				{	
					close(sd);
					stoixeia[0]=oura[0]->onoma;
					stoixeia[1]=oura[0]->xaraktiristika;
					stoixeia[2]=NULL;
					int error = execv(oura[0]->onoma,stoixeia); /*to process image ths child antika8istatai apo thn pros ektelesh process*/
					printf("error: %d\n",error);
				}
				sigaction(SIGCHLD, &sa, NULL);
				wait(NULL); //gia na mhn menoun zombie processes
				oura[0]->pidleitourgikou=pidfork; /* Dinetai to pidleitourgikou ths child diergasias apo to systhma */
			}
			else 
			{
				printf("i diergasia me pid: %d den etrexe swsta\n",oura[0]->pidouras);
			}
					
			
			if(oloklirwthike==1) // an exei teleiwsei mia child process             
			{
				gettimeofday(&oura[0]->lixi,NULL);
			
				timersub(&oura[0]->lixi,&oura[0]->enarxi,&xronos_ektelesis);
				timersub(&oura[0]->lixi,&oura[0]->xronos_upobolis,&xronos_apokrisis);

				printf("H diergasia %d exei xrono ektelesis %ld sec and %ld usec\n ",oura[0]->pidouras,xronos_ektelesis.tv_sec,xronos_ektelesis.tv_usec);
				printf("H diergasia %d exei xrono apokrisis %ld sec and %ld usec\n ",oura[0]->pidouras,xronos_apokrisis.tv_sec,xronos_apokrisis.tv_usec);
				printf("H diergasia me pid %d teleiwse\n",oura[0]->pidouras);
				oura[0]->pidleitourgikou=-1;
				diagrafi_diergasias(oura[0]->pidouras);
				wait(NULL);	//gia na mhn menoun zombie processes
				oloklirwthike=0;
			}
			else
			{
				printf("to paidi den mporese na teleiwsei\n");
			}
			}
			
		}
	}
		
		
		
	



	if(a==1)	/*an dinete ws orisma to 1 tote xrisimopoihse tin politiki dromologisis RR(ROUND ROBIN)*/
	{
		printf("Xrisimopoieite o RR algoritmos ws politiki dromologisis me time slice se microseconds\n");
		
		struct timeval diff;
		pid_t pidfork;	//edw orizete to pid tou paidiou
		char *stoixeia[2];
		
		int time_slice = atoi(argv[2]);
		
		sa.sa_sigaction=sig_handler; /*H sunartisi pou kaleitai otan teleiwnei mia child process (dil edw orizete i sunartisi xeirismou simatwnkathos kai to filtro shmatwn pou ua xrisimopoihthei otan ektelestei i sunartish xeirismou)*/
		sigemptyset(&sa.sa_mask);
		sa.sa_flags = SA_RESTART | SA_NOCLDSTOP | SA_SIGINFO; /* SA_RESTART gia thn synepeia me tis BSD ekdoseis*/					
							// SA_NOCLDSTOP na mhn kaleitai h sig_handler otan ena
							// child ginetai stopped
							// SA_SIGINFO gia na apo8hkeuontai kapoies xrhsimes plhrofories
							// opws p.x. poios esteile to signal kai ti signal einai
		
		if(sigaction(SIGCHLD, &sa, NULL) == -1)
		{
			perror("Sigaction");
			exit(1);
		}
		
		my_alarm.sa_handler=myalarm; //h yporoutina pou kaleitai otan "xtypaei" o timer
		sigemptyset(&my_alarm.sa_mask);
		my_alarm.sa_flags = SA_RESTART; // SA_RESTART gia thn synepeia me tis BSD ekdoseis
		if(sigaction(SIGALRM, &my_alarm, NULL)==-1)
		{
			perror("sigaction");
			exit(1);
		}
			
		// Thetoume tin timh tou timer sto time_slice ms
		//edw thetoume tin teliki timi dhl. tin timi pou otan ftasei na dwsei signal kai na stamatisei
		xronos_enarxis.it_interval.tv_sec =0;
		xronos_enarxis.it_interval.tv_usec =0;
		xronos_enarxis.it_value.tv_sec =0;
		xronos_enarxis.it_value.tv_usec =(time_slice*1000);/* edw pollaplasiasa me to 1000 giati to time_slice einai se ms enw to tv.usec einai se microsec*/
		//edw thetoume tin arxiki timi dhl. tin timi pou tha xekinisei na metra o timer
		xronos_lixis.it_interval.tv_sec =0;
		xronos_lixis.it_interval.tv_usec =0;
		xronos_lixis.it_value.tv_sec =0;
		xronos_lixis.it_value.tv_usec =0;
		
		int i;
		//diabazei tis 6 diergasies poy tou stelnei o client
		for(i=0; i<6; i++)
		{
			new_sd = accept(sd,(struct sockaddr *)&serv_addr,&servlen);
			
			if(new_sd <0)
			{
				error("server accept failure");
				exit(1);
			}
			
			munuma_eisodou();	//Diabazei to munima pou tou stelnai o client
		}
		
		
		while(megethos>0)	//loop oso yparxoun diergasies stin oura
		{
		
			for( i=0;i<megethos;i++) //loop oso yparxoun processes sthn oura
			{
				if(oura[i]->pidleitourgikou==-1) //dil. einai TRUE an den exei xanatrexei i diergasia 
				{
					printf("H diergasia %s me pid %d trexei gia prwti fora \n", oura[i]->onoma,oura[i]->pidouras);
		        		gettimeofday(&oura[i]->enarxi,NULL); //apothikeush tou xronou enarxis tis diergasias
					pidfork=fork();
					if(pidfork==0) 	//an pidfork einai 0 tote h diergasia einai h child
					{
						close(sd);
						stoixeia[0]=oura[i]->onoma;
						stoixeia[1]=oura[i]->xaraktiristika;
						stoixeia[2]=NULL;
						int error = execv(oura[i]->onoma,stoixeia); /*to process image ths child antika8istatai apo thn pros ektelesh process*/
						printf("error: %d\n",error);
					}
					oura[i]->pidleitourgikou=pidfork; // Dinetai stin oura to pid ths child diergasias apo to systhma
					
				}
				else	/*an exei xanatrexei i diergasia tote tha ginei continue(dil. ua sunexisei tin ektelesi tis)*/
				{
					kill(oura[i]->pidleitourgikou,SIGCONT); /*edw dinete signal(shma) na synexisei tin ektelesi tis i diergasia*/
				}
				
				setitimer(ITIMER_REAL, &xronos_enarxis, NULL); //enarxi metrisis tou myalarm
				pause(); /*Oso den "xtypaei" to myalarm kanei sleep h parent process*/
				setitimer(ITIMER_REAL,&xronos_lixis,NULL); /*Edw apenergopoieite to myalarm eite afou     xtuphsei to myalarm eite afou teleiwsei i diergasia*/
				oloklirwsi_xronou=0;
				
				if(oloklirwthike==1) // an exei teleiwsei kanonika mia child process 
				{
					gettimeofday(&oura[i]->lixi,NULL); /*apothikeush tou xronou termatismou tis      diergasias*/
					
					timersub(&oura[i]->lixi,&oura[i]->enarxi,&xronos_ektelesis);
					timersub(&oura[i]->lixi,&oura[i]->xronos_upobolis,&xronos_apokrisis);

					printf("H diergasia %d exei xrono ektelesis %ld sec and %ld usec\n ",oura[i]->pidouras,xronos_ektelesis.tv_sec,xronos_ektelesis.tv_usec);
					
					//ypologismos tou xronou apokrisis tis diergasias
					printf("H diergasia %d exei xrono apokrisis %ld sec and %ld usec\n ",oura[i]->pidouras,xronos_apokrisis.tv_sec,xronos_apokrisis.tv_usec);
			
					printf("H diergasia %d teleiwse\n",oura[i]->pidouras);
			
					oura[i]->pidleitourgikou=-1; /*dinetai pidleitourgikou=-1 gia na mhn proklhthei  lathos stin diagrafi tis diergasias apo tin oura afou i diergasia exei idi termatisei*/
					diagrafi_diergasias(oura[i]->pidouras); /*kaleitai h diagrafi_diergasias gia na   diagrapsei apo thn lista thn diergasia pou teleiwse*/
				                            
					oloklirwthike=0;
					
					i--; 	//meiwnetai kata 1 to i giati me tin diagrafi(skotwma) tis diergasias   
						//ta periexomena tis ouras metaferuikan kata 1 thesi dil apo th thesi
						//i+1 sth thesi i
				}	
				else	/*an exei stamatisei i diergasia*/
				{	
					int error=kill(oura[i]->pidleitourgikou,SIGSTOP); /*edw dinete signal(shma) na stamatisei tin ektelesi tis i diergasia*/
					if(error!=0)
					{
			       			printf("Parousiastike sfalma kata kata to stamatima tis diergasias: %d\n",error);
					}
				}
				
				
			}
		}

		
	}



	
	
	
	close(new_sd);
	close(sd);
	
	remove("mysock");
	
}	




void error(char *msg)
{
    perror(msg);
    exit(1);
}	




void munuma_eisodou()
{
	char buffer[80];
	int yparxei_munuma=1;
	int cnt,count,cnt1;
	char onoma[80];
	char xaraktiristika[80];
	int op;
	int xronos,pid;
	
	while(yparxei_munuma)
	{	
		cnt=read(new_sd,&op,sizeof(int));
		
		if(cnt>0)	//dil oso yparxei kapoio mynuma ekteleitai auto to if
		{
			if(op==1)	//an to munuma einai exec tote prostithete mia diergasia stin oura
			{	
				cnt=read(new_sd,&count,sizeof(int));
				
				int i = 1;
				for (i = 1; i < count; i++)
				{
				
					int size;
					cnt=read(new_sd, &size, sizeof(int));
					cnt=read(new_sd, buffer, size);
					if(i==1)
					{
						strcpy(onoma,buffer);
					}
					if(i==2)
					{
						strcpy(xaraktiristika,buffer);
					}		
					if(i==3)
					{
						xronos=atoi(buffer);
					}	
				}
				
				prosthiki_diergasias(++pid_ouras,onoma,xaraktiristika,xronos);
			
			} 
			if(op==2)	//an to munuma einai kill tote diagrafete h diergasia me pid apo tin oura
			{
				cnt=read(new_sd,&pid,sizeof(int));
				diagrafi_diergasias(pid);
			}
			if(op==3)	/*an to munuma einai ps tote emfanizei ti lista me oles tis ergasies pou ektelountai (dil tis ergasies pou briskontai stin oura)*/
			{
				emfanisi_listas();
			}
			if(op==4)	//an to munuma einai exec tote prostithete mia diergasia stin oura
			{
				free(oura);
                		megethos=0;
				close(new_sd);
				remove("mysock");
				exit(0);
			}
			
		}
		
		if(cnt<=0)
		{	yparxei_munuma=0;	}
		
	}
}




void prosthiki_diergasias(int pid,char *onoma,char *xaraktiristika,int anamenomenos_xronos)
{
	oura = realloc(oura,(megethos+1)*sizeof(process *)); //megalwnei ton xwro mnhmhs ths ouras kata mia thesh
	oura[megethos]=(process *)malloc(sizeof(process));
        oura[megethos]->pidouras=pid;
	oura[megethos]->pidleitourgikou=-1; 	/*to pidleitourgikou einai to pid pou tha dwsei to leitourgiko sustima. 						arxika einai -1*/
	oura[megethos]->onoma=(char *)malloc(80);
	strcpy(oura[megethos]->onoma ,onoma);
	oura[megethos]->anamenomenos_xronos = anamenomenos_xronos;
	gettimeofday(&oura[megethos]->xronos_upobolis,NULL);	
	
	
	oura[megethos]->xaraktiristika=(char *)malloc(80);
	strcpy(oura[megethos]->xaraktiristika, xaraktiristika);
	
	megethos++; // H metablhti megethos periexei to megethos tis ouras 
}





/*H sunartisi auti diagrafei ti diergasia me pidouras iso me pid apo tin oura(lista) */
void diagrafi_diergasias(int pid)
{
	int i;
	int error;
	
	if(megethos==0)	//den uparxei kamia diergasia gia diagrafi
	{
		return;
	}
	
	for(i=0;(i<(megethos))&&(oura[i]->pidouras!=pid); i++)
	{	/*briskei ti diergasia me pidouras iso me pid */
	}
	
	if(i>=megethos)
	{
		printf("Den uparxei diergasia me pidouras iso me pid gia na diagrafei\n");
	}
	
	if( i>=0 && i<megethos)
	{	
		if(oura[i]->pidleitourgikou!=-1)   /*an i diergasia exei ginei stop dil pidleitourgikou!=-1 (kai den exei teleiwsei kanonika) tote tin skotwnei*/
		{	printf("\nh diergasia skotwnete\n\n");
			error=kill(oura[i]->pidleitourgikou,SIGKILL);
			wait(NULL); //edw bazw to wait gia na min ginei zombie i diergasia
			if(error!=0)
			{
			       printf("den skotwthike i diergasia me pid: %d\n",oura[i]->pidleitourgikou); /* dil den mporese na skotwsei ti diergasia*/
			}
			oloklirwsi_xronou=0; /*dil den perase o xronos tou myalarm afou skwtothike nwris i diergasia*/
		}
			for(;i<megethos;i++)	/*oi epomenes diergasies (apo auti pou skotwthike) katebainoun mia thesi stin oura */
			{
				oura[i]=oura[i+1];
			}
			oura= realloc(oura,(megethos-1)*sizeof(process *)); //Meiwnetai o desmeumenh mnhmh gia tin oura kata mia thesh
			megethos--;	//Meiwnetai to megethos tis ouras kata mia thesi		
	}
}



//H sunartisi auti stelnei ston client tis diergasies pou briskontai gia ektelesi stin oura
void emfanisi_listas()
{
	int i;
	
	write(new_sd,&megethos,sizeof(int));	// auto to stelnw ston client gia naxerei poso(megetos) ta diabasei
	for(i=0;i<megethos;i++)
	{
		write (new_sd, &oura[i]->pidouras, sizeof(int));
		write (new_sd, &oura[i]->pidleitourgikou, sizeof(int));
		int size3 = strlen (oura[i]->onoma) + 1;
		write (new_sd, &size3, sizeof(int));
		write (new_sd, oura[i]->onoma, size3);
		write (new_sd, &oura[i]->anamenomenos_xronos, sizeof(float));
	}

}

// H sunartisi auth taxinomei tis diergasies se aujousa seira basei tou apaitoumenou xronou ekteleshs
void quicksort(int m,int n)
{
 	process *temp;
 	int i = m;
 	int j=n+1;
 	temp=oura[m];
 	while(i<j)
 	{
        	j--;
		while((oura[j]->anamenomenos_xronos)>(temp->anamenomenos_xronos) && j>i){j--; }
            	if(j>i) oura[i]=oura[j];

		i++;
		while((oura[i]->anamenomenos_xronos)<(temp->anamenomenos_xronos) && j>i){i++;}
           	if(j>i) oura[j]=oura[i];


 	}

 	oura[j]=temp;
 	if(m<j-1){quicksort(m,(j-1));}
 	if(j+1<n){quicksort((j+1),n);}
}	



/* sunartisi pou kaleitai otan lambanetai ena signal SIGCHLD (dil. otan teleiwnei mia diergasia paidi)*/
void sig_handler(int s, siginfo_t* info,void* what)
{
	while(waitpid(-1, NULL, WNOHANG) > 0);
	errno = 0;

	oloklirwthike=1;	//i diergasia paidi exei teleiwsei kanonika	
}

//Sunartisi pou kaleitai otan lambanetai ena signal SIGALRM(dil otan teleiwnei to time slice)
void myalarm(int s )
{
	oloklirwsi_xronou=1;
}

