mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-25 00:03:03 +01:00
1194 lines
26 KiB
C++
1194 lines
26 KiB
C++
/*
|
|
* PROGRAM: JRD Access Method
|
|
* MODULE: jrn.cpp
|
|
* 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
|
|
*
|
|
*/
|
|
|
|
#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 isc_arg_unix
|
|
#endif
|
|
|
|
#ifndef status_t
|
|
#define status_t int
|
|
#endif
|
|
|
|
#ifdef VMS
|
|
#define SYS_ERROR isc_arg_vms
|
|
#endif
|
|
|
|
#ifdef WIN_NT
|
|
#define SYS_ERROR isc_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/y_ref.h"
|
|
#include "../jrd/ibase.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);
|
|
|
|
|
|
/*************************************************************************
|
|
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 = 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, NULL, 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 = 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,
|
|
NULL, 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 = 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,
|
|
NULL, 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 = 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,
|
|
NULL, 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 = 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++ = isc_dpb_wal_backup_dir;
|
|
fb_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;
|
|
fb_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, NULL, 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, NULL, 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, NULL, 0, seqno,
|
|
offset, p_offset, dump_id, 0, NULL,
|
|
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, NULL, 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;
|
|
|
|
fb_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 = 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 = 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,
|
|
isc_sys_request,
|
|
isc_arg_string,
|
|
string,
|
|
SYS_ERROR,
|
|
status,
|
|
isc_arg_gds,
|
|
isc_journerr,
|
|
isc_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 != NULL)
|
|
break;
|
|
}
|
|
|
|
return FB_SUCCESS;
|
|
}
|