From 87a7d0f93b875161522e7d71bb59a5418d50d834 Mon Sep 17 00:00:00 2001 From: Nils <> Date: Tue, 8 Mar 2022 23:16:37 +0100 Subject: [PATCH] Handle new lockfile versions in nsmd 1.6.0. Will still work in older versions --- engine/nsmservercontrol.py | 38 ++++++++++++++++++++++++++++++---- engine/watcher.py | 5 +---- qtgui/sessiontreecontroller.py | 19 +++++++---------- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/engine/nsmservercontrol.py b/engine/nsmservercontrol.py index ae68e61..9657fcb 100644 --- a/engine/nsmservercontrol.py +++ b/engine/nsmservercontrol.py @@ -41,6 +41,7 @@ from uuid import uuid4 from datetime import datetime from sys import exit as sysexit from time import sleep +from ctypes import c_ulong #Our files from .comparedirectories import md5_dir @@ -316,7 +317,7 @@ class NsmServerControl(object): #Deactivate hooks for now. During init no hooks may be called, #but some functions want to do that already. We setup the true hooks at the end of init self.sessionOpenReadyHook= self.sessionOpenLoadingHook= self.sessionClosedHook= self.clientStatusHook= self.singleInstanceActivateWindowHook= self.dataClientNamesHook= self.dataClientDescriptionHook= nothing - + self.lockfile_directory = getenv("XDG_RUNTIME_DIR") self._queue = list() #Incoming OSC messages are buffered here. #Status variables that are set by our callbacks @@ -1413,10 +1414,39 @@ class NsmServerControl(object): return True return False + + def _simple_hash(self, s:str) -> int: + """This is a translation from the nsm/file.c function of the same name. + We use it to find the lock file on our system. + + It is a djb2 hash modulo 65521 + """ + hashAddress = 5381 + for i, char in enumerate(s): + hashAddress = ((hashAddress << 5) + hashAddress ) + ord(char) + + hashAddress = c_ulong(hashAddress).value #wrap around for whatever number of bits unsinged long is on this system. 2**64 most likely + return hashAddress % 65521 + + + def _get_lock_file_name(self, session_name:str, full_absolute_session_path:str) -> pathlib.Path: + """This is a translation from the nsm/nsmd.c function of the same name. + We use it to find the lock file on our system. + + To avoid collisions of two simple session names under either different subdirs or even + different session roots.""" + #session_name in Agordejo includes subdirs. We want only the basename, like in nsmd. Luckily they are paths. + session_name = pathlib.Path(session_name).name #basename + session_hash:int = self._simple_hash(full_absolute_session_path) + session_lock = pathlib.Path(self.lockfile_directory, "nsm", session_name + str(session_hash)) + return session_lock + + def _checkIfLocked(self, nsmSessionName:str)->bool: - basePath = pathlib.Path(self.sessionRoot, nsmSessionName) - assert basePath.exists() - lockFile = pathlib.Path(basePath, ".lock") + #basePath = pathlib.Path(self.sessionRoot, nsmSessionName) + #assert basePath.exists() + #lockFile = pathlib.Path(basePath, ".lock") + lockFile = self._get_lock_file_name(nsmSessionName, str(pathlib.Path(self.sessionRoot, nsmSessionName))) return lockFile.exists() def getSessionFiles(self, nsmSessionName:str)->list: diff --git a/engine/watcher.py b/engine/watcher.py index d99fd4a..58e89d3 100644 --- a/engine/watcher.py +++ b/engine/watcher.py @@ -155,7 +155,7 @@ class Watcher(object): #Lockfiles if self.lockFileHook: - lockfileState = entry["lockFile"].is_file() + lockfileState = self._nsmServerControl._checkIfLocked(nsmSessionName) if not self._lastLockfile[nsmSessionName] == lockfileState: self._lastLockfile[nsmSessionName] = lockfileState self.lockFileHook(nsmSessionName, lockfileState) @@ -168,6 +168,3 @@ class Watcher(object): logger.warning(f"File not found error for {entry}") self._lastExport.remove(entry) #avoid stumbling upon this again self._update() - - - diff --git a/qtgui/sessiontreecontroller.py b/qtgui/sessiontreecontroller.py index cbec784..377e7f7 100644 --- a/qtgui/sessiontreecontroller.py +++ b/qtgui/sessiontreecontroller.py @@ -98,16 +98,12 @@ class SessionItem(QtWidgets.QTreeWidgetItem): def setLocked(self, state:bool): """Number of clients, symlinks and size change frequently while a session is open/locked. - We deactivate the display of these values while locked""" - return - if not state == self.isDisabled(): - self.setDisabled(state) - if state: - self.setText(2, "") #number of clients - self.setText(3, "") #Symlinks - self.setText(4, "") #Size - else: - self.updateData() + We deactivate the display of these values while locked + + This is also used for nsmd lockfiles and locked sessions, aka session open by another nsmd. + """ + self.updateData() + self.setDisabled(state) def updateTimestamp(self, timestamp:str): #Column 1 "Last Save" @@ -282,7 +278,7 @@ class SessionTreeController(object): listOfLabelsAndFunctions = [ (QtCore.QCoreApplication.translate("SessionTree", "Copy Session"), lambda: self._askForCopyAndCopy(item.sessionDict["nsmSessionName"])) ] - if not item.isDisabled(): + if not item.isDisabled() and not item.sessionDict["locked"]: listOfLabelsAndFunctions.append((QtCore.QCoreApplication.translate("SessionTree", "Rename Session"), lambda: self._askForNameAndRenameSession(item.sessionDict["nsmSessionName"]))) #Delete should be the bottom item. listOfLabelsAndFunctions.append((QtCore.QCoreApplication.translate("SessionTree", "Delete Session"), lambda: self.deleteSessionItem(item))) @@ -310,7 +306,6 @@ class SessionTreeController(object): def _reactSelectionChanged(self, item, previous): """User clicks on an entry in the session chooser, or in the empty space. in any case, the selection changes and we can decide if we activate/deactivate certain buttons""" - if not item or not type(item) is SessionItem or item.sessionDict["locked"] == True: sessionSelectedState = False else: