Source code for linux_utils.fstab

# linux-utils: Linux system administration tools for Python.
#
# Author: Peter Odding <peter@peterodding.com>
# Last Change: February 9, 2020
# URL: https://linux-utils.readthedocs.io

"""
Parsing of `/etc/fstab`_ configuration files.

.. _/etc/fstab: https://manpages.debian.org/fstab
"""

# Standard library modules.
import logging

# External dependencies.
from humanfriendly.text import split
from property_manager import lazy_property

# Modules included in our package.
from linux_utils import coerce_device_file
from linux_utils.tabfile import TabFileEntry, parse_tab_file

# Public identifiers that require documentation.
__all__ = (
    'FileSystemEntry',
    'find_mounted_filesystems',
    'logger',
    'parse_fstab',
)

# Initialize a logger for this module.
logger = logging.getLogger(__name__)


[docs]def find_mounted_filesystems(filename='/proc/mounts', context=None): """ Get information about mounted filesystem from ``/proc/mounts``. :param filename: The absolute pathname of the file to parse (a string, defaults to ``/proc/mounts``). :param context: See :func:`.coerce_context()` for details. :returns: A generator of :class:`FileSystemEntry` objects. This function is a trivial wrapper for :func:`parse_fstab()` that instructs it to parse ``/proc/mounts`` instead of `/etc/fstab`_. Here's an example: >>> from humanfriendly import format_table >>> from linux_utils.fstab import find_mounted_filesystems >>> print(format_table( ... data=[ ... (entry.mount_point, entry.device_file, entry.vfs_type) ... for entry in find_mounted_filesystems() ... if entry.vfs_type not in ( ... # While writing this example I was actually surprised to ... # see how many `virtual filesystems' a modern Linux system ... # has mounted by default (based on Ubuntu 16.04). ... 'autofs', 'cgroup', 'debugfs', 'devpts', 'devtmpfs', 'efivarfs', ... 'fuse.gvfsd-fuse', 'fusectl', 'hugetlbfs', 'mqueue', 'proc', ... 'pstore', 'securityfs', 'sysfs', 'tmpfs', ... ) ... ], ... column_names=["Mount point", "Device", "Type"], ... )) --------------------------------------------------- | Mount point | Device | Type | --------------------------------------------------- | / | /dev/mapper/internal-root | ext4 | | /boot | /dev/sda5 | ext4 | | /boot/efi | /dev/sda1 | vfat | | /mnt/backups | /dev/mapper/backups | ext4 | --------------------------------------------------- """ return parse_fstab(filename=filename, context=context)
[docs]def parse_fstab(filename='/etc/fstab', context=None): """ Parse the Linux configuration file `/etc/fstab`_. :param filename: The absolute pathname of the file to parse (a string, defaults to `/etc/fstab`_). :param context: See :func:`.coerce_context()` for details. :returns: A generator of :class:`FileSystemEntry` objects. Here's an example: >>> from linux_utils.fstab import parse_fstab >>> next(e for e in parse_fstab() if e.mount_point == '/') FileSystemEntry( check_order=1, configuration_file='/etc/fstab', device='UUID=147f7d18-e0c9-499c-8791-401642581b90', device_file='/dev/disk/by-uuid/147f7d18-e0c9-499c-8791-401642581b90', dump_frequency=0, line_number=11, mount_point='/', options=['defaults', 'errors=remount-ro', 'discard', 'relatime', 'data=ordered'], vfs_type='ext4', ) Note that some miscellaneous :class:`FileSystemEntry` properties were omitted from the example above to make it more concise. """ for entry in parse_tab_file(filename=filename, context=context): if len(entry.tokens) >= 4: # Transform the object into our type. entry.__class__ = FileSystemEntry yield entry elif len(entry.tokens) > 0: logger.warning("Ignoring line %i in %s because I couldn't parse it!", entry.line_number, entry.configuration_file)
[docs]class FileSystemEntry(TabFileEntry): """ An entry parsed from `/etc/fstab`_. Each entry in the fstab file has six fields, these are mapped to the following properties: 1. :attr:`device` 2. :attr:`mount_point` 3. :attr:`vfs_type` 4. :attr:`options` 5. :attr:`dump_frequency` 6. :attr:`check_order` Refer to the `fstab man page`_ for more information about the meaning of each of these fields. The values of the following properties are computed based on the six fields above: - :attr:`device_file` - :attr:`nfs_directory` - :attr:`nfs_server` .. _fstab man page: https://manpages.debian.org/fstab """
[docs] @lazy_property def check_order(self): """The order in which the filesystem should be checked at boot time (an integer number, defaults to 0).""" try: return int(self.tokens[5]) except IndexError: return 0
@property def device(self): """ The block special device or remote filesystem to be mounted (a string). The value of this property may be a ``UUID=...`` expression. """ return self.tokens[0]
[docs] @lazy_property def device_file(self): """ The block special device to be mounted (a string). The value of this property is computed by passing :attr:`device` to :func:`.coerce_device_file()`. """ return coerce_device_file(self.device)
[docs] @lazy_property def dump_frequency(self): """The dump frequency for the filesystem (an integer number, defaults to 0).""" try: return int(self.tokens[4]) except IndexError: return 0
[docs] @lazy_property def mount_point(self): r""" The mount point for the filesystem (a string). Each occurrence of the escape sequence ``\040`` is replaced by a space. """ return self.tokens[1].replace(r'\040', ' ')
[docs] @lazy_property def nfs_directory(self): """ The directory on the NFS server (a string or :data:`None`). When :attr:`vfs_type` is ``nfs`` or ``nfs4`` and :attr:`device` is of the form ``<server>:<directory>`` the value of :attr:`nfs_directory` will be the part *after* the colon (``:``). """ if self.vfs_type in ('nfs', 'nfs4'): server, _, directory = self.device.partition(':') if server and directory: return directory
[docs] @lazy_property def nfs_server(self): """ The host name or IP address of the NFS server (a string or :data:`None`). When :attr:`vfs_type` is ``nfs`` or ``nfs4`` and :attr:`device` is of the form ``<server>:<directory>`` the value of :attr:`nfs_server` will be the part *before* the colon (``:``). """ if self.vfs_type in ('nfs', 'nfs4'): server, _, directory = self.device.partition(':') if server and directory: return server
[docs] @lazy_property def options(self): """The mount options for the filesystem (a list of strings).""" return split(self.tokens[3])
@property def vfs_type(self): """The type of filesystem (a string like 'ext4' or 'xfs').""" return self.tokens[2]