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"
|
2010-02-28 19:00:51 +01:00
|
|
|
#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"
|
2010-02-28 19:00:51 +01:00
|
|
|
#include "../common/classes/Hash.h"
|
2012-03-14 09:46:27 +01:00
|
|
|
#include "../common/isc_f_proto.h"
|
2010-02-28 19:00:51 +01:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-28 19:00:51 +01:00
|
|
|
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
|
|
|
|
2010-02-28 19:00:51 +01: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);
|
2010-02-28 19:00:51 +01:00
|
|
|
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)
|
|
|
|
{ }
|
2010-02-28 19:00:51 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
2010-02-28 19:00:51 +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)
|
|
|
|
{ }
|
2010-02-28 19:00:51 +01:00
|
|
|
|
|
|
|
AliasName* get()
|
|
|
|
{
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isEqual(const PathName& val) const
|
|
|
|
{
|
|
|
|
return val == name;
|
|
|
|
}
|
2009-09-22 17:49:55 +02:00
|
|
|
|
2010-02-28 19:00:51 +01:00
|
|
|
PathName name;
|
|
|
|
DbName* database;
|
|
|
|
};
|
2009-09-22 17:49:55 +02:00
|
|
|
|
2010-02-28 19:00:51 +01:00
|
|
|
class AliasesConf : public ConfigCache
|
2009-09-22 17:49:55 +02:00
|
|
|
{
|
2010-02-28 19:00:51 +01: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
|
|
|
{
|
2010-02-28 19:00:51 +01: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 = ¶ms[n];
|
|
|
|
|
2010-03-04 13:52:01 +01:00
|
|
|
PathName file(par->value.ToPathName());
|
2010-02-28 19:00:51 +01:00
|
|
|
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
|
2010-02-28 19:00:51 +01:00
|
|
|
{
|
|
|
|
// 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",
|
2010-02-28 19:00:51 +01:00
|
|
|
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
|
|
|
|
2010-03-04 13:52:01 +01:00
|
|
|
PathName correctedAlias(par->name.ToPathName());
|
2010-02-28 19:00:51 +01:00
|
|
|
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
|
|
|
|
2010-02-28 19:00:51 +01:00
|
|
|
alias = FB_NEW(getPool()) AliasName(getPool(), correctedAlias, db);
|
|
|
|
aliases.add(alias);
|
|
|
|
aliasHash.add(alias);
|
|
|
|
}
|
2009-09-22 17:49:55 +02:00
|
|
|
}
|
2010-02-28 19:00:51 +01:00
|
|
|
|
|
|
|
private:
|
|
|
|
HalfStaticArray<DbName*, 100> databases;
|
|
|
|
HalfStaticArray<AliasName*, 200> aliases;
|
|
|
|
|
|
|
|
public:
|
|
|
|
DbHash dbHash;
|
|
|
|
AliasHash aliasHash;
|
|
|
|
};
|
|
|
|
|
|
|
|
InitInstance<AliasesConf> aliasesConf;
|
|
|
|
}
|
|
|
|
|
2012-03-14 14:46:17 +01:00
|
|
|
// Search for 'alias' in aliases.conf, return it's 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)
|
2010-02-28 19:00:51 +01:00
|
|
|
{
|
|
|
|
PathName corrected_alias = alias;
|
|
|
|
replace_dir_sep(corrected_alias);
|
|
|
|
|
|
|
|
AliasName* a = aliasesConf().aliasHash.lookup(corrected_alias);
|
|
|
|
DbName* db = a ? a->database : NULL;
|
|
|
|
if (db)
|
2009-09-22 17:49:55 +02:00
|
|
|
{
|
2010-02-28 19:00:51 +01:00
|
|
|
file = db->name;
|
2012-03-15 16:05:31 +01:00
|
|
|
if (config)
|
|
|
|
{
|
|
|
|
*config = db->config.hasData() ? db->config : Config::getDefaultConfig();
|
|
|
|
}
|
2010-02-28 19:00:51 +01:00
|
|
|
|
2012-03-15 16:05:31 +01:00
|
|
|
return true;
|
2012-03-14 09:46:27 +01:00
|
|
|
}
|
|
|
|
|
2012-03-15 16:05:31 +01:00
|
|
|
return false;
|
2012-03-14 09:46:27 +01:00
|
|
|
}
|
|
|
|
|
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.
|
2012-03-14 09:46:27 +01:00
|
|
|
static bool resolveDatabaseAccess(const PathName& alias, PathName& file)
|
|
|
|
{
|
|
|
|
PathName corrected_alias = alias;
|
|
|
|
replace_dir_sep(corrected_alias);
|
|
|
|
|
|
|
|
bool rc = true;
|
|
|
|
|
|
|
|
PathName path, name;
|
|
|
|
PathUtils::splitLastComponent(path, name, corrected_alias);
|
|
|
|
|
|
|
|
// if path component not present in file_name
|
|
|
|
if (path.isEmpty())
|
|
|
|
{
|
|
|
|
// try to expand to existing file
|
|
|
|
if (!databaseDirectoryList().expandFileName(file, name))
|
2010-02-28 19:00:51 +01:00
|
|
|
{
|
2012-03-14 09:46:27 +01:00
|
|
|
// try to use default path
|
|
|
|
if (!databaseDirectoryList().defaultName(file, name))
|
2010-02-28 19:00:51 +01:00
|
|
|
{
|
2012-03-14 09:46:27 +01:00
|
|
|
rc = false;
|
2010-02-28 19:00:51 +01:00
|
|
|
}
|
|
|
|
}
|
2012-03-14 09:46:27 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rc = false;
|
|
|
|
}
|
2010-02-28 19:00:51 +01:00
|
|
|
|
2012-03-14 09:46:27 +01:00
|
|
|
if (! rc)
|
|
|
|
{
|
|
|
|
file = corrected_alias;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2012-03-14 09:46:27 +01:00
|
|
|
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.
|
|
|
|
for (const char* p = filename.c_str(); *p; p++)
|
|
|
|
{
|
|
|
|
if (*p == ':' || *p == '/' || *p == '\\')
|
|
|
|
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))
|
2012-03-14 09:46:27 +01:00
|
|
|
{
|
|
|
|
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
|
|
|
{
|
2012-03-14 09:46:27 +01: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
|
|
|
}
|
2010-02-28 19:00:51 +01:00
|
|
|
}
|
|
|
|
|
2012-03-14 09:46:27 +01:00
|
|
|
// Search for correct config in aliases.conf
|
2010-02-28 19:00:51 +01:00
|
|
|
if (config)
|
|
|
|
{
|
2012-03-14 09:46:27 +01:00
|
|
|
DbName* db = aliasesConf().dbHash.lookup(file);
|
2010-02-28 19:00:51 +01:00
|
|
|
*config = (db && db->config.hasData()) ? db->config : Config::getDefaultConfig();
|
2009-09-22 17:49:55 +02:00
|
|
|
}
|
|
|
|
|
2012-03-14 09:46:27 +01:00
|
|
|
return false;
|
2009-09-22 17:49:55 +02:00
|
|
|
}
|