From cdcd89b5b2142f94694090031e19621556d8eda7 Mon Sep 17 00:00:00 2001 From: Hyko Date: Tue, 12 May 2026 21:44:05 -0400 Subject: [PATCH] feat(tools): add welcome message (MOTD) management tool - add `tools/welcome-message.sh` with view, set, and reset actions - register `lxs tool welcome|motd` command in `lxs.sh` - add option 7 to interactive tools menu in `tools/index.sh` - document `lxs tool welcome` in README --- README.md | 4 +- lxs.sh | 1 + tools/index.sh | 6 +- tools/welcome-message.sh | 203 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+), 3 deletions(-) create mode 100755 tools/welcome-message.sh diff --git a/README.md b/README.md index c420204..ccf3618 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ lxs help # Show help | `lxs tool root-password` | Change the root password (interactive or generated) | | `lxs tool update` | Update the server (apt update + upgrade + autoremove + autoclean) | | `lxs tool root-ssh-login` | Enable or disable root login over SSH with a password | +| `lxs tool welcome` | View, edit, or reset the SSH welcome message (MOTD) | ## Project structure @@ -76,7 +77,8 @@ lxs/ ├── harden.sh ├── root-password.sh ├── update-server.sh - └── root-ssh-login.sh + ├── root-ssh-login.sh + └── welcome-message.sh ``` After `lxs setup`, the full tree lives in `/usr/local/share/lxs/` and sub-scripts execute from disk. If `lxs.sh` is run without being installed (the one-liner mode), it falls back to downloading sub-scripts on demand. diff --git a/lxs.sh b/lxs.sh index 12b05f5..6db86cd 100755 --- a/lxs.sh +++ b/lxs.sh @@ -290,6 +290,7 @@ cmd_tool() { root-password) download_and_run "tools/root-password.sh" "$@" ;; update) download_and_run "tools/update-server.sh" "$@" ;; root-ssh-login) download_and_run "tools/root-ssh-login.sh" "$@" ;; + welcome|motd) download_and_run "tools/welcome-message.sh" "$@" ;; "") echo -e "${RED}[✗] Missing tool name. Try: lxs help${NC}"; return 1 ;; *) echo -e "${RED}[✗] Unknown tool: $tool. Try: lxs help${NC}"; return 1 ;; esac diff --git a/tools/index.sh b/tools/index.sh index 1090755..9e05fa4 100755 --- a/tools/index.sh +++ b/tools/index.sh @@ -63,9 +63,10 @@ menu_tools() { echo -e " ${GREEN}[4]${NC} Change Root Password" echo -e " ${CYAN}[5]${NC} Update Server" echo -e " ${YELLOW}[6]${NC} Root SSH Password Login" + echo -e " ${PURPLE}[7]${NC} Welcome Message (MOTD)" echo -e " ${RED}[0]${NC} Back" echo "" - echo -e -n "${BOLD}Choice [0-6]: ${NC}" + echo -e -n "${BOLD}Choice [0-7]: ${NC}" read -r choice case $choice in @@ -75,8 +76,9 @@ menu_tools() { 4) run_sibling "tools/root-password.sh" ;; 5) run_sibling "tools/update-server.sh" ;; 6) run_sibling "tools/root-ssh-login.sh" ;; + 7) run_sibling "tools/welcome-message.sh" ;; 0) return ;; - *) echo -e "${RED}[✗] Invalid option. Please select 0-6.${NC}"; sleep 1; continue ;; + *) echo -e "${RED}[✗] Invalid option. Please select 0-7.${NC}"; sleep 1; continue ;; esac if [ "$choice" != "0" ]; then diff --git a/tools/welcome-message.sh b/tools/welcome-message.sh new file mode 100755 index 0000000..6d7bd85 --- /dev/null +++ b/tools/welcome-message.sh @@ -0,0 +1,203 @@ +#!/bin/bash + +# LXS - Welcome message (MOTD) +# Description: View, edit, or reset the SSH login welcome message (/etc/motd) +# Author: LXS +# Date: 2025 + +# Load LXS common library (colors, separator, run_spinner, loggers, helpers) +LXS_RAW_BASE="${LXS_RAW_BASE:-https://git.hyko.cx/hykocx/lxs/raw/branch/main}" +_lib=$(curl -fsSL "${LXS_RAW_BASE}/lib/common.sh") || { echo "Failed to fetch lib/common.sh" >&2; exit 1; } +eval "$_lib" +unset _lib +export LXS_LOG_FILE="/tmp/lxs_welcome_message.log" + +require_root "$0" "$@" + +set -u + +MOTD_FILE="/etc/motd" +BACKUP_FILE="/etc/motd.lxs.bak" +DYNAMIC_DIR="/etc/update-motd.d" + +# ═══════════════════════════════════════════════════════════════════════════ +# Arguments +# ═══════════════════════════════════════════════════════════════════════════ + +ACTION="" +TEXT="" +FROM_FILE="" + +while [ $# -gt 0 ]; do + case "$1" in + view|--view|show|--show) ACTION="view" ;; + set|--set|edit|--edit) ACTION="set" ;; + reset|--reset) ACTION="reset" ;; + --text) shift; TEXT=${1:-} ;; + --text=*) TEXT="${1#*=}" ;; + --from-file) shift; FROM_FILE=${1:-} ;; + --from-file=*) FROM_FILE="${1#*=}" ;; + -h|--help) + cat <&2 + exit 1 + ;; + esac + shift +done + +if [ -n "$TEXT" ] && [ -n "$FROM_FILE" ]; then + err "--text and --from-file are mutually exclusive" + exit 1 +fi + +# ═══════════════════════════════════════════════════════════════════════════ +# Helpers +# ═══════════════════════════════════════════════════════════════════════════ + +view_motd() { + echo -e "${WHITE}${BOLD}Current welcome message${NC} ${GRAY}(${MOTD_FILE})${NC}" + show_separator + if [ ! -s "$MOTD_FILE" ]; then + echo -e "${GRAY}(empty)${NC}" + else + cat "$MOTD_FILE" + fi + show_separator + + if [ -d "$DYNAMIC_DIR" ] && compgen -G "${DYNAMIC_DIR}/*" >/dev/null; then + echo "" + warn "Dynamic MOTD scripts are present in ${DYNAMIC_DIR}:" + find "$DYNAMIC_DIR" -maxdepth 1 -type f -executable -printf ' • %f\n' | sort + echo -e "${GRAY}These run at login and add to the banner.${NC}" + fi +} + +backup_motd() { + [ -f "$MOTD_FILE" ] || return 0 + cp -a "$MOTD_FILE" "$BACKUP_FILE" + info "Previous message backed up to ${BACKUP_FILE}" +} + +write_motd() { + local src=$1 + backup_motd + install -m 644 "$src" "$MOTD_FILE" + ok "Welcome message updated" + echo "" + view_motd +} + +set_from_text() { + local tmp + tmp=$(mktemp /tmp/lxs.motd.XXXXXX) || { err "mktemp failed"; return 1; } + # Preserve embedded newlines; ensure a trailing newline. + printf '%s\n' "$1" > "$tmp" + write_motd "$tmp" + rm -f "$tmp" +} + +set_from_file() { + local src=$1 + if [ ! -r "$src" ]; then + err "Cannot read file: ${src}" + return 1 + fi + write_motd "$src" +} + +set_interactive() { + local editor=${EDITOR:-} + if [ -z "$editor" ]; then + if command -v nano >/dev/null 2>&1; then editor="nano" + elif command -v vi >/dev/null 2>&1; then editor="vi" + else + err "No editor found (\$EDITOR unset; nano/vi missing). Use --text or --from-file." + return 1 + fi + fi + + local tmp + tmp=$(mktemp /tmp/lxs.motd.XXXXXX) || { err "mktemp failed"; return 1; } + [ -s "$MOTD_FILE" ] && cat "$MOTD_FILE" > "$tmp" + + info "Opening ${editor}... save and exit to apply, leave empty to cancel." + "$editor" "$tmp" + + if [ ! -s "$tmp" ]; then + warn "Empty content — change cancelled." + rm -f "$tmp" + return 0 + fi + + write_motd "$tmp" + rm -f "$tmp" +} + +reset_motd() { + if [ ! -s "$MOTD_FILE" ]; then + info "Welcome message is already empty." + return 0 + fi + backup_motd + : > "$MOTD_FILE" + chmod 644 "$MOTD_FILE" + ok "Welcome message cleared" +} + +# ═══════════════════════════════════════════════════════════════════════════ +# Menu (when no action is given on the CLI) +# ═══════════════════════════════════════════════════════════════════════════ + +if [ -z "$ACTION" ]; then + echo -e "${WHITE}${BOLD}LXS - Welcome message${NC}" + show_separator + echo -e " ${CYAN}[1]${NC} View current message" + echo -e " ${GREEN}[2]${NC} Set a new message" + echo -e " ${YELLOW}[3]${NC} Reset (clear) the message" + echo -e " ${RED}[0]${NC} Cancel" + echo "" + echo -e -n "${BOLD}Choice [0-3]: ${NC}" + read -r choice + case "$choice" in + 1) ACTION="view" ;; + 2) ACTION="set" ;; + 3) ACTION="reset" ;; + 0|"") info "Cancelled."; exit 0 ;; + *) err "Invalid option."; exit 1 ;; + esac +fi + +case "$ACTION" in + view) view_motd ;; + set) + if [ -n "$TEXT" ]; then set_from_text "$TEXT" + elif [ -n "$FROM_FILE" ]; then set_from_file "$FROM_FILE" + else set_interactive + fi + ;; + reset) reset_motd ;; +esac