Merge branch 'main' of ssh://git.methanium.net/tensamin/iota
This commit is contained in:
commit
6b4c144b73
7 changed files with 92 additions and 130 deletions
25
LICENSE
25
LICENSE
|
|
@ -1,16 +1,15 @@
|
||||||
Copyright (c) [2025] [Methanium]
|
Copyright (c) 2025 Methanium
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
This software is protected by copyright. Copying, editing,
|
No part of this software, source code, documentation, or
|
||||||
distributing, publicly performing, or any other use of this software
|
associated materials may be copied, reproduced, modified,
|
||||||
or its components, in source or binary form, is strictly prohibited without the express
|
distributed, published, sublicensed, sold, or used to create
|
||||||
written permission of the copyright holder.
|
derivative works without prior written permission from the
|
||||||
|
copyright holder.
|
||||||
|
|
||||||
FUTURE LICENSE ACCEPTANCE:
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY
|
||||||
It is the copyright holder's intention to release this software in the future
|
OF ANY KIND, EXPRESS OR IMPLIED. TO THE MAXIMUM
|
||||||
under a license yet to be defined, which will, among other things,
|
EXTENT PERMITTED BY LAW, THE COPYRIGHT HOLDER SHALL
|
||||||
allow private, non-commercial use. This statement does not constitute
|
NOT BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
||||||
a current license grant and does not alter the above
|
LIABILITY ARISING FROM THE SOFTWARE OR ITS USE.
|
||||||
prohibition on use, copying, or modification. Until the formal
|
|
||||||
publication of such a future license, all rights remain
|
|
||||||
reserved.
|
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,5 @@ usermod -aG iota-operators USER
|
||||||
```
|
```
|
||||||
|
|
||||||
The user must start a new login session before supplementary group membership
|
The user must start a new login session before supplementary group membership
|
||||||
is visible. `IOTA_SOCKET` remains authoritative for custom deployments.
|
is visible. Unix per-user deployments must set `IOTA_SOCKET` to an absolute
|
||||||
|
path; Iota does not derive its IPC socket from `XDG_RUNTIME_DIR`.
|
||||||
|
|
|
||||||
42
flake.nix
42
flake.nix
|
|
@ -42,13 +42,11 @@
|
||||||
commonNativeBuildInputs = with pkgs; [cmake perl pkg-config];
|
commonNativeBuildInputs = with pkgs; [cmake perl pkg-config];
|
||||||
in {
|
in {
|
||||||
packages = {
|
packages = {
|
||||||
default = self'.packages.iota-daemon;
|
default = pkgs.rustPlatform.buildRustPackage {
|
||||||
|
pname = "iota";
|
||||||
iota-daemon = pkgs.rustPlatform.buildRustPackage {
|
|
||||||
pname = "iota-daemon";
|
|
||||||
version = "0.1.0";
|
version = "0.1.0";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
cargoBuildFlags = ["-p" "iota-daemon"];
|
cargoBuildFlags = ["-p" "iota" "-p" "iota-daemon"];
|
||||||
cargoLock = {
|
cargoLock = {
|
||||||
lockFile = ./Cargo.lock;
|
lockFile = ./Cargo.lock;
|
||||||
allowBuiltinFetchGit = true;
|
allowBuiltinFetchGit = true;
|
||||||
|
|
@ -56,6 +54,12 @@
|
||||||
nativeBuildInputs = commonNativeBuildInputs;
|
nativeBuildInputs = commonNativeBuildInputs;
|
||||||
buildInputs = commonBuildInputs;
|
buildInputs = commonBuildInputs;
|
||||||
dontUseCmakeConfigure = true;
|
dontUseCmakeConfigure = true;
|
||||||
|
passthru.dataDir = "/var/lib/iota";
|
||||||
|
};
|
||||||
|
|
||||||
|
iota-daemon = self'.packages.default.overrideAttrs (old: {
|
||||||
|
pname = "iota-daemon";
|
||||||
|
cargoBuildFlags = ["-p" "iota-daemon"];
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
for f in $out/bin/*; do
|
for f in $out/bin/*; do
|
||||||
if [ "$(basename "$f")" != "iota-daemon" ]; then
|
if [ "$(basename "$f")" != "iota-daemon" ]; then
|
||||||
|
|
@ -63,33 +67,22 @@
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
passthru.dataDir = "/var/lib/iota";
|
});
|
||||||
};
|
|
||||||
|
|
||||||
iota-ui = pkgs.rustPlatform.buildRustPackage {
|
iota-ui = self'.packages.default.overrideAttrs (old: {
|
||||||
pname = "iota-ui";
|
pname = "iota-ui";
|
||||||
version = "0.1.0";
|
|
||||||
src = ./.;
|
|
||||||
cargoBuildFlags = ["-p" "iota"];
|
cargoBuildFlags = ["-p" "iota"];
|
||||||
cargoLock = {
|
|
||||||
lockFile = ./Cargo.lock;
|
|
||||||
allowBuiltinFetchGit = true;
|
|
||||||
};
|
|
||||||
nativeBuildInputs = commonNativeBuildInputs;
|
|
||||||
buildInputs = commonBuildInputs;
|
|
||||||
dontUseCmakeConfigure = true;
|
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
for f in $out/bin/*; do
|
for f in $out/bin/*; do
|
||||||
if [ "$(basename "$f")" != "iota" ]; then
|
if [ "$(basename "$f")" != "iota" ]; then
|
||||||
rm "$f"
|
rm "$f"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
# Rename to avoid confusion
|
|
||||||
if [ -f "$out/bin/iota" ]; then
|
if [ -f "$out/bin/iota" ]; then
|
||||||
mv "$out/bin/iota" "$out/bin/iota-ui"
|
mv "$out/bin/iota" "$out/bin/iota-ui"
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
};
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
|
|
@ -106,7 +99,7 @@
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
cfg = config.services.iota;
|
cfg = config.services.iota;
|
||||||
defaultPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.iota-daemon or (throw "iota: no pre-built package for system ${pkgs.stdenv.hostPlatform.system}");
|
defaultPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default or (throw "iota: no pre-built package for system ${pkgs.stdenv.hostPlatform.system}");
|
||||||
|
|
||||||
configFormat = pkgs.formats.yaml {};
|
configFormat = pkgs.formats.yaml {};
|
||||||
configFile =
|
configFile =
|
||||||
|
|
@ -190,7 +183,7 @@
|
||||||
|
|
||||||
users.groups.iota = {};
|
users.groups.iota = {};
|
||||||
|
|
||||||
systemd.sockets.iota-daemon = {
|
systemd.sockets.iota = {
|
||||||
description = "${descriptionText} IPC socket";
|
description = "${descriptionText} IPC socket";
|
||||||
wantedBy = ["sockets.target"];
|
wantedBy = ["sockets.target"];
|
||||||
socketConfig = {
|
socketConfig = {
|
||||||
|
|
@ -205,10 +198,11 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.iota-daemon = {
|
systemd.services.iota = {
|
||||||
description = descriptionText;
|
description = descriptionText;
|
||||||
after = ["network.target"];
|
wantedBy = ["multi-user.target"];
|
||||||
requires = ["iota-daemon.socket"];
|
after = ["network.target" "iota.socket"];
|
||||||
|
requires = ["iota.socket"];
|
||||||
|
|
||||||
serviceConfig =
|
serviceConfig =
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -172,20 +172,4 @@ fn run(program: &str, args: &[&str]) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn service_uses_installed_daemon_and_declared_identities() {
|
|
||||||
let service = include_str!("../../systemd/iota-daemon.service");
|
|
||||||
let socket = include_str!("../../systemd/iota-daemon.socket");
|
|
||||||
let sysusers = include_str!("../../systemd/sysusers.d/iota.conf");
|
|
||||||
assert!(service.contains("ExecStart=/usr/local/libexec/iota/iota-daemon"));
|
|
||||||
assert!(service.contains("User=iota"));
|
|
||||||
assert!(service.contains("Group=iota"));
|
|
||||||
assert!(socket.contains("SocketUser=iota"));
|
|
||||||
assert!(socket.contains("SocketGroup=iota-operators"));
|
|
||||||
assert!(socket.contains("NonBlocking=true"));
|
|
||||||
assert!(sysusers.contains("u iota "));
|
|
||||||
assert!(sysusers.contains("g iota-operators"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ pub enum IpcEndpoint {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PathError {
|
pub enum PathError {
|
||||||
MissingPlatformDirectory(&'static str),
|
MissingPlatformDirectory(&'static str),
|
||||||
|
MissingRequiredOverride(&'static str),
|
||||||
EmptyOverride(&'static str),
|
EmptyOverride(&'static str),
|
||||||
RelativeOverride {
|
RelativeOverride {
|
||||||
variable: &'static str,
|
variable: &'static str,
|
||||||
|
|
@ -37,6 +38,7 @@ impl fmt::Display for PathError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::MissingPlatformDirectory(name) => write!(f, "missing platform directory: {name}"),
|
Self::MissingPlatformDirectory(name) => write!(f, "missing platform directory: {name}"),
|
||||||
|
Self::MissingRequiredOverride(name) => write!(f, "{name} must be set"),
|
||||||
Self::EmptyOverride(name) => write!(f, "{name} must not be empty"),
|
Self::EmptyOverride(name) => write!(f, "{name} must not be empty"),
|
||||||
Self::RelativeOverride { variable, value } => {
|
Self::RelativeOverride { variable, value } => {
|
||||||
write!(f, "{variable} must be absolute, got {}", value.display())
|
write!(f, "{variable} must be absolute, got {}", value.display())
|
||||||
|
|
@ -81,7 +83,11 @@ impl IotaPaths {
|
||||||
let install_root = override_first(&["IOTA_INSTALL_ROOT"])?.unwrap_or(defaults.install_root);
|
let install_root = override_first(&["IOTA_INSTALL_ROOT"])?.unwrap_or(defaults.install_root);
|
||||||
let config_file = override_first(&["IOTA_CONFIG_FILE"])?
|
let config_file = override_first(&["IOTA_CONFIG_FILE"])?
|
||||||
.unwrap_or_else(|| config_dir.join("config.yaml"));
|
.unwrap_or_else(|| config_dir.join("config.yaml"));
|
||||||
let ipc_endpoint = resolve_ipc(scope, runtime_dir.as_deref(), defaults.ipc_endpoint)?;
|
let ipc_endpoint = resolve_ipc(scope, defaults.ipc_endpoint)?;
|
||||||
|
let runtime_dir = runtime_dir.or_else(|| match &ipc_endpoint {
|
||||||
|
IpcEndpoint::UnixSocket(path) => path.parent().map(Path::to_path_buf),
|
||||||
|
IpcEndpoint::WindowsPipe(_) => None,
|
||||||
|
});
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
scope,
|
scope,
|
||||||
config_dir,
|
config_dir,
|
||||||
|
|
@ -234,7 +240,7 @@ struct Defaults {
|
||||||
log_dir: PathBuf,
|
log_dir: PathBuf,
|
||||||
asset_dir: PathBuf,
|
asset_dir: PathBuf,
|
||||||
install_root: PathBuf,
|
install_root: PathBuf,
|
||||||
ipc_endpoint: IpcEndpoint,
|
ipc_endpoint: Option<IpcEndpoint>,
|
||||||
}
|
}
|
||||||
impl Defaults {
|
impl Defaults {
|
||||||
fn for_scope(scope: Scope) -> Result<Self, PathError> {
|
fn for_scope(scope: Scope) -> Result<Self, PathError> {
|
||||||
|
|
@ -250,7 +256,7 @@ impl Defaults {
|
||||||
log_dir: "/var/log/iota".into(),
|
log_dir: "/var/log/iota".into(),
|
||||||
asset_dir: "/usr/local/share/iota/web".into(),
|
asset_dir: "/usr/local/share/iota/web".into(),
|
||||||
install_root: "/usr/local/libexec/iota".into(),
|
install_root: "/usr/local/libexec/iota".into(),
|
||||||
ipc_endpoint: IpcEndpoint::UnixSocket("/run/iota/iota.sock".into()),
|
ipc_endpoint: Some(IpcEndpoint::UnixSocket("/run/iota/iota.sock".into())),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
|
@ -271,18 +277,15 @@ fn user_defaults() -> Result<Defaults, PathError> {
|
||||||
let state_base = xdg_or_home("XDG_STATE_HOME", &home, ".local/state")?;
|
let state_base = xdg_or_home("XDG_STATE_HOME", &home, ".local/state")?;
|
||||||
let cache_base = xdg_or_home("XDG_CACHE_HOME", &home, ".cache")?;
|
let cache_base = xdg_or_home("XDG_CACHE_HOME", &home, ".cache")?;
|
||||||
let data_base = xdg_or_home("XDG_DATA_HOME", &home, ".local/share")?;
|
let data_base = xdg_or_home("XDG_DATA_HOME", &home, ".local/share")?;
|
||||||
let runtime = absolute_env("XDG_RUNTIME_DIR")?
|
|
||||||
.ok_or(PathError::MissingPlatformDirectory("XDG_RUNTIME_DIR"))?
|
|
||||||
.join("iota");
|
|
||||||
Ok(Defaults {
|
Ok(Defaults {
|
||||||
config_dir: config_base.join("iota"),
|
config_dir: config_base.join("iota"),
|
||||||
state_dir: state_base.join("iota"),
|
state_dir: state_base.join("iota"),
|
||||||
cache_dir: cache_base.join("iota"),
|
cache_dir: cache_base.join("iota"),
|
||||||
runtime_dir: Some(runtime.clone()),
|
runtime_dir: None,
|
||||||
log_dir: state_base.join("iota/logs"),
|
log_dir: state_base.join("iota/logs"),
|
||||||
asset_dir: data_base.join("iota/web"),
|
asset_dir: data_base.join("iota/web"),
|
||||||
install_root: data_base.join("iota/bin"),
|
install_root: data_base.join("iota/bin"),
|
||||||
ipc_endpoint: IpcEndpoint::UnixSocket(runtime.join("iota.sock")),
|
ipc_endpoint: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
|
@ -301,7 +304,9 @@ fn user_defaults() -> Result<Defaults, PathError> {
|
||||||
log_dir: local.join("logs"),
|
log_dir: local.join("logs"),
|
||||||
asset_dir: local.join("data"),
|
asset_dir: local.join("data"),
|
||||||
install_root: local.join("bin"),
|
install_root: local.join("bin"),
|
||||||
ipc_endpoint: IpcEndpoint::WindowsPipe(r"\\.\pipe\Tensamin.Iota.User".into()),
|
ipc_endpoint: Some(IpcEndpoint::WindowsPipe(
|
||||||
|
r"\\.\pipe\Tensamin.Iota.User".into(),
|
||||||
|
)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -332,20 +337,14 @@ fn override_first(names: &[&'static str]) -> Result<Option<PathBuf>, PathError>
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
fn resolve_ipc(
|
fn resolve_ipc(scope: Scope, default: Option<IpcEndpoint>) -> Result<IpcEndpoint, PathError> {
|
||||||
scope: Scope,
|
|
||||||
runtime: Option<&Path>,
|
|
||||||
default: IpcEndpoint,
|
|
||||||
) -> Result<IpcEndpoint, PathError> {
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
if let Some(path) = absolute_env("IOTA_SOCKET")? {
|
if let Some(path) = absolute_env("IOTA_SOCKET")? {
|
||||||
return Ok(IpcEndpoint::UnixSocket(path));
|
return Ok(IpcEndpoint::UnixSocket(path));
|
||||||
}
|
}
|
||||||
if scope == Scope::User {
|
if scope == Scope::User {
|
||||||
return runtime
|
return Err(PathError::MissingRequiredOverride("IOTA_SOCKET"));
|
||||||
.map(|dir| IpcEndpoint::UnixSocket(dir.join("iota.sock")))
|
|
||||||
.ok_or(PathError::MissingPlatformDirectory("XDG_RUNTIME_DIR"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
|
@ -358,7 +357,7 @@ fn resolve_ipc(
|
||||||
return Ok(IpcEndpoint::WindowsPipe(name));
|
return Ok(IpcEndpoint::WindowsPipe(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(default)
|
default.ok_or(PathError::UnsupportedScope)
|
||||||
}
|
}
|
||||||
fn create_directory(path: &Path, private: bool) -> std::io::Result<()> {
|
fn create_directory(path: &Path, private: bool) -> std::io::Result<()> {
|
||||||
std::fs::create_dir_all(path)?;
|
std::fs::create_dir_all(path)?;
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,8 @@ mod systemd {
|
||||||
|
|
||||||
pub struct SystemdManager {
|
pub struct SystemdManager {
|
||||||
executor: Arc<dyn CommandExecutor>,
|
executor: Arc<dyn CommandExecutor>,
|
||||||
|
service: &'static str,
|
||||||
|
socket: &'static str,
|
||||||
}
|
}
|
||||||
impl SystemdManager {
|
impl SystemdManager {
|
||||||
pub async fn detect() -> Option<Self> {
|
pub async fn detect() -> Option<Self> {
|
||||||
|
|
@ -225,16 +227,29 @@ mod systemd {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let executor: Arc<dyn CommandExecutor> = Arc::new(RealExecutor);
|
let executor: Arc<dyn CommandExecutor> = Arc::new(RealExecutor);
|
||||||
executor
|
let mut manager = executor
|
||||||
.output("systemctl", &["--version", &COMMON[0], &COMMON[1]])
|
.output("systemctl", &["--version", &COMMON[0], &COMMON[1]])
|
||||||
.await
|
.await
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|r| r.success)
|
.filter(|r| r.success)
|
||||||
.map(|_| Self { executor })
|
.map(|_| Self {
|
||||||
|
executor,
|
||||||
|
service: SERVICE,
|
||||||
|
socket: SOCKET,
|
||||||
|
})?;
|
||||||
|
if manager.status("iota.service").await.is_ok() {
|
||||||
|
manager.service = "iota.service";
|
||||||
|
manager.socket = "iota.socket";
|
||||||
|
}
|
||||||
|
Some(manager)
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn with_executor(executor: Arc<dyn CommandExecutor>) -> Self {
|
pub fn with_executor(executor: Arc<dyn CommandExecutor>) -> Self {
|
||||||
Self { executor }
|
Self {
|
||||||
|
executor,
|
||||||
|
service: SERVICE,
|
||||||
|
socket: SOCKET,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async fn run(&self, action: &[&str]) -> Result<(), ProcessManagerError> {
|
async fn run(&self, action: &[&str]) -> Result<(), ProcessManagerError> {
|
||||||
let mut args = COMMON.to_vec();
|
let mut args = COMMON.to_vec();
|
||||||
|
|
@ -358,19 +373,25 @@ mod systemd {
|
||||||
async fn unit_status(&self, unit: &str) -> Result<UnitStatus, ProcessManagerError> {
|
async fn unit_status(&self, unit: &str) -> Result<UnitStatus, ProcessManagerError> {
|
||||||
self.status(unit).await
|
self.status(unit).await
|
||||||
}
|
}
|
||||||
|
async fn iota_startup_status(&self) -> Result<DaemonStartupStatus, ProcessManagerError> {
|
||||||
|
Ok(DaemonStartupStatus::classify(
|
||||||
|
self.status(self.service).await?,
|
||||||
|
self.status(self.socket).await?,
|
||||||
|
))
|
||||||
|
}
|
||||||
async fn set_iota_startup_mode(
|
async fn set_iota_startup_mode(
|
||||||
&self,
|
&self,
|
||||||
mode: StartupMode,
|
mode: StartupMode,
|
||||||
) -> Result<DaemonStartupStatus, ProcessManagerError> {
|
) -> Result<DaemonStartupStatus, ProcessManagerError> {
|
||||||
match mode {
|
match mode {
|
||||||
StartupMode::AlwaysOn => {
|
StartupMode::AlwaysOn => {
|
||||||
self.run(&["disable", SOCKET]).await?;
|
self.run(&["disable", self.socket]).await?;
|
||||||
self.run(&["enable", "--now", SERVICE]).await?;
|
self.run(&["enable", "--now", self.service]).await?;
|
||||||
self.verify(DetectedStartupMode::AlwaysOn).await
|
self.verify(DetectedStartupMode::AlwaysOn).await
|
||||||
}
|
}
|
||||||
StartupMode::SocketActivated => {
|
StartupMode::SocketActivated => {
|
||||||
self.run(&["disable", "--now", SERVICE]).await?;
|
self.run(&["disable", "--now", self.service]).await?;
|
||||||
self.run(&["enable", "--now", SOCKET]).await?;
|
self.run(&["enable", "--now", self.socket]).await?;
|
||||||
self.verify(DetectedStartupMode::SocketActivated).await
|
self.verify(DetectedStartupMode::SocketActivated).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -378,9 +399,21 @@ mod systemd {
|
||||||
async fn unit_action(&self, action: &[&str]) -> Result<(), ProcessManagerError> {
|
async fn unit_action(&self, action: &[&str]) -> Result<(), ProcessManagerError> {
|
||||||
self.run(action).await
|
self.run(action).await
|
||||||
}
|
}
|
||||||
|
async fn process_action(
|
||||||
|
&self,
|
||||||
|
action: ProcessAction,
|
||||||
|
) -> Result<DaemonStartupStatus, ProcessManagerError> {
|
||||||
|
let verb = match action {
|
||||||
|
ProcessAction::Start => "start",
|
||||||
|
ProcessAction::Stop => "stop",
|
||||||
|
ProcessAction::Restart => "restart",
|
||||||
|
};
|
||||||
|
self.run(&[verb, self.service]).await?;
|
||||||
|
self.iota_startup_status().await
|
||||||
|
}
|
||||||
async fn disable_startup(&self) -> Result<DaemonStartupStatus, ProcessManagerError> {
|
async fn disable_startup(&self) -> Result<DaemonStartupStatus, ProcessManagerError> {
|
||||||
self.run(&["disable", "--now", SERVICE]).await?;
|
self.run(&["disable", "--now", self.service]).await?;
|
||||||
self.run(&["disable", "--now", SOCKET]).await?;
|
self.run(&["disable", "--now", self.socket]).await?;
|
||||||
self.verify(DetectedStartupMode::Disabled).await
|
self.verify(DetectedStartupMode::Disabled).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -240,52 +240,4 @@ fn pending_forward_from_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<Pending
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn suffix(name: &str) -> String {
|
|
||||||
let unix = std::time::SystemTime::now()
|
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.as_nanos();
|
|
||||||
format!("{name}-{unix}")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn stores_and_retrieves_chat_secret_blob_for_owner_chat() {
|
|
||||||
let user_id = suffix("user");
|
|
||||||
let chat_id = suffix("chat");
|
|
||||||
|
|
||||||
put_chat_secret(StoredChatSecret {
|
|
||||||
user_id: user_id.clone(),
|
|
||||||
chat_id: chat_id.clone(),
|
|
||||||
secret_id: "main".to_string(),
|
|
||||||
version: 1,
|
|
||||||
encrypted_secret: vec![42, 43],
|
|
||||||
kem_ciphertext: vec![9, 8, 7],
|
|
||||||
wrapping_scheme: "mtp-kem-chacha20poly1305-hkdf-sha256-v1".to_string(),
|
|
||||||
created_at: 1,
|
|
||||||
updated_at: 2,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let found = get_chat_secret(ChatSecretQuery {
|
|
||||||
user_id: user_id.clone(),
|
|
||||||
chat_id: chat_id.clone(),
|
|
||||||
secret_id: Some("main".to_string()),
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(found.encrypted_secret, vec![42, 43]);
|
|
||||||
assert_eq!(found.kem_ciphertext, vec![9, 8, 7]);
|
|
||||||
|
|
||||||
let denied = get_chat_secret(ChatSecretQuery {
|
|
||||||
user_id: suffix("other-user"),
|
|
||||||
chat_id,
|
|
||||||
secret_id: Some("main".to_string()),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
assert!(denied.is_none());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue