mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
bugfixes for alpha.10 (#3032)
* bugfixes for alpha.10 * bump raspi kernel * rpi kernel bump * alpha.11
This commit is contained in:
2
container-runtime/package-lock.json
generated
2
container-runtime/package-lock.json
generated
@@ -38,7 +38,7 @@
|
|||||||
},
|
},
|
||||||
"../sdk/dist": {
|
"../sdk/dist": {
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.4.0-beta.40",
|
"version": "0.4.0-beta.41",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iarna/toml": "^3.0.0",
|
"@iarna/toml": "^3.0.0",
|
||||||
|
|||||||
9
core/Cargo.lock
generated
9
core/Cargo.lock
generated
@@ -4632,6 +4632,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
|
"typeid",
|
||||||
"yasi",
|
"yasi",
|
||||||
"zbus",
|
"zbus",
|
||||||
]
|
]
|
||||||
@@ -7265,7 +7266,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "start-os"
|
name = "start-os"
|
||||||
version = "0.4.0-alpha.10"
|
version = "0.4.0-alpha.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes 0.7.5",
|
"aes 0.7.5",
|
||||||
"arti-client",
|
"arti-client",
|
||||||
@@ -9249,6 +9250,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typeid"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.18.0"
|
version = "1.18.0"
|
||||||
|
|||||||
@@ -35,5 +35,6 @@ ts-rs = "9"
|
|||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tracing = "0.1.39"
|
tracing = "0.1.39"
|
||||||
|
typeid = "1"
|
||||||
yasi = { version = "0.1.6", features = ["serde", "ts-rs"] }
|
yasi = { version = "0.1.6", features = ["serde", "ts-rs"] }
|
||||||
zbus = "5"
|
zbus = "5"
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ impl Display for ErrorKind {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error {
|
pub struct Error {
|
||||||
pub source: color_eyre::eyre::Error,
|
pub source: color_eyre::eyre::Error,
|
||||||
|
pub debug: Option<color_eyre::eyre::Error>,
|
||||||
pub kind: ErrorKind,
|
pub kind: ErrorKind,
|
||||||
pub revision: Option<Revision>,
|
pub revision: Option<Revision>,
|
||||||
pub task: Option<JoinHandle<()>>,
|
pub task: Option<JoinHandle<()>>,
|
||||||
@@ -199,9 +200,15 @@ impl Display for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn new<E: Into<color_eyre::eyre::Error>>(source: E, kind: ErrorKind) -> Self {
|
pub fn new<E: Into<color_eyre::eyre::Error> + std::fmt::Debug + 'static>(
|
||||||
|
source: E,
|
||||||
|
kind: ErrorKind,
|
||||||
|
) -> Self {
|
||||||
|
let debug = (typeid::of::<E>() == typeid::of::<color_eyre::eyre::Error>())
|
||||||
|
.then(|| eyre!("{source:?}"));
|
||||||
Error {
|
Error {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
|
debug,
|
||||||
kind,
|
kind,
|
||||||
revision: None,
|
revision: None,
|
||||||
task: None,
|
task: None,
|
||||||
@@ -209,11 +216,8 @@ impl Error {
|
|||||||
}
|
}
|
||||||
pub fn clone_output(&self) -> Self {
|
pub fn clone_output(&self) -> Self {
|
||||||
Error {
|
Error {
|
||||||
source: ErrorData {
|
source: eyre!("{}", self.source),
|
||||||
details: format!("{}", self.source),
|
debug: self.debug.as_ref().map(|e| eyre!("{e}")),
|
||||||
debug: format!("{:?}", self.source),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
kind: self.kind,
|
kind: self.kind,
|
||||||
revision: self.revision.clone(),
|
revision: self.revision.clone(),
|
||||||
task: None,
|
task: None,
|
||||||
@@ -539,25 +543,24 @@ where
|
|||||||
impl<T, E> ResultExt<T, E> for Result<T, E>
|
impl<T, E> ResultExt<T, E> for Result<T, E>
|
||||||
where
|
where
|
||||||
color_eyre::eyre::Error: From<E>,
|
color_eyre::eyre::Error: From<E>,
|
||||||
|
E: std::fmt::Debug + 'static,
|
||||||
{
|
{
|
||||||
fn with_kind(self, kind: ErrorKind) -> Result<T, Error> {
|
fn with_kind(self, kind: ErrorKind) -> Result<T, Error> {
|
||||||
self.map_err(|e| Error {
|
self.map_err(|e| Error::new(e, kind))
|
||||||
source: e.into(),
|
|
||||||
kind,
|
|
||||||
revision: None,
|
|
||||||
task: None,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_ctx<F: FnOnce(&E) -> (ErrorKind, D), D: Display>(self, f: F) -> Result<T, Error> {
|
fn with_ctx<F: FnOnce(&E) -> (ErrorKind, D), D: Display>(self, f: F) -> Result<T, Error> {
|
||||||
self.map_err(|e| {
|
self.map_err(|e| {
|
||||||
let (kind, ctx) = f(&e);
|
let (kind, ctx) = f(&e);
|
||||||
|
let debug = (typeid::of::<E>() == typeid::of::<color_eyre::eyre::Error>())
|
||||||
|
.then(|| eyre!("{ctx}: {e:?}"));
|
||||||
let source = color_eyre::eyre::Error::from(e);
|
let source = color_eyre::eyre::Error::from(e);
|
||||||
let ctx = format!("{}: {}", ctx, source);
|
let with_ctx = format!("{ctx}: {source}");
|
||||||
let source = source.wrap_err(ctx);
|
let source = source.wrap_err(with_ctx);
|
||||||
Error {
|
Error {
|
||||||
kind,
|
kind,
|
||||||
source,
|
source,
|
||||||
|
debug,
|
||||||
revision: None,
|
revision: None,
|
||||||
task: None,
|
task: None,
|
||||||
}
|
}
|
||||||
@@ -578,25 +581,24 @@ where
|
|||||||
}
|
}
|
||||||
impl<T> ResultExt<T, Error> for Result<T, Error> {
|
impl<T> ResultExt<T, Error> for Result<T, Error> {
|
||||||
fn with_kind(self, kind: ErrorKind) -> Result<T, Error> {
|
fn with_kind(self, kind: ErrorKind) -> Result<T, Error> {
|
||||||
self.map_err(|e| Error {
|
self.map_err(|e| Error { kind, ..e })
|
||||||
source: e.source,
|
|
||||||
kind,
|
|
||||||
revision: e.revision,
|
|
||||||
task: e.task,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_ctx<F: FnOnce(&Error) -> (ErrorKind, D), D: Display>(self, f: F) -> Result<T, Error> {
|
fn with_ctx<F: FnOnce(&Error) -> (ErrorKind, D), D: Display>(self, f: F) -> Result<T, Error> {
|
||||||
self.map_err(|e| {
|
self.map_err(|e| {
|
||||||
let (kind, ctx) = f(&e);
|
let (kind, ctx) = f(&e);
|
||||||
let source = e.source;
|
let source = e.source;
|
||||||
let ctx = format!("{}: {}", ctx, source);
|
let with_ctx = format!("{ctx}: {source}");
|
||||||
let source = source.wrap_err(ctx);
|
let source = source.wrap_err(with_ctx);
|
||||||
|
let debug = e.debug.map(|e| {
|
||||||
|
let with_ctx = format!("{ctx}: {e}");
|
||||||
|
e.wrap_err(with_ctx)
|
||||||
|
});
|
||||||
Error {
|
Error {
|
||||||
kind,
|
kind,
|
||||||
source,
|
source,
|
||||||
revision: e.revision,
|
debug,
|
||||||
task: e.task,
|
..e
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ keywords = [
|
|||||||
name = "start-os"
|
name = "start-os"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/Start9Labs/start-os"
|
repository = "https://github.com/Start9Labs/start-os"
|
||||||
version = "0.4.0-alpha.10" # VERSION_BUMP
|
version = "0.4.0-alpha.11" # VERSION_BUMP
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
@@ -202,8 +202,10 @@ pub struct NetworkInfo {
|
|||||||
#[model = "Model<Self>"]
|
#[model = "Model<Self>"]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct DnsSettings {
|
pub struct DnsSettings {
|
||||||
pub dhcp_servers: Vec<SocketAddr>,
|
#[ts(type = "string[]")]
|
||||||
pub static_servers: Option<Vec<SocketAddr>>,
|
pub dhcp_servers: VecDeque<SocketAddr>,
|
||||||
|
#[ts(type = "string[] | null")]
|
||||||
|
pub static_servers: Option<VecDeque<SocketAddr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use tracing::instrument;
|
|||||||
|
|
||||||
use super::filesystem::{FileSystem, MountType, ReadOnly, ReadWrite};
|
use super::filesystem::{FileSystem, MountType, ReadOnly, ReadWrite};
|
||||||
use super::util::unmount;
|
use super::util::unmount;
|
||||||
use crate::Error;
|
|
||||||
use crate::util::{Invoke, Never};
|
use crate::util::{Invoke, Never};
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
pub const TMP_MOUNTPOINT: &'static str = "/media/startos/tmp";
|
pub const TMP_MOUNTPOINT: &'static str = "/media/startos/tmp";
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ impl MountGuard {
|
|||||||
}
|
}
|
||||||
pub async fn unmount(mut self, delete_mountpoint: bool) -> Result<(), Error> {
|
pub async fn unmount(mut self, delete_mountpoint: bool) -> Result<(), Error> {
|
||||||
if self.mounted {
|
if self.mounted {
|
||||||
unmount(&self.mountpoint, false).await?;
|
unmount(&self.mountpoint, !cfg!(feature = "unstable")).await?;
|
||||||
if delete_mountpoint {
|
if delete_mountpoint {
|
||||||
match tokio::fs::remove_dir(&self.mountpoint).await {
|
match tokio::fs::remove_dir(&self.mountpoint).await {
|
||||||
Err(e) if e.raw_os_error() == Some(39) => Ok(()), // directory not empty
|
Err(e) if e.raw_os_error() == Some(39) => Ok(()), // directory not empty
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
@@ -23,7 +23,9 @@ use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseI
|
|||||||
use hickory_server::ServerFuture;
|
use hickory_server::ServerFuture;
|
||||||
use imbl::OrdMap;
|
use imbl::OrdMap;
|
||||||
use imbl_value::InternedString;
|
use imbl_value::InternedString;
|
||||||
|
use itertools::Itertools;
|
||||||
use models::{GatewayId, OptionExt, PackageId};
|
use models::{GatewayId, OptionExt, PackageId};
|
||||||
|
use patch_db::json_ptr::JsonPointer;
|
||||||
use rpc_toolkit::{
|
use rpc_toolkit::{
|
||||||
from_fn_async, from_fn_blocking, Context, HandlerArgs, HandlerExt, ParentHandler,
|
from_fn_async, from_fn_blocking, Context, HandlerArgs, HandlerExt, ParentHandler,
|
||||||
};
|
};
|
||||||
@@ -36,6 +38,7 @@ use crate::db::model::public::NetworkInterfaceInfo;
|
|||||||
use crate::db::model::Database;
|
use crate::db::model::Database;
|
||||||
use crate::net::gateway::NetworkInterfaceWatcher;
|
use crate::net::gateway::NetworkInterfaceWatcher;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::util::actor::background::BackgroundJobQueue;
|
||||||
use crate::util::io::file_string_stream;
|
use crate::util::io::file_string_stream;
|
||||||
use crate::util::serde::{display_serializable, HandlerExtSerde};
|
use crate::util::serde::{display_serializable, HandlerExtSerde};
|
||||||
use crate::util::sync::{SyncRwLock, Watch};
|
use crate::util::sync::{SyncRwLock, Watch};
|
||||||
@@ -161,97 +164,146 @@ impl DnsClient {
|
|||||||
Self {
|
Self {
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
_thread: tokio::spawn(async move {
|
_thread: tokio::spawn(async move {
|
||||||
loop {
|
let (bg, mut runner) = BackgroundJobQueue::new();
|
||||||
if let Err::<(), Error>(e) = async {
|
runner
|
||||||
let mut stream = file_string_stream("/run/systemd/resolve/resolv.conf")
|
.run_while(async move {
|
||||||
.filter_map(|a| futures::future::ready(a.transpose()))
|
let dhcp_ns_db = db.clone();
|
||||||
.boxed();
|
bg.add_job(async move {
|
||||||
let mut conf: String = stream
|
loop {
|
||||||
.next()
|
if let Err(e) = async {
|
||||||
.await
|
let mut stream =
|
||||||
.or_not_found("/run/systemd/resolve/resolv.conf")??;
|
file_string_stream("/run/systemd/resolve/resolv.conf")
|
||||||
let mut prev_nameservers = Vec::new();
|
.filter_map(|a| futures::future::ready(a.transpose()))
|
||||||
let mut bg = BTreeMap::<SocketAddr, BoxFuture<_>>::new();
|
.boxed();
|
||||||
loop {
|
while let Some(conf) = stream.next().await {
|
||||||
let nameservers = conf
|
let conf: String = conf?;
|
||||||
.lines()
|
let mut nameservers = conf
|
||||||
.map(|l| l.trim())
|
.lines()
|
||||||
.filter_map(|l| l.strip_prefix("nameserver "))
|
.map(|l| l.trim())
|
||||||
.skip(2)
|
.filter_map(|l| l.strip_prefix("nameserver "))
|
||||||
.map(|n| {
|
.map(|n| {
|
||||||
n.parse::<SocketAddr>()
|
n.parse::<SocketAddr>().or_else(|_| {
|
||||||
.or_else(|_| n.parse::<IpAddr>().map(|a| (a, 53).into()))
|
n.parse::<IpAddr>().map(|a| (a, 53).into())
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
})
|
||||||
let static_nameservers = db
|
.collect::<Result<VecDeque<_>, _>>()?;
|
||||||
.mutate(|db| {
|
if nameservers
|
||||||
let dns = db
|
.front()
|
||||||
.as_public_mut()
|
.map_or(false, |addr| addr.ip().is_loopback())
|
||||||
.as_server_info_mut()
|
|
||||||
.as_network_mut()
|
|
||||||
.as_dns_mut();
|
|
||||||
dns.as_dhcp_servers_mut().ser(&nameservers)?;
|
|
||||||
dns.as_static_servers().de()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.result?;
|
|
||||||
let nameservers = static_nameservers.unwrap_or(nameservers);
|
|
||||||
if nameservers != prev_nameservers {
|
|
||||||
let mut existing: BTreeMap<_, _> =
|
|
||||||
client.peek(|c| c.iter().cloned().collect());
|
|
||||||
let mut new = Vec::with_capacity(nameservers.len());
|
|
||||||
for addr in &nameservers {
|
|
||||||
if let Some(existing) = existing.remove(addr) {
|
|
||||||
new.push((*addr, existing));
|
|
||||||
} else {
|
|
||||||
let client = if let Ok((client, bg_thread)) =
|
|
||||||
Client::connect(
|
|
||||||
UdpClientStream::builder(
|
|
||||||
*addr,
|
|
||||||
TokioRuntimeProvider::new(),
|
|
||||||
)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
bg.insert(*addr, bg_thread.boxed());
|
nameservers.pop_front();
|
||||||
client
|
}
|
||||||
} else {
|
if nameservers.front().map_or(false, |addr| {
|
||||||
let (stream, sender) = TcpClientStream::new(
|
addr.ip() == IpAddr::from([1, 1, 1, 1])
|
||||||
*addr,
|
}) {
|
||||||
None,
|
nameservers.pop_front();
|
||||||
Some(Duration::from_secs(30)),
|
}
|
||||||
TokioRuntimeProvider::new(),
|
dhcp_ns_db
|
||||||
);
|
.mutate(|db| {
|
||||||
let (client, bg_thread) =
|
let dns = db
|
||||||
Client::new(stream, sender, None)
|
.as_public_mut()
|
||||||
.await
|
.as_server_info_mut()
|
||||||
.with_kind(ErrorKind::Network)?;
|
.as_network_mut()
|
||||||
bg.insert(*addr, bg_thread.boxed());
|
.as_dns_mut();
|
||||||
client
|
dns.as_dhcp_servers_mut().ser(&nameservers)
|
||||||
};
|
})
|
||||||
new.push((*addr, client));
|
.await
|
||||||
|
.result?
|
||||||
}
|
}
|
||||||
|
Ok::<_, Error>(())
|
||||||
|
}
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::error!("{e}");
|
||||||
|
tracing::debug!("{e:?}");
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
}
|
}
|
||||||
bg.retain(|n, _| nameservers.iter().any(|a| a == n));
|
|
||||||
prev_nameservers = nameservers;
|
|
||||||
client.replace(new);
|
|
||||||
}
|
}
|
||||||
tokio::select! {
|
});
|
||||||
c = stream.next() => conf = c.or_not_found("/run/systemd/resolve/resolv.conf")??,
|
loop {
|
||||||
_ = futures::future::join(
|
if let Err::<(), Error>(e) = async {
|
||||||
futures::future::join_all(bg.values_mut()),
|
let mut static_changed = db
|
||||||
futures::future::pending::<()>(),
|
.subscribe(
|
||||||
) => (),
|
"/public/serverInfo/network/dns/staticServers"
|
||||||
|
.parse::<JsonPointer>()
|
||||||
|
.with_kind(ErrorKind::Database)?,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let mut prev_nameservers = VecDeque::new();
|
||||||
|
let mut bg = BTreeMap::<SocketAddr, BoxFuture<_>>::new();
|
||||||
|
loop {
|
||||||
|
let dns = db
|
||||||
|
.peek()
|
||||||
|
.await
|
||||||
|
.into_public()
|
||||||
|
.into_server_info()
|
||||||
|
.into_network()
|
||||||
|
.into_dns();
|
||||||
|
let nameservers = dns
|
||||||
|
.as_static_servers()
|
||||||
|
.transpose_ref()
|
||||||
|
.unwrap_or_else(|| dns.as_dhcp_servers())
|
||||||
|
.de()?;
|
||||||
|
if nameservers != prev_nameservers {
|
||||||
|
let mut existing: BTreeMap<_, _> =
|
||||||
|
client.peek(|c| c.iter().cloned().collect());
|
||||||
|
let mut new = Vec::with_capacity(nameservers.len());
|
||||||
|
for addr in &nameservers {
|
||||||
|
if let Some(existing) = existing.remove(addr) {
|
||||||
|
new.push((*addr, existing));
|
||||||
|
} else {
|
||||||
|
let client = if let Ok((client, bg_thread)) =
|
||||||
|
Client::connect(
|
||||||
|
UdpClientStream::builder(
|
||||||
|
*addr,
|
||||||
|
TokioRuntimeProvider::new(),
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
bg.insert(*addr, bg_thread.boxed());
|
||||||
|
client
|
||||||
|
} else {
|
||||||
|
let (stream, sender) = TcpClientStream::new(
|
||||||
|
*addr,
|
||||||
|
None,
|
||||||
|
Some(Duration::from_secs(30)),
|
||||||
|
TokioRuntimeProvider::new(),
|
||||||
|
);
|
||||||
|
let (client, bg_thread) =
|
||||||
|
Client::new(stream, sender, None)
|
||||||
|
.await
|
||||||
|
.with_kind(ErrorKind::Network)?;
|
||||||
|
bg.insert(*addr, bg_thread.boxed());
|
||||||
|
client
|
||||||
|
};
|
||||||
|
new.push((*addr, client));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bg.retain(|n, _| nameservers.iter().any(|a| a == n));
|
||||||
|
prev_nameservers = nameservers;
|
||||||
|
client.replace(new);
|
||||||
|
}
|
||||||
|
futures::future::select(
|
||||||
|
static_changed.recv().boxed(),
|
||||||
|
futures::future::join(
|
||||||
|
futures::future::join_all(bg.values_mut()),
|
||||||
|
futures::future::pending::<()>(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::error!("{e}");
|
||||||
|
tracing::debug!("{e:?}");
|
||||||
|
}
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
.await
|
.await;
|
||||||
{
|
|
||||||
tracing::error!("{e}");
|
|
||||||
tracing::debug!("{e:?}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,19 @@ use helpers::NonDetachingJoinHandle;
|
|||||||
use id_pool::IdPool;
|
use id_pool::IdPool;
|
||||||
use imbl::OrdMap;
|
use imbl::OrdMap;
|
||||||
use models::GatewayId;
|
use models::GatewayId;
|
||||||
|
use rpc_toolkit::{from_fn_async, Context, HandlerArgs, HandlerExt, ParentHandler};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
|
use crate::context::{CliContext, RpcContext};
|
||||||
use crate::db::model::public::NetworkInterfaceInfo;
|
use crate::db::model::public::NetworkInterfaceInfo;
|
||||||
use crate::net::gateway::{DynInterfaceFilter, InterfaceFilter};
|
use crate::net::gateway::{DynInterfaceFilter, InterfaceFilter, SecureFilter};
|
||||||
use crate::net::utils::ipv6_is_link_local;
|
use crate::net::utils::ipv6_is_link_local;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::util::Invoke;
|
use crate::util::serde::{display_serializable, HandlerExtSerde};
|
||||||
use crate::util::sync::Watch;
|
use crate::util::sync::Watch;
|
||||||
|
use crate::util::Invoke;
|
||||||
|
|
||||||
pub const START9_BRIDGE_IFACE: &str = "lxcbr0";
|
pub const START9_BRIDGE_IFACE: &str = "lxcbr0";
|
||||||
pub const FIRST_DYNAMIC_PRIVATE_PORT: u16 = 49152;
|
pub const FIRST_DYNAMIC_PRIVATE_PORT: u16 = 49152;
|
||||||
@@ -42,6 +45,42 @@ impl AvailablePorts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn forward_api<C: Context>() -> ParentHandler<C> {
|
||||||
|
ParentHandler::new().subcommand(
|
||||||
|
"dump-table",
|
||||||
|
from_fn_async(
|
||||||
|
|ctx: RpcContext| async move { ctx.net_controller.forward.dump_table().await },
|
||||||
|
)
|
||||||
|
.with_display_serializable()
|
||||||
|
.with_custom_display_fn(|HandlerArgs { params, .. }, res| {
|
||||||
|
use prettytable::*;
|
||||||
|
|
||||||
|
if let Some(format) = params.format {
|
||||||
|
return display_serializable(format, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.add_row(row![bc => "FROM", "TO", "FILTER / GATEWAY"]);
|
||||||
|
|
||||||
|
for (external, target) in res.0 {
|
||||||
|
table.add_row(row![external, target.target, target.filter]);
|
||||||
|
for (source, gateway) in target.gateways {
|
||||||
|
table.add_row(row![
|
||||||
|
format!("{}:{}", source, external),
|
||||||
|
target.target,
|
||||||
|
gateway
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.print_tty(false)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.with_call_remote::<CliContext>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
struct ForwardRequest {
|
struct ForwardRequest {
|
||||||
external: u16,
|
external: u16,
|
||||||
target: SocketAddr,
|
target: SocketAddr,
|
||||||
@@ -49,6 +88,7 @@ struct ForwardRequest {
|
|||||||
rc: Weak<()>,
|
rc: Weak<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct ForwardEntry {
|
struct ForwardEntry {
|
||||||
external: u16,
|
external: u16,
|
||||||
target: SocketAddr,
|
target: SocketAddr,
|
||||||
@@ -96,7 +136,7 @@ impl ForwardEntry {
|
|||||||
let mut keep = BTreeSet::<SocketAddr>::new();
|
let mut keep = BTreeSet::<SocketAddr>::new();
|
||||||
for (iface, info) in ip_info
|
for (iface, info) in ip_info
|
||||||
.iter()
|
.iter()
|
||||||
.chain([NetworkInterfaceInfo::loopback()])
|
// .chain([NetworkInterfaceInfo::loopback()])
|
||||||
.filter(|(id, info)| filter_ref.filter(*id, *info))
|
.filter(|(id, info)| filter_ref.filter(*id, *info))
|
||||||
{
|
{
|
||||||
if let Some(ip_info) = &info.ip_info {
|
if let Some(ip_info) = &info.ip_info {
|
||||||
@@ -155,10 +195,9 @@ impl ForwardEntry {
|
|||||||
*self = Self::new(external, target, rc);
|
*self = Self::new(external, target, rc);
|
||||||
self.update(ip_info, Some(filter)).await?;
|
self.update(ip_info, Some(filter)).await?;
|
||||||
} else {
|
} else {
|
||||||
if self.prev_filter != filter {
|
|
||||||
self.update(ip_info, Some(filter)).await?;
|
|
||||||
}
|
|
||||||
self.rc = rc;
|
self.rc = rc;
|
||||||
|
self.update(ip_info, Some(filter).filter(|f| f != &self.prev_filter))
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -174,7 +213,7 @@ impl Drop for ForwardEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
struct ForwardState {
|
struct ForwardState {
|
||||||
state: BTreeMap<u16, ForwardEntry>,
|
state: BTreeMap<u16, ForwardEntry>,
|
||||||
}
|
}
|
||||||
@@ -197,7 +236,7 @@ impl ForwardState {
|
|||||||
for entry in self.state.values_mut() {
|
for entry in self.state.values_mut() {
|
||||||
entry.update(ip_info, None).await?;
|
entry.update(ip_info, None).await?;
|
||||||
}
|
}
|
||||||
self.state.retain(|_, fwd| !fwd.forwards.is_empty());
|
self.state.retain(|_, fwd| fwd.rc.strong_count() > 0);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,28 +248,68 @@ fn err_has_exited<T>(_: T) -> Error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct ForwardTable(pub BTreeMap<u16, ForwardTarget>);
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct ForwardTarget {
|
||||||
|
pub target: SocketAddr,
|
||||||
|
pub filter: String,
|
||||||
|
pub gateways: BTreeMap<SocketAddr, GatewayId>,
|
||||||
|
}
|
||||||
|
impl From<&ForwardState> for ForwardTable {
|
||||||
|
fn from(value: &ForwardState) -> Self {
|
||||||
|
Self(
|
||||||
|
value
|
||||||
|
.state
|
||||||
|
.iter()
|
||||||
|
.map(|(external, entry)| {
|
||||||
|
(
|
||||||
|
*external,
|
||||||
|
ForwardTarget {
|
||||||
|
target: entry.target,
|
||||||
|
filter: format!("{:?}", entry.prev_filter),
|
||||||
|
gateways: entry.forwards.clone(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ForwardCommand {
|
||||||
|
Forward(ForwardRequest, oneshot::Sender<Result<(), Error>>),
|
||||||
|
Sync(oneshot::Sender<Result<(), Error>>),
|
||||||
|
DumpTable(oneshot::Sender<ForwardTable>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
assert_ne!(
|
||||||
|
false.into_dyn(),
|
||||||
|
SecureFilter { secure: false }.into_dyn().into_dyn()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub struct PortForwardController {
|
pub struct PortForwardController {
|
||||||
req: mpsc::UnboundedSender<(Option<ForwardRequest>, oneshot::Sender<Result<(), Error>>)>,
|
req: mpsc::UnboundedSender<ForwardCommand>,
|
||||||
_thread: NonDetachingJoinHandle<()>,
|
_thread: NonDetachingJoinHandle<()>,
|
||||||
}
|
}
|
||||||
impl PortForwardController {
|
impl PortForwardController {
|
||||||
pub fn new(mut ip_info: Watch<OrdMap<GatewayId, NetworkInterfaceInfo>>) -> Self {
|
pub fn new(mut ip_info: Watch<OrdMap<GatewayId, NetworkInterfaceInfo>>) -> Self {
|
||||||
let (req_send, mut req_recv) = mpsc::unbounded_channel::<(
|
let (req_send, mut req_recv) = mpsc::unbounded_channel::<ForwardCommand>();
|
||||||
Option<ForwardRequest>,
|
|
||||||
oneshot::Sender<Result<(), Error>>,
|
|
||||||
)>();
|
|
||||||
let thread = NonDetachingJoinHandle::from(tokio::spawn(async move {
|
let thread = NonDetachingJoinHandle::from(tokio::spawn(async move {
|
||||||
let mut state = ForwardState::default();
|
let mut state = ForwardState::default();
|
||||||
let mut interfaces = ip_info.read_and_mark_seen();
|
let mut interfaces = ip_info.read_and_mark_seen();
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
msg = req_recv.recv() => {
|
msg = req_recv.recv() => {
|
||||||
if let Some((msg, re)) = msg {
|
if let Some(cmd) = msg {
|
||||||
if let Some(req) = msg {
|
match cmd {
|
||||||
re.send(state.handle_request(req, &interfaces).await).ok();
|
ForwardCommand::Forward(req, re) => re.send(state.handle_request(req, &interfaces).await).ok(),
|
||||||
} else {
|
ForwardCommand::Sync(re) => re.send(state.sync(&interfaces).await).ok(),
|
||||||
re.send(state.sync(&interfaces).await).ok();
|
ForwardCommand::DumpTable(re) => re.send((&state).into()).ok(),
|
||||||
}
|
};
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -250,19 +329,19 @@ impl PortForwardController {
|
|||||||
pub async fn add(
|
pub async fn add(
|
||||||
&self,
|
&self,
|
||||||
external: u16,
|
external: u16,
|
||||||
filter: impl InterfaceFilter,
|
filter: DynInterfaceFilter,
|
||||||
target: SocketAddr,
|
target: SocketAddr,
|
||||||
) -> Result<Arc<()>, Error> {
|
) -> Result<Arc<()>, Error> {
|
||||||
let rc = Arc::new(());
|
let rc = Arc::new(());
|
||||||
let (send, recv) = oneshot::channel();
|
let (send, recv) = oneshot::channel();
|
||||||
self.req
|
self.req
|
||||||
.send((
|
.send(ForwardCommand::Forward(
|
||||||
Some(ForwardRequest {
|
ForwardRequest {
|
||||||
external,
|
external,
|
||||||
target,
|
target,
|
||||||
filter: filter.into_dyn(),
|
filter,
|
||||||
rc: Arc::downgrade(&rc),
|
rc: Arc::downgrade(&rc),
|
||||||
}),
|
},
|
||||||
send,
|
send,
|
||||||
))
|
))
|
||||||
.map_err(err_has_exited)?;
|
.map_err(err_has_exited)?;
|
||||||
@@ -271,13 +350,25 @@ impl PortForwardController {
|
|||||||
}
|
}
|
||||||
pub async fn gc(&self) -> Result<(), Error> {
|
pub async fn gc(&self) -> Result<(), Error> {
|
||||||
let (send, recv) = oneshot::channel();
|
let (send, recv) = oneshot::channel();
|
||||||
self.req.send((None, send)).map_err(err_has_exited)?;
|
self.req
|
||||||
|
.send(ForwardCommand::Sync(send))
|
||||||
|
.map_err(err_has_exited)?;
|
||||||
|
|
||||||
recv.await.map_err(err_has_exited)?
|
recv.await.map_err(err_has_exited)?
|
||||||
}
|
}
|
||||||
|
pub async fn dump_table(&self) -> Result<ForwardTable, Error> {
|
||||||
|
let (req, res) = oneshot::channel();
|
||||||
|
self.req
|
||||||
|
.send(ForwardCommand::DumpTable(req))
|
||||||
|
.map_err(err_has_exited)?;
|
||||||
|
res.await.map_err(err_has_exited)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn forward(interface: &str, source: SocketAddr, target: SocketAddr) -> Result<(), Error> {
|
async fn forward(interface: &str, source: SocketAddr, target: SocketAddr) -> Result<(), Error> {
|
||||||
|
if source.is_ipv6() {
|
||||||
|
return Ok(()); // TODO: socat? ip6tables?
|
||||||
|
}
|
||||||
Command::new("/usr/lib/startos/scripts/forward-port")
|
Command::new("/usr/lib/startos/scripts/forward-port")
|
||||||
.env("iiface", interface)
|
.env("iiface", interface)
|
||||||
.env("oiface", START9_BRIDGE_IFACE)
|
.env("oiface", START9_BRIDGE_IFACE)
|
||||||
@@ -291,6 +382,9 @@ async fn forward(interface: &str, source: SocketAddr, target: SocketAddr) -> Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn unforward(interface: &str, source: SocketAddr, target: SocketAddr) -> Result<(), Error> {
|
async fn unforward(interface: &str, source: SocketAddr, target: SocketAddr) -> Result<(), Error> {
|
||||||
|
if source.is_ipv6() {
|
||||||
|
return Ok(()); // TODO: socat? ip6tables?
|
||||||
|
}
|
||||||
Command::new("/usr/lib/startos/scripts/forward-port")
|
Command::new("/usr/lib/startos/scripts/forward-port")
|
||||||
.env("UNDO", "1")
|
.env("UNDO", "1")
|
||||||
.env("iiface", interface)
|
.env("iiface", interface)
|
||||||
|
|||||||
@@ -1329,6 +1329,9 @@ impl InterfaceFilter for DynInterfaceFilter {
|
|||||||
fn as_any(&self) -> &dyn Any {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self.0.as_any()
|
self.0.as_any()
|
||||||
}
|
}
|
||||||
|
fn into_dyn(self) -> DynInterfaceFilter {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl DynInterfaceFilter {
|
impl DynInterfaceFilter {
|
||||||
fn new<T: InterfaceFilter>(value: T) -> Self {
|
fn new<T: InterfaceFilter>(value: T) -> Self {
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ pub fn net_api<C: Context>() -> ParentHandler<C> {
|
|||||||
"dns",
|
"dns",
|
||||||
dns::dns_api::<C>().with_about("Manage and query DNS"),
|
dns::dns_api::<C>().with_about("Manage and query DNS"),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
"forward",
|
||||||
|
forward::forward_api::<C>().with_about("Manage port forwards"),
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
"gateway",
|
"gateway",
|
||||||
gateway::gateway_api::<C>().with_about("View and edit gateway configurations"),
|
gateway::gateway_api::<C>().with_about("View and edit gateway configurations"),
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ use std::fmt;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use clap::Parser;
|
|
||||||
use clap::builder::ValueParserFactory;
|
use clap::builder::ValueParserFactory;
|
||||||
|
use clap::Parser;
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use helpers::const_true;
|
use helpers::const_true;
|
||||||
use imbl_value::InternedString;
|
use imbl_value::InternedString;
|
||||||
use models::{FromStrParser, PackageId};
|
use models::{FromStrParser, PackageId};
|
||||||
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
|
use rpc_toolkit::{from_fn_async, Context, HandlerExt, ParentHandler};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
@@ -155,6 +155,16 @@ pub async fn remove(
|
|||||||
for id in ids {
|
for id in ids {
|
||||||
n.remove(&id)?;
|
n.remove(&id)?;
|
||||||
}
|
}
|
||||||
|
let mut unread = 0;
|
||||||
|
for (_, n) in n.as_entries()? {
|
||||||
|
if !n.as_seen().de()? {
|
||||||
|
unread += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.as_public_mut()
|
||||||
|
.as_server_info_mut()
|
||||||
|
.as_unread_notification_count_mut()
|
||||||
|
.ser(&unread)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@@ -179,6 +189,16 @@ pub async fn remove_before(
|
|||||||
for id in n.keys()?.range(..before) {
|
for id in n.keys()?.range(..before) {
|
||||||
n.remove(&id)?;
|
n.remove(&id)?;
|
||||||
}
|
}
|
||||||
|
let mut unread = 0;
|
||||||
|
for (_, n) in n.as_entries()? {
|
||||||
|
if !n.as_seen().de()? {
|
||||||
|
unread += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.as_public_mut()
|
||||||
|
.as_server_info_mut()
|
||||||
|
.as_unread_notification_count_mut()
|
||||||
|
.ser(&unread)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use futures::future::{BoxFuture, FusedFuture, abortable, pending};
|
use futures::future::{abortable, pending, BoxFuture, FusedFuture};
|
||||||
use futures::stream::{AbortHandle, Abortable, BoxStream};
|
use futures::stream::{AbortHandle, Abortable, BoxStream};
|
||||||
use futures::{Future, FutureExt, Stream, StreamExt};
|
use futures::{Future, FutureExt, Stream, StreamExt};
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
|
|||||||
@@ -1575,7 +1575,7 @@ pub fn file_string_stream(
|
|||||||
loop {
|
loop {
|
||||||
match stream.watches().add(
|
match stream.watches().add(
|
||||||
&path,
|
&path,
|
||||||
WatchMask::MODIFY | WatchMask::MOVE_SELF | WatchMask::MOVED_TO | WatchMask::DELETE_SELF,
|
WatchMask::MODIFY | WatchMask::CLOSE_WRITE | WatchMask::MOVE_SELF | WatchMask::MOVED_TO | WatchMask::DELETE_SELF,
|
||||||
) {
|
) {
|
||||||
Ok(_) => break,
|
Ok(_) => break,
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ use ::serde::{Deserialize, Serialize};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use color_eyre::eyre::{self, eyre};
|
use color_eyre::eyre::{self, eyre};
|
||||||
use fd_lock_rs::FdLock;
|
use fd_lock_rs::FdLock;
|
||||||
use futures::FutureExt;
|
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
pub use helpers::NonDetachingJoinHandle;
|
use futures::FutureExt;
|
||||||
use helpers::canonicalize;
|
use helpers::canonicalize;
|
||||||
|
pub use helpers::NonDetachingJoinHandle;
|
||||||
use imbl_value::InternedString;
|
use imbl_value::InternedString;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
pub use models::VersionString;
|
pub use models::VersionString;
|
||||||
@@ -25,7 +25,7 @@ use pin_project::pin_project;
|
|||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt, BufReader};
|
use tokio::io::{AsyncRead, AsyncReadExt, BufReader};
|
||||||
use tokio::sync::{Mutex, OwnedMutexGuard, RwLock, oneshot};
|
use tokio::sync::{oneshot, Mutex, OwnedMutexGuard, RwLock};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@@ -197,17 +197,17 @@ impl<'a> Invoke<'a> for ExtendedCommand<'a> {
|
|||||||
}
|
}
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error> {
|
async fn invoke(&mut self, error_kind: crate::ErrorKind) -> Result<Vec<u8>, Error> {
|
||||||
let cmd_str = self
|
|
||||||
.cmd
|
|
||||||
.as_std()
|
|
||||||
.get_program()
|
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned();
|
|
||||||
self.cmd.kill_on_drop(true);
|
self.cmd.kill_on_drop(true);
|
||||||
if self.input.is_some() {
|
if self.input.is_some() {
|
||||||
self.cmd.stdin(Stdio::piped());
|
self.cmd.stdin(Stdio::piped());
|
||||||
}
|
}
|
||||||
if self.pipe.is_empty() {
|
if self.pipe.is_empty() {
|
||||||
|
let cmd_str = self
|
||||||
|
.cmd
|
||||||
|
.as_std()
|
||||||
|
.get_program()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
if self.capture {
|
if self.capture {
|
||||||
self.cmd.stdout(Stdio::piped());
|
self.cmd.stdout(Stdio::piped());
|
||||||
self.cmd.stderr(Stdio::piped());
|
self.cmd.stderr(Stdio::piped());
|
||||||
@@ -256,6 +256,7 @@ impl<'a> Invoke<'a> for ExtendedCommand<'a> {
|
|||||||
.take()
|
.take()
|
||||||
.map(|i| Box::new(i) as Box<dyn AsyncRead + Unpin + Send>);
|
.map(|i| Box::new(i) as Box<dyn AsyncRead + Unpin + Send>);
|
||||||
for (idx, cmd) in IntoIterator::into_iter(cmds).enumerate() {
|
for (idx, cmd) in IntoIterator::into_iter(cmds).enumerate() {
|
||||||
|
let cmd_str = cmd.as_std().get_program().to_string_lossy().into_owned();
|
||||||
let last = idx == len - 1;
|
let last = idx == len - 1;
|
||||||
if self.capture || !last {
|
if self.capture || !last {
|
||||||
cmd.stdout(Stdio::piped());
|
cmd.stdout(Stdio::piped());
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ use std::panic::{RefUnwindSafe, UnwindSafe};
|
|||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
use futures::{Future, FutureExt};
|
use futures::{Future, FutureExt};
|
||||||
use imbl_value::{InternedString, to_value};
|
use imbl_value::{to_value, InternedString};
|
||||||
use patch_db::json_ptr::ROOT;
|
use patch_db::json_ptr::ROOT;
|
||||||
|
|
||||||
use crate::Error;
|
|
||||||
use crate::context::RpcContext;
|
use crate::context::RpcContext;
|
||||||
use crate::db::model::Database;
|
use crate::db::model::Database;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::progress::PhaseProgressTrackerHandle;
|
use crate::progress::PhaseProgressTrackerHandle;
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
mod v0_3_5;
|
mod v0_3_5;
|
||||||
mod v0_3_5_1;
|
mod v0_3_5_1;
|
||||||
@@ -50,8 +50,9 @@ mod v0_4_0_alpha_8;
|
|||||||
mod v0_4_0_alpha_9;
|
mod v0_4_0_alpha_9;
|
||||||
|
|
||||||
mod v0_4_0_alpha_10;
|
mod v0_4_0_alpha_10;
|
||||||
|
mod v0_4_0_alpha_11;
|
||||||
|
|
||||||
pub type Current = v0_4_0_alpha_10::Version; // VERSION_BUMP
|
pub type Current = v0_4_0_alpha_11::Version; // VERSION_BUMP
|
||||||
|
|
||||||
impl Current {
|
impl Current {
|
||||||
#[instrument(skip(self, db))]
|
#[instrument(skip(self, db))]
|
||||||
@@ -164,7 +165,8 @@ enum Version {
|
|||||||
V0_4_0_alpha_7(Wrapper<v0_4_0_alpha_7::Version>),
|
V0_4_0_alpha_7(Wrapper<v0_4_0_alpha_7::Version>),
|
||||||
V0_4_0_alpha_8(Wrapper<v0_4_0_alpha_8::Version>),
|
V0_4_0_alpha_8(Wrapper<v0_4_0_alpha_8::Version>),
|
||||||
V0_4_0_alpha_9(Wrapper<v0_4_0_alpha_9::Version>),
|
V0_4_0_alpha_9(Wrapper<v0_4_0_alpha_9::Version>),
|
||||||
V0_4_0_alpha_10(Wrapper<v0_4_0_alpha_10::Version>), // VERSION_BUMP
|
V0_4_0_alpha_10(Wrapper<v0_4_0_alpha_10::Version>),
|
||||||
|
V0_4_0_alpha_11(Wrapper<v0_4_0_alpha_11::Version>), // VERSION_BUMP
|
||||||
Other(exver::Version),
|
Other(exver::Version),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +219,8 @@ impl Version {
|
|||||||
Self::V0_4_0_alpha_7(v) => DynVersion(Box::new(v.0)),
|
Self::V0_4_0_alpha_7(v) => DynVersion(Box::new(v.0)),
|
||||||
Self::V0_4_0_alpha_8(v) => DynVersion(Box::new(v.0)),
|
Self::V0_4_0_alpha_8(v) => DynVersion(Box::new(v.0)),
|
||||||
Self::V0_4_0_alpha_9(v) => DynVersion(Box::new(v.0)),
|
Self::V0_4_0_alpha_9(v) => DynVersion(Box::new(v.0)),
|
||||||
Self::V0_4_0_alpha_10(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
Self::V0_4_0_alpha_10(v) => DynVersion(Box::new(v.0)),
|
||||||
|
Self::V0_4_0_alpha_11(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
||||||
Self::Other(v) => {
|
Self::Other(v) => {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
eyre!("unknown version {v}"),
|
eyre!("unknown version {v}"),
|
||||||
@@ -262,7 +265,8 @@ impl Version {
|
|||||||
Version::V0_4_0_alpha_7(Wrapper(x)) => x.semver(),
|
Version::V0_4_0_alpha_7(Wrapper(x)) => x.semver(),
|
||||||
Version::V0_4_0_alpha_8(Wrapper(x)) => x.semver(),
|
Version::V0_4_0_alpha_8(Wrapper(x)) => x.semver(),
|
||||||
Version::V0_4_0_alpha_9(Wrapper(x)) => x.semver(),
|
Version::V0_4_0_alpha_9(Wrapper(x)) => x.semver(),
|
||||||
Version::V0_4_0_alpha_10(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
Version::V0_4_0_alpha_10(Wrapper(x)) => x.semver(),
|
||||||
|
Version::V0_4_0_alpha_11(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
||||||
Version::Other(x) => x.clone(),
|
Version::Other(x) => x.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
core/startos/src/version/v0_4_0_alpha_11.rs
Normal file
37
core/startos/src/version/v0_4_0_alpha_11.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use exver::{PreReleaseSegment, VersionRange};
|
||||||
|
|
||||||
|
use super::v0_3_5::V0_3_0_COMPAT;
|
||||||
|
use super::{v0_4_0_alpha_10, VersionT};
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref V0_4_0_alpha_11: exver::Version = exver::Version::new(
|
||||||
|
[0, 4, 0],
|
||||||
|
[PreReleaseSegment::String("alpha".into()), 11.into()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct Version;
|
||||||
|
|
||||||
|
impl VersionT for Version {
|
||||||
|
type Previous = v0_4_0_alpha_10::Version;
|
||||||
|
type PreUpRes = ();
|
||||||
|
|
||||||
|
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn semver(self) -> exver::Version {
|
||||||
|
V0_4_0_alpha_11.clone()
|
||||||
|
}
|
||||||
|
fn compat(self) -> &'static VersionRange {
|
||||||
|
&V0_3_0_COMPAT
|
||||||
|
}
|
||||||
|
#[instrument]
|
||||||
|
fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<Value, Error> {
|
||||||
|
Ok(Value::Null)
|
||||||
|
}
|
||||||
|
fn down(self, _db: &mut Value) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@ PLATFORM_CONFIG_EXTRAS=()
|
|||||||
if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then
|
if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then
|
||||||
PLATFORM_CONFIG_EXTRAS+=( --firmware-binary false )
|
PLATFORM_CONFIG_EXTRAS+=( --firmware-binary false )
|
||||||
PLATFORM_CONFIG_EXTRAS+=( --firmware-chroot false )
|
PLATFORM_CONFIG_EXTRAS+=( --firmware-chroot false )
|
||||||
PLATFORM_CONFIG_EXTRAS+=( --linux-packages linux-image-6.12.20+rpt )
|
PLATFORM_CONFIG_EXTRAS+=( --linux-packages linux-image-6.12.47+rpt )
|
||||||
PLATFORM_CONFIG_EXTRAS+=( --linux-flavours "rpi-v8 rpi-2712" )
|
PLATFORM_CONFIG_EXTRAS+=( --linux-flavours "rpi-v8 rpi-2712" )
|
||||||
elif [ "${IB_TARGET_PLATFORM}" = "rockchip64" ]; then
|
elif [ "${IB_TARGET_PLATFORM}" = "rockchip64" ]; then
|
||||||
PLATFORM_CONFIG_EXTRAS+=( --linux-flavours rockchip64 )
|
PLATFORM_CONFIG_EXTRAS+=( --linux-flavours rockchip64 )
|
||||||
@@ -204,8 +204,8 @@ if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then
|
|||||||
echo "Configuring raspi kernel '\$v'"
|
echo "Configuring raspi kernel '\$v'"
|
||||||
extract-ikconfig "/usr/lib/modules/\$v/kernel/kernel/configs.ko.xz" > /boot/config-\$v
|
extract-ikconfig "/usr/lib/modules/\$v/kernel/kernel/configs.ko.xz" > /boot/config-\$v
|
||||||
done
|
done
|
||||||
mkinitramfs -c gzip -o /boot/initramfs8 6.12.25-v8+
|
mkinitramfs -c gzip -o /boot/initramfs8 6.12.47-v8+
|
||||||
mkinitramfs -c gzip -o /boot/initramfs_2712 6.12.25-v8-16k+
|
mkinitramfs -c gzip -o /boot/initramfs_2712 6.12.47-v8-16k+
|
||||||
fi
|
fi
|
||||||
|
|
||||||
useradd --shell /bin/bash -G startos -m start9
|
useradd --shell /bin/bash -G startos -m start9
|
||||||
@@ -231,7 +231,6 @@ lb chroot
|
|||||||
lb installer
|
lb installer
|
||||||
lb binary_chroot
|
lb binary_chroot
|
||||||
lb chroot_prep install all mode-apt-install-binary mode-archives-chroot
|
lb chroot_prep install all mode-apt-install-binary mode-archives-chroot
|
||||||
ln -sf /run/systemd/resolve/stub-resolv.conf chroot/chroot/etc/resolv.conf
|
|
||||||
lb binary_rootfs
|
lb binary_rootfs
|
||||||
|
|
||||||
cp $prep_results_dir/binary/live/filesystem.squashfs $RESULTS_DIR/$IMAGE_BASENAME.squashfs
|
cp $prep_results_dir/binary/live/filesystem.squashfs $RESULTS_DIR/$IMAGE_BASENAME.squashfs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
export type DnsSettings = {
|
export type DnsSettings = {
|
||||||
dhcpServers: Array<string>
|
dhcpServers: string[]
|
||||||
staticServers: Array<string> | null
|
staticServers: string[] | null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ import {
|
|||||||
} from "../../base/lib/inits"
|
} from "../../base/lib/inits"
|
||||||
import { DropGenerator } from "../../base/lib/util/Drop"
|
import { DropGenerator } from "../../base/lib/util/Drop"
|
||||||
|
|
||||||
export const OSVersion = testTypeVersion("0.4.0-alpha.10")
|
export const OSVersion = testTypeVersion("0.4.0-alpha.11")
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
type AnyNeverCond<T extends any[], Then, Else> =
|
type AnyNeverCond<T extends any[], Then, Else> =
|
||||||
|
|||||||
@@ -164,7 +164,6 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
|||||||
readonly started:
|
readonly started:
|
||||||
| ((onTerm: () => PromiseLike<void>) => PromiseLike<null>)
|
| ((onTerm: () => PromiseLike<void>) => PromiseLike<null>)
|
||||||
| null,
|
| null,
|
||||||
readonly daemons: Promise<Daemon<Manifest>>[],
|
|
||||||
readonly ids: Ids[],
|
readonly ids: Ids[],
|
||||||
readonly healthDaemons: HealthDaemon<Manifest>[],
|
readonly healthDaemons: HealthDaemon<Manifest>[],
|
||||||
) {}
|
) {}
|
||||||
@@ -193,7 +192,6 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
|||||||
options.started,
|
options.started,
|
||||||
[],
|
[],
|
||||||
[],
|
[],
|
||||||
[],
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,13 +213,11 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
|||||||
ready,
|
ready,
|
||||||
this.effects,
|
this.effects,
|
||||||
)
|
)
|
||||||
const daemons = daemon ? [...this.daemons, daemon] : [...this.daemons]
|
|
||||||
const ids = [...this.ids, id] as (Ids | Id)[]
|
const ids = [...this.ids, id] as (Ids | Id)[]
|
||||||
const healthDaemons = [...this.healthDaemons, healthDaemon]
|
const healthDaemons = [...this.healthDaemons, healthDaemon]
|
||||||
return new Daemons<Manifest, Ids | Id>(
|
return new Daemons<Manifest, Ids | Id>(
|
||||||
this.effects,
|
this.effects,
|
||||||
this.started,
|
this.started,
|
||||||
daemons,
|
|
||||||
ids,
|
ids,
|
||||||
healthDaemons,
|
healthDaemons,
|
||||||
)
|
)
|
||||||
@@ -358,7 +354,7 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
|||||||
const prev = this
|
const prev = this
|
||||||
const res = (options: AddHealthCheckParams<Ids, Id> | null) => {
|
const res = (options: AddHealthCheckParams<Ids, Id> | null) => {
|
||||||
if (!options) return prev
|
if (!options) return prev
|
||||||
return prev.addDaemonImpl(id, null, options.requires, EXIT_SUCCESS)
|
return prev.addDaemonImpl(id, null, options.requires, options.ready)
|
||||||
}
|
}
|
||||||
if (options instanceof Function) {
|
if (options instanceof Function) {
|
||||||
const opts = options()
|
const opts = options()
|
||||||
@@ -404,7 +400,6 @@ export class Daemons<Manifest extends T.SDKManifest, Ids extends string>
|
|||||||
const daemons = await new Daemons<Manifest, Ids>(
|
const daemons = await new Daemons<Manifest, Ids>(
|
||||||
this.effects,
|
this.effects,
|
||||||
this.started,
|
this.started,
|
||||||
[...this.daemons, daemon],
|
|
||||||
this.ids,
|
this.ids,
|
||||||
[...this.healthDaemons, healthDaemon],
|
[...this.healthDaemons, healthDaemon],
|
||||||
).build()
|
).build()
|
||||||
|
|||||||
4
sdk/package/package-lock.json
generated
4
sdk/package/package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.4.0-beta.40",
|
"version": "0.4.0-beta.41",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.4.0-beta.40",
|
"version": "0.4.0-beta.41",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iarna/toml": "^3.0.0",
|
"@iarna/toml": "^3.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@start9labs/start-sdk",
|
"name": "@start9labs/start-sdk",
|
||||||
"version": "0.4.0-beta.40",
|
"version": "0.4.0-beta.41",
|
||||||
"description": "Software development kit to facilitate packaging services for StartOS",
|
"description": "Software development kit to facilitate packaging services for StartOS",
|
||||||
"main": "./package/lib/index.js",
|
"main": "./package/lib/index.js",
|
||||||
"types": "./package/lib/index.d.ts",
|
"types": "./package/lib/index.d.ts",
|
||||||
|
|||||||
8693
web/package-lock.json
generated
8693
web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "startos-ui",
|
"name": "startos-ui",
|
||||||
"version": "0.4.0-alpha.10",
|
"version": "0.4.0-alpha.11",
|
||||||
"author": "Start9 Labs, Inc",
|
"author": "Start9 Labs, Inc",
|
||||||
"homepage": "https://start9.com/",
|
"homepage": "https://start9.com/",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export namespace Mock {
|
|||||||
squashfs: {
|
squashfs: {
|
||||||
aarch64: {
|
aarch64: {
|
||||||
publishedAt: '2025-04-21T20:58:48.140749883Z',
|
publishedAt: '2025-04-21T20:58:48.140749883Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_aarch64.squashfs',
|
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.11/startos-0.4.0-alpha.11-33ae46f~dev_aarch64.squashfs',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: '4elBFVkd/r8hNadKmKtLIs42CoPltMvKe2z3LRqkphk=',
|
hash: '4elBFVkd/r8hNadKmKtLIs42CoPltMvKe2z3LRqkphk=',
|
||||||
size: 1343500288,
|
size: 1343500288,
|
||||||
@@ -122,7 +122,7 @@ export namespace Mock {
|
|||||||
},
|
},
|
||||||
'aarch64-nonfree': {
|
'aarch64-nonfree': {
|
||||||
publishedAt: '2025-04-21T21:07:00.249285116Z',
|
publishedAt: '2025-04-21T21:07:00.249285116Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_aarch64-nonfree.squashfs',
|
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.11/startos-0.4.0-alpha.11-33ae46f~dev_aarch64-nonfree.squashfs',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: 'MrCEi4jxbmPS7zAiGk/JSKlMsiuKqQy6RbYOxlGHOIQ=',
|
hash: 'MrCEi4jxbmPS7zAiGk/JSKlMsiuKqQy6RbYOxlGHOIQ=',
|
||||||
size: 1653075968,
|
size: 1653075968,
|
||||||
@@ -134,7 +134,7 @@ export namespace Mock {
|
|||||||
},
|
},
|
||||||
raspberrypi: {
|
raspberrypi: {
|
||||||
publishedAt: '2025-04-21T21:16:12.933319237Z',
|
publishedAt: '2025-04-21T21:16:12.933319237Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_raspberrypi.squashfs',
|
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.11/startos-0.4.0-alpha.11-33ae46f~dev_raspberrypi.squashfs',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: '/XTVQRCqY3RK544PgitlKu7UplXjkmzWoXUh2E4HCw0=',
|
hash: '/XTVQRCqY3RK544PgitlKu7UplXjkmzWoXUh2E4HCw0=',
|
||||||
size: 1490731008,
|
size: 1490731008,
|
||||||
@@ -146,7 +146,7 @@ export namespace Mock {
|
|||||||
},
|
},
|
||||||
x86_64: {
|
x86_64: {
|
||||||
publishedAt: '2025-04-21T21:14:20.246908903Z',
|
publishedAt: '2025-04-21T21:14:20.246908903Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_x86_64.squashfs',
|
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.11/startos-0.4.0-alpha.11-33ae46f~dev_x86_64.squashfs',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: '/6romKTVQGSaOU7FqSZdw0kFyd7P+NBSYNwM3q7Fe44=',
|
hash: '/6romKTVQGSaOU7FqSZdw0kFyd7P+NBSYNwM3q7Fe44=',
|
||||||
size: 1411657728,
|
size: 1411657728,
|
||||||
@@ -158,7 +158,7 @@ export namespace Mock {
|
|||||||
},
|
},
|
||||||
'x86_64-nonfree': {
|
'x86_64-nonfree': {
|
||||||
publishedAt: '2025-04-21T21:15:17.955265284Z',
|
publishedAt: '2025-04-21T21:15:17.955265284Z',
|
||||||
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.10/startos-0.4.0-alpha.10-33ae46f~dev_x86_64-nonfree.squashfs',
|
url: 'https://alpha-registry-x.start9.com/startos/v0.4.0-alpha.11/startos-0.4.0-alpha.11-33ae46f~dev_x86_64-nonfree.squashfs',
|
||||||
commitment: {
|
commitment: {
|
||||||
hash: 'HCRq9sr/0t85pMdrEgNBeM4x11zVKHszGnD1GDyZbSE=',
|
hash: 'HCRq9sr/0t85pMdrEgNBeM4x11zVKHszGnD1GDyZbSE=',
|
||||||
size: 1731035136,
|
size: 1731035136,
|
||||||
@@ -385,7 +385,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoin.org',
|
docsUrl: 'https://bitcoin.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -420,7 +420,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoinknots.org',
|
docsUrl: 'https://bitcoinknots.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -465,7 +465,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoin.org',
|
docsUrl: 'https://bitcoin.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -500,7 +500,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoinknots.org',
|
docsUrl: 'https://bitcoinknots.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -547,7 +547,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://lightning.engineering/',
|
docsUrl: 'https://lightning.engineering/',
|
||||||
releaseNotes: 'Upstream release to 0.17.5',
|
releaseNotes: 'Upstream release to 0.17.5',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: LND_ICON,
|
icon: LND_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -595,7 +595,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://lightning.engineering/',
|
docsUrl: 'https://lightning.engineering/',
|
||||||
releaseNotes: 'Upstream release to 0.17.4',
|
releaseNotes: 'Upstream release to 0.17.4',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: LND_ICON,
|
icon: LND_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -647,7 +647,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoin.org',
|
docsUrl: 'https://bitcoin.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -682,7 +682,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://bitcoinknots.org',
|
docsUrl: 'https://bitcoinknots.org',
|
||||||
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
releaseNotes: 'Even better support for Bitcoin and wallets!',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: BTC_ICON,
|
icon: BTC_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -727,7 +727,7 @@ export namespace Mock {
|
|||||||
docsUrl: 'https://lightning.engineering/',
|
docsUrl: 'https://lightning.engineering/',
|
||||||
releaseNotes: 'Upstream release and minor fixes.',
|
releaseNotes: 'Upstream release and minor fixes.',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: LND_ICON,
|
icon: LND_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
@@ -775,7 +775,7 @@ export namespace Mock {
|
|||||||
marketingSite: '',
|
marketingSite: '',
|
||||||
releaseNotes: 'Upstream release and minor fixes.',
|
releaseNotes: 'Upstream release and minor fixes.',
|
||||||
osVersion: '0.3.6',
|
osVersion: '0.3.6',
|
||||||
sdkVersion: '0.4.0-beta.40',
|
sdkVersion: '0.4.0-beta.41',
|
||||||
gitHash: 'fakehash',
|
gitHash: 'fakehash',
|
||||||
icon: PROXY_ICON,
|
icon: PROXY_ICON,
|
||||||
sourceVersion: null,
|
sourceVersion: null,
|
||||||
|
|||||||
Reference in New Issue
Block a user