Esempio n. 1
0
    def handle(self, args: dict) -> (int, None):
        """
        Handle run the command
        first, validates arguments
        next, checks if --help inserted, show command help
        if not, run command

        Args:
            args: commands as dictonary
                  example:
                  {'arguments': list, 'options': dict{ '--foo': 'value', '-x': None } }
                  (this can be generated by )

        Returns:
            returns command exit code as int, or None value (None means 0 exit code)
        """
        self.validate(args)

        # handle --help option
        if self.has_option('--help'):
            help_cmd = HelpCommand.HelpCommand()
            return help_cmd.handle(ArgParser.parse(['cati', 'help',
                                                    self.name]))

        return self.run()
Esempio n. 2
0
def handle(argv: list):
    """
    handle cli
    gets argv and runs entered command as subcommand (if not subcommand inserted, runs help command as default)

    Args:
        argv: the program arguments as list
    """
    # parse inserted arguments
    parsed_args = ArgParser.parse(argv)

    # find called subcommand by args
    if len(parsed_args['arguments']) == 0:
        # no subcommand called
        # call help command as default
        parsed_args['arguments'].append('help')

    try:
        command = commands[parsed_args['arguments'][0]]
    except:
        pr.e(sys.argv[0] + ': unknow command "' + parsed_args['arguments'][0] +
             '"')
        pr.exit(1)

    cmdobj = command()
    sys.exit(cmdobj.handle(parsed_args))
Esempio n. 3
0
 def run_command(self, command_name: str, arguments=[]):
     """ Runs cmdline command """
     arguments.insert(0, 'cati')
     arguments.insert(1, command_name)
     cmd = commands[command_name]()
     out = cmd.handle(ArgParser.parse(arguments))
     if out == None:
         out = 0
     return int(out)
Esempio n. 4
0
    def run(self):
        """ Run command """

        options = []
        if self.has_option('-y') or self.has_option('--yes'):
            options = ['-y']

        update_cmd = UpdateCommand()
        res = update_cmd.handle(ArgParser.parse(['cati', 'update']))
        if res != 0 and res != None:
            return res

        upgrade_cmd = UpgradeCommand()
        res = upgrade_cmd.handle(ArgParser.parse(['cati', 'upgrade',
                                                  *options]))
        if res != 0 and res != None:
            return res

        autoremove_cmd = AutoremoveCommand()
        res = autoremove_cmd.handle(
            ArgParser.parse(['cati', 'autoremove', *options]))
        if res != 0 and res != None:
            return res
Esempio n. 5
0
    def run(self):
        """ Run command """

        # join all of arguments as one argument
        full_query_string = ''
        if len(self.arguments) > 1:
            for arg in self.arguments:
                full_query_string += arg + ' '
            full_query_string = full_query_string[:len(full_query_string) - 1]
        else:
            full_query_string = self.arguments[0]

        # search arguments with `list --search` command
        arguments = ['--search=' + full_query_string]
        arguments.insert(0, 'cati')
        arguments.insert(1, 'list')
        list_command = ListCommand()
        return list_command.handle(ArgParser.parse(arguments))
Esempio n. 6
0
    def run(self):
        """ Run command """

        if not self.is_quiet():
            pr.p('Checking unused packages...')

        self.unused_packages = []
        self.find_unused_packages()
        unused_packages = self.unused_packages

        package_names = [pkg.data['name'] for pkg in unused_packages]
        options = [op for op in self.args['options']]

        if not package_names:
            pr.p('There is not any unused package')
            return 0

        remove_cmd = RemoveCommand()
        return remove_cmd.handle(
            ArgParser.parse(['cati', 'remove', *package_names, *options]))
Esempio n. 7
0
    def run(self):
        """ Run command """

        # require root permission
        require_root_permission()

        result_code = 0

        packages_to_reinstall = []

        if not self.is_quiet():
            pr.p('Starting checking system health and security...')
            pr.p('===============================================')

        # check state
        state_cmd = StateCommand()
        out = state_cmd.handle(ArgParser.parse(['cati', 'state']))
        if out > 0:
            return out

        # search for conflict and dependency corruptions
        if not self.is_quiet():
            pr.p('Checking dependency and conflict corruptions...')
        dependency_problems = []
        conflict_problems = []
        installed_packages = Pkg.installed_list()['list']
        for pkg in installed_packages:
            if self.is_verbose():
                pr.p('[info] checking dependencies and conflicts for ' +
                     pkg.data['name'] + '...')
            for dp in pkg.get_depends():
                if not Pkg.check_state(dp):
                    dependency_problems.append([pkg, dp])
            for conflict in pkg.get_conflicts():
                if Pkg.check_state(conflict):
                    conflict_problems.append([pkg, conflict])

        if dependency_problems or conflict_problems:
            for depend in dependency_problems:
                pr.p(ansi.red + 'dependency problem for ' +
                     depend[0].data['name'] + ': ' + depend[1] + ansi.reset)
                packages_to_reinstall.append(depend[0])
            for conflict in conflict_problems:
                pr.p(ansi.red + 'conflict problem for ' +
                     conflict[0].data['name'] + ': ' + conflict[1] +
                     ansi.reset)
                packages_to_reinstall.append(conflict[0])
            result_code = 1
        else:
            pr.p(
                ansi.green +
                'There is not any conflict or dependnecy problem and everything is ok'
                + ansi.reset)

        # check static files
        if not self.is_quiet():
            pr.p('Checking packages static files...')
        staticfile_problems = []
        for pkg in installed_packages:
            if self.is_verbose():
                pr.p('[info] checking static files for ' + pkg.data['name'] +
                     '...')
            files = pkg.installed_static_files()
            for f in files:
                f[1] = Env.base_path(f[1])
                if os.path.isfile(f[1]):
                    wanted_hash = f[0]
                    current_hash = calc_file_sha256(f[1])
                    if wanted_hash != current_hash:
                        staticfile_problems.append([pkg, f, 'file is changed'])
                else:
                    staticfile_problems.append([pkg, f, 'file is deleted'])
        if staticfile_problems:
            for problem in staticfile_problems:
                pr.p(ansi.red + 'staticfile problem in package ' +
                     problem[0].data['name'] + ': ' + problem[1][1] + ': ' +
                     problem[2] + ansi.reset)
                packages_to_reinstall.append(problem[0])
            result_code = 1
        else:
            pr.p(ansi.green + 'all of static files are ok' + ansi.reset)

        # check repos config files health
        if not self.is_quiet():
            pr.p('Checking cati configuration files...')
        if self.is_verbose():
            pr.p('[info] checking repositories config...')
        repos = Repo.get_list()
        pr.p(ansi.red, end='')
        ReposListErrorShower.show(repos)
        pr.p(ansi.reset, end='')
        is_any_repo_error = False
        for repo in repos:
            if repo.syntax_errors:
                is_any_repo_error = True
                result_code = 1
        if not is_any_repo_error:
            pr.p(ansi.green + 'all of cati configuration files are ok' +
                 ansi.reset)

        # check database files
        if not self.is_quiet():
            pr.p('Checking cati database...')
        database_problems = []
        for f in os.listdir(Env.installed_lists()):
            if self.is_verbose():
                pr.p('[info] checking database install dir for ' + f + '...')
            if not os.path.isfile(Env.installed_lists(
                    '/' + f + '/files')) or not os.path.isfile(
                        Env.installed_lists('/' + f + '/ver')):
                database_problems.append(
                    'installed packages database: directory ' +
                    Env.installed_lists('/' + f) + ' is corrupt')
        for f in os.listdir(Env.security_blacklist()):
            if self.is_verbose():
                pr.p('[info] checking security blacklist part ' + f + '...')
            if not os.path.isfile(Env.security_blacklist('/' + f)):
                database_problems.append(
                    'security blacklist: an directory detected: ' +
                    Env.security_blacklist('/' + f))
            else:
                tmp = open(Env.security_blacklist('/' + f), 'r')
                try:
                    json.loads(tmp.read())
                except:
                    database_problems.append(
                        'security blacklist: invalid json data in ' +
                        Env.security_blacklist('/' + f))
        if database_problems:
            for problem in database_problems:
                pr.p(ansi.red + 'database: ' + problem + ansi.reset)
            result_code = 1
        else:
            pr.p(ansi.green + 'all of cati database is ok' + ansi.reset)

        if not self.is_quiet():
            if packages_to_reinstall:
                pr.p(ansi.blue + 'We suggest re-install this packages:')
                for pkg in packages_to_reinstall:
                    pr.p('- ' + pkg.data['name'])
                if not self.has_option('--autofix'):
                    pr.p(
                        'use --autofix option to re-install them or do this manually'
                    )
                    pr.p(ansi.reset, end='')
                else:
                    pr.p(ansi.reset, end='')
                    packages_names = [
                        pkg.data['name'] for pkg in packages_to_reinstall
                    ]
                    install_cmd = InstallCommand()
                    args = ['cati', 'install', '--reinstall', *packages_names]
                    cmd_str = ''
                    for arg in args:
                        cmd_str += arg + ' '
                    cmd_str = cmd_str.strip()
                    pr.p(cmd_str)
                    return install_cmd.handle(ArgParser.parse(args))

        return result_code
Esempio n. 8
0
    def run(self):
        """ Run command """

        if self.has_option('--abort'):
            require_root_permission()
            state_list = BaseTransaction.state_list()
            if not state_list:
                pr.p(
                    ansi.green +
                    'There is not any undoned transaction and everything is ok'
                    + ansi.reset)
                return 0
            user_answer = 'y'
            if not self.has_option('-y') and not self.has_option('--yes'):
                pr.p(ansi.yellow +
                     'WARNING: this process maybe dangerous and ' +
                     str(len(state_list)) +
                     ' transactions will be igonred. are you sure? [y/N] ' +
                     ansi.reset,
                     end='')
                user_answer = input()
            if user_answer in ['Y', 'y']:
                BaseTransaction.finish_all_state()
                pr.p(ansi.green + 'state was empty successfully' + ansi.reset)
            else:
                return
        elif self.has_option('--complete'):
            require_root_permission()
            state_list = BaseTransaction.state_list()
            if not state_list:
                pr.p(
                    ansi.green +
                    'There is not any undoned transaction and everything is ok'
                    + ansi.reset)
                return 0
            BaseTransaction.finish_all_state()
            # complete transactions
            for item in state_list:
                if item['action'] == 'remove':
                    tmp_arguments = [item['pkg'], '-y']
                    tmp_arguments.insert(0, 'cati')
                    tmp_arguments.insert(1, 'remove')
                    cmd = RemoveCommand()
                    cmd.handle(ArgParser.parse(tmp_arguments))
                elif item['action'] == 'install':
                    tmp_arguments = ['install', item['file']]
                    tmp_arguments.insert(0, 'cati')
                    tmp_arguments.insert(1, 'pkg')
                    cmd = PkgCommand()
                    cmd.handle(ArgParser.parse(tmp_arguments))
            return
        else:
            # show list of undoned transactions
            state_list = BaseTransaction.state_list()
            if state_list:
                StateContentShower.show(state_list)
                return 1
            else:
                pr.p(
                    ansi.green +
                    'There is not any undoned transaction and everything is ok'
                    + ansi.reset)
                return 0
Esempio n. 9
0
    def run(self):
        """ Run command """

        RootRequired.require_root_permission()

        pr.p('Loading packages list...')
        pr.p('========================')

        loaded_packages = []

        for argument in self.arguments:
            arg_parts = argument.split('=')
            if len(arg_parts) == 1:
                # load last version as default
                pkg = Pkg.load_last(argument)
            else:
                # load specify version
                pkg = Pkg.load_version(arg_parts[0], arg_parts[1])
                if pkg == 1:
                    pkg = False
                elif pkg == 2:
                    self.message('package "' + arg_parts[0] +
                                 '" has not version "' + arg_parts[1] + '"' +
                                 ansi.reset,
                                 before=ansi.red)
                    continue
            if pkg:
                loaded_packages.append(pkg)
            else:
                self.message('unknow package "' + argument + '"' + ansi.reset,
                             before=ansi.red)

        # remove local packages from list
        new_loaded_packages = []
        for pkg in loaded_packages:
            try:
                file_path = pkg.data['file_path']
                new_loaded_packages.append(pkg)
            except:
                self.message('package "' + pkg.data['name'] +
                             '" is a local package',
                             is_error=True)
        loaded_packages = new_loaded_packages

        if not loaded_packages:
            return 1

        # calculate transactions
        pr.p('Calculating transactions...')
        calc = Calculator(with_recommends=self.has_option('--with-recommends'))
        i = 0
        while i < len(loaded_packages):
            loaded_packages[i].is_manual = True
            i += 1
        try:
            calc.install(list(reversed(loaded_packages)))
        except:
            pr.e(ansi.red + 'ERROR: There is some dependnecy problems.' +
                 ansi.reset)
            return 1

        # handle reinstallable packages
        i = 0
        while i < len(calc.to_install):
            if calc.to_install[i].installed():
                if calc.to_install[i].installed(
                ) == calc.to_install[i].wanted_version:
                    if not self.has_option('--reinstall'):
                        pr.p(
                            'Package ' + calc.to_install[i].data['name'] +
                            '=' + calc.to_install[i].wanted_version +
                            ' is currently installed. use --reinstall option to re-install it.'
                        )
                        if calc.to_install[i].is_manual:
                            try:
                                pr.p(
                                    'Setting it as manual installed package...'
                                )
                                manual_f = open(
                                    Env.installed_lists('/' +
                                                        pkg.data['name'] +
                                                        '/manual'), 'w')
                                manual_f.write('')
                                manual_f.close()
                            except:
                                pass
                        calc.to_install.pop(i)
            i += 1

        # check transactions exists
        if not calc.has_any_thing():
            pr.p('Nothing to do.')
            return 0

        # show transaction
        TransactionShower.show(calc)

        if not self.has_option('-y') or self.has_option('--yes'):
            pr.p('Do you want to continue? [Y/n] ', end='')
            answer = input()
            if answer == 'y' or answer == 'Y' or answer == '':
                pass
            else:
                pr.p('Abort.')
                return 1

        # start download packages
        pr.p('Downloading packages...')
        downloaed_paths = []
        for pkg in calc.to_install:
            download_path = Env.cache_dir('/archives/' + pkg.data['name'] +
                                          '-' + pkg.wanted_version + '-' +
                                          pkg.data['arch'])
            if os.path.isfile(download_path):
                file_sha256 = calc_file_sha256(download_path)
                file_md5 = calc_file_md5(download_path)
                valid_sha256 = None
                valid_md5 = None
                try:
                    valid_sha256 = pkg.data['file_sha256']
                except:
                    valid_sha256 = file_sha256
                try:
                    valid_md5 = pkg.data['file_md5']
                except:
                    valid_md5 = file_md5
                if file_md5 != valid_md5 or file_sha256 != valid_sha256:
                    # file is corrupt and should be download again
                    os.remove(download_path)
                else:
                    pr.p('Using Cache for ' + pkg.data['name'] + ':' +
                         pkg.data['version'] + ':' + pkg.data['arch'] + '...')
                    downloaed_paths.append(download_path)
                    continue
            download_cmd = DownloadCommand()
            i = 0
            res = 1
            tmp = True
            while tmp:
                if i > 5:
                    pr.e(ansi.red + 'Failed to download packages' + ansi.reset)
                    return res
                pr.p('Downloading ' + pkg.data['name'] + ':' +
                     pkg.data['version'] + ':' + pkg.data['arch'] + '...')
                res = download_cmd.handle(
                    ArgParser.parse([
                        'cati', 'download', '-q',
                        pkg.data['name'] + '=' + pkg.wanted_version,
                        '--output=' + download_path
                    ]))
                if res == 1 or res == None:
                    tmp = False
                i += 1
            downloaed_paths.append(download_path)
        pr.p('Download completed.')

        # check --download-only option
        if self.has_option('--download-only'):
            return 0

        cati_pkg_cmd_options = []
        remove_cmd_options = []
        if self.has_option('--keep-conffiles'):
            cati_pkg_cmd_options.append('--keep-conffiles')
        if self.has_option('--target'):
            cati_pkg_cmd_options.append('--target')
        if self.has_option('--without-scripts'):
            cati_pkg_cmd_options.append('--without-scripts')
            remove_cmd_options.append('--without-scripts')

        # remove packages
        if calc.to_remove:
            pr.p('Removing packages...')
            package_names = [pkg.data['name'] for pkg in calc.to_remove]
            remove_cmd = RemoveCommand()
            res = remove_cmd.handle(
                ArgParser.parse([
                    'cati', 'remove', *package_names, '-y', *remove_cmd_options
                ]))
            if res != 0 and res != None:
                pr.e(ansi.red + 'Failed to remove packages' + ansi.reset)
                return res

        # install packages
        pr.p('Installing packages...')
        pkg_cmd = PkgCommand()
        res = pkg_cmd.handle(
            ArgParser.parse([
                'cati', 'pkg', 'install', *downloaed_paths,
                *cati_pkg_cmd_options
            ]))
        if res != 0 and res != None:
            self.set_manual_installs(calc.to_install)
            pr.e(ansi.red + 'Failed to install packages' + ansi.reset)
            return res

        self.set_manual_installs(calc.to_install)

        pr.p(ansi.green + 'Done.' + ansi.reset)
Esempio n. 10
0
def deb2cati(file_path: str) -> str:
    """
    Converts deb package to cati package and returns generated cati package file path

    Args:
        file_path: deb package filepath

    Returns:
        returns generated cati package filepath
    """
    file_path = os.path.abspath(file_path)
    tmp_dir = Temp.make_dir()
    cwd = os.getcwd()
    os.chdir(tmp_dir)
    try:
        shutil.copy(file_path, './package.deb')

        script = '''
        ar -x package.deb
        mkdir cati
        mkdir cati/control cati/scripts
        mv control.tar.* cati/control
        mkdir cati/files
        mv data.tar.* cati/files
        rm debian-binary package.deb
        touch cati/data.json
        '''.strip().split('\n')
        script = [l.strip() for l in script]

        for line in script:
            result = os.system(line)
            if result != 0:
                # script has error, return current filepath
                os.chdir(cwd)
                return file_path
        os.chdir('cati/control')
        os.system('tar -xf control.tar.*')
        os.system('rm control.tar.*')
        os.chdir('..')
        os.chdir('files')
        os.system('tar -xf data.tar.*')
        os.system('rm data.tar.*')
        os.chdir('..')

        # convert content data to cati json
        control_f = open('control/control', 'r').read()
        control_f_lines = control_f.strip().split('\n')
        control_fields = {}
        tmp_last_key = None
        for line in control_f_lines:
            if line != '':
                if line[0] == ' ':
                    if tmp_last_key != None:
                        control_fields[tmp_last_key] += '\n' + line
                else:
                    key = line.split(':', 1)[0]
                    value = line.split(':', 1)[1].strip()
                    tmp_last_key = key
                    control_fields[key] = value

        # convert scripts
        if os.path.isfile('control/preinst'):
            shutil.copy('control/preinst', 'scripts/ins-before')

        if os.path.isfile('control/postinst'):
            shutil.copy('control/postinst', 'scripts/ins-after')

        if os.path.isfile('control/prerm'):
            shutil.copy('control/prerm', 'scripts/rm-before')

        if os.path.isfile('control/postrm'):
            shutil.copy('control/postrm', 'scripts/rm-after')

        # convert control fields to cati data.json
        cati_data = {}
        for k in control_fields:
            if k == 'Package':
                cati_data['name'] = control_fields[k].strip()
            elif k == 'Version':
                cati_data['version'] = control_fields[k].strip()
            elif k == 'Architecture':
                cati_data['arch'] = control_fields[k].strip()
            elif k == 'Maintainer':
                cati_data['maintainer'] = control_fields[k].strip()
            elif k == 'Original-Maintainer':
                cati_data['X-Original-Maintainer'] = control_fields[k].strip()
            elif k == 'Uploaders':
                cati_data['uploaders'] = control_fields[k].strip().split(',')
                cati_data['uploaders'] = [a.strip() for a in cati_data['uploaders']]
            elif k == 'Description':
                cati_data['description'] = control_fields[k]
            elif k == 'Changed-By':
                cati_data['changed-by'] = control_fields[k]
            elif k == 'Changes':
                cati_data['changes'] = control_fields[k]
            elif k == 'Date':
                cati_data['date'] = control_fields[k]
            elif k == 'Urgency':
                cati_data['urgency'] = control_fields[k]
            elif k == 'Essential':
                cati_data['essential'] = control_fields[k]
                if cati_data['essential'] == 'yes' or cati_data['essential'] == 'Yes':
                    cati_data['essential'] = True
                else:
                    cati_data['essential'] = False
            elif k == 'Homepage':
                cati_data['homepage'] = control_fields[k].strip()
            elif k == 'Section':
                cati_data['category'] = [control_fields[k].strip()]
            elif k == 'Depends' or k == 'Pre-Depends':
                try:
                    cati_data['depends']
                except:
                    cati_data['depends'] = []
                cati_data['depends'] = [*cati_data['depends'], *convert_depends_list(control_fields[k].strip())]
            elif k == 'Conflicts' or k == 'Breaks':
                try:
                    cati_data['conflicts']
                except:
                    cati_data['conflicts'] = []
                cati_data['conflicts'] = [*cati_data['conflicts'], *convert_depends_list(control_fields[k].strip())]
            elif k == 'Recommends':
                cati_data['recommends'] = convert_depends_list(control_fields[k].strip())
            elif k == 'Replaces':
                cati_data['replaces'] = convert_depends_list(control_fields[k].strip())
            elif k == 'Suggests':
                cati_data['suggests'] = convert_depends_list(control_fields[k].strip())
                cati_data['suggests'] = [item_tmp.split(' ')[0] for item_tmp in cati_data['suggests']]
            elif k == 'Enhances':
                cati_data['enhances'] = convert_depends_list(control_fields[k].strip())
                cati_data['enhances'] = [item_tmp.split(' ')[0] for item_tmp in cati_data['enhances']]
            elif k == 'Provides':
                cati_data['provides'] = convert_depends_list(control_fields[k].strip())
                cati_data['provides'] = [item_tmp.split(' ')[0] for item_tmp in cati_data['provides']]
            elif k[0] == 'X' or k[0] == 'x':
                cati_data[k] = control_fields[k].strip()
        os.system('rm control -rf')
        cati_data_f = open('data.json', 'w')
        cati_data_f.write(json.dumps(cati_data))
        cati_data_f.close()
        os.chdir('..')

        # build pkg
        pkg_command = PkgCommand.PkgCommand()
        pkg_command.handle(ArgParser.parse(['cati', 'pkg', 'build', 'cati', '-q']))

        if os.path.isfile('cati.cati'):
            try:
                shutil.copy('cati.cati', file_path + '.cati')
                os.chdir(cwd)
                return file_path + '.cati'
            except:
                tmp_file_path = Temp.make_file()
                shutil.copy('cati.cati', tmp_file_path)
                return tmp_file_path
    except Exception as ex:
        print('error: ' + str(ex))
        os.chdir(cwd)
        return file_path

    os.chdir(cwd)
    return file_path