/**********************************************/                
/* Пример для работы N6                       */
/**********************************************/
/* ОБЩИЕ ОБЛАСТИ ПАМЯТИ                       */
/**********************************************/
/* Монитор Слонов  - файл ganesha6.c          */
/**********************************************/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <sys/time.h>
#include <time.h>

#include "../common/elephant.h"
#include "../common/curtime.h"
#include "../common/ganesha.h"

/*static*/ int ne; /* параметр циклов */
/*static*/ meleph mel[NE]; /* управляющая информация о Слонах */  
int cnt; /* число запущенных Слонов */

/*static*/ char chld_name[]="./elephant6";
/*static*/ char Alarm=0;  /* признак поступления сигнала тревоги */

/* размер буфера общей памяти */
#define shmSize 8

/* обработчик сигнала тревоги */
void hAlrm(int sn)
{
	Alarm=1;   /* установка признака */
}

/**********************************************/
main()
{
	char *shm[NE];    /* адреса буферов */
	int need[NE],     /* потребности Слонов */
	in[NE],       /* позиции в буферах */
	shmId[NE];    /* идентификаторы общих областей */
	int cnt1;         /* счетчик Слонов */
	int i;
	int stat;  /* состояние процесса при завершении */
	/* строки для символьного представления параметров */  
	char t1[PARAMSTR_LENGTH], t2[PARAMSTR_LENGTH],
	     t3[PARAMSTR_LENGTH], t4[PARAMSTR_LENGTH];
	/* идентификаторы процессов */
	pid_t pw;
	/* текст сообщения об ошибке */
	char eee[ERRMES_LENGTH];

	/* начало работы */
	srand(time(NULL));  
	printf("%s Начало работы\n",curtime());

	/* установка обработчика сигнала тревоги */
	signal(SIGALRM,hAlrm);

	*t4=0;
	/* порождение дочерних процессов */
	for (ne=0; ne<NE; ne++)
	{
		/* запись личных данных в управляющую информацию */
		mel[ne].el=&ee[ne];
		/* представление нестроковых данных в строковом виде */
		sprintf(t1,"%d",mel[ne].el->age);
		sprintf(t2,"%lf",mel[ne].el->weight);
		/* создание общей области, которая будет совместно
		использоваться родительским и дочерним процессами */
		shmId[ne]=shmget(IPC_PRIVATE,shmSize,IPC_CREAT|0x1ff);
		/* получение адреса общей области */
		shm[ne]=(char *)shmat(shmId[ne],(char *)0,0);
		/* общая область пустая (заполнена 0) */
		for (i=0; i<shmSize; i++) shm[ne][i]=0;
		/* вычисление потребности Слона */
		need[ne]=mel[ne].el->weight*mel[ne].el->weight*5;
		/* позиция - начало области */
		in[ne]=0;
		/* преобразование идентификатора общей области в строку */
		sprintf(t3,"%d",shmId[ne]);
		/* порождение процесса */
		pw=fork();
		if (pw==0)
		{
			/* для дочернего процесса - запуск программы-Слона */  
			/* личные данные передаются через параметры */
			if (execl(chld_name, mel[ne].el->name, t1, t2, t3, t4, NULL)<0)
			{
				/* если загрузка программы-Слона почему-либо не удалась,
				печатается сообщение об ошибке, и процесс завершается */  
				perror(eee);
				printf("%s\n",eee); exit(0);
			}
			/* если вызов execl выполнился успешно, то далее в
			дочернем процессе выполняется программа elephant1 */
		}
		/* эта часть - только для процесса - Ганеши */
		/* заполнение управляющей информации о процессе  */
		/* состояние процесса пока что - не запущен */
		mel[ne].status=-1;
		/* установка случайной добавки к приоритету */
		mel[ne].prty=(int)(10.*rand()/RAND_MAX);
		setpriority(PRIO_PROCESS,pw,mel[ne].prty);
		/* запоминание ID процесса-Слона */
		mel[ne].chpid=pw;
	}
	/* пауза, чтобы процессы успели загрузить Слонов */
	sleep(1);
	/* перебор запущенных процессов */
	for (cnt=ne=0; ne<NE; ne++)
	{
		/* проверка - не закончился ли процесс */
		pw=waitpid(mel[ne].chpid,&stat,WNOHANG);
		if (pw==mel[ne].chpid) 
			/* если процесс закончился, значит, запуск Слона не удался */  
			printf("Слон %s не запущен\n",mel[ne].el->name);
		else
		{
			/* состояние процесса - запущен */
			mel[ne].status=0;
			/* подсчет запущенных Слонов */
			cnt++;
		}
	}
	/* если счетчик запущенных 0 - завершение Ганеши */
	if (!cnt) exit(0);
	cnt1=cnt;
	alarm(15);  /* подать сигнал тревоги через 30 сек */
	/* цикл пока не удовлетворятся все Слоны 
	или не будет подан сигнал тревоги */
	while (cnt1&&!Alarm)
	{
		/* перебор всех Слонов */
		for (ne=0; ne<NE; ne++)
		{
			if (need[ne])
			{
				/* если Слон еще не удовлетворен */
				if (!shm[ne][in[ne]])
				{
					/* если в буфере есть свободное место */
					/* выдача порции */
					shm[ne][in[ne]]=1;
					/* уменьшение потребности и исключение удовлетворенного Слона */  
					if (--need[ne]==0) cnt1--;
					/* использование общей памяти как кольцевого буфера */
					if (++in[ne]==shmSize) in[ne]=0;
				}
			}
			/* задержка на формирование следующей порции */
			usleep((int)(60.*rand()/RAND_MAX*1000));
		}
	}
	/* перебор дочерних процессов и прекращение тех, которые еще не закончились */
	//В этом месте ошибка в методичке! Необходимо исправить HTML-код.	
	for (ne=0; ne < NE; ne++)
	{
		pw = waitpid(mel[ne].chpid,&stat,WNOHANG);
		if (mel[ne].chpid!=pw)
		{
			/* если Слон не завершился, ему посылается сигнал KILL */  
			kill(mel[ne].chpid,SIGKILL);
			/* ожидание завершения после сигнала */
			waitpid(mel[ne].chpid,&stat,0);
			/* сообщение о гибели */
			printf("%s - Слон %s погиб\n",
			curtime(),mel[ne].el->name,stat);
		}
	}

	/* уничтожение общих областей */
	for (ne=0; ne<NE; ne++)
	{
		shmdt(shm[ne]);
		shmctl(shmId[ne],IPC_RMID,NULL);
	}
}
