/*
 * Named_os2.c by Antonio Sanmarti <warper@ansaro.com>
 */

#ifdef __EMX__
#include "named_os2.h"
#include "port_before.h"

#ifdef HAVE_SPAWNXFER

#define STILL_ACTIVE		0x001

#ifndef BOOL
#define BOOL unsigned int
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef ULONG
#define ULONG unsigned long
#endif

#include <sys/types.h>
#include <sys/time.h>
#include <process.h>
#include <os2emx.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <errno.h>
#include <signal.h>
#include <resolv.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include <isc/eventlib.h>
#include <isc/logging.h>
#include <isc/memcluster.h>
#include <isc/dst.h>
#include <isc/list.h>
#include <res_update.h>
#include "../../../bin/named/ns_defs.h"
#include "../../../bin/named/ns_glob.h"
#include "port_after.h"

#include "xferldr.h"

char *xferldr_argv[] = {
	"xferldr.exe",
	"UsE_oS2_XfErLdR",	/* Cadena Magica */
	"",			/* contendra la cadena "\PIPE\NAMED\numero" */
	NULL};

/* Inicializa la lista de xfers */
static int os2_xfer_init = 0;

/* Lista de spawned xfers */
static struct _xferstatus {
	pid_t pid;
	unsigned status;
} xfers[MAX_XFERS_RUNNING];


struct _MY_PIDS {
	pid_t pid, pid_fake;
};

typedef struct _MY_PIDS MY_PIDS;

struct _MY_ARGS {
	struct _MY_PIDS pids;
	char **argv;
};

typedef struct _MY_ARGS MY_ARGS;

unsigned int getpid_fake(void);
void BuildPipeStr(char *, unsigned int);
void reapchildThread(void *);
void reapchildThread1(void *);
void Update_xfer_list(int , int);

pid_t spawnxfer(char **argv, struct zoneinfo *zp) {
	MY_ARGS my_args;
	char *s;
	int estado,i,stacksize,ThreadID;
	void *pargs;

	/* Limpiamos la lista de pids */
	if (!os2_xfer_init) {
		os2_xfer_init=1;
		for(i = 0; i < MAX_XFERS_RUNNING;i++) {
			xfers[i].pid=(pid_t)0;
			xfers[i].status=0;
			}
		}

	my_args.pids.pid=getpid();
	my_args.pids.pid_fake=getpid_fake();
	my_args.argv=argv;
	s=calloc(1,24);
	BuildPipeStr(s,my_args.pids.pid_fake);

	/* Llamamos a xferldr.exe */
	xferldr_argv[2]=s;
	estado=spawnvp(P_SESSION | P_UNRELATED | P_MINIMIZE, xferldr_argv[0],xferldr_argv);

	if (estado) {
		if (errno == 2) ns_notice(ns_log_default,"[OS/2] xferldr.exe not found.");
		else ns_notice(ns_log_default,"[OS/2] Error loading xferldr.exe. errno = %d", errno);
		estado = XFER_FAIL << 8;
		}

	free(s);

	for(i = 0; i < MAX_XFERS_RUNNING;i++) {
		if (!xfers[i].pid) {
			stacksize= 32768;
			xfers[i].pid=my_args.pids.pid_fake;
			if (!estado) {
				xfers[i].status = STILL_ACTIVE;
				pargs=(void*)&my_args;
				ThreadID = _beginthread(reapchildThread, NULL, stacksize, pargs);
				}else {
				xfers[i].status=estado;
				pargs=(void*)&my_args.pids.pid;
				ThreadID = _beginthread(reapchildThread1, NULL, stacksize, pargs);
				}
			break;
			}
		}

	_sleep(300);
	return (my_args.pids.pid_fake);
}

/*
 * Funcion encargada de enviar la senyal SIGCHLD.
 * reapchild() (ns_maint.c) es la funcion a llamar cuando esta senyal es enviada.
 */
void reapchildThread1(void *pdata) {
	int i,*pid;

	sleep(2); /* Damos tiempo a que se actualice la tabla xferstatus[] */
	pid=(int*)pdata;
	Update_xfer_list(*pid,XFER_FAIL << 8);
	i=kill(*pid,SIGCHLD);
	_endthread();
}

/*
 * Funcion encargada de recibir el exitcode de named-xfer.exe y enviar la senyal SIGCHLD.
 * reapchild() (ns_maint.c) es la funcion a llamar cuando esta senyal es enviada.
 */
void reapchildThread(void *args) {
	TUBO tubo;
	MY_ARGS *my_args;
	MY_PIDS my_pids;
	char **argv;
	int rc,i,j;
	char *s;
	ULONG nbytes;
	struct timeval t,t1;
	unsigned char *Buffer;

	/* Hacemos una copia inmediata */
	my_args=(MY_ARGS*)args;
	my_pids.pid=my_args->pids.pid;
	my_pids.pid_fake=my_args->pids.pid_fake;
	argv=my_args->argv;
	Buffer=calloc(1,WRITEBUFFSIZE);
	s=calloc(1,256);
	i=j=0;
	do { /* Copia de argumentos a pasar a named-xfer.exe" */
		sprintf(s,"%s",argv[i]);
		if (strcmp(s,"(null)") != 0) {
			rc=strlen(s);
			strcat((char*)&Buffer[j],s);
			j+=rc+1;
			} else rc = 0;
		++i;
	} while (rc);

	/* Creamos la cadena "\PIPE\NAMED\numero" */
	BuildPipeStr(s,my_pids.pid_fake);

	/* Creamos el pipe "\PIPE\NAMED\numero" */
	strcpy(tubo.Name,s);
	tubo.OpenMode = NP_ACCESS_DUPLEX | NP_NOINHERIT;
	tubo.Mode = NP_NOWAIT | NP_TYPE_BYTE | 0x01;	/* 1 instancia */
	tubo.TimeOut = 4000;				/* 4 segundos */
	tubo.ReadBuff  = calloc(1,READBUFFSIZE);
	tubo.WriteBuff = Buffer; /* calloc(1,WRITEBUFFSIZE); */

	rc=DosCreateNPipe(tubo.Name,&tubo.Hdl,tubo.OpenMode,tubo.Mode,
	               WRITEBUFFSIZE, READBUFFSIZE, tubo.TimeOut);
	if (rc) {
		Update_xfer_list(my_pids.pid_fake,XFER_FAIL);
		free(tubo.WriteBuff);
		free(tubo.ReadBuff);
		free(s);
		goto fin;
		}

	/* Bucle de espera para que el cliente conecte (con un timeout de 20 seg) */
	gettimeofday(&t,NULL);
	rc=DosConnectNPipe(tubo.Hdl);
	while (rc) {
		_sleep2(200);
		gettimeofday(&t1,NULL);
		if (t1.tv_sec >= (t.tv_sec + PIPE_TIMEOUT)) break;
		rc=DosConnectNPipe(tubo.Hdl);
		}
	if (rc) { /* No ha existido cliente a conectar */
		Update_xfer_list(my_pids.pid_fake,XFER_FAIL << 8);
		free(tubo.WriteBuff);
		free(tubo.ReadBuff);
		free(s);
		DosClose(tubo.Hdl);
		goto fin;
		} else { /* Pasamos a blocking */
		DosQueryNPHState(tubo.Hdl,&tubo.Mode);
		tubo.Mode ^= NP_NOWAIT;
		DosSetNPHState(tubo.Hdl,0);
		}

	/* Comunicacion via PIPE */

	DosRead(tubo.Hdl,tubo.ReadBuff,READBUFFSIZE, &nbytes); /* Debemos recibir "HOLA" */

	/* Envio de "namedxfer_argv[]" */
	DosWrite(tubo.Hdl,tubo.WriteBuff,j,&nbytes);

	/* Recepcion del exitcode del named-xfer.exe */
	rc=DosRead(tubo.Hdl,tubo.ReadBuff,READBUFFSIZE,&nbytes);
	i=atoi(tubo.ReadBuff);
	Update_xfer_list(my_pids.pid_fake,i);

	free(tubo.WriteBuff);
	free(tubo.ReadBuff);
	DosDisConnectNPipe(tubo.Hdl);
	DosClose(tubo.Hdl);

fin:
	/* Llamada a signal */
	_sleep2(200);
	i=kill(my_pids.pid,SIGCHLD);
	_endthread();
}

void Update_xfer_list(int pid, int estado) {
	int i;

	for(i = 0; i < MAX_XFERS_RUNNING;i++) {
		if (xfers[i].pid == pid) {
			xfers[i].status=estado;
			break;
			}
		}
}

unsigned int getpid_fake(void) {
	struct timeval t;

	sleep(1); /* Me aseguro que sucesivas llamadas den valores distintos */
	gettimeofday(&t,NULL);
	return(((unsigned int)t.tv_sec)%100000000);

}

void BuildPipeStr(char *s, unsigned int pid_fake) {
char *t;

	t=calloc(1,9);
	_itoa(pid_fake,t,10);
	strcpy(s,"\\PIPE\\NAMED\\");
	strcat(s,t);
}

/* Called by reapchild() int ns_maint.c emulate UNIX waitpid() for named */
int os2waitpid(pid_t pid, int *stat_loc, int options)
{
	int i;
	pid_t rc = 0;
	BOOL finished = FALSE;
	
	do {
	/* only check once if WNOHANG */
	if (options == WNOHANG) finished = TRUE;

	for(i = 0; i < MAX_XFERS_RUNNING; i++) {
	/* -1 indicates any child process, nonzero is a specific process */
		if(pid == -1 || (ULONG)pid == xfers[i].pid) {
			/* Check the state of the process */
			if(xfers[i].pid && (xfers[i].status != STILL_ACTIVE)) {
				if(xfers[i].pid) {
					finished = TRUE;
					if(stat_loc) {
						/* Put the exit code in the high byte of the high word */
						*stat_loc = xfers[i].status;
						}
					/* Clear this process from the record */
					rc = (pid_t)xfers[i].pid;
					xfers[i].pid = (pid_t)0;
					xfers[i].status=0;
					ns_debug(ns_log_default, 15, "os2waitpid() returns %d", rc);
					/* We want to return the pid of this process */
					return(rc);
					}
				}
			}
		}
	} while(!finished);
	return(rc);
}

#endif /* !HAVE_SPAWNXFER */
#endif /* !__EMX__*/

