8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-28 02:43:03 +01:00
firebird-mirror/src/jrd/svc.cpp

2572 lines
60 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: svc.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Service manager functions
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
2002-02-16 03:21:35 +01:00
*
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "EPSON" define
2002-02-16 04:27:33 +01:00
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "IMP" port
2002-02-16 03:21:35 +01:00
*
* 2002.10.27 Sean Leyne - Completed removal of obsolete "DELTA" port
* 2002.10.27 Sean Leyne - Completed removal of obsolete "IMP" port
*
2002-10-30 07:40:58 +01:00
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
2008-09-11 11:03:08 +02:00
* 2008 Alex Peshkoff - refactored services code for MT safe engine
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2004-04-29 00:43:34 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <string.h>
#include "../jrd/common.h"
#include "../jrd/file_params.h"
#include <stdarg.h>
#include "../jrd/jrd.h"
#include "../jrd/svc.h"
2008-01-16 10:48:41 +01:00
#include "../jrd/constants.h"
#include "../jrd/jrd_pwd.h"
2001-05-23 15:26:42 +02:00
#include "../alice/aliceswi.h"
#include "../burp/burpswi.h"
#include "../jrd/ibase.h"
#include "gen/iberror.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/license.h"
#include "../jrd/err_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/inf_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/thread_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/why_proto.h"
#include "../jrd/utl_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/enc_proto.h"
2003-07-15 04:43:36 +02:00
#include "../utilities/gsec/gsecswi.h"
#include "../utilities/gstat/dbaswi.h"
2008-11-20 18:23:06 +01:00
#include "../utilities/nbackup/nbkswi.h"
#include "../common/classes/alloc.h"
#include "../common/classes/init.h"
#include "../common/classes/ClumpletWriter.h"
#include "../jrd/ibase.h"
#include "../common/utils_proto.h"
2007-03-02 10:04:30 +01:00
#include "../jrd/scl.h"
2008-09-11 11:03:08 +02:00
#include "../jrd/msg_encode.h"
2001-07-12 07:46:06 +02:00
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
2004-05-13 21:47:30 +02:00
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
2001-07-12 07:46:06 +02:00
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_VFORK_H
#include <vfork.h>
#endif
2001-05-23 15:26:42 +02:00
#ifdef SOLARIS
#include <fcntl.h>
#endif
2002-02-16 03:21:35 +01:00
#ifdef SCO_UNIX
2001-05-23 15:26:42 +02:00
#include <fcntl.h>
#endif
#if !defined(WIN_NT)
# include <signal.h>
2008-01-16 10:48:41 +01:00
# include <sys/param.h>
# include <sys/stat.h>
2001-05-23 15:26:42 +02:00
# include <errno.h>
#else
2001-05-23 15:26:42 +02:00
# include <windows.h>
# include <io.h> // _open, _get_osfhandle
2001-05-23 15:26:42 +02:00
# include <stdlib.h>
# include <fcntl.h>
# include <sys/stat.h>
#endif
#define statistics stat
using namespace Firebird;
const int SVC_user_dba = 2;
const int SVC_user_any = 1;
const int SVC_user_none = 0;
2004-05-03 23:43:56 +02:00
const int GET_LINE = 1;
const int GET_EOF = 2;
const int GET_BINARY = 4;
2001-05-23 15:26:42 +02:00
const char* const SPB_SEC_USERNAME = "isc_spb_sec_username";
2001-05-23 15:26:42 +02:00
namespace {
// Option block for service parameter block
2009-01-20 09:33:59 +01:00
struct Options
2008-12-22 10:00:05 +01:00
{
2008-07-06 20:08:23 +02:00
string spb_sys_user_name;
string spb_user_name;
string spb_password;
string spb_password_enc;
string spb_command_line;
string spb_network_protocol;
string spb_remote_address;
string spb_trusted_login;
string spb_address_path;
USHORT spb_version;
bool spb_trusted_role;
bool spb_remote;
// Parse service parameter block picking up options and things.
2008-12-25 07:09:37 +01:00
explicit Options(ClumpletReader& spb) :
spb_version(0),
spb_trusted_role(false),
spb_remote(false)
{
const UCHAR p = spb.getBufferTag();
if (p != isc_spb_version1 && p != isc_spb_current_version) {
2008-08-31 08:24:24 +02:00
ERR_post(Arg::Gds(isc_bad_spb_form) <<
Arg::Gds(isc_wrospbver));
}
spb_version = p;
2008-01-16 10:48:41 +01:00
for (spb.rewind(); !(spb.isEof()); spb.moveNext())
{
2008-12-05 02:20:14 +01:00
switch (spb.getClumpTag())
{
case isc_spb_sys_user_name:
spb.getString(spb_sys_user_name);
break;
2008-01-16 10:48:41 +01:00
case isc_spb_user_name:
spb.getString(spb_user_name);
break;
case isc_spb_password:
spb.getString(spb_password);
break;
case isc_spb_password_enc:
spb.getString(spb_password_enc);
break;
case isc_spb_trusted_auth:
spb.getString(spb_trusted_login);
break;
case isc_spb_trusted_role:
spb_trusted_role = true;
break;
case isc_spb_command_line:
spb.getString(spb_command_line);
break;
case isc_spb_address_path:
spb.getString(spb_address_path);
spb_remote = true;
{
2008-12-05 02:20:14 +01:00
ClumpletReader address_stack(ClumpletReader::UnTagged,
spb.getBytes(), spb.getClumpLength());
2008-12-05 02:20:14 +01:00
while (!address_stack.isEof())
{
2008-12-05 02:20:14 +01:00
if (address_stack.getClumpTag() != isc_dpb_address)
{
address_stack.moveNext();
continue;
}
2008-02-03 03:32:39 +01:00
2008-12-05 02:20:14 +01:00
ClumpletReader address(ClumpletReader::UnTagged,
address_stack.getBytes(), address_stack.getClumpLength());
2008-02-03 03:32:39 +01:00
2008-12-05 02:20:14 +01:00
while (!address.isEof())
{
2008-12-05 02:20:14 +01:00
switch (address.getClumpTag())
{
case isc_dpb_addr_protocol:
address.getString(spb_network_protocol);
break;
case isc_dpb_addr_endpoint:
address.getString(spb_remote_address);
break;
default:
break;
}
2008-02-03 03:32:39 +01:00
address.moveNext();
}
2008-02-03 03:32:39 +01:00
break;
}
}
2008-02-03 03:32:39 +01:00
break;
}
}
}
};
// Generic mutex to synchronize services
GlobalPtr<Mutex> svc_mutex;
// All that we need to shutdown service threads when shutdown in progress
typedef Array<Jrd::Service*> AllServices;
GlobalPtr<AllServices> allServices; // protected by svc_mutex
volatile bool svcShutdown = false;
2008-09-11 11:03:08 +02:00
void put_status_arg(ISC_STATUS*& status, const MsgFormat::safe_cell& value)
{
using MsgFormat::safe_cell;
switch (value.type)
{
case safe_cell::at_int64:
case safe_cell::at_uint64:
*status++ = isc_arg_number;
*status++ = static_cast<SLONG>(value.i_value); // May truncate number!
break;
case safe_cell::at_str:
*status++ = isc_arg_string;
2008-09-12 13:05:35 +02:00
*status++ = (ISC_STATUS) (IPTR) value.st_value.s_string;
2008-09-11 11:03:08 +02:00
break;
default:
break;
}
}
} // anonymous namespace
using namespace Jrd;
2008-01-16 10:48:41 +01:00
void Service::parseSwitches()
{
svc_parsed_sw = svc_switches;
svc_parsed_sw.trim();
argv.clear();
argv.push("service"); // why not use it for argv[0]
if (svc_parsed_sw.isEmpty())
{
2008-01-16 10:48:41 +01:00
return;
}
2008-01-16 10:48:41 +01:00
bool inStr = false;
for (size_t i = 0; i < svc_parsed_sw.length(); ++i)
{
switch (svc_parsed_sw[i])
{
case SVC_TRMNTR:
2008-01-16 10:48:41 +01:00
svc_parsed_sw.erase(i, 1);
if (inStr)
{
if (i < svc_parsed_sw.length() && svc_parsed_sw[i] != SVC_TRMNTR)
{
2008-01-16 10:48:41 +01:00
inStr = false;
--i;
}
2008-01-16 10:48:41 +01:00
}
else
{
inStr = true;
--i;
}
break;
2008-12-05 02:20:14 +01:00
2008-01-16 10:48:41 +01:00
case ' ':
if (!inStr)
{
svc_parsed_sw[i] = 0;
}
2008-01-16 10:48:41 +01:00
break;
}
2008-01-16 10:48:41 +01:00
}
argv.push(svc_parsed_sw.c_str());
2008-12-25 07:09:37 +01:00
for (const char* p = svc_parsed_sw.begin(); p < svc_parsed_sw.end(); ++p)
2008-01-16 10:48:41 +01:00
{
2008-12-05 02:20:14 +01:00
if (!*p)
{
2008-01-16 10:48:41 +01:00
argv.push(p + 1);
}
}
2008-01-16 10:48:41 +01:00
}
void Service::output(const char* text)
{
ULONG len = strlen(text);
enqueue(reinterpret_cast<const UCHAR*>(text), len);
}
2008-01-16 10:48:41 +01:00
void Service::printf(const SCHAR* format, ...)
{
// Ensure that service is not detached.
2008-12-05 02:20:14 +01:00
if (svc_flags & SVC_detached)
2008-01-16 10:48:41 +01:00
{
return;
}
string buf;
2008-01-16 10:48:41 +01:00
va_list arglist;
va_start(arglist, format);
buf.vprintf(format, arglist);
va_end(arglist);
enqueue(reinterpret_cast<const UCHAR*>(buf.begin()), buf.length());
2008-01-16 10:48:41 +01:00
}
bool Service::isService()
{
2008-01-16 10:48:41 +01:00
return true;
}
void Service::started()
{
2008-12-05 02:20:14 +01:00
if (!(svc_flags & SVC_evnt_fired))
2008-01-16 10:48:41 +01:00
{
MutexLockGuard guard(svc_mutex);
svc_flags |= SVC_evnt_fired;
svcStart.release();
}
}
2008-01-16 10:48:41 +01:00
void Service::finish()
{
finish(SVC_finished);
2008-01-16 10:48:41 +01:00
}
2008-01-16 10:48:41 +01:00
void Service::putLine(char tag, const char* val)
{
const ULONG len = strlen(val) & 0xFFFF;
2001-05-23 15:26:42 +02:00
UCHAR buf[3];
buf[0] = tag;
buf[1] = len;
buf[2] = len >> 8;
enqueue(buf, sizeof buf);
enqueue(reinterpret_cast<const UCHAR*>(val), len);
2008-01-16 10:48:41 +01:00
}
void Service::putSLong(char tag, SLONG val)
{
UCHAR buf[5];
buf[0] = tag;
buf[1] = val;
buf[2] = val >> 8;
buf[3] = val >> 16;
buf[4] = val >> 24;
enqueue(buf, sizeof buf);
2008-01-16 10:48:41 +01:00
}
2008-12-05 02:20:14 +01:00
2008-01-16 10:48:41 +01:00
void Service::putChar(char tag, char val)
{
UCHAR buf[2];
buf[0] = tag;
buf[1] = val;
enqueue(buf, sizeof buf);
2008-01-16 10:48:41 +01:00
}
void Service::setServiceStatus(const ISC_STATUS* status_vector)
2008-01-16 10:48:41 +01:00
{
if (checkForShutdown())
{
return;
}
2008-12-05 02:20:14 +01:00
if (status_vector != svc_status)
2008-01-16 10:48:41 +01:00
{
Arg::StatusVector svc(svc_status);
Arg::StatusVector passed(status_vector);
svc.append(passed);
svc.copyTo(svc_status);
makePermanentVector(svc_status);
}
}
2001-05-23 15:26:42 +02:00
void Service::setServiceStatus(const USHORT facility, const USHORT errcode, const MsgFormat::SafeArg& args)
2008-01-16 10:48:41 +01:00
{
if (checkForShutdown())
{
return;
}
2008-09-11 11:03:08 +02:00
// Append error codes to the status vector
ISC_STATUS_ARRAY tmp_status;
// stuff the status into temp buffer
MOVE_CLEAR(tmp_status, sizeof(tmp_status));
2008-09-15 01:17:58 +02:00
ISC_STATUS* status = tmp_status;
2008-09-11 11:03:08 +02:00
*status++ = isc_arg_gds;
*status++ = ENCODE_ISC_MSG(errcode, facility);
int tmp_status_len = 3;
// We preserve the five params of the old code.
// Don't want to overflow the status vector.
for (unsigned int loop = 0; loop < 5 && loop < args.getCount(); ++loop)
{
put_status_arg(status, args.getCell(loop));
tmp_status_len += 2;
}
*status++ = isc_arg_end;
if (svc_status[0] != isc_arg_gds ||
2008-12-25 07:09:37 +01:00
(svc_status[0] == isc_arg_gds && svc_status[1] == 0 && svc_status[2] != isc_arg_warning))
2008-09-11 11:03:08 +02:00
{
memcpy(svc_status, tmp_status, sizeof(ISC_STATUS) * tmp_status_len);
}
2008-09-15 01:17:58 +02:00
else
{
2008-09-11 11:03:08 +02:00
int status_len = 0, warning_indx = 0;
PARSE_STATUS(svc_status, status_len, warning_indx);
if (status_len)
--status_len;
// check for duplicated error code
2008-09-12 13:05:35 +02:00
bool duplicate = false;
2008-09-11 11:03:08 +02:00
int i;
2008-09-15 01:17:58 +02:00
for (i = 0; i < ISC_STATUS_LENGTH; i++)
{
2008-09-11 11:03:08 +02:00
if (svc_status[i] == isc_arg_end && i == status_len)
break; // end of argument list
if (i && i == warning_indx)
break; // vector has no more errors
2008-09-15 01:17:58 +02:00
if (svc_status[i] == tmp_status[1] && i != 0 &&
2008-09-11 11:03:08 +02:00
svc_status[i - 1] != isc_arg_warning &&
i + tmp_status_len - 2 < ISC_STATUS_LENGTH &&
(memcmp(&svc_status[i], &tmp_status[1],
2008-12-25 07:09:37 +01:00
sizeof(ISC_STATUS) * (tmp_status_len - 2)) == 0))
2008-09-11 11:03:08 +02:00
{
// duplicate found
duplicate = true;
break;
}
}
2008-09-15 01:17:58 +02:00
2008-12-05 02:20:14 +01:00
if (!duplicate)
2008-09-11 11:03:08 +02:00
{
// if the status_vector has only warnings then adjust err_status_len
int err_status_len = i;
2008-09-15 01:17:58 +02:00
if (err_status_len == 2 && warning_indx != 0)
2008-09-11 11:03:08 +02:00
err_status_len = 0;
ISC_STATUS_ARRAY warning_status;
int warning_count = 0;
2008-12-05 02:20:14 +01:00
if (warning_indx)
2008-09-11 11:03:08 +02:00
{
// copy current warning(s) to a temp buffer
MOVE_CLEAR(warning_status, sizeof(warning_status));
memcpy(warning_status, &svc_status[warning_indx],
sizeof(ISC_STATUS) * (ISC_STATUS_LENGTH - warning_indx));
PARSE_STATUS(warning_status, warning_count, warning_indx);
}
// add the status into a real buffer right in between last error and first warning
2008-09-12 13:05:35 +02:00
i = err_status_len + tmp_status_len;
2008-09-15 01:17:58 +02:00
if (i < ISC_STATUS_LENGTH)
{
2008-12-25 07:09:37 +01:00
memcpy(&svc_status[err_status_len], tmp_status, sizeof(ISC_STATUS) * tmp_status_len);
2008-09-11 11:03:08 +02:00
// copy current warning(s) to the status_vector
2008-09-15 01:17:58 +02:00
if (warning_count && i + warning_count - 1 < ISC_STATUS_LENGTH)
2008-09-11 11:03:08 +02:00
{
2008-12-25 07:09:37 +01:00
memcpy(&svc_status[i - 1], warning_status, sizeof(ISC_STATUS) * warning_count);
2008-09-11 11:03:08 +02:00
}
}
}
}
makePermanentVector(svc_status);
2008-01-16 10:48:41 +01:00
}
void Service::hidePasswd(ArgvType&, int)
{
// no action
}
ISC_STATUS* Service::getStatus()
{
return svc_status;
}
void Service::checkService()
{
// no action
}
void Service::getAddressPath(ClumpletWriter& dpb)
{
if (svc_address_path.hasData())
{
dpb.insertString(isc_dpb_address_path, svc_address_path);
}
}
void Service::makePermanentVector(ISC_STATUS *s)
{
MutexLockGuard guard(svc_mutex);
if (!svc_strings_buffer)
{
svc_strings_buffer = FB_NEW(*getDefaultMemoryPool()) CircularStringsBuffer<MAXPATHLEN * 4>;
}
svc_strings_buffer->makePermanentVector(s, s);
}
void Service::need_admin_privs(Arg::StatusVector& status, const char* message)
{
status << Arg::Gds(isc_insufficient_svc_privileges) << Arg::Str(message);
}
2001-05-23 15:26:42 +02:00
bool Service::ck_space_for_numeric(char*& info, const char* const end)
{
2008-01-16 10:48:41 +01:00
if ((info + 1 + sizeof(ULONG)) > end)
{
if (info < end)
*info++ = isc_info_truncated;
return false;
}
return true;
}
2001-05-23 15:26:42 +02:00
#ifdef DEBUG
THREAD_ENTRY_DECLARE test_thread(THREAD_ENTRY_PARAM);
2001-05-23 15:26:42 +02:00
void test_cmd(USHORT, SCHAR *, TEXT **);
#define TEST_THREAD test_thread
#define TEST_CMD test_cmd
#else
#define TEST_THREAD NULL
#define TEST_CMD NULL
#endif
/* Service Functions */
2008-01-16 10:48:41 +01:00
#if !defined(BOOT_BUILD)
#include "../burp/burp_proto.h"
#include "../alice/alice_proto.h"
THREAD_ENTRY_DECLARE main_gstat(THREAD_ENTRY_PARAM arg);
2008-11-20 18:23:06 +01:00
#include "../utilities/nbackup/nbk_proto.h"
2001-05-23 15:26:42 +02:00
#define MAIN_GBAK BURP_main
#define MAIN_GFIX ALICE_main
2001-05-23 15:26:42 +02:00
#define MAIN_GSTAT main_gstat
2008-11-20 18:23:06 +01:00
#define MAIN_NBAK NBACKUP_main
2001-05-23 15:26:42 +02:00
#else
#define MAIN_GBAK NULL
#define MAIN_GFIX NULL
#define MAIN_GSTAT NULL
2008-11-20 18:23:06 +01:00
#define MAIN_NBAK NULL
2001-05-23 15:26:42 +02:00
#endif
2008-01-16 10:48:41 +01:00
#if !defined(EMBEDDED) && !defined(BOOT_BUILD)
2005-06-23 00:26:59 +02:00
#include "../utilities/gsec/gsec_proto.h"
#define MAIN_GSEC GSEC_main
#else
#define MAIN_GSEC NULL
#endif
struct serv_entry
{
USHORT serv_action; // isc_action_svc_....
const TEXT* serv_name; // old service name
const TEXT* serv_std_switches; // old cmd-line switches
ThreadEntryPoint* serv_thd; // thread to execute
};
static const serv_entry services[] =
2001-05-23 15:26:42 +02:00
{
2008-01-16 10:48:41 +01:00
{ isc_action_max, "print_cache", "", NULL },
{ isc_action_max, "print_locks", "", NULL },
{ isc_action_max, "start_cache", "", NULL },
{ isc_action_max, "analyze_database", "", MAIN_GFIX },
{ isc_action_max, "backup", "-b", MAIN_GBAK },
{ isc_action_max, "create", "-c", MAIN_GBAK },
{ isc_action_max, "restore", "-r", MAIN_GBAK },
{ isc_action_max, "gdef", "-svc", NULL },
{ isc_action_max, "gsec", "-svc", MAIN_GSEC },
{ isc_action_max, "disable_journal", "-disable", NULL },
{ isc_action_max, "dump_journal", "-online_dump", NULL },
{ isc_action_max, "enable_journal", "-enable", NULL },
{ isc_action_max, "monitor_journal", "-console", NULL },
{ isc_action_max, "query_server", NULL, NULL },
{ isc_action_max, "start_journal", "-server", NULL },
{ isc_action_max, "stop_cache", "-shut -cache", MAIN_GFIX },
{ isc_action_max, "stop_journal", "-console", NULL },
{ isc_action_max, "anonymous", NULL, NULL },
// NEW VERSION 2 calls, the name field MUST be different from those names above
{ isc_action_max, "service_mgr", NULL, NULL },
{ isc_action_svc_backup, "Backup Database", NULL, MAIN_GBAK },
{ isc_action_svc_restore, "Restore Database", NULL, MAIN_GBAK },
{ isc_action_svc_repair, "Repair Database", NULL, MAIN_GFIX },
{ isc_action_svc_add_user, "Add User", NULL, MAIN_GSEC },
{ isc_action_svc_delete_user, "Delete User", NULL, MAIN_GSEC },
{ isc_action_svc_modify_user, "Modify User", NULL, MAIN_GSEC },
{ isc_action_svc_display_user, "Display User", NULL, MAIN_GSEC },
{ isc_action_svc_properties, "Database Properties", NULL, MAIN_GFIX },
{ isc_action_svc_lock_stats, "Lock Stats", NULL, TEST_THREAD },
{ isc_action_svc_db_stats, "Database Stats", NULL, MAIN_GSTAT },
{ isc_action_svc_get_fb_log, "Get Log File", NULL, Service::readFbLog },
2008-11-20 18:23:06 +01:00
{ isc_action_svc_nbak, "Incremental Backup Database", NULL, MAIN_NBAK },
{ isc_action_svc_nrest, "Incremental Restore Database", NULL, MAIN_NBAK },
2001-05-23 15:26:42 +02:00
/* actions with no names are undocumented */
2008-01-16 10:48:41 +01:00
{ isc_action_svc_set_config, NULL, NULL, TEST_THREAD },
{ isc_action_svc_default_config, NULL, NULL, TEST_THREAD },
{ isc_action_svc_set_env, NULL, NULL, TEST_THREAD },
{ isc_action_svc_set_env_lock, NULL, NULL, TEST_THREAD },
{ isc_action_svc_set_env_msg, NULL, NULL, TEST_THREAD },
{ 0, NULL, NULL, NULL }
2001-05-23 15:26:42 +02:00
};
/* The SERVER_CAPABILITIES_FLAG is used to mark architectural
** differences across servers. This allows applications like server
2002-02-16 04:27:33 +01:00
** manager to disable features as necessary.
2001-05-23 15:26:42 +02:00
*/
#ifdef SUPERSERVER
2004-05-06 02:18:09 +02:00
const ULONG SERVER_CAPABILITIES = REMOTE_HOP_SUPPORT | MULTI_CLIENT_SUPPORT | SERVER_CONFIG_SUPPORT;
2001-05-23 15:26:42 +02:00
# ifdef WIN_NT
2004-05-06 02:18:09 +02:00
const ULONG SERVER_CAPABILITIES_FLAG = SERVER_CAPABILITIES | QUOTED_FILENAME_SUPPORT;
2001-05-23 15:26:42 +02:00
# else
2004-05-06 02:18:09 +02:00
const ULONG SERVER_CAPABILITIES_FLAG = SERVER_CAPABILITIES | NO_SERVER_SHUTDOWN_SUPPORT;
# endif // WIN_NT
#else // SUPERSERVER
const ULONG SERVER_CAPABILITIES_FLAG = REMOTE_HOP_SUPPORT | NO_SERVER_SHUTDOWN_SUPPORT;
#endif // SERVER_CAPABILITIES
2001-05-23 15:26:42 +02:00
2008-04-18 03:37:44 +02:00
Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_data)
2008-12-05 02:20:14 +01:00
: svc_parsed_sw(getPool()),
svc_stdout_head(0), svc_stdout_tail(0),
2008-02-11 10:55:16 +01:00
svc_resp_alloc(getPool()), svc_resp_buf(0), svc_resp_ptr(0), svc_resp_buf_len(0),
svc_resp_len(0), svc_flags(0), svc_user_flag(0), svc_spb_version(0), svc_do_shutdown(false),
2008-12-05 02:20:14 +01:00
svc_username(getPool()), svc_enc_password(getPool()),
svc_trusted_login(getPool()), svc_trusted_role(false), svc_uses_security_database(false),
svc_switches(getPool()), svc_perm_sw(getPool()), svc_address_path(getPool()),
2008-09-06 20:42:55 +02:00
svc_strings_buffer(NULL)
2001-05-23 15:26:42 +02:00
{
2008-09-11 11:03:08 +02:00
memset(svc_status, 0, sizeof svc_status);
2001-05-23 15:26:42 +02:00
2008-04-03 03:11:26 +02:00
{ // scope
// Account service block in global array
MutexLockGuard guard(svc_mutex);
checkForShutdown();
allServices->add(this);
}
2008-11-20 18:23:06 +01:00
// Since this moment we should remove this service from allServices in case of error thrown
try
{
// If the service name begins with a slash, ignore it.
if (*service_name == '/' || *service_name == '\\') {
service_name++;
}
2001-05-23 15:26:42 +02:00
2008-11-20 18:23:06 +01:00
// Find the service by looking for an exact match.
const string svcname(service_name);
const serv_entry* serv;
for (serv = services; serv->serv_name; serv++) {
if (svcname == serv->serv_name)
break;
}
2001-05-23 15:26:42 +02:00
2008-11-20 18:23:06 +01:00
if (!serv->serv_name) {
2008-12-05 02:20:14 +01:00
status_exception::raise(Arg::Gds(isc_service_att_err) <<
2008-11-20 18:23:06 +01:00
Arg::Gds(isc_svcnotdef) << Arg::Str(svcname));
}
2008-11-20 18:23:06 +01:00
// Process the service parameter block.
ClumpletReader spb(ClumpletReader::SpbAttach, spb_data, spb_length);
Options options(spb);
// Perhaps checkout the user in the security database.
SecurityDatabase::InitHolder siHolder;
USHORT user_flag;
if (!strcmp(serv->serv_name, "anonymous")) {
user_flag = SVC_user_none;
2006-12-08 19:38:15 +01:00
}
else {
2008-11-20 18:23:06 +01:00
// If we have embedded service connection, let's check for unix OS auth
2008-12-25 07:09:37 +01:00
if (!options.spb_trusted_login.hasData() && !options.spb_remote &&
!options.spb_user_name.hasData())
2008-11-20 18:23:06 +01:00
{
if (ISC_get_user(&options.spb_trusted_login, NULL, NULL, NULL)) {
options.spb_trusted_login = SYSDBA_USER_NAME;
}
}
2008-11-20 18:23:06 +01:00
if (options.spb_trusted_login.hasData()) {
options.spb_user_name = options.spb_trusted_login;
2006-12-10 14:43:31 +01:00
}
2008-11-20 18:23:06 +01:00
else {
// If we have embedded service connection, let's check for environment
if (!options.spb_remote) {
if (!options.spb_user_name.hasData()) {
fb_utils::readenv(ISC_USER, options.spb_user_name);
}
if (!options.spb_password.hasData()) {
fb_utils::readenv(ISC_PASSWORD, options.spb_password);
}
}
2008-12-05 02:20:14 +01:00
2008-11-20 18:23:06 +01:00
if (!options.spb_user_name.hasData()) {
// user name and password are required while
// attaching to the services manager
status_exception::raise(Arg::Gds(isc_service_att_err) << Arg::Gds(isc_svcnouser));
}
2008-11-20 18:23:06 +01:00
string name; // unused after retrieved
int id, group, node_id;
2008-11-20 18:23:06 +01:00
const string remote = options.spb_network_protocol +
2008-12-25 07:09:37 +01:00
(options.spb_network_protocol.isEmpty() ||
2008-12-25 15:25:01 +01:00
options.spb_remote_address.isEmpty() ? "" : "/") +
2008-12-25 07:09:37 +01:00
options.spb_remote_address;
2008-11-20 18:23:06 +01:00
SecurityDatabase::verifyUser(name, options.spb_user_name.nullStr(),
2008-12-05 02:20:14 +01:00
options.spb_password.nullStr(),
2008-11-20 18:23:06 +01:00
options.spb_password_enc.nullStr(),
&id, &group, &node_id, remote);
svc_uses_security_database = true;
}
2001-05-23 15:26:42 +02:00
2008-11-20 18:23:06 +01:00
if (options.spb_user_name.length() > USERNAME_LENGTH) {
status_exception::raise(Arg::Gds(isc_long_login) <<
Arg::Num(options.spb_user_name.length()) << Arg::Num(USERNAME_LENGTH));
}
// Check that the validated user has the authority to access this service
string uName(options.spb_user_name);
uName.upper();
if ((uName != SYSDBA_USER_NAME) && !options.spb_trusted_role) {
user_flag = SVC_user_any;
}
else {
user_flag = SVC_user_dba | SVC_user_any;
}
}
2008-11-20 18:23:06 +01:00
// move service switches in
string switches;
if (serv->serv_std_switches)
switches = serv->serv_std_switches;
if (options.spb_command_line.hasData() && serv->serv_std_switches)
switches += ' ';
switches += options.spb_command_line;
2008-12-05 02:20:14 +01:00
2008-11-20 18:23:06 +01:00
svc_flags = switches.hasData() ? SVC_cmd_line : 0;
svc_perm_sw = switches;
svc_user_flag = user_flag;
svc_service = serv;
svc_spb_version = options.spb_version;
svc_username = options.spb_user_name;
svc_trusted_login = options.spb_trusted_login;
svc_trusted_role = options.spb_trusted_role;
svc_address_path = options.spb_address_path;
// The password will be issued to the service threads on NT since
// there is no OS authentication. If the password is not yet
// encrypted, then encrypt it before saving it (since there is no
// decrypt function).
2008-12-05 02:20:14 +01:00
if (options.spb_password_enc.hasData())
2008-11-20 18:23:06 +01:00
{
svc_enc_password = options.spb_password_enc;
2003-12-31 06:36:12 +01:00
}
2008-12-05 02:20:14 +01:00
else if (options.spb_password.hasData())
2008-11-20 18:23:06 +01:00
{
svc_enc_password.resize(MAX_PASSWORD_LENGTH + 2);
2008-12-05 02:20:14 +01:00
ENC_crypt(svc_enc_password.begin(), svc_enc_password.length(),
2008-11-20 18:23:06 +01:00
options.spb_password.c_str(), PASSWORD_SALT);
svc_enc_password.recalculate_length();
svc_enc_password.erase(0, 2);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2008-11-20 18:23:06 +01:00
// If an executable is defined for the service, try to fork a new thread.
// Only do this if we are working with a version 1 service
if (serv->serv_thd && options.spb_version == isc_spb_version1)
{
start(serv->serv_thd);
}
2001-05-23 15:26:42 +02:00
2008-11-20 18:23:06 +01:00
if (svc_uses_security_database)
{
siHolder.clear();
}
2001-05-23 15:26:42 +02:00
}
2008-11-20 18:23:06 +01:00
catch (const Exception&)
{
2008-11-20 18:23:06 +01:00
removeFromAllServices();
throw;
}
2001-05-23 15:26:42 +02:00
}
2008-09-08 16:07:19 +02:00
static THREAD_ENTRY_DECLARE svcShutdownThread(THREAD_ENTRY_PARAM)
2001-05-23 15:26:42 +02:00
{
2008-09-08 16:07:19 +02:00
if (fb_shutdown(10 * 1000 /* 10 seconds */, fb_shutrsn_services) == FB_SUCCESS)
{
2008-09-08 16:07:19 +02:00
InstanceControl::registerShutdown(0);
exit(0);
2001-05-23 15:26:42 +02:00
}
2008-09-08 17:16:52 +02:00
return 0;
2008-09-08 16:07:19 +02:00
}
void Service::detach()
{
// save it cause after call to finish() we can't access class members any more
bool localDoShutdown = svc_do_shutdown;
2001-05-23 15:26:42 +02:00
if (svc_uses_security_database)
{
SecurityDatabase::shutdown();
}
// Mark service as detached.
finish(SVC_detached);
2008-09-08 16:07:19 +02:00
2008-12-05 02:20:14 +01:00
if (localDoShutdown)
2008-09-08 16:07:19 +02:00
{
// run in separate thread to avoid blocking in remote
gds__thread_start(svcShutdownThread, 0, 0, 0, 0);
}
}
2001-05-23 15:26:42 +02:00
Service::~Service()
{
2008-11-20 18:23:06 +01:00
removeFromAllServices();
}
void Service::removeFromAllServices()
{
MutexLockGuard guard(svc_mutex);
AllServices& all(allServices);
2008-04-03 03:11:26 +02:00
for (unsigned int pos = 0; pos < all.getCount(); ++pos)
{
if (all[pos] == this)
{
all.remove(pos);
break;
}
}
}
bool Service::checkForShutdown()
{
if (svcShutdown)
{
MutexLockGuard guard(svc_mutex);
2008-04-03 03:11:26 +02:00
if (svc_flags & SVC_shutdown)
{
// Here we avoid multiple exceptions thrown
return true;
}
2008-04-03 03:11:26 +02:00
svc_flags |= SVC_shutdown;
status_exception::raise(Arg::Gds(isc_att_shutdown));
}
2008-04-03 03:11:26 +02:00
return false;
}
void Service::shutdownServices()
{
svcShutdown = true;
2008-04-03 03:11:26 +02:00
MutexLockGuard guard(svc_mutex);
AllServices& all(allServices);
2008-04-03 03:11:26 +02:00
for (unsigned int pos = 0; pos < all.getCount(); )
{
if (all[pos]->svc_flags & SVC_thd_running)
{
svc_mutex->leave();
THD_sleep(1);
svc_mutex->enter();
pos = 0;
continue;
}
2008-04-03 03:11:26 +02:00
++pos;
}
2001-05-23 15:26:42 +02:00
}
ISC_STATUS Service::query2(thread_db* tdbb,
USHORT send_item_length,
const SCHAR* send_items,
USHORT recv_item_length,
const SCHAR* recv_items,
USHORT buffer_length,
SCHAR* info)
{
SCHAR item;
char buffer[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
USHORT l, length, version, get_flags;
// Setup the status vector
Arg::StatusVector status;
2001-05-23 15:26:42 +02:00
// Process the send portion of the query first.
2004-11-16 09:52:29 +01:00
USHORT timeout = 0;
const SCHAR* items = send_items;
const SCHAR* const end_items = items + send_item_length;
2001-05-23 15:26:42 +02:00
while (items < end_items && *items != isc_info_end)
{
switch ((item = *items++))
{
case isc_info_end:
break;
default:
if (items + 2 <= end_items) {
2008-12-25 07:09:37 +01:00
l = (USHORT) gds__vax_integer(reinterpret_cast<const UCHAR*>(items), 2);
2001-05-23 15:26:42 +02:00
items += 2;
if (items + l <= end_items) {
2009-01-20 09:33:59 +01:00
switch (item)
{
2001-05-23 15:26:42 +02:00
case isc_info_svc_line:
put(items, l);
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_message:
put(items - 3, l + 3);
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_timeout:
timeout =
2008-12-25 07:09:37 +01:00
(USHORT) gds__vax_integer(reinterpret_cast<const UCHAR*>(items), l);
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_version:
version =
2008-12-25 07:09:37 +01:00
(USHORT) gds__vax_integer(reinterpret_cast<const UCHAR*>(items), l);
2001-05-23 15:26:42 +02:00
break;
}
}
items += l;
}
else
items += 2;
break;
}
}
// Process the receive portion of the query now.
const SCHAR* const end = info + buffer_length;
2001-05-23 15:26:42 +02:00
items = recv_items;
const SCHAR* const end_items2 = items + recv_item_length;
SCHAR* start_info;
if (*items == isc_info_length) {
start_info = info;
items++;
}
else {
start_info = 0;
}
while (items < end_items2 && *items != isc_info_end)
2001-05-23 15:26:42 +02:00
{
/*
if we attached to the "anonymous" service we allow only following queries:
isc_info_svc_get_config - called from within remote/ibconfig.cpp
isc_info_svc_dump_pool_info - called from within utilities/print_pool.cpp
2001-05-23 15:26:42 +02:00
*/
if (svc_user_flag == SVC_user_none)
2001-05-23 15:26:42 +02:00
{
switch (*items)
{
case isc_info_svc_get_config:
case isc_info_svc_dump_pool_info:
break;
default:
status_exception::raise(Arg::Gds(isc_bad_spb_form));
2001-05-23 15:26:42 +02:00
break;
}
}
switch ((item = *items++))
{
case isc_info_end:
break;
case isc_info_svc_svr_db_info:
if (svc_user_flag & SVC_user_dba)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
UCHAR dbbuf[1024];
ULONG num_dbs = 0;
ULONG num_att = 0;
2001-05-23 15:26:42 +02:00
*info++ = item;
UCHAR* const ptr =
JRD_num_attachments(dbbuf, sizeof(dbbuf), JRD_info_dbnames, &num_att, &num_dbs);
2001-05-23 15:26:42 +02:00
/* Move the number of attachments into the info buffer */
if (!ck_space_for_numeric(info, end))
return 0;
2001-05-23 15:26:42 +02:00
*info++ = isc_spb_num_att;
ADD_SPB_NUMERIC(info, num_att);
/* Move the number of databases in use into the info buffer */
if (!ck_space_for_numeric(info, end))
return 0;
2001-05-23 15:26:42 +02:00
*info++ = isc_spb_num_db;
ADD_SPB_NUMERIC(info, num_dbs);
/* Move db names into the info buffer */
const TEXT* ptr2 = reinterpret_cast<const TEXT*>(ptr);
2004-11-16 09:52:29 +01:00
if (ptr2) {
USHORT num = (USHORT) isc_vax_integer(ptr2, sizeof(USHORT));
2003-11-04 00:59:24 +01:00
fb_assert(num == num_dbs);
2001-05-23 15:26:42 +02:00
ptr2 += sizeof(USHORT);
for (; num; num--) {
length = (USHORT) isc_vax_integer(ptr2, sizeof(USHORT));
2001-05-23 15:26:42 +02:00
ptr2 += sizeof(USHORT);
2008-12-25 07:09:37 +01:00
if (!(info = INF_put_item(isc_spb_dbname, length, ptr2, info, end)))
2004-11-16 09:52:29 +01:00
{
if (ptr != dbbuf)
gds__free(ptr); // memory has been allocated by JRD_num_attachments()
2001-05-23 15:26:42 +02:00
return 0;
}
ptr2 += length;
}
if (ptr != dbbuf)
gds__free(ptr); // memory has been allocated by JRD_num_attachments()
2001-05-23 15:26:42 +02:00
}
if (info < end)
*info++ = isc_info_flag_end;
}
else
need_admin_privs(status, "isc_info_svc_svr_db_info");
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_svr_online:
*info++ = item;
if (svc_user_flag & SVC_user_dba) {
svc_do_shutdown = false;
2008-02-13 17:52:29 +01:00
WHY_set_shutdown(false);
2001-05-23 15:26:42 +02:00
}
else
need_admin_privs(status, "isc_info_svc_svr_online");
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_svr_offline:
*info++ = item;
if (svc_user_flag & SVC_user_dba) {
svc_do_shutdown = true;
2008-02-13 17:52:29 +01:00
WHY_set_shutdown(true);
2001-05-23 15:26:42 +02:00
}
else
need_admin_privs(status, "isc_info_svc_svr_offline");
2001-05-23 15:26:42 +02:00
break;
/* The following 3 service commands (or items) stuff the response
buffer 'info' with values of environment variable FIREBIRD,
FIREBIRD_LOCK or FIREBIRD_MSG. If the environment variable
2001-05-23 15:26:42 +02:00
is not set then default value is returned.
*/
case isc_info_svc_get_env:
case isc_info_svc_get_env_lock:
case isc_info_svc_get_env_msg:
if (svc_user_flag & SVC_user_dba)
{
2008-12-05 02:20:14 +01:00
switch (item)
{
case isc_info_svc_get_env:
gds__prefix(buffer, "");
break;
case isc_info_svc_get_env_lock:
gds__prefix_lock(buffer, "");
break;
case isc_info_svc_get_env_msg:
gds__prefix_msg(buffer, "");
}
2001-05-23 15:26:42 +02:00
2008-04-18 03:37:44 +02:00
// Note: it is safe to use strlen to get a length of "buffer"
// because gds_prefix[_lock|_msg] return a zero-terminated
// string.
info = INF_put_item(item, strlen(buffer), buffer, info, end);
2008-12-05 02:20:14 +01:00
if (!info)
{
return 0;
}
}
else
{
need_admin_privs(status, "isc_info_svc_get_env");
2001-05-23 15:26:42 +02:00
}
break;
#ifdef SUPERSERVER
case isc_info_svc_dump_pool_info:
{
char fname[MAXPATHLEN];
2004-05-12 21:39:17 +02:00
size_t length2 = isc_vax_integer(items, sizeof(USHORT));
if (length2 >= sizeof(fname))
length2 = sizeof(fname) - 1; // truncation
2001-05-23 15:26:42 +02:00
items += sizeof(USHORT);
2004-05-12 21:39:17 +02:00
strncpy(fname, items, length2);
fname[length2] = 0;
2001-05-23 15:26:42 +02:00
break;
}
#endif
2001-05-23 15:26:42 +02:00
case isc_info_svc_get_config:
// TODO: iterate through all integer-based config values
// and return them to the client
2001-05-23 15:26:42 +02:00
break;
/*
2001-05-23 15:26:42 +02:00
case isc_info_svc_default_config:
*info++ = item;
if (svc_user_flag & SVC_user_dba) {
// TODO: reset the config values to defaults
2001-05-23 15:26:42 +02:00
}
else
need_admin_privs(status, "isc_info_svc_default_config");
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_set_config:
*info++ = item;
if (svc_user_flag & SVC_user_dba) {
// TODO: set the config values
2001-05-23 15:26:42 +02:00
}
else {
need_admin_privs(status, "isc_info_svc_set_config");
2001-05-23 15:26:42 +02:00
}
break;
*/
2001-05-23 15:26:42 +02:00
case isc_info_svc_version:
/* The version of the service manager */
if (!ck_space_for_numeric(info, end))
return 0;
2001-05-23 15:26:42 +02:00
*info++ = item;
ADD_SPB_NUMERIC(info, SERVICE_VERSION);
break;
case isc_info_svc_capabilities:
/* bitmask defining any specific architectural differences */
if (!ck_space_for_numeric(info, end))
return 0;
2001-05-23 15:26:42 +02:00
*info++ = item;
ADD_SPB_NUMERIC(info, SERVER_CAPABILITIES_FLAG);
break;
case isc_info_svc_running:
/* Returns the status of the flag SVC_thd_running */
if (!ck_space_for_numeric(info, end))
return 0;
2001-05-23 15:26:42 +02:00
*info++ = item;
if (svc_flags & SVC_thd_running)
2001-05-23 15:26:42 +02:00
ADD_SPB_NUMERIC(info, TRUE)
else
ADD_SPB_NUMERIC(info, FALSE)
break;
case isc_info_svc_server_version:
/* The version of the server engine */
info = INF_put_item(item, strlen(GDS_VERSION), GDS_VERSION, info, end);
if (!info) {
return 0;
}
break;
case isc_info_svc_implementation:
/* The server implementation - e.g. Firebird/sun4 */
2001-05-23 15:26:42 +02:00
isc_format_implementation(IMPLEMENTATION, sizeof(buffer), buffer,
0, 0, NULL);
info = INF_put_item(item, strlen(buffer), buffer, info, end);
if (!info) {
return 0;
}
break;
case isc_info_svc_user_dbpath:
if (svc_user_flag & SVC_user_dba)
2004-11-16 09:52:29 +01:00
{
/* The path to the user security database (security2.fdb) */
SecurityDatabase::getPath(buffer);
2008-12-25 07:09:37 +01:00
if (!(info = INF_put_item(item, strlen(buffer), buffer, info, end)))
{
return 0;
}
2001-05-23 15:26:42 +02:00
}
else
need_admin_privs(status, "isc_info_svc_user_dbpath");
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_response:
svc_resp_len = 0;
if (info + 4 >= end) {
2001-05-23 15:26:42 +02:00
*info++ = isc_info_truncated;
break;
}
put(&item, 1);
get(&item, 1, GET_BINARY, 0, &length);
get(buffer, 2, GET_BINARY, 0, &length);
2008-12-25 07:09:37 +01:00
l = (USHORT) gds__vax_integer(reinterpret_cast<UCHAR*>(buffer), 2);
length = MIN(end - (info + 5), l);
get(info + 3, length, GET_BINARY, 0, &length);
2001-05-23 15:26:42 +02:00
info = INF_put_item(item, length, info + 3, info, end);
if (length != l) {
*info++ = isc_info_truncated;
l -= length;
if (l > svc_resp_buf_len) {
2008-02-11 10:55:16 +01:00
try {
svc_resp_buf = svc_resp_alloc.getBuffer(l);
}
catch (const BadAlloc&) { // NOMEM:
2001-05-23 15:26:42 +02:00
DEV_REPORT("SVC_query: out of memory");
2008-02-14 02:31:15 +01:00
// NOMEM: not really handled well
l = 0; // set the length to zero
2001-05-23 15:26:42 +02:00
}
svc_resp_buf_len = l;
2001-05-23 15:26:42 +02:00
}
get(reinterpret_cast<char*>(svc_resp_buf), l, GET_BINARY, 0, &length);
svc_resp_ptr = svc_resp_buf;
svc_resp_len = l;
2001-05-23 15:26:42 +02:00
}
break;
case isc_info_svc_response_more:
if ( (l = length = svc_resp_len) )
length = MIN(end - (info + 5), l);
2001-05-23 15:26:42 +02:00
if (!
(info =
2008-12-25 07:09:37 +01:00
INF_put_item(item, length, reinterpret_cast<const char*>(svc_resp_ptr), info, end)))
{
2001-05-23 15:26:42 +02:00
return 0;
}
svc_resp_ptr += length;
svc_resp_len -= length;
2001-05-23 15:26:42 +02:00
if (length != l)
*info++ = isc_info_truncated;
break;
case isc_info_svc_total_length:
put(&item, 1);
get(&item, 1, GET_BINARY, 0, &length);
get(buffer, 2, GET_BINARY, 0, &length);
2008-02-03 03:32:39 +01:00
l = (USHORT) gds__vax_integer(reinterpret_cast<UCHAR*>(buffer), 2);
get(buffer, l, GET_BINARY, 0, &length);
2001-05-23 15:26:42 +02:00
if (!(info = INF_put_item(item, length, buffer, info, end))) {
return 0;
}
break;
case isc_info_svc_line:
case isc_info_svc_to_eof:
case isc_info_svc_limbo_trans:
case isc_info_svc_get_users:
if (info + 4 >= end) {
2001-05-23 15:26:42 +02:00
*info++ = isc_info_truncated;
break;
}
2008-01-16 10:48:41 +01:00
switch (item)
{
case isc_info_svc_line:
2001-05-23 15:26:42 +02:00
get_flags = GET_LINE;
2008-01-16 10:48:41 +01:00
break;
case isc_info_svc_to_eof:
get_flags = GET_EOF;
2008-01-16 10:48:41 +01:00
break;
default:
get_flags = GET_BINARY;
2008-01-16 10:48:41 +01:00
break;
}
2001-05-23 15:26:42 +02:00
get(info + 3, end - (info + 5), get_flags, timeout, &length);
2001-05-23 15:26:42 +02:00
2002-02-16 04:27:33 +01:00
/* If the read timed out, return the data, if any, & a timeout
2001-05-23 15:26:42 +02:00
item. If the input buffer was not large enough
to store a read to eof, return the data that was read along
with an indication that more is available. */
if (!(info = INF_put_item(item, length, info + 3, info, end))) {
return 0;
}
2001-05-23 15:26:42 +02:00
if (svc_flags & SVC_timeout)
2001-05-23 15:26:42 +02:00
{
*info++ = isc_info_svc_timeout;
}
else
{
if (!length && !(svc_flags & SVC_finished))
2001-05-23 15:26:42 +02:00
{
*info++ = isc_info_data_not_ready;
}
2008-02-03 03:32:39 +01:00
else if (item == isc_info_svc_to_eof && !(svc_flags & SVC_finished))
2001-05-23 15:26:42 +02:00
{
2008-02-03 03:32:39 +01:00
*info++ = isc_info_truncated;
2001-05-23 15:26:42 +02:00
}
}
break;
default:
status << Arg::Gds(isc_wish_list);
break;
2001-05-23 15:26:42 +02:00
}
if (svc_user_flag == SVC_user_none)
2001-05-23 15:26:42 +02:00
break;
}
if (info < end)
*info = isc_info_end;
if (start_info && (end - info >= 7))
{
2008-12-25 07:09:37 +01:00
const SLONG number = 1 + (info - start_info);
memmove(start_info + 7, start_info, number);
2006-09-01 12:51:57 +02:00
USHORT length2 = INF_convert(number, buffer);
INF_put_item(isc_info_length, length2, buffer, start_info, end);
}
2001-05-23 15:26:42 +02:00
if (!(svc_flags & SVC_thd_running))
{
finish(SVC_finished);
}
status.copyTo(tdbb->tdbb_status_vector);
2001-05-23 15:26:42 +02:00
return tdbb->tdbb_status_vector[1];
}
void Service::query(USHORT send_item_length,
const SCHAR* send_items,
USHORT recv_item_length,
const SCHAR* recv_items,
USHORT buffer_length,
SCHAR* info)
2001-05-23 15:26:42 +02:00
{
SCHAR item, *p;
char buffer[256];
TEXT PathBuffer[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
USHORT l, length, version, get_flags;
// Process the send portion of the query first.
2004-11-16 09:52:29 +01:00
USHORT timeout = 0;
const SCHAR* items = send_items;
const SCHAR* const end_items = items + send_item_length;
2001-05-23 15:26:42 +02:00
while (items < end_items && *items != isc_info_end)
{
switch ((item = *items++))
{
case isc_info_end:
break;
default:
if (items + 2 <= end_items)
{
l = (USHORT) gds__vax_integer(reinterpret_cast<const UCHAR*>(items), 2);
2001-05-23 15:26:42 +02:00
items += 2;
if (items + l <= end_items)
{
2009-01-20 09:33:59 +01:00
switch (item)
{
2001-05-23 15:26:42 +02:00
case isc_info_svc_line:
put(items, l);
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_message:
put(items - 3, l + 3);
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_timeout:
2008-12-25 07:09:37 +01:00
timeout = (USHORT) gds__vax_integer(reinterpret_cast<const UCHAR*>(items), l);
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_version:
2008-12-25 07:09:37 +01:00
version = (USHORT) gds__vax_integer(reinterpret_cast<const UCHAR*>(items), l);
2001-05-23 15:26:42 +02:00
break;
}
}
items += l;
}
else
items += 2;
break;
}
}
// Process the receive portion of the query now.
const SCHAR* const end = info + buffer_length;
2001-05-23 15:26:42 +02:00
items = recv_items;
const SCHAR* const end_items2 = items + recv_item_length;
while (items < end_items2 && *items != isc_info_end)
2001-05-23 15:26:42 +02:00
{
switch ((item = *items++))
{
case isc_info_end:
break;
case isc_info_svc_svr_db_info:
if (svc_user_flag & SVC_user_dba)
2001-12-24 03:51:06 +01:00
{
ULONG num_att = 0;
ULONG num_dbs = 0;
JRD_num_attachments(NULL, 0, JRD_info_none, &num_att, &num_dbs);
length = INF_convert(num_att, buffer);
2008-12-25 07:09:37 +01:00
info = INF_put_item(item, length, buffer, info, end);
2001-12-24 03:51:06 +01:00
if (!info) {
return;
}
length = INF_convert(num_dbs, buffer);
2008-12-25 07:09:37 +01:00
info = INF_put_item(item, length, buffer, info, end);
2001-12-24 03:51:06 +01:00
if (!info) {
return;
}
2001-05-23 15:26:42 +02:00
}
/*
* Can not return error for service v.1 => simply ignore request
else
need_admin_privs(status, "isc_info_svc_svr_db_info");
*/
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_svr_online:
*info++ = item;
if (svc_user_flag & SVC_user_dba) {
svc_do_shutdown = false;
2008-02-13 17:52:29 +01:00
WHY_set_shutdown(false);
2001-05-23 15:26:42 +02:00
*info++ = 0; /* Success */
}
else
*info++ = 2; /* No user authority */
break;
case isc_info_svc_svr_offline:
*info++ = item;
if (svc_user_flag & SVC_user_dba) {
svc_do_shutdown = true;
2008-02-13 17:52:29 +01:00
WHY_set_shutdown(true);
2001-05-23 15:26:42 +02:00
*info++ = 0; /* Success */
}
else
*info++ = 2; /* No user authority */
break;
/* The following 3 service commands (or items) stuff the response
buffer 'info' with values of environment variable FIREBIRD,
FIREBIRD_LOCK or FIREBIRD_MSG. If the environment variable
2001-05-23 15:26:42 +02:00
is not set then default value is returned.
*/
case isc_info_svc_get_env:
case isc_info_svc_get_env_lock:
case isc_info_svc_get_env_msg:
if (svc_user_flag & SVC_user_dba)
2004-12-24 09:52:39 +01:00
{
2009-01-20 09:33:59 +01:00
switch (item)
{
case isc_info_svc_get_env:
gds__prefix(PathBuffer, "");
break;
case isc_info_svc_get_env_lock:
gds__prefix_lock(PathBuffer, "");
break;
case isc_info_svc_get_env_msg:
gds__prefix_msg(PathBuffer, "");
}
2008-04-18 03:37:44 +02:00
// Note: it is safe to use strlen to get a length of "buffer"
// because gds_prefix[_lock|_msg] return a zero-terminated
// string.
if (!(info = INF_put_item(item, strlen(PathBuffer), PathBuffer, info, end)))
return;
2001-05-23 15:26:42 +02:00
}
/*
* Can not return error for service v.1 => simply ignore request
else
need_admin_privs(status, "isc_info_svc_get_env");
*/
2001-05-23 15:26:42 +02:00
break;
#ifdef SUPERSERVER
case isc_info_svc_dump_pool_info:
{
char fname[MAXPATHLEN];
2004-05-12 21:39:17 +02:00
size_t length2 = isc_vax_integer(items, sizeof(USHORT));
if (length2 >= sizeof(fname))
length2 = sizeof(fname) - 1; // truncation
2001-05-23 15:26:42 +02:00
items += sizeof(USHORT);
2004-05-12 21:39:17 +02:00
strncpy(fname, items, length2);
fname[length2] = 0;
2001-05-23 15:26:42 +02:00
break;
}
#endif
/*
2001-05-23 15:26:42 +02:00
case isc_info_svc_get_config:
// TODO: iterate through all integer-based config values
// and return them to the client
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_default_config:
*info++ = item;
if (svc_user_flag & SVC_user_dba) {
// TODO: reset the config values to defaults
2001-05-23 15:26:42 +02:00
}
*
* Can not return error for service v.1 => simply ignore request
2001-05-23 15:26:42 +02:00
else
need_admin_privs(status, "isc_info_svc_default_config:");
*
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_set_config:
*info++ = item;
if (svc_user_flag & SVC_user_dba) {
// TODO: set the config values
2001-05-23 15:26:42 +02:00
}
*
* Can not return error for service v.1 => simply ignore request
else
need_admin_privs(status, "isc_info_svc_set_config:");
*
2001-05-23 15:26:42 +02:00
break;
*/
2001-05-23 15:26:42 +02:00
case isc_info_svc_version:
/* The version of the service manager */
2008-02-03 03:32:39 +01:00
length = INF_convert(SERVICE_VERSION, buffer);
info = INF_put_item(item, length, buffer, info, end);
if (!info)
2001-05-23 15:26:42 +02:00
return;
break;
case isc_info_svc_capabilities:
/* bitmask defining any specific architectural differences */
2008-02-03 03:32:39 +01:00
length = INF_convert(SERVER_CAPABILITIES_FLAG, buffer);
info = INF_put_item(item, length, buffer, info, end);
if (!info)
2001-05-23 15:26:42 +02:00
return;
break;
case isc_info_svc_server_version:
{
/* The version of the server engine */
p = buffer;
*p++ = 1; /* Count */
*p++ = sizeof(GDS_VERSION) - 1;
for (const TEXT* gvp = GDS_VERSION; *gvp; p++, gvp++)
*p = *gvp;
if (!(info = INF_put_item(item, p - buffer, buffer, info, end)))
{
return;
}
break;
2001-05-23 15:26:42 +02:00
}
case isc_info_svc_implementation:
/* The server implementation - e.g. Firebird/sun4 */
2001-05-23 15:26:42 +02:00
p = buffer;
2001-05-23 15:26:42 +02:00
*p++ = 1; /* Count */
*p++ = IMPLEMENTATION;
if (!(info = INF_put_item(item, p - buffer, buffer, info, end)))
2001-05-23 15:26:42 +02:00
{
return;
}
break;
case isc_info_svc_user_dbpath:
if (svc_user_flag & SVC_user_dba)
{
/* The path to the user security database (security2.fdb) */
SecurityDatabase::getPath(buffer);
2001-05-23 15:26:42 +02:00
if (!(info = INF_put_item(item, strlen(buffer), buffer, info, end)))
{
return;
}
2001-05-23 15:26:42 +02:00
}
/*
* Can not return error for service v.1 => simply ignore request
else
need_admin_privs(status, "isc_info_svc_user_dbpath");
*/
2001-05-23 15:26:42 +02:00
break;
case isc_info_svc_response:
svc_resp_len = 0;
2001-05-23 15:26:42 +02:00
if (info + 4 > end)
{
*info++ = isc_info_truncated;
break;
}
put(&item, 1);
get(&item, 1, GET_BINARY, 0, &length);
get(buffer, 2, GET_BINARY, 0, &length);
l = (USHORT) gds__vax_integer(reinterpret_cast<UCHAR*>(buffer), 2);
2001-05-23 15:26:42 +02:00
length = MIN(end - (info + 4), l);
get(info + 3, length, GET_BINARY, 0, &length);
2001-05-23 15:26:42 +02:00
info = INF_put_item(item, length, info + 3, info, end);
if (length != l)
{
*info++ = isc_info_truncated;
l -= length;
if (l > svc_resp_buf_len)
2001-05-23 15:26:42 +02:00
{
2008-02-11 10:55:16 +01:00
try {
svc_resp_buf = svc_resp_alloc.getBuffer(l);
}
catch (const BadAlloc&) { // NOMEM:
2001-05-23 15:26:42 +02:00
DEV_REPORT("SVC_query: out of memory");
2008-02-14 02:31:15 +01:00
// NOMEM: not really handled well
l = 0; // set the length to zero
2001-05-23 15:26:42 +02:00
}
svc_resp_buf_len = l;
2001-05-23 15:26:42 +02:00
}
get(reinterpret_cast<char*>(svc_resp_buf), l, GET_BINARY, 0, &length);
svc_resp_ptr = svc_resp_buf;
svc_resp_len = l;
2001-05-23 15:26:42 +02:00
}
break;
case isc_info_svc_response_more:
if ( (l = length = svc_resp_len) )
2001-05-23 15:26:42 +02:00
length = MIN(end - (info + 4), l);
2009-01-20 09:33:59 +01:00
if (!(info =
2008-12-25 07:09:37 +01:00
INF_put_item(item, length, reinterpret_cast<const char*>(svc_resp_ptr), info, end)))
2001-05-23 15:26:42 +02:00
{
return;
}
svc_resp_ptr += length;
svc_resp_len -= length;
2001-05-23 15:26:42 +02:00
if (length != l)
*info++ = isc_info_truncated;
break;
case isc_info_svc_total_length:
put(&item, 1);
get(&item, 1, GET_BINARY, 0, &length);
get(buffer, 2, GET_BINARY, 0, &length);
l = (USHORT) gds__vax_integer(reinterpret_cast<UCHAR*>(buffer), 2);
get(buffer, l, GET_BINARY, 0, &length);
if (!(info = INF_put_item(item, length, buffer, info, end)))
2001-05-23 15:26:42 +02:00
{
return;
}
break;
case isc_info_svc_line:
case isc_info_svc_to_eof:
if (info + 4 > end)
{
*info++ = isc_info_truncated;
break;
}
get_flags = (item == isc_info_svc_line) ? GET_LINE : GET_EOF;
get(info + 3, end - (info + 4), get_flags, timeout, &length);
2001-05-23 15:26:42 +02:00
2002-02-16 04:27:33 +01:00
/* If the read timed out, return the data, if any, & a timeout
2001-05-23 15:26:42 +02:00
item. If the input buffer was not large enough
to store a read to eof, return the data that was read along
with an indication that more is available. */
info = INF_put_item(item, length, info + 3, info, end);
if (svc_flags & SVC_timeout)
2001-05-23 15:26:42 +02:00
*info++ = isc_info_svc_timeout;
else
{
if (!length && !(svc_flags & SVC_finished))
2001-05-23 15:26:42 +02:00
*info++ = isc_info_data_not_ready;
else
2008-01-16 10:48:41 +01:00
{
if (item == isc_info_svc_to_eof && !(svc_flags & SVC_finished))
2001-05-23 15:26:42 +02:00
*info++ = isc_info_truncated;
2008-01-16 10:48:41 +01:00
}
2001-05-23 15:26:42 +02:00
}
break;
}
}
if (info < end)
{
*info = isc_info_end;
}
if (!(svc_flags & SVC_thd_running))
{
finish(SVC_finished);
}
2001-05-23 15:26:42 +02:00
}
void Service::start(USHORT spb_length, const UCHAR* spb_data)
2001-05-23 15:26:42 +02:00
{
ClumpletReader spb(ClumpletReader::SpbStart, spb_data, spb_length);
2005-11-30 18:32:11 +01:00
2001-05-23 15:26:42 +02:00
/* The name of the service is the first element of the buffer */
2005-11-30 18:32:11 +01:00
const UCHAR svc_id = spb.getClumpTag();
const serv_entry* serv;
for (serv = services; serv->serv_action; serv++)
2008-01-16 10:48:41 +01:00
{
2001-05-23 15:26:42 +02:00
if (serv->serv_action == svc_id)
break;
2008-01-16 10:48:41 +01:00
}
2001-05-23 15:26:42 +02:00
if (!serv->serv_name)
status_exception::raise(Arg::Gds(isc_service_att_err) << Arg::Gds(isc_service_not_supported));
2001-05-23 15:26:42 +02:00
/* currently we do not use "anonymous" service for any purposes but
isc_service_query() */
if (svc_user_flag == SVC_user_none) {
status_exception::raise(Arg::Gds(isc_bad_spb_form));
}
2001-05-23 15:26:42 +02:00
{ // scope for locked svc_mutex
MutexLockGuard guard(svc_mutex);
if (svc_flags & SVC_thd_running) {
status_exception::raise(Arg::Gds(isc_svc_in_use) << Arg::Str(serv->serv_name));
}
/* Another service may have been started with this service block.
* If so, we must reset the service flags.
*/
svc_switches.erase();
if (!(svc_flags & SVC_detached))
{
svc_flags = 0;
}
2001-05-23 15:26:42 +02:00
}
if (!svc_perm_sw.hasData())
{
/* If svc_perm_sw is not used -- call a command-line parsing utility */
conv_switches(spb, svc_switches);
}
else
{
/* Command line options (isc_spb_options) is used.
* Currently the only case in which it might happen is -- gbak utility
* is called with a "-server" switch.
*/
svc_switches = svc_perm_sw;
}
2002-02-16 04:27:33 +01:00
/* Only need to add username and password information to those calls which need
2001-05-23 15:26:42 +02:00
* to make a database connection
*/
2005-11-30 18:32:11 +01:00
if (svc_id == isc_action_svc_backup ||
svc_id == isc_action_svc_restore ||
2008-11-20 18:23:06 +01:00
svc_id == isc_action_svc_nbak ||
svc_id == isc_action_svc_nrest ||
2005-11-30 18:32:11 +01:00
svc_id == isc_action_svc_repair ||
svc_id == isc_action_svc_add_user ||
svc_id == isc_action_svc_delete_user ||
svc_id == isc_action_svc_modify_user ||
svc_id == isc_action_svc_display_user ||
svc_id == isc_action_svc_db_stats ||
svc_id == isc_action_svc_properties)
{
2001-05-23 15:26:42 +02:00
/* add the username and password to the end of svc_switches if needed */
2008-12-05 02:20:14 +01:00
if (svc_switches.hasData())
2006-12-08 19:38:15 +01:00
{
2008-12-05 02:20:14 +01:00
if (svc_trusted_login.hasData())
{
2008-11-20 18:23:06 +01:00
string auth = "-";
auth += TRUSTED_USER_SWITCH;
auth += ' ';
auth += svc_username;
auth += ' ';
if (svc_trusted_role)
2008-01-16 10:48:41 +01:00
{
2008-11-20 18:23:06 +01:00
auth += "-";
svc_switches += TRUSTED_ROLE_SWITCH;
2008-11-20 18:23:06 +01:00
auth += ' ';
2008-01-16 10:48:41 +01:00
}
2008-11-20 18:23:06 +01:00
svc_switches = auth + svc_switches;
2001-05-23 15:26:42 +02:00
}
2006-12-08 19:38:15 +01:00
else
{
// No need repeating user validation in service worker thread
if (svc_username.hasData())
2006-12-08 19:38:15 +01:00
{
2008-11-20 18:23:06 +01:00
string auth = "-";
auth += TRUSTED_USER_SWITCH;
auth += ' ';
auth += svc_username;
auth += ' ';
svc_switches = auth + svc_switches;
2006-12-08 19:38:15 +01:00
}
2001-05-23 15:26:42 +02:00
}
}
}
// All services except for get_ib_log require switches
2005-11-30 18:32:11 +01:00
spb.rewind();
2008-12-05 02:20:14 +01:00
if ((!svc_switches.hasData()) && svc_id != isc_action_svc_get_fb_log)
2007-03-02 10:04:30 +01:00
{
status_exception::raise(Arg::Gds(isc_bad_spb_form));
}
2007-03-02 10:04:30 +01:00
// Do not let everyone look at server log
if (svc_id == isc_action_svc_get_fb_log && !(svc_user_flag & SVC_user_dba))
2007-03-02 10:04:30 +01:00
{
status_exception::raise(Arg::Gds(isc_adm_task_denied));
2007-03-02 10:04:30 +01:00
}
2001-05-23 15:26:42 +02:00
// Break up the command line into individual arguments.
parseSwitches();
2001-05-23 15:26:42 +02:00
// The service block can be reused hence init a status vector.
memset((void *) svc_status, 0, sizeof(ISC_STATUS_ARRAY));
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
if (serv->serv_thd)
{
2008-04-03 03:11:26 +02:00
{ // scope
MutexLockGuard guard(svc_mutex);
svc_flags &= ~SVC_evnt_fired;
svc_flags |= SVC_thd_running;
}
2001-05-23 15:26:42 +02:00
2008-12-28 16:26:05 +01:00
gds__thread_start(serv->serv_thd, this, THREAD_medium, 0, 0);
2001-05-23 15:26:42 +02:00
// Check for the service being detached. This will prevent the thread
// from waiting infinitely if the client goes away.
2008-12-05 02:20:14 +01:00
while (!(svc_flags & SVC_detached))
{
2008-12-05 02:20:14 +01:00
// The semaphore will be released once the particular service
// has reached a point in which it can start to return
// information to the client. This will allow isc_service_start
// to include in its status vector information about the service's
2008-12-05 02:20:14 +01:00
// ability to start.
// This is needed since gds__thread_start will almost always succeed.
if (svcStart.tryEnter(60))
2001-12-24 03:51:06 +01:00
{
// started() was called
2003-05-05 09:06:06 +02:00
break;
}
2001-05-23 15:26:42 +02:00
}
}
else
2001-12-24 03:51:06 +01:00
{
status_exception::raise(Arg::Gds(isc_svcnotdef) << Arg::Str(serv->serv_name));
2001-12-24 03:51:06 +01:00
}
}
2001-05-23 15:26:42 +02:00
THREAD_ENTRY_DECLARE Service::readFbLog(THREAD_ENTRY_PARAM arg)
{
2008-02-03 03:32:39 +01:00
Service* service = (Service*) arg;
service->readFbLog();
return 0;
2001-05-23 15:26:42 +02:00
}
void Service::readFbLog()
2001-05-23 15:26:42 +02:00
{
2003-08-31 14:55:33 +02:00
bool svc_started = false;
2001-05-23 15:26:42 +02:00
2004-11-16 09:52:29 +01:00
TEXT name[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
gds__prefix(name, LOGFILE);
2004-04-29 00:43:34 +02:00
FILE* file = fopen(name, "r");
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
try
{
2009-01-20 09:33:59 +01:00
if (file != NULL)
2003-08-31 14:55:33 +02:00
{
fb_utils::init_status(svc_status);
started();
svc_started = true;
TEXT buffer[100];
2009-01-20 09:33:59 +01:00
while (!feof(file) && !ferror(file))
{
fgets(buffer, sizeof(buffer), file);
output(buffer);
}
}
2009-01-17 20:07:07 +01:00
if (!file || (file && ferror(file)))
{
(Arg::Gds(isc_sys_request) << Arg::Str(file ? "fgets" : "fopen") <<
SYS_ERR(errno)).copyTo(svc_status);
StringsBuffer::makeEnginePermanentVector(svc_status);
if (!svc_started)
{
started();
}
2003-08-31 14:55:33 +02:00
}
2001-05-23 15:26:42 +02:00
}
catch (const Firebird::Exception& e)
{
e.stuff_exception(getStatus());
}
2001-05-23 15:26:42 +02:00
if (file)
{
2004-04-29 00:43:34 +02:00
fclose(file);
}
2001-05-23 15:26:42 +02:00
finish(SVC_finished);
2001-05-23 15:26:42 +02:00
}
void Service::start(ThreadEntryPoint* service_thread)
2001-05-23 15:26:42 +02:00
{
// Break up the command line into individual arguments.
parseSwitches();
if (svc_service && svc_service->serv_name)
{
argv[0] = svc_service->serv_name;
}
gds__thread_start(service_thread, this, THREAD_medium, 0, 0);
2001-05-23 15:26:42 +02:00
}
ULONG Service::add_one(ULONG i)
2001-05-23 15:26:42 +02:00
{
return (i + 1) % SVC_STDOUT_BUFFER_SIZE;
2001-05-23 15:26:42 +02:00
}
ULONG Service::add_val(ULONG i, ULONG val)
2001-05-23 15:26:42 +02:00
{
return (i + val) % SVC_STDOUT_BUFFER_SIZE;
2001-05-23 15:26:42 +02:00
}
bool Service::empty() const
2001-05-23 15:26:42 +02:00
{
return svc_stdout_tail == svc_stdout_head;
}
2001-05-23 15:26:42 +02:00
bool Service::full() const
{
return add_one(svc_stdout_tail) == svc_stdout_head;
2001-05-23 15:26:42 +02:00
}
void Service::enqueue(const UCHAR* s, ULONG len)
2001-05-23 15:26:42 +02:00
{
2009-01-17 20:07:07 +01:00
if (checkForShutdown() || (svc_flags & SVC_detached))
{
return;
}
while (len)
{
// Wait for space in buffer
while (full())
{
THREAD_SLEEP(1);
2009-01-17 20:07:07 +01:00
if (checkForShutdown() || (svc_flags & SVC_detached))
{
return;
}
}
2001-05-23 15:26:42 +02:00
2009-01-17 11:33:35 +01:00
const ULONG head = svc_stdout_head;
ULONG cnt = (head > svc_stdout_tail ? head : sizeof(svc_stdout)) - 1;
if (add_one(cnt) != head)
{
++cnt;
}
cnt -= svc_stdout_tail;
if (cnt > len)
{
cnt = len;
}
2006-07-28 03:44:36 +02:00
memcpy(&svc_stdout[svc_stdout_tail], s, cnt);
svc_stdout_tail = add_val(svc_stdout_tail, cnt);
s += cnt;
len -= cnt;
2008-01-16 10:48:41 +01:00
}
2001-05-23 15:26:42 +02:00
}
2008-12-25 07:09:37 +01:00
void Service::get(SCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, USHORT* return_length)
2001-05-23 15:26:42 +02:00
{
#ifdef HAVE_GETTIMEOFDAY
2001-05-23 15:26:42 +02:00
struct timeval start_time, end_time;
2003-10-31 12:33:45 +01:00
GETTIMEOFDAY(&start_time);
#else
time_t start_time, end_time;
time(&start_time);
2002-08-24 11:40:38 +02:00
#endif
2003-10-31 12:33:45 +01:00
2001-05-23 15:26:42 +02:00
*return_length = 0;
2008-04-03 03:11:26 +02:00
{ // scope
MutexLockGuard guard(svc_mutex);
svc_flags &= ~SVC_timeout;
}
2001-05-23 15:26:42 +02:00
2009-01-20 09:33:59 +01:00
while (length)
{
2009-01-17 20:07:07 +01:00
if ((empty() && (svc_flags & SVC_finished)) || checkForShutdown())
{
break;
}
if (empty())
{
THREAD_SLEEP(1);
}
#ifdef HAVE_GETTIMEOFDAY
2003-10-31 12:33:45 +01:00
GETTIMEOFDAY(&end_time);
2004-11-16 09:52:29 +01:00
const time_t elapsed_time = end_time.tv_sec - start_time.tv_sec;
#else
time(&end_time);
2004-11-16 09:52:29 +01:00
const time_t elapsed_time = end_time - start_time;
2001-05-23 15:26:42 +02:00
#endif
2008-04-03 03:11:26 +02:00
if ((timeout) && (elapsed_time >= timeout))
{
MutexLockGuard guard(svc_mutex);
svc_flags &= SVC_timeout;
break;
2001-05-23 15:26:42 +02:00
}
ULONG head = svc_stdout_head;
while (head != svc_stdout_tail && length > 0)
2008-01-16 10:48:41 +01:00
{
const UCHAR ch = svc_stdout[head];
head = add_one(head);
2001-05-23 15:26:42 +02:00
length--;
/* If returning a line of information, replace all new line
* characters with a space. This will ensure that the output is
* consistent when returning a line or to eof
*/
2009-01-17 20:07:07 +01:00
if ((flags & GET_LINE) && ch == '\n')
{
2001-05-23 15:26:42 +02:00
buffer[(*return_length)++] = ' ';
length = 0;
break;
2001-05-23 15:26:42 +02:00
}
2008-01-16 10:48:41 +01:00
buffer[(*return_length)++] = ch;
2001-05-23 15:26:42 +02:00
}
svc_stdout_head = head;
2001-05-23 15:26:42 +02:00
}
}
void Service::put(const SCHAR* buffer, USHORT length)
2001-05-23 15:26:42 +02:00
{
2008-02-03 03:32:39 +01:00
// Nothing
2001-05-23 15:26:42 +02:00
}
void Service::finish(USHORT flag)
2001-05-23 15:26:42 +02:00
{
if (flag == SVC_finished || flag == SVC_detached)
{
MutexLockGuard guard(svc_mutex);
svc_flags |= flag;
if (! (svc_flags & SVC_thd_running))
{
svc_flags |= SVC_finished;
}
if (svc_flags & SVC_finished && svc_flags & SVC_detached)
{
delete this;
return;
}
if (svc_flags & SVC_finished)
{
svc_flags &= ~SVC_thd_running;
2001-05-23 15:26:42 +02:00
}
}
}
void Service::conv_switches(ClumpletReader& spb, string& switches)
2001-05-23 15:26:42 +02:00
{
2005-11-30 18:32:11 +01:00
spb.rewind();
if (spb.getClumpTag() < isc_action_min || spb.getClumpTag() > isc_action_max) {
return; /* error - action not defined */
}
2001-05-23 15:26:42 +02:00
2005-11-30 18:32:11 +01:00
// convert to string
string sw;
if (! process_switches(spb, sw)) {
2001-05-23 15:26:42 +02:00
return;
2005-11-30 18:32:11 +01:00
}
switches = sw;
2001-05-23 15:26:42 +02:00
}
const TEXT* Service::find_switch(int in_spb_sw, const in_sw_tab_t* table)
2001-05-23 15:26:42 +02:00
{
2003-12-31 06:36:12 +01:00
for (const in_sw_tab_t* in_sw_tab = table; in_sw_tab->in_sw_name; in_sw_tab++)
{
2001-05-23 15:26:42 +02:00
if (in_spb_sw == in_sw_tab->in_spb_sw)
return in_sw_tab->in_sw_name;
}
return NULL;
2001-05-23 15:26:42 +02:00
}
2008-07-06 20:08:23 +02:00
bool Service::process_switches(ClumpletReader& spb, string& switches)
2001-05-23 15:26:42 +02:00
{
2005-11-30 18:32:11 +01:00
if (spb.getBufferLength() == 0)
return false;
2001-05-23 15:26:42 +02:00
2005-11-30 18:32:11 +01:00
spb.rewind();
const UCHAR svc_action = spb.getClumpTag();
spb.moveNext();
2001-05-23 15:26:42 +02:00
string burp_database, burp_backup;
int burp_options = 0;
2008-11-20 18:23:06 +01:00
string nbk_database, nbk_file;
int nbk_level = -1;
bool found = false;
2001-05-23 15:26:42 +02:00
2008-12-05 02:20:14 +01:00
do
2005-11-30 18:32:11 +01:00
{
2008-12-05 02:20:14 +01:00
switch (svc_action)
2005-11-30 18:32:11 +01:00
{
2008-11-20 18:23:06 +01:00
case isc_action_svc_nbak:
case isc_action_svc_nrest:
found = true;
2008-12-05 02:20:14 +01:00
switch (spb.getClumpTag())
2008-11-20 18:23:06 +01:00
{
case isc_spb_dbname:
if (nbk_database.hasData())
{
2008-11-23 15:04:41 +01:00
(Arg::Gds(isc_unexp_spb_form) << Arg::Str("only one isc_spb_dbname")).raise();
2008-11-20 18:23:06 +01:00
}
get_action_svc_string(spb, nbk_database);
break;
case isc_spb_nbk_level:
2008-11-23 15:04:41 +01:00
if (nbk_level >= 0)
{
(Arg::Gds(isc_unexp_spb_form) << Arg::Str("only one isc_spb_nbk_level")).raise();
}
2008-11-20 18:23:06 +01:00
nbk_level = spb.getInt();
break;
case isc_spb_nbk_file:
if (nbk_file.hasData() && svc_action != isc_action_svc_nrest)
{
2008-11-23 15:04:41 +01:00
(Arg::Gds(isc_unexp_spb_form) << Arg::Str("only one isc_spb_nbk_file")).raise();
2008-11-20 18:23:06 +01:00
}
get_action_svc_string(spb, nbk_file);
break;
case isc_spb_options:
if (!get_action_svc_bitmask(spb, nbackup_in_sw_table, switches))
{
return false;
}
break;
}
break;
2001-05-23 15:26:42 +02:00
case isc_action_svc_delete_user:
case isc_action_svc_display_user:
2008-01-16 10:48:41 +01:00
if (!found)
{
2005-11-30 18:32:11 +01:00
if (!get_action_svc_parameter(svc_action, gsec_action_in_sw_table, switches))
{
2005-11-30 18:32:11 +01:00
return false;
}
2007-10-10 04:06:20 +02:00
2008-01-16 10:48:41 +01:00
if (spb.isEof() && svc_action == isc_action_svc_display_user)
{
// in case of "display all users" the spb buffer contains
// nothing but isc_action_svc_display_user or isc_spb_dbname
break;
}
2008-12-25 07:09:37 +01:00
if (spb.getClumpTag() != isc_spb_sec_username && spb.getClumpTag() != isc_spb_dbname)
2008-01-16 10:48:41 +01:00
{
// unexpected item in service parameter block, expected @1
status_exception::raise(Arg::Gds(isc_unexp_spb_form) << Arg::Str(SPB_SEC_USERNAME));
2001-05-23 15:26:42 +02:00
}
2008-01-16 10:48:41 +01:00
found = true;
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
switch (spb.getClumpTag())
{
case isc_spb_sql_role_name:
case isc_spb_dbname:
2005-11-30 18:32:11 +01:00
if (!get_action_svc_parameter(spb.getClumpTag(), gsec_in_sw_table, switches))
{
2005-11-30 18:32:11 +01:00
return false;
}
// fall through ....
case isc_spb_sec_username:
2005-11-30 18:32:11 +01:00
get_action_svc_string(spb, switches);
break;
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
default:
2005-11-30 18:32:11 +01:00
return false;
2001-05-23 15:26:42 +02:00
}
break;
case isc_action_svc_add_user:
case isc_action_svc_modify_user:
2008-01-16 10:48:41 +01:00
if (!found)
{
2005-11-30 18:32:11 +01:00
if (!get_action_svc_parameter(svc_action, gsec_action_in_sw_table, switches))
{
2005-11-30 18:32:11 +01:00
return false;
}
2008-01-16 10:48:41 +01:00
if (spb.getClumpTag() != isc_spb_sec_username) {
// unexpected item in service parameter block, expected @1
status_exception::raise(Arg::Gds(isc_unexp_spb_form) << Arg::Str(SPB_SEC_USERNAME));
2001-05-23 15:26:42 +02:00
}
2008-01-16 10:48:41 +01:00
found = true;
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
switch (spb.getClumpTag())
{
2001-05-23 15:26:42 +02:00
case isc_spb_sec_userid:
case isc_spb_sec_groupid:
2005-11-30 18:32:11 +01:00
if (!get_action_svc_parameter(spb.getClumpTag(), gsec_in_sw_table, switches))
{
2005-11-30 18:32:11 +01:00
return false;
}
2005-11-30 18:32:11 +01:00
get_action_svc_data(spb, switches);
2001-05-23 15:26:42 +02:00
break;
case isc_spb_sql_role_name:
case isc_spb_sec_password:
case isc_spb_sec_groupname:
case isc_spb_sec_firstname:
case isc_spb_sec_middlename:
case isc_spb_sec_lastname:
case isc_spb_dbname:
2005-11-30 18:32:11 +01:00
if (!get_action_svc_parameter(spb.getClumpTag(), gsec_in_sw_table, switches))
{
2005-11-30 18:32:11 +01:00
return false;
}
// fall through ....
case isc_spb_sec_username:
2005-11-30 18:32:11 +01:00
get_action_svc_string(spb, switches);
2001-05-23 15:26:42 +02:00
break;
default:
2005-11-30 18:32:11 +01:00
return false;
2001-05-23 15:26:42 +02:00
}
break;
case isc_action_svc_db_stats:
2009-01-20 09:33:59 +01:00
switch (spb.getClumpTag())
{
2001-05-23 15:26:42 +02:00
case isc_spb_dbname:
2005-11-30 18:32:11 +01:00
get_action_svc_string(spb, switches);
2001-05-23 15:26:42 +02:00
break;
case isc_spb_options:
2005-11-30 18:32:11 +01:00
if (!get_action_svc_bitmask(spb, dba_in_sw_table, switches))
{
2005-11-30 18:32:11 +01:00
return false;
}
2001-05-23 15:26:42 +02:00
break;
case isc_spb_command_line:
{
string s;
spb.getString(s);
switches += s;
switches += ' ';
2008-12-22 10:00:05 +01:00
}
break;
2001-05-23 15:26:42 +02:00
default:
return false;
2001-05-23 15:26:42 +02:00
}
break;
case isc_action_svc_backup:
case isc_action_svc_restore:
2009-01-20 09:33:59 +01:00
switch (spb.getClumpTag())
{
2001-05-23 15:26:42 +02:00
case isc_spb_bkp_file:
get_action_svc_string(spb, burp_backup);
break;
2001-05-23 15:26:42 +02:00
case isc_spb_dbname:
get_action_svc_string(spb, burp_database);
2001-05-23 15:26:42 +02:00
break;
case isc_spb_options:
burp_options |= spb.getInt();
if (!get_action_svc_bitmask(spb, reference_burp_in_sw_table, switches))
{
return false;
}
2001-05-23 15:26:42 +02:00
break;
case isc_spb_bkp_length:
get_action_svc_data(spb, burp_backup);
break;
2001-05-23 15:26:42 +02:00
case isc_spb_res_length:
get_action_svc_data(spb, burp_database);
2001-05-23 15:26:42 +02:00
break;
case isc_spb_bkp_factor:
case isc_spb_res_buffers:
case isc_spb_res_page_size:
if (!get_action_svc_parameter(spb.getClumpTag(), reference_burp_in_sw_table, switches))
{
return false;
}
2005-11-30 18:32:11 +01:00
get_action_svc_data(spb, switches);
2001-05-23 15:26:42 +02:00
break;
case isc_spb_res_access_mode:
if (!get_action_svc_parameter(*(spb.getBytes()), reference_burp_in_sw_table, switches))
{
return false;
}
2001-05-23 15:26:42 +02:00
break;
case isc_spb_verbose:
if (!get_action_svc_parameter(spb.getClumpTag(), reference_burp_in_sw_table, switches))
{
return false;
}
2001-05-23 15:26:42 +02:00
break;
default:
return false;
2001-05-23 15:26:42 +02:00
}
break;
case isc_action_svc_repair:
case isc_action_svc_properties:
2009-01-20 09:33:59 +01:00
switch (spb.getClumpTag())
{
2001-05-23 15:26:42 +02:00
case isc_spb_dbname:
2005-11-30 18:32:11 +01:00
get_action_svc_string(spb, switches);
2001-05-23 15:26:42 +02:00
break;
case isc_spb_options:
2005-11-30 18:32:11 +01:00
if (!get_action_svc_bitmask(spb, alice_in_sw_table, switches))
{
return false;
}
2001-05-23 15:26:42 +02:00
break;
case isc_spb_prp_page_buffers:
case isc_spb_prp_sweep_interval:
case isc_spb_prp_shutdown_db:
case isc_spb_prp_deny_new_attachments:
case isc_spb_prp_deny_new_transactions:
case isc_spb_prp_force_shutdown:
case isc_spb_prp_attachments_shutdown:
case isc_spb_prp_transactions_shutdown:
2001-05-23 15:26:42 +02:00
case isc_spb_prp_set_sql_dialect:
case isc_spb_rpr_commit_trans:
case isc_spb_rpr_rollback_trans:
case isc_spb_rpr_recover_two_phase:
2005-11-30 18:32:11 +01:00
if (!get_action_svc_parameter(spb.getClumpTag(), alice_in_sw_table, switches))
{
return false;
}
2005-11-30 18:32:11 +01:00
get_action_svc_data(spb, switches);
2001-05-23 15:26:42 +02:00
break;
case isc_spb_prp_write_mode:
case isc_spb_prp_access_mode:
case isc_spb_prp_reserve_space:
2005-12-10 11:15:56 +01:00
if (!get_action_svc_parameter(*(spb.getBytes()), alice_in_sw_table, switches))
{
return false;
}
2001-05-23 15:26:42 +02:00
break;
case isc_spb_prp_shutdown_mode:
case isc_spb_prp_online_mode:
if (get_action_svc_parameter(spb.getClumpTag(), alice_in_sw_table, switches))
{
unsigned int val = spb.getInt();
if (val >= FB_NELEM(alice_mode_sw_table))
{
return false;
}
switches += alice_mode_sw_table[val];
switches += " ";
break;
}
return false;
2001-05-23 15:26:42 +02:00
default:
return false;
2001-05-23 15:26:42 +02:00
}
break;
default:
return false;
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
2005-11-30 18:32:11 +01:00
spb.moveNext();
} while (! spb.isEof());
2001-05-23 15:26:42 +02:00
2008-11-20 18:23:06 +01:00
// postfixes for burp & nbackup
switch (svc_action)
{
case isc_action_svc_backup:
switches += (burp_database + burp_backup);
break;
case isc_action_svc_restore:
if (! (burp_options & (isc_spb_res_create | isc_spb_res_replace)))
{
// user not specified create or replace database
// default to create for restore
switches += "-CREATE_DATABASE ";
}
switches += (burp_backup + burp_database);
break;
2008-11-20 18:23:06 +01:00
case isc_action_svc_nbak:
case isc_action_svc_nrest:
if (nbk_database.isEmpty())
{
2008-11-23 15:04:41 +01:00
(Arg::Gds(isc_missing_required_spb) << Arg::Str("isc_spb_dbname")).raise();
2008-11-20 18:23:06 +01:00
}
if (nbk_file.isEmpty())
{
(Arg::Gds(isc_missing_required_spb) << Arg::Str("isc_spb_nbk_file")).raise();
}
if (!get_action_svc_parameter(svc_action, nbackup_action_in_sw_table, switches))
{
return false;
}
if (svc_action == isc_action_svc_nbak)
{
if (nbk_level < 0)
{
(Arg::Gds(isc_missing_required_spb) << Arg::Str("isc_spb_nbk_level")).raise();
}
string temp;
temp.printf("%d ", nbk_level);
switches += temp;
}
switches += nbk_database;
switches += nbk_file;
break;
}
switches.rtrim();
return switches.length() > 0;
2001-05-23 15:26:42 +02:00
}
bool Service::get_action_svc_bitmask(const ClumpletReader& spb,
const in_sw_tab_t* table,
string& switches)
2001-05-23 15:26:42 +02:00
{
2005-11-30 18:32:11 +01:00
const int opt = spb.getInt();
ISC_ULONG mask = 1;
2008-12-05 02:20:14 +01:00
for (int count = (sizeof(ISC_ULONG) * 8) - 1; count--; mask <<= 1)
2005-11-30 18:32:11 +01:00
{
2008-12-05 02:20:14 +01:00
if (opt & mask)
2005-11-30 18:32:11 +01:00
{
2003-12-31 06:36:12 +01:00
const TEXT* s_ptr = find_switch((opt & mask), table);
if (!s_ptr)
2005-11-30 18:32:11 +01:00
{
return false;
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
2005-11-30 18:32:11 +01:00
switches += '-';
switches += s_ptr;
switches += ' ';
2001-05-23 15:26:42 +02:00
}
}
return true;
2001-05-23 15:26:42 +02:00
}
void Service::get_action_svc_string(const ClumpletReader& spb,
string& switches)
2001-05-23 15:26:42 +02:00
{
string s;
2005-11-30 18:32:11 +01:00
spb.getString(s);
addStringWithSvcTrmntr(s, switches);
2001-05-23 15:26:42 +02:00
}
void Service::get_action_svc_data(const ClumpletReader& spb,
string& switches)
2001-05-23 15:26:42 +02:00
{
string s;
2005-11-30 18:32:11 +01:00
s.printf("%"ULONGFORMAT" ", spb.getInt());
switches += s;
2001-05-23 15:26:42 +02:00
}
2008-12-05 02:20:14 +01:00
bool Service::get_action_svc_parameter(UCHAR action,
const in_sw_tab_t* table,
string& switches)
2001-05-23 15:26:42 +02:00
{
2005-11-30 18:32:11 +01:00
const TEXT* s_ptr = find_switch(action, table);
2003-12-31 06:36:12 +01:00
if (!s_ptr)
2005-11-30 18:32:11 +01:00
{
return false;
2001-05-23 15:26:42 +02:00
}
2005-11-30 18:32:11 +01:00
switches += '-';
switches += s_ptr;
switches += ' ';
return true;
2001-05-23 15:26:42 +02:00
}
#ifdef DEBUG
/* The following two functions are temporary stubs and will be
2002-02-16 04:27:33 +01:00
* removed as the services API takes shape. They are used to
2001-05-23 15:26:42 +02:00
* test that the paths for starting services and parsing command-lines
* are followed correctly.
*/
THREAD_ENTRY_DECLARE test_thread(THREAD_ENTRY_PARAM)
2001-05-23 15:26:42 +02:00
{
gds__log("Starting service");
return FINI_OK;
2001-05-23 15:26:42 +02:00
}
void test_cmd(USHORT spb_length, SCHAR* spb, TEXT** switches)
2001-05-23 15:26:42 +02:00
{
gds__log("test_cmd called");
}
#endif