From 0557d80652eccc0bafc288547d7b80ce51b1a47c Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:39:20 -0700 Subject: [PATCH 01/71] refactor: split up hardware checks, added memory c --- files/usr/etc/ublue-update/ublue-update.toml | 6 +- src/ublue_update/cli.py | 141 ++++-------------- src/ublue_update/config.py | 35 +++++ src/ublue_update/update_checks/wait.py | 2 +- .../update_inhibitors/hardware.py | 74 +++++++++ 5 files changed, 141 insertions(+), 117 deletions(-) create mode 100644 src/ublue_update/config.py create mode 100644 src/ublue_update/update_inhibitors/hardware.py diff --git a/files/usr/etc/ublue-update/ublue-update.toml b/files/usr/etc/ublue-update/ublue-update.toml index b981c46..67275f4 100644 --- a/files/usr/etc/ublue-update/ublue-update.toml +++ b/files/usr/etc/ublue-update/ublue-update.toml @@ -1,5 +1,9 @@ [checks] min_battery_percent = 50.0 - max_cpu_load = 50.0 + max_cpu_load_percent = 50.0 + max_mem_percent = 50.0 [notify] dbus_notify = true +[schedule] + frequency="weekly" + diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 834acd6..d0464c5 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -1,25 +1,25 @@ -import psutil import os import subprocess import logging -import tomllib import argparse - from ublue_update.update_checks.system import system_update_check from ublue_update.update_checks.wait import transaction_wait +from ublue_update.update_inhibitors.hardware import check_hardware_inhibitors +from ublue_update.config import load_value -def notify(title: str, body: str, actions: list = [], expire_time: int = 0): +def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): + if not dbus_notify: + return args = [ "/usr/bin/notify-send", title, body, "--app-name=Universal Blue Updater", "--icon=software-update-available-symbolic", + f"--urgency=${urgency}", ] - if expire_time != 0: - args.append(f"--expire-time={expire_time}") if actions != []: for action in actions: args.append(f"--action={action}") @@ -28,11 +28,13 @@ def notify(title: str, body: str, actions: list = [], expire_time: int = 0): def ask_for_updates(): + if not dbus_notify: + return out = notify( "System Updater", "Update available, but system checks failed. Update now?", ["universal-blue-update-confirm=Confirm"], - 15000, + "critical" ) # if the user has confirmed if "universal-blue-update-confirm" in out.stdout.decode("utf-8"): @@ -51,48 +53,11 @@ def check_for_updates(checks_failed: bool) -> bool: return False -def check_cpu_load() -> dict: - # get load average percentage in last 5 minutes: - # https://psutil.readthedocs.io/en/latest/index.html?highlight=getloadavg - cpu_load = psutil.getloadavg()[1] / psutil.cpu_count() * 100 - return { - "passed": cpu_load < max_cpu_load, - "message": f"CPU load is above {max_cpu_load}%", - } - - -def check_network_status() -> dict: - network_status = psutil.net_if_stats() - # check each network interface - network_up = False - for key in network_status.keys(): - if key != "lo": - if network_status[key][0]: - network_up = True - break - return {"passed": network_up, "message": "Network not enabled"} - - -def check_battery_status() -> dict: - battery_status = psutil.sensors_battery() - # null safety on the battery variable, it returns "None" - # when the system doesn't have a battery - battery_pass: bool = True - if battery_status is not None: - battery_pass = ( - battery_status.percent > min_battery_percent or battery_status.power_plugged - ) - return { - "passed": battery_pass, - "message": f"Battery less than {min_battery_percent}%", - } - - def hardware_inhibitor_checks_failed( - hardware_checks_failed: bool, failures: list, dbus_ask_for_updates: bool + hardware_checks_failed: bool, failures: list, hardware_check: bool ): # ask if an update can be performed through dbus notifications - if check_for_updates(hardware_checks_failed) and dbus_ask_for_updates: + if check_for_updates(hardware_checks_failed) and not hardware_check: log.info("Harware checks failed, but update is available") ask_for_updates() # notify systemd that the checks have failed, @@ -101,55 +66,6 @@ def hardware_inhibitor_checks_failed( raise Exception(f"update failed to pass checks: \n - {exception_log}") -def check_hardware_inhibitors() -> bool: - - hardware_inhibitors = [ - check_network_status(), - check_battery_status(), - check_cpu_load(), - ] - - failures = [] - hardware_checks_failed = False - for inhibitor_result in hardware_inhibitors: - if not inhibitor_result["passed"]: - hardware_checks_failed = True - failures.append(inhibitor_result["message"]) - if not hardware_checks_failed: - log.info("System passed hardware checks") - return hardware_checks_failed, failures - - -def load_config(): - # load config values - config_paths = [ - os.path.expanduser("~/.config/ublue-update/ublue-update.toml"), - "/etc/ublue-update/ublue-update.toml", - "/usr/etc/ublue-update/ublue-update.toml", - ] - - # search for the right config - config_path = "" - fallback_config_path = "" - for path in config_paths: - if os.path.isfile(path): - if config_path == "": - config_path = path - fallback_config_path = path - break - - fallback_config = tomllib.load(open(fallback_config_path, "rb")) - config = tomllib.load(open(config_path, "rb")) - return config, fallback_config - - -def load_value(key, value): - fallback = fallback_config[key][value] - if key in config.keys(): - return config[key].get(value, fallback) - return fallback - - def run_updates(): root_dir = "/etc/ublue-update.d/" @@ -164,6 +80,7 @@ def run_updates(): executable = os.access(full_path, os.X_OK) if executable: + log.info(f"Running update script: {full_path}") out = subprocess.run( [full_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) @@ -171,27 +88,21 @@ def run_updates(): if out.returncode != 0: log.info(f"{full_path} returned error code: {out.returncode}") log.info(f"Program output: \n {out.stdout}") - if dbus_notify: - notify( - "System Updater", - f"Error in update script: {file}, check logs for more info", - ) + notify( + "System Updater", + f"Error in update script: {file}, check logs for more info", + ) else: log.info(f"could not execute file {full_path}") - if dbus_notify: - notify( - "System Updater", - "System update complete, reboot for changes to take effect", - ) + notify( + "System Updater", + "System update complete, reboot for changes to take effect", + ) log.info("System update complete") os._exit(0) -config, fallback_config = load_config() - dbus_notify: bool = load_value("notify", "dbus_notify") -min_battery_percent: int = load_value("checks", "min_battery_percent") -max_cpu_load: int = load_value("checks", "max_cpu_load") # setup logging logging.basicConfig( @@ -200,6 +111,7 @@ def run_updates(): ) log = logging.getLogger(__name__) + def main(): # setup argparse @@ -238,7 +150,7 @@ def main(): hardware_inhibitor_checks_failed( hardware_checks_failed, failures, - dbus_notify and not args.check, + args.check, ) if args.check: os._exit(0) @@ -251,9 +163,8 @@ def main(): # system checks passed log.info("System passed all update checks") - if dbus_notify: - notify( - "System Updater", - "System passed checks, updating ...", - ) + notify( + "System Updater", + "System passed checks, updating ...", + ) run_updates() diff --git a/src/ublue_update/config.py b/src/ublue_update/config.py new file mode 100644 index 0000000..73cc743 --- /dev/null +++ b/src/ublue_update/config.py @@ -0,0 +1,35 @@ +import tomllib +import os + + +def load_config(): + # load config values + config_paths = [ + os.path.expanduser("~/.config/ublue-update/ublue-update.toml"), + "/etc/ublue-update/ublue-update.toml", + "/usr/etc/ublue-update/ublue-update.toml", + ] + + # search for the right config + config_path = "" + fallback_config_path = "" + for path in config_paths: + if os.path.isfile(path): + if config_path == "": + config_path = path + fallback_config_path = path + break + + fallback_config = tomllib.load(open(fallback_config_path, "rb")) + config = tomllib.load(open(config_path, "rb")) + return config, fallback_config + + +def load_value(key, value): + fallback = fallback_config[key][value] + if key in config.keys(): + return config[key].get(value, fallback) + return fallback + + +config, fallback_config = load_config() diff --git a/src/ublue_update/update_checks/wait.py b/src/ublue_update/update_checks/wait.py index 2108841..998c56b 100644 --- a/src/ublue_update/update_checks/wait.py +++ b/src/ublue_update/update_checks/wait.py @@ -5,7 +5,7 @@ def transaction(): """Pull deployment status via rpm-ostree""" - rpm_ostree_status = ["rpm-ostree", "status", "--json"] + rpm_ostree_status = ["rpm-ostree", "status", "--json"] status = run(rpm_ostree_status, stdout=PIPE) """Parse transaction state""" return loads(status.stdout)["transaction"] diff --git a/src/ublue_update/update_inhibitors/hardware.py b/src/ublue_update/update_inhibitors/hardware.py new file mode 100644 index 0000000..f91352c --- /dev/null +++ b/src/ublue_update/update_inhibitors/hardware.py @@ -0,0 +1,74 @@ +import psutil +from logging import getLogger +from ublue_update.config import load_value + +"""Setup logging""" +log = getLogger(__name__) + +min_battery_percent: float = load_value("checks", "min_battery_percent") +max_cpu_load_percent: float = load_value("checks", "max_cpu_load_percent") +max_mem_percent: float = load_value("checks", "max_mem_percent") + +def check_network_status() -> dict: + network_status = psutil.net_if_stats() + # check each network interface + network_up = False + for key in network_status.keys(): + if key != "lo": + if network_status[key][0]: + network_up = True + break + return {"passed": network_up, "message": "Network not enabled"} + + +def check_battery_status() -> dict: + battery_status = psutil.sensors_battery() + # null safety on the battery variable, it returns "None" + # when the system doesn't have a battery + battery_pass: bool = True + if battery_status is not None: + battery_pass = ( + battery_status.percent > min_battery_percent or battery_status.power_plugged + ) + return { + "passed": battery_pass, + "message": f"Battery less than {min_battery_percent}%", + } + + +def check_cpu_load() -> dict: + # get load average percentage in last 5 minutes: + # https://psutil.readthedocs.io/en/latest/index.html?highlight=getloadavg + cpu_load = psutil.getloadavg()[1] / psutil.cpu_count() * 100 + return { + "passed": cpu_load < max_cpu_load, + "message": f"CPU load is above {max_cpu_load}%", + } + + +def check_mem_percentage() -> dict: + mem = psutil.virtual_memory() + return { + "passed": mem > max_mem_percent, + "message": f"Memory usage is above {max_mem_percent}%", + } + + +def check_hardware_inhibitors() -> bool: + + hardware_inhibitors = [ + check_network_status(), + check_battery_status(), + check_cpu_load(), + check_mem_percentage(), + ] + + failures = [] + hardware_checks_failed = False + for inhibitor_result in hardware_inhibitors: + if not inhibitor_result["passed"]: + hardware_checks_failed = True + failures.append(inhibitor_result["message"]) + if not hardware_checks_failed: + log.info("System passed hardware checks") + return hardware_checks_failed, failures From 32485806b6182d7ad104bfe0eeb559288167a572 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:45:58 -0700 Subject: [PATCH 02/71] fix: formatting and errors --- files/usr/etc/ublue-update/ublue-update.toml | 2 -- src/ublue_update/cli.py | 2 +- src/ublue_update/update_inhibitors/hardware.py | 7 ++++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/files/usr/etc/ublue-update/ublue-update.toml b/files/usr/etc/ublue-update/ublue-update.toml index 67275f4..47ff658 100644 --- a/files/usr/etc/ublue-update/ublue-update.toml +++ b/files/usr/etc/ublue-update/ublue-update.toml @@ -4,6 +4,4 @@ max_mem_percent = 50.0 [notify] dbus_notify = true -[schedule] - frequency="weekly" diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index d0464c5..93046a9 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -34,7 +34,7 @@ def ask_for_updates(): "System Updater", "Update available, but system checks failed. Update now?", ["universal-blue-update-confirm=Confirm"], - "critical" + "critical", ) # if the user has confirmed if "universal-blue-update-confirm" in out.stdout.decode("utf-8"): diff --git a/src/ublue_update/update_inhibitors/hardware.py b/src/ublue_update/update_inhibitors/hardware.py index f91352c..5011632 100644 --- a/src/ublue_update/update_inhibitors/hardware.py +++ b/src/ublue_update/update_inhibitors/hardware.py @@ -9,6 +9,7 @@ max_cpu_load_percent: float = load_value("checks", "max_cpu_load_percent") max_mem_percent: float = load_value("checks", "max_mem_percent") + def check_network_status() -> dict: network_status = psutil.net_if_stats() # check each network interface @@ -39,10 +40,10 @@ def check_battery_status() -> dict: def check_cpu_load() -> dict: # get load average percentage in last 5 minutes: # https://psutil.readthedocs.io/en/latest/index.html?highlight=getloadavg - cpu_load = psutil.getloadavg()[1] / psutil.cpu_count() * 100 + cpu_load_percent = psutil.getloadavg()[1] / psutil.cpu_count() * 100 return { - "passed": cpu_load < max_cpu_load, - "message": f"CPU load is above {max_cpu_load}%", + "passed": cpu_load_percent < max_cpu_load_percent, + "message": f"CPU load is above {max_cpu_load_percent}%", } From 85de3b3e3d2c31889bc70695446f58e217ef8bda Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:53:35 -0700 Subject: [PATCH 03/71] fix(hardware.py): fixed memory check --- src/ublue_update/update_inhibitors/hardware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ublue_update/update_inhibitors/hardware.py b/src/ublue_update/update_inhibitors/hardware.py index 5011632..93a04e4 100644 --- a/src/ublue_update/update_inhibitors/hardware.py +++ b/src/ublue_update/update_inhibitors/hardware.py @@ -50,7 +50,7 @@ def check_cpu_load() -> dict: def check_mem_percentage() -> dict: mem = psutil.virtual_memory() return { - "passed": mem > max_mem_percent, + "passed": mem.percent < max_mem_percent, "message": f"Memory usage is above {max_mem_percent}%", } From 35a1c4f8a37c3661e2df268370ed51802fd4370c Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 4 Sep 2023 16:33:39 -0700 Subject: [PATCH 04/71] fix: change default max_mem_percent to 90.0 --- files/usr/etc/ublue-update/ublue-update.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/usr/etc/ublue-update/ublue-update.toml b/files/usr/etc/ublue-update/ublue-update.toml index 47ff658..957b128 100644 --- a/files/usr/etc/ublue-update/ublue-update.toml +++ b/files/usr/etc/ublue-update/ublue-update.toml @@ -1,7 +1,7 @@ [checks] min_battery_percent = 50.0 max_cpu_load_percent = 50.0 - max_mem_percent = 50.0 + max_mem_percent = 90.0 [notify] dbus_notify = true From 44af2b95db7ee5dc8eb6f954d963231c5f726ed4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 18:47:45 +0000 Subject: [PATCH 05/71] chore(deps): bump sigstore/cosign-installer from 3.1.1 to 3.1.2 Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.1.1 to 3.1.2. - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/v3.1.1...v3.1.2) --- updated-dependencies: - dependency-name: sigstore/cosign-installer dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1ae985f..02eddcd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,7 +89,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} # Sign container - - uses: sigstore/cosign-installer@v3.1.1 + - uses: sigstore/cosign-installer@v3.1.2 if: github.event_name != 'pull_request' - name: Sign container image From 1cdc0f3d3fa49da846323796ead842b87e44d5f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 19:22:27 +0000 Subject: [PATCH 06/71] chore(main): release 1.2.1 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c72bae..76cb050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.2.1](https://github.com/ublue-os/ublue-update/compare/v1.2.0...v1.2.1) (2023-09-04) + + +### Bug Fixes + +* missing build requires python-pip ([#56](https://github.com/ublue-os/ublue-update/issues/56)) ([c583cae](https://github.com/ublue-os/ublue-update/commit/c583cae21c10eca1b7bf146f2ee066d65dc8af2a)) + ## [1.2.0](https://github.com/ublue-os/ublue-update/compare/v1.1.6...v1.2.0) (2023-09-02) From e8f590ef809a32dba4f64de19405de4afe05e48e Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 4 Sep 2023 17:16:24 -0700 Subject: [PATCH 07/71] docs: update README --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c5bff12..81c57e9 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,12 @@ valid config paths (in order of priority) ## Config Variables section: `checks` -`battery_percent`: checks if battery is above specified percent +`min_battery_percent`: checks if battery is above specified percent + +`max_cpu_load_percent`: checks if cpu average load is under specified percent + +`max_mem_percent`: checks memory usage is below specified the percent -`cpu_load`: checks if cpu average load is under specified percent section: `notify` From 72bd8cae10e11cbc30a0c62772cd38f4bbfd146f Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 4 Sep 2023 17:17:25 -0700 Subject: [PATCH 08/71] docs: fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 81c57e9..5be4bd4 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ section: `checks` `max_cpu_load_percent`: checks if cpu average load is under specified percent -`max_mem_percent`: checks memory usage is below specified the percent +`max_mem_percent`: checks if memory usage is below specified the percent section: `notify` From 54d92a8a06e847f287d4f6f099978ce06df38776 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 4 Sep 2023 17:32:12 -0700 Subject: [PATCH 09/71] fix: remove typo in arguments for notify-send --- src/ublue_update/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 93046a9..a0887fc 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -18,7 +18,7 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): body, "--app-name=Universal Blue Updater", "--icon=software-update-available-symbolic", - f"--urgency=${urgency}", + f"--urgency={urgency}", ] if actions != []: for action in actions: From 8d683add501d59bb63751ad201d7656fcfb609aa Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Wed, 6 Sep 2023 23:52:46 -0700 Subject: [PATCH 10/71] feat: initial work for running system updates as root, and updates for each user --- .../usr/lib/systemd/user/ublue-update.service | 2 - src/ublue_update/cli.py | 43 +++++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/files/usr/lib/systemd/user/ublue-update.service b/files/usr/lib/systemd/user/ublue-update.service index 7fc8b4d..42d1297 100644 --- a/files/usr/lib/systemd/user/ublue-update.service +++ b/files/usr/lib/systemd/user/ublue-update.service @@ -6,5 +6,3 @@ Restart=on-failure RestartSec=1h Type=oneshot ExecStart=/usr/bin/ublue-update - - diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index a0887fc..c1b7003 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -2,6 +2,7 @@ import subprocess import logging import argparse +import pwd from ublue_update.update_checks.system import system_update_check from ublue_update.update_checks.wait import transaction_wait @@ -66,34 +67,44 @@ def hardware_inhibitor_checks_failed( raise Exception(f"update failed to pass checks: \n - {exception_log}") -def run_updates(): - root_dir = "/etc/ublue-update.d/" - - log.info("Running system update") - - """Wait on any existing transactions to complete before updating""" - transaction_wait() - +def run_user_updates(user: str, root_dir: str): for root, dirs, files in os.walk(root_dir): for file in files: full_path = root_dir + str(file) - executable = os.access(full_path, os.X_OK) if executable: log.info(f"Running update script: {full_path}") out = subprocess.run( - [full_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ["/usr/bin/sudo","-u",f"{user}",full_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) - if out.returncode != 0: log.info(f"{full_path} returned error code: {out.returncode}") log.info(f"Program output: \n {out.stdout}") - notify( - "System Updater", - f"Error in update script: {file}, check logs for more info", - ) + notify( + "System Updater", + f"Error in update script: {file}, check logs for more info", + ) else: log.info(f"could not execute file {full_path}") + +def run_updates(): + root_dir = "/etc/ublue-update.d" + + log.info("Running system update") + + """Wait on any existing transactions to complete before updating""" + transaction_wait() + users=[] + for user in pwd.getpwall(): + if "/home" in user.pw_dir: + users.append(user.pw_name) + + run_user_updates("root", root_dir + "/system") + + for user in users: + run_user_updates(user, root_dir + "/user") + + notify( "System Updater", "System update complete, reboot for changes to take effect", @@ -101,7 +112,6 @@ def run_updates(): log.info("System update complete") os._exit(0) - dbus_notify: bool = load_value("notify", "dbus_notify") # setup logging @@ -111,7 +121,6 @@ def run_updates(): ) log = logging.getLogger(__name__) - def main(): # setup argparse From 0b544961438c4db3acbbe1ac2bb6cf7402024be7 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:10:28 -0700 Subject: [PATCH 11/71] fix: add root requirement for running updates --- src/ublue_update/cli.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index c1b7003..0522b18 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -79,7 +79,7 @@ def run_user_updates(user: str, root_dir: str): ) if out.returncode != 0: log.info(f"{full_path} returned error code: {out.returncode}") - log.info(f"Program output: \n {out.stdout}") + log.info(f"Program output: \n {out.stdout.decode('utf-8')}") notify( "System Updater", f"Error in update script: {file}, check logs for more info", @@ -88,6 +88,16 @@ def run_user_updates(user: str, root_dir: str): log.info(f"could not execute file {full_path}") def run_updates(): + if os.getuid() != 0: + notify( + "System Updater", + "ublue-update needs root to run updates!", + ) + raise Exception("ublue-update needs root to run updates!") + notify( + "System Updater", + "System passed checks, updating ...", + ) root_dir = "/etc/ublue-update.d" log.info("Running system update") @@ -97,12 +107,13 @@ def run_updates(): users=[] for user in pwd.getpwall(): if "/home" in user.pw_dir: - users.append(user.pw_name) - - run_user_updates("root", root_dir + "/system") + users.append(str(user.pw_name)) + print(users) + run_user_updates("root", root_dir + "/system/") for user in users: - run_user_updates(user, root_dir + "/user") + print("why?") + run_user_updates(user, root_dir + "/user/") notify( @@ -172,8 +183,4 @@ def main(): # system checks passed log.info("System passed all update checks") - notify( - "System Updater", - "System passed checks, updating ...", - ) run_updates() From 03a70eb3234ddbbfbf576323dd8fd97d9a9bef0a Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:13:17 -0700 Subject: [PATCH 12/71] fix: remove home directory configuration, ublue-update is now system-wide --- README.md | 2 -- src/ublue_update/config.py | 1 - 2 files changed, 3 deletions(-) diff --git a/README.md b/README.md index 5be4bd4..45b386f 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,6 @@ options: ## Location valid config paths (in order of priority) -```"$HOME"/.config/ublue-update/ublue-update.toml``` - ```/etc/ublue-update/ublue-update.toml``` ```/usr/etc/ublue-update/ublue-update.toml``` diff --git a/src/ublue_update/config.py b/src/ublue_update/config.py index 73cc743..d9b4fec 100644 --- a/src/ublue_update/config.py +++ b/src/ublue_update/config.py @@ -5,7 +5,6 @@ def load_config(): # load config values config_paths = [ - os.path.expanduser("~/.config/ublue-update/ublue-update.toml"), "/etc/ublue-update/ublue-update.toml", "/usr/etc/ublue-update/ublue-update.toml", ] From 936db3f8b51bb0b5a488cd94973fbd0b495b10f4 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:20:10 -0700 Subject: [PATCH 13/71] chore: move to system service, update spec file and config --- .../ublue-update.d/{ => system}/00-system-update.sh | 0 .../{ => system}/01-flatpak-system-update.sh | 0 .../02-flatpak-system-repair-cleanup.sh} | 0 .../00-flatpak-user-update.sh} | 0 .../01-flatpak-user-repair-cleanup.sh} | 0 .../02-distrobox-user-update.sh} | 0 .../03-fleek-user-update.sh} | 0 .../00-ublue-update.preset | 0 .../systemd/{user => system}/ublue-update.service | 0 .../lib/systemd/{user => system}/ublue-update.timer | 0 ublue-update.spec.rpkg | 13 +++++++------ 11 files changed, 7 insertions(+), 6 deletions(-) rename files/etc/ublue-update.d/{ => system}/00-system-update.sh (100%) rename files/etc/ublue-update.d/{ => system}/01-flatpak-system-update.sh (100%) rename files/etc/ublue-update.d/{03-flatpak-system-repair-cleanup.sh => system/02-flatpak-system-repair-cleanup.sh} (100%) rename files/etc/ublue-update.d/{02-flatpak-user-update.sh => user/00-flatpak-user-update.sh} (100%) rename files/etc/ublue-update.d/{04-flatpak-user-repair-cleanup.sh => user/01-flatpak-user-repair-cleanup.sh} (100%) rename files/etc/ublue-update.d/{05-distrobox-user-update.sh => user/02-distrobox-user-update.sh} (100%) rename files/etc/ublue-update.d/{06-fleek-user-update.sh => user/03-fleek-user-update.sh} (100%) rename files/usr/lib/systemd/{user-preset => system-preset}/00-ublue-update.preset (100%) rename files/usr/lib/systemd/{user => system}/ublue-update.service (100%) rename files/usr/lib/systemd/{user => system}/ublue-update.timer (100%) diff --git a/files/etc/ublue-update.d/00-system-update.sh b/files/etc/ublue-update.d/system/00-system-update.sh similarity index 100% rename from files/etc/ublue-update.d/00-system-update.sh rename to files/etc/ublue-update.d/system/00-system-update.sh diff --git a/files/etc/ublue-update.d/01-flatpak-system-update.sh b/files/etc/ublue-update.d/system/01-flatpak-system-update.sh similarity index 100% rename from files/etc/ublue-update.d/01-flatpak-system-update.sh rename to files/etc/ublue-update.d/system/01-flatpak-system-update.sh diff --git a/files/etc/ublue-update.d/03-flatpak-system-repair-cleanup.sh b/files/etc/ublue-update.d/system/02-flatpak-system-repair-cleanup.sh similarity index 100% rename from files/etc/ublue-update.d/03-flatpak-system-repair-cleanup.sh rename to files/etc/ublue-update.d/system/02-flatpak-system-repair-cleanup.sh diff --git a/files/etc/ublue-update.d/02-flatpak-user-update.sh b/files/etc/ublue-update.d/user/00-flatpak-user-update.sh similarity index 100% rename from files/etc/ublue-update.d/02-flatpak-user-update.sh rename to files/etc/ublue-update.d/user/00-flatpak-user-update.sh diff --git a/files/etc/ublue-update.d/04-flatpak-user-repair-cleanup.sh b/files/etc/ublue-update.d/user/01-flatpak-user-repair-cleanup.sh similarity index 100% rename from files/etc/ublue-update.d/04-flatpak-user-repair-cleanup.sh rename to files/etc/ublue-update.d/user/01-flatpak-user-repair-cleanup.sh diff --git a/files/etc/ublue-update.d/05-distrobox-user-update.sh b/files/etc/ublue-update.d/user/02-distrobox-user-update.sh similarity index 100% rename from files/etc/ublue-update.d/05-distrobox-user-update.sh rename to files/etc/ublue-update.d/user/02-distrobox-user-update.sh diff --git a/files/etc/ublue-update.d/06-fleek-user-update.sh b/files/etc/ublue-update.d/user/03-fleek-user-update.sh similarity index 100% rename from files/etc/ublue-update.d/06-fleek-user-update.sh rename to files/etc/ublue-update.d/user/03-fleek-user-update.sh diff --git a/files/usr/lib/systemd/user-preset/00-ublue-update.preset b/files/usr/lib/systemd/system-preset/00-ublue-update.preset similarity index 100% rename from files/usr/lib/systemd/user-preset/00-ublue-update.preset rename to files/usr/lib/systemd/system-preset/00-ublue-update.preset diff --git a/files/usr/lib/systemd/user/ublue-update.service b/files/usr/lib/systemd/system/ublue-update.service similarity index 100% rename from files/usr/lib/systemd/user/ublue-update.service rename to files/usr/lib/systemd/system/ublue-update.service diff --git a/files/usr/lib/systemd/user/ublue-update.timer b/files/usr/lib/systemd/system/ublue-update.timer similarity index 100% rename from files/usr/lib/systemd/user/ublue-update.timer rename to files/usr/lib/systemd/system/ublue-update.timer diff --git a/ublue-update.spec.rpkg b/ublue-update.spec.rpkg index be564fe..1d7f3b2 100644 --- a/ublue-update.spec.rpkg +++ b/ublue-update.spec.rpkg @@ -53,18 +53,19 @@ shellcheck files/etc/%{NAME}.d/*.sh cp -rp files/etc files/usr %{buildroot} %post -%systemd_user_post %{NAME}.timer +%systemd_post %{NAME}.timer %preun -%systemd_user_preun %{NAME}.timer +%systemd_preun %{NAME}.timer %files -f %{pyproject_files} %attr(0755,root,root) %{_bindir}/%{name} -%attr(0644,root,root) %{_exec_prefix}/lib/systemd/user/%{NAME}.service -%attr(0644,root,root) %{_exec_prefix}/lib/systemd/user/%{NAME}.timer -%attr(0644,root,root) %{_exec_prefix}/lib/systemd/user-preset/00-%{NAME}.preset +%attr(0644,root,root) %{_exec_prefix}/lib/systemd/system/%{NAME}.service +%attr(0644,root,root) %{_exec_prefix}/lib/systemd/system/%{NAME}.timer +%attr(0644,root,root) %{_exec_prefix}/lib/systemd/system-preset/00-%{NAME}.preset %attr(0644,root,root) %{_exec_prefix}/etc/%{NAME}/%{NAME}.toml -%attr(0755,root,root) %{_sysconfdir}/%{NAME}.d/*.sh +%attr(0755,root,root) %{_sysconfdir}/%{NAME}.d/user/*.sh +%attr(0755,root,root) %{_sysconfdir}/%{NAME}.d/system/*.sh %changelog %autochangelog From ab866af9c05e0b1d116d09302aac4c10659b665c Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:24:27 -0700 Subject: [PATCH 14/71] chore: remove debug print statements --- src/ublue_update/cli.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 0522b18..19099d9 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -108,11 +108,9 @@ def run_updates(): for user in pwd.getpwall(): if "/home" in user.pw_dir: users.append(str(user.pw_name)) - print(users) run_user_updates("root", root_dir + "/system/") for user in users: - print("why?") run_user_updates(user, root_dir + "/user/") From 697a986001796c23ae573824e51ad6d7ea4f6b0e Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 15:01:08 -0700 Subject: [PATCH 15/71] feat: added --user and --system flags --- src/ublue_update/cli.py | 66 ++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 19099d9..1be5b58 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -39,7 +39,7 @@ def ask_for_updates(): ) # if the user has confirmed if "universal-blue-update-confirm" in out.stdout.decode("utf-8"): - run_updates() + run_updates(args) def check_for_updates(checks_failed: bool) -> bool: @@ -67,7 +67,18 @@ def hardware_inhibitor_checks_failed( raise Exception(f"update failed to pass checks: \n - {exception_log}") -def run_user_updates(user: str, root_dir: str): +def run_user_updates(process_uid: int, user_uid: int, root_dir: str): + if process_uid != 0 and user_uid != process_uid: + if user_uid == 0: + notify( + "System Updater", + "ublue-update needs root for system updates!", + ) + raise Exception("ublue-update needs root for system updates!") + return + + user_name = pwd.getpwuid(user_uid).pw_name + log.info(f"Running update for user: '{user_name}', update script directory: '{root_dir}'") for root, dirs, files in os.walk(root_dir): for file in files: full_path = root_dir + str(file) @@ -75,43 +86,40 @@ def run_user_updates(user: str, root_dir: str): if executable: log.info(f"Running update script: {full_path}") out = subprocess.run( - ["/usr/bin/sudo","-u",f"{user}",full_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ["/usr/bin/sudo","-u",f"{user_name}",full_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) if out.returncode != 0: log.info(f"{full_path} returned error code: {out.returncode}") log.info(f"Program output: \n {out.stdout.decode('utf-8')}") - notify( - "System Updater", - f"Error in update script: {file}, check logs for more info", - ) + notify( + "System Updater", + f"Error in update script: {file}, check logs for more info", + ) else: log.info(f"could not execute file {full_path}") -def run_updates(): - if os.getuid() != 0: - notify( - "System Updater", - "ublue-update needs root to run updates!", - ) - raise Exception("ublue-update needs root to run updates!") +def run_updates(args): notify( "System Updater", "System passed checks, updating ...", ) root_dir = "/etc/ublue-update.d" - log.info("Running system update") - """Wait on any existing transactions to complete before updating""" transaction_wait() - users=[] - for user in pwd.getpwall(): - if "/home" in user.pw_dir: - users.append(str(user.pw_name)) - run_user_updates("root", root_dir + "/system/") + user_uids=[] + not_specified=(not args.user and not args.system) + + if not_specified or args.user: + for user in pwd.getpwall(): + if "/home" in user.pw_dir: + user_uids.append(user.pw_uid) + process_uid = os.getuid() + if not_specified or args.system: + run_user_updates(process_uid, 0, root_dir + "/system/") - for user in users: - run_user_updates(user, root_dir + "/user/") + for user_uid in user_uids: + run_user_updates(process_uid, user_uid, root_dir + "/user/") notify( @@ -155,6 +163,16 @@ def main(): action="store_true", help="wait for transactions to complete and exit", ) + parser.add_argument( + "--user", + action="store_true", + help="run user updates", + ) + parser.add_argument( + "--system", + action="store_true", + help="run system updates", + ) args = parser.parse_args() hardware_checks_failed = False @@ -181,4 +199,4 @@ def main(): # system checks passed log.info("System passed all update checks") - run_updates() + run_updates(args) From 7856d988cc0b219acf368fc6bdf5650cb4889058 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 15:06:09 -0700 Subject: [PATCH 16/71] fix: reformat to please the formatting gods --- src/ublue_update/cli.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 1be5b58..90a0de7 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -39,7 +39,7 @@ def ask_for_updates(): ) # if the user has confirmed if "universal-blue-update-confirm" in out.stdout.decode("utf-8"): - run_updates(args) + run_updates(cli_args) def check_for_updates(checks_failed: bool) -> bool: @@ -78,7 +78,9 @@ def run_user_updates(process_uid: int, user_uid: int, root_dir: str): return user_name = pwd.getpwuid(user_uid).pw_name - log.info(f"Running update for user: '{user_name}', update script directory: '{root_dir}'") + log.info( + f"Running update for user: '{user_name}', update script directory: '{root_dir}'" + ) for root, dirs, files in os.walk(root_dir): for file in files: full_path = root_dir + str(file) @@ -86,7 +88,9 @@ def run_user_updates(process_uid: int, user_uid: int, root_dir: str): if executable: log.info(f"Running update script: {full_path}") out = subprocess.run( - ["/usr/bin/sudo","-u",f"{user_name}",full_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ["/usr/bin/sudo", "-u", f"{user_name}", full_path], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, ) if out.returncode != 0: log.info(f"{full_path} returned error code: {out.returncode}") @@ -98,6 +102,7 @@ def run_user_updates(process_uid: int, user_uid: int, root_dir: str): else: log.info(f"could not execute file {full_path}") + def run_updates(args): notify( "System Updater", @@ -107,8 +112,8 @@ def run_updates(args): """Wait on any existing transactions to complete before updating""" transaction_wait() - user_uids=[] - not_specified=(not args.user and not args.system) + user_uids = [] + not_specified = not args.user and not args.system if not_specified or args.user: for user in pwd.getpwall(): @@ -121,7 +126,6 @@ def run_updates(args): for user_uid in user_uids: run_user_updates(process_uid, user_uid, root_dir + "/user/") - notify( "System Updater", "System update complete, reboot for changes to take effect", @@ -129,6 +133,7 @@ def run_updates(args): log.info("System update complete") os._exit(0) + dbus_notify: bool = load_value("notify", "dbus_notify") # setup logging @@ -138,6 +143,9 @@ def run_updates(args): ) log = logging.getLogger(__name__) +cli_args = None + + def main(): # setup argparse @@ -173,25 +181,25 @@ def main(): action="store_true", help="run system updates", ) - args = parser.parse_args() + cli_args = parser.parse_args() hardware_checks_failed = False - if args.wait: + if cli_args.wait: transaction_wait() os._exit(0) - if not args.force and not args.updatecheck: + if not cli_args.force and not cli_args.updatecheck: hardware_checks_failed, failures = check_hardware_inhibitors() if hardware_checks_failed: hardware_inhibitor_checks_failed( hardware_checks_failed, failures, - args.check, + cli_args.check, ) - if args.check: + if cli_args.check: os._exit(0) - if args.updatecheck: + if cli_args.updatecheck: update_available = check_for_updates(False) if not update_available: raise Exception("Update not available") @@ -199,4 +207,4 @@ def main(): # system checks passed log.info("System passed all update checks") - run_updates(args) + run_updates(cli_args) From 3fcefb137ab8a46e6f9fbc25274bdd1a0859abce Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 19:52:51 -0700 Subject: [PATCH 17/71] fix: update shellcheck path in RPM spec --- ublue-update.spec.rpkg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ublue-update.spec.rpkg b/ublue-update.spec.rpkg index 1d7f3b2..1953db8 100644 --- a/ublue-update.spec.rpkg +++ b/ublue-update.spec.rpkg @@ -44,7 +44,8 @@ ls ls src black src flake8 src -shellcheck files/etc/%{NAME}.d/*.sh +shellcheck files/etc/%{NAME}.d/user/*.sh +shellcheck files/etc/%{NAME}.d/system/*.sh %pyproject_wheel %install From e99bb03fe04886ffe1f5e705b3590e3cad9a623e Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 19:53:28 -0700 Subject: [PATCH 18/71] chore: remove unneeded python deps --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 41d8c81..edd02f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,8 +16,6 @@ classifiers = [ ] dependencies = [ "psutil", - "pygobject", - "dbus-python", ] dynamic = ["version"] From ac6cf0027fee90688d50dc67bd1dfda13216e6b3 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 19:54:31 -0700 Subject: [PATCH 19/71] fix: added sudo as a dependency --- ublue-update.spec.rpkg | 1 + 1 file changed, 1 insertion(+) diff --git a/ublue-update.spec.rpkg b/ublue-update.spec.rpkg index 1953db8..6381868 100644 --- a/ublue-update.spec.rpkg +++ b/ublue-update.spec.rpkg @@ -30,6 +30,7 @@ BuildRequires: python-setuptools_scm BuildRequires: python-wheel Requires: skopeo Requires: libnotify +Requires: sudo %global sub_name %{lua:t=string.gsub(rpm.expand("%{NAME}"), "^ublue%-", ""); print(t)} From 5a97220aea79964cd66003751208397be8312f90 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 7 Sep 2023 19:58:55 -0700 Subject: [PATCH 20/71] docs: add note about rpm-ostreed config from ublue-os/config --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 45b386f..fbbda95 100644 --- a/README.md +++ b/README.md @@ -26,16 +26,23 @@ COPY --from=ghcr.io/ublue-os/ublue-update:latest /rpms/ublue-update.noarch.rpm / RUN rpm-ostree override remove ublue-os-update-services && rpm-ostree install /tmp/rpms/ublue-update.noarch.rpm ``` +> **Note** +> If you are on an image derived from uBlue main, you will need to remove or disable automatic updates with rpm-ostreed, to do this, you need to remove or change this line in the config file: `AutomaticUpdatePolicy=stage` (set to `none` if you don't want to remove it) + + ## Command Line ``` -usage: ublue-update [-h] [-f] [-c] [-u] +usage: ublue-update [-h] [-f] [-c] [-u] [-w] [--user] [--system] options: -h, --help show this help message and exit -f, --force force manual update, skipping update checks -c, --check run update checks and exit -u, --updatecheck check for updates and exit + -w, --wait wait for transactions to complete and exit + --user run user updates + --system run system updates ``` From bdc19c50c4adcab42bdd8e16d8bff4d74be81cb0 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Fri, 8 Sep 2023 09:24:15 -0700 Subject: [PATCH 21/71] fix: use less sudo, clean up process ee --- src/ublue_update/cli.py | 57 ++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 90a0de7..23afba5 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -67,20 +67,11 @@ def hardware_inhibitor_checks_failed( raise Exception(f"update failed to pass checks: \n - {exception_log}") -def run_user_updates(process_uid: int, user_uid: int, root_dir: str): - if process_uid != 0 and user_uid != process_uid: - if user_uid == 0: - notify( - "System Updater", - "ublue-update needs root for system updates!", - ) - raise Exception("ublue-update needs root for system updates!") - return +def get_xdg_runtime_dir(uid): + return f"/run/{os.getpwuid(uid).pw_name}/{uid}" - user_name = pwd.getpwuid(user_uid).pw_name - log.info( - f"Running update for user: '{user_name}', update script directory: '{root_dir}'" - ) + +def run_updates(root_dir: str): for root, dirs, files in os.walk(root_dir): for file in files: full_path = root_dir + str(file) @@ -112,19 +103,39 @@ def run_updates(args): """Wait on any existing transactions to complete before updating""" transaction_wait() - user_uids = [] - not_specified = not args.user and not args.system - if not_specified or args.user: - for user in pwd.getpwall(): - if "/home" in user.pw_dir: - user_uids.append(user.pw_uid) process_uid = os.getuid() - if not_specified or args.system: - run_user_updates(process_uid, 0, root_dir + "/system/") + if process_uid == 0: + user_uids = [] + not_specified = not args.user and not args.system + if not_specified or args.user: + for user in pwd.getpwall(): + if "/home" in user.pw_dir: + user_uids.append(user.pw_uid) + + if not_specified or args.system: + run_updates(root_dir + "/system") + + for user_uid in user_uids: + log.info( + f"Running update for user: '{user_name}', update script directory: '{root_dir}'" + ) + subprocess.run( + [ + f"XDG_RUNTIME_DIR={get_xdg_runtime_dir()}", + f"DBUS_SESSION_BUS=unix://{get_xdg_runtime_dir(process_uid)}/bus", + "sudo", + "-u", + f"{os.getpwuid(process_uid).pw_name}", + "/usr/bin/ublue-update", + "--user", + "-f", + ] + ) + else: + run_updates(root_dir + "/user") + - for user_uid in user_uids: - run_user_updates(process_uid, user_uid, root_dir + "/user/") notify( "System Updater", From 0dcd2e9badad3db42abc5ce8b4c7dce53bd66b4d Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Fri, 8 Sep 2023 09:30:45 -0700 Subject: [PATCH 22/71] chore: remove useless --user flag --- src/ublue_update/cli.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 23afba5..efe7225 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -107,14 +107,12 @@ def run_updates(args): process_uid = os.getuid() if process_uid == 0: user_uids = [] - not_specified = not args.user and not args.system - if not_specified or args.user: + if not args.system: for user in pwd.getpwall(): if "/home" in user.pw_dir: user_uids.append(user.pw_uid) - if not_specified or args.system: - run_updates(root_dir + "/system") + run_updates(root_dir + "/system") for user_uid in user_uids: log.info( @@ -128,15 +126,14 @@ def run_updates(args): "-u", f"{os.getpwuid(process_uid).pw_name}", "/usr/bin/ublue-update", - "--user", "-f", ] ) else: + if args.system: + raise Exception("ublue-update needs to be run as root to perform system updates!") run_updates(root_dir + "/user") - - notify( "System Updater", "System update complete, reboot for changes to take effect", @@ -182,15 +179,10 @@ def main(): action="store_true", help="wait for transactions to complete and exit", ) - parser.add_argument( - "--user", - action="store_true", - help="run user updates", - ) parser.add_argument( "--system", action="store_true", - help="run system updates", + help="only run system updates (requires root)", ) cli_args = parser.parse_args() hardware_checks_failed = False From 5a28f1fbd26faa352979d7a74cd322c1894ef596 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Fri, 8 Sep 2023 09:41:03 -0700 Subject: [PATCH 23/71] fix: don't run sudo for each script, fixed string formatting --- src/ublue_update/cli.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index efe7225..0481c2f 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -71,7 +71,7 @@ def get_xdg_runtime_dir(uid): return f"/run/{os.getpwuid(uid).pw_name}/{uid}" -def run_updates(root_dir: str): +def run_update_scripts(root_dir: str): for root, dirs, files in os.walk(root_dir): for file in files: full_path = root_dir + str(file) @@ -79,7 +79,7 @@ def run_updates(root_dir: str): if executable: log.info(f"Running update script: {full_path}") out = subprocess.run( - ["/usr/bin/sudo", "-u", f"{user_name}", full_path], + [full_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) @@ -103,7 +103,6 @@ def run_updates(args): """Wait on any existing transactions to complete before updating""" transaction_wait() - process_uid = os.getuid() if process_uid == 0: user_uids = [] @@ -112,11 +111,10 @@ def run_updates(args): if "/home" in user.pw_dir: user_uids.append(user.pw_uid) - run_updates(root_dir + "/system") - + run_updates(root_dir + "/system/") for user_uid in user_uids: log.info( - f"Running update for user: '{user_name}', update script directory: '{root_dir}'" + f"Running update for user: '{pwd.getpwuid(process_uid).pw_name}', update script directory: '{root_dir}/user'" ) subprocess.run( [ @@ -132,7 +130,7 @@ def run_updates(args): else: if args.system: raise Exception("ublue-update needs to be run as root to perform system updates!") - run_updates(root_dir + "/user") + run_update_scripts(root_dir + "/user/") notify( "System Updater", From 451e33c9d9b5df9c1043d6239a7fc637a2e6d278 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Sun, 10 Sep 2023 21:17:57 -0700 Subject: [PATCH 24/71] fix: fix python errors --- src/ublue_update/cli.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 0481c2f..0bd5d67 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -3,6 +3,7 @@ import logging import argparse import pwd +import psutil from ublue_update.update_checks.system import system_update_check from ublue_update.update_checks.wait import transaction_wait @@ -10,9 +11,14 @@ from ublue_update.config import load_value +def get_xdg_runtime_dir(uid): + return f"/run/{os.getpwuid(uid).pw_name}/{uid}" + + def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if not dbus_notify: return + process_uid = os.getuid() args = [ "/usr/bin/notify-send", title, @@ -21,6 +27,20 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): "--icon=software-update-available-symbolic", f"--urgency={urgency}", ] + if process_uid == 0: + users = psutil.users() + for user in users: + uid = pwd.getpwuid(user.name) + user_args = [ + "DISPLAY=:0", + "DBUS_SESSION_BUS_ADDRESS=unix:path={get_xdg_runtime_dir(uid)}/bus", + "sudo", + "-u", + f"{user.name}", + ] + user_args += args + out = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + return if actions != []: for action in actions: args.append(f"--action={action}") @@ -66,11 +86,6 @@ def hardware_inhibitor_checks_failed( exception_log = "\n - ".join(failures) raise Exception(f"update failed to pass checks: \n - {exception_log}") - -def get_xdg_runtime_dir(uid): - return f"/run/{os.getpwuid(uid).pw_name}/{uid}" - - def run_update_scripts(root_dir: str): for root, dirs, files in os.walk(root_dir): for file in files: @@ -103,6 +118,7 @@ def run_updates(args): """Wait on any existing transactions to complete before updating""" transaction_wait() + process_uid = os.getuid() if process_uid == 0: user_uids = [] @@ -111,7 +127,7 @@ def run_updates(args): if "/home" in user.pw_dir: user_uids.append(user.pw_uid) - run_updates(root_dir + "/system/") + run_update_scripts(root_dir + "/system/") for user_uid in user_uids: log.info( f"Running update for user: '{pwd.getpwuid(process_uid).pw_name}', update script directory: '{root_dir}/user'" From 5b54c9afd13b1a102f104ceb1cf50c9625e462ed Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Sun, 10 Sep 2023 21:26:17 -0700 Subject: [PATCH 25/71] style: fix formatting issues --- src/ublue_update/cli.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 0bd5d67..589789b 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -30,10 +30,10 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if process_uid == 0: users = psutil.users() for user in users: - uid = pwd.getpwuid(user.name) + xdg_runtime_dir = get_xdg_runtime_dir(pwd.getpwuid(user.name)) user_args = [ "DISPLAY=:0", - "DBUS_SESSION_BUS_ADDRESS=unix:path={get_xdg_runtime_dir(uid)}/bus", + f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", "sudo", "-u", f"{user.name}", @@ -86,6 +86,7 @@ def hardware_inhibitor_checks_failed( exception_log = "\n - ".join(failures) raise Exception(f"update failed to pass checks: \n - {exception_log}") + def run_update_scripts(root_dir: str): for root, dirs, files in os.walk(root_dir): for file in files: @@ -130,7 +131,11 @@ def run_updates(args): run_update_scripts(root_dir + "/system/") for user_uid in user_uids: log.info( - f"Running update for user: '{pwd.getpwuid(process_uid).pw_name}', update script directory: '{root_dir}/user'" + f""" + Running update for user: + '{pwd.getpwuid(process_uid).pw_name}', + update script directory: '{root_dir}/user' + """ ) subprocess.run( [ @@ -145,7 +150,9 @@ def run_updates(args): ) else: if args.system: - raise Exception("ublue-update needs to be run as root to perform system updates!") + raise Exception( + "ublue-update needs to be run as root to perform system updates!" + ) run_update_scripts(root_dir + "/user/") notify( From 8d8ef28d8c522ed20586815960a9a500a53aba2e Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:51:52 -0700 Subject: [PATCH 26/71] fix: properly initialize env vars for notifications --- src/ublue_update/cli.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 589789b..2f214c1 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -12,7 +12,7 @@ def get_xdg_runtime_dir(uid): - return f"/run/{os.getpwuid(uid).pw_name}/{uid}" + return f"/run/{pwd.getpwuid(uid).pw_name}/{uid}" def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): @@ -30,16 +30,17 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if process_uid == 0: users = psutil.users() for user in users: - xdg_runtime_dir = get_xdg_runtime_dir(pwd.getpwuid(user.name)) + xdg_runtime_dir = get_xdg_runtime_dir(pwd.getpwnam(user.name).pw_uid) user_args = [ - "DISPLAY=:0", - f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", "sudo", "-u", f"{user.name}", + "DISPLAY=:0", + f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", ] + print(user_args) user_args += args - out = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + out = subprocess.run(user_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return if actions != []: for action in actions: From cb41a15006fa196ad77ccffc8fa8bafe43a80af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20D=C3=ADaz?= <4808907+akdev1l@users.noreply.github.com> Date: Mon, 11 Sep 2023 21:20:04 -0400 Subject: [PATCH 27/71] chore: add polkit rule to allow passwordless access (#65) --- files/usr/etc/polkit-1/rules.d/ublue-update.rules | 8 ++++++++ ublue-update.spec.rpkg | 1 + 2 files changed, 9 insertions(+) create mode 100644 files/usr/etc/polkit-1/rules.d/ublue-update.rules diff --git a/files/usr/etc/polkit-1/rules.d/ublue-update.rules b/files/usr/etc/polkit-1/rules.d/ublue-update.rules new file mode 100644 index 0000000..52fbfc6 --- /dev/null +++ b/files/usr/etc/polkit-1/rules.d/ublue-update.rules @@ -0,0 +1,8 @@ +polkit.addRule(function(action, subject) { + if (subject.isInGroup("wheel") && + action.id == "org.freedesktop.systemd1.manage-units" && + action.lookup("unit") == "ublue-update.service") + { + return polkit.Result.YES; + } +}) diff --git a/ublue-update.spec.rpkg b/ublue-update.spec.rpkg index 6381868..be934ec 100644 --- a/ublue-update.spec.rpkg +++ b/ublue-update.spec.rpkg @@ -68,6 +68,7 @@ cp -rp files/etc files/usr %{buildroot} %attr(0644,root,root) %{_exec_prefix}/etc/%{NAME}/%{NAME}.toml %attr(0755,root,root) %{_sysconfdir}/%{NAME}.d/user/*.sh %attr(0755,root,root) %{_sysconfdir}/%{NAME}.d/system/*.sh +%attr(0644,root,root) %{_exec_prefix}/etc/polkit-1/rules.d/%{NAME}.rules %changelog %autochangelog From 5d77d74ea05cc79e01452bc4d8dd514cd5e24198 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:54:01 -0700 Subject: [PATCH 28/71] fix: change environment variables when running ublue-update as user --- src/ublue_update/cli.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 2f214c1..f61f495 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -112,10 +112,6 @@ def run_update_scripts(root_dir: str): def run_updates(args): - notify( - "System Updater", - "System passed checks, updating ...", - ) root_dir = "/etc/ublue-update.d" """Wait on any existing transactions to complete before updating""" @@ -123,6 +119,10 @@ def run_updates(args): process_uid = os.getuid() if process_uid == 0: + notify( + "System Updater", + "System passed checks, updating ...", + ) user_uids = [] if not args.system: for user in pwd.getpwall(): @@ -140,27 +140,27 @@ def run_updates(args): ) subprocess.run( [ - f"XDG_RUNTIME_DIR={get_xdg_runtime_dir()}", - f"DBUS_SESSION_BUS=unix://{get_xdg_runtime_dir(process_uid)}/bus", "sudo", "-u", - f"{os.getpwuid(process_uid).pw_name}", + "DISPLAY=:0", + f"XDG_RUNTIME_DIR={get_xdg_runtime_dir()}", + f"DBUS_SESSION_BUS_ADDRESS=unix:path={get_xdg_runtime_dir(process_uid)}/bus", + f"{pwd.getpwuid(process_uid).pw_name}", "/usr/bin/ublue-update", "-f", ] ) + notify( + "System Updater", + "System update complete, reboot for changes to take effect", + ) + log.info("System update complete") else: if args.system: raise Exception( "ublue-update needs to be run as root to perform system updates!" ) run_update_scripts(root_dir + "/user/") - - notify( - "System Updater", - "System update complete, reboot for changes to take effect", - ) - log.info("System update complete") os._exit(0) From 0e22e49a2e394f5708cc131d84494a55be65a866 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 11 Sep 2023 20:01:24 -0700 Subject: [PATCH 29/71] fix: reformat to please flake8 --- src/ublue_update/cli.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index f61f495..5afb500 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -40,7 +40,9 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): ] print(user_args) user_args += args - out = subprocess.run(user_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + out = subprocess.run( + user_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) return if actions != []: for action in actions: @@ -131,6 +133,7 @@ def run_updates(args): run_update_scripts(root_dir + "/system/") for user_uid in user_uids: + xdg_runtime_dir = get_xdg_runtime_dir(process_uid) log.info( f""" Running update for user: @@ -143,8 +146,8 @@ def run_updates(args): "sudo", "-u", "DISPLAY=:0", - f"XDG_RUNTIME_DIR={get_xdg_runtime_dir()}", - f"DBUS_SESSION_BUS_ADDRESS=unix:path={get_xdg_runtime_dir(process_uid)}/bus", + f"XDG_RUNTIME_DIR={xdg_runtime_dir}", + f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", f"{pwd.getpwuid(process_uid).pw_name}", "/usr/bin/ublue-update", "-f", From 2f38bf815a05dbf653d89776eb5471279e61472d Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 11 Sep 2023 23:01:13 -0700 Subject: [PATCH 30/71] fix: update cli args for `sudo` to be in the correct order --- src/ublue_update/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 5afb500..099e64f 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -145,10 +145,10 @@ def run_updates(args): [ "sudo", "-u", + f"{pwd.getpwuid(process_uid).pw_name}", "DISPLAY=:0", f"XDG_RUNTIME_DIR={xdg_runtime_dir}", f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", - f"{pwd.getpwuid(process_uid).pw_name}", "/usr/bin/ublue-update", "-f", ] From ef5d4e73a95051ae2b059920d9a2352cebb3daac Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 11 Sep 2023 23:31:14 -0700 Subject: [PATCH 31/71] fix: use user_uid instead of process_uid, fixes user updates --- src/ublue_update/cli.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 099e64f..bf0f62c 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -133,19 +133,21 @@ def run_updates(args): run_update_scripts(root_dir + "/system/") for user_uid in user_uids: - xdg_runtime_dir = get_xdg_runtime_dir(process_uid) + xdg_runtime_dir = get_xdg_runtime_dir(user_uid) + user = pwd.getpwuid(user_uid) log.info( f""" Running update for user: - '{pwd.getpwuid(process_uid).pw_name}', + 'user.pw_name}', update script directory: '{root_dir}/user' """ ) + subprocess.run( [ "sudo", "-u", - f"{pwd.getpwuid(process_uid).pw_name}", + f"{user.pw_name}", "DISPLAY=:0", f"XDG_RUNTIME_DIR={xdg_runtime_dir}", f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", From 7fae539ae8e880651417f816bb664ef7d8424662 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Mon, 11 Sep 2023 23:34:07 -0700 Subject: [PATCH 32/71] fix: added { to please formatter --- src/ublue_update/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index bf0f62c..8387f71 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -138,7 +138,7 @@ def run_updates(args): log.info( f""" Running update for user: - 'user.pw_name}', + '{user.pw_name}', update script directory: '{root_dir}/user' """ ) From e799b0afabd5eeab4daae4d30edc0f9ec964907a Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 00:01:09 -0700 Subject: [PATCH 33/71] fix: notify outside of for loop --- src/ublue_update/cli.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 8387f71..4465269 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -155,11 +155,11 @@ def run_updates(args): "-f", ] ) - notify( - "System Updater", - "System update complete, reboot for changes to take effect", - ) - log.info("System update complete") + notify( + "System Updater", + "System update complete, reboot for changes to take effect", + ) + log.info("System update complete") else: if args.system: raise Exception( From b40ed7a5101576d180f9e88334f23ead0e9222e2 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 00:05:14 -0700 Subject: [PATCH 34/71] fix: fixed user prompts when running as root --- src/ublue_update/cli.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 4465269..46fcc88 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -27,6 +27,9 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): "--icon=software-update-available-symbolic", f"--urgency={urgency}", ] + if actions != []: + for action in actions: + args.append(f"--action={action}") if process_uid == 0: users = psutil.users() for user in users: @@ -43,10 +46,9 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): out = subprocess.run( user_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT ) + if actions != []: + return out return - if actions != []: - for action in actions: - args.append(f"--action={action}") out = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) return out From 2907793477832eda0bd21d80aad8dc151280546d Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:06:31 -0700 Subject: [PATCH 35/71] fix: get active sessions with logind, use capture_output=True for subprocess.run() --- src/ublue_update/cli.py | 42 ++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 46fcc88..b6b6612 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -14,6 +14,31 @@ def get_xdg_runtime_dir(uid): return f"/run/{pwd.getpwuid(uid).pw_name}/{uid}" +def get_active_sessions(): + out = subprocess.run( + ["loginctl", "list-sessions", "--output=json"], + capture_output=True, + ) + sessions = json.loads(out.stdout.decode('utf-8')) + session_properties = [] + active_sessions = [] + for session in sessions: + args = [ + "loginctl", + "show-session", + f"{session.get('session')}", + ] + out = subprocess.run(args, capture_output=True) + loginctl_output = { + line.split("=")[0]: line.split("=")[1] + for line in list(filter(None, out.stdout.decode('utf-8').split("\n"))) + } + session_properties.append(loginctl_output) + for session_info in session_properties: + graphical = session_info.get("Type") == "x11" or session_info.get("Type") == "wayland" + if graphical and session_info.get("Active") == "yes": + active_sessions.append(session_info) + return active_sessions def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if not dbus_notify: @@ -31,25 +56,24 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): for action in actions: args.append(f"--action={action}") if process_uid == 0: - users = psutil.users() + users = get_active_sessions() for user in users: - xdg_runtime_dir = get_xdg_runtime_dir(pwd.getpwnam(user.name).pw_uid) + xdg_runtime_dir = get_xdg_runtime_dir(user.get("User")) user_args = [ "sudo", "-u", - f"{user.name}", + f"{user.get('Name')}", "DISPLAY=:0", f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", ] - print(user_args) user_args += args out = subprocess.run( - user_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + user_args, capture_output=True ) if actions != []: return out return - out = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + out = subprocess.run(args, capture_output=True) return out @@ -101,8 +125,7 @@ def run_update_scripts(root_dir: str): log.info(f"Running update script: {full_path}") out = subprocess.run( [full_path], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + capture_output=True, ) if out.returncode != 0: log.info(f"{full_path} returned error code: {out.returncode}") @@ -155,7 +178,8 @@ def run_updates(args): f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", "/usr/bin/ublue-update", "-f", - ] + ], + capture_output=True, ) notify( "System Updater", From 96586ee0e9e457a33484e0c817795f4babae62a2 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:28:19 -0700 Subject: [PATCH 36/71] fix: get xdg_runtime_dir through loginctl --- src/ublue_update/cli.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index b6b6612..e9b1fe7 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -3,7 +3,7 @@ import logging import argparse import pwd -import psutil +import json from ublue_update.update_checks.system import system_update_check from ublue_update.update_checks.wait import transaction_wait @@ -12,34 +12,46 @@ def get_xdg_runtime_dir(uid): - return f"/run/{pwd.getpwuid(uid).pw_name}/{uid}" + out = subprocess.run( + ["loginctl", "show-user", f"{uid}"], + capture_output=True, + ) + loginctl_output = { + line.split("=")[0]: line.split("=")[1] + for line in list(filter(None, out.stdout.decode("utf-8").split("\n"))) + } + return loginctl_output.get("RuntimePath") + def get_active_sessions(): out = subprocess.run( ["loginctl", "list-sessions", "--output=json"], capture_output=True, ) - sessions = json.loads(out.stdout.decode('utf-8')) + sessions = json.loads(out.stdout.decode("utf-8")) session_properties = [] active_sessions = [] for session in sessions: args = [ "loginctl", "show-session", - f"{session.get('session')}", + session.get("session"), ] out = subprocess.run(args, capture_output=True) loginctl_output = { line.split("=")[0]: line.split("=")[1] - for line in list(filter(None, out.stdout.decode('utf-8').split("\n"))) + for line in list(filter(None, out.stdout.decode("utf-8").split("\n"))) } session_properties.append(loginctl_output) for session_info in session_properties: - graphical = session_info.get("Type") == "x11" or session_info.get("Type") == "wayland" + graphical = ( + session_info.get("Type") == "x11" or session_info.get("Type") == "wayland" + ) if graphical and session_info.get("Active") == "yes": active_sessions.append(session_info) return active_sessions + def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if not dbus_notify: return @@ -62,14 +74,12 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): user_args = [ "sudo", "-u", - f"{user.get('Name')}", + user.get("Name"), "DISPLAY=:0", f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", ] user_args += args - out = subprocess.run( - user_args, capture_output=True - ) + out = subprocess.run(user_args, capture_output=True) if actions != []: return out return @@ -206,7 +216,6 @@ def run_updates(args): cli_args = None - def main(): # setup argparse From 609e05ca604e44ecab004c5e159c7a97e58ba3fd Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:43:41 -0700 Subject: [PATCH 37/71] docs(README): document user and system updates --- README.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fbbda95..3307a88 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,13 @@ RUN rpm-ostree override remove ublue-os-update-services && rpm-ostree install /t ``` > **Note** -> If you are on an image derived from uBlue main, you will need to remove or disable automatic updates with rpm-ostreed, to do this, you need to remove or change this line in the config file: `AutomaticUpdatePolicy=stage` (set to `none` if you don't want to remove it) +> If you are on an image derived from uBlue main, you will need to remove or disable automatic updates with rpm-ostreed, to do this, you need to remove or change this line in the config file: `AutomaticUpdatePolicy=stage` (set to `none` if you don't want to remove the line) ## Command Line ``` -usage: ublue-update [-h] [-f] [-c] [-u] [-w] [--user] [--system] +usage: ublue-update [-h] [-f] [-c] [-u] [-w] [--system] options: -h, --help show this help message and exit @@ -41,16 +41,33 @@ options: -c, --check run update checks and exit -u, --updatecheck check for updates and exit -w, --wait wait for transactions to complete and exit - --user run user updates - --system run system updates + --system only run system updates (requires root) ``` # Configuration +## Update Scripts +update scripts are seperated into two places + +### `/etc/ublue-update.d/user` + +This is for userspace updates, updates are ran as user. Examples include: + - user flatpak updates + - distrobox/rootless podman updates + - fleek/nix updates + +### `/etc/ublue-update.d/system` + +This is for system-level updates, update scripts are ran as root. Examples include: + - OS image updates + - flatpak updates + - rootful podman/distrobox updates + ## Location -valid config paths (in order of priority) + +### Valid config paths (in order of priority): ```/etc/ublue-update/ublue-update.toml``` @@ -58,7 +75,7 @@ valid config paths (in order of priority) ## Config Variables -section: `checks` +Section: `checks` `min_battery_percent`: checks if battery is above specified percent @@ -67,7 +84,7 @@ section: `checks` `max_mem_percent`: checks if memory usage is below specified the percent -section: `notify` +Section: `notify` `dbus_notify`: enable graphical notifications via dbus From 6f4a07302f4b8879e4e960ebf5f32f2f91aeecc9 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 20:47:54 -0700 Subject: [PATCH 38/71] docs(README): document running from systemd --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3307a88..dea6f74 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,21 @@ RUN rpm-ostree override remove ublue-os-update-services && rpm-ostree install /t ## Command Line +To run a complete system update, it's recommended to use systemd: + +``` systemctl start ublue-update.service``` + +This makes sure that the service isn't triggered again by timers, uses systemd-inhibit, and does passwordless system updates + + +### Run updates from command line (not recommended) + +only run user updates: +```ublue-update``` + +only run system updates: +```pkexec ublue-update --system``` + ``` usage: ublue-update [-h] [-f] [-c] [-u] [-w] [--system] @@ -60,7 +75,7 @@ This is for userspace updates, updates are ran as user. Examples include: ### `/etc/ublue-update.d/system` This is for system-level updates, update scripts are ran as root. Examples include: - - OS image updates + - OS updates - flatpak updates - rootful podman/distrobox updates From 6c167d0a787c8abf581dead09bae04123280c318 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 21:16:41 -0700 Subject: [PATCH 39/71] fix: add proper error handling --- src/ublue_update/cli.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index e9b1fe7..742f3ec 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -18,7 +18,8 @@ def get_xdg_runtime_dir(uid): ) loginctl_output = { line.split("=")[0]: line.split("=")[1] - for line in list(filter(None, out.stdout.decode("utf-8").split("\n"))) + for line in out.stdout.decode("utf-8").split("\n") + if not line.isspace() } return loginctl_output.get("RuntimePath") @@ -28,6 +29,8 @@ def get_active_sessions(): ["loginctl", "list-sessions", "--output=json"], capture_output=True, ) + if out.returncode != 0: + return [] sessions = json.loads(out.stdout.decode("utf-8")) session_properties = [] active_sessions = [] @@ -35,19 +38,20 @@ def get_active_sessions(): args = [ "loginctl", "show-session", - session.get("session"), + sessiob["session"], ] out = subprocess.run(args, capture_output=True) loginctl_output = { line.split("=")[0]: line.split("=")[1] - for line in list(filter(None, out.stdout.decode("utf-8").split("\n"))) + for line in None, out.stdout.decode("utf-8").split("\n") + if not line.isspace() } session_properties.append(loginctl_output) for session_info in session_properties: graphical = ( - session_info.get("Type") == "x11" or session_info.get("Type") == "wayland" + session_info["Type"] == "x11" or session_info["Type"] == "wayland" ) - if graphical and session_info.get("Active") == "yes": + if graphical and session_info["Active"] == "yes": active_sessions.append(session_info) return active_sessions @@ -70,11 +74,13 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if process_uid == 0: users = get_active_sessions() for user in users: - xdg_runtime_dir = get_xdg_runtime_dir(user.get("User")) + xdg_runtime_dir = get_xdg_runtime_dir(user["User"]) + if xdg_runtime_dir == None: + return user_args = [ "sudo", "-u", - user.get("Name"), + user["Name"], "DISPLAY=:0", f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", ] From 96ebf9709d5df26bc84d0c13fc4db185a8d9a8dc Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 21:21:22 -0700 Subject: [PATCH 40/71] chore: log errors to console, switch to splitlines() --- src/ublue_update/cli.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 742f3ec..23de4e8 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -16,12 +16,14 @@ def get_xdg_runtime_dir(uid): ["loginctl", "show-user", f"{uid}"], capture_output=True, ) + if out.returncode != 0: + log.error(f"failed to get xdg runtime dir for user: {uid}") + return None loginctl_output = { line.split("=")[0]: line.split("=")[1] - for line in out.stdout.decode("utf-8").split("\n") - if not line.isspace() + for line in out.stdout.decode("utf-8").splitlines() } - return loginctl_output.get("RuntimePath") + return loginctl_output["RuntimePath"] def get_active_sessions(): @@ -30,6 +32,7 @@ def get_active_sessions(): capture_output=True, ) if out.returncode != 0: + log.error("failed to get active logind sessions") return [] sessions = json.loads(out.stdout.decode("utf-8")) session_properties = [] @@ -43,8 +46,7 @@ def get_active_sessions(): out = subprocess.run(args, capture_output=True) loginctl_output = { line.split("=")[0]: line.split("=")[1] - for line in None, out.stdout.decode("utf-8").split("\n") - if not line.isspace() + for line in None, out.stdout.decode("utf-8").splitlines() } session_properties.append(loginctl_output) for session_info in session_properties: From 75f2dd0b1486cecf5bdbad84757019740e786648 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 21:29:49 -0700 Subject: [PATCH 41/71] chore: improve error handling with `try:` statements --- src/ublue_update/cli.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 23de4e8..719eed3 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -16,9 +16,6 @@ def get_xdg_runtime_dir(uid): ["loginctl", "show-user", f"{uid}"], capture_output=True, ) - if out.returncode != 0: - log.error(f"failed to get xdg runtime dir for user: {uid}") - return None loginctl_output = { line.split("=")[0]: line.split("=")[1] for line in out.stdout.decode("utf-8").splitlines() @@ -31,9 +28,6 @@ def get_active_sessions(): ["loginctl", "list-sessions", "--output=json"], capture_output=True, ) - if out.returncode != 0: - log.error("failed to get active logind sessions") - return [] sessions = json.loads(out.stdout.decode("utf-8")) session_properties = [] active_sessions = [] @@ -41,12 +35,12 @@ def get_active_sessions(): args = [ "loginctl", "show-session", - sessiob["session"], + session["session"], ] out = subprocess.run(args, capture_output=True) loginctl_output = { line.split("=")[0]: line.split("=")[1] - for line in None, out.stdout.decode("utf-8").splitlines() + for line in out.stdout.decode("utf-8").splitlines() } session_properties.append(loginctl_output) for session_info in session_properties: @@ -74,10 +68,15 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): for action in actions: args.append(f"--action={action}") if process_uid == 0: - users = get_active_sessions() + try: + users = get_active_sessions() + except KeyError as e: + log.error("failed to get active logind session info", e); for user in users: - xdg_runtime_dir = get_xdg_runtime_dir(user["User"]) - if xdg_runtime_dir == None: + try: + xdg_runtime_dir = get_xdg_runtime_dir(user["User"]) + except KeyError as e: + log.error(f"failed to get xdg_runtime_dir for user: {user['Name']}", e); return user_args = [ "sudo", @@ -104,6 +103,8 @@ def ask_for_updates(): ["universal-blue-update-confirm=Confirm"], "critical", ) + if out == None: + return # if the user has confirmed if "universal-blue-update-confirm" in out.stdout.decode("utf-8"): run_updates(cli_args) From 439c80b5b978bd90c2636fee324b3720df0673c9 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 21:32:00 -0700 Subject: [PATCH 42/71] style: format to please flake8 checks --- src/ublue_update/cli.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 719eed3..f810226 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -44,9 +44,7 @@ def get_active_sessions(): } session_properties.append(loginctl_output) for session_info in session_properties: - graphical = ( - session_info["Type"] == "x11" or session_info["Type"] == "wayland" - ) + graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" if graphical and session_info["Active"] == "yes": active_sessions.append(session_info) return active_sessions @@ -71,12 +69,12 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): try: users = get_active_sessions() except KeyError as e: - log.error("failed to get active logind session info", e); + log.error("failed to get active logind session info", e) for user in users: try: xdg_runtime_dir = get_xdg_runtime_dir(user["User"]) except KeyError as e: - log.error(f"failed to get xdg_runtime_dir for user: {user['Name']}", e); + log.error(f"failed to get xdg_runtime_dir for user: {user['Name']}", e) return user_args = [ "sudo", @@ -103,7 +101,7 @@ def ask_for_updates(): ["universal-blue-update-confirm=Confirm"], "critical", ) - if out == None: + if out is None: return # if the user has confirmed if "universal-blue-update-confirm" in out.stdout.decode("utf-8"): @@ -225,6 +223,7 @@ def run_updates(args): cli_args = None + def main(): # setup argparse From b4c88db0f3aa3ac8729b8b38423d0c4ef6088586 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 23:16:53 -0700 Subject: [PATCH 43/71] docs(README): clear up documentation around system vs user updates --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index dea6f74..39ba089 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ To run a complete system update, it's recommended to use systemd: ``` systemctl start ublue-update.service``` -This makes sure that the service isn't triggered again by timers, uses systemd-inhibit, and does passwordless system updates +This makes sure that the service will not be triggered again by timers while also making use systemd-inhibit, and allows for passwordless system updates ### Run updates from command line (not recommended) @@ -63,21 +63,20 @@ options: # Configuration ## Update Scripts -update scripts are seperated into two places +Update scripts are separated into two directories inside of `/etc/ublue-update.d` ### `/etc/ublue-update.d/user` -This is for userspace updates, updates are ran as user. Examples include: - - user flatpak updates +Update scripts are ran as user. Scripts included: + - per-user flatpak updates - distrobox/rootless podman updates - fleek/nix updates ### `/etc/ublue-update.d/system` -This is for system-level updates, update scripts are ran as root. Examples include: +Update scripts are ran as root, these updates are meant to be system-wide. Scripts included: - OS updates - - flatpak updates - - rootful podman/distrobox updates + - system-wide flatpak updates ## Location From 442275be06135e0622bca29c41eaa5a5ffc61260 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 23:28:01 -0700 Subject: [PATCH 44/71] docs:style: fix code block formatting, clear up wording --- README.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 39ba089..45de990 100644 --- a/README.md +++ b/README.md @@ -34,20 +34,26 @@ RUN rpm-ostree override remove ublue-os-update-services && rpm-ostree install /t To run a complete system update, it's recommended to use systemd: -``` systemctl start ublue-update.service``` +```sh +$ systemctl start ublue-update.service +``` This makes sure that the service will not be triggered again by timers while also making use systemd-inhibit, and allows for passwordless system updates ### Run updates from command line (not recommended) -only run user updates: -```ublue-update``` - -only run system updates: -```pkexec ublue-update --system``` +only run user updates (rootless): +```sh +$ ublue-update +``` +only run system updates (requires root): +```sh +$ pkexec ublue-update --system ``` + +```sh usage: ublue-update [-h] [-f] [-c] [-u] [-w] [--system] options: @@ -68,15 +74,15 @@ Update scripts are separated into two directories inside of `/etc/ublue-update.d ### `/etc/ublue-update.d/user` Update scripts are ran as user. Scripts included: - - per-user flatpak updates - - distrobox/rootless podman updates - - fleek/nix updates + - per-user flatpak update scripts (uninstalling unused deps and repairing flatpak install for maintenence) + - distrobox update script + - fleek update script ### `/etc/ublue-update.d/system` Update scripts are ran as root, these updates are meant to be system-wide. Scripts included: - - OS updates - - system-wide flatpak updates + - OS update script (depends on [`rpm-ostree`](https://github.com/coreos/rpm-ostree)) + - system-wide flatpak update scripts (uninstalling unused deps and repairing flatpak install for maintenence) ## Location From ddffeeffc69b1e180143e499db3035b92d220b45 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Tue, 12 Sep 2023 23:29:08 -0700 Subject: [PATCH 45/71] docs(README): clear up wording --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 45de990..71a16e0 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ To run a complete system update, it's recommended to use systemd: $ systemctl start ublue-update.service ``` -This makes sure that the service will not be triggered again by timers while also making use systemd-inhibit, and allows for passwordless system updates +This makes sure there's only one instance of `ublue-update`, uses systemd-inhibit, and allows for passwordless system updates (user must be in `wheel` group) ### Run updates from command line (not recommended) From bcf1167b10bacd26d136d235a6e7efa091426771 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Wed, 13 Sep 2023 08:10:30 -0700 Subject: [PATCH 46/71] docs(README): remove section about systemd-inhibit --- README.md | 2 +- src/ublue_update/cli.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 71a16e0..75c85be 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ To run a complete system update, it's recommended to use systemd: $ systemctl start ublue-update.service ``` -This makes sure there's only one instance of `ublue-update`, uses systemd-inhibit, and allows for passwordless system updates (user must be in `wheel` group) +This makes sure there's only one instance of `ublue-update` and allows for passwordless system updates (user must be in `wheel` group) ### Run updates from command line (not recommended) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index f810226..2029996 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -17,7 +17,7 @@ def get_xdg_runtime_dir(uid): capture_output=True, ) loginctl_output = { - line.split("=")[0]: line.split("=")[1] + line.split("=")[0]: line.split("=")[-1] for line in out.stdout.decode("utf-8").splitlines() } return loginctl_output["RuntimePath"] @@ -39,7 +39,7 @@ def get_active_sessions(): ] out = subprocess.run(args, capture_output=True) loginctl_output = { - line.split("=")[0]: line.split("=")[1] + line.split("=")[0]: line.split("=")[-1] for line in out.stdout.decode("utf-8").splitlines() } session_properties.append(loginctl_output) From d9ca0fcc927d960623d4833d8c406d707558c1d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20D=C3=ADaz?= <4808907+akdev1l@users.noreply.github.com> Date: Fri, 8 Sep 2023 10:57:56 -0400 Subject: [PATCH 47/71] chore: add copr build so we are packaged in COPR This will make use of wip akdev1l/copr-build (I will fork this into ublue-os/copr-build when it is ready) to generate copr repo pointing to this github repo. The copr repo build will be triggered by this action as well. I have removed the scheduled 5 AM build because the application build does not really change that often (the dependencies get pulled in when RPM is installed so it will not install outdated dependencies) --- .github/workflows/build.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02eddcd..4f1bcf9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,8 +5,6 @@ on: push: branches: - main - schedule: - - cron: '0 5 * * *' # 5 am everyday workflow_dispatch: env: IMAGE_NAME: ublue-update @@ -105,3 +103,19 @@ jobs: if: github.event_name != 'pull_request' run: | echo "${{ toJSON(steps.push.outputs) }}" + + copr-build: + permissions: + contents: read + packages: read + runs-on: ubuntu-latest + container: + image: ghcr.io/akdev1l/copr-build:latest + steps: + - name: trigger copr build + uses: akdev1l/copr-build@main + id: copr-build + env: + COPR_API_TOKEN_CONFIG: ${{ secrets.UBLUE_COPR_API_TOKEN }} + with: + owner: ublue-os From 7ef783c27e1619434f2d81677f16bd15c4743ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20D=C3=ADaz?= <4808907+akdev1l@users.noreply.github.com> Date: Fri, 8 Sep 2023 11:09:41 -0400 Subject: [PATCH 48/71] fix: skip copr-build on PRs --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f1bcf9..f7467df 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -111,6 +111,7 @@ jobs: runs-on: ubuntu-latest container: image: ghcr.io/akdev1l/copr-build:latest + if: github.event_name != 'pull_request' steps: - name: trigger copr build uses: akdev1l/copr-build@main From ff74a784f8b991f9e0d1af7f3c0574c37a48fe2a Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Wed, 13 Sep 2023 08:24:10 -0700 Subject: [PATCH 49/71] fix: make systemd service more robust --- files/usr/lib/systemd/system/ublue-update.service | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/files/usr/lib/systemd/system/ublue-update.service b/files/usr/lib/systemd/system/ublue-update.service index 42d1297..e93558a 100644 --- a/files/usr/lib/systemd/system/ublue-update.service +++ b/files/usr/lib/systemd/system/ublue-update.service @@ -4,5 +4,7 @@ Description=Universal Blue Update Oneshot Service [Service] Restart=on-failure RestartSec=1h -Type=oneshot +Type=simple ExecStart=/usr/bin/ublue-update +KillMode=process +TimeoutSec=240 From c2f16ae3fd6a6569fcc5abac4f3721536e2c6bb9 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Wed, 13 Sep 2023 09:59:34 -0700 Subject: [PATCH 50/71] fix: revert systemd service changes --- files/usr/lib/systemd/system/ublue-update.service | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/files/usr/lib/systemd/system/ublue-update.service b/files/usr/lib/systemd/system/ublue-update.service index e93558a..42d1297 100644 --- a/files/usr/lib/systemd/system/ublue-update.service +++ b/files/usr/lib/systemd/system/ublue-update.service @@ -4,7 +4,5 @@ Description=Universal Blue Update Oneshot Service [Service] Restart=on-failure RestartSec=1h -Type=simple +Type=oneshot ExecStart=/usr/bin/ublue-update -KillMode=process -TimeoutSec=240 From 9592dec3f5e40915ec56560baa18b1c30ed837b2 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:15:44 -0700 Subject: [PATCH 51/71] fix: make subprocess.run() statements not rely on $PATH --- src/ublue_update/cli.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 2029996..928fb08 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -13,7 +13,7 @@ def get_xdg_runtime_dir(uid): out = subprocess.run( - ["loginctl", "show-user", f"{uid}"], + ["/usr/bin/loginctl", "show-user", f"{uid}"], capture_output=True, ) loginctl_output = { @@ -25,7 +25,7 @@ def get_xdg_runtime_dir(uid): def get_active_sessions(): out = subprocess.run( - ["loginctl", "list-sessions", "--output=json"], + ["/usr/bin/loginctl", "list-sessions", "--output=json"], capture_output=True, ) sessions = json.loads(out.stdout.decode("utf-8")) @@ -33,7 +33,7 @@ def get_active_sessions(): active_sessions = [] for session in sessions: args = [ - "loginctl", + "/usr/bin/loginctl", "show-session", session["session"], ] @@ -77,7 +77,7 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): log.error(f"failed to get xdg_runtime_dir for user: {user['Name']}", e) return user_args = [ - "sudo", + "/usr/bin/sudo", "-u", user["Name"], "DISPLAY=:0", @@ -187,7 +187,7 @@ def run_updates(args): subprocess.run( [ - "sudo", + "/usr/bin/sudo", "-u", f"{user.pw_name}", "DISPLAY=:0", From 5b9702a5966322715fe2eb4c6197e16884f16e0d Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Wed, 13 Sep 2023 21:44:19 -0700 Subject: [PATCH 52/71] feat: add pidlock, only run updates for active sessions --- src/ublue_update/cli.py | 65 ++++++++++--------------------------- src/ublue_update/session.py | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 48 deletions(-) create mode 100644 src/ublue_update/session.py diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 928fb08..adfde9b 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -9,46 +9,7 @@ from ublue_update.update_checks.wait import transaction_wait from ublue_update.update_inhibitors.hardware import check_hardware_inhibitors from ublue_update.config import load_value - - -def get_xdg_runtime_dir(uid): - out = subprocess.run( - ["/usr/bin/loginctl", "show-user", f"{uid}"], - capture_output=True, - ) - loginctl_output = { - line.split("=")[0]: line.split("=")[-1] - for line in out.stdout.decode("utf-8").splitlines() - } - return loginctl_output["RuntimePath"] - - -def get_active_sessions(): - out = subprocess.run( - ["/usr/bin/loginctl", "list-sessions", "--output=json"], - capture_output=True, - ) - sessions = json.loads(out.stdout.decode("utf-8")) - session_properties = [] - active_sessions = [] - for session in sessions: - args = [ - "/usr/bin/loginctl", - "show-session", - session["session"], - ] - out = subprocess.run(args, capture_output=True) - loginctl_output = { - line.split("=")[0]: line.split("=")[-1] - for line in out.stdout.decode("utf-8").splitlines() - } - session_properties.append(loginctl_output) - for session_info in session_properties: - graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" - if graphical and session_info["Active"] == "yes": - active_sessions.append(session_info) - return active_sessions - +from ublue_update.session import get_xdg_runtime_dir get_active_sessions check_pidlock def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if not dbus_notify: @@ -66,6 +27,7 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): for action in actions: args.append(f"--action={action}") if process_uid == 0: + users = [] try: users = get_active_sessions() except KeyError as e: @@ -156,6 +118,7 @@ def run_update_scripts(root_dir: str): def run_updates(args): + check_pidlock() root_dir = "/etc/ublue-update.d" """Wait on any existing transactions to complete before updating""" @@ -167,16 +130,22 @@ def run_updates(args): "System Updater", "System passed checks, updating ...", ) - user_uids = [] - if not args.system: - for user in pwd.getpwall(): - if "/home" in user.pw_dir: - user_uids.append(user.pw_uid) + users = [] + try: + users = get_active_sessions() + except KeyError as e: + log.error("failed to get active logind session info", e) + + if args.system: + users = [] run_update_scripts(root_dir + "/system/") - for user_uid in user_uids: - xdg_runtime_dir = get_xdg_runtime_dir(user_uid) - user = pwd.getpwuid(user_uid) + for user in users: + try: + xdg_runtime_dir = get_xdg_runtime_dir(user["User"]) + except KeyError as e: + log.error(f"failed to get xdg_runtime_dir for user: {user['Name']}", e) + break log.info( f""" Running update for user: diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py new file mode 100644 index 0000000..12fcad7 --- /dev/null +++ b/src/ublue_update/session.py @@ -0,0 +1,57 @@ +import subprocess +import os + + +def get_xdg_runtime_dir(uid): + out = subprocess.run( + ["/usr/bin/loginctl", "show-user", f"{uid}"], + capture_output=True, + ) + loginctl_output = { + line.split("=")[0]: line.split("=")[-1] + for line in out.stdout.decode("utf-8").splitlines() + } + return loginctl_output["RuntimePath"] + + +def get_active_sessions(): + out = subprocess.run( + ["/usr/bin/loginctl", "list-sessions", "--output=json"], + capture_output=True, + ) + sessions = json.loads(out.stdout.decode("utf-8")) + session_properties = [] + active_sessions = [] + for session in sessions: + args = [ + "/usr/bin/loginctl", + "show-session", + session["session"], + ] + out = subprocess.run(args, capture_output=True) + loginctl_output = { + line.split("=")[0]: line.split("=")[-1] + for line in out.stdout.decode("utf-8").splitlines() + } + session_properties.append(loginctl_output) + for session_info in session_properties: + graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" + if graphical and session_info["Active"] == "yes": + active_sessions.append(session_info) + return active_sessions + +def check_pidlock(): + path = "" + if os.getuid() != 0: + path = os.environ["XDG_RUNTIME_DIR"] + "/ublue-update.pid" + else: + path = "/run/ublue-update.pid" + + if os.path.isfile(path): + with open(path, 'r', encoding='utf-8') as file: + if psutil.pid_exists(int(file.read())): + raise Exception("Another instance of ublue-update is running for this user!") + + with open(path, 'w', encoding='utf-8') as file: + file.write(str(os.getpid())) + From 729f65687d49155dc773ccb0ef48c0c7c2c695d9 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Wed, 13 Sep 2023 21:46:29 -0700 Subject: [PATCH 53/71] chore: reformat --- src/ublue_update/cli.py | 7 ++++--- src/ublue_update/session.py | 12 ++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index adfde9b..b406d29 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -2,14 +2,15 @@ import subprocess import logging import argparse -import pwd -import json from ublue_update.update_checks.system import system_update_check from ublue_update.update_checks.wait import transaction_wait from ublue_update.update_inhibitors.hardware import check_hardware_inhibitors from ublue_update.config import load_value -from ublue_update.session import get_xdg_runtime_dir get_active_sessions check_pidlock +from ublue_update.session import get_xdg_runtime_dir +from ublue_update.session import get_active_sessions +from ublue_update.session import check_pidlock + def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if not dbus_notify: diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index 12fcad7..6f5e383 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -1,5 +1,7 @@ import subprocess import os +import psutil +import json def get_xdg_runtime_dir(uid): @@ -40,6 +42,7 @@ def get_active_sessions(): active_sessions.append(session_info) return active_sessions + def check_pidlock(): path = "" if os.getuid() != 0: @@ -48,10 +51,11 @@ def check_pidlock(): path = "/run/ublue-update.pid" if os.path.isfile(path): - with open(path, 'r', encoding='utf-8') as file: + with open(path, "r", encoding="utf-8") as file: if psutil.pid_exists(int(file.read())): - raise Exception("Another instance of ublue-update is running for this user!") + raise Exception( + "Another instance of ublue-update is running for this user!" + ) - with open(path, 'w', encoding='utf-8') as file: + with open(path, "w", encoding="utf-8") as file: file.write(str(os.getpid())) - From 28a6e9a9e1339967e0d785436a7002d2d8d58b9d Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:44:44 -0700 Subject: [PATCH 54/71] feat: add filelocking to make updates not run at the same time for same user --- pyproject.toml | 1 + src/ublue_update/cli.py | 21 +++++++++++------ src/ublue_update/filelock.py | 44 ++++++++++++++++++++++++++++++++++++ src/ublue_update/session.py | 20 ---------------- 4 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 src/ublue_update/filelock.py diff --git a/pyproject.toml b/pyproject.toml index edd02f7..3f45bcd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ classifiers = [ ] dependencies = [ "psutil", + "filelock", ] dynamic = ["version"] diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index b406d29..153a958 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -7,10 +7,8 @@ from ublue_update.update_checks.wait import transaction_wait from ublue_update.update_inhibitors.hardware import check_hardware_inhibitors from ublue_update.config import load_value -from ublue_update.session import get_xdg_runtime_dir -from ublue_update.session import get_active_sessions -from ublue_update.session import check_pidlock - +from ublue_update.session import get_xdg_runtime_dir, get_active_sessions +from ublue_update.filelock import acquire_lock, release_lock def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if not dbus_notify: @@ -119,13 +117,21 @@ def run_update_scripts(root_dir: str): def run_updates(args): - check_pidlock() + process_uid = os.getuid() + filelock_path = "/run/ublue-update.lock" + if process_uid != 0: + xdg_runtime_dir = os.environ.get("XDG_RUNTIME_DIR") + if os.path.isdir(xdg_runtime_dir): + filelock_path = f"{xdg_runtime_dir}/ublue-update.lock" + print(filelock_path) + fd = acquire_lock(filelock_path) + if fd is None: + raise Exception("updates are already running for this user") root_dir = "/etc/ublue-update.d" """Wait on any existing transactions to complete before updating""" transaction_wait() - process_uid = os.getuid() if process_uid == 0: notify( "System Updater", @@ -150,7 +156,7 @@ def run_updates(args): log.info( f""" Running update for user: - '{user.pw_name}', + '{user['Name']}', update script directory: '{root_dir}/user' """ ) @@ -179,6 +185,7 @@ def run_updates(args): "ublue-update needs to be run as root to perform system updates!" ) run_update_scripts(root_dir + "/user/") + release_lock(fd) os._exit(0) diff --git a/src/ublue_update/filelock.py b/src/ublue_update/filelock.py new file mode 100644 index 0000000..35b881c --- /dev/null +++ b/src/ublue_update/filelock.py @@ -0,0 +1,44 @@ +import os +import fcntl +import time + + +def acquire_lock(lock_file): + open_mode = os.O_RDWR | os.O_CREAT | os.O_TRUNC + fd = os.open(lock_file, open_mode) + + pid = os.getpid() + lock_file_fd = None + + timeout = 5.0 + start_time = current_time = time.time() + while current_time < start_time + timeout: + try: + # The LOCK_EX means that only one process can hold the lock + # The LOCK_NB means that the fcntl.flock() is not blocking + # and we are able to implement termination of while loop, + # when timeout is reached. + # More information here: + # https://docs.python.org/3/library/fcntl.html#fcntl.flock + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except (IOError, OSError): + pass + else: + lock_file_fd = fd + break + print(f' {pid} waiting for lock') + time.sleep(1.0) + current_time = time.time() + if lock_file_fd is None: + os.close(fd) + return lock_file_fd + + +def release_lock(lock_file_fd): + # Do not remove the lockfile: + # + # https://github.com/benediktschmitt/py-filelock/issues/31 + # https://stackoverflow.com/questions/17708885/flock-removing-locked-file-without-race-condition + fcntl.flock(lock_file_fd, fcntl.LOCK_UN) + os.close(lock_file_fd) + return None diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index 6f5e383..620d0c4 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -1,6 +1,4 @@ import subprocess -import os -import psutil import json @@ -41,21 +39,3 @@ def get_active_sessions(): if graphical and session_info["Active"] == "yes": active_sessions.append(session_info) return active_sessions - - -def check_pidlock(): - path = "" - if os.getuid() != 0: - path = os.environ["XDG_RUNTIME_DIR"] + "/ublue-update.pid" - else: - path = "/run/ublue-update.pid" - - if os.path.isfile(path): - with open(path, "r", encoding="utf-8") as file: - if psutil.pid_exists(int(file.read())): - raise Exception( - "Another instance of ublue-update is running for this user!" - ) - - with open(path, "w", encoding="utf-8") as file: - file.write(str(os.getpid())) From 33008035c7c7cc3ac147d2de9a4df41b14951af9 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:47:33 -0700 Subject: [PATCH 55/71] chore: make filelocking use logger --- src/ublue_update/filelock.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ublue_update/filelock.py b/src/ublue_update/filelock.py index 35b881c..01967b6 100644 --- a/src/ublue_update/filelock.py +++ b/src/ublue_update/filelock.py @@ -2,6 +2,8 @@ import fcntl import time +log = logging.getLogger(__name__) + def acquire_lock(lock_file): open_mode = os.O_RDWR | os.O_CREAT | os.O_TRUNC @@ -26,7 +28,7 @@ def acquire_lock(lock_file): else: lock_file_fd = fd break - print(f' {pid} waiting for lock') + log.info(f" {pid} waiting for lock") time.sleep(1.0) current_time = time.time() if lock_file_fd is None: From 89315020aa4bfbadd0056f60cc93198625e6c4d9 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:52:45 -0700 Subject: [PATCH 56/71] chore: remove filelock from pyproject.toml --- pyproject.toml | 1 - src/ublue_update/filelock.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3f45bcd..edd02f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,6 @@ classifiers = [ ] dependencies = [ "psutil", - "filelock", ] dynamic = ["version"] diff --git a/src/ublue_update/filelock.py b/src/ublue_update/filelock.py index 01967b6..9c0edc0 100644 --- a/src/ublue_update/filelock.py +++ b/src/ublue_update/filelock.py @@ -28,7 +28,7 @@ def acquire_lock(lock_file): else: lock_file_fd = fd break - log.info(f" {pid} waiting for lock") + log.info(f"{pid} waiting for lock") time.sleep(1.0) current_time = time.time() if lock_file_fd is None: From 3287b6441d6a388151bbf8113149f92aa8c9747c Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:05:11 -0700 Subject: [PATCH 57/71] fix: format to please flake8 --- src/ublue_update/cli.py | 1 + src/ublue_update/filelock.py | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 153a958..fb701bb 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -10,6 +10,7 @@ from ublue_update.session import get_xdg_runtime_dir, get_active_sessions from ublue_update.filelock import acquire_lock, release_lock + def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if not dbus_notify: return diff --git a/src/ublue_update/filelock.py b/src/ublue_update/filelock.py index 9c0edc0..799cfc4 100644 --- a/src/ublue_update/filelock.py +++ b/src/ublue_update/filelock.py @@ -1,8 +1,9 @@ import os import fcntl import time +from logging import getLogger -log = logging.getLogger(__name__) +log = getLogger(__name__) def acquire_lock(lock_file): @@ -38,8 +39,6 @@ def acquire_lock(lock_file): def release_lock(lock_file_fd): # Do not remove the lockfile: - # - # https://github.com/benediktschmitt/py-filelock/issues/31 # https://stackoverflow.com/questions/17708885/flock-removing-locked-file-without-race-condition fcntl.flock(lock_file_fd, fcntl.LOCK_UN) os.close(lock_file_fd) From b84e2e3de6829c6cd2f0d6dcadc93c896b492c07 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 18:09:52 -0700 Subject: [PATCH 58/71] fix: use user['Name'] instead of user.pw_name --- src/ublue_update/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index fb701bb..cc2fcba 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -166,7 +166,7 @@ def run_updates(args): [ "/usr/bin/sudo", "-u", - f"{user.pw_name}", + f"{user['Name']}", "DISPLAY=:0", f"XDG_RUNTIME_DIR={xdg_runtime_dir}", f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", From 19a97ed47437264042f94173bf8bed0a78ec60e5 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 18:20:49 -0700 Subject: [PATCH 59/71] docs(README): add troubleshooting section --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 75c85be..be5c0cc 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ RUN rpm-ostree override remove ublue-os-update-services && rpm-ostree install /t To run a complete system update, it's recommended to use systemd: -```sh +``` $ systemctl start ublue-update.service ``` @@ -43,17 +43,17 @@ This makes sure there's only one instance of `ublue-update` and allows for passw ### Run updates from command line (not recommended) -only run user updates (rootless): -```sh +Only run user updates (rootless): +``` $ ublue-update ``` -only run system updates (requires root): -```sh +Only run system updates (requires root): +``` $ pkexec ublue-update --system ``` -```sh +``` usage: ublue-update [-h] [-f] [-c] [-u] [-w] [--system] options: @@ -65,6 +65,12 @@ options: --system only run system updates (requires root) ``` +## Troubleshooting + +You can check the ublue-update logs by running this command: +``` +$ journalctl -exu ublue-update.service +``` # Configuration From a5ce2217122e607c2df3aa55c332c11c650f8b0d Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 18:53:29 -0700 Subject: [PATCH 60/71] fix: formatted code, used f-strings for cmd args --- README.md | 2 +- src/ublue_update/cli.py | 7 +++---- src/ublue_update/session.py | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index be5c0cc..32c1617 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ options: You can check the ublue-update logs by running this command: ``` -$ journalctl -exu ublue-update.service +$ journalctl -exu 'ublue-update.service' ``` # Configuration diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index cc2fcba..b6a0fd2 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -41,7 +41,7 @@ def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): user_args = [ "/usr/bin/sudo", "-u", - user["Name"], + f"{user['Name']}", "DISPLAY=:0", f"DBUS_SESSION_BUS_ADDRESS=unix:path={xdg_runtime_dir}/bus", ] @@ -124,7 +124,6 @@ def run_updates(args): xdg_runtime_dir = os.environ.get("XDG_RUNTIME_DIR") if os.path.isdir(xdg_runtime_dir): filelock_path = f"{xdg_runtime_dir}/ublue-update.lock" - print(filelock_path) fd = acquire_lock(filelock_path) if fd is None: raise Exception("updates are already running for this user") @@ -147,7 +146,7 @@ def run_updates(args): if args.system: users = [] - run_update_scripts(root_dir + "/system/") + run_update_scripts(f"{root_dir}/system/") for user in users: try: xdg_runtime_dir = get_xdg_runtime_dir(user["User"]) @@ -185,7 +184,7 @@ def run_updates(args): raise Exception( "ublue-update needs to be run as root to perform system updates!" ) - run_update_scripts(root_dir + "/user/") + run_update_scripts(f"{root_dir}/user/") release_lock(fd) os._exit(0) diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index 620d0c4..68591f5 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -26,7 +26,7 @@ def get_active_sessions(): args = [ "/usr/bin/loginctl", "show-session", - session["session"], + f"{session['session']}", ] out = subprocess.run(args, capture_output=True) loginctl_output = { From 4fb47e924aba7408f4c307de2ddc8262f8353d60 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 19:15:16 -0700 Subject: [PATCH 61/71] chore: add debug print statements --- src/ublue_update/session.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index 68591f5..a3dea2c 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -29,10 +29,13 @@ def get_active_sessions(): f"{session['session']}", ] out = subprocess.run(args, capture_output=True) + print(out.stdout.decode("utf-8")) + print(out.stdout.decode("utf-8").splitlines()) loginctl_output = { line.split("=")[0]: line.split("=")[-1] for line in out.stdout.decode("utf-8").splitlines() } + print(loginctl_output) session_properties.append(loginctl_output) for session_info in session_properties: graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" From 3ee6d7be5eeaa301a0c400e76ce8fe2f62f082bb Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 19:17:58 -0700 Subject: [PATCH 62/71] chore: add more debug print statements --- src/ublue_update/session.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index a3dea2c..4cf832b 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -31,6 +31,7 @@ def get_active_sessions(): out = subprocess.run(args, capture_output=True) print(out.stdout.decode("utf-8")) print(out.stdout.decode("utf-8").splitlines()) + print("loginctl return code: ", out.returncode) loginctl_output = { line.split("=")[0]: line.split("=")[-1] for line in out.stdout.decode("utf-8").splitlines() From c0f0856ffa9fa10a67230e796fe73d4e6c16f4dd Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 23:35:15 -0700 Subject: [PATCH 63/71] chore: remove error handling for debugging --- src/ublue_update/cli.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index b6a0fd2..331df82 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -138,10 +138,11 @@ def run_updates(args): "System passed checks, updating ...", ) users = [] - try: - users = get_active_sessions() - except KeyError as e: - log.error("failed to get active logind session info", e) + users = get_active_sessions() + #try: + # users = get_active_sessions() + #except KeyError as e: + # log.error("failed to get active logind session info", e) if args.system: users = [] From 53d4b139b20a0fc43122892d8d28536c8e13c410 Mon Sep 17 00:00:00 2001 From: RJ Trujillo Date: Thu, 14 Sep 2023 21:47:49 -0600 Subject: [PATCH 64/71] fix(session): Ensure session type exists before appending active session --- src/ublue_update/session.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index 4cf832b..c12d8c9 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -39,7 +39,8 @@ def get_active_sessions(): print(loginctl_output) session_properties.append(loginctl_output) for session_info in session_properties: - graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" - if graphical and session_info["Active"] == "yes": - active_sessions.append(session_info) + if "Type" in session_info.keys(): + graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" + if graphical and session_info["Active"] == "yes": + active_sessions.append(session_info) return active_sessions From 039f10477500017340db21014c83965ff484142a Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 23:36:15 -0700 Subject: [PATCH 65/71] chore: remove check for key --- src/ublue_update/session.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index c12d8c9..e7b7fb4 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -39,8 +39,7 @@ def get_active_sessions(): print(loginctl_output) session_properties.append(loginctl_output) for session_info in session_properties: - if "Type" in session_info.keys(): - graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" - if graphical and session_info["Active"] == "yes": + graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" + if graphical and session_info["Active"] == "yes": active_sessions.append(session_info) return active_sessions From 96bf4d3ca1131c16964e8bda57c4abbba1461b21 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Thu, 14 Sep 2023 23:55:51 -0700 Subject: [PATCH 66/71] chore: add more debug code --- src/ublue_update/session.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index e7b7fb4..eb574fa 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -29,6 +29,8 @@ def get_active_sessions(): f"{session['session']}", ] out = subprocess.run(args, capture_output=True) + if out.returncode != 0: + print(f"Session: {session}") print(out.stdout.decode("utf-8")) print(out.stdout.decode("utf-8").splitlines()) print("loginctl return code: ", out.returncode) @@ -36,6 +38,7 @@ def get_active_sessions(): line.split("=")[0]: line.split("=")[-1] for line in out.stdout.decode("utf-8").splitlines() } + print(loginctl_output) session_properties.append(loginctl_output) for session_info in session_properties: From 086e0270706d841ab0601d6d0f3c5340d75cd5c4 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Fri, 15 Sep 2023 00:05:36 -0700 Subject: [PATCH 67/71] fix: remove debug code, fixed crash because of keyerror with temporary sessions --- src/ublue_update/cli.py | 9 ++++----- src/ublue_update/session.py | 18 ++++++------------ 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 331df82..b6a0fd2 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -138,11 +138,10 @@ def run_updates(args): "System passed checks, updating ...", ) users = [] - users = get_active_sessions() - #try: - # users = get_active_sessions() - #except KeyError as e: - # log.error("failed to get active logind session info", e) + try: + users = get_active_sessions() + except KeyError as e: + log.error("failed to get active logind session info", e) if args.system: users = [] diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index eb574fa..64dc161 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -29,18 +29,12 @@ def get_active_sessions(): f"{session['session']}", ] out = subprocess.run(args, capture_output=True) - if out.returncode != 0: - print(f"Session: {session}") - print(out.stdout.decode("utf-8")) - print(out.stdout.decode("utf-8").splitlines()) - print("loginctl return code: ", out.returncode) - loginctl_output = { - line.split("=")[0]: line.split("=")[-1] - for line in out.stdout.decode("utf-8").splitlines() - } - - print(loginctl_output) - session_properties.append(loginctl_output) + if out.returncode == 0: + loginctl_output = { + line.split("=")[0]: line.split("=")[-1] + for line in out.stdout.decode("utf-8").splitlines() + } + session_properties.append(loginctl_output) for session_info in session_properties: graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" if graphical and session_info["Active"] == "yes": From cad419aa236716b980751b35d7b9023f657460ff Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Fri, 15 Sep 2023 00:12:59 -0700 Subject: [PATCH 68/71] chore: reformat with black --- src/ublue_update/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ublue_update/session.py b/src/ublue_update/session.py index 64dc161..f02001a 100644 --- a/src/ublue_update/session.py +++ b/src/ublue_update/session.py @@ -38,5 +38,5 @@ def get_active_sessions(): for session_info in session_properties: graphical = session_info["Type"] == "x11" or session_info["Type"] == "wayland" if graphical and session_info["Active"] == "yes": - active_sessions.append(session_info) + active_sessions.append(session_info) return active_sessions From 652a29c0f893243051d61057d7b4ecac092f2252 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Fri, 15 Sep 2023 08:40:46 -0700 Subject: [PATCH 69/71] feat(systemd): timing changes --- files/etc/ublue-update.d/system/00-system-update.sh | 2 +- files/etc/ublue-update.d/system/01-flatpak-system-update.sh | 2 +- .../system/02-flatpak-system-repair-cleanup.sh | 3 +-- files/etc/ublue-update.d/user/00-flatpak-user-update.sh | 2 +- .../ublue-update.d/user/01-flatpak-user-repair-cleanup.sh | 2 +- files/etc/ublue-update.d/user/02-distrobox-user-update.sh | 2 +- files/etc/ublue-update.d/user/03-fleek-user-update.sh | 2 +- files/usr/lib/systemd/system/ublue-update.service | 2 -- files/usr/lib/systemd/system/ublue-update.timer | 5 ++--- src/ublue_update/cli.py | 3 +-- 10 files changed, 10 insertions(+), 15 deletions(-) diff --git a/files/etc/ublue-update.d/system/00-system-update.sh b/files/etc/ublue-update.d/system/00-system-update.sh index aca8bbc..440a77d 100755 --- a/files/etc/ublue-update.d/system/00-system-update.sh +++ b/files/etc/ublue-update.d/system/00-system-update.sh @@ -1,3 +1,3 @@ -#!/usr/bin/bash +#!/usr/bin/env bash /usr/bin/rpm-ostree update diff --git a/files/etc/ublue-update.d/system/01-flatpak-system-update.sh b/files/etc/ublue-update.d/system/01-flatpak-system-update.sh index accbfb9..cba7208 100755 --- a/files/etc/ublue-update.d/system/01-flatpak-system-update.sh +++ b/files/etc/ublue-update.d/system/01-flatpak-system-update.sh @@ -1,3 +1,3 @@ -#!/usr/bin/bash +#!/usr/bin/env bash /usr/bin/flatpak --system update -y --noninteractive diff --git a/files/etc/ublue-update.d/system/02-flatpak-system-repair-cleanup.sh b/files/etc/ublue-update.d/system/02-flatpak-system-repair-cleanup.sh index 9ba0a76..b10acc8 100755 --- a/files/etc/ublue-update.d/system/02-flatpak-system-repair-cleanup.sh +++ b/files/etc/ublue-update.d/system/02-flatpak-system-repair-cleanup.sh @@ -1,5 +1,4 @@ -#!/usr/bin/bash - +#!/usr/bin/env bash /usr/bin/flatpak uninstall --system --unused -y --noninteractive /usr/bin/flatpak repair --system diff --git a/files/etc/ublue-update.d/user/00-flatpak-user-update.sh b/files/etc/ublue-update.d/user/00-flatpak-user-update.sh index a6fcfaa..76d3b79 100755 --- a/files/etc/ublue-update.d/user/00-flatpak-user-update.sh +++ b/files/etc/ublue-update.d/user/00-flatpak-user-update.sh @@ -1,3 +1,3 @@ -#!/usr/bin/bash +#!/usr/bin/env bash /usr/bin/flatpak --user update -y --noninteractive diff --git a/files/etc/ublue-update.d/user/01-flatpak-user-repair-cleanup.sh b/files/etc/ublue-update.d/user/01-flatpak-user-repair-cleanup.sh index dee7ce8..8046e76 100755 --- a/files/etc/ublue-update.d/user/01-flatpak-user-repair-cleanup.sh +++ b/files/etc/ublue-update.d/user/01-flatpak-user-repair-cleanup.sh @@ -1,4 +1,4 @@ -#!/usr/bin/bash +#!/usr/bin/env bash /usr/bin/flatpak uninstall --user --unused -y --noninteractive diff --git a/files/etc/ublue-update.d/user/02-distrobox-user-update.sh b/files/etc/ublue-update.d/user/02-distrobox-user-update.sh index 0f338fe..81b7aed 100755 --- a/files/etc/ublue-update.d/user/02-distrobox-user-update.sh +++ b/files/etc/ublue-update.d/user/02-distrobox-user-update.sh @@ -1,4 +1,4 @@ -#!/usr/bin/bash +#!/usr/bin/env bash if [ -x /usr/bin/distrobox ]; then /usr/bin/distrobox upgrade -a diff --git a/files/etc/ublue-update.d/user/03-fleek-user-update.sh b/files/etc/ublue-update.d/user/03-fleek-user-update.sh index 1573628..c322993 100755 --- a/files/etc/ublue-update.d/user/03-fleek-user-update.sh +++ b/files/etc/ublue-update.d/user/03-fleek-user-update.sh @@ -1,4 +1,4 @@ -#!/usr/bin/bash +#!/usr/bin/env bash if [ -x /var/usrlocal/bin/fleek ]; then /var/usrlocal/bin/fleek update -a diff --git a/files/usr/lib/systemd/system/ublue-update.service b/files/usr/lib/systemd/system/ublue-update.service index 42d1297..04bf208 100644 --- a/files/usr/lib/systemd/system/ublue-update.service +++ b/files/usr/lib/systemd/system/ublue-update.service @@ -2,7 +2,5 @@ Description=Universal Blue Update Oneshot Service [Service] -Restart=on-failure -RestartSec=1h Type=oneshot ExecStart=/usr/bin/ublue-update diff --git a/files/usr/lib/systemd/system/ublue-update.timer b/files/usr/lib/systemd/system/ublue-update.timer index 4de3904..6eabc5a 100644 --- a/files/usr/lib/systemd/system/ublue-update.timer +++ b/files/usr/lib/systemd/system/ublue-update.timer @@ -3,9 +3,8 @@ Description=Auto Update System Timer For Universal Blue Wants=network-online.target [Timer] -RandomizedDelaySec=10m -OnBootSec=2m -OnCalendar=*-*-* 4:00:00 +OnBootSec=20min +OnUnitInactiveSec=6h Persistent=true [Install] diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index b6a0fd2..d21e254 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -107,8 +107,7 @@ def run_update_scripts(root_dir: str): capture_output=True, ) if out.returncode != 0: - log.info(f"{full_path} returned error code: {out.returncode}") - log.info(f"Program output: \n {out.stdout.decode('utf-8')}") + log.error(f"{full_path} returned error code: {out.returncode}", out.stdout.decode('utf-8')) notify( "System Updater", f"Error in update script: {file}, check logs for more info", From bebeae48cf4f3c3dc49ed591202efa329f9758f4 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:00:25 -0700 Subject: [PATCH 70/71] chore: remove merge artifact --- src/ublue_update/cli.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 7712794..4f749de 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -255,7 +255,3 @@ def main(): # system checks passed log.info("System passed all update checks") run_updates(cli_args) - notify( - "System Updater", - "System passed checks, updating ...", - ) \ No newline at end of file From 53095c7a1feea532ca71d86ed486cd7155925eb2 Mon Sep 17 00:00:00 2001 From: gerblesh <101901964+gerblesh@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:06:19 -0700 Subject: [PATCH 71/71] style: reformat with black --- src/ublue_update/cli.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ublue_update/cli.py b/src/ublue_update/cli.py index 4f749de..b11dcd5 100644 --- a/src/ublue_update/cli.py +++ b/src/ublue_update/cli.py @@ -11,7 +11,6 @@ from ublue_update.filelock import acquire_lock, release_lock - def notify(title: str, body: str, actions: list = [], urgency: str = "normal"): if not dbus_notify: return @@ -108,7 +107,10 @@ def run_update_scripts(root_dir: str): capture_output=True, ) if out.returncode != 0: - log.error(f"{full_path} returned error code: {out.returncode}", out.stdout.decode('utf-8')) + log.error( + f"{full_path} returned error code: {out.returncode}", + out.stdout.decode("utf-8"), + ) notify( "System Updater", f"Error in update script: {file}, check logs for more info", @@ -116,6 +118,7 @@ def run_update_scripts(root_dir: str): else: log.info(f"could not execute file {full_path}") + def run_updates(args): process_uid = os.getuid() filelock_path = "/run/ublue-update.lock"