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
This commit is contained in:
@@ -53,6 +53,7 @@ lxs help # Show help
|
|||||||
| `lxs tool root-password` | Change the root password (interactive or generated) |
|
| `lxs tool root-password` | Change the root password (interactive or generated) |
|
||||||
| `lxs tool update` | Update the server (apt update + upgrade + autoremove + autoclean) |
|
| `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 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
|
## Project structure
|
||||||
|
|
||||||
@@ -76,7 +77,8 @@ lxs/
|
|||||||
├── harden.sh
|
├── harden.sh
|
||||||
├── root-password.sh
|
├── root-password.sh
|
||||||
├── update-server.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.
|
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.
|
||||||
|
|||||||
@@ -290,6 +290,7 @@ cmd_tool() {
|
|||||||
root-password) download_and_run "tools/root-password.sh" "$@" ;;
|
root-password) download_and_run "tools/root-password.sh" "$@" ;;
|
||||||
update) download_and_run "tools/update-server.sh" "$@" ;;
|
update) download_and_run "tools/update-server.sh" "$@" ;;
|
||||||
root-ssh-login) download_and_run "tools/root-ssh-login.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}[✗] Missing tool name. Try: lxs help${NC}"; return 1 ;;
|
||||||
*) echo -e "${RED}[✗] Unknown tool: $tool. Try: lxs help${NC}"; return 1 ;;
|
*) echo -e "${RED}[✗] Unknown tool: $tool. Try: lxs help${NC}"; return 1 ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
+4
-2
@@ -63,9 +63,10 @@ menu_tools() {
|
|||||||
echo -e " ${GREEN}[4]${NC} Change Root Password"
|
echo -e " ${GREEN}[4]${NC} Change Root Password"
|
||||||
echo -e " ${CYAN}[5]${NC} Update Server"
|
echo -e " ${CYAN}[5]${NC} Update Server"
|
||||||
echo -e " ${YELLOW}[6]${NC} Root SSH Password Login"
|
echo -e " ${YELLOW}[6]${NC} Root SSH Password Login"
|
||||||
|
echo -e " ${PURPLE}[7]${NC} Welcome Message (MOTD)"
|
||||||
echo -e " ${RED}[0]${NC} Back"
|
echo -e " ${RED}[0]${NC} Back"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e -n "${BOLD}Choice [0-6]: ${NC}"
|
echo -e -n "${BOLD}Choice [0-7]: ${NC}"
|
||||||
read -r choice
|
read -r choice
|
||||||
|
|
||||||
case $choice in
|
case $choice in
|
||||||
@@ -75,8 +76,9 @@ menu_tools() {
|
|||||||
4) run_sibling "tools/root-password.sh" ;;
|
4) run_sibling "tools/root-password.sh" ;;
|
||||||
5) run_sibling "tools/update-server.sh" ;;
|
5) run_sibling "tools/update-server.sh" ;;
|
||||||
6) run_sibling "tools/root-ssh-login.sh" ;;
|
6) run_sibling "tools/root-ssh-login.sh" ;;
|
||||||
|
7) run_sibling "tools/welcome-message.sh" ;;
|
||||||
0) return ;;
|
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
|
esac
|
||||||
|
|
||||||
if [ "$choice" != "0" ]; then
|
if [ "$choice" != "0" ]; then
|
||||||
|
|||||||
Executable
+203
@@ -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 <<EOF
|
||||||
|
Usage: welcome-message.sh [action] [options]
|
||||||
|
|
||||||
|
Actions:
|
||||||
|
view Show the current welcome message
|
||||||
|
set Set a new welcome message (see source options below)
|
||||||
|
reset Clear the welcome message (a backup is kept)
|
||||||
|
|
||||||
|
Source options for 'set' (mutually exclusive):
|
||||||
|
--text "..." Use the given string as the new message
|
||||||
|
--from-file PATH Read the new message from PATH
|
||||||
|
(none) Open an interactive editor (\$EDITOR or nano/vi)
|
||||||
|
|
||||||
|
-h, --help Show this help
|
||||||
|
|
||||||
|
With no action, an interactive menu is shown.
|
||||||
|
|
||||||
|
The welcome message lives in ${MOTD_FILE}.
|
||||||
|
On Ubuntu, dynamic MOTD scripts in ${DYNAMIC_DIR} also contribute to the
|
||||||
|
banner shown at login — those are not modified by this tool.
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${RED}Unknown option: $1${NC}" >&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
|
||||||
Reference in New Issue
Block a user