mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-04-13 20:28:21 +10:00
subprocess_wrapper.py: Add unified error handling
Additionally adds backend support for Privileged Helper Tool
This commit is contained in:
@@ -11,6 +11,8 @@ import subprocess
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from . import subprocess_wrapper
|
||||
|
||||
from .. import constants
|
||||
|
||||
from ..wx_gui import gui_entry
|
||||
@@ -172,7 +174,7 @@ class arguments:
|
||||
if "GPUCompanionBundles" not in kext_plist:
|
||||
continue
|
||||
logging.info(f" - Removing {kext.name}")
|
||||
subprocess.run(["/bin/rm", "-rf", kext])
|
||||
subprocess_wrapper.run_as_root(["/bin/rm", "-rf", kext])
|
||||
|
||||
|
||||
def _build_handler(self) -> None:
|
||||
|
||||
@@ -10,7 +10,8 @@ ie. during automated patching
|
||||
import os
|
||||
import logging
|
||||
import plistlib
|
||||
import subprocess
|
||||
|
||||
from . import subprocess_wrapper
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
@@ -115,12 +116,11 @@ class GlobalEnviromentSettings:
|
||||
This in turn breaks normal OCLP execution to write to settings file
|
||||
"""
|
||||
|
||||
if os.geteuid() != 0:
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
return
|
||||
|
||||
# Set file permission to allow any user to write to log file
|
||||
result = subprocess.run(["/bin/chmod", "777", self.global_settings_plist], capture_output=True)
|
||||
result = subprocess_wrapper.run_as_root(["/bin/chmod", "777", self.global_settings_plist], capture_output=True)
|
||||
if result.returncode != 0:
|
||||
logging.warning("Failed to fix settings file permissions:")
|
||||
if result.stderr:
|
||||
logging.warning(result.stderr.decode("utf-8"))
|
||||
subprocess_wrapper.log(result)
|
||||
@@ -9,7 +9,7 @@ import applescript
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from . import utilities
|
||||
from . import utilities, subprocess_wrapper
|
||||
|
||||
from .. import constants
|
||||
|
||||
@@ -92,7 +92,7 @@ class tui_disk_installation:
|
||||
def install_opencore(self, full_disk_identifier: str):
|
||||
# TODO: Apple Script fails in Yosemite(?) and older
|
||||
logging.info(f"Mounting partition: {full_disk_identifier}")
|
||||
if self.constants.detected_os >= os_data.os_data.el_capitan and not self.constants.recovery_status:
|
||||
if self.constants.detected_os >= os_data.os_data.el_capitan and not self.constants.recovery_status and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
try:
|
||||
applescript.AppleScript(f'''do shell script "diskutil mount {full_disk_identifier}" with prompt "OpenCore Legacy Patcher needs administrator privileges to mount this volume." with administrator privileges without altering line endings''').run()
|
||||
except applescript.ScriptError as e:
|
||||
@@ -105,10 +105,10 @@ class tui_disk_installation:
|
||||
logging.info("Please disable Safe Mode and try again.")
|
||||
return
|
||||
else:
|
||||
result = subprocess.run(["/usr/sbin/diskutil", "mount", full_disk_identifier], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
result = subprocess_wrapper.run_as_root(["/usr/sbin/diskutil", "mount", full_disk_identifier], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if result.returncode != 0:
|
||||
logging.info("Mount failed")
|
||||
logging.info(result.stderr.decode())
|
||||
subprocess_wrapper.log(result)
|
||||
return
|
||||
|
||||
partition_info = plistlib.loads(subprocess.run(["/usr/sbin/diskutil", "info", "-plist", full_disk_identifier], stdout=subprocess.PIPE).stdout.decode().strip().encode())
|
||||
|
||||
@@ -18,8 +18,8 @@ from .. import constants
|
||||
from ..datasets import os_data
|
||||
|
||||
from . import (
|
||||
utilities,
|
||||
network_handler
|
||||
network_handler,
|
||||
subprocess_wrapper
|
||||
)
|
||||
|
||||
KDK_INSTALL_PATH: str = "/Library/Developer/KDKs"
|
||||
@@ -464,7 +464,7 @@ class KernelDebugKitObject:
|
||||
if self.passive is True:
|
||||
return
|
||||
|
||||
if os.getuid() != 0:
|
||||
if os.getuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
logging.warning("Cannot remove KDK, not running as root")
|
||||
return
|
||||
|
||||
@@ -474,10 +474,10 @@ class KernelDebugKitObject:
|
||||
|
||||
rm_args = ["/bin/rm", "-rf" if Path(kdk_path).is_dir() else "-f", kdk_path]
|
||||
|
||||
result = utilities.elevated(rm_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
result = subprocess_wrapper.run_as_root(rm_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.warning(f"Failed to remove KDK: {kdk_path}")
|
||||
logging.warning(f"{result.stdout.decode('utf-8')}")
|
||||
subprocess_wrapper.log(result)
|
||||
return
|
||||
|
||||
logging.info(f"Successfully removed KDK: {kdk_path}")
|
||||
@@ -545,7 +545,7 @@ class KernelDebugKitObject:
|
||||
result = subprocess.run(["/usr/bin/hdiutil", "verify", self.constants.kdk_download_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if result.returncode != 0:
|
||||
logging.info("Error: Kernel Debug Kit checksum verification failed!")
|
||||
logging.info(f"Output: {result.stderr.decode('utf-8')}")
|
||||
subprocess_wrapper.log(result)
|
||||
msg = "Kernel Debug Kit checksum verification failed, please try again.\n\nIf this continues to fail, ensure you're downloading on a stable network connection (ie. Ethernet)"
|
||||
logging.info(f"{msg}")
|
||||
|
||||
@@ -579,7 +579,7 @@ class KernelDebugKitUtilities:
|
||||
bool: True if successful, False if not
|
||||
"""
|
||||
|
||||
if os.getuid() != 0:
|
||||
if os.getuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
logging.warning("Cannot install KDK, not running as root")
|
||||
return False
|
||||
|
||||
@@ -588,12 +588,10 @@ class KernelDebugKitUtilities:
|
||||
|
||||
# TODO: Check whether enough disk space is available
|
||||
|
||||
result = utilities.elevated(["/usr/sbin/installer", "-pkg", kdk_path, "-target", "/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
result = subprocess_wrapper.run_as_root(["/usr/sbin/installer", "-pkg", kdk_path, "-target", "/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info("Failed to install KDK:")
|
||||
logging.info(result.stdout.decode('utf-8'))
|
||||
if result.stderr:
|
||||
logging.info(result.stderr.decode('utf-8'))
|
||||
subprocess_wrapper.log(result)
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -609,20 +607,19 @@ class KernelDebugKitUtilities:
|
||||
bool: True if successful, False if not
|
||||
"""
|
||||
|
||||
if os.getuid() != 0:
|
||||
if os.getuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
logging.warning("Cannot install KDK, not running as root")
|
||||
return False
|
||||
|
||||
logging.info(f"Extracting downloaded KDK disk image")
|
||||
with tempfile.TemporaryDirectory() as mount_point:
|
||||
result = subprocess.run(["/usr/bin/hdiutil", "attach", kdk_path, "-mountpoint", mount_point, "-nobrowse"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
result = subprocess_wrapper.run_as_root(["/usr/bin/hdiutil", "attach", kdk_path, "-mountpoint", mount_point, "-nobrowse"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info("Failed to mount KDK:")
|
||||
logging.info(result.stdout.decode('utf-8'))
|
||||
subprocess_wrapper.log(result)
|
||||
return False
|
||||
|
||||
kdk_pkg_path = Path(f"{mount_point}/KernelDebugKit.pkg")
|
||||
|
||||
if not kdk_pkg_path.exists():
|
||||
logging.warning("Failed to find KDK package in DMG, likely corrupted!!!")
|
||||
self._unmount_disk_image(mount_point)
|
||||
@@ -672,12 +669,12 @@ class KernelDebugKitUtilities:
|
||||
logging.warning("Malformed KDK Info.plist provided, cannot create backup")
|
||||
return
|
||||
|
||||
if os.getuid() != 0:
|
||||
if os.getuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
logging.warning("Cannot create KDK backup, not running as root")
|
||||
return
|
||||
|
||||
if not Path(KDK_INSTALL_PATH).exists():
|
||||
subprocess.run(["/bin/mkdir", "-p", KDK_INSTALL_PATH], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root(["/bin/mkdir", "-p", KDK_INSTALL_PATH], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
kdk_dst_name = f"KDK_{kdk_info_dict['version']}_{kdk_info_dict['build']}.pkg"
|
||||
kdk_dst_path = Path(f"{KDK_INSTALL_PATH}/{kdk_dst_name}")
|
||||
@@ -687,7 +684,7 @@ class KernelDebugKitUtilities:
|
||||
logging.info("Backup already exists, skipping")
|
||||
return
|
||||
|
||||
result = utilities.elevated(["/bin/cp", "-R", kdk_path, kdk_dst_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
result = subprocess_wrapper.run_as_root(["/bin/cp", "-R", kdk_path, kdk_dst_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info("Failed to create KDK backup:")
|
||||
logging.info(result.stdout.decode('utf-8'))
|
||||
subprocess_wrapper.log(result)
|
||||
@@ -18,7 +18,8 @@ from .. import constants
|
||||
|
||||
from . import (
|
||||
analytics_handler,
|
||||
global_settings
|
||||
global_settings,
|
||||
subprocess_wrapper
|
||||
)
|
||||
|
||||
|
||||
@@ -130,7 +131,7 @@ class InitializeLoggingSupport:
|
||||
This in turn breaks normal OCLP execution to write to log file
|
||||
"""
|
||||
|
||||
if os.geteuid() != 0:
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
return
|
||||
|
||||
paths = [
|
||||
@@ -139,15 +140,10 @@ class InitializeLoggingSupport:
|
||||
]
|
||||
|
||||
for path in paths:
|
||||
result = subprocess.run(["/bin/chmod", "777", path], capture_output=True)
|
||||
result = subprocess_wrapper.run_as_root(["/bin/chmod", "777", path], capture_output=True)
|
||||
if result.returncode != 0:
|
||||
logging.error(f"Failed to fix log file permissions")
|
||||
if result.stdout:
|
||||
logging.error("STDOUT:")
|
||||
logging.error(result.stdout.decode("utf-8"))
|
||||
if result.stderr:
|
||||
logging.error("STDERR:")
|
||||
logging.error(result.stderr.decode("utf-8"))
|
||||
subprocess_wrapper.log(result)
|
||||
|
||||
|
||||
def _initialize_logging_configuration(self, log_to_file: bool = True) -> None:
|
||||
|
||||
@@ -12,6 +12,8 @@ import logging
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from . import subprocess_wrapper
|
||||
|
||||
from .. import constants
|
||||
|
||||
|
||||
@@ -55,8 +57,7 @@ class RoutePayloadDiskImage:
|
||||
atexit.register(self._unmount_active_dmgs, unmount_all_active=False)
|
||||
else:
|
||||
logging.info("Failed to mount payloads.dmg")
|
||||
logging.info(f"Output: {output.stdout.decode()}")
|
||||
logging.info(f"Return Code: {output.returncode}")
|
||||
subprocess_wrapper.log(output)
|
||||
|
||||
|
||||
def _unmount_active_dmgs(self, unmount_all_active: bool = True) -> None:
|
||||
|
||||
179
opencore_legacy_patcher/support/subprocess_wrapper.py
Normal file
179
opencore_legacy_patcher/support/subprocess_wrapper.py
Normal file
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
subprocess_wrapper.py: Wrapper for subprocess module to better handle errors and output
|
||||
Additionally handles our Privileged Helper Tool
|
||||
"""
|
||||
|
||||
import os
|
||||
import enum
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
from pathlib import Path
|
||||
from functools import cache
|
||||
|
||||
from . import utilities
|
||||
|
||||
|
||||
OCLP_PRIVILEGED_HELPER = "/Library/PrivilegedHelperTools/com.dortania.opencore-legacy-patcher.privileged-helper"
|
||||
|
||||
|
||||
class PrivilegedHelperErrorCodes(enum.IntEnum):
|
||||
"""
|
||||
Error codes for Privileged Helper Tool.
|
||||
|
||||
Reference:
|
||||
payloads/Tools/PrivilegedHelperTool/main.m
|
||||
"""
|
||||
OCLP_PHT_ERROR_MISSING_ARGUMENTS = 160
|
||||
OCLP_PHT_ERROR_SET_UID_MISSING = 161
|
||||
OCLP_PHT_ERROR_SET_UID_FAILED = 162
|
||||
OCLP_PHT_ERROR_SELF_PATH_MISSING = 163
|
||||
OCLP_PHT_ERROR_PARENT_PATH_MISSING = 164
|
||||
OCLP_PHT_ERROR_SIGNING_INFORMATION_MISSING = 165
|
||||
OCLP_PHT_ERROR_INVALID_TEAM_ID = 166
|
||||
OCLP_PHT_ERROR_INVALID_CERTIFICATES = 167
|
||||
OCLP_PHT_ERROR_COMMAND_MISSING = 168
|
||||
OCLP_PHT_ERROR_COMMAND_FAILED = 169
|
||||
OCLP_PHT_ERROR_CATCH_ALL = 170
|
||||
|
||||
|
||||
@cache
|
||||
def supports_privileged_helper() -> bool:
|
||||
"""
|
||||
Check if Privileged Helper Tool is supported.
|
||||
|
||||
When privileged helper is officially shipped, this function should always return True.
|
||||
Something would have gone very wrong if it doesn't exist past that point.
|
||||
"""
|
||||
return Path(OCLP_PRIVILEGED_HELPER).exists()
|
||||
|
||||
|
||||
def run(*args, **kwargs) -> subprocess.CompletedProcess:
|
||||
"""
|
||||
Basic subprocess.run wrapper.
|
||||
"""
|
||||
return subprocess.run(*args, **kwargs)
|
||||
|
||||
|
||||
def run_as_root(*args, **kwargs) -> subprocess.CompletedProcess:
|
||||
"""
|
||||
Run subprocess as root.
|
||||
|
||||
Note: Full path to first argument is required.
|
||||
Helper tool does not resolve PATH.
|
||||
"""
|
||||
# Check if first argument exists
|
||||
if not Path(args[0][0]).exists():
|
||||
raise FileNotFoundError(f"File not found: {args[0][0]}")
|
||||
|
||||
if supports_privileged_helper() is False:
|
||||
# Fall back to old logic
|
||||
# This should be removed when we start shipping the helper tool officially
|
||||
if os.getuid() == 0 or utilities.check_cli_args() is not None:
|
||||
return subprocess.run(*args, **kwargs)
|
||||
else:
|
||||
return subprocess.run(["/usr/bin/sudo"] + [args[0][0]] + args[0][1:], **kwargs)
|
||||
|
||||
return subprocess.run([OCLP_PRIVILEGED_HELPER] + [args[0][0]] + args[0][1:], **kwargs)
|
||||
|
||||
|
||||
def verify(process_result: subprocess.CompletedProcess) -> None:
|
||||
"""
|
||||
Verify process result and raise exception if failed.
|
||||
"""
|
||||
if process_result.returncode == 0:
|
||||
return
|
||||
|
||||
log(process_result)
|
||||
|
||||
raise Exception(f"Process failed with exit code {process_result.returncode}")
|
||||
|
||||
|
||||
def run_and_verify(*args, **kwargs) -> None:
|
||||
"""
|
||||
Run subprocess and verify result.
|
||||
|
||||
Asserts on failure.
|
||||
"""
|
||||
verify(run(*args, **kwargs))
|
||||
|
||||
|
||||
def run_as_root_and_verify(*args, **kwargs) -> None:
|
||||
"""
|
||||
Run subprocess as root and verify result.
|
||||
|
||||
Asserts on failure.
|
||||
"""
|
||||
verify(run_as_root(*args, **kwargs))
|
||||
|
||||
|
||||
def log(process: subprocess.CompletedProcess) -> None:
|
||||
"""
|
||||
Display subprocess error output in formatted string.
|
||||
"""
|
||||
for line in generate_log(process).split("\n"):
|
||||
logging.error(line)
|
||||
|
||||
|
||||
def generate_log(process: subprocess.CompletedProcess) -> str:
|
||||
"""
|
||||
Display subprocess error output in formatted string.
|
||||
Note this function is still used for zero return code errors, since
|
||||
some software don't ever return non-zero regardless of success.
|
||||
|
||||
Format:
|
||||
|
||||
Command: <command>
|
||||
Return Code: <return code>
|
||||
Standard Output:
|
||||
<standard output line 1>
|
||||
<standard output line 2>
|
||||
...
|
||||
Standard Error:
|
||||
<standard error line 1>
|
||||
<standard error line 2>
|
||||
...
|
||||
"""
|
||||
output = "Subprocess failed.\n"
|
||||
output += f" Command: {process.args}\n"
|
||||
output += f" Return Code: {process.returncode}\n"
|
||||
_returned_error = __resolve_privileged_helper_errors(process.returncode)
|
||||
if _returned_error:
|
||||
output += f" Likely Enum: {_returned_error}\n"
|
||||
output += f" Standard Output:\n"
|
||||
if process.stdout:
|
||||
output += __format_output(process.stdout.decode("utf-8"))
|
||||
else:
|
||||
output += " None\n"
|
||||
output += f" Standard Error:\n"
|
||||
if process.stderr:
|
||||
output += __format_output(process.stderr.decode("utf-8"))
|
||||
else:
|
||||
output += " None\n"
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def __resolve_privileged_helper_errors(return_code: int) -> str:
|
||||
"""
|
||||
Attempt to resolve Privileged Helper Tool error codes.
|
||||
"""
|
||||
if return_code not in [error_code.value for error_code in PrivilegedHelperErrorCodes]:
|
||||
return None
|
||||
|
||||
return PrivilegedHelperErrorCodes(return_code).name
|
||||
|
||||
|
||||
def __format_output(output: str) -> str:
|
||||
"""
|
||||
Format output.
|
||||
"""
|
||||
if not output:
|
||||
# Shouldn't happen, but just in case
|
||||
return " None\n"
|
||||
|
||||
_result = "\n".join([f" {line}" for line in output.split("\n") if line not in ["", "\n"]])
|
||||
if not _result.endswith("\n"):
|
||||
_result += "\n"
|
||||
|
||||
return _result
|
||||
@@ -41,13 +41,6 @@ def string_to_hex(input_string):
|
||||
return input_string
|
||||
|
||||
|
||||
def process_status(process_result):
|
||||
if process_result.returncode != 0:
|
||||
logging.info(f"Process failed with exit code {process_result.returncode}")
|
||||
logging.info(f"Please report the issue on the Discord server")
|
||||
raise Exception(f"Process result: \n{process_result.stdout.decode()}")
|
||||
|
||||
|
||||
def human_fmt(num):
|
||||
for unit in ["B", "KB", "MB", "GB", "TB", "PB"]:
|
||||
if abs(num) < 1000.0:
|
||||
@@ -553,14 +546,6 @@ def check_boot_mode():
|
||||
except (KeyError, TypeError, plistlib.InvalidFileException):
|
||||
return None
|
||||
|
||||
def elevated(*args, **kwargs) -> subprocess.CompletedProcess:
|
||||
# When running through our GUI, we run as root, however we do not get uid 0
|
||||
# Best to assume CLI is running as root
|
||||
if os.getuid() == 0 or check_cli_args() is not None:
|
||||
return subprocess.run(*args, **kwargs)
|
||||
else:
|
||||
return subprocess.run(["/usr/bin/sudo"] + [args[0][0]] + args[0][1:], **kwargs)
|
||||
|
||||
|
||||
def fetch_staged_update(variant: str = "Update") -> tuple[str, str]:
|
||||
"""
|
||||
|
||||
@@ -13,6 +13,7 @@ from .. import constants
|
||||
|
||||
from ..sys_patch import sys_patch_helpers
|
||||
from ..efi_builder import build
|
||||
from ..support import subprocess_wrapper
|
||||
|
||||
from ..datasets import (
|
||||
example_data,
|
||||
@@ -83,7 +84,7 @@ class PatcherValidation:
|
||||
result = subprocess.run([self.constants.ocvalidate_path, f"{self.constants.opencore_release_folder}/EFI/OC/config.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info("Error on build!")
|
||||
logging.info(result.stdout.decode())
|
||||
subprocess_wrapper.log(result)
|
||||
raise Exception(f"Validation failed for predefined model: {model}")
|
||||
else:
|
||||
logging.info(f"Validation succeeded for predefined model: {model}")
|
||||
@@ -103,7 +104,7 @@ class PatcherValidation:
|
||||
result = subprocess.run([self.constants.ocvalidate_path, f"{self.constants.opencore_release_folder}/EFI/OC/config.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info("Error on build!")
|
||||
logging.info(result.stdout.decode())
|
||||
subprocess_wrapper.log(result)
|
||||
raise Exception(f"Validation failed for predefined model: {self.constants.computer.real_model}")
|
||||
else:
|
||||
logging.info(f"Validation succeeded for predefined model: {self.constants.computer.real_model}")
|
||||
@@ -178,8 +179,7 @@ class PatcherValidation:
|
||||
|
||||
if output.returncode != 0:
|
||||
logging.info("Failed to unmount Universal-Binaries.dmg")
|
||||
logging.info(f"Output: {output.stdout.decode()}")
|
||||
logging.info(f"Return Code: {output.returncode}")
|
||||
subprocess_wrapper.log(output)
|
||||
|
||||
raise Exception("Failed to unmount Universal-Binaries.dmg")
|
||||
|
||||
@@ -196,8 +196,7 @@ class PatcherValidation:
|
||||
|
||||
if output.returncode != 0:
|
||||
logging.info("Failed to mount Universal-Binaries.dmg")
|
||||
logging.info(f"Output: {output.stdout.decode()}")
|
||||
logging.info(f"Return Code: {output.returncode}")
|
||||
subprocess_wrapper.log(output)
|
||||
|
||||
raise Exception("Failed to mount Universal-Binaries.dmg")
|
||||
|
||||
@@ -226,8 +225,7 @@ class PatcherValidation:
|
||||
|
||||
if output.returncode != 0:
|
||||
logging.info("Failed to unmount Universal-Binaries.dmg")
|
||||
logging.info(f"Output: {output.stdout.decode()}")
|
||||
logging.info(f"Return Code: {output.returncode}")
|
||||
subprocess_wrapper.log(output)
|
||||
|
||||
raise Exception("Failed to unmount Universal-Binaries.dmg")
|
||||
|
||||
|
||||
@@ -49,7 +49,8 @@ from ..datasets import os_data
|
||||
|
||||
from ..support import (
|
||||
utilities,
|
||||
kdk_handler
|
||||
kdk_handler,
|
||||
subprocess_wrapper
|
||||
)
|
||||
from . import (
|
||||
sys_patch_detect,
|
||||
@@ -126,7 +127,7 @@ class PatchSysVolume:
|
||||
else:
|
||||
if self.root_supports_snapshot is True:
|
||||
logging.info("- Mounting APFS Snapshot as writable")
|
||||
result = utilities.elevated(["/sbin/mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
result = subprocess_wrapper.run_as_root(["/sbin/mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode == 0:
|
||||
logging.info(f"- Mounted APFS Snapshot as writable at: {self.mount_location}")
|
||||
if Path(self.mount_extensions).exists():
|
||||
@@ -136,8 +137,7 @@ class PatchSysVolume:
|
||||
logging.info("- Root Volume appears to have unmounted unexpectedly")
|
||||
else:
|
||||
logging.info("- Unable to mount APFS Snapshot as writable")
|
||||
logging.info("Reason for mount failure:")
|
||||
logging.info(result.stdout.decode().strip())
|
||||
subprocess_wrapper.log(result)
|
||||
return False
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ class PatchSysVolume:
|
||||
if kdk_obj.kdk_already_installed is False:
|
||||
# We shouldn't get here, but just in case
|
||||
logging.warning(f"KDK was not installed, but should have been: {kdk_obj.error_msg}")
|
||||
raise Exception("KDK was not installed, but should have been: {kdk_obj.error_msg}")
|
||||
raise Exception(f"KDK was not installed, but should have been: {kdk_obj.error_msg}")
|
||||
|
||||
kdk_path = Path(kdk_obj.kdk_installed_path) if kdk_obj.kdk_installed_path != "" else None
|
||||
|
||||
@@ -222,10 +222,10 @@ class PatchSysVolume:
|
||||
if save_hid_cs is True and cs_path.exists():
|
||||
logging.info("- Backing up IOHIDEventDriver CodeSignature")
|
||||
# Note it's a folder, not a file
|
||||
utilities.elevated(["/bin/cp", "-r", cs_path, f"{self.constants.payload_path}/IOHIDEventDriver_CodeSignature.bak"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root(["/bin/cp", "-r", cs_path, f"{self.constants.payload_path}/IOHIDEventDriver_CodeSignature.bak"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
logging.info(f"- Merging KDK with Root Volume: {kdk_path.name}")
|
||||
utilities.elevated(
|
||||
subprocess_wrapper.run_as_root(
|
||||
# Only merge '/System/Library/Extensions'
|
||||
# 'Kernels' and 'KernelSupport' is wasted space for root patching (we don't care above dev kernels)
|
||||
["/usr/bin/rsync", "-r", "-i", "-a", f"{kdk_path}/System/Library/Extensions/", f"{self.mount_location}/System/Library/Extensions"],
|
||||
@@ -243,9 +243,9 @@ class PatchSysVolume:
|
||||
logging.info("- Restoring IOHIDEventDriver CodeSignature")
|
||||
if not cs_path.exists():
|
||||
logging.info(" - CodeSignature folder missing, creating")
|
||||
utilities.elevated(["/bin/mkdir", "-p", cs_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
utilities.elevated(["/bin/cp", "-r", f"{self.constants.payload_path}/IOHIDEventDriver_CodeSignature.bak", cs_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
utilities.elevated(["/bin/rm", "-rf", f"{self.constants.payload_path}/IOHIDEventDriver_CodeSignature.bak"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root(["/bin/mkdir", "-p", cs_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root(["/bin/cp", "-r", f"{self.constants.payload_path}/IOHIDEventDriver_CodeSignature.bak", cs_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root(["/bin/rm", "-rf", f"{self.constants.payload_path}/IOHIDEventDriver_CodeSignature.bak"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def _unpatch_root_vol(self):
|
||||
@@ -257,11 +257,10 @@ class PatchSysVolume:
|
||||
logging.info("- OS version does not support snapshotting, skipping revert")
|
||||
|
||||
logging.info("- Reverting to last signed APFS snapshot")
|
||||
result = utilities.elevated(["/usr/sbin/bless", "--mount", self.mount_location, "--bootefi", "--last-sealed-snapshot"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
result = subprocess_wrapper.run_as_root(["/usr/sbin/bless", "--mount", self.mount_location, "--bootefi", "--last-sealed-snapshot"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info("- Unable to revert root volume patches")
|
||||
logging.info("Reason for unpatch Failure:")
|
||||
logging.info(result.stdout.decode())
|
||||
subprocess_wrapper.log(result)
|
||||
logging.info("- Failed to revert snapshot via Apple's 'bless' command")
|
||||
else:
|
||||
self._clean_skylight_plugins()
|
||||
@@ -363,7 +362,7 @@ class PatchSysVolume:
|
||||
else:
|
||||
args = ["/usr/sbin/kextcache", "-i", f"{self.mount_location}/"]
|
||||
|
||||
result = utilities.elevated(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
result = subprocess_wrapper.run_as_root(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
# kextcache notes:
|
||||
# - kextcache always returns 0, even if it fails
|
||||
@@ -374,19 +373,17 @@ class PatchSysVolume:
|
||||
# - will return -10 if the volume is missing (ie. unmounted by another process)
|
||||
if result.returncode != 0 or (self.constants.detected_os < os_data.os_data.catalina and "KernelCache ID" not in result.stdout.decode()):
|
||||
logging.info("- Unable to build new kernel cache")
|
||||
logging.info(f"\nReason for Patch Failure ({result.returncode}):")
|
||||
logging.info(result.stdout.decode())
|
||||
subprocess_wrapper.log(result)
|
||||
logging.info("")
|
||||
logging.info("\nPlease reboot the machine to avoid potential issues rerunning the patcher")
|
||||
return False
|
||||
|
||||
if self.skip_root_kmutil_requirement is True:
|
||||
# Force rebuild the Auxiliary KC
|
||||
result = utilities.elevated(["/usr/bin/killall", "syspolicyd", "kernelmanagerd"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
result = subprocess_wrapper.run_as_root(["/usr/bin/killall", "syspolicyd", "kernelmanagerd"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info("- Unable to remove kernel extension policy files")
|
||||
logging.info(f"\nReason for Patch Failure ({result.returncode}):")
|
||||
logging.info(result.stdout.decode())
|
||||
subprocess_wrapper.log(result)
|
||||
logging.info("")
|
||||
logging.info("\nPlease reboot the machine to avoid potential issues rerunning the patcher")
|
||||
return False
|
||||
@@ -411,7 +408,7 @@ class PatchSysVolume:
|
||||
|
||||
if self.root_supports_snapshot is True:
|
||||
logging.info("- Creating new APFS snapshot")
|
||||
bless = utilities.elevated(
|
||||
bless = subprocess_wrapper.run_as_root(
|
||||
[
|
||||
"/usr/sbin/bless",
|
||||
"--folder", f"{self.mount_location}/System/Library/CoreServices",
|
||||
@@ -420,8 +417,7 @@ class PatchSysVolume:
|
||||
)
|
||||
if bless.returncode != 0:
|
||||
logging.info("- Unable to create new snapshot")
|
||||
logging.info("Reason for snapshot failure:")
|
||||
logging.info(bless.stdout.decode())
|
||||
subprocess_wrapper.log(bless)
|
||||
if "Can't use last-sealed-snapshot or create-snapshot on non system volume" in bless.stdout.decode():
|
||||
logging.info("- This is an APFS bug with Monterey and newer! Perform a clean installation to ensure your APFS volume is built correctly")
|
||||
return False
|
||||
@@ -435,7 +431,7 @@ class PatchSysVolume:
|
||||
"""
|
||||
if self.root_mount_path:
|
||||
logging.info("- Unmounting Root Volume (Don't worry if this fails)")
|
||||
utilities.elevated(["/usr/sbin/diskutil", "unmount", self.root_mount_path], stdout=subprocess.PIPE).stdout.decode().strip().encode()
|
||||
subprocess_wrapper.run_as_root(["/usr/sbin/diskutil", "unmount", self.root_mount_path], stdout=subprocess.PIPE)
|
||||
else:
|
||||
logging.info("- Skipping Root Volume unmount")
|
||||
|
||||
@@ -449,7 +445,7 @@ class PatchSysVolume:
|
||||
if self.constants.detected_os > os_data.os_data.catalina:
|
||||
return
|
||||
logging.info("- Rebuilding dyld shared cache")
|
||||
utilities.process_status(utilities.elevated(["/usr/bin/update_dyld_shared_cache", "-root", f"{self.mount_location}/"]))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/usr/bin/update_dyld_shared_cache", "-root", f"{self.mount_location}/"])
|
||||
|
||||
|
||||
def _update_preboot_kernel_cache(self) -> None:
|
||||
@@ -460,7 +456,7 @@ class PatchSysVolume:
|
||||
|
||||
if self.constants.detected_os == os_data.os_data.catalina:
|
||||
logging.info("- Rebuilding preboot kernel cache")
|
||||
utilities.process_status(utilities.elevated(["/usr/sbin/kcditto"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/usr/sbin/kcditto"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def _clean_skylight_plugins(self) -> None:
|
||||
@@ -470,11 +466,11 @@ class PatchSysVolume:
|
||||
|
||||
if (Path(self.mount_application_support) / Path("SkyLightPlugins/")).exists():
|
||||
logging.info("- Found SkylightPlugins folder, removing old plugins")
|
||||
utilities.process_status(utilities.elevated(["/bin/rm", "-Rf", f"{self.mount_application_support}/SkyLightPlugins"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
utilities.process_status(utilities.elevated(["/bin/mkdir", f"{self.mount_application_support}/SkyLightPlugins"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", "-Rf", f"{self.mount_application_support}/SkyLightPlugins"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/mkdir", f"{self.mount_application_support}/SkyLightPlugins"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
logging.info("- Creating SkylightPlugins folder")
|
||||
utilities.process_status(utilities.elevated(["/bin/mkdir", "-p", f"{self.mount_application_support}/SkyLightPlugins/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/mkdir", "-p", f"{self.mount_application_support}/SkyLightPlugins/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def _delete_nonmetal_enforcement(self) -> None:
|
||||
@@ -487,7 +483,7 @@ class PatchSysVolume:
|
||||
result = subprocess.run(["/usr/bin/defaults", "read", "/Library/Preferences/com.apple.CoreDisplay", arg], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode("utf-8").strip()
|
||||
if result in ["0", "false", "1", "true"]:
|
||||
logging.info(f"- Removing non-Metal Enforcement Preference: {arg}")
|
||||
utilities.elevated(["/usr/bin/defaults", "delete", "/Library/Preferences/com.apple.CoreDisplay", arg])
|
||||
subprocess_wrapper.run_as_root(["/usr/bin/defaults", "delete", "/Library/Preferences/com.apple.CoreDisplay", arg])
|
||||
|
||||
|
||||
def _clean_auxiliary_kc(self) -> None:
|
||||
@@ -530,15 +526,15 @@ class PatchSysVolume:
|
||||
|
||||
relocation_path = "/Library/Relocated Extensions"
|
||||
if not Path(relocation_path).exists():
|
||||
utilities.elevated(["/bin/mkdir", relocation_path])
|
||||
subprocess_wrapper.run_as_root(["/bin/mkdir", relocation_path])
|
||||
|
||||
for file in Path("/Library/Extensions").glob("*.kext"):
|
||||
try:
|
||||
if datetime.fromtimestamp(file.stat().st_mtime) < datetime(2021, 10, 1):
|
||||
logging.info(f" - Relocating {file.name} kext to {relocation_path}")
|
||||
if Path(relocation_path) / Path(file.name).exists():
|
||||
utilities.elevated(["/bin/rm", "-Rf", relocation_path / Path(file.name)])
|
||||
utilities.elevated(["/bin/mv", file, relocation_path])
|
||||
subprocess_wrapper.run_as_root(["/bin/rm", "-Rf", relocation_path / Path(file.name)])
|
||||
subprocess_wrapper.run_as_root(["/bin/mv", file, relocation_path])
|
||||
except:
|
||||
# Some users have the most cursed /L*/E* folders
|
||||
# ex. Symlinks pointing to symlinks pointing to dead files
|
||||
@@ -559,8 +555,8 @@ class PatchSysVolume:
|
||||
if sys_patch_helpers.SysPatchHelpers(self.constants).generate_patchset_plist(patchset, file_name, self.kdk_path):
|
||||
logging.info("- Writing patchset information to Root Volume")
|
||||
if Path(destination_path_file).exists():
|
||||
utilities.process_status(utilities.elevated(["/bin/rm", destination_path_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
utilities.process_status(utilities.elevated(["/bin/cp", f"{self.constants.payload_path}/{file_name}", destination_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", destination_path_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/cp", f"{self.constants.payload_path}/{file_name}", destination_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def _add_auxkc_support(self, install_file: str, source_folder_path: str, install_patch_directory: str, destination_folder_path: str) -> str:
|
||||
@@ -727,10 +723,10 @@ class PatchSysVolume:
|
||||
# Instead, call elevated funtion if string's boolean is True
|
||||
if required_patches[patch]["Processes"][process] is True:
|
||||
logging.info(f"- Running Process as Root:\n{process}")
|
||||
utilities.process_status(utilities.elevated(process.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(process.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
logging.info(f"- Running Process:\n{process}")
|
||||
utilities.process_status(subprocess.run(process, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True))
|
||||
subprocess_wrapper.run_and_verify(process, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
|
||||
if any(x in required_patches for x in ["AMD Legacy GCN", "AMD Legacy Polaris", "AMD Legacy Vega"]):
|
||||
sys_patch_helpers.SysPatchHelpers(self.constants).disable_window_server_caching()
|
||||
if "Metal 3802 Common Extended" in required_patches:
|
||||
@@ -800,25 +796,25 @@ class PatchSysVolume:
|
||||
if file_name_str.endswith(".framework"):
|
||||
# merge with rsync
|
||||
logging.info(f" - Installing: {file_name}")
|
||||
utilities.elevated(["/usr/bin/rsync", "-r", "-i", "-a", f"{source_folder}/{file_name}", f"{destination_folder}/"], stdout=subprocess.PIPE)
|
||||
subprocess_wrapper.run_as_root(["/usr/bin/rsync", "-r", "-i", "-a", f"{source_folder}/{file_name}", f"{destination_folder}/"], stdout=subprocess.PIPE)
|
||||
self._fix_permissions(destination_folder + "/" + file_name)
|
||||
elif Path(source_folder + "/" + file_name_str).is_dir():
|
||||
# Applicable for .kext, .app, .plugin, .bundle, all of which are directories
|
||||
if Path(destination_folder + "/" + file_name).exists():
|
||||
logging.info(f" - Found existing {file_name}, overwriting...")
|
||||
utilities.process_status(utilities.elevated(["/bin/rm", "-R", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", "-R", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
logging.info(f" - Installing: {file_name}")
|
||||
utilities.process_status(utilities.elevated(["/bin/cp", "-R", f"{source_folder}/{file_name}", destination_folder], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/cp", "-R", f"{source_folder}/{file_name}", destination_folder], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
self._fix_permissions(destination_folder + "/" + file_name)
|
||||
else:
|
||||
# Assume it's an individual file, replace as normal
|
||||
if Path(destination_folder + "/" + file_name).exists():
|
||||
logging.info(f" - Found existing {file_name}, overwriting...")
|
||||
utilities.process_status(utilities.elevated(["/bin/rm", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
logging.info(f" - Installing: {file_name}")
|
||||
utilities.process_status(utilities.elevated(["/bin/cp", f"{source_folder}/{file_name}", destination_folder], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/cp", f"{source_folder}/{file_name}", destination_folder], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
self._fix_permissions(destination_folder + "/" + file_name)
|
||||
|
||||
|
||||
@@ -834,9 +830,9 @@ class PatchSysVolume:
|
||||
if Path(destination_folder + "/" + file_name).exists():
|
||||
logging.info(f" - Removing: {file_name}")
|
||||
if Path(destination_folder + "/" + file_name).is_dir():
|
||||
utilities.process_status(utilities.elevated(["/bin/rm", "-R", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", "-R", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
else:
|
||||
utilities.process_status(utilities.elevated(["/bin/rm", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def _fix_permissions(self, destination_file: Path) -> None:
|
||||
@@ -850,8 +846,8 @@ class PatchSysVolume:
|
||||
# Strip recursive arguments
|
||||
chmod_args.pop(1)
|
||||
chown_args.pop(1)
|
||||
utilities.process_status(utilities.elevated(chmod_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
utilities.process_status(utilities.elevated(chown_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(chmod_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root_and_verify(chown_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def _check_files(self) -> bool:
|
||||
@@ -882,8 +878,7 @@ class PatchSysVolume:
|
||||
|
||||
if output.returncode != 0:
|
||||
logging.info("- Failed to mount Universal-Binaries.dmg")
|
||||
logging.info(f"Output: {output.stdout.decode()}")
|
||||
logging.info(f"Return Code: {output.returncode}")
|
||||
subprocess_wrapper.log(output)
|
||||
return False
|
||||
|
||||
logging.info("- Mounted Universal-Binaries.dmg")
|
||||
@@ -923,13 +918,11 @@ class PatchSysVolume:
|
||||
return True
|
||||
|
||||
logging.info("- Failed to merge DortaniaInternal resources")
|
||||
logging.info(f"Output: {result.stdout.decode()}")
|
||||
logging.info(f"Return Code: {result.returncode}")
|
||||
subprocess_wrapper.log(result)
|
||||
return False
|
||||
|
||||
logging.info("- Failed to mount DortaniaInternal resources")
|
||||
logging.info(f"Output: {result.stdout.decode()}")
|
||||
logging.info(f"Return Code: {result.returncode}")
|
||||
subprocess_wrapper.log(result)
|
||||
|
||||
if "Authentication error" not in result.stdout.decode():
|
||||
try:
|
||||
|
||||
@@ -29,7 +29,8 @@ from ..support import (
|
||||
utilities,
|
||||
updates,
|
||||
global_settings,
|
||||
network_handler
|
||||
network_handler,
|
||||
subprocess_wrapper
|
||||
)
|
||||
|
||||
|
||||
@@ -363,16 +364,16 @@ Please check the Github page for more information about this release."""
|
||||
logging.info(f" - {name} checksums match, skipping")
|
||||
continue
|
||||
logging.info(f" - Existing service found, removing")
|
||||
utilities.process_status(utilities.elevated(["/bin/rm", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
# Create parent directories
|
||||
if not Path(services[service]).parent.exists():
|
||||
logging.info(f" - Creating {Path(services[service]).parent} directory")
|
||||
utilities.process_status(utilities.elevated(["/bin/mkdir", "-p", Path(services[service]).parent], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
utilities.process_status(utilities.elevated(["/bin/cp", service, services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/mkdir", "-p", Path(services[service]).parent], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/cp", service, services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
# Set the permissions on the service
|
||||
utilities.process_status(utilities.elevated(["/bin/chmod", "644", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
utilities.process_status(utilities.elevated(["/usr/sbin/chown", "root:wheel", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/chmod", "644", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
subprocess_wrapper.run_as_root_and_verify(["/usr/sbin/chown", "root:wheel", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
if self.constants.launcher_binary.startswith("/Library/Application Support/Dortania/"):
|
||||
logging.info("- Skipping Patcher Install, already installed")
|
||||
@@ -384,24 +385,24 @@ Please check the Github page for more information about this release."""
|
||||
|
||||
if not Path("Library/Application Support/Dortania").exists():
|
||||
logging.info("- Creating /Library/Application Support/Dortania/")
|
||||
utilities.process_status(utilities.elevated(["/bin/mkdir", "-p", "/Library/Application Support/Dortania"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/mkdir", "-p", "/Library/Application Support/Dortania"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
logging.info("- Copying OpenCore Patcher to /Library/Application Support/Dortania/")
|
||||
if Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists():
|
||||
logging.info("- Deleting existing OpenCore-Patcher")
|
||||
utilities.process_status(utilities.elevated(["/bin/rm", "-R", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/rm", "-R", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
# Strip everything after OpenCore-Patcher.app
|
||||
path = str(self.constants.launcher_binary).split("/Contents/MacOS/OpenCore-Patcher")[0]
|
||||
logging.info(f"- Copying {path} to /Library/Application Support/Dortania/")
|
||||
utilities.process_status(utilities.elevated(["/usr/bin/ditto", path, "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/usr/bin/ditto", path, "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
if not Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists():
|
||||
# Sometimes the binary the user launches may have a suffix (ie. OpenCore-Patcher 3.app)
|
||||
# We'll want to rename it to OpenCore-Patcher.app
|
||||
path = path.split("/")[-1]
|
||||
logging.info(f"- Renaming {path} to OpenCore-Patcher.app")
|
||||
utilities.process_status(utilities.elevated(["/bin/mv", f"/Library/Application Support/Dortania/{path}", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/mv", f"/Library/Application Support/Dortania/{path}", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
subprocess.run(["/usr/bin/xattr", "-cr", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
@@ -410,7 +411,7 @@ Please check the Github page for more information about this release."""
|
||||
# If there's already an alias or exiting app, skip
|
||||
if not Path("/Applications/OpenCore-Patcher.app").exists():
|
||||
logging.info("- Making app alias")
|
||||
utilities.process_status(utilities.elevated(["/bin/ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
|
||||
def _create_rsr_monitor_daemon(self) -> bool:
|
||||
|
||||
@@ -18,7 +18,8 @@ from ..datasets import os_data
|
||||
from ..support import (
|
||||
bplist,
|
||||
generate_smbios,
|
||||
utilities
|
||||
utilities,
|
||||
subprocess_wrapper
|
||||
)
|
||||
|
||||
|
||||
@@ -138,9 +139,9 @@ class SysPatchHelpers:
|
||||
|
||||
logging.info("Disabling WindowServer Caching")
|
||||
# Invoke via 'bash -c' to resolve pathing
|
||||
utilities.elevated(["bash", "-c", "rm -rf /private/var/folders/*/*/*/WindowServer/com.apple.WindowServer"])
|
||||
subprocess_wrapper.run_as_root(["/bin/bash", "-c", "rm -rf /private/var/folders/*/*/*/WindowServer/com.apple.WindowServer"])
|
||||
# Disable writing to WindowServer folder
|
||||
utilities.elevated(["bash", "-c", "chflags uchg /private/var/folders/*/*/*/WindowServer"])
|
||||
subprocess_wrapper.run_as_root(["/bin/bash", "-c", "chflags uchg /private/var/folders/*/*/*/WindowServer"])
|
||||
# Reference:
|
||||
# To reverse write lock:
|
||||
# 'chflags nouchg /private/var/folders/*/*/*/WindowServer'
|
||||
@@ -219,9 +220,10 @@ class SysPatchHelpers:
|
||||
return
|
||||
|
||||
logging.info("Installing Kernel Collection syncing utility")
|
||||
result = utilities.elevated([self.constants.rsrrepair_userspace_path, "--install"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
result = subprocess_wrapper.run_as_root([self.constants.rsrrepair_userspace_path, "--install"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info(f"- Failed to install RSRRepair: {result.stdout.decode()}")
|
||||
logging.info("- Failed to install RSRRepair")
|
||||
subprocess_wrapper.log(result)
|
||||
|
||||
|
||||
def patch_gpu_compiler_libraries(self, mount_point: Union[str, Path]):
|
||||
@@ -282,6 +284,6 @@ class SysPatchHelpers:
|
||||
|
||||
src_dir = f"{LIBRARY_DIR}/{file.name}"
|
||||
if not Path(f"{DEST_DIR}/lib").exists():
|
||||
utilities.process_status(utilities.elevated(["/bin/cp", "-cR", f"{src_dir}/lib", f"{DEST_DIR}/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
subprocess_wrapper.run_as_root_and_verify(["/bin/cp", "-cR", f"{src_dir}/lib", f"{DEST_DIR}/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
break
|
||||
@@ -26,6 +26,7 @@ from ..support import (
|
||||
utilities,
|
||||
network_handler,
|
||||
kdk_handler,
|
||||
subprocess_wrapper
|
||||
)
|
||||
|
||||
|
||||
@@ -525,7 +526,7 @@ class macOSInstallerFlashFrame(wx.Frame):
|
||||
result = subprocess.run(["/usr/bin/hdiutil", "attach", kdk_dmg_path, "-mountpoint", mount_point, "-nobrowse"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info("Failed to mount KDK")
|
||||
logging.info(result.stdout.decode("utf-8"))
|
||||
subprocess_wrapper.log(result)
|
||||
return
|
||||
|
||||
logging.info("Copying KDK")
|
||||
@@ -535,7 +536,7 @@ class macOSInstallerFlashFrame(wx.Frame):
|
||||
result = subprocess.run(["/usr/bin/hdiutil", "detach", mount_point], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
if result.returncode != 0:
|
||||
logging.info("Failed to unmount KDK")
|
||||
logging.info(result.stdout.decode("utf-8"))
|
||||
subprocess_wrapper.log(result)
|
||||
return
|
||||
|
||||
logging.info("Removing KDK Disk Image")
|
||||
|
||||
@@ -24,7 +24,8 @@ from ..support import (
|
||||
global_settings,
|
||||
defaults,
|
||||
generate_smbios,
|
||||
network_handler
|
||||
network_handler,
|
||||
subprocess_wrapper
|
||||
)
|
||||
from ..datasets import (
|
||||
model_array,
|
||||
@@ -1324,7 +1325,7 @@ Hardware Information:
|
||||
raise Exception("Test Exception")
|
||||
|
||||
def on_mount_root_vol(self, event: wx.Event) -> None:
|
||||
if os.geteuid() != 0:
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
wx.MessageDialog(self.parent, "Please relaunch as Root to mount the Root Volume", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
else:
|
||||
#Don't need to pass model as we're bypassing all logic
|
||||
@@ -1334,7 +1335,7 @@ Hardware Information:
|
||||
wx.MessageDialog(self.parent, "Root Volume Mount Failed, check terminal output", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def on_bless_root_vol(self, event: wx.Event) -> None:
|
||||
if os.geteuid() != 0:
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
wx.MessageDialog(self.parent, "Please relaunch as Root to save changes", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
else:
|
||||
#Don't need to pass model as we're bypassing all logic
|
||||
|
||||
@@ -15,10 +15,12 @@ import packaging.version
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from . import gui_about
|
||||
|
||||
from .. import constants
|
||||
|
||||
from ..wx_gui import gui_about
|
||||
from ..detections import device_probe
|
||||
from ..support import subprocess_wrapper
|
||||
|
||||
from ..datasets import (
|
||||
model_array,
|
||||
@@ -75,7 +77,7 @@ class GenerateMenubar:
|
||||
self.frame.Bind(wx.EVT_MENU, lambda event: RelaunchApplicationAsRoot(self.frame, self.constants).relaunch(None), relaunchItem)
|
||||
self.frame.Bind(wx.EVT_MENU, lambda event: subprocess.run(["/usr/bin/open", "--reveal", self.constants.log_filepath]), revealLogItem)
|
||||
|
||||
if os.geteuid() == 0:
|
||||
if os.geteuid() == 0 or subprocess_wrapper.supports_privileged_helper() is True:
|
||||
relaunchItem.Enable(False)
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ from pathlib import Path
|
||||
from .. import constants
|
||||
|
||||
from ..sys_patch import sys_patch_detect
|
||||
from ..support import subprocess_wrapper
|
||||
|
||||
from ..wx_gui import (
|
||||
gui_main_menu,
|
||||
@@ -242,7 +243,7 @@ class SysPatchDisplayFrame(wx.Frame):
|
||||
revert_button.Disable()
|
||||
|
||||
# Relaunch as root if not root
|
||||
if os.geteuid() != 0:
|
||||
if os.geteuid() != 0 and subprocess_wrapper.supports_privileged_helper() is False:
|
||||
start_button.Bind(wx.EVT_BUTTON, gui_support.RelaunchApplicationAsRoot(frame, self.constants).relaunch)
|
||||
revert_button.Bind(wx.EVT_BUTTON, gui_support.RelaunchApplicationAsRoot(frame, self.constants).relaunch)
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ from ..wx_gui import (
|
||||
)
|
||||
from ..support import (
|
||||
network_handler,
|
||||
updates
|
||||
updates,
|
||||
subprocess_wrapper
|
||||
)
|
||||
|
||||
|
||||
@@ -200,7 +201,8 @@ class UpdateFrame(wx.Frame):
|
||||
["/usr/bin/ditto", "-xk", str(self.constants.payload_path / "OpenCore-Patcher-GUI.app.zip"), str(self.constants.payload_path)], capture_output=True
|
||||
)
|
||||
if result.returncode != 0:
|
||||
logging.error(f"Failed to extract update. Error: {result.stderr.decode('utf-8')}")
|
||||
logging.error(f"Failed to extract update.")
|
||||
subprocess_wrapper.log(result)
|
||||
wx.CallAfter(self.progress_bar_animation.stop_pulse)
|
||||
wx.CallAfter(self.progress_bar.SetValue, 0)
|
||||
wx.CallAfter(wx.MessageBox, f"Failed to extract update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR)
|
||||
@@ -278,7 +280,8 @@ EOF
|
||||
logging.info("User cancelled update")
|
||||
wx.CallAfter(wx.MessageBox, "User cancelled update", "Update Cancelled", wx.OK | wx.ICON_INFORMATION)
|
||||
else:
|
||||
logging.critical(f"Failed to install update. Error: {result.stderr.decode('utf-8')}")
|
||||
logging.critical("Failed to install update.")
|
||||
subprocess_wrapper.log(result)
|
||||
wx.CallAfter(wx.MessageBox, f"Failed to install update. Error: {result.stderr.decode('utf-8')}", "Critical Error!", wx.OK | wx.ICON_ERROR)
|
||||
wx.CallAfter(sys.exit, 1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user