Simplify helpers under line cap
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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[@]}"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user