#!/bin/bash
### Copyright 1999-2025. WebPros International GmbH. All rights reserved.
#
#
# Plesk script
#
#default values
product_default_conf()
{
PRODUCT_ROOT_D=/opt/psa
PRODUCT_RC_D=/etc/init.d
PRODUCT_ETC_D=/opt/psa/etc
PLESK_LIBEXEC_DIR=/usr/lib/plesk-9.0
HTTPD_VHOSTS_D=/var/www/vhosts
HTTPD_CONF_D=/etc/apache2
HTTPD_INCLUDE_D=/etc/apache2/conf-enabled
HTTPD_BIN=/usr/sbin/apache2
HTTPD_LOG_D=/var/log/apache2
HTTPD_SERVICE=apache2
QMAIL_ROOT_D=/var/qmail
PLESK_MAILNAMES_D=/var/qmail/mailnames
RBLSMTPD=/usr/sbin/rblsmtpd
NAMED_RUN_ROOT_D=/var/named/run-root
WEB_STAT=/usr/bin/webalizer
MYSQL_VAR_D=/var/lib/mysql
MYSQL_BIN_D=/usr/bin
MYSQL_SOCKET=/var/run/mysqld/mysqld.sock
PGSQL_DATA_D=/var/lib/postgresql/16/main
PGSQL_CONF_D=/etc/postgresql/16/main
PGSQL_BIN_D=/usr/lib/postgresql/16/bin
DUMP_D=/var/lib/psa/dumps
DUMP_TMP_D=/tmp
MAILMAN_ROOT_D=/usr/lib/mailman
MAILMAN_VAR_D=/var/lib/mailman
PYTHON_BIN=/usr/bin/python2
GPG_BIN=/usr/bin/gpg
TAR_BIN=/usr/lib/plesk-9.0/sw-tar
AWSTATS_ETC_D=/etc/awstats
AWSTATS_BIN_D=/usr/lib/cgi-bin
AWSTATS_TOOLS_D=/usr/share/awstats/tools
AWSTATS_DOC_D=/usr/share/awstats
OPENSSL_BIN=/usr/bin/openssl
LIB_SSL_PATH=/lib/libssl.so
LIB_CRYPTO_PATH=/lib/libcrypto.so
CLIENT_PHP_BIN=/opt/psa/bin/php-cli
SNI_SUPPORT=true
APS_DB_DRIVER_LIBRARY=/usr/lib/x86_64-linux-gnu/sw/libmysqlserver.so.2.0
SA_MAX_MAIL_SIZE=256000
}
# echo message to product log, also to console in debug mode
p_echo()
{
if [ -n "$product_log" ] ; then
echo "$@" >> "$product_log" 2>&1
fi
if [ -n "$PLESK_INSTALLER_DEBUG" -o -n "$PLESK_INSTALLER_VERBOSE" -o -z "$product_log" ] ; then
echo "$@" >&2
fi
}
# same as p_echo, but without new line
pnnl_echo()
{
p_echo -n "$@"
}
int_err()
{
report_problem "internal" "Internal error: $@"
exit 1
}
p_see_product_log()
{
log_is_in_dev "${product_log}" || printf " (see log file: ${product_log})" >&2
}
warn()
{
local inten="$1"
if [ -n "$PLESK_INSTALLER_DEBUG" -o -n "$PLESK_INSTALLER_VERBOSE" ]; then
p_echo
p_echo "WARNING!"
pnnl_echo "Some problems are found during $inten"
p_see_product_log
p_echo
p_echo "Continue..."
p_echo
fi
report_problem "warning" "Warning: $inten"
}
detect_vz()
{
[ -z "$PLESK_VZ_RESULT" ] || return $PLESK_VZ_RESULT
PLESK_VZ_RESULT=1
PLESK_VZ=0
PLESK_VE_HW_NODE=0
PLESK_VZ_TYPE=
local issue_file="/etc/issue"
local vzcheck_file="/proc/self/status"
[ -f "$vzcheck_file" ] || return 1
local env_id=`sed -ne 's|^envID\:[[:space:]]*\([[:digit:]]\+\)$|\1|p' "$vzcheck_file"`
[ -n "$env_id" ] || return 1
if [ "$env_id" = "0" ]; then
# Either VZ/OpenVZ HW node or unjailed CloudLinux
PLESK_VE_HW_NODE=1
return 1
fi
if grep -q "CloudLinux" "$issue_file" >/dev/null 2>&1 ; then
return 1
fi
if [ -f "/proc/vz/veredir" ]; then
PLESK_VZ_TYPE="vz"
elif [ -d "/proc/vz" ]; then
PLESK_VZ_TYPE="openvz"
fi
PLESK_VZ=1
PLESK_VZ_RESULT=0
return 0
}
# detects lxc and docker containers
detect_lxc()
{
[ -z "$PLESK_LXC_RESULT" ] || return $PLESK_LXC_RESULT
PLESK_LXC_RESULT=1
PLESK_LXC=0
if { [ -f /proc/1/cgroup ] && grep -q 'docker\|lxc' /proc/1/cgroup; } || \
{ [ -f /proc/1/environ ] && cat /proc/1/environ | tr \\0 \\n | grep -q "container=lxc"; };
then
PLESK_LXC_RESULT=0
PLESK_LXC=1
fi
return "$PLESK_LXC_RESULT"
}
problems_log_tail()
{
[ -f "$product_problems_log" ] || return 0
{
tac "$product_problems_log" | awk '/^START/ { exit } { print }' | tac
} 2>/dev/null
}
product_log_tail()
{
[ -f "$product_log" ] || return 0
{
tac "$product_log" | awk '/^START/ { exit } { print }' | tac
} 2>/dev/null
}
product_and_problems_log_tail()
{
product_log_tail
[ "$product_log" = "$product_problems_log" ] || problems_log_tail
}
log_is_in_dev()
{
test "${1:0:5}" = "/dev/"
}
### Copyright 1999-2025. WebPros International GmbH. All rights reserved.
construct_report_template()
{
local severity="${1:-error}"
local summary="$2"
local update_ticket="`get_update_ticket`"
set_error_report_source
set_error_report_component
set_error_report_params
set_error_report_environment
true construct_report_code construct_report_debug construct_report_message
cat <<-EOL
$report_source$severity`date --iso-8601=seconds`$report_component/] ]>/g'`]]>`construct_report_message | base64`$report_params`construct_report_code | base64``construct_report_debug | base64`$report_environment$update_ticket
EOL
}
construct_report_code()
{
local call_level=${1:-5}
local func_level=$[call_level - 1]
local lineno_func=${BASH_LINENO[ $func_level ]}
local script_name=${BASH_SOURCE[ $[func_level + 1] ]}
echo "# Call of ${FUNCNAME[$func_level]}() from ${FUNCNAME[$[func_level + 1]]}() at `readlink -m $script_name`:${BASH_LINENO[$func_level]}"
head -n $[lineno_func + 4] "$script_name" 2>/dev/null | tail -n 8
}
construct_report_debug()
{
local call_level=${1:-5}
call_level=$[call_level-1]
# Generate calls stack trace.
for i in `seq $call_level ${#FUNCNAME[@]}`; do
[ "${FUNCNAME[$i]}" != "main" ] || break
local func_call="`sed -n -e "${BASH_LINENO[$i]}p" "${BASH_SOURCE[$[i+1]]}" 2>/dev/null |
sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'`"
[ -n "$func_call" -a -z "${func_call##*${FUNCNAME[$i]}*}" ] || func_call="${FUNCNAME[$i]}"
echo "#$[i - $call_level] `readlink -m ${BASH_SOURCE[$[i+1]]}`(${BASH_LINENO[$i]}): $func_call"
done
}
construct_report_message()
{
product_and_problems_log_tail
echo ""
if [ -n "$report_context" ]; then
echo "Context: $report_context"
echo ""
fi
if [ -n "$RP_LOADED_PATCHES" ]; then
echo "Loaded runtime patches: $RP_LOADED_PATCHES"
echo ""
fi
}
# Construct report to send it to our errors tracker
construct_report()
{
local severity="${1:-error}"
local summary="$2"
[ -n "$summary" ] || int_err "Unable to send error report. Some parameters are not defined."
set_error_report_source
get_product_versions
construct_report_template "$severity" "$summary" \
| $PRODUCT_ROOT_D/admin/bin/send-error-report --version "$product_this_version" $report_source >/dev/null 2>&1
}
# Use this function to report failed actions.
# Typical report should contain
# - reason or problem description (example: file copying failed)
# - how to resolve or investigate problem (example: check file permissions, free disk space)
# - how to re-run action (example: perform specific command, restart bootstrapper script, run installation again)
report_problem()
{
local severity="${1:-error}"
# Get first string of error as a summary of report
shift
local summary="$1"
[ -n "$product_problems_log" ] || product_problems_log="/dev/stderr"
p_echo
if [ "0$problems_occured" -eq 0 ]; then
echo "***** $process problem report *****" >> "$product_problems_log" 2>&1
fi
for problem_message in "$@"; do
p_echo "$problem_message"
if [ "$product_log" != "$product_problems_log" ]; then
echo "$problem_message" >> "$product_problems_log" 2>&1
fi
done
p_echo
construct_report "$severity" "$summary"
[ -n "$PLESK_INSTALLER_DEBUG" -o -n "$PLESK_INSTALLER_VERBOSE" ] || \
product_log_tail
problems_occured=1
}
set_error_report_source()
{
[ -z "$1" ] || report_source="$1"
[ -n "$report_source" ] || {
if [ -n "$PACKAGE_ID" -o -n "$PACKAGE_ACTION" -o -n "$PACKAGE_NAME" -o -n "$PACKAGE_VERSION" ]; then
report_source="install"
else
report_source="backend"
fi
}
}
set_error_report_component()
{
local component="$1"
if [ "$report_source" = "install" ]; then
[ -n "$report_component" ] || report_component="$PACKAGE_ID"
return 0
fi
[ -z "$component" ] || report_component="$1"
[ -n "$report_component" ] || report_component="`basename $0`"
}
set_error_report_params()
{
if [ "$report_source" = "install" ]; then
[ -n "$report_params" ] || report_params="`echo "$PACKAGE_ACTION of $PACKAGE_NAME $PACKAGE_VERSION" | base64`"
return 0
fi
[ -z "$*" ] || report_params="`echo "$*" | base64`"
[ -n "$report_params" ] || report_params="`echo "$PLESK_SCRIPT_COMMAND_LINE" | base64`"
}
detect_virtualization()
{
detect_vz
detect_lxc
local is_docker="`[ -f "/.dockerenv" ] && echo yes || :`"
local systemd_detect_virt_ct="`/usr/bin/systemd-detect-virt -c 2>/dev/null | grep -v '^none$' || :`"
local systemd_detect_virt_vm="`/usr/bin/systemd-detect-virt -v 2>/dev/null | grep -v '^none$' || :`"
local virt_what="`/usr/sbin/virt-what 2>/dev/null | xargs || :`"
if [ -n "$is_docker" ]; then
echo "docker $virt_what"
elif [ "$PLESK_VZ" = "1" ]; then
echo "${PLESK_VZ_TYPE:-virtuozzo}"
elif [ "$PLESK_LXC" = "1" ]; then
echo "lxc $virt_what"
elif [ -n "$systemd_detect_virt_ct" ]; then
echo "$systemd_detect_virt_ct $systemd_detect_virt_vm"
elif [ -n "$virt_what" ]; then
echo "$virt_what"
elif [ -n "$systemd_detect_virt_vm" ]; then
echo "$systemd_detect_virt_vm"
fi
}
default_error_report_environment()
{
local virtualization="`detect_virtualization`"
if [ -n "$virtualization" ]; then
echo "virtualization: $virtualization"
fi
}
set_error_report_environment()
{
[ -z "$*" ] || report_environment="`echo "$*" | base64`"
[ -n "$report_environment" ] || report_environment="`default_error_report_environment | base64`"
}
get_update_ticket()
{
[ -r $PRODUCT_ROOT_D/var/update_ticket ] && cat $PRODUCT_ROOT_D/var/update_ticket | awk '{$1=$1};1'
}
get_product_versions()
{
# Don't use global variables set elsewhere in this code. Use substitutions if needed.
local prod_root_d="/opt/psa"
product_name="psa"
if [ -z "$product_this_version" ]; then
# 1. Try to fetch version from file created by bootstrapper (should be 3-component).
product_this_version="`cat "/var/lock/plesk-target-version" 2>/dev/null`"
# 2. Fallback to $PRODUCT_ROOT_D/version (should be 3-component).
if [ -z "$product_this_version" -a -r "$prod_root_d/version" ]; then
product_this_version="`awk '{ print $1 }' "$prod_root_d/version"`"
fi
# 3. Fallback to hardcoded version (2-component). This may cause some other code to fail.
if [ -z "$product_this_version" ]; then
product_this_version="18.0"
echo "Unable to determine \$product_this_version, will use less precise value '$product_this_version'" >&2
fi
fi
product_version="$product_this_version"
if [ -z "$product_prev_version" ]; then
if [ -r "$prod_root_d/version.upg" ]; then
product_prev_version=`awk '{ print $1 }' "$prod_root_d/version.upg"`
elif [ -r "$prod_root_d/version" ]; then
product_prev_version=`awk '{ print $1 }' "$prod_root_d/version"`
else
product_prev_version="$product_this_version"
fi
fi
}
### Copyright 1999-2025. WebPros International GmbH. All rights reserved.
err()
{
echo "$*" >&2
}
php_module_extract_name()
{
local config="$1"
local state="${2:-}" # "", enabled, disabled
local short="$3"
local prefix=""
[ ! "$state" = "enabled" ] || prefix='^\s*'
[ ! "$state" = "disabled" ] || prefix='^\s*;\s*'
local module=$(perl -nle 'print $2 if /'"${prefix}"'(?:zend_|)extension\s*=\s*([\"'"\'"']?)(.*)\.so\1/' "$config")
if [ -z "$module" -a -n "$short" ]; then
perl -nle 'print $2 if /'"${prefix}"'(?:zend_|)extension\s*=\s*([\"'"\'"']?)(.*)\1/' "$config"
else
echo "$module"
fi
}
php_module_is_enabled()
{
local config="$1"
local ext_s="$2"
local short="$3"
php_module_extract_name "$config" 'enabled' "${short}" | grep -q --ignore-case "^$ext_s\$"
}
php_module_is_disabled()
{
local config="$1"
local ext_s="$2"
local short="$3"
php_module_extract_name "$config" 'disabled' "${short}" | grep -q --ignore-case "^$ext_s\$"
}
php_module_enable()
{
local config="$1"
local ext_s="$2"
local short="$3"
local suffix=
[ -n "$short" ] || suffix='\.so'
perl -pi -e 's#^\s*;\s*((?:zend_|)extension\s*=\s*([\"'"\'"']?)'"${ext_s}"''"${suffix}"'\b\2)#\1#gi' "$config"
}
php_module_disable()
{
local config="$1"
local ext_s="$2"
local short="$3"
local suffix=
[ -n "$short" ] || suffix='\.so'
perl -pi -e 's#^\s*((?:zend_|)extension\s*=\s*([\"'"\'"']?)'"${ext_s}"''"${suffix}"'\b\2)#; \1#gi' "$config"
}
pecl_package_to_ext_name()
{
# PECL package syntax: [channel/]name[-version|stability][.tar|tgz|xml]
local package="$1"
local file_name="${package##*/}"
local name_version="${file_name%.gz}"
name_version="${name_version%.[ttxz][agmi][rzlp]}"
local ext_name="${name_version%%[-]*}"
echo "$ext_name"
}
pecl_package_to_version_or_stability()
{
local package="$1"
local file_name="${package##*/}"
local version_and_extension="${file_name#*-}"
if [ "$version_and_extension" = "$file_name" ]; then
version_and_extension=
else
version_and_extension="${version_and_extension%.gz}"
fi
local version="${version_and_extension%.[ttxz][agmi][rzlp]}"
echo "$version"
}
is_ioncube_loader_package_name()
{
[[ "$1" =~ ^ioncube_loader(_lin_[0-9]+\.[0-9]+)?(-[0-9.]+)?$ ]]
}
have()
{
local needle="$1"
[ -n "$needle" ] || {
err "Error: not enough arguments for the 'have' function"
return 1
}
shift
[[ " $* " =~ " $needle " ]]
}
prioritize_configs_by_name()
{
local extension_name="`basename "$1" .so`"
extension_name="${extension_name,,}"
[ -n "$extension_name" ] || {
cat -
return 0
}
local exact_matches=()
local partial_matches=()
local non_matches=()
local path=
while IFS= read -r path; do
[ -n "$path" ] || continue
local ext="`basename "$path" .ini`"
case "${ext,,}" in
"$extension_name") exact_matches+=("$path") ;;
*"$extension_name"*) partial_matches+=("$path") ;;
*) non_matches+=("$path") ;;
esac
done
[ -z "${exact_matches[*]}" ] || printf '%s\n' "${exact_matches[@]}"
[ -z "${partial_matches[*]}" ] || printf '%s\n' "${partial_matches[@]}"
[ -z "${non_matches[*]}" ] || printf '%s\n' "${non_matches[@]}"
}
# vim:ft=sh:
#!/bin/bash
### Copyright 1999-2025. WebPros International GmbH. All rights reserved.
# vim:ft=sh:
option_error()
{
err "$*"
err ""
usage
}
debug()
{
if [ $_V -ge 1 ]; then
echo "debug: $*" >&2
fi
}
usage()
{
cat << EOT >&2
Usage: $prog [OPTIONS]
-p,--php-cli Appropriate PHP cli for operations. Mandatory.
-c,--php-ini php.ini config for operations. Mandatory.
-l,--list Display extensions availiable for load
-s,--list-all Display all extensions (loadable and static)
-a,--list-installable Display installable extensions as JSON
-e,--enable=ext Enable specified extension. Can be specified several times.
-d,--disable=ext Disable specified extension. Can be specified several times.
-i,--install=ext Install extension. Can be specified several times.
-u,--uninstall=ext Uninstall extension. Can be specified several times.
-v,--verbose Be more verbose.
-h,--help Display this message.
Notes on --install and --uninstall commands:
* no checks whether the corresponding extensions are already installed and provided
by system package manager are performed (i.e. running these commands for packaged
extensions is destructive);
* these commands accept PECL packages as arguments - this means you may specify
versions, e.g. '--install redis-5.3.5', or states, e.g. '--install redis-beta',
using '--install redis' also works and will use the latest stable version;
* these commands mostly use PECL, except when operating on 'ioncube_loader' or
'ioncube_loader_lin_.' (preferable name), in which case
the extension is downloaded directly from the vendor site;
* '--install' expects that required system dependencies are already installed,
this includes PHP development files, compiler, make, libraries' development files;
* no version compatibility checks are performed except those that PECL does;
* neither of the commands restarts or reloads any PHP-FPM or web server services.
Notes on --enable and --disable commands:
* these commands accept extension names as arguments, but PECL packages are also
accepted for uniformity with '--install' and '--uninstall' options (note that
in general PECL package name may not match corresponding extension name).
EOT
exit 1
}
get_extension_type()
{
# Check got from ext/standard/dl.c. But may be we should attempt to load via dl or -d
# However, dl can be disabled, and failure of load in -d doesn't cause php to return 1
local ext_file="$extension_dir/$1.so"
if [ ! -e "$ext_file" ] ; then
ext_file="$extension_dir/$(echo "$1" | tr '[:upper:]' '[:lower:]').so"
fi
if readelf -s --wide "$ext_file" | awk '{print $8}' 2>/dev/null | grep -q -E '^(_|)zend_extension_entry$'; then
debug "$ext_file: zend_extension detected"
echo "zend_extension"
return
elif readelf -s --wide "$ext_file" | awk '{print $8}' 2>/dev/null | grep -q -E '^(_|)get_module$'; then
debug "$ext_file: common_extension detected"
echo "extension"
return
else
debug "$ext_file: not a php extension"
echo ""
fi
}
find_config()
{
local ext="$1"
local status="$2"
for config_path in `get_configs | prioritize_configs_by_name "$ext"`; do
php_module_extract_name "$config_path" "$status" "$shortname_allowed" |
xargs -r -n1 basename |
grep --ignore-case "^$ext\$" >/dev/null ||
continue
echo "$config_path"
return
done
}
enmod()
{
local ext="$1"
if [ -z "$ext" ]; then
err "empty extension specified"
return 1
fi
local config=$(find_config "$ext")
local ext_s="$ext"
local ext_type=$(get_extension_type "$ext")
if [ -z "$config" ]; then
err "cannot find config for extension '$ext'"
return 1
fi
if [ "$ext_type" != "extension" -a "$ext_type" != "zend_extension" ]; then
err "$ext: invalid extension format - not a PHP library"
return 1
fi
if [ "$ext_type" = "zend_extension" ]; then
ext_s="$extension_dir/${ext}"
fi
if php_module_is_enabled "$config" "$ext" "$shortname_allowed"; then
debug "$ext (${ext}.so) already enabled in $config, do nothing"
return 0
fi
if [ "$ext_type" = "zend_extension" ] && php_module_is_enabled "$config" "$ext_s" "$shortname_allowed"; then
debug "$ext (${ext_s}.so) already enabled in $config, do nothing"
return 0
fi
if php_module_is_disabled "$config" "$ext" "$shortname_allowed"; then
debug "$ext (${ext}.so) disabled in $config, switch it on"
php_module_enable "$config" "$ext" "$shortname_allowed"
return 0
fi
if [ "$ext_type" = "zend_extension" ] && php_module_is_disabled "$config" "$ext_s" "$shortname_allowed"; then
debug "$ext (${ext_s}.so) disabled in $config, switch it on"
php_module_enable "$config" "$ext_s" "$shortname_allowed"
return 0
fi
debug "$ext is not found in $config, add new line"
echo "${ext_type}=${ext_s}.so" >> "$config"
return 0
}
dismod()
{
local ext="$1"
if [ -z "$ext" ]; then
err "empty extension specified"
return 1
fi
if [ -z "`find_config "$ext"`" ]; then
err "cannot find config for extension '$ext'"
return 1
fi
# Disable the extension in all configuration files
local config=
while config="`find_config "$ext" "enabled"`" && [ -n "$config" ]; do
local ext_s="$ext"
local ext_type=$(get_extension_type "$ext")
if [ "$ext_type" != "extension" -a "$ext_type" != "zend_extension" ]; then
err "$ext: invalid extension format - not a PHP library"
return 1
fi
if [ "$ext_type" = "zend_extension" ]; then
ext_s="$extension_dir/${ext}"
fi
if php_module_is_disabled "$config" "$ext" "$shortname_allowed"; then
debug "$ext ($ext) already disabled in $config, do nothing"
continue
fi
if [ "$ext_type" = "zend_extension" ] && php_module_is_disabled "$config" "$ext_s" "$shortname_allowed"; then
debug "$ext (${ext_s}.so) already disabled in $config, do nothing"
continue
fi
if php_module_is_enabled "$config" "$ext" "$shortname_allowed"; then
debug "$ext (${ext}.so) enabled in $config, switch it off"
php_module_disable "$config" "$ext" "$shortname_allowed"
fi
if [ "$ext_type" = "zend_extension" ] && php_module_is_enabled "$config" "$ext_s" "$shortname_allowed"; then
debug "$ext (${ext_s}.so) enabled in $config, switch it off"
php_module_disable "$config" "$ext_s" "$shortname_allowed"
fi
done
return 0
}
get_native_php_version()
{
local php_bin="`readlink -e "$php_cli"`"
if [[ "$php_bin" =~ ^/usr/bin/php.*$ ]] ; then
echo "${php_bin:12}"
fi
}
native_debian_php_extensions_op()
{
local enmod_or_dismod_bin="$1"
shift
local extensions=("$@")
for i in "${!extensions[@]}"; do
# make sure ionCube Loader has the same name as the config under mods-availiable/
! is_ioncube_loader_package_name "${extensions[$i]}" || extensions[$i]="ioncube-loader"
done
local php_version=`get_native_php_version`
if [ -z "$php_version" ] ; then
if [ -L "$php_cli" ] ; then
err "Debian native PHP binary has unexpected link: '`readlink -e "$php_cli"`'. The endpoint should be at /usr/bin and named phpX.Y"
else
err "Unexpected path for debian native PHP binary: '$php_cli'. The binary should be at /usr/bin and named phpX.Y"
fi
return 1
fi
"$enmod_or_dismod_bin" "-v" "$php_version" "${extensions[@]}"
}
enable_extensions()
{
[ -n "$1" ] || return 0
if check_native_debian_php; then
native_debian_php_extensions_op "$debian_mods_enmod_bin" "$@"
return $?
fi
for i in "$@"; do
if ! enmod $i; then
err "Cannot enable extension '$i'"
return 1
fi
done
}
disable_extensions()
{
[ -n "$1" ] || return 0
if check_native_debian_php; then
native_debian_php_extensions_op "$debian_mods_dismod_bin" "$@"
return $?
fi
for i in "$@"; do
if ! dismod $i; then
err "Cannot disable extension '$i'"
return 1
fi
done
}
check_native_debian_php()
{
if [ -n "${debian}" ]; then
return $debian
fi
debian=1
if [ ! -x "/usr/sbin/phpenmod" ]; then
return "$debian"
fi
local php_version=`get_native_php_version`
[ -n "$php_version" ] || return "$debian"
debian=0
debian_mods_availiable_dir="/etc/php/$php_version/mods-available"
debian_mods_enmod_bin="/usr/sbin/phpenmod"
debian_mods_dismod_bin="/usr/sbin/phpdismod"
return "$debian"
}
get_configs()
{
if ! check_native_debian_php; then
_get_configs
return
fi
# On debian, we have separate configs for enabled and disabled extensions.
if [ "$1" = "enabled" ]; then
_get_configs
return
fi
{
_get_configs
ls $debian_mods_availiable_dir/*.ini
} | xargs -n1 readlink -f | sort -u
}
_get_configs()
{
$php_cli -c $php_ini -r 'print(php_ini_scanned_files());' | tr ',' '\n' | sort -u | grep -v '^$'
if [ -f "$php_ini" ] ; then
echo "$php_ini"
fi
}
get_all_extensions()
{
"$php_cli" -c "$php_ini" -m |
sed -ne '/^\[PHP Modules\]$/,/^\[/ p' |
sed -e '/^\[/ d' -e '/^\s*$/ d' |
tr '[A-Z]' '[a-z]' |
sort -u |
while read -r ext; do
case "$ext" in
"ioncube loader") ext="ioncube_loader" ;;
"zend opcache") ext="opcache" ;;
esac
echo "$ext"
done
}
list_extensions()
{
local list_all="$1"
declare -a got_extensions=()
_list_extensions_by_status()
{
local status="$1"
local mark="$2"
for config_path in `get_configs "$status"`; do
for line in `php_module_extract_name "$config_path" "$status" "$shortname_allowed"`; do
[ -n "$line" ] || continue
[ -f "$extension_dir/${line}.so" -o -f "${line}.so" ] || continue
local ext="`basename "$line"`"
! have "$ext" "${got_extensions[@]}" || continue
got_extensions+=("$ext")
if is_ioncube_loader_package_name "$ext" && [ "$ext" != "ioncube_loader" ]; then
# ensure ioncube_loader is not listed as "static" even if its name differs
got_extensions+=("ioncube_loader")
fi
echo "$ext $mark"
done
done
}
_list_extensions_static()
{
local mark="$1"
for ext in `get_all_extensions`; do
! have "$ext" "${got_extensions[@]}" || continue
got_extensions+=("$ext")
echo "$ext $mark"
done
}
_list_extensions_by_status "enabled" "on"
_list_extensions_by_status "" "off"
[ -z "$list_all" ] ||
_list_extensions_static "static"
}
list_installable_extensions()
{
product_default_conf
local ioncube_loader="`list_installable_ioncube_loader_as_python_dict`"
local python_bin="$PRODUCT_ROOT_D/bin/py3-python"
"$pecl_bin" list-all |
"$python_bin" -c '
import sys, operator, json
lines = [""]
for line in sys.stdin:
# Process line continuations for description
if line[:1].isspace():
lines[-1] += line.lstrip()
else:
lines.append(line)
fields = ["package", "latest", "local", "description"]
packages = ['"$ioncube_loader"']
slice_start = {}
slice_end = {}
cookie_cutter = {}
for line in lines:
# Header includes only 3 first columns
if not slice_start and line.split() == [field.upper() for field in fields[:3]]:
starts = {item.lower(): line.index(item) for item in line.split()}
slice_start = {sf: starts.get(sf) for sf in fields}
slice_end = {sf: starts.get(ef) for sf, ef in zip(fields, fields[1:])}
continue
# Then determine the 4th column parameters based on the first row
if not cookie_cutter and slice_start.get("local") and not slice_end.get("local"):
last_columns = line[slice_start["local"]:]
if last_columns[:1].isspace():
desc_start = slice_start["local"] + (len(last_columns) - len(last_columns.lstrip()))
else:
desc_start = slice_start["local"] + (len(last_columns) - len(last_columns.split(maxsplit=1)[1]))
slice_start["description"] = slice_end["local"] = desc_start
slice_end["description"] = None
cookie_cutter = {field: operator.itemgetter(slice(slice_start[field], slice_end[field])) for field in fields}
# Parse each table row
if cookie_cutter:
pkg = {field: cutter(line).strip() for field, cutter in cookie_cutter.items()}
pkg["package"] = pkg["package"].rsplit("/", maxsplit=1)[-1]
pkg["latest"] = pkg["latest"] or pkg["local"]
packages.append(pkg)
json.dump(packages, sys.stdout, indent=2)
'
}
check_runtime_for_pecl()
{
[ -z "${CHECK_RUNTIME_FOR_PECL_DONE:-}" ] || return 0
debug "Check 'temp_dir' configured in $pecl_bin"
local pecl_temp_dir="`"$pecl_bin" config-get temp_dir`"
if [ -d "$pecl_temp_dir" ]; then
# PECL will create directory if needed.
# 'pecl config-set' doesn't work, but 'pear config-set' does.
! findmnt -o OPTIONS -nkT "$pecl_temp_dir" | grep -qwF noexec ||
echo "WARNING: PECL temp_dir '$pecl_temp_dir' is mounted with 'noexec'." \
"Extension installation will most likely fail." \
"Remount the FS with 'exec' option or configure a new directory" \
"using '${pecl_bin/pecl/pear} config-set temp_dir ... system'."
fi
CHECK_RUNTIME_FOR_PECL_DONE="yes"
}
install_extension_pecl()
{
local package="$1"
local ext="`pecl_package_to_ext_name "$package"`"
local libname="$ext.so"
check_runtime_for_pecl
debug "Execute: $pecl_bin install $package"
"$pecl_bin" install "$package" "$config_dir/$ext.ini" <<-EOT
; Installed by Plesk via 'pecl'
; priority=20
$extension_type=$libname
EOT
else
cat > "$config_dir/$ext.ini" <<-EOT
; Installed by Plesk via 'pecl'
; $extension_type=$libname
EOT
fi
}
uninstall_extension_pecl()
{
local package="$1"
local ext="`pecl_package_to_ext_name "$package"`"
disable_extensions "$ext"
debug "Execute: $pecl_bin uninstall $package"
"$pecl_bin" uninstall "$package" /dev/null || {
err "Downloaded ionCube Loaders archive doesn't contain expected $ext_name.so file"
rm -f "$archive"
return 1
}
tar -xzf "$archive" --strip-components 1 -C "$extension_dir" "ioncube/$ext_name.so" &&
[ -f "$extension_dir/$ext_name.so" ] ||
{
err "Failed to unpack ionCube loader file $ext_name.so"
rm -f "$archive"
return 1
}
# Clean up
rm -f "$archive"
debug "Set up correct permissions on $extension_dir/$ext_name.so"
# We'll consider 'execstack -c "$extension_dir/$ext_name.so"' excessive nowadays
chown root:root "$extension_dir/$ext_name.so"
chmod 0644 "$extension_dir/$ext_name.so"
[ ! -x "/usr/sbin/restorecon" ] || /usr/sbin/restorecon "$extension_dir/$ext_name.so"
# The loader should be declared first in PHP configuration, hence the config name or priority
if check_native_debian_php; then
local config="$config_dir/ioncube-loader.ini"
debug "Create: $config (uncommented, but not enabled)"
cat > "$config" <<-EOT
; Installed by Plesk (non-packaged file)
; priority=00
zend_extension=$ext_name.so
EOT
else
local config="$config_dir/00-ioncube-loader.ini"
debug "Create: $config (in disabled state)"
cat > "$config" <<-EOT
; Installed by Plesk (non-packaged file)
; zend_extension=$ext_name.so
EOT
fi
}
uninstall_extension_ioncube_loader()
{
local package="$1"
local ext="`pecl_package_to_ext_name "$package"`"
local php_major_minor="${phpversion%.*}"
local ext_name="ioncube_loader_lin_${php_major_minor}"
is_ioncube_loader_package_name "$ext" || return 1
disable_extensions "$ext"
debug "Remove: $extension_dir/$ext_name.so and $config_dir/*ioncube-loader.ini (should already be disabled)"
if check_native_debian_php; then
rm -f "$config_dir/ioncube-loader.ini"
else
rm -f "$config_dir/00-ioncube-loader.ini"
fi
rm -f "$extension_dir/$ext_name.so"
}
install_extensions()
{
for ext in "$@"; do
debug "Installing '$ext'"
if is_ioncube_loader_package_name "$ext"; then
install_extension_ioncube_loader "$ext" || return $?
else
install_extension_pecl "$ext" || return $?
fi
done
}
uninstall_extensions()
{
for ext in "$@"; do
debug "Uninstalling '$ext'"
if is_ioncube_loader_package_name "$ext"; then
uninstall_extension_ioncube_loader "$ext" || return $?
else
uninstall_extension_pecl "$ext" || return $?
fi
done
}
prog="`basename $0`"
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
umask 022
TEMP=`getopt -o vhlsap:c:e:d:i:u: \
--long help,verbose,php-cli:,php-ini:,list,list-all,list-installable,enable:,disable:,install:,uninstall: \
-n "$prog" -- "$@"`
if [ $? != 0 ]; then
echo "Internal Error: getopt cannot parse this command line: $@" >&2
exit 1
fi
php_cli=
php_ini=
declare -a ext_enable=()
declare -a ext_disable=()
declare -a ext_install=()
declare -a ext_uninstall=()
list_cmd=
list_all_cmd=
list_installable_cmd=
_V=0
eval set -- "$TEMP"
while true ; do
case "$1" in
-p|--php-cli) php_cli="$2"; shift 2;;
-c|--php-ini) php_ini="$2"; shift 2;;
-e|--enable) ext_enable+=("`pecl_package_to_ext_name "$2"`"); shift 2;;
-d|--disable) ext_disable+=("`pecl_package_to_ext_name "$2"`"); shift 2;;
-i|--install) ext_install+=("$2"); shift 2;;
-u|--uninstall) ext_uninstall+=("$2"); shift 2;;
-l|--list) list_cmd="1"; shift;;
-s|--list-all) list_all_cmd="1"; shift;;
-a|--list-installable) list_installable_cmd="1"; shift;;
-v|--verbose) let _V++; shift;;
-h|--help) usage ;;
--) shift ; break ;;
*) echo "Unknown option '$1'" ; exit 1 ;;
esac
done
if [ -z "$php_cli" ]; then
option_error "No php cli (--php-cli) specified"
fi
if [ ! -x "$php_cli" ]; then
option_error "PHP cli ($php_cli) should be executable"
fi
pecl_bin="`dirname "$php_cli"`/pecl"
if [ ! -x "$pecl_bin" -a \( -n "${ext_install[*]}" -o -n "${ext_uninstall[*]}" \) ]; then
option_error "PECL binary ($pecl_bin) should be executable"
fi
if [ -z "$php_ini" ]; then
option_error "No php ini (--php-ini) specified"
fi
if [ ! -f "$php_ini" ]; then
warn "PHP ini ($php_ini) doesn't exist, or not a file"
fi
extension_dir=$("$php_cli" -c "$php_ini" -r 'print(ini_get("extension_dir"));')
if [ -z "$extension_dir" -o ! -d "$extension_dir" ]; then
option_error "Extension dir \"$extension_dir\" is empty or not a directory"
fi
config_dir="`"$php_cli" --ini -c "$php_ini" |
sed -ne 's|^Scan for additional \.ini files in:\s*\(.*\)$|\1| p' | xargs`"
! check_native_debian_php || config_dir="$debian_mods_availiable_dir"
if [ -z "$config_dir" -o ! -d "$config_dir" ]; then
option_error "PHP ini configuration directory \"$config_dir\" is empty or not a directory"
fi
if [ -z "$list_cmd" -a -z "$list_all_cmd" -a -z "$list_installable_cmd" -a \
-z "${ext_enable[*]}" -a -z "${ext_disable[*]}" -a \
-z "${ext_install[*]}" -a -z "${ext_uninstall[*]}" ]; then
option_error "No action specified"
fi
if [ "`echo $list_cmd $list_all_cmd $list_installable_cmd | wc -w`" -gt 1 ]; then
option_error "Cannot use multiple listing options (--list, --list-all, --list-installable) simultaneously"
fi
if [ -n "${ext_enable[*]}" -o -n "${ext_disable[*]}" -o \
-n "${ext_install[*]}" -o -n "${ext_uninstall[*]}" ]; then
[ -z "$list_cmd" ] ||
option_error "Cannot use --list and --enable/--disable/--install/--uninstall simultaneously"
[ -z "$list_all_cmd" ] ||
option_error "Cannot use --list-all and --enable/--disable/--install/--uninstall simultaneously"
[ -z "$list_installable_cmd" ] ||
option_error "Cannot use --list-installable and --enable/--disable/--install/--uninstall simultaneously"
fi
phpversion=$("$php_cli" -r '
if (defined("PHP_MAJOR_VERSION"))
echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION . "." . PHP_RELEASE_VERSION;
else
echo phpversion();
')
shortname_allowed=""
case $phpversion in
[45]*) shortname_allowed="" ;;
7.0.*) shortname_allowed="" ;;
7.1.*) shortname_allowed="" ;;
*) shortname_allowed="1" ;;
esac
if [ -n "$list_cmd" ]; then
list_extensions
exit $?
elif [ -n "$list_all_cmd" ]; then
list_extensions --all
exit $?
elif [ -n "$list_installable_cmd" ]; then
list_installable_extensions
exit $?
fi
true &&
install_extensions "${ext_install[@]}" &&
uninstall_extensions "${ext_uninstall[@]}" &&
enable_extensions "${ext_enable[@]}" &&
disable_extensions "${ext_disable[@]}"