summaryrefslogtreecommitdiff
path: root/src/installation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/installation.rs')
-rw-r--r--src/installation.rs152
1 files changed, 152 insertions, 0 deletions
diff --git a/src/installation.rs b/src/installation.rs
new file mode 100644
index 0000000..bc86db6
--- /dev/null
+++ b/src/installation.rs
@@ -0,0 +1,152 @@
+use crate::{
+ controller::Controller,
+ storage::{atomic_private_write, private_directory},
+ subscription::HttpSource,
+ xray::systemctl,
+};
+use anyhow::{Result, ensure};
+use std::{fs, path::Path, process::Stdio};
+use tokio::process::Command;
+
+const SERVICE: &str = include_str!("../assets/roentgen.service");
+const GUARD: &str = include_str!("../assets/roentgen-guard.service");
+const XRAY: &str = include_str!("../assets/roentgen-xray.service");
+const BUS: &str = include_str!("../assets/org.roentgen.Controller1.conf");
+const POLKIT: &str = include_str!("../assets/org.roentgen.policy");
+
+fn root() -> Result<()> {
+ ensure!(
+ unsafe { libc::geteuid() } == 0,
+ "Run this explicit installation/recovery command as root"
+ );
+ Ok(())
+}
+
+pub async fn install() -> Result<()> {
+ root()?;
+ let _ = crate::daemon::runtime()?;
+ crate::xray::preflight().await?;
+ private_directory(Path::new("/etc/roentgen"))?;
+ private_directory(Path::new("/var/lib/roentgen"))?;
+ if crate::xray::identity().is_err() {
+ let status = Command::new("/usr/sbin/useradd")
+ .args([
+ "--system",
+ "--user-group",
+ "--no-create-home",
+ "--shell",
+ "/sbin/nologin",
+ "roentgen-xray",
+ ])
+ .stdin(Stdio::null())
+ .status()
+ .await?;
+ ensure!(
+ status.success(),
+ "Could not create the dedicated Xray account"
+ );
+ }
+ let _ = systemctl(&["stop", "roentgen.service"]).await;
+ let _lock = crate::locking::controller_lock()?;
+ let mut controller = Controller::open_paths(
+ Path::new("/etc/roentgen"),
+ Path::new("/var/lib/roentgen"),
+ crate::daemon::runtime()?,
+ Box::new(HttpSource),
+ )?;
+ // Do not overwrite the saved guard or enabled intent on reinstallation.
+ #[cfg(feature = "linux-runtime")]
+ if !Path::new("/etc/roentgen/guard.toml").exists() {
+ crate::linux::write_guard(controller.status()?.requested_enabled)?;
+ }
+ let current = std::env::current_exe()?;
+ let target = Path::new("/usr/local/bin/roentgen");
+ if current != target {
+ atomic_private_write(target, &fs::read(current)?)?;
+ use std::os::unix::fs::PermissionsExt;
+ fs::set_permissions(target, fs::Permissions::from_mode(0o755))?;
+ }
+ for (path, content) in assets() {
+ if let Some(parent) = Path::new(path).parent() {
+ fs::create_dir_all(parent)?;
+ }
+ fs::write(path, content)?;
+ }
+ systemctl(&["daemon-reload"]).await?;
+ // Ask dbus to reload policy without restarting the system bus.
+ let connection = zbus::Connection::system().await?;
+ zbus::fdo::DBusProxy::new(&connection)
+ .await?
+ .reload_config()
+ .await?;
+ systemctl(&["enable", "roentgen-guard.service", "roentgen.service"]).await?;
+ systemctl(&["start", "roentgen-guard.service"]).await?;
+ controller.restore().await?;
+ drop(controller);
+ drop(_lock);
+ systemctl(&["restart", "roentgen.service"]).await?;
+ Ok(())
+}
+
+fn assets() -> [(&'static str, &'static str); 8] {
+ [
+ ("/etc/systemd/system/roentgen.service", SERVICE),
+ ("/etc/systemd/system/roentgen-guard.service", GUARD),
+ ("/etc/systemd/system/roentgen-xray.service", XRAY),
+ ("/etc/dbus-1/system.d/org.roentgen.Controller1.conf", BUS),
+ ("/usr/share/polkit-1/actions/org.roentgen.policy", POLKIT),
+ (
+ "/etc/systemd/system/NetworkManager.service.d/50-roentgen-guard.conf",
+ "[Unit]\nRequires=roentgen-guard.service\nAfter=roentgen-guard.service\n",
+ ),
+ (
+ "/etc/systemd/system/systemd-networkd.service.d/50-roentgen-guard.conf",
+ "[Unit]\nRequires=roentgen-guard.service\nAfter=roentgen-guard.service\n",
+ ),
+ (
+ "/etc/systemd/system/network.service.d/50-roentgen-guard.conf",
+ "[Unit]\nRequires=roentgen-guard.service\nAfter=roentgen-guard.service\n",
+ ),
+ ]
+}
+
+pub async fn recover() -> Result<()> {
+ root()?;
+ // Serialize recovery against ordinary controller transactions by stopping it first.
+ let _ = systemctl(&["stop", "roentgen.service"]).await;
+ let _lock = crate::locking::controller_lock()?;
+ crate::storage::persist_recovery_disabled(Path::new("/var/lib/roentgen"))?;
+ let mut runtime = crate::daemon::runtime()?;
+ runtime.disable().await
+}
+
+pub async fn uninstall(purge: bool) -> Result<()> {
+ root()?;
+ recover().await?;
+ systemctl(&[
+ "disable",
+ "--now",
+ "roentgen.service",
+ "roentgen-guard.service",
+ ])
+ .await?;
+ #[cfg(feature = "linux-runtime")]
+ crate::linux::firewall::remove()?;
+ for (path, _) in assets() {
+ if Path::new(path).exists() {
+ fs::remove_file(path)?;
+ }
+ }
+ systemctl(&["daemon-reload"]).await?;
+ if Path::new("/run/roentgen").exists() {
+ fs::remove_dir_all("/run/roentgen")?;
+ }
+ if purge {
+ fs::remove_dir_all("/etc/roentgen")?;
+ fs::remove_dir_all("/var/lib/roentgen")?;
+ }
+ if Path::new("/usr/local/bin/roentgen").exists() {
+ fs::remove_file("/usr/local/bin/roentgen")?;
+ }
+ Ok(())
+}