mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-06-21 06:30:52 +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 pathlib import Path
|
||||||
|
|
||||||
|
from . import subprocess_wrapper
|
||||||
|
|
||||||
from .. import constants
|
from .. import constants
|
||||||
|
|
||||||
from ..wx_gui import gui_entry
|
from ..wx_gui import gui_entry
|
||||||
@@ -172,7 +174,7 @@ class arguments:
|
|||||||
if "GPUCompanionBundles" not in kext_plist:
|
if "GPUCompanionBundles" not in kext_plist:
|
||||||
continue
|
continue
|
||||||
logging.info(f" - Removing {kext.name}")
|
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:
|
def _build_handler(self) -> None:
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ ie. during automated patching
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import plistlib
|
import plistlib
|
||||||
import subprocess
|
|
||||||
|
from . import subprocess_wrapper
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -115,12 +116,11 @@ class GlobalEnviromentSettings:
|
|||||||
This in turn breaks normal OCLP execution to write to settings file
|
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
|
return
|
||||||
|
|
||||||
# Set file permission to allow any user to write to log file
|
# 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:
|
if result.returncode != 0:
|
||||||
logging.warning("Failed to fix settings file permissions:")
|
logging.warning("Failed to fix settings file permissions:")
|
||||||
if result.stderr:
|
subprocess_wrapper.log(result)
|
||||||
logging.warning(result.stderr.decode("utf-8"))
|
|
||||||
@@ -9,7 +9,7 @@ import applescript
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from . import utilities
|
from . import utilities, subprocess_wrapper
|
||||||
|
|
||||||
from .. import constants
|
from .. import constants
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ class tui_disk_installation:
|
|||||||
def install_opencore(self, full_disk_identifier: str):
|
def install_opencore(self, full_disk_identifier: str):
|
||||||
# TODO: Apple Script fails in Yosemite(?) and older
|
# TODO: Apple Script fails in Yosemite(?) and older
|
||||||
logging.info(f"Mounting partition: {full_disk_identifier}")
|
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:
|
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()
|
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:
|
except applescript.ScriptError as e:
|
||||||
@@ -105,10 +105,10 @@ class tui_disk_installation:
|
|||||||
logging.info("Please disable Safe Mode and try again.")
|
logging.info("Please disable Safe Mode and try again.")
|
||||||
return
|
return
|
||||||
else:
|
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:
|
if result.returncode != 0:
|
||||||
logging.info("Mount failed")
|
logging.info("Mount failed")
|
||||||
logging.info(result.stderr.decode())
|
subprocess_wrapper.log(result)
|
||||||
return
|
return
|
||||||
|
|
||||||
partition_info = plistlib.loads(subprocess.run(["/usr/sbin/diskutil", "info", "-plist", full_disk_identifier], stdout=subprocess.PIPE).stdout.decode().strip().encode())
|
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 ..datasets import os_data
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
utilities,
|
network_handler,
|
||||||
network_handler
|
subprocess_wrapper
|
||||||
)
|
)
|
||||||
|
|
||||||
KDK_INSTALL_PATH: str = "/Library/Developer/KDKs"
|
KDK_INSTALL_PATH: str = "/Library/Developer/KDKs"
|
||||||
@@ -464,7 +464,7 @@ class KernelDebugKitObject:
|
|||||||
if self.passive is True:
|
if self.passive is True:
|
||||||
return
|
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")
|
logging.warning("Cannot remove KDK, not running as root")
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -474,10 +474,10 @@ class KernelDebugKitObject:
|
|||||||
|
|
||||||
rm_args = ["/bin/rm", "-rf" if Path(kdk_path).is_dir() else "-f", kdk_path]
|
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:
|
if result.returncode != 0:
|
||||||
logging.warning(f"Failed to remove KDK: {kdk_path}")
|
logging.warning(f"Failed to remove KDK: {kdk_path}")
|
||||||
logging.warning(f"{result.stdout.decode('utf-8')}")
|
subprocess_wrapper.log(result)
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info(f"Successfully removed KDK: {kdk_path}")
|
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)
|
result = subprocess.run(["/usr/bin/hdiutil", "verify", self.constants.kdk_download_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
logging.info("Error: Kernel Debug Kit checksum verification failed!")
|
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)"
|
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}")
|
logging.info(f"{msg}")
|
||||||
|
|
||||||
@@ -579,7 +579,7 @@ class KernelDebugKitUtilities:
|
|||||||
bool: True if successful, False if not
|
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")
|
logging.warning("Cannot install KDK, not running as root")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -588,12 +588,10 @@ class KernelDebugKitUtilities:
|
|||||||
|
|
||||||
# TODO: Check whether enough disk space is available
|
# 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:
|
if result.returncode != 0:
|
||||||
logging.info("Failed to install KDK:")
|
logging.info("Failed to install KDK:")
|
||||||
logging.info(result.stdout.decode('utf-8'))
|
subprocess_wrapper.log(result)
|
||||||
if result.stderr:
|
|
||||||
logging.info(result.stderr.decode('utf-8'))
|
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -609,20 +607,19 @@ class KernelDebugKitUtilities:
|
|||||||
bool: True if successful, False if not
|
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")
|
logging.warning("Cannot install KDK, not running as root")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logging.info(f"Extracting downloaded KDK disk image")
|
logging.info(f"Extracting downloaded KDK disk image")
|
||||||
with tempfile.TemporaryDirectory() as mount_point:
|
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:
|
if result.returncode != 0:
|
||||||
logging.info("Failed to mount KDK:")
|
logging.info("Failed to mount KDK:")
|
||||||
logging.info(result.stdout.decode('utf-8'))
|
subprocess_wrapper.log(result)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
kdk_pkg_path = Path(f"{mount_point}/KernelDebugKit.pkg")
|
kdk_pkg_path = Path(f"{mount_point}/KernelDebugKit.pkg")
|
||||||
|
|
||||||
if not kdk_pkg_path.exists():
|
if not kdk_pkg_path.exists():
|
||||||
logging.warning("Failed to find KDK package in DMG, likely corrupted!!!")
|
logging.warning("Failed to find KDK package in DMG, likely corrupted!!!")
|
||||||
self._unmount_disk_image(mount_point)
|
self._unmount_disk_image(mount_point)
|
||||||
@@ -672,12 +669,12 @@ class KernelDebugKitUtilities:
|
|||||||
logging.warning("Malformed KDK Info.plist provided, cannot create backup")
|
logging.warning("Malformed KDK Info.plist provided, cannot create backup")
|
||||||
return
|
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")
|
logging.warning("Cannot create KDK backup, not running as root")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not Path(KDK_INSTALL_PATH).exists():
|
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_name = f"KDK_{kdk_info_dict['version']}_{kdk_info_dict['build']}.pkg"
|
||||||
kdk_dst_path = Path(f"{KDK_INSTALL_PATH}/{kdk_dst_name}")
|
kdk_dst_path = Path(f"{KDK_INSTALL_PATH}/{kdk_dst_name}")
|
||||||
@@ -687,7 +684,7 @@ class KernelDebugKitUtilities:
|
|||||||
logging.info("Backup already exists, skipping")
|
logging.info("Backup already exists, skipping")
|
||||||
return
|
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:
|
if result.returncode != 0:
|
||||||
logging.info("Failed to create KDK backup:")
|
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 (
|
from . import (
|
||||||
analytics_handler,
|
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
|
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
|
return
|
||||||
|
|
||||||
paths = [
|
paths = [
|
||||||
@@ -139,15 +140,10 @@ class InitializeLoggingSupport:
|
|||||||
]
|
]
|
||||||
|
|
||||||
for path in paths:
|
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:
|
if result.returncode != 0:
|
||||||
logging.error(f"Failed to fix log file permissions")
|
logging.error(f"Failed to fix log file permissions")
|
||||||
if result.stdout:
|
subprocess_wrapper.log(result)
|
||||||
logging.error("STDOUT:")
|
|
||||||
logging.error(result.stdout.decode("utf-8"))
|
|
||||||
if result.stderr:
|
|
||||||
logging.error("STDERR:")
|
|
||||||
logging.error(result.stderr.decode("utf-8"))
|
|
||||||
|
|
||||||
|
|
||||||
def _initialize_logging_configuration(self, log_to_file: bool = True) -> None:
|
def _initialize_logging_configuration(self, log_to_file: bool = True) -> None:
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import logging
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from . import subprocess_wrapper
|
||||||
|
|
||||||
from .. import constants
|
from .. import constants
|
||||||
|
|
||||||
|
|
||||||
@@ -55,8 +57,7 @@ class RoutePayloadDiskImage:
|
|||||||
atexit.register(self._unmount_active_dmgs, unmount_all_active=False)
|
atexit.register(self._unmount_active_dmgs, unmount_all_active=False)
|
||||||
else:
|
else:
|
||||||
logging.info("Failed to mount payloads.dmg")
|
logging.info("Failed to mount payloads.dmg")
|
||||||
logging.info(f"Output: {output.stdout.decode()}")
|
subprocess_wrapper.log(output)
|
||||||
logging.info(f"Return Code: {output.returncode}")
|
|
||||||
|
|
||||||
|
|
||||||
def _unmount_active_dmgs(self, unmount_all_active: bool = True) -> None:
|
def _unmount_active_dmgs(self, unmount_all_active: bool = True) -> None:
|
||||||
|
|||||||
@@ -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
|
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):
|
def human_fmt(num):
|
||||||
for unit in ["B", "KB", "MB", "GB", "TB", "PB"]:
|
for unit in ["B", "KB", "MB", "GB", "TB", "PB"]:
|
||||||
if abs(num) < 1000.0:
|
if abs(num) < 1000.0:
|
||||||
@@ -553,14 +546,6 @@ def check_boot_mode():
|
|||||||
except (KeyError, TypeError, plistlib.InvalidFileException):
|
except (KeyError, TypeError, plistlib.InvalidFileException):
|
||||||
return None
|
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]:
|
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 ..sys_patch import sys_patch_helpers
|
||||||
from ..efi_builder import build
|
from ..efi_builder import build
|
||||||
|
from ..support import subprocess_wrapper
|
||||||
|
|
||||||
from ..datasets import (
|
from ..datasets import (
|
||||||
example_data,
|
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)
|
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:
|
if result.returncode != 0:
|
||||||
logging.info("Error on build!")
|
logging.info("Error on build!")
|
||||||
logging.info(result.stdout.decode())
|
subprocess_wrapper.log(result)
|
||||||
raise Exception(f"Validation failed for predefined model: {model}")
|
raise Exception(f"Validation failed for predefined model: {model}")
|
||||||
else:
|
else:
|
||||||
logging.info(f"Validation succeeded for predefined model: {model}")
|
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)
|
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:
|
if result.returncode != 0:
|
||||||
logging.info("Error on build!")
|
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}")
|
raise Exception(f"Validation failed for predefined model: {self.constants.computer.real_model}")
|
||||||
else:
|
else:
|
||||||
logging.info(f"Validation succeeded for predefined model: {self.constants.computer.real_model}")
|
logging.info(f"Validation succeeded for predefined model: {self.constants.computer.real_model}")
|
||||||
@@ -178,8 +179,7 @@ class PatcherValidation:
|
|||||||
|
|
||||||
if output.returncode != 0:
|
if output.returncode != 0:
|
||||||
logging.info("Failed to unmount Universal-Binaries.dmg")
|
logging.info("Failed to unmount Universal-Binaries.dmg")
|
||||||
logging.info(f"Output: {output.stdout.decode()}")
|
subprocess_wrapper.log(output)
|
||||||
logging.info(f"Return Code: {output.returncode}")
|
|
||||||
|
|
||||||
raise Exception("Failed to unmount Universal-Binaries.dmg")
|
raise Exception("Failed to unmount Universal-Binaries.dmg")
|
||||||
|
|
||||||
@@ -196,8 +196,7 @@ class PatcherValidation:
|
|||||||
|
|
||||||
if output.returncode != 0:
|
if output.returncode != 0:
|
||||||
logging.info("Failed to mount Universal-Binaries.dmg")
|
logging.info("Failed to mount Universal-Binaries.dmg")
|
||||||
logging.info(f"Output: {output.stdout.decode()}")
|
subprocess_wrapper.log(output)
|
||||||
logging.info(f"Return Code: {output.returncode}")
|
|
||||||
|
|
||||||
raise Exception("Failed to mount Universal-Binaries.dmg")
|
raise Exception("Failed to mount Universal-Binaries.dmg")
|
||||||
|
|
||||||
@@ -226,8 +225,7 @@ class PatcherValidation:
|
|||||||
|
|
||||||
if output.returncode != 0:
|
if output.returncode != 0:
|
||||||
logging.info("Failed to unmount Universal-Binaries.dmg")
|
logging.info("Failed to unmount Universal-Binaries.dmg")
|
||||||
logging.info(f"Output: {output.stdout.decode()}")
|
subprocess_wrapper.log(output)
|
||||||
logging.info(f"Return Code: {output.returncode}")
|
|
||||||
|
|
||||||
raise Exception("Failed to unmount Universal-Binaries.dmg")
|
raise Exception("Failed to unmount Universal-Binaries.dmg")
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ from ..datasets import os_data
|
|||||||
|
|
||||||
from ..support import (
|
from ..support import (
|
||||||
utilities,
|
utilities,
|
||||||
kdk_handler
|
kdk_handler,
|
||||||
|
subprocess_wrapper
|
||||||
)
|
)
|
||||||
from . import (
|
from . import (
|
||||||
sys_patch_detect,
|
sys_patch_detect,
|
||||||
@@ -126,7 +127,7 @@ class PatchSysVolume:
|
|||||||
else:
|
else:
|
||||||
if self.root_supports_snapshot is True:
|
if self.root_supports_snapshot is True:
|
||||||
logging.info("- Mounting APFS Snapshot as writable")
|
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:
|
if result.returncode == 0:
|
||||||
logging.info(f"- Mounted APFS Snapshot as writable at: {self.mount_location}")
|
logging.info(f"- Mounted APFS Snapshot as writable at: {self.mount_location}")
|
||||||
if Path(self.mount_extensions).exists():
|
if Path(self.mount_extensions).exists():
|
||||||
@@ -136,8 +137,7 @@ class PatchSysVolume:
|
|||||||
logging.info("- Root Volume appears to have unmounted unexpectedly")
|
logging.info("- Root Volume appears to have unmounted unexpectedly")
|
||||||
else:
|
else:
|
||||||
logging.info("- Unable to mount APFS Snapshot as writable")
|
logging.info("- Unable to mount APFS Snapshot as writable")
|
||||||
logging.info("Reason for mount failure:")
|
subprocess_wrapper.log(result)
|
||||||
logging.info(result.stdout.decode().strip())
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ class PatchSysVolume:
|
|||||||
if kdk_obj.kdk_already_installed is False:
|
if kdk_obj.kdk_already_installed is False:
|
||||||
# We shouldn't get here, but just in case
|
# We shouldn't get here, but just in case
|
||||||
logging.warning(f"KDK was not installed, but should have been: {kdk_obj.error_msg}")
|
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
|
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():
|
if save_hid_cs is True and cs_path.exists():
|
||||||
logging.info("- Backing up IOHIDEventDriver CodeSignature")
|
logging.info("- Backing up IOHIDEventDriver CodeSignature")
|
||||||
# Note it's a folder, not a file
|
# 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}")
|
logging.info(f"- Merging KDK with Root Volume: {kdk_path.name}")
|
||||||
utilities.elevated(
|
subprocess_wrapper.run_as_root(
|
||||||
# Only merge '/System/Library/Extensions'
|
# Only merge '/System/Library/Extensions'
|
||||||
# 'Kernels' and 'KernelSupport' is wasted space for root patching (we don't care above dev kernels)
|
# '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"],
|
["/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")
|
logging.info("- Restoring IOHIDEventDriver CodeSignature")
|
||||||
if not cs_path.exists():
|
if not cs_path.exists():
|
||||||
logging.info(" - CodeSignature folder missing, creating")
|
logging.info(" - CodeSignature folder missing, creating")
|
||||||
utilities.elevated(["/bin/mkdir", "-p", cs_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
subprocess_wrapper.run_as_root(["/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)
|
subprocess_wrapper.run_as_root(["/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/rm", "-rf", f"{self.constants.payload_path}/IOHIDEventDriver_CodeSignature.bak"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
|
||||||
def _unpatch_root_vol(self):
|
def _unpatch_root_vol(self):
|
||||||
@@ -257,11 +257,10 @@ class PatchSysVolume:
|
|||||||
logging.info("- OS version does not support snapshotting, skipping revert")
|
logging.info("- OS version does not support snapshotting, skipping revert")
|
||||||
|
|
||||||
logging.info("- Reverting to last signed APFS snapshot")
|
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:
|
if result.returncode != 0:
|
||||||
logging.info("- Unable to revert root volume patches")
|
logging.info("- Unable to revert root volume patches")
|
||||||
logging.info("Reason for unpatch Failure:")
|
subprocess_wrapper.log(result)
|
||||||
logging.info(result.stdout.decode())
|
|
||||||
logging.info("- Failed to revert snapshot via Apple's 'bless' command")
|
logging.info("- Failed to revert snapshot via Apple's 'bless' command")
|
||||||
else:
|
else:
|
||||||
self._clean_skylight_plugins()
|
self._clean_skylight_plugins()
|
||||||
@@ -363,7 +362,7 @@ class PatchSysVolume:
|
|||||||
else:
|
else:
|
||||||
args = ["/usr/sbin/kextcache", "-i", f"{self.mount_location}/"]
|
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 notes:
|
||||||
# - kextcache always returns 0, even if it fails
|
# - 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)
|
# - 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()):
|
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("- Unable to build new kernel cache")
|
||||||
logging.info(f"\nReason for Patch Failure ({result.returncode}):")
|
subprocess_wrapper.log(result)
|
||||||
logging.info(result.stdout.decode())
|
|
||||||
logging.info("")
|
logging.info("")
|
||||||
logging.info("\nPlease reboot the machine to avoid potential issues rerunning the patcher")
|
logging.info("\nPlease reboot the machine to avoid potential issues rerunning the patcher")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.skip_root_kmutil_requirement is True:
|
if self.skip_root_kmutil_requirement is True:
|
||||||
# Force rebuild the Auxiliary KC
|
# 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:
|
if result.returncode != 0:
|
||||||
logging.info("- Unable to remove kernel extension policy files")
|
logging.info("- Unable to remove kernel extension policy files")
|
||||||
logging.info(f"\nReason for Patch Failure ({result.returncode}):")
|
subprocess_wrapper.log(result)
|
||||||
logging.info(result.stdout.decode())
|
|
||||||
logging.info("")
|
logging.info("")
|
||||||
logging.info("\nPlease reboot the machine to avoid potential issues rerunning the patcher")
|
logging.info("\nPlease reboot the machine to avoid potential issues rerunning the patcher")
|
||||||
return False
|
return False
|
||||||
@@ -411,7 +408,7 @@ class PatchSysVolume:
|
|||||||
|
|
||||||
if self.root_supports_snapshot is True:
|
if self.root_supports_snapshot is True:
|
||||||
logging.info("- Creating new APFS snapshot")
|
logging.info("- Creating new APFS snapshot")
|
||||||
bless = utilities.elevated(
|
bless = subprocess_wrapper.run_as_root(
|
||||||
[
|
[
|
||||||
"/usr/sbin/bless",
|
"/usr/sbin/bless",
|
||||||
"--folder", f"{self.mount_location}/System/Library/CoreServices",
|
"--folder", f"{self.mount_location}/System/Library/CoreServices",
|
||||||
@@ -420,8 +417,7 @@ class PatchSysVolume:
|
|||||||
)
|
)
|
||||||
if bless.returncode != 0:
|
if bless.returncode != 0:
|
||||||
logging.info("- Unable to create new snapshot")
|
logging.info("- Unable to create new snapshot")
|
||||||
logging.info("Reason for snapshot failure:")
|
subprocess_wrapper.log(bless)
|
||||||
logging.info(bless.stdout.decode())
|
|
||||||
if "Can't use last-sealed-snapshot or create-snapshot on non system volume" in bless.stdout.decode():
|
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")
|
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
|
return False
|
||||||
@@ -435,7 +431,7 @@ class PatchSysVolume:
|
|||||||
"""
|
"""
|
||||||
if self.root_mount_path:
|
if self.root_mount_path:
|
||||||
logging.info("- Unmounting Root Volume (Don't worry if this fails)")
|
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:
|
else:
|
||||||
logging.info("- Skipping Root Volume unmount")
|
logging.info("- Skipping Root Volume unmount")
|
||||||
|
|
||||||
@@ -449,7 +445,7 @@ class PatchSysVolume:
|
|||||||
if self.constants.detected_os > os_data.os_data.catalina:
|
if self.constants.detected_os > os_data.os_data.catalina:
|
||||||
return
|
return
|
||||||
logging.info("- Rebuilding dyld shared cache")
|
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:
|
def _update_preboot_kernel_cache(self) -> None:
|
||||||
@@ -460,7 +456,7 @@ class PatchSysVolume:
|
|||||||
|
|
||||||
if self.constants.detected_os == os_data.os_data.catalina:
|
if self.constants.detected_os == os_data.os_data.catalina:
|
||||||
logging.info("- Rebuilding preboot kernel cache")
|
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:
|
def _clean_skylight_plugins(self) -> None:
|
||||||
@@ -470,11 +466,11 @@ class PatchSysVolume:
|
|||||||
|
|
||||||
if (Path(self.mount_application_support) / Path("SkyLightPlugins/")).exists():
|
if (Path(self.mount_application_support) / Path("SkyLightPlugins/")).exists():
|
||||||
logging.info("- Found SkylightPlugins folder, removing old plugins")
|
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))
|
subprocess_wrapper.run_as_root_and_verify(["/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/mkdir", f"{self.mount_application_support}/SkyLightPlugins"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
else:
|
else:
|
||||||
logging.info("- Creating SkylightPlugins folder")
|
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:
|
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()
|
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"]:
|
if result in ["0", "false", "1", "true"]:
|
||||||
logging.info(f"- Removing non-Metal Enforcement Preference: {arg}")
|
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:
|
def _clean_auxiliary_kc(self) -> None:
|
||||||
@@ -530,15 +526,15 @@ class PatchSysVolume:
|
|||||||
|
|
||||||
relocation_path = "/Library/Relocated Extensions"
|
relocation_path = "/Library/Relocated Extensions"
|
||||||
if not Path(relocation_path).exists():
|
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"):
|
for file in Path("/Library/Extensions").glob("*.kext"):
|
||||||
try:
|
try:
|
||||||
if datetime.fromtimestamp(file.stat().st_mtime) < datetime(2021, 10, 1):
|
if datetime.fromtimestamp(file.stat().st_mtime) < datetime(2021, 10, 1):
|
||||||
logging.info(f" - Relocating {file.name} kext to {relocation_path}")
|
logging.info(f" - Relocating {file.name} kext to {relocation_path}")
|
||||||
if Path(relocation_path) / Path(file.name).exists():
|
if Path(relocation_path) / Path(file.name).exists():
|
||||||
utilities.elevated(["/bin/rm", "-Rf", relocation_path / Path(file.name)])
|
subprocess_wrapper.run_as_root(["/bin/rm", "-Rf", relocation_path / Path(file.name)])
|
||||||
utilities.elevated(["/bin/mv", file, relocation_path])
|
subprocess_wrapper.run_as_root(["/bin/mv", file, relocation_path])
|
||||||
except:
|
except:
|
||||||
# Some users have the most cursed /L*/E* folders
|
# Some users have the most cursed /L*/E* folders
|
||||||
# ex. Symlinks pointing to symlinks pointing to dead files
|
# 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):
|
if sys_patch_helpers.SysPatchHelpers(self.constants).generate_patchset_plist(patchset, file_name, self.kdk_path):
|
||||||
logging.info("- Writing patchset information to Root Volume")
|
logging.info("- Writing patchset information to Root Volume")
|
||||||
if Path(destination_path_file).exists():
|
if Path(destination_path_file).exists():
|
||||||
utilities.process_status(utilities.elevated(["/bin/rm", destination_path_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
subprocess_wrapper.run_as_root_and_verify(["/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/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:
|
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
|
# Instead, call elevated funtion if string's boolean is True
|
||||||
if required_patches[patch]["Processes"][process] is True:
|
if required_patches[patch]["Processes"][process] is True:
|
||||||
logging.info(f"- Running Process as Root:\n{process}")
|
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:
|
else:
|
||||||
logging.info(f"- Running Process:\n{process}")
|
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"]):
|
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()
|
sys_patch_helpers.SysPatchHelpers(self.constants).disable_window_server_caching()
|
||||||
if "Metal 3802 Common Extended" in required_patches:
|
if "Metal 3802 Common Extended" in required_patches:
|
||||||
@@ -800,25 +796,25 @@ class PatchSysVolume:
|
|||||||
if file_name_str.endswith(".framework"):
|
if file_name_str.endswith(".framework"):
|
||||||
# merge with rsync
|
# merge with rsync
|
||||||
logging.info(f" - Installing: {file_name}")
|
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)
|
self._fix_permissions(destination_folder + "/" + file_name)
|
||||||
elif Path(source_folder + "/" + file_name_str).is_dir():
|
elif Path(source_folder + "/" + file_name_str).is_dir():
|
||||||
# Applicable for .kext, .app, .plugin, .bundle, all of which are directories
|
# Applicable for .kext, .app, .plugin, .bundle, all of which are directories
|
||||||
if Path(destination_folder + "/" + file_name).exists():
|
if Path(destination_folder + "/" + file_name).exists():
|
||||||
logging.info(f" - Found existing {file_name}, overwriting...")
|
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:
|
else:
|
||||||
logging.info(f" - Installing: {file_name}")
|
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)
|
self._fix_permissions(destination_folder + "/" + file_name)
|
||||||
else:
|
else:
|
||||||
# Assume it's an individual file, replace as normal
|
# Assume it's an individual file, replace as normal
|
||||||
if Path(destination_folder + "/" + file_name).exists():
|
if Path(destination_folder + "/" + file_name).exists():
|
||||||
logging.info(f" - Found existing {file_name}, overwriting...")
|
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:
|
else:
|
||||||
logging.info(f" - Installing: {file_name}")
|
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)
|
self._fix_permissions(destination_folder + "/" + file_name)
|
||||||
|
|
||||||
|
|
||||||
@@ -834,9 +830,9 @@ class PatchSysVolume:
|
|||||||
if Path(destination_folder + "/" + file_name).exists():
|
if Path(destination_folder + "/" + file_name).exists():
|
||||||
logging.info(f" - Removing: {file_name}")
|
logging.info(f" - Removing: {file_name}")
|
||||||
if Path(destination_folder + "/" + file_name).is_dir():
|
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:
|
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:
|
def _fix_permissions(self, destination_file: Path) -> None:
|
||||||
@@ -850,8 +846,8 @@ class PatchSysVolume:
|
|||||||
# Strip recursive arguments
|
# Strip recursive arguments
|
||||||
chmod_args.pop(1)
|
chmod_args.pop(1)
|
||||||
chown_args.pop(1)
|
chown_args.pop(1)
|
||||||
utilities.process_status(utilities.elevated(chmod_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
subprocess_wrapper.run_as_root_and_verify(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(chown_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
|
|
||||||
def _check_files(self) -> bool:
|
def _check_files(self) -> bool:
|
||||||
@@ -882,8 +878,7 @@ class PatchSysVolume:
|
|||||||
|
|
||||||
if output.returncode != 0:
|
if output.returncode != 0:
|
||||||
logging.info("- Failed to mount Universal-Binaries.dmg")
|
logging.info("- Failed to mount Universal-Binaries.dmg")
|
||||||
logging.info(f"Output: {output.stdout.decode()}")
|
subprocess_wrapper.log(output)
|
||||||
logging.info(f"Return Code: {output.returncode}")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logging.info("- Mounted Universal-Binaries.dmg")
|
logging.info("- Mounted Universal-Binaries.dmg")
|
||||||
@@ -923,13 +918,11 @@ class PatchSysVolume:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
logging.info("- Failed to merge DortaniaInternal resources")
|
logging.info("- Failed to merge DortaniaInternal resources")
|
||||||
logging.info(f"Output: {result.stdout.decode()}")
|
subprocess_wrapper.log(result)
|
||||||
logging.info(f"Return Code: {result.returncode}")
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
logging.info("- Failed to mount DortaniaInternal resources")
|
logging.info("- Failed to mount DortaniaInternal resources")
|
||||||
logging.info(f"Output: {result.stdout.decode()}")
|
subprocess_wrapper.log(result)
|
||||||
logging.info(f"Return Code: {result.returncode}")
|
|
||||||
|
|
||||||
if "Authentication error" not in result.stdout.decode():
|
if "Authentication error" not in result.stdout.decode():
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ from ..support import (
|
|||||||
utilities,
|
utilities,
|
||||||
updates,
|
updates,
|
||||||
global_settings,
|
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")
|
logging.info(f" - {name} checksums match, skipping")
|
||||||
continue
|
continue
|
||||||
logging.info(f" - Existing service found, removing")
|
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
|
# Create parent directories
|
||||||
if not Path(services[service]).parent.exists():
|
if not Path(services[service]).parent.exists():
|
||||||
logging.info(f" - Creating {Path(services[service]).parent} directory")
|
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))
|
subprocess_wrapper.run_as_root_and_verify(["/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/cp", service, services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
# Set the permissions on the service
|
# Set the permissions on the service
|
||||||
utilities.process_status(utilities.elevated(["/bin/chmod", "644", 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)
|
||||||
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(["/usr/sbin/chown", "root:wheel", services[service]], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
if self.constants.launcher_binary.startswith("/Library/Application Support/Dortania/"):
|
if self.constants.launcher_binary.startswith("/Library/Application Support/Dortania/"):
|
||||||
logging.info("- Skipping Patcher Install, already installed")
|
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():
|
if not Path("Library/Application Support/Dortania").exists():
|
||||||
logging.info("- Creating /Library/Application Support/Dortania/")
|
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/")
|
logging.info("- Copying OpenCore Patcher to /Library/Application Support/Dortania/")
|
||||||
if Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists():
|
if Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists():
|
||||||
logging.info("- Deleting existing OpenCore-Patcher")
|
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
|
# Strip everything after OpenCore-Patcher.app
|
||||||
path = str(self.constants.launcher_binary).split("/Contents/MacOS/OpenCore-Patcher")[0]
|
path = str(self.constants.launcher_binary).split("/Contents/MacOS/OpenCore-Patcher")[0]
|
||||||
logging.info(f"- Copying {path} to /Library/Application Support/Dortania/")
|
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():
|
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)
|
# 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
|
# We'll want to rename it to OpenCore-Patcher.app
|
||||||
path = path.split("/")[-1]
|
path = path.split("/")[-1]
|
||||||
logging.info(f"- Renaming {path} to OpenCore-Patcher.app")
|
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)
|
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 there's already an alias or exiting app, skip
|
||||||
if not Path("/Applications/OpenCore-Patcher.app").exists():
|
if not Path("/Applications/OpenCore-Patcher.app").exists():
|
||||||
logging.info("- Making app alias")
|
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:
|
def _create_rsr_monitor_daemon(self) -> bool:
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ from ..datasets import os_data
|
|||||||
from ..support import (
|
from ..support import (
|
||||||
bplist,
|
bplist,
|
||||||
generate_smbios,
|
generate_smbios,
|
||||||
utilities
|
utilities,
|
||||||
|
subprocess_wrapper
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -138,9 +139,9 @@ class SysPatchHelpers:
|
|||||||
|
|
||||||
logging.info("Disabling WindowServer Caching")
|
logging.info("Disabling WindowServer Caching")
|
||||||
# Invoke via 'bash -c' to resolve pathing
|
# 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
|
# 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:
|
# Reference:
|
||||||
# To reverse write lock:
|
# To reverse write lock:
|
||||||
# 'chflags nouchg /private/var/folders/*/*/*/WindowServer'
|
# 'chflags nouchg /private/var/folders/*/*/*/WindowServer'
|
||||||
@@ -219,9 +220,10 @@ class SysPatchHelpers:
|
|||||||
return
|
return
|
||||||
|
|
||||||
logging.info("Installing Kernel Collection syncing utility")
|
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:
|
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]):
|
def patch_gpu_compiler_libraries(self, mount_point: Union[str, Path]):
|
||||||
@@ -282,6 +284,6 @@ class SysPatchHelpers:
|
|||||||
|
|
||||||
src_dir = f"{LIBRARY_DIR}/{file.name}"
|
src_dir = f"{LIBRARY_DIR}/{file.name}"
|
||||||
if not Path(f"{DEST_DIR}/lib").exists():
|
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
|
break
|
||||||
@@ -26,6 +26,7 @@ from ..support import (
|
|||||||
utilities,
|
utilities,
|
||||||
network_handler,
|
network_handler,
|
||||||
kdk_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)
|
result = subprocess.run(["/usr/bin/hdiutil", "attach", kdk_dmg_path, "-mountpoint", mount_point, "-nobrowse"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
logging.info("Failed to mount KDK")
|
logging.info("Failed to mount KDK")
|
||||||
logging.info(result.stdout.decode("utf-8"))
|
subprocess_wrapper.log(result)
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info("Copying KDK")
|
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)
|
result = subprocess.run(["/usr/bin/hdiutil", "detach", mount_point], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
logging.info("Failed to unmount KDK")
|
logging.info("Failed to unmount KDK")
|
||||||
logging.info(result.stdout.decode("utf-8"))
|
subprocess_wrapper.log(result)
|
||||||
return
|
return
|
||||||
|
|
||||||
logging.info("Removing KDK Disk Image")
|
logging.info("Removing KDK Disk Image")
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ from ..support import (
|
|||||||
global_settings,
|
global_settings,
|
||||||
defaults,
|
defaults,
|
||||||
generate_smbios,
|
generate_smbios,
|
||||||
network_handler
|
network_handler,
|
||||||
|
subprocess_wrapper
|
||||||
)
|
)
|
||||||
from ..datasets import (
|
from ..datasets import (
|
||||||
model_array,
|
model_array,
|
||||||
@@ -1324,7 +1325,7 @@ Hardware Information:
|
|||||||
raise Exception("Test Exception")
|
raise Exception("Test Exception")
|
||||||
|
|
||||||
def on_mount_root_vol(self, event: wx.Event) -> None:
|
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()
|
wx.MessageDialog(self.parent, "Please relaunch as Root to mount the Root Volume", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||||
else:
|
else:
|
||||||
#Don't need to pass model as we're bypassing all logic
|
#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()
|
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:
|
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()
|
wx.MessageDialog(self.parent, "Please relaunch as Root to save changes", "Error", wx.OK | wx.ICON_ERROR).ShowModal()
|
||||||
else:
|
else:
|
||||||
#Don't need to pass model as we're bypassing all logic
|
#Don't need to pass model as we're bypassing all logic
|
||||||
|
|||||||
@@ -15,10 +15,12 @@ import packaging.version
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from . import gui_about
|
||||||
|
|
||||||
from .. import constants
|
from .. import constants
|
||||||
|
|
||||||
from ..wx_gui import gui_about
|
|
||||||
from ..detections import device_probe
|
from ..detections import device_probe
|
||||||
|
from ..support import subprocess_wrapper
|
||||||
|
|
||||||
from ..datasets import (
|
from ..datasets import (
|
||||||
model_array,
|
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: 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)
|
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)
|
relaunchItem.Enable(False)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ from pathlib import Path
|
|||||||
from .. import constants
|
from .. import constants
|
||||||
|
|
||||||
from ..sys_patch import sys_patch_detect
|
from ..sys_patch import sys_patch_detect
|
||||||
|
from ..support import subprocess_wrapper
|
||||||
|
|
||||||
from ..wx_gui import (
|
from ..wx_gui import (
|
||||||
gui_main_menu,
|
gui_main_menu,
|
||||||
@@ -242,7 +243,7 @@ class SysPatchDisplayFrame(wx.Frame):
|
|||||||
revert_button.Disable()
|
revert_button.Disable()
|
||||||
|
|
||||||
# Relaunch as root if not root
|
# 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)
|
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)
|
revert_button.Bind(wx.EVT_BUTTON, gui_support.RelaunchApplicationAsRoot(frame, self.constants).relaunch)
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ from ..wx_gui import (
|
|||||||
)
|
)
|
||||||
from ..support import (
|
from ..support import (
|
||||||
network_handler,
|
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
|
["/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:
|
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_animation.stop_pulse)
|
||||||
wx.CallAfter(self.progress_bar.SetValue, 0)
|
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)
|
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")
|
logging.info("User cancelled update")
|
||||||
wx.CallAfter(wx.MessageBox, "User cancelled update", "Update Cancelled", wx.OK | wx.ICON_INFORMATION)
|
wx.CallAfter(wx.MessageBox, "User cancelled update", "Update Cancelled", wx.OK | wx.ICON_INFORMATION)
|
||||||
else:
|
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(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)
|
wx.CallAfter(sys.exit, 1)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user