Add rar2fs mount service
This commit is contained in:
parent
50dedefe79
commit
ab489e3bd8
5 changed files with 181 additions and 1 deletions
|
|
@ -6,7 +6,7 @@
|
||||||
./dyndns.nix
|
./dyndns.nix
|
||||||
./esphome
|
./esphome
|
||||||
./home-assistant.nix
|
./home-assistant.nix
|
||||||
./jellyfin.nix
|
./jellyfin
|
||||||
./mail.nix
|
./mail.nix
|
||||||
./matrix.nix
|
./matrix.nix
|
||||||
./navidrome.nix
|
./navidrome.nix
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@ let
|
||||||
inherit (config.networking) ports;
|
inherit (config.networking) ports;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
imports = [
|
||||||
|
./rar2fs.nix
|
||||||
|
];
|
||||||
|
|
||||||
services.jellyfin = {
|
services.jellyfin = {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
62
hosts/iron/services/jellyfin/rar2fs.nix
Normal file
62
hosts/iron/services/jellyfin/rar2fs.nix
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
{ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
rar2fs = pkgs.rar2fs.override { unrar = pkgs.unrar_6; };
|
||||||
|
rar2fs_mounts = pkgs.writeScriptBin "rar2fs_mounts" (lib.strings.concatLines [
|
||||||
|
"#!${pkgs.python3}/bin/python"
|
||||||
|
(builtins.readFile ./rar2fs_mounts.py)
|
||||||
|
]);
|
||||||
|
rar_path = "/var/lib/qBittorrent/downloads";
|
||||||
|
mount_path = "/run/jellyfin/rar2fs";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
programs.fuse = {
|
||||||
|
userAllowOther = true;
|
||||||
|
mountMax = 1000;
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
rar2fs
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.services.jellyfin-rar2fs = {
|
||||||
|
after = [ "jellyfin.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
path = [ rar2fs "/run/wrappers/bin" ];
|
||||||
|
environment.USER = "jellyfin";
|
||||||
|
serviceConfig = {
|
||||||
|
AmbientCapabilities = "CAP_SYS_ADMIN CAP_SETUID CAP_SETGID";
|
||||||
|
CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_SETUID CAP_SETGID";
|
||||||
|
DeviceAllow = "/dev/fuse rw";
|
||||||
|
ExecStart = "${rar2fs_mounts}/bin/rar2fs_mounts ${rar_path} ${mount_path}";
|
||||||
|
Group = "jellyfin";
|
||||||
|
IPAddressDeny = "any";
|
||||||
|
LockPersonality = true;
|
||||||
|
NoNewPrivileges = "no";
|
||||||
|
PrivateDevices = false;
|
||||||
|
PrivateMounts = false;
|
||||||
|
PrivateTmp = false;
|
||||||
|
PrivateUsers = false;
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectControlGroups = false; # implies MountAPIVFS
|
||||||
|
ProtectHome = false;
|
||||||
|
ProtectHostname = true;
|
||||||
|
ProtectKernelLogs = false;
|
||||||
|
ProtectKernelModules = false;
|
||||||
|
ProtectKernelTunables = false; # implies MountAPIVFS
|
||||||
|
#ProtectProc = "noaccess"; # implies MountAPIVFS
|
||||||
|
ProtectSystem = false;
|
||||||
|
RestrictAddressFamilies = "none";
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
"@mount"
|
||||||
|
"@setuid"
|
||||||
|
"umount2"
|
||||||
|
];
|
||||||
|
User = "jellyfin";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
112
hosts/iron/services/jellyfin/rar2fs_mounts.py
Normal file
112
hosts/iron/services/jellyfin/rar2fs_mounts.py
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
from pathlib import Path
|
||||||
|
import argparse
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
mounts = {}
|
||||||
|
|
||||||
|
|
||||||
|
class RarMount:
|
||||||
|
process = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mountpoint(self):
|
||||||
|
result = self.mount_root / self.rar_file.relative_to(self.rar_root).parent
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __init__(self, mount_root: str, rar_file: Path, rar_root: Path):
|
||||||
|
self.mount_root = mount_root
|
||||||
|
self.rar_file = rar_file
|
||||||
|
self.rar_root = rar_root
|
||||||
|
|
||||||
|
os.makedirs(self.mountpoint, exist_ok=True)
|
||||||
|
|
||||||
|
print(f"Mounting '{self.rar_file}' at '{self.mountpoint}'")
|
||||||
|
self.process = subprocess.Popen(
|
||||||
|
[
|
||||||
|
"rar2fs",
|
||||||
|
"-f",
|
||||||
|
"-o",
|
||||||
|
"auto_unmount",
|
||||||
|
"-o",
|
||||||
|
"allow_other",
|
||||||
|
"--no-inherit-perm",
|
||||||
|
self.rar_file,
|
||||||
|
self.mountpoint,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.process:
|
||||||
|
self.process.terminate()
|
||||||
|
self.process.communicate()
|
||||||
|
|
||||||
|
for i in range(10):
|
||||||
|
try:
|
||||||
|
os.rmdir(self.mountpoint)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
except OSError as ex:
|
||||||
|
# if ex.errno == errno.ENOEMPTY:
|
||||||
|
# break
|
||||||
|
if ex.errno == errno.EBUSY:
|
||||||
|
time.sleep(1)
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
for dir in self.mountpoint.relative_to(self.mount_root).parents:
|
||||||
|
try:
|
||||||
|
os.rmdir(self.mount_root.joinpath(dir))
|
||||||
|
except OSError as ex:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
for rar_file, mount in mounts.items():
|
||||||
|
del mount
|
||||||
|
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Recursively globs a path containing rar files and mounts them under a given mount path."
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument("rar_path", type=Path, help="Path to the RAR directory")
|
||||||
|
|
||||||
|
parser.add_argument("mount_path", type=Path, help="Path to the mount directory")
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
if not args.rar_path.is_dir():
|
||||||
|
parser.error(f"RAR path '{args.rar_path}' is not a valid directory.")
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
for rar_file in args.rar_path.rglob("*.rar"):
|
||||||
|
if rar_file in mounts:
|
||||||
|
continue
|
||||||
|
if len(rar_file.parts) >= 2 and rar_file.parts[-2].lower() in ["subs", "proof"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
mounts[rar_file] = RarMount(args.mount_path, rar_file, args.rar_path)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
time.sleep(600)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
{
|
{
|
||||||
nixpkgs.config.allowUnfreePredicate = pkg: lib.elem (lib.getName pkg) [
|
nixpkgs.config.allowUnfreePredicate = pkg: lib.elem (lib.getName pkg) [
|
||||||
"mongodb"
|
"mongodb"
|
||||||
|
"rar2fs"
|
||||||
"roomeqwizard"
|
"roomeqwizard"
|
||||||
"unifi-controller"
|
"unifi-controller"
|
||||||
|
"unrar"
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue