/*
 *	rpclocal.c
 *
 *	Server (Processor Module site)
 */

/* remove comment for debugging
#define MC_RPCDEBUG
*/


#include "rpclocal.h"
#include "mcrpc.h"
#include "rpcfktns.h"
#include "mclocal.h"
#include "mcfktns.h"

char hostname[256];

char	*processtype = "PROCESS";

void	*vret = NULL;

void
check_started_comms(i, hostname, prognum)
			/*
			 * Called by a parallel process of the
			 * processor-module-communication-server
			 * i<>0 --> Ausgabe des Checks auf stdout
			 */
	int	i;
	char	*hostname;
	unsigned long	prognum;
{
	CLIENT	*cl;
	int	*active;
	int	cnt;

#ifdef  MC_RPCDEBUG
	printf ("rpclocal (check_started_comms)\n");
#endif	
	i = 1;

		/*
		 * requesting the parallel process
		 * on the local machine for the test
		 * of the connection and the test
		 * for inactive communications
		 */
			
	cl = clnt_create (hostname, prognum, V1, "tcp");

	for (cnt = 0; cnt < 10; cnt ++) {
		if (cl != NULL) {
			active = procmod_check_active_1 (&i, cl);
		} else {
			runtime_error (P_INTERN, "Connection to local Communication Server Process cannot be established. Program system is stopped");
		}

		if (active == NULL) {
			clnt_perror (cl, "Error during Initialization");
			runtime_error (P_INTERN, "Call to local Communication Server Process failed. Program system is stopped");
		} else if (*active == 1) {
			break;
		}
		sleep (1);
	}

	clnt_destroy (cl);

	if (*active == 1) {		
		runtime_error (P_INTERN, "Not all Communication Processes can be initialized. Program system is stopped");
	}
}


void
stop_single_comm(hostname, prognum)
	char	*hostname;
	unsigned long	prognum;
{
			/* Called by a parallel process of the
			 * processor-module-communication-server */
	CLIENT	*cl;
	void	*vp;

#ifdef  MC_RPCDEBUG
	printf ("rpclocal (stop_single_comm (hostname %s, prognum %lu))\n", hostname, prognum);
#endif	

	if (fork () > 0) {

			/* Child sends stop-request and terminates */
			
		cl = clnt_create (hostname, prognum, V1, "tcp");

		if (cl != NULL) {
			rpc_service_stop_1 (vp, cl);
		}

		clnt_destroy (cl);

		exit (0);
	}

		/* Parent continues */
}


void
start_communication_server(phostname, pprognumc, pprognumu)
			/* Die uebergebenen Pointer zeigen alle auf
			 * Inhalte im Shared-Memory-Bereich
			 * Die Inhalte werden von hier aus gesetzt
			 * bzw. fuer die char-Pointer zuerst
			 * Shared-Memory-Bereiche angefordert.
			 * Als letztes wird *pprognumc gestzt,
			 * da der Parent-Prozess ueber diesen
			 * Inhalt wartet, bis die RPC program number
			 * vergeben ist */
	char	**phostname;
	char	**pprognumc;
	unsigned long	*pprognumu;
{
	char	*pchelp;
	char	buf[80];
	int ans, s;
	SVCXPRT *xprt;

#ifdef  MC_RPCDEBUG
	printf ("rpclocal (start_communication_server) --> PROCESSID: %d\n", m0_processid);
#endif	

	gethostname (hostname, sizeof (hostname));
	if (hostname [sizeof (hostname)]) {
		runtime_error (P_INTERN, "start_communication_server: Length of local hostname exceeded. Program system is stopped");
	}
	
	s = RPC_ANYSOCK;
	prognum_of_initialization_server = gettransient(IPPROTO_TCP, 1, &s);
	
#ifdef  MC_RPCDEBUG
	printf("rpclocal (start_communication_server): Received prognum is %ld\n", prognum_of_initialization_server);
#endif	

	if ((xprt = svctcp_create(s, 0, 0)) == NULL) {
		runtime_error (P_INTERN, "start_communication_server: svctcp_create failed. Program system is stopped");
	}
		/* protocol is 0 - gettransient does registering */
      
	(void)svc_register(xprt, prognum_of_initialization_server, V1, initialization_server_1, IPPROTO_TCP);

	/*
	printf ("Hostname is %s\n", hostname);
	printf ("Program Number is decimal %ld, Hex %x\n",
		prognum_of_initialization_server, prognum_of_initialization_server);
	*/

		/* hostname */
	if ((pchelp = (char *) get_shared_memory_client (strlen (hostname) + 1)) == NULL) {
		runtime_error (P_INTERN, "start_communication_server: No more Shared Memory for registration of local hostname");
	}
	strcpy (pchelp, hostname);
	*phostname = pchelp;


		/* program number unsigned */
	*pprognumu = prognum_of_initialization_server;

	
		/* program number chararcter */
	sprintf (buf, "%lu\0", prognum_of_initialization_server);
	if ((pchelp = (char *) get_shared_memory_client (strlen (buf) + 1)) == NULL) {
		runtime_error (P_INTERN, "start_communication_server: No more Shared Memory for registration of local hostname");
	}
	strcpy (pchelp, buf);
		/*
		 * Die folgende Zuweisung muss die letzte
		 * Zuweisung vor dem Server-Start sein,
		 * weil der zugehoerige Parent-Prozess
		 * auf die Belegung dieses Variablen-Inhalt
		 * wartet und danach weitermacht
		 */
	*pprognumc = pchelp;

	svc_run();
		/* should never return */
	runtime_error (P_INTERN, "start_communication_server: Local Communication Server Process ended abnormal. Program system is stopped");
}


m0_computer
get_computer(wsname)
			/*
			 * Holen des Computer-Pointers
			 * durch Angabe des Workstation-Namens
			 */
	char	*wsname;
{
	COMPUTERLIST	*clhelp;

#ifdef  MC_RPCDEBUG
	printf ("rpclocal (get_computer)\n");
#endif	

	clhelp = begin_of_workstationlist;

	while (clhelp != NULL) {
		if (strcmp (clhelp->wsid->modula_p_workstation_name, wsname) == 0) {
			return (clhelp->wsid);
		}
		clhelp = clhelp->next;
	}
	return (NULL);
}


COMMLIST
*get_commlist_entry(wsid, commid, procno)
			/*
			 * Holen des Communication-Pointers
			 * durch Angabe des Computers,
			 * der Communication-Id
			 * und der (selbst vergebenen)
			 * Prozessnummer
			 */
	m0_computer	wsid;
	char	*commid;
	int	procno;
{
	COMMLIST	*commhelp;

#ifdef  MC_RPCDEBUG
	printf ("rpclocal (get_commlist_entry (wsid->{workstation_class %s, modula_p_workstation_name %s, real_workstation_name %s, ...}, commid %s, procno %d)\n",
		wsid->workstation_class, wsid->modula_p_workstation_name, wsid->real_workstation_name, commid, procno);
#endif	

	commhelp = wsid->communicationlist;

	while (commhelp != NULL) {
		if (strcmp (commhelp->communicationid, commid) == 0) {
			if (procno == commhelp->processno) {
				return (commhelp);
			}
		}
		commhelp = commhelp->next;
	}
	return (NULL);
}


		/* communication runtime procedures */

CLNT_PROCNO
*mk_connect(wsid, commid, computerid)
	m0_computer	wsid;
	char		*commid;
	char		*computerid;
{
	static	CLNT_PROCNO	clpn;
	COMMLIST	*cle;
	int	registered,
		ended;
	
#ifdef  MC_RPCDEBUG
	printf ("rpclocal (mk_connect (wsid->{workstation_class %s, modula_p_workstation_name %s, real_workstation_name %s, ...}, commid %s)\n",
		wsid->workstation_class, wsid->modula_p_workstation_name, wsid->real_workstation_name, commid);
#endif	
	registered = 0;
	ended = 0;

	for ( ; ; ) {
		p_exclusive_sem ();
	
		cle = wsid->communicationlist;

		while (cle != NULL) {
			if (strcmp (cle->communicationid, commid) == 0) {
				if (strcmp (cle->computerid, computerid) == 0) {
					if (cle->commstat == comm_started) {
						break;
					} else if (cle->commstat == comm_ended) {
						ended++;
					}
					registered++;
				}
			}
			cle = cle->next;
		}

		if (cle != NULL) {
					/* Connect ok */
			break;
		}

		if ((registered > 0) && (registered == ended)) {
					/* Remote Procedures registered and all of them already ended -> no connect possible */
			return (NULL);
		}

					/* All connections in use --> waiting */
		v_exclusive_sem ();
		sleep (1);
		registered = 0;
		ended = 0;
	}
	cle->commstat = comm_in_use;
	v_exclusive_sem ();
	
	clpn.procno = cle->processno;
	clpn.clnt = clnt_create (wsid->real_workstation_name, cle->prognum, V1, "tcp");

	return (&clpn);
}

	
void
release_connect(wsid, commid, pclpn)
	m0_computer	wsid;
	char		*commid;
	CLNT_PROCNO	*pclpn;
{
	COMMLIST	*cle;

#ifdef  MC_RPCDEBUG
	printf ("rpclocal (release_connect)\n");
#endif	

	if ((cle = get_commlist_entry (wsid, commid, pclpn->procno)) != NULL) {
		p_exclusive_sem ();
			if (cle->commstat != comm_ended) {
				cle->commstat = comm_started;
			}
		v_exclusive_sem ();
	}

	clnt_destroy (pclpn->clnt);
}



		/*
		 * server procedures to the clients
		 * with extension _1 instead of _server_site
		 */

void *
procmod_register_server_site(pwspn)
			/* A Communication Prozess registers here */
	workstationprognum	*pwspn;
{
	m0_computer	ws;
	COMMLIST	*cle;
	
#ifdef  MC_RPCDEBUG
	printf ("rpclocal (procmod_register_server_site (pwspn->{processid %d, processno %d, prognum %lu, modula_p_wsname %s, commid %s, real_wsname %s}))\n",
		pwspn->processid, pwspn->processno, pwspn->prognum, pwspn->modula_p_wsname, pwspn->commid, pwspn->real_wsname);
#endif	

	if ((ws = get_computer (pwspn->modula_p_wsname)) == NULL) {
			/* No match --> return without working */
		return (vret);
	}

	p_exclusive_sem ();
	if (ws->real_workstation_name == NULL) {
		if ((ws->real_workstation_name = (char *) get_shared_memory_client (strlen (pwspn->real_wsname) + 1)) == NULL) {
			runtime_error (P_INTERN, "procmod_register_server_site: No more Shared Memory for registration of real_wsname");
		}
		strcpy (ws->real_workstation_name, pwspn->real_wsname);
	}
	v_exclusive_sem ();

	if ((cle = get_commlist_entry (ws, pwspn->commid, pwspn->processno)) == NULL) {
			/* No match --> return without working */
		return (vret);
	}

	p_exclusive_sem ();
	cle->processid = pwspn->processid;
	cle->prognum = pwspn->prognum;
	cle->commstat = comm_started;
	v_exclusive_sem ();

	return (vret);	
}


void *
procmod_unregister_server_site(pwspn)
			/* A Communication Prozess unregisters here */
	workstationprognum	*pwspn;
{
	m0_computer	ws;
	COMMLIST	*cle;
	
#ifdef  MC_RPCDEBUG
	printf ("rpclocal (procmod_unregister_server_site (pwspn->{processid %d, processno %d, prognum %lu, modula_p_wsname %s, commid %s, real_wsname %s}))\n",
		pwspn->processid, pwspn->processno, pwspn->prognum, pwspn->modula_p_wsname, pwspn->commid, pwspn->real_wsname);
#endif

	if ((ws = get_computer (pwspn->modula_p_wsname)) == NULL) {
			/* No match --> return without working */
		return (vret);
	}

	if ((cle = get_commlist_entry (ws, pwspn->commid, pwspn->processno)) == NULL) {
			/* No match --> return without working */
		return (vret);
	}

	p_exclusive_sem ();
	cle->processid = pwspn->processid;
	cle->prognum = pwspn->prognum;
	cle->commstat = comm_ended;
	v_exclusive_sem ();

	return (vret);	
}


int *
procmod_check_active_server_site(i)
			/* if *argument = 1 --> output of all communications is shown */
			/* if all communications are started --> returncode is 1 otherwise 0 */
	int	*i;
{
	static	int	iretval;

	COMPUTERLIST	*clhelp;
	COMMLIST	*commhelp;

#ifdef  MC_RPCDEBUG
	printf ("rpclocal (procmod_check_active_server_site (i %d))\n", *i);
#endif	

	iretval = 1;

	clhelp = begin_of_workstationlist;

	while (clhelp != NULL) {
		commhelp = clhelp->wsid->communicationlist;

		if (*i != 0) {	/* show workstations */
			printf ("\nWorkstation with Modula-P-Name '%s'\n", clhelp->wsid->modula_p_workstation_name);
			printf ("\tof Workstation Class '%s'\n", clhelp->wsid->workstation_class);
			if (clhelp->wsid->real_workstation_name != NULL) {
				printf ("\thas its own name identified as '%s'\n", clhelp->wsid->real_workstation_name);
			}
			if (commhelp != NULL) {
				printf ("\tand owns the following communications:\n");
			} else {
				printf ("\tand has no communication!\n");
			}
		}

		while (commhelp != NULL) {
			if (*i != 0) {	/* show communications */
				char	*status;

				switch (commhelp->commstat) {
				case comm_null:
					status = "not yet started";
					break;
				case comm_started:
					status = "started";
					break;
				case comm_in_use:
					status = "used";
					break;
				case comm_ended:
					status = "ended";
					break;
				default:
					status = "unknown";
					break;
				}
				
				printf ("\t\tCommunication\n\t\t\t'%s'\n\t\t\thas PROCESSID '%d'\n\t\t\tand PROCESSNO '%d'\n\t\t\tand the RPC Program Number '%lu'\n\t\t\tand Status '%s'\n",
					commhelp->communicationid,
					commhelp->processid,
					commhelp->processno,
					commhelp->prognum,
					status);
			}
			if (commhelp->commstat != comm_started || commhelp->commstat != comm_in_use) {
				iretval = 0;
			}
			commhelp = commhelp->next;
		}

		clhelp = clhelp->next;
	}

	return (&iretval);	
}

	
void *
rpc_service_stop_server_site(x)
	void	*x;
{
#ifdef  MC_RPCDEBUG
	printf ("rpclocal (rpc_service_stop_server_site)\n");
#endif	

		/* Vom Deamon abmelden */
	svc_unregister (prognum_of_initialization_server, V1);

	exit (0);
}



void *
rpc_service_stop_client_site(x)
	void	*x;	/* This Procedure is included here
			 * only because of the implementation of
			 * the command HALT
			 */
{
	exit (0);
}
