From b7d97ffd416ebdabfa73316a0ee6b2b0ebef022a Mon Sep 17 00:00:00 2001 From: Endial Fang Date: Mon, 14 Aug 2023 15:20:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E9=80=9A=E7=94=A8?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E5=BA=93=E5=AD=98=E5=82=A8=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{usr/local/license => colovu}/LICENSE | 0 .../local/scripts => colovu/lib}/libcommon.sh | 33 +- .../local/scripts => colovu/lib}/libfile.sh | 19 +- .../local/scripts => colovu/lib}/libfs.sh | 34 +- .../local/scripts => colovu/lib}/liblog.sh | 17 +- .../local/scripts => colovu/lib}/libnet.sh | 13 +- prebuilds/colovu/lib/libos.sh | 302 ++++++++++++++++++ prebuilds/colovu/lib/libservice.sh | 212 ++++++++++++ .../scripts => colovu/lib}/libvalidations.sh | 27 +- prebuilds/usr/local/scripts/libos.sh | 109 ------- prebuilds/usr/local/scripts/libservice.sh | 87 ----- 11 files changed, 613 insertions(+), 240 deletions(-) rename prebuilds/{usr/local/license => colovu}/LICENSE (100%) rename prebuilds/{usr/local/scripts => colovu/lib}/libcommon.sh (75%) rename prebuilds/{usr/local/scripts => colovu/lib}/libfile.sh (78%) rename prebuilds/{usr/local/scripts => colovu/lib}/libfs.sh (80%) rename prebuilds/{usr/local/scripts => colovu/lib}/liblog.sh (83%) rename prebuilds/{usr/local/scripts => colovu/lib}/libnet.sh (92%) create mode 100644 prebuilds/colovu/lib/libos.sh create mode 100644 prebuilds/colovu/lib/libservice.sh rename prebuilds/{usr/local/scripts => colovu/lib}/libvalidations.sh (90%) delete mode 100644 prebuilds/usr/local/scripts/libos.sh delete mode 100644 prebuilds/usr/local/scripts/libservice.sh diff --git a/prebuilds/usr/local/license/LICENSE b/prebuilds/colovu/LICENSE similarity index 100% rename from prebuilds/usr/local/license/LICENSE rename to prebuilds/colovu/LICENSE diff --git a/prebuilds/usr/local/scripts/libcommon.sh b/prebuilds/colovu/lib/libcommon.sh similarity index 75% rename from prebuilds/usr/local/scripts/libcommon.sh rename to prebuilds/colovu/lib/libcommon.sh index 2078677..e262d9c 100644 --- a/prebuilds/usr/local/scripts/libcommon.sh +++ b/prebuilds/colovu/lib/libcommon.sh @@ -1,29 +1,29 @@ #!/bin/bash -# Ver: 1.3 by Endial Fang (endial@126.com) +# Ver: 1.5 by Endial Fang (endial@126.com) # # 通用函数库 # 加载依赖项 -. /usr/local/scripts/liblog.sh # 日志输出函数库 +. /colovu/lib/liblog.sh # 日志输出函数库 # 函数列表 # 打印包含包含Logo的欢迎信息 print_welcome_info() { - [[ -n "${APP_NAME}" ]] && github_url="/docker-${APP_NAME}" + [[ -n "${APP_NAME:-}" ]] && github_repo="https://github.com/colovu/docker-${APP_NAME}" - LOG_I ' ____ _ ' - LOG_I ' / ___|___ | | _____ ___ _ ' - LOG_I '| | / _ \| |/ _ \ \ / / | | | '"Docker : ${BOLD}${APP_NAME:-undefined}${RESET}" - LOG_I '| |__| (_) | | (_) \ V /| |_| | '"Version: ${BOLD}${APP_VERSION:-0.0}${RESET}" - LOG_I ' \____\___/|_|\___/ \_/ \__,_| '"PowerBy: ${BOLD}Endial@126.com${RESET}" - LOG_D " Project Repo: https://github.com/colovu/${github_url:-}" + LOG_I " ____ _ " + LOG_I " / ___|___ | | _____ ___ _ " + LOG_I "| | / _ \| |/ _ \ \ / / | | | Docker : ${BOLD}${APP_NAME:-undefined}${RESET}" + LOG_I "| |__| (_) | | (_) \ V /| |_| | Version: ${BOLD}${APP_VER:-0.0}${RESET}" + LOG_I " \____\___/|_|\___/ \_/ \__,_| PowerBy: ${BOLD}Endial@126.com${RESET}" + LOG_D " Project Repo: ${github_repo:-}" LOG_I "" } # 根据需要打印欢迎信息 print_image_welcome() { - if [[ "$(id -u)" = "0" ]]; then + if [[ ! "$(id -u)" = "0" ]]; then print_welcome_info fi } @@ -35,7 +35,7 @@ print_command_help() { local arg for arg; do case "$arg" in - -'?'|--help|-V|--version|-version) + -'?'|-H|-h|--help|-help|-V|-v|--version|-version) exec "$@" exit ;; @@ -100,17 +100,6 @@ process_init_files() { done } -# 检测当前是否为 root 用户 -is_root() { - if [[ "$(id -u)" = "0" ]]; then - LOG_D "Run as root." - true - else - LOG_D "Run as non-root: $(id -u)" - false - fi -} - # 检测当前脚本是被直接执行的,还是从其他脚本中使用 "source" 调用的 is_sourced() { [ "${#FUNCNAME[@]}" -ge 2 ] \ diff --git a/prebuilds/usr/local/scripts/libfile.sh b/prebuilds/colovu/lib/libfile.sh similarity index 78% rename from prebuilds/usr/local/scripts/libfile.sh rename to prebuilds/colovu/lib/libfile.sh index 1e664c1..1cf7344 100644 --- a/prebuilds/usr/local/scripts/libfile.sh +++ b/prebuilds/colovu/lib/libfile.sh @@ -1,10 +1,10 @@ #!/bin/bash -# Ver: 1.0 by Endial Fang (endial@126.com) +# Ver: 1.1 by Endial Fang (endial@126.com) # # 文件操作函数库 # 加载依赖项 -. /usr/local/scripts/liblog.sh # 日志输出函数库 +. /colovu/lib/liblog.sh # 日志输出函数库 # 函数列表 @@ -76,3 +76,18 @@ remove_in_file() { fi echo "$result" > "$filename" } + +# 在符合条件的行后增加文本 +# 参数: +# $1 - 文件名 +# $2 - 正则表达式 +# $3 - 待增加的文本 +append_in_file() { + local file="${1:?missing file}" + local match_regex="${2:?missing pattern}" + local value="${3:?missing value}" + + # We read the file in reverse, replace the first match (0,/pattern/s) and then reverse the results again + result="$(tac "$file" | sed -E "0,/($match_regex)/s||${value}\n\1|" | tac)" + echo "$result" > "$file" +} diff --git a/prebuilds/usr/local/scripts/libfs.sh b/prebuilds/colovu/lib/libfs.sh similarity index 80% rename from prebuilds/usr/local/scripts/libfs.sh rename to prebuilds/colovu/lib/libfs.sh index 1f3c60d..7cb4831 100644 --- a/prebuilds/usr/local/scripts/libfs.sh +++ b/prebuilds/colovu/lib/libfs.sh @@ -1,13 +1,24 @@ #!/bin/bash -# Ver: 1.1 by Endial Fang (endial@126.com) +# Ver: 1.2 by Endial Fang (endial@126.com) # # 文件管理函数库 # 加载依赖项 -. /usr/local/scripts/liblog.sh # 日志输出函数库 +. /colovu/lib/liblog.sh # 日志输出函数库 # 函数列表 +# 确保指定的 文件/路径 所属权为指定的 用户/组 +# 参数: +# $1 - 文件路径 +# $2 - 用户 +ensure_owned_by() { + local path="${1:?path is missing}" + local owner="${2:?owner is missing}" + + chown "$owner":"$owner" "$path" +} + # 检测目录是否存在,如果不存在则创建,同时修改为指定的用户 # 参数: # $1 - 目录路径 @@ -18,7 +29,7 @@ ensure_dir_exists() { mkdir -p "${dir}" if [[ -n $owner ]]; then - chown "$owner":"$owner" "$dir" + ensure_owned_by "$dir" "$owner" fi } @@ -35,6 +46,23 @@ is_dir_empty() { fi } +# 检测指定的路径当前用户是否可写入 +# 参数: +# $1 - 文件或路径 +# 返回值: +# true / false +is_writable() { + local file="${1:?missing file}" + local dir + dir="$(dirname "$file")" + + if [[ ( -f "$file" && -w "$file" ) || ( ! -f "$file" && -d "$dir" && -w "$dir" ) ]]; then + true + else + false + fi +} + # 循环设置目录中子目录及文件权限 # 参数: # $1 - paths (as a string). diff --git a/prebuilds/usr/local/scripts/liblog.sh b/prebuilds/colovu/lib/liblog.sh similarity index 83% rename from prebuilds/usr/local/scripts/liblog.sh rename to prebuilds/colovu/lib/liblog.sh index 62f50da..983143e 100644 --- a/prebuilds/usr/local/scripts/liblog.sh +++ b/prebuilds/colovu/lib/liblog.sh @@ -38,8 +38,7 @@ stderr_print() { # 输出实际日志信息 # 参数: -# $1 - 日志类型 -# $2 - 日志信息 +# $1 - 日志信息 LOG() { local -r bool="${ENV_DEBUG:-false}" shopt -s nocasematch @@ -48,13 +47,12 @@ LOG() { else debugInfo="${CYAN}${APP_NAME:-}" fi - stderr_print "${debugInfo} ${MAGENTA}$(date "+%T")${RESET} ${*}" + stderr_print "${debugInfo} ${MAGENTA}$(date "+%F %T.%3N")${RESET} ${*}" } # 输出调试类日志信息,尽量少使用 # 参数: -# $1 - 日志类型 -# $2 - 日志信息 +# $1 - 日志信息 LOG_D() { local -r bool="${ENV_DEBUG:-false}" shopt -s nocasematch @@ -65,24 +63,21 @@ LOG_D() { # 输出提示信息类日志信息 # 参数: -# $1 - 日志类型 -# $2 - 日志信息 +# $1 - 日志信息 LOG_I() { LOG "${GREEN}INF${RESET}: ${*}" } # 输出警告类日志信息至sterr # 参数: -# $1 - 日志类型 -# $2 - 日志信息 +# $1 - 日志信息 LOG_W() { LOG "${YELLOW}WRN${RESET}: ${*}" } # 输出错误类日志信息至sterr,并退出脚本 # 参数: -# $1 - 日志类型 -# $2 - 日志信息 +# $1 - 日志信息 LOG_E() { LOG "${RED}ERR${RESET}: ${*}" } diff --git a/prebuilds/usr/local/scripts/libnet.sh b/prebuilds/colovu/lib/libnet.sh similarity index 92% rename from prebuilds/usr/local/scripts/libnet.sh rename to prebuilds/colovu/lib/libnet.sh index 9853696..6edc878 100644 --- a/prebuilds/usr/local/scripts/libnet.sh +++ b/prebuilds/colovu/lib/libnet.sh @@ -4,13 +4,16 @@ # 文件管理函数库 # 加载依赖项 -. /usr/local/scripts/liblog.sh # 日志输出函数库 +. /colovu/lib/liblog.sh # 日志输出函数库 # 函数列表 # 域名解析 # 参数: # $1 - 需要解析的主机名 +# $2 - IP 地址版本 v4/v6, 为空时解析所有版本 +# 返回值: +# IP地址 dns_lookup() { local host="${1:?host is missing}" getent ahosts "$host" | awk '/STREAM/ {print $1 }' @@ -21,6 +24,8 @@ dns_lookup() { # $1 - 主机名 # $2 - 尝试次数 # $3 - 重试间隔时间(秒) +# 返回值: +# IP地址 wait_for_dns_lookup() { local hostname="${1:?hostname is missing}" local retries="${2:-5}" @@ -38,6 +43,8 @@ wait_for_dns_lookup() { } # 获取当前主机 IP +# 返回值: +# IP地址 get_machine_ip() { local -a ip_addresses local hostname @@ -52,11 +59,11 @@ get_machine_ip() { echo "${ip_addresses[0]}" } -# Check if the provided argument is a resolved hostname +# 检测指定的主机名是否可解析 # 参数: # $1 - 待检测的主机名 # 返回值: -# 布尔值 +# true / false is_hostname_resolved() { local -r host="${1:?missing value}" if [[ -n "$(dns_lookup "$host")" ]]; then diff --git a/prebuilds/colovu/lib/libos.sh b/prebuilds/colovu/lib/libos.sh new file mode 100644 index 0000000..20486b3 --- /dev/null +++ b/prebuilds/colovu/lib/libos.sh @@ -0,0 +1,302 @@ +#!/bin/bash +# Ver: 1.3 by Endial Fang (endial@126.com) +# +# 操作系统控制函数库 + +# 加载依赖项 +. /colovu/lib/libfs.sh # 文件系统函数库 + +# 函数列表 + +# 检测指定用户账户是否存在 +# 参数: +# $1 - 用户账户 +# 返回值: +# 0 / 1 +is_user_exists() { + local user="${1:?user is missing}" + id "$user" >/dev/null 2>&1 +} + +# 检测指定用户分组是否存在 +# 参数: +# $1 - 用户组 +# 返回值: +# 0 / 1 +is_group_exists() { + local group="${1:?group is missing}" + getent group "$group" >/dev/null 2>&1 +} + +# 检测当前是否为 root 用户 +# 返回值: +# true / false +is_root() { + if [[ "$(id -u)" = "0" ]]; then + LOG_D "Run as root." + true + else + LOG_D "Run as non-root: $(id -u)" + false + fi +} + +# 确保指定用户组在系统中存在 +# 参数: +# $1 - 用户组 +# 标志位: +# -s|--system - 创建系统用户 (uid <= 999) +ensure_group_exists() { + local group="${1:?group is missing}" + local is_system_user=false + + # 检测标志位 + shift 1 + while [ "$#" -gt 0 ]; do + case "$1" in + -s|--system) + is_system_user=true + ;; + *) + echo "Invalid command line flag $1" >&2 + return 1 + ;; + esac + shift + done + + if ! is_group_exists "$group"; then + local -a args=("$group") + $is_system_user && args+=("--system") + groupadd "${args[@]}" >/dev/null 2>&1 + fi +} + +# 确保指定用户在系统中存在 +# 参数: +# $1 - 用户 +# 标志位: +# -g|--group - 用户组 +# -h|--home - 用户家目录 +# -s|--system - 创建系统用户 (uid <= 999) +ensure_user_exists() { + local user="${1:?user is missing}" + local group="" + local home="" + local is_system_user=false + + # Validate arguments + shift 1 + while [ "$#" -gt 0 ]; do + case "$1" in + -g|--group) + shift + group="${1:?missing group}" + ;; + -h|--home) + shift + home="${1:?missing home directory}" + ;; + -s|--system) + is_system_user=true + ;; + *) + echo "Invalid command line flag $1" >&2 + return 1 + ;; + esac + shift + done + + if ! is_user_exists "$user"; then + local -a user_args=("-N" "$user") + $is_system_user && user_args+=("--system") + useradd "${user_args[@]}" >/dev/null 2>&1 + fi + + if [[ -n "$group" ]]; then + local -a group_args=("$group") + $is_system_user && group_args+=("--system") + ensure_group_exists "${group_args[@]}" + usermod -g "$group" "$user" >/dev/null 2>&1 + fi + + if [[ -n "$home" ]]; then + mkdir -p "$home" + usermod -d "$home" "$user" >/dev/null 2>&1 + configure_permissions_ownership "$home" -d "775" -f "664" -u "$user" -g "$group" + fi +} + +# 获取系统可用内存大小(MB)信息 +# 返回值: +# 内存大小(兆字节) +get_total_memory() { + echo $(($(grep MemTotal /proc/meminfo | awk '{print $2}') / 1024)) +} + +# 获取以内存定量方式描述的机器类型 +# 标志位: +# --memory - 内存大小 (MB,可选) +# 返回值: +# 类型名称 +get_machine_size() { + local memory="" + + # 检测标志位 + while [[ "$#" -gt 0 ]]; do + case "$1" in + --memory) + shift + memory="${1:?missing memory}" + ;; + *) + echo "Invalid command line flag $1" >&2 + return 1 + ;; + esac + shift + done + + if [[ -z "$memory" ]]; then + debug "Memory was not specified, detecting available memory automatically" + memory="$(get_total_memory)" + fi + sanitized_memory=$(convert_to_mb "$memory") + if [[ "$sanitized_memory" -gt 26000 ]]; then + echo 2xlarge + elif [[ "$sanitized_memory" -gt 13000 ]]; then + echo xlarge + elif [[ "$sanitized_memory" -gt 6000 ]]; then + echo large + elif [[ "$sanitized_memory" -gt 3000 ]]; then + echo medium + elif [[ "$sanitized_memory" -gt 1500 ]]; then + echo small + else + echo micro + fi +} + +# 获取已定义的所有内存大小描述 +# 返回值: +# 描述值列表 +get_supported_machine_sizes() { + echo micro small medium large xlarge 2xlarge +} + +# 将以字符串表示的内存大小转换为以MB为单位的内存大小值 (i.e. 2G -> 2048) +# 参数: +# $1 - 内存大小 +# 返回值: +# 转换后的数值 +convert_to_mb() { + local amount="${1:-}" + if [[ $amount =~ ^([0-9]+)(M|G) ]]; then + size="${BASH_REMATCH[1]}" + unit="${BASH_REMATCH[2]}" + if [[ "$unit" = "G" ]]; then + amount="$((size * 1024))" + else + amount="$size" + fi + fi + echo "$amount" +} + +# 如果禁用调试模式,将输出信息重定向至 /dev/null +# 参数: +# $@ - 待执行的命令 +debug_execute() { + local bool="${ENV_DEBUG:-false}" + shopt -s nocasematch + if [[ "$bool" = 1 || "$bool" =~ ^(yes|true)$ ]]; then + "$@" + else + "$@" >/dev/null 2>&1 + fi +} + +# 重试执行命令 +# 参数: +# $1 - 命令 (字符串) +# $2 - 最大尝试次数. 默认值: 12 +# $3 - 重试前等待时间(秒). 默认值: 5 +# 返回值: +# 0 / 1 +retry_while() { + local -r cmd="${1:?cmd is missing}" + local -r retries="${2:-12}" + local -r sleep_time="${3:-5}" + local return_value=1 + + read -r -a command <<< "$cmd" + for ((i = 1 ; i <= retries ; i+=1 )); do + "${command[@]}" && return_value=0 && break + sleep "$sleep_time" + done + return $return_value +} + +# 生成随机字符串 +# 标志位: +# -t|--type - 字符串类型 (ascii, alphanumeric, numeric). 默认值: ascii +# -c|--count - 字符串长度. 默认值: 32 +# 返回值: +# 字符串 +generate_random_string() { + local type="ascii" + local count="32" + local filter + local result + + # 检测标志位 + while [[ "$#" -gt 0 ]]; do + case "$1" in + -t|--type) + shift + type="$1" + ;; + -c|--count) + shift + count="$1" + ;; + *) + echo "Invalid command line flag $1" >&2 + return 1 + ;; + esac + shift + done + + # 检测类型 + case "$type" in + ascii) + filter="[:print:]" + ;; + alphanumeric) + filter="a-zA-Z0-9" + ;; + numeric) + filter="0-9" + ;; + *) + echo "Invalid type ${type}" >&2 + return 1 + esac + # Obtain count + 10 lines from /dev/urandom to ensure that the resulting string has the expected size + # Note there is a very small chance of strings starting with EOL character + # Therefore, the higher amount of lines read, this will happen less frequently + result="$(head -n "$((count + 10))" /dev/urandom | tr -dc "$filter" | head -c "$count")" + echo "$result" +} + +# 为指定字符串生成 MD5 值 +# 参数: +# $1 - 字符串 +# 返回值: +# 字符串对应的 MD5 +generate_md5_hash() { + local -r str="${1:?missing input string}" + echo -n "$str" | md5sum | awk '{print $1}' +} diff --git a/prebuilds/colovu/lib/libservice.sh b/prebuilds/colovu/lib/libservice.sh new file mode 100644 index 0000000..23cc35c --- /dev/null +++ b/prebuilds/colovu/lib/libservice.sh @@ -0,0 +1,212 @@ +#!/bin/bash +# Ver: 1.1 by Endial Fang (endial@126.com) +# +# 服务管理函数库 + +# shellcheck disable=SC1091 + +# 加载依赖项 +. /colovu/lib/libvalidations.sh # 数据有效性检测函数库 + +# 函数列表 + +# 获取并返回服务 PID +# 参数: +# $1 - PID 文件 +# 返回值: +# PID +get_pid_from_file() { + local pid_file="${1:?pid file is missing}" + + if [[ -f "$pid_file" ]]; then + if [[ -n "$(< "$pid_file")" ]] && [[ "$(< "$pid_file")" -gt 0 ]]; then + echo "$(< "$pid_file")" + fi + fi +} + +# 检测 PID 对应的服务是否在运行中 +# 参数: +# $1 - PID +# 返回值: +# 0 / 1 +is_service_running() { + local pid="${1:?pid is missing}" + + kill -0 "$pid" 2>/dev/null +} + +# 通过发送信号停止一个指定 PID 的服务 +# 参数: +# $1 - PID 文件 +# $2 - 信号 (可选) +stop_service_using_pid() { + local pid_file="${1:?pid file is missing}" + local signal="${2:-}" + local pid + + pid="$(get_pid_from_file "$pid_file")" + [[ -z "$pid" ]] || ! is_service_running "$pid" && return + + if [[ -n "$signal" ]]; then + kill "-${signal}" "$pid" + else + kill "$pid" + fi + + local counter=10 + while [[ "$counter" -ne 0 ]] && is_service_running "$pid"; do + sleep 1 + counter=$((counter - 1)) + done +} + +# 启动一个 cron 守护进程 +# 返回值: +# true / false +cron_start() { + if [[ -x "/usr/sbin/cron" ]]; then + /usr/sbin/cron + elif [[ -x "/usr/sbin/crond" ]]; then + /usr/sbin/crond + else + false + fi +} + +# 为指定的服务生成 cron 配置文件 +# 参数: +# $1 - 服务名称 +# $2 - 命令 +# 标志位: +# --run-as - 运行的用户. 默认值: root +# --schedule - Cron 周期配置. 默认值: * * * * * +generate_cron_conf() { + local service_name="${1:?service name is missing}" + local cmd="${2:?command is missing}" + local run_as="root" + local schedule="* * * * *" + local clean="true" + + local clean="true" + + # 检测标志位 + shift 2 + while [[ "$#" -gt 0 ]]; do + case "$1" in + --run-as) + shift + run_as="$1" + ;; + --schedule) + shift + schedule="$1" + ;; + --no-clean) + clean="false" + ;; + *) + echo "Invalid command line flag ${1}" >&2 + return 1 + ;; + esac + shift + done + + mkdir -p /etc/cron.d + if "$clean"; then + echo "${schedule} ${run_as} ${cmd}" > /etc/cron.d/"$service_name" + else + echo "${schedule} ${run_as} ${cmd}" >> /etc/cron.d/"$service_name" + fi +} + +# 为指定的服务生成 monit 配置文件 +# 参数: +# $1 - 服务名 +# $2 - PID 文件 +# $3 - 启动命令 +# $4 - 停止命令 +# 标志位: +# --disabled - 是否禁用. 默认值: no +generate_monit_conf() { + local service_name="${1:?service name is missing}" + local pid_file="${2:?pid file is missing}" + local start_command="${3:?start command is missing}" + local stop_command="${4:?stop command is missing}" + local monit_conf_dir="/etc/monit/conf.d" + local disabled="no" + + # 检测标志位 + shift 4 + while [[ "$#" -gt 0 ]]; do + case "$1" in + --disabled) + shift + disabled="$1" + ;; + *) + echo "Invalid command line flag ${1}" >&2 + return 1 + ;; + esac + shift + done + + is_boolean_yes "$disabled" && conf_suffix=".disabled" + mkdir -p "$monit_conf_dir" + cat >"${monit_conf_dir}/${service_name}.conf${conf_suffix:-}" <&2 + return 1 + ;; + esac + shift + done + + mkdir -p "$logrotate_conf_dir" + cat <"${logrotate_conf_dir}/${service_name}" +${log_path} { + ${period} + rotate ${rotations} + dateext + compress + copytruncate + missingok +$(indent "$extra" 2) +} +EOF +} diff --git a/prebuilds/usr/local/scripts/libvalidations.sh b/prebuilds/colovu/lib/libvalidations.sh similarity index 90% rename from prebuilds/usr/local/scripts/libvalidations.sh rename to prebuilds/colovu/lib/libvalidations.sh index be29b14..c55c67a 100644 --- a/prebuilds/usr/local/scripts/libvalidations.sh +++ b/prebuilds/colovu/lib/libvalidations.sh @@ -1,16 +1,18 @@ #!/bin/bash -# Ver: 1.0 by Endial Fang (endial@126.com) +# Ver: 1.1 by Endial Fang (endial@126.com) # # 数据有效性校验函数库 # 加载依赖项 -. /usr/local/scripts/liblog.sh # 日志输出函数库 +. /colovu/lib/liblog.sh # 日志输出函数库 # 函数列表 # 检测数据是否为整数 # 参数: # $1 - 待检测的数据 +# 返回值: +# true / false is_int() { local -r int="${1:?missing value}" if [[ "$int" =~ ^-?[0-9]+ ]]; then @@ -23,6 +25,8 @@ is_int() { # 检测数据是否为正整数 # 参数: # $1 - 待检测的数据 +# 返回值: +# true / false is_positive_int() { local -r int="${1:?missing value}" if is_int "$int" && (( "${int}" >= 0 )); then @@ -35,6 +39,8 @@ is_positive_int() { # 检测数据是否为布尔值 '1' 或字符串 'yes/true' # 参数: # $1 - 待检测的数据 +# 返回值: +# true / false is_boolean_yes() { local -r bool="${1:-}" # comparison is performed without regard to the case of alphabetic characters @@ -49,6 +55,8 @@ is_boolean_yes() { # 检测数据是否为字符串 'yes/no' # 参数: # $1 - 待检测的数据 +# 返回值: +# true / false is_yes_no_value() { local -r bool="${1:-}" if [[ "$bool" =~ ^(yes|no)$ ]]; then @@ -61,6 +69,8 @@ is_yes_no_value() { # 检测数据是否为字符串 'true/false' # 参数: # $1 - 待检测的数据 +# 返回值: +# true / false is_true_false_value() { local -r bool="${1:-}" if [[ "$bool" =~ ^(true|false)$ ]]; then @@ -73,6 +83,8 @@ is_true_false_value() { # 检测提供的参数是否为空字符串或未定义 # 参数: # $1 - 待检测的数据 +# 返回值: +# true / false is_empty_value() { local -r val="${1:-}" if [[ -z "$val" ]]; then @@ -83,10 +95,12 @@ is_empty_value() { } # 检测数据是否为有效的端口号 +# 标志位: +# -unprivileged - 没有特权的端口 # 参数: # $1 - 待检测的数据 # 返回值: -# 布尔值 或 错误消息 +# true / false 或 错误消息 validate_port() { local value local unprivileged=0 @@ -145,6 +159,8 @@ validate_port() { # 检测数据是否为有效的IPv4地址 # 参数: # $1 - 待检测的数据 +# 返回值: +# 0 / 1 validate_ipv4() { local ip="${1:?ip is missing}" local stat=1 @@ -159,8 +175,13 @@ validate_ipv4() { } # 校验字符串格式 +# 标志位: +# -min-length - 最小长度 +# -max-length - 最大长度 # 参数: # $1 - 待检测的数据 +# 返回值: +# 0 / 1 validate_string() { local string local min_length=-1 diff --git a/prebuilds/usr/local/scripts/libos.sh b/prebuilds/usr/local/scripts/libos.sh deleted file mode 100644 index 047fa9d..0000000 --- a/prebuilds/usr/local/scripts/libos.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash -# Ver: 1.2 by Endial Fang (endial@126.com) -# -# 操作系统控制函数库 - -# 加载依赖项 -. /usr/local/scripts/liblog.sh # 日志输出函数库 - -# 函数列表 - -# 检测指定用户账户是否存在 -# 参数: -# $1 - 用户账户 -user_exists() { - local user="${1:?user is missing}" - id "$user" >/dev/null 2>&1 -} - -# 检测指定用户分组是否存在 -# 参数: -# $1 - 用户组 -group_exists() { - local group="${1:?group is missing}" - getent group "$group" >/dev/null 2>&1 -} - -# 获取系统可用内存大小(MB)信息 -get_total_memory() { - echo $(($(grep MemTotal /proc/meminfo | awk '{print $2}') / 1024)) -} - -# 获取以定量方式描述的内存大小 -# 参数: -# $1 - 内存大小 (MB,可选) -get_machine_size() { - local memory="${1:-}" - if [[ -z "$memory" ]]; then - debug "Memory was not specified, detecting available memory automatically" - memory="$(get_total_memory)" - fi - sanitized_memory=$(convert_to_mb "$memory") - if [[ "$sanitized_memory" -gt 26000 ]]; then - echo 2xlarge - elif [[ "$sanitized_memory" -gt 13000 ]]; then - echo xlarge - elif [[ "$sanitized_memory" -gt 6000 ]]; then - echo large - elif [[ "$sanitized_memory" -gt 3000 ]]; then - echo medium - elif [[ "$sanitized_memory" -gt 1500 ]]; then - echo small - else - echo micro - fi -} - -# 获取已定义的所有内存大小描述 -get_supported_machine_sizes() { - echo micro small medium large xlarge 2xlarge -} - -# 将以字符串表示的内存大小转换为以MB为单位的内存大小值 (i.e. 2G -> 2048) -# 参数: -# $1 - 内存大小 -convert_to_mb() { - local amount="${1:-}" - if [[ $amount =~ ^([0-9]+)(M|G) ]]; then - size="${BASH_REMATCH[1]}" - unit="${BASH_REMATCH[2]}" - if [[ "$unit" = "G" ]]; then - amount="$((size * 1024))" - else - amount="$size" - fi - fi - echo "$amount" -} - -# 如果禁用调试模式,将输出信息重定向至 /dev/null -# 参数: -# $@ - 待执行的命令 -debug_execute() { - local -r bool="${ENV_DEBUG:-false}" - shopt -s nocasematch - if [[ "$bool" = 1 || "$bool" =~ ^(yes|true)$ ]]; then - "$@" - else - "$@" >/dev/null 2>&1 - fi -} - -# 重试执行命令 -# 参数: -# $1 - cmd (as a string) -# $2 - 最大尝试次数. Default: 12 -# $3 - 重试前等待时间(秒). Default: 5 -retry_while() { - local -r cmd="${1:?cmd is missing}" - local -r retries="${2:-12}" - local -r sleep_time="${3:-5}" - local return_value=1 - - read -r -a command <<< "$cmd" - for ((i = 1 ; i <= retries ; i+=1 )); do - "${command[@]}" && return_value=0 && break - sleep "$sleep_time" - done - return $return_value -} diff --git a/prebuilds/usr/local/scripts/libservice.sh b/prebuilds/usr/local/scripts/libservice.sh deleted file mode 100644 index bbf90b4..0000000 --- a/prebuilds/usr/local/scripts/libservice.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/bin/bash -# Ver: 1.0 by Endial Fang (endial@126.com) -# -# 服务管理函数库 - -# shellcheck disable=SC1091 - -# 加载依赖项 -. /usr/local/scripts/liblog.sh # 日志输出函数库 - -# 函数列表 - -# 获取并返回服务 PID -# 参数: -# $1 - PID 文件 -get_pid_from_file() { - local pid_file="${1:?pid file is missing}" - - if [[ -f "$pid_file" ]]; then - if [[ -n "$(< "$pid_file")" ]] && [[ "$(< "$pid_file")" -gt 0 ]]; then - echo "$(< "$pid_file")" - fi - fi -} - -# 检测 PID 对应的服务是否在运行中 -# 参数: -# $1 - PID -is_service_running() { - local pid="${1:?pid is missing}" - - kill -0 "$pid" 2>/dev/null -} - -# 通过发送信号停止一个指定的服务 -# 参数: -# $1 - PID 文件 -# $2 - 信号 (可选) -stop_service_using_pid() { - local pid_file="${1:?pid file is missing}" - local signal="${2:-}" - local pid - - pid="$(get_pid_from_file "$pid_file")" - [[ -z "$pid" ]] || ! is_service_running "$pid" && return - - if [[ -n "$signal" ]]; then - kill "-${signal}" "$pid" - else - kill "$pid" - fi - - local counter=10 - while [[ "$counter" -ne 0 ]] && is_service_running "$pid"; do - sleep 1 - counter=$((counter - 1)) - done -} - -# 生成一个 Logrotate 配置文件 -# 参数: -# $1 - 应用名称 -# $2 - 日志路径及日志文件名 -# $3 - 周期 -# $4 - Rotations 存储的数量 -# $5 - 其他参数 (可选) -generate_logrotate_conf() { - local service_name="${1:?service name is missing}" - local log_path="${2:?log path is missing}" - local period="${3:-weekly}" - local rotations="${4:-150}" - local extra_options="${5:-}" - local logrotate_conf_dir="/etc/logrotate.d" - - mkdir -p "$logrotate_conf_dir" - cat >"${logrotate_conf_dir}/${service_name}" <<-'EOF' - ${log_path} { - ${period} - rotate ${rotations} - dateext - compress - copytruncate - missingok - ${extra_options} - } -EOF -}