Browse Source

MacOSRamDisk: configure size in cfg file and at runtime

The MMGen Project 6 months ago
parent
commit
14f4839447
5 changed files with 54 additions and 4 deletions
  1. 19 2
      mmgen/autosign.py
  2. 2 0
      mmgen/cfg.py
  3. 5 0
      mmgen/data/mmgen.cfg
  4. 5 0
      mmgen/main_autosign.py
  5. 23 2
      mmgen/platform/darwin/util.py

+ 19 - 2
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():

+ 2 - 0
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',

+ 5 - 0
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 ##
 ############################

+ 5 - 0
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

+ 23 - 2
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)