# # Copyright 2006-2013 Red Hat, Inc. # # This work is licensed under the GNU GPLv2 or later. # See the COPYING file in the top-level directory. # import os import re from .logger import log _ETC_VCONSOLE = "/etc/vconsole.conf" _KEYBOARD_DIR = "/etc/sysconfig/keyboard" _XORG_CONF = "/etc/X11/xorg.conf" _CONSOLE_SETUP_CONF = "/etc/default/console-setup" _KEYBOARD_DEFAULT = "/etc/default/keyboard" def _find_xkblayout(f): """ Reads a keyboard layout from a file that defines an XKBLAYOUT variable, e.g. /etc/default/{keyboard,console-setup}. The format of these files is such that they can be 'sourced' in a shell script. Used for both /etc/default/keyboard and /etc/default/console-setup. The former is used by Debian 6.0 (Squeeze) and later. The latter is used by older versions of Debian, and Ubuntu. """ kt = None keymap_re = re.compile(r'\s*XKBLAYOUT="(?P[a-z-]+)"') for line in f: m = keymap_re.match(line) if m: kt = m.group('kt') break return kt def _xorg_keymap(f): """ Look in /etc/X11/xorg.conf for the host machine's keymap, and attempt to map it to a keymap supported by qemu """ kt = None keymap_re = re.compile(r'\s*Option\s+"XkbLayout"\s+"(?P[a-z-]+)"') for line in f: m = keymap_re.match(line) if m: kt = m.group('kt') break return kt def _sysconfig_keyboard(f): kt = None while 1: s = f.readline() if s == "": break s = s.strip() if s.startswith("#"): continue if (re.search("KEYMAP", s) is not None or re.search("KEYTABLE", s) is not None or (re.search("KEYBOARD", s) is not None and re.search("KEYBOARDTYPE", s) is None)): if '"' in s: delim = '"' elif '=' in s: delim = '=' else: continue kt = s.split(delim)[1].strip() break return kt def _default_keymap(): """ Look in various config files for the host machine's keymap, and attempt to map it to a keymap supported by qemu """ # Set keymap to same as hosts default = "en-us" keymap = None kt = None if "VIRTINST_TEST_SUITE" in os.environ: return default for path, cb in [ (_ETC_VCONSOLE, _sysconfig_keyboard), (_KEYBOARD_DIR, _sysconfig_keyboard), (_XORG_CONF, _xorg_keymap), (_KEYBOARD_DEFAULT, _find_xkblayout), (_CONSOLE_SETUP_CONF, _find_xkblayout)]: if not os.path.exists(path): continue try: f = open(path, "r") kt = cb(f) f.close() if kt: log.debug("Found keymap=%s in %s", kt, path) break log.debug("Didn't find keymap in '%s'", path) except Exception as e: log.debug("Error parsing '%s': %s", path, str(e)) if kt is None: log.debug("Did not parse any usable keymapping.") return default kt = kt.lower() keymap = sanitize_keymap(kt) if not keymap: log.debug("Didn't match keymap '%s' in keytable!", kt) return default return keymap ################## # Public helpers # ################## # Host keytable entry : keymap name in qemu/xen # Only use lower case entries: all lookups are .lower()'d keytable = { "ar": "ar", "da": "da", "dk": "da", "de": "de", "de-ch": "de-ch", "en-gb": "en-gb", "gb": "en-gb", "uk": "en-gb", "en-us": "en-us", "us": "en-us", "es": "es", "et": "et", "fi": "fi", "se_fi": "fi", "fo": "fo", "fr": "fr", "fr-be": "fr-be", "be": "fr-be", "fr-ca": "fr-ca", "ca": "fr-ca", "fr-ch": "fr-ch", "fr_ch": "fr-ch", "hr": "hr", "hu": "hu", "is": "is", "it": "it", "ja": "ja", "jp106": "ja", "jp": "ja", "lt": "lt", "lv": "lv", "mk": "mk", "nl": "nl", "nl-be": "nl-be", "no": "no", "pl": "pl", "pt": "pt", "pt-br": "pt-br", "br": "pt-br", "br-abnt2": "pt-br", "ru": "ru", "sl": "sl", "sv": "sv", "th": "th", "tr": "tr", } _cached_keymap = None def default_keymap(): global _cached_keymap if _cached_keymap is None: _cached_keymap = _default_keymap() return _cached_keymap def sanitize_keymap(kt): """ Make sure the passed keymap roughly matches something in keytable """ if kt.lower() in keytable: return keytable[kt] # Try a more intelligent lookup: strip out all '-' and '_', sort # the keytable keys putting the longest first, then compare # by string prefix clean_kt = kt.replace("-", "").replace("_", "") sorted_keys = sorted(list(keytable.keys()), key=len) for key in sorted_keys: origkey = key key = key.replace("-", "").replace("_", "") if clean_kt.startswith(key): return keytable[origkey] return None