def download_once(self, pkg, output=None): """ Download once package """ try: file_path = pkg.data['file_path'] except: self.message('package "' + pkg.data['name'] + '" is local and cannot be downloaded' + ansi.reset, is_error=True, before=ansi.red) return False if not self.is_quiet(): pr.p('Downloading ' + pkg.data['name'] + ':' + pkg.data['version'] + ':' + pkg.data['arch'] + '...') if output == None: output = file_path.split('/')[-1] if file_path[:7] == 'http://' or file_path[:8] == 'https://': i = 0 while i < 5: res = DownloadProgress.download(file_path, output) if res == True: break else: pr.e(ansi.red + str(res) + ansi.reset) i += 1 if i == 5: return False else: if not os.path.isfile(file_path): return False shutil.copy(file_path, output) return True
def directory_not_empty_event(self, package: BaseArchive, dirpath: str): """ installer directory_not_empty event will run when package old directory is not empty """ pr.e(ansi.yellow + 'warning: directory "' + dirpath + '" is not empty and will not be deleted' + ansi.reset)
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))
def arch_error_event(self, pkg: BaseArchive): """ installer arch_error event will run when package architecture is not supported on the system for example while installing `amd64` package on `i386` system """ pr.e(ansi.red + 'Architecture error while installing "' + pkg.data['name'] + '": your system does not support "' + pkg.data['arch'] + '" packages.' + ansi.reset) return 1
def run(self): """ Run command """ if not os.path.exists(self.arguments[0]): pr.e('file "' + self.arguments[0] + '" not exists.') return 1 filepath = os.path.abspath(self.arguments[0]) return os.system(self.cati_exec + ' files --installed | grep ": ' + filepath + '"')
def cati_installation_is_corrupt(filepath: str, filetype: str): """ Will run when cati installation is corrupt shows error to user """ pr.e( ansi.red + 'Cati installation is corrupt. to repair it, just run cati with root access' + ansi.reset) pr.exit(1)
def show(repos: list): """ Shows errors in the repositories list Args: repos: list of loaded repositories (list[repo.Repo.Repo]) """ for repo in repos: if repo.syntax_errors: for error in repo.syntax_errors: pr.e('error in ' + repo.loaded_from_file + ':' + str(repo.line_number) + ': ' + error)
def show(state_list: list): """ Shows and renders list of undoned transactions in state Args: state_list: loaded state from `transaction.BaseTransaction.state_list()` as (list) """ pr.e(ansi.yellow + 'Error: state is not done') pr.p('\tthere is some undoned transactions:') for item in state_list: pr.p('\t\t' + BaseTransaction.state_item_to_string(item)) pr.p('to complete them, run `' + sys.argv[0] + ' state --complete`') pr.p(ansi.reset, end='')
def message(self, msg, is_error=False, before=''): """ Prints a message like this: `cati: <command-name>: <the-message>` Args: is_error: (bool) if this is True, message will print on stderr before: (str) before will print in the first of message for example `message("hello world")` will print `cati: cmdname: hello world` but `message("hello world", before="AAA ")` will print `AAA cati: cmdname: hello world` """ msg = before + self.cati_exec + ': ' + self.name + ': ' + msg if is_error: pr.e(msg) else: pr.p(msg)
def dep_and_conflict_error_event(self, pkg: BaseArchive, ex: Exception): """ installer dep_and_conflict_error event will run when package has conflict/dependency error while installing it `ex` argument can be DependencyError or ConflictError """ pr.e( ansi.red +\ 'Error while installing ' + pkg.data['name'] + ' (' + pkg.data['version'] + '):', ) if isinstance(ex, DependencyError): pr.e('\tdependency error:') elif isinstance(ex, ConflictError): pr.e('\tconflict error:') pr.e('\t\t' + str(ex)) pr.e(ansi.reset) return 1
def require_root_permission(is_cli=True, die_action=None): """ checks root premission. Args: is_cli (bool): if is True, when user have not root permission, error will print in terminal. but if is False, the `die_action` will run as a function. (will be disable in testing environment) die_action (callable): the function will be run when `is_cli` is False """ # if program is in testing mode don't check permission if is_testing: return if os.getuid() == 0: return # check write and read access for needed files files_to_check = [ Env.packages_lists(), Env.installed_lists(), Env.state_file(), Env.unremoved_conffiles(), Env.security_blacklist(), Env.any_scripts(), Env.repos_config(), Env.repos_config_dir(), Env.cache_dir(), Env.allowed_archs(), ] for f in files_to_check: if not os.access(f, os.W_OK) or not os.access(f, os.R_OK): if is_cli: pr.e(ansi.red + sys.argv[0] + ': permission is denied' + ansi.reset) pr.exit(1) return else: die_action() return
def run(self): """ Run command """ RootRequired.require_root_permission() if not self.is_quiet(): pr.p('Loading repositories list...') repos = Repo.get_list() ReposListErrorShower.show(repos) if not self.is_quiet(): pr.p('Prepairing to update repos...') orig_repos = [] for repo in repos: if repo.successful_loaded: if repo.test(): orig_repos.append(repo) else: pr.e(ansi.red + 'Cannot make connection to repo "' + repo.full_string + '"' + ansi.reset) if not self.is_quiet(): pr.p('Updating repositories...') pr.p('=============================') # downloaded repos data files paths downloaded_paths = [] # update repos for repo in list(reversed(orig_repos)): if not self.is_quiet(): pr.p('Fetching ' + repo.name + ' (' + repo.url + ') data...') data = repo.get_data(download_event=self.download_event) if type(data) == int: pr.e(ansi.red + 'Cannot update ' + repo.name + ' (' + repo.url + '): error code ' + str(data) + ansi.reset) elif isinstance(data, Exception): pr.e(ansi.red + 'Cannot update ' + repo.name + ' (' + repo.url + '): ' + str(data) + ansi.reset) else: # validate data try: tmp = json.loads(data) # save data in an file path = Env.cache_dir('/' + repo.name + '-' + str(time.time()) + str(random.random())) + '.json' f = open(path, 'w') f.write(data) f.close() downloaded_paths.append(path) except: pr.e(ansi.red + 'Cannot update ' + repo.name + ' (' + repo.url + '): invalid json data recived' + ansi.reset) if not self.is_quiet(): pr.p('Updating packages list...') # load downloaded data packages = [] for path in downloaded_paths: f = open(path, 'r') data = f.read().strip() f.close() items = json.loads(data) for item in items: if PackageJsonValidator.validate(item): packages.append(item) else: pass for pkg in packages: if PackageJsonValidator.validate(pkg): if self.is_verbose(): pr.p('adding ' + pkg['name'] + ':' + pkg['version'] + ':' + pkg['arch'] + '...') # write package on list if not os.path.isdir(Env.packages_lists('/' + pkg['name'])): os.mkdir(Env.packages_lists('/' + pkg['name'])) try: f = open( Env.packages_lists('/' + pkg['name'] + '/' + pkg['version'] + '-' + pkg['arch']), 'w') f.write(json.dumps(pkg)) f.close() except: pr.e(ansi.red + 'error while adding ' + pkg['name'] + ':' + pkg['version'] + ':' + pkg['arch'] + ansi.reset) else: if self.is_verbose(): pr.p(ansi.yellow + 'invalid json data in an item. ignored...' + ansi.reset) if self.is_quiet(): pr.p('Finishing update...') ListUpdater.update_indexes({ 'cannot_read_file': self.empty_method, 'invalid_json_data': self.empty_method, }) pr.p(ansi.green + 'Done.' + ansi.reset)
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)
def install_once(self, pkg: BaseArchive): """ installs once package is called from install sub command """ target_path = self.option_value('--target') if target_path == None: target_path = '' installer = Installer() try: out = installer.install( pkg, { 'cannot_read_file': self.cannot_read_file_event, 'invalid_json_data': self.invalid_json_data_event, }, { 'package_currently_installed': self.package_currently_installed_event, 'package_new_installs': self.package_new_installs_event, 'package_installed': self.package_installed_event, 'directory_not_empty': self.directory_not_empty_event, 'dep_and_conflict_error': self.dep_and_conflict_error_event, 'arch_error': self.arch_error_event, }, (not self.has_option('--auto')), run_scripts=(not self.has_option('--without-scripts')), target_path=str(target_path), keep_conffiles=self.has_option('--keep-conffiles'), check_security_blacklist=(not self.has_option('--force') and not self.has_option('-f'))) if type(out) == int: return out except PackageScriptError as ex: pr.e(ansi.red + 'cannot install "' + pkg.data['name'] + ':' + pkg.data['version'] + '": ' + str(ex) + ansi.reset) return 1 except PackageIsInSecurityBlacklist as ex: pr.e(ansi.red + 'cannot install ' + pkg.data['name'] + ':' + pkg.data['version'] + ' because this is in security blacklist:') pr.e(' ' + ex.blacklist_item['title'] + ':') for l in ex.blacklist_item['description'].split('\n'): pr.e('\t' + l) pr.p(ansi.reset, end='') return 1 except CannotReadFileException as ex: self.message(ansi.red + str(ex), True, before=ansi.reset) return 1 except FileConflictError as ex: pr.e(ansi.red + '\nCannot install ' + pkg.data['name'] + ':' + pkg.data['version'] + ': File conflict error:') pr.e(' ' + str(ex)) pr.p(ansi.reset, end='') return 1