/*
 * Copyright (c) 1998,2001
 *	Traakan, Inc., Los Altos, CA
 *	All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Project:  NDMJOB
 * Ident:    $Id: $
 *
 * Description:
 *	This contains code fragments common between the
 *	O/S (Operating System) portions of NDMJOBLIB.
 *
 *	This file is #include'd by the O/S specific ndmos_*.c
 *	file, and fragments are selected by #ifdef's.
 *
 *	There are four major portions:
 *	1) Misc support routines: password check, local info, etc
 *	2) Non-blocking I/O support routines
 *	3) Tape interfacs ndmos_tape_xxx()
 *	4) OS Specific NDMP request dispatcher which intercepts
 *	   requests implemented here, such as SCSI operations
 *	   and system configuration queries.
 */




/*
 * CONFIG SUPPORT
 ****************************************************************
 */

#ifdef NDMOS_COMMON_SYNC_CONFIG_INFO
/*
 * Get local info. Supports NDMPx_CONFIG_GET_HOST_INFO,
 * NDMP3_CONFIG_GET_SERVER_INFO, and NDMPx_CONFIG_GET_SCSI_INFO.
 */
void
ndmos_sync_config_info (struct ndm_session *sess)
{
	static struct utsname	unam;
	static char		osbuf[100];
	static char		idbuf[30];
	static char		revbuf[100];
	char			obuf[5];

	if (sess->config_info.hostname) {
		/* already set */
		return;
	}

	obuf[0] = (char)(NDMOS_ID >> 24);
	obuf[1] = (char)(NDMOS_ID >> 16);
	obuf[2] = (char)(NDMOS_ID >> 8);
	obuf[3] = (char)(NDMOS_ID >> 0);
	obuf[4] = 0;

	uname (&unam);
	sprintf (idbuf, "%lu", gethostid());
	/*
	 * give CONTROL via NDMPv2 a chance to recognize this
	 * implementation (no ndmp2_config_get_server).
	 */
	sprintf (osbuf, "%s (running %s from %s)",
			unam.sysname,
			NDMOS_CONST_PRODUCT_NAME,
			NDMOS_CONST_VENDOR_NAME);

	sess->config_info.hostname = unam.nodename;
	sess->config_info.os_type = osbuf;
	sess->config_info.os_vers = unam.release;
	sess->config_info.hostid = idbuf;

	sess->config_info.vendor_name = NDMOS_CONST_VENDOR_NAME;
	sess->config_info.product_name = NDMOS_CONST_PRODUCT_NAME;

	sprintf (revbuf, "%s LIB:%d.%d/%s OS:%s (%s)",
		NDMOS_CONST_PRODUCT_REVISION,
		NDMJOBLIB_VERSION, NDMJOBLIB_RELEASE,
		NDMOS_CONST_NDMJOBLIB_REVISION,
		NDMOS_CONST_NDMOS_REVISION,
		obuf);

	sess->config_info.revision_number = revbuf;

	/* best effort; note that this loads scsi and tape config */
	ndmcfg_load (sess->param.config_file_name, &sess->config_info);
}
#endif /* NDMOS_COMMON_SYNC_CONFIG_INFO */




/*
 * AUTHENTICATION SUPPORT
 ****************************************************************
 */

#ifdef NDMOS_COMMON_OK_NAME_PASSWORD
/*
 * Determine whether the clear-text account name and password
 * are valid. Supports NDMPx_CONNECT_CLIENT_AUTH requests.
 */
int
ndmos_ok_name_password (struct ndm_session *sess, char *name, char *pass)
{
	if (strcmp (name, "ndmp") != 0)
		return 0;

	if (strcmp (pass, "ndmp") != 0)
		return 0;

	return 1;	/* OK */
}
#endif /* NDMOS_COMMON_OK_NAME_PASSWORD */




#ifdef NDMOS_COMMON_MD5
/*
 * MD5 authentication support
 *
 * See ndml_md5.c
 */

int
ndmos_get_md5_challenge (struct ndm_session *sess)
{
	ndmmd5_generate_challenge (sess->md5_challenge);
	sess->md5_challenge_valid = 1;
	return 0;
}

int
ndmos_ok_name_md5_digest (struct ndm_session *sess,
  char *name, char digest[16])
{
	if (strcmp (name, "ndmp") != 0)
		return 0;

	if (!ndmmd5_ok_digest (sess->md5_challenge, "ndmp", digest))
		return 0;

	return 1;	/* OK */
}
#endif /* NDMOS_COMMON_MD5 */



#ifdef NDMOS_COMMON_NONBLOCKING_IO_SUPPORT
/*
 * NON-BLOCKING I/O SUPPORT
 ****************************************************************
 * As support non-blocking I/O for NDMCHAN, condition different
 * types of file descriptors to not block.
 */

void
ndmos_condition_listen_socket (struct ndm_session *sess, int sock)
{
	int		flag;

	flag = 1;
	setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (void*)&flag, sizeof flag);
}

void
ndmos_condition_control_socket (struct ndm_session *sess, int sock)
{
	/* nothing */
}

void
ndmos_condition_image_stream_socket (struct ndm_session *sess, int sock)
{
	fcntl (sock, F_SETFL, O_NONBLOCK);
	signal (SIGPIPE, SIG_IGN);
}

void
ndmos_condition_pipe_fd (struct ndm_session *sess, int fd)
{
	fcntl (fd, F_SETFL, O_NONBLOCK);
	signal (SIGPIPE, SIG_IGN);
}
#endif /* NDMOS_COMMON_NONBLOCKING_IO_SUPPORT */




#ifdef NDMOS_COMMON_TAPE_INTERFACE
#ifndef NDMOS_OPTION_NO_TAPE_AGENT
#ifndef NDMOS_OPTION_TAPE_SIMULATOR
/*
 * TAPE INTERFACE
 ****************************************************************
 * These interface to the O/S specific tape drivers and subsystem.
 * They must result in functionality equivalent to the reference
 * tape simulator. The NDMP TAPE model is demanding, and it is
 * often necessary to workaround the native device driver(s)
 * to achieve NDMP TAPE model conformance.
 *
 * It's easy to test this ndmos_tape_xxx() implementation
 * using the ndmjob(1) command in test-tape conformance mode.
 * The tape simulator passes this test. To test this implementation,
 * rebuild ndmjob, then use this command:
 *
 *	ndmjob -o test-tape -T. -f /dev/whatever
 *
 * These ndmos_tape_xxx() interfaces must maintain the tape state
 * (sess->tape_agent.tape_state). In particular, the position
 * information (file_num and blockno) must be accurate at all
 * times. A typical workaround is to maintain these here rather
 * than relying on the native device drivers. Another workaround
 * is to implement NDMP MTIO operations using repeated native MTIO
 * operations with count=1, then interpret the results and errors
 * to maintain accurate position and residual information.
 *
 * Workarounds in this implementation (please keep this updated):
 *
 */

int
ndmos_tape_initialize (struct ndm_session *sess)
{
	return -1;
}

void
ndmos_tape_sync_state (struct ndm_session *sess)
{
	the_tape_state.error = NDMP9_DEV_NOT_OPEN_ERR;
}

ndmp9_error
ndmos_tape_open (struct ndm_session *sess, char *name, int will_write)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

ndmp9_error
ndmos_tape_close (struct ndm_session *sess)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

ndmp9_error
ndmos_tape_write (struct ndm_session *sess, char *data,
  unsigned long count, unsigned long * done_count)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

ndmp9_error
ndmos_tape_read (struct ndm_session *sess, char *data,
  unsigned long count, unsigned long * done_count)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

ndmp9_error
ndmos_tape_mtio (struct ndm_session *sess, ndmp9_tape_mtio_op op,
  unsigned long count, unsigned long * done_count)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

ndmp9_error
ndmos_tape_execute_cdb (struct ndm_session *sess,
  ndmp9_execute_cdb_request *request,
  ndmp9_execute_cdb_reply *reply)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

#endif /* !NDMOS_OPTION_TAPE_SIMULATOR */
#else /* !NDMOS_OPTION_NO_TAPE_AGENT */
/* tape interfaces implemented in ndma_tape_simulator.c */
#endif /* !NDMOS_OPTION_NO_TAPE_AGENT */
#endif /* NDMOS_COMMON_TAPE_INTERFACE */


#ifdef NDMOS_COMMON_ROBOT_INTERFACE
#ifndef NDMOS_OPTION_NO_ROBOT_AGENT
#ifndef NDMOS_OPTION_ROBOT_SIMULATOR

/* ndmos_robot_* functions here */

#endif /* !NDMOS_OPTION_ROBOT_SIMULATOR */
#else /* !NDMOS_OPTION_NO_ROBOT_AGENT */
/* robot interfaces implemented in ndma_robot_simulator.c */
#endif /* !NDMOS_OPTION_NO_ROBOT_AGENT */
#endif /* NDMOS_COMMON_ROBOT_INTERFACE */


#ifdef NDMOS_COMMON_SCSI_INTERFACE
#ifndef NDMOS_OPTION_NO_ROBOT_AGENT	/* Surrounds all SCSI intfs */
#ifndef NDMOS_OPTION_ROBOT_SIMULATOR
/*
 * SCSI INTERFACE
 ****************************************************************
 */

int
ndmos_scsi_initialize (struct ndm_session *sess)
{
	return -1;
}

void
ndmos_scsi_sync_state (struct ndm_session *sess)
{
	sess->robot_acb.scsi_state.error = NDMP9_DEV_NOT_OPEN_ERR;
}

ndmp9_error
ndmos_scsi_open (struct ndm_session *sess, char *name)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

ndmp9_error
ndmos_scsi_close (struct ndm_session *sess)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

/* deprecated */
ndmp9_error
ndmos_scsi_set_target (struct ndm_session *sess)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}


ndmp9_error
ndmos_scsi_reset_device (struct ndm_session *sess)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

/* deprecated */
ndmp9_error
ndmos_scsi_reset_bus (struct ndm_session *sess)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

ndmp9_error
ndmos_scsi_execute_cdb (struct ndm_session *sess,
  ndmp9_execute_cdb_request *request,
  ndmp9_execute_cdb_reply *reply)
{
	return NDMP9_NOT_SUPPORTED_ERR;
}

#endif /* NDMOS_OPTION_ROBOT_SIMULATOR */
#endif /* NDMOS_OPTION_NO_ROBOT_AGENT	Surrounds all SCSI intfs */
#endif /* NDMOS_COMMON_SCSI_INTERFACE */




#ifdef NDMOS_COMMON_DISPATCH_REQUEST
/*
 * ndmos_dispatch_request() -- O/S Specific Agent Dispatch Request (ADR)
 ****************************************************************
 * Some NDMP requests can only be handled as O/S specific portions,
 * and are implemented here.
 *
 * The more generic NDMP requests may be re-implemented here rather
 * than modifying the main body of code. Extensions to the NDMP protocol
 * may also be implemented here. Neither is ever a good idea beyond
 * experimentation. The structures in ndmagents.h provide for O/S
 * specific extensions. Such extensions are #define'd in the ndmos_xxx.h.
 *
 * The return value from ndmos_dispatch_request() tells the main
 * dispatcher, ndma_dispatch_request(), whether or not the request
 * was intercepted. ndmos_dispatch_request() is called after basic
 * reply setup is done (message headers and buffers), but before
 * the request is interpretted.
 */

#ifndef I_HAVE_DISPATCH_REQUEST

/*
 * If we're not intercepting, keep it simple
 */
int
ndmos_dispatch_request (struct ndm_session *sess,
  struct ndmp_xa_buf *xa, struct ndmconn *ref_conn)
{
	return -1;	/* not intercepted */
}

#else /* !I_HAVE_DISPATCH_REQUEST */

/*
 * The following fragment is here for reference.
 * If the O/S module intercepts requests, copy
 * all this into the module source file and
 * #undef NDMOS_COMMON_DISPATCH_REQUEST.
 */

extern struct ndm_dispatch_version_table ndmos_dispatch_version_table[];

int
ndmos_dispatch_request (struct ndm_session *sess,
  struct ndmp_xa_buf *xa, struct ndmconn *ref_conn)
{
	struct ndm_dispatch_request_table *drt;
	unsigned	protocol_version = ref_conn->protocol_version;
	unsigned	msg = xa->request.header.message;
	int		rc;

	drt = ndma_drt_lookup (ndmos_dispatch_version_table,
					protocol_version, msg);

	if (!drt) {
		return -1;	/* not intercepted */
	}

	/*
	 * Replicate the ndma_dispatch_request() permission checks
	 */
	if (!sess->conn_open
	 && !(drt->flags & NDM_DRT_FLAG_OK_NOT_CONNECTED)) {
		xa->reply.header.error = NDMP0_PERMISSION_ERR;
		return 0;
	}

	if (!sess->conn_authorized
	 && !(drt->flags & NDM_DRT_FLAG_OK_NOT_AUTHORIZED)) {
		xa->reply.header.error = NDMP9_NOT_AUTHORIZED_ERR;
		return 0;
	}

	rc = (*drt->dispatch_request)(sess, xa, ref_conn);

	if (rc < 0) {
		xa->reply.header.error = NDMP0_NOT_SUPPORTED_ERR;
	}

	return 0;
}




/*
 * Dispatch Version Table and Dispatch Request Tables (DVT/DRT)
 ****************************************************************
 */

struct ndm_dispatch_request_table ndmos_dispatch_request_table_v0[] = {
   {0}
};

#ifndef NDMOS_OPTION_NO_NDMP2
struct ndm_dispatch_request_table ndmos_dispatch_request_table_v2[] = {
   {0}
};
#endif /* !NDMOS_OPTION_NO_NDMP2 */

#ifndef NDMOS_OPTION_NO_NDMP3
struct ndm_dispatch_request_table ndmos_dispatch_request_table_v3[] = {
   {0}
};
#endif /* !NDMOS_OPTION_NO_NDMP3 */

#ifndef NDMOS_OPTION_NO_NDMP4
struct ndm_dispatch_request_table ndmos_dispatch_request_table_v4[] = {
   {0}
};
#endif /* !NDMOS_OPTION_NO_NDMP4 */


struct ndm_dispatch_version_table ndmos_dispatch_version_table[] = {
	{ 0,		ndmos_dispatch_request_table_v0 },
#ifndef NDMOS_OPTION_NO_NDMP2
	{ NDMP2VER,	ndmos_dispatch_request_table_v2 },
#endif /* !NDMOS_OPTION_NO_NDMP2 */
#ifndef NDMOS_OPTION_NO_NDMP3
	{ NDMP3VER,	ndmos_dispatch_request_table_v3 },
#endif /* !NDMOS_OPTION_NO_NDMP2 */
#ifndef NDMOS_OPTION_NO_NDMP4
	{ NDMP4VER,	ndmos_dispatch_request_table_v4 },
#endif /* !NDMOS_OPTION_NO_NDMP4 */
	{ -1 }
};
#endif /* !I_HAVE_DISPATCH_REQUEST */
#endif /* NDMOS_COMMON_DISPATCH_REQUEST */
