def _generate_file(self): """Decrypts the first file in :attr:`self.sources<dyanmicfile.sources>` using gpg. Returns: bytearray: The content of the decrypted file """ # Get sources and temp file encryped_file = self.sources[0] tmp = os.path.join(self.getdir(), self.name) # Set arguments for OpenPGP args = ["gpg", "-q", "-d", "--yes"] strargs = " ".join(args) if constants.DECRYPT_PWD: args += ["--batch", "--passphrase", constants.DECRYPT_PWD] strargs += " " + " ".join(args[-3:-1]) + " " strargs += "*" * len(constants.DECRYPT_PWD) else: log("Tipp: You can set a password in uberdots " + "config that will be used for all encrypted files.") args += ["-o", tmp, encryped_file] strargs += " " + " ".join(args[-3:]) log_debug("Invoking OpenPGP with '" + strargs + "'") # Use OpenPGP to decrypt the file process = Popen(args, stdin=PIPE) process.communicate() # Remove the decrypted file. It will be written by the update function # of the super class to its correct location. result = open(tmp, "rb").read() os.remove(tmp) return result
def handle_custom_error(err): # An error occured that we (more or less) expected. # Print error, a stacktrace and exit if isinstance(err, FatalError): logger.critical(traceback.format_exc()) logger.critical(err.message + "\n") else: log_debug(traceback.format_exc()) log_error(err.message) sys.exit(err.EXITCODE)
def getdir(self): """Gets the path of the directory that is used to store the generated file. Returns: str: The path to the directory """ path = normpath(os.path.join(constants.DATA_DIR, self.SUBDIR)) # Create dir if it doesn't exist if not os.path.isdir(path): log_debug("Creating directory '" + path + "'") os.mkdir(path) return path
def update(self): """Generates the newest version of the file and writes it if it is not in its subdir yet.""" # Generate file and calc checksum file_bytes = self._generate_file() self.md5sum = md5(file_bytes) # If this version of the file (with same checksum) doesn't exist, # write it to the correct location if not os.path.isfile(self.getpath()): log_debug("Writing dynamic file '" + self.getpath() + "'.") file = open(self.getpath(), "wb") file.write(file_bytes) file.flush() # Also create a backup that can be used to restore the original copyfile(self.getpath(), self.getpath() + "." + constants.BACKUP_EXTENSION)
def load_installed(self): """Reads the installed-file and parses it's content into :attr:`self.installed<UberDot.installed>`. Raises: :class:`~errors.PreconditionError`: uberdot and installed-file aren't version compatible. """ try: self.installed = json.load(open(constants.INSTALLED_FILE)) except FileNotFoundError: log_debug("No installed profiles found.") # Check installed-file version if (int(self.installed["@version"].split("_")[1]) != int( constants.VERSION.split("_")[1])): msg = "There was a change of the installed-file schema " msg += "with the last update. Please revert to version " msg += self.installed["@version"] + " and uninstall " msg += "all of your profiles before using this version." raise PreconditionError(msg)
def generator(self): """This is the wrapper for :func:`generate()`. It overwrites the builtins and maps it own commands to them. :func:`generate()` must not be called without this wrapper. .. warning:: Do NOT call this from within the same profile, only from outside!! Returns: dict: The result dictionary :attr:`self.result<Profile.result>` """ if self.executed: self._gen_err("A profile can be only generated " + "one time to prevent side-effects!") self.executed = True self.__set_builtins() try: log_debug("Generating event scripts for profile '" + self.name + "'.") self.__generate_scripts() log_debug("Generating profile '" + self.name + "'.") self.generate() log_debug("Successfully generated profile '" + self.name + "'.") except Exception as err: if isinstance(err, CustomError): raise msg = "An unkown error occured in your generate() function: " self._gen_err(msg + type(err).__name__ + ": " + str(err)) finally: self.__reset_builtins() return self.result
def dryrun(self, difflog): """Like `run()` but instead of resolving it it will be just printed out Args: difflog (DiffLog): The DiffLog that will be checked Raises: :class:`~errors.CustomError`: Executed interpreters can and will raise all kinds of :class:`~errors.CustomError`. """ log_warning("This is just a dry-run! Nothing of the following " + "is actually happening.") # Run tests log_debug("Checking operations for errors and conflicts.") difflog.run_interpreter( CheckProfilesInterpreter(self.installed, self.args.parent)) tests = [ CheckLinksInterpreter(self.installed), CheckLinkBlacklistInterpreter(self.args.superforce), CheckLinkDirsInterpreter(self.args.makedirs), CheckLinkExistsInterpreter(self.args.force), CheckDynamicFilesInterpreter(True) ] difflog.run_interpreter(*tests) log_debug("Checking if root would be needed") difflog.run_interpreter(RootNeededInterpreter()) # Simulate events before if not self.args.skipevents and not self.args.skipbefore: difflog.run_interpreter( EventPrintInterpreter(self.profiles, self.installed, "before")) # Simulate execution difflog.run_interpreter(PrintInterpreter()) # Simulate events after if not self.args.skipevents and not self.args.skipafter: difflog.run_interpreter( EventPrintInterpreter(self.profiles, self.installed, "after"))
def execute_arguments(self): """Executes whatever was specified via commandline arguments.""" # Check which mode, then run it if self.args.show: self.print_installed_profiles() elif self.args.version: print(constants.BOLD + "Version: " + constants.ENDC + constants.VERSION) elif self.args.debuginfo: self.print_debuginfo() else: # The above are modes that just print stuff, but here we # have to actually do something: # 1. Decide how to solve the differences if self.args.uninstall: dfs = UninstallDiffSolver(self.installed, self.args.profiles) elif self.args.install: self.execute_profiles() profile_results = [p.result for p in self.profiles] dfs = UpdateDiffSolver(self.installed, profile_results, self.args.parent) # elif TODO history resolve... else: raise FatalError("None of the expected modes were set") # 2. Solve differences log_debug("Calculate operations for linking process.") dfl = dfs.solve() # 3. Eventually manipulate the result if self.args.dui: log_debug("Reordering operations according to --dui.") dfl.run_interpreter(DUIStrategyInterpreter()) if self.args.skiproot: log_debug("Removing operations that require root.") dfl.run_interpreter(SkipRootInterpreter()) # 4. Simmulate a run, print the result or actually resolve the # differences if self.args.dryrun: self.dryrun(dfl) elif self.args.plain: dfl.run_interpreter(PlainPrintInterpreter()) elif self.args.print: dfl.run_interpreter(PrintInterpreter()) else: self.run(dfl)
def gen_script(event_name, script): def get_prepare_scripts(profile=self, profilename=self.name): result = "" # First check if prepare_script is available and is a string if profile.prepare_script is not None: if isinstance(profile.prepare_script, str): # Save prepare_script as result result = profile.prepare_script else: self._gen_err("prepare_script of " + profilename + " needs to be a string.") # Prepend prepare_scripts of parents to result if profile.parent is not None: result = get_prepare_scripts(profile.parent, profilename) + result return result # Change dir automatically if enabled and the main script doesn't # start with a cd command if constants.SMART_CD: if not script.strip().startswith("cd "): script = "\ncd " + self.directory + "\n" + script # Prepend prepare_scripts script = get_prepare_scripts() + "\n" + script # Prettify script a little bit pretty_script = "" start = 0 end = 0 i = 0 for line in script.splitlines(): line = line.strip() if line: if start == 0: start = i end = i pretty_script += line + "\n" i += 1 # Remove empty lines at beginning and end of script pretty_script = "\n".join(pretty_script.splitlines()[start:end + 1]) # Build path where the script will be stored script_dir = os.path.join(constants.DATA_DIR, "scripts") if not os.path.exists(script_dir): os.mkdir(script_dir) script_name = self.name + "_" + event_name script_path = script_dir + "/" + script_name script_path += "_" + md5(pretty_script) + ".sh" # Write new script to file if not os.path.exists(script_path): try: script_file = open(script_path, "w") script_file.write(pretty_script) script_file.close() except IOError: self._gen_err("Could not write file '" + script_path + "'") log_debug("Generated script '" + script_path + "'") # Create symlink for easy access of latest generated script for event link_path = script_dir + "/" + script_name if os.path.exists(link_path): os.remove(link_path) os.symlink(script_path, link_path)
def run(self, difflog): """Performs checks on DiffLog and resolves it. Furthermore this function handles backups, converts exceptions into UnkownErrors and might replace the entire process when uberdot was started with insufficient permissions. Args: difflog (DiffLog): The DiffLog that will be resolved. Raises: :class:`~errors.UnkownError`: All exceptions that are no :class:`~errors.CustomError` and occured in the critical section will be converted to this error. :class:`~errors.CustomError`: Executed interpreters can and will raise all kinds of :class:`~errors.CustomError`. """ # Run integration tests on difflog log_debug("Checking operations for errors and conflicts.") difflog.run_interpreter( CheckProfilesInterpreter(self.installed, self.args.parent)) tests = [ CheckLinksInterpreter(self.installed), CheckLinkDirsInterpreter(self.args.makedirs), CheckLinkExistsInterpreter(self.args.force), CheckDynamicFilesInterpreter(False) ] difflog.run_interpreter(*tests) # Gain root if needed if not has_root_priveleges(): log_debug("Checking if root is needed") difflog.run_interpreter(GainRootInterpreter()) else: log_debug("uberdot was started with root priveleges") # Check blacklist not until now, because the user would need confirm it # twice if the programm is restarted with sudo difflog.run_interpreter( CheckLinkBlacklistInterpreter(self.args.superforce)) # Now the critical part begins, devided into three main tasks: # 1. running events before, 2. linking, 3. running events after # Each part is surrounded with a try-catch block that wraps every # exception which isn't a CustomError into UnkownError and reraises them # to handle them in the outer pokemon handler old_installed = dict(self.installed) # Execute all events before linking and print them try: if not self.args.skipevents and not self.args.skipbefore: difflog.run_interpreter( EventExecInterpreter(self.profiles, old_installed, "before")) try: # We need to run those tests again because the executed event # might have f****d with some links or dynamic files difflog.run_interpreter( CheckLinkExistsInterpreter(self.args.force), CheckDynamicFilesInterpreter(False)) except CustomError as err: # We add some additional information to the raised errors err._message += "This error occured because at least one of " err._message += "the previously executed events interfered " err._message += "with files that are defined by a profile." raise err except CustomError: raise except Exception as err: msg = "An unkown error occured during before_event execution." raise UnkownError(err, msg) # Execute operations from difflog try: # Create Backup in case something wents wrong, # so the user can fix the mess we caused if os.path.isfile(constants.INSTALLED_FILE): shutil.copyfile(constants.INSTALLED_FILE, constants.INSTALLED_FILE_BACKUP) # Apply difflog operations and print them simultaneously difflog.run_interpreter( ExecuteInterpreter(self.installed, self.args.force), PrintInterpreter()) # Remove Backup if os.path.isfile(constants.INSTALLED_FILE_BACKUP): os.remove(constants.INSTALLED_FILE_BACKUP) log_success("Updated links successfully.") except CustomError: raise except Exception as err: msg = "An unkown error occured during linking/unlinking. Some " msg += "links or your installed-file may be corrupted! Check the " msg += "backup of your installed-file to resolve all possible " msg += "issues before you proceed to use this tool!" raise UnkownError(err, msg) # Execute all events after linking and print them try: if not self.args.skipevents and not self.args.skipafter: difflog.run_interpreter( EventExecInterpreter(self.profiles, old_installed, "after")) except CustomError: raise except Exception as err: msg = "An unkown error occured during after_event execution." raise UnkownError(err, msg)