diff --git a/src/asset/libc.rs b/src/asset/libc.rs index 3d2a0f9..9bbeddd 100644 --- a/src/asset/libc.rs +++ b/src/asset/libc.rs @@ -1,3 +1,5 @@ +use anyhow::Result; + #[cfg(target_arch = "x86_64")] #[derive(rust_embed::RustEmbed)] #[folder = "src/libc/x86_64/"] @@ -8,8 +10,7 @@ struct Asset; #[folder = "src/libc/aarch64/"] struct Asset; -#[cfg(target_os = "linux")] -pub(crate) fn ld_env(envs: &mut std::collections::HashMap) -> anyhow::Result<()> { +pub(crate) fn ld_env(envs: &mut std::collections::HashMap) -> Result<()> { use crate::{constant, util}; use anyhow::Context; use std::ops::Not; diff --git a/src/asset/mod.rs b/src/asset/mod.rs index 4ab9b81..5e9c6fe 100644 --- a/src/asset/mod.rs +++ b/src/asset/mod.rs @@ -1,3 +1,3 @@ -#[cfg(all(target_os = "linux", target_env = "musl"))] +#[cfg(target_os = "linux")] pub mod libc; pub mod thunder; diff --git a/src/asset/thunder.rs b/src/asset/thunder.rs index ed6da2e..6f17c47 100644 --- a/src/asset/thunder.rs +++ b/src/asset/thunder.rs @@ -1,3 +1,5 @@ +use anyhow::Context; +use anyhow::Result; use core::str; use std::{ borrow::Cow, @@ -6,8 +8,6 @@ use std::{ ops::Not, path::{Path, PathBuf}, }; - -use anyhow::Context; use tar::Archive; pub struct Asset { @@ -31,7 +31,7 @@ impl Asset { }) } - pub fn init(&self) -> anyhow::Result<()> { + pub fn init(&self) -> Result<()> { match self.package { Some(ref filepath) => { // check filepath is exists @@ -90,7 +90,7 @@ impl Asset { } } - fn decompressor>(dir: T, archive_path: T) -> anyhow::Result<()> { + fn decompressor>(dir: T, archive_path: T) -> Result<()> { const PACKAGE_XZ: &str = "package.tgz"; const PACKAGE_TAR: &str = "package.tar"; @@ -158,7 +158,7 @@ impl Asset { Ok(()) } - fn copy_write(mut src: impl Read, dest: &mut File) -> anyhow::Result<()> { + fn copy_write(mut src: impl Read, dest: &mut File) -> Result<()> { let mut buffer = [0; 1024]; loop { diff --git a/src/daemon.rs b/src/daemon.rs index c581354..f8df151 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,3 +1,4 @@ +use anyhow::Result; use daemonize::Daemonize; use std::{ fs::{File, Permissions}, @@ -22,7 +23,7 @@ pub fn check_root() { } /// Get the pid of the daemon -pub(crate) fn get_pid() -> Option { +pub fn get_pid() -> Option { if let Ok(data) = std::fs::read(PID_PATH) { let binding = String::from_utf8(data).expect("pid file is not utf8"); return Some(binding.trim().to_string()); @@ -31,7 +32,7 @@ pub(crate) fn get_pid() -> Option { } /// Start the daemon -pub(super) fn start() -> anyhow::Result<()> { +pub fn start() -> Result<()> { if let Some(pid) = get_pid() { println!("Thunder is already running with pid: {}", pid); return Ok(()); @@ -73,7 +74,7 @@ pub(super) fn start() -> anyhow::Result<()> { } /// Stop the daemon -pub(super) fn stop() -> anyhow::Result<()> { +pub fn stop() -> Result<()> { use nix::sys::signal; use nix::unistd::Pid; @@ -94,7 +95,7 @@ pub(super) fn stop() -> anyhow::Result<()> { } /// Show the status of the daemon -pub(super) fn status() -> anyhow::Result<()> { +pub fn status() -> Result<()> { match get_pid() { Some(pid) => println!("Thunder is running with pid: {}", pid), None => println!("Thunder is not running"), @@ -103,8 +104,8 @@ pub(super) fn status() -> anyhow::Result<()> { } /// Show the log of the daemon -pub(super) fn log() -> anyhow::Result<()> { - fn read_and_print_file(file_path: &Path, placeholder: &str) -> anyhow::Result<()> { +pub fn log() -> Result<()> { + fn read_and_print_file(file_path: &Path, placeholder: &str) -> Result<()> { if !file_path.exists() { return Ok(()); } diff --git a/src/install.rs b/src/install.rs index e0dbfbb..aa20ecb 100644 --- a/src/install.rs +++ b/src/install.rs @@ -2,20 +2,20 @@ use std::ops::Not; use std::path::Path; use std::path::PathBuf; -use anyhow::Context; -use rand::Rng; - use crate::asset::thunder::Asset; use crate::constant; use crate::util; use crate::InstallConfig; use crate::Running; +use anyhow::Context; +use anyhow::Result; +use rand::Rng; /// Install xunlei pub struct XunleiInstall(pub InstallConfig); impl Running for XunleiInstall { - fn run(self) -> anyhow::Result<()> { + fn run(self) -> Result<()> { // If the package is already installed, skip the installation if Path::new(constant::SYNOPKG_VAR).exists() { println!("Thunder already installed"); @@ -66,6 +66,8 @@ impl Running for XunleiInstall { let target_dir = PathBuf::from(constant::SYNOPKG_PKGDEST); // /var/packages/pan-xunlei-com/target/host let host_dir = PathBuf::from(constant::SYNOPKG_HOST); + // If Synology NAS is not installed, the backend service will not be started + let var_path = Path::new(constant::SYNOPKG_VAR); // uid and gid let uid = self.0.uid; @@ -73,6 +75,12 @@ impl Running for XunleiInstall { util::create_dir_all(&target_dir, 0o755)?; + // path: /var/packages/pan-xunlei-com/target/var + if !var_path.exists() { + util::create_dir_all(var_path, 0o777)?; + util::chown(var_path, uid, gid)?; + } + // download xunlei binary let xunlei = Asset::new(self.0.package)?; xunlei.init()?; @@ -175,7 +183,7 @@ impl Running for XunleiInstall { pub struct XunleiUninstall(pub Option); impl Running for XunleiUninstall { - fn run(self) -> anyhow::Result<()> { + fn run(self) -> Result<()> { // path: /var/packages/pan-xunlei-com let path = Path::new(constant::SYNOPKG_PKGBASE); if path.exists() { diff --git a/src/main.rs b/src/main.rs index c099b0b..1309a66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,13 +9,14 @@ mod install; mod serve; pub mod util; +use anyhow::Result; use clap::{Args, Parser, Subcommand}; use std::io::{BufRead, Write}; use std::net::SocketAddr; use std::path::{Path, PathBuf}; pub trait Running { - fn run(self) -> anyhow::Result<()>; + fn run(self) -> Result<()>; } #[derive(Parser)] @@ -69,7 +70,7 @@ impl InstallConfig { const PATH: &'static str = "/etc/.thunder"; /// Remove config file - pub fn remove_file(self) -> anyhow::Result<()> { + pub fn remove_file(self) -> Result<()> { let path = Path::new(Self::PATH); if path.exists() { std::fs::remove_file(&Self::PATH)?; @@ -78,7 +79,7 @@ impl InstallConfig { } /// Write to file - fn write_to_file(&self) -> anyhow::Result<()> { + fn write_to_file(&self) -> Result<()> { let path = Path::new(Self::PATH); if !path.exists() { let mut file = std::fs::File::create(path)?; @@ -175,7 +176,7 @@ pub struct ServeConfig { tls_key: Option, } -fn main() -> anyhow::Result<()> { +fn main() -> Result<()> { let opt = Opt::parse(); match opt.commands { @@ -187,12 +188,16 @@ fn main() -> anyhow::Result<()> { let install_config = InstallConfig::read_from_file().map_or(None, |v| Some(v)); install::XunleiUninstall(install_config).run()?; } - Commands::Run(config) => { - serve::Serve::new(config, InstallConfig::read_from_file()?).run()?; + Commands::Run(server_config) => { + let install_config = InstallConfig::read_from_file()?; + before_action(&install_config)?; + serve::Serve::new(server_config, install_config).run()?; } - Commands::Start(config) => { + Commands::Start(server_config) => { + let install_config = InstallConfig::read_from_file()?; + before_action(&install_config)?; daemon::start()?; - serve::Serve::new(config, InstallConfig::read_from_file()?).run()?; + serve::Serve::new(server_config, install_config).run()?; } Commands::Stop => { daemon::stop()?; @@ -206,3 +211,29 @@ fn main() -> anyhow::Result<()> { } Ok(()) } + +/// Running before the daemon starts, execute the following code +fn before_action(install_config: &InstallConfig) -> Result<()> { + #[cfg(target_os = "linux")] + use nix::mount::MsFlags; + + #[cfg(target_os = "linux")] + let _ = nix::mount::umount(&install_config.mount_bind_download_path); + #[cfg(target_os = "linux")] + if nix::mount::mount( + Some(&install_config.download_path), + &install_config.mount_bind_download_path, + >::None, + MsFlags::MS_BIND, + >::None, + ) + .is_err() + { + anyhow::bail!( + "Mount {} to {} failed", + install_config.download_path.display(), + install_config.mount_bind_download_path.display() + ); + } + Ok(()) +} diff --git a/src/serve/auth/token.rs b/src/serve/auth/token.rs index 0616141..d92c274 100644 --- a/src/serve/auth/token.rs +++ b/src/serve/auth/token.rs @@ -1,8 +1,8 @@ +use super::{CHECK_AUTH, EXP, TOKEN_SECRET}; +use anyhow::Result; use jsonwebtokens::{encode, Algorithm, AlgorithmID, Verifier}; use std::{collections::HashMap, time::Duration}; -use super::{CHECK_AUTH, EXP, TOKEN_SECRET}; - fn get_or_init_secret() -> &'static String { TOKEN_SECRET.get_or_init(|| { let secret = if let Some(Some(auth_password)) = CHECK_AUTH.get() { @@ -27,7 +27,7 @@ pub fn generate_token() -> anyhow::Result { Ok(encode(&header, &claims, &alg)?) } -pub fn verifier(token_str: &str) -> anyhow::Result<()> { +pub fn verifier(token_str: &str) -> Result<()> { let s = get_or_init_secret(); let alg = Algorithm::new_hmac(AlgorithmID::HS256, s.to_owned())?; let verifier = Verifier::create().build()?; diff --git a/src/serve/backend.rs b/src/serve/backend.rs index f2d146e..03f71e0 100644 --- a/src/serve/backend.rs +++ b/src/serve/backend.rs @@ -1,14 +1,12 @@ -#[cfg(target_os = "linux")] -use nix::mount::MsFlags; +use crate::serve::ConfigExt; +use crate::ServeConfig; +use crate::{constant, InstallConfig, Running}; +use anyhow::Result; use nix::sys::signal; use nix::unistd::Pid; use signal_hook::iterator::Signals; use std::os::unix::process::CommandExt; - -use crate::serve::ConfigExt; -use crate::{constant, InstallConfig, Running}; -use crate::{util, ServeConfig}; -use std::{ops::Not, path::Path, process::Stdio}; +use std::process::Stdio; pub(super) struct BackendServer(ServeConfig, InstallConfig, tokio::sync::mpsc::Sender<()>); @@ -23,33 +21,7 @@ impl BackendServer { } impl Running for BackendServer { - fn run(self) -> anyhow::Result<()> { - // If Synology NAS is not installed, the backend service will not be started - let var_path = Path::new(constant::SYNOPKG_VAR); - if var_path.exists().not() { - util::create_dir_all(var_path, 0o777)?; - util::chown(var_path, self.1.uid, self.1.gid)?; - } - - #[cfg(target_os = "linux")] - let _ = nix::mount::umount(&self.1.mount_bind_download_path); - #[cfg(target_os = "linux")] - if nix::mount::mount( - Some(&self.1.download_path), - &self.1.mount_bind_download_path, - >::None, - MsFlags::MS_BIND, - >::None, - ) - .is_err() - { - anyhow::bail!( - "Mount {} to {} failed", - self.1.download_path.display(), - self.1.mount_bind_download_path.display() - ); - } - + fn run(self) -> Result<()> { // environment variables let envs = (&self.0, &self.1).envs()?; diff --git a/src/serve/frontend.rs b/src/serve/frontend.rs index 0ff38f0..e47b048 100644 --- a/src/serve/frontend.rs +++ b/src/serve/frontend.rs @@ -6,6 +6,7 @@ use super::{ }; use crate::{constant, InstallConfig, Running, ServeConfig}; use anyhow::Context; +use anyhow::Result; use axum::{ body::{Body, StreamBody}, extract::State, @@ -41,7 +42,7 @@ struct User { pub(super) struct FrontendServer(ServeConfig, InstallConfig, tokio::sync::mpsc::Receiver<()>); impl Running for FrontendServer { - fn run(self) -> anyhow::Result<()> { + fn run(self) -> Result<()> { self.start_server() } } @@ -56,7 +57,7 @@ impl FrontendServer { } #[tokio::main] - async fn start_server(self) -> anyhow::Result<()> { + async fn start_server(self) -> Result<()> { log::info!("Starting frontend server: {}", self.0.bind); // Set check auth diff --git a/src/serve/mod.rs b/src/serve/mod.rs index e81b9e5..cdf7a74 100644 --- a/src/serve/mod.rs +++ b/src/serve/mod.rs @@ -10,6 +10,7 @@ use crate::{ serve::{backend::BackendServer, frontend::FrontendServer}, InstallConfig, Running, ServeConfig, }; +use anyhow::Result; use std::collections::HashMap; pub(crate) trait ConfigExt { @@ -25,7 +26,7 @@ impl Serve { } impl Running for Serve { - fn run(self) -> anyhow::Result<()> { + fn run(self) -> Result<()> { use std::thread::{Builder, JoinHandle}; let serve_config = self.0.clone(); diff --git a/src/util.rs b/src/util.rs index 10ec56b..a9b04c4 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,8 +1,7 @@ -use std::{fs, os::unix::prelude::PermissionsExt, path::Path}; - -use std::{borrow::Cow, io::Write, path::PathBuf}; - use anyhow::Context; +use anyhow::Result; +use std::{borrow::Cow, io::Write, path::PathBuf}; +use std::{fs, os::unix::prelude::PermissionsExt, path::Path}; fn set_dir_permission(path: &Path, permission: u32) -> std::io::Result<()> { if path.is_dir() { @@ -22,7 +21,7 @@ fn set_dir_permission(path: &Path, permission: u32) -> std::io::Result<()> { Ok(()) } -pub fn chown(target_path: &Path, uid: u32, gid: u32) -> anyhow::Result<()> { +pub fn chown(target_path: &Path, uid: u32, gid: u32) -> Result<()> { nix::unistd::chown(target_path, Some(uid.into()), Some(gid.into())) .context(format!("chown {} error", target_path.display()))?; Ok(()) @@ -55,7 +54,7 @@ pub fn recursive_chown(path: &Path, uid: u32, gid: u32) { } } -pub fn write_file(target_path: &PathBuf, content: Cow<[u8]>, mode: u32) -> anyhow::Result<()> { +pub fn write_file(target_path: &PathBuf, content: Cow<[u8]>, mode: u32) -> Result<()> { let mut target_file = std::fs::File::create(target_path)?; target_file .write_all(&content) @@ -71,7 +70,7 @@ pub fn write_file(target_path: &PathBuf, content: Cow<[u8]>, mode: u32) -> anyho Ok(()) } -pub fn create_dir_all(target_path: &Path, mode: u32) -> anyhow::Result<()> { +pub fn create_dir_all(target_path: &Path, mode: u32) -> Result<()> { std::fs::create_dir_all(target_path).context(format!( "Failed to create folder: {}", target_path.display()