From f903cdfa507ef141b9b99a66a8b55c1a919aeddd Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 17 Mar 2020 16:05:24 +0000 Subject: [PATCH] Linux/macOS: use flock() to implement exclusive launch. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous implementation wasn’t correct, use a better one now. In the case of a non-clean exit we will leave a stale .pid file in FG_HOME, but we clear stale files on the next launch. --- src/Main/fg_init.cxx | 78 +++++++++++++++++++++++++++++--------------- src/Main/main.cxx | 3 +- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 2ff531d71..3e3b170f3 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -473,35 +473,56 @@ bool fgInitHome() } #else // write our PID, and check writeability - SGPath pidPath(dataPath, "fgfs.pid"); - if (pidPath.exists()) { - SG_LOG(SG_GENERAL, SG_ALERT, "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]; + SGPath pidPath(dataPath, "fgfs_lock.pid"); std::string ps = pidPath.utf8Str(); - // do open+unlink trick to the file is deleted on exit, even if we - // crash or exit(-1) - ssize_t len = snprintf(buf, 16, "%d", getpid()); - int fd = ::open(ps.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644); - if (fd >= 0) { - result = ::write(fd, buf, len) == len; - if( ::unlink(ps.c_str()) != 0 ) // delete file when app quits - result = false; - } + if (pidPath.exists()) { + int fd = ::open(ps.c_str(), O_RDONLY, 0644); + if (fd < 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + int err = ::flock(fd, LOCK_EX | LOCK_NB); + if (err < 0) { + if ( errno == EWOULDBLOCK) { + SG_LOG(SG_GENERAL, SG_ALERT, "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; + } else { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + } + + // we locked it! + result = true; + } else { + char buf[16]; + std::string ps = pidPath.utf8Str(); - if (!result) { - flightgear::fatalMessageBoxWithoutExit( - "File permissions problem", - "Can't write to user-data storage folder, check file permissions " - "and FG_HOME.", - "User-data at '" + dataPath.utf8Str() + "'."); - return false; + ssize_t len = snprintf(buf, 16, "%d\n", getpid()); + int fd = ::open(ps.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to open local file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + write(fd, buf, len); + + int err = flock(fd, LOCK_EX); + if (err != 0) { + SG_LOG(SG_GENERAL, SG_ALERT, "failed to lock file:" << pidPath + << "\n\tdue to:" << simgear::strutils::error_string(errno)); + return false; + } + + result = true; } #endif fgSetBool("/sim/fghome-readonly", false); @@ -514,6 +535,11 @@ void fgShutdownHome() if (static_fgHomeWriteMutex) { CloseHandle(static_fgHomeWriteMutex); } +#else + if (fgGetBool("/sim/fghome-readonly") == false) { + SGPath pidPath = globals->get_fg_home() / "fgfs_lock.pid"; + pidPath.remove(); + } #endif } diff --git a/src/Main/main.cxx b/src/Main/main.cxx index d8be13396..6bb2828d6 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -706,7 +706,8 @@ int fgMainInit( int argc, char **argv ) int result = fgOSMainLoop(); frame_signal.clear(); fgOSCloseWindow(); - + fgShutdownHome(); + simgear::Emesary::GlobalTransmitter::instance()->NotifyAll(mln_stopped); simgear::clearEffectCache();