One-KVM/kvmd/helpers/remount/__init__.py
2024-09-18 04:37:43 +03:00

177 lines
5.8 KiB
Python

# ========================================================================== #
# #
# KVMD - The main PiKVM daemon. #
# #
# Copyright (C) 2018-2024 Maxim Devaev <mdevaev@gmail.com> #
# #
# 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 <https://www.gnu.org/licenses/>. #
# #
# ========================================================================== #
import sys
import os
import stat
import pwd
import grp
import shutil
import subprocess
from os.path import join # pylint: disable=ungrouped-imports
from os.path import exists # pylint: disable=ungrouped-imports
from ...fstab import Partition
from ...fstab import find_msd
from ...fstab import find_pst
# =====
def _log(msg: str) -> None:
print(msg, file=sys.stderr)
def _remount(path: str, rw: bool) -> None:
mode = ("rw" if rw else "ro")
_log(f"Remounting {path} to {mode.upper()}-mode ...")
try:
subprocess.check_call(["/bin/mount", "--options", f"remount,{mode}", path])
except subprocess.CalledProcessError as ex:
raise SystemExit(f"Can't remount: {ex}")
def _mkdir(path: str) -> None:
if not exists(path):
_log(f"MKDIR --- {path}")
try:
os.mkdir(path)
except Exception as ex:
raise SystemExit(f"Can't create directory: {ex}")
def _rmtree(path: str) -> None:
if exists(path):
_log(f"RMALL --- {path}")
try:
shutil.rmtree(path)
except Exception as ex:
raise SystemExit(f"Can't remove directory: {ex}")
def _rm(path: str) -> None:
if exists(path):
_log(f"RM --- {path}")
try:
os.remove(path)
except Exception as ex:
raise SystemExit(f"Can't remove file: {ex}")
def _move(src: str, dest: str) -> None:
_log(f"MOVE --- {src} --> {dest}")
try:
os.rename(src, dest)
except Exception as ex:
raise SystemExit(f"Can't move file: {ex}")
def _chown(path: str, user: str) -> None:
if pwd.getpwuid(os.stat(path).st_uid).pw_name != user:
_log(f"CHOWN --- {user} - {path}")
try:
shutil.chown(path, user=user)
except Exception as ex:
raise SystemExit(f"Can't change ownership: {ex}")
def _chgrp(path: str, group: str) -> None:
if grp.getgrgid(os.stat(path).st_gid).gr_name != group:
_log(f"CHGRP --- {group} - {path}")
try:
shutil.chown(path, group=group)
except Exception as ex:
raise SystemExit(f"Can't change group: {ex}")
def _chmod(path: str, mode: int) -> None:
if stat.S_IMODE(os.stat(path).st_mode) != mode:
_log(f"CHMOD --- 0o{mode:o} - {path}")
try:
os.chmod(path, mode)
except Exception as ex:
raise SystemExit(f"Can't change permissions: {ex}")
# =====
def _fix_msd(part: Partition) -> None:
# First images migration
images_path = join(part.root_path, "images")
meta_path = join(part.root_path, "meta")
if exists(images_path) and exists(meta_path):
for name in os.listdir(images_path):
_move(join(images_path, name), os.path.join(part.root_path, name))
if not exists(join(meta_path, f"{name}.complete")):
open(os.path.join(part.root_path, f".__{name}.incomplete")).close() # pylint: disable=consider-using-with
_rmtree(images_path)
_rmtree(meta_path)
# Second images migration
for name in os.listdir(part.root_path):
if name.startswith(".__") and name.endswith(".complete"):
_rm(join(part.root_path, name))
if part.user:
_chown(part.root_path, part.user)
if part.group:
_chgrp(part.root_path, part.group)
def _fix_pst(part: Partition) -> None:
path = os.path.join(part.root_path, "data")
_mkdir(path)
if part.user:
_chown(part.root_path, part.user)
_chown(path, part.user)
if part.group:
_chgrp(part.root_path, part.group)
_chgrp(path, part.group)
if part.user and part.group:
_chmod(part.root_path, 0o1775)
# =====
def main() -> None:
if len(sys.argv) != 2 or sys.argv[1] not in ["ro", "rw"]:
raise SystemExit(f"Usage: {sys.argv[0]} [ro|rw]")
finder = None
fix = None
app = os.path.basename(sys.argv[0])
if app == "kvmd-helper-otgmsd-remount":
finder = find_msd
fix = _fix_msd
elif app == "kvmd-helper-pst-remount":
finder = find_pst
fix = _fix_pst
else:
raise SystemExit("Unknown application target")
rw = (sys.argv[1] == "rw")
assert finder is not None
part = finder()
_remount(part.mount_path, rw)
if rw and part.root_path:
fix(part)
_log(f"Storage in the {'RW' if rw else 'RO'}-mode now")