From 0286d854d61bad81d9ce641380df821001365c13 Mon Sep 17 00:00:00 2001 From: _ <_@_> Date: Fri, 21 Mar 2025 13:38:33 -0500 Subject: [PATCH] cleaned up and added install subcommand --- Cargo.lock | 40 ++++++++++++++++++++++++ Cargo.toml | 3 ++ README.md | 39 ++++++++--------------- src/app_common.rs | 4 +++ src/install.rs | 80 +++++++++++++++++++++++++++++++++++++++++++++-- src/main.rs | 23 ++++++++------ src/prelude.rs | 3 +- src/server.rs | 5 ++- 8 files changed, 155 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bbda06f..de32554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + [[package]] name = "autocfg" version = "1.1.0" @@ -20,6 +26,21 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" + +[[package]] +name = "cc" +version = "1.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -88,11 +109,14 @@ dependencies = [ name = "lookaround" version = "0.1.6" dependencies = [ + "anyhow", + "camino", "configparser", "directories", "mac_address", "nix", "rand", + "sys-info", "thiserror", "tokio", ] @@ -249,6 +273,12 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "syn" version = "1.0.82" @@ -260,6 +290,16 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "sys-info" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "thiserror" version = "1.0.30" diff --git a/Cargo.toml b/Cargo.toml index b59e288..75a118b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,11 +12,14 @@ repository = "https://six-five-six-four.com/git/reactor/lookaround" version = "0.1.6" [dependencies] +anyhow = "1.0.97" +camino = "1.1.9" configparser = "3.0.0" directories = "5.0.0" mac_address = "1.1.8" nix = "0.29.0" rand = "0.8.4" +sys-info = "0.9.1" thiserror = "1.0.30" tokio = { version = "1.14.0", features = ["fs", "net", "rt", "time"] } diff --git a/README.md b/README.md index 11ec0e7..af8f4a9 100644 --- a/README.md +++ b/README.md @@ -28,51 +28,38 @@ Make sure Cargo is installed from [RustUp.](https://rustup.rs/) cargo install lookaround # Find your config directory -# Prints something like `Using config dir "/home/user/.config/lookaround"` +# e.g. `$HOME/.config/lookaround` lookaround config ``` -Create the files `client.ini` and/or `server.ini` in that directory -(e.g. /home/user/.config/lookaround/server.ini) +Use `$HOME/.config/lookaround/client.ini` as a hosts file if it's more convenient +for your client to be the source of truth for nicknames. ```ini -# Clients can store MAC-nickname pairs in client.ini, like a hosts file. -# This is useful if your servers are short-lived and you want the clients -# to be the source of truth for nicknames. [nicknames] -11-11-11-11-11-11 = laptop -22-22-22-22-22-22 = desktop +11-11-11-11-11-11 = bob-laptop +22-22-22-22-22-22 = alice-desktop ``` +LookAround will use the system's hostname as its server nickname. +If you have a generic hostname like `ubuntu`, override this in `$HOME/.config/lookaround/server.ini`: + ```ini -# Long-lived servers can have their nickname configured in server.ini [server] -nickname = my-computer +nickname = alice-desktop ``` ## Auto-Start (Linux) -Put this systemd unit in `~/.config/systemd/user/lookaround.service`: +Run `lookaround install` or `lookaround install $NICKNAME` if you want to set a nickname. -```ini -[Unit] -Description=LookAround +This will create `$HOME/.config/lookaround/server.ini` and `$HOME/.config/systemd/user/lookaround.service`, and start the systemd user service. -[Service] -ExecStart=/home/user/.cargo/bin/lookaround server -Restart=always - -[Install] -WantedBy=default.target -``` - -Then start the service, check that it's running okay, and enable it for -auto-start: +To check that it's running ```bash -systemctl --user start lookaround systemctl --user status lookaround -systemctl --user enable lookaround +lookaround client ``` ## Auto-Start (Windows) diff --git a/src/app_common.rs b/src/app_common.rs index c2dd2fc..18a8e6f 100644 --- a/src/app_common.rs +++ b/src/app_common.rs @@ -9,6 +9,10 @@ pub fn try_config_dir() -> Option { Some(try_project_dir()?.config_local_dir().into()) } +pub fn try_server_config_path() -> Option { + Some(try_config_dir()?.join("server.ini")) +} + #[derive(Debug, thiserror::Error)] pub enum AppError { #[error(transparent)] diff --git a/src/install.rs b/src/install.rs index 4ae1ecf..ffa3856 100644 --- a/src/install.rs +++ b/src/install.rs @@ -1,7 +1,83 @@ use crate::prelude::*; +use anyhow::{Context as _, Result, anyhow, ensure}; +use std::{io::Write as _, process::Command}; -pub(crate) fn main() -> Result<(), AppError> { - let path = app_common::try_config_dir().unwrap().join("server.ini"); +pub(crate) fn main(nickname: Option<&str>) -> Result<()> { + if let Some(nickname) = nickname { + let path = app_common::try_server_config_path().context("can't find config dir")?; + let text = format!( + "[server] +nickname = {nickname} +" + ); + + write_new(&path, &text).context("Didn't install LookAround server.ini")?; + } + + setup_autostart()?; Ok(()) } + +#[cfg(not(target_os = "linux"))] +fn setup_autostart() -> Result<()> { + anyhow::bail!("LookAround autostart is only implemented for systemd + GNU/Linux"); +} + +#[cfg(target_os = "linux")] +fn setup_autostart() -> Result<()> { + let dirs = directories::BaseDirs::new().context("Could not find home dir")?; + let home = dirs.config_local_dir(); + let path = home.join("systemd").join("user").join("lookaround.service"); + + let exe_path = + Utf8PathBuf::from_path_buf(std::env::current_exe().context("Can't get current_exe")?) + .map_err(|_| anyhow!("current_exe isn't valid UTF-8"))?; + let text = format!( + "[Unit] +Description=LookAround + +[Service] +ExecStart={exe_path} server +Restart=always + +[Install] +WantedBy=default.target +" + ); + + write_new(&path, &text).context("Didn't install systemd service unit")?; + + ensure!( + Command::new("systemctl") + .args(["--user", "start", "lookaround"]) + .status()? + .success(), + "starting service failed" + ); + ensure!( + Command::new("systemctl") + .args(["--user", "enable", "lookaround"]) + .status()? + .success(), + "enabling service failed" + ); + + Ok(()) +} + +fn write_new(path: &Path, content: &str) -> Result<()> { + use std::fs; + fs::create_dir_all( + path.parent() + .context("Impossible, path should always have a parent")?, + )?; + match fs::File::create_new(path) { + Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => { + eprintln!("File already exists, won't overwrite: `{path:?}`") + } + Err(err) => Err(err)?, + Ok(mut f) => f.write_all(content.as_bytes())?, + } + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index c10a911..0d50866 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use anyhow::{Result, bail}; use prelude::*; pub mod app_common; @@ -9,7 +10,7 @@ mod prelude; mod server; pub mod tlv; -fn main() -> Result<(), AppError> { +fn main() -> Result<()> { let rt = tokio::runtime::Builder::new_current_thread() .enable_io() .enable_time() @@ -20,7 +21,7 @@ fn main() -> Result<(), AppError> { Ok(()) } -async fn async_main() -> Result<(), AppError> { +async fn async_main() -> Result<()> { let mut args = env::args(); let _exe_name = args.next(); @@ -32,19 +33,21 @@ async fn async_main() -> Result<(), AppError> { match subcommand.as_ref() { "--version" => { println!("lookaround v{}", env!("CARGO_PKG_VERSION")); - Ok(()) } - "client" => client::client(args).await, + "client" => client::client(args).await?, "config" => { config(); - Ok(()) } - "find-nick" => client::find_nick(args).await, - "install" => install::main(), - "my-ips" => my_ips(), - "server" => server::server(args).await, - x => Err(CliArgError::UnknownSubcommand(x.to_string()).into()), + "find-nick" => client::find_nick(args).await?, + "install" => { + let nickname = args.next(); + install::main(nickname.as_deref())? + } + "my-ips" => my_ips()?, + "server" => server::server(args).await?, + x => bail!("Unknown subcommand `{x}`"), } + Ok(()) } fn config() { diff --git a/src/prelude.rs b/src/prelude.rs index 2f8effa..c403331 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -3,12 +3,13 @@ pub use std::{ env, io::{Cursor, Write}, net::{Ipv4Addr, SocketAddr, SocketAddrV4}, - path::PathBuf, + path::{Path, PathBuf}, str::FromStr, sync::Arc, time::Duration, }; +pub use camino::Utf8PathBuf; pub use configparser::ini::Ini; pub use mac_address::{MacAddress, get_mac_address}; pub use rand::RngCore; diff --git a/src/server.rs b/src/server.rs index 0d458e2..a471c42 100644 --- a/src/server.rs +++ b/src/server.rs @@ -42,10 +42,9 @@ pub async fn server>(args: I) -> Result<(), AppError> fn configure>(mut args: I) -> Result { let common = app_common::Params::default(); let mut bind_addrs = vec![]; - let mut nickname = String::new(); + let mut nickname = sys_info::hostname().unwrap_or_default(); - if let Some(dir) = app_common::try_config_dir() { - let path = dir.join("server.ini"); + if let Some(path) = app_common::try_server_config_path() { let mut ini = Ini::new_cs(); if ini.load(&path).is_ok() { if let Some(x) = ini.get("server", "nickname") {