Commit ac536f85 authored by rgaudin's avatar rgaudin

Added --root for CLI on linux to use losetup instead of udisks (for docker)

parent 3f7c2baa
Pipeline #100466 failed with stage
in 337 minutes and 42 seconds
2.0.3
* Added --root for CLI on linux to use losetup instead of udisks (for docker)
2.0.2 2.0.2
* Better etcher-cli calling and on-error logging on macOS via GUI * Better etcher-cli calling and on-error logging on macOS via GUI
* Added `wipe --sdcard /dev/xxx` CLI command * Added `wipe --sdcard /dev/xxx` CLI command
......
...@@ -42,6 +42,9 @@ elif sys.platform == "linux": ...@@ -42,6 +42,9 @@ elif sys.platform == "linux":
udisksctl_exe = "/usr/bin/udisksctl" udisksctl_exe = "/usr/bin/udisksctl"
udisks_nou = "--no-user-interaction" udisks_nou = "--no-user-interaction"
mkfs_exe = "/sbin/mkfs.exfat" mkfs_exe = "/sbin/mkfs.exfat"
umount_exe = "/bin/umount"
mount_exe = "/bin/mount"
losetup_exe = "/sbin/losetup"
elif sys.platform == "darwin": elif sys.platform == "darwin":
hdiutil_exe = "/usr/bin/hdiutil" hdiutil_exe = "/usr/bin/hdiutil"
diskutil_exe = "/usr/sbin/diskutil" diskutil_exe = "/usr/sbin/diskutil"
...@@ -190,6 +193,21 @@ def restore_mode(fpath, mode, logger): ...@@ -190,6 +193,21 @@ def restore_mode(fpath, mode, logger):
def guess_next_loop_device(logger): def guess_next_loop_device(logger):
def _no_udisks(logger):
""" guess loop device w/o udisks (using losetup/root) """
try:
lines = subprocess_pretty_call(
[losetup_exe, "--find"], logger, check=True, decode=True
)
except Exception as exp:
logger.err(exp)
return None
return re.search(r"(\/dev\/loop[0-9]+)$", lines[-1].strip()).groups()[0]
if bool(os.getenv("NO_UDISKS", False)):
return _no_udisks(logger)
try: try:
lines = subprocess_pretty_call( lines = subprocess_pretty_call(
[udisksctl_exe, "dump"], logger, check=True, decode=True [udisksctl_exe, "dump"], logger, check=True, decode=True
...@@ -286,26 +304,41 @@ def get_virtual_device(image_fpath, logger): ...@@ -286,26 +304,41 @@ def get_virtual_device(image_fpath, logger):
) )
# prepare loop device # prepare loop device
udisks_loop = subprocess_pretty_call( if bool(os.getenv("NO_UDISKS", False)):
[ loop_maker = subprocess_pretty_call(
udisksctl_exe, [
"loop-setup", "/sbin/losetup",
"--offset", "--offset",
str(offset), str(offset),
"--size", "--sizelimit",
str(size), str(size),
"--file", "--find",
image_fpath, "--show",
udisks_nou, image_fpath,
], ],
logger, logger,
check=True, check=True,
decode=True, decode=True,
)[0].strip() )[0].strip()
else:
target_dev = re.search(r"(\/dev\/loop[0-9]+)\.$", udisks_loop).groups()[0] loop_maker = subprocess_pretty_call(
[
udisksctl_exe,
"loop-setup",
"--offset",
str(offset),
"--size",
str(size),
"--file",
image_fpath,
udisks_nou,
],
logger,
check=True,
decode=True,
)[0].strip()
return target_dev target_dev = re.search(r"(\/dev\/loop[0-9]+)\.?$", loop_maker).groups()[0]
elif sys.platform == "darwin": elif sys.platform == "darwin":
# attach image to create loop devices # attach image to create loop devices
...@@ -317,8 +350,6 @@ def get_virtual_device(image_fpath, logger): ...@@ -317,8 +350,6 @@ def get_virtual_device(image_fpath, logger):
)[0].strip() )[0].strip()
target_dev = str(hdiutil_out.splitlines()[0].split()[0]) target_dev = str(hdiutil_out.splitlines()[0].split()[0])
return target_dev
elif sys.platform == "win32": elif sys.platform == "win32":
# make sure we have imdisk installed # make sure we have imdisk installed
install_imdisk(logger) install_imdisk(logger)
...@@ -326,7 +357,16 @@ def get_virtual_device(image_fpath, logger): ...@@ -326,7 +357,16 @@ def get_virtual_device(image_fpath, logger):
# get an available letter # get an available letter
target_dev = get_avail_drive_letter(logger) target_dev = get_avail_drive_letter(logger)
return target_dev return target_dev
def release_virtual_device(device, logger):
if bool(os.getenv("NO_UDISKS", False)):
subprocess_pretty_call([losetup_exe, "--detach", device], logger)
else:
subprocess_pretty_call(
[udisksctl_exe, "loop-delete", "--block-device", device, udisks_nou], logger
)
def format_data_partition(image_fpath, logger): def format_data_partition(image_fpath, logger):
...@@ -335,10 +375,15 @@ def format_data_partition(image_fpath, logger): ...@@ -335,10 +375,15 @@ def format_data_partition(image_fpath, logger):
target_dev = get_virtual_device(image_fpath, logger) target_dev = get_virtual_device(image_fpath, logger)
if sys.platform == "linux": if sys.platform == "linux":
# make sure it's not mounted (gnoe automounts)
subprocess_pretty_call( # make sure it's not mounted (gnome automounts)
[udisksctl_exe, "unmount", "--block-device", target_dev, udisks_nou], logger if bool(os.getenv("NO_UDISKS", False)):
) subprocess_pretty_call([umount_exe, target_dev], logger)
else:
subprocess_pretty_call(
[udisksctl_exe, "unmount", "--block-device", target_dev, udisks_nou],
logger,
)
# change mode via elevation if we can't format it # change mode via elevation if we can't format it
previous_mode = None previous_mode = None
...@@ -399,7 +444,21 @@ def mount_data_partition(image_fpath, logger): ...@@ -399,7 +444,21 @@ def mount_data_partition(image_fpath, logger):
target_dev = get_virtual_device(image_fpath, logger) target_dev = get_virtual_device(image_fpath, logger)
if sys.platform == "linux": if sys.platform == "linux" and bool(os.getenv("NO_UDISKS", False)):
# create a mount point in /tmp
mount_point = tempfile.mkdtemp()
try:
subprocess_pretty_check_call(
[mount_exe, "-t", "exfat", target_dev, mount_point], logger
)
except Exception:
# ensure we release the loop device on mount failure
unmount_data_partition(mount_point, target_dev, logger)
raise
return mount_point, target_dev
elif sys.platform == "linux":
# mount the loop-device (udisksctl sets the mount point) # mount the loop-device (udisksctl sets the mount point)
udisks_mount_ret, udisks_mount = subprocess_pretty_call( udisks_mount_ret, udisks_mount = subprocess_pretty_call(
[udisksctl_exe, "mount", "--block-device", target_dev, udisks_nou], [udisksctl_exe, "mount", "--block-device", target_dev, udisks_nou],
...@@ -416,6 +475,7 @@ def mount_data_partition(image_fpath, logger): ...@@ -416,6 +475,7 @@ def mount_data_partition(image_fpath, logger):
# udisksctl always mounts under /media/ # udisksctl always mounts under /media/
mount_point = re.search(r"at (\/media\/.+)\.$", udisks_mount).groups()[0] mount_point = re.search(r"at (\/media\/.+)\.$", udisks_mount).groups()[0]
else: else:
release_virtual_device(target_dev, logger) # release loop if attached
raise OSError("failed to mount {}".format(target_dev)) raise OSError("failed to mount {}".format(target_dev))
return mount_point, target_dev return mount_point, target_dev
...@@ -469,17 +529,20 @@ def unmount_data_partition(mount_point, device, logger): ...@@ -469,17 +529,20 @@ def unmount_data_partition(mount_point, device, logger):
if mount_point: if mount_point:
# unmount using device path # unmount using device path
subprocess_pretty_call( if bool(os.getenv("NO_UDISKS", False)):
[udisksctl_exe, "unmount", "--block-device", device, udisks_nou], logger subprocess_pretty_call([umount_exe, device], logger)
) else:
subprocess_pretty_call(
[udisksctl_exe, "unmount", "--block-device", device, udisks_nou],
logger,
)
try: try:
os.rmdir(mount_point) os.rmdir(mount_point)
except (FileNotFoundError, PermissionError): except (FileNotFoundError, PermissionError):
pass pass
# delete the loop device (might have already been deletec)
subprocess_pretty_call( # delete the loop device (might have already been deleted)
[udisksctl_exe, "loop-delete", "--block-device", device, udisks_nou], logger release_virtual_device(device, logger)
)
elif sys.platform == "darwin": elif sys.platform == "darwin":
......
...@@ -7,7 +7,7 @@ import sys ...@@ -7,7 +7,7 @@ import sys
from backend.mount import system_has_exfat from backend.mount import system_has_exfat
if sys.platform == "linux": if sys.platform == "linux":
from backend.mount import udisksctl_exe from backend.mount import udisksctl_exe, losetup_exe
requirements_url = "https://github.com/kiwix/kiwix-hotspot/wiki/System-Requirements" requirements_url = "https://github.com/kiwix/kiwix-hotspot/wiki/System-Requirements"
...@@ -27,8 +27,16 @@ def host_matches_requirements(build_dir): ...@@ -27,8 +27,16 @@ def host_matches_requirements(build_dir):
if sys.platform == "linux": if sys.platform == "linux":
# udisks2 # udisks2
if not os.path.exists(udisksctl_exe) or not os.access(udisksctl_exe, os.X_OK): if bool(os.getenv("NO_UDISKS", False)):
missing_reqs.append("udisks2 (udisksctl) is required.") if not os.path.exists(losetup_exe) or not os.access(losetup_exe, os.X_OK):
missing_reqs.append(
"losetup (mount) is required when excluding udisks2."
)
else:
if not os.path.exists(udisksctl_exe) or not os.access(
udisksctl_exe, os.X_OK
):
missing_reqs.append("udisks2 (udisksctl) is required.")
# exfat # exfat
mount_exfat = "/sbin/mount.exfat" mount_exfat = "/sbin/mount.exfat"
......
...@@ -27,7 +27,7 @@ from util import get_adjusted_image_size ...@@ -27,7 +27,7 @@ from util import get_adjusted_image_size
from backend.catalog import get_catalogs from backend.catalog import get_catalogs
from run_installation import run_installation from run_installation import run_installation
from util import human_readable_size, get_cache from util import human_readable_size, get_cache
from backend.util import sd_has_single_partition from backend.util import sd_has_single_partition, is_admin
import tzlocal import tzlocal
import humanfriendly import humanfriendly
...@@ -200,9 +200,22 @@ parser.add_argument( ...@@ -200,9 +200,22 @@ parser.add_argument(
parser.add_argument("--filename", help="Output file name (without suffix)") parser.add_argument("--filename", help="Output file name (without suffix)")
parser.add_argument("--ram", help="Max RAM for QEMU", default="2G") parser.add_argument("--ram", help="Max RAM for QEMU", default="2G")
parser.add_argument("--sdcard", help="Device to copy image to") parser.add_argument("--sdcard", help="Device to copy image to")
parser.add_argument(
"--root",
action="store_true",
help="Don't use udisks2 (linux-only, must be ran as root)",
)
args = parser.parse_args() args = parser.parse_args()
# handle root option (disable udisks use)
if args.root:
if not is_admin():
print("You must be root/elevated to use --root option")
sys.exit(1)
else:
os.environ["NO_UDISKS"] = "yes"
# apply options from config file if requested # apply options from config file if requested
if args.config: if args.config:
try: try:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment