8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-31 06:43:02 +01:00
firebird-mirror/src/common/db_alias.cpp

402 lines
9.2 KiB
C++
Raw Normal View History

2009-09-22 17:49:55 +02:00
/*
* The contents of this file are subject to the Initial
* Developer's 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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* 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 Dmitry Yemanov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2002 Dmitry Yemanov <dimitr@users.sf.net>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "../common/classes/init.h"
#include "../common/config/config.h"
#include "../common/config/config_file.h"
#include "../common/config/ConfigCache.h"
2009-09-22 17:49:55 +02:00
#include "../common/config/dir_list.h"
2010-10-12 10:02:57 +02:00
#include "../common/os/path_utils.h"
#include "../yvalve/gds_proto.h"
#include "../common/isc_proto.h"
2009-09-22 17:49:55 +02:00
#include "../common/utils_proto.h"
#include "../common/classes/Hash.h"
#include "../common/isc_f_proto.h"
#include <ctype.h>
2009-09-22 17:49:55 +02:00
using namespace Firebird;
namespace
{
class DatabaseDirectoryList : public DirectoryList
{
private:
const PathName getConfigString() const
{
return PathName(Config::getDatabaseAccess());
}
public:
explicit DatabaseDirectoryList(MemoryPool& p)
: DirectoryList(p)
{
initialize();
}
};
InitInstance<DatabaseDirectoryList> databaseDirectoryList;
const char* const ALIAS_FILE = "aliases.conf";
void replace_dir_sep(PathName& s)
{
const char correct_dir_sep = PathUtils::dir_sep;
const char incorrect_dir_sep = (correct_dir_sep == '/') ? '\\' : '/';
for (char* itr = s.begin(); itr < s.end(); ++itr)
{
if (*itr == incorrect_dir_sep)
{
*itr = correct_dir_sep;
}
}
}
template <typename T>
struct PathHash
{
static const PathName& generate(const void* /*sender*/, const T& item)
{
return item.name;
}
2009-09-22 17:49:55 +02:00
static size_t hash(const PathName& value, size_t hashSize)
{
return hash(value.c_str(), value.length(), hashSize);
}
static void upcpy(size_t* toPar, const char* from, size_t length)
{
2010-03-08 02:21:24 +01:00
char* to = reinterpret_cast<char*>(toPar);
while (length--)
{
if (CASE_SENSITIVITY)
{
*to++ = *from++;
}
else
{
*to++ = toupper(*from++);
}
}
}
static size_t hash(const char* value, size_t length, size_t hashSize)
{
size_t sum = 0;
size_t val;
while (length >= sizeof(size_t))
{
upcpy(&val, value, sizeof(size_t));
sum += val;
value += sizeof(size_t);
length -= sizeof(size_t);
}
if (length)
{
val = 0;
upcpy(&val, value, length);
sum += val;
}
size_t rc = 0;
while (sum)
{
rc += (sum % hashSize);
sum /= hashSize;
}
return rc % hashSize;
}
};
struct DbName;
typedef Hash<DbName, 127, PathName, PathHash<DbName>, PathHash<DbName> > DbHash;
struct DbName : public DbHash::Entry
{
2010-03-01 03:14:36 +01:00
DbName(MemoryPool& p, const PathName& db)
: name(p, db)
{ }
DbName* get()
{
return this;
}
bool isEqual(const PathName& val) const
{
return val == name;
}
PathName name;
RefPtr<Config> config;
};
struct AliasName;
typedef Hash<AliasName, 251, PathName, PathHash<AliasName>, PathHash<AliasName> > AliasHash;
2010-03-01 03:14:36 +01:00
struct AliasName : public AliasHash::Entry
{
2010-03-01 03:14:36 +01:00
AliasName(MemoryPool& p, const PathName& al, DbName* db)
: name(p, al), database(db)
{ }
AliasName* get()
{
return this;
}
bool isEqual(const PathName& val) const
{
return val == name;
}
2009-09-22 17:49:55 +02:00
PathName name;
DbName* database;
};
2009-09-22 17:49:55 +02:00
class AliasesConf : public ConfigCache
2009-09-22 17:49:55 +02:00
{
public:
explicit AliasesConf(MemoryPool& p)
: ConfigCache(p, fb_utils::getPrefix(fb_utils::FB_DIR_CONF, ALIAS_FILE)),
databases(getPool()), aliases(getPool())
{ }
void loadConfig()
2009-09-22 17:49:55 +02:00
{
size_t n;
// clean old data
for (n = 0; n < aliases.getCount(); ++n)
{
delete aliases[n];
}
aliases.clear();
for (n = 0; n < databases.getCount(); ++n)
{
delete databases[n];
}
databases.clear();
ConfigFile aliasConfig(fileName, ConfigFile::HAS_SUB_CONF);
const ConfigFile::Parameters& params = aliasConfig.getParameters();
for (n = 0; n < params.getCount(); ++n)
{
const ConfigFile::Parameter* par = &params[n];
PathName file(par->value.ToPathName());
replace_dir_sep(file);
if (PathUtils::isRelative(file))
{
gds__log("Value %s configured for alias %s "
"is not a fully qualified path name, ignored",
file.c_str(), par->name.c_str());
continue;
}
DbName* db = dbHash.lookup(file);
if (! db)
{
db = FB_NEW(getPool()) DbName(getPool(), file);
databases.add(db);
dbHash.add(db);
}
2010-03-01 03:14:36 +01:00
else
{
// check for duplicated config
if (par->sub && db->config.hasData())
{
2010-03-01 03:14:36 +01:00
fatal_exception::raiseFmt("Duplicated configuration for database %s\n",
file.c_str());
}
}
if (par->sub)
{
// load per-database configuration
db->config = new Config(*par->sub, *Config::getDefaultConfig());
}
2010-03-01 03:14:36 +01:00
PathName correctedAlias(par->name.ToPathName());
AliasName* alias = aliasHash.lookup(correctedAlias);
if (alias)
{
fatal_exception::raiseFmt("Duplicated alias %s\n", correctedAlias.c_str());
}
2010-03-01 03:14:36 +01:00
alias = FB_NEW(getPool()) AliasName(getPool(), correctedAlias, db);
aliases.add(alias);
aliasHash.add(alias);
}
2009-09-22 17:49:55 +02:00
}
private:
HalfStaticArray<DbName*, 100> databases;
HalfStaticArray<AliasName*, 200> aliases;
public:
DbHash dbHash;
AliasHash aliasHash;
};
InitInstance<AliasesConf> aliasesConf;
}
// Checks that argument doesn't contain colon or directory separator
static inline bool hasSeparator(const PathName& name)
{
2012-08-08 04:20:30 +02:00
for (const char* p = name.c_str(); *p; p++)
{
if (*p == ':' || *p == '/' || *p == '\\')
return true;
}
return false;
}
2012-03-17 03:26:59 +01:00
// Search for 'alias' in aliases.conf, return its value in 'file' if found. Else set file to alias.
2012-03-15 16:05:31 +01:00
// Returns true if alias is found in aliases.conf.
static bool resolveAlias(const PathName& alias, PathName& file, RefPtr<Config>* config)
{
2012-03-17 03:26:59 +01:00
PathName correctedAlias = alias;
replace_dir_sep(correctedAlias);
2012-03-17 03:26:59 +01:00
AliasName* a = aliasesConf().aliasHash.lookup(correctedAlias);
DbName* db = a ? a->database : NULL;
if (db)
2009-09-22 17:49:55 +02:00
{
file = db->name;
2012-03-15 16:05:31 +01:00
if (config)
{
*config = db->config.hasData() ? db->config : Config::getDefaultConfig();
}
2012-03-15 16:05:31 +01:00
return true;
}
2012-03-15 16:05:31 +01:00
return false;
}
2012-03-14 14:46:17 +01:00
// Search for filenames, containing no path component, in dirs from DatabaseAccess list
// of firebird.conf. If not found try first entry in that list as default entry.
2012-03-15 16:05:31 +01:00
// Returns true if expanded successfully.
static bool resolveDatabaseAccess(const PathName& alias, PathName& file)
{
file = alias;
if (hasSeparator(alias))
return false;
// try to expand to existing file
if (!databaseDirectoryList().expandFileName(file, alias))
{
// try to use default path
if (!databaseDirectoryList().defaultName(file, alias))
{
return false;
}
}
2012-03-17 03:26:59 +01:00
return true;
}
// Set a prefix to a filename based on the ISC_PATH user variable.
2012-03-15 16:05:31 +01:00
// Returns true if database name is expanded using ISC_PATH.
static bool setPath(const PathName& filename, PathName& expandedName)
{
// Look for the environment variables to tack onto the beginning of the database path.
PathName pathname;
if (!fb_utils::readenv("ISC_PATH", pathname))
return false;
// If the file already contains a remote node or any path at all forget it.
if (hasSeparator(filename))
return false;
// concatenate the strings
expandedName = pathname;
// CVC: Make the concatenation work if no slash is present.
char lastChar = expandedName[expandedName.length() - 1];
if (lastChar != ':' && lastChar != '/' && lastChar != '\\')
expandedName.append(1, PathUtils::dir_sep);
expandedName.append(filename);
return true;
}
// Full processing of database name
// Returns true if alias was found in aliases.conf
bool expandDatabaseName(const Firebird::PathName& alias,
Firebird::PathName& file,
Firebird::RefPtr<Config>* config)
{
try
{
aliasesConf().checkLoadConfig();
}
catch (const fatal_exception& ex)
{
gds__log("File aliases.conf contains bad data: %s", ex.what());
(Arg::Gds(isc_random) << "Server misconfigured - contact administrator please").raise();
}
ReadLockGuard guard(aliasesConf().rwLock);
// First of all check in aliases.conf
2012-03-15 16:05:31 +01:00
if (resolveAlias(alias, file, config))
{
return true;
}
// Now try ISC_PATH environment variable
if (!setPath(alias, file))
{
// At this step check DatabaseAccess paths in firebird.conf
if (!resolveDatabaseAccess(alias, file))
2009-09-22 17:49:55 +02:00
{
// Last chance - regular filename expansion
file = alias;
ISC_systemToUtf8(file);
ISC_unescape(file);
ISC_utf8ToSystem(file);
ISC_expand_filename(file, true);
ISC_systemToUtf8(file);
ISC_escape(file);
ISC_utf8ToSystem(file);
2009-09-22 17:49:55 +02:00
}
}
// Search for correct config in aliases.conf
if (config)
{
DbName* db = aliasesConf().dbHash.lookup(file);
*config = (db && db->config.hasData()) ? db->config : Config::getDefaultConfig();
2009-09-22 17:49:55 +02:00
}
return false;
2009-09-22 17:49:55 +02:00
}