diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 44b704b..6b4cfd6 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -40,3 +40,7 @@ impl CommandExecute for HarmonicCli { } } } + +pub fn is_root() -> bool { + nix::unistd::getuid() == nix::unistd::Uid::from_raw(0) +} diff --git a/src/cli/subcommand/install.rs b/src/cli/subcommand/install.rs index 2bbb32b..ff57cc5 100644 --- a/src/cli/subcommand/install.rs +++ b/src/cli/subcommand/install.rs @@ -3,7 +3,9 @@ use std::{ process::ExitCode, }; -use crate::{action::ActionState, plan::RECEIPT_LOCATION, BuiltinPlanner, InstallPlan, Planner}; +use crate::{ + action::ActionState, cli::is_root, plan::RECEIPT_LOCATION, BuiltinPlanner, InstallPlan, Planner, +}; use clap::{ArgAction, Parser}; use eyre::{eyre, WrapErr}; @@ -46,6 +48,12 @@ impl CommandExecute for Install { explain, } = self; + if !is_root() { + return Err(eyre!( + "`harmonic install` must be run as `root`, try `sudo harmonic install`" + )); + } + let existing_receipt: Option = match Path::new(RECEIPT_LOCATION).exists() { true => { let install_plan_string = tokio::fs::read_to_string(&RECEIPT_LOCATION) diff --git a/src/cli/subcommand/plan.rs b/src/cli/subcommand/plan.rs index 41c3796..5a3d894 100644 --- a/src/cli/subcommand/plan.rs +++ b/src/cli/subcommand/plan.rs @@ -9,11 +9,11 @@ use crate::cli::CommandExecute; /// Plan an install that can be repeated on an identical host later #[derive(Debug, Parser)] -#[command(args_conflicts_with_subcommands = true, arg_required_else_help = true)] +#[command(arg_required_else_help = true)] pub struct Plan { #[clap(subcommand)] pub planner: Option, - #[clap(env = "HARMONIC_PLAN")] + #[clap(env = "HARMONIC_PLAN", default_value = "/dev/stdout")] pub output: PathBuf, } diff --git a/src/cli/subcommand/uninstall.rs b/src/cli/subcommand/uninstall.rs index 5f2db68..16fe68a 100644 --- a/src/cli/subcommand/uninstall.rs +++ b/src/cli/subcommand/uninstall.rs @@ -1,8 +1,8 @@ use std::{path::PathBuf, process::ExitCode}; -use crate::{plan::RECEIPT_LOCATION, InstallPlan}; +use crate::{cli::is_root, plan::RECEIPT_LOCATION, InstallPlan}; use clap::{ArgAction, Parser}; -use eyre::WrapErr; +use eyre::{eyre, WrapErr}; use crate::{cli::CommandExecute, interaction}; @@ -37,6 +37,12 @@ impl CommandExecute for Uninstall { explain, } = self; + if !is_root() { + return Err(eyre!( + "`harmonic install` must be run as `root`, try `sudo harmonic install`" + )); + } + let install_receipt_string = tokio::fs::read_to_string(receipt) .await .wrap_err("Reading receipt")?;