Exemple #1
0
 def extlink(self, path: RelPath, **kwargs: Options) -> None:
     """Link any file specified by its absolute path"""
     read_opt = self.__make_read_opt(kwargs)
     path = expanduser(expandvars(path))
     if not os.path.isabs(path):
         log_warning("'path' should be specified as an absolut path" +
                     " for extlink(). Relative paths are not forbidden" +
                     " but can cause undesired side-effects.")
     if not read_opt("optional") or os.path.exists(path):
         self.__create_link_descriptor(os.path.abspath(path), **kwargs)
Exemple #2
0
 def inspect_file(self, target: Path) -> None:
     """Checks if file is dynamic and has changed. """
     # Calculate new hash and get old has of file
     md5_calc = hashlib.md5(open(target, "rb").read()).hexdigest()
     md5_old = os.path.basename(target)[-32:]
     # Check for changes
     if is_dynamic_file(target) and md5_calc != md5_old:
         log_warning(f"You made changes to '{target}'. Those changes " +
                     "will be lost, if you don't write them back to " +
                     "the original file.")
         self.user_interaction(target)
Exemple #3
0
 def print_installed_profiles(self) -> None:
     """Shows only the profiles specified.
     If none are specified shows all."""
     if self.args.profiles:
         for profilename in self.args.profiles:
             if profilename in self.installed:
                 self.print_installed(self.installed[profilename])
             else:
                 log_warning("\nThe profile '" + profilename +
                             "' is not installed. Skipping...\n")
     else:
         for key in self.installed.keys():
             if key[0] != "@":
                 self.print_installed(self.installed[key])
Exemple #4
0
 def dryrun(self, difflog: DiffLog) -> None:
     """Runs Checks and pretty prints the DiffLog"""
     log_warning("This is just a dry-run! Nothing of this " +
                 "is actually happening.")
     difflog.run_interpreter(
         CheckProfilesI(self.installed, self.args.parent))
     tests = [
         CheckLinksI(self.installed),
         CheckLinkBlacklistI(self.args.superforce),
         CheckLinkDirsI(self.args.makedirs),
         CheckLinkExistsI(self.args.force),
         CheckDynamicFilesI(True)
     ]
     difflog.run_interpreter(*tests)
     difflog.run_interpreter(RootNeededI())
     difflog.run_interpreter(PrintI())
Exemple #5
0
 def user_interaction(self, target: Path) -> None:
     """Gives the user the ability to interact with a changed file"""
     target_bak = target + "." + constants.BACKUP_EXTENSION
     done = False
     while not done:
         inp = input("[A]bort / [I]gnore / Show [D]iff " +
                     "/ Create [P]atch / [U]ndo changes: ")
         if inp == "A":
             raise UserAbortion
         elif inp == "I":
             done = True
         elif inp == "D":
             # Create a colored diff between the file and its original
             process = Popen(["diff", "--color=auto", target_bak, target])
             process.communicate()
         elif inp == "P":
             # Create a git patch with git diff
             patch_file = os.path.join(constants.TARGET_FILES,
                                       os.path.basename(target))
             patch_file += ".patch"
             patch_file = input("Enter filename for patch [" + patch_file +
                                "]: ") or patch_file
             args = ["git", "diff", "--no-index", target_bak, target]
             process = Popen(args, stdout=PIPE)
             try:
                 with open(patch_file, "wb") as file:
                     file.write(process.stdout.read())
                 print("Patch file written successfully")
             except IOError:
                 msg = f"Could not write patch file '{patch_file}'."
                 raise PreconditionError(msg)
         elif inp == "U":
             if self.dryrun:
                 print("This does nothing this time since " +
                       "this is just a dry-run")
             else:
                 # Copy the original to the changed
                 copyfile(target_bak, target)
             done = True
         else:
             log_warning("Invalid option")
Exemple #6
0
 def check_blacklist(self, symlink_name: Path, action: str) -> None:
     """Checks if the symlink matches on a pattern in the blacklist"""
     for entry in self.blacklist:
         if re.search(entry, symlink_name):
             log_warning(f"You are trying to {action} '" + symlink_name +
                         "' which is blacklisted. It is considered " +
                         f"dangerous to {action} those files!")
             if self.superforce:
                 log_warning(f"Are you sure that you want to {action} " +
                             "a blacklisted file?")
                 confirmation = input("Type \"YES\" to confirm or " +
                                      "anything else to cancel: ")
                 if confirmation != "YES":
                     raise UserError("Canceled by user")
             else:
                 log_warning("If you really want to modify this file" +
                             " you can use the --superforce flag to" +
                             " ignore the blacklist.")
                 raise IntegrityError(f"Won't {action} blacklisted file!")
Exemple #7
0
 def _root_needed(self, operation: str, filename: Path) -> None:
     self.root_needed = True
     if (operation, filename) not in self.logged:
         log_warning("You will need to give me root permission to " +
                     operation + " '" + filename + "'.")
         self.logged.append((operation, filename))
Exemple #8
0
    def __create_link_descriptor(self,
                                 target: Path,
                                 directory: RelPath = "",
                                 **kwargs: Options) -> None:
        """Creates a link entry for current options and a given target.
        Also lets you set the dir like cd or options
        temporarily only for a link"""
        read_opt = self.__make_read_opt(kwargs)

        # Now generate the correct name for the symlink
        replace = read_opt("replace")
        if replace:  # When using regex pattern, name property is ignored
            if read_opt("name") != "":
                log_warning("'name'-property is useless if 'replace' is used")
            replace_pattern = read_opt("replace_pattern")
            if replace_pattern:
                base = os.path.basename(target)
                if "%" in base:
                    base = base.split("%", 1)[1]
                name = re.sub(replace_pattern, replace, base)
            else:
                msg = "You are trying to use 'replace', but no "
                msg += "'replace_pattern' was set."
                self.__raise_generation_error(msg)
        else:
            name = expandvars(read_opt("name"))
        # And prevent exceptions in os.symlink()
        if name and name[-1:] == "/":
            self.__raise_generation_error("name mustn't represent a directory")

        # Put together the path of the dir we create the link in
        if not directory:
            directory = self.directory  # Use the current dir
        else:
            directory = expandvars(directory)
            if directory[0] != '/':  # Path is realtive, join with current
                directory = os.path.join(self.directory, directory)
        directory = expandvars(directory)
        # Concat directory and name. The users $HOME needs to be set for this
        # when executing as root, otherwise ~ will be expanded to the home
        # directory of the root user (/root)
        name = expanduser(os.path.join(directory, name))

        # Add prefix an suffix to name
        base, ext = os.path.splitext(os.path.basename(name))
        if not base:
            # If base is empty it means that "name" was never set by the user,
            # so we fallback to use the target name (but without the tag)
            base, ext = os.path.splitext(
                os.path.basename(target.split("%", 1)[-1]))
        name = os.path.join(
            os.path.dirname(name),
            read_opt("prefix") + base + read_opt("suffix") + ext)
        name = os.path.normpath(name)

        # Get user and group id of owner
        owner = read_opt("owner")
        if owner:
            # Check for the correct format of owner
            try:
                user, group = owner.split(":")
            except ValueError:
                msg = "The owner needs to be specified in the format"
                self.__raise_generation_error(msg + 'user:group')
            try:
                uid = shutil._get_uid(user)
            except LookupError:
                msg = "You want to set the owner of '" + name + "' to '" + user
                msg += "', but there is no such user on this system."
                self.__raise_generation_error(msg)
            try:
                gid = shutil._get_gid(group)
            except LookupError:
                msg = "You want to set the owner of '" + name + "' to '"
                msg += group + "', but there is no such group on this system."
                self.__raise_generation_error(msg)
        else:
            # if no owner was specified, we need to set it
            # to the owner of the dir
            uid, gid = get_dir_owner(name)

        # Finally create the result entry
        linkdescriptor = {}
        linkdescriptor["target"] = target
        linkdescriptor["name"] = name
        linkdescriptor["uid"] = uid
        linkdescriptor["gid"] = gid
        linkdescriptor["permission"] = read_opt("permission")
        self.result["links"].append(linkdescriptor)
Exemple #9
0
            dotm.load_installed()
            dotm.execute_arguments()
    except CustomError as err:
        # An error occured that we (more or less) expected.
        # Print error, a stacktrace and exit
        logger.debug(traceback.format_exc())
        if isinstance(err, FatalError):
            logger.critical(err.message)
        else:
            logger.error(err.message)
        sys.exit(err.exitcode)
    except Exception:
        # This works because all critical parts will catch also all
        # exceptions and convert them into a CustomError
        logger.info(traceback.format_exc())
        log_warning("The error above was unexpected. But it's fine," +
                    " I haven't done anything yet :)")
        sys.exit(100)
    finally:
        # Write installed back to json file
        try:
            with open(constants.INSTALLED_FILE, "w") as file:
                file.write(json.dumps(dotm.installed, indent=4))
                file.flush()
            os.chown(constants.INSTALLED_FILE, get_uid(), get_gid())
        except Exception as err:
            unkw = UnkownError(
                err, "An unkown error occured when trying to " +
                "write all changes back to the installed-file")
            logger.error(unkw.message)