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
This commit is contained in:
Ka Ho Ng 2020-11-01 00:04:18 +08:00 committed by Jim
parent 013dd5a7a3
commit f8aa02897f
4 changed files with 90 additions and 0 deletions

View File

@ -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)

View File

@ -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) {

View File

@ -35,6 +35,17 @@
#include <stdio.h>
#include <sys/un.h>
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
#include <sys/param.h>
#include <fcntl.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <libprocstat.h>
#include <condition_variable>
#include <mutex>
#include <thread>
#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<std::mutex> 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<std::mutex> 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<std::mutex> 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)

View File

@ -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