/*===========================================================================
  Copyright (C) 1995 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		UNIX Command 
.NAME		tapeserver
.LANGUAGE	C
.AUTHOR		Carlos Guirao [ESO-IPG]
.CATEGORY	Remote interface to i/o on devices like tapes / OD / etc
.ENVIRONMENT	Unix
.COMMENTS	The tapeserver is command which provides a interface 
		between the osu module in a remote host and a iodev 
		module in the local host.

		Is based in the osu interface (and iodev module list) for 
		performing basic i/o to/from magnetic tape and optical
		devices.

		The server is spawned by the "inetd" daemon which takes
		cares of stablishing the communication with the client.

.REMARKS	The server and the client assume the INET service defined
		in the file /etc/services: mtape (the service is typically
		in the range 1024-2048.)

.VERSION 1.1	900212: Implementation (for SUN4/110 and SparcStation1)
.VERSION 1.2	900418: ioclose needs two parameters ...
.VERSION 2.1	910905: Using inetd to spawn the tapeserver.
.VERSION 2.2	910919: Implementing something similar to syslog()
			The server must be compiled with flag -DNO_SYSLOG
.VERSION 2.3	920122: Returns DEVCAPFILE not found when applies. CG.
.VERSION 2.4	920219: Also character ':' as separator. CG.
.VERSION 2.5	920422: Improving the access to log-files. CG.
.VERSION 2.6	920728: LOG_CONS & LOG_LOCAL0-7 not defined in Ultrix. CG.
.VERSION 3.1	940624: Using XDR interface (machine indepentent). CG.

 051021		last modif

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

/*
 * Define _POSIX_SOURCE to indicate
 * that this is a POSIX program
 * Except for SUN & GNU-SUN & HP-UX & OSF & AIX & ULTRIX, 
 * because the POSIX definition
 * couses undefines (eg. u_long) in socket includes.
 */
#if !defined(__hpux) && !defined(sun) && !defined(__sun) && !defined(__osf__) && !defined(_AIX) && !defined(ultrix) && !defined(__sgi) && !defined(__linux__) && !defined(__FreeBSD__) && !defined(__Darwin__)
#define _POSIX_SOURCE 1
#endif

#include <sys/types.h>		/* getuid() */
#include <proto_os.h>
#include <stdio.h>
#include <stdlib.h>
#include <rpc/rpc.h>		/* XDR definitions */
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#ifndef NO_SYSLOG
#include <syslog.h>		/* syslog(); */
#endif /* NO_SYSLOG */
#include <sys/time.h>
#include <oserror.h>		/* Midas error definitions */
#include <osudef.h>
#include <osnull.h>		/* Contains NULL_DEV definition	*/
#include <devstat.h>		/* Contains devstat structure 	*/
#include <filedef.h>		/* Contains READ, WRITE, etc...	*/
#include <rtape.h>		/* Contains command and result struct */
#include <atype.h>

#define IODEV0  iodevg		/* IODEV0: First "iodevg" in the list */

#ifdef NO_SYSLOG
/*
 * For those systems without the "syslog()" routine, I use something
 * equivalent with "fprintf()" to send messages to a logfile.
 */
#include <string.h>
static char mesg[160];		/* message to be compound */
#define imsg "%s mtaped: "	/* message header */
#ifdef LOG_DEBUG		/* Remove LOG_DEBUG definition. Just in case */
# undef LOG_DEBUG
# undef LOG_INFO
# undef LOG_ERR
#endif
#define LOG_DEBUG 0
#define LOG_ERR   1
#define LOG_INFO  2
static FILE *LOG;
#define LOGFILE "/var/log/mtaped.log"

#ifndef DEBUG
#define syslog(a,b,c) { strcpy(mesg,imsg); \
	if ( a != LOG_DEBUG ) { \
	fprintf(LOG,strcat(mesg,b),gettime(),c); \
	fflush(LOG);}  }
#else
#define syslog(a,b,c) { strcpy(mesg,imsg); \
	fprintf(LOG,strcat(mesg,b),gettime(),c); \
	fflush(LOG); }
#endif /* DEBUG */

#endif /* NO_SYSLOG */

#ifndef LOG_CONS	/* Ultrix has no idea about LOG_CONS */
# define LOG_CONS 0
#endif /* LOG_CONS */

#ifndef LOG_LOCAL0	/* Ultrix has no idea about facility in openlog() */
# define LOG_LOCAL0 0
#endif /* LOG_LOCAL0 */

char *osmsg();

#define NULL_PTR(x)	(x *)0
#define RET_ERROR(a,b) { oserror=(a); oserrmsg=(b); return(-1); }
#define P_ERROR  { syslog(LOG_ERR,"ERROR: %s\n",osmsg());  }
#define PEXIT(x) { syslog(LOG_ERR,"ERROR: %s\n",osmsg()); ospexit((x)); } 
#define DEVCAPFILE "/usr/local/lib/devcap.dat" /* tape file description */

#define ERR_SYNC 	msg0
#define ERR_CMD		msg1
#define ERR_CLASS	msg2
#define ERR_DEVENT	msg3
#define ERR_OPENDEV	msg4
#define ERR_XDR		msg5
static char msg0[] = "SERVER:No synchronism with tape client";
static char msg1[] = "SERVER:Tape command unexpected";
static char msg2[] = "SERVER:Class does not exist";
static char msg3[] = "SERVER:Entry unknown in DEVCAPFILE";
static char msg4[] = "SERVER:Cannot open DEVCAPFILE";
static char msg5[] = "SERVER:XDR library error";

/* 
 * Definition of Functions 
 */
#define ioinfo(f,b,fn,bn)	(ops[U_INFO])(f,b,fn,bn)
#define ioopen(f,m,d)		(ops[U_OPEN])(f,m,d)
#define ioclose(f,o)		(ops[U_CLOSE])(f,o)
#define ioread(f,b,l)		(ops[U_READ])(f,b,l) 
#define iowrite(f,b,l)		(ops[U_WRITE])(f,b,l) 
#define iorew(f)		(ops[U_REWIND])(f) 
#define ioeom(f)		(ops[U_EOM])(f) 
#define ioweof(f,n)		(ops[U_WEOF])(f,n)	 
#define iofsf(f,n)		(ops[U_FMF])(f,n)	
#define iobsf(f,n)		(ops[U_FMB])(f,n)	
#define iofsr(f,n)		(ops[U_BMF])(f,n)	
#define iobsr(f,n)		(ops[U_BMB])(f,n)	
#define iosread(f,s,ss,b,l)	(ops[U_SREAD])(f,s,ss,b,l) 
#define ioswrite(f,s,ss,b,l)	(ops[U_SWRITE])(f,s,ss,b,l)	


/* 
 * This structure, created for each device, allows to know 
 * the current position on any device
 */
typedef int (*FCT_PTR)();		/* Pointer to function */
FCT_PTR ops[U_MAX+1];			/* Available Operations	*/

#define TAPE_CLOSED	0
#define TAPE_OPENED	1

static struct tapestat {
	int 	status;
	int 	fd;
	int 	mode;
	char	lhost[MAX_HOST_NAME];
	char	devname[MAX_DEVICE_NAME];
	char	ruser[MAX_USER_NAME];
	char	klass[MAX_STRING];
	} TAPE;

struct info {
	struct osustat stat;
	int filenum;
	long blkno;
	};

/*
 * XDR routines
 */
static XDR xdrs_in, xdrs_out;
extern bool_t xdr_command();
extern bool_t xdr_osustat();
extern bool_t xdr_result();

/*=====================================================================
		Internal Routines
*====================================================================*/

#ifdef NO_SYSLOG
static char *gettime()
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE get the current time.
.RETURNS Returns a character string of the form:
        Sep 16 01:03:52 1973
----------------------------------------------------------------------*/
{
    struct timeval tp;
    struct timezone tzp;
    struct tm *tm;
    struct tm *localtime();
    static char timenow[80];
    char *date;
    gettimeofday(&tp,&tzp);
    tm = localtime(&tp.tv_sec);
    date = asctime(tm);
    (void)strncpy(timenow,date,(strchr(date,'\n') - date ));
    return(timenow);
}
#endif /* NO_SYSLOG */

static int osuerror(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Set Error Message
.RETURNS -1
----------------------------------------------------------------------*/
int op;	/* IN: The operation to execute */
{
	static char msg[60] = "Function not available: ";
	register char *pm, *pl;
	static 	char *text[] = {
		/* U_INFO  */ "info",
    		/* U_OPEN  */ "open",
    		/* U_CLOSE */ "close",
    		/* U_READ  */ "read",
    		/* U_WRITE */ "write",
    		/* U_REWIND*/ "rewind",
    		/* U_SREAD */ "sector_read",
    		/* U_SWRITE*/ "sector_write",
    		/* U_WEOF  */ "write_EOF",	
    		/* U_FMF   */ "file_move_forward",
    		/* U_FMB   */ "file_move_backwards",
    		/* U_BMF   */ "block_move_forward",	
    		/* U_BMB   */ "block_move_backwards",
    		/* U_EOM   */ "to_EOMedia"
	};

  oserror = -1;
  oserrmsg = msg;
  pm = &msg[24];

  	/* Append the signification of the function */
  for(pl = text[op]; *pl; pl++) 	*(pm++) = *pl;
  *pm = '\0';

  return(-1);
}

	/* Declare here the various error functions */
static int err0()  { return(osuerror(0));}
static int err1()  { return(osuerror(1));}
static int err2()  { return(osuerror(2));}
static int err3()  { return(osuerror(3));}
static int err4()  { return(osuerror(4));}
static int err5()  { return(osuerror(5));}
static int err6()  { return(osuerror(6));}
static int err7()  { return(osuerror(7));}
static int err8()  { return(osuerror(8));}
static int err9()  { return(osuerror(9));}
static int err10() { return(osuerror(10));}
static int err11() { return(osuerror(11));}
static int err12() { return(osuerror(12));}
static int err13() { return(osuerror(13));}

static FCT_PTR err_fct[1+U_MAX] = 
	{ err0, err1, err2, err3, err4, err5, err6, err7, err8, err9,
	err10, err11, err12, err13};


static int ret_client(resul,error,msg)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Open device.
.REMARKS Also a write channel is opened for sending results back to client
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int resul;		/* IN: return code to send to client */
int error;		/* IN: oserror code to send to client */
char *msg;		/* IN: oserrmsg to send to client if any */
{
	struct result res;
	
	res.ret = resul;
	res.oserror = error;

	/* 
	 * Check if there is any message to send to client 
 	 */
	if (msg != NULL_PTR(char)) res.nobyt = strlen(msg)+1;
	else  res.nobyt = 0;

	syslog(LOG_DEBUG,"return res.ret=%x\n",res.ret);
	syslog(LOG_DEBUG,"return res.error=%x\n",res.oserror);
	syslog(LOG_DEBUG,"return res.nobyt=%x\n",res.nobyt);
	/* 
	 * Send results to client 
	 */
	if (!xdr_result(&xdrs_out, &res)) RET_ERROR(-1,ERR_XDR);
	fflush(stdout);

	/* Send message to client if any */
	if ( res.nobyt > 0 ) {
		syslog(LOG_DEBUG,"return msg=%s\n", msg);
		if (!xdr_string(&xdrs_out, &msg,MAX_MSG)) RET_ERROR(-1,ERR_XDR);
		fflush(stdout);
		}
	return(0);
}

static int rewind_tape(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Rewind device.
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	int fd;

	fd = op->param[0];

	/* Execute command and return result to client */
	if ( iorew(fd) < 0 ) {
		if ( ret_client(-1,oserror,osmsg()) < 0 ) return(-1);
		return(0);
                }

	if ( ret_client(0,0,NULL_PTR(char)) < 0 ) return(-1);
	return(0);
}	

static int skip_eom(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Skip to End of Media.
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	int fd;
	int ret;

	fd = op->param[0];

	/* Execute command and return result to client */
	if ( (ret = ioeom(fd)) < 0 ) {
		if ( ret_client(-1,oserror,osmsg()) < 0 ) return(-1);
		return(0);
                }
	if ( ret_client(ret,0,NULL_PTR(char)) < 0 ) return(-1);
	return(0);
}	

static int read_info(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Read information about device.
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	int nobyt;
	int fd, nbytes;
        struct osustat s;
        int filenum;
        long blkno;
 
	fd = op->param[0];		/* file descriptor for tape */
	nbytes = op->param[1];		/* num. of bytes to send back */

	/* Execute command and return error to client if any */
	if ( ioinfo(fd, &s, &filenum, &blkno) < 0 ) {
		if (ret_client(-1,oserror,osmsg()) < 0 ) return(-1);
		return(0);
                }

	if (ret_client(0,0,NULL_PTR(char)) < 0 )  return(-1);

	/* Send info to client */
	if (!xdr_osustat(&xdrs_out, &s))  RET_ERROR(-1,ERR_XDR);
	if (!xdr_int(&xdrs_out, &filenum)) RET_ERROR(-1,ERR_XDR);
	if (!xdr_long(&xdrs_out, &blkno)) RET_ERROR(-1,ERR_XDR);
	fflush(stdout);

	return(0);
}	

static struct iolist *findclass(aclass)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Retrieve the class name
.RETURNS Pointer to found class / NULL
.REMARKS 
----------------------------------------------------------------------*/
	char	*aclass;	/* IN: Class to look for */
{
	struct iolist *plist, *IODEV0();
	struct iolist *(*def)();
	char	*p, *q;
	
	/* Follow the linked list of iolist's */

   plist = (*IODEV0)();
   for (def = IODEV0; def; def = plist->next)
   {
	plist = (*def)();
	for(q=aclass, p=plist->klass; (*p == *q) && (*p); p++, q++) ;
	if ((*q == '\0') && (*p == '\0'))	break;
   }
   if (!def)	/* Class Not Found */
   	plist = NULL_PTR(struct iolist);
   
   return(plist);
}

static int scandev(fd, dev)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Get Capabilities in opened DEVCAPFILE
.RETURNS 0  / -1
.REMARKS Recursive call if `dc=' is found.
----------------------------------------------------------------------*/
	int	fd;	/* IN: DEVCAPFILE file descriptor */
	char	*dev;	/* IN: Device name */
{
	int	i;
	char	buf[133];	/* One line of DEVCAPFILE */
	char	*p, *q;

	/* Be sure dev is terminated with Null */

  for (p = dev; isgraph(*p); p++)	;
  *p = '\0';

  osaseek(fd, 0L, FILE_START);
  while (osaread(fd, buf, sizeof(buf)) >= 0)
  {
	if (buf[0] == '#')	continue;
	for (p = buf, q = dev; *q && (*p == *q); p++, q++)	;
	if(*q == '\0')		goto found_dev;
  }
  return(-1); 

  found_dev:
	/* Here when device is found. Get interesting items, i.e.
	 * cl = class_name
	 */
  while(*p)
  {	
  	while (isspace(*p))	p++;	/* Skip blanks 	*/
	if(!*p)	continue;		/* Empty line	*/

	if (*p == '\\')		/* There is a continuation. Read Next record */
	{
		next_record:
  		if (osaread(fd, buf, sizeof(buf)) < 0)	buf[0] = '\0';
		if (buf[0] == '#')	/* It's a comment */
			goto next_record;	
		p = buf;
  		if (isspace(buf[0]))	continue;
  		goto terminated;
	}
	if (oscomp(p, "dc=", 3) == 0)		/* Fetch another name */
		return(scandev(fd, p+3));
	if (oscomp(p, "cl=", 3) == 0) 	/* Class	*/
		{ for (i=0, p+=3; (i<sizeof(TAPE.klass)-1)&&(isgraph(*p)); i++, p++)
			TAPE.klass[i] =  *p; 
		  TAPE.klass[i] = '\0';
		}
	while (isgraph(*p))	p++;		/* Look for space */
  }

  terminated:
  return(0);
}

static int getclass()
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Retrieve the class for name TAPE.class, to retrieve operators.
         
.RETURNS 0 / -1 (class doesn't exist)
.REMARKS Functions added in FCB
----------------------------------------------------------------------*/
{
	struct 	iolist *plist;
	int	i;
	OPITEM	*pop;
	
   plist = findclass(TAPE.klass);
   if (!plist)	
   {
   	oserror = -1, oserrmsg = ERR_CLASS;
   	return(-1);
   }
   
   /*
   ** We have just to insert the functions in the list
   */

   for (pop = plist->oplist, i = plist->nop; --i >= 0; pop++)
   	ops[pop->opid] = pop->opf;
   
   return(0);
}

static int getdev(dev)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Get Capabilities from DEVCAPFILE.
.RETURNS 0 / -1
.REMARKS Relevant Cap's are used to update the current fcb
----------------------------------------------------------------------*/
	char	*dev;	/* IN: Device name */
{
	int	fd;
	char	*oshenv();

	/*  Open DEVCAPFILE in read_only mode	*/

  fd = osaopen(DEVCAPFILE, READ);
  if (fd < 0){
	oserror = -1; oserrmsg = ERR_OPENDEV;
	return(-1);
  }
  
	/* Scan the file for the device name, expand dc= references */
	 
  if (scandev(fd, dev) < 0) {
	oserror = -1; oserrmsg = ERR_DEVENT;
	return(-1);
  }

	/* Finally Close DEVCAPFILE	*/

  osaclose(fd);
  return(0);
}

/*=====================================================================
		Callable Routines
 *=====================================================================*/

char *osuname(f)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Retrieve name of current unit (when f = -1)
.RETURNS Name / NULL pointer if failed
----------------------------------------------------------------------*/
	int f;		/* IN: The unit number	*/
{ 
    if (f != -1)
	return((char *)0);	/* Bad unit number...	*/

    return(TAPE.devname);
}

int osumode(f)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Retrieve opening mode of current unit (when f = -1)
.RETURNS Mode / -1 when error
----------------------------------------------------------------------*/
	int f;		/* IN: The unit number	*/
{ 
    if (f != -1)
	return(-1);	/* Bad unit number...	*/

    return(TAPE.mode);
}

int osugrep (class_name, item)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Get the item function in a class of devices
.RETURNS 0 / -1
----------------------------------------------------------------------*/
	char	*class_name;	/* IN: Class of Devices (NULL for any) */
	OPITEM	*item;		/* OUT: op_code + Function Pointer */
{ 
	struct iolist *plist, *IODEV0();
	struct iolist *(*def)();
	int	i;
	OPITEM	*pop;
	
   			
   plist = (*IODEV0)();
   for (def = IODEV0; def; def = plist->next)
   {
   	if (!class_name)	/* Class not specified: look to next one */
   		plist = (*def)();
   	else	plist = findclass(class_name);
   	if (!plist)	   	return(-1);

		/* Retrieve the item in list */
	
   	for (pop = plist->oplist, i = plist->nop; 
		(--i >= 0) && (pop->opid != item->opid);  pop++)  ;

	if (i >= 0)		/* I found the relevant item... */
	{ 	item->opf = pop->opf;
		return(0);
	}
   	if (class_name)		break;
	
   }
   return(-1);
}

static int open_device(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Open device.
.REMARKS Also a write channel is opened for sending results back to client
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	static char *channame[2];
	static char name[80];
	register char *p, *q;
	register int l;
	char *devname, isnulldev;
	char *pname, *puser;

	/* 
	 * Open needs a second message with device name, and 
	 * remote host 
	 */

	pname = name;
	puser = TAPE.ruser;
	/* Read strings */
        if (!xdr_string(&xdrs_in, &pname, MAX_HOST_NAME+MAX_DEVICE_NAME))
		RET_ERROR(-1,ERR_XDR);
        if (!xdr_string(&xdrs_in, &puser, MAX_USER_NAME))
		RET_ERROR(-1,ERR_XDR);

	/* Copy open parameters to TAPE structure */
	TAPE.mode	= op->param[0];

	/* Convert NULL device name if necessary */
	devname = name;
	if (!devname) devname = "";
	isnulldev = !*devname;		/* Is zero for NULL device */
	if (isnulldev)	devname = NULL_DEV;

	oserror = 0;
	oserrmsg = NULL_PTR(char);
	
	syslog(LOG_DEBUG,"Remote user is %s\n",TAPE.ruser);

	/* 
	 * Try to guess the device class from name;
	 * If device name contains a D, assume "disk" class
	 */
	l = strlen(name);
	if (isnulldev)		p = "dumb";
	else {		/* Look for a 'd' for Disk, otherwise Tape */
		q = name + oscbloc(name, l, '/') + 1;
		l -= (q - name);	/* Keep filename */
		if ((oscbloc(q, l, 'd') >= 0) || (oscbloc(q, l, 'D') >= 0))
			p = "disk";
		else 	p = "generic";
	}
	oscopy(TAPE.klass, p, 1+strlen(p));

	/* 
	 * Fill all entries in class structure ops to point osuerror 
	 * routine 
	 */
	for (l = 0; l < sizeof(ops)/sizeof(ops[0]); l++)
		ops[l] = err_fct[l];


	/* Fill class from DEVCAPFILE */
	getdev(name);
	syslog(LOG_DEBUG,"Class is %s\n",TAPE.klass);
	
	/* Fill Local Host, and Device name */
	for (q = TAPE.lhost , p = name ; (*p) && (*p != '!') && (*p != ':'); p++, q++)  
		*q = *p ;
	*q = '\0';
	if (*p == '!' || *p == ':')  p++; 
	else    p = name;
        for (q = TAPE.devname; *p; p++, q++)       *q = *p;
        *q = '\0';

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


	/* Get class routines for TAPE.class name */
	if ( getclass() < 0 ) {
		(void)ret_client(-1,oserror,osmsg());
		return(-1);
		}

	/* Open specified device and returns error if not possible */
	if ( (TAPE.fd = ioopen(TAPE.devname, TAPE.mode, 0)) < 0 ) {
		P_ERROR;
		(void)ret_client(-1,oserror,osmsg());
		return(-1);	/* Device needs to be opened again */
                }

	/* Return open result to client */
	if ( ret_client(TAPE.fd,0,NULL_PTR(char)) < 0 ) return(-1);
	TAPE.status	= TAPE_OPENED;
	return(0);
}	

static int close_device(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Close device.
.REMARKS Also write channel is closed.
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	/* Close device and return result to client */
	if ( ioclose(op->param[0], 0) < 0 ) {
		P_ERROR;
		if ( ret_client(-1,oserror,oserrmsg) < 0 ) return(-1);
		return(0);
                }
	if ( ret_client(0,0,NULL_PTR(char)) < 0 ) return(-1);
	TAPE.status = TAPE_CLOSED;

	xdr_destroy(&xdrs_in);
	xdr_destroy(&xdrs_out);
	return(0);
}	

static int read_data(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Read data from device and return it to client.
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	int nobyt, ret;
	char *buff;
	int fd, nbytes;

	/* Get parameters for read operation */
	fd = op->param[0];		/* file descriptor for tape */
	nbytes = op->param[1];		/* num. bytes to read from tape */

	if (nbytes <= 0) {		/* return 0 bytes read */
		if ( ret_client(0,0,NULL_PTR(char)) < 0 )  return(-1);
		return(0);
		}

	/*
	** First of all allocate buffer for data to be read. 
	** If an error allocating memory then send error to client 
	** and return -1.
	*/
	if ( (buff = malloc((size_t)nbytes)) == NULL_PTR(char)) {
		oserror = ENOMEM;
		(void)ret_client(-1,oserror,osmsg());	/* Send error */
		return(-1);
		}
	
	/* Read data from tape and send result to client */
	if ( (nobyt = ioread(fd, buff, nbytes)) < 0 ) { /* Send error */
		free(buff);
		if ( ret_client(-1,oserror,osmsg()) < 0 ) return(-1);
		return(0);
                }
	if ( ret_client(nobyt,0,NULL_PTR(char)) < 0 )  return(-1);


	/* Send data read to client */
	if (nobyt > 0) {
		syslog(LOG_DEBUG,"read_data, sending=%x\n",nobyt);
		if (!xdr_bytes(&xdrs_out, &buff,(u_int *)&nobyt,nbytes)) 
			RET_ERROR(-1,ERR_XDR);
		fflush(stdout);
		}

	free(buff);

	return(0);
}	

static int write_data(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write data to device.
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	int nobyt, ret;
	char *buff;
	int fd, nbytes;

	fd = op->param[0];		/* file descriptor for tape */
	nbytes = op->param[1];		/* num. bytes to write on tape */

	if ( nbytes <= 0 ) {		/* return 0 bytes writen */
		if ( ret_client(0,0,NULL_PTR(char)) < 0 ) return(-1);
		return(0);
		}

        /*
        ** First of all allocate buffer for data to be written.
        ** If an error allocating memory then send error to client
        ** and return -1.
        */
	if ( (buff = malloc((size_t)nbytes)) == NULL_PTR(char)) {
		oserror = ENOMEM;
		(void)ret_client(-1,oserror,osmsg());
		return(-1);
		}

	/* Read data from client into a buffer*/
	if (!xdr_bytes(&xdrs_in,&buff,(u_int *)&ret,nbytes)) RET_ERROR(-1,ERR_XDR);
        if ( ret != nbytes ) RET_ERROR(-1,ERR_SYNC);
  
	/* Write buffer on tape, and return error to client if any */
	if ( (nobyt = iowrite(fd, buff, nbytes)) < 0 ) {
		free(buff);
		if ( ret_client(-1,oserror,osmsg()) < 0 ) return(-1);
		return(0);
                }

	free(buff);

	/* Send number of bytes written to client */
	if ( ret_client(nobyt,0,NULL_PTR(char)) < 0 ) return(-1);

	return(0);
}	

static int write_eof(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Write Tape Mark (End of file) in device.
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	int ret;
	int fd, ntm;

	fd = op->param[0];
	ntm = op->param[1];

	/* Execute command and return result to client */
	if ( (ret = ioweof(fd, ntm)) < 0 ) {
		if ( ret_client(-1,oserror,osmsg()) < 0 ) return(-1);
		return(0);
                }
	if ( ret_client(ret,0,NULL_PTR(char)) < 0 ) return(-1);

	return(0);
}	

static int skip_fsf(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Skip forward a file.
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	int ret;
	int fd, ntm;

	fd = op->param[0];
	ntm = op->param[1];

	/* Execute command and return result to client */
	if ( (ret = iofsf(fd, ntm)) < 0 ) {
		if ( ret_client(-1,oserror,osmsg()) < 0 ) return(-1);
		return(0);
                }

	if ( ret_client(ret,0,NULL_PTR(char)) < 0 ) return(-1);
	return(0);
}	

static int skip_bsf(op)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Skip backward a file.
.RETURNS 0 (success) / -1 (failure)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct command *op;		/* IN: Command structure */
{
	int ret;
	int fd, ntm;

	fd = op->param[0];
	ntm = op->param[1];

	/* Execute command and return result to client */
	if ( (ret = iobsf(fd, ntm)) < 0 ) {
		if ( ret_client(-1,oserror,osmsg()) < 0 ) return(-1);
		return(0);
                }

	if ( ret_client(ret,0,NULL_PTR(char)) < 0 ) return(-1);
	return(0);
}	

main()
{
    register int ret;
    struct command op;
    struct sockaddr_in name;
    int namelen = sizeof(name);

#ifdef NO_SYSLOG
    /*
     * Here I open first the logfile, if error I open the "/dev/console" 
     * There must be a place where to drop my messages, or?
     */ 
    if ( (LOG=fopen(LOGFILE,"a")) == NULL ) 
    	LOG=fopen("/dev/console","w");
#else
    /* 
     * Identify ourselves to the system log deamon 
     */
    openlog("tape_server",(LOG_PID | LOG_CONS), LOG_LOCAL0);
#endif /* NO_SYSLOG */

    if (getpeername(0, (struct sockaddr *)&name, &namelen) < 0) {
	oserror = errno;
	PEXIT(2);
	}
    else syslog(LOG_INFO, "Connection from %s\n", inet_ntoa(name.sin_addr));

    xdrstdio_create(&xdrs_in, stdin, XDR_DECODE);
    xdrstdio_create(&xdrs_out, stdout, XDR_ENCODE);
    /* Read untill getting an error or client connection closed */
    for (;;) {

	/* 
	 * Read a command 
	 */
	if (!xdr_command(&xdrs_in, &op)) RET_ERROR(-1,ERR_XDR);

	/* 
	 * Analize command 
 	 */
        syslog(LOG_DEBUG, "Read command %d\n", op.cmd);
        syslog(LOG_DEBUG, "Param[0] %x\n", op.param[0]);
        syslog(LOG_DEBUG, "Param[1] %x\n", op.param[1]);
	switch (op.cmd) {
	case OPEN_DEVICE:
		ret=open_device(&op);
		break;
	case CLOSE_DEVICE:
		ret=close_device(&op);
		syslog(LOG_INFO,"Connection closed for %s\n",TAPE.ruser);
		ospexit(0);
		break;
	case READ_DATA:
		ret=read_data(&op);
		break;
	case WRITE_DATA:
		ret=write_data(&op);
		break;
	case WRITE_EOF:
		ret=write_eof(&op);
		break;
	case SKIP_FSF:
		ret=skip_fsf(&op);
		break;
	case SKIP_BSF:
		ret=skip_bsf(&op);
		break;
	case REWIND:
		ret=rewind_tape(&op);
		break;
	case SKIP_EOM:
		ret=skip_eom(&op);
		break;
	case READ_INFO:
		ret=read_info(&op);
		break;
	default:
		oserror = -1;
		oserrmsg = ERR_CMD;
		ret = -1;
		break;
	}
	/* If anny error, close write channel */
	if ( ret < 0 )  PEXIT(3);
	}
}
