Logo Search packages:      
Sourcecode: jockey version File versions  Download package

oslib.py

# -*- coding: UTF-8 -*-
# (c) 2007 Canonical Ltd.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

'''Encapsulate operations which are Linux distribution specific.'''

import fcntl, os, subprocess, sys

00022 class OSLib:
    '''Encapsulation of operating system/Linux distribution specific operations.'''

    # global default instance
    inst = None

00028     def __init__(self):
        '''Set default paths and load the module blacklist.
        
        Distributors might want to override some default paths.
        '''
        # default paths

        # /sys/ path; the main purpose of changing this is for test
        # suites, but some vendors might have /sys in a nonstandard place
        self.sys_dir = '/sys'

        # path to a modprobe.d configuration file where kernel modules are
        # enabled and disabled with blacklisting
        self.module_blacklist_file = '/etc/modprobe.d/blacklist-local'

        # path to modinfo binary
        self.modinfo_path = '/sbin/modinfo'

        # path to modprobe binary
        self.modprobe_path = '/sbin/modprobe'

        # path to kernel's list of loaded modules
        self.proc_modules = '/proc/modules'

        # default path to custom handlers
        self.handler_dir = '/usr/share/jockey/handlers'

        # default paths to modalias files (directory entries will consider all
        # files in them)
        self.modaliases = [
            '/lib/modules/%s/modules.alias' % os.uname()[2],
            '/usr/share/jockey/modaliases/',
        ]

        # path to X.org configuration file
        self.xorg_conf_path = '/etc/X11/xorg.conf'

        # directory where backup files are stored (like previous xorg.conf)
        self.backup_dir = '/var/cache/jockey'

        if not os.path.isdir(self.backup_dir):
            print >> sys.stderr, 'ERROR: cache directory %s does not exist. Aborting.' % \
                self.backup_dir
            sys.exit(1)

        # cache file for previously seen/newly used handlers lists (for --check)
        self.check_cache = os.path.join(self.backup_dir, 'check')

        self._load_module_blacklist()

        self._get_os_version()

    # 
    # The following functions MUST be implemented by distributors
    #

00084     def package_description(self, package):
        '''Return a tuple (short_description, long_description) for a package.
        
        This should raise a ValueError if the package is not available.
        '''
        raise NotImplementedError, 'subclasses need to implement this'

00091     def is_package_free(self, package):
        '''Return if given package is free software.'''

        raise NotImplementedError, 'subclasses need to implement this'

00096     def package_installed(self, package):
        '''Return if the given package is installed.'''

        raise NotImplementedError, 'subclasses need to implement this'

00101     def install_package(self, package, ui):
        '''Install the given package.

        The current UI object is passed as well, in case package installation
        wants to do some callbacks/confirmation dialogs/queries.

        If this succeeds, subsequent package_installed(package) calls must
        return True.
        '''
        raise NotImplementedError, 'subclasses need to implement this'

00112     def remove_package(self, package, ui):
        '''Uninstall the given package.

        The current UI object is passed as well, in case package installation
        wants to do some callbacks/confirmation dialogs/queries.

        If this succeeds, subsequent package_installed(package) calls must
        return False.
        '''
        raise NotImplementedError, 'subclasses need to implement this'

00123     def ui_help_available(self, ui):
        '''Return if help is available.

        This gets the current UI object passed, which can be used to determine
        whether GTK/KDE is used, etc.
        '''
        return False

00131     def ui_help(self, ui):
        '''The UI's help button was clicked.

        This should open a help HTML page or website, call yelp with an
        appropriate topic, etc. This gets the current UI object passed, which
        can be used to determine whether GTK/KDE is used, etc.
        '''
        pass

00140     def ui_notify_reboot(self, ui):
        '''Indicate that the user should do a reboot to activate changes.
        
        This happens for things like changing an X.org video driver which
        cannot be activated immediately.
        '''
        raise NotImplementedError, 'subclasses need to implement this'

    # 
    # The following functions have a reasonable default implementation for
    # Linux, but can be tweaked by distributors
    #

00153     def open_app(self, ui, custom_args = None):
        '''Open the application with admin privileges.
        
        This uses gksu for the Gtk UI and kdesu for the KDE one.
        '''
        if custom_args is None:
            argv = []
            for i in sys.argv:
                if i not in ('-c', '--check'):
                    argv.append(i)
        else:
            argv = [sys.argv[0]] + custom_args

        if 'gtk' in str(ui.__class__).lower():
            argv = ['gksu', '--debug', '-D', ui._('Driver Setup'), '--'] + argv
        elif 'kde' in str(ui.__class__).lower():
            argv = ['kdesu', ' '.join(argv)]
        else:
            raise NotImplementedError, 'subclasses need to implement this for frontend ' + str(ui.__class__)

        os.execvp(argv[0], argv)
        logging.error('could not execute ' + str(argv))
        sys.exit(1)

00177     def ignored_modules(self):
        '''Return a set of kernel modules which should be ignored.

        This particularly effects free kernel modules which are shipped by the
        OS vendor by default, and thus should not be controlled with this
        program.  Since this will include the large majority of existing kernel
        modules, implementing this is also important for speed reasons; without
        it, detecting existing modules will take quite long.
        
        Note that modules which are ignored here, but covered by a custom
        handler will still be considered.
        '''
        return set()

00191     def module_blacklisted(self, module):
        '''Check if a module is on the modprobe blacklist.'''

        return module in self._module_blacklist

00196     def blacklist_module(self, module, blacklist):
        '''Add or remove a kernel module from the modprobe blacklist.
        
        If blacklist is True, the module is blacklisted, otherwise it is
        removed from the blacklist.
        '''
        if blacklist:
            self._module_blacklist.add(module)
        else:
            try:
                self._module_blacklist.remove(module)
            except KeyError:
                return # no need to save the blacklist

        self._save_module_blacklist()

00212     def _load_module_blacklist(self):
        '''Initialize self._module_blacklist.'''

        self._module_blacklist = set()

        try:
            f = open(self.module_blacklist_file)
        except IOError:
            return

        try:
            fcntl.flock(f.fileno(), fcntl.LOCK_SH)
            for line in f:
                # strip off comments
                line = line[:line.find('#')].strip()

                if not line.startswith('blacklist'):
                    continue

                module = line[len('blacklist'):].strip()
                if module:
                    self._module_blacklist.add(module)
        finally:
            f.close()

00237     def _save_module_blacklist(self):
        '''Save module blacklist.'''

        if len(self._module_blacklist) == 0 and \
            os.path.exists(self.module_blacklist_file):
                os.unlink(self.module_blacklist_file)
                return

        os.umask(022)
        f = open(self.module_blacklist_file, 'w')
        try:
            fcntl.flock(f.fileno(), fcntl.LOCK_EX)
            for module in sorted(self._module_blacklist):
                print >> f, 'blacklist', module
        finally:
            f.close()

00254     def _get_os_version(self):
        '''Initialize self.os_vendor and self.os_version.

        This defaults to reading the values from lsb_release.
        '''
        p = subprocess.Popen(['lsb_release', '-sir'], stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT, close_fds=True)
        lines = p.communicate()[0].splitlines()
        assert p.returncode == 0
        assert len(lines) == 2
        (self.os_vendor, self.os_version) = lines

00266     def is_admin(self):
        '''Return True if the user is considered an administrator, i. e. should
        be able to use --check and can install packages.
        
        The default implementation considers the user an administrator if he
        can write check_cache, and create its directory if it does not exist.
        '''
        if os.path.exists(self.check_cache):
            return os.access(self.check_cache, os.W_OK)

        # try to create the dir if it does not exist
        d = os.path.dirname(self.check_cache)
        try:
            if not os.path.isdir(d):
                os.makedirs(d)
        except (IOError, OSError):
            return False
        return os.access(d, os.W_OK)

Generated by  Doxygen 1.6.0   Back to index