/*
 *	PROGRAM:	JRD Access Method
 *	MODULE:		jrn.c
 *	DESCRIPTION:	Journalling interface for database system.
 *
 * The contents of this file are subject to the Interbase Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy
 * of the License at http://www.Inprise.com/IPL.html
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code was created by Inprise Corporation
 * and its predecessors. Portions created by Inprise Corporation are
 * Copyright (C) Inprise Corporation.
 *
 * All Rights Reserved.
 * Contributor(s): ______________________________________.
 *
 * 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "IMP" port
 *
 * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
 *
 */

#ifdef SHLIB_DEFS
#define LOCAL_SHLIB_DEFS
#endif

#include "firebird.h"
#include "../jrd/jrd_time.h"
#include "../jrd/common.h"
#include <string.h>
#include <stdlib.h>

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif



#ifdef UNIX
#define UNIX_JOURNALLING
#define BSD_SOCKETS
#define SYS_ERROR	gds_arg_unix
#endif

#ifndef status_t
#define status_t	int
#endif

#ifdef VMS
#define SYS_ERROR	gds_arg_vms
#endif

#ifdef WIN_NT
#include <windows.h>
#ifdef TEXT
#undef TEXT
#endif
#define TEXT		SCHAR
#define SYS_ERROR	gds_arg_win32
#endif

#ifdef BSD_SOCKETS
#include "../jrd/ib_stdio.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#endif

#include "../jrd/gds.h"
#include "../jrd/jrd.h"
#include "../jrd/ods.h"
#include "../jrd/jrn.h"
#include "../jrd/iberr_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/jrn_proto.h"

static int do_connect(JRN *, ISC_STATUS *, TEXT *, USHORT, LTJC *, USHORT,
					  UCHAR *, USHORT, USHORT);
static void error(ISC_STATUS *, JRN, int, TEXT *);
#ifdef BSD_SOCKETS
static int find_address(ISC_STATUS *, JRN, struct sockaddr_in *);
#endif
static int get_reply(ISC_STATUS *, JRN, JRNR *);
static int journal_close(ISC_STATUS *, JRN);
static int jrn_put(ISC_STATUS *, JRN, JRNH *, USHORT, UCHAR *, USHORT);
static int retry_connect(ISC_STATUS *, JRN *, TEXT *, USHORT, LTJC *, USHORT,
						 UCHAR *, USHORT);

#ifdef SHLIB_DEFS
#define strlen		(*_libgds_strlen)
#define strcpy		(*_libgds_strcpy)
#define errno		(*_libgds_errno)
#define ib_fopen		(*_libgds_fopen)
#define ib_fclose		(*_libgds_fclose)
#define close		(*_libgds_close)
#define ib_fscanf		(*_libgds_fscanf)
#define connect		(*_libgds_connect)
#define send		(*_libgds_send)
#define socket		(*_libgds_socket)
#define recv		(*_libgds_recv)
#define sleep		(*_libgds_sleep)
#define memcpy		(*_libgds_memcpy)

extern int strlen();
extern SCHAR *strcpy();
extern int errno;
extern IB_FILE *ib_fopen();
extern int ib_fclose();
extern int close();
extern int ib_fscanf();
extern int connect();
extern int send();
extern int socket();
extern int recv();
extern uint sleep();
extern void *memcpy();
#endif


/*************************************************************************
Error Handling:
*	The error handling has been changed for journal routines.  The
*	routines now require a status vector and a journal handle to be
*	passed in for all calls.  The error is returned via the status
*	vector.  The caller has to call the appropriate error routine.
*	In case of jrd -
*
* Return values:
*	FB_SUCCESS	- everything is fine.
*	FB_FAILURE - typically some kind of OS error.
*	< 0	- BUGCHECK errors, unexpected values.
*	> 1	- ERROR conditions.
*************************************************************************
*/


int JRN_archive_begin(
					  ISC_STATUS * status_vector,
					  JRN * ret_journal,
					  SLONG db_id,
					  SLONG file_id, TEXT * journal_name, USHORT j_length)
{
/**************************************
 *
 *	J R N _ a r c h i v e _ b e g i n
 *
 **************************************
 *
 * Functional description
 *	Sign on with the journal server for starting archive
 * Returns:
 *	journal descriptor in ret_journal.
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *
 **************************************/
	LTJA record;

	*ret_journal = (JRN) NULL;

	record.ltja_header.jrnh_type = JRN_ARCHIVE_BEGIN;
	record.ltja_db_id = db_id;
	record.ltja_file_id = file_id;
	record.ltja_error_code = 0;
	record.ltja_length = 0;

/* Establish contact with the journal server */

	return retry_connect(status_vector, ret_journal, journal_name, j_length,
						 (LTJC *) & record, LTJA_SIZE, (UCHAR *) 0, 0);
}


int JRN_archive_end(
					ISC_STATUS * status_vector,
					JRN * ret_journal, SLONG db_id, SLONG file_id)
{
/**************************************
 *
 *	J R N _ a r c h i v e _ e n d
 *
 **************************************
 *
 * Functional description
 *	Sign off with the journal server after archive
 * Returns:
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *
 **************************************/
	LTJA record;
	JRN journal;
	int ret_val;

	journal = *ret_journal;
	*ret_journal = (JRN) NULL;

/* If there is either a null journal block or
   a zero channel, there is no connection to
   server */

	if (!(journal))
		return FB_SUCCESS;

	if (!journal->jrn_channel) {
		gds__free(journal);
		return FB_SUCCESS;
	}

	record.ltja_header.jrnh_type = JRN_ARCHIVE_END;
	record.ltja_db_id = db_id;
	record.ltja_file_id = file_id;
	record.ltja_error_code = 0;
	record.ltja_length = 0;

	if (
		(ret_val =
		 jrn_put(status_vector, journal, (JRNH *) & record, LTJA_SIZE,
				 (UCHAR *) 0, 0)) != FB_SUCCESS)
		return ret_val;

	if ((ret_val = journal_close(status_vector, journal)) != FB_SUCCESS)
		return ret_val;

/* Free up the space */

	gds__free(journal);

	return FB_SUCCESS;
}


int JRN_archive_error(
					  ISC_STATUS * status_vector,
					  JRN * ret_journal,
					  SLONG db_id, SLONG file_id, SLONG error_code)
{
/**************************************
 *
 *	J R N _ a r c h i v e _ e r r o r
 *
 **************************************
 *
 * Functional description
 *	Sign off with the journal server after an error during archive
 * Returns:
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *
 **************************************/
	LTJA record;
	JRN journal;
	int ret_val;

	journal = *ret_journal;
	*ret_journal = (JRN) NULL;

	if (!(journal))
		return FB_SUCCESS;

	if (!journal->jrn_channel) {
		gds__free(journal);
		return FB_SUCCESS;
	}

	record.ltja_header.jrnh_type = JRN_ARCHIVE_ERROR;
	record.ltja_db_id = db_id;
	record.ltja_file_id = file_id;
	record.ltja_error_code = error_code;
	record.ltja_length = 0;

	if (
		(ret_val =
		 jrn_put(status_vector, journal, (JRNH *) & record, LTJA_SIZE,
				 (UCHAR *) 0, 0)) != FB_SUCCESS)
		return ret_val;

	if ((ret_val = journal_close(status_vector, journal)) != FB_SUCCESS)
		return ret_val;

/* Free up the space */

	gds__free(journal);

	return FB_SUCCESS;
}


int JRN_disable(
				ISC_STATUS * status_vector,
				JRN journal, JRNH * header, UCHAR * data, USHORT d_len)
{
/**************************************
 *
 *	J R N _ d i s a b l e
 *
 **************************************
 *
 * Functional description
 * 	Journal server entry should be delete from header page before this.  
 *	Disable journalling, if enabled.
 *
 **************************************/

	if (journal->jrn_channel)
		return jrn_put(status_vector, journal, header, LTJC_SIZE, data,
					   d_len);

	return FB_SUCCESS;
}


void JRN_dump_page(void)
{
/**************************************
 *
 *	J R N _ d u m p _ p a g e
 *
 **************************************
 *
 * Functional description
 *	Dump a page to the journal.  If necessary, fetch it.
 *
 **************************************/

/* Stub function for now */
}


int JRN_enable(
			   ISC_STATUS * status_vector,
			   JRN * ret_journal,
			   TEXT * journal_name,
			   USHORT j_length, UCHAR * data, USHORT d_len, LTJC * control)
{
/**************************************
 *
 *	J R N _ e n a b l e
 *
 **************************************
 *
 * Functional description
 *	Enable journalling for the database.
 * Returns	
 *	journal descriptor in ret_journal.
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *
 **************************************/

	return retry_connect(status_vector, ret_journal, journal_name, j_length,
						 control, LTJC_SIZE, data, d_len);
}


int JRN_fini(ISC_STATUS * status_vector, JRN * jrn)
{
/**************************************
 *
 *	J R N _ f i n i
 *
 **************************************
 *
 * Functional description
 *	Check out with journal system.
 *
 **************************************/
	LTJC record;
	JRN journal;
	int ret_val;

	journal = *jrn;
	*jrn = (JRN) NULL;

/* If there is either a null journal block or
   a zero channel, there is no connection to
   server */

	if (!(journal))
		return FB_SUCCESS;

	if (!journal->jrn_channel) {
		gds__free(journal);
		return FB_SUCCESS;
	}

/* Send a signoff record */

	record.ltjc_header.jrnh_type = JRN_SIGN_OFF;
	record.ltjc_length = 0;

	if (
		(ret_val =
		 jrn_put(status_vector, journal, (JRNH *) & record, LTJC_SIZE,
				 (UCHAR *) 0, 0)) != FB_SUCCESS)
		return ret_val;

/* Read reply.  This will fail since the server will break the connection.
   Don't worry about it. */

	if ((ret_val = journal_close(status_vector, journal)) != FB_SUCCESS)
		return ret_val;

/* Free up the space */

	gds__free(journal);

	return FB_SUCCESS;
}


int JRN_init(
			 ISC_STATUS * status_vector,
			 JRN * ret_journal,
			 USHORT page_size,
			 UCHAR * journal_dir, USHORT jd_len, UCHAR * data, USHORT d_len)
{
/**************************************
 *
 *	J R N _ i n i t
 *
 **************************************
 *
 * Functional description
 *	Initialize the journalling system.  
 *	If journal directory is specified, set up a connection to the 
 *	journal server.
 * Returns:
 *	journal descriptor in ret_journal.
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *
 **************************************/
	TEXT server_name[MAXPATHLEN];
	LTJC control;

	if (!journal_dir) {
		*ret_journal = (JRN) NULL;
		return FB_FAILURE;
	}

	memcpy(server_name, journal_dir, jd_len);

	server_name[jd_len] = 0;

	control.ltjc_header.jrnh_type = JRN_SIGN_ON;
	control.ltjc_page_size = page_size;
	control.ltjc_seqno = 0;
	control.ltjc_offset = 0;
	control.ltjc_length = d_len;

/* Establish contact with the journal server */

	return retry_connect(status_vector, ret_journal, server_name, jd_len,
						 &control, LTJC_SIZE, data, d_len);
}


void JRN_make_init_data(
						UCHAR * data,
						SSHORT * ret_len,
						UCHAR * db_name,
						USHORT db_len, UCHAR * backup_dir, USHORT b_length)
{
/**************************************
 *
 *	J R N _ m a k e _ i n i t _ d a t a
 *
 **************************************
 *
 * Functional description
 *	Encode the database and backup information into a string.
 *	Returns the length of the string.
 *
 **************************************/
	UCHAR *q, *t;
	int len;

	t = data;

	if (len = b_length) {
		*t++ = gds_dpb_wal_backup_dir;
		assert(b_length <= MAX_UCHAR);
		*t++ = (UCHAR) b_length;
		q = backup_dir;
		do
			*t++ = *q++;
		while (--len);
	}

	if (len = db_len) {
		*t++ = JRNW_DB_NAME;
		assert(db_len <= MAX_UCHAR);
		*t++ = (UCHAR) db_len;
		q = db_name;
		do
			*t++ = *q++;
		while (--len);
	}
	*t++ = JRNW_END;

	*ret_len = t - data;
}


int JRN_put_wal_name(
					 ISC_STATUS * status_vector,
					 JRN journal,
					 TEXT * walname,
					 USHORT w_length,
SLONG seqno, SLONG offset, SLONG p_offset, USHORT mode)
{
/**************************************
 *
 *	J R N _ p u t _ w a l _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Put in WAL file name to the journal server.
 *	mode is used for JRN_WAL_FILE (JRNW_OPEN/JRNW_CLOSE)
 * Input Params:
 *	status_vector 
 *	journal		- journal descriptor
 *	walname, w_length -	wal file name and length
 *	seqno, offset	- seqno/offset pair
 *	p_offset	- partition offset
 *	mode		- mode of operation open/close of file
 * Returns:
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *
 **************************************/

	return JRN_put_wal_info(status_vector, journal, walname, w_length, seqno,
							offset, p_offset, mode, 1, (USHORT *) 0,
							JRN_WAL_NAME);
}


int JRN_put_old_start(
					  ISC_STATUS * status_vector,
					  JRN journal,
					  SLONG seqno,
					  SLONG offset, SLONG p_offset, USHORT * dump_id)
{
/**************************************
 *
 *	J R N _ p u t _ o l d _ s t a r t
 *
 **************************************
 *
 * Functional description
 *	Put in Online Dump information
 * Input Params:
 *	status_vector 
 *	journal		- journal descriptor
 *	seqno, offset	- seqno/offset pair.
 *	p_offset	- partition offset
 *	dump_id		- address of dump id.
 * Returns:
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *	For start online dump, returns the dump id.
 *
 **************************************/

	return JRN_put_wal_info(status_vector, journal, (TEXT *) 0, 0, seqno,
							offset, p_offset, 0, 0, dump_id,
							JRN_START_ONLINE_DMP);
}


int JRN_put_old_end(
					ISC_STATUS * status_vector,
					JRN journal,
					SLONG seqno, SLONG offset, SLONG p_offset, USHORT dump_id)
{
/**************************************
 *
 *	J R N _ p u t _ o l d _ e n d
 *
 **************************************
 *
 * Functional description
 *	Put in Online Dump information
 * Input Params:
 *	status_vector 
 *	journal		- journal descriptor
 *	seqno, offset	- BSN of a wal record (0 if not needed)
 *	p_offset	- partition offset
 *	dump_id		- id of dump.
 * Returns:
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 **************************************/

	return JRN_put_wal_info(status_vector, journal, (TEXT *) 0, 0, seqno,
							offset, p_offset, dump_id, 0, (USHORT *) 0,
							JRN_END_ONLINE_DMP);
}


int JRN_put_old_file(
					 ISC_STATUS * status_vector,
					 JRN journal,
					 TEXT * old_file_name,
					 USHORT file_length,
SLONG file_size, USHORT file_seqno, USHORT dump_id)
{
/**************************************
 *
 *	J R N _ p u t _ o l d _ f i l e
 *
 **************************************
 *
 * Functional description
 *	Put in online dump file information.
 * Input Params:
 *	status_vector 
 *	journal		- journal descriptor
 *	old_file_name, file_length -	file name and name length
 *	file_size	- file size.
 *	file_count	- sequence number of file.
 *	dump_id		- id of dump.
 * Returns:
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *	For start online dump, returns the dump id.
 *
 **************************************/

	return JRN_put_wal_info(status_vector, journal, old_file_name,
							file_length, (SLONG) 0, file_size, (SLONG) 0,
							dump_id, file_seqno, (USHORT *) 0,
							JRN_ONLINE_DMP_FILE);
}


int JRN_put_wal_info(
					 ISC_STATUS * status_vector,
					 JRN journal,
					 TEXT * walname,
					 USHORT w_length,
SLONG seqno,
SLONG offset,
SLONG p_offset, USHORT mode, USHORT file_count, USHORT * dump_id, USHORT type)
{
/**************************************
 *
 *	J R N _ p u t _ w a l _ i n f o
 *
 **************************************
 *
 * Functional description
 *	Put in some kind of WAL info (Control point, New WAL file etc)
 *	mode is used for JRN_WAL_FILE (=JRNW_OPEN)
 * Input Params:
 *	status_vector 
 *	journal		- journal descriptor
 *	walname, w_length -	wal file name and length
 *	seqno, offset	- BSN of a wal record (0 if not needed)
 *	p_offset	- partition offset
 *	mode		- mode of operation open/close of file
 *	file_count	- number of files (for start online dump )
 *	type		- type of record (JRN_WAL_NAME/JRN_CNTRL_PT etc.)
 *			  JNR_START_ONLINE_DMP/JRN_ONLINE_DUMP_FILE
 * Returns:
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *	For start online dump, returns the dump id.
 *
 **************************************/
	LTJW jrnwal;
	struct jrnr reply;
	int ret_val;

	assert(type <= MAX_UCHAR);

	jrnwal.ltjw_header.jrnh_type = (UCHAR) type;
	jrnwal.ltjw_mode = mode;
	jrnwal.ltjw_seqno = seqno;
	jrnwal.ltjw_offset = offset;
	jrnwal.ltjw_p_offset = p_offset;
	jrnwal.ltjw_count = file_count;
	jrnwal.ltjw_length = w_length;

	if ((ret_val = jrn_put(status_vector, journal, (JRNH *) & jrnwal,
						   LTJW_SIZE, (UCHAR *) walname,
						   w_length)) != FB_SUCCESS) return ret_val;

#ifndef VMS
	if ((ret_val = get_reply(status_vector, journal, &reply)) != FB_SUCCESS)
		return ret_val;

	switch (reply.jrnr_response) {
	case jrnr_ack:
		break;

	case jrnr_start_dump:

		if (dump_id)
			*dump_id = (USHORT) reply.jrnr_page;
		break;

	case jrnr_end_dump:
		break;

	default:
		return (-170);			/* msg 170 unexpected reply from journal server */
	}
#endif

	return FB_SUCCESS;
}


void JRN_sync(void)
{
/**************************************
 *
 *	J R N _ s y n c
 *
 **************************************
 *
 * Functional description
 *	Synchronize with the journal -- wait until all bits have
 *	been flushed to oxide.
 *
 **************************************/

/* stub function for now */
}


static int do_connect(
					  JRN * ret_jrn,
					  ISC_STATUS * status_vector,
					  TEXT * journal_name,
					  USHORT j_length,
LTJC * control,
USHORT control_length, UCHAR * data, USHORT d_length, USHORT retry)
{
/**************************************
 *
 *	d o _ c o n n e c t
 *
 **************************************
 *
 * Functional description
 *	Connect to a journal server and get response.  Return the journal
 *	block.
 * Returns
 *	FB_SUCCESS/FB_FAILURE/BUGCHECK/ERROR.
 *
 **************************************/
#ifdef BSD_SOCKETS
	struct sockaddr_in address;
#endif
	TEXT *p, *q, name[MAXPATHLEN];
	SSHORT l;
	JRN journal;
	struct jrnr reply;
#ifdef BSD_SOCKETS
	int loop;
#endif
	int ret_val;


/* Make sure we don't return a bogus journal handle */

	*ret_jrn = NULL;

/* Start by copying file name */

	p = name;

#ifdef WIN_NT
	strcpy(p, "\\\\.\\pipe");
	p += strlen(p);
	if (!j_length || (*journal_name != '/' && *journal_name != '\\'))
		*p++ = '\\';
#endif

	q = journal_name;
	if (l = j_length)
		do
			*p++ = *q++;
		while (--l);

/* Generate connect string */

#ifdef UNIX_JOURNALLING
	if (j_length && p[-1] != '/')
		*p++ = '/';
	strcpy(p, JOURNAL_ADDR);
#endif

#ifdef VMS
	if (j_length && p[-1] != ':' && p[-1] != ']')
		*p++ = ':';
	strcpy(p, JOURNAL_FILE);
#endif

#ifdef WIN_NT
	for (q = name; q < p; q++)
		if (*q == ':')
			*q = '_';
		else if (*q == '/')
			*q = '\\';
	if (p[-1] != '\\')
		*p++ = '\\';
	strcpy(p, JOURNAL_FILE);
#endif

/* Make up the block */

	l = strlen(name);

	journal = (JRN) gds__alloc((SLONG) ((sizeof(struct jrn)) + l));
/* FREE: by error returns from this module, otherwise by JRN_fini & friends */
	if (!journal) {				/* NOMEM: */
		error(status_vector, NULL, 0, "gds__alloc");
		return FB_FAILURE;
	}

	strcpy(journal->jrn_server, name);

#ifdef BSD_SOCKETS
	for (loop = 0; loop < 20; loop++) {
		for (;;) {
			journal->jrn_channel = (int *) socket(AF_INET, SOCK_STREAM, 0);
			if ((int) journal->jrn_channel != -1)
				break;
			if (!SYSCALL_INTERRUPTED(errno)) {
				error(status_vector, journal, errno, "socket");
				gds__free(journal);
				return FB_FAILURE;
			}
		}

		/* Zero out the structure */

		p = (TEXT *) & address;
		l = sizeof(address);
		do
			*p++ = 0;
		while (--l);

		if ((ret_val = find_address(status_vector, journal, &address)) !=
			FB_SUCCESS) {
			gds__free(journal);
			return ret_val;
		}

		if (!connect((int) journal->jrn_channel, (sockaddr*)&address, sizeof(address)))
			break;

		sleep(3);

		if (loop < 16) {
			close((int) journal->jrn_channel);
			continue;
		}
		if (!SYSCALL_INTERRUPTED(errno)) {
			if (retry) {
				close((int) journal->jrn_channel);
				gds__free(journal);
				*ret_jrn = (JRN) NULL;
				return FB_SUCCESS;
			}
			error(status_vector, journal, errno, "connect (journal server)");
			gds__free(journal);
			return FB_FAILURE;
		}
		close((int) journal->jrn_channel);
	}
#endif

#ifdef VMS
	journal->jrn_status_vector = status_vector;
	JIO_open(journal);
#endif

#ifdef WIN_NT
	journal->jrn_channel = (int *) CreateFile(journal->jrn_server,
											  GENERIC_READ | GENERIC_WRITE,
											  0,
											  NULL, OPEN_EXISTING, 0, NULL);
	if (journal->jrn_channel == INVALID_HANDLE_VALUE) {
		error(status_vector, journal, GetLastError(), "CreateFile");
		gds__free(journal);
		return FB_FAILURE;
	}
#endif

/* Send an enable message */

	if ((ret_val = jrn_put(status_vector, journal, (JRNH *) control,
						   control_length, data, d_length)) != FB_SUCCESS) {
		gds__free(journal);
		return ret_val;
	}


#ifndef VMS
	if ((ret_val = get_reply(status_vector, journal, &reply)) != FB_SUCCESS) {
		gds__free(journal);
		return ret_val;
	}

	journal->jrn_handle = reply.jrnr_header.jrnh_handle;

	if (reply.jrnr_header.jrnh_version != JOURNAL_VERSION) {
		gds__free(journal);
		return (171);			/* msg 171 journal server is incompatible version */
	}

	switch (reply.jrnr_response) {
	case jrnr_accepted:
	case jrnr_enabled:
		break;

	case jrnr_archive_error:
		gds__free(journal);
		return (282);

	case jrnr_rejected:
		gds__free(journal);
		return (172);			/* msg 172 journal server refused connection */

	default:
		if (retry)
		{
#ifdef BSD_SOCKETS
			close((int) journal->jrn_channel);
#endif

#ifdef WIN_NT
			CloseHandle((HANDLE) journal->jrn_channel);
#endif
			gds__free(journal);
			*ret_jrn = (JRN) NULL;
			return FB_SUCCESS;
		}
		gds__free(journal);
		return (-170);			/* msg 170 unexpected reply from journal server */
	}
#endif

	*ret_jrn = journal;

	return FB_SUCCESS;
}


static void error(
				  ISC_STATUS * status_vector,
				  JRN journal, status_t status, TEXT * string)
{
/**************************************
 *
 *	e r r o r
 *
 **************************************
 *
 * Functional description
 *	An operating system error occurred somewhere.
 *	Fill up the status vector.
 *
 **************************************/

	IBERR_build_status(status_vector,
					   gds_sys_request,
					   gds_arg_string,
					   string,
					   SYS_ERROR,
					   status,
					   gds_arg_gds,
					   gds_journerr,
					   gds_arg_string,
					   (journal) ? journal->jrn_server : "", 0);
}


#ifdef BSD_SOCKETS
static int find_address(
						ISC_STATUS * status_vector,
						JRN journal, struct sockaddr_in *address)
{
/**************************************
 *
 *	f i n d _ a d d r e s s		( U N I X   &   N E T W A R E )
 *
 **************************************
 *
 * Functional description
 *	Get the socket address established by the server by
 *	looking in the designated files.
 *
 **************************************/
	IB_FILE *file;
	SLONG addr, family, port, version;
	TEXT *filename;

	filename = journal->jrn_server;

	if (!(file = ib_fopen(filename, "r"))) {
		error(status_vector, journal, errno, "ib_fopen");
		return FB_FAILURE;
	}

	if (ib_fscanf(file, "%ld %ld %ld %ld", &version, &addr, &family, &port) !=
		4) {
		error(status_vector, journal, 0, "journal socket file format");
		return FB_FAILURE;
	}
	ib_fclose(file);

	if (version != JOURNAL_VERSION) {
		error(status_vector, journal, 0, " address version");
		return FB_FAILURE;
	}

	address->sin_addr.s_addr = addr;
	address->sin_family = family;
	address->sin_port = port;

	return FB_SUCCESS;
}
#endif


static int get_reply(ISC_STATUS * status_vector, JRN journal, JRNR * reply)
{
/**************************************
 *
 *	g e t _ r e p l y
 *
 **************************************
 *
 * Functional description
 *	Get reply from database.
 *
 **************************************/
	SLONG l;

#ifdef BSD_SOCKETS
/*  If the recv call is interrupted, another process is in contention
    with us, so keep trying until successful.  */

	do {
		l = recv((int) journal->jrn_channel, (char *) reply, sizeof(struct jrnr), 0);
	} while (l < 0 && SYSCALL_INTERRUPTED(errno));

	if (l < 0) {
		error(status_vector, journal, errno, "socket recv");
		return FB_FAILURE;
	}
#endif

#ifdef WIN_NT
	if (!ReadFile((HANDLE) journal->jrn_channel,
				  reply,
				  sizeof(struct jrnr),
				  reinterpret_cast < DWORD * >(&l),
				  NULL) || l != sizeof(struct jrnr)) {
		error(status_vector, journal, GetLastError(), "ReadFile");
		return FB_FAILURE;
	}
#endif

	return FB_SUCCESS;
}


static int journal_close(ISC_STATUS * status_vector, JRN journal)
{
/**************************************
 *
 *	j o u r n a l _ c l o s e
 *
 **************************************
 *
 * Functional description
 *	Close network connection
 *	Has to be valid handle.
 *
 **************************************/

#ifdef BSD_SOCKETS
	if (close((int) journal->jrn_channel) < 0) {
		error(status_vector, journal, errno, "close");
		return FB_FAILURE;
	}
#endif

#ifdef VMS
	journal->jrn_status_vector = status_vector;
	JIO_fini(journal);
#endif

#ifdef WIN_NT
	CloseHandle((HANDLE) journal->jrn_channel);
#endif

	return FB_SUCCESS;
}


static int jrn_put(
				   ISC_STATUS * status_vector,
				   JRN journal,
				   JRNH * header,
				   USHORT h_length, UCHAR * data, USHORT d_length)
{
/**************************************
 *
 *	j r n _ p u t
 *
 **************************************
 *
 * Functional description
 *	Write a journal record.
 *
 **************************************/
	USHORT length;
	SLONG l;
	UCHAR *p, *q;
	UCHAR  buffer[MAX_RECORD];

/* Prepare journal message for sending */

	length = h_length + d_length;
	header->jrnh_handle = journal->jrn_handle;
	header->jrnh_length = length;
	header->jrnh_series = journal->jrn_series;
	header->jrnh_version = JOURNAL_VERSION;

	header->jrnh_prev_seqno = 0;
	header->jrnh_prev_offset = 0;

/* Concatenate header and data.  This is boring but necessary. */

	p = buffer;
	q = (UCHAR *) header;
	do
		*p++ = *q++;
	while (--h_length);

	if (d_length)
		do
			*p++ = *data++;
		while (--d_length);

/* Send message */

#ifdef BSD_SOCKETS
/*  If the send call is interrupted, another process is in contention
    with us, so keep trying until successful.  */

	do {
		l = send((int) journal->jrn_channel, (char *) buffer, (int) length, 0);
	} while (l < 0 && SYSCALL_INTERRUPTED(errno));

	if (l < 0) {
		error(status_vector, journal, errno, "journal send");
		return FB_FAILURE;
	}

	if (l != length) {
		error(status_vector, journal, 0, "journal send truncated");
		return FB_FAILURE;
	}
#endif

#ifdef VMS
	journal->jrn_status_vector = status_vector;
	JIO_put(journal, buffer, length);
#endif

#ifdef WIN_NT
	if (!WriteFile
		((HANDLE) journal->jrn_channel, buffer, (SLONG) length,
		 reinterpret_cast < DWORD * >(&l), NULL) || l != length) {
		error(status_vector, journal, GetLastError(), "WriteFile");
		return FB_FAILURE;
	}
#endif

	return FB_SUCCESS;
}


static int retry_connect(
						 ISC_STATUS * status_vector,
						 JRN * journal,
						 TEXT * journal_name,
						 USHORT j_length,
LTJC * control, USHORT control_length, UCHAR * data, USHORT d_length)
{
/**************************************
 *
 *      r e t r y _ c o n n e c t
 *
 **************************************
 *
 * Functional description
 *      Wrapper around do_connect ().  Tries to connect 5 times.
 *	If FB_SUCCESS and journal handle is still null, keep trying.
 *
 **************************************/
	SSHORT count;
	int ret_val;

	for (count = 4; count >= 0; count--) {
		if ((ret_val = do_connect(journal, status_vector, journal_name,
								  j_length, control, control_length, data,
								  d_length, count)) != FB_SUCCESS)
			return ret_val;

		if (*journal != (JRN) NULL)
			break;
	}

	return FB_SUCCESS;
}
