mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 04:01:58 +00:00
bugfixes for alpha.10 (#3032)
* bugfixes for alpha.10 * bump raspi kernel * rpi kernel bump * alpha.11
This commit is contained in:
@@ -14,7 +14,7 @@ keywords = [
|
||||
name = "start-os"
|
||||
readme = "README.md"
|
||||
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"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
@@ -202,8 +202,10 @@ pub struct NetworkInfo {
|
||||
#[model = "Model<Self>"]
|
||||
#[ts(export)]
|
||||
pub struct DnsSettings {
|
||||
pub dhcp_servers: Vec<SocketAddr>,
|
||||
pub static_servers: Option<Vec<SocketAddr>>,
|
||||
#[ts(type = "string[]")]
|
||||
pub dhcp_servers: VecDeque<SocketAddr>,
|
||||
#[ts(type = "string[] | null")]
|
||||
pub static_servers: Option<VecDeque<SocketAddr>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, HasModel, TS)]
|
||||
|
||||
@@ -10,8 +10,8 @@ use tracing::instrument;
|
||||
|
||||
use super::filesystem::{FileSystem, MountType, ReadOnly, ReadWrite};
|
||||
use super::util::unmount;
|
||||
use crate::Error;
|
||||
use crate::util::{Invoke, Never};
|
||||
use crate::Error;
|
||||
|
||||
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> {
|
||||
if self.mounted {
|
||||
unmount(&self.mountpoint, false).await?;
|
||||
unmount(&self.mountpoint, !cfg!(feature = "unstable")).await?;
|
||||
if delete_mountpoint {
|
||||
match tokio::fs::remove_dir(&self.mountpoint).await {
|
||||
Err(e) if e.raw_os_error() == Some(39) => Ok(()), // directory not empty
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Weak};
|
||||
@@ -23,7 +23,9 @@ use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseI
|
||||
use hickory_server::ServerFuture;
|
||||
use imbl::OrdMap;
|
||||
use imbl_value::InternedString;
|
||||
use itertools::Itertools;
|
||||
use models::{GatewayId, OptionExt, PackageId};
|
||||
use patch_db::json_ptr::JsonPointer;
|
||||
use rpc_toolkit::{
|
||||
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::net::gateway::NetworkInterfaceWatcher;
|
||||
use crate::prelude::*;
|
||||
use crate::util::actor::background::BackgroundJobQueue;
|
||||
use crate::util::io::file_string_stream;
|
||||
use crate::util::serde::{display_serializable, HandlerExtSerde};
|
||||
use crate::util::sync::{SyncRwLock, Watch};
|
||||
@@ -161,97 +164,146 @@ impl DnsClient {
|
||||
Self {
|
||||
client: client.clone(),
|
||||
_thread: tokio::spawn(async move {
|
||||
loop {
|
||||
if let Err::<(), Error>(e) = async {
|
||||
let mut stream = file_string_stream("/run/systemd/resolve/resolv.conf")
|
||||
.filter_map(|a| futures::future::ready(a.transpose()))
|
||||
.boxed();
|
||||
let mut conf: String = stream
|
||||
.next()
|
||||
.await
|
||||
.or_not_found("/run/systemd/resolve/resolv.conf")??;
|
||||
let mut prev_nameservers = Vec::new();
|
||||
let mut bg = BTreeMap::<SocketAddr, BoxFuture<_>>::new();
|
||||
loop {
|
||||
let nameservers = conf
|
||||
.lines()
|
||||
.map(|l| l.trim())
|
||||
.filter_map(|l| l.strip_prefix("nameserver "))
|
||||
.skip(2)
|
||||
.map(|n| {
|
||||
n.parse::<SocketAddr>()
|
||||
.or_else(|_| n.parse::<IpAddr>().map(|a| (a, 53).into()))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let static_nameservers = db
|
||||
.mutate(|db| {
|
||||
let dns = db
|
||||
.as_public_mut()
|
||||
.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
|
||||
let (bg, mut runner) = BackgroundJobQueue::new();
|
||||
runner
|
||||
.run_while(async move {
|
||||
let dhcp_ns_db = db.clone();
|
||||
bg.add_job(async move {
|
||||
loop {
|
||||
if let Err(e) = async {
|
||||
let mut stream =
|
||||
file_string_stream("/run/systemd/resolve/resolv.conf")
|
||||
.filter_map(|a| futures::future::ready(a.transpose()))
|
||||
.boxed();
|
||||
while let Some(conf) = stream.next().await {
|
||||
let conf: String = conf?;
|
||||
let mut nameservers = conf
|
||||
.lines()
|
||||
.map(|l| l.trim())
|
||||
.filter_map(|l| l.strip_prefix("nameserver "))
|
||||
.map(|n| {
|
||||
n.parse::<SocketAddr>().or_else(|_| {
|
||||
n.parse::<IpAddr>().map(|a| (a, 53).into())
|
||||
})
|
||||
})
|
||||
.collect::<Result<VecDeque<_>, _>>()?;
|
||||
if nameservers
|
||||
.front()
|
||||
.map_or(false, |addr| addr.ip().is_loopback())
|
||||
{
|
||||
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));
|
||||
nameservers.pop_front();
|
||||
}
|
||||
if nameservers.front().map_or(false, |addr| {
|
||||
addr.ip() == IpAddr::from([1, 1, 1, 1])
|
||||
}) {
|
||||
nameservers.pop_front();
|
||||
}
|
||||
dhcp_ns_db
|
||||
.mutate(|db| {
|
||||
let dns = db
|
||||
.as_public_mut()
|
||||
.as_server_info_mut()
|
||||
.as_network_mut()
|
||||
.as_dns_mut();
|
||||
dns.as_dhcp_servers_mut().ser(&nameservers)
|
||||
})
|
||||
.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")??,
|
||||
_ = futures::future::join(
|
||||
futures::future::join_all(bg.values_mut()),
|
||||
futures::future::pending::<()>(),
|
||||
) => (),
|
||||
});
|
||||
loop {
|
||||
if let Err::<(), Error>(e) = async {
|
||||
let mut static_changed = db
|
||||
.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
|
||||
{
|
||||
tracing::error!("{e}");
|
||||
tracing::debug!("{e:?}");
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
})
|
||||
.into(),
|
||||
}
|
||||
|
||||
@@ -7,16 +7,19 @@ use helpers::NonDetachingJoinHandle;
|
||||
use id_pool::IdPool;
|
||||
use imbl::OrdMap;
|
||||
use models::GatewayId;
|
||||
use rpc_toolkit::{from_fn_async, Context, HandlerArgs, HandlerExt, ParentHandler};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
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::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
use crate::util::serde::{display_serializable, HandlerExtSerde};
|
||||
use crate::util::sync::Watch;
|
||||
use crate::util::Invoke;
|
||||
|
||||
pub const START9_BRIDGE_IFACE: &str = "lxcbr0";
|
||||
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 {
|
||||
external: u16,
|
||||
target: SocketAddr,
|
||||
@@ -49,6 +88,7 @@ struct ForwardRequest {
|
||||
rc: Weak<()>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ForwardEntry {
|
||||
external: u16,
|
||||
target: SocketAddr,
|
||||
@@ -96,7 +136,7 @@ impl ForwardEntry {
|
||||
let mut keep = BTreeSet::<SocketAddr>::new();
|
||||
for (iface, info) in ip_info
|
||||
.iter()
|
||||
.chain([NetworkInterfaceInfo::loopback()])
|
||||
// .chain([NetworkInterfaceInfo::loopback()])
|
||||
.filter(|(id, info)| filter_ref.filter(*id, *info))
|
||||
{
|
||||
if let Some(ip_info) = &info.ip_info {
|
||||
@@ -155,10 +195,9 @@ impl ForwardEntry {
|
||||
*self = Self::new(external, target, rc);
|
||||
self.update(ip_info, Some(filter)).await?;
|
||||
} else {
|
||||
if self.prev_filter != filter {
|
||||
self.update(ip_info, Some(filter)).await?;
|
||||
}
|
||||
self.rc = rc;
|
||||
self.update(ip_info, Some(filter).filter(|f| f != &self.prev_filter))
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -174,7 +213,7 @@ impl Drop for ForwardEntry {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
struct ForwardState {
|
||||
state: BTreeMap<u16, ForwardEntry>,
|
||||
}
|
||||
@@ -197,7 +236,7 @@ impl ForwardState {
|
||||
for entry in self.state.values_mut() {
|
||||
entry.update(ip_info, None).await?;
|
||||
}
|
||||
self.state.retain(|_, fwd| !fwd.forwards.is_empty());
|
||||
self.state.retain(|_, fwd| fwd.rc.strong_count() > 0);
|
||||
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 {
|
||||
req: mpsc::UnboundedSender<(Option<ForwardRequest>, oneshot::Sender<Result<(), Error>>)>,
|
||||
req: mpsc::UnboundedSender<ForwardCommand>,
|
||||
_thread: NonDetachingJoinHandle<()>,
|
||||
}
|
||||
impl PortForwardController {
|
||||
pub fn new(mut ip_info: Watch<OrdMap<GatewayId, NetworkInterfaceInfo>>) -> Self {
|
||||
let (req_send, mut req_recv) = mpsc::unbounded_channel::<(
|
||||
Option<ForwardRequest>,
|
||||
oneshot::Sender<Result<(), Error>>,
|
||||
)>();
|
||||
let (req_send, mut req_recv) = mpsc::unbounded_channel::<ForwardCommand>();
|
||||
let thread = NonDetachingJoinHandle::from(tokio::spawn(async move {
|
||||
let mut state = ForwardState::default();
|
||||
let mut interfaces = ip_info.read_and_mark_seen();
|
||||
loop {
|
||||
tokio::select! {
|
||||
msg = req_recv.recv() => {
|
||||
if let Some((msg, re)) = msg {
|
||||
if let Some(req) = msg {
|
||||
re.send(state.handle_request(req, &interfaces).await).ok();
|
||||
} else {
|
||||
re.send(state.sync(&interfaces).await).ok();
|
||||
}
|
||||
if let Some(cmd) = msg {
|
||||
match cmd {
|
||||
ForwardCommand::Forward(req, re) => re.send(state.handle_request(req, &interfaces).await).ok(),
|
||||
ForwardCommand::Sync(re) => re.send(state.sync(&interfaces).await).ok(),
|
||||
ForwardCommand::DumpTable(re) => re.send((&state).into()).ok(),
|
||||
};
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -250,19 +329,19 @@ impl PortForwardController {
|
||||
pub async fn add(
|
||||
&self,
|
||||
external: u16,
|
||||
filter: impl InterfaceFilter,
|
||||
filter: DynInterfaceFilter,
|
||||
target: SocketAddr,
|
||||
) -> Result<Arc<()>, Error> {
|
||||
let rc = Arc::new(());
|
||||
let (send, recv) = oneshot::channel();
|
||||
self.req
|
||||
.send((
|
||||
Some(ForwardRequest {
|
||||
.send(ForwardCommand::Forward(
|
||||
ForwardRequest {
|
||||
external,
|
||||
target,
|
||||
filter: filter.into_dyn(),
|
||||
filter,
|
||||
rc: Arc::downgrade(&rc),
|
||||
}),
|
||||
},
|
||||
send,
|
||||
))
|
||||
.map_err(err_has_exited)?;
|
||||
@@ -271,13 +350,25 @@ impl PortForwardController {
|
||||
}
|
||||
pub async fn gc(&self) -> Result<(), Error> {
|
||||
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)?
|
||||
}
|
||||
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> {
|
||||
if source.is_ipv6() {
|
||||
return Ok(()); // TODO: socat? ip6tables?
|
||||
}
|
||||
Command::new("/usr/lib/startos/scripts/forward-port")
|
||||
.env("iiface", interface)
|
||||
.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> {
|
||||
if source.is_ipv6() {
|
||||
return Ok(()); // TODO: socat? ip6tables?
|
||||
}
|
||||
Command::new("/usr/lib/startos/scripts/forward-port")
|
||||
.env("UNDO", "1")
|
||||
.env("iiface", interface)
|
||||
|
||||
@@ -1329,6 +1329,9 @@ impl InterfaceFilter for DynInterfaceFilter {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self.0.as_any()
|
||||
}
|
||||
fn into_dyn(self) -> DynInterfaceFilter {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl DynInterfaceFilter {
|
||||
fn new<T: InterfaceFilter>(value: T) -> Self {
|
||||
|
||||
@@ -33,6 +33,10 @@ pub fn net_api<C: Context>() -> ParentHandler<C> {
|
||||
"dns",
|
||||
dns::dns_api::<C>().with_about("Manage and query DNS"),
|
||||
)
|
||||
.subcommand(
|
||||
"forward",
|
||||
forward::forward_api::<C>().with_about("Manage port forwards"),
|
||||
)
|
||||
.subcommand(
|
||||
"gateway",
|
||||
gateway::gateway_api::<C>().with_about("View and edit gateway configurations"),
|
||||
|
||||
@@ -3,13 +3,13 @@ use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use clap::Parser;
|
||||
use clap::builder::ValueParserFactory;
|
||||
use clap::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use helpers::const_true;
|
||||
use imbl_value::InternedString;
|
||||
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 tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
@@ -155,6 +155,16 @@ pub async fn remove(
|
||||
for id in ids {
|
||||
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(())
|
||||
})
|
||||
.await
|
||||
@@ -179,6 +189,16 @@ pub async fn remove_before(
|
||||
for id in n.keys()?.range(..before) {
|
||||
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(())
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::pin::Pin;
|
||||
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::{Future, FutureExt, Stream, StreamExt};
|
||||
use tokio::sync::watch;
|
||||
|
||||
@@ -1575,7 +1575,7 @@ pub fn file_string_stream(
|
||||
loop {
|
||||
match stream.watches().add(
|
||||
&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,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
|
||||
@@ -14,10 +14,10 @@ use ::serde::{Deserialize, Serialize};
|
||||
use async_trait::async_trait;
|
||||
use color_eyre::eyre::{self, eyre};
|
||||
use fd_lock_rs::FdLock;
|
||||
use futures::FutureExt;
|
||||
use futures::future::BoxFuture;
|
||||
pub use helpers::NonDetachingJoinHandle;
|
||||
use futures::FutureExt;
|
||||
use helpers::canonicalize;
|
||||
pub use helpers::NonDetachingJoinHandle;
|
||||
use imbl_value::InternedString;
|
||||
use lazy_static::lazy_static;
|
||||
pub use models::VersionString;
|
||||
@@ -25,7 +25,7 @@ use pin_project::pin_project;
|
||||
use sha2::Digest;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, BufReader};
|
||||
use tokio::sync::{Mutex, OwnedMutexGuard, RwLock, oneshot};
|
||||
use tokio::sync::{oneshot, Mutex, OwnedMutexGuard, RwLock};
|
||||
use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
use url::Url;
|
||||
@@ -197,17 +197,17 @@ impl<'a> Invoke<'a> for ExtendedCommand<'a> {
|
||||
}
|
||||
#[instrument(skip_all)]
|
||||
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);
|
||||
if self.input.is_some() {
|
||||
self.cmd.stdin(Stdio::piped());
|
||||
}
|
||||
if self.pipe.is_empty() {
|
||||
let cmd_str = self
|
||||
.cmd
|
||||
.as_std()
|
||||
.get_program()
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
if self.capture {
|
||||
self.cmd.stdout(Stdio::piped());
|
||||
self.cmd.stderr(Stdio::piped());
|
||||
@@ -256,6 +256,7 @@ impl<'a> Invoke<'a> for ExtendedCommand<'a> {
|
||||
.take()
|
||||
.map(|i| Box::new(i) as Box<dyn AsyncRead + Unpin + Send>);
|
||||
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;
|
||||
if self.capture || !last {
|
||||
cmd.stdout(Stdio::piped());
|
||||
|
||||
@@ -5,14 +5,14 @@ use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use color_eyre::eyre::eyre;
|
||||
use futures::future::BoxFuture;
|
||||
use futures::{Future, FutureExt};
|
||||
use imbl_value::{InternedString, to_value};
|
||||
use imbl_value::{to_value, InternedString};
|
||||
use patch_db::json_ptr::ROOT;
|
||||
|
||||
use crate::Error;
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::Database;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::PhaseProgressTrackerHandle;
|
||||
use crate::Error;
|
||||
|
||||
mod v0_3_5;
|
||||
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_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 {
|
||||
#[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_8(Wrapper<v0_4_0_alpha_8::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),
|
||||
}
|
||||
|
||||
@@ -217,7 +219,8 @@ impl Version {
|
||||
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_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) => {
|
||||
return Err(Error::new(
|
||||
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_8(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(),
|
||||
}
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user