소스 검색

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 5 년 전
부모
커밋
f8aa02897f
4개의 변경된 파일90개의 추가작업 그리고 0개의 파일을 삭제
  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