mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 21:23:04 +01:00
2670 lines
67 KiB
C++
2670 lines
67 KiB
C++
/*
|
|
* PROGRAM: JRD Remote Interface/Server
|
|
* MODULE: xnet.cpp
|
|
* DESCRIPTION: Interprocess Server Communications module.
|
|
*
|
|
* 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): ______________________________________.
|
|
*
|
|
* 2003.05.01 Victor Seryodkin, Dmitry Yemanov: Completed XNET implementation
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include <stdio.h>
|
|
#include "../remote/remote.h"
|
|
#include "../jrd/ibase.h"
|
|
#include "../common/thd.h"
|
|
#include "../remote/xnet.h"
|
|
#include "../utilities/install/install_nt.h"
|
|
#include "../remote/proto_proto.h"
|
|
#include "../remote/remot_proto.h"
|
|
#include "../remote/xnet_proto.h"
|
|
#include "../remote/serve_proto.h"
|
|
#include "../remote/os/win32/window.h"
|
|
#include "../jrd/gds_proto.h"
|
|
#include "../jrd/isc_proto.h"
|
|
#include "../common/classes/init.h"
|
|
#include "../common/classes/fb_string.h"
|
|
#include "../common/config/config.h"
|
|
#include "../common/classes/ClumpletWriter.h"
|
|
#include "../common/utils_proto.h"
|
|
#include <time.h>
|
|
|
|
#ifdef WIN_NT
|
|
#include <process.h>
|
|
#include <windows.h>
|
|
#else
|
|
#pragma FB_COMPILER_MESSAGE("POSIX implementation is required")
|
|
#endif // WIN_NT
|
|
|
|
using namespace Firebird;
|
|
|
|
static bool accept_connection(rem_port*, const P_CNCT*);
|
|
static rem_port* alloc_port(rem_port*, UCHAR*, ULONG, UCHAR*, ULONG);
|
|
static rem_port* aux_connect(rem_port*, PACKET*);
|
|
static rem_port* aux_request(rem_port*, PACKET*);
|
|
|
|
static void cleanup_comm(XCC);
|
|
static void cleanup_mapping(XPM);
|
|
static void cleanup_port(rem_port*);
|
|
static rem_port* connect_client(PACKET*, ISC_STATUS*);
|
|
static rem_port* connect_server(ISC_STATUS*, USHORT);
|
|
static void disconnect(rem_port*);
|
|
static void force_close(rem_port*);
|
|
static void exit_handler(rem_port*);
|
|
static int cleanup_ports(const int, const int, void* arg);
|
|
|
|
static rem_port* receive(rem_port*, PACKET*);
|
|
static int send_full(rem_port*, PACKET*);
|
|
static int send_partial(rem_port*, PACKET*);
|
|
|
|
#ifdef SUPERCLIENT
|
|
static HANDLE server_process_handle = 0;
|
|
static void server_shutdown(rem_port* port);
|
|
#endif
|
|
static rem_port* get_server_port(ULONG, XPM, ULONG, ULONG, ULONG, ISC_STATUS*);
|
|
static bool make_map(ULONG, ULONG, FILE_ID*, CADDR_T*);
|
|
static XPM make_xpm(ULONG, ULONG);
|
|
static bool server_init(ISC_STATUS*, USHORT);
|
|
static XPM get_free_slot(ULONG*, ULONG*, ULONG*);
|
|
static bool fork(ULONG, USHORT, ULONG*);
|
|
|
|
static int xdrxnet_create(XDR*, rem_port*, UCHAR*, USHORT, xdr_op);
|
|
|
|
static int xnet_destroy(XDR*);
|
|
static bool_t xnet_getbytes(XDR*, SCHAR*, u_int);
|
|
static bool_t xnet_getlong(XDR*, SLONG*);
|
|
static u_int xnet_getpostn(XDR*);
|
|
static caddr_t xnet_inline(XDR*, u_int);
|
|
static bool_t xnet_putlong(XDR*, const SLONG*);
|
|
static bool_t xnet_putbytes(XDR*, const SCHAR*, u_int);
|
|
static bool_t xnet_setpostn(XDR*, u_int);
|
|
static bool_t xnet_read(XDR* xdrs);
|
|
static bool_t xnet_write(XDR* xdrs);
|
|
|
|
static xdr_t::xdr_ops xnet_ops =
|
|
{
|
|
xnet_getlong,
|
|
xnet_putlong,
|
|
xnet_getbytes,
|
|
xnet_putbytes,
|
|
xnet_getpostn,
|
|
xnet_setpostn,
|
|
xnet_inline,
|
|
xnet_destroy
|
|
};
|
|
|
|
#ifdef SUPERCLIENT
|
|
const ISC_STATUS CONN_LOST_ERROR = isc_lost_db_connection;
|
|
#else
|
|
const ISC_STATUS CONN_LOST_ERROR = isc_conn_lost;
|
|
#endif
|
|
|
|
static ULONG global_pages_per_slot = XPS_DEF_PAGES_PER_CLI;
|
|
static ULONG global_slots_per_map = XPS_DEF_NUM_CLI;
|
|
static XPM global_client_maps = NULL;
|
|
|
|
#ifdef WIN_NT
|
|
|
|
static HANDLE xnet_connect_mutex = 0;
|
|
static HANDLE xnet_connect_map_h = 0;
|
|
static CADDR_T xnet_connect_map = 0;
|
|
|
|
static HANDLE xnet_connect_event = 0;
|
|
static HANDLE xnet_response_event = 0;
|
|
static DWORD current_process_id;
|
|
|
|
// XNET endpoint is the IPC prefix name used to access the server.
|
|
// It may have to be dynamically determined and has to be initialized
|
|
// before the protocol can be used. It is initialized at the following points:
|
|
// - XNET_reconnect (classic servant size)
|
|
// - connect_client (client side)
|
|
// - server_init (listener side)
|
|
static char xnet_endpoint[BUFFER_TINY] = "";
|
|
|
|
#endif // WIN_NT
|
|
|
|
static volatile bool xnet_initialized = false;
|
|
static volatile bool xnet_shutdown = false;
|
|
static Firebird::GlobalPtr<Firebird::Mutex> xnet_mutex;
|
|
static Firebird::GlobalPtr<PortsCleanup> xnet_ports;
|
|
static ULONG xnet_next_free_map_num = 0;
|
|
|
|
#ifdef SUPERCLIENT
|
|
static bool connect_init(ISC_STATUS* status);
|
|
#endif
|
|
|
|
static void connect_fini();
|
|
static void release_all();
|
|
|
|
inline void make_obj_name(char* buffer, size_t size, const char* format)
|
|
{
|
|
fb_assert(strcmp(xnet_endpoint, "") != 0);
|
|
|
|
fb_utils::snprintf(buffer, size, format, xnet_endpoint);
|
|
}
|
|
|
|
inline void make_map_name(char* buffer, size_t size, const char* format, ULONG arg1, ULONG arg2)
|
|
{
|
|
fb_assert(strcmp(xnet_endpoint, "") != 0);
|
|
|
|
fb_utils::snprintf(buffer, size, format, xnet_endpoint, arg1, arg2);
|
|
}
|
|
|
|
inline void make_event_name(char* buffer, size_t size, const char* format, ULONG arg1, ULONG arg2, ULONG arg3)
|
|
{
|
|
fb_assert(strcmp(xnet_endpoint, "") != 0);
|
|
|
|
fb_utils::snprintf(buffer, size, format, xnet_endpoint, arg1, arg2, arg3);
|
|
}
|
|
|
|
|
|
static void xnet_error(rem_port*, ISC_STATUS, int);
|
|
|
|
static void xnet_log_error(const char* err_msg, const ISC_STATUS* status = NULL)
|
|
{
|
|
if (status && status[1])
|
|
{
|
|
Firebird::string str("XNET error: ");
|
|
str += err_msg;
|
|
iscLogStatus(str.c_str(), status);
|
|
}
|
|
else
|
|
gds__log("XNET error: %s", err_msg);
|
|
}
|
|
|
|
#ifdef DEV_BUILD
|
|
#define ERR_STR2(str, lnum) (str #lnum)
|
|
#define ERR_STR1(str, lnum) ERR_STR2(str " at line ", lnum)
|
|
#define ERR_STR(str) ERR_STR1(str, __LINE__)
|
|
#else
|
|
#define ERR_STR(str) (str)
|
|
#endif
|
|
|
|
rem_port* XNET_analyze(const Firebird::PathName& file_name,
|
|
ISC_STATUS* status_vector,
|
|
//const TEXT* node_name,
|
|
//const TEXT* user_string,
|
|
bool uv_flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* X N E T _ a n a l y z e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Client performs attempt to establish connection
|
|
* based on the set of protocols.
|
|
* If a connection is established, return a port block,
|
|
* otherwise return NULL.
|
|
*
|
|
**************************************/
|
|
|
|
// We need to establish a connection to a remote server.
|
|
// Allocate the necessary blocks and get ready to go.
|
|
|
|
Rdb* rdb = new Rdb;
|
|
PACKET* packet = &rdb->rdb_packet;
|
|
|
|
// Pick up some user identification information
|
|
|
|
Firebird::string buffer;
|
|
Firebird::ClumpletWriter user_id(Firebird::ClumpletReader::UnTagged, MAX_DPB_SIZE);
|
|
|
|
ISC_get_user(&buffer, 0, 0, 0);
|
|
buffer.lower();
|
|
user_id.insertString(CNCT_user, buffer);
|
|
|
|
ISC_get_host(buffer);
|
|
buffer.lower();
|
|
user_id.insertString(CNCT_host, buffer);
|
|
|
|
if (uv_flag) {
|
|
user_id.insertTag(CNCT_user_verification);
|
|
}
|
|
|
|
// Establish connection to server
|
|
|
|
P_CNCT* cnct = &packet->p_cnct;
|
|
packet->p_operation = op_connect;
|
|
cnct->p_cnct_operation = op_attach;
|
|
cnct->p_cnct_cversion = CONNECT_VERSION2;
|
|
cnct->p_cnct_client = ARCHITECTURE;
|
|
cnct->p_cnct_file.cstr_length = (USHORT) file_name.length();
|
|
cnct->p_cnct_file.cstr_address = reinterpret_cast<const UCHAR*>(file_name.c_str());
|
|
|
|
// Note: prior to V3.1E a recievers could not in truth handle more
|
|
// then 5 protocol descriptions; however, the interprocess server
|
|
// was created in 4.0 so this does not apply.
|
|
|
|
cnct->p_cnct_user_id.cstr_length = (USHORT) user_id.getBufferLength();
|
|
cnct->p_cnct_user_id.cstr_address = user_id.getBuffer();
|
|
|
|
static const p_cnct::p_cnct_repeat protocols_to_try1[] =
|
|
{
|
|
REMOTE_PROTOCOL(PROTOCOL_VERSION7, ptype_rpc, ptype_batch_send, 1),
|
|
REMOTE_PROTOCOL(PROTOCOL_VERSION8, ptype_rpc, ptype_batch_send, 2),
|
|
REMOTE_PROTOCOL(PROTOCOL_VERSION10, ptype_rpc, ptype_batch_send, 3),
|
|
REMOTE_PROTOCOL(PROTOCOL_VERSION11, ptype_rpc, ptype_batch_send, 4),
|
|
REMOTE_PROTOCOL(PROTOCOL_VERSION12, ptype_rpc, ptype_batch_send, 5)
|
|
#ifdef SCROLLABLE_CURSORS
|
|
,
|
|
REMOTE_PROTOCOL(PROTOCOL_SCROLLABLE_CURSORS, ptype_rpc, ptype_batch_send, 99)
|
|
#endif
|
|
};
|
|
cnct->p_cnct_count = FB_NELEM(protocols_to_try1);
|
|
|
|
for (size_t i = 0; i < cnct->p_cnct_count; i++) {
|
|
cnct->p_cnct_versions[i] = protocols_to_try1[i];
|
|
}
|
|
|
|
// If we can't talk to a server, punt. Let somebody else generate an error.
|
|
|
|
rem_port* port = XNET_connect(/*node_name,*/ packet, status_vector, 0);
|
|
if (!port)
|
|
{
|
|
delete rdb;
|
|
return NULL;
|
|
}
|
|
|
|
// Get response packet from server
|
|
|
|
rdb->rdb_port = port;
|
|
port->port_context = rdb;
|
|
port->receive(packet);
|
|
|
|
if (packet->p_operation == op_reject && !uv_flag)
|
|
{
|
|
disconnect(port);
|
|
packet->p_operation = op_connect;
|
|
cnct->p_cnct_operation = op_attach;
|
|
cnct->p_cnct_cversion = CONNECT_VERSION2;
|
|
cnct->p_cnct_client = ARCHITECTURE;
|
|
cnct->p_cnct_file.cstr_length = (USHORT) file_name.length();
|
|
cnct->p_cnct_file.cstr_address = reinterpret_cast<const UCHAR*>(file_name.c_str());
|
|
|
|
// try again with next set of known protocols
|
|
|
|
cnct->p_cnct_user_id.cstr_length = (USHORT) user_id.getBufferLength();
|
|
cnct->p_cnct_user_id.cstr_address = user_id.getBuffer();
|
|
|
|
static const p_cnct::p_cnct_repeat protocols_to_try2[] =
|
|
{
|
|
REMOTE_PROTOCOL(PROTOCOL_VERSION4, ptype_rpc, ptype_batch_send, 1),
|
|
REMOTE_PROTOCOL(PROTOCOL_VERSION6, ptype_rpc, ptype_batch_send, 2),
|
|
};
|
|
cnct->p_cnct_count = FB_NELEM(protocols_to_try2);
|
|
|
|
for (size_t i = 0; i < cnct->p_cnct_count; i++) {
|
|
cnct->p_cnct_versions[i] = protocols_to_try2[i];
|
|
}
|
|
|
|
if (!(port = XNET_connect(/*node_name,*/ packet, status_vector, 0)))
|
|
{
|
|
delete rdb;
|
|
return NULL;
|
|
}
|
|
|
|
// Get response packet from server
|
|
|
|
rdb->rdb_port = port;
|
|
port->port_context = rdb;
|
|
port->receive(packet);
|
|
}
|
|
|
|
if (packet->p_operation == op_reject && !uv_flag)
|
|
{
|
|
disconnect(port);
|
|
packet->p_operation = op_connect;
|
|
cnct->p_cnct_operation = op_attach;
|
|
cnct->p_cnct_cversion = CONNECT_VERSION2;
|
|
cnct->p_cnct_client = ARCHITECTURE;
|
|
cnct->p_cnct_file.cstr_length = (USHORT) file_name.length();
|
|
cnct->p_cnct_file.cstr_address = reinterpret_cast<const UCHAR*>(file_name.c_str());
|
|
|
|
// try again with next set of known protocols
|
|
|
|
cnct->p_cnct_user_id.cstr_length = (USHORT) user_id.getBufferLength();
|
|
cnct->p_cnct_user_id.cstr_address = user_id.getBuffer();
|
|
|
|
static const p_cnct::p_cnct_repeat protocols_to_try3[] =
|
|
{
|
|
REMOTE_PROTOCOL(PROTOCOL_VERSION3, ptype_rpc, ptype_batch_send, 1)
|
|
};
|
|
cnct->p_cnct_count = FB_NELEM(protocols_to_try3);
|
|
|
|
for (size_t i = 0; i < cnct->p_cnct_count; i++) {
|
|
cnct->p_cnct_versions[i] = protocols_to_try3[i];
|
|
}
|
|
|
|
if (!(port = XNET_connect(/*node_name,*/ packet, status_vector, 0)))
|
|
{
|
|
delete rdb;
|
|
return NULL;
|
|
}
|
|
|
|
// Get response packet from server
|
|
|
|
rdb->rdb_port = port;
|
|
port->port_context = rdb;
|
|
port->receive(packet);
|
|
}
|
|
|
|
if (packet->p_operation != op_accept)
|
|
{
|
|
*status_vector++ = isc_arg_gds;
|
|
*status_vector++ = isc_connect_reject;
|
|
*status_vector++ = isc_arg_end;
|
|
disconnect(port);
|
|
delete rdb;
|
|
return NULL;
|
|
}
|
|
|
|
port->port_protocol = packet->p_acpt.p_acpt_version;
|
|
|
|
// Once we've decided on a protocol, concatenate the version
|
|
// string to reflect it...
|
|
|
|
Firebird::string temp;
|
|
temp.printf("%s/P%d", port->port_version->str_data,
|
|
port->port_protocol & FB_PROTOCOL_MASK);
|
|
|
|
delete port->port_version;
|
|
port->port_version = REMOTE_make_string(temp.c_str());
|
|
|
|
if (packet->p_acpt.p_acpt_architecture == ARCHITECTURE)
|
|
port->port_flags |= PORT_symmetric;
|
|
|
|
if (packet->p_acpt.p_acpt_type == ptype_rpc)
|
|
port->port_flags |= PORT_rpc;
|
|
|
|
if (packet->p_acpt.p_acpt_type != ptype_out_of_band)
|
|
port->port_flags |= PORT_no_oob;
|
|
|
|
return port;
|
|
}
|
|
|
|
|
|
rem_port* XNET_connect(//const TEXT* name,
|
|
PACKET* packet,
|
|
ISC_STATUS* status_vector,
|
|
USHORT flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* X N E T _ c o n n e c t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Establish half of a communication link.
|
|
*
|
|
**************************************/
|
|
if (xnet_shutdown)
|
|
{
|
|
Arg::StatusVector temp;
|
|
temp << Arg::Gds(isc_net_server_shutdown) << Arg::Str("XNET");
|
|
temp.copyTo(status_vector);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
if (packet)
|
|
{
|
|
return connect_client(packet, status_vector);
|
|
}
|
|
|
|
return connect_server(status_vector, flag);
|
|
}
|
|
|
|
|
|
rem_port* XNET_reconnect(ULONG client_pid, ISC_STATUS* status_vector)
|
|
{
|
|
/**************************************
|
|
*
|
|
* X N E T _ r e c o n n e c t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Classic server initialization code
|
|
*
|
|
**************************************/
|
|
|
|
rem_port* port = NULL;
|
|
XPM xpm = NULL;
|
|
|
|
// Initialize server-side IPC endpoint to a value we know we have permissions to listen at
|
|
if (strcmp(xnet_endpoint, "") == 0)
|
|
{
|
|
fb_utils::copy_terminate(xnet_endpoint, Config::getIpcName(), sizeof(xnet_endpoint));
|
|
fb_utils::prefix_kernel_object_name(xnet_endpoint, sizeof(xnet_endpoint));
|
|
}
|
|
|
|
global_slots_per_map = 1;
|
|
global_pages_per_slot = XPS_MAX_PAGES_PER_CLI;
|
|
xnet_response_event = 0;
|
|
|
|
// current_process_id used as map number
|
|
current_process_id = getpid();
|
|
|
|
try
|
|
{
|
|
TEXT name_buffer[BUFFER_TINY];
|
|
make_obj_name(name_buffer, sizeof(name_buffer), XNET_RESPONSE_EVENT);
|
|
xnet_response_event = OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xnet_response_event) {
|
|
Firebird::system_error::raise(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
xpm = make_xpm(current_process_id, 0);
|
|
|
|
port = get_server_port(client_pid, xpm, current_process_id, 0, 0, status_vector);
|
|
}
|
|
catch (const Firebird::Exception& ex)
|
|
{
|
|
stuff_exception(status_vector, ex);
|
|
xnet_log_error("Unable to initialize child process", status_vector);
|
|
|
|
if (port)
|
|
{
|
|
cleanup_port(port);
|
|
port = NULL;
|
|
}
|
|
else if (xpm)
|
|
cleanup_mapping(xpm);
|
|
}
|
|
|
|
if (xnet_response_event)
|
|
{
|
|
SetEvent(xnet_response_event); // to prevent client blocking
|
|
CloseHandle(xnet_response_event);
|
|
}
|
|
|
|
return port;
|
|
}
|
|
|
|
|
|
#ifdef SUPERCLIENT
|
|
static bool connect_init(ISC_STATUS* status)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c o n n e c t _ i n i t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Initialization of client side resources used
|
|
* when client performs connect to server
|
|
*
|
|
**************************************/
|
|
TEXT name_buffer[BUFFER_TINY];
|
|
|
|
xnet_connect_mutex = 0;
|
|
xnet_connect_map_h = 0;
|
|
xnet_connect_map = 0;
|
|
|
|
xnet_connect_event = 0;
|
|
xnet_response_event = 0;
|
|
|
|
try
|
|
{
|
|
make_obj_name(name_buffer, sizeof(name_buffer), XNET_CONNECT_MUTEX);
|
|
xnet_connect_mutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, name_buffer);
|
|
if (!xnet_connect_mutex)
|
|
{
|
|
if (ERRNO == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
Arg::Gds temp(isc_unavailable);
|
|
temp.copyTo(status);
|
|
|
|
return false;
|
|
}
|
|
|
|
Firebird::system_error::raise(ERR_STR("OpenMutex"));
|
|
}
|
|
|
|
make_obj_name(name_buffer, sizeof(name_buffer), XNET_CONNECT_EVENT);
|
|
xnet_connect_event = OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xnet_connect_event) {
|
|
Firebird::system_error::raise(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
make_obj_name(name_buffer, sizeof(name_buffer), XNET_RESPONSE_EVENT);
|
|
xnet_response_event = OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xnet_response_event) {
|
|
Firebird::system_error::raise(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
make_obj_name(name_buffer, sizeof(name_buffer), XNET_CONNECT_MAP);
|
|
xnet_connect_map_h = OpenFileMapping(FILE_MAP_WRITE, TRUE, name_buffer);
|
|
if (!xnet_connect_map_h) {
|
|
Firebird::system_error::raise(ERR_STR("OpenFileMapping"));
|
|
}
|
|
|
|
xnet_connect_map = MapViewOfFile(xnet_connect_map_h, FILE_MAP_WRITE, 0, 0,
|
|
XNET_CONNECT_RESPONZE_SIZE);
|
|
if (!xnet_connect_map) {
|
|
Firebird::system_error::raise(ERR_STR("MapViewOfFile"));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (const Firebird::Exception& ex)
|
|
{
|
|
stuff_exception(status, ex);
|
|
connect_fini();
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
static void connect_fini()
|
|
{
|
|
/**************************************
|
|
*
|
|
* c o n n e c t _ f i n i
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Release resources allocated in
|
|
* connect_init()
|
|
*
|
|
**************************************/
|
|
|
|
if (xnet_connect_mutex)
|
|
{
|
|
CloseHandle(xnet_connect_mutex);
|
|
xnet_connect_mutex = 0;
|
|
}
|
|
|
|
if (xnet_connect_event)
|
|
{
|
|
CloseHandle(xnet_connect_event);
|
|
xnet_connect_event = 0;
|
|
}
|
|
|
|
if (xnet_response_event)
|
|
{
|
|
CloseHandle(xnet_response_event);
|
|
xnet_response_event = 0;
|
|
}
|
|
|
|
if (xnet_connect_map)
|
|
{
|
|
UnmapViewOfFile(xnet_connect_map);
|
|
xnet_connect_map = 0;
|
|
}
|
|
|
|
if (xnet_connect_map_h)
|
|
{
|
|
CloseHandle(xnet_connect_map_h);
|
|
xnet_connect_map_h = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static bool accept_connection(rem_port* port, const P_CNCT*)
|
|
{
|
|
/**************************************
|
|
*
|
|
* a c c e p t _ c o n n e c t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Accept an incoming request for connection.
|
|
*
|
|
**************************************/
|
|
port->port_protocol_str = REMOTE_make_string("XNET");
|
|
|
|
// Use client process ID as remote address for XNET protocol
|
|
|
|
XCC xcc = port->port_xcc;
|
|
if (xcc)
|
|
{
|
|
XPS xps = (XPS) xcc->xcc_mapped_addr;
|
|
if (xps)
|
|
{
|
|
TEXT address[MAX_COMPUTERNAME_LENGTH + 1];
|
|
ISC_get_host(address, sizeof(address));
|
|
port->port_address_str = REMOTE_make_string(address);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static rem_port* alloc_port(rem_port* parent,
|
|
UCHAR* send_buffer,
|
|
ULONG send_length,
|
|
UCHAR* receive_buffer,
|
|
ULONG receive_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* a l l o c _ p o r t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Allocate a port block, link it in to parent (if there is a parent),
|
|
* and initialize input and output XDR streams.
|
|
*
|
|
**************************************/
|
|
rem_port* const port = new rem_port(rem_port::XNET, 0);
|
|
|
|
TEXT buffer[BUFFER_TINY];
|
|
ISC_get_host(buffer, sizeof(buffer));
|
|
port->port_host = REMOTE_make_string(buffer);
|
|
port->port_connection = REMOTE_make_string(buffer);
|
|
fb_utils::snprintf(buffer, sizeof(buffer), "XNet (%s)", port->port_host->str_data);
|
|
port->port_version = REMOTE_make_string(buffer);
|
|
|
|
port->port_accept = accept_connection;
|
|
port->port_disconnect = disconnect;
|
|
port->port_force_close = force_close;
|
|
port->port_receive_packet = receive;
|
|
port->port_send_packet = send_full;
|
|
port->port_send_partial = send_partial;
|
|
port->port_connect = aux_connect;
|
|
port->port_request = aux_request;
|
|
port->port_buff_size = send_length;
|
|
port->port_status_vector = NULL;
|
|
|
|
xdrxnet_create(&port->port_send, port, send_buffer, send_length, XDR_ENCODE);
|
|
xdrxnet_create(&port->port_receive, port, receive_buffer, 0, XDR_DECODE);
|
|
|
|
if (parent)
|
|
{
|
|
delete port->port_connection;
|
|
port->port_connection = REMOTE_make_string(parent->port_connection->str_data);
|
|
|
|
port->linkParent(parent);
|
|
}
|
|
|
|
return port;
|
|
}
|
|
|
|
|
|
static rem_port* aux_connect(rem_port* port, PACKET* /*packet*/)
|
|
{
|
|
/**************************************
|
|
*
|
|
* a u x _ c o n n e c t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Try to establish an alternative connection for handling events.
|
|
* Somebody has already done a successfull connect request.
|
|
* This uses the existing xcc for the parent port to more
|
|
* or less duplicate a new xcc for the new aux port pointing
|
|
* to the event stuff in the map.
|
|
*
|
|
**************************************/
|
|
|
|
if (port->port_server_flags)
|
|
{
|
|
port->port_flags |= PORT_async;
|
|
return port;
|
|
}
|
|
|
|
XCC parent_xcc = NULL;
|
|
XCC xcc = NULL;
|
|
TEXT name_buffer[BUFFER_TINY];
|
|
XPS xps = NULL;
|
|
XPM xpm = NULL;
|
|
|
|
try {
|
|
|
|
// make a new xcc
|
|
parent_xcc = port->port_xcc;
|
|
xps = (XPS) parent_xcc->xcc_mapped_addr;
|
|
|
|
xcc = new struct xcc;
|
|
|
|
xpm = xcc->xcc_xpm = parent_xcc->xcc_xpm;
|
|
xcc->xcc_map_num = parent_xcc->xcc_map_num;
|
|
xcc->xcc_slot = parent_xcc->xcc_slot;
|
|
DuplicateHandle(GetCurrentProcess(), parent_xcc->xcc_proc_h,
|
|
GetCurrentProcess(), &xcc->xcc_proc_h,
|
|
0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
xcc->xcc_flags = 0;
|
|
xcc->xcc_map_handle = parent_xcc->xcc_map_handle;
|
|
xcc->xcc_mapped_addr = parent_xcc->xcc_mapped_addr;
|
|
xcc->xcc_xpm->xpm_count++;
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_C2S_EVNT_CHAN_FILLED,
|
|
xcc->xcc_map_num, xcc->xcc_slot, xpm->xpm_timestamp);
|
|
xcc->xcc_event_send_channel_filled =
|
|
OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_send_channel_filled) {
|
|
Firebird::system_call_failed::raise(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_C2S_EVNT_CHAN_EMPTED,
|
|
xcc->xcc_map_num, xcc->xcc_slot, xpm->xpm_timestamp);
|
|
xcc->xcc_event_send_channel_empted =
|
|
OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_send_channel_empted) {
|
|
Firebird::system_call_failed::raise(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_S2C_EVNT_CHAN_FILLED,
|
|
xcc->xcc_map_num, xcc->xcc_slot, xpm->xpm_timestamp);
|
|
xcc->xcc_event_recv_channel_filled =
|
|
OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_recv_channel_filled) {
|
|
Firebird::system_call_failed::raise(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_S2C_EVNT_CHAN_EMPTED,
|
|
xcc->xcc_map_num, xcc->xcc_slot, xpm->xpm_timestamp);
|
|
xcc->xcc_event_recv_channel_empted =
|
|
OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_recv_channel_empted) {
|
|
Firebird::system_call_failed::raise(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
// send and receive events channels
|
|
|
|
xcc->xcc_send_channel = &xps->xps_channels[XPS_CHANNEL_C2S_EVENTS];
|
|
xcc->xcc_recv_channel = &xps->xps_channels[XPS_CHANNEL_S2C_EVENTS];
|
|
|
|
UCHAR* const channel_c2s_client_ptr =
|
|
((UCHAR*) xcc->xcc_mapped_addr + sizeof(struct xps));
|
|
UCHAR* const channel_s2c_client_ptr =
|
|
((UCHAR*) xcc->xcc_mapped_addr + sizeof(struct xps) + (XNET_EVENT_SPACE));
|
|
|
|
// alloc new port and link xcc to it
|
|
rem_port* const new_port = alloc_port(NULL,
|
|
channel_c2s_client_ptr, xcc->xcc_send_channel->xch_size,
|
|
channel_s2c_client_ptr, xcc->xcc_recv_channel->xch_size);
|
|
|
|
port->port_async = new_port;
|
|
new_port->port_flags = port->port_flags & PORT_no_oob;
|
|
new_port->port_flags |= PORT_async;
|
|
new_port->port_xcc = xcc;
|
|
|
|
return new_port;
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{
|
|
|
|
xnet_log_error("aux_connect() failed");
|
|
|
|
if (xcc)
|
|
{
|
|
if (xcc->xcc_event_send_channel_filled) {
|
|
CloseHandle(xcc->xcc_event_send_channel_filled);
|
|
}
|
|
if (xcc->xcc_event_send_channel_empted) {
|
|
CloseHandle(xcc->xcc_event_send_channel_empted);
|
|
}
|
|
if (xcc->xcc_event_recv_channel_filled) {
|
|
CloseHandle(xcc->xcc_event_recv_channel_filled);
|
|
}
|
|
if (xcc->xcc_event_recv_channel_empted) {
|
|
CloseHandle(xcc->xcc_event_recv_channel_empted);
|
|
}
|
|
delete xcc;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static rem_port* aux_request(rem_port* port, PACKET* packet)
|
|
{
|
|
/**************************************
|
|
*
|
|
* a u x _ r e q u e s t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* A remote interface has requested the server to
|
|
* prepare an auxiliary connection. This is done
|
|
* by allocating a new port and comm (xcc) structure,
|
|
* using the event stuff in the map rather than the
|
|
* normal database channels.
|
|
*
|
|
**************************************/
|
|
|
|
XCC xcc = NULL;
|
|
TEXT name_buffer[BUFFER_TINY];
|
|
|
|
try {
|
|
|
|
// make a new xcc
|
|
|
|
XCC parent_xcc = port->port_xcc;
|
|
XPS xps = (XPS) parent_xcc->xcc_mapped_addr;
|
|
|
|
xcc = new struct xcc;
|
|
|
|
XPM xpm = xcc->xcc_xpm = parent_xcc->xcc_xpm;
|
|
xcc->xcc_map_num = parent_xcc->xcc_map_num;
|
|
xcc->xcc_slot = parent_xcc->xcc_slot;
|
|
DuplicateHandle(GetCurrentProcess(), parent_xcc->xcc_proc_h,
|
|
GetCurrentProcess(), &xcc->xcc_proc_h,
|
|
0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
xcc->xcc_flags = XCCF_ASYNC;
|
|
xcc->xcc_map_handle = parent_xcc->xcc_map_handle;
|
|
xcc->xcc_mapped_addr = parent_xcc->xcc_mapped_addr;
|
|
xcc->xcc_xpm->xpm_count++;
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_C2S_EVNT_CHAN_FILLED,
|
|
xcc->xcc_map_num, xcc->xcc_slot, xpm->xpm_timestamp);
|
|
xcc->xcc_event_recv_channel_filled =
|
|
CreateEvent(ISC_get_security_desc(), FALSE, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_recv_channel_filled ||
|
|
(xcc->xcc_event_recv_channel_filled && ERRNO == ERROR_ALREADY_EXISTS))
|
|
{
|
|
Firebird::system_call_failed::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_C2S_EVNT_CHAN_EMPTED,
|
|
xcc->xcc_map_num, xcc->xcc_slot, xpm->xpm_timestamp);
|
|
xcc->xcc_event_recv_channel_empted =
|
|
CreateEvent(ISC_get_security_desc(), FALSE, TRUE, name_buffer);
|
|
if (!xcc->xcc_event_recv_channel_empted ||
|
|
(xcc->xcc_event_recv_channel_empted && ERRNO == ERROR_ALREADY_EXISTS))
|
|
{
|
|
Firebird::system_call_failed::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_S2C_EVNT_CHAN_FILLED,
|
|
xcc->xcc_map_num, xcc->xcc_slot, xpm->xpm_timestamp);
|
|
xcc->xcc_event_send_channel_filled =
|
|
CreateEvent(ISC_get_security_desc(), FALSE, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_send_channel_filled ||
|
|
(xcc->xcc_event_send_channel_filled && ERRNO == ERROR_ALREADY_EXISTS))
|
|
{
|
|
Firebird::system_call_failed::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_S2C_EVNT_CHAN_EMPTED,
|
|
xcc->xcc_map_num, xcc->xcc_slot, xpm->xpm_timestamp);
|
|
xcc->xcc_event_send_channel_empted =
|
|
CreateEvent(ISC_get_security_desc(), FALSE, TRUE, name_buffer);
|
|
if (!xcc->xcc_event_send_channel_empted ||
|
|
(xcc->xcc_event_send_channel_empted && ERRNO == ERROR_ALREADY_EXISTS))
|
|
{
|
|
Firebird::system_call_failed::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
// send and receive events channels
|
|
|
|
xcc->xcc_send_channel = &xps->xps_channels[XPS_CHANNEL_S2C_EVENTS];
|
|
xcc->xcc_recv_channel = &xps->xps_channels[XPS_CHANNEL_C2S_EVENTS];
|
|
|
|
UCHAR* const channel_s2c_client_ptr =
|
|
((UCHAR*) xcc->xcc_mapped_addr + sizeof(struct xps) + (XNET_EVENT_SPACE));
|
|
UCHAR* const channel_c2s_client_ptr =
|
|
((UCHAR*) xcc->xcc_mapped_addr + sizeof(struct xps));
|
|
|
|
// alloc new port and link xcc to it
|
|
rem_port* const new_port = alloc_port(NULL,
|
|
channel_s2c_client_ptr, xcc->xcc_send_channel->xch_size,
|
|
channel_c2s_client_ptr, xcc->xcc_recv_channel->xch_size);
|
|
|
|
port->port_async = new_port;
|
|
new_port->port_xcc = xcc;
|
|
new_port->port_flags = port->port_flags & PORT_no_oob;
|
|
new_port->port_server_flags = port->port_server_flags;
|
|
|
|
P_RESP* response = &packet->p_resp;
|
|
response->p_resp_data.cstr_length = 0;
|
|
response->p_resp_data.cstr_address = NULL;
|
|
|
|
return new_port;
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{
|
|
|
|
xnet_log_error("aux_request() failed");
|
|
|
|
if (xcc)
|
|
{
|
|
if (xcc->xcc_event_send_channel_filled) {
|
|
CloseHandle(xcc->xcc_event_send_channel_filled);
|
|
}
|
|
if (xcc->xcc_event_send_channel_empted) {
|
|
CloseHandle(xcc->xcc_event_send_channel_empted);
|
|
}
|
|
if (xcc->xcc_event_recv_channel_filled) {
|
|
CloseHandle(xcc->xcc_event_recv_channel_filled);
|
|
}
|
|
if (xcc->xcc_event_recv_channel_empted) {
|
|
CloseHandle(xcc->xcc_event_recv_channel_empted);
|
|
}
|
|
delete xcc;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
static void cleanup_comm(XCC xcc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c l e a n u p _ c o m m
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Clean up an xcc structure, close its handles,
|
|
* unmap its file, and free it.
|
|
*
|
|
**************************************/
|
|
XPS xps = (XPS) xcc->xcc_mapped_addr;
|
|
if (xps) {
|
|
xps->xps_flags |= XPS_DISCONNECTED;
|
|
}
|
|
|
|
if (xcc->xcc_event_send_channel_filled) {
|
|
CloseHandle(xcc->xcc_event_send_channel_filled);
|
|
}
|
|
if (xcc->xcc_event_send_channel_empted) {
|
|
CloseHandle(xcc->xcc_event_send_channel_empted);
|
|
}
|
|
if (xcc->xcc_event_recv_channel_filled) {
|
|
CloseHandle(xcc->xcc_event_recv_channel_filled);
|
|
}
|
|
if (xcc->xcc_event_recv_channel_empted) {
|
|
CloseHandle(xcc->xcc_event_recv_channel_empted);
|
|
}
|
|
if (xcc->xcc_proc_h) {
|
|
CloseHandle(xcc->xcc_proc_h);
|
|
}
|
|
|
|
XPM xpm = xcc->xcc_xpm;
|
|
if (xpm && !(xcc->xcc_flags & XCCF_ASYNC)) {
|
|
cleanup_mapping(xpm);
|
|
}
|
|
|
|
delete xcc;
|
|
}
|
|
|
|
|
|
static void cleanup_mapping(XPM xpm)
|
|
{
|
|
Firebird::MutexLockGuard guard(xnet_mutex);
|
|
|
|
// if this was the last area for this map, unmap it
|
|
xpm->xpm_count--;
|
|
if (!xpm->xpm_count && global_client_maps)
|
|
{
|
|
UnmapViewOfFile(xpm->xpm_address);
|
|
CloseHandle(xpm->xpm_handle);
|
|
|
|
// find xpm in chain and release
|
|
|
|
for (XPM* pxpm = &global_client_maps; *pxpm; pxpm = &(*pxpm)->xpm_next)
|
|
{
|
|
if (*pxpm == xpm)
|
|
{
|
|
*pxpm = xpm->xpm_next;
|
|
break;
|
|
}
|
|
}
|
|
|
|
delete xpm;
|
|
}
|
|
}
|
|
|
|
|
|
static void cleanup_port(rem_port* port)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c l e a n u p _ p o r t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Walk through the port structure freeing
|
|
* allocated memory and then free the port.
|
|
*
|
|
**************************************/
|
|
|
|
if (port->port_xcc)
|
|
{
|
|
cleanup_comm(port->port_xcc);
|
|
port->port_xcc = NULL;
|
|
}
|
|
|
|
port->release();
|
|
}
|
|
|
|
|
|
#ifdef SUPERCLIENT
|
|
static void raise_lostconn_or_syserror(const char* msg)
|
|
{
|
|
if (ERRNO == ERROR_FILE_NOT_FOUND)
|
|
Firebird::status_exception::raise(Arg::Gds(isc_lost_db_connection));
|
|
else
|
|
Firebird::system_error::raise(msg);
|
|
}
|
|
#endif
|
|
|
|
|
|
static rem_port* connect_client(PACKET* packet, ISC_STATUS* status_vector)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c o n n e c t _ c l i e n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Establish a client side part of the connection
|
|
*
|
|
**************************************/
|
|
#ifndef SUPERCLIENT
|
|
return NULL;
|
|
#else
|
|
if (!xnet_initialized)
|
|
{
|
|
Firebird::MutexLockGuard guard(xnet_mutex);
|
|
if (!xnet_initialized)
|
|
{
|
|
xnet_initialized = true;
|
|
current_process_id = getpid();
|
|
gds__register_cleanup((FPTR_VOID_PTR) exit_handler, NULL);
|
|
|
|
// Allow other (server) process to SYNCHRONIZE with our process
|
|
// to establish communication
|
|
ISC_get_security_desc();
|
|
}
|
|
}
|
|
|
|
// set up for unavailable server
|
|
status_vector[0] = isc_arg_gds;
|
|
status_vector[1] = isc_unavailable;
|
|
status_vector[2] = isc_arg_end;
|
|
|
|
XNET_RESPONSE response;
|
|
|
|
{ // xnet_mutex scope
|
|
Firebird::MutexLockGuard guard(xnet_mutex);
|
|
|
|
// First, try to connect using default kernel namespace.
|
|
// This should work on Win9X, NT4 and on later OS when server is running
|
|
// under restricted account in the same session as the client
|
|
fb_utils::copy_terminate(xnet_endpoint, Config::getIpcName(), sizeof(xnet_endpoint));
|
|
|
|
if (!connect_init(status_vector))
|
|
{
|
|
// The client may not have permissions to create global objects,
|
|
// but still be able to connect to a local server that has such permissions.
|
|
// This is why we try to connect using Global\ namespace unconditionally
|
|
fb_utils::snprintf(xnet_endpoint, sizeof(xnet_endpoint), "Global\\%s", Config::getIpcName());
|
|
|
|
fb_utils::init_status(status_vector);
|
|
if (!connect_init(status_vector)) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// setup status with net read error in case of wait timeout
|
|
Arg::StatusVector temp;
|
|
temp << Arg::Gds(isc_net_read_err);
|
|
DWORD err = 0;
|
|
|
|
static const int timeout = Config::getConnectionTimeout() * 1000;
|
|
|
|
// waiting for XNET connect lock to release
|
|
|
|
err = WaitForSingleObject(xnet_connect_mutex, timeout);
|
|
if (err != WAIT_OBJECT_0)
|
|
{
|
|
connect_fini();
|
|
|
|
temp << SYS_ERR(err);
|
|
temp.copyTo(status_vector);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// writing connect request
|
|
|
|
// mark connect area with XNET_INVALID_MAP_NUM to
|
|
// detect server faults on response
|
|
|
|
((XNET_RESPONSE*) xnet_connect_map)->map_num = XNET_INVALID_MAP_NUM;
|
|
((XNET_RESPONSE*) xnet_connect_map)->proc_id = current_process_id;
|
|
|
|
SetEvent(xnet_connect_event);
|
|
|
|
// waiting for server response
|
|
|
|
err = WaitForSingleObject(xnet_response_event, timeout);
|
|
if (err != WAIT_OBJECT_0)
|
|
{
|
|
ReleaseMutex(xnet_connect_mutex);
|
|
connect_fini();
|
|
|
|
temp << SYS_ERR(err);
|
|
temp.copyTo(status_vector);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(&response, xnet_connect_map, XNET_CONNECT_RESPONZE_SIZE);
|
|
ReleaseMutex(xnet_connect_mutex);
|
|
connect_fini();
|
|
} // xnet_mutex scope
|
|
|
|
if (response.map_num == XNET_INVALID_MAP_NUM)
|
|
{
|
|
xnet_log_error("Server failed to respond on connect request");
|
|
|
|
Arg::StatusVector temp;
|
|
temp << Arg::Gds(isc_net_connect_err);
|
|
temp.copyTo(status_vector);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
global_pages_per_slot = response.pages_per_slot;
|
|
global_slots_per_map = response.slots_per_map;
|
|
const ULONG map_num = response.map_num;
|
|
const ULONG slot_num = response.slot_num;
|
|
const ULONG timestamp = response.timestamp;
|
|
|
|
TEXT name_buffer[BUFFER_TINY];
|
|
FILE_ID file_handle = 0;
|
|
CADDR_T mapped_address = 0;
|
|
|
|
XCC xcc = NULL;
|
|
XPM xpm = NULL;
|
|
XPS xps = NULL;
|
|
|
|
try {
|
|
|
|
{ // xnet_mutex scope
|
|
Firebird::MutexLockGuard guard(xnet_mutex);
|
|
|
|
// see if area is already mapped for this client
|
|
|
|
for (xpm = global_client_maps; xpm; xpm = xpm->xpm_next)
|
|
{
|
|
if (xpm->xpm_number == map_num &&
|
|
xpm->xpm_timestamp == timestamp &&
|
|
!(xpm->xpm_flags & XPMF_SERVER_SHUTDOWN))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!xpm)
|
|
{
|
|
|
|
// Area hasn't been mapped. Open new file mapping.
|
|
|
|
make_map_name(name_buffer, sizeof(name_buffer), XNET_MAPPED_FILE_NAME,
|
|
map_num, timestamp);
|
|
file_handle = OpenFileMapping(FILE_MAP_WRITE, FALSE, name_buffer);
|
|
if (!file_handle) {
|
|
raise_lostconn_or_syserror(ERR_STR("OpenFileMapping"));
|
|
}
|
|
|
|
mapped_address = MapViewOfFile(file_handle, FILE_MAP_WRITE, 0L, 0L,
|
|
XPS_MAPPED_SIZE(global_slots_per_map, global_pages_per_slot));
|
|
if (!mapped_address) {
|
|
Firebird::system_error::raise(ERR_STR("MapViewOfFile"));
|
|
}
|
|
|
|
xpm = new struct xpm;
|
|
|
|
xpm->xpm_next = global_client_maps;
|
|
global_client_maps = xpm;
|
|
xpm->xpm_count = 0;
|
|
xpm->xpm_number = map_num;
|
|
xpm->xpm_handle = file_handle;
|
|
xpm->xpm_address = mapped_address;
|
|
xpm->xpm_timestamp = timestamp;
|
|
xpm->xpm_flags = 0;
|
|
}
|
|
|
|
xpm->xpm_count++;
|
|
} // xnet_mutex scope
|
|
|
|
// there's no thread structure, so make one
|
|
xcc = new struct xcc;
|
|
|
|
xcc->xcc_map_handle = xpm->xpm_handle;
|
|
xcc->xcc_mapped_addr =
|
|
(UCHAR*) xpm->xpm_address + XPS_SLOT_OFFSET(global_pages_per_slot, slot_num);
|
|
xcc->xcc_map_num = map_num;
|
|
xcc->xcc_slot = slot_num;
|
|
xcc->xcc_xpm = xpm;
|
|
xcc->xcc_flags = 0;
|
|
xcc->xcc_proc_h = 0;
|
|
|
|
xps = (XPS) xcc->xcc_mapped_addr;
|
|
|
|
// only speak if server has correct protocol
|
|
|
|
if (xps->xps_server_protocol != XPI_SERVER_PROTOCOL_VERSION) {
|
|
Firebird::fatal_exception::raise("Unknown XNET protocol version");
|
|
}
|
|
|
|
xps->xps_client_protocol = XPI_CLIENT_PROTOCOL_VERSION;
|
|
|
|
// open server process handle to watch server health
|
|
// during communication session
|
|
|
|
xcc->xcc_proc_h = OpenProcess(SYNCHRONIZE, 0, xps->xps_server_proc_id);
|
|
if (!xcc->xcc_proc_h) {
|
|
Firebird::system_error::raise(ERR_STR("OpenProcess"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_C2S_DATA_CHAN_FILLED,
|
|
map_num, slot_num, timestamp);
|
|
xcc->xcc_event_send_channel_filled = OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_send_channel_filled) {
|
|
raise_lostconn_or_syserror(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_C2S_DATA_CHAN_EMPTED,
|
|
map_num, slot_num, timestamp);
|
|
xcc->xcc_event_send_channel_empted = OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_send_channel_empted) {
|
|
raise_lostconn_or_syserror(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_S2C_DATA_CHAN_FILLED,
|
|
map_num, slot_num, timestamp);
|
|
xcc->xcc_event_recv_channel_filled = OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_recv_channel_filled) {
|
|
raise_lostconn_or_syserror(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_S2C_DATA_CHAN_EMPTED,
|
|
map_num, slot_num, timestamp);
|
|
xcc->xcc_event_recv_channel_empted = OpenEvent(EVENT_ALL_ACCESS, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_recv_channel_empted) {
|
|
raise_lostconn_or_syserror(ERR_STR("OpenEvent"));
|
|
}
|
|
|
|
// added this here from the server side as this part is called by the client
|
|
// and the server address need not be valid for the client -smistry 10/29/98
|
|
xcc->xcc_recv_channel = &xps->xps_channels[XPS_CHANNEL_S2C_DATA];
|
|
xcc->xcc_send_channel = &xps->xps_channels[XPS_CHANNEL_C2S_DATA];
|
|
|
|
// we also need to add client side flags or channel pointer as they
|
|
// differ from the server side
|
|
|
|
const ULONG avail =
|
|
(ULONG) (XPS_USEFUL_SPACE(global_pages_per_slot) - (XNET_EVENT_SPACE * 2)) / 2;
|
|
UCHAR* start_ptr = (UCHAR*) xps + (sizeof(struct xps) + (XNET_EVENT_SPACE * 2));
|
|
|
|
// send and receive channels
|
|
UCHAR* const channel_c2s_client_ptr = start_ptr;
|
|
UCHAR* const channel_s2c_client_ptr = start_ptr + avail;
|
|
|
|
rem_port* const port =
|
|
alloc_port(NULL,
|
|
channel_c2s_client_ptr, xcc->xcc_send_channel->xch_size,
|
|
channel_s2c_client_ptr, xcc->xcc_recv_channel->xch_size);
|
|
|
|
status_vector[1] = FB_SUCCESS;
|
|
port->port_status_vector = status_vector;
|
|
port->port_xcc = xcc;
|
|
gds__register_cleanup((FPTR_VOID_PTR) exit_handler, port);
|
|
send_full(port, packet);
|
|
|
|
return port;
|
|
}
|
|
catch (const Firebird::Exception& ex)
|
|
{
|
|
stuff_exception(status_vector, ex);
|
|
|
|
if (xcc)
|
|
cleanup_comm(xcc);
|
|
else if (xpm)
|
|
cleanup_mapping(xpm);
|
|
else if (file_handle)
|
|
{
|
|
if (mapped_address) {
|
|
UnmapViewOfFile(mapped_address);
|
|
}
|
|
CloseHandle(file_handle);
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
static rem_port* connect_server(ISC_STATUS* status_vector, USHORT flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c o n n e c t _ s e r v e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Establish a server side part of the connection
|
|
*
|
|
**************************************/
|
|
current_process_id = getpid();
|
|
|
|
if (!server_init(status_vector, flag))
|
|
return NULL;
|
|
|
|
XNET_RESPONSE* const presponse = (XNET_RESPONSE*) xnet_connect_map;
|
|
|
|
while (!xnet_shutdown)
|
|
{
|
|
|
|
const DWORD wait_res = WaitForSingleObject(xnet_connect_event, INFINITE);
|
|
|
|
if (wait_res != WAIT_OBJECT_0)
|
|
{
|
|
xnet_log_error("WaitForSingleObject() failed");
|
|
break;
|
|
}
|
|
|
|
if (xnet_shutdown)
|
|
break;
|
|
|
|
// read client process id
|
|
const ULONG client_pid = presponse->proc_id;
|
|
if (!client_pid)
|
|
continue; // dummy xnet_connect_event fire - no connect request
|
|
|
|
presponse->slots_per_map = global_slots_per_map;
|
|
presponse->pages_per_slot = global_pages_per_slot;
|
|
presponse->timestamp = 0;
|
|
|
|
if (flag & (SRVR_debug | SRVR_multi_client))
|
|
{
|
|
XPM xpm = NULL;
|
|
try
|
|
{
|
|
// MSDN says: In Visual C++ 2005, time is a wrapper for _time64 and time_t is,
|
|
// by default, equivalent to __time64_t.
|
|
// This means that sizeof(time_t) > sizeof(ULONG).
|
|
ULONG timestamp = (ULONG) time(NULL);
|
|
ULONG map_num, slot_num;
|
|
// searching for free slot
|
|
xpm = get_free_slot(&map_num, &slot_num, ×tamp);
|
|
|
|
// pack combined mapped area and number
|
|
presponse->proc_id = 0;
|
|
presponse->map_num = map_num;
|
|
presponse->slot_num = slot_num;
|
|
presponse->timestamp = timestamp;
|
|
|
|
rem_port* port = get_server_port(client_pid, xpm, map_num, slot_num,
|
|
timestamp, status_vector);
|
|
|
|
SetEvent(xnet_response_event);
|
|
|
|
return port;
|
|
}
|
|
catch (const Firebird::Exception& ex)
|
|
{
|
|
stuff_exception(status_vector, ex);
|
|
xnet_log_error("Failed to allocate server port for communication", status_vector);
|
|
fb_utils::init_status(status_vector);
|
|
|
|
if (xpm)
|
|
cleanup_mapping(xpm);
|
|
|
|
SetEvent(xnet_response_event);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// in case process we'll fail to start child process
|
|
presponse->slot_num = 0;
|
|
|
|
// child process ID (presponse->map_num) used as map number
|
|
if (!fork(client_pid, flag, &presponse->map_num))
|
|
{
|
|
// if fork successfully creates child process, then
|
|
// child process will call SetEvent(xnet_response_event)by itself
|
|
SetEvent(xnet_response_event);
|
|
}
|
|
}
|
|
|
|
if (xnet_shutdown)
|
|
{
|
|
Arg::StatusVector temp;
|
|
temp << Arg::Gds(isc_net_server_shutdown) << Arg::Str("XNET");
|
|
temp.copyTo(status_vector);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void disconnect(rem_port* port)
|
|
{
|
|
/**************************************
|
|
*
|
|
* d i s c o n n e c t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Break a remote connection.
|
|
*
|
|
**************************************/
|
|
|
|
if (port->port_async)
|
|
{
|
|
disconnect(port->port_async);
|
|
port->port_async = NULL;
|
|
}
|
|
|
|
// If this is a sub-port, unlink it from it's parent
|
|
port->unlinkParent();
|
|
|
|
if (port->port_flags & PORT_server)
|
|
xnet_ports->unRegisterPort(port);
|
|
else
|
|
gds__unregister_cleanup((FPTR_VOID_PTR)(exit_handler), port);
|
|
|
|
cleanup_port(port);
|
|
}
|
|
|
|
|
|
static void force_close(rem_port* port)
|
|
{
|
|
/**************************************
|
|
*
|
|
* f o r c e _ c l o s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Forcibly close remote connection.
|
|
*
|
|
**************************************/
|
|
if (port->port_state != rem_port::PENDING || !port->port_xcc)
|
|
return;
|
|
|
|
port->port_state = rem_port::BROKEN;
|
|
|
|
XCC xcc = port->port_xcc;
|
|
|
|
if (!xcc)
|
|
return;
|
|
|
|
if (xcc->xcc_event_send_channel_filled)
|
|
{
|
|
CloseHandle(xcc->xcc_event_send_channel_filled);
|
|
xcc->xcc_event_send_channel_filled = 0;
|
|
}
|
|
if (xcc->xcc_event_send_channel_empted)
|
|
{
|
|
CloseHandle(xcc->xcc_event_send_channel_empted);
|
|
xcc->xcc_event_send_channel_empted = 0;
|
|
}
|
|
if (xcc->xcc_event_recv_channel_filled)
|
|
{
|
|
CloseHandle(xcc->xcc_event_recv_channel_filled);
|
|
xcc->xcc_event_recv_channel_filled = 0;
|
|
}
|
|
if (xcc->xcc_event_recv_channel_empted)
|
|
{
|
|
CloseHandle(xcc->xcc_event_recv_channel_empted);
|
|
xcc->xcc_event_recv_channel_empted = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void exit_handler(rem_port* main_port)
|
|
{
|
|
/**************************************
|
|
*
|
|
* e x i t _ h a n d l e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Free port resources or everything
|
|
*
|
|
**************************************/
|
|
xnet_shutdown = true;
|
|
if (main_port) {
|
|
disconnect(main_port);
|
|
}
|
|
else {
|
|
release_all();
|
|
}
|
|
}
|
|
|
|
static int cleanup_ports(const int, const int, void* /*arg*/)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c l e a n u p _ p o r t s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Shutdown all active connections
|
|
* to allow correct shutdown.
|
|
*
|
|
**************************************/
|
|
xnet_shutdown = true;
|
|
|
|
SetEvent(xnet_connect_event);
|
|
xnet_ports->closePorts();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static rem_port* receive( rem_port* main_port, PACKET* packet)
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e c e i v e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Receive a message from a port.
|
|
*
|
|
**************************************/
|
|
|
|
if (!xdr_protocol(&main_port->port_receive, packet))
|
|
packet->p_operation = op_exit;
|
|
|
|
return main_port;
|
|
}
|
|
|
|
|
|
static int send_full( rem_port* port, PACKET* packet)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s e n d _ f u l l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Send a packet across a port to another process.
|
|
* Flush data to remote interface
|
|
*
|
|
**************************************/
|
|
|
|
if (!xdr_protocol(&port->port_send, packet))
|
|
return FALSE;
|
|
|
|
if (xnet_write(&port->port_send))
|
|
return TRUE;
|
|
|
|
xnet_error(port, isc_net_write_err, ERRNO);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static int send_partial( rem_port* port, PACKET* packet)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s e n d _ p a r t i a l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Send a packet across a port to another process.
|
|
*
|
|
**************************************/
|
|
|
|
return xdr_protocol(&port->port_send, packet);
|
|
}
|
|
|
|
#ifdef SUPERCLIENT
|
|
static void server_shutdown(rem_port* port)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s e r v e r _ s h u t d o w n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Server shutdown handler (client side only).
|
|
*
|
|
**************************************/
|
|
xnet_log_error("Server shutdown detected");
|
|
|
|
XCC xcc = port->port_xcc;
|
|
xcc->xcc_flags |= XCCF_SERVER_SHUTDOWN;
|
|
|
|
XPM xpm = xcc->xcc_xpm;
|
|
if (!(xpm->xpm_flags & XPMF_SERVER_SHUTDOWN))
|
|
{
|
|
|
|
const ULONG dead_proc_id = XPS(xpm->xpm_address)->xps_server_proc_id;
|
|
|
|
// mark all mapped areas connected to server with dead_proc_id
|
|
|
|
Firebird::MutexLockGuard guard(xnet_mutex);
|
|
|
|
for (xpm = global_client_maps; xpm; xpm = xpm->xpm_next)
|
|
{
|
|
if (!(xpm->xpm_flags & XPMF_SERVER_SHUTDOWN) &&
|
|
XPS(xpm->xpm_address)->xps_server_proc_id == dead_proc_id)
|
|
{
|
|
xpm->xpm_flags |= XPMF_SERVER_SHUTDOWN;
|
|
xpm->xpm_handle = 0;
|
|
xpm->xpm_address = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // SUPERCLIENT
|
|
|
|
|
|
static int xdrxnet_create(XDR* xdrs, rem_port* port, UCHAR* buffer, USHORT length, xdr_op x_op)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x d r x n e t _ c r e a t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Initialize an XDR stream.
|
|
*
|
|
**************************************/
|
|
|
|
xdrs->x_public = (caddr_t) port;
|
|
xdrs->x_private = reinterpret_cast<SCHAR*>(buffer);
|
|
xdrs->x_base = xdrs->x_private;
|
|
xdrs->x_handy = length;
|
|
xdrs->x_ops = &xnet_ops;
|
|
xdrs->x_op = x_op;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static int xnet_destroy( XDR* /*xdrs*/)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ d e s t r o y
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Destroy a stream. A no-op.
|
|
*
|
|
**************************************/
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void xnet_gen_error (rem_port* port, const Firebird::Arg::StatusVector& v)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ g e n _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* An error has occurred. Mark the port as broken.
|
|
* Format the status vector if there is one and
|
|
* save the status vector strings in a permanent place.
|
|
*
|
|
**************************************/
|
|
port->port_state = rem_port::BROKEN;
|
|
|
|
ISC_STATUS* status_vector = NULL;
|
|
if (port->port_context != NULL) {
|
|
status_vector = port->port_context->get_status_vector();
|
|
}
|
|
if (status_vector == NULL) {
|
|
status_vector = port->port_status_vector;
|
|
}
|
|
if (status_vector != NULL)
|
|
{
|
|
v.copyTo(status_vector);
|
|
REMOTE_save_status_strings(status_vector);
|
|
}
|
|
}
|
|
|
|
|
|
static void xnet_error(rem_port* port, ISC_STATUS operation, int status)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* An I/O error has occurred. If a status vector is present,
|
|
* generate an error return. In any case, return NULL, which
|
|
* is used to indicate and error.
|
|
*
|
|
**************************************/
|
|
if (status)
|
|
xnet_gen_error(port, Arg::Gds(operation) << SYS_ERR(status));
|
|
else
|
|
xnet_gen_error(port, Arg::Gds(operation));
|
|
}
|
|
|
|
|
|
static bool_t xnet_getbytes(XDR* xdrs, SCHAR* buff, u_int count)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ g e t b y t e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Fetch a bunch of bytes from remote interface.
|
|
*
|
|
**************************************/
|
|
|
|
SLONG bytecount = count;
|
|
|
|
#ifdef SUPERCLIENT
|
|
rem_port* port = (rem_port*) xdrs->x_public;
|
|
XCC xcc = port->port_xcc;
|
|
//XCH xch = xcc->xcc_recv_channel;
|
|
XPM xpm = xcc->xcc_xpm;
|
|
#endif
|
|
|
|
while (bytecount && !xnet_shutdown)
|
|
{
|
|
#ifdef SUPERCLIENT
|
|
if (xpm->xpm_flags & XPMF_SERVER_SHUTDOWN)
|
|
{
|
|
if (!(xcc->xcc_flags & XCCF_SERVER_SHUTDOWN))
|
|
{
|
|
xcc->xcc_flags |= XCCF_SERVER_SHUTDOWN;
|
|
xnet_error(port, isc_lost_db_connection, 0);
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
SLONG to_copy;
|
|
if (xdrs->x_handy >= bytecount)
|
|
to_copy = bytecount;
|
|
else
|
|
to_copy = xdrs->x_handy;
|
|
|
|
if (xdrs->x_handy)
|
|
{
|
|
if (to_copy == sizeof(SLONG))
|
|
*((SLONG*)buff) = *((SLONG*)xdrs->x_private);
|
|
else
|
|
memcpy(buff, xdrs->x_private, to_copy);
|
|
|
|
xdrs->x_handy -= to_copy;
|
|
xdrs->x_private += to_copy;
|
|
}
|
|
else
|
|
{
|
|
if (!xnet_read(xdrs))
|
|
return FALSE;
|
|
}
|
|
|
|
if (to_copy)
|
|
{
|
|
bytecount -= to_copy;
|
|
buff += to_copy;
|
|
}
|
|
}
|
|
|
|
return xnet_shutdown ? FALSE : TRUE;
|
|
}
|
|
|
|
|
|
static bool_t xnet_getlong(XDR* xdrs, SLONG* lp)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ g e t l o n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Fetch a longword from memory stream.
|
|
*
|
|
**************************************/
|
|
|
|
return (*xdrs->x_ops->x_getbytes) (xdrs, reinterpret_cast<SCHAR*>(lp), 4);
|
|
}
|
|
|
|
|
|
static u_int xnet_getpostn(XDR* xdrs)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ g e t p o s t n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get the current position (which is also current length) from stream.
|
|
*
|
|
**************************************/
|
|
|
|
return (u_int) (xdrs->x_private - xdrs->x_base);
|
|
}
|
|
|
|
|
|
static caddr_t xnet_inline(XDR* xdrs, u_int bytecount)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ i n l i n e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Return a pointer to somewhere in the buffer.
|
|
*
|
|
**************************************/
|
|
|
|
if (bytecount > (u_int) xdrs->x_handy)
|
|
return FALSE;
|
|
|
|
return xdrs->x_base + bytecount;
|
|
}
|
|
|
|
|
|
static bool_t xnet_putbytes(XDR* xdrs, const SCHAR* buff, u_int count)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ p u t b y t e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Put a bunch of bytes into a memory stream.
|
|
*
|
|
**************************************/
|
|
SLONG bytecount = count;
|
|
|
|
rem_port* port = (rem_port*)xdrs->x_public;
|
|
XCC xcc = port->port_xcc;
|
|
XCH xch = xcc->xcc_send_channel;
|
|
#ifdef SUPERCLIENT
|
|
XPM xpm = xcc->xcc_xpm;
|
|
#endif
|
|
XPS xps = (XPS) xcc->xcc_mapped_addr;
|
|
|
|
while (bytecount && !xnet_shutdown)
|
|
{
|
|
#ifdef SUPERCLIENT
|
|
if (xpm->xpm_flags & XPMF_SERVER_SHUTDOWN)
|
|
{
|
|
if (!(xcc->xcc_flags & XCCF_SERVER_SHUTDOWN))
|
|
{
|
|
xcc->xcc_flags |= XCCF_SERVER_SHUTDOWN;
|
|
xnet_error(port, isc_lost_db_connection, 0);
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
SLONG to_copy;
|
|
if (xdrs->x_handy >= bytecount)
|
|
to_copy = bytecount;
|
|
else
|
|
to_copy = xdrs->x_handy;
|
|
|
|
if (xdrs->x_handy)
|
|
{
|
|
|
|
if ((ULONG) xdrs->x_handy == xch->xch_size)
|
|
{
|
|
|
|
while (!xnet_shutdown)
|
|
{
|
|
#ifdef SUPERCLIENT
|
|
if (xpm->xpm_flags & XPMF_SERVER_SHUTDOWN)
|
|
{
|
|
if (!(xcc->xcc_flags & XCCF_SERVER_SHUTDOWN))
|
|
{
|
|
xcc->xcc_flags |= XCCF_SERVER_SHUTDOWN;
|
|
xnet_error(port, isc_lost_db_connection, 0);
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
const DWORD wait_result =
|
|
WaitForSingleObject(xcc->xcc_event_send_channel_empted,
|
|
XNET_SEND_WAIT_TIMEOUT);
|
|
|
|
if (wait_result == WAIT_OBJECT_0) {
|
|
break;
|
|
}
|
|
if (wait_result == WAIT_TIMEOUT)
|
|
{
|
|
// Check whether another side is alive
|
|
if (WaitForSingleObject(xcc->xcc_proc_h, 1) == WAIT_TIMEOUT)
|
|
{
|
|
// Check whether the channel has been disconnected
|
|
if (!(xps->xps_flags & XPS_DISCONNECTED))
|
|
continue; // another side is alive
|
|
}
|
|
|
|
// Another side is dead or something bad has happened
|
|
#ifdef SUPERCLIENT
|
|
if (!(xps->xps_flags & XPS_DISCONNECTED)) {
|
|
server_shutdown(port);
|
|
}
|
|
#endif
|
|
xnet_error(port, CONN_LOST_ERROR, 0);
|
|
return FALSE;
|
|
}
|
|
|
|
xnet_error(port, isc_net_write_err, ERRNO);
|
|
return FALSE; // a non-timeout result is an error
|
|
}
|
|
}
|
|
|
|
if (to_copy == sizeof(SLONG))
|
|
*((SLONG*)xdrs->x_private) = *((SLONG*)buff);
|
|
else
|
|
memcpy(xdrs->x_private, buff, to_copy);
|
|
|
|
xdrs->x_handy -= to_copy;
|
|
xdrs->x_private += to_copy;
|
|
}
|
|
else
|
|
{
|
|
if (!xnet_write(xdrs))
|
|
{
|
|
xnet_error(port, isc_net_write_err, ERRNO);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (to_copy)
|
|
{
|
|
bytecount -= to_copy;
|
|
buff += to_copy;
|
|
}
|
|
}
|
|
|
|
return xnet_shutdown ? FALSE : TRUE;
|
|
}
|
|
|
|
|
|
static bool_t xnet_putlong(XDR* xdrs, const SLONG* lp)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ p u t l o n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Fit a longword into a memory stream if it fits.
|
|
*
|
|
**************************************/
|
|
|
|
return (*xdrs->x_ops->x_putbytes) (xdrs, reinterpret_cast<const char*>(lp), 4);
|
|
}
|
|
|
|
|
|
static bool_t xnet_read(XDR* xdrs)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ r e a d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Read a buffer full of data.
|
|
*
|
|
**************************************/
|
|
rem_port* port = (rem_port*)xdrs->x_public;
|
|
XCC xcc = port->port_xcc;
|
|
XCH xch = xcc->xcc_recv_channel;
|
|
#ifdef SUPERCLIENT
|
|
XPM xpm = xcc->xcc_xpm;
|
|
#endif
|
|
XPS xps = (XPS) xcc->xcc_mapped_addr;
|
|
|
|
if (xnet_shutdown)
|
|
return FALSE;
|
|
|
|
if (!SetEvent(xcc->xcc_event_recv_channel_empted))
|
|
{
|
|
xnet_error(port, isc_net_read_err, ERRNO);
|
|
return FALSE;
|
|
}
|
|
|
|
while (!xnet_shutdown)
|
|
{
|
|
#ifdef SUPERCLIENT
|
|
if (xpm->xpm_flags & XPMF_SERVER_SHUTDOWN)
|
|
{
|
|
if (!(xcc->xcc_flags & XCCF_SERVER_SHUTDOWN))
|
|
{
|
|
xcc->xcc_flags |= XCCF_SERVER_SHUTDOWN;
|
|
xnet_error(port, isc_lost_db_connection, 0);
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
const DWORD wait_result =
|
|
WaitForSingleObject(xcc->xcc_event_recv_channel_filled, XNET_RECV_WAIT_TIMEOUT);
|
|
|
|
if (wait_result == WAIT_OBJECT_0)
|
|
{
|
|
// Client has written some data for us (server) to read
|
|
xdrs->x_handy = xch->xch_length;
|
|
xdrs->x_private = xdrs->x_base;
|
|
return TRUE;
|
|
}
|
|
if (wait_result == WAIT_TIMEOUT)
|
|
{
|
|
// Check if another side is alive
|
|
if (WaitForSingleObject(xcc->xcc_proc_h, 1) == WAIT_TIMEOUT)
|
|
{
|
|
// Check whether the channel has been disconnected
|
|
if (!(xps->xps_flags & XPS_DISCONNECTED))
|
|
continue; // another side is alive
|
|
}
|
|
|
|
// Another side is dead or something bad has happened
|
|
#ifdef SUPERCLIENT
|
|
if (!(xps->xps_flags & XPS_DISCONNECTED)) {
|
|
server_shutdown(port);
|
|
}
|
|
#endif
|
|
xnet_error(port, CONN_LOST_ERROR, 0);
|
|
return FALSE;
|
|
}
|
|
|
|
xnet_error(port, isc_net_read_err, ERRNO);
|
|
return FALSE; // a non-timeout result is an error
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static bool_t xnet_write(XDR* xdrs)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ w r i t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Signal remote interface that memory stream is
|
|
* filled and ready for reading.
|
|
*
|
|
**************************************/
|
|
rem_port* port = (rem_port*)xdrs->x_public;
|
|
XCC xcc = port->port_xcc;
|
|
XCH xch = xcc->xcc_send_channel;
|
|
|
|
xch->xch_length = xdrs->x_private - xdrs->x_base;
|
|
if (SetEvent(xcc->xcc_event_send_channel_filled))
|
|
{
|
|
xdrs->x_private = xdrs->x_base;
|
|
xdrs->x_handy = xch->xch_size;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static bool_t xnet_setpostn(XDR* xdrs, u_int bytecount)
|
|
{
|
|
/**************************************
|
|
*
|
|
* x n e t _ s e t p o s t n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Set the current position (which is also current length) from stream.
|
|
*
|
|
**************************************/
|
|
|
|
if (bytecount > (u_int) xdrs->x_handy)
|
|
return FALSE;
|
|
xdrs->x_private = xdrs->x_base + bytecount;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void release_all()
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e l e a s e _ a l l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Release all connections and dependant stuff.
|
|
*
|
|
**************************************/
|
|
|
|
if (!xnet_initialized)
|
|
return;
|
|
|
|
#ifndef SUPERCLIENT
|
|
connect_fini();
|
|
#endif
|
|
|
|
Firebird::MutexLockGuard guard(xnet_mutex);
|
|
|
|
// release all map stuf left not released by broken ports
|
|
|
|
XPM xpm, nextxpm;
|
|
for (xpm = nextxpm = global_client_maps; nextxpm; xpm = nextxpm)
|
|
{
|
|
nextxpm = nextxpm->xpm_next;
|
|
UnmapViewOfFile(xpm->xpm_address);
|
|
CloseHandle(xpm->xpm_handle);
|
|
delete xpm;
|
|
}
|
|
|
|
global_client_maps = NULL;
|
|
xnet_initialized = false;
|
|
}
|
|
|
|
|
|
/***********************************************************************/
|
|
/********************** ONLY SERVER CODE FROM HERE *********************/
|
|
/***********************************************************************/
|
|
|
|
static bool make_map(ULONG map_number, ULONG timestamp, FILE_ID* map_handle, CADDR_T* map_address)
|
|
{
|
|
/**************************************
|
|
*
|
|
* m a k e _ m a p
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Create memory map
|
|
*
|
|
**************************************/
|
|
TEXT name_buffer[BUFFER_TINY];
|
|
|
|
make_map_name(name_buffer, sizeof(name_buffer), XNET_MAPPED_FILE_NAME, map_number, timestamp);
|
|
*map_handle = CreateFileMapping(INVALID_HANDLE_VALUE,
|
|
ISC_get_security_desc(),
|
|
PAGE_READWRITE,
|
|
0L,
|
|
XPS_MAPPED_SIZE(global_slots_per_map, global_pages_per_slot),
|
|
name_buffer);
|
|
|
|
try
|
|
{
|
|
if (!(*map_handle) || (ERRNO == ERROR_ALREADY_EXISTS))
|
|
Firebird::system_error::raise(ERR_STR("CreateFileMapping"));
|
|
|
|
*map_address = MapViewOfFile(*map_handle, FILE_MAP_WRITE, 0, 0,
|
|
XPS_MAPPED_SIZE(global_slots_per_map, global_pages_per_slot));
|
|
|
|
if (!(*map_address))
|
|
Firebird::system_error::raise(ERR_STR("MapViewOfFile"));
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{
|
|
if (*map_handle)
|
|
CloseHandle(*map_handle);
|
|
throw;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static XPM make_xpm(ULONG map_number, ULONG timestamp)
|
|
{
|
|
/**************************************
|
|
*
|
|
* m a k e _ x p m
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Create new xpm structure
|
|
*
|
|
**************************************/
|
|
FILE_ID map_handle = 0;
|
|
CADDR_T map_address = 0;
|
|
|
|
if (!make_map(map_number, timestamp, &map_handle, &map_address))
|
|
return NULL;
|
|
|
|
// allocate XPM structure and initialize it
|
|
|
|
XPM xpm = new struct xpm;
|
|
|
|
xpm->xpm_handle = map_handle;
|
|
xpm->xpm_address = map_address;
|
|
xpm->xpm_number = map_number;
|
|
xpm->xpm_count = 0;
|
|
xpm->xpm_timestamp = timestamp;
|
|
|
|
for (USHORT i = 0; i < global_slots_per_map; i++) {
|
|
xpm->xpm_ids[i] = XPM_FREE;
|
|
}
|
|
xpm->xpm_flags = 0;
|
|
|
|
Firebird::MutexLockGuard guard(xnet_mutex);
|
|
|
|
xpm->xpm_next = global_client_maps;
|
|
global_client_maps = xpm;
|
|
|
|
return xpm;
|
|
}
|
|
|
|
|
|
static bool server_init(ISC_STATUS* status, USHORT flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s e r v e r _ i n i t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Initialization of server side resources used
|
|
* when clients perform connect to server
|
|
*
|
|
**************************************/
|
|
if (xnet_initialized)
|
|
return true;
|
|
|
|
TEXT name_buffer[BUFFER_TINY];
|
|
|
|
// Initialize server-side IPC endpoint to a value we know we have permissions to listen at
|
|
if (strcmp(xnet_endpoint, "") == 0)
|
|
{
|
|
fb_utils::copy_terminate(xnet_endpoint, Config::getIpcName(), sizeof(name_buffer));
|
|
fb_utils::prefix_kernel_object_name(xnet_endpoint, sizeof(xnet_endpoint));
|
|
}
|
|
|
|
// init the limits
|
|
|
|
if (flag & (SRVR_multi_client | SRVR_debug)) {
|
|
global_slots_per_map = XPS_MAX_NUM_CLI;
|
|
}
|
|
else {
|
|
global_slots_per_map = 1;
|
|
}
|
|
global_pages_per_slot = XPS_MAX_PAGES_PER_CLI;
|
|
|
|
xnet_connect_mutex = 0;
|
|
xnet_connect_map_h = 0;
|
|
xnet_connect_map = 0;
|
|
|
|
xnet_connect_event = 0;
|
|
xnet_response_event = 0;
|
|
|
|
try {
|
|
|
|
make_obj_name(name_buffer, sizeof(name_buffer), XNET_CONNECT_MUTEX);
|
|
xnet_connect_mutex = CreateMutex(ISC_get_security_desc(), FALSE, name_buffer);
|
|
if (!xnet_connect_mutex || (xnet_connect_mutex && ERRNO == ERROR_ALREADY_EXISTS))
|
|
{
|
|
Firebird::system_error::raise(ERR_STR("CreateMutex"));
|
|
}
|
|
|
|
make_obj_name(name_buffer, sizeof(name_buffer), XNET_CONNECT_EVENT);
|
|
xnet_connect_event = CreateEvent(ISC_get_security_desc(), FALSE, FALSE, name_buffer);
|
|
if (!xnet_connect_event || (xnet_connect_event && ERRNO == ERROR_ALREADY_EXISTS))
|
|
{
|
|
Firebird::system_error::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
make_obj_name(name_buffer, sizeof(name_buffer), XNET_RESPONSE_EVENT);
|
|
xnet_response_event = CreateEvent(ISC_get_security_desc(), FALSE, FALSE, name_buffer);
|
|
if (!xnet_response_event || (xnet_response_event && ERRNO == ERROR_ALREADY_EXISTS))
|
|
{
|
|
Firebird::system_error::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
make_obj_name(name_buffer, sizeof(name_buffer), XNET_CONNECT_MAP);
|
|
xnet_connect_map_h = CreateFileMapping(INVALID_HANDLE_VALUE,
|
|
ISC_get_security_desc(),
|
|
PAGE_READWRITE,
|
|
0,
|
|
XNET_CONNECT_RESPONZE_SIZE,
|
|
name_buffer);
|
|
if (!xnet_connect_map_h || (xnet_connect_map_h && ERRNO == ERROR_ALREADY_EXISTS))
|
|
{
|
|
Firebird::system_error::raise(ERR_STR("CreateFileMapping"));
|
|
}
|
|
|
|
xnet_connect_map = MapViewOfFile(xnet_connect_map_h, FILE_MAP_WRITE, 0L, 0L,
|
|
XNET_CONNECT_RESPONZE_SIZE);
|
|
if (!xnet_connect_map) {
|
|
Firebird::system_error::raise(ERR_STR("MapViewOfFile"));
|
|
}
|
|
}
|
|
catch (const Firebird::Exception& ex)
|
|
{
|
|
stuff_exception(status, ex);
|
|
xnet_log_error("XNET server initialization failed. "
|
|
"Probably another instance of server is already running.", status);
|
|
|
|
connect_fini();
|
|
xnet_shutdown = true;
|
|
|
|
// the real error is already logged, return isc_net_server_shutdown instead
|
|
Arg::StatusVector temp;
|
|
temp << Arg::Gds(isc_net_server_shutdown) << Arg::Str("XNET");
|
|
temp.copyTo(status);
|
|
|
|
return false;
|
|
}
|
|
|
|
xnet_initialized = true;
|
|
gds__register_cleanup((FPTR_VOID_PTR) exit_handler, NULL);
|
|
fb_shutdown_callback(0, cleanup_ports, fb_shut_postproviders, 0);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static XPM get_free_slot(ULONG* map_num, ULONG* slot_num, ULONG* timestamp)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ f r e e _ s l o t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Search for free slot in map stuff
|
|
*
|
|
**************************************/
|
|
|
|
XPM xpm = NULL;
|
|
ULONG free_slot = 0, free_map = 0;
|
|
|
|
Firebird::MutexLockGuard guard(xnet_mutex);
|
|
|
|
// go through list of maps
|
|
|
|
for (xpm = global_client_maps; xpm; xpm = xpm->xpm_next)
|
|
{
|
|
// find an available unused comm area
|
|
|
|
for (free_slot = 0; free_slot < global_slots_per_map; free_slot++)
|
|
{
|
|
if (xpm->xpm_ids[free_slot] == XPM_FREE)
|
|
break;
|
|
}
|
|
|
|
if (free_slot < global_slots_per_map)
|
|
{
|
|
xpm->xpm_count++;
|
|
xpm->xpm_ids[free_slot] = XPM_BUSY;
|
|
free_map = xpm->xpm_number;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if the mapped file structure has not yet been initialized,
|
|
// make one now
|
|
|
|
if (!xpm)
|
|
{
|
|
free_map = xnet_next_free_map_num++;
|
|
|
|
// allocate new map file and first slot
|
|
|
|
xpm = make_xpm(free_map, *timestamp);
|
|
|
|
free_slot = 0;
|
|
xpm->xpm_ids[0] = XPM_BUSY;
|
|
xpm->xpm_count++;
|
|
}
|
|
else
|
|
*timestamp = xpm->xpm_timestamp;
|
|
|
|
*map_num = free_map;
|
|
*slot_num = free_slot;
|
|
|
|
return xpm;
|
|
}
|
|
|
|
|
|
static bool fork(ULONG client_pid, USHORT flag, ULONG* forked_pid)
|
|
{
|
|
/**************************************
|
|
*
|
|
* f o r k
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Create child process to serve client connection
|
|
* It's for classic server only
|
|
*
|
|
**************************************/
|
|
TEXT name[MAXPATHLEN];
|
|
GetModuleFileName(NULL, name, sizeof(name));
|
|
|
|
Firebird::string cmdLine;
|
|
cmdLine.printf("%s -x -h %"ULONGFORMAT, name, client_pid);
|
|
|
|
STARTUPINFO start_crud;
|
|
start_crud.cb = sizeof(STARTUPINFO);
|
|
start_crud.lpReserved = NULL;
|
|
start_crud.lpReserved2 = NULL;
|
|
start_crud.cbReserved2 = 0;
|
|
start_crud.lpDesktop = NULL;
|
|
start_crud.lpTitle = NULL;
|
|
start_crud.dwFlags = STARTF_FORCEOFFFEEDBACK;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
const bool cp_result =
|
|
CreateProcess(NULL, cmdLine.begin(), NULL, NULL, FALSE,
|
|
(flag & SRVR_high_priority ? HIGH_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS)
|
|
| DETACHED_PROCESS | CREATE_SUSPENDED,
|
|
NULL, NULL, &start_crud, &pi);
|
|
|
|
// Child process ID (forked_pid) used as map number
|
|
|
|
if (cp_result)
|
|
{
|
|
*forked_pid = pi.dwProcessId;
|
|
ResumeThread(pi.hThread);
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
else {
|
|
xnet_log_error("CreateProcess() failed");
|
|
}
|
|
|
|
return cp_result;
|
|
}
|
|
|
|
|
|
static rem_port* get_server_port(ULONG client_pid,
|
|
XPM xpm,
|
|
ULONG map_num,
|
|
ULONG slot_num,
|
|
ULONG timestamp,
|
|
ISC_STATUS* status_vector)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ s e r v e r _ p o r t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Allocates new rem_port for server side communication.
|
|
*
|
|
**************************************/
|
|
rem_port* port = NULL;
|
|
TEXT name_buffer[BUFFER_TINY];
|
|
|
|
// allocate a communications control structure and fill it in
|
|
|
|
XCC xcc = new struct xcc;
|
|
|
|
try {
|
|
|
|
UCHAR* p = (UCHAR*) xpm->xpm_address + XPS_SLOT_OFFSET(global_pages_per_slot, slot_num);
|
|
memset(p, 0, XPS_MAPPED_PER_CLI(global_pages_per_slot));
|
|
xcc->xcc_next = NULL;
|
|
xcc->xcc_mapped_addr = p;
|
|
xcc->xcc_xpm = xpm;
|
|
xcc->xcc_slot = slot_num;
|
|
xcc->xcc_flags = 0;
|
|
|
|
// Open client process handle to watch clients health
|
|
// during communication session
|
|
|
|
xcc->xcc_proc_h = OpenProcess(SYNCHRONIZE, 0, client_pid);
|
|
if (!xcc->xcc_proc_h) {
|
|
Firebird::system_error::raise(ERR_STR("OpenProcess"));
|
|
}
|
|
|
|
xcc->xcc_map_num = map_num;
|
|
XPS xps = (XPS) xcc->xcc_mapped_addr;
|
|
xps->xps_client_proc_id = client_pid;
|
|
xps->xps_server_proc_id = current_process_id;
|
|
|
|
// make sure client knows what this server speaks
|
|
|
|
xps->xps_server_protocol = XPI_SERVER_PROTOCOL_VERSION;
|
|
xps->xps_client_protocol = 0L;
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_C2S_DATA_CHAN_FILLED,
|
|
map_num, slot_num, timestamp);
|
|
xcc->xcc_event_recv_channel_filled =
|
|
CreateEvent(ISC_get_security_desc(), FALSE, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_recv_channel_filled) {
|
|
Firebird::system_error::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_C2S_DATA_CHAN_EMPTED,
|
|
map_num, slot_num, timestamp);
|
|
xcc->xcc_event_recv_channel_empted =
|
|
CreateEvent(ISC_get_security_desc(), FALSE, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_recv_channel_empted) {
|
|
Firebird::system_error::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_S2C_DATA_CHAN_FILLED,
|
|
map_num, slot_num, timestamp);
|
|
xcc->xcc_event_send_channel_filled =
|
|
CreateEvent(ISC_get_security_desc(), FALSE, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_send_channel_filled) {
|
|
Firebird::system_error::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
make_event_name(name_buffer, sizeof(name_buffer), XNET_E_S2C_DATA_CHAN_EMPTED,
|
|
map_num, slot_num, timestamp);
|
|
xcc->xcc_event_send_channel_empted =
|
|
CreateEvent(ISC_get_security_desc(), FALSE, FALSE, name_buffer);
|
|
if (!xcc->xcc_event_send_channel_empted) {
|
|
Firebird::system_error::raise(ERR_STR("CreateEvent"));
|
|
}
|
|
|
|
// set up the channel structures
|
|
|
|
p += sizeof(struct xps);
|
|
|
|
const ULONG avail =
|
|
(ULONG) (XPS_USEFUL_SPACE(global_pages_per_slot) - (XNET_EVENT_SPACE * 2)) / 2;
|
|
|
|
// client to server events
|
|
//UCHAR* const channel_c2s_event_buffer = p;
|
|
xps->xps_channels[XPS_CHANNEL_C2S_EVENTS].xch_size = XNET_EVENT_SPACE;
|
|
|
|
p += XNET_EVENT_SPACE;
|
|
// server to client events
|
|
//UCHAR* const channel_s2c_event_buffer = p;
|
|
xps->xps_channels[XPS_CHANNEL_S2C_EVENTS].xch_size = XNET_EVENT_SPACE;
|
|
|
|
p += XNET_EVENT_SPACE;
|
|
// client to server data
|
|
UCHAR* const channel_c2s_data_buffer = p;
|
|
xps->xps_channels[XPS_CHANNEL_C2S_DATA].xch_size = avail;
|
|
|
|
p += avail;
|
|
// server to client data
|
|
UCHAR* const channel_s2c_data_buffer = p;
|
|
xps->xps_channels[XPS_CHANNEL_S2C_DATA].xch_size = avail;
|
|
|
|
xcc->xcc_recv_channel = &xps->xps_channels[XPS_CHANNEL_C2S_DATA];
|
|
xcc->xcc_send_channel = &xps->xps_channels[XPS_CHANNEL_S2C_DATA];
|
|
|
|
// finally, allocate and set the port structure for this client
|
|
|
|
port = alloc_port(NULL,
|
|
channel_s2c_data_buffer, xcc->xcc_send_channel->xch_size,
|
|
channel_c2s_data_buffer, xcc->xcc_recv_channel->xch_size);
|
|
|
|
port->port_xcc = xcc;
|
|
port->port_server_flags |= SRVR_server;
|
|
port->port_flags |= PORT_server;
|
|
|
|
status_vector[0] = isc_arg_gds;
|
|
status_vector[1] = FB_SUCCESS;
|
|
status_vector[2] = isc_arg_end;
|
|
port->port_status_vector = status_vector;
|
|
|
|
xnet_ports->registerPort(port);
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{
|
|
if (port)
|
|
cleanup_port(port);
|
|
else if (xcc)
|
|
cleanup_comm(xcc);
|
|
|
|
throw;
|
|
}
|
|
|
|
return port;
|
|
}
|