diff --git a/flake.nix b/flake.nix index 9101c4b..b422ab5 100644 --- a/flake.nix +++ b/flake.nix @@ -14,15 +14,8 @@ }; }; - 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" @@ -30,230 +23,203 @@ "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" + 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 - done - ''; - passthru.dataDir = "/var/lib/iota"; + ''; + 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 ]; }; }; - 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 = "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."; + 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"; - 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."; + 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."; + }; }; - certFile = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Path to the SSL certificate file (cert.pem)."; - }; + config = lib.mkIf cfg.enable { + users.users.iota = { + isSystemUser = true; + group = "iota"; + home = cfg.dataDir; + createHome = true; + description = "Iota service user"; + }; - keyFile = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = "Path to the SSL private key file (cert.key)."; - }; + users.groups.iota = { }; - environmentFiles = lib.mkOption { - type = lib.types.listOf lib.types.path; - default = []; - description = "Environment files to load for the Iota service."; - }; + systemd.services.iota = { + description = descriptionText; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = [ pkgs.tmux pkgs.bash pkgs.coreutils pkgs.systemd ]; - 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"; + serviceConfig = { + Type = if cfg.useTmux then "forking" else "simple"; User = "iota"; Group = "iota"; WorkingDirectory = cfg.dataDir; - 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} + 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"; - 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"; + 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; 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} - '') + 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 + ''} + '') ]; 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; @@ -265,18 +231,17 @@ "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 8769c7f..b0cd3a3 100755 --- a/iota-util/src/file_util.rs +++ b/iota-util/src/file_util.rs @@ -149,8 +149,9 @@ pub fn get_children(path: &str) -> Vec { } pub fn get_directory() -> String { - std::env::current_dir() - .unwrap_or_else(|_| PathBuf::from(".")) + let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from(".")); + exe.parent() + .unwrap_or(Path::new(".")) .to_string_lossy() .to_string() }