added new scripts

This commit is contained in:
DeSqBlocki
2026-09-13 10:08:10 +02:00
parent b07b7e901f
commit 46b78816b9
17 changed files with 3391 additions and 0 deletions
+542
View File
@@ -0,0 +1,542 @@
#!/bin/bash
# /usr/lib/check_mk_agent/local/300/check_pm2
set -u
CONFIG="/etc/check_mk/pm2.conf"
OK=0
WARN=1
CRIT=2
UNKNOWN=3
#
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
#
if [[ ! -r "$CONFIG" ]]; then
printf '%s\n' "${UNKNOWN} \"PM2\" - Configuration file ${CONFIG} missing or unreadable"
exit 0
fi
# shellcheck disable=SC1090
source "$CONFIG"
PM2_USER="${PM2_USER:-root}"
PM2_HOME="${PM2_HOME:-}"
CHECK_PM2_UPDATES="${CHECK_PM2_UPDATES:-yes}"
CHECK_NODE_UPDATES="${CHECK_NODE_UPDATES:-yes}"
PM2_PACKAGE_URL="${PM2_PACKAGE_URL:-https://registry.npmjs.org/pm2/latest}"
NODE_RELEASES_URL="${NODE_RELEASES_URL:-https://nodejs.org/dist/index.json}"
#
# ---------------------------------------------------------------------------
# Requirements
# ---------------------------------------------------------------------------
#
if ! command -v jq >/dev/null 2>&1; then
printf '%s\n' "${UNKNOWN} \"PM2\" - jq is not installed"
exit 0
fi
if ! command -v curl >/dev/null 2>&1; then
printf '%s\n' "${UNKNOWN} \"PM2\" - curl is not installed"
exit 0
fi
if ! id "$PM2_USER" >/dev/null 2>&1; then
printf '%s\n' "${UNKNOWN} \"PM2\" - Configured PM2 user ${PM2_USER} does not exist"
exit 0
fi
#
# ---------------------------------------------------------------------------
# User / PM2 environment
# ---------------------------------------------------------------------------
#
USER_HOME="$(
getent passwd "$PM2_USER" |
cut -d: -f6
)"
if [[ -z "$USER_HOME" ]]; then
printf '%s\n' "${UNKNOWN} \"PM2\" - Unable to determine home directory for ${PM2_USER}"
exit 0
fi
if [[ -z "$PM2_HOME" ]]; then
PM2_HOME="${USER_HOME}/.pm2"
fi
#
# ---------------------------------------------------------------------------
# Locate PM2
# ---------------------------------------------------------------------------
#
PM2_BIN=""
for candidate in \
/usr/bin/pm2 \
/usr/local/bin/pm2 \
"${USER_HOME}/.local/bin/pm2"
do
if [[ -x "$candidate" ]]; then
PM2_BIN="$candidate"
break
fi
done
if [[ -z "$PM2_BIN" ]]; then
PM2_BIN="$(command -v pm2 2>/dev/null || true)"
fi
#
# NVM fallback
#
if [[ -z "$PM2_BIN" && -d "${USER_HOME}/.nvm/versions/node" ]]; then
PM2_BIN="$(
find "${USER_HOME}/.nvm/versions/node" \
-mindepth 3 \
-maxdepth 3 \
-type f \
-path '*/bin/pm2' \
-perm -u+x \
2>/dev/null |
sort -V |
tail -n 1
)"
fi
if [[ -z "$PM2_BIN" || ! -x "$PM2_BIN" ]]; then
printf '%s\n' "${UNKNOWN} \"PM2\" - Unable to locate PM2 executable"
exit 0
fi
PM2_BIN_DIR="$(dirname "$PM2_BIN")"
#
# Node used by this PM2 installation.
#
NODE_BIN="${PM2_BIN_DIR}/node"
if [[ ! -x "$NODE_BIN" ]]; then
NODE_BIN="$(command -v node 2>/dev/null || true)"
fi
#
# ---------------------------------------------------------------------------
# Retrieve PM2 process list
# ---------------------------------------------------------------------------
#
if [[ "$PM2_USER" == "root" ]]; then
PM2_JSON="$(
PM2_HOME="$PM2_HOME" \
PATH="${PM2_BIN_DIR}:${PATH}" \
"$PM2_BIN" jlist 2>/dev/null
)"
PM2_RC=$?
else
PM2_JSON="$(
runuser -u "$PM2_USER" -- \
env \
PM2_HOME="$PM2_HOME" \
PATH="${PM2_BIN_DIR}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
"$PM2_BIN" jlist 2>/dev/null
)"
PM2_RC=$?
fi
if [[ $PM2_RC -ne 0 || -z "$PM2_JSON" ]]; then
printf '%s\n' "${CRIT} \"PM2\" - Unable to retrieve PM2 process list for user ${PM2_USER}"
exit 0
fi
if ! printf '%s' "$PM2_JSON" | jq -e 'type == "array"' >/dev/null 2>&1; then
printf '%s\n' "${UNKNOWN} \"PM2\" - PM2 returned invalid JSON"
exit 0
fi
PROCESS_COUNT="$(
printf '%s' "$PM2_JSON" |
jq 'length'
)"
if [[ "$PROCESS_COUNT" -eq 0 ]]; then
printf '%s\n' "${WARN} \"PM2\" processes=0 No processes managed by PM2 for user ${PM2_USER}"
exit 0
fi
#
# ---------------------------------------------------------------------------
# Overall PM2 state
# ---------------------------------------------------------------------------
#
ONLINE_COUNT="$(
printf '%s' "$PM2_JSON" |
jq '[.[] | select(.pm2_env.status == "online")] | length'
)"
OFFLINE_COUNT=$((PROCESS_COUNT - ONLINE_COUNT))
if [[ "$OFFLINE_COUNT" -eq 0 ]]; then
printf '%s\n' \
"${OK} \"PM2\" processes=${PROCESS_COUNT};;;0|online=${ONLINE_COUNT};;;0|offline=0;;;0 ${PROCESS_COUNT}/${PROCESS_COUNT} processes online"
else
printf '%s\n' \
"${CRIT} \"PM2\" processes=${PROCESS_COUNT};;;0|online=${ONLINE_COUNT};;;0|offline=${OFFLINE_COUNT};;;0 ${ONLINE_COUNT}/${PROCESS_COUNT} processes online, ${OFFLINE_COUNT} not online"
fi
#
# ---------------------------------------------------------------------------
# PM2 version / update
# ---------------------------------------------------------------------------
#
PM2_VERSION="$(
PATH="${PM2_BIN_DIR}:${PATH}" \
"$PM2_BIN" --version 2>/dev/null |
tail -n 1 |
tr -d '[:space:]'
)"
if [[ "$CHECK_PM2_UPDATES" == "yes" ]]; then
if [[ -z "$PM2_VERSION" ]]; then
printf '%s\n' "${UNKNOWN} \"PM2 Update\" - Unable to determine installed PM2 version"
else
PM2_RELEASE_RESPONSE="$(
curl \
--silent \
--show-error \
--fail \
--connect-timeout 3 \
--max-time 8 \
"$PM2_PACKAGE_URL" \
2>/dev/null
)"
CURL_RC=$?
if [[ $CURL_RC -ne 0 || -z "$PM2_RELEASE_RESPONSE" ]]; then
printf '%s\n' \
"${UNKNOWN} \"PM2 Update\" - Installed ${PM2_VERSION}, unable to query npm registry"
elif ! printf '%s' "$PM2_RELEASE_RESPONSE" | jq -e . >/dev/null 2>&1; then
printf '%s\n' \
"${UNKNOWN} \"PM2 Update\" - npm registry returned invalid JSON"
else
PM2_LATEST="$(
printf '%s' "$PM2_RELEASE_RESPONSE" |
jq -r '.version // empty'
)"
if [[ -z "$PM2_LATEST" ]]; then
printf '%s\n' \
"${UNKNOWN} \"PM2 Update\" - Unable to determine latest PM2 version"
else
NEWEST="$(
printf '%s\n%s\n' \
"$PM2_VERSION" \
"$PM2_LATEST" |
sort -V |
tail -n 1
)"
if [[ "$PM2_VERSION" == "$PM2_LATEST" ]]; then
printf '%s\n' \
"${OK} \"PM2 Update\" - Current: installed ${PM2_VERSION}, latest ${PM2_LATEST}"
elif [[ "$NEWEST" == "$PM2_LATEST" ]]; then
printf '%s\n' \
"${WARN} \"PM2 Update\" - Update available: installed ${PM2_VERSION}, latest ${PM2_LATEST}"
else
printf '%s\n' \
"${OK} \"PM2 Update\" - Installed ${PM2_VERSION} is newer than latest ${PM2_LATEST}"
fi
fi
fi
fi
fi
#
# ---------------------------------------------------------------------------
# Node.js / NVM version
# ---------------------------------------------------------------------------
#
if [[ "$CHECK_NODE_UPDATES" == "yes" ]]; then
if [[ -z "$NODE_BIN" || ! -x "$NODE_BIN" ]]; then
printf '%s\n' "${UNKNOWN} \"Node.js Update\" - Unable to locate Node.js executable"
else
NODE_VERSION="$(
"$NODE_BIN" --version 2>/dev/null |
sed 's/^v//'
)"
if [[ -z "$NODE_VERSION" ]]; then
printf '%s\n' "${UNKNOWN} \"Node.js Update\" - Unable to determine installed Node.js version"
else
NODE_RELEASES="$(
curl \
--silent \
--show-error \
--fail \
--connect-timeout 3 \
--max-time 8 \
"$NODE_RELEASES_URL" \
2>/dev/null
)"
CURL_RC=$?
if [[ $CURL_RC -ne 0 || -z "$NODE_RELEASES" ]]; then
printf '%s\n' \
"${UNKNOWN} \"Node.js Update\" - Installed ${NODE_VERSION}, unable to query Node.js releases"
elif ! printf '%s' "$NODE_RELEASES" | jq -e 'type == "array"' >/dev/null 2>&1; then
printf '%s\n' \
"${UNKNOWN} \"Node.js Update\" - Node.js release API returned invalid data"
else
INSTALLED_MAJOR="${NODE_VERSION%%.*}"
LATEST_CURRENT="$(
printf '%s' "$NODE_RELEASES" |
jq -r '
[
.[]
| select(.lts == false)
| .version
][0] // empty
' |
sed 's/^v//'
)"
LATEST_LTS="$(
printf '%s' "$NODE_RELEASES" |
jq -r '
[
.[]
| select(.lts != false)
| .version
][0] // empty
' |
sed 's/^v//'
)"
LATEST_SAME_MAJOR="$(
printf '%s' "$NODE_RELEASES" |
jq -r \
--arg prefix "v${INSTALLED_MAJOR}." '
[
.[]
| select(.version | startswith($prefix))
| .version
][0] // empty
' |
sed 's/^v//'
)"
SAME_MAJOR_LTS="$(
printf '%s' "$NODE_RELEASES" |
jq -r \
--arg prefix "v${INSTALLED_MAJOR}." '
[
.[]
| select(.version | startswith($prefix))
| .lts
][0] // false
'
)"
#
# If no releases for the installed major exist in the current
# Node.js release index, consider it unsupported/EOL.
#
if [[ -z "$LATEST_SAME_MAJOR" ]]; then
printf '%s\n' \
"${WARN} \"Node.js Update\" - Installed ${NODE_VERSION} appears unsupported/EOL; latest LTS ${LATEST_LTS}, current ${LATEST_CURRENT}"
elif [[ "$SAME_MAJOR_LTS" == "false" && "$INSTALLED_MAJOR" -lt "${LATEST_CURRENT%%.*}" ]]; then
printf '%s\n' \
"${WARN} \"Node.js Update\" - Installed ${NODE_VERSION} is on a non-LTS release line; latest LTS ${LATEST_LTS}, current ${LATEST_CURRENT}"
elif [[ "$NODE_VERSION" != "$LATEST_SAME_MAJOR" ]]; then
NEWEST="$(
printf '%s\n%s\n' \
"$NODE_VERSION" \
"$LATEST_SAME_MAJOR" |
sort -V |
tail -n 1
)"
if [[ "$NEWEST" == "$LATEST_SAME_MAJOR" ]]; then
printf '%s\n' \
"${WARN} \"Node.js Update\" - Update available in Node ${INSTALLED_MAJOR}.x: installed ${NODE_VERSION}, latest ${LATEST_SAME_MAJOR}; latest LTS ${LATEST_LTS}"
else
printf '%s\n' \
"${OK} \"Node.js Update\" - Installed ${NODE_VERSION}; latest LTS ${LATEST_LTS}, current ${LATEST_CURRENT}"
fi
else
printf '%s\n' \
"${OK} \"Node.js Update\" - Current on Node ${INSTALLED_MAJOR}.x: ${NODE_VERSION}; latest LTS ${LATEST_LTS}, current ${LATEST_CURRENT}"
fi
fi
fi
fi
fi
#
# ---------------------------------------------------------------------------
# Individual PM2 processes
# ---------------------------------------------------------------------------
#
while IFS= read -r PROCESS; do
NAME="$(
printf '%s' "$PROCESS" |
jq -r '.name // "unknown"'
)"
STATUS="$(
printf '%s' "$PROCESS" |
jq -r '.pm2_env.status // "unknown"'
)"
PID="$(
printf '%s' "$PROCESS" |
jq -r '.pid // 0'
)"
RESTARTS="$(
printf '%s' "$PROCESS" |
jq -r '.pm2_env.restart_time // 0'
)"
CRON_RESTART="$(
printf '%s' "$PROCESS" |
jq -r '.pm2_env.cron_restart // empty'
)"
AUTORESTART="$(
printf '%s' "$PROCESS" |
jq -r '.pm2_env.autorestart // true'
)"
CPU="$(
printf '%s' "$PROCESS" |
jq -r '.monit.cpu // 0'
)"
MEMORY_BYTES="$(
printf '%s' "$PROCESS" |
jq -r '.monit.memory // 0'
)"
START_TIME="$(
printf '%s' "$PROCESS" |
jq -r '.pm2_env.pm_uptime // 0'
)"
NAME="${NAME//\"/\'}"
MEMORY_MB="$(
awk -v bytes="$MEMORY_BYTES" \
'BEGIN { printf "%.1f", bytes / 1024 / 1024 }'
)"
NOW_MS=$(( $(date +%s) * 1000 ))
if [[ "$START_TIME" =~ ^[0-9]+$ && "$START_TIME" -gt 0 ]]; then
UPTIME_SECONDS="$(
awk -v now="$NOW_MS" -v start="$START_TIME" \
'BEGIN {
diff = (now - start) / 1000
if (diff < 0) diff = 0
printf "%.0f", diff
}'
)"
else
UPTIME_SECONDS=0
fi
DAYS=$((UPTIME_SECONDS / 86400))
HOURS=$(((UPTIME_SECONDS % 86400) / 3600))
MINUTES=$(((UPTIME_SECONDS % 3600) / 60))
if [[ "$DAYS" -gt 0 ]]; then
UPTIME_TEXT="${DAYS}d ${HOURS}h ${MINUTES}m"
elif [[ "$HOURS" -gt 0 ]]; then
UPTIME_TEXT="${HOURS}h ${MINUTES}m"
else
UPTIME_TEXT="${MINUTES}m"
fi
STATE=$OK
STATE_TEXT="Online"
if [[ "$STATUS" != "online" ]]; then
STATE=$CRIT
STATE_TEXT="Status ${STATUS}"
fi
if [[ -n "$CRON_RESTART" ]]; then
RESTART_INFO="restarts ${RESTARTS}, scheduled restart ${CRON_RESTART}"
else
RESTART_INFO="restarts ${RESTARTS}"
fi
if [[ "$AUTORESTART" == "false" ]]; then
RESTART_INFO="${RESTART_INFO}, autorestart disabled"
fi
PERFDATA="cpu=${CPU}%;;;0;100|memory=${MEMORY_BYTES}B;;;0|restarts=${RESTARTS};;;0|uptime=${UPTIME_SECONDS}s;;;0"
printf '%s\n' \
"${STATE} \"PM2 ${NAME}\" ${PERFDATA} ${STATE_TEXT} - PID ${PID}, uptime ${UPTIME_TEXT}, CPU ${CPU}%, memory ${MEMORY_MB} MiB, ${RESTART_INFO}"
done < <(
printf '%s' "$PM2_JSON" |
jq -c '.[]'
)
exit 0