269 lines
6.7 KiB
Bash
Executable File
269 lines
6.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
WORK_DIR="${WORK_DIR:-/opt/app}"
|
|
PURGE_DATA="${PURGE_DATA:-0}"
|
|
|
|
# Keep this list in sync with install_iot.sh and install_efka.sh.
|
|
# Format:
|
|
# "service_name|download_url|start_command|stop_command|dependency_dirs|service_user|service_group"
|
|
INSTALL_ITEMS=(
|
|
"iot|https://punchsky.cn/down/iot-0.1.0.tar.gz|{install_dir}/bin/iot foreground|{install_dir}/bin/iot stop|/var/lib/iot/mnesia,/var/lib/endpoint/database,/var/lib/endpoint/endpoint_log"
|
|
"efka|https://punchsky.cn/down/efka-0.1.0.tar.gz|{install_dir}/bin/efka foreground|{install_dir}/bin/efka stop|/var/lib/efka/mnesia,/var/lib/efka/dets,/var/lib/efka/docker"
|
|
)
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
sudo ./uninstall_service.sh [options]
|
|
|
|
Options:
|
|
-w, --work-dir Working directory used during installation. Default: /opt/app.
|
|
--purge-data Also remove dependency/data directories from INSTALL_ITEMS.
|
|
-h, --help Show this help.
|
|
|
|
Default behavior:
|
|
- Stop services in reverse INSTALL_ITEMS order.
|
|
- Disable services from boot.
|
|
- Remove /etc/systemd/system/<service>.service and service backups.
|
|
- Run systemctl daemon-reload and reset-failed.
|
|
- Remove downloaded archives and service-name program directories under WORK_DIR.
|
|
- Remove /usr/local/bin/iot_ctrl.
|
|
- Keep dependency/data directories unless --purge-data is passed.
|
|
EOF
|
|
}
|
|
|
|
log() {
|
|
printf '[uninstall] %s\n' "$*"
|
|
}
|
|
|
|
fail() {
|
|
printf '[uninstall][error] %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
require_command() {
|
|
command -v "$1" >/dev/null 2>&1 || fail "Missing required command: $1"
|
|
}
|
|
|
|
require_option_value() {
|
|
local option="$1"
|
|
local value="${2:-}"
|
|
|
|
[[ -n "$value" ]] || fail "$option requires a value"
|
|
}
|
|
|
|
require_root_runner() {
|
|
if [[ "${EUID}" -ne 0 ]]; then
|
|
require_command sudo
|
|
fi
|
|
}
|
|
|
|
run_as_root() {
|
|
if [[ "${EUID}" -eq 0 ]]; then
|
|
"$@"
|
|
else
|
|
sudo "$@"
|
|
fi
|
|
}
|
|
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-w|--work-dir)
|
|
require_option_value "$1" "${2:-}"
|
|
WORK_DIR="${2:-}"
|
|
shift 2
|
|
;;
|
|
--purge-data)
|
|
PURGE_DATA=1
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
fail "Unknown argument: $1"
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
validate_args() {
|
|
[[ -n "$WORK_DIR" ]] || fail "--work-dir is required"
|
|
[[ "$WORK_DIR" = /* ]] || fail "--work-dir must be an absolute path"
|
|
|
|
set +u
|
|
local item_count="${#INSTALL_ITEMS[@]}"
|
|
set -u
|
|
|
|
[[ "$item_count" -gt 0 ]] || fail "No programs configured. Fill INSTALL_ITEMS inside this script"
|
|
}
|
|
|
|
archive_name_from_url() {
|
|
local download_url="$1"
|
|
local clean_url="$download_url"
|
|
|
|
clean_url="${clean_url%%\?*}"
|
|
clean_url="${clean_url%%#*}"
|
|
basename "$clean_url"
|
|
}
|
|
|
|
replace_placeholders() {
|
|
local value="$1"
|
|
local install_dir="$2"
|
|
local service_name="$3"
|
|
|
|
value="${value//\{install_dir\}/$install_dir}"
|
|
value="${value//\{work_dir\}/$WORK_DIR}"
|
|
value="${value//\{service_name\}/$service_name}"
|
|
|
|
printf '%s\n' "$value"
|
|
}
|
|
|
|
service_name_from_item() {
|
|
local item="$1"
|
|
local service_name
|
|
|
|
IFS='|' read -r service_name _ <<< "$item"
|
|
printf '%s\n' "$service_name"
|
|
}
|
|
|
|
stop_and_disable_services() {
|
|
local index
|
|
local service_name
|
|
|
|
set +u
|
|
for ((index=${#INSTALL_ITEMS[@]} - 1; index >= 0; index--)); do
|
|
set -u
|
|
service_name="$(service_name_from_item "${INSTALL_ITEMS[$index]}")"
|
|
[[ -n "$service_name" ]] || continue
|
|
|
|
log "Stopping service: ${service_name}.service"
|
|
run_as_root systemctl stop "${service_name}.service" || log "Service not running or not found: ${service_name}.service"
|
|
|
|
log "Disabling service on boot: ${service_name}.service"
|
|
run_as_root systemctl disable "${service_name}.service" || log "Service not enabled or not found: ${service_name}.service"
|
|
set +u
|
|
done
|
|
set -u
|
|
}
|
|
|
|
remove_service_files() {
|
|
local item
|
|
local service_name
|
|
local service_file
|
|
|
|
set +u
|
|
for item in "${INSTALL_ITEMS[@]}"; do
|
|
set -u
|
|
service_name="$(service_name_from_item "$item")"
|
|
[[ -n "$service_name" ]] || continue
|
|
|
|
service_file="/etc/systemd/system/${service_name}.service"
|
|
log "Removing systemd service file: $service_file"
|
|
run_as_root rm -f "$service_file"
|
|
|
|
log "Removing systemd service backups: ${service_file}.bak.*"
|
|
run_as_root sh -c 'rm -f "$1".bak.*' sh "$service_file"
|
|
set +u
|
|
done
|
|
set -u
|
|
}
|
|
|
|
remove_program_files() {
|
|
local item
|
|
local service_name
|
|
local download_url
|
|
local start_command
|
|
local stop_command
|
|
local dependency_dirs
|
|
local service_user
|
|
local service_group
|
|
local archive_name
|
|
local archive_path
|
|
local install_dir
|
|
|
|
set +u
|
|
for item in "${INSTALL_ITEMS[@]}"; do
|
|
set -u
|
|
IFS='|' read -r service_name download_url start_command stop_command dependency_dirs service_user service_group <<< "$item"
|
|
[[ -n "$service_name" ]] || continue
|
|
[[ -n "$download_url" ]] || fail "Invalid package config for $service_name: download URL is empty"
|
|
|
|
archive_name="$(archive_name_from_url "$download_url")"
|
|
[[ -n "$archive_name" ]] || fail "Cannot parse archive filename from URL: $download_url"
|
|
|
|
archive_path="${WORK_DIR}/${archive_name}"
|
|
install_dir="${WORK_DIR}/${service_name}"
|
|
|
|
log "Removing downloaded archive: $archive_path"
|
|
run_as_root rm -f "$archive_path"
|
|
|
|
log "Removing installed program directory: $install_dir"
|
|
run_as_root rm -rf "$install_dir"
|
|
|
|
if [[ "$PURGE_DATA" = "1" ]]; then
|
|
remove_dependency_dirs "$dependency_dirs" "$install_dir" "$service_name"
|
|
fi
|
|
set +u
|
|
done
|
|
set -u
|
|
}
|
|
|
|
remove_global_links() {
|
|
local link_path="/usr/local/bin/iot_ctrl"
|
|
|
|
log "Removing global command link: $link_path"
|
|
run_as_root rm -f "$link_path"
|
|
}
|
|
|
|
remove_dependency_dirs() {
|
|
local dependency_dirs="$1"
|
|
local install_dir="$2"
|
|
local service_name="$3"
|
|
local dependency_dir
|
|
local dependency_dir_list
|
|
local -a dependency_dir_array
|
|
|
|
[[ -n "$dependency_dirs" ]] || return 0
|
|
|
|
dependency_dir_list="$(replace_placeholders "$dependency_dirs" "$install_dir" "$service_name")"
|
|
|
|
IFS=',' read -ra dependency_dir_array <<< "$dependency_dir_list"
|
|
for dependency_dir in "${dependency_dir_array[@]}"; do
|
|
[[ -n "$dependency_dir" ]] || continue
|
|
[[ "$dependency_dir" = /* ]] || fail "Dependency directory must be an absolute path: $dependency_dir"
|
|
|
|
log "Removing dependency/data directory: $dependency_dir"
|
|
run_as_root rm -rf "$dependency_dir"
|
|
done
|
|
}
|
|
|
|
reload_systemd() {
|
|
log "Reloading systemd"
|
|
run_as_root systemctl daemon-reload
|
|
|
|
log "Resetting failed systemd state"
|
|
run_as_root systemctl reset-failed || true
|
|
}
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
validate_args
|
|
|
|
require_root_runner
|
|
require_command systemctl
|
|
|
|
stop_and_disable_services
|
|
remove_service_files
|
|
reload_systemd
|
|
remove_global_links
|
|
remove_program_files
|
|
|
|
log "Uninstall completed"
|
|
}
|
|
|
|
main "$@"
|