124 lines
		
	
	
	
		
			2.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			124 lines
		
	
	
	
		
			2.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| This file is part of Telegram Desktop,
 | |
| the official desktop application for the Telegram messaging service.
 | |
| 
 | |
| For license and copyright information please follow this link:
 | |
| https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | |
| */
 | |
| #include "storage/storage_file_lock.h"
 | |
| 
 | |
| #include "base/variant.h"
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #include <sys/types.h>
 | |
| #include <signal.h>
 | |
| 
 | |
| namespace Storage {
 | |
| namespace {
 | |
| 
 | |
| bool KillProcess(pid_t pid) {
 | |
| 	auto signal = SIGTERM;
 | |
| 	auto attempts = 0;
 | |
| 	while (true) {
 | |
| 		const auto result = kill(pid, signal);
 | |
| 		if (result < 0) {
 | |
| 			return (errno == ESRCH);
 | |
| 		}
 | |
| 		usleep(10000);
 | |
| 		if (++attempts == 50) {
 | |
| 			signal = SIGKILL;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| struct FileLock::Descriptor {
 | |
| 	int value;
 | |
| };
 | |
| 
 | |
| struct FileLock::LockingPid {
 | |
| 	pid_t value;
 | |
| };
 | |
| 
 | |
| class FileLock::Lock {
 | |
| public:
 | |
| 	using Result = base::variant<Descriptor, LockingPid>;
 | |
| 	static Result Acquire(const QFile &file);
 | |
| 
 | |
| 	explicit Lock(int descriptor);
 | |
| 	~Lock();
 | |
| 
 | |
| private:
 | |
| 	int _descriptor = 0;
 | |
| 
 | |
| };
 | |
| 
 | |
| FileLock::Lock::Result FileLock::Lock::Acquire(const QFile &file) {
 | |
| 	const auto descriptor = file.handle();
 | |
| 	if (!descriptor || !file.isOpen()) {
 | |
| 		return Descriptor{ 0 };
 | |
| 	}
 | |
| 	while (true) {
 | |
| 		struct flock lock;
 | |
| 		lock.l_type = F_WRLCK;
 | |
| 		lock.l_whence = SEEK_SET;
 | |
| 		lock.l_start = kLockOffset;
 | |
| 		lock.l_len = kLockLimit;
 | |
| 		if (fcntl(descriptor, F_SETLK, &lock) == 0) {
 | |
| 			return Descriptor{ descriptor };
 | |
| 		} else if (fcntl(descriptor, F_GETLK, &lock) < 0) {
 | |
| 			return LockingPid{ 0 };
 | |
| 		} else if (lock.l_type != F_UNLCK) {
 | |
| 			return LockingPid{ lock.l_pid };
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| FileLock::Lock::Lock(int descriptor) : _descriptor(descriptor) {
 | |
| }
 | |
| 
 | |
| FileLock::Lock::~Lock() {
 | |
| 	struct flock unlock;
 | |
| 	unlock.l_type = F_UNLCK;
 | |
| 	unlock.l_whence = SEEK_SET;
 | |
| 	unlock.l_start = kLockOffset;
 | |
| 	unlock.l_len = kLockLimit;
 | |
| 	fcntl(_descriptor, F_SETLK, &unlock);
 | |
| }
 | |
| 
 | |
| FileLock::FileLock() = default;
 | |
| 
 | |
| bool FileLock::lock(QFile &file, QIODevice::OpenMode mode) {
 | |
| 	Expects(_lock == nullptr || file.isOpen());
 | |
| 
 | |
| 	unlock();
 | |
| 	file.close();
 | |
| 	if (!file.open(mode)) {
 | |
| 		return false;
 | |
| 	}
 | |
| 	while (true) {
 | |
| 		const auto result = Lock::Acquire(file);
 | |
| 		if (const auto descriptor = base::get_if<Descriptor>(&result)) {
 | |
| 			if (descriptor->value > 0) {
 | |
| 				_lock = std::make_unique<Lock>(descriptor->value);
 | |
| 				return true;
 | |
| 			}
 | |
| 			break;
 | |
| 		} else if (const auto pid = base::get_if<LockingPid>(&result)) {
 | |
| 			if (pid->value <= 0 || !KillProcess(pid->value)) {
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| void FileLock::unlock() {
 | |
| 	_lock = nullptr;
 | |
| }
 | |
| 
 | |
| FileLock::~FileLock() = default;
 | |
| 
 | |
| } // namespace Storage
 | 
