/*
 *	mclocal.c
 */

#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <errno.h>
#include "rpclocal.h"
#include "mclocal.h"
#include "os.h"
#include "mcfktns.h"



#define P_MAXSHMSEGS	1
#define P_MAX_RANGE_OF_SHMSEGS_POSSIBLE	512

#define P_MAXSEMSETS	2
#define P_MAX_RANGE_OF_SEMSETS_POSSIBLE	512

#define P_MAXSEMSETELEMENTS	512

#define	P_MAX_PROCESSES		128

#ifdef sequent
void *
bzero (p, len)  /* define is insufficient because of  mcrpc_*.c */
        void    *p;
        int     len;
{
        return (memset (p, 0, len));
}
#endif

/*
 * REMOVE COMMENT FOR DEBUGGING
#define MCDEBUG 1
 */


/*
 * Shared-Memory- und Semaphor- und Communication-Steuerungs-Variablen
 */

m0_shmseg	used_shmsegs[P_MAXSHMSEGS];
int	used_shmsegs_max_index = 0;

int	*required_shm_size,
	*shm_request;
void	**shm_pointer;

m0_semaphore_set	(*shm_used_sems)[];
#define	used_sems	(*shm_used_sems)

int	*shm_used_sems_max_index;
#define	used_sems_max_index	(*shm_used_sems_max_index)

m0_semaphore_set	global_and_shm_sem;
m0_semaphore	get_shm_sem[2];
m0_semaphore	global_sem;
m0_semaphore	exclusive_sem; /* fuer Prozess-Verwaltungs-Aufgaben etc. */

		/* Wird communicationflag gesetzt,
		 * bedeutet dies, dass Communications
		 * gestartet werden muessen und der
		 * zugehoerige Server-Prozess auf der
		 * lokalen Maschine ebenfalls hochfahren
		 * muss */
int	*shm_communicationflag;
#define	communicationflag	(*shm_communicationflag)

int	*shm_killflag;
#define	killflag	(*shm_killflag)

char	**shm_local_host_name;
#define	local_host_name	(*shm_local_host_name)

unsigned long	*shm_program_number_unsigned;
#define	program_number_unsigned	(*shm_program_number_unsigned)

char	**shm_program_number_char;
#define	program_number_char	(*shm_program_number_char)

COMPUTERLIST	**shm_begin_of_workstationlist;

PROCESSES	**shm_begin_of_processnumberlist;
#define begin_of_processnumberlist	(*shm_begin_of_processnumberlist)



/*
 * Shared-Memory-Steuerung
 */

extern unsigned long	mc_shmsize;

#define	MAX_SHM_SIZE	(mc_shmsize)
 
void *
shm_define(required_shmsize)
	int	required_shmsize;
{
	static	int	firsttime = 1;
	static	m0_shmseg	shmid;
	static	char		*begin_of_shm;
	static	int		used_shm_bytes;
	int		shmsize;
	char		*shmaddress;

		/* shmsize muss in ganzen Integer-Wortbreiten zugeteilt werden wegen Verwendung bei Register-Maschinen */
	shmsize = (required_shmsize / sizeof (int)) * sizeof (int);
	if (shmsize != required_shmsize) {
		shmsize += sizeof (int);
	}

	if (firsttime) {
		key_t	i = 2; /* Ab Nr.2 keine reservierten Shared-Memory-Segmente */

		firsttime = 0;

		for ( ; i <= P_MAX_RANGE_OF_SHMSEGS_POSSIBLE; i++) {
			if (i == P_MAX_RANGE_OF_SHMSEGS_POSSIBLE) {
				runtime_error (P_WARN, "No free Shared Memory Segment found. Last errno: %d", errno);
				return (NULL);
			}
			shmid = shmget (i, MAX_SHM_SIZE, IPC_CREAT | IPC_EXCL | 0644);
			if (shmid >= 0) {
					/* Shared Memory Segment created */
				used_shmsegs [used_shmsegs_max_index] = shmid;
				used_shmsegs_max_index++;
				break;
			}
		}

		if ((int) (begin_of_shm = (void *) shmat (shmid, NULL, 0)) == -1) {
			runtime_error (P_WARN, "Shared-Memory-Operation shmat failed: %d", errno);
			return (NULL);
		}
		shmaddress = begin_of_shm;
		used_shm_bytes = shmsize;
	} else {
					/*
					 * Verlaengerung des Shared-
					 * Memory-Segments um shmsize
					 * NUR FUER RS/6000
					 
		struct	shmid_ds	ds,
					*buf = &ds;
		if (shmctl (shmid, IPC_STAT, buf) != 0) {
			runtime_error (P_INTERN, "Shared-Memory-Operation shctl (IPC_STAT) failed: %d", errno);
		}

#ifdef MCDEBUG
		printf ("Shared Memory Requested: %d Bytes, Assigned: %d Bytes; Old Size is %d, New Size is %d\n", required_shmsize, shmsize, ds.shm_segsz, ds.shm_segsz + shmsize);
#endif
		shmaddress = begin_of_shm + ds.shm_segsz;
		ds.shm_segsz += shmsize;
		
		if (shmctl (shmid, SHM_SIZE, buf) != 0) {
			runtime_error (P_INTERN, "Shared-Memory-Operation shctl (SHM_SIZE) failed: %d", errno);
		}
					 */
					/*
					 * Shared-Memory-Zuweisung
					 * aus dem bereits vorhandenen
					 * Shared-Memory-Segment
					 */
		shmaddress = begin_of_shm + used_shm_bytes;
#ifdef MCDEBUG
		printf ("Shared Memory Requested: %d Bytes, Assigned: %d Bytes; Old Size is %d, New Size is %d\n", required_shmsize, shmsize, used_shm_bytes, used_shm_bytes + shmsize);
#endif
		used_shm_bytes += shmsize;
		
		if (used_shm_bytes > MAX_SHM_SIZE) {
			runtime_error (P_INTERN, "Shared-Memory-Operation failed: No more Shared Memory available (program-limit %d Bytes)", MAX_SHM_SIZE);
		}
	}
	
	return (shmaddress);
}


void
semop_get_shm_sem(index, i)
	int	index;
	int	i;
{
					/*
					 * Wird nur von
					 * get_shared_memory_server und
					 * get_shared_memory_client zur
					 * Synchronisation aufgerufen
					 */
	struct sembuf	sops[1];

#ifdef MCDEBUG
	printf ("semop_get_shm_sem (%d, %d)\n", index, i);
#endif

	sops [0].sem_num = get_shm_sem [index];
	sops [0].sem_op = i;
	sops [0].sem_flg = 0;
	if (semop (global_and_shm_sem, sops, 1)) {
		runtime_error (P_INTERN, "Semaphore-Operation semop (%s (get_shm_sem)) failed: %d", (i<0?"P":"V"), errno);
	}

	return;
}

#define consume_get_shm_sem(index)	semop_get_shm_sem (index, -1)
#define produce_get_shm_sem(index)	semop_get_shm_sem (index, 1)


void
get_shared_memory_server()
				/* Server-Aufruf zu get_shared_memory_client */
{
	*required_shm_size = 0;
	*shm_pointer = NULL;

	*shm_request = 0;

					/*
					 * Produce get_shm_sem, damit der
					 * Request abgesetzt wird und
					 * die Kommunikation ber Shared-
					 * Memory nicht gestoert wird
					 */
#ifdef MCDEBUG
	printf ("PID: %d waiting for request req %d size %d\n", m0_processid, *shm_request, *required_shm_size);
#endif
	produce_get_shm_sem (0);

				/* Warten auf Request */
	while (*shm_request == 0) {
		usleep (10);	/* 10 Microsec. */
	}

#ifdef MCDEBUG
	printf ("PID: %d request received req %d size %d\n", m0_processid, *shm_request, *required_shm_size);
#endif
				/* Request erfolgt */
	if (*required_shm_size) {
				/* NEW */
		*shm_pointer = shm_define (*required_shm_size);
	} else if (*shm_pointer) {
				/* DISPOSE */
	}

	*shm_request = 0;

	return;
}


void *
get_shared_memory_client(shmsize)
	int	shmsize;
				/* Client-Aufruf zu get_shared_memory_server */
{
	void	*help;
					/*
					 * Dies wird aufgerufen, wenn von
					 * einem Prozess eine Shared-Memory-
					 * Operation verlangt wird; siehe
					 * first_init (Parent-Teil der fork)
					 */
	struct sembuf	sops[1];

					/*
					 * Semaphor fuer exklusiven Zugriff auf den
					 * get_shared_memory-Mechanismus, falls
					 * mehrere Clients gleichzeitig kommen
					 * sollten
					 */
	consume_get_shm_sem (1);

					/*
					 * Produce global_sem, damit der
					 * Kontroll-Prozess die Shared-
					 * Memory-Verwaltung vornimmt
					 */
	sops [0].sem_num = global_sem;
	sops [0].sem_op = P_MAX_PROCESSES;
	sops [0].sem_flg = 0;
	if (semop (global_and_shm_sem, sops, 1)) {
		runtime_error (P_EXCEPTION256, "Semaphore-Operation semop (V (global_sem)) failed: %d", errno);
	}
					/*
					 * Consume get_shm_sem, damit die
					 * Kommunikation mit dem
					 * Kontroll-Prozess ber Shared-
					 * Memory nicht gestoert wird
					 */
	consume_get_shm_sem (0);

#ifdef MCDEBUG
	printf ("PID: %d before request req %d size %d\n", m0_processid, *shm_request, *required_shm_size);
#endif

	*required_shm_size = shmsize;
	*shm_request = 1;

#ifdef MCDEBUG
	printf ("PID: %d requesting req %d size %d\n", m0_processid, *shm_request, *required_shm_size);
#endif
					/* Warten auf die Zuteilung */
	while (*shm_request == 1) {
		usleep (10);	/* 10 Microsec. */
	}

#ifdef MCDEBUG
	printf ("PID: %d request answer received req %d size %d\n", m0_processid, *shm_request, *required_shm_size);
#endif
					/*
					 * Zuteilung erfolgt, Pointer wird zuerst gesichert,
					 * kritischer Abschnitt freigegeben und gesicherter
					 * Pointer zurueckgeliefert
					 */
	help = *shm_pointer;	
	produce_get_shm_sem (1);
	return (help);
}


/*
 * Communication-Identifikation und -Steuerung
 */

m0_computer
add_computer(wsklasse, wsname)
				/* COMPUTER (..., ...) */
				/*
				 * Computer werden registriert,
				 * in eine Liste eingetragen und
				 * ein Pointer auf den Computer
				 * (NICHT auf den Listeneintrag!!!)
				 * zuruckgeliefert.
				 * Sowohl wsklasse als auch wsname
				 * werden erwartet als
				 * Konstanter String, d. h.
				 * er wird nicht dupliziert,
				 * sondern der uebergebene
				 * Pointer direkt verwendet.
				 */
	char	*wsklasse;
	char	*wsname;
{
	COMPUTERLIST	*clhelp;

				/* Listen-Element */
	if ((clhelp = (COMPUTERLIST *) get_shared_memory_client (sizeof (COMPUTERLIST))) == NULL) {
		runtime_error (P_INTERN, "add_computer: No more Shared Memory for registration of Computerlist Element");
	}

				/* Computer registrieren
				 * --> Am Anfang der Liste eintragen;
				 * falls Liste = NULL, wird der
				 * ->next-Eintrag automatisch auch NULL
				 */
				 
	clhelp->next = begin_of_workstationlist;
	begin_of_workstationlist = clhelp;

				/* Computer selbst (m0_computer == (COMPUTER *)) */
	if ((begin_of_workstationlist->wsid = (m0_computer) get_shared_memory_client (sizeof (COMPUTER))) == NULL) {
		runtime_error (P_INTERN, "add_computer: No more Shared Memory for registration of Computer");
	}

		/* Neuer Computer wird initialisiert */
	begin_of_workstationlist->wsid->workstation_class = wsklasse;
	begin_of_workstationlist->wsid->modula_p_workstation_name = wsname;
	begin_of_workstationlist->wsid->communicationlist = NULL;

	return (begin_of_workstationlist->wsid);
}


void
init_communication(wsid, computerid, commid, cnt, commno)
				/* INITCOM (..., ..., ...) */
				/*
				 * Communications werden initialisiert
				 * und dabei in die Liste der Workstation
				 * eingetragen und die PROCESSNO (*commno)
				 * entsprechend erhoeht.
				 * commid wird erwartet als
				 * Konstanter String, d. h.
				 * er wird nicht dupliziert,
				 * sondern der uebergebene
				 * Pointer direkt verwendet.
				 */
	m0_computer	wsid;
	char	*computerid;
	char	*commid;
	int	cnt;
	int	*commno;
{
	COMMLIST	*communicationhelp;
	char	buf[256];
	char	ibuf[16];
	int	i;

	if (communicationflag++ == 0) {
				/*
				 * Der Communication-Server-Prozess wird hier gestartet
				 */

		if ((fork_return_code = fork ()) == 0) {
				/* Child wird zum Communication-Server */

				/*
				 * Hangup, Control-C oder kill-Signal beim
				 * Initialisieren werden ignoriert
				 */
			signal (SIGHUP, SIG_IGN);
			signal (SIGINT, SIG_IGN);
			signal (SIGTERM, SIG_IGN);
		
			m0_processid = (int) getpid ();

			start_communication_server (shm_local_host_name, shm_program_number_char, shm_program_number_unsigned);

				/* wird normalerweise nie erreicht */
			runtime_error (P_INTERN, "Communication-Server stopped irregularly. Please kill remote processes manually!");
		}
	
			/* Parent laeuft als Modul-Initialisierungsprozess weiter */

		if (fork_return_code == -1) {
			runtime_error (P_INTERN, "Too many Processes. Program system is stopped");
		}
	}

	if (strlen (module_name) + strlen (commid) + strlen (wsid->workstation_class) > 250) {
		runtime_error (P_INTERN, "init_communication: Name '%s_%s_%s' too long", module_name, commid, wsid->workstation_class);
	}

	sprintf (buf, "%s_%s_%s\0", module_name, commid, wsid->workstation_class);

	while (program_number_char == NULL) {
			/* Warten auf die Server-Nummer (nur beim ersten Mal) */
		usleep (100);
	}

	for (i = *commno; i < *commno + cnt; i++) {
					/* Listen-Element */
		if ((communicationhelp = (COMMLIST *) get_shared_memory_client (sizeof (COMMLIST))) == NULL) {
			runtime_error (P_INTERN, "init_communication: No more Shared Memory for registration of Initialized Communication");
		}
		communicationhelp->next = wsid->communicationlist;
		if ((communicationhelp->computerid = (char *) get_shared_memory_client (strlen (computerid) + 1)) == NULL) {
			runtime_error (P_INTERN, "init_communication: No more Shared Memory for registration of ComputerId");
		}
		strcpy (communicationhelp->computerid, computerid);
		wsid->communicationlist = communicationhelp;
		wsid->communicationlist->communicationid = commid;
		wsid->communicationlist->processno = i;
		wsid->communicationlist->commstat = comm_null;

		sprintf (ibuf, "%d\0", i);

		if ((fork_return_code = fork ()) == 0) {
				/* Child started die Communication */

				/*
				 * Hangup, Control-C oder kill-Signal beim
				 * Initialisieren werden ignoriert
				 */
			signal (SIGHUP, SIG_IGN);
			signal (SIGINT, SIG_IGN);
			signal (SIGTERM, SIG_IGN);
			
			execlp ("rsh", wsid->modula_p_workstation_name, buf,
				wsid->modula_p_workstation_name,	/* arg 1 des Communication-Programms */
				commid,					/* arg 2 */
				ibuf,					/* arg 3 */
				local_host_name,			/* arg 4 */
				program_number_char,			/* arg 5 des Communication-Programms */
				0);

				/* darf nie zurueckkehren */
			runtime_error (P_INTERN, "init_communication: Communication '%s' not started at host '%s'. Error of execlp: %d!",
					buf, wsid->modula_p_workstation_name, errno);
		}
			/* Parent setzt die Initialisierung fort */
	}

	if (fork_return_code == -1) {
		runtime_error (P_INTERN, "Too many Processes. Program system is stopped");
	}
	
	*commno += cnt;
}




/*
 * Shared-Memory- und Semaphor-Initialisierung und -Beendigung,
 * Communication-Beendigung
 */

void
shm_and_sem_init()
{
	char	*shmsegment;
	int	i;

	if ((shmsegment = (char *) shm_define (sizeof (int) * 3
						+ sizeof (char *)
						+ sizeof (unsigned long)
						+ sizeof (char *)
						+ sizeof (int)
						+ sizeof (void *)
						+ sizeof (COMPUTERLIST *)
						+ sizeof (PROCESSES *)
						+ sizeof (int)
						+ sizeof (m0_semaphore_set) * P_MAXSEMSETS)) == NULL) {
		runtime_error (P_INTERN, "Minimal required Shared-Memory not available");
	}

	required_shm_size = (int *) shmsegment;
	shm_communicationflag = (int *) (shmsegment + sizeof (int));
	shm_killflag = (int *) (shmsegment + 2 * sizeof (int));
	shm_local_host_name = (char **) (shmsegment + 3 * sizeof (int));
	shm_program_number_unsigned = (unsigned long *) (shmsegment + 3 * sizeof (int) + sizeof (char *));
	shm_program_number_char = (char **) (shmsegment + 3 * sizeof (int) + sizeof (char *) + sizeof (unsigned long));
	shm_request = (int *) (shmsegment + 3 * sizeof (int) + 2 * sizeof (char *) + sizeof (unsigned long));
	shm_pointer = (void **) (shmsegment + 4 * sizeof (int) + 2 * sizeof (char *) + sizeof (unsigned long));
	shm_begin_of_workstationlist = (COMPUTERLIST **) (shmsegment + 4 * sizeof (int) + 2 * sizeof (char *) + sizeof (unsigned long) + sizeof (void *));
	shm_begin_of_processnumberlist = (PROCESSES **) (shmsegment + 4 * sizeof (int) + 2 * sizeof (char *) + sizeof (unsigned long) + sizeof (void *) + sizeof (COMPUTERLIST *));
	shm_used_sems_max_index = (int *) (shmsegment + 4 * sizeof (int) + 2 * sizeof (char *) + sizeof (unsigned long) + sizeof (void *) + sizeof (COMPUTERLIST *) + sizeof (PROCESSES *));
	shm_used_sems = (m0_semaphore_set (*)[]) (shmsegment + 5 * sizeof (int) + 2 * sizeof (char *) + sizeof (unsigned long) + sizeof (void *) + sizeof (COMPUTERLIST *) + sizeof (PROCESSES *));

	*shm_communicationflag = 0;
	*shm_killflag = 0;
	*shm_local_host_name = NULL;
	*shm_program_number_unsigned = 0;
	*shm_program_number_char = NULL;
	*shm_begin_of_workstationlist = NULL;
	*shm_begin_of_processnumberlist = NULL;
	*shm_used_sems_max_index = 0;

	return;
}


void
stop_communications (total, ended)
			/* first time called, it stops the communications,
			 * later calls check only the success */
	int	*total, *ended;
{
			/* stop communications */
	COMPUTERLIST	*computerhelp;
	COMMLIST	*communicationhelp;
	char	buf[120];

	static int	mode = 0;

	*total = 0;
	*ended = 0;

	computerhelp = begin_of_workstationlist;
	while (computerhelp != NULL) {

		communicationhelp = computerhelp->wsid->communicationlist;
		while (communicationhelp != NULL) {

			if (mode == 0) {
				if (communicationhelp->commstat != comm_null && communicationhelp->commstat != comm_ended) {
					stop_single_comm (computerhelp->wsid->real_workstation_name, communicationhelp->prognum);
				}
			}

			if (communicationhelp->commstat != comm_null) {
				(*total)++;
			}
			if (communicationhelp->commstat == comm_ended) {
				(*ended)++;
			}

			communicationhelp = communicationhelp->next;
		}
		
	computerhelp = computerhelp->next;
	}

	mode++;
}


void
kill_communications ()
{
	COMPUTERLIST	*computerhelp;
	COMMLIST	*communicationhelp;
	char	buf[120];

	computerhelp = begin_of_workstationlist;
	while (computerhelp != NULL) {

		communicationhelp = computerhelp->wsid->communicationlist;
		while (communicationhelp != NULL) {

			if (communicationhelp->commstat != comm_null && communicationhelp->commstat != comm_ended) {
				if (communicationhelp->processid != 0) {
					sprintf (buf, "rsh %.64s kill -9 %d\0", computerhelp->wsid->modula_p_workstation_name, communicationhelp->processid);
					fprintf (stderr, "Trying to kill communication: %s\n", buf);
					system (buf);
					sprintf (buf, "rsh %.64s %s_%s_%s %s %lu wrdlprmpft\0",
						 computerhelp->wsid->modula_p_workstation_name,
						 module_name,
						 communicationhelp->communicationid,
						 computerhelp->wsid->workstation_class,
						 computerhelp->wsid->modula_p_workstation_name,
						 communicationhelp->prognum);
					fprintf (stderr, "Trying to remove program number '%lu' from portmap deamon at workstation '%s'\n",
						 communicationhelp->prognum,
						 computerhelp->wsid->modula_p_workstation_name);
					system (buf);
				} else {
					fprintf (stderr, "Communication '%s' may be active on workstation '%s'\n",
						 communicationhelp->communicationid, computerhelp->wsid->modula_p_workstation_name);
				}
			}

			communicationhelp = communicationhelp->next;
		}
		
	computerhelp = computerhelp->next;
	}
}


void
shm_and_sem_and_comm_kill()
{
	int	i;
	union semun {
		int	val;
		struct	semid_ds	*semid_ds_buf;
		ushort	*array;
	} arg;
	struct	shmid_ds	*shmid_ds_buf = NULL;
	int	slp, total, ended, diff, diffneu = 0;

	if (killflag++ == 0) {
			/* Only the first process which comes to this point
			 * kills really all communications, semaphores and
			 * shared memory segments */

		if (communicationflag) {

			runtime_error (P_INFO, "Communications will be stopped. Please wait.");

				/* Stop Communications */
			do {
					/* first time stop_communications is called,
					 * the stopping will be activated, all later times
					 * only the return values are evaluated
					 *
					 * this do-while-loop ends when there are no
					 * additionally stopped communications in the
					 * following pass or when all started communications
					 * are stopped */
				if ((diff = diffneu) != 0) {
					slp = 5;
					do {
						slp = sleep (slp);
					} while (slp);
				}

				stop_communications (&total, &ended);

				if (total == ended) {
					break;
				}

				diffneu = total - ended;

			} while (diff != diffneu);

		
				/* Kill Communications (for safety) */
			kill_communications ();



				/* stop communication server process */
			stop_single_comm (local_host_name, program_number_unsigned);
		}

		slp = 10;
		do {
			slp = sleep (slp);
		} while (slp);

			/* Kill Semaphores */
		for (i = 0; i < used_sems_max_index; i++) {
			arg.val = 0;
			if (semctl (used_sems [i], 0, IPC_RMID, arg)) {
				runtime_error (P_WARN, "Semaphore-Operation semctl (IPC_RMID) failed at Semaphore nr %d with error: %d", used_sems [i], errno);
			}
		}

			/* Kill Shared Memory Segments */
		for (i = 0; i < used_shmsegs_max_index; i++) {
			arg.val = 0;
			if (shmctl (used_shmsegs [i], IPC_RMID, shmid_ds_buf)) {
				runtime_error (P_WARN, "Shared-Memory-Operation shmctl (IPC_RMID) failed at Segment nr %d with error: %d", used_shmsegs [i], errno);
			}
		}
		runtime_error (P_INFO, "All Semaphores and Shared Memory Segments cleared. Program-System terminated\n");
	}

	return;
}




/*
 * Semaphore-Steuerung
 */

int	consume_verbot = 1;
m0_semaphore	sems_in_set = 0;
m0_semaphore	initial_sem_vals_of_sem_set[P_MAXSEMSETELEMENTS];
m0_semaphore_set	sem_set_id;


void
get_global_and_shm_semaphore()
{
	key_t	i = 2; /* Ab Nr.2 keine reservierten Semaphore */
	union semun {
		int	val;
		struct	semid_ds	*buf;
		ushort	*array;
	} arg;

	for ( ; i <= P_MAX_RANGE_OF_SEMSETS_POSSIBLE; i++) {
		if (i == P_MAX_RANGE_OF_SEMSETS_POSSIBLE) {
			runtime_error (P_EXCEPTION256, "No free Semaphore found. Last errno: %d", errno);
		}
		global_and_shm_sem = semget (i, 4, IPC_CREAT | IPC_EXCL | 0644);
		if (global_and_shm_sem >= 0) {
				/* Semaphore created */
			used_sems [used_sems_max_index] = global_and_shm_sem;
			used_sems_max_index++;
			break;
		}
	}

		/* get_shm_sem */
	get_shm_sem [0] = 0;
	arg.val = 0;
	if (semctl (global_and_shm_sem, get_shm_sem [0], SETVAL, arg)) {
		runtime_error (P_EXCEPTION256, "get_global_and_shm_semaphore: Semaphore-Operation semctl (SETVAL (get_shm_sem)) failed: %d", errno);
	}
	get_shm_sem [1] = 1;
	arg.val = 1;
	if (semctl (global_and_shm_sem, get_shm_sem [1], SETVAL, arg)) {
		runtime_error (P_EXCEPTION256, "get_global_and_shm_semaphore: Semaphore-Operation semctl (SETVAL (get_shm_sem)) failed: %d", errno);
	}

		/* global_sem */
	global_sem = 2;
	arg.val = P_MAX_PROCESSES;
	if (semctl (global_and_shm_sem, global_sem, SETVAL, arg)) {
		runtime_error (P_EXCEPTION256, "get_global_and_semaphore: Semaphore-Operation semctl (SETVAL (global_sem)) failed: %d", errno);
	}

		/* exclusive_sem */
	exclusive_sem = 3;
	arg.val = 1;
	if (semctl (global_and_shm_sem, exclusive_sem, SETVAL, arg)) {
		runtime_error (P_EXCEPTION256, "get_global_and_semaphore: Semaphore-Operation semctl (SETVAL (exclusive_sem)) failed: %d", errno);
	}
}


void
p_exclusive_sem()
{
	struct sembuf	sops[1];

#ifdef MCDEBUG
	printf ("p_exclusive_sem ()\n");
#endif

	sops [0].sem_num = exclusive_sem;
	sops [0].sem_op = -1;
	sops [0].sem_flg = 0;
	if (semop (global_and_shm_sem, sops, 1)) {
		runtime_error (P_INTERN, "p_exclusive_sem: Semaphore-Operation semop (exclusive_sem) failed: %d", errno);
	}

	return;
}


void
v_exclusive_sem()
{
	struct sembuf	sops[1];

#ifdef MCDEBUG
	printf ("v_exclusive_sem ()\n");
#endif

	sops [0].sem_num = exclusive_sem;
	sops [0].sem_op = 1;
	sops [0].sem_flg = 0;
	if (semop (global_and_shm_sem, sops, 1)) {
		runtime_error (P_INTERN, "v_exclusive_sem: Semaphore-Operation semop (exclusive_sem) failed: %d", errno);
	}

	return;
}


m0_semaphore
sem_define(cnt)
	int	cnt;
{
		/* defining semaphore with initial value */
	initial_sem_vals_of_sem_set [sems_in_set] = cnt;
	sems_in_set++;

	return (sems_in_set - 1);
}


void
get_defined_sems()
{
		/* getting a set of semaphores and initializing it with its values */
	int	z;
	key_t	i = 2; /* Ab Nr.2 keine reservierten Semaphore */
	union semun {
		int	val;
		struct	semid_ds	*buf;
		ushort	*array;
	} arg;
		
	if (sems_in_set == 0) {
		return;
	}

	for ( ; i <= P_MAX_RANGE_OF_SEMSETS_POSSIBLE; i++) {
		if (i == P_MAX_RANGE_OF_SEMSETS_POSSIBLE) {
			runtime_error (P_EXCEPTION256, "No free Semaphore found. Last errno: %d", errno);
		}
		sem_set_id = semget (i, sems_in_set, IPC_CREAT | IPC_EXCL | 0644);
		if (sem_set_id >= 0) {
				/* Semaphore created */
			used_sems [used_sems_max_index] = sem_set_id;
			used_sems_max_index++;
			break;
		}
	}

	for (z = 0; z < sems_in_set; z++) {
		arg.val = initial_sem_vals_of_sem_set [z];
		if (semctl (sem_set_id, z, SETVAL, arg)) {
			runtime_error (P_EXCEPTION256, "get_defined_sems: Semaphore-Operation semctl (SETVAL) failed: %d", errno);
		}
	}
}


void
consume_global_sem()
{
					/*
					 * Jeder erzeugte Prozess konsumiert
					 * das globale Semaphor (initialisiert
					 * mit P_MAX_PROCESSES) mit Wert 1;
					 * dies geschieht hier mittels
					 * consume_global_sem.
					 * Der Ueberwachungsprozess konsumiert
					 * mit Wert P_MAX_PROCESSES, er laeuft
					 * also erst weiter, wenn alle anderen
					 * Prozesse beendet sind oder von
					 * einem dieser Prozesse eine
					 * Shared-Memory-Zuteilung verlangt
					 * wird; siehe get_shared_memory_client
					 * und first_init (Parent-Teil der fork)
					 */
	struct sembuf	sops[1];

	sops [0].sem_num = global_sem;
	sops [0].sem_op = -1;
	sops [0].sem_flg = SEM_UNDO;
	if (semop (global_and_shm_sem, sops, 1)) {
		runtime_error (P_EXCEPTION256, "Semaphore-Operation semop (P (global_sem)) failed: %d", errno);
	}
}


void
m0_p(s)
	m0_semaphore	s;
{
	struct sembuf	sops[1];

	if (consume_verbot) {
		runtime_error (P_EXCEPTION256, "Semaphore-Operation semop (P) here not allowed");
	}
	
	sops [0].sem_num = s;
	sops [0].sem_op = -1;
	sops [0].sem_flg = 0;
	if (semop (sem_set_id, sops, 1)) {
		runtime_error (P_EXCEPTION256, "Semaphore-Operation semop (P) failed: %d", errno);
	}
}

void
m0_v(s)
	m0_semaphore	s;
{
	struct sembuf	sops[1];
	
	sops [0].sem_num = s;
	sops [0].sem_op = 1;
	sops [0].sem_flg = 0;
	if (semop (sem_set_id, sops, 1)) {
		runtime_error (P_EXCEPTION256, "Semaphore-Operation semop (V) failed: %d", errno);
	}
}


/*
 * Condition-Steuerung
 */
void
m0_v_and_wait(s, c)
	m0_semaphore	s;
	m0_condition	c;
{
	struct sembuf	sops[1];

					/* Phase 1: Incrementing Condition-Semaphore (NOWAIT) */
	sops [0].sem_num = c;
	sops [0].sem_op = 1;
	sops [0].sem_flg = 0;
	if (semop (sem_set_id, sops, 1)) {
		runtime_error (P_EXCEPTION256, "Semaphore-Operation semop failed at WAIT-statement (Phase 1): %d", errno);
	}

					/* Phase 2: Incrementing Monitor-Semaphor */
	sops [0].sem_num = s;
	sops [0].sem_op = 1;
	sops [0].sem_flg = 0;
	if (semop (sem_set_id, sops, 1)) {
		runtime_error (P_EXCEPTION256, "Semaphore-Operation semop failed at WAIT-statement (Phase 2): %d", errno);
	}

					/* Phase 3: Waiting for Condition-Semaphore to become 0 */
	sops [0].sem_num = c;
	sops [0].sem_op = 0;
	sops [0].sem_flg = 0;
	if (semop (sem_set_id, sops, 1)) {
		runtime_error (P_EXCEPTION256, "Semaphore-Operation semop failed at WAIT-statement (Phase 3): %d", errno);
	}
	return;
}


void
m0_signal(c)
	m0_condition	c;
{
	struct sembuf	sops[1];
	int	i, count_stat, count_wait;
	union semun {
		int	val;
		struct	semid_ds	*buf;
		ushort	*array;
	} arg;

					/* Decrements Condition-Semaphore until it has the value 0
					 * and waits until all waiting processes are released */
	do {
		arg.val = 0;
		if ((count_wait = semctl (sem_set_id, c, GETZCNT, arg)) < 0) {
			runtime_error (P_EXCEPTION256, "Semaphore-Operation semctl (GETZCNT) failed at SIGNAL-statement: %d", errno);
		}

		count_stat = m0_status (c);

		if (count_stat != count_wait)
			continue;	/* Loop until stat == wait, 
					 * otherwise danger of deadlock */

		for (i = 1; i <= count_stat; i++) {
			sops [0].sem_num = c;
			sops [0].sem_op = -1;
			sops [0].sem_flg = 0;
			if (semop (sem_set_id, sops, 1)) {
				runtime_error (P_EXCEPTION256, "Semaphore-Operation semop (P) failed at SIGNAL-statement: %d", errno);
			}
		}
	} while (count_stat > 0 || count_wait > 0);
	
	return;
}


int
m0_status(c)
	m0_condition	c;
{
	int	count;

	union semun {
		int	val;
		struct	semid_ds	*buf;
		ushort	*array;
	} arg;

	arg.val = 0;
	if ((count = semctl (sem_set_id, c, GETVAL, arg)) < 0) {
		runtime_error (P_EXCEPTION256, "Semaphore-Operation semctl (GETVAL) failed at STATUS-statement: %d", errno);
	}
	return (count);
}



/*
 * Prozess-Identifikation und -Steuerung
 */


PROCESSES
*get_process_entry (pname)
	char	*pname;
				/*
				 * Prozessnamen werden registriert,
				 * in eine Liste eingetragen und
				 * ein Pointer zuruckgeliefert.
				 * Der Prozessname wird erwartet
				 * als Konstanter String, d. h.
				 * er wird nicht dupliziert,
				 * sondern der uebergebene
				 * Pointer direkt verwendet.
				 */
{
	PROCESSES	*pnhelp1, *pnhelp2;
	
	if (begin_of_processnumberlist == NULL) {
				/* Noch kein Prozess registriert */
		if ((pnhelp1 = (PROCESSES *) get_shared_memory_client (sizeof (PROCESSES))) == NULL) {
			runtime_error (P_INTERN, "get_process_entry: No more Shared Memory for registration of first Process");
		}
		begin_of_processnumberlist = pnhelp1;
	} else {
				/* Prozesse bereits registriert --> Suchen, ob der aktuelle Prozessname dabei ist */
		pnhelp1 = begin_of_processnumberlist;

		do {
			if (strcmp (pnhelp1->processname, pname) == 0) {
					/* Prozess bereits vorhanden */
				return (pnhelp1);
			}
			pnhelp2 = pnhelp1;
			
		} while ((pnhelp1 = pnhelp1->next));
		
			/* Aktueller Prozess noch nie gestarted */
		if ((pnhelp1 = (PROCESSES *) get_shared_memory_client (sizeof (PROCESSES))) == NULL) {
			runtime_error (P_INTERN, "get_process_entry: No more Shared Memory for registration of Processes");
		}
		pnhelp2->next = pnhelp1;
	}
	
		/* Neuer Prozesslisteneintrag erhaelt die Initialwerte */
	pnhelp1->processname = pname;
	pnhelp1->maxnumber = 0;
	pnhelp1->exception = NULL;
	pnhelp1->next = NULL;
	return (pnhelp1);
}


int
inc_processno_activ_exception (pname)
	char	*pname;
				/*
				 * Die Prozessnummer des Prozesses
				 * wird zuruckgeliefert und die zugehoerige
				 * Exception aktiviert. Falls der Prozess
				 * noch nicht registriert wurde, wird dies
				 * ebenfalls getan.
				 * Der Prozessname wird erwartet
				 * als Konstanter String, d. h.
				 * er wird nicht dupliziert,
				 * sondern der uebergebene
				 * Pointer direkt verwendet.
				 */
{
	PROCESSES	*pnhelp;
	int	proc_no;

	p_exclusive_sem ();

	pnhelp = get_process_entry (pname);

	if (pnhelp->exception != NULL) {
		sig_exception = pnhelp->exception;
			/* Illegal Instruction (4) */
		sig_neu (SIGILL, sig_exception);
		signal (SIGILL, sig_exception);
			/* Floating Point Exception (8) */
		sig_neu (SIGFPE, sig_exception);
		signal (SIGFPE, sig_exception);
	}

	proc_no = ++(pnhelp->maxnumber);

	v_exclusive_sem ();

	return (proc_no);
}


void
define_exception (pname, except)
	char	*pname;
	sigreturntype	(*except) ();
				/*
				 * Die Exception-Prozedur wird registriert
				 * (und, falls noch nicht geschehen,
				 * der Prozess ebenfalls).
				 */
{
	PROCESSES	*pnhelp;

	p_exclusive_sem ();

	pnhelp = get_process_entry (pname);

	pnhelp->exception = except;

	v_exclusive_sem ();

	return;
}




/*
 * Signal-Steuerung
 */


void
sig_set()
{
	int	i;

	for (i = 1; i <= NSIG; i++) {
		signal (i, sig_save [i]);
	}

	consume_verbot = 0;

	return;
}




/*
 * Programm-System-Initialisierung
 */

int	fork_return_code;
 
int	cnt;

typedef	struct	fktnlst {
			void	(*name) ();
			struct	fktnlst	*next;
		} fktnlst;

fktnlst	*fktnlststart = NULL,
	*fktnlsthelp = NULL;

void
add_fktn(fktnname)
	void	(*fktnname) ();
{
	static	fktnlst	**fktnlstend;

	if ((fktnlsthelp = (fktnlst *) malloc (sizeof (fktnlst))) == NULL) {
		runtime_error (P_INTERN, "add_fktn (malloc): No more memory for saving initialization functions!");
	}

	fktnlsthelp->name = fktnname;
	fktnlsthelp->next = NULL;

	if (fktnlststart == NULL) {
			/* first element of list */
		fktnlststart = fktnlsthelp;
	} else {
			/* not first element of list */
		*fktnlstend = fktnlsthelp;
	}

	fktnlstend = &(fktnlsthelp->next);
}

	
void
execute_fktn()
{
	for (fktnlsthelp = fktnlststart; fktnlsthelp != NULL; fktnlsthelp = fktnlsthelp->next) {
		fktnlsthelp->name ();
	}
}


int
init_cancel ()
{
	runtime_error (P_INTERN, "init_cancel: Cancelling on request");
		/*
		 * Der Prozess beendet sich und
		 * loescht die Semaphore und Shared-Memory-Segmente
		 */
}



void
first_init()
{
	/*
	 * Zuerst werden alle Initialisierungen durchgefuehrt
	 */

	m0_processid = (int) getpid ();

	srand48 (((long) m0_processid) * ((long) m0_processid - 1L));

	setbuf (stdin, NULL);
	setlinebuf (stdout);
	setlinebuf (stderr);
	
	sig_init ();
	sig_ign ();

	shm_and_sem_init ();
	
			/* Der Puffer wird auf Non-Buffered gesetzt */
	setbuf (IN_FILE, NULL);
	setbuf (OUT_FILE, NULL);

			/*
			 * Das Shared-Memory-Verwaltungs- und das
			 * globale Semaphor werden erzeugt
			 */
	get_global_and_shm_semaphore ();

			/*
			 * Ein Ueberwachung-Prozess fuer die
			 * Shared-Memory-Verwaltung und fuer die
			 * Loeschung der benutzten Semaphore,
			 * Shared-Memory-Segmente und
			 * gestarteten Communications am Schluss
			 * der Programmbearbeitung wird hier erzeugt
			 */

	
	if ((fork_return_code = fork ())) {
			/* Parent wird zum Kontroll-Prozess */

		struct sembuf	sops[1];

		union semun {
			int	val;
			struct	semid_ds	*buf;
			ushort	*array;
		} arg;

		int	gsemval;

		if (fork_return_code == -1) {
			runtime_error (P_INTERN, "Too many Processes. Program system is stopped");
		}

		sleep (4);

		for ( ; ; ) {
			sops [0].sem_num = global_sem;
			sops [0].sem_op = - P_MAX_PROCESSES;
			sops [0].sem_flg = 0;

			if (semop (global_and_shm_sem, sops, 1)) {
				runtime_error (P_INTERN, "Semaphore-Operation semop (P (global_sem)) failed: %d", errno);
			}

			arg.val = 0;
			if ((gsemval = semctl (global_and_shm_sem, global_sem, GETVAL, arg)) < 0) {
				runtime_error (P_INTERN, "Semaphore-Operation semctl (GETVAL (global_sem)) failed with error: %d", errno);
			}
			if (gsemval == 0)
				break;

			get_shared_memory_server ();
		}

		shm_and_sem_and_comm_kill ();
		exit (0);
	} else {
			/* Child wird fuer die Modul-Initialisierung weiterverwendet */
	
		m0_processid = (int) getpid ();

		consume_global_sem ();

			/*
			 * Hangup, Control-C oder kill-Signal beim
			 * Initialisieren fuehrt zum Abbruch
			 */
		signal (SIGHUP, init_cancel);
		signal (SIGINT, init_cancel);
		signal (SIGTERM, init_cancel);
		
		return;
	}
}


void
second_init()
{
	int	i;

	/*
	 * Die angeforderten Semaphore werden endgueltig zugewiesen,
	 */

	get_defined_sems ();

	if (communicationflag) {
			/*
			 * Communications wurden gestarted.
			 * Jetzt wird gewartet, bis alle
			 * Communications aktiv sind, d. h.
			 * sich beim Communication-Server-Porzess
			 * gemeldet haben oder nach 10
			 * Fehlversuchen abgebrochen
			 */
		int	i;

		i = 1;	/* i <> 0 --> Verbose-Mode */
		
		check_started_comms (i, local_host_name, program_number_unsigned);
	}

	return;
}



/*
 * Program-Abort
 */

void
modula_p_abort()
{
	shm_and_sem_and_comm_kill ();
}


