summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNamilskyy <alive6863@gmail.com>2025-12-28 15:19:04 +0300
committerNamilskyy <alive6863@gmail.com>2025-12-28 15:19:04 +0300
commitbbcbdcbbf95cda5115bbb484b5e2d01669a7a1a0 (patch)
treed57a5ec1044713f2ebbdf694f8b6ecca7f800edf
parent007ee0bc3534218b2d084f368444d96c16d9d7f9 (diff)
Implementing integrated router funtions and other fuctions
-rw-r--r--.woodpecker.yaml7
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml12
-rw-r--r--src/cfg/config.rs95
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs454
-rw-r--r--src/openpgp/signatures.rs1
-rw-r--r--src/pkgtoolkit/build.rs8
-rw-r--r--src/pkgtoolkit/install.rs44
-rw-r--r--src/pkgtoolkit/types.rs5
-rw-r--r--src/router/manager.rs144
-rw-r--r--src/router/mod.rs2
-rw-r--r--src/router/router.rs197
13 files changed, 678 insertions, 293 deletions
diff --git a/.woodpecker.yaml b/.woodpecker.yaml
index 0b89995..a198f5c 100644
--- a/.woodpecker.yaml
+++ b/.woodpecker.yaml
@@ -1,14 +1,14 @@
# Define the sequence of steps for the CI pipeline
steps:
dependencies:
- image: rust
+ image: rust:1.78-bullseye
environment:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
commands:
- rustup default stable
- apt update
- - apt install openssl gnupg libgpgme-dev libgpg-error-dev libassuan-dev -y
+ - apt install -y pkg-config libgpg-error-dev libgpgme-dev libassuan-dev gnupg openssl
when:
branch: main
event: [ push, pull_request ]
@@ -29,7 +29,8 @@ steps:
CARGO_TERM_COLOR: always
commands:
- rustup component add clippy rustfmt
- # - cargo fmt --all -- --check
+ - cargo fmt --all
+ - export PKG_CONFIG_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig"
- cargo clippy --jobs 2 -- -D clippy::all # -D warnings
when:
branch: main
diff --git a/Cargo.lock b/Cargo.lock
index 830ecd2..c746dad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1696,6 +1696,7 @@ dependencies = [
"glob",
"gpgme",
"indicatif",
+ "lazy_static",
"log",
"mockito",
"num_cpus",
diff --git a/Cargo.toml b/Cargo.toml
index 6e0fe96..4016a05 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,8 +3,7 @@ name = "mesk"
version = "0.0.1"
edition = "2024"
license = "GPL-3.0"
-authors = [ "namilsk <namilsk@namilsk.tech>",
- "asya <asyamozgoff@gmail.com>" ]
+authors = [ "namilsk <namilsk@namilsk.tech>" ]
description = "An i2p-based package manager developed by Anthrill project"
categories = ["command-line-utilities"]
repository = "https://codeberg.org/Anthrill/mesk.git"
@@ -16,6 +15,8 @@ toml = { version = "0.9.8", features = ["serde"] }
serde = { version = "1.0.228", features = ["derive"] }
tokio = { version = "1.48.0", features = ["full"] }
reqwest = { version = "0.12.24", features = ["stream"] }
+emissary-util = { version = "0.3.0", features = ["tokio"] }
+emissary-core = "0.3.0"
flate2 = "1.1.5"
log = "0.4.28"
tar = "0.4.44"
@@ -26,8 +27,7 @@ num_cpus = "1.17.0"
git2 = "0.19.0"
gpgme = "0.11.0"
glob = "0.3.3"
-emissary-core = "0.3.0"
-emissary-util = "0.3.0"
+lazy_static = "1.5.0"
[dev-dependencies]
env_logger = "0.11.8"
@@ -40,5 +40,5 @@ uuid = { version = "1.19.0", features = ["v4"] }
[profile.release]
strip = true
lto = true
-opt-level = "s"
-
+opt-level = 3
+codegen-units = 1 \ No newline at end of file
diff --git a/src/cfg/config.rs b/src/cfg/config.rs
index ce9df87..eaa703c 100644
--- a/src/cfg/config.rs
+++ b/src/cfg/config.rs
@@ -14,10 +14,54 @@ pub enum Loglevel {
/// `mesk.toml` configuration fields here
#[derive(Deserialize, Debug, Serialize, Clone)]
+pub struct RouterConfig {
+ #[serde(default = "default_integrated_router")]
+ pub integrated_router: bool,
+ #[serde(default = "default_router_storage")]
+ pub storage_path: String,
+ #[serde(default = "default_http_proxy_port")]
+ pub http_proxy_port: u16,
+ #[serde(default = "default_socks_proxy_port")]
+ pub socks_proxy_port: u16,
+ #[serde(default = "default_auto_update")]
+ pub auto_update: bool,
+}
+
+fn default_integrated_router() -> bool {
+ false
+}
+fn default_router_storage() -> String {
+ "/var/lib/mesk/router/".to_string()
+}
+fn default_http_proxy_port() -> u16 {
+ 4445
+}
+fn default_socks_proxy_port() -> u16 {
+ 4446
+}
+fn default_auto_update() -> bool {
+ true
+}
+
+#[derive(Deserialize, Debug, Serialize, Clone)]
pub struct Config {
pub repo: Repo,
pub log: Log,
pub paths: Paths,
+ #[serde(default = "RouterConfig::default")]
+ pub router: RouterConfig,
+}
+
+impl Default for RouterConfig {
+ fn default() -> Self {
+ Self {
+ integrated_router: default_integrated_router(),
+ storage_path: default_router_storage(),
+ http_proxy_port: default_http_proxy_port(),
+ socks_proxy_port: default_socks_proxy_port(),
+ auto_update: default_auto_update(),
+ }
+ }
}
#[derive(Deserialize, Debug, Serialize, Clone)]
@@ -107,6 +151,7 @@ impl Config {
build_dir: String::from("/var/lib/mesk"),
installed_db: String::from("/var/lib/mesk/pkgdb"),
},
+ router: RouterConfig::default(),
};
let toml_str = toml::to_string(&default)?;
@@ -119,16 +164,21 @@ impl Config {
buildir: &Option<String>,
installed_db: &Option<String>,
) -> Result<String, toml::ser::Error> {
- let generator: Config = Config {
+ let config = Config {
repo: Repo {
- repo_url: if repo.is_none() {
- format!("https://mesk.anthrill.i2p/repo/{}/", std::env::consts::ARCH)
- } else {
- repo.clone().unwrap()
- },
+ repo_url: repo
+ .as_deref()
+ .unwrap_or(&format!(
+ "https://mesk.anthrill.i2p/repo/{}/",
+ std::env::consts::ARCH
+ ))
+ .to_string(),
auto_update: true,
destination: (String::from("mesk"), String::from("mesk")),
- repo_http_url: None,
+ repo_http_url: repo.as_ref().map(|r| {
+ r.replace("i2p", "http")
+ .replace("b32.i2p", "github.com/Anthrill/repo-mirror")
+ }),
i2p_http_proxy_port: 4444,
},
log: Log {
@@ -136,27 +186,18 @@ impl Config {
log_level: Loglevel::Info,
},
paths: Paths {
- cache_dir: if cachedir.is_none() {
- String::from("/var/cache/mesk")
- } else {
- cachedir.clone().unwrap()
- },
- build_dir: if buildir.is_none() {
- String::from("/var/cache/mesk/build")
- } else {
- buildir.clone().unwrap()
- },
- installed_db: if installed_db.is_none() {
- String::from("/var/cache/mesk/pkgdb")
- } else {
- installed_db.clone().unwrap()
- },
- /*
- FIXME: I can leave this parameter, but I think it would be better to make the build
- path in the /var/cache/mesk/$pkgname-$pkgver/BUILD/
- */
+ cache_dir: cachedir
+ .clone()
+ .unwrap_or_else(|| "/var/cache/mesk".to_string()),
+ build_dir: buildir
+ .clone()
+ .unwrap_or_else(|| "/var/tmp/mesk/build".to_string()),
+ installed_db: installed_db
+ .clone()
+ .unwrap_or_else(|| "/var/lib/mesk/installed.db".to_string()),
},
+ router: RouterConfig::default(),
};
- toml::to_string(&generator)
+ toml::to_string(&config)
}
}
diff --git a/src/lib.rs b/src/lib.rs
index beaf75b..39c57cd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,3 +8,4 @@ pub mod net {
}
pub mod pkgtoolkit;
+pub mod router;
diff --git a/src/main.rs b/src/main.rs
index bbd4187..4e1402d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,22 +2,64 @@ mod cfg;
mod net;
mod openpgp;
mod pkgtoolkit;
-mod router;
+mod router;
use crate::cfg::config::Config;
use crate::net::{http_package::HTTPPackage, i2p_package::I2PPackage};
-use crate::openpgp::trusted;
use crate::pkgtoolkit::Package;
use crate::pkgtoolkit::archive::ArchiveOperations;
use crate::pkgtoolkit::build::BuildOperations;
use crate::pkgtoolkit::git_source::GitSource;
use crate::pkgtoolkit::index::IndexOperations;
use crate::pkgtoolkit::install::InstallOperations;
+use crate::router::router::{Emissary, EmissaryConfig, RouterUtils};
+use emissary_core::router::Router;
+use emissary_util::runtime::tokio::Runtime as TokioRuntime;
+use std::sync::Arc;
+use tokio::sync::Mutex;
use clap::{Args, Parser, Subcommand};
use std::io::Write;
use std::path::Path;
+lazy_static::lazy_static! {
+ static ref ROUTER: Arc<Mutex<Option<Router<TokioRuntime>>>> = Arc::new(Mutex::new(None));
+}
+
+async fn init_router(
+ config: &crate::cfg::config::Config,
+) -> Result<(), Box<dyn std::error::Error>> {
+ if !config.router.integrated_router {
+ return Ok(());
+ }
+
+ let router_config = EmissaryConfig {
+ storage: Some(std::path::PathBuf::from(&config.router.storage_path)),
+ auto_update: Some(config.router.auto_update),
+ http_proxy_port: Some(config.router.http_proxy_port),
+ socks_proxy_port: Some(config.router.socks_proxy_port),
+ };
+
+ let mut router = Emissary::new(router_config);
+
+ // Check if router storage exists and is not empty
+ let storage_path = std::path::Path::new(&config.router.storage_path);
+ if !storage_path.exists() || storage_path.read_dir()?.next().is_none() {
+ log::info!("Router storage is empty, performing initial reseed...");
+ router.config().await?;
+ router.reseed().await?;
+ } else {
+ router.config().await?;
+ }
+
+ // Start the router and store the Router instance
+ let router = router.start().await?;
+ *ROUTER.lock().await = Some(router);
+
+ log::info!("Integrated router initialized successfully");
+ Ok(())
+}
+
#[derive(Parser)]
struct Cli {
#[command(subcommand)]
@@ -80,97 +122,50 @@ struct RemoteInstallArgs {
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
- let cli: Cli = Cli::parse();
-
- match &cli.command {
- Commands::Validate { path } => {
- println!("Validating {}", path);
- match Package::check(path.to_string()) {
- Ok(is_valid) => {
- if is_valid {
- println!("Package archive is valid.");
- } else {
- println!("Package archive is invalid.");
- return Err(std::io::Error::other("Invalid package archive").into());
- }
- }
- Err(e) => {
- log::error!("Failed to validate package '{}': {}", path, e);
- return Err(e.into());
- }
- }
- return Ok(());
- }
- Commands::Build { pkgname } => {
- println!("Building package from archive: {}", pkgname);
-
- let path = Path::new(&pkgname);
- if !path.exists() {
- return Err(std::io::Error::other(format!(
- "Package archive not found: {}",
- pkgname
- ))
- .into());
- }
+ let config = crate::cfg::config::Config::parse()?;
- if !path.is_file() {
- return Err(
- std::io::Error::other(format!("Path is not a file: {}", pkgname)).into(),
- );
- }
+ init_router(&config).await?;
+ let cli: Cli = Cli::parse();
- println!("Extracting archive...");
- Package::extract_archive(pkgname)?;
-
- let config = Config::parse().unwrap();
- let cache_dir = &config.paths.cache_dir;
-
- // Find the package directory (should be name-version format)
- let mut pkg_dir_name = None;
- for entry in std::fs::read_dir(cache_dir)? {
- let entry = entry?;
- let path = entry.path();
- if path.is_dir() {
- let dir_name = path.file_name().unwrap().to_string_lossy();
- if dir_name.contains('-') && dir_name != "temp_extract" {
- let install_path = path.join("INSTALL");
- if install_path.exists() {
- pkg_dir_name = Some(dir_name.to_string());
- break;
+ let config = Arc::new(config);
+
+ let result = {
+ match &cli.command {
+ Commands::Validate { path } => {
+ println!("Validating {}", path);
+ match Package::check(path.to_string()) {
+ Ok(is_valid) => {
+ if is_valid {
+ println!("Package archive is valid.");
+ } else {
+ println!("Package archive is invalid.");
+ return Err(std::io::Error::other("Invalid package archive").into());
}
}
+ Err(e) => {
+ log::error!("Failed to validate package '{}': {}", path, e);
+ return Err(e.into());
+ }
}
+ return Ok(());
}
+ Commands::Build { pkgname } => {
+ println!("Building package from archive: {}", pkgname);
+
+ let path = Path::new(&pkgname);
+ if !path.exists() {
+ return Err(std::io::Error::other(format!(
+ "Package archive not found: {}",
+ pkgname
+ ))
+ .into());
+ }
- let pkg_dir_name = pkg_dir_name
- .ok_or_else(|| std::io::Error::other("Package directory not found in cache"))?;
-
- let install_toml_path = Path::new(cache_dir).join(format!("{}/INSTALL", pkg_dir_name));
-
- if !install_toml_path.exists() {
- return Err(
- std::io::Error::other("INSTALL file not found in package directory").into(),
- );
- }
-
- let install_content = std::fs::read_to_string(&install_toml_path)?;
- let install_data: crate::pkgtoolkit::types::Install = toml::from_str(&install_content)?;
-
- let mut pkg = install_data.package;
-
- println!("Building package '{}'...", pkg.name);
- pkg.build()?;
- println!("Package '{}' built successfully.", pkg.name);
- return Ok(());
- }
- Commands::Install {
- pkgname,
- source: _,
- path,
- args,
- } => {
- if *path {
- println!("Installing package from local file: {}", pkgname);
+ if !path.is_file() {
+ return Err(
+ std::io::Error::other(format!("Path is not a file: {}", pkgname)).into(),
+ );
+ }
println!("Extracting archive...");
Package::extract_archive(pkgname)?;
@@ -178,6 +173,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::parse().unwrap();
let cache_dir = &config.paths.cache_dir;
+ // Find the package directory (should be name-version format)
let mut pkg_dir_name = None;
for entry in std::fs::read_dir(cache_dir)? {
let entry = entry?;
@@ -199,6 +195,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let install_toml_path =
Path::new(cache_dir).join(format!("{}/INSTALL", pkg_dir_name));
+
+ if !install_toml_path.exists() {
+ return Err(std::io::Error::other(
+ "INSTALL file not found in package directory",
+ )
+ .into());
+ }
+
let install_content = std::fs::read_to_string(&install_toml_path)?;
let install_data: crate::pkgtoolkit::types::Install =
toml::from_str(&install_content)?;
@@ -207,107 +211,199 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Building package '{}'...", pkg.name);
pkg.build()?;
+ println!("Package '{}' built successfully.", pkg.name);
+ return Ok(());
+ }
+ Commands::Install {
+ pkgname,
+ source: _,
+ path,
+ args,
+ } => {
+ if *path {
+ println!("Installing package from local file: {}", pkgname);
+
+ println!("Extracting archive...");
+ Package::extract_archive(pkgname)?;
+
+ let config = Config::parse().unwrap();
+ let cache_dir = &config.paths.cache_dir;
+
+ let mut pkg_dir_name = None;
+ for entry in std::fs::read_dir(cache_dir)? {
+ let entry = entry?;
+ let path = entry.path();
+ if path.is_dir() {
+ let dir_name = path.file_name().unwrap().to_string_lossy();
+ if dir_name.contains('-') && dir_name != "temp_extract" {
+ let install_path = path.join("INSTALL");
+ if install_path.exists() {
+ pkg_dir_name = Some(dir_name.to_string());
+ break;
+ }
+ }
+ }
+ }
+
+ let pkg_dir_name = pkg_dir_name.ok_or_else(|| {
+ std::io::Error::other("Package directory not found in cache")
+ })?;
+
+ let install_toml_path =
+ Path::new(cache_dir).join(format!("{}/INSTALL", pkg_dir_name));
+ let install_content = std::fs::read_to_string(&install_toml_path)?;
+ let install_data: crate::pkgtoolkit::types::Install =
+ toml::from_str(&install_content)?;
- println!("Installing package '{}'...", pkg.name);
- pkg.install()?;
- println!("Package '{}' installed successfully.", pkg.name);
- } else {
+ let mut pkg = install_data.package;
+
+ println!("Building package '{}'...", pkg.name);
+ pkg.build()?;
+
+ println!("Installing package '{}'...", pkg.name);
+ pkg.install()?;
+ println!("Package '{}' installed successfully.", pkg.name);
+ } else {
+ let config = Config::parse().unwrap();
+ if args.http {
+ println!("Installing {} via HTTP", pkgname);
+ let mut http_client = HTTPPackage::new(config);
+ http_client.fetch_index_http().await?;
+ log::info!("Index fetched successfully.");
+ http_client.fetch_package_http(pkgname).await?;
+ log::info!("Package '{}' installed successfully.", pkgname);
+ } else {
+ println!("Installing {} via I2P", pkgname);
+ let mut i2p_client = I2PPackage::new(config);
+ i2p_client.fetch_index().await?;
+ log::info!("Index fetched successfully.");
+ i2p_client.fetch_package(pkgname).await?;
+ log::info!("Package '{}' installed successfully.", pkgname);
+ }
+ }
+ return Ok(());
+ }
+ Commands::Uninstall { pkgname } => {
+ println!("Uninstalling package: {}", pkgname);
+
+ let installed_packages = Package::list_installed_packages().map_err(|e| {
+ log::error!("Failed to list installed packages: {}", e);
+ e
+ })?;
+
+ let package_manifest = installed_packages
+ .iter()
+ .find(|p| p.name == *pkgname)
+ .ok_or_else(|| {
+ std::io::Error::other(format!("Package '{}' is not installed", pkgname))
+ })?;
+
+ let package = Package {
+ name: package_manifest.name.clone(),
+ version: package_manifest.version.clone(),
+ ..Default::default()
+ };
+
+ match package.uninstall() {
+ Ok(true) => {
+ println!("Successfully uninstalled package: {}", pkgname);
+ Ok(())
+ }
+ Ok(false) => {
+ log::warn!(
+ "Some files could not be removed during uninstallation of {}",
+ pkgname
+ );
+ Err(std::io::Error::other("Partial uninstallation occurred").into())
+ }
+ Err(e) => {
+ log::error!("Failed to uninstall package {}: {}", pkgname, e);
+ Err(e.into())
+ }
+ }
+ }
+ Commands::GetSource { pkgname } => {
let config = Config::parse().unwrap();
- if args.http {
- println!("Installing {} via HTTP", pkgname);
- let mut http_client = HTTPPackage::new(config);
- http_client.fetch_index_http().await?;
- log::info!("Index fetched successfully.");
- http_client.fetch_package_http(pkgname).await?;
- log::info!("Package '{}' installed successfully.", pkgname);
+ println!("Getting source of {}", pkgname);
+
+ let source_path = GitSource::get_source_by_name(pkgname, &config)?;
+ println!("Source code successfully downloaded to: {}", source_path);
+ return Ok(());
+ }
+ Commands::DefaultConfig {
+ repo,
+ cachedir,
+ buildir,
+ installed_db,
+ } => {
+ println!("Generating config file");
+ if cachedir.is_none() && repo.is_none() && buildir.is_none() {
+ let config = Config::default().unwrap();
+
+ println!("---- Start of generated config ----");
+ println!("{}", config);
+ println!("---- End of generated config ----");
+
+ log::warn!("Writing the default config to /etc/mesk/mesk.toml");
+
+ let path = Path::new("/etc/mesk/mesk.toml");
+ std::fs::create_dir_all(path.parent().unwrap())?;
+ let mut file = std::fs::File::create(path)?;
+ file.write_all(config.as_bytes())?;
+ println!("Config tool ending work.");
} else {
- println!("Installing {} via I2P", pkgname);
- let mut i2p_client = I2PPackage::new(config);
- i2p_client.fetch_index().await?;
- log::info!("Index fetched successfully.");
- i2p_client.fetch_package(pkgname).await?;
- log::info!("Package '{}' installed successfully.", pkgname);
+ let config = Config::generate(repo, cachedir, buildir, installed_db).unwrap();
+
+ println!("---- Start of generated config ----");
+ println!("{:?}", config);
+ println!("---- End of generated config ----");
+
+ log::warn!("Writing the default config to /etc/mesk/mesk.toml");
+
+ let path = Path::new("/etc/mesk/mesk.toml");
+ std::fs::create_dir_all(path.parent().unwrap())?;
+ let mut file = std::fs::File::create(path)?;
+ file.write_all(config.as_bytes())?;
+ println!("Config tool ending work.");
}
+ return Ok(());
}
- return Ok(());
- }
- Commands::Uninstall { pkgname } => {
- println!("Uninstalling {}", pkgname);
- return Ok(());
- }
- Commands::GetSource { pkgname } => {
- let config = Config::parse().unwrap();
- println!("Getting source of {}", pkgname);
+ Commands::Update => {
+ let config = Config::parse().unwrap();
+ println!("Updating index from {}", config.repo.repo_url);
- let source_path = GitSource::get_source_by_name(pkgname, &config)?;
- println!("Source code successfully downloaded to: {}", source_path);
- return Ok(());
- }
- Commands::DefaultConfig {
- repo,
- cachedir,
- buildir,
- installed_db,
- } => {
- println!("Generating config file");
- if cachedir.is_none() && repo.is_none() && buildir.is_none() {
- let config = Config::default().unwrap();
-
- println!("---- Start of generated config ----");
- println!("{}", config);
- println!("---- End of generated config ----");
-
- log::warn!("Writing the default config to /etc/mesk/mesk.toml");
-
- let path = Path::new("/etc/mesk/mesk.toml");
- std::fs::create_dir_all(path.parent().unwrap())?;
- let mut file = std::fs::File::create(path)?;
- file.write_all(config.as_bytes())?;
- println!("Config tool ending work.");
- } else {
- let config = Config::generate(repo, cachedir, buildir, installed_db).unwrap();
-
- println!("---- Start of generated config ----");
- println!("{:?}", config);
- println!("---- End of generated config ----");
-
- log::warn!("Writing the default config to /etc/mesk/mesk.toml");
-
- let path = Path::new("/etc/mesk/mesk.toml");
- std::fs::create_dir_all(path.parent().unwrap())?;
- let mut file = std::fs::File::create(path)?;
- file.write_all(config.as_bytes())?;
- println!("Config tool ending work.");
+ let mut i2p_client = I2PPackage::new(config);
+ i2p_client.fetch_index().await?;
+ println!("Index updated successfully.");
+ return Ok(());
}
- return Ok(());
- }
- Commands::Update => {
- let config = Config::parse().unwrap();
- println!("Updating index from {}", config.repo.repo_url);
-
- let mut i2p_client = I2PPackage::new(config);
- i2p_client.fetch_index().await?;
- println!("Index updated successfully.");
- return Ok(());
- }
- Commands::Upgrade { pkgname } => {
- println!("Upgrading {}", pkgname.as_deref().unwrap_or("all packages"));
- return Ok(());
- }
- Commands::Credits => {
- println!(
- "CREATED BY: Asya and Namilsk as part of the Anthrill independent Global network distribution project"
- );
- println!(" ");
- println!("The Anthrill project repos: https://codeberg.org/NamelessTeam ");
- return Ok(());
- }
- Commands::GenIndex { path } => {
- println!("Generating index for {}", path);
+ Commands::Upgrade { pkgname } => {
+ println!("Upgrading {}", pkgname.as_deref().unwrap_or("all packages"));
+ return Ok(());
+ }
+ Commands::Credits => {
+ println!(
+ "CREATED BY: Namilsk as part of the Anthrill independent Global network distribution project"
+ );
+ println!(" ");
+ println!("The Anthrill project repos: https://codeberg.org/NamelessTeam ");
+ return Ok(());
+ }
+ Commands::GenIndex { path } => {
+ println!("Generating index for {}", path);
- Package::gen_index(path)?;
- println!("Index generated successfully.");
- return Ok(());
+ Package::gen_index(path)?;
+ println!("Index generated successfully.");
+ return Ok(());
+ }
}
+ };
+
+ // Shutdown router if it was initialized
+ if let Some(router) = ROUTER.lock().await.take() {
+ // TODO: Add proper router shutdown when implemented
+ log::info!("Shutting down integrated router...");
}
+
+ result
}
diff --git a/src/openpgp/signatures.rs b/src/openpgp/signatures.rs
index ac21d2b..63860f0 100644
--- a/src/openpgp/signatures.rs
+++ b/src/openpgp/signatures.rs
@@ -1 +1,2 @@
// Worker with signs-db
+// todo!("Keys from keyserver, signatures from db");
diff --git a/src/pkgtoolkit/build.rs b/src/pkgtoolkit/build.rs
index 8c5119c..8a46809 100644
--- a/src/pkgtoolkit/build.rs
+++ b/src/pkgtoolkit/build.rs
@@ -145,10 +145,10 @@ impl BuildOperations for Package {
search_dir.join(pattern).to_string_lossy().into_owned()
};
- let entries = glob(&glob_pattern)
+ let mut entries = glob(&glob_pattern)
.map_err(|e| std::io::Error::other(format!("Invalid glob pattern: {}", e)))?;
- for entry in entries {
+ if let Some(entry) = entries.next() {
let path =
entry.map_err(|e| std::io::Error::other(format!("Glob error: {}", e)))?;
return Ok(Some(path));
@@ -237,7 +237,7 @@ impl BuildOperations for Package {
match build_meta.build_system {
BuildSystems::Make => {
let found = self
- .find_makefile(&build_meta, &build_dir)
+ .find_makefile(build_meta, &build_dir)
.map_err(|e| {
std::io::Error::other(format!("Failed to search for Makefile: {}", e))
})?
@@ -261,7 +261,7 @@ impl BuildOperations for Package {
}
BuildSystems::CMake => {
let found = self
- .find_makefile(&build_meta, &build_dir)
+ .find_makefile(build_meta, &build_dir)
.map_err(|e| {
std::io::Error::other(format!("Failed to search for CMakeLists: {}", e))
})?
diff --git a/src/pkgtoolkit/install.rs b/src/pkgtoolkit/install.rs
index 4eeff2f..7ba0598 100644
--- a/src/pkgtoolkit/install.rs
+++ b/src/pkgtoolkit/install.rs
@@ -1,17 +1,31 @@
use crate::cfg::config::Config;
+use crate::router::manager::RouterManager;
use std::{
fs::{self, Permissions, create_dir_all, set_permissions},
os::unix::fs::PermissionsExt,
- path::{Path, StripPrefixError},
+ path::Path,
+ path::StripPrefixError,
process::Command,
};
+/// Arguments for remote package installation
+#[derive(Clone)]
+pub struct RemoteInstallArgs {
+ /// Install binary package
+ pub bin: bool,
+ /// Use non-I2P mirror
+ pub http: bool,
+ /// Clean cache before install
+ pub clean: bool,
+}
+
use toml;
use super::archive::ArchiveOperations;
use super::build::BuildOperations;
use super::types::{Package, PackageManifest};
+#[allow(dead_code)]
pub trait InstallOperations {
fn collect_files_from_dir(root: &Path, base: &Path) -> Result<Vec<String>, std::io::Error>;
fn install(&mut self) -> Result<bool, std::io::Error>;
@@ -19,9 +33,37 @@ pub trait InstallOperations {
fn load_manifest(&self) -> Result<PackageManifest, std::io::Error>;
fn list_installed_packages() -> Result<Vec<PackageManifest>, std::io::Error>;
fn is_installed(&self) -> Result<bool, std::io::Error>;
+
+ /// Installs a package using the specified parameters
+ ///
+ /// # Arguments
+ /// * `pkgname` - Name of the package to install
+ /// * `source` - Optional source URL or path
+ /// * `is_path` - Whether the source is a local path
+ /// * `args` - Additional installation arguments
+ /// * `router_manager` - Optional router manager for I2P connections
+ async fn install_package(
+ pkgname: &str,
+ source: Option<&str>,
+ is_path: bool,
+ args: &RemoteInstallArgs,
+ router_manager: &RouterManager,
+ ) -> Result<(), Box<dyn std::error::Error>>;
}
impl InstallOperations for Package {
+ async fn install_package(
+ pkgname: &str,
+ source: Option<&str>,
+ is_path: bool,
+ args: &RemoteInstallArgs,
+ router_manager: &RouterManager,
+ ) -> Result<(), Box<dyn std::error::Error>> {
+ // Implementation of install_package
+ // TODO: Add actual implementation based on your requirements
+ Ok(())
+ }
+
/// Recursively collects all files from a directory and its subdirectories
/// and returns them as a vector of strings.
///
diff --git a/src/pkgtoolkit/types.rs b/src/pkgtoolkit/types.rs
index 0439175..9d8f915 100644
--- a/src/pkgtoolkit/types.rs
+++ b/src/pkgtoolkit/types.rs
@@ -1,8 +1,9 @@
use serde::{Deserialize, Serialize};
use std::fmt;
-#[derive(Serialize, Debug, Deserialize, Clone, PartialEq)]
+#[derive(Serialize, Debug, Deserialize, Clone, PartialEq, Default)]
pub enum Archs {
+ #[default]
X86_64,
Aarch64,
X86,
@@ -10,7 +11,7 @@ pub enum Archs {
ArmV8,
}
-#[derive(Serialize, Debug, Deserialize, Clone)]
+#[derive(Serialize, Debug, Deserialize, Clone, Default)]
pub struct Package {
pub name: String,
pub version: String,
diff --git a/src/router/manager.rs b/src/router/manager.rs
new file mode 100644
index 0000000..23b7c69
--- /dev/null
+++ b/src/router/manager.rs
@@ -0,0 +1,144 @@
+use crate::router::router::RouterUtils;
+use crate::router::router::{Emissary, EmissaryConfig};
+use log::{debug, error, info};
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use tokio::sync::mpsc;
+use tokio::task::JoinHandle;
+
+#[derive(Debug)]
+pub enum RouterMessage {
+ Start,
+ Stop,
+ Status,
+}
+
+pub struct RouterManager {
+ handle: Option<JoinHandle<()>>,
+ tx: mpsc::Sender<RouterMessage>,
+ is_running: Arc<AtomicBool>,
+}
+
+impl RouterManager {
+ pub async fn new(
+ config: &crate::cfg::config::Config,
+ ) -> Result<Self, Box<dyn std::error::Error>> {
+ if !config.router.integrated_router {
+ return Ok(Self {
+ handle: None,
+ tx: {
+ let (tx, _) = mpsc::channel(1);
+ tx
+ },
+ is_running: Arc::new(AtomicBool::new(false)),
+ });
+ }
+
+ let (tx, mut rx) = mpsc::channel::<RouterMessage>(1);
+ let is_running = Arc::new(AtomicBool::new(false));
+ let is_running_clone = is_running.clone();
+
+ let router_config = config.router.clone();
+ let handle = tokio::spawn(async move {
+ info!("Router manager task started");
+
+ let mut router = Emissary::new(EmissaryConfig {
+ storage: Some(std::path::PathBuf::from(&router_config.storage_path)),
+ auto_update: Some(router_config.auto_update),
+ http_proxy_port: Some(router_config.http_proxy_port),
+ socks_proxy_port: Some(router_config.socks_proxy_port),
+ });
+
+ if let Err(e) = router.config().await {
+ error!("Failed to configure router: {}", e);
+ return;
+ }
+
+ let storage_path = std::path::Path::new(&router_config.storage_path);
+ if !storage_path.exists()
+ || storage_path
+ .read_dir()
+ .ok()
+ .is_none_or(|mut d| d.next().is_none())
+ {
+ info!("Router storage is empty, performing initial reseed...");
+ if let Err(e) = router.reseed().await {
+ error!("Failed to reseed router: {}", e);
+ return;
+ }
+ }
+
+ let router_task = tokio::spawn(async move {
+ info!("Starting router service...");
+ match router.start().await {
+ Ok(_) => info!("Router service stopped"),
+ Err(e) => error!("Router service error: {}", e),
+ }
+ });
+
+ is_running_clone.store(true, Ordering::SeqCst);
+ info!("Router manager is now running");
+
+ while let Some(msg) = rx.recv().await {
+ match msg {
+ RouterMessage::Stop => {
+ info!("Stopping router...");
+ // TODO: Implement proper router shutdown
+ is_running_clone.store(false, Ordering::SeqCst);
+ break;
+ }
+ RouterMessage::Status => {
+ debug!(
+ "Router status: {}",
+ if is_running_clone.load(Ordering::SeqCst) {
+ "running"
+ } else {
+ "stopped"
+ }
+ );
+ }
+ _ => {}
+ }
+ }
+
+ if let Err(e) = router_task.await {
+ error!("Router task panicked: {}", e);
+ }
+
+ info!("Router manager task finished");
+ });
+
+ tokio::time::sleep(std::time::Duration::from_secs(1)).await;
+
+ Ok(Self {
+ handle: Some(handle),
+ tx,
+ is_running,
+ })
+ }
+
+ pub async fn stop(&self) {
+ if self.is_running.load(Ordering::SeqCst) {
+ if let Err(e) = self.tx.send(RouterMessage::Stop).await {
+ error!("Failed to send stop message to router: {}", e);
+ }
+ }
+ }
+
+ pub fn is_running(&self) -> bool {
+ self.is_running.load(Ordering::SeqCst)
+ }
+}
+
+impl Drop for RouterManager {
+ fn drop(&mut self) {
+ if self.is_running() {
+ let tx = self.tx.clone();
+ tokio::spawn(async move {
+ if let Err(e) = tx.send(RouterMessage::Stop).await {
+ error!("Failed to send stop message during drop: {}", e);
+ }
+ });
+ }
+ }
+}
diff --git a/src/router/mod.rs b/src/router/mod.rs
index 772c177..91da2e5 100644
--- a/src/router/mod.rs
+++ b/src/router/mod.rs
@@ -1,2 +1,2 @@
+pub mod manager;
pub mod router;
-
diff --git a/src/router/router.rs b/src/router/router.rs
index c438d75..7c36382 100644
--- a/src/router/router.rs
+++ b/src/router/router.rs
@@ -1,92 +1,149 @@
-use emissary_util::storage::{Storage, StorageBundle};
+use emissary_core::router::Router;
+use emissary_core::{Config, Ntcp2Config, SamConfig, TransitConfig};
+use emissary_util::port_mapper::{PortMapper, PortMapperConfig};
use emissary_util::reseeder::Reseeder;
+use emissary_util::runtime::tokio::Runtime as TokioRuntime;
+use emissary_util::storage::{Storage, StorageBundle};
+use serde::{Deserialize, Serialize};
use std::path::PathBuf;
+use std::sync::Arc;
+use std::future::Future;
-
-pub trait RouterConfigUtils {
- async fn config(&mut self) -> Result<(StorageBundle, Storage), Box<dyn std::error::Error>>;
- async fn prepare_router(&mut self) -> Result<(), Box<dyn std::error::Error>>;
- async fn reseed(&mut self, config: &StorageBundle, storage: &Storage) -> Result<(), Box<dyn std::error::Error>>;
+pub trait RouterUtils {
+ fn config(&mut self) -> impl Future<Output = Result<(), Box<dyn std::error::Error>>> + Send;
+ fn reseed(&mut self) -> impl Future<Output = Result<(), Box<dyn std::error::Error>>> + Send;
+ fn start(self) -> impl Future<Output = Result<Router<TokioRuntime>, Box<dyn std::error::Error>>> + Send;
}
+#[derive(Debug, Deserialize, Serialize)]
pub struct EmissaryConfig {
- Storage: Option<PathBuf>,
- AutoUpdate: Option<bool>,
- HttpProxtPort: Option<u16>,
- SocksProxyPort: Option<u16>,
+ pub storage: Option<PathBuf>,
+ pub auto_update: Option<bool>,
+ pub http_proxy_port: Option<u16>,
+ pub socks_proxy_port: Option<u16>,
}
-impl RouterConfigUtils for EmissaryConfig {
- /// Configures the router with the given configuration.
- ///
- /// If the `Storage` field is `None`, it will be set to `/var/lib/mesk/router/`.
- ///
- /// # Errors
- ///
- /// Returns an error if there's an issue while configuring the router.
- async fn config(&mut self) -> Result<(StorageBundle, Storage), Box<dyn std::error::Error>> {
- self.Storage.get_or_insert_with(|| PathBuf::from("/var/lib/mesk/router/"));
- self.AutoUpdate.get_or_insert(true);
- self.HttpProxtPort.get_or_insert(4445);
- self.SocksProxyPort.get_or_insert(4446);
-
- let storage = Storage::new(self.Storage.clone().into()).await?;
-
- let StorageBundle {
- ntcp2_iv,
- ntcp2_key,
- profiles,
- router_info,
- routers,
- signing_key,
- static_key,
- ssu2_intro_key,
- ssu2_static_key,
- } = storage.load().await;
-
- Ok((StorageBundle {
- ntcp2_iv,
- ntcp2_key,
- profiles,
- router_info,
- routers,
- signing_key,
- static_key,
- ssu2_intro_key,
- ssu2_static_key,
- }, storage))
+pub struct Emissary {
+ config: EmissaryConfig,
+ storage: Option<Storage>,
+ storage_bundle: Option<StorageBundle>,
+}
+
+impl Emissary {
+ pub fn new(config: EmissaryConfig) -> Self {
+ Self {
+ config,
+ storage: None,
+ storage_bundle: None,
+ }
+ }
+
+ fn ensure_configured(&self) -> Result<(), Box<dyn std::error::Error>> {
+ if self.storage.is_none() || self.storage_bundle.is_none() {
+ Err("Router not configured. Call `config()` first.".into())
+ } else {
+ Ok(())
+ }
+ }
+}
+
+impl RouterUtils for Emissary {
+ async fn config(&mut self) -> Result<(), Box<dyn std::error::Error>> {
+ if self.config.storage.is_none() {
+ self.config.storage = Some(PathBuf::from("/var/lib/mesk/router/"));
+ }
+ if self.config.auto_update.is_none() {
+ self.config.auto_update = Some(true);
+ }
+ if self.config.http_proxy_port.is_none() {
+ self.config.http_proxy_port = Some(4445);
+ }
+ if self.config.socks_proxy_port.is_none() {
+ self.config.socks_proxy_port = Some(4446);
+ }
+
+ let storage = Storage::new(Some(self.config.storage.clone().unwrap())).await?;
+ let bundle = storage.load().await;
+
+ self.storage = Some(storage);
+ self.storage_bundle = Some(bundle);
+
+ Ok(())
}
- async fn reseed(&mut self, config: &StorageBundle, storage: &Storage) -> Result<(), Box<dyn std::error::Error>> {
- let mut routers = config.routers.clone();
-
-
- if routers.is_empty() {
+ async fn reseed(&mut self) -> Result<(), Box<dyn std::error::Error>> {
+ self.ensure_configured()?;
+
+ let storage = self.storage.as_ref().unwrap();
+ let mut bundle = self.storage_bundle.take().unwrap();
+
+ if bundle.routers.is_empty() {
match Reseeder::reseed(None, false).await {
Ok(reseed_routers) => {
for info in reseed_routers {
- let _ = storage
+ if let Err(e) = storage
.store_router_info(info.name.to_string(), info.router_info.clone())
- .await;
- routers.push(info.router_info);
+ .await
+ {
+ log::warn!("Failed to store reseeded router info: {}", e);
+ }
+ bundle.routers.push(info.router_info);
}
}
- Err(_) if routers.is_empty() => {
- return Err("Could not reseed routers.".into());
- }
- Err(error) => {
- log::warn!(
- "Failed to reseed routers: {} - using existing routers", error.to_string(),
- );
+ Err(e) => {
+ if bundle.routers.is_empty() {
+ self.storage_bundle = Some(bundle);
+ return Err(format!("Reseed failed and no routers available: {}", e).into());
+ } else {
+ log::warn!("Reseed failed, but using existing routers: {}", e);
+ }
}
}
- }
+ }
+
+ self.storage_bundle = Some(bundle);
Ok(())
}
- async fn prepare_router(&mut self) -> Result<(), Box<dyn std::error::Error>> {
-
-
- Ok(())
+ async fn start(self) -> Result<Router<TokioRuntime>, Box<dyn std::error::Error>> {
+ let storage = self.storage.unwrap();
+ let bundle = self.storage_bundle.unwrap();
+
+ let config = Config {
+ ntcp2: Some(Ntcp2Config {
+ port: 25515,
+ key: bundle.ntcp2_key,
+ iv: bundle.ntcp2_iv,
+ publish: true,
+ host: None,
+ }),
+ samv3_config: Some(SamConfig {
+ tcp_port: self.config.http_proxy_port.unwrap_or(4445),
+ udp_port: self.config.socks_proxy_port.unwrap_or(4446),
+ host: "127.0.0.1".to_string(),
+ }),
+ routers: bundle.routers,
+ profiles: bundle.profiles,
+ router_info: bundle.router_info,
+ static_key: Some(bundle.static_key),
+ signing_key: Some(bundle.signing_key),
+ transit: Some(TransitConfig {
+ max_tunnels: Some(1000),
+ }),
+ ..Default::default()
+ };
+
+ let (router, _events, router_info) = Router::<TokioRuntime>::new(
+ config,
+ None, // AddressBook
+ Some(Arc::new(storage.clone())),
+ )
+ .await
+ .map_err(|e| format!("Router creation failed: {}", e))?;
+
+ // Сохраняем router_info, если он новый
+ storage.store_local_router_info(router_info).await?;
+
+ Ok(router)
}
-} \ No newline at end of file
+}