]> git.stg.codes - stg.git/blobdiff - libs/ibpp/service.cpp
Port to CMake, get rid of os_int.h.
[stg.git] / libs / ibpp / service.cpp
diff --git a/libs/ibpp/service.cpp b/libs/ibpp/service.cpp
new file mode 100644 (file)
index 0000000..26726ea
--- /dev/null
@@ -0,0 +1,774 @@
+///////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     File    : $Id: service.cpp,v 1.1 2007/05/05 17:00:43 faust Exp $\r
+//     Subject : IBPP, Service class implementation\r
+//\r
+///////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     (C) Copyright 2000-2006 T.I.P. Group S.A. and the IBPP Team (www.ibpp.org)\r
+//\r
+//     The contents of this file are subject to the IBPP License (the "License");\r
+//     you may not use this file except in compliance with the License.  You may\r
+//     obtain a copy of the License at http://www.ibpp.org or in the 'license.txt'\r
+//     file which must have been distributed along with this file.\r
+//\r
+//     This software, distributed under the License, is distributed on an "AS IS"\r
+//     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the\r
+//     License for the specific language governing rights and limitations\r
+//     under the License.\r
+//\r
+///////////////////////////////////////////////////////////////////////////////\r
+//\r
+//     COMMENTS\r
+//     * Tabulations should be set every four characters when editing this file.\r
+//\r
+///////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef _MSC_VER\r
+#pragma warning(disable: 4786 4996)\r
+#ifndef _DEBUG\r
+#pragma warning(disable: 4702)\r
+#endif\r
+#endif\r
+\r
+#include "_ibpp.h"\r
+\r
+#ifdef HAS_HDRSTOP\r
+#pragma hdrstop\r
+#endif\r
+\r
+using namespace ibpp_internals;\r
+\r
+#ifdef IBPP_UNIX\r
+#include <unistd.h>\r
+#define Sleep(x) usleep(x)\r
+#endif\r
+\r
+//     (((((((( OBJECT INTERFACE IMPLEMENTATION ))))))))\r
+\r
+void ServiceImpl::Connect()\r
+{\r
+       if (mHandle     != 0) return;   // Already connected\r
+       \r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mUserName.empty())\r
+               throw LogicExceptionImpl("Service::Connect", _("Unspecified user name."));\r
+       if (mUserPassword.empty())\r
+               throw LogicExceptionImpl("Service::Connect", _("Unspecified user password."));\r
+\r
+       // Attach to the Service Manager\r
+       IBS status;\r
+       SPB spb;\r
+       std::string connect;\r
+\r
+       // Build a SPB based on the     properties\r
+       spb.Insert(isc_spb_version);\r
+       spb.Insert(isc_spb_current_version);\r
+       spb.InsertString(isc_spb_user_name, 1, mUserName.c_str());\r
+       spb.InsertString(isc_spb_password, 1, mUserPassword.c_str());\r
+\r
+       if (! mServerName.empty())\r
+       {\r
+               connect = mServerName;\r
+               connect += ":";\r
+       }\r
+\r
+       connect += "service_mgr";\r
+\r
+       (*gds.Call()->m_service_attach)(status.Self(), (short)connect.size(), (char*)connect.c_str(),\r
+               &mHandle, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+       {\r
+               mHandle = 0;            // Should be, but better be     sure...\r
+               throw SQLExceptionImpl(status, "Service::Connect", _("isc_service_attach failed"));\r
+       }\r
+}\r
+\r
+void ServiceImpl::Disconnect()\r
+{\r
+       if (mHandle     == 0) return; // Already disconnected\r
+       \r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+\r
+       IBS status;\r
+\r
+       // Detach from the service manager\r
+       (*gds.Call()->m_service_detach)(status.Self(), &mHandle);\r
+\r
+       // Set mHandle to 0 now, just in case we need to throw, because Disconnect()\r
+       // is called from Service destructor and we want to maintain a coherent state.\r
+       mHandle = 0;\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::Disconnect", _("isc_service_detach failed"));\r
+}\r
+\r
+void ServiceImpl::GetVersion(std::string& version)\r
+{\r
+       // Based on a patch provided by Torsten Martinsen (SourceForge 'bullestock')\r
+\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle == 0)\r
+               throw LogicExceptionImpl("Service::GetVersion", _("Service is not connected."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+       RB result(250);\r
+\r
+       spb.Insert(isc_info_svc_server_version);\r
+\r
+       (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0, 0, spb.Size(), spb.Self(),\r
+               result.Size(), result.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::GetVersion", _("isc_service_query failed"));\r
+\r
+       result.GetString(isc_info_svc_server_version, version);\r
+}\r
+\r
+void ServiceImpl::AddUser(const IBPP::User& user)\r
+{\r
+       if (gds.Call()->mGDSVersion >= 60 && mHandle == 0)\r
+               throw LogicExceptionImpl("Service::AddUser", _("Service is not connected."));\r
+       if (user.username.empty())\r
+               throw LogicExceptionImpl("Service::AddUser", _("Username required."));\r
+       if (user.password.empty())\r
+               throw LogicExceptionImpl("Service::AddUser", _("Password required."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+       spb.Insert(isc_action_svc_add_user);\r
+       spb.InsertString(isc_spb_sec_username, 2, user.username.c_str());\r
+       spb.InsertString(isc_spb_sec_password, 2, user.password.c_str());\r
+       if (! user.firstname.empty())\r
+                       spb.InsertString(isc_spb_sec_firstname, 2, user.firstname.c_str());\r
+       if (! user.middlename.empty())\r
+                       spb.InsertString(isc_spb_sec_middlename, 2, user.middlename.c_str());\r
+       if (! user.lastname.empty())\r
+                       spb.InsertString(isc_spb_sec_lastname, 2, user.lastname.c_str());\r
+       if (user.userid != 0)\r
+                       spb.InsertQuad(isc_spb_sec_userid, (int32_t)user.userid);\r
+       if (user.groupid != 0)\r
+                       spb.InsertQuad(isc_spb_sec_groupid, (int32_t)user.groupid);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::AddUser", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::ModifyUser(const IBPP::User& user)\r
+{\r
+       if (gds.Call()->mGDSVersion >= 60 && mHandle == 0)\r
+               throw LogicExceptionImpl("Service::ModifyUser", _("Service is not connected."));\r
+       if (user.username.empty())\r
+               throw LogicExceptionImpl("Service::ModifyUser", _("Username required."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_modify_user);\r
+       spb.InsertString(isc_spb_sec_username, 2, user.username.c_str());\r
+       if (! user.password.empty())\r
+                       spb.InsertString(isc_spb_sec_password, 2, user.password.c_str());\r
+       if (! user.firstname.empty())\r
+                       spb.InsertString(isc_spb_sec_firstname, 2, user.firstname.c_str());\r
+       if (! user.middlename.empty())\r
+                       spb.InsertString(isc_spb_sec_middlename, 2, user.middlename.c_str());\r
+       if (! user.lastname.empty())\r
+                       spb.InsertString(isc_spb_sec_lastname, 2, user.lastname.c_str());\r
+       if (user.userid != 0)\r
+                       spb.InsertQuad(isc_spb_sec_userid, (int32_t)user.userid);\r
+       if (user.groupid != 0)\r
+                       spb.InsertQuad(isc_spb_sec_groupid, (int32_t)user.groupid);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::ModifyUser", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::RemoveUser(const std::string& username)\r
+{\r
+\r
+       if (gds.Call()->mGDSVersion >= 60 && mHandle == 0)\r
+               throw LogicExceptionImpl("Service::RemoveUser", _("Service is not connected."));\r
+       if (username.empty())\r
+               throw LogicExceptionImpl("Service::RemoveUser", _("Username required."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_delete_user);\r
+       spb.InsertString(isc_spb_sec_username, 2, username.c_str());\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::RemoveUser", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::GetUser(IBPP::User& user)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle == 0)\r
+               throw LogicExceptionImpl("Service::GetUser", _("Service is not connected."));\r
+       if (user.username.empty())\r
+               throw LogicExceptionImpl("Service::GetUser", _("Username required."));\r
+\r
+       SPB spb;\r
+       spb.Insert(isc_action_svc_display_user);\r
+       spb.InsertString(isc_spb_sec_username, 2, user.username.c_str());\r
+\r
+       IBS status;\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::GetUser", _("isc_service_start failed"));\r
+\r
+       RB result(8000);\r
+       char request[] = {isc_info_svc_get_users};\r
+       status.Reset();\r
+       (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0, 0,\r
+               sizeof(request), request, result.Size(), result.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::GetUser", _("isc_service_query failed"));\r
+\r
+       char* p = result.Self();\r
+       if (*p != isc_info_svc_get_users)\r
+               throw SQLExceptionImpl(status, "Service::GetUser", _("isc_service_query returned unexpected answer"));\r
+\r
+       p += 3; // Skips the 'isc_info_svc_get_users' and its total length\r
+       user.clear();\r
+       while (*p != isc_info_end)\r
+       {\r
+               if (*p == isc_spb_sec_userid)\r
+               {\r
+                       user.userid = (uint32_t)(*gds.Call()->m_vax_integer)(p+1, 4);\r
+                       p += 5;\r
+               }\r
+               else if (*p == isc_spb_sec_groupid)\r
+               {\r
+                       user.groupid = (uint32_t)(*gds.Call()->m_vax_integer)(p+1, 4);\r
+                       p += 5;\r
+               }\r
+               else\r
+               {\r
+                       unsigned short len = (unsigned short)(*gds.Call()->m_vax_integer)(p+1, 2);\r
+                       switch (*p)\r
+                       {\r
+                       case isc_spb_sec_username :\r
+                               // For each user, this is the first element returned\r
+                               if (len != 0) user.username.assign(p+3, len);\r
+                               break;\r
+                       case isc_spb_sec_password :\r
+                               if (len != 0) user.password.assign(p+3, len);\r
+                               break;\r
+                       case isc_spb_sec_firstname :\r
+                               if (len != 0) user.firstname.assign(p+3, len);\r
+                               break;\r
+                       case isc_spb_sec_middlename :\r
+                               if (len != 0) user.middlename.assign(p+3, len);\r
+                               break;\r
+                       case isc_spb_sec_lastname :\r
+                               if (len != 0) user.lastname.assign(p+3, len);\r
+                               break;\r
+                       }\r
+                       p += (3 + len);\r
+               }\r
+    }\r
+}\r
+\r
+void ServiceImpl::GetUsers(std::vector<IBPP::User>& users)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle == 0)\r
+               throw LogicExceptionImpl("Service::GetUsers", _("Service is not connected."));\r
+\r
+       SPB spb;\r
+       spb.Insert(isc_action_svc_display_user);\r
+\r
+       IBS status;\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::GetUsers", _("isc_service_start failed"));\r
+\r
+       RB result(8000);\r
+       char request[] = {isc_info_svc_get_users};\r
+       status.Reset();\r
+       (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0, 0,\r
+               sizeof(request), request, result.Size(), result.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::GetUsers", _("isc_service_query failed"));\r
+\r
+       users.clear();\r
+       char* p = result.Self();\r
+       if (*p != isc_info_svc_get_users)\r
+               throw SQLExceptionImpl(status, "Service::GetUsers", _("isc_service_query returned unexpected answer"));\r
+\r
+       p += 3; // Skips the 'isc_info_svc_get_users' and its total length\r
+       IBPP::User user;\r
+       while (*p != isc_info_end)\r
+       {\r
+               if (*p == isc_spb_sec_userid)\r
+               {\r
+                       user.userid = (uint32_t)(*gds.Call()->m_vax_integer)(p+1, 4);\r
+                       p += 5;\r
+               }\r
+               else if (*p == isc_spb_sec_groupid)\r
+               {\r
+                       user.groupid = (uint32_t)(*gds.Call()->m_vax_integer)(p+1, 4);\r
+                       p += 5;\r
+               }\r
+               else\r
+               {\r
+                       unsigned short len = (unsigned short)(*gds.Call()->m_vax_integer)(p+1, 2);\r
+                       switch (*p)\r
+                       {\r
+                       case isc_spb_sec_username :\r
+                               // For each user, this is the first element returned\r
+                               if (! user.username.empty()) users.push_back(user);     // Flush previous user\r
+                               user.clear();\r
+                               if (len != 0) user.username.assign(p+3, len);\r
+                               break;\r
+                       case isc_spb_sec_password :\r
+                               if (len != 0) user.password.assign(p+3, len);\r
+                               break;\r
+                       case isc_spb_sec_firstname :\r
+                               if (len != 0) user.firstname.assign(p+3, len);\r
+                               break;\r
+                       case isc_spb_sec_middlename :\r
+                               if (len != 0) user.middlename.assign(p+3, len);\r
+                               break;\r
+                       case isc_spb_sec_lastname :\r
+                               if (len != 0) user.lastname.assign(p+3, len);\r
+                               break;\r
+                       }\r
+                       p += (3 + len);\r
+               }\r
+    }\r
+       if (! user.username.empty()) users.push_back(user);     // Flush last user\r
+}\r
+\r
+void ServiceImpl::SetPageBuffers(const std::string& dbfile, int buffers)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::SetPageBuffers", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::SetPageBuffers", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_properties);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       spb.InsertQuad(isc_spb_prp_page_buffers, buffers);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::SetPageBuffers", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::SetSweepInterval(const std::string& dbfile, int sweep)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::SetSweepInterval", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::SetSweepInterval", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_properties);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       spb.InsertQuad(isc_spb_prp_sweep_interval, sweep);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::SetSweepInterval", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::SetSyncWrite(const std::string& dbfile, bool sync)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::SetSyncWrite", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::SetSyncWrite", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_properties);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       if (sync) spb.InsertByte(isc_spb_prp_write_mode, (char)isc_spb_prp_wm_sync);\r
+       else spb.InsertByte(isc_spb_prp_write_mode, (char)isc_spb_prp_wm_async);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::SetSyncWrite", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::SetReadOnly(const std::string& dbfile, bool readonly)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::SetReadOnly", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::SetReadOnly", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_properties);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       if (readonly) spb.InsertByte(isc_spb_prp_access_mode, (char)isc_spb_prp_am_readonly);\r
+       else spb.InsertByte(isc_spb_prp_access_mode, (char)isc_spb_prp_am_readwrite);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::SetReadOnly", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::SetReserveSpace(const std::string& dbfile, bool reserve)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::SetReserveSpace", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::SetReserveSpace", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_properties);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       if (reserve) spb.InsertByte(isc_spb_prp_reserve_space, (char)isc_spb_prp_res);\r
+       else spb.InsertByte(isc_spb_prp_reserve_space, (char)isc_spb_prp_res_use_full);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::SetReserveSpace", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::Shutdown(const std::string& dbfile, IBPP::DSM mode, int sectimeout)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::Shutdown", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::Shutdown", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_properties);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       switch (mode)\r
+       {\r
+               case IBPP::dsDenyAttach :\r
+                       spb.InsertQuad(isc_spb_prp_deny_new_attachments, sectimeout);\r
+                       break;\r
+               case IBPP::dsDenyTrans :\r
+                       spb.InsertQuad(isc_spb_prp_deny_new_transactions, sectimeout);\r
+                       break;\r
+               case IBPP::dsForce :\r
+                       spb.InsertQuad(isc_spb_prp_shutdown_db, sectimeout);\r
+                       break;\r
+       }\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::Shutdown", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::Restart(const std::string& dbfile)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::Restart", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::Restart", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_properties);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       spb.InsertQuad(isc_spb_options, isc_spb_prp_db_online);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::Restart", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::Sweep(const std::string& dbfile)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::Sweep", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::Sweep", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_repair);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       spb.InsertQuad(isc_spb_options, isc_spb_rpr_sweep_db);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::Sweep", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::Repair(const std::string& dbfile, IBPP::RPF flags)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::Repair", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::Repair", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_repair);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+\r
+       unsigned int mask;\r
+       if (flags & IBPP::rpValidateFull) mask = (isc_spb_rpr_full | isc_spb_rpr_validate_db);\r
+       else if (flags & IBPP::rpValidatePages) mask = isc_spb_rpr_validate_db;\r
+       else if (flags & IBPP::rpMendRecords) mask = isc_spb_rpr_mend_db;\r
+       else throw LogicExceptionImpl("Service::Repair",\r
+               _("One of rpMendRecords, rpValidatePages, rpValidateFull is required."));\r
+\r
+       if (flags & IBPP::rpReadOnly)                   mask |= isc_spb_rpr_check_db;\r
+       if (flags & IBPP::rpIgnoreChecksums)    mask |= isc_spb_rpr_ignore_checksum;\r
+       if (flags & IBPP::rpKillShadows)                mask |= isc_spb_rpr_kill_shadows;\r
+       \r
+       spb.InsertQuad(isc_spb_options, mask);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::Repair", _("isc_service_start failed"));\r
+\r
+       Wait();\r
+}\r
+\r
+void ServiceImpl::StartBackup(const std::string& dbfile,\r
+       const std::string& bkfile, IBPP::BRF flags)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::Backup", _("Service is not connected."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::Backup", _("Main database file must be specified."));\r
+       if (bkfile.empty())\r
+               throw LogicExceptionImpl("Service::Backup", _("Backup file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_backup);\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       spb.InsertString(isc_spb_bkp_file, 2, bkfile.c_str());\r
+       if (flags & IBPP::brVerbose) spb.Insert(isc_spb_verbose);\r
+\r
+       unsigned int mask = 0;\r
+       if (flags & IBPP::brIgnoreChecksums)    mask |= isc_spb_bkp_ignore_checksums;\r
+       if (flags & IBPP::brIgnoreLimbo)                mask |= isc_spb_bkp_ignore_limbo;\r
+       if (flags & IBPP::brMetadataOnly)               mask |= isc_spb_bkp_metadata_only;\r
+       if (flags & IBPP::brNoGarbageCollect)   mask |= isc_spb_bkp_no_garbage_collect;\r
+       if (flags & IBPP::brNonTransportable)   mask |= isc_spb_bkp_non_transportable;\r
+       if (flags & IBPP::brConvertExtTables)   mask |= isc_spb_bkp_convert;\r
+       if (mask != 0) spb.InsertQuad(isc_spb_options, mask);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::Backup", _("isc_service_start failed"));\r
+}\r
+\r
+void ServiceImpl::StartRestore(const std::string& bkfile, const std::string& dbfile,\r
+       int     pagesize, IBPP::BRF flags)\r
+{\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+       if (mHandle     == 0)\r
+               throw LogicExceptionImpl("Service::Restore", _("Service is not connected."));\r
+       if (bkfile.empty())\r
+               throw LogicExceptionImpl("Service::Restore", _("Backup file must be specified."));\r
+       if (dbfile.empty())\r
+               throw LogicExceptionImpl("Service::Restore", _("Main database file must be specified."));\r
+\r
+       IBS status;\r
+       SPB spb;\r
+\r
+       spb.Insert(isc_action_svc_restore);\r
+       spb.InsertString(isc_spb_bkp_file, 2, bkfile.c_str());\r
+       spb.InsertString(isc_spb_dbname, 2, dbfile.c_str());\r
+       if (flags & IBPP::brVerbose) spb.Insert(isc_spb_verbose);\r
+       if (pagesize != 0) spb.InsertQuad(isc_spb_res_page_size, pagesize);\r
+\r
+       unsigned int mask;\r
+       if (flags & IBPP::brReplace) mask = isc_spb_res_replace;\r
+               else mask = isc_spb_res_create; // Safe default mode\r
+\r
+       if (flags & IBPP::brDeactivateIdx)      mask |= isc_spb_res_deactivate_idx;\r
+       if (flags & IBPP::brNoShadow)           mask |= isc_spb_res_no_shadow;\r
+       if (flags & IBPP::brNoValidity)         mask |= isc_spb_res_no_validity;\r
+       if (flags & IBPP::brPerTableCommit)     mask |= isc_spb_res_one_at_a_time;\r
+       if (flags & IBPP::brUseAllSpace)        mask |= isc_spb_res_use_all_space;\r
+       if (mask != 0) spb.InsertQuad(isc_spb_options, mask);\r
+\r
+       (*gds.Call()->m_service_start)(status.Self(), &mHandle, 0, spb.Size(), spb.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "Service::Restore", _("isc_service_start failed"));\r
+}\r
+\r
+const char* ServiceImpl::WaitMsg()\r
+{\r
+       IBS status;\r
+       SPB req;\r
+       RB result(1024);\r
+\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+\r
+       req.Insert(isc_info_svc_line);  // Request one line of textual output\r
+\r
+       // _service_query will only block until a line of result is available\r
+       // (or until the end of the task if it does not report information)\r
+       (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0, 0,\r
+               req.Size(),     req.Self(),     result.Size(), result.Self());\r
+       if (status.Errors())\r
+               throw SQLExceptionImpl(status, "ServiceImpl::Wait", _("isc_service_query failed"));\r
+\r
+       // If message length is zero bytes,     task is finished\r
+       if (result.GetString(isc_info_svc_line, mWaitMessage) == 0) return 0;\r
+\r
+       // Task is not finished, but we have something to report\r
+       return mWaitMessage.c_str();\r
+}\r
+\r
+void ServiceImpl::Wait()\r
+{\r
+       IBS status;\r
+       SPB spb;\r
+       RB result(1024);\r
+       std::string msg;\r
+\r
+       if (gds.Call()->mGDSVersion < 60)\r
+               throw LogicExceptionImpl("Service", _("Requires the version 6 of GDS32.DLL"));\r
+\r
+       spb.Insert(isc_info_svc_line);\r
+       for (;;)\r
+       {\r
+               // Sleeps 1 millisecond upfront. This will release the remaining\r
+               // timeslot of the thread. Doing so will give a good chance for small\r
+               // services tasks to finish before we check if they are still running.\r
+               // The deal is to limit (in that particular case) the number of loops\r
+               // polling _service_query that will happen.\r
+\r
+               Sleep(1);\r
+\r
+               // _service_query will only block until a line of result is available\r
+               // (or until the end of the task if it does not report information) \r
+               (*gds.Call()->m_service_query)(status.Self(), &mHandle, 0, 0,   0,\r
+                       spb.Size(),     spb.Self(),     result.Size(), result.Self());\r
+               if (status.Errors())\r
+                       throw SQLExceptionImpl(status, "ServiceImpl::Wait", _("isc_service_query failed"));\r
+\r
+               // If message length is zero bytes,     task is finished\r
+               if (result.GetString(isc_info_svc_line, msg) == 0) return;\r
+\r
+               status.Reset();\r
+               result.Reset();\r
+       }\r
+}\r
+\r
+IBPP::IService* ServiceImpl::AddRef()\r
+{\r
+       ASSERTION(mRefCount >= 0);\r
+       ++mRefCount;\r
+       return this;\r
+}\r
+\r
+void ServiceImpl::Release()\r
+{\r
+       // Release cannot throw, except in DEBUG builds on assertion\r
+       ASSERTION(mRefCount >= 0);\r
+       --mRefCount;\r
+       try { if (mRefCount <= 0) delete this; }\r
+               catch (...) { }\r
+}\r
+\r
+//     (((((((( OBJECT INTERNAL METHODS ))))))))\r
+\r
+void ServiceImpl::SetServerName(const char* newName)\r
+{\r
+       if (newName == 0) mServerName.erase();\r
+       else mServerName = newName;\r
+}\r
+\r
+void ServiceImpl::SetUserName(const char* newName)\r
+{\r
+       if (newName == 0) mUserName.erase();\r
+       else mUserName = newName;\r
+}\r
+\r
+void ServiceImpl::SetUserPassword(const char* newPassword)\r
+{\r
+       if (newPassword == 0) mUserPassword.erase();\r
+       else mUserPassword = newPassword;\r
+}\r
+\r
+ServiceImpl::ServiceImpl(const std::string& ServerName,\r
+                       const std::string& UserName, const std::string& UserPassword)\r
+       :       mRefCount(0), mHandle(0),\r
+               mServerName(ServerName), mUserName(UserName), mUserPassword(UserPassword)\r
+{\r
+}\r
+\r
+ServiceImpl::~ServiceImpl()\r
+{\r
+       try { if (Connected()) Disconnect(); }\r
+               catch (...) { }\r
+}\r
+\r
+//\r
+//     Eof\r
+//\r