]> git.mxchange.org Git - flightgear.git/commitdiff
Multiple-instance support.
authorJames Turner <zakalawe@mac.com>
Thu, 14 Nov 2013 16:48:14 +0000 (16:48 +0000)
committerJames Turner <zakalawe@mac.com>
Fri, 15 Nov 2013 21:04:15 +0000 (21:04 +0000)
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.

src/Airports/airport.cxx
src/GUI/FGPUIDialog.cxx
src/Main/fg_init.cxx
src/Main/fg_init.hxx
src/Main/main.cxx
src/Main/options.cxx
src/Navaids/NavDataCache.cxx
src/Navaids/NavDataCache.hxx

index 42d76f5ba7d18f7c390870edff068e9cc9cad59b..2b6020d9f64271d8ed633b4dd90c8ba8d00a9b5f 100644 (file)
@@ -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
index 5620ae0fc6c9d95a6587301da1f469d8348883e5..a57aeba5e75afb4bfbf2f5ddc373caa8122519b7 100644 (file)
@@ -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());
index 8ad9803f9be9c1de0270c8c76838b9406603b6db..f478098b5dbc73b4239547645b8f296493252abe 100644 (file)
 #  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)
index 5329b03811ed279b1f80c4c3309773725b1455e3..ffadf6d22507b5d8fe6a3b8b79c1a74c9bf8231d 100644 (file)
@@ -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 );
index 3f82429bf1c7f39d6b6718215bf5a4dc934607a7..84f03a6fd3458f1c39fc643230b1ff1c3b1cdc8c 100644 (file)
@@ -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
index f0906b966b6bb1bbfcaf8bfa8f1e350a5c5117d5..eecb6e6681181e2d78b9ea61c356965f302409f1 100644 (file)
@@ -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 },
index 28b8a636b1d23afcbbb3890f33e139c4e11f4cb9..80422483d2d2130cd2be74501bce8734f14418c8 100644 (file)
@@ -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
     
index ed6579249329a9e8ea04dd9d9f27fcc0210b1577..4975b0954fa89d5f96c0ce7ce0689e3989e7ad8b 100644 (file)
@@ -267,6 +267,8 @@ public:
         NavDataCache* _instance;
         bool _committed;
     };
+    
+    bool isReadOnly() const;
 private:
   NavDataCache();