Simplify helpers under line cap

This commit is contained in:
2026-04-17 11:33:45 +08:00
parent 4c431584eb
commit af871a67a3
5 changed files with 87 additions and 168 deletions

View File

@@ -46,6 +46,15 @@ usage() {
USAGE
}
MENU_LINES=(
'[1] 查看所有转发规则'
'[2] 添加新的转发规则'
'[3] 删除现有转发规则'
'[4] 查看系统环境状态'
'[5] 立即保存到磁盘'
'[0] 退出'
)
bootstrap() {
if [[ ${IPF_SKIP_ENV_CHECK} != 1 ]]; then
env_check_all
@@ -54,7 +63,7 @@ bootstrap() {
}
render_main_menu() {
local status persist count width
local status persist count width line
status=$(env_status_summary)
count=$(storage_count)
if persist_available; then
@@ -64,31 +73,20 @@ render_main_menu() {
fi
if use_box_ui; then
width=$(term_width)
if (( width > 78 )); then
width=78
fi
width=$(box_width)
box_top "${width}"
box_line "${width}" "${IPF_APP_NAME}"
box_separator "${width}"
box_line "${width}" "状态: ${status} 规则数: ${count} 持久化: ${persist}"
box_separator "${width}"
box_line "${width}" '[1] 查看所有转发规则'
box_line "${width}" '[2] 添加新的转发规则'
box_line "${width}" '[3] 删除现有转发规则'
box_line "${width}" '[4] 查看系统环境状态'
box_line "${width}" '[5] 立即保存到磁盘'
box_line "${width}" '[0] 退出'
for line in "${MENU_LINES[@]}"; do
box_line "${width}" "${line}"
done
box_bottom "${width}"
else
printf '%s\n' "${IPF_APP_NAME}"
printf '状态: %s | 规则数: %s | 持久化: %s\n' "${status}" "${count}" "${persist}"
printf '[1] 查看所有转发规则\n'
printf '[2] 添加新的转发规则\n'
printf '[3] 删除现有转发规则\n'
printf '[4] 查看系统环境状态\n'
printf '[5] 立即保存到磁盘\n'
printf '[0] 退出\n'
printf '%s\n' "${MENU_LINES[@]}"
fi
}

View File

@@ -83,19 +83,20 @@ repeat_char() {
}
term_width() {
if [[ -n ${COLUMNS:-} ]]; then
printf '%s\n' "${COLUMNS}"
return
fi
if command_is_available tput; then
tput cols 2>/dev/null && return
fi
[[ -n ${COLUMNS:-} ]] && { printf '%s\n' "${COLUMNS}"; return; }
command_is_available tput && tput cols 2>/dev/null && return
printf '80\n'
}
use_box_ui() {
[[ ${IPF_FORCE_PLAIN_UI} != 1 ]] || return 1
[[ $(term_width) -ge 60 ]]
[[ ${IPF_FORCE_PLAIN_UI} != 1 && $(term_width) -ge 60 ]]
}
box_width() {
local width
width=$(term_width)
(( width > 78 )) && width=78
printf '%s\n' "${width}"
}
pad_right() {
@@ -111,20 +112,14 @@ pad_right() {
printf '%s%*s' "${text}" "${padding}" ''
}
box_top() {
local width=${1:-60}
printf '%s\n' "$(repeat_char '═' "$((width - 2))")"
box_border() {
local left=$1 right=$2 width=${3:-60}
printf '%s%s%s\n' "${left}" "$(repeat_char '═' "$((width - 2))")" "${right}"
}
box_bottom() {
local width=${1:-60}
printf '╚%s╝\n' "$(repeat_char '═' "$((width - 2))")"
}
box_separator() {
local width=${1:-60}
printf '╠%s╣\n' "$(repeat_char '═' "$((width - 2))")"
}
box_top() { box_border '╔' '╗' "${1:-60}"; }
box_bottom() { box_border '╚' '╝' "${1:-60}"; }
box_separator() { box_border '╠' '╣' "${1:-60}"; }
box_line() {
local width=${1:-60}
@@ -134,19 +129,15 @@ box_line() {
}
print_section() {
local title=${1-}
local title=${1-} width
if use_box_ui; then
local width
width=$(term_width)
if (( width > 78 )); then
width=78
fi
width=$(box_width)
box_top "${width}"
box_line "${width}" "${title}"
box_bottom "${width}"
else
printf '== %s ==\n' "${title}"
return
fi
printf '== %s ==\n' "${title}"
}
read_test_input() {
@@ -181,7 +172,7 @@ prompt_input() {
if [[ -n ${default_value} ]]; then
read -r -p "${prompt} [${default_value}]: " reply
reply=${reply:-${default_value}}
[[ -n ${reply} ]] || reply=${default_value}
else
read -r -p "${prompt}: " reply
fi
@@ -338,17 +329,11 @@ validate_port() {
}
validate_proto() {
case ${1-} in
tcp|udp|both) return 0 ;;
*) return 1 ;;
esac
[[ ${1-} =~ ^(tcp|udp|both)$ ]]
}
validate_ipver() {
case ${1-} in
4|6|both) return 0 ;;
*) return 1 ;;
esac
[[ ${1-} =~ ^(4|6|both)$ ]]
}
require_root() {

View File

@@ -32,6 +32,20 @@ _env_add_warning() {
ENV_CHECK_WARNINGS+=("$1")
}
_env_require_cmd() {
local cmd=$1 issue=$2
command_is_available "${cmd}" && return 0
_env_add_issue "${issue}"
ENV_CHECK_NEED_PACKAGES=1
}
_env_require_forward() {
local file=$1 issue=$2
[[ $(_env_file_value "${file}" 2>/dev/null || printf '0') == 1 ]] && return 0
_env_add_issue "${issue}"
ENV_CHECK_NEED_SYSCTL=1
}
_env_file_value() {
local file=$1
[[ -f ${file} ]] || return 1
@@ -52,30 +66,14 @@ env_check_collect_issues() {
ENV_CHECK_NEED_SYSCTL=0
ENV_CHECK_NEED_STORAGE=0
if ! command_is_available "${IPF_CHECK_IPTABLES_CMD}"; then
_env_add_issue '缺少 iptables 命令。'
ENV_CHECK_NEED_PACKAGES=1
fi
if ! command_is_available "${IPF_CHECK_IP6TABLES_CMD}"; then
_env_add_issue '缺少 ip6tables 命令。'
ENV_CHECK_NEED_PACKAGES=1
fi
_env_require_cmd "${IPF_CHECK_IPTABLES_CMD}" '缺少 iptables 命令。'
_env_require_cmd "${IPF_CHECK_IP6TABLES_CMD}" '缺少 ip6tables 命令。'
if ! _env_package_installed || ! command_is_available "${IPF_CHECK_PERSIST_CMD}"; then
_env_add_issue '缺少 iptables-persistent / netfilter-persistent。'
ENV_CHECK_NEED_PACKAGES=1
fi
if [[ $(_env_file_value "${IPF_IPV4_FORWARD_FILE}" 2>/dev/null || printf '0') != 1 ]]; then
_env_add_issue 'IPv4 转发未开启。'
ENV_CHECK_NEED_SYSCTL=1
fi
if [[ $(_env_file_value "${IPF_IPV6_FORWARD_FILE}" 2>/dev/null || printf '0') != 1 ]]; then
_env_add_issue 'IPv6 转发未开启。'
ENV_CHECK_NEED_SYSCTL=1
fi
_env_require_forward "${IPF_IPV4_FORWARD_FILE}" 'IPv4 转发未开启。'
_env_require_forward "${IPF_IPV6_FORWARD_FILE}" 'IPv6 转发未开启。'
if [[ ! -d ${IPF_STORAGE_DIR} || ! -w ${IPF_STORAGE_DIR} ]]; then
_env_add_issue "状态目录不可用: ${IPF_STORAGE_DIR}"
@@ -114,10 +112,7 @@ env_check_print_report() {
}
_env_install_packages() {
command_is_available "${APT_GET_BIN}" || {
log_err '缺少 apt-get无法自动安装依赖。'
return 1
}
command_is_available "${APT_GET_BIN}" || { log_err '缺少 apt-get无法自动安装依赖。'; return 1; }
command_is_available "${DEBCONF_SET_SELECTIONS_BIN}" || {
log_err '缺少 debconf-set-selections无法预置安装选项。'
return 1
@@ -161,31 +156,16 @@ SYSCTL
}
env_check_apply_fixes() {
if (( ENV_CHECK_NEED_PACKAGES == 1 )); then
log_info '正在安装缺失软件包...'
_env_install_packages || return 1
fi
if (( ENV_CHECK_NEED_STORAGE == 1 )); then
log_info '正在创建状态目录...'
mkdir -p "${IPF_STORAGE_DIR}"
chmod 750 "${IPF_STORAGE_DIR}"
fi
if (( ENV_CHECK_NEED_SYSCTL == 1 )); then
log_info '正在写入 sysctl 配置并启用转发...'
_env_write_sysctl || return 1
fi
(( ENV_CHECK_NEED_PACKAGES == 1 )) && { log_info '正在安装缺失软件包...'; _env_install_packages || return 1; }
(( ENV_CHECK_NEED_STORAGE == 1 )) && { log_info '正在创建状态目录...'; mkdir -p "${IPF_STORAGE_DIR}"; chmod 750 "${IPF_STORAGE_DIR}"; }
(( ENV_CHECK_NEED_SYSCTL == 1 )) && { log_info '正在写入 sysctl 配置并启用转发...'; _env_write_sysctl || return 1; }
}
env_check_all() {
env_check_collect_issues
env_check_print_report
if ((${#ENV_CHECK_ISSUES[@]} == 0)); then
return 0
fi
((${#ENV_CHECK_ISSUES[@]} == 0)) && return 0
if [[ ${IPF_ASSUME_YES} == 1 ]]; then
log_info '已启用自动确认,开始修复。'
elif ! prompt_confirm '是否自动修复以上问题?' n; then
@@ -197,19 +177,12 @@ env_check_all() {
env_check_collect_issues
env_check_print_report
if ((${#ENV_CHECK_ISSUES[@]} > 0)); then
log_err '自动修复后仍存在未解决的问题。'
return 1
fi
return 0
((${#ENV_CHECK_ISSUES[@]} == 0)) && return 0
log_err '自动修复后仍存在未解决的问题。'
return 1
}
env_status_summary() {
env_check_collect_issues
if ((${#ENV_CHECK_ISSUES[@]} == 0)); then
printf '就绪\n'
else
printf '待修复(%d)\n' "${#ENV_CHECK_ISSUES[@]}"
fi
((${#ENV_CHECK_ISSUES[@]} == 0)) && printf '就绪\n' || printf '待修复(%d)\n' "${#ENV_CHECK_ISSUES[@]}"
}

View File

@@ -9,24 +9,17 @@ IPF_PERSIST_SH_LOADED=1
: "${IPF_SKIP_PERSIST:=0}"
persist_available() {
[[ ${IPF_SKIP_PERSIST} == 1 ]] && return 0
command_is_available "${NETFILTER_PERSISTENT_BIN}"
[[ ${IPF_SKIP_PERSIST} == 1 ]] || command_is_available "${NETFILTER_PERSISTENT_BIN}"
}
persist_save() {
[[ ${IPF_SKIP_PERSIST} == 1 ]] && return 0
persist_available || {
log_err '未找到 netfilter-persistent无法保存规则。'
return 1
}
persist_available || { log_err '未找到 netfilter-persistent无法保存规则。'; return 1; }
"${NETFILTER_PERSISTENT_BIN}" save >/dev/null
}
persist_reload() {
[[ ${IPF_SKIP_PERSIST} == 1 ]] && return 0
persist_available || {
log_err '未找到 netfilter-persistent无法重载规则。'
return 1
}
persist_available || { log_err '未找到 netfilter-persistent无法重载规则。'; return 1; }
"${NETFILTER_PERSISTENT_BIN}" reload >/dev/null
}

View File

@@ -9,60 +9,38 @@ IPF_STORAGE_SH_LOADED=1
: "${IPF_STORAGE_DB:=${IPF_STORAGE_DIR}/rules.db}"
: "${IPF_LOCK_FILE:=${IPF_STORAGE_DIR}/.lock}"
storage_dir() {
printf '%s\n' "${IPF_STORAGE_DIR}"
}
storage_db_path() {
printf '%s\n' "${IPF_STORAGE_DB}"
}
storage_lock_path() {
printf '%s\n' "${IPF_LOCK_FILE}"
}
storage_dir() { printf '%s\n' "${IPF_STORAGE_DIR}"; }
storage_db_path() { printf '%s\n' "${IPF_STORAGE_DB}"; }
storage_lock_path() { printf '%s\n' "${IPF_LOCK_FILE}"; }
storage_init() {
local dir db lock
dir=$(storage_dir)
db=$(storage_db_path)
lock=$(storage_lock_path)
mkdir -p "${dir}"
touch "${db}" "${lock}"
chmod 750 "${dir}"
chmod 640 "${db}"
chmod 600 "${lock}"
mkdir -p "${IPF_STORAGE_DIR}"
touch "${IPF_STORAGE_DB}" "${IPF_LOCK_FILE}"
chmod 750 "${IPF_STORAGE_DIR}"
chmod 640 "${IPF_STORAGE_DB}"
chmod 600 "${IPF_LOCK_FILE}"
}
storage_with_lock() {
local lock
storage_init
lock=$(storage_lock_path)
(
flock -x 9
"$@"
) 9>>"${lock}"
) 9>>"${IPF_LOCK_FILE}"
}
storage_add_unlocked() {
local line=${1-}
local db
[[ -n ${line} ]] || return 1
storage_init
db=$(storage_db_path)
printf '%s\n' "${line}" >>"${db}"
printf '%s\n' "${line}" >>"${IPF_STORAGE_DB}"
}
storage_add() {
storage_with_lock storage_add_unlocked "$@"
}
storage_add() { storage_with_lock storage_add_unlocked "$@"; }
storage_list() {
local db
db=$(storage_db_path)
[[ -f ${db} ]] || return 0
cat "${db}"
[[ -f ${IPF_STORAGE_DB} ]] || return 0
cat "${IPF_STORAGE_DB}"
}
storage_parse() {
@@ -95,11 +73,10 @@ storage_get() {
storage_delete_unlocked() {
local uuid=${1-}
local db tmp found=0 line current
local tmp found=0 line current
[[ -n ${uuid} ]] || return 1
storage_init
db=$(storage_db_path)
tmp="${db}.tmp.$$"
tmp="${IPF_STORAGE_DB}.tmp.$$"
: >"${tmp}"
while IFS= read -r line || [[ -n ${line} ]]; do
@@ -109,19 +86,12 @@ storage_delete_unlocked() {
continue
fi
printf '%s\n' "${line}" >>"${tmp}"
done <"${db}"
if (( found == 0 )); then
rm -f "${tmp}"
return 1
fi
mv "${tmp}" "${db}"
done <"${IPF_STORAGE_DB}"
(( found == 1 )) || { rm -f "${tmp}"; return 1; }
mv "${tmp}" "${IPF_STORAGE_DB}"
}
storage_delete() {
storage_with_lock storage_delete_unlocked "$@"
}
storage_delete() { storage_with_lock storage_delete_unlocked "$@"; }
storage_count() {
local count=0 line