OpenSolaris_b135/cmd/ndmpd/ndmp/ndmpd_comm.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * BSD 3 Clause License
 *
 * Copyright (c) 2007, The Storage Networking Industry Association.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 	- Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 *
 * 	- 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.
 *
 *	- Neither the name of The Storage Networking Industry Association (SNIA)
 *	  nor the names of its contributors may be used to endorse or promote
 *	  products derived from this software without specific prior written
 *	  permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
 */
/* Copyright (c) 2007, The Storage Networking Industry Association. */
/* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ndmpd.h"
#include "ndmpd_common.h"

#define	NDMP_PROC_ERR	-1
#define	NDMP_PROC_MSG	1
#define	NDMP_PROC_REP	0
#define	NDMP_PROC_REP_ERR	2

/*
 * The ndmp connection version can be set through command line. If command line
 * is not specified it will be set from the ndmp SMF version property.
 */
int ndmp_ver = 0;

/*
 * The NDMP listening port number
 */
int ndmp_port = 0;

/*
 * Restore path mechanism definition
 * 0 means partial path restore and
 * 1 means full path restore.
 * Refer to NDMP_FULL_RESTORE_PATH for partial path and full path definition.
 */
int ndmp_full_restore_path = 1;

/*
 * Do we support Direct Access Restore?
 */
int ndmp_dar_support = 0;

/*
 * ndmp_connection_t handler function
 */
static ndmpd_file_handler_func_t connection_file_handler;

extern ndmp_handler_t ndmp_msghdl_tab[];

static int ndmp_readit(void *connection_handle,
    caddr_t buf,
    int len);
static int ndmp_writeit(void *connection_handle,
    caddr_t buf,
    int len);
static int ndmp_recv_msg(ndmp_connection_t *connection);
static int ndmp_process_messages(ndmp_connection_t *connection,
    boolean_t reply_expected);
static ndmp_msg_handler_t *ndmp_get_handler(ndmp_connection_t *connection,
    ndmp_message message);
static boolean_t ndmp_check_auth_required(ndmp_message message);
static ndmp_handler_t *ndmp_get_interface(ndmp_message message);
void *ndmpd_worker(void *ptarg);

#ifdef	lint
bool_t
xdr_ndmp_header(XDR *xdrs, ndmp_header *objp)
{
	xdrs = xdrs;
	objp = objp;
	return (0);
}
#endif	/* lint */

/*
 * ndmp_create_connection
 *
 * Allocate and initialize a connection structure.
 *
 * Parameters:
 *   handler_tbl (input) - message handlers.
 *
 * Returns:
 *   NULL - error
 *   connection pointer
 *
 * Notes:
 *   The returned connection should be destroyed using
 *   ndmp_destroy_connection().
 */
ndmp_connection_t *
ndmp_create_connection(void)
{
	ndmp_connection_t *connection;

	connection = ndmp_malloc(sizeof (ndmp_connection_t));
	if (connection == NULL)
		return (NULL);

	connection->conn_sock = -1;
	connection->conn_my_sequence = 0;
	connection->conn_authorized = FALSE;
	connection->conn_eof = FALSE;
	connection->conn_msginfo.mi_body = 0;
	connection->conn_version = ndmp_ver;
	connection->conn_client_data = 0;
	(void) mutex_init(&connection->conn_lock, 0, NULL);
	connection->conn_xdrs.x_ops = 0;

	xdrrec_create(&connection->conn_xdrs, 0, 0, (caddr_t)connection,
	    ndmp_readit, ndmp_writeit);

	if (connection->conn_xdrs.x_ops == 0) {
		NDMP_LOG(LOG_DEBUG, "xdrrec_create failed");
		(void) mutex_destroy(&connection->conn_lock);
		(void) close(connection->conn_sock);
		free(connection);
		return (0);
	}
	return ((ndmp_connection_t *)connection);
}

/*
 * ndmp_destroy_connection
 *
 * Shutdown a connection and release allocated resources.
 *
 * Parameters:
 *   connection_handle (Input) - connection handle.
 *
 * Returns:
 *   void
 */
void
ndmp_destroy_connection(ndmp_connection_t *connection_handle)
{
	ndmp_connection_t *connection = (ndmp_connection_t *)connection_handle;

	if (connection->conn_sock >= 0) {
		(void) mutex_destroy(&connection->conn_lock);
		(void) close(connection->conn_sock);
		connection->conn_sock = -1;
	}
	xdr_destroy(&connection->conn_xdrs);
	free(connection);
}


/*
 * ndmp_close
 *
 * Close a connection.
 *
 * Parameters:
 *   connection_handle (Input) - connection handle.
 *
 * Returns:
 *   void
 */
void
ndmp_close(ndmp_connection_t *connection_handle)
{
	ndmp_connection_t *connection = (ndmp_connection_t *)connection_handle;

	ndmpd_audit_disconnect(connection);
	if (connection->conn_sock >= 0) {
		(void) mutex_destroy(&connection->conn_lock);
		(void) close(connection->conn_sock);
		connection->conn_sock = -1;
	}
	connection->conn_eof = TRUE;

	/*
	 * We should close all the tapes that are used by this connection.
	 * In some cases the ndmp client opens a tape, but does not close the
	 * tape and closes the connection.
	 */
	ndmp_open_list_release(connection_handle);
}

/*
 * ndmp_start_worker
 *
 * Initializes and starts a ndmp_worker thread
 */
int
ndmp_start_worker(ndmpd_worker_arg_t *argp)
{
	pthread_attr_t tattr;
	int rc;

	(void) pthread_attr_init(&tattr);
	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
	rc = pthread_create(NULL, &tattr, ndmpd_worker, (void *)argp);
	(void) pthread_attr_destroy(&tattr);
	return (rc);
}

/*
 * ndmp_run
 *
 * Creates a socket for listening and accepting connections
 * from NDMP clients.
 * Accepts connections and passes each connection to the connection
 * handler.
 *
 * Parameters:
 *   port (input)   -  NDMP server port.
 *		     If 0, the port number will be retrieved from
 *		     the network service database. If not found there,
 *		     the default NDMP port number (from ndmp.x)
 *		     will be used.
 *   handler (input) - connection handler function.
 *
 * Returns:
 *   This function normally never returns unless there's error.
 *   -1 : error
 *
 * Notes:
 *   This function does not return unless encountering an error
 *   related to the listen socket.
 */
int
ndmp_run(ulong_t port, ndmp_con_handler_func_t con_handler_func)
{
	int ns;
	int on, tmp;
	int server_socket;
	unsigned int ipaddr;
	struct sockaddr_in sin;
	int flag = 1;
	ndmpd_worker_arg_t *argp;

	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = htons(port);

	if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		NDMP_LOG(LOG_DEBUG, "Socket error: %m");
		return (-1);
	}

	on = 1;
	(void) setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
	    (char *)&on, sizeof (on));


	if (bind(server_socket, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
		NDMP_LOG(LOG_DEBUG, "bind error: %m");
		(void) close(server_socket);
		return (-1);
	}
	if (listen(server_socket, 5) < 0) {
		NDMP_LOG(LOG_DEBUG, "listen error: %m");
		(void) close(server_socket);
		return (-1);
	}

	for (; ; ) {
		if ((ns = tcp_accept(server_socket, &ipaddr)) < 0) {
			NDMP_LOG(LOG_DEBUG, "tcp_accept error: %m");
			continue;
		}

		NDMP_LOG(LOG_DEBUG, "connection fd: %d", ns);

		/*
		 * 'css' and 'crs' in the following env variables stand for:
		 * 'connection send size' and 'connection receive size'.
		 */
		tmp = atoi((const char *)ndmpd_get_prop_default(NDMP_SOCKET_CSS,
		    "65"));
		if (tmp <= 0)
			tmp = 65;
		NDMP_LOG(LOG_DEBUG, "css: %d_KB", tmp);
		ndmp_set_socket_snd_buf(ns, tmp * KILOBYTE);

		tmp = atoi((const char *)ndmpd_get_prop_default(NDMP_SOCKET_CRS,
		    "80"));
		if (tmp <= 0)
			tmp = 80;
		NDMP_LOG(LOG_DEBUG, "crs: %d_KB", tmp);
		ndmp_set_socket_rcv_buf(ns, tmp * KILOBYTE);

		ndmp_set_socket_nodelay(ns);
		(void) setsockopt(ns, SOL_SOCKET, SO_KEEPALIVE, &flag,
		    sizeof (flag));

		if ((argp = ndmp_malloc(sizeof (ndmpd_worker_arg_t))) != NULL) {
			argp->nw_sock = ns;
			argp->nw_ipaddr = ipaddr;
			argp->nw_con_handler_func = con_handler_func;
			(void) ndmp_start_worker(argp);
		}
	}
}

/*
 * ndmpd_worker thread
 *
 * Parameters:
 *   argp (input) - structure containing socket and handler function
 *
 * Returns:
 *   0 - successful connection.
 *  -1 - error.
 */
void *
ndmpd_worker(void *ptarg)
{
	int sock;
	ndmp_connection_t *connection;
	ndmpd_worker_arg_t *argp = (ndmpd_worker_arg_t *)ptarg;

	if (!argp)
		return ((void *)-1);

	NS_INC(trun);
	sock = argp->nw_sock;

	if ((connection = ndmp_create_connection()) == NULL) {
		(void) close(sock);
		free(argp);
		exit(1);
	}

	/* initialize auditing session */
	if (adt_start_session(&connection->conn_ah, NULL, 0) != 0) {
		free(argp);
		return ((void *)-1);
	}

	((ndmp_connection_t *)connection)->conn_sock = sock;
	(*argp->nw_con_handler_func)(connection);
	(void) adt_end_session(connection->conn_ah);
	ndmp_destroy_connection(connection);
	NS_DEC(trun);

	free(argp);
	return (NULL);
}

/*
 * ndmp_process_requests
 *
 * Reads the next request message into the stream buffer.
 * Processes messages until the stream buffer is empty.
 *
 * Parameters:
 *   connection_handle (input) - connection handle.
 *
 * Returns:
 *   0 - 1 or more messages successfully processed.
 *  -1 - error; connection no longer established.
 */
int
ndmp_process_requests(ndmp_connection_t *connection_handle)
{
	int rv;
	ndmp_connection_t *connection = (ndmp_connection_t *)connection_handle;

	(void) mutex_lock(&connection->conn_lock);
	rv = 0;
	if (ndmp_process_messages(connection, FALSE) < 0)
		rv = -1;

	(void) mutex_unlock(&connection->conn_lock);
	return (rv);
}


/*
 * ndmp_send_request
 *
 * Send an NDMP request message.
 *
 * Parameters:
 *   connection_handle (input) - connection pointer.
 *   message (input) - message number.
 *   err (input)  - error code to place in header.
 *   request_data (input) - message body.
 *   reply (output) - reply message. If 0, reply will be
 *				discarded.
 *
 * Returns:
 *   0	- successful send.
 *  -1	- error.
 *   otherwise - error from reply header.
 *
 * Notes:
 *   - The reply body is only returned if the error code is NDMP_NO_ERR.
 */
int
ndmp_send_request(ndmp_connection_t *connection_handle, ndmp_message message,
    ndmp_error err, void *request_data, void **reply)
{
	ndmp_connection_t *connection = (ndmp_connection_t *)connection_handle;
	ndmp_header header;
	ndmp_msg_handler_t *handler;
	int r;
	struct timeval time;

	/* Lookup info necessary for processing this request. */
	if (!(handler = ndmp_get_handler(connection, message))) {
		NDMP_LOG(LOG_DEBUG, "Sending message 0x%x: not supported",
		    message);
		return (-1);
	}
	(void) gettimeofday(&time, 0);

	header.sequence = ++(connection->conn_my_sequence);
	header.time_stamp = time.tv_sec;
	header.message_type = NDMP_MESSAGE_REQUEST;
	header.message = message;
	header.reply_sequence = 0;
	header.error = err;

	connection->conn_xdrs.x_op = XDR_ENCODE;
	if (!xdr_ndmp_header(&connection->conn_xdrs, &header)) {
		NDMP_LOG(LOG_DEBUG,
		    "Sending message 0x%x: encoding request header", message);
		(void) xdrrec_endofrecord(&connection->conn_xdrs, 1);
		return (-1);
	}
	if (err == NDMP_NO_ERR && handler->mh_xdr_request && request_data) {
		if (!(*handler->mh_xdr_request)(&connection->conn_xdrs,
		    request_data)) {
			NDMP_LOG(LOG_DEBUG,
			    "Sending message 0x%x: encoding request body",
			    message);
			(void) xdrrec_endofrecord(&connection->conn_xdrs, 1);
			return (-1);
		}
	}
	(void) xdrrec_endofrecord(&connection->conn_xdrs, 1);

	if (handler->mh_xdr_reply == 0) {
		NDMP_LOG(LOG_DEBUG, "handler->mh_xdr_reply == 0");
		return (0);
	}

	/*
	 * Process messages until the reply to this request has been
	 * processed.
	 */
	for (; ; ) {
		r = ndmp_process_messages(connection, TRUE);

		/* connection error? */
		if (r < 0)
			return (-1);

		/* no reply received? */
		if (r == 0)
			continue;

		/* reply received? */
		if (r == 1) {
			if (message !=
			    connection->conn_msginfo.mi_hdr.message) {
				NDMP_LOG(LOG_DEBUG,
				    "Received unexpected reply 0x%x",
				    connection->conn_msginfo.mi_hdr.message);
				ndmp_free_message(connection_handle);
				return (-1);
			}
			if (reply != NULL)
				*reply = connection->conn_msginfo.mi_body;
			else
				ndmp_free_message(connection_handle);

			return (connection->conn_msginfo.mi_hdr.error);
		}
		/* error handling reply */

		return (-1);
	}
}


/*
 * ndmp_send_request_lock
 *
 * A wrapper for ndmp_send_request with locks.
 *
 * Parameters:
 *   connection_handle (input) - connection pointer.
 *   message (input) - message number.
 *   err (input) - error code to place in header.
 *   request_data (input) - message body.
 *   reply (output) - reply message. If 0, reply will be
 *				discarded.
 *
 * Returns:
 *   0	- successful send.
 *  -1	- error.
 *   otherwise - error from reply header.
 *
 * Notes:
 *   - The reply body is only returned if the error code is NDMP_NO_ERR.
 */
int
ndmp_send_request_lock(ndmp_connection_t *connection_handle,
    ndmp_message message, ndmp_error err, void *request_data, void **reply)
{
	int rv;
	ndmp_connection_t *connection = (ndmp_connection_t *)connection_handle;

	(void) mutex_lock(&connection->conn_lock);

	rv = ndmp_send_request(connection_handle, message, err, request_data,
	    reply);
	(void) mutex_unlock(&connection->conn_lock);
	return (rv);
}


/*
 * ndmp_send_response
 *
 * Send an NDMP reply message.
 *
 * Parameters:
 *   connection_handle  (input)  - connection pointer.
 *   err	       (input)  - error code to place in header.
 *   reply	     (input)  - reply message body.
 *
 * Returns:
 *   0 - successful send.
 *  -1 - error.
 *
 * Notes:
 *   - The body is only sent if the error code is NDMP_NO_ERR.
 */
int
ndmp_send_response(ndmp_connection_t *connection_handle, ndmp_error err,
    void *reply)
{
	ndmp_connection_t *connection = (ndmp_connection_t *)connection_handle;
	ndmp_header header;
	struct timeval time;

	(void) gettimeofday(&time, 0);

	header.sequence = ++(connection->conn_my_sequence);
	header.time_stamp = time.tv_sec;
	header.message_type = NDMP_MESSAGE_REPLY;
	header.message = connection->conn_msginfo.mi_hdr.message;
	header.reply_sequence = connection->conn_msginfo.mi_hdr.sequence;
	header.error = err;

	connection->conn_xdrs.x_op = XDR_ENCODE;
	if (!xdr_ndmp_header(&connection->conn_xdrs, &header)) {
		NDMP_LOG(LOG_DEBUG, "Sending message 0x%x: "
		    "encoding reply header",
		    header.message);
		(void) xdrrec_endofrecord(&connection->conn_xdrs, 1);
		return (-1);
	}
	if (err == NDMP_NO_ERR &&
	    connection->conn_msginfo.mi_handler->mh_xdr_reply &&
	    reply) {
		if (!(*connection->conn_msginfo.mi_handler->mh_xdr_reply)(
		    &connection->conn_xdrs, reply)) {
			NDMP_LOG(LOG_DEBUG,
			    "Sending message 0x%x: encoding reply body",
			    header.message);
			(void) xdrrec_endofrecord(&connection->conn_xdrs, 1);
			return (-1);
	}
	}
	(void) xdrrec_endofrecord(&connection->conn_xdrs, 1);
	return (0);
}

/*
 * ndmp_free_message
 *
 * Free the memory of NDMP message body.
 *
 * Parameters:
 *   connection_handle  (input)  - connection pointer.
 *
 * Returns:
 *   void
 *
 */
void
ndmp_free_message(ndmp_connection_t *connection_handle)
{
	ndmp_connection_t *connection = (ndmp_connection_t *)connection_handle;

	if (connection->conn_msginfo.mi_handler == NULL ||
	    connection->conn_msginfo.mi_body == NULL)
		return;

	connection->conn_xdrs.x_op = XDR_FREE;
	if (connection->conn_msginfo.mi_hdr.message_type ==
	    NDMP_MESSAGE_REQUEST) {
		if (connection->conn_msginfo.mi_handler->mh_xdr_request)
			(*connection->conn_msginfo.mi_handler->mh_xdr_request)(
			    &connection->conn_xdrs,
			    connection->conn_msginfo.mi_body);
	} else {
		if (connection->conn_msginfo.mi_handler->mh_xdr_reply)
			(*connection->conn_msginfo.mi_handler->mh_xdr_reply)(
			    &connection->conn_xdrs,
			    connection->conn_msginfo.mi_body);
	}

	(void) free(connection->conn_msginfo.mi_body);
	connection->conn_msginfo.mi_body = 0;
}

/*
 * ndmp_get_fd
 *
 * Returns the connection file descriptor.
 *
 * Parameters:
 *   connection_handle (input) - connection handle
 *
 * Returns:
 *   >=0 - file descriptor.
 *   -1  - connection not open.
 */
int
ndmp_get_fd(ndmp_connection_t *connection_handle)
{
	return (((ndmp_connection_t *)connection_handle)->conn_sock);
}


/*
 * ndmp_set_client_data
 *
 * This function provides a means for the library client to provide
 * a pointer to some user data structure that is retrievable by
 * each message handler via ndmp_get_client_data.
 *
 * Parameters:
 *   connection_handle  (input) - connection handle.
 *   client_data	(input) - user data pointer.
 *
 * Returns:
 *   void
 */
void
ndmp_set_client_data(ndmp_connection_t *connection_handle, void *client_data)
{
	((ndmp_connection_t *)connection_handle)->conn_client_data =
	    client_data;
}


/*
 * ndmp_get_client_data
 *
 * This function provides a means for the library client to provide
 * a pointer to some user data structure that is retrievable by
 * each message handler via ndmp_get_client_data.
 *
 * Parameters:
 *   connection_handle (input) - connection handle.
 *
 * Returns:
 *   client data pointer.
 */
void *
ndmp_get_client_data(ndmp_connection_t *connection_handle)
{
	return (((ndmp_connection_t *)connection_handle)->conn_client_data);
}


/*
 * ndmp_set_version
 *
 * Sets the NDMP protocol version to be used on the connection.
 *
 * Parameters:
 *   connection_handle  (input) - connection handle.
 *   version	   (input) - protocol version.
 *
 * Returns:
 *   void
 */
void
ndmp_set_version(ndmp_connection_t *connection_handle, ushort_t version)
{
	((ndmp_connection_t *)connection_handle)->conn_version = version;
}


/*
 * ndmp_get_version
 *
 * Gets the NDMP protocol version in use on the connection.
 *
 * Parameters:
 *   connection_handle  (input) - connection handle.
 *   version	   (input) - protocol version.
 *
 * Returns:
 *   void
 */
ushort_t
ndmp_get_version(ndmp_connection_t *connection_handle)
{
	return (((ndmp_connection_t *)connection_handle)->conn_version);
}


/*
 * ndmp_set_authorized
 *
 * Mark the connection as either having been authorized or not.
 *
 * Parameters:
 *   connection_handle  (input) - connection handle.
 *   authorized	(input) - TRUE or FALSE.
 *
 * Returns:
 *   void
 */
void
ndmp_set_authorized(ndmp_connection_t *connection_handle, boolean_t authorized)
{
	((ndmp_connection_t *)connection_handle)->conn_authorized = authorized;
}


/*
 * ndmpd_main
 *
 * NDMP main function called from main().
 *
 * Parameters:
 *   void
 *
 * Returns:
 *   void
 */
void
ndmpd_main(void)
{
	char *propval;

	ndmp_load_params();
	if (ndmp_log_open_file() != 0) {
		NDMP_LOG(LOG_ERR,
		    "Could not open log file properly.");
	}

	/*
	 * Find ndmp port number to be used. If ndmpd is run as command line
	 * and port number is supplied, use that port number. If port number is
	 * is not supplied, find out if ndmp port property is set. If ndmp
	 * port property is set, use that port number otherwise use the defaule
	 * port number.
	 */
	if (ndmp_port == 0) {
		if ((propval = ndmpd_get_prop(NDMP_TCP_PORT)) == NULL ||
		    *propval == 0)
			ndmp_port = NDMPPORT;
		else
			ndmp_port = strtol(propval, 0, 0);
	}

	if (ndmp_run(ndmp_port, connection_handler) == -1)
		perror("ndmp_run ERROR");

	ndmp_log_close_file();
}

/*
 * connection_handler
 *
 * NDMP connection handler.
 * Waits for, reads, and processes NDMP requests on a connection.
 *
 * Parameters:
 *   connection (input) - connection handle.
 *
 * Return:
 *   void
 */
void
connection_handler(ndmp_connection_t *connection)
{
	static int conn_id = 1;
	ndmpd_session_t session;
	ndmp_notify_connected_request req;
	int connection_fd;

	(void) memset(&session, 0, sizeof (session));
	session.ns_connection = connection;
	session.ns_eof = FALSE;
	/*
	 * The 'protocol_version' must be 1 at first, since the client talks
	 * to the server in version 1 then they can move to a higher
	 * protocol version.
	 */
	session.ns_protocol_version = ndmp_ver;

	session.ns_scsi.sd_is_open = -1;
	session.ns_scsi.sd_devid = -1;

	session.ns_scsi.sd_sid = 0;
	session.ns_scsi.sd_lun = 0;
	session.ns_scsi.sd_valid_target_set = 0;
	(void) memset(session.ns_scsi.sd_adapter_name, 0,
	    sizeof (session.ns_scsi.sd_adapter_name));

	session.ns_tape.td_fd = -1;
	session.ns_tape.td_sid = 0;
	session.ns_tape.td_lun = 0;
	(void) memset(session.ns_tape.td_adapter_name, 0,
	    sizeof (session.ns_tape.td_adapter_name));
	session.ns_tape.td_pos = 0;
	session.ns_tape.td_record_count = 0;
	session.ns_file_handler_list = 0;

	(void) ndmpd_data_init(&session);
	ndmpd_file_history_init(&session);
	if (ndmpd_mover_init(&session) < 0)
		return;

	if (ndmp_lbr_init(&session) < 0)
		return;

	/*
	 * Setup defaults here. The init functions can not set defaults
	 * since the init functions are called by the stop request handlers
	 * and client set variables need to persist across data operations.
	 */
	session.ns_mover.md_record_size = MAX_RECORD_SIZE;

	ndmp_set_client_data(connection, (void *)&session);

	req.reason = NDMP_CONNECTED;
	req.protocol_version = ndmp_ver;
	req.text_reason = "";

	if (ndmp_send_request_lock(connection, NDMP_NOTIFY_CONNECTION_STATUS,
	    NDMP_NO_ERR, (void *)&req, 0) < 0) {
		NDMP_LOG(LOG_DEBUG, "Connection terminated");
		return;
	}
	connection_fd = ndmp_get_fd(connection);

	NDMP_LOG(LOG_DEBUG, "connection_fd: %d", connection_fd);

	/*
	 * Add the handler function for the connection to the DMA.
	 */
	if (ndmpd_add_file_handler(&session, (void *)&session, connection_fd,
	    NDMPD_SELECT_MODE_READ, HC_CLIENT, connection_file_handler) != 0) {
		NDMP_LOG(LOG_DEBUG, "Could not register session handler.");
		return;
	}

	/*
	 * Register the connection in the list of active connections.
	 */
	if (ndmp_connect_list_add(connection, &conn_id) != 0) {
		NDMP_LOG(LOG_ERR,
		    "Could not register the session to the server.");
		(void) ndmpd_remove_file_handler(&session, connection_fd);
		return;
	}

	session.hardlink_q = hardlink_q_init();

	while (session.ns_eof == FALSE)
		(void) ndmpd_select(&session, TRUE, HC_ALL);

	hardlink_q_cleanup(session.hardlink_q);

	NDMP_LOG(LOG_DEBUG, "Connection terminated");

	(void) ndmpd_remove_file_handler(&session, connection_fd);

	if (session.ns_scsi.sd_is_open != -1) {
		NDMP_LOG(LOG_DEBUG, "scsi.is_open: %d",
		    session.ns_scsi.sd_is_open);
		(void) ndmp_open_list_del(session.ns_scsi.sd_adapter_name,
		    session.ns_scsi.sd_sid, session.ns_scsi.sd_lun);
	}
	if (session.ns_tape.td_fd != -1) {
		NDMP_LOG(LOG_DEBUG, "tape.fd: %d", session.ns_tape.td_fd);
		(void) close(session.ns_tape.td_fd);
		(void) ndmp_open_list_del(session.ns_tape.td_adapter_name,
		    session.ns_tape.td_sid, session.ns_tape.td_lun);
	}
	ndmpd_mover_shut_down(&session);
	ndmp_lbr_cleanup(&session);
	ndmpd_data_cleanup(&session);
	ndmpd_file_history_cleanup(&session, FALSE);
	ndmpd_mover_cleanup(&session);

	(void) ndmp_connect_list_del(connection);
}


/*
 * connection_file_handler
 *
 * ndmp_connection_t file handler function.
 * Called by ndmpd_select when data is available to be read on the
 * NDMP connection.
 *
 * Parameters:
 *   cookie (input) - session pointer.
 *   fd      (input) - connection file descriptor.
 *   mode    (input) - select mode.
 *
 * Returns:
 *   void.
 */
/*ARGSUSED*/
static void
connection_file_handler(void *cookie, int fd, ulong_t mode)
{
	ndmpd_session_t *session = (ndmpd_session_t *)cookie;

	if (ndmp_process_requests(session->ns_connection) < 0)
		session->ns_eof = TRUE;
}


/* ************* private functions *************************************** */

/*
 * ndmp_readit
 *
 * Low level read routine called by the xdrrec library.
 *
 * Parameters:
 *   connection (input) - connection pointer.
 *   buf	(input) - location to store received data.
 *   len	(input) - max number of bytes to read.
 *
 * Returns:
 *   >0 - number of bytes received.
 *   -1 - error.
 */
static int
ndmp_readit(void *connection_handle, caddr_t buf, int len)
{
	ndmp_connection_t *connection = (ndmp_connection_t *)connection_handle;

	len = read(connection->conn_sock, buf, len);
	if (len <= 0) {
		/* ndmp_connection_t has been closed. */
		connection->conn_eof = TRUE;
		return (-1);
	}
	return (len);
}

/*
 * ndmp_writeit
 *
 * Low level write routine called by the xdrrec library.
 *
 * Parameters:
 *   connection (input) - connection pointer.
 *   buf	(input) - location to store received data.
 *   len	(input) - max number of bytes to read.
 *
 * Returns:
 *   >0 - number of bytes sent.
 *   -1 - error.
 */
static int
ndmp_writeit(void *connection_handle, caddr_t buf, int len)
{
	ndmp_connection_t *connection = (ndmp_connection_t *)connection_handle;
	register int n;
	register int cnt;

	for (cnt = len; cnt > 0; cnt -= n, buf += n) {
		if ((n = write(connection->conn_sock, buf, cnt)) < 0) {
			connection->conn_eof = TRUE;
			return (-1);
		}
	}

	return (len);
}


/*
 * ndmp_recv_msg
 *
 * Read the next message.
 *
 * Parameters:
 *   connection (input)  - connection pointer.
 *   msg	(output) - received message.
 *
 * Returns:
 *   0 - Message successfully received.
 *   error number - Message related error.
 *  -1 - Error decoding the message header.
 */
static int
ndmp_recv_msg(ndmp_connection_t *connection)
{
	bool_t(*xdr_func) (XDR *, ...) = NULL;

	/* Decode the header. */
	connection->conn_xdrs.x_op = XDR_DECODE;
	(void) xdrrec_skiprecord(&connection->conn_xdrs);
	if (!xdr_ndmp_header(&connection->conn_xdrs,
	    &connection->conn_msginfo.mi_hdr))
		return (-1);

	/* Lookup info necessary for processing this message. */
	if ((connection->conn_msginfo.mi_handler = ndmp_get_handler(connection,
	    connection->conn_msginfo.mi_hdr.message)) == 0) {
		NDMP_LOG(LOG_DEBUG, "Message 0x%x not supported",
		    connection->conn_msginfo.mi_hdr.message);
		return (NDMP_NOT_SUPPORTED_ERR);
	}
	connection->conn_msginfo.mi_body = 0;

	if (connection->conn_msginfo.mi_hdr.error != NDMP_NO_ERR)
		return (0);

	/* Determine body type */
	if (connection->conn_msginfo.mi_hdr.message_type ==
	    NDMP_MESSAGE_REQUEST) {
		if (ndmp_check_auth_required(
		    connection->conn_msginfo.mi_hdr.message) &&
		    !connection->conn_authorized) {
			NDMP_LOG(LOG_DEBUG,
			    "Processing request 0x%x:connection not authorized",
			    connection->conn_msginfo.mi_hdr.message);
			return (NDMP_NOT_AUTHORIZED_ERR);
		}
		if (connection->conn_msginfo.mi_handler->mh_sizeof_request >
		    0) {
			xdr_func =
			    connection->conn_msginfo.mi_handler->mh_xdr_request;
			if (xdr_func == NULL) {
				NDMP_LOG(LOG_DEBUG,
				    "Processing request 0x%x: no xdr function "
				    "in handler table",
				    connection->conn_msginfo.mi_hdr.message);
				return (NDMP_NOT_SUPPORTED_ERR);
			}
			connection->conn_msginfo.mi_body = ndmp_malloc(
			    connection->conn_msginfo.mi_handler->
			    mh_sizeof_request);
			if (connection->conn_msginfo.mi_body == NULL)
				return (NDMP_NO_MEM_ERR);

			(void) memset(connection->conn_msginfo.mi_body, 0,
			    connection->conn_msginfo.mi_handler->
			    mh_sizeof_request);
		}
	} else {
		if (connection->conn_msginfo.mi_handler->mh_sizeof_reply > 0) {
			xdr_func =
			    connection->conn_msginfo.mi_handler->mh_xdr_reply;
			if (xdr_func == NULL) {
				NDMP_LOG(LOG_DEBUG,
				    "Processing reply 0x%x: no xdr function "
				    "in handler table",
				    connection->conn_msginfo.mi_hdr.message);
				return (NDMP_NOT_SUPPORTED_ERR);
			}
			connection->conn_msginfo.mi_body = ndmp_malloc(
			    connection->conn_msginfo.mi_handler->
			    mh_sizeof_reply);
			if (connection->conn_msginfo.mi_body == NULL)
				return (NDMP_NO_MEM_ERR);

			(void) memset(connection->conn_msginfo.mi_body, 0,
			    connection->conn_msginfo.mi_handler->
			    mh_sizeof_reply);
		}
	}

	/* Decode message arguments if needed */
	if (xdr_func) {
		if (!(*xdr_func)(&connection->conn_xdrs,
		    connection->conn_msginfo.mi_body)) {
			NDMP_LOG(LOG_DEBUG,
			    "Processing message 0x%x: error decoding arguments",
			    connection->conn_msginfo.mi_hdr.message);
			free(connection->conn_msginfo.mi_body);
			connection->conn_msginfo.mi_body = 0;
			return (NDMP_XDR_DECODE_ERR);
		}
	}
	return (0);
}

/*
 * ndmp_process_messages
 *
 * Reads the next message into the stream buffer.
 * Processes messages until the stream buffer is empty.
 *
 * This function processes all data in the stream buffer before returning.
 * This allows functions like poll() to be used to determine when new
 * messages have arrived. If only some of the messages in the stream buffer
 * were processed and then poll was called, poll() could block waiting for
 * a message that had already been received and read into the stream buffer.
 *
 * This function processes both request and reply messages.
 * Request messages are dispatched using the appropriate function from the
 * message handling table.
 * Only one reply messages may be pending receipt at a time.
 * A reply message, if received, is placed in connection->conn_msginfo
 * before returning to the caller.
 * Errors are reported if a reply is received but not expected or if
 * more than one reply message is received
 *
 * Parameters:
 *   connection     (input)  - connection pointer.
 *   reply_expected (output) - TRUE  - a reply message is expected.
 *			     FALSE - no reply message is expected and
 *			     an error will be reported if a reply
 *			     is received.
 *
 * Returns:
 *   NDMP_PROC_REP_ERR - 1 or more messages successfully processed,
 *   	error processing reply message.
 *   NDMP_PROC_REP_ERR - 1 or more messages successfully processed,
 *	reply seen.
 *   NDMP_PROC_REP_ERR - 1 or more messages successfully processed,
 * 	no reply seen.
 *   NDMP_PROC_REP_ERR - error; connection no longer established.
 *
 * Notes:
 *   If the peer is generating a large number of requests, a caller
 *   looking for a reply will be blocked while the requests are handled.
 *   This is because this function does not return until the stream
 *   buffer is empty.
 *   Code needs to be added to allow a return if the stream buffer
 *   is not empty but there is data available on the socket. This will
 *   prevent poll() from blocking and prevent a caller looking for a reply
 *   from getting blocked by a bunch of requests.
 */
static int
ndmp_process_messages(ndmp_connection_t *connection, boolean_t reply_expected)
{
	msg_info_t reply_msginfo;
	boolean_t reply_read = FALSE;
	boolean_t reply_error = FALSE;
	int err;

	NDMP_LOG(LOG_DEBUG, "reply_expected: %s",
	    reply_expected == TRUE ? "TRUE" : "FALSE");

	(void) memset((void *)&reply_msginfo, 0, sizeof (msg_info_t));

	do {
		(void) memset((void *)&connection->conn_msginfo, 0,
		    sizeof (msg_info_t));

		if ((err = ndmp_recv_msg(connection)) != NDMP_NO_ERR) {
			if (connection->conn_eof) {
				NDMP_LOG(LOG_DEBUG, "detected eof");
				return (NDMP_PROC_ERR);
			}
			if (err < 1) {
				NDMP_LOG(LOG_DEBUG, "error decoding header");

				/*
				 * Error occurred decoding the header.
				 * Don't send a reply since we don't know
				 * the message or if the message was even
				 * a request message.  To be safe, assume
				 * that the message was a reply if a reply
				 * was expected. Need to do this to prevent
				 * hanging ndmp_send_request() waiting for a
				 * reply.  Don't set reply_read so that the
				 * reply will be processed if it is received
				 * later.
				 */
				if (reply_read == FALSE)
					reply_error = TRUE;

				continue;
			}
			if (connection->conn_msginfo.mi_hdr.message_type
			    != NDMP_MESSAGE_REQUEST) {
				NDMP_LOG(LOG_DEBUG, "received reply: 0x%x",
				    connection->conn_msginfo.mi_hdr.message);

				if (reply_expected == FALSE ||
				    reply_read == TRUE)
					NDMP_LOG(LOG_DEBUG,
					    "Unexpected reply message: 0x%x",
					    connection->conn_msginfo.mi_hdr.
					    message);

				ndmp_free_message((ndmp_connection_t *)
				    connection);

				if (reply_read == FALSE) {
					reply_read = TRUE;
					reply_error = TRUE;
				}
				continue;
			}
			NDMP_LOG(LOG_DEBUG, "received request: 0x%x",
			    connection->conn_msginfo.mi_hdr.message);

			(void) ndmp_send_response((ndmp_connection_t *)
			    connection, err, NULL);
			ndmp_free_message((ndmp_connection_t *)connection);
			continue;
		}
		if (connection->conn_msginfo.mi_hdr.message_type
		    != NDMP_MESSAGE_REQUEST) {
			NDMP_LOG(LOG_DEBUG, "received reply: 0x%x",
			    connection->conn_msginfo.mi_hdr.message);

			if (reply_expected == FALSE || reply_read == TRUE) {
				NDMP_LOG(LOG_DEBUG,
				    "Unexpected reply message: 0x%x",
				    connection->conn_msginfo.mi_hdr.message);
				ndmp_free_message((ndmp_connection_t *)
				    connection);
				continue;
			}
			reply_read = TRUE;
			reply_msginfo = connection->conn_msginfo;
			continue;
		}
		NDMP_LOG(LOG_DEBUG, "received request: 0x%x",
		    connection->conn_msginfo.mi_hdr.message);

		/*
		 * The following is needed to catch an improperly constructed
		 * handler table or to deal with an NDMP client that is not
		 * conforming to the negotiated protocol version.
		 */
		if (connection->conn_msginfo.mi_handler->mh_func == NULL) {
			NDMP_LOG(LOG_DEBUG, "No handler for message 0x%x",
			    connection->conn_msginfo.mi_hdr.message);

			(void) ndmp_send_response((ndmp_connection_t *)
			    connection, NDMP_NOT_SUPPORTED_ERR, NULL);
			ndmp_free_message((ndmp_connection_t *)connection);
			continue;
		}
		/*
		 * Call the handler function.
		 * The handler will send any necessary reply.
		 */
		(*connection->conn_msginfo.mi_handler->mh_func) (connection,
		    connection->conn_msginfo.mi_body);

		ndmp_free_message((ndmp_connection_t *)connection);

	} while (xdrrec_eof(&connection->conn_xdrs) == FALSE &&
	    connection->conn_eof == FALSE);

	NDMP_LOG(LOG_DEBUG, "no more messages in stream buffer");

	if (connection->conn_eof == TRUE) {
		if (reply_msginfo.mi_body)
			free(reply_msginfo.mi_body);
		return (NDMP_PROC_ERR);
	}
	if (reply_error) {
		if (reply_msginfo.mi_body)
			free(reply_msginfo.mi_body);
		return (NDMP_PROC_REP_ERR);
	}
	if (reply_read) {
		connection->conn_msginfo = reply_msginfo;
		return (NDMP_PROC_MSG);
	}
	return (NDMP_PROC_REP);
}


/*
 * ndmp_get_interface
 *
 * Return the NDMP interface (e.g. config, scsi, tape) for the
 * specific message.
 *
 * Parameters:
 *   message (input) - message number.
 *
 * Returns:
 *   NULL - message not found.
 *   pointer to handler info.
 */
static ndmp_handler_t *
ndmp_get_interface(ndmp_message message)
{
	ndmp_handler_t *ni = &ndmp_msghdl_tab[(message >> 8) % INT_MAXCMD];

	if ((message & 0xff) >= ni->hd_cnt)
		return (NULL);

	/* Sanity check */
	if (ni->hd_msgs[message & 0xff].hm_message != message)
		return (NULL);

	return (ni);
}

/*
 * ndmp_get_handler
 *
 * Return the handler info for the specified NDMP message.
 *
 * Parameters:
 *   connection (input) - connection pointer.
 *   message (input) - message number.
 *
 * Returns:
 *   NULL - message not found.
 *   pointer to handler info.
 */
static ndmp_msg_handler_t *
ndmp_get_handler(ndmp_connection_t *connection, ndmp_message message)
{
	ndmp_msg_handler_t *handler = NULL;

	ndmp_handler_t *ni = ndmp_get_interface(message);
	int ver = connection->conn_version;

	if (ni)
		handler = &ni->hd_msgs[message & 0xff].hm_msg_v[ver - 2];

	return (handler);
}

/*
 * ndmp_check_auth_required
 *
 * Check if the connection needs to be authenticated before
 * this message is being processed.
 *
 * Parameters:
 *   message (input) - message number.
 *
 * Returns:
 *   TRUE - required
 *   FALSE - not required
 */
static boolean_t
ndmp_check_auth_required(ndmp_message message)
{
	boolean_t auth_req = FALSE;
	ndmp_handler_t *ni = ndmp_get_interface(message);

	if (ni)
		auth_req = ni->hd_msgs[message & 0xff].hm_auth_required;

	return (auth_req);
}

/*
 * tcp_accept
 *
 * A wrapper around accept for retrying and getting the IP address
 *
 * Parameters:
 *   listen_sock (input) - the socket for listening
 *   inaddr_p (output) - the IP address of peer connection
 *
 * Returns:
 *   socket for the accepted connection
 *   -1: error
 */
int
tcp_accept(int listen_sock, unsigned int *inaddr_p)
{
	struct sockaddr_in	sin;
	int			sock, i;
	int			try;

	for (try = 0; try < 3; try++) {
		i = sizeof (sin);
		sock = accept(listen_sock, (struct sockaddr *)&sin, &i);
		if (sock < 0) {
			continue;
		}
		*inaddr_p = sin.sin_addr.s_addr;
		return (sock);
	}
	return (-1);
}


/*
 * tcp_get_peer
 *
 * Get the peer IP address for a connection
 *
 * Parameters:
 *   sock (input) - the active socket
 *   inaddr_p (output) - the IP address of peer connection
 *   port_p (output) - the port number of peer connection
 *
 * Returns:
 *   socket for the accepted connection
 *   -1: error
 */
int
tcp_get_peer(int sock, unsigned int *inaddr_p, int *port_p)
{
	struct sockaddr_in sin;
	int i, rc;

	i = sizeof (sin);
	rc = getpeername(sock, (struct sockaddr *)&sin, &i);
	if (rc != 0)
		return (-1);

	if (inaddr_p)
		*inaddr_p = sin.sin_addr.s_addr;

	if (port_p)
		*port_p = ntohs(sin.sin_port);

	return (sock);

}

/*
 * gethostaddr
 *
 * Get the IP address string of the current host
 *
 * Parameters:
 *   void
 *
 * Returns:
 *   IP address
 *   NULL: error
 */
char *
gethostaddr(void)
{
	static char s[MAXHOSTNAMELEN];
	struct hostent *h;
	struct in_addr in;
	char *p;

	if (gethostname(s, sizeof (s)) == -1)
		return (NULL);

	if ((h = gethostbyname(s)) == NULL)
		return (NULL);

	p = h->h_addr_list[0];
	(void) memcpy(&in.s_addr, p, sizeof (in.s_addr));
	return (inet_ntoa(in));
}

/*
 * ndmpd_audit_backup
 *
 * Generate AUE_ndmp_backup audit record
 */
/*ARGSUSED*/
void
ndmpd_audit_backup(ndmp_connection_t *conn,
    char *path, int dest, char *local_path, int result)
{
	adt_event_data_t *event;

	if ((event = adt_alloc_event(conn->conn_ah, ADT_ndmp_backup)) == NULL) {
		NDMP_LOG(LOG_ERR, "Audit failure: %m.");
		return;
	}
	event->adt_ndmp_backup.source = path;

	if (dest == NDMP_ADDR_LOCAL) {
		event->adt_ndmp_backup.local_dest = local_path;
	} else {
		event->adt_ndmp_backup.remote_dest = conn->conn_sock;
	}

	if (result == 0) {
		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0)
			NDMP_LOG(LOG_ERR, "Audit failure: %m.");
	} else {
		if (adt_put_event(event, ADT_FAILURE, result) != 0)
			NDMP_LOG(LOG_ERR, "Audit failure: %m.");
	}

	adt_free_event(event);
}


/*
 * ndmpd_audit_restore
 *
 * Generate AUE_ndmp_restore audit record
 */
/*ARGSUSED*/
void
ndmpd_audit_restore(ndmp_connection_t *conn,
    char *path, int dest, char *local_path, int result)
{
	adt_event_data_t *event;

	if ((event = adt_alloc_event(conn->conn_ah,
	    ADT_ndmp_restore)) == NULL) {
		NDMP_LOG(LOG_ERR, "Audit failure: %m.");
		return;
	}
	event->adt_ndmp_restore.destination = path;

	if (dest == NDMP_ADDR_LOCAL) {
		event->adt_ndmp_restore.local_source = local_path;
	} else {
		event->adt_ndmp_restore.remote_source = conn->conn_sock;
	}

	if (result == 0) {
		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0)
			NDMP_LOG(LOG_ERR, "Audit failure: %m.");
	} else {
		if (adt_put_event(event, ADT_FAILURE, result) != 0)
			NDMP_LOG(LOG_ERR, "Audit failure: %m.");
	}

	adt_free_event(event);
}


/*
 * ndmpd_audit_connect
 *
 * Generate AUE_ndmp_connect audit record
 */
/*ARGSUSED*/
void
ndmpd_audit_connect(ndmp_connection_t *conn, int result)
{
	adt_event_data_t *event;
	adt_termid_t *termid;

	if (adt_load_termid(conn->conn_sock, &termid) != 0) {
		NDMP_LOG(LOG_ERR, "Audit failure: %m.");
		return;
	}

	if (adt_set_user(conn->conn_ah, ADT_NO_ATTRIB, ADT_NO_ATTRIB,
	    ADT_NO_ATTRIB, ADT_NO_ATTRIB, termid, ADT_NEW) != 0) {
		NDMP_LOG(LOG_ERR, "Audit failure: %m.");
		free(termid);
		return;
	}
	free(termid);

	if ((event = adt_alloc_event(conn->conn_ah,
	    ADT_ndmp_connect)) == NULL) {
		NDMP_LOG(LOG_ERR, "Audit failure: %m.");
		return;
	}

	if (result == 0) {
		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0)
			NDMP_LOG(LOG_ERR, "Audit failure: %m.");
	} else {
		if (adt_put_event(event, ADT_FAILURE, result) != 0)
			NDMP_LOG(LOG_ERR, "Audit failure: %m.");
	}

	adt_free_event(event);
}


/*
 * ndmpd_audit_disconnect
 *
 * Generate AUE_ndmp_disconnect audit record
 */
/*ARGSUSED*/
void
ndmpd_audit_disconnect(ndmp_connection_t *conn)
{
	adt_event_data_t *event;

	if ((event = adt_alloc_event(conn->conn_ah,
	    ADT_ndmp_disconnect)) == NULL) {
		NDMP_LOG(LOG_ERR, "Audit failure: %m.");
		return;
	}
	if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0)
		NDMP_LOG(LOG_ERR, "Audit failure: %m.");

	adt_free_event(event);
}

void *
ndmp_malloc(size_t size)
{
	void *data;

	if ((data = calloc(1, size)) == NULL) {
		NDMP_LOG(LOG_ERR, "Out of memory.");
	}

	return (data);
}