Explorar o código

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
Ka Ho Ng %!s(int64=5) %!d(string=hai) anos
pai
achega
f8aa02897f
Modificáronse 4 ficheiros con 90 adicións e 0 borrados
  1. 5 0
      UI/CMakeLists.txt
  2. 2 0
      UI/obs-app.cpp
  3. 80 0
      UI/platform-x11.cpp
  4. 3 0
      UI/platform.hpp

+ 5 - 0
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)

+ 2 - 0
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) {

+ 80 - 0
UI/platform-x11.cpp

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

+ 3 - 0
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