From 14f4839447946d28b8ebb9b256238799069619f4 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 4 Sep 2024 13:08:51 +0000 Subject: [PATCH] MacOSRamDisk: configure size in cfg file and at runtime --- mmgen/autosign.py | 21 +++++++++++++++++++-- mmgen/cfg.py | 2 ++ mmgen/data/mmgen.cfg | 5 +++++ mmgen/main_autosign.py | 5 +++++ mmgen/platform/darwin/util.py | 25 +++++++++++++++++++++++-- 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/mmgen/autosign.py b/mmgen/autosign.py index f6b0d5b0..691c195a 100755 --- a/mmgen/autosign.py +++ b/mmgen/autosign.py @@ -18,9 +18,10 @@ from pathlib import Path from subprocess import run, PIPE, DEVNULL from .cfg import Config -from .util import msg, msg_r, ymsg, rmsg, gmsg, bmsg, die, suf, fmt, fmt_list +from .util import msg, msg_r, ymsg, rmsg, gmsg, bmsg, die, suf, fmt, fmt_list, is_int from .color import yellow,red,orange,brown from .wallet import Wallet,get_wallet_cls +from .addrlist import AddrIdxList from .filename import find_file_in_dir from .ui import keypress_confirm @@ -399,7 +400,7 @@ class Autosign: self.ramdisk = MacOSRamDisk( cfg, self.macOS_ramdisk_name, - 10, + self._get_macOS_ramdisk_size(), path = self.shm_dir) self.keyfile = self.mountpoint / 'autosign.key' @@ -611,6 +612,22 @@ class Autosign: if not no_unmount: self.do_umount() + def _get_macOS_ramdisk_size(self): + from .platform.darwin.util import MacOSRamDisk, warn_ramdisk_too_small + # allow 1MB for each Monero wallet + xmr_size = len(AddrIdxList(self.cfg.xmrwallets)) if self.cfg.xmrwallets else 0 + calc_size = xmr_size + 1 + usr_size = self.cfg.macos_ramdisk_size or self.cfg.macos_autosign_ramdisk_size + if is_int(usr_size): + usr_size = int(usr_size) + else: + die(1, f'{usr_size}: invalid user-specified macOS ramdisk size (not an integer)') + min_size = MacOSRamDisk.min_size + size = max(usr_size, calc_size, min_size) + if usr_size and usr_size < min_size: + warn_ramdisk_too_small(usr_size, min_size) + return size + def setup(self): def remove_wallet_dir(): diff --git a/mmgen/cfg.py b/mmgen/cfg.py index 780263ba..c0e3092d 100755 --- a/mmgen/cfg.py +++ b/mmgen/cfg.py @@ -145,6 +145,7 @@ class Config(Lockable): max_input_size = 1024 * 1024 min_urandchars = 10 max_urandchars = 80 + macos_autosign_ramdisk_size = 10 # see MacOSRamDisk # debug debug = False @@ -255,6 +256,7 @@ class Config(Lockable): 'force_256_color', 'hash_preset', 'http_timeout', + 'macos_autosign_ramdisk_size', 'max_input_size', 'max_tx_file_size', 'mnemonic_entry_modes', diff --git a/mmgen/data/mmgen.cfg b/mmgen/data/mmgen.cfg index 55ad2ead..b613ef77 100644 --- a/mmgen/data/mmgen.cfg +++ b/mmgen/data/mmgen.cfg @@ -84,6 +84,11 @@ # When change addresses are chosen manually the option is ignored: # autochg_ignore_labels true +# Set the size in MB of the ramdisk used to store the temporary offline +# autosign wallet(s) on macOS machines. This option is of interest only for +# setups with unusually large Monero wallets: +# macos_autosign_ramdisk_size 10 + ############################ ## Ignore daemon versions ## ############################ diff --git a/mmgen/main_autosign.py b/mmgen/main_autosign.py index 76285d81..8d621b30 100755 --- a/mmgen/main_autosign.py +++ b/mmgen/main_autosign.py @@ -41,6 +41,11 @@ opts_data = { -M, --mnemonic-fmt=F During setup, prompt for mnemonic seed phrase of format 'F' (choices: {mn_fmts}; default: {asi.dfl_mn_fmt!r}) -n, --no-summary Don’t print a transaction summary +-r, --macos-ramdisk-size=S Set the size (in MB) of the ramdisk used to store + the offline signing wallet(s) on macOS machines. By + default, a runtime-calculated value will be used. This + option is of interest only for setups with unusually + large Monero wallets -s, --stealth-led Stealth LED mode - signal busy and error only, and only after successful authorization. -S, --full-summary Print a full summary of each signed transaction after diff --git a/mmgen/platform/darwin/util.py b/mmgen/platform/darwin/util.py index ae7662f0..09aeb2c4 100755 --- a/mmgen/platform/darwin/util.py +++ b/mmgen/platform/darwin/util.py @@ -16,6 +16,7 @@ from pathlib import Path from subprocess import run, PIPE, DEVNULL from ...obj import MMGenLabel +from ...util import oneshot_warning def get_device_size(path_or_label): import re @@ -27,6 +28,12 @@ def get_device_size(path_or_label): assert m, f'{errmsg}:\n{res[0]}' return int(m[1]) +class warn_ramdisk_too_small(oneshot_warning): + message = 'requested ramdisk size ({}MB) too small, using {}MB instead' + color = 'yellow' + def __init__(self, usr_size, min_size): + oneshot_warning.__init__(self, div=usr_size, fmt_args=[usr_size, min_size]) + class RamDiskLabel(MMGenLabel): max_len = 24 desc = 'ramdisk label' @@ -34,8 +41,12 @@ class RamDiskLabel(MMGenLabel): class MacOSRamDisk: desc = 'ramdisk' + min_size = 10 # 10MB is the minimum supported by hdiutil def __init__(self, cfg, label, size, path=None): + if size < self.min_size: + warn_ramdisk_too_small(size, self.min_size) + size = self.min_size self.cfg = cfg self.label = RamDiskLabel(label) self.size = size # size in MiB @@ -45,16 +56,26 @@ class MacOSRamDisk: def exists(self): return self.path.is_mount() + def get_diskutil_size(self): + return get_device_size(self.label) // (2**20) + def create(self, quiet=False): redir = DEVNULL if quiet else None if self.exists(): - self.cfg._util.qmsg('{} {} [{}] already exists'.format(self.desc, self.label.hl(), self.path)) - return + diskutil_size = self.get_diskutil_size() + if diskutil_size != self.size: + self.cfg._util.qmsg(f'Existing ramdisk has incorrect size {diskutil_size}MB, deleting') + self.destroy() + else: + self.cfg._util.qmsg(f'{self.desc.capitalize()} {self.label.hl()} at path {self.path} already exists') + return self.cfg._util.qmsg(f'Creating {self.desc} {self.label.hl()} of size {self.size}MB') cp = run(['hdiutil', 'attach', '-nomount', f'ram://{2048 * self.size}'], stdout=PIPE, check=True) self.dev_name = cp.stdout.decode().strip() self.cfg._util.qmsg(f'Created {self.desc} {self.label.hl()} [{self.dev_name}]') run(['diskutil', 'eraseVolume', 'APFS', self.label, self.dev_name], stdout=redir, check=True) + diskutil_size = self.get_diskutil_size() + assert diskutil_size == self.size, 'Reported ramdisk size {diskutil_size}MB is incorrect!' if self.path != self.dfl_path: run(['diskutil', 'umount', self.label], stdout=redir, check=True) self.path.mkdir(parents=True, exist_ok=True)