From f8aa02897fc3c06eed62a1cecf8e2258be1f7cdd Mon Sep 17 00:00:00 2001 From: Ka Ho Ng Date: Sun, 1 Nov 2020 00:04:18 +0800 Subject: [PATCH] UI: Detect other instances of obs on FreeBSD Detect other instances of the obs by creating an extra dummy thread, named "OBS runonce". The process of threads enumeration of current user is guarded by an O_EXLOCK file advisory lock when opening the lock file. Such file lock would be dropped once the thread name is changed. This should be usable on FreeBSD and possibly compile on DragonFly BSD. fixes: #3053 --- UI/CMakeLists.txt | 5 +++ UI/obs-app.cpp | 2 ++ UI/platform-x11.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++ UI/platform.hpp | 3 ++ 4 files changed, 90 insertions(+) diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 256bb6646..1206f2bef 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -132,6 +132,11 @@ elseif(UNIX) set(obs_PLATFORM_LIBRARIES Qt5::X11Extras) + + if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") + list(APPEND obs_PLATFORM_LIBRARIES + procstat) + endif() endif() if(BROWSER_AVAILABLE_INTERNAL) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index bc0fcabc4..93098fa11 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1950,6 +1950,8 @@ static int run_program(fstream &logFile, int argc, char *argv[]) CheckAppWithSameBundleID(already_running); #elif defined(__linux__) RunningInstanceCheck(already_running); +#elif defined(__FreeBSD__) || defined(__DragonFly__) + PIDFileCheck(already_running); #endif if (!already_running) { diff --git a/UI/platform-x11.cpp b/UI/platform-x11.cpp index f74e0f43b..dd4a9c321 100644 --- a/UI/platform-x11.cpp +++ b/UI/platform-x11.cpp @@ -35,6 +35,17 @@ #include #include #endif +#if defined(__FreeBSD__) || defined(__DragonFly__) +#include +#include +#include +#include +#include + +#include +#include +#include +#endif using namespace std; @@ -90,6 +101,75 @@ void RunningInstanceCheck(bool &already_running) fclose(fp); } #endif +#if defined(__FreeBSD__) || defined(__DragonFly__) +struct RunOnce { + std::thread thr; + static const char *thr_name; + std::condition_variable cv; + std::condition_variable wait_cv; + std::mutex mtx; + bool exiting = false; + bool name_changed = false; + + void thr_proc() + { + std::unique_lock lk(mtx); + pthread_setname_np(pthread_self(), thr_name); + name_changed = true; + wait_cv.notify_all(); + cv.wait(lk, [this]() { return exiting; }); + } + + ~RunOnce() + { + if (thr.joinable()) { + std::unique_lock lk(mtx); + exiting = true; + cv.notify_one(); + lk.unlock(); + thr.join(); + } + } +} RO; +const char *RunOnce::thr_name = "OBS runonce"; + +void PIDFileCheck(bool &already_running) +{ + std::string tmpfile_name = + "/tmp/obs-studio.lock." + to_string(geteuid()); + int fd = open(tmpfile_name.c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0600); + if (fd == -1) { + already_running = true; + return; + } + + already_running = false; + + procstat *ps = procstat_open_sysctl(); + unsigned int count; + auto procs = procstat_getprocs(ps, KERN_PROC_UID | KERN_PROC_INC_THREAD, + geteuid(), &count); + for (unsigned int i = 0; i < count; i++) { + if (!strncmp(procs[i].ki_tdname, RunOnce::thr_name, + sizeof(procs[i].ki_tdname))) { + already_running = true; + break; + } + } + procstat_freeprocs(ps, procs); + procstat_close(ps); + + RO.thr = std::thread(std::mem_fn(&RunOnce::thr_proc), &RO); + + { + std::unique_lock lk(RO.mtx); + RO.wait_cv.wait(lk, []() { return RO.name_changed; }); + } + + unlink(tmpfile_name.c_str()); + close(fd); +} +#endif static inline bool check_path(const char *data, const char *path, string &output) diff --git a/UI/platform.hpp b/UI/platform.hpp index f8717cb43..7e7d47ed1 100644 --- a/UI/platform.hpp +++ b/UI/platform.hpp @@ -73,3 +73,6 @@ void CheckAppWithSameBundleID(bool &already_running); #ifdef __linux__ void RunningInstanceCheck(bool &already_running); #endif +#if defined(__FreeBSD__) || defined(__DragonFly__) +void PIDFileCheck(bool &already_running); +#endif