summaryrefslogtreecommitdiff
path: root/psutil/_pslinux.py
diff options
context:
space:
mode:
Diffstat (limited to 'psutil/_pslinux.py')
-rw-r--r--psutil/_pslinux.py76
1 files changed, 76 insertions, 0 deletions
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index 1917e20d..f6a83301 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -1189,6 +1189,80 @@ def disk_io_counters(perdisk=False):
return retdict
+class RootFsDeviceFinder:
+ """disk_partitions() may return partitions with device == "/dev/root"
+ or "rootfs". This container class uses different strategies to try to
+ obtain the real device path. Resources:
+ https://bootlin.com/blog/find-root-device/
+ https://www.systutorials.com/how-to-find-the-disk-where-root-is-on-in-bash-on-linux/
+ """
+ __slots__ = ['major', 'minor']
+
+ def __init__(self):
+ dev = os.stat("/").st_dev
+ self.major = os.major(dev)
+ self.minor = os.minor(dev)
+
+ def ask_proc_partitions(self):
+ with open_text("%s/partitions" % get_procfs_path()) as f:
+ for line in f.readlines()[2:]:
+ fields = line.split()
+ if len(fields) < 4: # just for extra safety
+ continue
+ major = int(fields[0]) if fields[0].isdigit() else None
+ minor = int(fields[1]) if fields[1].isdigit() else None
+ name = fields[3]
+ if major == self.major and minor == self.minor:
+ if name: # just for extra safety
+ return "/dev/%s" % name
+
+ def ask_sys_dev_block(self):
+ path = "/sys/dev/block/%s:%s/uevent" % (self.major, self.minor)
+ with open_text(path) as f:
+ for line in f:
+ if line.startswith("DEVNAME="):
+ name = line.strip().rpartition("DEVNAME=")[2]
+ if name: # just for extra safety
+ return "/dev/%s" % name
+
+ def ask_sys_class_block(self):
+ needle = "%s:%s" % (self.major, self.minor)
+ files = glob.iglob("/sys/class/block/*/dev")
+ for file in files:
+ try:
+ f = open_text(file)
+ except FileNotFoundError: # race condition
+ continue
+ else:
+ with f:
+ data = f.read().strip()
+ if data == needle:
+ name = os.path.basename(os.path.dirname(file))
+ return "/dev/%s" % name
+
+ def find(self):
+ path = None
+ if path is None:
+ try:
+ path = self.ask_proc_partitions()
+ except (IOError, OSError) as err:
+ debug(err)
+ if path is None:
+ try:
+ path = self.ask_sys_dev_block()
+ except (IOError, OSError) as err:
+ debug(err)
+ if path is None:
+ try:
+ path = self.ask_sys_class_block()
+ except (IOError, OSError) as err:
+ debug(err)
+ # We use exists() because the "/dev/*" part of the path is hard
+ # coded, so we want to be sure.
+ if path is not None and os.path.exists(path):
+ return path
+
+
def disk_partitions(all=False):
"""Return mounted disk partitions as a list of namedtuples."""
fstypes = set()
@@ -1216,6 +1290,8 @@ def disk_partitions(all=False):
device, mountpoint, fstype, opts = partition
if device == 'none':
device = ''
+ if device in ("/dev/root", "rootfs"):
+ device = RootFsDeviceFinder().find() or device
if not all:
if device == '' or fstype not in fstypes:
continue