aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/plugins/locks.py193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/plugins/locks.py b/src/plugins/locks.py
new file mode 100644
index 0000000..c22474c
--- /dev/null
+++ b/src/plugins/locks.py
@@ -0,0 +1,193 @@
+# -*- python -*-
+'''
+xpybar – xmobar replacement written in python
+Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+'''
+
+
+class Locks:
+ '''
+ File lock statistics
+
+ @variable locks:list<Locks.Lock> List of file-locks
+ '''
+
+ def __init__(self):
+ '''
+ Constructor
+ '''
+ def convert(fields):
+ fields[0] = int(fields[0])
+ fields[4] = int(fields[4])
+ fields[5] = int(fields[5], 16)
+ fields[6] = int(fields[6], 16)
+ fields[7] = int(fields[7])
+ fields[8] = int(fields[8])
+ fields[9] = int(fields[9]) if not fields[9] == 'EOF' else None
+ return fields
+ with open('/proc/locks', 'rb') as file:
+ data = file.read()
+ data = data.decode('utf-8', 'replace').rstrip('\n').replace(':', ' ').replace(' ', ' ')
+ while ' ' in data:
+ data = data.replace(' ', ' ')
+ self.locks = [Locks.Lock(*convert(line.split(' '))) for line in data.split('\n')]
+
+
+ @staticmethod
+ def find_device(major, minor):
+ '''
+ Find a device from its major and minor number
+
+ This method will not work if a mount's mountpoint or a
+ device's pathname includes spaces or line feeds.
+
+ @param major:int The major number of the device, in decimal form
+ @param minor:int The minor number of the device, in decimal form
+ @return (root:str, mountpoint:str, device:str)? The root of the mount within the filesystem,
+ the mountpoint relative to the process's root,
+ and the mounted device (pathname or RAM filesystem),
+ `None` if not found
+ '''
+ with open('/proc/self/mountinfo', 'rb') as file:
+ data = file.read()
+ data = data.decode('utf-8', 'strict').rstrip('\n').split('\n')
+ devnum = '%i:%i' % (major, minor)
+ for line in data:
+ line = line.split(' ')
+ if not line[2] == devnum:
+ continue
+ return (line[3], line[4], line[-2])
+ return None
+
+
+ @staticmethod
+ def find_pathname(mountpoint, inode, alarm = 1):
+ '''
+ Find the pathname of a file based on its mountpoint and inode number
+
+ @param mountpoint:str The mountpoint of the file
+ @param inode:int The inode number of the file
+ @param alarm:int|str? The time limit of the search, `None` to search forever
+ @return :list<str> All found pathnames of the file
+ '''
+ from subprocess import Popen, PIPE
+ command = ['find', mountpoint, '-inum', str(inode), '-mount', '-print0']
+ if alarm is not None:
+ command = ['alarm', str(inode)] + command
+ found = Popen(command, stdin = PIPE, stdout = PIPE, stderr = PIPE).communicate()[0]
+ found = found.decode('utf-8', 'replace').rstrip('\n')
+ found = [pathname for pathname in found.split('\0') if not pathname == '']
+ return found
+
+
+ @staticmethod
+ def find(major, minor, inode, alarm = 1):
+ '''
+ Find the pathname of a file based on its device's major and minor number and its inode number
+
+ @param major:int The major number of the device, in decimal form
+ @param minor:int The minor number of the device, in decimal form
+ @param inode:int The inode number of the file
+ @param alarm:int|str? The time limit of the search, `None` to search forever
+ @return :list<str> All found pathnames of the file
+ '''
+ device = Locks.find_device()
+ if device is None:
+ return []
+ return Locks.find_pathname(device[1], inode, alarm)
+
+
+ class Lock:
+ '''
+ Information about a file-lock
+
+ @variable lock_id:int The unique ID of the lock
+ @variable lock_type:str Either 'FLOCK' or 'POSIX', indicating what lock system has been used,
+ 'FLOCK' indicates the 'flock' system call, and 'POSIX' indicates the
+ 'lockf' system call.
+ @variable mandatory:bool Whether the lock is mandatory rather than advisory
+ @variable shared:bool Whether the process has locked the file for reading rather than writing
+ @variable pid:int The ID of the process that has locked the file
+ @variable major:int The major number of the device on which the locked file is stored
+ @variable minor:int The minor number of the device on which the locked file is stored
+ @variable inode:int The inode number of the locked file
+ @variable start:int The index of the first bytes of the locked area
+ @variable end:int? The index of the last bytes of the locked area, `None` for end-of-file
+ '''
+ def __init__(self, lock_id, lock_type, lock_level, sharedness, pid, major, minor, inode, start, end):
+ '''
+ Constructor
+
+ @param lock_id:int The unique ID of the lock
+ @param lock_type:str Either 'FLOCK' or 'POSIX', indicating what lock system has been used,
+ 'FLOCK' indicates the 'flock' system call, and 'POSIX' indicates the
+ 'lockf' system call.
+ @param lock_level:str Either 'MANDATORY' or 'ADVISORY' indicating the level of the lock
+ @param sharedness:str Either 'READ' or 'WRITE' indicating the sharedness of the lock
+ @param pid:int The ID of the process that has locked the file
+ @param major:int The major number of the device on which the locked file is stored
+ @param minor:int The minor number of the device on which the locked file is stored
+ @param inode:int The inode number of the locked file
+ @param start:int The index of the first bytes of the locked area
+ @param end:int? The index of the last bytes of the locked area, `None` for end-of-file
+ '''
+ self.lock_id = lock_id
+ self.lock_type = lock_type
+ self.mandatory = lock_level == 'MANDATORY'
+ self.shared = sharedness == 'READ'
+ self.pid = pid
+ self.major = major
+ self.minor = minor
+ self.inode = inode
+ self.start = start
+ self.end = end
+
+
+ def find_device(self):
+ '''
+ Find the device that the locked file is located on
+
+ This method will not work if a mount's mountpoint or a
+ device's pathname includes spaces or line feeds.
+
+ @return (root:str, mountpoint:str, device:str)? The root of the mount within the filesystem,
+ the mountpoint relative to the process's root,
+ and the mounted device (pathname or RAM
+ filesystem), `None` if not found
+ '''
+ return Locks.find_device(self.major, self.minor)
+
+
+ def find_pathname(self, mountpoint, alarm = 1):
+ '''
+ Find the pathname of the locked file based on its mountpoint
+
+ @param mountpoint:str The mountpoint of the file
+ @param alarm:int|str? The time limit of the search, `None` to search forever
+ @return :list<str> All found pathnames of the file
+ '''
+ return Locks.find_pathname(mountpoint, self.inode, alarm)
+
+
+ def find(self, alarm = 1):
+ '''
+ Find the pathname of the locked file
+
+ @param alarm:int|str? The time limit of the search, `None` to search forever
+ @return :list<str> All found pathnames of the file
+ '''
+ return Locks.find(self.major, self.minor, self.inode, alarm)
+