Don't require root to print usage

Both `mkdir -p "$LOG_DIR"` and `exec > >(tee -a "$LOG_FILE")` ran at file
scope, before a single argument was parsed. On any machine where
/var/log is not writable by the caller -- which is every machine, since
this needs sudo -- `--help` and a mistyped flag both failed with a raw
mkdir or tee error rather than printing usage or naming the bad flag.

Both now happen in ensure_log_dir(), called from main() once a real
command has been dispatched, so usage and argument errors work for
anyone while everything from the command onward is still logged. exec
applies to the shell rather than the function, so moving it changes
nothing about what gets captured -- verified by running a command with
the log directory redirected and confirming the output landed in it.

A non-root command now also fails with "Cannot create /var/log/... --
re-run with sudo" instead of leaking mkdir's own message.
This commit is contained in:
2026-08-22 22:39:35 -07:00
parent db71e25612
commit ece4ac2735
+19 -2
View File
@@ -53,9 +53,15 @@ SCRIPT_VERSION="5.1"
# Globals / Defaults # Globals / Defaults
# ========================= # =========================
LOG_DIR="/var/log/ubuntu-to-mint" LOG_DIR="/var/log/ubuntu-to-mint"
mkdir -p "$LOG_DIR" # Deliberately NOT created here. This runs at file scope, before any
# argument is parsed, so creating it meant `--help` and a mistyped flag
# both died with "mkdir: Permission denied" instead of printing usage.
# ensure_log_dir is called once a real command has been dispatched.
LOG_FILE="${LOG_DIR}/ubuntu-to-mint-$(date +%Y%m%d-%H%M%S).log" LOG_FILE="${LOG_DIR}/ubuntu-to-mint-$(date +%Y%m%d-%H%M%S).log"
exec > >(tee -a "$LOG_FILE") 2>&1 # Logging is started by ensure_log_dir once a command has been dispatched,
# for the same reason the directory is not created here: at file scope this
# ran before argument parsing, so `--help` tried to tee into a directory
# that does not exist and may not be creatable.
# CLI defaults # CLI defaults
CMD="" CMD=""
@@ -1158,10 +1164,21 @@ parse_args_any_order() {
# ========================= # =========================
# Main # Main
# ========================= # =========================
ensure_log_dir() {
mkdir -p "$LOG_DIR" 2>/dev/null || \
die "Cannot create ${LOG_DIR}. Every command here needs root -- re-run with sudo."
# exec applies to the shell, not just this function, so everything from
# here on is both shown and logged.
exec > >(tee -a "$LOG_FILE") 2>&1
}
main() { main() {
parse_args_any_order "$@" parse_args_any_order "$@"
[[ -n "$CMD" ]] || { usage; exit 1; } [[ -n "$CMD" ]] || { usage; exit 1; }
# Past this point a command was named, so the log directory is wanted.
ensure_log_dir
case "$CMD" in case "$CMD" in
doctor) doctor ;; doctor) doctor ;;
plan) plan_mode ;; plan) plan_mode ;;