Merge remote-tracking branch 'origin/main' into fix/mic-input

This commit is contained in:
lucannez64 committed 2026-10-19 00:55:17 +02:00
commit 01fdb0f28f
7 files changed
+119 -14

No files matched your search

+16 -7
View File
@@ -1,9 +1,12 @@
# Explicit platform identity overrides
By default, Darling keeps the existing host-derived UUID and `DARLING-` serial.
No override file is generated by installation or launch. Import mode uses values supplied by the user. Explicit `--provision` mode can
generate synthetic identifiers; it does not issue authentic Apple identities
or change CPU, GPU, or security metadata.
First-time setup (`scripts/install-runtime.sh` creating a new prefix) runs
`--provision --write` once, so each new prefix gets its own synthetic identity.
A prefix without saved identity files keeps the host-derived UUID and
`DARLING-` serial; launching never generates an override. Import mode uses
values supplied by the user. `--provision` generates synthetic identifiers; it
does not issue authentic Apple identities or change CPU, GPU, or security
metadata.
Preview an OpenCore `config.plist`, a flat GenSMBIOS JSON file, or an existing
`darling-smbios.conf` without changing the selected prefix:
@@ -89,8 +92,13 @@ python3 tools/configure-darling-platform.py --prefix /absolute/prefix --provisio
```
Launch Darling normally afterwards. This requires the updated iokitd from this
PR; the config tool alone cannot update an older running daemon. Provisioning
is never called automatically by installation or launch.
PR; the config tool alone cannot update an older running daemon. Besides
first-time setup above, provisioning runs automatically in one other case:
after installing, `scripts/update.sh` runs
`configure-darling-platform.py --replace-known-defaults --write`, which
provisions the prefix only if its saved identity still contains the shared
serial or UUID below. Prefixes without saved identity files, or whose saved
values are all custom, are left byte-for-byte unchanged.
Missing serials use 12 characters and missing MLBs use 17 characters, sampled
with Python's `secrets` module from `0123456789ABCDEFGHJKLMNPQRSTUVWXYZ` (Base-34).
@@ -106,7 +114,8 @@ example with your chosen unicast OUI). It only affects a missing ROM. An OUI
does not make a generated identity Apple-issued.
Provisioning replaces the known shared serial `C02ZL0GVMD6N` and UUID
`6F3B2A1E-8C4D-4E5F-9A7B-1D2C3E4F5A6B`, along with the latter's derived fields.
`6F3B2A1E-8C4D-4E5F-9A7B-1D2C3E4F5A6B`, along with the latter's derived fields
(including a standalone `system-id` or `provisioning_UDID` derived from it).
It preserves other existing custom values, including imported serials and UUIDs
that do not follow the generated formats. A standalone custom `system-id` is
preserved by deriving its UUID instead of generating a different one. Model,
+9
View File
@@ -16,6 +16,9 @@ if [ -d build/venv/bin ]; then PATH="$repo/build/venv/bin:$PATH"; export PATH; f
# A server killed by the base install leaves a stale .init.pid that makes darling shell fail.
python3 build/runtime/tools/ensure-darling-runtime.py
# Initialize the normal user-owned prefix before installing overrides.
# Darling treats a missing prefix directory as new; only then is an identity generated below.
new_prefix=
[ -e "$prefix_dir" ] || new_prefix=1
darling shell /usr/bin/true
# The prefix has no /private/tmp of its own, so the guest falls through to the root-owned
# installed layer and every write there (the Riot installer's temp files) fails with EACCES.
@@ -26,6 +29,12 @@ chmod 1777 "$prefix/private/tmp"
python3 build/runtime/tools/ensure-darling-root-certificates.py || \
echo 'Darling system root store not installed; the launcher retries it' >&2
python3 build/runtime/tools/stop-darling-runtime.py
# Give a new prefix its own synthetic serial, MLB, UUID and ROM instead of the host-derived defaults.
# Existing prefixes keep their identity; update.sh only replaces the old shared serial and UUID.
if [ -n "$new_prefix" ]; then
python3 tools/configure-darling-platform.py --prefix "$prefix_dir" --provision --write || \
echo 'Platform identity not provisioned; run tools/configure-darling-platform.py --provision --write' >&2
fi
sh build/runtime/tools/install-riot-presentation-runtime.sh
sh build/runtime/tools/install-riot-gpu-runtime.sh
# darling-lol perf: build and install optimized libc/libc++/libplatform.
+9
View File
@@ -7,6 +7,13 @@
set -eu
repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
cd "$repo"
# Older setups shipped one serial and UUID shared by every install. Give a prefix that still
# has them its own identity; any other saved values, and prefixes without them, are left alone.
replace_shared_identity() {
python3 build/runtime/tools/stop-darling-runtime.py
python3 tools/configure-darling-platform.py --replace-known-defaults --write || \
echo 'Could not replace the shared platform identity; run tools/configure-darling-platform.py --provision --write' >&2
}
if [ -n "${DARLING_WORKSPACE:-}" ]; then
python3 scripts/nix-idle.py
# Initial setup and upgrades use the same supported user-owned install path.
@@ -21,6 +28,7 @@ if [ -n "${DARLING_WORKSPACE:-}" ]; then
cmake --install build/darling-src/build
python3 scripts/nix-state.py stamp-base
./scripts/install-runtime.sh
replace_shared_identity
echo 'Rootless update installed. In this shell use scripts/run.sh; otherwise exit this shell and start darling-lol.'
exit 0
fi
@@ -45,4 +53,5 @@ PY
python3 build/runtime/tools/stop-darling-runtime.py
sudo cmake --install build/darling-src/build
./scripts/install-runtime.sh
replace_shared_identity
echo 'Update installed. Start Darling with: sh scripts/run.sh'
+13 -1
View File
@@ -91,6 +91,17 @@ class InGameCloseTests(unittest.TestCase):
self.assertIn('closed the client UI for the running match (2 processes: 123,124)',
self.output.getvalue())
def test_match_soon_after_boot_still_closes_the_client_ui(self):
# time.monotonic() counts from boot; a fresh machine (or CI runner) is
# still inside the first cooldown window.
self.client_ui(123)
self.process(125, GAME)
with patch.object(in_game.time, 'monotonic', return_value=5.0):
result, calls = self.run_once()
self.assertEqual(result, 0)
self.assertEqual(sorted(calls), sorted([(123, in_game.signal.SIGTERM),
(123, in_game.signal.SIGKILL)]))
def test_marker_in_arguments_does_not_make_a_process_client_ui(self):
self.client_ui(123)
self.process(124, CRASH_HANDLER, args=CRASH_ARGUMENTS)
@@ -209,7 +220,8 @@ class InGameCloseTests(unittest.TestCase):
def test_close_decision_covers_limits_and_cooldown(self):
state = {'game': True, 'ui': {'123': 1}}
self.assertEqual(in_game.close_decision(state, 0, 0.0, 100.0), 'close')
self.assertEqual(in_game.close_decision(state, 0, None, 100.0), 'close')
self.assertEqual(in_game.close_decision(state, 0, None, 5.0), 'close')
self.assertIsNone(in_game.close_decision(state, 0, 90.0, 100.0))
self.assertEqual(in_game.close_decision(state, in_game.MAX_CLOSES, 0.0, 100.0), 'limit')
self.assertIsNone(in_game.close_decision({'game': False, 'ui': {'123': 1}}, 0, 0.0, 100.0))
+40
View File
@@ -157,3 +157,43 @@ class ProvisioningChecks(unittest.TestCase):
self.assertEqual(platform.provision(supplied)['IOPlatformUUID'], value['IOPlatformUUID'])
with self.assertRaises(ValueError):
platform.validate({'IOPlatformUUID': value['IOPlatformUUID'], 'provisioning_UDID': str(uuid.uuid4())})
def test_replace_known_defaults_only_touches_shared_identities(self):
replace = [sys.executable, str(SCRIPT), '--prefix', str(self.prefix), '--runtime-root',
str(self.runtime), '--replace-known-defaults', '--write']
# A prefix without identity files is not created or provisioned.
result = subprocess.run(replace, capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertFalse(self.prefix.exists())
# A custom identity stays byte-for-byte unchanged.
custom = {'IOPlatformSerialNumber': 'MY-SERIAL', 'model': 'CustomModel'}
platform.save(self.prefix, custom)
canonical = platform.destination(self.prefix)
before = canonical.read_bytes()
result = subprocess.run(replace, capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(canonical.read_bytes(), before)
self.assertFalse((self.prefix / 'private/etc/darling-smbios.conf').exists())
# The shared values are replaced; custom fields survive and a rerun is stable.
platform.save(self.prefix, {**custom, 'IOPlatformSerialNumber': platform.KNOWN_SERIAL,
'IOPlatformUUID': platform.KNOWN_UUID})
result = subprocess.run(replace, capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
value = platform.load(canonical)
self.assertNotEqual(value['IOPlatformSerialNumber'], platform.KNOWN_SERIAL)
self.assertNotEqual(value['IOPlatformUUID'], platform.KNOWN_UUID)
self.assertFalse(platform.has_known_defaults(value))
self.assertEqual(value['model'], 'CustomModel')
self.assertNotIn('board-id', value)
self.assertNotIn('manufacturer', value)
self.assertEqual(platform.load(self.prefix / 'private/etc/darling-smbios.conf'), value)
after = canonical.read_bytes()
subprocess.run(replace, capture_output=True, text=True, check=True)
self.assertEqual(canonical.read_bytes(), after)
def test_standalone_fields_derived_from_known_uuid_are_replaced(self):
known_id = uuid.UUID(platform.KNOWN_UUID).bytes_le
for supplied in [{'system-id': known_id}, {'provisioning_UDID': platform.KNOWN_UUID}]:
value = platform.provision(supplied)
self.assertNotEqual(value['IOPlatformUUID'], platform.KNOWN_UUID)
self.assertNotEqual(value['system-id'], known_id)
+5 -3
View File
@@ -160,7 +160,9 @@ def close_decision(state, closes, last_close, now):
return None
if closes >= MAX_CLOSES:
return 'limit'
if now - last_close < COOLDOWN_SECONDS:
# last_close is None until the first close: monotonic time counts from boot,
# so a 0.0 start would block closes for the first COOLDOWN_SECONDS of uptime.
if last_close is not None and now - last_close < COOLDOWN_SECONDS:
return None
return 'close'
@@ -189,7 +191,7 @@ def supervise(proc_root=Path('/proc'), prefix=None, once=False, dry_run=False, o
'opt out with RCLOL_CLOSE_CLIENT_IN_GAME=0' % prefix, file=out, flush=True)
in_game = False
closes = 0
last_close = 0.0
last_close = None
limit_reported = False
seen = False
idle_since = None
@@ -203,7 +205,7 @@ def supervise(proc_root=Path('/proc'), prefix=None, once=False, dry_run=False, o
if state['game'] and not in_game:
in_game = True
closes = 0
last_close = 0.0
last_close = None
limit_reported = False
print('close-league-client-in-game: a match started', file=out, flush=True)
elif not state['game'] and in_game:
+27 -3
View File
@@ -182,13 +182,23 @@ def save(prefix, properties):
return atomic_write(destination(prefix), encoded)
def has_known_defaults(properties):
"""True when an identity still carries the shared serial, UUID, or a field derived from that UUID."""
return (properties.get('IOPlatformSerialNumber') == KNOWN_SERIAL
or str(properties.get('IOPlatformUUID', '')).upper() == KNOWN_UUID
or str(properties.get('provisioning_UDID', '')).upper() == KNOWN_UUID
or properties.get('system-id') == uuid.UUID(KNOWN_UUID).bytes_le)
def provision(properties, rom_oui=None):
"""Fill missing identifiers, replacing only the two known shared defaults."""
result = validate(properties)
if result.get('IOPlatformSerialNumber') == KNOWN_SERIAL:
result.pop('IOPlatformSerialNumber')
if result.get('IOPlatformUUID', '').upper() == KNOWN_UUID:
result.pop('IOPlatformUUID')
if (result.get('IOPlatformUUID', '').upper() == KNOWN_UUID
or result.get('provisioning_UDID', '').upper() == KNOWN_UUID
or result.get('system-id') == uuid.UUID(KNOWN_UUID).bytes_le):
result.pop('IOPlatformUUID', None)
result.pop('system-id', None)
result.pop('provisioning_UDID', None)
for key, length in [('IOPlatformSerialNumber', 12), ('mlb-serial-number', 17)]:
@@ -274,8 +284,14 @@ def main(argv=None):
parser.add_argument('--provision', action='store_true', help='Generate missing synthetic identifiers and provision prefix metadata')
parser.add_argument('--runtime-root', type=Path, help='Runtime supplying SystemVersion.plist (default: saved SDK or /usr/local/libexec/darling)')
parser.add_argument('--rom-oui', help='Explicit three-byte unicast OUI for a missing ROM; default is locally administered')
parser.add_argument('--replace-known-defaults', action='store_true',
help='Provision only if the prefix identity still has the shared serial or UUID; otherwise change nothing')
parser.add_argument('--write', action='store_true', help='Write validated overrides to a stopped prefix (otherwise preview only)')
args = parser.parse_args(argv)
if args.replace_known_defaults:
if args.source or args.set:
parser.error('--replace-known-defaults cannot be combined with --import or --set')
args.provision = True
config = Path(__file__).resolve().parents[1] / 'build/config.json'
saved = json.loads(config.read_text()) if config.exists() else {}
prefix = args.prefix or Path(os.environ.get('DPREFIX') or saved.get('prefix', str(Path.home() / '.darling')))
@@ -288,11 +304,19 @@ def main(argv=None):
rom_oui = bytes.fromhex(args.rom_oui.replace(':', '').replace('-', ''))
if len(rom_oui) != 3 or rom_oui[0] & 1:
raise ValueError('--rom-oui requires exactly three unicast bytes')
identity_files = [checked_path(prefix, 'private/etc/darling-smbios.conf'), destination(prefix)]
if args.replace_known_defaults and not any(path.exists() for path in identity_files):
print('No saved platform identity; nothing to replace.')
return 0
with prefix_lock(prefix) if args.write else nullcontext():
path = destination(prefix)
properties = {}
if args.replace_known_defaults and not any(
path.exists() and has_known_defaults(load(path)) for path in identity_files):
print('Platform identity has no shared defaults; nothing changed.')
return 0
if args.provision:
legacy = checked_path(prefix, 'private/etc/darling-smbios.conf')
legacy = identity_files[0]
if legacy.exists():
properties.update(load(legacy))
if path.exists():