#!/usr/bin/python import sys sys.path.insert(0, '/usr/lib/entropy/lib') sys.path.insert(0, '/usr/lib/entropy/server') sys.path.insert(0, '/usr/lib/entropy/client') sys.path.insert(0, '../lib') sys.path.insert(0, '../server') sys.path.insert(0, '../client') import os import subprocess import errno import codecs import argparse from entropy.const import etpUi, etpConst, const_convert_to_unicode, \ const_convert_to_rawstring from entropy.output import teal, purple, darkgreen, blue, brown, print_info, \ red, nocolor, print_warning, print_error from entropy.exceptions import DependenciesNotRemovable from entropy.i18n import _ from entropy.client.interfaces import Client import entropy.dep import entropy.tools # solo modules from solo.commands.install import SoloInstall from solo.utils import print_package_info APP_NAME = os.path.basename(sys.argv[0]) KERNEL_BINARY_VIRTUAL = const_convert_to_unicode("virtual/linux-binary") KERNELS_DIR = const_convert_to_rawstring("/etc/kernels") RELEASE_LEVEL = const_convert_to_rawstring("RELEASE_LEVEL") def _get_kernels(etp_client): matches, x_rc = etp_client.atom_match(KERNEL_BINARY_VIRTUAL, multi_match = True, multi_repo = True) return matches, x_rc def _remove_tag_from_slot(slot): if not hasattr(entropy.dep, "remove_tag_from_slot"): # backward compatibility return slot[::-1].split(",", 1)[-1][::-1] return entropy.dep.remove_tag_from_slot(slot) def _get_target_tag(etp_client, kernel_match): try: matches = etp_client.get_reverse_queue([kernel_match], recursive = False) except DependenciesNotRemovable: # wtf should not happen raise tags = set() for pkg_id, pkg_repo in matches: tag = etp_client.open_repository(pkg_repo).retrieveTag(pkg_id) if tag: tags.add(tag) if tags: tags = sorted(tags, reverse = True) return tags.pop(0) def _setup_kernel_symlink(target_tag): eselect_exec = "/usr/bin/eselect" if os.access(eselect_exec, os.X_OK): subprocess.call((eselect_exec, "kernel", "set", target_tag)) def _guess_kernel_name(kernel_atom): """ This method takes advantage of Entropy kernel package info files available at /etc/kernels/-/ directory. This function tries to read uname -r from the RELEASE_LEVEL file. """ namever = entropy.dep.remove_cat(kernel_atom) kernel_meta_file = os.path.join(KERNELS_DIR, namever, RELEASE_LEVEL) if os.path.isfile(kernel_meta_file): try: with open(kernel_meta_file, "r") as km_f: kernel_name = km_f.readline().strip() if kernel_name: return kernel_name except (OSError, IOError): return None def _guess_kernel_package_file(release_level): """ This method takes advantage of Entropy kernel package info files available at /etc/kernels/-/ directory looking for a RELEASE_LEVEL file whose content matches release_level (uname -r). """ if not os.path.isdir(KERNELS_DIR): return None subs = [] for curdir, _subs, files in os.walk(KERNELS_DIR): subs.extend(_subs) for sub in subs: sub_path = os.path.join(KERNELS_DIR, sub) try: dir_list = os.listdir(sub_path) except OSError as err: if err.errno != errno.ENOENT: raise continue if RELEASE_LEVEL not in dir_list: continue level_path = os.path.join( sub_path, RELEASE_LEVEL) with codecs.open( level_path, "r", etpConst['conf_raw_encoding']) as rel_f: rel_line = rel_f.readline().strip() if release_level == rel_line: return const_convert_to_unicode(level_path) # removing and installing proprietary drivers might reset the selected # OpenGL implementation def _get_opengl_impl(): eselect_exec = "/usr/bin/eselect" sts = 1 out = "xorg-x11" if os.access(eselect_exec, os.X_OK) and not etpUi['pretend']: sts, xout = entropy.tools.getstatusoutput("%s opengl show" % ( eselect_exec,)) if sts == 0: out = xout return out def _set_opengl_impl(opengl): eselect_exec = "/usr/bin/eselect" if os.access(eselect_exec, os.X_OK): args = (eselect_exec, "opengl", "set", opengl) subprocess.call(args) def _show_kernel_warnings(kernel_atom): print_info("%s %s" % (purple(kernel_atom), teal(_("has been installed.")))) print_warning("%s: %s" % (red(_("Attention")), brown(_("some external drivers cannot work across multiple kernels.")))) print_warning(darkgreen(_("Please reboot your computer now !"))) def _switch_kernel(nsargs): from_running = nsargs.from_running kernel_package = nsargs.kernel etp_client = None acquired = False try: etp_client = Client() acquired = entropy.tools.acquire_entropy_locks(etp_client) if not acquired: print_error(brown(_("Another Entropy is currently running."))) return 1 pkg_id, pkg_repo = etp_client.atom_match(kernel_package) if pkg_id == -1: print_error("%s: %s" % (brown(_("Package does not exist")), teal(kernel_package),)) return 1 kernel_matches, rc = _get_kernels(etp_client) kernel_match = (pkg_id, pkg_repo) if kernel_match not in kernel_matches: print_error("%s: %s" % (brown(_("Not a kernel")), teal(kernel_package),)) return 1 kernel_atom = etp_client.open_repository(pkg_repo).retrieveAtom(pkg_id) # this can be None ! target_tag = _get_target_tag(etp_client, kernel_match) inst_repo = etp_client.installed_repository() # try to look for the currently running kernel first if # --from-running is specified (use uname -r) latest_kernel = -1 k_rc = 1 if from_running: try: uname_r = os.uname()[2] except OSError: uname_r = None except IndexError: uname_r = None pkg_file = None if uname_r is not None: pkg_file = _guess_kernel_package_file(uname_r) if pkg_file is not None: _pkg_ids = list(inst_repo.searchBelongs(pkg_file)) # if more than one, get the latest _pkg_ids.sort(reverse=True) if _pkg_ids: latest_kernel = _pkg_ids[0] if latest_kernel == -1: print_error( brown(_("Cannot find your currently running kernel."))) print_error( brown(_("Try without --from-running."))) return 1 if latest_kernel == -1: latest_kernel, k_rc = inst_repo.atomMatch( KERNEL_BINARY_VIRTUAL) installed_revdeps = [] if (latest_kernel != -1) and target_tag: installed_revdeps = etp_client.get_removal_queue( [latest_kernel], recursive = False) # only pull in packages that are installed at this time. def _installed_pkgs_translator(inst_pkg_id): if inst_pkg_id == latest_kernel: # will be added later return None key, slot = inst_repo.retrieveKeySlot(inst_pkg_id) target_slot = _remove_tag_from_slot(slot) + "," + target_tag pkg_id, pkg_repo = etp_client.atom_match(key, match_slot = target_slot) if pkg_id == -1: return None return pkg_id, pkg_repo matches = map(_installed_pkgs_translator, installed_revdeps) matches = [x for x in matches if x is not None] matches.append(kernel_match) opengl = _get_opengl_impl() install = SoloInstall([]) rc, _show_cfgupd = install._install_action( etp_client, True, True, etpUi['pretend'], etpUi['ask'], etpUi['verbose'], etpUi['quiet'], False, False, False, False, False, False, 1, [], package_matches=list(matches)) if _show_cfgupd: install._show_config_files_update(etp_client) if (rc == 0) and (not etpUi['pretend']): _set_opengl_impl(opengl) if target_tag: # if target_tag is None, we are unable to set the symlink _setup_kernel_symlink(target_tag) else: # try to guess, sigh, for now guessed_kernel_name = _guess_kernel_name(kernel_atom) if guessed_kernel_name: _setup_kernel_symlink(guessed_kernel_name) _show_kernel_warnings(kernel_atom) return rc finally: if acquired and (etp_client is not None): entropy.tools.release_entropy_locks(etp_client) if etp_client is not None: etp_client.shutdown() def _list_kernels(nsargs): etp_client = None acquired = False try: etp_client = Client() acquired = entropy.tools.acquire_entropy_locks(etp_client) if not acquired: print_error(brown(_("Another Entropy is currently running."))) return 1 matches, rc = _get_kernels(etp_client) if (rc != 0) or (not matches): print_warning(_("No kernel packages found")) return 1 key_sorter = lambda x: \ etp_client.open_repository(x[1]).retrieveAtom(x[0]) for pkg_id, pkg_repo in sorted(matches, key = key_sorter): repo = etp_client.open_repository(pkg_repo) print_package_info(pkg_id, etp_client, repo, show_repo_if_quiet = True, extended = etpUi['verbose'], quiet = etpUi['quiet']) return 0 finally: if acquired and (etp_client is not None): entropy.tools.release_entropy_locks(etp_client) if etp_client is not None: etp_client.shutdown() if __name__ == "__main__": parser = argparse.ArgumentParser( description=_("Sabayon Kernel Switcher"), formatter_class=argparse.RawDescriptionHelpFormatter, prog=APP_NAME) def _add_standard_args(_parser, restricted): _parser.add_argument("--quiet", "-q", action="store_true", default=False, help=_("quiet mode")) if not restricted: _parser.add_argument("--ask", "-a", action="store_true", default=False, help=_("ask confirmation")) _parser.add_argument("--pretend", "-p", action="store_true", default=False, help=_("just show what would be done")) subparsers = parser.add_subparsers( title="kernel-switcher", description=_("available commands")) switch_parser = subparsers.add_parser( "switch", help=_("install a new or just another kernel")) switch_parser.set_defaults(func=_switch_kernel) switch_parser.add_argument( "--from-running", action="store_true", default=False, help=_("use 'uname -r' to determine the running kernel")) switch_parser.add_argument( "kernel", metavar="", help=_("the new kernel package dependency name")) _add_standard_args(switch_parser, False) list_parser = subparsers.add_parser( "list", help=_("list kernels")) list_parser.set_defaults(func=_list_kernels) _add_standard_args(list_parser, True) help_parser = subparsers.add_parser( "help", help=_("this help")) def _print_help(*args): parser.print_help() help_parser.set_defaults(func=_print_help) try: nsargs = parser.parse_args(sys.argv[1:]) except IOError as err: parser.print_help() raise SystemExit(1) etpUi['ask'] = getattr(nsargs, "ask", etpUi['ask']) etpUi['quiet'] = getattr(nsargs, "quiet", etpUi['quiet']) etpUi['pretend'] = getattr(nsargs, "pretend", etpUi['pretend']) try: rc = nsargs.func(nsargs) except KeyboardInterrupt: rc = 1 raise SystemExit(rc)