def beta_3_12_1_to_beta_3_13_0(self): was_audio_hdmi = False # Get the currently set audio output channel. try: from kano_settings.system.audio import is_HDMI was_audio_hdmi = is_HDMI() except: logger.error( "beta_3_12_1_to_beta_3_13_0: Failed to get audio output channel" ) # Replace the config.txt after numerous changes there (see kano-settings). try: import shutil shutil.copyfile('/boot/config.txt', '/boot/beta_3_12_1_to_beta_3_13_0_bck_config.txt') shutil.copyfile('/usr/share/kano-settings/boot_default/config.txt', '/boot/config.txt') except: logger.error( "beta_3_12_1_to_beta_3_13_0: Failed to replace config.txt") # Set the audio output channel back to HDMI if it was set. try: from kano_settings.system.audio import set_to_HDMI if was_audio_hdmi: set_to_HDMI(True, force=True) except: logger.error( "beta_3_12_1_to_beta_3_13_0: Failed to set HDMI audio back") # Remove the orphan udhcpc client, it is now obsoleted by dhcpcd5 run_cmd_log('apt-get -y purge udhcpc')
def beta_121_to_beta_122(self): run_cmd_log("kano-apps install --no-gui --icon-only xbmc") if not os.path.exists("/etc/apt/sources.list.d/kano-xbmc.list"): run_cmd_log("apt-key adv --keyserver keyserver.ubuntu.com --recv-key 5243CDED") with open("/etc/apt/sources.list.d/kano-xbmc.list", "w") as f: f.write("deb http://repo.kano.me/xbmc/ wheezy contrib\n")
def fix_broken(self, progress): progress.split( Phase('dpkg-clean', _("Cleaning dpkg journal")), Phase('fix-broken', _("Fixing broken packages")) ) if self._cache.dpkg_journal_dirty: progress.start('dpkg-clean') logger.info("Cleaning dpkg journal") run_cmd_log("dpkg --configure -a") self._cache.clear() self._cache.open() progress.start('fix-broken') # Naughty but don't want to re-initialise if self._cache._depcache.broken_count: try: self._cache._depcache.fix_broken() except SystemError as e: logger.error('Error attempting to fix broken pkgs', exception=e) self._cache.clear() self._cache.open()
def beta_370_to_beta_380(self): # linux kernel 4.4.21 shipped with Kano 3.8.0 emits systemd boot messages. # fix by telling the kernel to enable an empty splash screen. command = "sed -i 's/\\bsystemd.show_status=0\\b/splash/' {}".format('/boot/cmdline.txt') run_cmd_log(command) self._bootconfig_set_value_helper("gpu_mem", "256") new_apps = [ 'adventure', 'openttd', 'tux-paint', 'tux-typing', 'libreoffice', 'numpty-physics', 'gmail', 'google-drive', 'google-maps', 'wikipedia', 'whatsapp' ] for app in new_apps: run_cmd_log('kano-apps install --no-gui {app}'.format(app=app)) # Tell kano-init to put the automatic logins up-to-date reconfigure_autostart_policy()
def beta_134_to_beta_200(self): if not is_installed('kano-character-cli'): logger.info("kano-character-cli not installed," " attempt to install kano-profile") install('kano-profile') run_cmd_log( 'kano-character-cli -c "judoka" "Hair_Black" "Skin_Orange" -s')
def beta_121_to_beta_122(self): run_cmd_log("kano-apps install --no-gui --icon-only xbmc") if not os.path.exists("/etc/apt/sources.list.d/kano-xbmc.list"): run_cmd_log("apt-key adv --keyserver keyserver.ubuntu.com " \ "--recv-key 5243CDED") with open("/etc/apt/sources.list.d/kano-xbmc.list", "w") as f: f.write("deb http://repo.kano.me/xbmc/ wheezy contrib\n")
def create_user(username): """ Create and initialise an account for a new user. The user will be added to several default groups, including kanousers. This function requires root permissions to run properly. Will rase in case of an error. :param username: The name of the new user :type name: str """ if user_exists(username): raise UserError( _("The user '{string_username}' already exists").format( string_username=username)) home = "/home/{}".format(username) home_old = '/home/' + username + '-old' if os.path.exists(home): msg = ("The home directory for the new user '{}' was there already, " + "moving it to {}".format(username, home_old)) logger.warn(msg) shutil.move(home, home_old) # The umask force is used to blind the actual /home/username # folder from other users umask_override = '0077' cmd = "useradd -u {} -m -K UMASK={} -s /bin/bash {}".format( get_next_uid(), umask_override, username) _, _, rv = run_cmd_log(cmd) if rv != 0: msg = N_("Unable to create new user, useradd failed.") logger.error(msg) raise UserError(_(msg)) cmd = "echo '{}:{}' | chpasswd".format(username, DEFAULT_USER_PASSWORD) _, _, rv = run_cmd_log(cmd) if rv != 0: delete_user(username) msg = N_("Unable to change the new user's password, chpasswd failed.") logger.error(msg) raise UserError(_(msg)) # Make sure the kanousers group exists if not group_exists('kanousers'): _, _, rv = run_cmd_log('groupadd kanousers -f') if rv != 0: msg = N_("Unable to create the kanousers group, groupadd failed.") raise UserError(_(msg)) # Add the new user to all necessary groups cmd = "usermod -G '{}' {}".format(DEFAULT_USER_GROUPS, username) _, _, rv = run_cmd_log(cmd)
def beta_220_to_beta_230(self): out, err, rv = run_cmd_log('apt-mark showauto | grep modemmanager') # If the user has manually installed modemmanager, it will be marked as # manually installed. # Return value will be 0 if modemmanager is marked as an auto installed if rv == 0: out, err, rv = run_cmd_log('apt-get -y purge modemmanager') if rv == 0: run_cmd_log('apt-get -y autoremove')
def beta_134_to_beta_200(self): if not is_installed('kano-character-cli'): logger.info( "kano-character-cli not installed, "\ "attempt to install kano-profile" ) install('kano-profile') run_cmd_log( 'kano-character-cli -c "judoka" "Hair_Black" "Skin_Orange" -s' )
def make_low_prio(): # set IO class of this process to Idle pid = os.getpid() unused1, unused2, rc = run_cmd_log("ionice -c 3 -p {}".format(pid)) if rc != 0: logger.error("ionice command returned non-zero code: [{}]".format(rc)) # Set the lowest scheduling priority unused1, unused2, rc = run_cmd_log("schedtool -D {}".format(pid)) if rc != 0: logger.error( "schedtool command returned non-zero code: [{}]".format(rc)) os.nice(19)
def make_normal_prio(): # set IO class of this process to Idle pid = os.getpid() unused1, unused2, rc = run_cmd_log("ionice -c 0 -p {}".format(pid)) if rc != 0: logger.error("ionice command returned non-zero code: [{}]".format(rc)) # Set the lowest scheduling priority unused1, unused2, rc = run_cmd_log("schedtool -N {}".format(pid)) if rc != 0: logger.error( "schedtool command returned non-zero code: [{}]".format(rc)) try: current_niceness = os.nice(0) os.nice(-1 * current_niceness) except OSError as os_ex: logger.error("Can't renice to 0 due to permissions [{}]".format(os_ex))
def beta_370_to_beta_380(self): # linux kernel 4.4.21 shipped with Kano 3.8.0 emits systemd boot messages. # fix by telling the kernel to enable an empty splash screen. command = "sed -i 's/\\bsystemd.show_status=0\\b/splash/' {}".format( '/boot/cmdline.txt') run_cmd_log(command) self._bootconfig_set_value_helper("gpu_mem", "256") new_apps = [ 'adventure', 'openttd', 'tux-paint', 'tux-typing', 'libreoffice', 'numpty-physics', 'gmail', 'google-drive', 'google-maps', 'wikipedia', 'whatsapp' ] for app in new_apps: run_cmd_log('kano-apps install --no-gui {app}'.format(app=app)) # Tell kano-init to put the automatic logins up-to-date reconfigure_autostart_policy()
def purge(pkgs, die_on_err=False): if isinstance(pkgs, list): pkgs = ' '.join(pkgs) _, _, rv = run_cmd_log('apt-get -y purge ' + str(pkgs)) if die_on_err and rv != 0: msg = "Unable to purge '{}'".format(pkgs) update_failed(msg) raise Exception(msg) return rv
def install(pkgs, die_on_err=True): if isinstance(pkgs, list): pkgs = ' '.join(pkgs) cmd = 'apt-get install --no-install-recommends -o Dpkg::Options::="--force-confdef" ' \ '-o Dpkg::Options::="--force-confold" -y --force-yes ' + str(pkgs) _, _, rv = run_cmd_log(cmd) if die_on_err and rv != 0: msg = "Unable to install '{}'".format(pkgs) update_failed(msg) raise Exception(msg) return rv
def rename_user(current, new): ''' Renames current username to new, by changing its login name and home folder. ''' # Sanity checks if not user_exists(current) or user_exists(new): msg = N_("cannot rename user {} to {} due to conflicting names".format( current, new)) logger.error(msg) raise UserError(msg) # Rename the username and move his home directory. cmd = 'usermod --login {} --home /home/{} --move-home {}'.format( new, new, current) _, _, rv = run_cmd_log(cmd) if rv != 0: msg = N_( "Unable to rename user, perhaps user has processes running? usermod rc={}" .format(rv)) logger.error(msg) raise UserError(msg) # Do the same with his initial user group cmd = 'groupmod --new-name {} {}'.format(new, current) _, _, rv = run_cmd_log(cmd) if rv != 0: msg = N_("Unable to rename user group, rename_user failed.") logger.error(msg) # Try to rollback the first step cmd = 'usermod --login {} --home /home/{} --move-home {}'.format( current, current, new) _, _, rv = run_cmd_log(cmd) raise UserError(msg)
def delete_user(username): """ Terminates all processes of the user in question using SIGKILL and removes the user. Requires root permissions to run properly. :param username: The name of the user to be deleted. :type name: str """ # kill all process from the user run_cmd("killall -KILL -u {}".format(username)) _, _, rv = run_cmd_log("userdel -r {}".format(username)) if rv != 0: raise UserError( _("Deleting the '{string_username}' failed.".format( string_username=username)))
def fix_broken(msg): # Try to fix incorrectly configured packages run_cmd_log("dpkg --configure -a") o, _, _ = run_cmd("dpkg -l | grep '^..R'") reinstall = [] for line in o.splitlines(): pkg_info = line.split() try: reinstall.append(pkg_info[1]) except: pass if reinstall: logger.error("Reinstalling broken packages: {}".format(" ".join(reinstall))) cmd = 'yes "" | apt-get -y -o Dpkg::Options::="--force-confdef" ' \ '-o Dpkg::Options::="--force-confold" install --reinstall {}'.format(" ".join(reinstall)) run_cmd_log(cmd) cmd = 'yes "" | apt-get -y -o Dpkg::Options::="--force-confdef" ' \ '-o Dpkg::Options::="--force-confold" install -f' run_cmd_log(cmd)
def beta_111_to_beta_120(self): purge("kano-unlocker") repo_url = "deb http://mirrordirector.raspbian.org/raspbian/ wheezy main contrib non-free rpi" write_file_contents('/etc/apt/sources.list', repo_url + '\n') run_cmd_log('apt-get -y clean') run_cmd_log('apt-get -y update')
def _finalise(self): # When bluez is installed through a dependency it fails to configure # Get around this by installing it first run_cmd_log('apt-get -y install bluez')
def run_for_every_user(cmd): for user in get_users(): run_cmd_log("sudo su -c '{cmd}' - {user}".format(cmd=cmd, user=user))
def visible_run(cmd): if visible: return run_print_output_error(cmd) else: return run_cmd_log(cmd)
def upgrade_debian(gui_process): from kano_updater.utils import fix_broken, launch_gui_if_not_running, set_gui_stage # setting up apt-get for non-interactive mode os.environ['DEBIAN_FRONTEND'] = 'noninteractive' # Try to fix any broken packages prior to the upgrade fix_broken("Preparing packages to be upgraded") # apt upgrade gui_process = launch_gui_if_not_running(gui_process) set_gui_stage(4) # try to download all files first, retry in a loop for i in xrange(5): _, _, rc = run_cmd_log('apt-get -y -d dist-upgrade') if rc == 0: break elif i == 4: return -1 set_gui_stage(5) # do the actual update using the downloaded files cmd = 'yes "" | apt-get -y -o Dpkg::Options::="--force-confdef" ' + \ '-o Dpkg::Options::="--force-confold" dist-upgrade' _, debian_err, _ = run_cmd_log(cmd) # apt autoremove gui_process = launch_gui_if_not_running(gui_process) set_gui_stage(6) cmd = 'apt-get -y autoremove --purge' run_cmd_log(cmd) # apt autoclean cmd = 'apt-get -y autoclean' run_cmd_log(cmd) # Try to fix any broken packages after the upgrade fix_broken("Finalising package upgrade") # parsing debian error log if debian_err: err_split = debian_err.splitlines() dirs_delete = [] err_packages = [] for l in err_split: if 'dpkg: warning: unable to delete old directory' in l: parts = l.split("'") dirs_delete.append(parts[1].strip()) if 'dpkg: error processing' in l: parts = l.split('/var/cache/apt/archives/') packagename = parts[1].split()[0].strip()[:-4] err_packages.append(packagename) # remove left-over non-empty directories for dir in dirs_delete: delete_dir(dir) # return err_packages return err_packages return None
def beta_330_to_beta_340(self): # fix locale database if it was # corrupted by the NOOBS file hole problem run_cmd_log('locale-gen')
def _check_for_app_updates(): run_cmd_log('/usr/bin/kano-apps check-for-updates')
def _refresh_kdesk(): # Ignoring the return value here if the refresh fails run_cmd_log('kdesk -r')
def beta_111_to_beta_120(self): run_cmd_log("kano-apps install --no-gui painter epdfview geany " \ "codecademy calculator leafpad vnc")
def beta_200_to_beta_201(self): # All users upgrading from KanoOS 1.* should have their # users home directory permissions fixed run_cmd_log('/usr/bin/repair-homedir-permissions')
def beta_132_to_beta_133(self): # Downgrade the improved FBTurbo X11 driver # to the official stable version run_cmd_log('apt-get -y remove xf86-video-fbturbo-improved') run_cmd_log('apt-get -y install xserver-xorg-video-fbturbo')
def _expand_rootfs(): # TODO: Do we care about the return value? run_cmd_log('/usr/bin/expand-rootfs')
def run_pip_command(pip_args): # TODO Incorporate suppress_output when this is working _, _, rv = run_cmd_log("pip {} --log {}".format(pip_args, PIP_LOG_FILE)) return rv == 0
def beta_132_to_beta_133(self): run_cmd_log('kano-apps install --no-gui terminal-quest')
def beta_220_to_beta_230(self): # A few helper fns to keep the scenario tidy def ensure_system_group_exists(group): if (os.system('getent group {}'.format(group)) != 0): rc = os.system('groupadd -f -r {}'.format(group)) if rc != 0: logger.error("could not create group") def add_users_to_group(group): try: linux_users = get_users() if not linux_users: logger.error('beta_220_to_beta_230: linux_users is empty!') for user in linux_users: os.system('sudo usermod -a -G {} {}'.format(group, user)) except Exception as e: logger.error("Couldn't add users to {} group - [{}]".format( group, e)) def add_i2c_module_to_auto_loaded(): try: found_i2c_dev = False with open('/etc/modules', 'r') as f: for line in f: if line.strip() == 'i2c_dev': found_i2c_dev = True if not found_i2c_dev: with open('/etc/modules', 'a') as f: f.write('\ni2c_dev\n') except Exception as e: logger.error( "Couldn't add i2c_dev to /etc/modules - [{}]".format(e)) def remove_powerup_lnk_file(): try: linux_users = get_users() if not linux_users: logger.error('beta_220_to_beta_230: linux_users is empty!') lnk_dir_template = os.path.join(os.sep, 'home', '{user}', '.kdesktop', 'Powerup.lnk') for user in linux_users: lnk_dir = lnk_dir_template.format(user=user) if os.path.exists(lnk_dir): os.remove(lnk_dir) except Exception as e: logger.error("Couldn't remove Powerup.lnk - [{}]".format(e)) def enable_spi_device(): from kano_settings.boot_config import set_config_value set_config_value("dtparam=spi", "on") try: from kano_settings.boot_config import end_config_transaction end_config_transaction() except ImportError: logger.error( "end_config_transaciton not present - update to kano-settings failed?" ) # Scenario work starts here install('rsync') run_cmd_log('kano-apps install --no-gui powerup') remove_powerup_lnk_file() ensure_system_group_exists('gpio') ensure_system_group_exists('spi') add_users_to_group('i2c') add_users_to_group('gpio') add_users_to_group('spi') add_i2c_module_to_auto_loaded() # enable spi device enable_spi_device()
def beta_3_10_2_to_beta_3_10_3(self): # Attempt to fix overture starting after the update. run_cmd_log('kano-init finalise --force')
def beta_220_to_beta_230(self): # A few helper fns to keep the scenario tidy def ensure_system_group_exists(group): if(os.system('getent group {}'.format(group)) != 0): rc = os.system('groupadd -f -r {}'.format(group)) if rc != 0: logger.error("could not create group") def add_users_to_group(group): try: linux_users = get_users() if not linux_users: logger.error('beta_220_to_beta_230: linux_users is empty!') for user in linux_users: os.system('sudo usermod -a -G {} {}'.format(group, user)) except Exception as e: logger.error( "Couldn't add users to {} group - [{}]".format(group, e) ) def add_i2c_module_to_auto_loaded(): try: found_i2c_dev = False with open('/etc/modules', 'r') as f: for line in f: if line.strip() == 'i2c_dev': found_i2c_dev = True if not found_i2c_dev: with open('/etc/modules', 'a') as f: f.write('\ni2c_dev\n') except Exception as e: logger.error( "Couldn't add i2c_dev to /etc/modules - [{}]".format(e) ) def remove_powerup_lnk_file(): try: linux_users = get_users() if not linux_users: logger.error('beta_220_to_beta_230: linux_users is empty!') lnk_dir_template = os.path.join( os.sep, 'home', '{user}', '.kdesktop', 'Powerup.lnk' ) for user in linux_users: lnk_dir = lnk_dir_template.format(user=user) if os.path.exists(lnk_dir): os.remove(lnk_dir) except Exception as e: logger.error("Couldn't remove Powerup.lnk - [{}]".format(e)) def enable_spi_device(): from kano_settings.boot_config import set_config_value set_config_value("dtparam=spi", "on") try: from kano_settings.boot_config import end_config_transaction end_config_transaction() except ImportError: logger.error("end_config_transaciton not present - update to kano-settings failed?") # Scenario work starts here install('rsync') run_cmd_log('kano-apps install --no-gui powerup') remove_powerup_lnk_file() ensure_system_group_exists('gpio') ensure_system_group_exists('spi') add_users_to_group('i2c') add_users_to_group('gpio') add_users_to_group('spi') add_i2c_module_to_auto_loaded() # enable spi device enable_spi_device()
def beta_111_to_beta_120(self): run_cmd_log("kano-apps install --no-gui painter epdfview geany " "codecademy calculator leafpad vnc")
def upgrade_debian(gui_process): from kano_updater.utils import fix_broken, launch_gui_if_not_running, set_gui_stage # setting up apt-get for non-interactive mode os.environ['DEBIAN_FRONTEND'] = 'noninteractive' # Try to fix any broken packages prior to the upgrade fix_broken(_("Preparing packages to be upgraded")) # apt upgrade gui_process = launch_gui_if_not_running(gui_process) set_gui_stage(4) # try to download all files first, retry in a loop for i in xrange(5): _, _, rc = run_cmd_log('apt-get -y -d dist-upgrade') if rc == 0: break elif i == 4: return -1 set_gui_stage(5) # do the actual update using the downloaded files cmd = 'yes "" | apt-get -y -o Dpkg::Options::="--force-confdef" ' + \ '-o Dpkg::Options::="--force-confold" dist-upgrade' _, debian_err, _ = run_cmd_log(cmd) # apt autoremove gui_process = launch_gui_if_not_running(gui_process) set_gui_stage(6) cmd = 'apt-get -y autoremove --purge' run_cmd_log(cmd) # apt autoclean cmd = 'apt-get -y autoclean' run_cmd_log(cmd) # Try to fix any broken packages after the upgrade fix_broken(_("Finalising package upgrade")) # parsing debian error log if debian_err: err_split = debian_err.splitlines() dirs_delete = [] err_packages = [] for l in err_split: if 'dpkg: warning: unable to delete old directory' in l: parts = l.split("'") dirs_delete.append(parts[1].strip()) if 'dpkg: error processing' in l: parts = l.split('/var/cache/apt/archives/') packagename = parts[1].split()[0].strip()[:-4] err_packages.append(packagename) # remove left-over non-empty directories for dir in dirs_delete: delete_dir(dir) # return err_packages return err_packages return None
def _sync(): run_cmd_log( "su '{}' -c 'kano-sync --skip-kdesk --sync --backup --upload-tracking-data -s'".format(get_user_unsudoed()) )
def beta_3_9_2_to_beta_3_10_0(self): # The new Overture onboarding needs to be enabled - disabling old tty-based kano-init run_cmd_log('kano-init finalise --force') # Install the kano-os metapackage for top level OS packages. install('kano-os')