From 41deb0d6deca6c7e3485ca4039073e7c1e9a824d Mon Sep 17 00:00:00 2001 From: Alois Date: Sun, 17 May 2026 22:22:03 +0200 Subject: [PATCH] [Fix] systemd stuff fr this time --- flake.nix | 379 ++++++++++++++++++++----------------- iota-util/src/file_util.rs | 5 +- 2 files changed, 209 insertions(+), 175 deletions(-) diff --git a/flake.nix b/flake.nix index b422ab5..9101c4b 100644 --- a/flake.nix +++ b/flake.nix @@ -14,8 +14,15 @@ }; }; - outputs = inputs@{ self, nixpkgs, flake-parts, rust-overlay, ttp, ... }: - flake-parts.lib.mkFlake { inherit inputs; } { + outputs = inputs @ { + self, + nixpkgs, + flake-parts, + rust-overlay, + ttp, + ... + }: + flake-parts.lib.mkFlake {inherit inputs;} { systems = [ "x86_64-linux" "aarch64-linux" @@ -23,203 +30,230 @@ "aarch64-darwin" ]; - perSystem = { self', pkgs, system, ... }: - let - rustPkgs = import nixpkgs { - inherit system; - overlays = [ (import rust-overlay) ]; - }; - rustToolchain = rustPkgs.rust-bin.stable.latest.default.override { - extensions = [ "rust-src" "rust-analyzer" "clippy" "rustfmt" ]; - }; - in - { - packages = { - default = self'.packages.iota; - iota = pkgs.rustPlatform.buildRustPackage { - pname = "iota"; - version = "0.1.0"; - src = ./.; - cargoLock = { - lockFile = ./Cargo.lock; - allowBuiltinFetchGit = true; - }; - nativeBuildInputs = with pkgs; [ cmake perl pkg-config ]; - buildInputs = with pkgs; [ openssl sqlite ]; - dontUseCmakeConfigure = true; - preConfigure = '' - if [ -d ../cargo-vendor-dir/ttp-core-0.1.0 ]; then - cp ${ttp}/ttp-codec.json ../cargo-vendor-dir/ttp-codec.json - fi - ''; - postInstall = '' - mv $out/bin/iota-core $out/bin/iota - for f in $out/bin/*; do - if [ "$(basename "$f")" != "iota" ]; then - rm "$f" - fi - done - ''; - passthru.dataDir = "/var/lib/iota"; + perSystem = { + self', + pkgs, + system, + ... + }: let + rustPkgs = import nixpkgs { + inherit system; + overlays = [(import rust-overlay)]; + }; + rustToolchain = rustPkgs.rust-bin.stable.latest.default.override { + extensions = ["rust-src" "rust-analyzer" "clippy" "rustfmt"]; + }; + in { + packages = { + default = self'.packages.iota; + iota = pkgs.rustPlatform.buildRustPackage { + pname = "iota"; + version = "0.1.0"; + src = ./.; + cargoLock = { + lockFile = ./Cargo.lock; + allowBuiltinFetchGit = true; }; - }; - - devShells.default = pkgs.mkShell { - nativeBuildInputs = with pkgs; [ rustToolchain git cmake perl pkg-config ]; - buildInputs = with pkgs; [ openssl sqlite ]; + nativeBuildInputs = with pkgs; [cmake perl pkg-config]; + buildInputs = with pkgs; [openssl sqlite]; + dontUseCmakeConfigure = true; + preConfigure = '' + if [ -d ../cargo-vendor-dir/ttp-core-0.1.0 ]; then + cp ${ttp}/ttp-codec.json ../cargo-vendor-dir/ttp-codec.json + fi + ''; + postInstall = '' + mv $out/bin/iota-core $out/bin/iota + for f in $out/bin/*; do + if [ "$(basename "$f")" != "iota" ]; then + rm "$f" + fi + done + ''; + passthru.dataDir = "/var/lib/iota"; }; }; + devShells.default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [rustToolchain git cmake perl pkg-config]; + buildInputs = with pkgs; [openssl sqlite]; + }; + }; + flake = { - nixosModules.default = { config, pkgs, lib, ... }: - let - cfg = config.services.iota; - defaultPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default or (throw "iota: no pre-built package for system ${pkgs.stdenv.hostPlatform.system}"); + nixosModules.default = { + config, + pkgs, + lib, + ... + }: let + cfg = config.services.iota; + defaultPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default or (throw "iota: no pre-built package for system ${pkgs.stdenv.hostPlatform.system}"); - configFile = if cfg.settingsFile != null then cfg.settingsFile else - pkgs.writeText "iota-config.json" (builtins.toJSON cfg.settings); + configFile = + if cfg.settingsFile != null + then cfg.settingsFile + else pkgs.writeText "iota-config.json" (builtins.toJSON cfg.settings); - descriptionText = "Iota Service" - + lib.optionalString cfg.useTmux " (attach TUI: tmux -S ${cfg.dataDir}/tmux.sock attach -t iota)"; - in - { - options.services.iota = { - enable = lib.mkEnableOption "the Iota service"; + descriptionText = "Tensamin Iota"; + #+ lib.optionalString cfg.useTmux " (attach TUI: tmux -S ${cfg.dataDir}/tmux.sock attach -t iota)"; + in { + options.services.iota = { + enable = lib.mkEnableOption "Enable the Iota service."; - dataDir = lib.mkOption { - type = lib.types.str; - default = cfg.package.passthru.dataDir or "/var/lib/iota"; - defaultText = lib.literalExpression ''config.services.iota.package.passthru.dataDir or "/var/lib/iota"''; - description = "Directory where Iota stores its data, config, and certificates."; - }; - - certFile = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Path to the SSL certificate file (cert.pem)."; - }; - - keyFile = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Path to the SSL private key file (cert.key)."; - }; - - environmentFiles = lib.mkOption { - type = lib.types.listOf lib.types.path; - default = [ ]; - description = "Environment files to load for the Iota service."; - }; - - openFirewall = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to open the firewall for ports used by Iota."; - }; - - ttpBind = lib.mkOption { - type = lib.types.str; - default = "0.0.0.0"; - description = "IP address to bind the TTP/QUIC server to."; - }; - - bindAddress = lib.mkOption { - type = lib.types.str; - default = "0.0.0.0"; - description = "IP address to bind the HTTP server to."; - }; - - package = lib.mkOption { - type = lib.types.package; - default = defaultPackage; - description = "The Iota package to use."; - }; - - useTmux = lib.mkOption { - type = lib.types.bool; - default = true; - description = "Whether to run Iota inside a tmux session for shared TUI access."; - }; - - settings = lib.mkOption { - type = lib.types.attrs; - default = { }; - description = "Configuration attributes for Iota, written to config.json."; - }; - - settingsFile = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Path to an existing config.json file to use instead of generating from settings."; - }; + dataDir = lib.mkOption { + type = lib.types.str; + default = cfg.package.passthru.dataDir or "/var/lib/iota"; + defaultText = lib.literalExpression ''config.services.iota.package.passthru.dataDir or "/var/lib/iota"''; + description = "Directory where Iota stores its data, config, and certificates."; }; - config = lib.mkIf cfg.enable { - users.users.iota = { - isSystemUser = true; - group = "iota"; - home = cfg.dataDir; - createHome = true; - description = "Iota service user"; - }; + certFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = "Path to the SSL certificate file (cert.pem)."; + }; - users.groups.iota = { }; + keyFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = "Path to the SSL private key file (cert.key)."; + }; - systemd.services.iota = { - description = descriptionText; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - path = [ pkgs.tmux pkgs.bash pkgs.coreutils pkgs.systemd ]; + environmentFiles = lib.mkOption { + type = lib.types.listOf lib.types.path; + default = []; + description = "Environment files to load for the Iota service."; + }; - serviceConfig = { - Type = if cfg.useTmux then "forking" else "simple"; + openFirewall = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to open the firewall for ports used by Iota."; + }; + + ttpBind = lib.mkOption { + type = lib.types.str; + default = "0.0.0.0"; + description = "IP address to bind the TTP/QUIC server to."; + }; + + bindAddress = lib.mkOption { + type = lib.types.str; + default = "0.0.0.0"; + description = "IP address to bind the HTTP server to."; + }; + + package = lib.mkOption { + type = lib.types.package; + default = defaultPackage; + description = "The Iota package to use."; + }; + + useTmux = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether to run Iota inside a tmux session for shared TUI access."; + }; + + settings = lib.mkOption { + type = lib.types.attrs; + default = {}; + description = "Configuration attributes for Iota, written to config.json."; + }; + + settingsFile = lib.mkOption { + type = lib.types.nullOr lib.types.path; + default = null; + description = "Path to an existing config.json file to use instead of generating from settings."; + }; + }; + + config = lib.mkIf cfg.enable { + users.users.iota = { + isSystemUser = true; + group = "iota"; + home = cfg.dataDir; + createHome = true; + description = "Iota service user"; + shell = pkgs.bash; + }; + + users.groups.iota = {}; + + systemd.services.iota = let + iotaTmuxCmd = pkgs.writeShellScript "iota-tmux-cmd" '' + mkdir -p ${cfg.dataDir} + echo "[$(date)] Running Iota..." + ${cfg.package}/bin/iota + status=$? + echo "" + echo "[$(date)] Iota exited with status: $status" + echo "Press any key to exit..." + read -r -n 1 + exit $status + ''; + in { + description = descriptionText; + wantedBy = ["multi-user.target"]; + after = ["network.target"]; + + serviceConfig = + { + Type = "simple"; User = "iota"; Group = "iota"; WorkingDirectory = cfg.dataDir; - ExecStart = if cfg.useTmux then - pkgs.writeShellScript "iota-start" '' - ${pkgs.tmux}/bin/tmux -S ${cfg.dataDir}/tmux.sock new-session -d -s iota \ - "${pkgs.bash}/bin/bash -lc 'exec > >(${pkgs.coreutils}/bin/tee -a ${cfg.dataDir}/iota-output.log >(${pkgs.systemd}/bin/systemd-cat -t iota-daemon)) 2>&1; ${cfg.package}/bin/iota; status=$?; printf \"\nProcess exited with status %s. Press any key to close this tmux session...\" \"\$status\"; read -r -n 1; exit \"\$status\"'" - '' - else - "${cfg.package}/bin/iota"; + ExecStart = + if cfg.useTmux + then + pkgs.writeShellScript "iota-start" '' + set -e + export TMUX_TMPDIR=${cfg.dataDir} + ${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir} + ${pkgs.coreutils}/bin/chown iota:iota ${cfg.dataDir} - ExecStop = if cfg.useTmux then - (pkgs.writeShellScript "iota-stop" '' - ${pkgs.tmux}/bin/tmux -S ${cfg.dataDir}/tmux.sock kill-session -t iota 2>/dev/null || true - '') - else - null; + echo "[iota-start] Creating tmux session..." + if ! ${pkgs.tmux}/bin/tmux -S ${cfg.dataDir}/tmux.sock new-session -d -s iota "${iotaTmuxCmd}"; then + echo "[iota-start] ERROR: tmux new-session failed" + exit 1 + fi + echo "[iota-start] tmux session created, waiting..." + echo "[iota-start] Run 'tmux -S ${cfg.dataDir}/tmux.sock attach -t iota' to attach to the tmux session." + + while ${pkgs.tmux}/bin/tmux -S ${cfg.dataDir}/tmux.sock has-session -t iota 2>/dev/null; do + sleep 2 + done + echo "[iota-start] tmux session ended" + '' + else "${cfg.package}/bin/iota"; ExecStartPre = [ - ("+" + pkgs.writeShellScript "iota-setup" '' - mkdir -p ${cfg.dataDir}/certs + ("+" + + pkgs.writeShellScript "iota-setup" '' + mkdir -p ${cfg.dataDir}/certs - ${lib.optionalString (cfg.certFile != null) "ln -sf ${cfg.certFile} ${cfg.dataDir}/certs/cert.pem"} - ${lib.optionalString (cfg.keyFile != null) "ln -sf ${cfg.keyFile} ${cfg.dataDir}/certs/cert.key"} + ${lib.optionalString (cfg.certFile != null) "ln -sf ${cfg.certFile} ${cfg.dataDir}/certs/cert.pem"} + ${lib.optionalString (cfg.keyFile != null) "ln -sf ${cfg.keyFile} ${cfg.dataDir}/certs/cert.key"} - install -m 644 ${configFile} ${cfg.dataDir}/config.json + install -m 644 ${configFile} ${cfg.dataDir}/config.json - chown -R iota:iota ${cfg.dataDir} - - ${lib.optionalString cfg.useTmux '' - echo "Iota started under tmux. Attach with: tmux -S ${cfg.dataDir}/tmux.sock attach -t iota" >&2 - ''} - '') + chown -R iota:iota ${cfg.dataDir} + '') ]; Restart = "always"; RestartSec = "5s"; - AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; - CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; + AmbientCapabilities = ["CAP_NET_BIND_SERVICE"]; + CapabilityBoundingSet = ["CAP_NET_BIND_SERVICE"]; ProtectSystem = "strict"; ProtectHome = true; PrivateTmp = true; NoNewPrivileges = true; - ReadWritePaths = [ cfg.dataDir ]; + ReadWritePaths = [cfg.dataDir]; ProtectKernelTunables = true; ProtectKernelModules = true; ProtectControlGroups = true; @@ -231,17 +265,18 @@ "TTP_BIND=${cfg.ttpBind}" "BIND_ADDRESS=${cfg.bindAddress}" ]; - } // lib.optionalAttrs (cfg.environmentFiles != [ ]) { + } + // lib.optionalAttrs (cfg.environmentFiles != []) { EnvironmentFile = cfg.environmentFiles; }; - }; + }; - networking.firewall = lib.mkIf cfg.openFirewall { - allowedTCPPorts = [ 1984 ]; - allowedUDPPorts = [ 1984 ]; - }; + networking.firewall = lib.mkIf cfg.openFirewall { + allowedTCPPorts = [1984]; + allowedUDPPorts = [1984]; }; }; + }; }; }; } diff --git a/iota-util/src/file_util.rs b/iota-util/src/file_util.rs index b0cd3a3..8769c7f 100755 --- a/iota-util/src/file_util.rs +++ b/iota-util/src/file_util.rs @@ -149,9 +149,8 @@ pub fn get_children(path: &str) -> Vec { } pub fn get_directory() -> String { - let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from(".")); - exe.parent() - .unwrap_or(Path::new(".")) + std::env::current_dir() + .unwrap_or_else(|_| PathBuf::from(".")) .to_string_lossy() .to_string() }