mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
miscellaneous bugfixes for alpha12 (#2823)
* miscellaneous bugfixes for alpha12 * fix deserialization of path in cifs share * catch error in setup.status * actually reserialize db after migration * better progress reporting for migrations * fix infinite drop * fix raspi build * fix race condition * version bump --------- Co-authored-by: Matt Hill <mattnine@protonmail.com>
This commit is contained in:
2
Makefile
2
Makefile
@@ -81,6 +81,8 @@ clean:
|
||||
rm -rf container-runtime/dist
|
||||
rm -rf container-runtime/node_modules
|
||||
rm -f container-runtime/*.squashfs
|
||||
if [ -d container-runtime/tmp/combined ] && mountpoint container-runtime/tmp/combined; then sudo umount container-runtime/tmp/combined; fi
|
||||
if [ -d container-runtime/tmp/lower ] && mountpoint container-runtime/tmp/lower; then sudo umount container-runtime/tmp/lower; fi
|
||||
rm -rf container-runtime/tmp
|
||||
(cd sdk && make clean)
|
||||
rm -f ENVIRONMENT.txt
|
||||
|
||||
@@ -101,7 +101,9 @@ done
|
||||
killall firefox-esr
|
||||
) &
|
||||
matchbox-window-manager -use_titlebar no &
|
||||
firefox-esr http://localhost --profile /home/kiosk/fx-profile
|
||||
cp -r /home/kiosk/fx-profile /home/kiosk/fx-profile-tmp
|
||||
firefox-esr http://localhost --profile /home/kiosk/fx-profile-tmp
|
||||
rm -rf /home/kiosk/fx-profile-tmp
|
||||
EOF
|
||||
chmod +x /home/kiosk/kiosk.sh
|
||||
|
||||
|
||||
@@ -18,6 +18,13 @@ sudo mount -t overlay -olowerdir=tmp/lower,upperdir=tmp/upper,workdir=tmp/work o
|
||||
QEMU=
|
||||
if [ "$ARCH" != "$(uname -m)" ]; then
|
||||
QEMU=/usr/bin/qemu-${ARCH}-static
|
||||
if ! which qemu-$ARCH-static > /dev/null; then
|
||||
>&2 echo qemu-user-static is required for cross-platform builds
|
||||
sudo umount tmp/combined
|
||||
sudo umount tmp/lower
|
||||
sudo rm -rf tmp
|
||||
exit 1
|
||||
fi
|
||||
sudo cp $(which qemu-$ARCH-static) tmp/combined${QEMU}
|
||||
fi
|
||||
|
||||
|
||||
2
core/Cargo.lock
generated
2
core/Cargo.lock
generated
@@ -5933,7 +5933,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "start-os"
|
||||
version = "0.3.6-alpha.12"
|
||||
version = "0.3.6-alpha.13"
|
||||
dependencies = [
|
||||
"aes 0.7.5",
|
||||
"async-acme",
|
||||
|
||||
@@ -14,7 +14,7 @@ keywords = [
|
||||
name = "start-os"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/Start9Labs/start-os"
|
||||
version = "0.3.6-alpha.12"
|
||||
version = "0.3.6-alpha.13" # VERSION_BUMP
|
||||
license = "MIT"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -12,6 +12,7 @@ use tracing::instrument;
|
||||
use crate::context::config::ServerConfig;
|
||||
use crate::context::rpc::InitRpcContextPhases;
|
||||
use crate::context::{DiagnosticContext, InitContext, RpcContext};
|
||||
use crate::net::network_interface::SelfContainedNetworkInterfaceListener;
|
||||
use crate::net::utils::ipv6_is_local;
|
||||
use crate::net::web_server::{Acceptor, UpgradableListener, WebServer};
|
||||
use crate::shutdown::Shutdown;
|
||||
@@ -150,17 +151,9 @@ pub fn main(args: impl IntoIterator<Item = OsString>) {
|
||||
.expect("failed to initialize runtime");
|
||||
rt.block_on(async {
|
||||
let addrs = crate::net::utils::all_socket_addrs_for(80).await?;
|
||||
let mut server = WebServer::new(
|
||||
Acceptor::bind_upgradable(addrs.into_iter().filter(|addr| match addr.ip() {
|
||||
IpAddr::V4(ip4) => {
|
||||
ip4.is_loopback()
|
||||
|| (ip4.is_private() && !ip4.octets().starts_with(&[10, 59])) // reserving 10.59 for public wireguard configurations
|
||||
|| ip4.is_link_local()
|
||||
}
|
||||
IpAddr::V6(ip6) => ipv6_is_local(ip6),
|
||||
}))
|
||||
.await?,
|
||||
);
|
||||
let mut server = WebServer::new(Acceptor::bind_upgradable(
|
||||
SelfContainedNetworkInterfaceListener::bind(80),
|
||||
));
|
||||
match inner_main(&mut server, &config).await {
|
||||
Ok(a) => {
|
||||
server.shutdown().await;
|
||||
|
||||
@@ -85,7 +85,7 @@ pub struct InitRpcContextPhases {
|
||||
load_db: PhaseProgressTrackerHandle,
|
||||
init_net_ctrl: PhaseProgressTrackerHandle,
|
||||
cleanup_init: CleanupInitPhases,
|
||||
// TODO: migrations
|
||||
run_migrations: PhaseProgressTrackerHandle,
|
||||
}
|
||||
impl InitRpcContextPhases {
|
||||
pub fn new(handle: &FullProgressTracker) -> Self {
|
||||
@@ -93,6 +93,7 @@ impl InitRpcContextPhases {
|
||||
load_db: handle.add_phase("Loading database".into(), Some(5)),
|
||||
init_net_ctrl: handle.add_phase("Initializing network".into(), Some(1)),
|
||||
cleanup_init: CleanupInitPhases::new(handle),
|
||||
run_migrations: handle.add_phase("Running migrations".into(), Some(10)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,6 +126,7 @@ impl RpcContext {
|
||||
mut load_db,
|
||||
mut init_net_ctrl,
|
||||
cleanup_init,
|
||||
run_migrations,
|
||||
}: InitRpcContextPhases,
|
||||
) -> Result<Self, Error> {
|
||||
let tor_proxy = config.tor_socks.unwrap_or(SocketAddr::V4(SocketAddrV4::new(
|
||||
@@ -276,7 +278,9 @@ impl RpcContext {
|
||||
let res = Self(seed.clone());
|
||||
res.cleanup_and_initialize(cleanup_init).await?;
|
||||
tracing::info!("Cleaned up transient states");
|
||||
crate::version::post_init(&res).await?;
|
||||
|
||||
crate::version::post_init(&res, run_migrations).await?;
|
||||
tracing::info!("Completed migrations");
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -286,7 +290,6 @@ impl RpcContext {
|
||||
self.services.shutdown_all().await?;
|
||||
self.is_closed.store(true, Ordering::SeqCst);
|
||||
tracing::info!("RPC Context is shutdown");
|
||||
// TODO: shutdown http servers
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ use ts_rs::TS;
|
||||
use crate::account::AccountInfo;
|
||||
use crate::db::model::package::AllPackageData;
|
||||
use crate::net::acme::AcmeProvider;
|
||||
use crate::net::host::address::DomainConfig;
|
||||
use crate::net::host::binding::{AddSslOptions, BindInfo, BindOptions, NetInfo};
|
||||
use crate::net::host::Host;
|
||||
use crate::net::utils::ipv6_is_local;
|
||||
use crate::net::vhost::AlpnInfo;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::FullProgress;
|
||||
@@ -190,14 +190,29 @@ impl NetworkInterfaceInfo {
|
||||
pub fn public(&self) -> bool {
|
||||
self.public.unwrap_or_else(|| {
|
||||
!self.ip_info.as_ref().map_or(true, |ip_info| {
|
||||
ip_info.subnets.iter().all(|ipnet| {
|
||||
match ipnet.addr() {
|
||||
IpAddr::V4(ip4) => {
|
||||
let ip4s = ip_info
|
||||
.subnets
|
||||
.iter()
|
||||
.filter_map(|ipnet| {
|
||||
if let IpAddr::V4(ip4) = ipnet.addr() {
|
||||
Some(ip4)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<BTreeSet<_>>();
|
||||
if !ip4s.is_empty() {
|
||||
return ip4s.iter().all(|ip4| {
|
||||
ip4.is_loopback()
|
||||
|| (ip4.is_private() && !ip4.octets().starts_with(&[10, 59])) // reserving 10.59 for public wireguard configurations
|
||||
|| ip4.is_link_local()
|
||||
});
|
||||
}
|
||||
IpAddr::V6(_) => true,
|
||||
ip_info.subnets.iter().all(|ipnet| {
|
||||
if let IpAddr::V6(ip6) = ipnet.addr() {
|
||||
ipv6_is_local(ip6)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -219,7 +219,6 @@ pub struct InitPhases {
|
||||
enable_zram: PhaseProgressTrackerHandle,
|
||||
update_server_info: PhaseProgressTrackerHandle,
|
||||
launch_service_network: PhaseProgressTrackerHandle,
|
||||
run_migrations: PhaseProgressTrackerHandle,
|
||||
validate_db: PhaseProgressTrackerHandle,
|
||||
postinit: Option<PhaseProgressTrackerHandle>,
|
||||
}
|
||||
@@ -244,7 +243,6 @@ impl InitPhases {
|
||||
enable_zram: handle.add_phase("Enabling ZRAM".into(), Some(1)),
|
||||
update_server_info: handle.add_phase("Updating server info".into(), Some(1)),
|
||||
launch_service_network: handle.add_phase("Launching service intranet".into(), Some(1)),
|
||||
run_migrations: handle.add_phase("Running migrations".into(), Some(10)),
|
||||
validate_db: handle.add_phase("Validating database".into(), Some(1)),
|
||||
postinit: if Path::new("/media/startos/config/postinit.sh").exists() {
|
||||
Some(handle.add_phase("Running postinit.sh".into(), Some(5)))
|
||||
@@ -297,7 +295,6 @@ pub async fn init(
|
||||
mut enable_zram,
|
||||
mut update_server_info,
|
||||
mut launch_service_network,
|
||||
run_migrations,
|
||||
mut validate_db,
|
||||
postinit,
|
||||
}: InitPhases,
|
||||
@@ -412,20 +409,6 @@ pub async fn init(
|
||||
Command::new("update-ca-certificates")
|
||||
.invoke(crate::ErrorKind::OpenSsl)
|
||||
.await?;
|
||||
if tokio::fs::metadata("/home/kiosk/profile").await.is_ok() {
|
||||
Command::new("certutil")
|
||||
.arg("-A")
|
||||
.arg("-n")
|
||||
.arg("StartOS Local Root CA")
|
||||
.arg("-t")
|
||||
.arg("TCu,Cuw,Tuw")
|
||||
.arg("-i")
|
||||
.arg("/usr/local/share/ca-certificates/startos-root-ca.crt")
|
||||
.arg("-d")
|
||||
.arg("/home/kiosk/fx-profile")
|
||||
.invoke(ErrorKind::OpenSsl)
|
||||
.await?;
|
||||
}
|
||||
load_ca_cert.complete();
|
||||
|
||||
load_wifi.start();
|
||||
|
||||
@@ -219,7 +219,7 @@ impl NetServiceData {
|
||||
|
||||
// LAN
|
||||
let server_info = peek.as_public().as_server_info();
|
||||
let net_ifaces = server_info.as_network_interfaces().de()?;
|
||||
let net_ifaces = ctrl.net_iface.ip_info();
|
||||
let hostname = server_info.as_hostname().de()?;
|
||||
for (port, bind) in &host.bindings {
|
||||
if !bind.enabled {
|
||||
@@ -586,21 +586,24 @@ impl NetServiceData {
|
||||
|
||||
async fn update_all(&mut self) -> Result<(), Error> {
|
||||
let ctrl = self.net_controller()?;
|
||||
if let Some(id) = &self.id {
|
||||
if let Some(id) = self.id.clone() {
|
||||
for (host_id, host) in ctrl
|
||||
.db
|
||||
.peek()
|
||||
.await
|
||||
.as_public()
|
||||
.as_package_data()
|
||||
.as_idx(id)
|
||||
.or_not_found(id)?
|
||||
.as_idx(&id)
|
||||
.or_not_found(&id)?
|
||||
.as_hosts()
|
||||
.as_entries()?
|
||||
{
|
||||
self.update(&*ctrl, host_id, host.de()?).await?;
|
||||
tracing::info!("Updating host {host_id} for {id}");
|
||||
self.update(&*ctrl, host_id.clone(), host.de()?).await?;
|
||||
tracing::info!("Updated host {host_id} for {id}");
|
||||
}
|
||||
} else {
|
||||
tracing::info!("Updating host for Main UI");
|
||||
self.update(
|
||||
&*ctrl,
|
||||
HostId::default(),
|
||||
@@ -613,6 +616,7 @@ impl NetServiceData {
|
||||
.de()?,
|
||||
)
|
||||
.await?;
|
||||
tracing::info!("Updated host for Main UI");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -710,6 +714,7 @@ impl NetService {
|
||||
drop(ctrl);
|
||||
Ok(())
|
||||
} else {
|
||||
self.shutdown = true;
|
||||
tracing::warn!("NetService dropped after NetController is shutdown");
|
||||
Err(Error::new(
|
||||
eyre!("NetController is shutdown"),
|
||||
|
||||
@@ -29,6 +29,7 @@ use crate::db::model::public::{IpInfo, NetworkInterfaceInfo, NetworkInterfaceTyp
|
||||
use crate::db::model::Database;
|
||||
use crate::net::forward::START9_BRIDGE_IFACE;
|
||||
use crate::net::utils::{ipv6_is_link_local, ipv6_is_local};
|
||||
use crate::net::web_server::Accept;
|
||||
use crate::prelude::*;
|
||||
use crate::util::future::Until;
|
||||
use crate::util::io::open_file;
|
||||
@@ -411,8 +412,10 @@ async fn watcher(
|
||||
}
|
||||
|
||||
async fn get_wan_ipv4(iface: &str) -> Result<Option<Ipv4Addr>, Error> {
|
||||
Ok(reqwest::Client::builder()
|
||||
.interface(iface)
|
||||
let client = reqwest::Client::builder();
|
||||
#[cfg(target_os = "linux")]
|
||||
let client = client.interface(iface);
|
||||
Ok(client
|
||||
.build()?
|
||||
.get("http://ip4only.me/api/")
|
||||
.timeout(Duration::from_secs(10))
|
||||
@@ -647,6 +650,10 @@ impl NetworkInterfaceController {
|
||||
self.ip_info.clone_unseen()
|
||||
}
|
||||
|
||||
pub fn ip_info(&self) -> BTreeMap<InternedString, NetworkInterfaceInfo> {
|
||||
self.ip_info.read()
|
||||
}
|
||||
|
||||
async fn sync(
|
||||
db: &TypedPatchDb<Database>,
|
||||
info: &BTreeMap<InternedString, NetworkInterfaceInfo>,
|
||||
@@ -790,11 +797,13 @@ impl NetworkInterfaceController {
|
||||
|
||||
pub fn upgrade_listener(
|
||||
&self,
|
||||
listener: impl IntoIterator<Item = TcpListener>,
|
||||
SelfContainedNetworkInterfaceListener {
|
||||
mut listener,
|
||||
..
|
||||
}: SelfContainedNetworkInterfaceListener,
|
||||
) -> Result<NetworkInterfaceListener, Error> {
|
||||
let listeners = ListenerMap::from_listener(listener)?;
|
||||
let port = listeners.port;
|
||||
let arc = Arc::new(());
|
||||
let port = listener.listeners.port;
|
||||
let arc = &listener._arc;
|
||||
self.listeners.mutate(|l| {
|
||||
if l.get(&port).filter(|w| w.strong_count() > 0).is_some() {
|
||||
return Err(Error::new(
|
||||
@@ -802,16 +811,13 @@ impl NetworkInterfaceController {
|
||||
ErrorKind::Network,
|
||||
));
|
||||
}
|
||||
l.insert(port, Arc::downgrade(&arc));
|
||||
l.insert(port, Arc::downgrade(arc));
|
||||
Ok(())
|
||||
})?;
|
||||
let ip_info = self.ip_info.clone_unseen();
|
||||
ip_info.mark_changed();
|
||||
Ok(NetworkInterfaceListener {
|
||||
_arc: arc,
|
||||
ip_info,
|
||||
listeners,
|
||||
})
|
||||
listener.change_ip_info_source(ip_info);
|
||||
Ok(listener)
|
||||
}
|
||||
|
||||
pub async fn set_public(
|
||||
@@ -1050,6 +1056,26 @@ impl NetworkInterfaceListener {
|
||||
self.listeners.poll_accept(cx)
|
||||
}
|
||||
|
||||
pub(super) fn new(
|
||||
mut ip_info: Watch<BTreeMap<InternedString, NetworkInterfaceInfo>>,
|
||||
port: u16,
|
||||
) -> Self {
|
||||
ip_info.mark_unseen();
|
||||
Self {
|
||||
ip_info,
|
||||
listeners: ListenerMap::new(port),
|
||||
_arc: Arc::new(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change_ip_info_source(
|
||||
&mut self,
|
||||
mut ip_info: Watch<BTreeMap<InternedString, NetworkInterfaceInfo>>,
|
||||
) {
|
||||
ip_info.mark_unseen();
|
||||
self.ip_info = ip_info;
|
||||
}
|
||||
|
||||
pub async fn accept(&mut self, public: bool) -> Result<Accepted, Error> {
|
||||
futures::future::poll_fn(|cx| self.poll_accept(cx, public)).await
|
||||
}
|
||||
@@ -1063,82 +1089,25 @@ pub struct Accepted {
|
||||
pub bind: SocketAddr,
|
||||
}
|
||||
|
||||
// async fn _ips() -> Result<BTreeSet<IpAddr>, Error> {
|
||||
// Ok(init_ips()
|
||||
// .await?
|
||||
// .values()
|
||||
// .flat_map(|i| {
|
||||
// std::iter::empty()
|
||||
// .chain(i.ipv4.map(IpAddr::from))
|
||||
// .chain(i.ipv6.map(IpAddr::from))
|
||||
// })
|
||||
// .collect())
|
||||
// }
|
||||
|
||||
// pub async fn ips() -> Result<BTreeSet<IpAddr>, Error> {
|
||||
// let ips = CACHED_IPS.read().await.clone();
|
||||
// if !ips.is_empty() {
|
||||
// return Ok(ips);
|
||||
// }
|
||||
// let ips = _ips().await?;
|
||||
// *CACHED_IPS.write().await = ips.clone();
|
||||
// Ok(ips)
|
||||
// }
|
||||
|
||||
// pub async fn init_ips() -> Result<BTreeMap<String, IpInfo>, Error> {
|
||||
// let mut res = BTreeMap::new();
|
||||
// let mut ifaces = list_interfaces();
|
||||
// while let Some(iface) = ifaces.try_next().await? {
|
||||
// if iface_is_physical(&iface).await {
|
||||
// let ip_info = IpInfo::for_interface(&iface).await?;
|
||||
// res.insert(iface, ip_info);
|
||||
// }
|
||||
// }
|
||||
// Ok(res)
|
||||
// }
|
||||
|
||||
// // #[command(subcommands(update))]
|
||||
// pub fn dhcp<C: Context>() -> ParentHandler<C> {
|
||||
// ParentHandler::new().subcommand(
|
||||
// "update",
|
||||
// from_fn_async::<_, _, (), Error, (RpcContext, UpdateParams)>(update)
|
||||
// .no_display()
|
||||
// .with_about("Update IP assigned by dhcp")
|
||||
// .with_call_remote::<CliContext>(),
|
||||
// )
|
||||
// }
|
||||
// #[derive(Deserialize, Serialize, Parser, TS)]
|
||||
// #[serde(rename_all = "camelCase")]
|
||||
// #[command(rename_all = "kebab-case")]
|
||||
// pub struct UpdateParams {
|
||||
// interface: String,
|
||||
// }
|
||||
|
||||
// pub async fn update(
|
||||
// ctx: RpcContext,
|
||||
// UpdateParams { interface }: UpdateParams,
|
||||
// ) -> Result<(), Error> {
|
||||
// if iface_is_physical(&interface).await {
|
||||
// let ip_info = IpInfo::for_interface(&interface).await?;
|
||||
// ctx.db
|
||||
// .mutate(|db| {
|
||||
// db.as_public_mut()
|
||||
// .as_server_info_mut()
|
||||
// .as_ip_info_mut()
|
||||
// .insert(&interface, &ip_info)
|
||||
// })
|
||||
// .await?;
|
||||
|
||||
// let mut cached = CACHED_IPS.write().await;
|
||||
// if cached.is_empty() {
|
||||
// *cached = _ips().await?;
|
||||
// } else {
|
||||
// cached.extend(
|
||||
// std::iter::empty()
|
||||
// .chain(ip_info.ipv4.map(IpAddr::from))
|
||||
// .chain(ip_info.ipv6.map(IpAddr::from)),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
pub struct SelfContainedNetworkInterfaceListener {
|
||||
_watch_thread: NonDetachingJoinHandle<()>,
|
||||
listener: NetworkInterfaceListener,
|
||||
}
|
||||
impl SelfContainedNetworkInterfaceListener {
|
||||
pub fn bind(port: u16) -> Self {
|
||||
let ip_info = Watch::new(BTreeMap::new());
|
||||
let _watch_thread = tokio::spawn(watcher(ip_info.clone(), Watch::new(false))).into();
|
||||
Self {
|
||||
_watch_thread,
|
||||
listener: NetworkInterfaceListener::new(ip_info, port),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Accept for SelfContainedNetworkInterfaceListener {
|
||||
fn poll_accept(
|
||||
&mut self,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<super::web_server::Accepted, Error>> {
|
||||
Accept::poll_accept(&mut self.listener, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6};
|
||||
use std::path::Path;
|
||||
|
||||
@@ -5,12 +6,18 @@ use async_stream::try_stream;
|
||||
use color_eyre::eyre::eyre;
|
||||
use futures::stream::BoxStream;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use helpers::NonDetachingJoinHandle;
|
||||
use imbl_value::InternedString;
|
||||
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
|
||||
use nix::net::if_::if_nametoindex;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::db::model::public::NetworkInterfaceInfo;
|
||||
use crate::net::network_interface::NetworkInterfaceListener;
|
||||
use crate::net::web_server::Accept;
|
||||
use crate::prelude::*;
|
||||
use crate::util::sync::Watch;
|
||||
use crate::util::Invoke;
|
||||
|
||||
pub fn ipv6_is_link_local(addr: Ipv6Addr) -> bool {
|
||||
@@ -121,7 +128,7 @@ pub async fn find_eth_iface() -> Result<String, Error> {
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn all_socket_addrs_for(port: u16) -> Result<Vec<SocketAddr>, Error> {
|
||||
pub async fn all_socket_addrs_for(port: u16) -> Result<Vec<(InternedString, SocketAddr)>, Error> {
|
||||
let mut res = Vec::new();
|
||||
|
||||
let raw = String::from_utf8(
|
||||
@@ -153,14 +160,17 @@ pub async fn all_socket_addrs_for(port: u16) -> Result<Vec<SocketAddr>, Error> {
|
||||
.parse::<IpNet>()
|
||||
.with_ctx(|_| (ErrorKind::ParseSysInfo, err("ipnet", idx, ipnet_str)))?;
|
||||
match ipnet.addr() {
|
||||
IpAddr::V4(ip4) => res.push(SocketAddr::new(ip4.into(), port)),
|
||||
IpAddr::V6(ip6) => res.push(SocketAddr::V6(SocketAddrV6::new(
|
||||
IpAddr::V4(ip4) => res.push((ifname.into(), SocketAddr::new(ip4.into(), port))),
|
||||
IpAddr::V6(ip6) => res.push((
|
||||
ifname.into(),
|
||||
SocketAddr::V6(SocketAddrV6::new(
|
||||
ip6,
|
||||
port,
|
||||
0,
|
||||
if_nametoindex(ifname)
|
||||
.with_ctx(|_| (ErrorKind::ParseSysInfo, "reading scope_id"))?,
|
||||
))),
|
||||
)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@ use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::context::{DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext};
|
||||
use crate::net::network_interface::NetworkInterfaceListener;
|
||||
use crate::net::network_interface::{
|
||||
NetworkInterfaceListener, SelfContainedNetworkInterfaceListener,
|
||||
};
|
||||
use crate::net::static_server::{
|
||||
diagnostic_ui_router, init_ui_router, install_ui_router, main_ui_router, redirecter, refresher,
|
||||
setup_ui_router,
|
||||
@@ -106,15 +108,12 @@ impl Acceptor<Vec<TcpListener>> {
|
||||
}
|
||||
}
|
||||
|
||||
pub type UpgradableListener = Option<Either<Vec<TcpListener>, NetworkInterfaceListener>>;
|
||||
pub type UpgradableListener =
|
||||
Option<Either<SelfContainedNetworkInterfaceListener, NetworkInterfaceListener>>;
|
||||
|
||||
impl Acceptor<UpgradableListener> {
|
||||
pub async fn bind_upgradable(
|
||||
listen: impl IntoIterator<Item = SocketAddr>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self::new(Some(Either::Left(
|
||||
futures::future::try_join_all(listen.into_iter().map(TcpListener::bind)).await?,
|
||||
))))
|
||||
pub fn bind_upgradable(listener: SelfContainedNetworkInterfaceListener) -> Self {
|
||||
Self::new(Some(Either::Left(listener)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ use patch_db::json_ptr::ROOT;
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::Database;
|
||||
use crate::prelude::*;
|
||||
use crate::progress::PhaseProgressTrackerHandle;
|
||||
use crate::Error;
|
||||
|
||||
mod v0_3_5;
|
||||
@@ -31,8 +32,9 @@ mod v0_3_6_alpha_9;
|
||||
mod v0_3_6_alpha_10;
|
||||
mod v0_3_6_alpha_11;
|
||||
mod v0_3_6_alpha_12;
|
||||
mod v0_3_6_alpha_13;
|
||||
|
||||
pub type Current = v0_3_6_alpha_12::Version; // VERSION_BUMP
|
||||
pub type Current = v0_3_6_alpha_13::Version; // VERSION_BUMP
|
||||
|
||||
impl Current {
|
||||
#[instrument(skip(self, db))]
|
||||
@@ -55,8 +57,7 @@ impl Current {
|
||||
let pre_ups = PreUps::load(&from, &self).await?;
|
||||
db.apply_function(|mut db| {
|
||||
migrate_from_unchecked(&from, &self, pre_ups, &mut db)?;
|
||||
from_value::<Database>(db.clone())?;
|
||||
Ok::<_, Error>((db, ()))
|
||||
Ok::<_, Error>((to_value(&from_value::<Database>(db.clone())?)?, ()))
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
@@ -66,8 +67,18 @@ impl Current {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn post_init(ctx: &RpcContext) -> Result<(), Error> {
|
||||
let mut peek;
|
||||
pub async fn post_init(
|
||||
ctx: &RpcContext,
|
||||
mut progress: PhaseProgressTrackerHandle,
|
||||
) -> Result<(), Error> {
|
||||
let mut peek = ctx.db.peek().await;
|
||||
let todos = peek
|
||||
.as_public()
|
||||
.as_server_info()
|
||||
.as_post_init_migration_todos()
|
||||
.de()?;
|
||||
if !todos.is_empty() {
|
||||
progress.set_total(todos.len() as u64);
|
||||
while let Some(version) = {
|
||||
peek = ctx.db.peek().await;
|
||||
peek.as_public()
|
||||
@@ -90,7 +101,10 @@ pub async fn post_init(ctx: &RpcContext) -> Result<(), Error> {
|
||||
.mutate(|m| Ok(m.remove(&version.0.semver())))
|
||||
})
|
||||
.await?;
|
||||
progress += 1;
|
||||
}
|
||||
}
|
||||
progress.complete();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -115,6 +129,7 @@ enum Version {
|
||||
V0_3_6_alpha_10(Wrapper<v0_3_6_alpha_10::Version>),
|
||||
V0_3_6_alpha_11(Wrapper<v0_3_6_alpha_11::Version>),
|
||||
V0_3_6_alpha_12(Wrapper<v0_3_6_alpha_12::Version>),
|
||||
V0_3_6_alpha_13(Wrapper<v0_3_6_alpha_13::Version>), // VERSION_BUMP
|
||||
Other(exver::Version),
|
||||
}
|
||||
|
||||
@@ -151,6 +166,7 @@ impl Version {
|
||||
Self::V0_3_6_alpha_10(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_3_6_alpha_11(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_3_6_alpha_12(v) => DynVersion(Box::new(v.0)),
|
||||
Self::V0_3_6_alpha_13(v) => DynVersion(Box::new(v.0)), // VERSION_BUMP
|
||||
Self::Other(v) => {
|
||||
return Err(Error::new(
|
||||
eyre!("unknown version {v}"),
|
||||
@@ -179,6 +195,7 @@ impl Version {
|
||||
Version::V0_3_6_alpha_10(Wrapper(x)) => x.semver(),
|
||||
Version::V0_3_6_alpha_11(Wrapper(x)) => x.semver(),
|
||||
Version::V0_3_6_alpha_12(Wrapper(x)) => x.semver(),
|
||||
Version::V0_3_6_alpha_13(Wrapper(x)) => x.semver(), // VERSION_BUMP
|
||||
Version::Other(x) => x.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ pub struct Version;
|
||||
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_3_5_2::Version;
|
||||
type PreUpRes = (AccountInfo, SshKeys, CifsTargets, Notifications);
|
||||
type PreUpRes = (AccountInfo, SshKeys, CifsTargets);
|
||||
fn semver(self) -> exver::Version {
|
||||
V0_3_6_alpha_0.clone()
|
||||
}
|
||||
@@ -208,15 +208,9 @@ impl VersionT for Version {
|
||||
|
||||
let cifs = previous_cifs(&pg).await?;
|
||||
|
||||
let notifications = previous_notifications(pg).await?;
|
||||
|
||||
Ok((account, ssh_keys, cifs, notifications))
|
||||
Ok((account, ssh_keys, cifs))
|
||||
}
|
||||
fn up(
|
||||
self,
|
||||
db: &mut Value,
|
||||
(account, ssh_keys, cifs, notifications): Self::PreUpRes,
|
||||
) -> Result<(), Error> {
|
||||
fn up(self, db: &mut Value, (account, ssh_keys, cifs): Self::PreUpRes) -> Result<(), Error> {
|
||||
let wifi = json!({
|
||||
"infterface": db["server-info"]["wifi"]["interface"],
|
||||
"ssids": db["server-info"]["wifi"]["ssids"],
|
||||
@@ -298,7 +292,7 @@ impl VersionT for Version {
|
||||
value["sshPubkeys"] = to_value(&ssh_keys)?;
|
||||
value["availablePorts"] = to_value(&AvailablePorts::new())?;
|
||||
value["sessions"] = to_value(&Sessions::new())?;
|
||||
value["notifications"] = to_value(¬ifications)?;
|
||||
value["notifications"] = to_value(&Notifications::new())?;
|
||||
value["cifs"] = to_value(&cifs)?;
|
||||
value["packageStores"] = json!({});
|
||||
value
|
||||
@@ -375,64 +369,6 @@ impl VersionT for Version {
|
||||
}
|
||||
}
|
||||
|
||||
async fn previous_notifications(pg: sqlx::Pool<sqlx::Postgres>) -> Result<Notifications, Error> {
|
||||
let notification_cursor = sqlx::query(r#"SELECT * FROM notifications"#)
|
||||
.fetch_all(&pg)
|
||||
.await?;
|
||||
let notifications = {
|
||||
let mut notifications = Notifications::default();
|
||||
for row in notification_cursor {
|
||||
let package_id = serde_json::from_str::<PackageId>(
|
||||
row.try_get("package_id")
|
||||
.with_ctx(|_| (ErrorKind::Database, "package_id"))?,
|
||||
)
|
||||
.ok();
|
||||
|
||||
let created_at = row
|
||||
.try_get("created_at")
|
||||
.with_ctx(|_| (ErrorKind::Database, "created_at"))?;
|
||||
let code = row
|
||||
.try_get::<i64, _>("code")
|
||||
.with_ctx(|_| (ErrorKind::Database, "code"))? as u32;
|
||||
let id = row
|
||||
.try_get::<i64, _>("id")
|
||||
.with_ctx(|_| (ErrorKind::Database, "id"))? as u32;
|
||||
let level = serde_json::from_str(
|
||||
row.try_get("level")
|
||||
.with_ctx(|_| (ErrorKind::Database, "level"))?,
|
||||
)
|
||||
.with_kind(ErrorKind::Database)
|
||||
.with_ctx(|_| (ErrorKind::Database, "level: serde_json "))?;
|
||||
let title = row
|
||||
.try_get("title")
|
||||
.with_ctx(|_| (ErrorKind::Database, "title"))?;
|
||||
let message = row
|
||||
.try_get("message")
|
||||
.with_ctx(|_| (ErrorKind::Database, "message"))?;
|
||||
let data = serde_json::from_str(
|
||||
row.try_get("data")
|
||||
.with_ctx(|_| (ErrorKind::Database, "data"))?,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
notifications.0.insert(
|
||||
id,
|
||||
Notification {
|
||||
package_id,
|
||||
created_at,
|
||||
code,
|
||||
level,
|
||||
title,
|
||||
message,
|
||||
data,
|
||||
},
|
||||
);
|
||||
}
|
||||
notifications
|
||||
};
|
||||
Ok(notifications)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn previous_cifs(pg: &sqlx::Pool<sqlx::Postgres>) -> Result<CifsTargets, Error> {
|
||||
let cifs = sqlx::query(r#"SELECT * FROM cifs_shares"#)
|
||||
@@ -440,16 +376,17 @@ async fn previous_cifs(pg: &sqlx::Pool<sqlx::Postgres>) -> Result<CifsTargets, E
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|row| {
|
||||
let id: i64 = row.try_get("id")?;
|
||||
let id: i32 = row.try_get("id")?;
|
||||
Ok::<_, Error>((
|
||||
id,
|
||||
Cifs {
|
||||
hostname: row
|
||||
.try_get("hostname")
|
||||
.with_ctx(|_| (ErrorKind::Database, "hostname"))?,
|
||||
path: serde_json::from_str(row.try_get("path")?)
|
||||
.with_kind(ErrorKind::Database)
|
||||
.with_ctx(|_| (ErrorKind::Database, "path"))?,
|
||||
path: row
|
||||
.try_get::<String, _>("path")
|
||||
.with_ctx(|_| (ErrorKind::Database, "path"))?
|
||||
.into(),
|
||||
username: row
|
||||
.try_get("username")
|
||||
.with_ctx(|_| (ErrorKind::Database, "username"))?,
|
||||
|
||||
68
core/startos/src/version/v0_3_6_alpha_13.rs
Normal file
68
core/startos/src/version/v0_3_6_alpha_13.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use exver::{PreReleaseSegment, VersionRange};
|
||||
use imbl_value::json;
|
||||
|
||||
use super::v0_3_5::V0_3_0_COMPAT;
|
||||
use super::{v0_3_6_alpha_11, VersionT};
|
||||
use crate::prelude::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref V0_3_6_alpha_12: exver::Version = exver::Version::new(
|
||||
[0, 3, 6],
|
||||
[PreReleaseSegment::String("alpha".into()), 12.into()]
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Version;
|
||||
|
||||
impl VersionT for Version {
|
||||
type Previous = v0_3_6_alpha_11::Version;
|
||||
type PreUpRes = ();
|
||||
|
||||
async fn pre_up(self) -> Result<Self::PreUpRes, Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn semver(self) -> exver::Version {
|
||||
V0_3_6_alpha_12.clone()
|
||||
}
|
||||
fn compat(self) -> &'static VersionRange {
|
||||
&V0_3_0_COMPAT
|
||||
}
|
||||
fn up(self, db: &mut Value, _: Self::PreUpRes) -> Result<(), Error> {
|
||||
let bindings: BTreeMap<u16, Value> = [(
|
||||
80,
|
||||
json!({
|
||||
"enabled": false,
|
||||
"options": {
|
||||
"preferredExternalPort": 80,
|
||||
"addSsl": {
|
||||
"preferredExternalPort": 443,
|
||||
"alpn": { "specified": [ "http/1.1", "h2" ] },
|
||||
},
|
||||
"secure": null,
|
||||
},
|
||||
"net": {
|
||||
"assignedPort": null,
|
||||
"assignedSslPort": 443,
|
||||
"public": false,
|
||||
}
|
||||
}),
|
||||
)]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let onion = db["public"]["serverInfo"]["onionAddress"].clone();
|
||||
db["public"]["serverInfo"]["host"] = json!({
|
||||
"bindings": bindings,
|
||||
"onions": [onion],
|
||||
"domains": {},
|
||||
"hostnameInfo": {},
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn down(self, _db: &mut Value) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -206,8 +206,8 @@ if [ "${IB_TARGET_PLATFORM}" = "raspberrypi" ]; then
|
||||
echo "Configuring raspi kernel '\$v'"
|
||||
extract-ikconfig "/usr/lib/modules/\$v/kernel/kernel/configs.ko.xz" > /boot/config-\$v
|
||||
done
|
||||
mkinitramfs -c gzip -o /boot/initramfs8 6.6.62-v8+
|
||||
mkinitramfs -c gzip -o /boot/initramfs_2712 6.6.62-v8-16k+
|
||||
mkinitramfs -c gzip -o /boot/initramfs8 6.6.74-v8+
|
||||
mkinitramfs -c gzip -o /boot/initramfs_2712 6.6.74-v8-16k+
|
||||
fi
|
||||
|
||||
useradd --shell /bin/bash -G startos -m start9
|
||||
|
||||
4
web/package-lock.json
generated
4
web/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "startos-ui",
|
||||
"version": "0.3.6-alpha.12",
|
||||
"version": "0.3.6-alpha.13",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "startos-ui",
|
||||
"version": "0.3.6-alpha.12",
|
||||
"version": "0.3.6-alpha.13",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^14.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "startos-ui",
|
||||
"version": "0.3.6-alpha.12",
|
||||
"version": "0.3.6-alpha.13",
|
||||
"author": "Start9 Labs, Inc",
|
||||
"homepage": "https://start9.com/",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -76,6 +76,7 @@ export class LoadingPage {
|
||||
guid: string
|
||||
progress: T.FullProgress
|
||||
} | void> {
|
||||
try {
|
||||
const res = await this.api.getStatus()
|
||||
|
||||
if (!res) {
|
||||
@@ -85,6 +86,9 @@ export class LoadingPage {
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
} catch (e: any) {
|
||||
this.errorService.handleError(e)
|
||||
}
|
||||
}
|
||||
|
||||
private getRunningStatus$(): Observable<{
|
||||
|
||||
Reference in New Issue
Block a user