diff --git a/gui/gui_main.py b/gui/gui_main.py index 787878cf0..23fc62ced 100644 --- a/gui/gui_main.py +++ b/gui/gui_main.py @@ -7,8 +7,10 @@ import sys import webbrowser import subprocess import time +import os +import wx.adv -from resources import constants, defaults, build, install, installer, utilities, sys_patch_detect +from resources import constants, defaults, build, install, installer, utilities, sys_patch_detect, sys_patch, run from data import model_array, os_data, smbios_data from gui import menu_redirect @@ -21,6 +23,7 @@ class wx_python_gui: # Backup stdout for usage with wxPython self.stock_stdout = sys.stdout + self.stock_stderr = sys.stderr # Define Window Size self.WINDOW_WIDTH_MAIN = 350 @@ -56,9 +59,94 @@ class wx_python_gui: self.frame.DestroyChildren() self.frame.SetSize(self.WINDOW_WIDTH_MAIN, self.WINDOW_HEIGHT_MAIN) sys.stdout = self.stock_stdout + sys.stderr = self.stock_stderr - def print_test(self, text): - print(text) + def relaunch_as_root(self, event=None): + + # Add Dialog Box asking if it's ok to relaunch as root + # If yes, relaunch as root + # If no, do nothing + + # Create Dialog Box + self.dialog = wx.MessageDialog( + self.frame, + "OpenCore Legacy Patcher needs to relaunch as admin to continue. You will be prompted to enter your password.", + "Relaunch as root?", + wx.YES_NO | wx.ICON_QUESTION + ) + + # Show Dialog Box + if self.dialog.ShowModal() == wx.ID_YES: + print("Relaunching as root") + if self.constants.launcher_script is None: + args_string = f"{self.constants.launcher_binary}""" + else: + args_string = f"{self.constants.launcher_binary} {self.constants.launcher_script}" + + args = [ + "osascript", + "-e", + f'''do shell script "{args_string}"''' + ' with prompt "OpenCore Legacy Patcher needs administrator privileges to mount your EFI."' + " with administrator privileges" + " without altering line endings", + ] + + self.frame.DestroyChildren() + self.frame.SetSize(self.WINDOW_WIDTH_MAIN, self.WINDOW_HEIGHT_MAIN) + + # Header + self.header = wx.StaticText(self.frame, label="Relaunching as root") + self.header.SetFont(wx.Font(18, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) + self.header.Centre(wx.HORIZONTAL) + + # Add count down label + self.countdown_label = wx.StaticText(self.frame, label="Closing old process in 15 seconds") + self.countdown_label.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) + # Set below header + self.countdown_label.SetPosition( + ( + self.header.GetPosition().x + 3, + self.header.GetPosition().y + self.header.GetSize().height + 3 + ) + ) + self.countdown_label.Centre(wx.HORIZONTAL) + # Label: You can close this window if app finished relaunching + self.countdown_label2 = wx.StaticText(self.frame, label="You can close this window if app finished relaunching") + self.countdown_label2.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) + # Set below countdown label + self.countdown_label2.SetPosition( + ( + self.countdown_label.GetPosition().x, + self.countdown_label.GetPosition().y + self.countdown_label.GetSize().height + 3 + ) + ) + self.countdown_label2.Centre(wx.HORIZONTAL) + + # Set frame right below countdown label + self.frame.SetSize( + ( + -1, + self.countdown_label2.GetPosition().y + self.countdown_label2.GetSize().height + 40 + ) + ) + + wx.GetApp().Yield() + subprocess.Popen( + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT + ) + timer_val = 15 + while True: + wx.GetApp().Yield() + self.countdown_label.SetLabel(f"Closing old process in {timer_val} seconds") + time.sleep(1) + timer_val -= 1 + if timer_val == 0: + break + # Close Current Application + self.frame.Close() def not_yet_implemented_menu(self, event=None): self.frame.DestroyChildren() @@ -263,6 +351,8 @@ class wx_python_gui: # Reset Data in the event of re-run self.reset_window() + # Set header text + self.frame.SetTitle(f"OpenCore Legacy Patcher v{self.constants.patcher_version}") # Header self.header = wx.StaticText(self.frame, label=f"OpenCore Legacy Patcher v{self.constants.patcher_version}") self.header.SetFont(wx.Font(18, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) @@ -499,6 +589,7 @@ class wx_python_gui: self.stdout_text.Centre(wx.HORIZONTAL) self.stdout_text.SetValue("") sys.stdout=menu_redirect.RedirectText(self.stdout_text) + sys.stderr=menu_redirect.RedirectText(self.stdout_text) # Return to Main Menu self.return_to_main_menu = wx.Button(self.frame, label="Return to Main Menu") @@ -519,6 +610,7 @@ class wx_python_gui: # Reset stdout sys.stdout = self.stock_stdout + sys.stderr = self.stock_stderr def install_menu(self, event=None): self.frame.DestroyChildren() @@ -666,6 +758,7 @@ class wx_python_gui: self.stdout_text.Centre(wx.HORIZONTAL) self.stdout_text.SetValue("") sys.stdout=menu_redirect.RedirectText(self.stdout_text) + sys.stderr=menu_redirect.RedirectText(self.stdout_text) # Update frame height to right below self.frame.SetSize(self.WINDOW_WIDTH_BUILD, self.stdout_text.GetPosition().y + self.stdout_text.GetSize().height + 40) @@ -700,7 +793,7 @@ class wx_python_gui: self.frame.DestroyChildren() # Header - self.header = wx.StaticText(self.frame, label="Post-Install Menu") + self.header = wx.StaticText(self.frame, label=f"Post-Install Menu") self.header.SetFont(wx.Font(18, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) self.header.Centre(wx.HORIZONTAL) @@ -755,7 +848,12 @@ class wx_python_gui: self.patch_label.GetPosition().y + self.patch_label.GetSize().height + 10 ) ) - self.start_root_patching.Bind(wx.EVT_BUTTON, self.not_yet_implemented_menu) + uid = os.geteuid() + print(f"Effective UID: {uid}") + if uid == 0: + self.start_root_patching.Bind(wx.EVT_BUTTON, self.root_patch_start) + else: + self.start_root_patching.Bind(wx.EVT_BUTTON, self.relaunch_as_root) self.start_root_patching.Centre(wx.HORIZONTAL) if not patches: self.start_root_patching.Disable() @@ -768,7 +866,10 @@ class wx_python_gui: self.start_root_patching.GetPosition().y + self.start_root_patching.GetSize().height + 3 ) ) - self.revert_root_patches.Bind(wx.EVT_BUTTON, self.root_patch_revert) + if uid == 0: + self.revert_root_patches.Bind(wx.EVT_BUTTON, self.root_patch_revert) + else: + self.revert_root_patches.Bind(wx.EVT_BUTTON, self.relaunch_as_root) self.revert_root_patches.Centre(wx.HORIZONTAL) if self.constants.detected_os < os_data.os_data.big_sur: self.revert_root_patches.Disable() @@ -808,7 +909,7 @@ class wx_python_gui: ) self.subheader.Centre(wx.HORIZONTAL) - self.developer_note = wx.StaticText(self.frame, label="Developer Note: OCLP-CLI output will print after finishing") + self.developer_note = wx.StaticText(self.frame, label="Starting shortly") self.developer_note.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) self.developer_note.SetPosition( wx.Point( @@ -830,7 +931,7 @@ class wx_python_gui: self.text_box.SetSize( wx.Size( self.frame.GetSize().width - 10, - self.frame.GetSize().height - self.text_box.GetPosition().y + 40 + self.frame.GetSize().height + self.text_box.GetPosition().y + 80 ) ) self.text_box.Centre(wx.HORIZONTAL) @@ -850,27 +951,16 @@ class wx_python_gui: self.frame.SetSize(-1, self.return_to_main_menu.GetPosition().y + self.return_to_main_menu.GetSize().height + 40) wx.GetApp().Yield() - if self.constants.launcher_script is None: - self.text_box.AppendText("- Starting OCLP-CLI via Binary\n") - args = [self.constants.oclp_helper_path, self.constants.launcher_binary, "--patch_sys_vol"] - else: - self.text_box.AppendText("- Starting OCLP-CLI via Python\n") - args = [self.constants.oclp_helper_path, self.constants.launcher_binary, self.constants.launcher_script, "--patch_sys_vol"] - process = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT - ) + + sys.stdout = menu_redirect.RedirectText(self.text_box) + sys.stderr = menu_redirect.RedirectText(self.text_box) + wx.GetApp().Yield() + self.frame.Show() + sys_patch.PatchSysVolume(self.constants.custom_model or self.constants.computer.real_model, self.constants).start_patch() + sys.stdout = self.stock_stdout + sys.stderr = self.stock_stderr + wx.GetApp().Yield() - while True: - line = process.stdout.readline() - wx.GetApp().Yield() - if line.strip() == "": - pass - else: - self.text_box.AppendText(line) - if not line: break - process.wait() def root_patch_revert(self, event=None): self.frame.DestroyChildren() @@ -896,7 +986,7 @@ class wx_python_gui: ) self.subheader.Centre(wx.HORIZONTAL) - self.developer_note = wx.StaticText(self.frame, label="Developer Note: OCLP-CLI output will print after finishing") + self.developer_note = wx.StaticText(self.frame, label="Starting shortly") self.developer_note.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) self.developer_note.SetPosition( wx.Point( @@ -918,7 +1008,7 @@ class wx_python_gui: self.text_box.SetSize( wx.Size( self.frame.GetSize().width - 10, - self.frame.GetSize().height - self.text_box.GetPosition().y + 40 + self.frame.GetSize().height + self.text_box.GetPosition().y + 80 ) ) self.text_box.Centre(wx.HORIZONTAL) @@ -938,31 +1028,14 @@ class wx_python_gui: self.frame.SetSize(-1, self.return_to_main_menu.GetPosition().y + self.return_to_main_menu.GetSize().height + 40) # Start reverting root patches - # Grab binary path, launch second instance as CLI - # This is the cleanest way to implement admin root patching without either seperating OCLP or including duplicate code + sys.stdout = menu_redirect.RedirectText(self.text_box) + sys.stderr = menu_redirect.RedirectText(self.text_box) wx.GetApp().Yield() - if self.constants.launcher_script is None: - self.text_box.AppendText("- Starting OCLP-CLI via Binary\n") - args = [self.constants.oclp_helper_path, self.constants.launcher_binary, "--unpatch_sys_vol"] - else: - self.text_box.AppendText("- Starting OCLP-CLI via Python\n") - args = [self.constants.oclp_helper_path, self.constants.launcher_binary, self.constants.launcher_script, "--unpatch_sys_vol"] - process = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT - ) + sys_patch.PatchSysVolume(self.constants.custom_model or self.constants.computer.real_model, self.constants).start_unpatch() + sys.stdout = self.stock_stdout + sys.stderr = self.stock_stderr + wx.GetApp().Yield() - while True: - line = process.stdout.readline() - wx.GetApp().Yield() - if line.strip() == "": - pass - else: - self.text_box.AppendText(line) - if not line: break - - process.wait() def create_macos_menu(self, event=None): # Define Menu @@ -1162,8 +1235,10 @@ class wx_python_gui: # Update Label: sys.stdout=menu_redirect.RedirectLabelAll(self.download_label) + sys.stderr=menu_redirect.RedirectLabelAll(self.download_label) installer.install_macOS_installer(self.constants.payload_path) sys.stdout = self.stock_stdout + sys.stderr = self.stock_stderr # Update Label: self.download_label.SetLabel(f"Finished Installing {installer_name}") self.download_label.Centre(wx.HORIZONTAL) @@ -1383,34 +1458,14 @@ class wx_python_gui: print("- Starting creation script as admin") wx.GetApp().Yield() time.sleep(1) - sys.stdout=menu_redirect.RedirectText(self.stdout_text) - cim_start = subprocess.Popen( - [self.constants.oclp_helper_path, "/bin/sh", self.constants.installer_sh_path], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT - ) - - wx.GetApp().Yield() - while True: - line = cim_start.stdout.readline() - wx.GetApp().Yield() - if line.strip() == "": - pass - else: - self.stdout_text.AppendText(line) - if not line: break - - cim_start.wait() - - if cim_start.returncode == 0: - print("Installer created successfully!") - else: - print("Installer creation failed") - print(f"Return Code {cim_start.returncode}") - sys.stdout = self.stock_stdout + args = [self.constants.oclp_helper_path, "/bin/sh", self.constants.installer_sh_path] + sys.stdout = menu_redirect.RedirectText(self.stdout_text) + sys.stderr = menu_redirect.RedirectText(self.stdout_text) + run.Run()._stream_output(comm=args) else: print("- Failed to create installer script") - sys.stdout = self.stock_stdout + sys.stdout = self.stock_stdout + sys.stderr = self.stock_stderr def settings_menu(self, event=None): @@ -1430,7 +1485,7 @@ class wx_python_gui: self.frame.DestroyChildren() self.frame.SetSize(self.WINDOW_SETTINGS_WIDTH, self.WINDOW_SETTINGS_HEIGHT) - self.frame.SetLabel("Settings") + self.frame.SetTitle("Settings") # Header self.header = wx.StaticText(self.frame, label="Settings") @@ -1769,12 +1824,20 @@ class wx_python_gui: self.apple_alc_checkbox.GetPosition().x, self.apple_alc_checkbox.GetPosition().y + self.apple_alc_checkbox.GetSize().height)) + # Button: Developer Debug Info + self.debug_button = wx.Button(self.frame, label="Developer Debug Info") + self.debug_button.Bind(wx.EVT_BUTTON, self.additional_info_menu) + self.debug_button.SetPosition(wx.Point( + self.set_writeflash_checkbox.GetPosition().x, + self.set_writeflash_checkbox.GetPosition().y + self.set_writeflash_checkbox.GetSize().height)) + self.debug_button.Center(wx.HORIZONTAL) + # Button: return to main menu self.return_to_main_menu_button = wx.Button(self.frame, label="Return to Main Menu") self.return_to_main_menu_button.Bind(wx.EVT_BUTTON, self.main_menu) self.return_to_main_menu_button.SetPosition(wx.Point( - self.set_writeflash_checkbox.GetPosition().x, - self.set_writeflash_checkbox.GetPosition().y + self.set_writeflash_checkbox.GetSize().height + 10)) + self.debug_button.GetPosition().x, + self.debug_button.GetPosition().y + self.debug_button.GetSize().height + 10)) self.return_to_main_menu_button.Center(wx.HORIZONTAL) # set frame size below return to main menu button @@ -1973,3 +2036,103 @@ class wx_python_gui: selection = self.smbios_model_dropdown.GetStringSelection() print(f"SMBIOS Spoof Model: {selection}") self.constants.override_smbios = selection + + def additional_info_menu(self, event=None): + # Define Menu: + + # Header: Additional Info + # Label: Model Dump + # Textbox: Model Dump + # Label: Real User ID + # Label: Effective User ID + # Label: Launcher Binary + # Textbox: Launcher Binary + # Label: Launcher Script + # Textbox: Launcher Script + + self.frame.DestroyChildren() + + # Header: Additional Info + self.additional_info_header = wx.StaticText(self.frame, label="Developer Debug Info", pos=wx.Point(10, 10)) + self.additional_info_header.SetFont(wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)) + self.additional_info_header.Center(wx.HORIZONTAL) + + # Label: Real User ID + self.real_user_id_label = wx.StaticText(self.frame, label=f"Current UID: {os.getuid()} - ({os.geteuid()})") + self.real_user_id_label.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) + self.real_user_id_label.SetPosition( + wx.Point(self.additional_info_header.GetPosition().x, self.additional_info_header.GetPosition().y + self.additional_info_header.GetSize().height + 10) + ) + self.real_user_id_label.Center(wx.HORIZONTAL) + + # Label: Model Dump + self.model_dump_label = wx.StaticText(self.frame, label="Model Dump") + self.model_dump_label.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) + self.model_dump_label.SetPosition( + wx.Point(self.real_user_id_label.GetPosition().x, self.real_user_id_label.GetPosition().y + self.real_user_id_label.GetSize().height + 10) + ) + self.model_dump_label.Center(wx.HORIZONTAL) + + # Textbox: Model Dump + self.model_dump_textbox = wx.TextCtrl(self.frame, style=wx.TE_MULTILINE, pos=wx.Point(self.model_dump_label.GetPosition().x, self.model_dump_label.GetPosition().y + self.model_dump_label.GetSize().height + 10)) + self.model_dump_textbox.SetValue(str(self.constants.computer)) + self.model_dump_textbox.SetPosition( + wx.Point(self.model_dump_label.GetPosition().x, self.model_dump_label.GetPosition().y + self.model_dump_label.GetSize().height + 10) + ) + self.model_dump_textbox.SetSize( + wx.Size( + self.frame.GetSize().width - 5, + self.model_dump_textbox.GetSize().height + self.model_dump_textbox.GetSize().height + ) + ) + self.model_dump_textbox.Center(wx.HORIZONTAL) + self.model_dump_textbox.SetEditable(False) + + + + # Label: Launcher Binary + self.launcher_binary_label = wx.StaticText(self.frame, label="Launcher Binary") + self.launcher_binary_label.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) + self.launcher_binary_label.SetPosition( + wx.Point(self.model_dump_textbox.GetPosition().x, self.model_dump_textbox.GetPosition().y + self.model_dump_textbox.GetSize().height + 10) + ) + self.launcher_binary_label.Center(wx.HORIZONTAL) + + # Textbox: Launcher Binary + self.launcher_binary_textbox = wx.TextCtrl(self.frame, style=wx.TE_MULTILINE, pos=wx.Point(self.launcher_binary_label.GetPosition().x, self.launcher_binary_label.GetPosition().y + self.launcher_binary_label.GetSize().height + 10)) + self.launcher_binary_textbox.SetValue(self.constants.launcher_binary) + self.launcher_binary_textbox.SetPosition( + wx.Point(self.launcher_binary_label.GetPosition().x, self.launcher_binary_label.GetPosition().y + self.launcher_binary_label.GetSize().height + 10) + ) + self.launcher_binary_textbox.SetSize(wx.Size(self.frame.GetSize().width - 5, 50)) + self.launcher_binary_textbox.Center(wx.HORIZONTAL) + self.launcher_binary_textbox.SetEditable(False) + + # Label: Launcher Script + self.launcher_script_label = wx.StaticText(self.frame, label="Launcher Script") + self.launcher_script_label.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) + self.launcher_script_label.SetPosition( + wx.Point(self.launcher_binary_textbox.GetPosition().x, self.launcher_binary_textbox.GetPosition().y + self.launcher_binary_textbox.GetSize().height + 10) + ) + self.launcher_script_label.Center(wx.HORIZONTAL) + + # Textbox: Launcher Script + self.launcher_script_textbox = wx.TextCtrl(self.frame, style=wx.TE_MULTILINE, pos=wx.Point(self.launcher_script_label.GetPosition().x, self.launcher_script_label.GetPosition().y + self.launcher_script_label.GetSize().height + 10)) + self.launcher_script_textbox.SetValue(str(self.constants.launcher_script)) + self.launcher_script_textbox.SetPosition( + wx.Point(self.launcher_script_label.GetPosition().x, self.launcher_script_label.GetPosition().y + self.launcher_script_label.GetSize().height + 10) + ) + self.launcher_script_textbox.SetSize(wx.Size(self.frame.GetSize().width - 5, 60)) + self.launcher_script_textbox.Center(wx.HORIZONTAL) + self.launcher_script_textbox.SetEditable(False) + + # Return to Main Menu Button + self.return_to_main_menu_button = wx.Button(self.frame, label="Return to Main Menu") + self.return_to_main_menu_button.SetPosition( + wx.Point(self.launcher_script_textbox.GetPosition().x, self.launcher_script_textbox.GetPosition().y + self.launcher_script_textbox.GetSize().height + 10) + ) + self.return_to_main_menu_button.Bind(wx.EVT_BUTTON, self.main_menu) + self.return_to_main_menu_button.Center(wx.HORIZONTAL) + + # Set frame below return to main menu button + self.frame.SetSize(wx.Size(-1, self.return_to_main_menu_button.GetPosition().y + self.return_to_main_menu_button.GetSize().height + 40)) \ No newline at end of file diff --git a/gui/menu_redirect.py b/gui/menu_redirect.py index b6e79d8a9..4fe58f779 100644 --- a/gui/menu_redirect.py +++ b/gui/menu_redirect.py @@ -1,4 +1,5 @@ import wx +import time class RedirectText(object): def __init__(self,aWxTextCtrl): @@ -7,6 +8,7 @@ class RedirectText(object): def write(self,string): self.out.WriteText(string) wx.GetApp().Yield() + time.sleep(0.01) def fileno(self): return 1 @@ -23,6 +25,7 @@ class RedirectLabel(object): self.out.SetLabel(string) self.out.Centre(wx.HORIZONTAL) wx.GetApp().Yield() + time.sleep(0.01) def flush(self): pass @@ -34,4 +37,5 @@ class RedirectLabelAll(object): def write(self,string): self.out.SetLabel(string) self.out.Centre(wx.HORIZONTAL) - wx.GetApp().Yield() \ No newline at end of file + wx.GetApp().Yield() + time.sleep(0.01) \ No newline at end of file diff --git a/resources/run.py b/resources/run.py new file mode 100644 index 000000000..7d38a2dbe --- /dev/null +++ b/resources/run.py @@ -0,0 +1,155 @@ +# Module for running processes with real time output +# Written by CorpNewt +# Source: https://github.com/corpnewt/pymodules/blob/884c3de15b6a2570afde52fe8a14a3e946ffb18a/run.py + +import sys, subprocess, time, threading, shlex +try: + from Queue import Queue, Empty +except: + from queue import Queue, Empty + +ON_POSIX = 'posix' in sys.builtin_module_names + +class Run: + + def __init__(self): + return + + def _read_output(self, pipe, q): + try: + for line in iter(lambda: pipe.read(1), b''): + q.put(line) + except ValueError: + pass + pipe.close() + + def _create_thread(self, output): + # Creates a new queue and thread object to watch based on the output pipe sent + q = Queue() + t = threading.Thread(target=self._read_output, args=(output, q)) + t.daemon = True + return (q,t) + + def _stream_output(self, comm, shell = False): + output = error = "" + p = None + try: + if shell and type(comm) is list: + comm = " ".join(shlex.quote(x) for x in comm) + if not shell and type(comm) is str: + comm = shlex.split(comm) + p = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, universal_newlines=True, close_fds=ON_POSIX) + # Setup the stdout thread/queue + q,t = self._create_thread(p.stdout) + qe,te = self._create_thread(p.stderr) + # Start both threads + t.start() + te.start() + + while True: + c = z = "" + try: c = q.get_nowait() + except Empty: pass + else: + sys.stdout.write(c) + output += c + sys.stdout.flush() + try: z = qe.get_nowait() + except Empty: pass + else: + sys.stderr.write(z) + error += z + sys.stderr.flush() + if not c==z=="": continue # Keep going until empty + # No output - see if still running + p.poll() + if p.returncode != None: + # Subprocess ended + break + # No output, but subprocess still running - stall for 20ms + time.sleep(0.02) + + o, e = p.communicate() + return (output+o, error+e, p.returncode) + except: + if p: + try: o, e = p.communicate() + except: o = e = "" + return (output+o, error+e, p.returncode) + return ("", "Command not found!", 1) + + def _decode(self, value, encoding="utf-8", errors="ignore"): + # Helper method to only decode if bytes type + if sys.version_info >= (3,0) and isinstance(value, bytes): + return value.decode(encoding,errors) + return value + + def _run_command(self, comm, shell = False): + c = None + try: + if shell and type(comm) is list: + comm = " ".join(shlex.quote(x) for x in comm) + if not shell and type(comm) is str: + comm = shlex.split(comm) + p = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + c = p.communicate() + except: + if c == None: + return ("", "Command not found!", 1) + return (self._decode(c[0]), self._decode(c[1]), p.returncode) + + def run(self, command_list, leave_on_fail = False): + # Command list should be an array of dicts + if type(command_list) is dict: + # We only have one command + command_list = [command_list] + output_list = [] + for comm in command_list: + args = comm.get("args", []) + shell = comm.get("shell", False) + stream = comm.get("stream", False) + sudo = comm.get("sudo", False) + stdout = comm.get("stdout", False) + stderr = comm.get("stderr", False) + mess = comm.get("message", None) + show = comm.get("show", False) + + if not mess == None: + print(mess) + + if not len(args): + # nothing to process + continue + if sudo: + # Check if we have sudo + out = self._run_command(["which", "sudo"]) + if "sudo" in out[0]: + # Can sudo + if type(args) is list: + args.insert(0, out[0].replace("\n", "")) # add to start of list + elif type(args) is str: + args = out[0].replace("\n", "") + " " + args # add to start of string + + if show: + print(" ".join(args)) + + if stream: + # Stream it! + out = self._stream_output(args, shell) + else: + # Just run and gather output + out = self._run_command(args, shell) + if stdout and len(out[0]): + print(out[0]) + if stderr and len(out[1]): + print(out[1]) + # Append output + output_list.append(out) + # Check for errors + if leave_on_fail and out[2] != 0: + # Got an error - leave + break + if len(output_list) == 1: + # We only ran one command - just return that output + return output_list[0] + return output_list \ No newline at end of file diff --git a/resources/sys_patch.py b/resources/sys_patch.py index f7dddeff1..e6857b778 100644 --- a/resources/sys_patch.py +++ b/resources/sys_patch.py @@ -83,9 +83,12 @@ class PatchSysVolume: self.unpatch_root_vol() return True else: + print(f"User ID: {os.getuid()} - {os.geteuid()}") if self.constants.detected_os > os_data.os_data.catalina: print("- Mounting APFS Snapshot as writable") - utilities.elevated(["mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE).stdout.decode().strip().encode() + result = utilities.elevated(["mount", "-o", "nobrowse", "-t", "apfs", f"/dev/{self.root_mount_path}", self.mount_location], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if result.returncode == 0: + print(f"- Mounted APFS Snapshot as writable at: {self.mount_location}") if Path(self.mount_extensions).exists(): print("- Successfully mounted the Root Volume") if patch is True: @@ -272,7 +275,7 @@ class PatchSysVolume: print(bless.stdout.decode()) if "Can't use last-sealed-snapshot or create-snapshot on non system volume" in bless.stdout.decode(): print("- This is an APFS bug with Monterey! Perform a clean installation to ensure your APFS volume is built correctly") - sys.exit(1) + return else: self.unmount_drive() else: