已發布的原始碼
下載原始碼 ZIP
CH-J Server Manager
瀏覽特定應用程式版本的目錄與檔案。
CH-J / Server Manager
368 個檔案
core-20261017T201717Z-057348c4a70f
新增時間: 未記錄日期 · 建置時間:
1
# CH-J Server Manager - fixed remote helper for swap inspection and management (POSIX sh).2
# Started by Core as "sh -c SCRIPT chj OPERATION ARGS..." (through sudo/root for changes). Arguments are3
# validated by Core and again here. Changes are never made from the monitoring loop.4
set -u5
umask 0776
LC_ALL=C; export LC_ALL7
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin; export PATH8
FSTAB=/etc/fstab9
MARK='# CH-J chj.system-monitor'10
LOCK=/run/chj-swap.lock11
# Overridable only for the unit test of the swapoff safety rule.12
PROC=${CHJ_SWAP_TEST_PROC:-/proc}13
op=${1:-}14
[ $# -gt 0 ] && shift15
printf '\036CHJ_READY\n'17
fail() { printf '\036CHJ_ERR=%s\n' "$1"; [ $# -gt 1 ] && printf '%s\n' "$2" | head -c 2000 >&2; exit 3; }18
have() { command -v "$1" >/dev/null 2>&1; }19
is_root() { [ "$(id -u)" = 0 ]; }20
need_root() { is_root || fail PRIVILEGE_REQUIRED "Root privileges are required."; }21
# Swap files may live only at /swapfile*, /swap/<name> or /var/lib/swap/<name>.22
managed_path() {23
case $1 in24
/swapfile|/swapfile[A-Za-z0-9._-]*|/swap/[A-Za-z0-9._-]*|/var/lib/swap/[A-Za-z0-9._-]*) ;;25
*) return 1 ;;26
esac27
case $1 in *..*|*/./*|*//*) return 1 ;; esac28
[ ${#1} -le 96 ]29
}30
safe_path() { case $1 in /*) ;; *) return 1 ;; esac; case $1 in *[!A-Za-z0-9._/-]*|*..*) return 1 ;; esac; [ ${#1} -le 200 ]; }31
valid_int() { case $1 in ''|*[!0-9]*) return 1 ;; esac; [ ${#1} -le 9 ]; }32
valid_prio() { case $1 in -1) return 0 ;; ''|*[!0-9]*) return 1 ;; esac; [ "$1" -le 32767 ]; }33
meminfo() { awk -v key="$1" '$1==key":" { print $2*1024; found=1 } END { if (!found) print 0 }' "$PROC/meminfo"; }34
active_line() { awk -v p="$1" 'NR>1 && $1==p { print; exit }' "$PROC/swaps"; }35
is_active() { [ -n "$(active_line "$1")" ]; }36
used_of() { active_line "$1" | awk '{ printf "%.0f", $4*1024 }'; }37
is_swap_signature() {38
page=$(getconf PAGESIZE 2>/dev/null || echo 4096)39
[ "$(dd if="$1" bs=1 skip=$((page - 10)) count=10 2>/dev/null)" = SWAPSPACE2 ]40
}41
# Mount table first: statfs reports ext4 as "ext2/ext3". The longest mount point containing $1 wins.42
fs_type() {43
t=$(findmnt -no FSTYPE -T "$1" 2>/dev/null)44
[ -n "$t" ] || t=$(awk -v p="$1" '{ m=$2; if (m=="/" || p==m || index(p, m "/")==1) if (length(m)>=best) { best=length(m); fs=$3 } } END { if (fs!="") print fs }' "$PROC/mounts" 2>/dev/null)45
[ -n "$t" ] || t=$(stat -f -c %T "$1" 2>/dev/null)46
printf '%s\n' "${t:-unknown}"47
}49
lock() {50
mkdir "$LOCK" 2>/dev/null || fail SWAP_BUSY "Another swap operation is running on this server."51
trap 'rmdir "$LOCK" 2>/dev/null' EXIT INT TERM52
}54
# swapoff only when the pages can move to RAM (keeping a reserve) or to other active swap.55
assert_swapoff_safe() {56
target=$157
used=$(used_of "$target"); [ -n "$used" ] || used=058
[ "$used" -gt 0 ] || return 059
available=$(meminfo MemAvailable); total=$(meminfo MemTotal)60
# Reserve: 10 % of RAM, at least 512 MiB, but never more than a quarter of RAM (small servers).61
reserve=$((total / 10)); [ "$reserve" -ge 536870912 ] || reserve=53687091262
[ "$reserve" -le $((total / 4)) ] || reserve=$((total / 4))63
other_free=$(awk -v p="$target" 'NR>1 && $1!=p { free += ($3-$4)*1024 } END { printf "%.0f", free+0 }' "$PROC/swaps")64
room=$((available - reserve + other_free))65
if [ "$used" -gt "$room" ]; then66
fail SWAPOFF_UNSAFE "Deactivating $target would move $((used / 1048576)) MiB into memory, but only $((room / 1048576)) MiB can be absorbed safely (MemAvailable minus a reserve of $((reserve / 1048576)) MiB plus free space in other swap areas)."67
fi68
}70
fstab_entry() { awk -v p="$1" '$1==p && $3=="swap" { print NR; exit }' "$FSTAB" 2>/dev/null; }71
# Rewrites /etc/fstab atomically: removes the swap entry for $1, optionally appends a new one. Other lines stay byte-identical.72
fstab_set() {73
path=$1; persist=$2; prio=$374
[ -f "$FSTAB" ] || fail FSTAB_MISSING "/etc/fstab does not exist."75
[ ! -L "$FSTAB" ] || fail FSTAB_UNSAFE "/etc/fstab is a symbolic link."76
stamp=$(date -u +%Y%m%dT%H%M%SZ)77
cp -p "$FSTAB" "$FSTAB.chj-backup-$stamp" || fail FSTAB_BACKUP_FAILED "The /etc/fstab backup could not be created."78
tmp=$(mktemp "$FSTAB.chj-XXXXXX") || fail FSTAB_WRITE_FAILED "Temporary file failed."79
awk -v p="$path" '!($1==p && $3=="swap") { print }' "$FSTAB" > "$tmp" || { rm -f "$tmp"; fail FSTAB_WRITE_FAILED "fstab could not be rewritten."; }80
if [ "$persist" = 1 ]; then81
options=sw; [ "$prio" != "-1" ] && options="sw,pri=$prio"82
printf '%s none swap %s 0 0 %s\n' "$path" "$options" "$MARK" >> "$tmp"83
fi84
chmod --reference="$FSTAB" "$tmp" 2>/dev/null || chmod 644 "$tmp"85
chown --reference="$FSTAB" "$tmp" 2>/dev/null || chown 0:0 "$tmp"86
mv -f "$tmp" "$FSTAB" || { rm -f "$tmp"; fail FSTAB_WRITE_FAILED "fstab could not be replaced."; }87
ls -1t "$FSTAB".chj-backup-* 2>/dev/null | tail -n +11 | while IFS= read -r old; do rm -f -- "$old"; done88
if have systemctl && [ -d /run/systemd/system ]; then systemctl daemon-reload >/dev/null 2>&1 || true; fi89
printf '\036CHJ_STEP=fstab|ok|backup %s\n' "$FSTAB.chj-backup-$stamp"90
}92
# Builds a valid swap file at $1 with $2 MiB, honouring file system rules (Btrfs: NOCOW, no holes on XFS/ext4).93
build_swapfile() {94
target=$1; size=$295
dir=${target%/*}; [ -n "$dir" ] || dir=/96
fstype=$(fs_type "$dir")97
case $fstype in98
ext2/ext3|ext4|ext2|ext3|xfs|btrfs|f2fs) ;;99
*) fail SWAP_FS_UNSUPPORTED "Swap files are not supported safely on the $fstype file system." ;;100
esac101
free=$(df -PB1 "$dir" | awk 'NR==2 { print $4 }')102
need=$((size * 1048576 + 1073741824))103
[ "$free" -ge "$need" ] || fail SWAP_DISK_FULL "Not enough free space in $dir: $((free / 1048576)) MiB free, $((need / 1048576)) MiB needed including a 1 GiB reserve."104
tmp="$dir/.${target##*/}.chj-new"105
rm -f -- "$tmp"106
if [ "$fstype" = btrfs ]; then107
if have btrfs && btrfs filesystem mkswapfile --help >/dev/null 2>&1; then108
out=$(btrfs filesystem mkswapfile --size "${size}m" "$tmp" 2>&1) || { rm -f -- "$tmp"; fail SWAP_BTRFS_FAILED "$out"; }109
else110
: > "$tmp" && chattr +C "$tmp" 2>/dev/null || { rm -f -- "$tmp"; fail SWAP_BTRFS_NOCOW "The NOCOW attribute could not be set (chattr +C); Btrfs swap files need it."; }111
dd if=/dev/zero of="$tmp" bs=1M count="$size" status=none conv=fsync || { rm -f -- "$tmp"; fail SWAP_WRITE_FAILED "Writing the swap file failed."; }112
chmod 600 "$tmp"; out=$(mkswap "$tmp" 2>&1) || { rm -f -- "$tmp"; fail SWAP_MKSWAP_FAILED "$out"; }113
fi114
else115
# dd writes every block: fallocate/truncate can leave unwritten extents that swapon rejects (e.g. XFS).116
dd if=/dev/zero of="$tmp" bs=1M count="$size" status=none conv=fsync || { rm -f -- "$tmp"; fail SWAP_WRITE_FAILED "Writing the swap file failed (disk full?)."; }117
chmod 600 "$tmp"; out=$(mkswap "$tmp" 2>&1) || { rm -f -- "$tmp"; fail SWAP_MKSWAP_FAILED "$out"; }118
fi119
chown 0:0 "$tmp"; chmod 600 "$tmp"120
mv -f -- "$tmp" "$target" || { rm -f -- "$tmp"; fail SWAP_WRITE_FAILED "The swap file could not be moved into place."; }121
if have restorecon; then restorecon "$target" >/dev/null 2>&1 || true; fi122
}124
activate() {125
target=$1; prio=$2126
if [ "$prio" = "-1" ]; then out=$(swapon "$target" 2>&1); else out=$(swapon -p "$prio" "$target" 2>&1); fi127
[ $? -eq 0 ] || fail SWAPON_FAILED "swapon $target failed: $out"128
}130
status() {131
printf '\036CHJ_SECTION=swaps\n'; cat /proc/swaps132
printf '\036CHJ_SECTION=meminfo\n'; grep -E '^(MemTotal|MemAvailable|SwapTotal|SwapFree):' /proc/meminfo133
}135
op_inspect() {136
printf '\036CHJ_SECTION=meta\n'137
printf 'root=%s\n' "$(is_root && echo 1 || echo 0)"138
printf 'systemd=%s\n' "$( (have systemctl && [ -d /run/systemd/system ]) && echo 1 || echo 0)"139
printf 'btrfsMkswapfile=%s\n' "$( (have btrfs && btrfs filesystem mkswapfile --help >/dev/null 2>&1) && echo 1 || echo 0)"140
printf 'os=%s\n' "$(sed -n 's/^PRETTY_NAME=//p' /etc/os-release 2>/dev/null | head -n 1 | tr -d '"')"141
status142
printf '\036CHJ_SECTION=fstab\n'143
[ -r "$FSTAB" ] && awk '$3=="swap" || /^[^#]*[[:space:]]swap[[:space:]]/ { print NR "|" $0 }' "$FSTAB"144
printf '\036CHJ_SECTION=units\n'145
if have systemctl && [ -d /run/systemd/system ]; then146
systemctl list-units --type=swap --all --no-legend --plain 2>/dev/null | awk '{ print $1 "|" $3 "|" $4 }' | head -n 32147
fi148
printf '\036CHJ_SECTION=files\n'149
listed=" $(awk 'NR>1 { printf "%s ", $1 }' /proc/swaps) $( [ -r "$FSTAB" ] && awk '$3=="swap" && $1 ~ /^\// { printf "%s ", $1 }' "$FSTAB") "150
for path in $listed /swapfile /swapfile* /swap/* /var/lib/swap/*; do151
[ -e "$path" ] || continue152
# Directory candidates count only when they are regular files carrying the swap signature.153
case $listed in *" $path "*) ;; *) [ -f "$path" ] && [ ! -L "$path" ] && is_swap_signature "$path" || continue ;; esac154
case $path in /dev/*) kind=partition; [ "${path#/dev/zram}" != "$path" ] && kind=zram ;; *) kind=file ;; esac155
if [ "$kind" = file ]; then156
printf '%s|%s|%s|%s|%s|%s\n' "$path" "$kind" "$(fs_type "${path%/*}/")" "$(stat -c '%s|%U|%a' "$path" 2>/dev/null)" "$(managed_path "$path" && echo managed || echo foreign)" "$(df -PB1 "${path%/*}/" 2>/dev/null | awk 'NR==2 { print $4 }')"157
else158
printf '%s|%s|-|%s|0|-|foreign|-\n' "$path" "$kind" "$(blockdev --getsize64 "$path" 2>/dev/null || echo 0)"159
fi160
done | awk -F'|' '!seen[$1]++'161
printf '\036CHJ_SECTION=locations\n'162
for dir in / /swap /var/lib/swap; do163
[ -d "$dir" ] || continue164
printf '%s|%s|%s\n' "$dir" "$(fs_type "$dir")" "$(df -PB1 "$dir" 2>/dev/null | awk 'NR==2 { print $4 }')"165
done166
printf '\036CHJ_SECTION=zram\n'167
for zram in /sys/block/zram*; do [ -r "$zram/mm_stat" ] && printf '%s|%s|%s\n' "$(basename "$zram")" "$(cat "$zram/disksize")" "$(tr -s ' ' ',' < "$zram/mm_stat")"; done168
printf '\036CHJ_SECTION=end\n'169
}171
op_manage() {172
[ $# -eq 5 ] || fail ARGUMENTS_INVALID "Invalid arguments."173
action=$1; path=$2; size=$3; prio=$4; persist=$5174
need_root175
safe_path "$path" || fail SWAP_PATH_INVALID "Invalid swap path."176
valid_int "$size" && valid_prio "$prio" || fail ARGUMENTS_INVALID "Invalid size or priority."177
case $persist in 0|1|keep) ;; *) fail ARGUMENTS_INVALID "Invalid persistence flag." ;; esac178
lock179
case $path in /dev/zram*) fail SWAP_ZRAM_READ_ONLY "zram is shown read-only; CH-J does not configure it." ;; esac180
case $action in181
create)182
managed_path "$path" || fail SWAP_PATH_INVALID "New swap files may be created only as /swapfile*, /swap/<name> or /var/lib/swap/<name>."183
[ ! -e "$path" ] || fail SWAP_EXISTS "The file already exists."184
[ "$size" -ge 64 ] && [ "$size" -le 65536 ] || fail SWAP_SIZE_INVALID "Size must be between 64 MiB and 64 GiB."185
mkdir -p "${path%/*}" 2>/dev/null186
build_swapfile "$path" "$size"187
printf '\036CHJ_STEP=create|ok|%s MiB\n' "$size"188
activate "$path" "$prio"; printf '\036CHJ_STEP=activate|ok|\n'189
[ "$persist" = 1 ] && fstab_set "$path" 1 "$prio" ;;190
activate)191
[ -e "$path" ] || fail SWAP_NOT_FOUND "The swap space does not exist."192
is_active "$path" && fail SWAP_ALREADY_ACTIVE "The swap space is already active."193
activate "$path" "$prio"; printf '\036CHJ_STEP=activate|ok|\n'194
[ "$persist" = keep ] || { [ -n "$(fstab_entry "$path")" ] && [ "$persist" = 1 ]; } || fstab_set "$path" "$persist" "$prio" ;;195
deactivate)196
is_active "$path" || fail SWAP_NOT_ACTIVE "The swap space is not active."197
assert_swapoff_safe "$path"198
out=$(swapoff "$path" 2>&1) || fail SWAPOFF_FAILED "$out"; printf '\036CHJ_STEP=deactivate|ok|\n'199
[ "$persist" = 0 ] && [ -n "$(fstab_entry "$path")" ] && fstab_set "$path" 0 "$prio" ;;200
priority)201
if is_active "$path"; then202
assert_swapoff_safe "$path"203
out=$(swapoff "$path" 2>&1) || fail SWAPOFF_FAILED "$out"204
activate "$path" "$prio"205
fi206
printf '\036CHJ_STEP=priority|ok|%s\n' "$prio"207
[ -n "$(fstab_entry "$path")" ] && fstab_set "$path" 1 "$prio" ;;208
persist)209
[ -e "$path" ] || fail SWAP_NOT_FOUND "The swap space does not exist."210
fstab_set "$path" "$persist" "$prio" ;;211
resize)212
managed_path "$path" || fail SWAP_MANUAL_ONLY "Only swap files at /swapfile*, /swap/* or /var/lib/swap/* are resized by CH-J."213
[ -f "$path" ] && [ ! -L "$path" ] || fail SWAP_NOT_FOUND "The swap file does not exist."214
[ "$size" -ge 64 ] && [ "$size" -le 65536 ] || fail SWAP_SIZE_INVALID "Size must be between 64 MiB and 64 GiB."215
was_active=0216
if is_active "$path"; then assert_swapoff_safe "$path"; was_active=1; fi217
# The new file is built next to the old one first; the old file is replaced only after mkswap succeeded.218
if [ $was_active = 1 ]; then out=$(swapoff "$path" 2>&1) || fail SWAPOFF_FAILED "$out"; fi219
( build_swapfile "$path" "$size" ) || { [ $was_active = 1 ] && swapon "$path" >/dev/null 2>&1; fail SWAP_RESIZE_FAILED "The new swap file could not be created; the previous file was reactivated."; }220
printf '\036CHJ_STEP=resize|ok|%s MiB\n' "$size"221
if [ $was_active = 1 ]; then activate "$path" "$prio"; printf '\036CHJ_STEP=activate|ok|\n'; fi ;;222
delete)223
managed_path "$path" || fail SWAP_MANUAL_ONLY "Only swap files at /swapfile*, /swap/* or /var/lib/swap/* are deleted by CH-J; partitions are never deleted."224
[ -f "$path" ] && [ ! -L "$path" ] || fail SWAP_NOT_FOUND "The swap file does not exist."225
if is_active "$path"; then assert_swapoff_safe "$path"; out=$(swapoff "$path" 2>&1) || fail SWAPOFF_FAILED "$out"; printf '\036CHJ_STEP=deactivate|ok|\n'; fi226
[ -n "$(fstab_entry "$path")" ] && fstab_set "$path" 0 "$prio"227
rm -f -- "$path" || fail SWAP_DELETE_FAILED "The file could not be removed."228
printf '\036CHJ_STEP=delete|ok|\n' ;;229
*) fail ACTION_INVALID "Unsupported swap action." ;;230
esac231
status232
printf '\036CHJ_RESULT=ok\n'233
}235
case $op in236
safety-check) [ -n "${CHJ_SWAP_TEST_PROC:-}" ] || fail ACTION_INVALID "Test-only operation."; assert_swapoff_safe "$1"; printf 'safe\n' ;;237
inspect) op_inspect ;;238
manage) op_manage "$@" ;;239
*) fail ACTION_INVALID "Unsupported operation." ;;240
esacSHA-256
726dc4a5cdcf9be4bfae71e3faec18d2a66824cf9b9ea8cc6aed8d571b596d9eCH-J Proprietary Software License 1.14 · 原始碼依 CH-J Proprietary Software License 1.14 提供。公開原始碼不會改變授權條款,也不會授予額外的權利。
壓縮檔 SHA-256
cf4455b698f0f28e456a37b1834976a0194d8486acb7101052d59a838745cca3