From 9119afd23cd3aeef6bfd558a61f71c63e75af9c8 Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 15:11:56 -0400 Subject: [PATCH 01/10] Set the service environment variable `XAUTHORITY` to the value of the outer environment variable when creating the service. (https://github.com/ProjectNeura/LEADS/issues/156#issuecomment-2105995160) --- leads_vec/_bootloader/systemd.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/leads_vec/_bootloader/systemd.py b/leads_vec/_bootloader/systemd.py index 11609e32..9ad3315c 100644 --- a/leads_vec/_bootloader/systemd.py +++ b/leads_vec/_bootloader/systemd.py @@ -1,5 +1,6 @@ -from os import chmod as _chmod, getlogin as _get_login, mkdir as _mkdir +from os import chmod as _chmod, getlogin as _get_login, mkdir as _mkdir, environ as _environ from os.path import abspath as _abspath, exists as _exists +from subprocess import run as _run, CalledProcessError as _CalledProcessError from leads import L as _L from leads_gui import Config as _Config @@ -28,7 +29,7 @@ def create_service() -> None: f"User={(user := _get_login())}\n" f"Environment=\"USERNAME={user}\"\n" "Environment=\"DISPLAY=:0\"\n" - f"Environment=\"XAUTHORITY=/home/{user}/.Xauthority\"\n" + f"Environment=\"XAUTHORITY={_environ["XAUTHORITY"]}\"\n" f"ExecStart=/bin/bash {script}\n" "[Install]\n" "WantedBy=graphical.target" From d841b13d23ae593333f8e263e1641c72812810e0 Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 15:12:22 -0400 Subject: [PATCH 02/10] Removed XWS configuring. (#156) --- leads_vec/_bootloader/leads_vec.service.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/leads_vec/_bootloader/leads_vec.service.sh b/leads_vec/_bootloader/leads_vec.service.sh index f7c1c2cc..b59b2146 100644 --- a/leads_vec/_bootloader/leads_vec.service.sh +++ b/leads_vec/_bootloader/leads_vec.service.sh @@ -8,7 +8,5 @@ fi while ! xhost >& /dev/null do sleep 1 done -# configure xws -/usr/bin/xhost +SI:localuser:"$USERNAME" # change the interpreter or adjust the arguments according to your needs python-leads -m leads_vec -c /usr/local/leads/config.json run \ No newline at end of file From 1a7b5d97ad513b5eaceda0c762d1adcd70e5701b Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 15:21:56 -0400 Subject: [PATCH 03/10] Supported more precise display manager dependencies. (#156) --- leads_vec/__main__.py | 2 +- leads_vec/_bootloader/systemd.py | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/leads_vec/__main__.py b/leads_vec/__main__.py index aa2296ce..171d2055 100644 --- a/leads_vec/__main__.py +++ b/leads_vec/__main__.py @@ -57,7 +57,7 @@ sep="\n") _exit() if args.register == "systemd": - from ._bootloader import create_service as _create_service + from ._bootloader import register_leads_vec as _create_service _create_service() _L.info("Service registered") diff --git a/leads_vec/_bootloader/systemd.py b/leads_vec/_bootloader/systemd.py index 9ad3315c..c68ca650 100644 --- a/leads_vec/_bootloader/systemd.py +++ b/leads_vec/_bootloader/systemd.py @@ -7,9 +7,26 @@ from leads_gui.system import get_system_kernel as _get_system_kernel -def create_service() -> None: +def is_service_active(name: str = "leads_vec") -> bool: + try: + _run(("systemctl", "is-active", f"{name}.service"), check=True) + return True + except _CalledProcessError: + return False + + +def display_manager_service_name() -> str: + for dm in ("gdm", "lightdm", "sddm", "xdm", "kdm"): + if is_service_active(dm): + return dm + return "display-manager" + + +def register_leads_vec() -> None: if _get_system_kernel() != "linux": raise SystemError("Unsupported operating system") + if is_service_active(): + raise SystemError("LEADS VeC is running") if not _exists("/usr/local/leads/config.json"): _L.debug("Config file not found. Creating \"/usr/local/leads/config.json\"...") if not _exists("/usr/local/leads"): @@ -18,12 +35,14 @@ def create_service() -> None: f.write(str(_Config({}))) _chmod("/usr/local/leads/config.json", 0x644) _chmod(script := f"{_abspath(__file__)[:-10]}leads_vec.service.sh", 0o755) + dm = display_manager_service_name() + _L.debug(f"Detected display manager: {dm}") with open("/etc/systemd/system/leads_vec.service", "w") as f: f.write( "[Unit]\n" "Description=LEADS VeC\n" - "After=display-manager.service\n" - "Requires=display-manager.service\n" + f"After={dm}.service\n" + f"Requires={dm}.service\n" "[Service]\n" "Type=simple\n" f"User={(user := _get_login())}\n" From 94ad6deadca8c10d20fe1a3e63da48ceb5c30d12 Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 16:22:45 -0400 Subject: [PATCH 04/10] Code reformatted. --- scripts/frp-config.sh | 14 +++++++------- scripts/frp-install.sh | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/frp-config.sh b/scripts/frp-config.sh index 80437a37..a8c05e00 100644 --- a/scripts/frp-config.sh +++ b/scripts/frp-config.sh @@ -8,7 +8,7 @@ abort() { if [ "${EUID:-$(id -u)}" -ne 0 ] then abort "Error: This script requires root permission" fi -if ! test -d "/usr/local/frp" +if test ! -d "/usr/local/frp" then abort "Error: /usr/local/frp not found" fi @@ -23,16 +23,16 @@ execute_root() { } require_argument() { - if [ -n "$1" ] - then echo "$1" - else abort "Required argument $2 does not exist" + if test -z "$1" + then abort "Required argument $2 does not exist" + else echo "$1" fi } argument_exists_or() { - if [ -n "$1" ] - then echo "$1" - else echo "$2" + if test -z "$1" + then echo "$2" + else echo "$1" fi } diff --git a/scripts/frp-install.sh b/scripts/frp-install.sh index 24ad807c..f02ec12b 100644 --- a/scripts/frp-install.sh +++ b/scripts/frp-install.sh @@ -29,7 +29,7 @@ then execute_root "apt" "install" "-y" "curl" fi latest_release=$(curl -s "https://api.github.com/repos/fatedier/frp/releases/latest" | grep -o '"tag_name": "[^"]*' | grep -o '[^"]*$' | cut -c 2-) -if [ -z "$latest_release" ] +if test -z "$latest_release" then abort "Failed to retrieve the latest release" fi filename="frp_${latest_release}_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m)" From 13e67d9c150e245fe790bb3dd4126094d54f34a4 Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 17:19:32 -0400 Subject: [PATCH 05/10] Converted `leads_vec` to a user service `leads-vec`. (https://github.com/ProjectNeura/LEADS/issues/156#issuecomment-2106001543) --- leads_vec/__main__.py | 2 +- ...ds_vec.service.sh => leads-vec.service.sh} | 5 +-- leads_vec/_bootloader/systemd.py | 34 +++++-------------- setup.py | 2 +- 4 files changed, 11 insertions(+), 32 deletions(-) rename leads_vec/_bootloader/{leads_vec.service.sh => leads-vec.service.sh} (70%) diff --git a/leads_vec/__main__.py b/leads_vec/__main__.py index 171d2055..d5172e4c 100644 --- a/leads_vec/__main__.py +++ b/leads_vec/__main__.py @@ -61,7 +61,7 @@ _create_service() _L.info("Service registered") - _L.info(f"Service script is located at \"{_MODULE_PATH}/_bootloader/leads_vec.service.sh\"") + _L.info(f"Service script is located at \"{_MODULE_PATH}/_bootloader/leads-vec.service.sh\"") elif args.register == "config": if _exists("config.json"): r = input("\"config.json\" already exists. Overwrite? (y/N) >>>").lower() diff --git a/leads_vec/_bootloader/leads_vec.service.sh b/leads_vec/_bootloader/leads-vec.service.sh similarity index 70% rename from leads_vec/_bootloader/leads_vec.service.sh rename to leads_vec/_bootloader/leads-vec.service.sh index b59b2146..1c1853ed 100644 --- a/leads_vec/_bootloader/leads_vec.service.sh +++ b/leads_vec/_bootloader/leads-vec.service.sh @@ -1,12 +1,9 @@ #!/bin/bash -if [ ! -r "/usr/local/leads/config.json" ] +if test ! -r "/usr/local/leads/config.json" then echo "Error: Config file does not exist" exit 1 fi -while ! xhost >& /dev/null -do sleep 1 -done # change the interpreter or adjust the arguments according to your needs python-leads -m leads_vec -c /usr/local/leads/config.json run \ No newline at end of file diff --git a/leads_vec/_bootloader/systemd.py b/leads_vec/_bootloader/systemd.py index c68ca650..c84ef4d0 100644 --- a/leads_vec/_bootloader/systemd.py +++ b/leads_vec/_bootloader/systemd.py @@ -1,32 +1,14 @@ -from os import chmod as _chmod, getlogin as _get_login, mkdir as _mkdir, environ as _environ +from os import chmod as _chmod, getlogin as _get_login, mkdir as _mkdir from os.path import abspath as _abspath, exists as _exists -from subprocess import run as _run, CalledProcessError as _CalledProcessError from leads import L as _L from leads_gui import Config as _Config from leads_gui.system import get_system_kernel as _get_system_kernel -def is_service_active(name: str = "leads_vec") -> bool: - try: - _run(("systemctl", "is-active", f"{name}.service"), check=True) - return True - except _CalledProcessError: - return False - - -def display_manager_service_name() -> str: - for dm in ("gdm", "lightdm", "sddm", "xdm", "kdm"): - if is_service_active(dm): - return dm - return "display-manager" - - def register_leads_vec() -> None: if _get_system_kernel() != "linux": raise SystemError("Unsupported operating system") - if is_service_active(): - raise SystemError("LEADS VeC is running") if not _exists("/usr/local/leads/config.json"): _L.debug("Config file not found. Creating \"/usr/local/leads/config.json\"...") if not _exists("/usr/local/leads"): @@ -34,15 +16,15 @@ def register_leads_vec() -> None: with open("/usr/local/leads/config.json", "w") as f: f.write(str(_Config({}))) _chmod("/usr/local/leads/config.json", 0x644) - _chmod(script := f"{_abspath(__file__)[:-10]}leads_vec.service.sh", 0o755) - dm = display_manager_service_name() - _L.debug(f"Detected display manager: {dm}") - with open("/etc/systemd/system/leads_vec.service", "w") as f: + _chmod(script := f"{_abspath(__file__)[:-10]}leads-vec.service.sh", 0o755) + if not _exists(systemd_path := f"/home/{_get_login()}/.config/systemd/user"): + _L.debug(f"User systemd not found. Creating \"{systemd_path}\"...") + _mkdir(systemd_path) + with open(f"{systemd_path}/leads-vec.service", "w") as f: f.write( "[Unit]\n" "Description=LEADS VeC\n" - f"After={dm}.service\n" - f"Requires={dm}.service\n" + "After=graphical-session.target\n" "[Service]\n" "Type=simple\n" f"User={(user := _get_login())}\n" @@ -51,5 +33,5 @@ def register_leads_vec() -> None: f"Environment=\"XAUTHORITY={_environ["XAUTHORITY"]}\"\n" f"ExecStart=/bin/bash {script}\n" "[Install]\n" - "WantedBy=graphical.target" + "WantedBy=default.target" ) diff --git a/setup.py b/setup.py index 182278a0..04848f36 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ package_data={ "leads_audio": ["assets/*"], "leads_gui": ["assets/*", "assets/icons/*"], - "leads_vec": ["_bootloader/leads_vec.service.sh"] + "leads_vec": ["_bootloader/leads-vec.service.sh"] }, include_package_data=True, install_requires=["numpy", "pandas"] From 7fd00d2d1d4692ed14629527f335e330ab8f658a Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 17:20:17 -0400 Subject: [PATCH 06/10] Using auto restart to replace delaying. (#156) --- leads_vec/_bootloader/systemd.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/leads_vec/_bootloader/systemd.py b/leads_vec/_bootloader/systemd.py index c84ef4d0..4a2d0780 100644 --- a/leads_vec/_bootloader/systemd.py +++ b/leads_vec/_bootloader/systemd.py @@ -27,11 +27,9 @@ def register_leads_vec() -> None: "After=graphical-session.target\n" "[Service]\n" "Type=simple\n" - f"User={(user := _get_login())}\n" - f"Environment=\"USERNAME={user}\"\n" - "Environment=\"DISPLAY=:0\"\n" - f"Environment=\"XAUTHORITY={_environ["XAUTHORITY"]}\"\n" f"ExecStart=/bin/bash {script}\n" + f"Restart=always\n" + f"RestartSec=1s\n" "[Install]\n" "WantedBy=default.target" ) From 7aadd234b4f3136d316e0d2594c49796a33da9de Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 17:23:11 -0400 Subject: [PATCH 07/10] Docs. (#156) --- README.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d5669430..4f47fb13 100644 --- a/README.md +++ b/README.md @@ -149,13 +149,25 @@ This will generate a default "config.json" file under the current directory. python -m leads_vec -r systemd run ``` -This will register a system service to start the program. +This will register a user systemd service to start the program. To enable auto-start at boot, run the following. ```shell -systemctl daemon-reload -systemctl enable leads_vec +systemctl --user daemon-reload +systemctl --user enable leads-vec +``` + +You will have to stop the service by this command otherwise it will automatically restart when it exits. + +```shell +systemctl --user stop leads-vec +``` + +Use the following to disable the service. + +```shell +systemctl --user disable leads-vec ``` ##### Use Reverse Proxy From 02beee0ed77110db83498d207786a90023f459eb Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 17:46:22 -0400 Subject: [PATCH 08/10] Docs. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f47fb13..82ee6ba2 100644 --- a/README.md +++ b/README.md @@ -343,7 +343,7 @@ Note that a purely empty file could cause an error. |---------------------|---------|-----------------------------------------------------------|--------------|---------------| | `w_debug_level` | `str` | `"DEBUG"`, `"INFO"`, `"WARN"`, `"ERROR"` | Main, Remote | `"DEBUG"` | | `data_seq_size` | `int` | Buffer size of history data | Main | `100` | -| `data_dir` | `str` | The directory for the data recording system | Remote | `"data"` | +| `data_dir` | `str` | The directory for the data recording system | Main, Remote | `"data"` | | `width` | `int` | Window width | Main | `720` | | `height` | `int` | Window height | Main | `480` | | `fullscreen` | `bool` | `True`: auto maximize; `False`: window mode | Main | `False` | From f9ec82fc168e8c766860b7ef44cfbe2c2d027725 Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 17:46:54 -0400 Subject: [PATCH 09/10] Bug fixed: mkdir: no such file or directory. (#156) --- leads_vec/_bootloader/systemd.py | 12 ++++++------ leads_vec_rc/cli.py | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/leads_vec/_bootloader/systemd.py b/leads_vec/_bootloader/systemd.py index 4a2d0780..dae360ea 100644 --- a/leads_vec/_bootloader/systemd.py +++ b/leads_vec/_bootloader/systemd.py @@ -1,4 +1,4 @@ -from os import chmod as _chmod, getlogin as _get_login, mkdir as _mkdir +from os import chmod as _chmod, getlogin as _get_login, makedirs as _mkdirs from os.path import abspath as _abspath, exists as _exists from leads import L as _L @@ -12,15 +12,15 @@ def register_leads_vec() -> None: if not _exists("/usr/local/leads/config.json"): _L.debug("Config file not found. Creating \"/usr/local/leads/config.json\"...") if not _exists("/usr/local/leads"): - _mkdir("/usr/local/leads") + _mkdirs("/usr/local/leads") with open("/usr/local/leads/config.json", "w") as f: f.write(str(_Config({}))) _chmod("/usr/local/leads/config.json", 0x644) _chmod(script := f"{_abspath(__file__)[:-10]}leads-vec.service.sh", 0o755) - if not _exists(systemd_path := f"/home/{_get_login()}/.config/systemd/user"): - _L.debug(f"User systemd not found. Creating \"{systemd_path}\"...") - _mkdir(systemd_path) - with open(f"{systemd_path}/leads-vec.service", "w") as f: + if not _exists(user_systemd := f"/home/{_get_login()}/.config/systemd/user"): + _L.debug(f"User systemd not found. Creating \"{user_systemd}\"...") + _mkdirs(user_systemd) + with open(f"{user_systemd}/leads-vec.service", "w") as f: f.write( "[Unit]\n" "Description=LEADS VeC\n" diff --git a/leads_vec_rc/cli.py b/leads_vec_rc/cli.py index 98198503..11d5599b 100644 --- a/leads_vec_rc/cli.py +++ b/leads_vec_rc/cli.py @@ -1,7 +1,7 @@ from atexit import register from datetime import datetime from json import loads, JSONDecodeError -from os import mkdir +from os import makedirs from os.path import abspath, exists from time import sleep from typing import Any @@ -15,8 +15,8 @@ config = require_config() if not exists(config.data_dir): - mkdir(config.data_dir) - L.info(f"Data dir \"{abspath(config.data_dir)}\" created") + L.debug(f"Data directory not found, creating \"{abspath(config.data_dir)}\"...") + makedirs(config.data_dir) data_record: DataPersistence[DataContainer] = DataPersistence(1, compressor=lambda o, s: o[-s:]) time_stamp_record: DataPersistence[int] = DataPersistence(2000) From a8321e047dc4800095d4ef7867f706fe369d908a Mon Sep 17 00:00:00 2001 From: ATATC Date: Sat, 11 May 2024 17:47:26 -0400 Subject: [PATCH 10/10] Code reformatted. --- leads_vec_rc/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/leads_vec_rc/cli.py b/leads_vec_rc/cli.py index 11d5599b..1d368e4d 100644 --- a/leads_vec_rc/cli.py +++ b/leads_vec_rc/cli.py @@ -15,7 +15,7 @@ config = require_config() if not exists(config.data_dir): - L.debug(f"Data directory not found, creating \"{abspath(config.data_dir)}\"...") + L.debug(f"Data directory not found. Creating \"{abspath(config.data_dir)}\"...") makedirs(config.data_dir) data_record: DataPersistence[DataContainer] = DataPersistence(1, compressor=lambda o, s: o[-s:])