Files
server/opt/psa/admin/bin/collect
2026-01-07 20:52:11 +01:00

759 lines
17 KiB
Bash
Executable File

#!/bin/bash
### Copyright 1999-2025. WebPros International GmbH. All rights reserved.
export LANG="C"
export LC_ALL="C"
set -u
# Here you can define checking routines list for every mode
brief_list="
get_sysinfo
get_common_info
get_common_logs
get_ipconfig
get_mounts_info
get_last
get_selinux_info
get_pkglist
get_components
get_system_users
get_system_services
get_ai_log
get_apache_info
get_nginx_info
get_cpserver_info
get_php_info
get_psa_conf
get_plesk_repair_status
"
# removed from brief: get_history
middle_list="
$brief_list
get_db_misc
get_db_eventhandlers
get_maillog
get_mail_handlers_list
get_vhost_conf
get_php_conf
get_pmm_info
get_webmail_conf
"
full_list="
$middle_list
get_db_dumps
"
MYSQL_BIN_D="/usr/bin"
# common options
curdir="`dirname $0`"
tmp_d="/usr/local/psa/var"
infodir="$tmp_d/collect"
info_plesk="$infodir/plesk"
info_system="$infodir/system"
info_db="$infodir/db"
my_access="-uadmin -p`cat /etc/psa/.psa.shadow`"
# global options
pkgtype='' # defined in get_sysinfo
os_descr='' # defined in get_sysinfo
root_d='' # defined in get_sysinfo
# Common functions
# ---------------------------------------------------------------------------------------
help()
{
cat <<-EOT
USAGE: $progname [--quiet] [--output FILE] [--mode [brief|middle|full]]
$progname --help
OPTIONS:
--mode Set required verbosity level. See MODES section
to get an additional info.
--quiet Do not output any info on stdout.
--output Specify file where data will be saved.
--sanitizer Specify path to sanitizer unitity.
--tail-size Number of lines collected from logfiles.
--help Show this message
MODES:
brief Short basic info.
middle Detailed info about system and product. This is default.
full Includes detailed info, product logs and database dumps.
EOT
}
parse_options()
{
local count="$#"
# global params:
progname="`basename $0`"
mode=""
quiet=""
output=""
sanitize_bin="/usr/local/psa/admin/sbin/sanitize"
tail_size=1000
actions_list=""
local TEMP
TEMP=`getopt -o m:qo:S:t:h --long mode:,quiet,output:,sanitizer:,tail-size:,actions:,help -n "$progname" -- "$@"`
if [ $? != 0 ]; then
echo "Internal error in parsing options" >&2
exit 1
fi
eval set -- "$TEMP"
while true; do
case "$1" in
-m|--mode)
case "$2" in
brief) actions_list="$brief_list" ;;
middle) actions_list="$middle_list" ;;
full) actions_list="$full_list" ;;
*) help && exit 1 ;;
esac
shift 2
;;
-q|--quiet)
quiet=1;
exec >/dev/null
shift
;;
-o|--output) output="$2"; shift 2;;
-h|--help) help && exit 0;;
-S|--sanitizer) sanitize_bin="$2"; shift 2;;
-t|--tail-size) tail_size="$2"; shift 2;;
--actions) actions_list="$2"; shift 2;;
--) shift; break;;
*) echo "Internal error in processing options: '$1'"; exit 1;;
esac
done
# allow relative path for $output:
if [ -n "$output" ]; then
expr match "$output" "/" >/dev/null || output="`pwd`/$output"
fi
# allow relative path for $sanitize_bin
if [ -n "$sanitize_bin" ]; then
expr match "$sanitize_bin" "/" >/dev/null || sanitize_bin="`pwd`/$sanitize_bin"
fi
# default mode is middle:
[ -n "$actions_list" ] || actions_list="$middle_list"
}
ok()
{
[ ! -z "$quiet" ] || echo "ok"
}
info()
{
[ ! -z "$quiet" ] || echo "$*"
}
try()
{
[ ! -z "$quiet" ] || echo -n "Trying to $*... "
}
fail()
{
[ ! -z "$quiet" ] || echo "failed"
}
die()
{
echo "ERROR: $*.. exiting..." >&2
exit 1
}
is_function()
{
local type_output=$(type -t "$1")
test "X${type_output}" = "Xfunction"
}
cleanup()
{
rm -rf $infodir >/dev/null 2>&1
rm -f $tmp_d/collect.zip
}
cp_tail()
{
local src="$1"
local dest="$2"
expr match "$dest" / > /dev/null || dest="`pwd`/$dest"
if [ -d "$dest" ]; then
dest="$dest/`basename $src`"
fi
if [ -d "$src" ]; then
[ -e "$dest" ] || mkdir "$dest"
pushd "$src" > /dev/null
find -type d -exec mkdir -p "$dest/{}" \;
for i in `find -type f`; do
tail -n "$tail_size" "$i" > "$dest/$i"
done
popd > /dev/null
else
tail -n "$tail_size" "$src" > "$dest"
fi
}
db_do()
{
local query="$*"
local mysql_client="$MYSQL_BIN_D/mysql"
if [ -f "$MYSQL_BIN_D/mariadb" ]; then
mysql_client="$MYSQL_BIN_D/mariadb"
fi
echo "$query" | "$mysql_client" -n -N $my_access psa
}
db_dump()
{
local database="$1"
local table where my_opts
local dump_client="$MYSQL_BIN_D/mysqldump"
[ -f "$MYSQL_BIN_D/mariadb-dump" ] && dump_client="$MYSQL_BIN_D/mariadb-dump"
my_opts="--skip-extended-insert --skip-quick --skip-lock-tables --databases $1"
shift
[ ! "$#" -ge 1 ] || my_opts+=" --tables $1"
"$dump_client" $my_access $my_opts
}
collect()
{
local functions="$1"
for func in $functions; do
if is_function $func; then
$func
continue
fi
echo "Internal error: Unable to find a function '$func'. Skipping..."
done
}
sanitize()
{
try "filter out user private data"
[ -x "$sanitize_bin" ] || die "$sanitize_bin not found"
"$sanitize_bin" "$tmp_d/collect"
ok
}
pack()
{
local output_path="$1"
[ -n "$output_path" ] || output_path="$tmp_d/collect.zip"
try "pack gathered info to '$output_path'"
local zip_bin="`which zip 2>/dev/null`"
[ -z "$zip_bin" ] && die "ZIP archiver didn't find"
pushd "$(dirname "$infodir")" > /dev/null
local include_list="$(mktemp "$tmp_d/include.lst.XXXXXX")"
find "$(basename "$infodir")" -type f > "$include_list"
# prevent zip from adding .zip to output_path
$zip_bin -r -q -b . - "$(basename "$infodir")" --include @"$include_list" > "$output_path"
popd > /dev/null
rm -f "$include_list"
rm -rf "$infodir"
ok
}
# Routines
# ---------------------------------------------------------------------------------------
get_sysinfo()
{
try "collect system info"
res="`/bin/uname -m`"
[ "$res" = "amd64" -o "$res" = "x86_64" ] && arch="x86_64" || arch="i386"
found_file=""
for f in redhat-release SuSE-release lsb-release debian_version; do
[ -f "/etc/$f" ] && found_file="$f" && break
done
case $found_file in
redhat-release)
pkgtype="rpm"
os_descr="`cat /etc/$found_file` $arch"
root_d="/usr/local/psa"
;;
SuSE-release)
pkgtype="rpm"
os_descr="`cat /etc/$found_file | head -n 1` $arch"
root_d="/usr/local/psa"
;;
lsb-release)
pkgtype="deb"
os_descr="`awk -F '=' '/DISTRIB_DESCRIPTION/ {print $2}' < /etc/$found_file | tr -d '"'` $arch"
root_d="/opt/psa"
;;
debian_version)
pkgtype="deb"
os_descr="Debian `cat /etc/$found_file` $arch"
root_d="/opt/psa"
;;
*)
fail
die "Unsupported os has been detected"
;;
esac
ok
}
get_ipconfig()
{
try "collect IP configuration"
mkdir -p $info_system
ifconfig > $info_system/ipconfig.txt 2>&1
ok
}
get_mounts_info()
{
try "get mountpoints and volumes info"
mkdir -p $info_system
mount > $info_system/mounts.txt
echo "" >> $info_system/mounts.txt
df -h >> $info_system/mounts.txt
ok
}
get_last()
{
try "get last events"
mkdir -p $info_system
last > $info_system/last.txt
ok
}
get_db_misc()
{
try "collect info from psa.misc database table"
mkdir -p $info_db
db_dump "psa" "misc" > $info_db/misc.sql
ok
}
get_db_eventhandlers()
{
try "collect info from psa.event_handlers"
mkdir -p $info_db
db_dump "psa" "event_handlers" > $info_db/event_handlers.sql
ok
}
get_selinux_info()
{
try "get selinux status"
enforced="`getenforce 2>/dev/null`"
[ "$pkgtype" != "rpm" -o -z "$enforced" ] \
&& info "not installed" \
&& return
mkdir -p $info_system
echo -e "SELinux mode:\n $enforced" > $info_system/selinux.txt
ok
}
get_common_info()
{
try "collect Plesk common info"
[ ! -f "$root_d/version" ] && fail && return || info="`cat $root_d/version`"
up_hist="`db_do "select * from upgrade_history"`"
domains="`db_do "select count(*) from domains"`"
case $pkgtype in
rpm) nginx_str="`head -1 /etc/sysconfig/nginx`" ;;
deb) nginx_str="`head -1 /etc/default/nginx`" ;;
*) die "Unsupported OS" ;;
esac
nginx_status="`echo $nginx_str | awk -F '=' '{print $2}' | tr -d '"'`"
vz_env_id="`awk '/envID/ {print $2}' /proc/self/status`"
if [ -z "$vz_env_id" -o "$vz_env_id" = "0" ]; then
vz_status="Nope. Is not VZ."
counters="Not found."
else
vz_status="Yep. Is VZ."
counters="`cat /proc/user_beancounters`"
fi
mkdir -p $infodir
out="$infodir/common_info.txt"
echo "OS:" > $out
echo " $os_descr" >> $out
echo "" >> $out
echo "Is virtuozzo container?:" >> $out
echo " $vz_status" >> $out
echo "" >> $out
echo "Product:" >> $out
echo " $info" >> $out
echo "" >> $out
echo "Upgrade history:" >> $out
if [ -z "$up_hist" ]; then
echo " Clean installation." >> $out
else
IFS_OLD="$IFS"
IFS=$'\n'
for str in $up_hist; do
echo " $str" >> $out
done
IFS="$IFS_OLD"
fi
echo "" >> $out
echo "Count of domains:" >> $out
echo " $domains" >> $out
echo "" >> $out
echo "Nginx enabled:" >> $out
echo " $nginx_status" >> $out
echo "" >> $out
echo "Collected info date:" >> $out
echo " `date +%s` (`date -u`)" >> $out
echo "" >> $out
echo "User Beancounters:" >> $out
echo " $counters" >> $out
echo "" >> $out
cp -f /etc/sw/keys/registry.xml $infodir/
ok
}
get_common_logs()
{
try "collect common system logs"
dstdir="$info_system/var_log"
mkdir -p $dstdir
for entry in cron mail.info syslog maillog messages mysqld.log secure yum.log audit dmesg; do
[ -e "/var/log/$entry" ] && cp_tail /var/log/$entry $dstdir/
done
ok
}
get_pkglist()
{
try "get packages list"
mkdir -p $info_system
case $pkgtype in
rpm)
rpm -qa >$info_system/pkglist.txt 2>&1
;;
deb)
dpkg --list > $info_system/pkglist.txt 2>&1
;;
*)
die "Unknown packages type."
;;
esac
ok
}
get_components()
{
try "get Plesk components list"
mkdir -p $info_plesk
$root_d/admin/sbin/packagemng --list > $info_plesk/components.txt 2>&1
ok
}
get_history()
{
try "get shell history"
history > $info_system/history.txt 2>&1
ok
}
get_system_users()
{
try "get system users and groups"
mkdir -p $info_system
cp -f /etc/passwd $info_system/sysusers.txt
cp -f /etc/group $info_system/sysgroups.txt
ok
}
get_system_services()
{
try "get system active services list"
mkdir -p $info_system
if [ -x "/bin/systemctl" ]; then
echo >> "$info_system/services.txt"
/bin/systemctl list-unit-files >> "$info_system/services.txt"
fi
top -b -n 1 > $info_system/top.txt 2>&1
ps auxwww > $info_system/ps.txt 2>&1
ok
}
get_ai_log()
{
try "get autoinstaller logs"
mkdir -p "$info_plesk"
local latest_ai_log="`ls -t /var/log/plesk/install/autoinstaller3*.log 2>/dev/null | head -n 1`"
[ -z "$latest_ai_log" ] || cp_tail "$latest_ai_log" "$info_plesk/"
ok
}
get_mail_handlers_list()
{
try "get mail handlers info"
mkdir -p $info_plesk
$root_d/admin/sbin/mail_handlers_control --list > $info_plesk/mhandlers.txt
echo "" >> $info_plesk/mhandlers.txt
$root_d/admin/sbin/mail_handlers_control --list --extent >> $info_plesk/mhandlers.txt
ok
}
get_apache_info()
{
try "collect customer's web server configuration"
local cmd
local out
local subdir
mkdir -p $info_plesk/apache/logs
out="$info_plesk/apache/common.txt"
cmd="`which httpd 2>/dev/null`"
[ -z "$cmd" ] && cmd="`which apache 2>/dev/null`"
[ -z "$cmd" ] && cmd="`which apache2 2>/dev/null`"
[ -z "$cmd" ] && echo "apache not found.. " && fail && return
[ "$pkgtype" = "rpm" ] && cmd_bin="$cmd" || cmd_bin="apachectl"
$cmd_bin -t >> $out 2>&1
echo "" >> $out
$cmd_bin -V >> $out 2>&1
echo "" >> $out
$cmd_bin -M >> $out 2>&1
echo "" >> $out
$cmd_bin -S > $info_plesk/apache/vhosts.txt 2>&1
subdir="`basename $cmd`"
[ ! -s "/var/log/$subdir/error_log" ] || cp_tail /var/log/$subdir/error_log $info_plesk/apache/logs/
cp -fR /etc/$subdir $info_plesk/apache/conf
ok
}
get_nginx_info()
{
try "collect nginx web server info"
mkdir -p $info_plesk/nginx
cp -fR /etc/nginx/* $info_plesk/nginx/
ok
}
get_cpserver_info()
{
try "collect cp-server info"
dstdir="$info_plesk/cp-server"
mkdir -p $dstdir/etc/conf $dstdir/var_logs
cp -fR /etc/sw-cp-server/* $dstdir/etc/
for l in error_log sw-engine.log; do
[ ! -s "/var/log/sw-cp-server/$l" ] || cp_tail "/var/log/sw-cp-server/$l" "$dstdir/var_logs/"
done
mkdir -p $dstdir/admin
cp -fR $root_d/admin/conf $dstdir/admin/
for l in panel.log sitebuilder.log; do
[ ! -s "/var/log/plesk/$l" ] || cp_tail "/var/log/plesk/$l" "$dstdir/admin/"
done
# Remove pem files that store private key to avoid leakage of them
rm -f $dstdir/admin/conf/*.pem*
ok
}
get_php_info()
{
try "collect info about installed php"
mkdir -p $info_plesk
php -r "@phpinfo();" < /dev/null > $info_plesk/phpinfo.txt
ok
}
get_psa_conf()
{
try "get psa common configuration"
mkdir -p $info_plesk/etc_psa
for l in psa.conf php_versions.json; do
[ ! -s "/etc/psa/$l" ] || cp -f /etc/psa/$l $info_plesk/etc_psa/
done
ok
}
get_webmail_conf()
{
try "get webmails configuration"
mkdir -p $info_plesk/webmails
cp -fR /etc/psa-webmail/* $info_plesk/webmails/
ok
}
get_maillog()
{
try "collect mail logs"
mkdir -p $info_plesk
cp_tail $root_d/var/log $info_plesk/maillog
ok
}
get_vhost_conf()
{
try "collect info from virtual domains"
local vhosts_d
vhosts_d="`awk '/^HTTPD_VHOSTS_D/ {print $2}' /etc/psa/psa.conf`"
is_new_vhost="`which plesk 2>/dev/null`"
count=0
for domain in `db_do "select name from domains"`; do
[ $count -eq 100 ] && echo -n "." && count=0
[ -n "$is_new_vhost" ] \
&& srcdir="$vhosts_d/system/$domain" \
|| srcdir="$vhosts_d/$domain"
mkdir -p $info_plesk/vhosts/$domain/logs
for l in error_log proxy_error_log; do
[ ! -s "$srcdir/logs/$l" ] || cp_tail $srcdir/logs/$l $info_plesk/vhosts/$domain/logs
done
cp -fR $srcdir/conf $info_plesk/vhosts/$domain/ 2>/dev/null
count=$(expr $count + 1)
done
ok
}
get_php_conf()
{
try "collect php configs"
mkdir -p $info_plesk/php
find /etc -maxdepth 1 -type f -name "*php*" -exec cp -f {} $info_plesk/php/ \;
for p in $(find /etc -maxdepth 1 -type d -name "*php*"); do
pushd "$p" >/dev/null
find . -type d -exec mkdir -p $info_plesk/php/$(basename $p)/{} \;
find . -type f -name "*.ini" -exec cp -f {} $info_plesk/php/$(basename $p)/{} \;
find . -type f -name "*.conf" -exec cp -f {} $info_plesk/php/$(basename $p)/{} \;
popd >/dev/null
done
ok
}
get_plesk_perms()
{
try "collect plesk permissions"
mkdir -p $info_plesk
find $root_d -type d -exec ls -ld {} \; \
| awk '{print $1" "$3" "$4" "$5" "$6" "$7" "$8" "$9" "$10" "$11}' \
> $info_plesk/permissions.txt
ok
}
get_pmm_info()
{
try "collect backup/migration logs"
local pmmd='/var/log/plesk/PMM'
[ -d "$pmmd" ] || { ok; return; }
local dstdir
local srcdir
dstdir="$info_plesk/PMM"
mkdir -p $dstdir
local cur_month=`date +"%Y-%m"`
local prev_month=`date +"%Y-%m" --date='last month'`
pushd "$pmmd" > /dev/null
for f in `ls -d *.log *$cur_month* *$prev_month* 2>/dev/null | sort -u`; do
cp_tail "$f" "$dstdir"
done
popd > /dev/null
ok
}
get_db_dumps()
{
mkdir -p $info_db
try "get psa database dump"
db_dump psa > $info_db/psa.sql
ok
try "get apsc database dump"
db_dump apsc > $info_db/apsc.sql
ok
}
get_plesk_repair_status()
{
mkdir -p "$info_plesk"
"$root_d/bin/repair" --all -n >"$info_plesk/repair_status.txt" 2> "$info_plesk/repair_status.stderr.txt"
}
# --------------------------------------------------------------------------------------------
if [ "`id -u`" -ne 0 ]; then
echo "$0: This script must be run as root" >&2
echo "Log in as root then run this script again." >&2
echo >&2
exit 1
fi
parse_options "$@"
cleanup
collect "$actions_list"
sanitize
pack "$output"