#!/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[@]}"