/*===========================================================================
  Copyright (C) 1995-2005 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.TYPE        Module
.NAME        iodev
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.CATEGORY    Host operating system interfaces. Tape management.
.COMMENTS    	Remote tape management. 
             	The functions of this module perform basic i/o to
             	magnetic tapes on UNIX enviroments
.ENVIRONment 	UNIX

.VERSION 1.1	24-Jan-1990   Implementation     C. Guirao
051021		last modif

------------------------------------------------------------*/

static char class_name[] = "remote";	/* MAgnetic Tape Class */

#define  DEBUG	1
#include <osudef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <errno.h>
#include <osparms.h>

#include <stdlib.h>

extern int errno;
extern int oserror;
extern char *oserrmsg;

#ifdef SYSV_V2
#define	BCOPY(a,b,c)	memcpy((b),(a),(c))
#else /* BSD */
#define	BCOPY(a,b,c)	bcopy((a),(b),(c))
#endif

#define OPEN_DEVICE	0
#define CLOSE_DEVICE	1
#define READ_DATA	2
#define WRITE_DATA	3
#define WRITE_EOF	4
#define SKIP_FSF	5
#define SKIP_BSF	6
#define REWIND		7
#define SKIP_FSR	8
#define SKIP_BSR	9
#define SKIP_EOM	10
#define READ_INFO	11

#define NULL	0
#define RET_ERROR(x) { oserror = (x); return(-1); }
#define RET_OUT_SYNC { oserrmsg = msg0; oserror = -1; return(-1); }
#define TAPE_SERVICE "tcp_tape"	/* must be defined in /etc/services */
#define MAXMSG 160		/* Max. lenth for a error message */
static int fdr, fdw;		/* Read and Write descriptors for sockets*/
static char *message;

static char msg0[] = "Out of syncronism with tape server";

struct command {
	int cmd;
	int param[4];
	int nobyt;
	};

struct result {
	int ret;
	int oserror;
	int nobyt;
	};

static char errmsg[MAXMSG];


static int ioctop(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Interface to remote ioctl routine.
.RETURNS 0 (OK) / -1 (error)
------------------------------------------------------------*/
struct command *op;
{
	int nobyt, ret;
	struct result res;

        oserror=0;

	nobyt = sizeof(struct command);
	if ( (ret=osxwrite(fdw,(char *)op,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt) RET_OUT_SYNC;

	nobyt = sizeof(struct result);
	if ( (ret=osxread(fdr,(char *)&res,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	oserror=res.oserror;
	if ( res.ret < 0 && res.nobyt > 0) {
		if (( message = malloc((size_t)res.nobyt)) == NULL) 
			RET_ERROR(ENOMEM);
		if ( (ret=osxread(fdr,message,res.nobyt,'c')) < 0) return(-1);
		if ( ret != res.nobyt ) RET_OUT_SYNC;
		strncpy(errmsg,message,MIN(res.nobyt,MAXMSG));
		oserrmsg=errmsg;
		free(message);
		}

        return(res.ret);
}

static int ioinfo(fd, s, filenum, blkno)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Retrieve Info concerning an opened device
.RETURNS 0 (success) / -1 (error)
------------------------------------------------------------*/
int	fd;		/* IN: File Descriptor		*/
struct osustat	*s;	/* OUT: The filled components */
int     *filenum;        /* OUT: Where we are    */
long    *blkno;         /* OUT: Where we are    */
{
	int nobyt, ret;
	char *buffer;
	struct command op;
	struct result res;

        oserror=0;

	op.cmd = READ_INFO;
	op.param[0] = fd;
	op.param[1] = sizeof(struct osustat) + sizeof(int) + sizeof(long);

	nobyt = sizeof(struct command);
	if ( (ret=osxwrite(fdw,(char *)&op,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	nobyt = sizeof(struct result);
	if ( (ret=osxread(fdr,(char *)&res,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	if ((buffer = malloc((size_t)op.param[1])) == NULL) RET_ERROR(ENOMEM);

	if (res.ret = 0) {
		if ( (ret=osxread(fdr,buffer,op.param[1],'c')) < 0) return(-1);
		if ( ret != op.param[1] ) RET_OUT_SYNC;
		}

	BCOPY(s,buffer,sizeof(struct osustat));
	*filenum = (int) buffer[sizeof(struct osustat)];
	*blkno = (long) buffer[sizeof(struct osustat)+sizeof(int)];

	free(buffer);

	oserror=res.oserror;
	if ( res.ret < 0 && res.nobyt > 0) {
		if (( message = malloc((size_t)res.nobyt)) == NULL) 
			 RET_ERROR(ENOMEM);
		if ( (ret=osxread(fdr,message,res.nobyt,'c')) < 0) return(-1);
		if ( ret != res.nobyt ) RET_OUT_SYNC;
		strncpy(errmsg,message,MIN(res.nobyt,MAXMSG));
		oserrmsg=errmsg;
		free(message);
		}

        return(res.ret);
}


static int ioopen(name,class,mode,den)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Open a tape device
.RETURNS File descriptor / -1 (error)
------------------------------------------------------------*/
char *name;             /* IN:  Physical name of tape device */
char *class;		/* IN:  Class name */
int mode;               /* IN:  Open mode */
int den;                /* IN:  Density. Not used */
{
	static char devname[80], remotehost[64];
	static char *channame[2];
	static char localhost[64];
	register char *p, *q;
	char *buff;
	int nobyt, ret;
	struct command op;
	struct result res;

        oserror = 0;            /* Clear error from getdev      */

        for (q = remotehost , p = name ; (*p) && (*p != '!'); p++)  *q = *p ;
        *q = '\0';
        if (*p == '!')  p++;
        else 	{
		oserror = -1;
		oserrmsg = "Name not in <remotehost!device> format";
		return(-1);
		}
        for (q = devname; *p; p++, q++)       *q = *p;
        *q = '\0';

	channame[0] = TAPE_SERVICE;
	channame[1] = remotehost;
	if ( (fdr=osxopen(channame,NETW|IPC_READ)) < 0 ) return(-1);
	if ( (fdw=osxopen(channame,NETW|IPC_WRITE)) < 0 ) return(-1);

	gethostname(localhost,64);
	op.cmd = OPEN_DEVICE;
	op.param[0] = mode;
	op.param[1] = den;
	op.param[2] = strlen(name);
	op.param[3] = strlen(class);
	op.nobyt = strlen(name) + strlen(class) + strlen(localhost);

	nobyt = sizeof(struct command);
	if ( (ret=osxwrite(fdw,(char *)&op,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	if (( buff = malloc((size_t)op.nobyt)) == NULL) RET_ERROR(ENOMEM);
	strcpy(buff,name);
	strcat(buff,class);
	strcat(buff,localhost);
	if ( (ret=osxwrite(fdw,buff,op.nobyt,'c')) < 0) return(-1);
	if ( ret != op.nobyt ) RET_OUT_SYNC;
	free(buff);

	nobyt = sizeof(struct result);
	if ( (ret=osxread(fdr,(char *)&res,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	oserror=res.oserror;
	if ( res.ret < 0 && res.nobyt > 0) {
		if (( message = malloc((size_t)res.nobyt)) == NULL) 
			RET_ERROR(ENOMEM);
		if ( (ret=osxread(fdr,message,res.nobyt,'c')) < 0) return(-1);
		if ( ret != res.nobyt ) RET_OUT_SYNC;
		strncpy(errmsg,message,MIN(res.nobyt,MAXMSG));
		oserrmsg=errmsg;
		free(message);
		}
        return(res.ret);
}

static int ioclose(fd)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Close the opened device
.RETURNS 0
------------------------------------------------------------*/
int fd;                 /* IN:  Tape file descriptor */
{
	int nobyt, ret;
	struct command op;
	struct result res;

        oserror=0;

	op.cmd = CLOSE_DEVICE;
	op.param[0] = fd;
	op.nobyt = 0;

	nobyt = sizeof(struct command);
	if ( (ret=osxwrite(fdw,(char *)&op,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	nobyt = sizeof(struct result);
	if ( (ret=osxread(fdr,(char *)&res,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	oserror=res.oserror;
	if ( res.ret < 0 && res.nobyt > 0) {
		if (( message = malloc((size_t)res.nobyt)) == NULL)
			RET_ERROR(ENOMEM);
		if ( (ret=osxread(fdr,message,res.nobyt,'c')) < 0) return(-1);
		if ( ret != res.nobyt ) RET_OUT_SYNC;
		strncpy(errmsg,message,MIN(res.nobyt,MAXMSG));
		oserrmsg=errmsg;
		free(message);
		}

	if ( osxclose(fdr) < 0) return(-1);
	if ( osxclose(fdw) < 0) return(-1);

        return(res.ret);
}


static int ioread(fd,buffer,size)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Read a block from a magnetic tape.
.RETURNS Bytes read / -1 if error
.REMARKS 0 Bytes read, means a File Mark was detected.
.REMARKS oserror set to -2 if buffer too small
------------------------------------------------------------*/
int fd;                 /* IN:  Tape device file descriptor */
char *buffer;           /* IN:  Buffer for reading */
int size;               /* IN:  Length of bytes to be read */
{
	int nobyt, ret;
	struct command op;
	struct result res;

        oserror=0;

	op.cmd = READ_DATA;
	op.param[0] = fd;
	op.param[1] = size;
	op.nobyt = 0;

	nobyt = sizeof(struct command);
	if ( (ret=osxwrite(fdw,(char *)&op,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	nobyt = sizeof(struct result);
	if ( (ret=osxread(fdr,(char *)&res,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	if (res.ret > 0) {
		if ( (ret=osxread(fdr,buffer,res.ret,'c')) < 0) return(-1);
		if ( ret != res.ret ) RET_OUT_SYNC;
		}

	oserror=res.oserror;
	if ( res.ret < 0 && res.nobyt > 0) {
		if (( message = malloc((size_t)res.nobyt)) == NULL)
			RET_ERROR(ENOMEM);
		if ( (ret=osxread(fdr,message,res.nobyt,'c')) < 0) return(-1);
		if ( ret != res.nobyt ) RET_OUT_SYNC;
		strncpy(errmsg,message,MIN(res.nobyt,MAXMSG));
		oserrmsg=errmsg;
		free(message);
		}

        return(res.ret);
}

static int iowrite(fd,buffer,size)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write a block on a magnetic tape.
.RETURNS Bytes written / -1 (error)
------------------------------------------------------------*/
int fd;                 /* IN:  Tape device file descriptor */
char *buffer;           /* IN:  Buffer for reading */
int size;               /* IN:  Length of bytes to be read */
{
	int nobyt, ret;
	struct command op;
	struct result res;

        oserror=0;

	op.cmd = WRITE_DATA;
	op.param[0] = fd;
	op.param[1] = size;
	op.nobyt = 0;

	nobyt = sizeof(struct command);
	if ( (ret=osxwrite(fdw,(char *)&op,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt) RET_OUT_SYNC;

	if ( (ret=osxwrite(fdr,buffer,size,'c')) < 0) return(-1);
	if ( ret != size ) RET_OUT_SYNC;

	nobyt = sizeof(struct result);
	if ( (ret=osxread(fdr,(char *)&res,nobyt,'c')) < 0) return(-1);
	if ( ret != nobyt ) RET_OUT_SYNC;

	oserror=res.oserror;
	if ( res.ret < 0 && res.nobyt > 0) {
		if (( message = malloc((size_t)res.nobyt)) == NULL)
			RET_ERROR(ENOMEM);
		if ( (ret=osxread(fdr,message,res.nobyt,'c')) < 0) return(-1);
		if ( ret != res.nobyt ) RET_OUT_SYNC;
		strncpy(errmsg,message,MIN(res.nobyt,MAXMSG));
		oserrmsg=errmsg;
		free(message);
		}

        return(res.ret);
}

static int ioweof(fd,ntm)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write end-of-file record (tape_mark) on the tape.
.RETURNS 0 / -1 (error)
------------------------------------------------------------*/
int fd;                 /* IN:  Tape device file descriptor */
int ntm;		/* IN:  Number of tape marks */
{
	struct command op;

	op.cmd = WRITE_EOF;
	op.param[0] = fd;
	op.param[1] = ntm;
	op.nobyt = 0;

	return(ioctop(&op));
}

static int iofsf(fd,ntm)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Skip forward space file on a tape. 
.RETURNS Tape marks skipped/ -1 (error)
------------------------------------------------------------*/
int fd;                 /* IN:  Tape device file descriptor */
int ntm;                /* IN:  Number of tape marks */
{
	struct command op;

	op.cmd = SKIP_FSF;
	op.param[0] = fd;
	op.param[1] = ntm;
	op.nobyt = 0;

	return(ioctop(&op));
}


static int iobsf(fd,ntm)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Skip backward space file on a tape. 
.RETURNS Tape marks skipped/ -1 (error)
------------------------------------------------------------*/
int fd;                 /* IN:  Tape device file descriptor */
int ntm;                /* IN:  Number of tape marks */
{
	struct command op;

	op.cmd = SKIP_BSF;
	op.param[0] = fd;
	op.param[1] = ntm;
	op.nobyt = 0;

	return(ioctop(&op));
}


static int iorew(fd)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Rewind tape
.RETURNS 0 / -1 (error)
------------------------------------------------------------*/
int fd;                 /* IN:  Tape device file descriptor */
{
	struct command op;

	op.cmd = REWIND;
	op.param[0] = fd;
	op.nobyt = 0;

	return(ioctop(&op));
}

static int iofsr(fd,count)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Forward space record on tape
.RETURNS records skipped/ -1 (error)
------------------------------------------------------------*/
int fd;                 /* IN:  Tape device file descriptor */
int count;              /* IN:  Number of records */
{
	struct command op;

	op.cmd = SKIP_FSR;
	op.param[0] = fd;
	op.param[1] = count;
	op.nobyt = 0;

	return(ioctop(&op));
}

static int iobsr(fd,count)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Backward space record on tape
.RETURNS records skipped/ -1 (error)
------------------------------------------------------------*/
int fd;                 /* IN:  Tape device file descriptor */
int count;              /* IN:  Number of records */
{
	struct command op;

	op.cmd = SKIP_BSR;
	op.param[0] = fd;
	op.param[1] = count;
	op.nobyt = 0;

	return(ioctop(&op));
}

/* #ifdef MTEOM	/* Does the driver know the End-Of-Media ? */
static int ioeom(fd)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Move to EOMedia
.RETURNS 0 (OK) / -1 (not done, error)
------------------------------------------------------------*/
int fd;                 /* IN:  Tape device file descriptor */
{
	struct command op;

	op.cmd = SKIP_EOM;
	op.param[0] = fd;
	op.nobyt = 0;

	return(ioctop(&op));
}
/*#endif */


/*=====================================================================
 * 		Definition of the structure returned to osu
 *=====================================================================*/

struct iolist *dumb();


static OPITEM list_of_functions[] = {
	{ U_INFO,	ioinfo},
	{ U_OPEN,	ioopen},
	{ U_CLOSE,  	ioclose},
	{ U_READ,	ioread},
	{ U_WRITE,	iowrite},
	{ U_REWIND, 	iorew},
	{ U_WEOF,	ioweof},
	{ U_FMF,	iofsf},	/* File Move Forward  	*/
	{ U_FMB,	iobsf},	/* File Move Backward 	*/
	{ U_BMF,	iofsr},	/* Block Move Forward  	*/
	{ U_BMB,	iobsr},	/* Block Move Backward 	*/
#ifdef MTEOM
	{ U_EOM,	ioeom}	/* Move to EOMedia 	*/
#endif
 };

static struct iolist this_dev = {
	dumb,		/* Next iodev in List */
	class_name,	/* How it's written in DEVCAPFILE */
	sizeof(list_of_functions)/sizeof(list_of_functions[0]),
	list_of_functions
 };
 
struct iolist *iodev()
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Get All definitions concerning this class of Devices
.RETURNS The iolist
.REMARKS Simply returns the local iolist address...
------------------------------------------------------------*/
{
  return(&this_dev);
}
