diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 4c912c3346..5264d50b34 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -717,6 +717,9 @@ interface Service : ReferenceCounted version: // 3.0.7 => 3.0.8, 4.0.0 => 4.0.1 [notImplementedAction if ::FB_UsedInYValve then defaultAction else call deprecatedDetach(status) endif] void detach(Status status); + +version: // 3.0.9 => 3.0.10, 4.0.1 => 4.0.2 + void cancel(Status status); } interface Provider : PluginBase diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index e16bede394..fe65e16291 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -2714,6 +2714,7 @@ namespace Firebird void (CLOOP_CARG *query)(IService* self, IStatus* status, unsigned sendLength, const unsigned char* sendItems, unsigned receiveLength, const unsigned char* receiveItems, unsigned bufferLength, unsigned char* buffer) throw(); void (CLOOP_CARG *start)(IService* self, IStatus* status, unsigned spbLength, const unsigned char* spb) throw(); void (CLOOP_CARG *detach)(IService* self, IStatus* status) throw(); + void (CLOOP_CARG *cancel)(IService* self, IStatus* status) throw(); }; protected: @@ -2727,7 +2728,7 @@ namespace Firebird } public: - static const unsigned VERSION = 4; + static const unsigned VERSION = 5; template void deprecatedDetach(StatusType* status) { @@ -2767,6 +2768,19 @@ namespace Firebird static_cast(this->cloopVTable)->detach(this, status); StatusType::checkException(status); } + + template void cancel(StatusType* status) + { + if (cloopVTable->version < 5) + { + StatusType::setVersionError(status, "IService", cloopVTable->version, 5); + StatusType::checkException(status); + return; + } + StatusType::clearException(status); + static_cast(this->cloopVTable)->cancel(this, status); + StatusType::checkException(status); + } }; class IProvider : public IPluginBase @@ -11460,6 +11474,7 @@ namespace Firebird this->query = &Name::cloopqueryDispatcher; this->start = &Name::cloopstartDispatcher; this->detach = &Name::cloopdetachDispatcher; + this->cancel = &Name::cloopcancelDispatcher; } } vTable; @@ -11522,6 +11537,20 @@ namespace Firebird } } + static void CLOOP_CARG cloopcancelDispatcher(IService* self, IStatus* status) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::cancel(&status2); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + static void CLOOP_CARG cloopaddRefDispatcher(IReferenceCounted* self) throw() { try @@ -11565,6 +11594,7 @@ namespace Firebird virtual void query(StatusType* status, unsigned sendLength, const unsigned char* sendItems, unsigned receiveLength, const unsigned char* receiveItems, unsigned bufferLength, unsigned char* buffer) = 0; virtual void start(StatusType* status, unsigned spbLength, const unsigned char* spb) = 0; virtual void detach(StatusType* status) = 0; + virtual void cancel(StatusType* status) = 0; }; template diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index fcb11c7153..a8e82aa6b6 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -414,6 +414,7 @@ type IService_queryPtr = procedure(this: IService; status: IStatus; sendLength: Cardinal; sendItems: BytePtr; receiveLength: Cardinal; receiveItems: BytePtr; bufferLength: Cardinal; buffer: BytePtr); cdecl; IService_startPtr = procedure(this: IService; status: IStatus; spbLength: Cardinal; spb: BytePtr); cdecl; IService_detachPtr = procedure(this: IService; status: IStatus); cdecl; + IService_cancelPtr = procedure(this: IService; status: IStatus); cdecl; IProvider_attachDatabasePtr = function(this: IProvider; status: IStatus; fileName: PAnsiChar; dpbLength: Cardinal; dpb: BytePtr): IAttachment; cdecl; IProvider_createDatabasePtr = function(this: IProvider; status: IStatus; fileName: PAnsiChar; dpbLength: Cardinal; dpb: BytePtr): IAttachment; cdecl; IProvider_attachServiceManagerPtr = function(this: IProvider; status: IStatus; service: PAnsiChar; spbLength: Cardinal; spb: BytePtr): IService; cdecl; @@ -1817,15 +1818,17 @@ type query: IService_queryPtr; start: IService_startPtr; detach: IService_detachPtr; + cancel: IService_cancelPtr; end; IService = class(IReferenceCounted) - const VERSION = 4; + const VERSION = 5; procedure deprecatedDetach(status: IStatus); procedure query(status: IStatus; sendLength: Cardinal; sendItems: BytePtr; receiveLength: Cardinal; receiveItems: BytePtr; bufferLength: Cardinal; buffer: BytePtr); procedure start(status: IStatus; spbLength: Cardinal; spb: BytePtr); procedure detach(status: IStatus); + procedure cancel(status: IStatus); end; IServiceImpl = class(IService) @@ -1837,6 +1840,7 @@ type procedure query(status: IStatus; sendLength: Cardinal; sendItems: BytePtr; receiveLength: Cardinal; receiveItems: BytePtr; bufferLength: Cardinal; buffer: BytePtr); virtual; abstract; procedure start(status: IStatus; spbLength: Cardinal; spb: BytePtr); virtual; abstract; procedure detach(status: IStatus); virtual; abstract; + procedure cancel(status: IStatus); virtual; abstract; end; ProviderVTable = class(PluginBaseVTable) @@ -7085,6 +7089,17 @@ begin FbException.checkException(status); end; +procedure IService.cancel(status: IStatus); +begin + if (vTable.version < 5) then begin + FbException.setVersionError(status, 'IService', vTable.version, 5); + end + else begin + ServiceVTable(vTable).cancel(Self, status); + end; + FbException.checkException(status); +end; + function IProvider.attachDatabase(status: IStatus; fileName: PAnsiChar; dpbLength: Cardinal; dpb: BytePtr): IAttachment; begin Result := ProviderVTable(vTable).attachDatabase(Self, status, fileName, dpbLength, dpb); @@ -11307,6 +11322,15 @@ begin end end; +procedure IServiceImpl_cancelDispatcher(this: IService; status: IStatus); cdecl; +begin + try + IServiceImpl(this).cancel(status); + except + on e: Exception do FbException.catchException(status, e); + end +end; + var IServiceImpl_vTable: ServiceVTable; @@ -15740,13 +15764,14 @@ initialization IAttachmentImpl_vTable.dropDatabase := @IAttachmentImpl_dropDatabaseDispatcher; IServiceImpl_vTable := ServiceVTable.create; - IServiceImpl_vTable.version := 4; + IServiceImpl_vTable.version := 5; IServiceImpl_vTable.addRef := @IServiceImpl_addRefDispatcher; IServiceImpl_vTable.release := @IServiceImpl_releaseDispatcher; IServiceImpl_vTable.deprecatedDetach := @IServiceImpl_deprecatedDetachDispatcher; IServiceImpl_vTable.query := @IServiceImpl_queryDispatcher; IServiceImpl_vTable.start := @IServiceImpl_startDispatcher; IServiceImpl_vTable.detach := @IServiceImpl_detachDispatcher; + IServiceImpl_vTable.cancel := @IServiceImpl_cancelDispatcher; IProviderImpl_vTable := ProviderVTable.create; IProviderImpl_vTable.version := 4; diff --git a/src/jrd/EngineInterface.h b/src/jrd/EngineInterface.h index 943bf4fae8..e935315837 100644 --- a/src/jrd/EngineInterface.h +++ b/src/jrd/EngineInterface.h @@ -506,6 +506,7 @@ public: unsigned int bufferLength, unsigned char* buffer) override; void start(Firebird::CheckStatusWrapper* status, unsigned int spbLength, const unsigned char* spb) override; + void cancel(Firebird::CheckStatusWrapper* status) override; public: explicit JService(Jrd::Service* handle); diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 70f7daa617..27fd8077d2 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -4407,6 +4407,26 @@ void JService::query(CheckStatusWrapper* user_status, } +void JService::cancel(CheckStatusWrapper* user_status) +{ + try + { + ThreadContextHolder tdbb(user_status); + + validateHandle(svc); + + svc->cancel(tdbb); + } + catch (const Exception& ex) + { + ex.stuffException(user_status); + return; + } + + successful_completion(user_status); +} + + void JService::start(CheckStatusWrapper* user_status, unsigned int spbLength, const unsigned char* spb) { /************************************** diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 538fd890b5..b1536c03a7 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -688,7 +688,8 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d svc_stdout_head(0), svc_stdout_tail(0), svc_service_run(NULL), svc_resp_alloc(getPool()), svc_resp_buf(0), svc_resp_ptr(0), svc_resp_buf_len(0), svc_resp_len(0), svc_flags(SVC_finished), svc_user_flag(0), svc_spb_version(0), - svc_do_shutdown(false), svc_shutdown_in_progress(false), svc_timeout(false), + svc_shutdown_server(false), svc_shutdown_request(false), + svc_shutdown_in_progress(false), svc_timeout(false), svc_username(getPool()), svc_sql_role(getPool()), svc_auth_block(getPool()), svc_expected_db(getPool()), svc_trusted_role(false), svc_utf8(false), svc_switches(getPool()), svc_perm_sw(getPool()), svc_address_path(getPool()), @@ -864,7 +865,7 @@ void Service::detach() } // save it cause after call to finish() we can't access class members any more - const bool localDoShutdown = svc_do_shutdown; + const bool localDoShutdown = svc_shutdown_server; if (svc_trace_manager->needs(ITraceFactory::TRACE_EVENT_SERVICE_DETACH)) { @@ -950,7 +951,7 @@ ULONG Service::totalCount() bool Service::checkForShutdown() { - if (svcShutdown) + if (svcShutdown || svc_shutdown_request) { if (svc_shutdown_in_progress) { @@ -966,6 +967,20 @@ bool Service::checkForShutdown() } +void Service::cancel(thread_db* /*tdbb*/) +{ + svc_shutdown_request = true; + + // signal once + if (!(svc_flags & SVC_finished)) + svc_detach_sem.release(); + if (svc_stdin_size_requested) + svc_stdin_semaphore.release(); + + svc_sem_full.release(); +} + + void Service::shutdownServices() { svcShutdown = true; @@ -1158,7 +1173,7 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/, *info++ = item; if (svc_user_flag & SVC_user_dba) { - svc_do_shutdown = false; + svc_shutdown_server = false; } else need_admin_privs(status, "isc_info_svc_svr_online"); @@ -1168,7 +1183,7 @@ ISC_STATUS Service::query2(thread_db* /*tdbb*/, *info++ = item; if (svc_user_flag & SVC_user_dba) { - svc_do_shutdown = true; + svc_shutdown_server = true; } else need_admin_privs(status, "isc_info_svc_svr_offline"); @@ -1613,7 +1628,7 @@ void Service::query(USHORT send_item_length, *info++ = item; if (svc_user_flag & SVC_user_dba) { - svc_do_shutdown = false; + svc_shutdown_server = false; *info++ = 0; // Success } else @@ -1624,7 +1639,7 @@ void Service::query(USHORT send_item_length, *info++ = item; if (svc_user_flag & SVC_user_dba) { - svc_do_shutdown = true; + svc_shutdown_server = true; *info++ = 0; // Success } else @@ -1937,6 +1952,9 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data) try { + if (!svcShutdown) + svc_shutdown_request = svc_shutdown_in_progress = false; + ClumpletReader spb(ClumpletReader::SpbStart, spb_data, spb_length); // The name of the service is the first element of the buffer diff --git a/src/jrd/svc.h b/src/jrd/svc.h index 3f2e1d8647..d43c858e01 100644 --- a/src/jrd/svc.h +++ b/src/jrd/svc.h @@ -172,6 +172,8 @@ public: // external interface with service const UCHAR* recv_items, USHORT buffer_length, UCHAR* info); ISC_STATUS query2(thread_db* tdbb, USHORT send_item_length, const UCHAR* send_items, USHORT recv_item_length, const UCHAR* recv_items, USHORT buffer_length, UCHAR* info); + // Cancel wait in query service + void cancel(thread_db* tdbb); // Detach from service void detach(); // get service version @@ -305,7 +307,8 @@ private: USHORT svc_flags; USHORT svc_user_flag; USHORT svc_spb_version; - bool svc_do_shutdown; + bool svc_shutdown_server; + bool svc_shutdown_request; bool svc_shutdown_in_progress; bool svc_timeout; char svc_arg_conv[MsgFormat::SAFEARG_MAX_ARG * 2]; diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index ebe4524ab0..b04cbb9c8f 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -151,6 +151,8 @@ namespace { cstring* ptr; cstring oldValue; }; + + GlobalPtr outPorts; } namespace Remote { @@ -936,6 +938,7 @@ public: unsigned int receiveLength, const unsigned char* receiveItems, unsigned int bufferLength, unsigned char* buffer) override; void start(CheckStatusWrapper* status, unsigned int spbLength, const unsigned char* spb) override; + void cancel(CheckStatusWrapper* status) override; public: Service(Rdb* handle) : rdb(handle) { } @@ -998,6 +1001,15 @@ private: void RProvider::shutdown(CheckStatusWrapper* status, unsigned int /*timeout*/, const int /*reason*/) { status->init(); + + try + { + outPorts->closePorts(); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } } void RProvider::setDbCryptCallback(CheckStatusWrapper* status, ICryptKeyCallback* callback) @@ -1063,7 +1075,7 @@ static void batch_gds_receive(rem_port*, struct rmtque *, USHORT); static void batch_dsql_fetch(rem_port*, struct rmtque *, USHORT); static void clear_queue(rem_port*); static void clear_stmt_que(rem_port*, Rsr*); -static void disconnect(rem_port*); +static void disconnect(rem_port*, bool rmRef = true); static void enqueue_receive(rem_port*, t_rmtque_fn, Rdb*, void*, Rrq::rrq_repeat*); static void dequeue_receive(rem_port*); static THREAD_ENTRY_DECLARE event_thread(THREAD_ENTRY_PARAM); @@ -6435,6 +6447,28 @@ void Service::query(CheckStatusWrapper* status, } +void Service::cancel(CheckStatusWrapper* status) +{ + try + { + reset(status); + + // Check and validate handles, etc. + CHECK_HANDLE(rdb, isc_bad_svc_handle); +/* + rem_port* port = rdb->rdb_port; + RefMutexGuard portGuard(*port->port_sync, FB_FUNCTION); +*/ + + Arg::Gds(isc_wish_list).raise(); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } +} + + void Service::start(CheckStatusWrapper* status, unsigned int spbLength, const unsigned char* spb) { @@ -7197,10 +7231,11 @@ static rem_port* analyze(ClntAuthBlock& cBlock, PathName& attach_name, unsigned } catch (const Exception&) { - disconnect(port); + disconnect(port, false); throw; } + outPorts->registerPort(port); return port; } @@ -7556,7 +7591,7 @@ static void clear_queue(rem_port* port) } -static void disconnect( rem_port* port) +static void disconnect(rem_port* port, bool rmRef) { /************************************** * @@ -7625,6 +7660,12 @@ static void disconnect( rem_port* port) port->port_flags |= PORT_disconnect; port->disconnect(); delete rdb; + port->port_context = nullptr; + + // Remove from active ports + + if (rmRef) + outPorts->unRegisterPort(port); } diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index 4da2ac638b..9f3e8da4f2 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -2681,23 +2681,28 @@ static void cancel_operation(rem_port* port, USHORT kind) if ((port->port_flags & (PORT_async | PORT_disconnect)) || !(port->port_context)) return; - ServAttachment iface; + ServAttachment dbIface; + ServService svcIface; { RefMutexGuard portGuard(*port->port_cancel_sync, FB_FUNCTION); - Rdb* rdb; - if ((port->port_flags & PORT_disconnect) || !(rdb = port->port_context)) + Rdb* rdb = port->port_context; + if ((port->port_flags & PORT_disconnect) || !rdb) return; - iface = rdb->rdb_iface; + if (rdb->rdb_svc) + svcIface = rdb->rdb_svc->svc_iface; + else + dbIface = rdb->rdb_iface; } - if (iface) - { - LocalStatus ls; - CheckStatusWrapper status_vector(&ls); - iface->cancelOperation(&status_vector, kind); - } + LocalStatus ls; + CheckStatusWrapper status_vector(&ls); + + if (dbIface) + dbIface->cancelOperation(&status_vector, kind); + else if (svcIface && kind == fb_cancel_raise) + svcIface->cancel(&status_vector); } @@ -5054,8 +5059,11 @@ static bool process_packet(rem_port* port, PACKET* sendL, PACKET* receive, rem_p { if (!port->port_parent) { - if (!Worker::isShuttingDown() && !(port->port_flags & (PORT_rdb_shutdown | PORT_detached))) + if (!Worker::isShuttingDown() && !(port->port_flags & (PORT_rdb_shutdown | PORT_detached)) && + ((port->port_server_flags & (SRVR_server | SRVR_multi_client)) != SRVR_server)) + { gds__log("SERVER/process_packet: broken port, server exiting"); + } port->disconnect(sendL, receive); return false; } diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index efddb0f942..63630fab67 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -613,6 +613,7 @@ public: unsigned int bufferLength, unsigned char* buffer); void start(Firebird::CheckStatusWrapper* status, unsigned int spbLength, const unsigned char* spb); + void cancel(Firebird::CheckStatusWrapper* status); public: typedef Firebird::IService NextInterface; diff --git a/src/yvalve/why.cpp b/src/yvalve/why.cpp index a4c8f2714e..90cf66c988 100644 --- a/src/yvalve/why.cpp +++ b/src/yvalve/why.cpp @@ -6197,6 +6197,19 @@ void YService::query(CheckStatusWrapper* status, unsigned int sendLength, const } } +void YService::cancel(CheckStatusWrapper* status) +{ + try + { + YEntry entry(status, this); + entry.next()->cancel(status); + } + catch (const Exception& e) + { + e.stuffException(status); + } +} + void YService::start(CheckStatusWrapper* status, unsigned int spbLength, const unsigned char* spbItems) { try