Multiple-instance support.
Write PID file to FG_HOME, use this to detect multiple launches. When this situation is detected, set a marker property and place various objects into read-only mode, such as the NavCache and TerraSync. PID file is created using open+unlink semantics on POSIX, and DELETE_ON_CLOSE on Windows, so it will be removed when fgfs exits, even if killed or crashes.
This commit is contained in:
parent
13d5fd4c25
commit
0cda3cbfb2
8 changed files with 153 additions and 47 deletions
|
@ -556,6 +556,10 @@ void FGAirport::loadProcedures() const
|
|||
void FGAirport::loadSceneryDefinitions() const
|
||||
{
|
||||
NavDataCache* cache = NavDataCache::instance();
|
||||
if (cache->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SGPath path;
|
||||
if (!XMLLoader::findAirportData(ident(), "threshold", path)) {
|
||||
return; // no XML threshold data
|
||||
|
@ -636,6 +640,10 @@ void FGAirport::validateTowerData() const
|
|||
|
||||
mTowerDataLoaded = true;
|
||||
NavDataCache* cache = NavDataCache::instance();
|
||||
if (cache->isReadOnly()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SGPath path;
|
||||
if (!XMLLoader::findAirportData(ident(), "twr", path)) {
|
||||
return; // no XML tower data
|
||||
|
@ -684,6 +692,10 @@ bool FGAirport::validateILSData()
|
|||
|
||||
mILSDataLoaded = true;
|
||||
NavDataCache* cache = NavDataCache::instance();
|
||||
if (cache->isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SGPath path;
|
||||
if (!XMLLoader::findAirportData(ident(), "ils", path)) {
|
||||
return false; // no XML tower data
|
||||
|
|
|
@ -1057,7 +1057,9 @@ FGPUIDialog::makeObject (SGPropertyNode *props, int parentWidth, int parentHeigh
|
|||
string logClass = props->getStringValue("logclass");
|
||||
if (logClass == "terrasync") {
|
||||
simgear::SGTerraSync* tsync = (simgear::SGTerraSync*) globals->get_subsystem("terrasync");
|
||||
obj->setBuffer(tsync->log());
|
||||
if (tsync) {
|
||||
obj->setBuffer(tsync->log());
|
||||
}
|
||||
} else {
|
||||
FGNasalSys* nasal = (FGNasalSys*) globals->get_subsystem("nasal");
|
||||
obj->setBuffer(nasal->log());
|
||||
|
|
|
@ -25,17 +25,19 @@
|
|||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // strcmp()
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(SG_WINDOWS)
|
||||
# include <io.h> // isatty()
|
||||
# include <process.h> // _getpid()
|
||||
# include <Windows.h>
|
||||
# define isatty _isatty
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
#include <boost/algorithm/string/compare.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
@ -407,7 +409,7 @@ static SGPath platformDefaultDataPath()
|
|||
}
|
||||
#endif
|
||||
|
||||
void fgInitHome()
|
||||
bool fgInitHome()
|
||||
{
|
||||
SGPath dataPath = SGPath::fromEnv("FG_HOME", platformDefaultDataPath());
|
||||
globals->set_fg_home(dataPath.c_str());
|
||||
|
@ -416,6 +418,66 @@ void fgInitHome()
|
|||
if (!fgHome.exists()) {
|
||||
fgHome.create(0755);
|
||||
}
|
||||
|
||||
if (!fgHome.exists()) {
|
||||
flightgear::fatalMessageBox("Problem setting up user data",
|
||||
"Unable to create the user-data storage folder at: '"
|
||||
+ dataPath.str() + "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fgGetBool("/sim/fghome-readonly", false)) {
|
||||
// user / config forced us into readonly mode, fine
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "Running with FG_HOME readonly");
|
||||
return true;
|
||||
}
|
||||
|
||||
// write our PID, and check writeability
|
||||
SGPath pidPath(dataPath, "fgfs.pid");
|
||||
if (pidPath.exists()) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "flightgear instance already running, switching to FG_HOME read-only.");
|
||||
// set a marker property so terrasync/navcache don't try to write
|
||||
// from secondary instances
|
||||
fgSetBool("/sim/fghome-readonly", true);
|
||||
return true;
|
||||
}
|
||||
|
||||
char buf[16];
|
||||
bool result = false;
|
||||
#if defined(SG_WINDOWS)
|
||||
size_t len = snprintf(buf, 16, "%d", _getpid());
|
||||
|
||||
HANDLE f = CreateFileA(pidPath.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ, /* sharing */
|
||||
NULL, /* security attributes */
|
||||
CREATE_NEW, /* error if already exists */
|
||||
FILE_FLAG_DELETE_ON_CLOSE,
|
||||
NULL /* template */);
|
||||
|
||||
result = (f != INVALID_HANDLE_VALUE);
|
||||
if (result) {
|
||||
DWORD written;
|
||||
WriteFile(f, buf, len, &written, NULL /* overlapped */);
|
||||
}
|
||||
#else
|
||||
// POSIX, do open+unlink trick to the file is deleted on exit, even if we
|
||||
// crash or exit(-1)
|
||||
size_t len = snprintf(buf, 16, "%d", getpid());
|
||||
int fd = ::open(pidPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
|
||||
if (fd >= 0) {
|
||||
::write(fd, buf, len);
|
||||
::unlink(pidPath.c_str()); // delete file when app quits
|
||||
result = true;
|
||||
}
|
||||
|
||||
fgSetBool("/sim/fghome-readonly", false);
|
||||
#endif
|
||||
if (!result) {
|
||||
flightgear::fatalMessageBox("File permissions problem",
|
||||
"Can't write to user-data storage folder, check file permissions and FG_HOME.",
|
||||
"User-data at:" + dataPath.str());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read in configuration (file and command line)
|
||||
|
|
|
@ -34,7 +34,7 @@ class SGPropertyNode;
|
|||
// Return the current base package version
|
||||
std::string fgBasePackageVersion();
|
||||
|
||||
void fgInitHome();
|
||||
bool fgInitHome();
|
||||
|
||||
// Read in configuration (file and command line)
|
||||
int fgInitConfig ( int argc, char **argv );
|
||||
|
|
|
@ -96,6 +96,39 @@ static void fgMainLoop( void )
|
|||
simgear::AtomicChangeListener::fireChangeListeners();
|
||||
}
|
||||
|
||||
static void initTerrasync()
|
||||
{
|
||||
if (fgGetBool("/sim/fghome-readonly", false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start TerraSync up now, so it can be synchronizing shared models
|
||||
// and airports data in parallel with a nav-cache rebuild.
|
||||
SGPath tsyncCache(globals->get_fg_home());
|
||||
tsyncCache.append("terrasync-cache.xml");
|
||||
|
||||
// wipe the cache file if requested
|
||||
if (flightgear::Options::sharedInstance()->isOptionSet("restore-defaults")) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "restore-defaults requested, wiping terrasync update cache at " <<
|
||||
tsyncCache);
|
||||
if (tsyncCache.exists()) {
|
||||
tsyncCache.remove();
|
||||
}
|
||||
}
|
||||
|
||||
fgSetString("/sim/terrasync/cache-path", tsyncCache.c_str());
|
||||
|
||||
simgear::SGTerraSync* terra_sync = new simgear::SGTerraSync();
|
||||
terra_sync->setRoot(globals->get_props());
|
||||
globals->add_subsystem("terrasync", terra_sync);
|
||||
|
||||
terra_sync->bind();
|
||||
terra_sync->init();
|
||||
|
||||
// add the terrasync root as a data path so data can be retrieved from it
|
||||
std::string terraSyncDir(fgGetString("/sim/terrasync/scenery-dir"));
|
||||
globals->append_data_path(terraSyncDir);
|
||||
}
|
||||
|
||||
static void registerMainLoop()
|
||||
{
|
||||
|
@ -126,36 +159,8 @@ static void fgIdleFunction ( void ) {
|
|||
}
|
||||
|
||||
} else if ( idle_state == 2 ) {
|
||||
|
||||
// start TerraSync up now, so it can be synchronizing shared models
|
||||
// and airports data in parallel with a nav-cache rebuild.
|
||||
SGPath tsyncCache(globals->get_fg_home());
|
||||
tsyncCache.append("terrasync-cache.xml");
|
||||
|
||||
// wipe the cache file if requested
|
||||
if (flightgear::Options::sharedInstance()->isOptionSet("restore-defaults")) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "restore-defaults requested, wiping terrasync update cache at " <<
|
||||
tsyncCache);
|
||||
if (tsyncCache.exists()) {
|
||||
tsyncCache.remove();
|
||||
}
|
||||
}
|
||||
|
||||
fgSetString("/sim/terrasync/cache-path", tsyncCache.c_str());
|
||||
|
||||
simgear::SGTerraSync* terra_sync = new simgear::SGTerraSync();
|
||||
terra_sync->setRoot(globals->get_props());
|
||||
globals->add_subsystem("terrasync", terra_sync);
|
||||
|
||||
|
||||
|
||||
terra_sync->bind();
|
||||
terra_sync->init();
|
||||
|
||||
// add the terrasync root as a data path so data can be retrieved from it
|
||||
std::string terraSyncDir(fgGetString("/sim/terrasync/scenery-dir"));
|
||||
globals->append_data_path(terraSyncDir);
|
||||
|
||||
|
||||
initTerrasync();
|
||||
idle_state++;
|
||||
fgSplashProgress("loading-nav-dat");
|
||||
|
||||
|
@ -325,10 +330,14 @@ int fgMainInit( int argc, char **argv ) {
|
|||
sglog().setLogLevels( SG_ALL, SG_ALERT );
|
||||
|
||||
globals = new FGGlobals;
|
||||
fgInitHome();
|
||||
if (!fgInitHome()) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// now home is initialised, we can log to a file inside it
|
||||
logToFile();
|
||||
if (!fgGetBool("/sim/fghome-readonly")) {
|
||||
// now home is initialised, we can log to a file inside it
|
||||
logToFile();
|
||||
}
|
||||
|
||||
std::string version;
|
||||
#ifdef FLIGHTGEAR_VERSION
|
||||
|
|
|
@ -1409,6 +1409,7 @@ struct OptionDesc {
|
|||
{"enable-fullscreen", false, OPTION_BOOL, "/sim/startup/fullscreen", true, "", 0 },
|
||||
{"disable-save-on-exit", false, OPTION_BOOL, "/sim/startup/save-on-exit", false, "", 0 },
|
||||
{"enable-save-on-exit", false, OPTION_BOOL, "/sim/startup/save-on-exit", true, "", 0 },
|
||||
{"read-only", false, OPTION_BOOL, "/sim/fghome-readonly", true, "", 0 },
|
||||
{"ignore-autosave", false, OPTION_FUNC, "", false, "", fgOptIgnoreAutosave },
|
||||
{"restore-defaults", false, OPTION_BOOL, "/sim/startup/restore-defaults", true, "", 0 },
|
||||
{"shading-flat", false, OPTION_BOOL, "/sim/rendering/shading", false, "", 0 },
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#include <simgear/threads/SGGuard.hxx>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/options.hxx>
|
||||
#include "markerbeacon.hxx"
|
||||
#include "navrecord.hxx"
|
||||
|
@ -209,6 +210,7 @@ public:
|
|||
outer(o),
|
||||
db(NULL),
|
||||
path(p),
|
||||
readOnly(false),
|
||||
cacheHits(0),
|
||||
cacheMisses(0),
|
||||
transactionLevel(0),
|
||||
|
@ -225,12 +227,14 @@ public:
|
|||
{
|
||||
SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache at:" << path);
|
||||
|
||||
// see http://code.google.com/p/flightgear-bugs/issues/detail?id=1055
|
||||
// for the logic here. Sigh.
|
||||
readOnly = fgGetBool("/sim/fghome-readonly", false);
|
||||
|
||||
int openFlags = readOnly ? SQLITE_OPEN_READONLY :
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
||||
// see http://code.google.com/p/flightgear-bugs/issues/detail?id=1055
|
||||
// for the UTF8 / path logic here
|
||||
std::string pathUtf8 = simgear::strutils::convertWindowsLocal8BitToUtf8(path.str());
|
||||
sqlite3_open_v2(pathUtf8.c_str(), &db,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
||||
|
||||
sqlite3_open_v2(pathUtf8.c_str(), &db, openFlags, NULL);
|
||||
|
||||
sqlite3_stmt_ptr checkTables =
|
||||
prepare("SELECT count(*) FROM sqlite_master WHERE name='properties'");
|
||||
|
@ -240,7 +244,7 @@ public:
|
|||
|
||||
execSelect(checkTables);
|
||||
bool didCreate = false;
|
||||
if (sqlite3_column_int(checkTables, 0) == 0) {
|
||||
if (!readOnly && (sqlite3_column_int(checkTables, 0) == 0)) {
|
||||
SG_LOG(SG_NAVCACHE, SG_INFO, "will create tables");
|
||||
initTables();
|
||||
didCreate = true;
|
||||
|
@ -858,7 +862,8 @@ public:
|
|||
NavDataCache* outer;
|
||||
sqlite3* db;
|
||||
SGPath path;
|
||||
|
||||
bool readOnly;
|
||||
|
||||
/// the actual cache of ID -> instances. This holds an owning reference,
|
||||
/// so once items are in the cache they will never be deleted until
|
||||
/// the cache drops its reference
|
||||
|
@ -1048,7 +1053,11 @@ NavDataCache::NavDataCache()
|
|||
SG_LOG(SG_NAVCACHE, t == 0 ? SG_WARN : SG_ALERT, "NavCache: init failed:" << e.what()
|
||||
<< " (attempt " << t << ")");
|
||||
d.reset();
|
||||
homePath.remove();
|
||||
|
||||
// only wipe the existing if not readonly
|
||||
if (!fgGetBool("/sim/fghome-readonly", false)) {
|
||||
homePath.remove();
|
||||
}
|
||||
}
|
||||
} // of retry loop
|
||||
|
||||
|
@ -1097,6 +1106,10 @@ NavDataCache* NavDataCache::instance()
|
|||
|
||||
bool NavDataCache::isRebuildRequired()
|
||||
{
|
||||
if (d->readOnly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flightgear::Options::sharedInstance()->isOptionSet("restore-defaults")) {
|
||||
SG_LOG(SG_NAVCACHE, SG_INFO, "NavCache: restore-defaults requested, will rebuild cache");
|
||||
return true;
|
||||
|
@ -2148,6 +2161,11 @@ void NavDataCache::dropGroundnetFor(PositionedID aAirport)
|
|||
d->execUpdate(q);
|
||||
}
|
||||
|
||||
bool NavDataCache::isReadOnly() const
|
||||
{
|
||||
return d->readOnly;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Transaction RAII object
|
||||
|
||||
|
|
|
@ -267,6 +267,8 @@ public:
|
|||
NavDataCache* _instance;
|
||||
bool _committed;
|
||||
};
|
||||
|
||||
bool isReadOnly() const;
|
||||
private:
|
||||
NavDataCache();
|
||||
|
||||
|
|
Loading…
Reference in a new issue