예제 #1
0
def enable_console_autologin(username, restart=False):
    '''
    Sets the system to automatically login username on tty1
    at boot time, or when you close the console session.
    '''

    #
    # Change systemd symlink that points to what needs to happen on tty1
    # https://wiki.archlinux.org/index.php/Systemd_FAQ#How_do_I_change_the_default_number_of_gettys.3F
    #
    systemd_tty1_linkfile = '/etc/systemd/system/getty.target.wants/[email protected]'
    kano_init_tty1_kanoautologin = '******'
    kano_init_tty1_kanoinit = '/usr/share/kano-init/systemd_ttys/[email protected]'
    if os.path.isfile(systemd_tty1_linkfile):
        os.unlink(systemd_tty1_linkfile)

    if username == 'root':
        os.symlink(kano_init_tty1_kanoinit, systemd_tty1_linkfile)
    else:
        sed('^ExecStart.*', "ExecStart=/bin/su - {}".format(username), kano_init_tty1_kanoautologin)
        os.symlink(kano_init_tty1_kanoautologin, systemd_tty1_linkfile)

    if restart:
        # replace the tty process immediately, otherwise on next boot
        run_cmd('systemctl restart [email protected]')
예제 #2
0
def login_and_share(file_path, title, app_name):
    """ Make a share to Kano World. If the user is not logged in, present the
    register/login window to the user.

    :param file_path: Code file to be uploaded to Kano World
    :type file_path: str

    :param title: Title for the share
    :type title: str

    :param app_name: App making the share
    :type app_name: str

    :returns: Success, error message or None
    :rtype: Tuple of (bool, str)
    """
    success, unused = login_using_token()

    if not success:
        run_cmd('kano-login -r', localised=True)
        success, unused = login_using_token()
        if not success:
            return False, _("Cannot login")

    return upload_share(file_path, title, app_name)
예제 #3
0
def start_dashboard_services(username):
    '''
    Starts the Dashboard app and related user services on top of the XServer,
    Using su to impersonate them as the specified "username".
    '''
    run_cmd('su - "{}" -c "systemctl --user start kano-dashboard.service"'.format(username))
    run_cmd('su - "{}" -c "systemctl --user restart kano-common.target"'.format(username))
예제 #4
0
def login_and_share(file_path, title, app_name):
    """ Make a share to Kano World. If the user is not logged in, present the
    register/login window to the user.

    :param file_path: Code file to be uploaded to Kano World
    :type file_path: str

    :param title: Title for the share
    :type title: str

    :param app_name: App making the share
    :type app_name: str

    :returns: Success, error message or None
    :rtype: Tuple of (bool, str)
    """
    success, unused = login_using_token()

    if not success:
        run_cmd('kano-login -r')
        success, unused = login_using_token()
        if not success:
            return False, _("Cannot login")

    return upload_share(file_path, title, app_name)
예제 #5
0
def kill_apps():
    # since kano-updater is run as root, need to inform
    # kanolauncher about user
    user = get_user_unsudoed()
    home = os.path.join('/home/', user)
    variables = 'HOME={} USER={}'.format(home, user)
    run_cmd('{} /usr/bin/kano-launcher /bin/true kano-kill-apps'.format(
        variables))
예제 #6
0
def write_json(filepath, data, prettyprint=False, sort_keys=True):
    with open(filepath, 'w') as outfile:
        json.dump(data, outfile, indent=2, sort_keys=sort_keys)
    if prettyprint:
        _, _, rc = run_cmd('which underscore')
        if rc == 0:
            cmd = 'underscore print -i {filepath} -o {filepath}'.format(filepath=filepath)
            run_cmd(cmd)
예제 #7
0
def pkill(clues):
    if type(clues) == str:
        clues = [clues]

    psx, _, _ = run_cmd("ps x")
    for line in psx.split("\n"):
        for clue in clues:
            if clue in line:
                pid = line.split()[0]
                run_cmd("kill {}".format(pid))
예제 #8
0
def pkill(clues):
    if type(clues) == str:
        clues = [clues]

    psx, _, _ = run_cmd("ps x")
    for line in psx.split("\n"):
        for clue in clues:
            if clue in line:
                pid = line.split()[0]
                run_cmd("kill {}".format(pid))
def set_keyboard(locale_code, variant, save=False):
    if variant == 'generic':
        variant = ''

    # Notify and apply changes to the XServer
    run_cmd("setxkbmap {} {}".format(locale_code, variant))
    set_keyboard_config(locale.split_locale(locale_code)[0], variant)
    run_cmd("ACTIVE_CONSOLE=guess /bin/setupcon -k </dev/tty1")

    if save:
        save_keyboard_settings(locale_code, variant)
예제 #10
0
def set_keyboard(locale_code, variant, save=False):
    if variant == 'generic':
        variant = ''

    # Notify and apply changes to the XServer
    run_cmd("setxkbmap {} {}".format(locale_code, variant))
    set_keyboard_config(locale.split_locale(locale_code)[0], variant)
    run_cmd("ACTIVE_CONSOLE=guess /bin/setupcon -k </dev/tty1")

    if save:
        save_keyboard_settings(locale_code, variant)
예제 #11
0
def install(progress=None, gui=True):
    progress.split(
        Phase('checks', _('Checking system'), 1, is_main=True),
        Phase('download', _("Downloading updates"), 39, is_main=True),
        Phase('install', _("Installing updates"), 60, is_main=True),
    )

    progress.start('checks')
    status = UpdaterStatus.get_instance()
    logger.debug("Installing update (updater state = {})".format(status.state))

    if not progress:
        progress = DummyProgress()

    priority = Priority.NONE

    if status.is_urgent:
        priority = Priority.URGENT

    run_cmd('sudo kano-empty-trash')
    enough_space, space_msg = check_disk_space(priority)
    if not enough_space:
        logger.error(space_msg)
        progress.abort(_(space_msg))
        RCState.get_instance().rc = RC.NOT_ENOUGH_SPACE
        return False

    logger.debug("Downloading any new updates that might be available.")
    progress.start('download')
    if not download(progress):
        logger.error("Downloading updates failed, cannot update.")
        return False

    progress.start('install')

    logger.debug("Installing with priority {}".format(priority.priority))

    try:
        return do_install(progress, status, priority=priority)
    except Relaunch as err:
        raise
    except Exception as err:
        # Reset the state back to the previous one, so the updater
        # doesn't get stuck in 'installing' forever.
        status.state = UpdaterStatus.UPDATES_DOWNLOADED
        status.save()

        logger.error(err.message)
        progress.fail(err.message)
        RCState.get_instance().rc = RC.UNEXPECTED_ERROR
        return False
예제 #12
0
def check_safe_mode_hotkeys(blink_leds=False):
    """Check for Safe Mode keys being pressed.

    Returns:
        bool: whether or not the Safe Mode keys were pressed
    """

    if blink_leds:
        # Start a board LED blink in the background for a few seconds
        # so the user knows it's time to press Ctrl-Alt
        _, _, _ = run_cmd("/usr/bin/kano-led &")

    _, _, rv = run_cmd("kano-keys-pressed -r 5 -d 10")
    return (rv == 10)
def main(args):
    """The main functionality of the stop-unsupported-rpi-boot script.

    The ``args`` are described in the executable docstring.

    Returns:
        int: The return code of the binary
    """

    if os.getuid() != 0:
        return RC.WRONG_PERMISSIONS

    if not args['--no-detect'] and not is_unsupported_rpi():
        return RC.SUCCESS

    # Prepare the console and screen to show a console message.
    run_cmd('/bin/setupcon')
    # TODO: Change this to `systemctl stop kano-boot-splash` when ready.
    run_cmd('kano-stop-splash boot')
    # Clear the console screen.
    print_tty('\033\0143')

    # TODO: This string is marked with N_() because the text printed to the
    # console which needs a utf8 font. When that is supported, just switch
    # to _().
    print_tty(N_(
        "Sorry! This version of Kano OS requires a Raspberry Pi 3 or later.\n"
        "Don't worry. You can get a version optimized for your computer.\n"
        "Head to kano.me/downloads and grab the right one for you.\n"
    ))
    print(N_(
        "Sorry! This version of Kano OS requires a Raspberry Pi 3 or later.\n"
        "Don't worry. You can get a version optimized for your computer.\n"
        "Head to kano.me/downloads and grab the right one for you.\n"
    ))
    time.sleep(60)

    # Prevent boot loops during testing by disabling the service. Because of
    # how early during boot this script is executed, the filesystem needs to
    # be temporarily mounted to enable writes.
    if args['--no-detect']:
        run_cmd('sudo mount -o remount,rw /dev/mmcblk0p2 /')
        run_cmd('sudo systemctl disable stop-unsupported-rpi-boot.service')
        run_cmd('sudo mount -o remount,ro /dev/mmcblk0p2 /')

    if not args['--dry-run']:
        sysrq_power_off()

    # Exit cleanly.
    return RC.SUCCESS
예제 #14
0
def ensure_internet():
    """Ensure there is an internet connection.

    Check for an internet connection and uses ``kano-settings`` to configure
    one if not setup. If the popup subsequently fails, the operation quits.

    Returns:
        bool: Whether there is an internet connection
    """

    if not is_internet():
        run_cmd('sudo kano-settings --label no-internet', localised=True)
        return is_internet()

    return True
def _get_display_modes(mode):
    """TODO"""
    display_modes, err, rc = run_cmd('tvservice --json --modes {}'.format(mode))
    try:
        return json.loads(display_modes)
    except:
        return list()
예제 #16
0
def main():
    # Ensure script is not run as root.
    if os.getuid() == 0:
        return 10

    if _logs_already_sent():
        return

    if not os.path.exists(LOGS_PATH):
        return

    # Service is running too early in user space. Give the system some
    # time to settle.
    time.sleep(30)

    out, err, rc = run_cmd(
        'kano-feedback-cli'
        '   --title "Kano OS: Safe Mode Boot Logs"'
        '   --description "Kano OS booted into Safe Mode. Debugging logs attached."'
        '   --send --logs {path}'
        '   --flag {flag}'
        '   >{output} 2>&1'.format(path=LOGS_PATH,
                                   flag=LOGS_FLAG_PATH,
                                   output=TMP_LOGS_OUTPUT_PATH))
    if rc != 0:
        logger.error('Could not send logs, kano-feedback-cli rc {}'.format(rc))
        return 20
예제 #17
0
def get_mac_address():
    cmd = '/sbin/ifconfig -a eth0 | grep HWaddr'
    o, _, _ = run_cmd(cmd)
    if len(o.split('HWaddr')) != 2:
        return
    mac_addr = o.split('HWaddr')[1].strip()
    mac_addr_str = mac_addr.upper()
    if len(mac_addr_str) == 17:
        return mac_addr_str
예제 #18
0
def ensure_kano_world_login():
    """Ensure user has logged in Kano World.

    Checks for a login session and uses ``kano-login`` to configure one if not
    setup. If the popup subsequently fails, the operation quits.

    Returns:
        bool: Whether there is a valid login session to Kano World
    """

    is_logged_in, error = login_using_token()

    if not is_logged_in:
        run_cmd('kano-login', localised=True)
        is_logged_in, error = login_using_token()
        return is_logged_in

    return True
예제 #19
0
def get_mac_address():
    cmd = '/sbin/ifconfig -a eth0 | grep HWaddr'
    o, _, _ = run_cmd(cmd)
    if len(o.split('HWaddr')) != 2:
        return
    mac_addr = o.split('HWaddr')[1].strip()
    mac_addr_str = mac_addr.upper()
    if len(mac_addr_str) == 17:
        return mac_addr_str
예제 #20
0
def expand_fs():
    '''
    Expands the root filesystem partition to the maximum available size and,
    with it, any extended partition container.

    Returns:
        int: Success code for the operation as defined by members of
             :class:`kano_updater.expand_fs.return_codes.RC`
    '''

    # Figure out which partition is the root filesystem
    root_disk = get_root_partition()

    if not root_disk:
        logger.error('Could not determine which partition is root partition')
        return RC.E_ROOT_FS_NOT_FOUND

    partitions = get_partition_table()

    try:
        root_partition = partitions[root_disk]
    except KeyError:
        logger.error('Root filesystem could not be found in partition table')
        return RC.E_PARTITION_NOT_IN_TABLE

    # Determine if a logical partition exists
    extended_partition = get_extended_partition()

    # Expand logical partition
    if extended_partition:
        rc = expand_partition(extended_partition)
        if rc != RC.SUCCESS:
            return rc

    # Expand root partition
    rc = expand_partition(root_partition)
    if rc != RC.SUCCESS:
        return rc

    # Notify OS of changes
    run_cmd('partprobe {disk}'.format(disk=get_disk_info()['device']))
    run_cmd('resize2fs {rootfs}'.format(rootfs=root_partition['node']))

    return RC.SUCCESS
예제 #21
0
def expand_fs():
    '''
    Expands the root filesystem partition to the maximum available size and,
    with it, any extended partition container.

    Returns:
        int: Success code for the operation as defined by members of
             :class:`kano_updater.expand_fs.return_codes.RC`
    '''

    # Figure out which partition is the root filesystem
    root_disk = get_root_partition()

    if not root_disk:
        logger.error('Could not determine which partition is root partition')
        return RC.E_ROOT_FS_NOT_FOUND

    partitions = get_partition_table()

    try:
        root_partition = partitions[root_disk]
    except KeyError:
        logger.error('Root filesystem could not be found in partition table')
        return RC.E_PARTITION_NOT_IN_TABLE

    # Determine if a logical partition exists
    extended_partition = get_extended_partition()

    # Expand logical partition
    if extended_partition:
        rc = expand_partition(extended_partition)
        if rc != RC.SUCCESS:
            return rc

    # Expand root partition
    rc = expand_partition(root_partition)
    if rc != RC.SUCCESS:
        return rc

    # Notify OS of changes
    run_cmd('partprobe {disk}'.format(disk=get_disk_info()['device']))
    run_cmd('resize2fs {rootfs}'.format(rootfs=root_partition['node']))

    return RC.SUCCESS
예제 #22
0
def disable_console_autologin(restart=False):
    '''
    Disable automatic login on tty1, default getty login prompt will be provided.
    '''

    #
    # Change systemd symlink that points to what needs to happen on tty1
    # https://wiki.archlinux.org/index.php/Systemd_FAQ#How_do_I_change_the_default_number_of_gettys.3F
    #
    systemd_tty1_linkfile = '/etc/systemd/system/getty.target.wants/[email protected]'
    systemd_tty1_getty = '/lib/systemd/system/[email protected]'

    if os.path.isfile(systemd_tty1_linkfile):
        os.unlink(systemd_tty1_linkfile)

    os.symlink(systemd_tty1_getty, systemd_tty1_linkfile)

    if restart:
        run_cmd('systemctl restart [email protected]')
예제 #23
0
def detect_kano_keyboard_type():
    # Get information of all devices
    o, _, _ = run_cmd('lsusb')

    keyboard_ids = {'en': 'ID 1997:2433', 'es': 'ID 1997:2434'}
    kano_keyboard = None
    for lang, id in keyboard_ids.iteritems():
        if id in o:
            kano_keyboard = lang
    return kano_keyboard
예제 #24
0
def get_space_available():
    out, err, rc = run_cmd('df -h / | tail -1')
    device, size, used, free, percent, mp = out.split()

    info = {'used': '', 'total': ''}

    if not err:
        info['used'] = used
        info['total'] = size

    return info
예제 #25
0
def expand_partition(partition):
    '''
    Expands the given partition to the maximum available size.

    Args:
        partition (dict): Partition to expand. Must be of the form of
                          :const:`kano_updater.expand_fs.schemas.DISK_SCHEMA`

    Returns:
        int: Success code for the operation as defined by members of
             :class:`kano_updater.expand_fs.return_codes.RC`
    '''

    try:
        jsonschema.validate(partition, PARTITION_SCHEMA)
    except jsonschema.ValidationError:
        logger.error(
            'Partiton supplied for expand does not match schema.\n'
            'Expected: {expected}\n'
            'Got: {got}\n'
            .format(expected=PARTITION_SCHEMA, got=partition)
        )
        return RC.E_INVALID_PARTITION_FORMAT

    partition_number = get_partition_number(partition['node'])

    if partition_number < 0:
        logger.error('Could not determine extended partition number')
        return RC.E_PARTITION_NUMBER_NOT_FOUND

    # TODO: Check that the extended and root partitions lie at the end of the
    #       partition table

    # Run parted command first as a script and if that fails due to it asking
    # a question, revert to command which auto-answers yes
    cmd = (
        "parted {disk} --script unit % resizepart {partition} 100 || "
        "parted {disk} ---pretend-input-tty unit % resizepart {partition} "
        "Yes 100"
        .format(
            disk=DISK,
            partition=partition_number,
        )
    )
    out, err, rc = run_cmd(cmd)

    if rc != 0:
        logger.error('Partition expand command failed: {cmd}'.format(cmd=cmd))
        logger.warn('Parted stdout: {out}'.format(out=out))
        logger.warn('Parted stderr: {err}'.format(err=err))

        return RC.E_PARTITION_EXPAND_FAILED

    return RC.SUCCESS
예제 #26
0
def show_kano_dialog(title, description, buttons, blocking=True):
    retval = None
    cmd = 'kano-dialog title="{}" description="{}" buttons="{}" no-taskbar'.format(
          title, description, buttons)

    if blocking:
        _, _, retval = run_cmd(cmd)
    else:
        retval = run_bg('exec ' + cmd)

    return retval
예제 #27
0
def set_to_HDMI(HDMI, force=False):
    '''
    Set audio output to HDMI if supported by the display,
    otherwise set it to Analogue output.

    Returns 'HDMI' or 'Analogue', whichever was applied.
    '''

    if not is_hdmi_audio_supported() and not force:
        HDMI = False

    # 1 analog
    # 2 hdmi

    # These are the changes we'll apply if they have changed from what they were
    if HDMI:
        amixer_cmd = hdmi_cmd
        set_config_value('hdmi_ignore_edid_audio', None)
        set_config_value('hdmi_drive', 2)
        config = _("HDMI")
    else:
        amixer_cmd = analogue_cmd
        set_config_value('hdmi_ignore_edid_audio', 1)
        set_config_value('hdmi_drive', None)
        config = _("Analogue")

    end_config_transaction()

    # Set audio path in amixer
    o, e, rc = run_cmd(amixer_cmd)
    if rc:
        logger.warn("error from amixer: {} {} {}".format(o, e, rc))

    # trigger alsa-store to store the path in /var/lib/alsa/asound.state
    o, e, rc = run_cmd(store_cmd)
    if rc:
        logger.warn("error from alsa-store: {} {} {}".format(o, e, rc))

    set_setting('Audio', config)
    return config
예제 #28
0
def detect_kano_keyboard_type():
    # Get information of all devices
    o, _, _ = run_cmd('lsusb')

    keyboard_ids = {
        'en': 'ID 1997:2433',
        'es': 'ID 1997:2434'
    }
    kano_keyboard = None
    for lang, id in keyboard_ids.iteritems():
        if id in o:
            kano_keyboard = lang
    return kano_keyboard
예제 #29
0
def main():
    """TODO"""

    if os.getuid() != 0:
        return 10

    logger.force_log_level('info')

    if was_safe_mode_requested():
        logger.warn("Safe Mode boot")
        clear_safe_mode_requested()
        set_safe_mode()
        return

    elif was_safe_mode():
        clear_safe_mode()

    if check_safe_mode_hotkeys():
        logger.warn("Safe Mode requested")
        set_safe_mode_requested()
        run_cmd('kano-checked-reboot safeboot systemctl reboot')
        return
예제 #30
0
def get_space_available():
    out, err, rc = run_cmd('df -h / | tail -1')
    device, size, used, free, percent, mp = out.split()

    info = {
        'used': '',
        'total': ''
    }

    if not err:
        info['used'] = used
        info['total'] = size

    return info
예제 #31
0
def expand_partition(partition):
    '''
    Expands the given partition to the maximum available size.

    Args:
        partition (dict): Partition to expand. Must be of the form of
                          :const:`kano_updater.expand_fs.schemas.DISK_SCHEMA`

    Returns:
        int: Success code for the operation as defined by members of
             :class:`kano_updater.expand_fs.return_codes.RC`
    '''

    try:
        jsonschema.validate(partition, PARTITION_SCHEMA)
    except jsonschema.ValidationError:
        logger.error('Partiton supplied for expand does not match schema.\n'
                     'Expected: {expected}\n'
                     'Got: {got}\n'.format(expected=PARTITION_SCHEMA,
                                           got=partition))
        return RC.E_INVALID_PARTITION_FORMAT

    partition_number = get_partition_number(partition['node'])

    if partition_number < 0:
        logger.error('Could not determine extended partition number')
        return RC.E_PARTITION_NUMBER_NOT_FOUND

    # TODO: Check that the extended and root partitions lie at the end of the
    #       partition table

    # Run parted command first as a script and if that fails due to it asking
    # a question, revert to command which auto-answers yes
    cmd = ("parted {disk} --script unit % resizepart {partition} 100 || "
           "parted {disk} ---pretend-input-tty unit % resizepart {partition} "
           "Yes 100".format(
               disk=DISK,
               partition=partition_number,
           ))
    out, err, rc = run_cmd(cmd)

    if rc != 0:
        logger.error('Partition expand command failed: {cmd}'.format(cmd=cmd))
        logger.warn('Parted stdout: {out}'.format(out=out))
        logger.warn('Parted stderr: {err}'.format(err=err))

        return RC.E_PARTITION_EXPAND_FAILED

    return RC.SUCCESS
예제 #32
0
def get_volume():
    from kano.logging import logger

    percent = 100

    cmd = "amixer | head -n 6 | grep -Po '(\d{1,3})(?=%)'"
    output, _, _ = run_cmd(cmd)

    try:
        percent = int(output.strip())
    except Exception:
        msg = 'amixer format bad for percent, output: {}'.format(output)
        logger.error(msg)
        pass

    return percent
예제 #33
0
def get_volume():
    from kano.logging import logger

    percent = 100

    cmd = "amixer | head -n 6 | grep -Po '(\d{1,3})(?=%)'"  # noqa
    output, _, _ = run_cmd(cmd)

    try:
        percent = int(output.strip())
    except Exception:
        msg = 'amixer format bad for percent, output: {}'.format(output)
        logger.error(msg)
        pass

    return percent
예제 #34
0
def get_free_space(path="/"):
    """
        Returns the amount of free space in certain location in MB

        :param path: The location to measure the free space at.
        :type path: str

        :return: Number of free megabytes.
        :rtype: int
    """

    out, dummy_err, dummy_rv = run_cmd("df {}".format(path))

    dummy_device, dummy_size, dummy_used, free, dummy_percent, dummy_mp = \
        out.split('\n')[1].split()

    return int(free) / 1024
예제 #35
0
def detect_kano_keyboard_type():
    # Get information of all devices
    stdout, dummy_stderr, dummy_ret = run_cmd('lsusb')

    keyboard_ids = {
        'en': ['ID 1997:2433', 'ID 1997:2435'],
        'es': 'ID 1997:2434'
    }

    for lang, ids in keyboard_ids.iteritems():
        if not isinstance(ids, list):
            ids = [ids]

        if any(kb_id in stdout for kb_id in ids):
            return lang

    return None
예제 #36
0
def sysrq_power_off():
    """Turn off the Raspberry Pi during very early boot process.

    This function requires root permissions.
    """

    # Safety check: Do not trigger a reboot if the filesystem has been
    # mounted as read/write to avoid potentially corrupting the SD card.
    out, err, rc = run_cmd('/bin/mount | /bin/grep "on / " | /bin/grep "ro"')
    if rc != 0:
        return

    with open('/proc/sys/kernel/sysrq', 'a') as sysrq:
        sysrq.write('1')

    with open('/proc/sysrq-trigger', 'a') as sysrq_trigger:
        sysrq_trigger.write('o')
예제 #37
0
def get_rpi_model(revision=None):
    '''
    Returns a string identifying the RaspberryPI model (RPI A/B/B+/2B)

    Source for RaspberrPI model numbers documented at:
    http://elinux.org/RPi_HardwareHistory
    '''
    try:
        model_name = overclocked = ''

        if not revision:
            o, _, _ = run_cmd('cat {}'.format('/proc/cpuinfo'))
            o = o.splitlines()
            for entry in o:
                if entry.startswith('Revision'):
                    revision = entry.split(':')[1]

        if revision == 'Beta':
            model_name = RPI_B_BETA_KEY
        elif int(revision, 16) & 0x00ff in (0x2, 0x3, 0x4, 0x5, 0x6, 0xd, 0xe,
                                            0xf):
            model_name = RPI_B_KEY
        elif int(revision, 16) & 0x00ff in (0x7, 0x8, 0x9):
            model_name = RPI_A_KEY
        elif int(revision, 16) & 0x00ff in (0x10, 0x13):
            model_name = RPI_B_PLUS_KEY
        elif int(revision, 16) & 0x00ff == 0x11:
            model_name = RPI_COMPUTE_KEY
        elif int(revision, 16) & 0x00ff == 0x12:
            model_name = RPI_A_PLUS_KEY
        elif int(revision, 16) & 0x00FFFFFF in (0x00A01041, 0x00A21041):
            model_name = RPI_2_B_KEY
        elif int(revision, 16) & 0x00FFFFFF == 0x00900092:
            model_name = RPI_ZERO_KEY
        elif int(revision, 16) & 0x00FFFFFF == 0x00a02082:
            model_name = RPI_3_KEY
        elif int(revision, 16) & 0x00FFFFFF == 0x00a22082:
            model_name = RPI_3_KEY
        else:
            model_name = 'unknown revision: {}'.format(revision)

        return '{} {}'.format(model_name, overclocked).strip()

    except:
        return 'Error getting model name'
예제 #38
0
def is_HDMI():
    # Find the audio setting
    amixer_string, e, rc = run_cmd(amixer_get_cmd)
    if rc:
        logger.warn("error from amixer: {} {} {}".format(amixer_string, e, rc))

    if amixer_string.find(hdmi_string) != -1:
        # Make sure config file is up to date
        if get_setting('Audio') != _("HDMI"):
            set_setting('Audio', _("HDMI"))
        return True

    # Default to Analogue
    else:
        # Make sure config file is up to date
        if get_setting('Audio') != _("Analogue"):
            set_setting('Audio', _("Analogue"))
        return False
예제 #39
0
def get_rpi_model(revision=None):
    '''
    Returns a string identifying the RaspberryPI model (RPI A/B/B+/2B)

    Source for RaspberrPI model numbers documented at:
    http://elinux.org/RPi_HardwareHistory
    '''
    try:
        model_name = overclocked = ''

        if not revision:
            o, _, _ = run_cmd('cat {}'.format('/proc/cpuinfo'))
            o = o.splitlines()
            for entry in o:
                if entry.startswith('Revision'):
                    revision = entry.split(':')[1]

        if revision == 'Beta':
            model_name = RPI_B_BETA_KEY
        elif int(revision, 16) & 0x00ff in (0x2, 0x3, 0x4, 0x5, 0x6, 0xd, 0xe, 0xf):
            model_name = RPI_B_KEY
        elif int(revision, 16) & 0x00ff in (0x7, 0x8, 0x9):
            model_name = RPI_A_KEY
        elif int(revision, 16) & 0x00ff in (0x10, 0x13):
            model_name = RPI_B_PLUS_KEY
        elif int(revision, 16) & 0x00ff == 0x11:
            model_name = RPI_COMPUTE_KEY
        elif int(revision, 16) & 0x00ff == 0x12:
            model_name = RPI_A_PLUS_KEY
        elif int(revision, 16) & 0x00FFFFFF in (0x00A01041, 0x00A21041):
            model_name = RPI_2_B_KEY
        elif int(revision, 16) & 0x00FFFFFF == 0x00900092:
            model_name = RPI_ZERO_KEY
        elif int(revision, 16) & 0x00FFFFFF == 0x00a02082:
            model_name = RPI_3_KEY
        elif int(revision, 16) & 0x00FFFFFF == 0x00a22082:
            model_name = RPI_3_KEY
        else:
            model_name = 'unknown revision: {}'.format(revision)

        return '{} {}'.format(model_name, overclocked).strip()

    except:
        return 'Error getting model name'
예제 #40
0
def get_partition_info():
    device = '/dev/mmcblk0'

    try:
        cmd = 'lsblk -n -b {} -o SIZE'.format(device)
        stdout, dummy_stderr, returncode = run_cmd(cmd)

        if returncode != 0:
            from kano.logging import logger
            logger.warning("error running lsblk")

            return []

        lines = stdout.strip().split('\n')
        sizes = map(int, lines)

        return sizes
    except Exception:
        return []
예제 #41
0
def get_dpkg_dict(include_unpacked=False):
    logger.warn("get_dpkg_dict() has been deprecated.")
    apps_ok = dict()
    apps_other = dict()

    cmd = 'dpkg -l'
    o, _, _ = run_cmd(cmd)
    lines = o.splitlines()
    for l in lines[5:]:
        parts = l.split()
        state = parts[0]
        name = parts[1]
        version = parts[2]

        if state == 'ii' or (include_unpacked and state == 'iU'):
            apps_ok[name] = version
        else:
            apps_other[name] = version

    return apps_ok, apps_other
예제 #42
0
def detect_kano_keyboard_type():
    # Get information of all devices
    stdout, dummy_stderr, dummy_ret = run_cmd('lsusb')

    keyboard_ids = {
        'en': [
            'ID 1997:2433',
            'ID 1997:2435'
        ],
        'es': 'ID 1997:2434'
    }

    for lang, ids in keyboard_ids.iteritems():
        if not isinstance(ids, list):
            ids = [ids]

        if any(kb_id in stdout for kb_id in ids):
            return lang

    return None
예제 #43
0
def get_disk_info():
    '''
    Load information about the current disk and its partition table.

    Returns:
        dict: The partition table output from sfdisk.
              When valid, outputs of the form of the
              :const:`kano_updater.expand_fs.schemas.DISK_SCHEMA` schema.
              On fail, returns empty dict
    '''

    global DISK_INFO

    if DISK_INFO:
        return DISK_INFO

    cmd = 'sfdisk --json {disk}'.format(disk=DISK)
    disks_str, dummy_err, dummy_rc = run_cmd(cmd)

    try:
        disk = json.loads(disks_str)
    except ValueError:
        logger.error('Could not get disk info: {cmd}'.format(cmd=cmd))
        return {}

    try:
        jsonschema.validate(disk, DISK_SCHEMA)
    except jsonschema.ValidationError:
        logger.error(
            'Output from {cmd} does not match disk schema.\n'
            'Expected: {expected}\n'
            'Got: {got}\n'
            .format(cmd=cmd, expected=DISK_SCHEMA, got=disk)
        )
        return {}

    DISK_INFO = disk['partitiontable']

    return DISK_INFO
예제 #44
0
def restart_alsa(background=True):
    """
    Restart ALSA service.

    This is helpful when changes have been made to the ALSA config and need to
    be applied without a system reboot.

    NOTE: Applications already running would most likely need to
          restart for the changes to be noticeable.

    Args:
        background - bool whether the operation should be done in another process

    Returns:
        bool whether or not the operation was successful (for background, always True)
    """
    cmd = '/etc/init.d/alsa-utils restart'

    if background:
        run_bg(cmd)
        return True
    else:
        dummy, dummy, rc = run_cmd(cmd)
        return rc == 0
예제 #45
0
def install(progress=None, gui=True):
    progress.split(
        Phase(
            'checks',
            _('Checking system'),
            1,
            is_main=True
        ),
        Phase(
            'download',
            _("Downloading updates"),
            39,
            is_main=True
        ),
        Phase(
            'install',
            _("Installing updates"),
            60,
            is_main=True
        ),
    )

    progress.start('checks')
    status = UpdaterStatus.get_instance()
    logger.info("Installing update (updater state = {})".format(status.state))

    if not progress:
        progress = DummyProgress()

    priority = Priority.NONE

    if status.is_urgent:
        priority = Priority.URGENT

    run_cmd('sudo kano-empty-trash')
    enough_space, space_msg = check_disk_space(priority)
    if not enough_space:
        logger.error(space_msg)
        progress.abort(_(space_msg))
        RCState.get_instance().rc = RC.NOT_ENOUGH_SPACE
        return False

    logger.info("Downloading any new updates that might be available.")
    progress.start('download')
    if not download(progress):
        logger.error("Downloading updates failed, cannot update.")
        return False

    progress.start('install')

    logger.info("Installing with priority {}".format(priority.priority))

    try:
        return do_install(progress, status, priority=priority)
    except Relaunch as err:
        raise
    except Exception as err:
        # Reset the state back to the previous one, so the updater
        # doesn't get stuck in 'installing' forever.
        status.state = UpdaterStatus.UPDATES_DOWNLOADED
        status.save()

        logger.error(err.message)
        progress.fail(err.message)
        RCState.get_instance().rc = RC.UNEXPECTED_ERROR
        return False
예제 #46
0
def is_monitor():
    status_str, _, _ = run_cmd('/usr/bin/tvservice -s')
    return 'RGB full' in status_str
예제 #47
0
def is_installed(program):
    '''
    Returns True if "program" is recognized as an executable command in PATH
    '''
    _, _, rc = run_cmd('which {}'.format(program))
    return rc == 0
예제 #48
0
def reboot(title, description):
    from kano.gtk3 import kano_dialog
    kdialog = kano_dialog.KanoDialog(title, description)
    kdialog.run()
    run_cmd('systemctl reboot')
예제 #49
0
def set_volume(percent):
    cmd = 'amixer set Master {}%'.format(percent)
    run_cmd(cmd)
예제 #50
0
def download(progress=None, gui=True):
    status = UpdaterStatus.get_instance()
    dialog_proc = None

    if not progress:
        progress = DummyProgress()

    if status.state in [UpdaterStatus.NO_UPDATES, UpdaterStatus.UPDATES_DOWNLOADED]:
        progress.split(
            Phase(
                'checking',
                _("Checking for updates"),
                10,
                is_main=True
            ),
            Phase(
                'downloading',
                _("Downloading updates"),
                90,
                is_main=True
            )
        )
        pre_check_state = status.state
        progress.start('checking')
        check_for_updates(progress=progress)

        if status.state == UpdaterStatus.NO_UPDATES:
            if pre_check_state == UpdaterStatus.NO_UPDATES:
                msg = N_("No updates to download")
                logger.info(msg)
                progress.finish(_(msg))
                return False
            elif pre_check_state == UpdaterStatus.UPDATES_DOWNLOADED:
                err_msg = N_("Latest updates have been downloaded already")
                logger.info(err_msg)
                progress.abort(_(err_msg))
                return True

        progress.start('downloading')

    elif status.state == UpdaterStatus.DOWNLOADING_UPDATES:
        err_msg = N_("The download is already running")
        logger.error(err_msg)
        progress.abort(_(err_msg))
        return False

    if not is_internet():
        err_msg = N_("Must have internet to download the updates")
        logger.error(err_msg)
        progress.fail(_(err_msg))
        RCState.get_instance().rc = RC.NO_NETWORK
        return False

    priority = Priority.NONE

    if status.is_urgent:
        priority = Priority.URGENT

    if not is_server_available():
        err_msg = N_("Could not connect to the download server")
        logger.error(err_msg)
        progress.fail(_(err_msg))
        RCState.get_instance().rc = RC.CANNOT_REACH_KANO
        return False

    run_cmd('sudo kano-empty-trash')
    enough_space, space_msg = check_disk_space(priority)
    if not enough_space:
        logger.error(space_msg)
        progress.abort(_(space_msg))
        RCState.get_instance().rc = RC.NOT_ENOUGH_SPACE
        return False

    # show a dialog informing the user of an automatic urgent download
    if status.is_urgent and not gui:
        # TODO: mute notifications?
        title = _("Updater")
        description = _(
            "Kano HQ has just released a critical update that will repair some"
            " important things on your system! We'll download these automatically,"
            " and ask you to schedule the install when they finish."
        )
        buttons = _("OK:green:1")
        dialog_proc = show_kano_dialog(title, description, buttons, blocking=False)

    # If the Updater is running in recovery mode, do not update the state
    # out of the installing ones, otherwise the recovery flow will quit.
    if not status.is_recovery_needed():
        status.state = UpdaterStatus.DOWNLOADING_UPDATES
        status.save()

    priority = Priority.NONE

    if status.is_urgent:
        priority = Priority.URGENT
        logger.info("Urgent update detected, bumping to normal priority")
        make_normal_prio()

    logger.debug("Downloading with priority {}".format(priority.priority))

    try:
        success = do_download(
            progress, status, priority=priority, dialog_proc=dialog_proc
        )
    except Exception as err:
        progress.fail(err.message)
        logger.error(err.message)
        RCState.get_instance().rc = RC.UNEXPECTED_ERROR

        status.state = UpdaterStatus.UPDATES_AVAILABLE
        status.save()

        return False

    if not status.is_recovery_needed():
        status.state = UpdaterStatus.UPDATES_DOWNLOADED
        status.save()

    return success
예제 #51
0
def kill_child_processes(parent_pid):
    cmd = "ps -o pid --ppid {} --noheaders".format(parent_pid)
    o, _, _ = run_cmd(cmd)
    processes = [int(p) for p in o.splitlines()]
    for process in processes:
        os.kill(process, signal.SIGTERM)