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]')
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)
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))
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)
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))
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)
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)
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
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
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()
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
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
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
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
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]')
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
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
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
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
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
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
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
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
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
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
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
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
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
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')
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'
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
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 []
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
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
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
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
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
def is_monitor(): status_str, _, _ = run_cmd('/usr/bin/tvservice -s') return 'RGB full' in status_str
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
def reboot(title, description): from kano.gtk3 import kano_dialog kdialog = kano_dialog.KanoDialog(title, description) kdialog.run() run_cmd('systemctl reboot')
def set_volume(percent): cmd = 'amixer set Master {}%'.format(percent) run_cmd(cmd)
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
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)