D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
opt
/
psa
/
bootstrapper
/
components
/
Filename :
core.sh
back
Copy
#!/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/14/main PGSQL_CONF_D=/etc/postgresql/14/main PGSQL_BIN_D=/usr/lib/postgresql/14/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 } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. #admin set_admin_params() { ADMIN_ROOT=$PRODUCT_ROOT_D/admin UTILS_DIR=$ADMIN_ROOT/sbin admin_user="${product}adm" admin_UID=8444 admin_group="${product}adm" admin_GID=8444 ADMIN_CERT="$ADMIN_ROOT/conf/httpsd.pem" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. clean_panel_sessions() { # use subshell to catch die's inside db_do: ( db_do "DELETE FROM sessions" db_do "DELETE FROM SessionContexts" ) } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. set_apache_params() { apache_user="www-data" apache_UID=80 apache_group="www-data" apache_GID=80 apache_pid_file="$APACHE_ROOT/logs/httpd.pid" apache_lock_file="$APACHE_ROOT/logs/httpd.lock" product_lock_file="$HTTPD_CONF_D/cnf.lock" apache_service_name="apache2" apache_modules_d="/usr/lib/apache2/modules" apache_service="$apache_service_name" apache_httpd_conf="$HTTPD_CONF_D/apache2.conf" apache_httpd_include="$HTTPD_INCLUDE_D/zz010_psa_httpd.conf" APACHE_ROOT="/usr" min_suexec_UID=10000 max_suexec_UID=16000 min_suexec_GID=$min_suexec_UID max_suexec_GID=$max_suexec_UID suexec_storage=/usr/lib/plesk-9.0/suexec suexec=/usr/lib/apache2/suexec suexec_dir="`dirname "$suexec"`" suexec_file="`basename "$suexec"`" rpm_httpd_bin=/usr/sbin/httpd } httpdmng_reconfigure() { local httpdmng_tool="${PRODUCT_ROOT_D}/admin/sbin/httpdmng" local what="$1" shift case "$what" in all|server|webmail) : ;; *) simply_die "Unexpected argument '$what' to httpdmng_reconfigure()" ;; esac if [ ! -x "$httpdmng_tool" ]; then p_echo "Unable to do 'httpdmng --reconfigure-$what': the utility is not yet on its place. Maybe will attempt later." return 1 fi ! "$httpdmng_tool" "--reconfigure-$what" -skip-removing -service-node local "$@" >> $product_log 2>&1 || return 0 # There are 2 well known possible reasons of webserver templates reconfiguration failure: # 1. domain/server/webmail templates are well, but arbitrary domain unable to be configured due to the some reasons, # for example broken vhost structure # 2. broken domain/server/webmail templates # STEP 1. try to rollback to the default templates ! httpdmng_reconfigure_rollback_to_default_templates "${what}" "$@" || return 0 if [ "all" = "${what}" ]; then # STEP 2. ignore broken domains pp_echo "Warning: web server configuration is broken. We will try to repair it. This operation can take a lot of time, please do not interrupt the process." p_echo "Unable to rebuild web server configuration, possible there are broken domains" local inten="reconfigure web-server configurations skipping broken domains" echo_try "${inten}" if "$httpdmng_tool" "--reconfigure-$what" "-skip-broken" -service-node local "$@" >> $product_log 2>&1; then suc return 0 else # STEP 3. try to rollback to the default templates, broken domains are ignored (possibly problem are in the server/webmail configs) warn "${inten}" ! httpdmng_reconfigure_rollback_to_default_templates "${what}" "-skip-broken" -service-node local "$@" || return 0 fi fi return 1 } # NOTE: do not use function directly, it just helper for httpdmng_reconfigure !!! # here we try to reconfigure webserver configs using default temnplates httpdmng_reconfigure_rollback_to_default_templates() { local template_d="${PRODUCT_ROOT_D}/admin/conf/templates" local inten="rebuild web server configs with default templates after reconfiguration failure" local tmp_d= local what="$1" shift [ -d "${template_d}/custom" ] || return 1 p_echo "Unable to rebuild web server configuration with currently active default templates" echo_try "${inten}" if ! tmp_d=`mktemp -d "${template_d}/broken_templates_XXXXXXXX" 2>>$product_log`; then warn "${inten} - unable to create temporary directory for custom templates" return 1 fi if ! mv -f "${template_d}/custom/"* "${tmp_d}"; then warn "${inten} - unable to move custom templates into ${tmp_d}" return 1 fi rm -rf "${template_d}/custom" if ! "$httpdmng_tool" "--reconfigure-$what" -service-node local "$@" >> $product_log 2>&1; then warn "${inten} - using default templates does not solve the problem, restore custom ones" mv -f "${tmp_d}" "${template_d}/custom" return 1 fi suc pp_echo "Custom templates which are breaking web server reconfiguration were moved to ${tmp_d}" cat <<EOF >"${tmp_d}/README" This directory contains broken custom webserver templates which are breaking web server reconfiguration. Custom templates were moved here on `date` during product update or upgrade. EOF } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. #-*- vim:ft=sh repeat_until_succeeds() { local repeat_func="$1" local modify_func="$2" local max_times="$3" for i in `seq 1 "$max_times"`; do eval "$repeat_func" && return 0 || eval "$modify_func" done return 1 } apsc_try_create_db() { local mysql_db_name="mysql" echo "CREATE DATABASE $apsc_db_name" | dbclient_invoke >> "$product_log" 2>&1 } apsc_modify_db_name() { apsc_db_name="${apsc_db_name_base}_`get_random_string 4 'a-z0-9'`" } apsc_try_create_db_user() { local mysql_db_name="mysql" local host='localhost' local dsn_apsc_host="`get_dsn apsc host`" [ -z "$dsn_apsc_host" ] || host='%' echo "CREATE USER '$apsc_db_user'@'$host' IDENTIFIED BY '$apsc_db_passwd'" | dbclient_invoke >> "$product_log" 2>&1 && \ echo "GRANT ALL PRIVILEGES ON $apsc_db_name.* TO '$apsc_db_user'@'$host'" | dbclient_invoke >> "$product_log" 2>&1 } apsc_modify_db_user() { apsc_db_user="${apsc_db_user_base}_`get_random_string 4 'a-z0-9'`" } apsc_init_db() { local mysql_db_name="$apsc_db_name" local mysql_user="--user=$apsc_db_user" local mysql_passwd="$apsc_db_passwd" local sql_dump_file="$PRODUCT_BOOTSTRAPPER_DIR/db/apsc_mysql_db.sql" [ -r "$sql_dump_file" ] || return 1 dbclient_invoke < "$sql_dump_file" >> "$product_log" 2>&1 } apsc_remove_db() { [ -n "$apsc_db_name" ] || return 1 local mysql_db_name="mysql" echo "DROP DATABASE $apsc_db_name" | dbclient_invoke >> "$product_log" 2>&1 } apsc_remove_db_user() { [ -n "$apsc_db_user" ] || return 1 local mysql_db_name="mysql" echo "DROP USER '$apsc_db_user'@'localhost'" | dbclient_invoke >> "$product_log" 2>&1 } apsc_detect_odbc_driver() { odbc_config_bin="/usr/bin/odbc_config" # present at least on CentOS 5 if [ -x "$odbc_config_bin" ]; then odbc_dsn_conf=`$odbc_config_bin --odbcini 2>> "$product_log"` odbc_drivers_conf=`$odbc_config_bin --odbcinstini 2>> "$product_log"` fi if [ -z "$odbc_dsn_conf" ]; then odbc_dsn_conf="/etc/odbc.ini" fi if [ -z "$odbc_drivers_conf" ]; then odbc_drivers_conf="/etc/odbcinst.ini" fi odbc_mysql_drivers="/usr/lib/x86_64-linux-gnu/odbc/libmaodbc-plesk.so" for d in $odbc_mysql_drivers; do [ -s $d ] || continue odbc_mysql_driver="$d" break done if [ -z "$odbc_mysql_driver" ]; then die "No ODBC MySQL mysql drivers found, trying $odbc_mysql_drivers" fi if [ -s "/usr/lib/x86_64-linux-gnu/sw/libmysqlserver.so.2.0" ]; then apsc_driver_library="/usr/lib/x86_64-linux-gnu/sw/libmysqlserver.so.2.0" elif [ -s "/usr/lib/x86_64-linux-gnu/libmysqlserver.so.2.0" ]; then apsc_driver_library="/usr/lib/x86_64-linux-gnu/libmysqlserver.so.2.0" else die "find MySQL mysql platform driver for APSC" fi apsc_odbc_driver_name="MySQL" } set_apsc_version_params() { # Frozen, since we don't change apsc DB anymore product_apsc_db_version="017009013" } apsc_sanity_check_binaries() { # To easily detect breakage on new OSes [ -f "$odbc_mysql_driver" ] || die "find MySQL ODBC driver" [ -f "$apsc_driver_library" ] || die "find MySQL platform driver library" } read_apsc_connection_params_db() { # Warning: password can be encrypted. Don't use it. apsc_db_name=`db_read_misc_attr "aps_database"` apsc_db_user=`db_read_misc_attr "aps_login"` apsc_db_passwd=`db_read_misc_attr "aps_password"` apsc_db_host=`db_read_misc_attr "aps_host"` } save_apsc_connection_params_db() { local register_script="$PRODUCT_ROOT_D/admin/plib/scripts/register_apsc_database.php" [ -f "$register_script" ] || die 'can not find "register_apsc_database.php"' local dsn_apsc_host="`get_dsn apsc host`" local dsn_apsc_port="`get_dsn apsc port`" sw_engine_pleskrun "$register_script" --register \ -host "${dsn_apsc_host:-localhost}" \ -port "${dsn_apsc_port:-3306}" \ -database "$apsc_db_name" \ -login "$apsc_db_user" \ -password "$apsc_db_passwd" >> "$product_log" 2>&1 } save_apsc_connection_params_conf() { conf_setval "$prod_conf_t" APS_DB_DRIVER_LIBRARY "$apsc_driver_library" } save_apsc_connection_params() { save_apsc_connection_params_conf || die "store driver library path for APS controller db connection" save_apsc_connection_params_db || die "store APS controller db connection parameters into product db" } check_ini_section_exists() { local section="$1" local file="$2" grep -q "\[$section\]" "$file" >/dev/null 2>&1 } check_odbc_driver_exists() { # We could use odbcinst here, but it wouldn't save us much trouble local driver="$1" check_ini_section_exists "$driver" "$odbc_drivers_conf" } remove_ini_section() { local section="$1" local file="$2" [ -r "$file" ] || return 0 awk "/\[.*\]/ { del = 0 } /\[$section\]/ { del = 1 } ( del != 1 ) { print }" "$file" > "$file.new" && \ mv -f "$file.new" "$file" } remove_odbc_driver() { local driver="$1" remove_ini_section "$driver" "$odbc_drivers_conf" } apsc_try_create_odbc_driver() { check_odbc_driver_exists "$apsc_odbc_driver_name" && return 1 local odbc_mysql_driver64= if echo "$odbc_mysql_driver" | grep -q lib64 2>/dev/null ; then odbc_mysql_driver64="$odbc_mysql_driver" fi cp -f "$odbc_drivers_conf" "$odbc_drivers_conf.new" && \ cat <<EOF >> "$odbc_drivers_conf.new" && mv -f "$odbc_drivers_conf.new" "$odbc_drivers_conf" [$apsc_odbc_driver_name] Description = MySQL driver for Plesk Driver = $odbc_mysql_driver Setup = FileUsage = 1 Driver64 = $odbc_mysql_driver64 Setup64 = UsageCount = 1 EOF local rc="$?" [ "$rc" -eq 0 ] || rm -f "$odbc_drivers_conf.new" return "$rc" } apsc_modify_odbc_driver() { # just remove the one that bothers us remove_odbc_driver "$apsc_odbc_driver_name" } check_apsc_installed() { local db_name=`db_read_misc_attr "aps_database"` [ -n "$db_name" ] || return 1 db_test_database "$db_name" } odbc_test_connection() { # This function should be implemented via sw-engine. Until then skip the check. p_echo "APS db accessibility check was skipped." return 0 } install_apsc() { apsc_detect_odbc_driver if ! check_apsc_installed; then inten="set up APS controller database" echo_try "$inten" local dsn_apsc_dbname="`get_dsn apsc dbname`" local dsn_apsc_username="`get_dsn apsc username`" local dsn_apsc_password="`get_dsn apsc password`" apsc_db_name_base="${dsn_apsc_dbname:-apsc}" apsc_db_name="$apsc_db_name_base" apsc_db_user_base="${dsn_apsc_username:-apsc}" apsc_db_user="$apsc_db_user_base" apsc_db_passwd="${dsn_apsc_password:-`get_random_string 12 'A-Za-z0-9_'`}" if is_remote_db_feature_enabled; then local DB_USER_NAME="$apsc_db_user" local DB_PASSWORD="$apsc_db_passwd" dbi_test_connection || apsc_try_create_db_user || die "create database user for APS controller" apsc_try_create_db && apsc_init_db || die "create database for APS controller" else repeat_until_succeeds apsc_try_create_db apsc_modify_db_name 50 && \ repeat_until_succeeds apsc_try_create_db_user apsc_modify_db_user 50 && \ apsc_init_db || die "create database for APS controller" fi [ -r "$odbc_drivers_conf" ] || touch "$odbc_drivers_conf" apsc_sanity_check_binaries repeat_until_succeeds apsc_try_create_odbc_driver apsc_modify_odbc_driver 50 || die "create ODBC driver configuration" if odbc_test_connection ; then set_apsc_version_params save_apsc_current_db_version "$product_apsc_db_version" save_apsc_connection_params suc else p_echo "Failed to establish test connection. Cleaning up." remove_odbc_driver "$apsc_odbc_driver_name" apsc_remove_db_user || true apsc_remove_db || true die "establish test connection to APS database via ODBC" fi fi } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: # For upgrade we use admin credentials instead of apsc since password for the # latter might be encrypted. apsc_upgrader_repair_arg() { if [ -n "$1" -o -n "$do_repair" ]; then echo "--repair" fi } apsc_upgrader_action_name() { if [ -n "`apsc_upgrader_repair_arg $1`" ]; then echo "upgrade and repair" else echo "upgrade" fi } upgrade_apsc_post_stage() { read_apsc_connection_params_db local action_name="`apsc_upgrader_action_name $1`" local optional_repair_arg="`apsc_upgrader_repair_arg $1`" p_echo "===> Cumulative APS controller $action_name (final stage) has been started." if ! check_apsc_installed; then install_apsc else local apsc_db_version=`read_apsc_current_db_version` cu_run_upgrader "apsc" apsc_upgrader \ --load-config-data apsc_cu_load_config_data --store-config-data apsc_cu_store_config_data \ --commit apsc_upgrader_commit \ --from-version "$apsc_db_version" --stage "post" $optional_repair_arg --update-version fi if [ "$?" -eq 0 ]; then p_echo "===> Cumulative $action_name of APS controller (final stage) has been completed." else pp_echo "===> APS controler database was not upgraded completely. See installation log for details." fi } create_apsc_meta_info_table() { local mysql_db_name="$apsc_db_name" local query=" CREATE TABLE IF NOT EXISTS meta_info ( param VARCHAR(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL PRIMARY KEY , value VARCHAR(2000) CHARACTER SET binary NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;" echo "$query" | dbclient_invoke >> "$product_log" 2>&1 } read_apsc_current_db_version() { local mysql_db_name="$apsc_db_name" db_test_table "meta_info" || return 0 apsc_read_meta_info_attr 'db_version' } save_apsc_current_db_version() { local apsc_db_version="$1" local mysql_db_name="$apsc_db_name" db_test_table "meta_info" || create_apsc_meta_info_table || die "create APSC meta_info DB table" db_do "REPLACE INTO meta_info (param, value) VALUES ('db_version', '$apsc_db_version')" } apsc_read_meta_info_attr() { db_select "SELECT value FROM meta_info WHERE param = '$1'" echo "$db_select_output" } apsc_cu_load_config_data() { local upgrader_name="$1" local mysql_db_name="$apsc_db_name" [ "$upgrader_name" = "apsc" ] || return 0 db_test_table "meta_info" || return 0 cu_db_current_timestamp="` apsc_read_meta_info_attr 'cu_current_timestamp'`" cu_db_continue_from="` apsc_read_meta_info_attr 'cu_continue_from'`" cu_db_failed_actions_list="`apsc_read_meta_info_attr 'cu_failed_actions_list'`" cu_db_latest_timestamp="` apsc_read_meta_info_attr 'cu_latest_timestamp'`" } apsc_cu_store_config_data() { local upgrader_name="$1" local mysql_db_name="$apsc_db_name" [ "$upgrader_name" = "apsc" ] || return 0 db_test_table "meta_info" || create_apsc_meta_info_table || die "create APSC meta_info DB table" db_do "REPLACE INTO meta_info (param, value) VALUES \ ('cu_current_timestamp', '$cu_db_current_timestamp'), \ ('cu_continue_from', '$cu_db_continue_from'), \ ('cu_failed_actions_list', '$cu_db_failed_actions_list'), \ ('cu_latest_timestamp', '$cu_db_latest_timestamp')" } apsc_upgrader_commit() { set_apsc_version_params save_apsc_current_db_version "$product_apsc_db_version" } apsc_upgrader() { local ver="cu_check_version" local files="cu_check_stage post" local do="cu_do" cu_start_upgrade_from_version 017009011 # no upgrade actions anymore, see also set_apsc_version_params # DO NOT MODIFY ANYTHING BELOW THIS LINE! (without approval of review from the entire Linux team) call_optional_function dev_apsc_upgrader cu_verify_latest_timestamp_lt '2015/08/14 12:00:00' } # ----- APSC upgrade routines start here ----- # For a function to be recognized as an APSC db upgrader, you should call it from apsc_upgrader() # with appropriate "tags", e.g.: # $ts "2012/11/12 15:08:45" $do APSC_DB_UPGRADE_FUNCTION_NAME # where "2012/11/12 15:08:45" is a timestamp that is produced by `date '+%Y/%m/%d %H:%M:%0S'` # or `date '+%Y%m%d%H%M%0S'` or any other sane format with the same order of format specifiers. # # Please note that to safely use '$ts' here, one should call 'cu_upgrader_conf_init "apsc" ...' # somewhere during APSC DB installation. # # Upgrade function names may be arbitrary, but let's prefix them with APSC_DB_UPGRADE_FUNCTION_. apsc_odbcinst_upgrade_18_0_20() { : } # ----- APSC upgrade routines end here ----- ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # Backup manager tables will be managed by plesk pbm_create_certificate() { local inten="generate certificate for backup signing" [ -x "$PRODUCT_ROOT_D/admin/sbin/backup_sign" ] || return 0 local bu_cert_id="`db_read_misc_attr 'bu_cert_id'`" if [ -n "$bu_cert_id" ]; then return fi echo_try $inten if ! $PRODUCT_ROOT_D/admin/sbin/backup_sign generate >> $product_log 2>&1; then warn "backup certificate generation" return 1 fi suc } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # import certificate into database when installing import_default_certificate() { local default_certificate_id="`db_read_misc_attr 'default_certificate_id'`" if [ -z "$default_certificate_id" ]; then # Update misc table if needed echo_try "import default certificate" import_certificate "$certificate_file" "default certificate" db_do "REPLACE INTO misc set param='default_certificate_id', val='$last_certificate_id'" default_certificate_id="$last_certificate_id" suc fi local admin_repository_id="`db_read_misc_attr 'cert_rep_id'`" if [ -z "$admin_repository_id" ]; then echo_try "push default certificate into repository" db_select "INSERT INTO CertificateRepositories VALUES();SELECT LAST_INSERT_ID();" admin_repository_id="$db_select_output" db_do "REPLACE INTO Repository SET rep_id='${admin_repository_id}', component_id='${default_certificate_id}'" db_do "REPLACE INTO misc SET param='cert_rep_id', val='${admin_repository_id}'" else # simple certificate insertion to repository db_do "REPLACE INTO Repository SET rep_id='${admin_repository_id}', component_id='${default_certificate_id}'" fi } # import certificate from a file to certificates table # $1 - certoificate file path # $2 - short description of certificate # last_certificate_id variable will contain its identifier import_certificate() { local certificate_file certificate_comment inten certificate_file=$1 certificate_comment=$2 cert_pub_val=`cat ${certificate_file} | awk 'BEGIN { state=0 } { if (state == 0) { if (index($0, "-BEGIN CERTIFICATE-") != 0) state = 1; }; if (state == 1) { print $0; if (index($0, "-END CERTIFICATE-") != 0) state = 2; } }' | perl -n -e 's/([^ A-Za-z0-9])/"%" . sprintf("%02X", unpack("C", $1))/ge; s/ /\+/g; print $_;'` cert_priv_val=`cat ${certificate_file} | awk 'BEGIN { state=0 } { if (state == 0) { if ((index($0, "-BEGIN ") != 0) && (index($0, " PRIVATE KEY-") != 0)) state = 1; }; if (state == 1) { print $0; if ((index($0, "-END ") != 0) && (index($0, " PRIVATE KEY-") != 0)) state = 2; } }' | perl -n -e 's/([^ A-Za-z0-9])/"%" . sprintf("%02X", unpack("C", $1))/ge; s/ /\+/g; print $_;'` # copy certificate file to file repository newcertfile=`mktemp "${PRODUCT_ROOT_D}"/var/certificates/certXXXXXXX` cp "${certificate_file}" "${newcertfile}" || die "place certificate to its directory" chown root:0 "${newcertfile}" chmod 400 "${newcertfile}" newcertfile=`basename ${newcertfile}` db_select "INSERT INTO certificates (pvt_key, cert, name, ca_cert, csr, cert_file, ca_file) VALUES ('${cert_priv_val}', '${cert_pub_val}', '${certificate_comment}', '', '', '${newcertfile}', ''); SELECT LAST_INSERT_ID()" last_certificate_id="$db_select_output" } # vim:ft=sh: ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. run_bl_upgrade() { local inten="execute post install/upgrade actions" if sw_engine_pleskrun $PRODUCT_ROOT_D/admin/plib/Upgrade/upgrade.php "$@" >> "$product_log" 2>&1; then suc else warn "$inten" # reflect error in exit-code in repair mode: [ ! "$1" = "--repair" ] || return 1 fi } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: #courier-imap set_courier_imap_params() { COURIERIMAP_CONFDIR="/etc/courier-imap" COURIERIMAP_PIDPATH="/run" IMAPD_CERT="/usr/share/imapd.pem" POP3D_CERT="/usr/share/pop3d.pem" COURIER_DHPARAMS="/usr/share/dhparams.pem" # Certificate paths for Courier-IMAP <= 3.0.8 OLD_IMAPD_CERT="/usr/share/courier-imap/imapd.pem" OLD_POP3D_CERT="/usr/share/courier-imap/pop3d.pem" COURIER_DELIVER_QUOTA="/usr/bin/deliverquota" courier_imapd_service="courier-imapd" courier_imaps_service="courier-imaps" courier_pop3d_service="courier-pop3d" courier_pop3s_service="courier-pop3s" courier_authdaemon_service="courier-authdaemon" # Service name for Courier-IMAP <= 3.0.8 old_courier_service="courier-imap" } # db_test test_query awk_script # Runs test_query and processes it with awk_script. If the output is # not empty, return 0, otherwise return 1. Hint: supply '1' for # awk_script to test just for the presence of any output. db_test() { local any_db= eval `sh_get_args '--any-db) any_db=yes;;'` local test_query="$1" local awk_script="$2" local output if [ -n "$any_db" ]; then output="`mysql_raw_anydb -e \"$test_query\" 2>>\"$product_log\"`" else output="`mysql_raw -e \"$test_query\" 2>>\"$product_log\"`" fi local status=$? if [ "$status" -ne 0 ]; then p_echo "$output" die "run the following SQL query: $1" fi echo -n "$output" | awk -F '\t' -- "$awk_script" | test `wc -l` -ne 0 } # db_do [--inten <inten>] query # Runs query. If it fails, die # the inten string describes the query reason (to make finding the bug simpler) db_do() { local desc="execute SQL query" eval `sh_get_args '--inten) desc=" (to $2)"; shift;;'` if [ "$db_fix_check_stage" = "yes" ]; then return fi local query="$1" dbclient_invoke -e "$query" >>"$product_log" 2>&1 || die "$desc, the query was: $query" } # db_select <query> # runs <query> via mysql_raw # writes output to db_select_output # if query fails, output errors and return 1 db_select() { local desc="execute SQL query" local query="$1" local output output="`mysql_raw -e \"$query\" 2>>\"$product_log\"`" local status="$?" if [ "$status" -ne "0" ]; then p_echo "$output" die "run the following SQL query: $query" fi db_select_output="$output" return 0 } # db_test_database database # Returns 0 if the database exists db_test_database() { local database="$1" local mysql_db_name="mysql" db_test "SHOW DATABASES" "\$1 == \"$database\"" } # db_test_table table # Returns 0 if the table exists db_test_table() { local table="$1" db_test "SHOW TABLES LIKE '$table'" 1 } dbi_test_connection() { local inten="establish test connection" local mysql_user="--user=$DB_USER_NAME" local mysql_passwd="$DB_PASSWORD" local rc= echo_try "$inten" ( echo '' | mysql_direct ) rc=$? [ "$rc" = "0" ] && p_echo "." || p_echo "!" return $rc } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: db_read_misc_attr() { db_select "SELECT val FROM misc WHERE param = '$1'" echo "$db_select_output" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. deferred_httpd_reconfigure() { touch "/var/lock/plesk_deferred_httpd_reconfigure" } deferred_httpd_reconfigure_all() { touch "/var/lock/plesk_deferred_httpd_reconfigure_all" } deferred_apache_restart() { touch "/var/lock/plesk_deferred_apache_restart" } deferred_nginx_restart() { touch "/var/lock/plesk_deferred_nginx_restart" } deferred_swcpserver_restart() { touch "/var/lock/plesk_deferred_swcpserver_restart" } deferred_systemctl_daemon_reload() { touch "/var/lock/plesk_deferred_systemctl_daemon_reload" } deferred_nginx_configure() { touch "/var/lock/plesk_deferred_nginx_configure" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: set_dovecot_params() { DOVECOT_CONFDIR="/etc/dovecot" DOVECOT_INCLUDE_DIR="/etc/dovecot/conf.d" DOVECOT_DIST_CONFDIR="/usr/share/doc/plesk-dovecot/dist-config" DOVECOT_CERT_DIR="/etc/dovecot/private" DOVECOT_CERT="$DOVECOT_CERT_DIR/ssl-cert-and-key.pem" DOVECOT_INTERNAL_USERGROUP="dovecot" DOVECOT_LOGIN_USERGROUP="dovenull" DOVECOT_LDA="/usr/lib/dovecot/dovecot-lda" dovecot_service="dovecot" } is_dovecot_active() { # For now it's rather "is Dovecot installed?". Check is the same as in backend. set_dovecot_params [ -x "$DOVECOT_LDA" ] } is_remote_db_feature_enabled() { [ -s "/etc/psa/private/dsn.ini" ] } get_dsn() { local section="$1" local param="$2" ! is_remote_db_feature_enabled || get_ini_conf_var "/etc/psa/private/dsn.ini" "$section" "$param" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh # Usage: pleskrc <service> <action> pleskrc() { [ 2 -le $# ] || die "Not enough arguments" local service_name=${1//[-.@]/_} local action=$2 local ret=0 local inten shift shift # Now check redefined functions if test "$machine" = "linux" && is_function "${service_name}_${action}_${machine}_${linux_distr}"; then "${service_name}_${action}_${machine}_${linux_distr}" "$@" return $? elif is_function "${service_name}_${action}_${machine}"; then "${service_name}_${action}_${machine}" "$@" return $? elif is_function "${service_name}_${action}"; then "${service_name}_${action}" "$@" return $? fi # Not redefined - call default action eval "service=\$${service_name}_service" [ -n "$service" ] || die "$action $service_name service (Empty service name for '$service_name')" if [ "$action" = "name" ]; then echo "${service}.service" return 0 fi inten="$action service $service" pleskrc_is_failure_for_action_ok "$action" || echo_try "$inten" if [ -x "/bin/systemctl" -a "$do_upgrade" = "1" -a ! -f "/var/lock/parallels-panel-bootstrapper-running.lock" -a -z "$SYSTEMD_DAEMON_RELOADED" ]; then # reload systemd units if requested from an upgrade package script - in case a unit was changed /bin/systemctl daemon-reload SYSTEMD_DAEMON_RELOADED="yes" fi service_ctl "$action" "$service" "$service_name" ret="$?" pleskrc_is_failure_for_action_ok "$action" || { if [ "$ret" -eq 0 ]; then suc else if [ -x "/bin/systemctl" ]; then p_echo "`/bin/systemctl -l status \"${service}.service\" | awk 'BEGIN {s=0} s==1 {s=2} /^$/ {s=1} s==2 {print}'`" fi warn "$inten failed" fi } return $ret } pleskrc_is_failure_for_action_ok() { local action="$1" case "$action" in status|exists|is-active|is-enabled|is-failed) return 0 ;; esac return 1 } # NOTE: # Function service_ctl is just helper for pleskrc(). # Do not call it directly, use pleskrc()!!! service_ctl() { local action=$1 local service=$2 local service_name=$3 if [ "$action" != "exists" ]; then _service_exec $service exists if [ "$?" != "0" ]; then p_echo "attempt to ${inten} - service doesn't exist (missing unit file or not executable control script)" return 1 fi fi case "$action" in start) pleskrc "$service_name" status || _service_exec "$service" "$action" ;; stop) ! pleskrc "$service_name" status || _service_exec "$service" "$action" ;; restart) if pleskrc "$service_name" status; then _service_exec "$service" "$action" else _service_exec "$service" start fi ;; reload) ! pleskrc "$service_name" status || _service_exec "$service" "$action" ;; status) _service_exec "$service" status ;; try-restart) if [ -x "/bin/systemctl" ]; then _service_exec "$service" "$action" else ! pleskrc "$service_name" status || _service_exec "$service" "restart" fi ;; try-reload) ! pleskrc "$service_name" status || _service_exec "$service" "reload" ;; reload-or-restart) if [ -x "/bin/systemctl" ]; then _service_exec "$service" "$action" elif pleskrc "$service_name" status; then _service_exec "$service" "reload" else _service_exec "$service" "start" fi ;; *) _service_exec "$service" "$action" ;; esac >> "$product_log" } _service_exec() { # Keep in sync with pylibplesk/plesk_service.py local service=$1 local action=$2 local action_cmd local sysvinit_service="/etc/init.d/$service" if [ -x "/bin/systemctl" ]; then case "${action}" in exists) if /bin/systemctl cat "$service.service" >/dev/null 2>&1; then return 0 # systemd unit elif [ -f "/lib/systemd/system/$service.service" ]; then /bin/systemctl daemon-reload return 0 # systemd unit which exists but was changed and has not been reloaded before elif [ -x "$sysvinit_service" ]; then return 0 # sysvinit compat fi return 1 # not found ;; status) action="is-active" ;; reload|graceful) action='reload-or-try-restart' ;; esac /bin/systemctl "$action" "${service}.service" else warn "Cannot $action $service on this system: no executable /bin/systemctl" return 1 fi } is_function() { local type_output=$(type -t "$1") test "X${type_output}" = "Xfunction" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. form_undotted_version() { local major=`echo $1 | awk -F. '{ print $1 }'` local minor=`echo $1 | awk -F. '{ print $2 }'` local patch=`echo $1 | awk -F. '{ print $3 }'` if [ -z "$major" -o -z "$minor" -o -z "$patch" ]; then return 1 fi printf "%03d%03d%03d" $major $minor $patch } get_prev_version() { get_product_versions # Debian package system reports previous version as script argument [ -n "$prev_version" ] || prev_version="$product_prev_version" prev_short_version=`form_undotted_version ${prev_version}` if [ "X${prev_version}" = "X" -o -z "$prev_short_version" ]; then p_echo " Unable to determine version of ${PRODUCT_NAME} installed. It is impossible" p_echo " to upgrade ${PRODUCT_NAME} to version $product_version." p_echo " To try to install a new version without saving data remove '$1' directory" p_echo " manually. Please backup all data before removing it if necessary." exit 1 fi if [ "$prev_short_version" -ge "017000017" ]; then return 0 fi # version is unknown for upgrader. let the user to know the propblem p_echo " Unable to upgrade. You have $prev_version version installed," p_echo " but the upgrader is incompatible with this version." p_echo " Please consider using migration instead of an upgrade." exit 1 } check_product_root() { if [ ! -s "$PRODUCT_ROOT_D/version" -a ! -s "$PRODUCT_ROOT_D/version.upg" ]; then do_upgrade=0 get_product_versions else get_prev_version ${PRODUCT_ROOT_D} # Normally do_upgrade is set by package scripts. # In case of bootstrapper it may not be set yet. if [ -z "$do_upgrade" ]; then if [ -s "$PRODUCT_ROOT_D/version.upg" ]; then do_upgrade=1 else do_upgrade=0 fi fi fi } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # echo message to product log and console (always visible) pp_echo() { if [ -n "$product_log" ] ; then echo "$@" >> "$product_log" 2>&1 fi echo "$@" >&2 } # 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 } die() { report_problem "fatal" "ERROR while trying to $@" printf "Check the error reason" >&2 p_see_product_log echo ", fix and try again" >&2 selinux_close exit 1 } simply_die() { report_problem "fatal" "$@" exit 1 } 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" } echo_try() { msg="$*" pnnl_echo " Trying to $msg... " } suc() { p_echo "done" } write_structured_report() { # Write json error report into file $PLESK_INSTALLER_ERROR_REPORT if specified. # @params are tags in format "key=value" # Report body (human readable information) is read from stdin. local report_file="$PLESK_INSTALLER_ERROR_REPORT" [ -n "$report_file" ] || return 0 local python_bin= for bin in "/opt/psa/bin/python" "/usr/local/psa/bin/python" "/usr/bin/python2" "/usr/libexec/platform-python" "/usr/bin/python3"; do if [ -x "$bin" ]; then python_bin="$bin" break fi done if [ ! -x "$python_bin" ]; then p_echo "Cannot write structured error report: python interpreter not found." return 1 fi "$python_bin" -c 'import sys, json report_file = sys.argv[1] error = sys.stdin.read() data = { "error": error, } for tag in sys.argv[2:]: k, v = tag.split("=", 1) data[k] = v with open(report_file, "a") as f: json.dump(data, f) f.write("\n") ' "$report_file" "date=$(date --utc --iso-8601=ns)" "$@" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. get_ip_address_list() { local inet_str="inet addr" if test -x /sbin/ip ; then /sbin/ip addr list | grep -v 'inet6' | grep -v '127\.[0-9]*\.[0-9]*\.[0-9]*' | sed -n -e 's/^ *inet \([0-9\.]*\).*$/\1/ p' else $ifconfig | grep "$inet_str" | grep -v "inet6" | sed -e "s/$inet_str.\([0-9.]*\).*/\1/g" | awk '{print $1}' - | grep -v '^127\.[0-9]*\.[0-9]*\.[0-9]*$' fi } get_domain() { local hostname_original="`(hostname -f || hostname) 2>>$product_log`" host="$hostname_original" local try_dot=`echo "$host" | sed -e 's/^[^.]*\..*$/\./'` case "$try_dot" in .) domain="`echo "$host" | sed -e 's/^[^.]*\.//'`" host="`echo $host | sed -e 's/\..*//'`" ;; *) domain="`dnsdomainname 2>>$product_log`" ;; esac domain=`echo "$domain" | sed 's/\.$//'` if [ -n "$host" -a -n "$domain" ]; then fullhost="${host}.${domain}" return fi [ -n "$host" ] || host="localhost" [ -n "$domain" ] || domain="localdomain" p_echo "Hostname $hostname_original is not FQDN, using $host.$domain instead" non_fqdn_hostname="1" fullhost="${host}.${domain}" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. reexec_with_clean_env() { # Usage: call this function as 'reexec_with_clean_env "$@"' at the start of a script. # Don't use with scripts that require sensitive environment variables. # Don't put the call under any input/output redirection. # Purpose: make sure the script is executed with a sane environment. local lc="`get_default_locale`" export LANG="$lc" LC_MESSAGES="$lc" LC_ALL="$lc" export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin umask 022 PLESK_SCRIPT_COMMAND_LINE="$0 $*" [ -z "$PLESK_INSTALLER_ENV_CLEANED" ] || { unset PLESK_INSTALLER_ENV_CLEANED; return 0; } [ -n "$BASH" ] || exec /bin/bash "$0" "$@" # N.B.: the following code requires Bash. On Dash it would cause syntax error upon parse w/o eval. eval ' local extra_vars=() # list of variables to preserve for var in "${!PLESK_@}"; do # enumerate all PLESK_* variables extra_vars+=("$var=${!var}") done extra_vars+=("PLESK_INSTALLER_ENV_CLEANED=1") # Exec self with clean env except for extra_vars, shell opts, and arguments. exec /usr/bin/env -i "${extra_vars[@]}" /bin/bash ${-:+-$-} "$0" "$@" || { echo "Failed to reexec self ($0) with clean environment" >&2 exit 91 # Just some relatively unique error code } ' } get_default_locale() { # Note that CentOS 7 typically doesn't have C.UTF-8 for lc in "C.UTF-8" "en_US.UTF-8" "C"; do if [ -z "`LC_ALL=$lc locale 2>&1 >/dev/null`" ]; then echo "$lc" return 0 fi done echo "C" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh mk_backup() { local target dup opts target="$1" dup="$2" opts="$3" if [ -L "$target" ]; then rm "$target" elif [ -$opts "$target" ]; then if [ ! -$opts "$target.$product_suffo" ]; then case "$dup" in mv) mv -f $target $target.$product_suffo || die "mv -f $target $target.$product_suffo" ;; cp) cp -fp $target $target.$product_suffo || die "cp -fp $target $target.$product_suffo" ;; *) p_echo " mk_backup: wrong option -- must be 'cp' or 'mv'" die "mk_backup" ;; esac else case "$dup" in mv) mv -f $target $target.$product_suff || die "mv -f $target $target.$product_suff" ;; cp) cp -fp $target $target.$product_suff || die "cp -fp $target $target.$product_suff" ;; *) p_echo " mk_backup: wrong option -- must be 'cp' or 'mv'" die "mk_backup" ;; esac fi else case "$opts" in f|d) ;; *) p_echo " mk_backup: wrong option -- must be 'f' or 'd'" die "mk_backup" ;; esac fi } # accumulates chown and chmod set_ac() { local u_owner g_owner perms node u_owner="$1" g_owner="$2" perms="$3" node="$4" # A very small optimization - replacing of two execs by one, # it works only if the following conditions are observed: # - u_owner is username (not UID); # - g_owner is group (not GID); # - perms is in octal mode. # If some conditions aren't observed, # optimization doesn't work, # but it doesn't break function [ "$(stat -c '%U:%G 0%a' $node)" != "$u_owner:$g_owner $perms" ] || return 0 chown $u_owner:$g_owner $node || die "chown $u_owner:$g_owner $node" chmod $perms $node || die "chmod $perms $node" } 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" } call_optional_function() { local type_output="`LC_ALL=C type \"$1\" 2>/dev/null | head -n 1`" case "$type_output" in *function) "$@" ;; *) return 0 ;; esac } sh_get_args() { echo 'while true; do case "$1" in '"$1"'*) break;; esac; shift; done' } ### the function similar to awk -F'$fs' 'print $N' get_narg_fs() { local IFS="$2" get_narg $3 $1 } get_narg() { shift $1 2>/dev/null || return 0 echo $1 } get_random_string() { local str_length="$1" local str_symbols="$2" if [ -x "$PRODUCT_ROOT_D/admin/sbin/random_str" -a -z "$str_symbols" ]; then "$PRODUCT_ROOT_D/admin/sbin/random_str" "$str_length" else # random_str utility may be unavailable in pre phase if [ -z "$str_length" ]; then str_length="14" fi if [ -z "$str_symbols" ]; then str_symbols="A-Za-z0-9_" fi < /dev/urandom tr -dc "$str_symbols" 2>/dev/null | head -c "$str_length" 2>/dev/null fi } sequence() { if type seq >/dev/null 2>&1; then seq $* elif type jot >/dev/null 2>&1; then jot $* else die "Unable to find seq or jot command" fi } get_ini_conf_var() { local conf="$1" local section="$2" local param="$3" [ -n "$conf" -a -n "$param" ] || die "get_ini_conf_var(): required parameters missing" local section_empty=0 [ -n "$section" ] || section_empty=1 perl -n -e 'BEGIN { $insect='$section_empty' } next if (/^\s*;/); $insect=0 if (/^\s*\[.*\]/); $insect=1 if (/^\s*\['$section'\]/); $val = $2, $val =~ s/\s+$//, print $val . "\n" if ($insect && /^\s*('$param')\s*=\s*([^;\n]*)(;.*)?$/);' $conf | head -n 1 } sw_engine_pleskrun() { if [ -x $PRODUCT_ROOT_D/bin/sw-engine-pleskrun ]; then $PRODUCT_ROOT_D/bin/sw-engine-pleskrun "$@" elif [ -x "/usr/bin/sw-engine" ]; then local args if [ -e "$PRODUCT_ROOT_D/admin/conf/php.ini" ]; then args="-c $PRODUCT_ROOT_D/admin/conf/php.ini" else args="-d psasem.semfile=\"$PRODUCT_ROOT_D/var/psasem.sem\"" fi /usr/bin/sw-engine $args "$@" else echo "No Plesk PHP interpreter found" return 1 fi } install_default_license_key() { pnnl_echo "install default license key ..." # check OF-key if sw_engine_pleskrun -r 'exit(of_get_key_by_product("plesk-unix") === false ? 1 :0);'; then p_echo "not needed, OF license found" return fi # check Unified key if sw_engine_pleskrun -r 'exit(of_get_key_by_product("plesk-unified") === false ? 1 :0);'; then p_echo "not needed, unified license found" return fi local dest_dir="/etc/sw/keys/keys" local key=plesk-default-key.xml [ ! -s "$dest_dir/$key" ] || return 0 cp -f "${PRODUCT_ROOT_D}/var/$key" "$dest_dir/" chown root:swkey-data "$dest_dir/$key" chmod 0660 "$dest_dir/$key" suc } configure_ssl_ciphers_protocols() { [ -x "$PRODUCT_ROOT_D/admin/sbin/sslmng" ] || return 0 local inten="configure SSL ciphers and protocols for: $*" echo_try "$inten" $PRODUCT_ROOT_D/admin/sbin/sslmng --strong-dh >>$product_log 2>&1 || warn "$inten (strong dh configuration)" $PRODUCT_ROOT_D/admin/sbin/sslmng --set >>$product_log 2>&1 && suc && return 0 warn "$inten" return 1 } configure_service_ssl_ciphers_protocols() { [ -x "$PRODUCT_ROOT_D/admin/sbin/sslmng" ] || return 0 local inten="configure SSL ciphers and protocols for: $1" echo_try "$inten" "$PRODUCT_ROOT_D/admin/sbin/sslmng" --service="$1" --set >> "$product_log" 2>&1 && suc && return 0 warn "$inten" return 1 } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. label_file_once() { local _fname _mess _messLines _fileHead _tmpFile test $# -eq 2 || die 'label_file_once() not enough arguments' _fname="${1}" _mess="${2}" inten="add header to file ${_fname}" echo_try $inten if [ ! -f "${_fname}" ]; then p_echo "can not add header to file ${_fname} : file does not exist" return fi _messLines=`printf "${_mess}" | wc -l | awk '{print $NF}'` _tmpFile=`basename "${_fname}"` _tmpFile=`mktemp "/tmp/${_tmpFile}.XXXXXX"` || die "can not create temporary file" head -n ${_messLines} "${_fname}" > "${_tmpFile}" if printf "${_mess}" | diff -q - "${_tmpFile}" 1>/dev/null; then p_echo "file ${_fname} already contains required header" rm -f "${_tmpFile}" return fi rm -f "${_tmpFile}" # preserve permissions, not optimal, need to rework cp -p "${_fname}" "${_fname}.tmp" printf "${_mess}" | cat - "${_fname}" > "${_fname}.tmp" cp -f "${_fname}.tmp" "${_fname}" rm -f "${_fname}.tmp" p_echo "completed successfully" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. #-*- vim:syntax=sh product_log_name_ex() { local aux_descr="$1" local action="${CUSTOM_LOG_ACTION_NAME-installation}" if [ -n "$aux_descr" ]; then aux_descr="_${aux_descr}" fi if [ -n "$CUSTOM_LOG_NAME" ]; then echo "${CUSTOM_LOG_NAME}${action:+_$action}${aux_descr}.log" else get_product_versions echo "plesk_${product_this_version}${action:+_$action}${aux_descr}.log" fi } product_log_name() { product_log_name_ex } product_problems_log_name() { product_log_name_ex "problems" } 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 } cleanup_problems_log() { [ -f "$product_problems_log" ] || return 0 touch "$product_problems_log.tmp" chmod 0600 "$product_problems_log.tmp" awk 'BEGIN { st = "" } /^START/ && (st ~ /^START/) { print st; } /^START/ { st=$0; next } /^STOP/ && (st ~ /^START/) { st=""; next } (st != "") { print st; st="" } { print } ' "$product_problems_log" > "$product_problems_log.tmp" && \ mv -f "$product_problems_log.tmp" "$product_problems_log" || \ rm -f "$product_problems_log.tmp" if [ ! -s "$product_problems_log" ]; then rm -f "$product_problems_log" fi } mktemp_log() { local logname="$1" local dir="$2" if [ "${logname:0:1}" != "/" ]; then logname="$dir/$logname" fi dir="`dirname $logname`" if [ ! -d "$dir" ]; then mkdir -p "$dir" || { echo "Unable to create log directory : $dir"; exit 1; } if [ "$EUID" -eq "0" ]; then set_ac root root 0700 "$dir" fi fi if [ "${logname%XXX}" != "$logname" ]; then mktemp "$logname" else echo "$logname" fi } log_is_in_dev() { test "${1:0:5}" = "/dev/" } start_writing_logfile() { local logfile="$1" local title="$2" ! log_is_in_dev "$logfile" || return 0 echo "START $title" >> "$logfile" || { echo "Cannot write installation log $logfile" >&2; exit 1; } [ "$EUID" -ne "0" ] || set_ac root root 0600 "$logfile" } log_start() { true product_log_name product_problems_log_name mktemp_log local title="$1" local custom_log="$2" local custom_problems_log="$3" local product_log_dir="/var/log/plesk/install" product_log="$product_log_dir/`product_log_name`" product_problems_log="$product_log_dir/`product_problems_log_name`" problems_occured=0 # init product log [ ! -n "$custom_log" ] || product_log="$custom_log" product_log=`mktemp_log "$product_log" "$product_log_dir"` # init problems log if [ -n "$custom_problems_log" ]; then product_problems_log=`mktemp_log "$custom_problems_log" "$product_log_dir"` elif [ -n "$custom_log" ]; then product_problems_log="$product_log" else product_problems_log=`mktemp_log "$product_problems_log" "$product_log_dir"` fi # write starting message into logs start_writing_logfile "$product_log" "$title" if [ "$product_log" != "$product_problems_log" ]; then start_writing_logfile "$product_problems_log" "$title" fi is_function profiler_setup && profiler_setup "$title" || : } log_stop() { local title="$1" local subject="$2" if [ "$product_log" = "$product_problems_log" ] || \ log_is_in_dev "$product_problems_log"; then [ -e "$product_log" ] && echo "STOP $title" >>"$product_log" is_function profiler_stop && profiler_stop || : return fi if [ -z "$subject" ]; then subject="[${title}]" fi # check if problems are non-empty, check for problems_occured local status local problem_lines="`problems_log_tail | wc -l`" if [ "$problem_lines" -eq 0 ]; then status="completed successfully" else if [ $problems_occured -ne 0 ]; then status="failed" else status="completed with warnings" fi fi if [ -e "$product_log" ]; then p_echo p_echo "**** $subject $status." p_echo fi if [ "$problem_lines" -ne 0 ]; then [ ! -e "$product_log" ] || problems_log_tail >>"$product_log" 2>&1 problems_log_tail fi [ ! -e "$product_log" ] || echo "STOP $title" >>"$product_log" if [ $problems_occured -ne 0 ]; then echo "STOP $title: PROBLEMS FOUND" >>"$product_problems_log" else [ ! -s "$product_problems_log" ] || echo "STOP $title: OK" >>"$product_problems_log" fi if [ "X${PLESK_INSTALLER_KEEP_PROBLEMS_LOG}" = "X" ]; then cleanup_problems_log fi is_function profiler_stop && profiler_stop || : } package_script_begin() { if [ -z "$process" ]; then if [ "$do_reconfigure" = "1" ]; then process="reconfiguration" elif [ "$do_upgrade" = "1" ]; then process="upgrade" elif [ -n "$PACKAGE_TRIGGERING_NAMES" ]; then process="trigger" else process="installation" fi fi log_start "`package_script_log_title`" set_error_report_context "`package_script_log_title`. `report_context_action`" product_default_conf initial_conf set_common_params read_conf } package_script_end() { packagemng_set_dirty_flag log_stop "`package_script_log_title`" "Package $PACKAGE_NAME scriptlet" } package_script_log_title() { local stage case "$do_upgrade" in 0) stage="installing";; 1) stage="upgrading";; *) stage="installing";; esac [ -z "$PACKAGE_TRIGGERING_NAMES" ] || stage="triggered (by $PACKAGE_TRIGGERING_NAMES)" echo "$PACKAGE_NAME-$PACKAGE_VERSION $stage AT `date`" } report_context_action() { local action get_product_versions if [ "X$do_upgrade" = "X1" -o "$product_prev_version" != "$product_this_version" ]; then action="Doing $product_name upgrade from $product_prev_version to $product_this_version" else action="Doing $product_name $product_this_version installation" fi echo "$action" } # <fun> NAME VERSION <function name 1> [function name 2] ... [function name N] package_script_safe_run() { PACKAGE_NAME="$1" PACKAGE_VERSION="$2" shift 2 local func_list="$@" # Run functions in subshell because package scriptlet must exit with status code indicating success. # Postpone package problem report till post-install package scriptlet (package_script_safe_run_with_report) package_script_begin ( transaction_begin transaction_add_rollback_action "package_script_end" transaction_add_rollback_action "set_upgrade_failure_flag" # Execute all mentioned functions for func in $func_list; do call_optional_function "$func" done transaction_add_commit_action "package_script_end" transaction_commit ) } # <fun> NAME VERSION <function name 1> [function name 2] ... [function name N] package_script_safe_run_with_report() { package_script_safe_run "$@" local ret=$? # package problem report was shown if necessary in log_stop() if [ $ret -ne 0 ]; then # fatal error # show re-start recommendation only if bootstrapper mode is disabled (no AI, no EZ) if [ ! -f /tmp/pp-bootstrapper-mode.flag ]; then p_echo "To complete product installation one should run bootstrapper script:" p_echo " $PRODUCT_BOOTSTRAPPER_DIR/bootstrapper.sh post-install" p_echo fi fi } set_upgrade_failure_flag() { touch /var/lock/parallels-panel-upgrade-failure.flag } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. get_pid() { local i local ex_f="$1" local opt="$2" local owner="$3" local min_num="1" local ps_long="ps axuw" # Use pidof by default, bug 121868, except for FreeBSD - 140182 if type pidof >/dev/null 2>&1 && [ "$os" != "BSD" ]; then for pid in `pidof -o $$ -o $PPID -o %PPID -x $ex_f`; do # Check for owner [ "$opt" = "true" -a "$owner" != "`ps -p $pid -o ruser=`" ] && continue min_num=$pid break done common_var=$min_num return $min_num fi case "$opt" in false) for i in `$ps_long | grep $ex_f | grep -v grep | grep -v httpsdctl | grep -v apachectl | awk '{print $2}' -`; do min_num=$i break done ;; true) for i in `$ps_long | grep $ex_f | grep -v grep | grep -v httpsdctl | grep -v apachectl | grep "$owner" | awk '{print $2}' -`; do min_num=$i break done ;; *) p_echo "get_pid: wrong parameter" die "get_pid $ex_f $opt $owner" ;; esac common_var=$min_num return $min_num } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. deinstall_python_modules() { echo_try "remove compiled Python modules" for path in "$@"; do { if [ -f "$path" ] && [ -n "`echo \"$path\" | sed -n -e '/.py$/p'`" ]; then rm -f "${path}c" "${path}o" elif [ -d "$path" ]; then find "$path"/* -name '*.py[co]' -type f -delete rmdir --ignore-fail-on-non-empty "$path" elif [ -e "$path" ]; then echo "Don't know how to remove Python modules from '$path', skipped" fi } >> $product_log 2>&1 done suc } ### 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 <?xml version="1.0" encoding="UTF-8" ?> <error> <source>$report_source</source> <severity>$severity</severity> <datetime>`date --iso-8601=seconds`</datetime> <component>$report_component</component> <summary><![CDATA[`echo "$summary" | sed -e 's/\]\]>/] ]>/g'`]]></summary> <message encoding="base64">`construct_report_message | base64`</message> <additional_info> <component_params encoding="base64">$report_params</component_params> <code encoding="base64">`construct_report_code | base64`</code> <debug encoding="base64">`construct_report_debug | base64`</debug> <environment encoding="base64">$report_environment</environment> <update_ticket>$update_ticket</update_ticket> </additional_info> </error> 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_context() { report_context="$*" } 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' } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # # Support for runtime patching of shell scripts (including utilities and package scripts). # # --- Service functions --- # Load and apply a patch in a relatively safe way rp_safe_load_patch() { local patch_file="$1" echo_try "load shell patch '$patch_file'" /bin/sh -n "$RP_BASEDIR/$patch_file" && { . "$RP_BASEDIR/$patch_file" RP_LOADED_PATCHES="$RP_LOADED_PATCHES $patch_file" } && suc } # Apply patches specific to the current context (e.g., depending on utility basename or package name) # This is currently not implemented. This may be overriden by "spark". rp_patch_runtime_context_specific() { : } # --- Main entry points --- rp_patch_runtime() { # List of loaded patch files RP_LOADED_PATCHES= local RP_BASEDIR="$PRODUCT_BOOTSTRAPPER_DIR/rp" [ -d "$RP_BASEDIR" ] || return 0 if [ -r "$RP_BASEDIR/spark" ]; then rp_safe_load_patch "spark" fi call_optional_function rp_patch_runtime_context_specific "$@" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. transaction_begin() { [ -n "$TRANSACTION_STARTED" ] && die "Another transaction in progress!" TRANSACTION_STARTED="true" TRANSACTION_ROLLBACK_FUNCS= TRANSACTION_COMMIT_FUNCS= local transaction_autocommit="$1" if [ -n "$transaction_autocommit" ]; then trap "transaction_commit_auto" EXIT trap "transaction_rollback" HUP PIPE INT QUIT TERM else trap "transaction_rollback" HUP PIPE INT QUIT TERM EXIT fi } transaction_rollback() { TRANSACTION_RETURN_CODE="${TRANSACTION_RETURN_CODE:-$?}" [ -z "$TRANSACTION_STARTED" ] && die "Transaction is not started!" # perform rollback actions local f for f in ${TRANSACTION_ROLLBACK_FUNCS}; do "$f" done TRANSACTION_STARTED= TRANSACTION_ROLLBACK_FUNCS= TRANSACTION_COMMIT_FUNCS= trap - HUP PIPE INT QUIT TERM EXIT exit 1 } transaction_commit() { TRANSACTION_RETURN_CODE="${TRANSACTION_RETURN_CODE:-$?}" [ -z "$TRANSACTION_STARTED" ] && die "Transaction is not started!" # perform commit actions local f for f in ${TRANSACTION_COMMIT_FUNCS}; do "$f" done TRANSACTION_STARTED= TRANSACTION_ROLLBACK_FUNCS= TRANSACTION_COMMIT_FUNCS= trap - HUP PIPE INT QUIT TERM EXIT } transaction_commit_auto() { TRANSACTION_RETURN_CODE="$?" if [ "$TRANSACTION_RETURN_CODE" -eq 0 ]; then transaction_commit "$@" else transaction_rollback "$@" fi } transaction_add_rollback_action() { [ -z "$TRANSACTION_STARTED" ] && die "Transaction is not started!" # LIFO rollback order [ -z "$TRANSACTION_ROLLBACK_FUNCS" ] \ && TRANSACTION_ROLLBACK_FUNCS="$1" \ || TRANSACTION_ROLLBACK_FUNCS="$1 $TRANSACTION_ROLLBACK_FUNCS" } transaction_remove_rollback_action() { [ -z "$TRANSACTION_STARTED" ] && die "Transaction is not started!" # Preserves order of remaining records TRANSACTION_ROLLBACK_FUNCS=`remove_from_array "$1" "$TRANSACTION_ROLLBACK_FUNCS"` } transaction_add_commit_action() { [ -z "$TRANSACTION_STARTED" ] && die "Transaction is not started!" # FIFO commit order [ -z "$TRANSACTION_COMMIT_FUNCS" ] \ && TRANSACTION_COMMIT_FUNCS="$1" \ || TRANSACTION_COMMIT_FUNCS="$TRANSACTION_COMMIT_FUNCS $1" } txtdb_set_params() { db_file="" db_owner="" db_group="" db_perms="0644" db_delim=':' db_key_field="1" "$@" [ -n "$db_file" -a -n "$db_delim" ] || die "set txtdb parameters. Check arguments for 'txtdb_*' calls." } txtdb_fix_perms() { [ -n "$db_owner" -a -n "$db_group" ] && chown "$db_owner":"$db_group" "$db_file" [ -n "$db_perms" ] && chmod "$db_perms" "$db_file" : } # Create new empty DB or replace an existing one txtdb_truncate() { local set_params_func="$1" local db_file db_owner db_group db_perms db_delim db_key_field txtdb_set_params "$set_params_func" rm -f "$db_file" && touch "$db_file" && txtdb_fix_perms } # Check whether DB file already exists and is writable txtdb_exists() { local set_params_func="$1" local db_file db_owner db_group db_perms db_delim db_key_field txtdb_set_params "$set_params_func" [ -w "$db_file" ] } txtdb_do_update() { local set_params_func="$1" local awk_script="$2" local key_value="$3" local data_value="$4" local db_file db_owner db_group db_perms db_delim db_key_field txtdb_set_params "$set_params_func" awk -F "$db_delim" -v KEY="$key_value" -v DATA="$data_value" -v KEY_FIELD="$db_key_field" \ "$awk_script" "$db_file" > "$db_file.new" 2>> $product_log && \ mv -f "$db_file.new" "$db_file" && txtdb_fix_perms } txtdb_do_select() { local set_params_func="$1" local awk_script="$2" local key_value="$3" local data_value="$4" local db_file db_owner db_group db_perms db_delim db_key_field txtdb_set_params "$set_params_func" awk -F "$db_delim" -v KEY="$key_value" -v DATA="$data_value" -v KEY_FIELD="$db_key_field" \ "$awk_script" "$db_file" 2>> $product_log } # Insert or replace existing record identified by key field txtdb_replace() { local db_file db_owner db_group db_perms db_delim db_key_field txtdb_set_params "$1" # Note that records order is not preserved local replace_with="$2" local key_to_replace=`get_narg_fs "$replace_with" "$db_delim" "$db_key_field"` txtdb_do_update "$1" 'END { print DATA } ( $KEY_FIELD != KEY )' "$key_to_replace" "$replace_with" } # Select entire record by key field txtdb_select() { local key_to_select="$2" txtdb_do_select "$1" '( $KEY_FIELD == KEY )' "$key_to_select" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # # Unified per-component cumulative upgrade support. # # --- Upgrader utility functions --- cu_get_continue_stage() { local continue_from="$1" get_narg_fs "$continue_from" '-' 1 } cu_get_continue_id() { local continue_from="$1" echo "` get_narg_fs "$continue_from" '-' 2 `-` get_narg_fs "$continue_from" '-' 3 `" } cu_get_current_continue_from() { echo "$CU_LAST_STAGE-$CU_LAST_ACTION_ID" } # Remove extraneous delimiters (usually spaces) between array elements normalize_array() { echo $@ } # Check that first argument is in array (other arguments) is_in_array() { local needle="$1" shift # everything else is haystack for elem in $@; do [ "$elem" = "$needle" ] && return 0 done return 1 } # Remove all instances of first argument from array (other arguments). Echoes normalized array with space delimiters. remove_from_array() { local what="$1" shift # everything else is array local new_array= for elem in $@; do [ "$elem" = "$what" ] || new_array="$new_array $elem" done echo $new_array } # --- Chained upgrade actions checks --- # cu_do <upgrade action> [<arguments>]: Execute an action or echo information about it cu_do() { if [ -n "$CU_DO_GET_ACTION_NAME" ]; then echo "$1" return fi if [ -n "$CU_CONTINUE_FROM_ID" ]; then # Run only actions starting from CU_CONTINUE_FROM_ID # WARNING: this will not work properly if there is a subshell execution somewhere along the chain! [ "$CU_LAST_ACTION_ID" = "$CU_CONTINUE_FROM_ID" ] && CU_CONTINUE_FROM_ID= || return 0 fi if "$@"; then cu_remove_failed_action "$CU_LAST_ACTION_ID" return 0 else cu_add_failed_action "$CU_LAST_ACTION_ID" return 1 fi } # Echo resulting action name (function name) or empty if skipped cu_get_action_name() { local CU_DO_GET_ACTION_NAME="yes" "$@" } # Execute action only if it wasn't applied before (by timestamp, e.g. use `date '+%Y%m%d%H%M%0S'` or even `date '+%Y/%m/%d %H:%M:%0S'`) cu_check_timestamp() { local timestamp="`echo \"$1\" | tr -dc '[:digit:]'`" shift true cu_get_action_name CU_LAST_ACTION_ID="$timestamp-`cu_get_action_name "$@"`" # Skip timestamp check if this action should be done again (during repair) if [ -z "$CU_FAILED_ACTIONS" ] || ! is_in_array "$CU_LAST_ACTION_ID" "$CU_FAILED_ACTIONS"; then [ "$timestamp" -gt "$CU_START_TIMESTAMP" ] || return 0 fi if [ "$timestamp" -gt "$CU_LATEST_TIMESTAMP" ]; then CU_LATEST_TIMESTAMP="$timestamp" fi "$@" } # Execute action only if it wasn't applied before (by version) cu_check_version() { true cu_get_action_name CU_LAST_ACTION_ID="$CU_UPGRADE_VERSION_FROM-`cu_get_action_name "$@"`" # Skip version check if this action should be done again (during repair) if [ -z "$CU_FAILED_ACTIONS" ] || ! is_in_array "$CU_LAST_ACTION_ID" "$CU_FAILED_ACTIONS"; then [ "$CU_UPGRADE_VERSION_FROM" -ge "$CU_START_VERSION_FROM" ] || return 0 fi "$@" } # Execute action only if it belongs to a current stage cu_check_stage() { local stage="$1" shift [ "$stage" = "$CU_STAGE" ] || return 0 "$@" } # Check that all previous cu_check_timestamp actions contain timestamps less than a given one cu_verify_latest_timestamp_lt() { local timestamp="`echo \"$1\" | tr -dc '[:digit:]'`" [ "$CU_LATEST_TIMESTAMP" -lt "$timestamp" ] || simply_die "Upgrader actions for '$CU_STAGE' stage of '$CU_LAST_DB_UPGRADER_NAME' upgrader are not valid. Timestamp values MUST be less than $timestamp. Please fix upgrader definition!" } # --- Upgrader bookkeeping functions --- # Start a block of upgrade actions that should be executed when upgrading from a given version cu_start_upgrade_from_version() { CU_UPGRADE_VERSION_FROM="$1" } # Remember failed upgrade action (to be able to rerun it afterwards) cu_add_failed_action() { local action_id="$1" [ -n "$action_id" ] && CU_FAILED_ACTIONS="$CU_FAILED_ACTIONS $action_id" && CU_HAS_NEW_FAILED_ACTIONS="yes" } cu_remove_failed_action() { local action_id="$1" [ -n "$CU_FAILED_ACTIONS" -a -n "$action_id" ] && CU_FAILED_ACTIONS="` remove_from_array "$action_id" "$CU_FAILED_ACTIONS" `" } # Store configuration data using supplied callback. Duplicate to default location if necessary. cu_do_store_config_data() { local upgrader_name="$1" local store_config_data_func="$2" $store_config_data_func "$upgrader_name" # Duplicate into default storage, if custom one was used. This is mostly for humans. [ "$store_config_data_func" = "cu_txtdb_store_config_data" ] || cu_txtdb_store_config_data "$upgrader_name" } # Handler for cases when critical action fails and there is no rollback procedure defined cu_critical_failure_handler() { [ -n "$CU_LAST_DB_UPGRADER_NAME" -a -z "$CU_LAST_DB_CONTINUE_FROM" ] || { p_echo "Can't handle upgrader critical failure - upgrader state is broken. Probably a programming error." return } p_echo "Updating '$CU_LAST_DB_UPGRADER_NAME' configuration status after a critical failure during upgrade stage '$CU_LAST_STAGE' on '$CU_LAST_ACTION_ID' action" local cu_db_current_timestamp cu_db_continue_from cu_db_failed_actions_list cu_db_latest_timestamp # Update only continue_from and failed_actions_list fields. cu_db_current_timestamp="$CU_LAST_DB_CURRENT_TIMESTAMP" cu_db_continue_from="`cu_get_current_continue_from`" cu_db_failed_actions_list="$CU_FAILED_ACTIONS" cu_db_latest_timestamp="$CU_LAST_DB_LATEST_TIMESTAMP" cu_do_store_config_data "$CU_LAST_DB_UPGRADER_NAME" "$CU_LAST_STORE_CONFIG_DATA_FUNC" CU_LAST_DB_UPGRADER_NAME= } # Check that following upgrader run will actually have any actions executed cu_check_upgrade_required() { local upgrader_func="$1" local CU_DO_GET_ACTION_NAME="yes" local upgrade_actions="`$upgrader_func`" [ -n "$upgrade_actions" ] } # --- Upgrader set up functions --- # Set up installed (product/package) version and installed configuration timestamp cu_set_initial_versions() { CU_START_VERSION_FROM="${1:-starting_component_version_not_specified}" CU_START_TIMESTAMP="${2:-0}" } # Reset upgrader global variables cu_set_globals() { local upgrader_name="$1" local stage="$2" local store_config_data_func="$3" CU_DO_GET_ACTION_NAME= CU_LATEST_TIMESTAMP=0 CU_LAST_ACTION_ID= # Initial DB record values. Used by cu_critical_failure_handler() CU_LAST_DB_UPGRADER_NAME="$upgrader_name" CU_LAST_DB_CURRENT_TIMESTAMP="$cu_db_current_timestamp" CU_LAST_DB_CONTINUE_FROM="$cu_db_continue_from" CU_LAST_DB_FAILED_ACTIONS_LIST="$cu_db_failed_actions_list" CU_LAST_DB_LATEST_TIMESTAMP="$cu_db_latest_timestamp" CU_LAST_STAGE="$stage" CU_LAST_STORE_CONFIG_DATA_FUNC="$store_config_data_func" } # --- Default implementations to load/store configuration data --- cu_txtdb_config_data_params() { db_file="$PLESK_DB_DIR/components-configuration-state.db" db_delim='#' } # Path to default upgrader configuration db changed in Plesk 11.5.26. This function moves existing db. cu_txtdb_try_upgrade_location() { local old_db_file="$PRODUCT_ROOT_D/var/components-configuration-state.db" local db_file db_owner db_group db_perms db_delim db_key_field txtdb_set_params cu_txtdb_config_data_params if [ -s "$old_db_file" -a ! -e "$db_file" ]; then mv -f "$old_db_file" "$db_file" fi } cu_txtdb_ensure_config_data_exists() { cu_txtdb_try_upgrade_location txtdb_exists cu_txtdb_config_data_params >/dev/null 2>&1 || txtdb_truncate cu_txtdb_config_data_params } cu_txtdb_load_config_data() { local upgrader_name="$1" cu_txtdb_ensure_config_data_exists local record="`txtdb_select cu_txtdb_config_data_params "$upgrader_name"`" cu_db_current_timestamp="` get_narg_fs "$record" '#' 2`" cu_db_continue_from="` get_narg_fs "$record" '#' 3`" cu_db_failed_actions_list="`get_narg_fs "$record" '#' 4`" cu_db_latest_timestamp="` get_narg_fs "$record" '#' 5`" } cu_txtdb_store_config_data() { local upgrader_name="$1" local record="$upgrader_name#$cu_db_current_timestamp#$cu_db_continue_from#$cu_db_failed_actions_list#$cu_db_latest_timestamp" cu_txtdb_ensure_config_data_exists txtdb_replace cu_txtdb_config_data_params "$record" } # --- Main code for generic cumulative upgrader --- cu_run_upgrader() { local opt_upgrader_name="$1" local opt_upgrader_func="$2" shift 2 local opt_prepare_func= opt_rollback_func= opt_commit_func= opt_start_version= opt_stage= opt_do_repair= opt_do_update_version_in_db= opt_do_init= local opt_load_config_data_func=cu_txtdb_load_config_data opt_store_config_data_func=cu_txtdb_store_config_data local inten="perform upgrade for $opt_upgrader_name" while [ "$#" -gt 0 ]; do case "$1" in --prepare) opt_prepare_func="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --rollback) opt_rollback_func="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --commit) opt_commit_func="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --load-config-data) opt_load_config_data_func="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --store-config-data) opt_store_config_data_func="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --from-version) opt_start_version="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --stage) opt_stage="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --repair) opt_do_repair="yes" shift ;; --update-version) opt_do_update_version_in_db="yes" shift ;; --init) opt_do_init="yes" shift ;; *) die "$inten: unknown key '$1'" ;; esac done # Sanity checks [ -n "$opt_upgrader_name" ] || die "$inten: empty component upgrader name argument" [ -n "$opt_upgrader_func" ] && is_function "$opt_upgrader_func" || die "$inten: invalid component upgrader function argument" [ -n "$opt_load_config_data_func" ] && is_function "$opt_load_config_data_func" || die "$inten: invalid value for --load-config-data argument" [ -n "$opt_store_config_data_func" ] && is_function "$opt_store_config_data_func" || die "$inten: invalid value for --store-config-data argument" [ -z "$opt_prepare_func" ] || is_function "$opt_prepare_func" || die "$inten: invalid value for --prepare argument" [ -z "$opt_rollback_func" ] || is_function "$opt_rollback_func" || die "$inten: invalid value for --rollback argument" [ -z "$opt_commit_func" ] || is_function "$opt_commit_func" || die "$inten: invalid value for --commit argument" [ -n "$opt_do_init" -a \( -n "$opt_do_repair" -o -n "$opt_prepare_func" -o -n "$opt_rollback_func" -o -n "$opt_commit_func" \) ] && die "$inten: excessive options prohibited for use with --init" # Upgrader setup local CU_STAGE="$opt_stage" local cu_db_current_timestamp cu_db_continue_from cu_db_failed_actions_list cu_db_latest_timestamp $opt_load_config_data_func "$opt_upgrader_name" cu_set_initial_versions "$opt_start_version" "$cu_db_current_timestamp" cu_set_globals "$opt_upgrader_name" "$opt_stage" "$opt_store_config_data_func" local CU_CONTINUE_FROM_ID= CU_FAILED_ACTIONS= CU_HAS_NEW_FAILED_ACTIONS= if [ -z "$opt_do_init" ]; then if [ -n "$cu_db_continue_from" ]; then if [ -z "$opt_do_repair" ]; then pp_echo "Unable to upgrade '$opt_upgrader_name' to the latest version, since its configuration is in broken state. Run bootstrapper repair to fix." return 2 fi local cu_continue_from_stage="` cu_get_continue_stage "$cu_db_continue_from" `" CU_CONTINUE_FROM_ID="` cu_get_continue_id "$cu_db_continue_from" `" if [ "$cu_continue_from_stage" != "$CU_STAGE" ]; then p_echo "Skipping stage '$CU_STAGE' of upgrade for '$opt_upgrader_name' as previous upgrade attempt was not finished on another stage." return 3 fi elif [ -n "$cu_db_failed_actions_list" ]; then if [ -z "$opt_do_repair" ]; then p_echo "Before start of upgrade for '$opt_upgrader_name' (stage '$CU_STAGE') following actions are registered as failed: $cu_db_failed_actions_list." else CU_FAILED_ACTIONS="` normalize_array "$cu_db_failed_actions_list" `" fi fi if ! cu_check_upgrade_required "$opt_upgrader_func" ; then p_echo "Upgrade or repair for '$opt_upgrader_name' (stage '$CU_STAGE') is not required" [ -n "$opt_prepare_func" ] && $opt_prepare_func "$opt_upgrader_name" "$CU_STAGE" noop-stage [ -n "$opt_commit_func" ] && $opt_commit_func "$opt_upgrader_name" "$CU_STAGE" noop-stage return 0 fi # Preparation step [ -n "$opt_prepare_func" ] && $opt_prepare_func "$opt_upgrader_name" "$CU_STAGE" # Register rollback actions. # Note that rollback scope currently spans to the end of upgrade procedure (this function). if [ -n "$opt_rollback_func" ]; then transaction_add_rollback_action "$opt_rollback_func" # Rollback function contract: everything changed in this upgrader pass will be # rolled back. Component version should not change. Therefore there is no need # to update anything in DB. else transaction_add_rollback_action "cu_critical_failure_handler" fi # Run upgrade actions $opt_upgrader_func # Commit (post) step - if we are here upgrade has completed (but there may be failed actions) [ -n "$opt_commit_func" ] && $opt_commit_func "$opt_upgrader_name" "$CU_STAGE" else # This is just an initialization run to set proper upgrader configuration on component installation p_echo "Initializing '$opt_upgrader_name' upgrader configuration (stage '$CU_STAGE')" local CU_DO_GET_ACTION_NAME="yes" $opt_upgrader_func >/dev/null 2>&1 fi # Update DB record if there are changes. Update current_timestamp only if explicitly allowed (e.g. this is the last stage). cu_db_continue_from= if [ -n "$opt_do_repair" -o -z "$CU_LAST_DB_FAILED_ACTIONS_LIST" ]; then cu_db_failed_actions_list="$CU_FAILED_ACTIONS" else # Append failed actions from this stage. Don't care for duplicates - they will be removed when a given action succeeds. cu_db_failed_actions_list="` normalize_array "$CU_LAST_DB_FAILED_ACTIONS_LIST" "$CU_FAILED_ACTIONS" `" fi if [ -z "$cu_db_latest_timestamp" -a "$CU_LATEST_TIMESTAMP" -gt 0 ] || [ "0$cu_db_latest_timestamp" -lt "$CU_LATEST_TIMESTAMP" ]; then cu_db_latest_timestamp="$CU_LATEST_TIMESTAMP" fi [ -n "$opt_do_update_version_in_db" -a "0$cu_db_latest_timestamp" -gt 0 ] && cu_db_current_timestamp="$cu_db_latest_timestamp" if [ "$cu_db_current_timestamp" != "$CU_LAST_DB_CURRENT_TIMESTAMP" \ -o "$cu_db_latest_timestamp" != "$CU_LAST_DB_LATEST_TIMESTAMP" \ -o "$cu_db_failed_actions_list" != "$CU_LAST_DB_FAILED_ACTIONS_LIST" \ -o "$cu_db_continue_from" != "$CU_LAST_DB_CONTINUE_FROM" ]; then cu_do_store_config_data "$opt_upgrader_name" "$opt_store_config_data_func" fi # If this is just an initialization, we don't need to disarm rollback, etc. [ -z "$opt_do_init" ] || return 0 # Upgrade is complete - disarm rollback actions if [ -n "$opt_rollback_func" ]; then transaction_remove_rollback_action "$opt_rollback_func" else transaction_remove_rollback_action "cu_critical_failure_handler" fi if [ -n "$cu_db_failed_actions_list" ]; then p_echo "After end of upgrade for '$opt_upgrader_name' (stage '$CU_STAGE') following actions are registered as failed: $cu_db_failed_actions_list." fi [ ! -n "$CU_HAS_NEW_FAILED_ACTIONS" ] } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh initial_conf() { PRODNAME="psa" PRODUCT_NAME="psa" product=${PRODNAME} PRODUCT_FULL_NAME="Plesk" product_etc="/etc/${PRODNAME}" prod_conf_t="/etc/psa/psa.conf" support_contact="https://support.plesk.com/" conceived_os_vendor=Ubuntu conceived_os_version="22.04" clients_group="psacln" clients_GID="10001" services_group="psaserv" services_GID="10003" product_suff="saved_by_${product}".`date "+%m.%d;%H:%M"` product_suffo="saved_by_${product}" # plesk default password PRODUCT_DEFAULT_PASSWORD="setup" } read_conf() { [ -n "$prod_conf_t" ] || prod_conf_t=/etc/psa/psa.conf if [ -s $prod_conf_t ]; then tmp_var=`perl -e 'undef $/; $_=<>; s/#.*$//gm; s/^\s*(\S+)\s*/$1=/mg; print' $prod_conf_t` eval $tmp_var else if ! is_product_installation; then p_echo "Unable to find product configuration file: $prod_conf_t. Default values will be used." return 1 fi fi return 0 } get_my_cnf_param() { local r= local my_cnf find_my_cnf "non-fatal" && \ r=`perl -e '$p="'"$1"'"; undef $/; $_=<>; s/#.*$//gm; /\[mysqld\](.*?)\[/sg; $_=substr($1, rindex $1,"$p") and /$p\s*=(.*)/m and print $1 ' ${my_cnf}` echo $r } get_mysql_socket() { # Marked as local as it's not used anywhere else now. local mysql_socket="/var/run/mysqld/mysqld.sock" local mysqlsock=`get_my_cnf_param socket` local MYSQL_SOCKETS="/var/lib/mysql/mysql.sock /tmp/mysql.sock /var/run/mysqld/mysqld.sock" for i in $mysql_socket $mysqlsock $MYSQL_SOCKETS; do if [ -S "$i" ]; then # This is used internally by mysqld_safe. Maybe this whole function isn't required nowadays. # See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-mysql-sock.html MYSQL_UNIX_PORT=$i export MYSQL_UNIX_PORT mysql_socket="$i" break fi done } # setup new value for parameter # $1 config file name $2 paramater name, $3 parameter value conf_setval() { local filename="$1" local varname="$2" local varvalue="$3" oldval="`conf_getvar $filename $varname`" [ "$oldval" != "$3" ] || return 0 cat "$1" | awk -v varname="$varname" -v varvalue="$varvalue" \ 'BEGIN { f = 0 } { if ($1 == varname) { f = 1; print varname "\t" varvalue } else { print $0 } } END { if (f == 0) { print "\n" varname "\t" varvalue } }' \ > "$filename.new" && \ mv -f "$filename.new" "$filename" && \ chmod 644 "$filename" } # A set of functions for work with config variables # $1 is config file name, $2 is variable name, $3 is variable value conf_getvar() { cat $1 | perl -n -e '$p="'$2'"; print $1 if m/^$p\s+(.*?)\s*$/' } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. #-*- vim:ft=sh register_service() { [ -n "$1" ] || die "register_service: service name not specified" local inten="register service $1" echo_try "$inten" { # sysvinit tools will not be called on systemd OS'es # since such OS'es are not explicitly supported enable_respawn_service "$1.service" # systemctl daemon-reload is performed implicitly unless --no-reload is passed /bin/systemctl enable "$1.service" local rs_db="$PRODUCT_ROOT_D/admin/sbin/register_service_db" [ ! -x "$rs_db" ] || "$rs_db" -a "$@" } suc } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. #-*- vim:ft=sh enable_respawn_service() { grep_q_recursive() { local val="$1" local file="$2" if [ -f "$file" ]; then ! grep -q "$val" "$file" || return 0 for f in `sed -n -e "s/^\.include\s//p" $file`; do ! grep_q_recursive "$val" "$f" || return 0 done fi return 1 } [ -n "$1" ] || die "enable_respawn_service: service name not specified" local inten="enable automatic respawn for service $1" echo_try "$inten" local service=$1 local main_unit=`systemctl show $service | sed -n -e "s/FragmentPath=//p"` local respawn_unit="/lib/systemd/system/$service.d/respawn.conf" local dropin_units=`systemctl show $service | sed -n -e "s/DropInPaths=//p" | sed "s|$respawn_unit||"` local ini="/opt/psa/admin/conf/panel.ini" local ini_section="systemd" local respawn [ ! -f "$ini" ] || respawn=`get_ini_conf_var "$ini" "$ini_section" respawn` if [ -z "${respawn/on/}" ]; then for unit in $main_unit $dropin_units; do ! grep_q_recursive "^Restart=" "$unit" || respawn="off" ! grep_q_recursive "^Type=oneshot" "$unit" || respawn="off" done fi rm -f "$respawn_unit" if [ -z "${respawn/on/}" ]; then mkdir -p "$(dirname $respawn_unit)" if [ -f "$ini" ]; then local restart=` get_ini_conf_var "$ini" "$ini_section" Service.Restart` local restartsec=` get_ini_conf_var "$ini" "$ini_section" Service.RestartSec` fi cat <<EOT > "$respawn_unit" [Service] Restart=${restart:-"on-failure"} RestartSec=${restartsec:-"5"} EOT fi suc } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh selinux_is_active() { if [ -z "$SELINUX_ENFORCE" ]; then selinux_getenforce fi case "$SELINUX_ENFORCE" in Enforcing|Permissive) return 0;; *) return 1;; esac } selinux_support_is_installed() { # This function checks if Plesk SELinux support component is installed set_selinux_params [ -s "$selinux_module" ] } selinux_configuration_is_required() { # All public functions that modify SELinux state should check that this is true! selinux_is_active && selinux_support_is_installed } selinux_get_mount_dir() { unset SELINUX_MOUNT_DIR if awk '$2 == "/selinux"{exit(1)}' /proc/mounts && mkdir -p /selinux; then SELINUX_MOUNT_DIR=/selinux else SELINUX_MOUNT_DIR="`mktemp -d /tmp/selinuxXXXXXX`" fi >>"$product_log" 2>&1 } selinux_getenforce() { if [ "$1" = "--check" -a -n "$SELINUX_ENFORCE" ]; then return fi unset SELINUX_ENFORCE if ! ( command -v selinuxenabled >/dev/null 2>&1 && selinuxenabled ); then SELINUX_ENFORCE=Disabled return fi if awk '$3 == "selinuxfs"{exit(1)}' /proc/mounts; then selinux_get_mount_dir mount -t selinuxfs none "$SELINUX_MOUNT_DIR" fi if ! command -v getenforce >/dev/null 2>&1; then SELINUX_ENFORCE=Disabled return fi SELINUX_ENFORCE="`getenforce`" if test $? -ne 0; then SELINUX_ENFORCE=Disabled return fi } selinux_init() { unset SELINUX_ENFORCE if selinux_is_active; then setenforce Permissive else unset SELINUX_ENFORCE fi } selinux_close() { if [ -z "$SELINUX_ENFORCE" -o "$SELINUX_ENFORCE" = "Disabled" ]; then return fi setenforce "$SELINUX_ENFORCE" } selinux_relabel_dir() { selinux_configuration_is_required || return 0 if ! command -v restorecon >/dev/null 2>&1; then return fi local ret=0 if ! restorecon -i -R "$@" >>"$product_log" 2>&1; then warn "Error while setting SELinux types. Command was: restorecon -i -R $*" ret=1 fi [ -z "$do_repair" ] || return $ret } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh set_selinux_params() { selinux_module="$PRODUCT_ROOT_D/etc/plesk.pp" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: #set_params set_common_params() { common_var=0 PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin LANG="`get_default_locale`" export PATH LANG unset GREP_OPTIONS umask 022 ulimit -n 65535 2>/dev/null get_product_versions certificate_file="$PRODUCT_ETC_D/httpsd.pem" services="/etc/services" crontab="/usr/bin/crontab" SYSTEM_RC_D="/etc/init.d" PLESK_LIBEXEC_DIR="/usr/lib/plesk-9.0" PLESK_DB_DIR="/var/lib/plesk" PRODUCT_BOOTSTRAPPER_DIR="`printf "/opt/psa/bootstrapper/pp%s-bootstrapper" "$product_this_version"`" AUTOGENERATED_CONFIGS="#ATTENTION!\n#\n#DO NOT MODIFY THIS FILE BECAUSE IT WAS GENERATED AUTOMATICALLY,\n#SO ALL YOUR CHANGES WILL BE LOST THE NEXT TIME THE FILE IS GENERATED.\n" AUTOGENERATED_CONFIGS_UPGRADE="#ATTENTION!\n#\n#DO NOT MODIFY THIS FILE BECAUSE IT WAS GENERATED AUTOMATICALLY,\n#SO ALL YOUR CHANGES WILL BE LOST AFTER YOU UPGRADE PLESK.\n" PRODUCT_LOGS_D="/var/log/plesk" sendmail="/usr/sbin/sendmail" ps="ps axw" ifconfig="/sbin/ifconfig -a" machine="linux" if [ -f /etc/debian_version ]; then linux_distr="debian" else linux_distr="redhat" fi dummy_home="/" if [ -x /usr/sbin/nologin ]; then dummy_shell="/usr/sbin/nologin" else dummy_shell="/bin/false" fi rp_patch_runtime } set_local_params() { local CONSTRUCTORS=" set_admin_params set_apache_params set_courier_imap_params set_mysqld_params " for i in $CONSTRUCTORS; do $i local res="$?" if [ "0$res" -gt 0 ]; then warn "$i function completed with errors (error code $res)" fi done } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. set_sw_engine_params() { sw_engine_service="sw-engine" sw_engine_process="${sw_engine_service}-fpm" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. set_swcpserver_params() { swcpserver_service=sw-cp-server swcpserver_binary=/usr/sbin/sw-cp-serverd swcpserver_pidfile=/var/run/sw-cp-server.pid swcpserver_includedir=/etc/sw-cp-server/conf.d true swcpserver_status } swcpserver_status() { get_pid "$swcpserver_binary" false local pid=$common_var if [ "$pid" -ne 1 ]; then return 0 fi return 1 } # vim:ft=sh ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # Prints string in format "<OS_NAME> <OS_VERSION>" get_runtime_os_info() { # TODO: check and improve (if needed) runtime os detection. local os_name os_version # arch? local getver='sed -e s/[^0-9.]*\([0-9.]*\).*/\1/g' if [ -e '/etc/debian_version' ]; then if [ -e '/etc/lsb-release' ]; then ### Ubuntu . /etc/lsb-release os_name=$DISTRIB_ID os_version=$DISTRIB_RELEASE else ### Debian os_name='Debian' os_version=$(head -1 /etc/debian_version) fi elif [ -e '/etc/redhat-release' ]; then local header=$(head -n 1 /etc/redhat-release) if expr match "$header" "Red Hat" > /dev/null; then os_name='RedHat' elif expr match "$header" "CentOS" > /dev/null; then os_name='CentOS' elif expr match "$header" "CloudLinux" > /dev/null; then os_name='CloudLinux' else os_name="$(echo $header | sed -e 's/ release.*//i')" fi os_version=$(echo $header | ${getver}) fi echo "$os_name $os_version" } update_upgrade_history() { local query local inten="insert upgrade info into history table" local os_info="`get_runtime_os_info`" local release_version="`plesk_release_version`" local hotfix_version="`plesk_hotfix_version`" if [ -n "$hotfix_version" ]; then query="SELECT * FROM upgrade_history WHERE version_info='${release_version}' AND os='${os_info}' AND mu='${hotfix_version}' LIMIT 1" else query="SELECT * FROM upgrade_history WHERE version_info='${release_version}' AND os='${os_info}' LIMIT 1" fi if db_test "$query" '1'; then # record already exists return 0 fi local update_ticket="`get_update_ticket`" query="SELECT * FROM upgrade_history WHERE update_ticket='${update_ticket}' LIMIT 1" if db_test "$query" '1'; then # update_ticket has already been reported update_ticket="" fi if [ -n "$hotfix_version" ]; then query="INSERT INTO upgrade_history (upgrade_date, version_info, os, update_ticket, mu) VALUES (now(), '${release_version}', '${os_info}', '${update_ticket}', '${hotfix_version}')" else query="INSERT INTO upgrade_history (upgrade_date, version_info, os, update_ticket) VALUES (now(), '${release_version}', '${os_info}', '${update_ticket}')" fi db_do --inten "$inten" "$query" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # -*- vim:syntax=sh packagemng_set_dirty_flag() { if [ -f ${PRODUCT_ROOT_D}/admin/bin/packagemng ]; then ${PRODUCT_ROOT_D}/admin/bin/packagemng --set-dirty-flag 1>>$product_log 2>&1 fi } 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 } plesk_full_version() { # "`plesk_full_version`" = "`plesk_release_version`.`plesk_hotfix_version`" # Beware: this shows information from a file, which changes during plesk-release package upgrade head -n1 /etc/plesk-release | cut -d' ' -f1 } plesk_release_version() { plesk_full_version | cut -d. -f-3 } plesk_hotfix_version() { plesk_full_version | cut -d. -f4- } # Product is being upgraded to a new version is_product_upgrade() { get_product_versions [ "$product_prev_version" != "$product_this_version" ] } # Clean installation of the product is being performed is_product_installation() { [ "X$do_upgrade" != "X1" -a ! -s "/opt/psa/version.upg" ] } true exim_status_linux_debian exim_status_linux_debian() { get_pid /usr/lib/exim/exim3 false local pid=$common_var if test "$pid" -ne 1; then #running return 0; fi return 1 } autoinstall_extensions() { for e in $PRODUCT_ROOT_D/var/modules/install/*.zip; do [ -e "$e" -a ! -L $e ] || continue local inten="install or update `basename $e` extension" echo_try "$inten" if ! $PRODUCT_ROOT_D/bin/extension --upgrade "$e"; then warn "$inten" continue fi rm -f $e suc done } set_mysql_auth() { # This function requires set_mysql_server_params and set_mysql_client_params (or set_mysqld_params) # to be called before local inten="set up mysql authentication" local dsn_plesk_host="`get_dsn plesk host`" local dsn_plesk_port="`get_dsn plesk port`" local dsn_plesk_username="`get_dsn plesk username`" local dsn_plesk_password="`get_dsn plesk password`" get_admin_passwd [ -n "$dsn_plesk_host" ] || pleskrc mysql start mysql_host="${dsn_plesk_host:+--host=$dsn_plesk_host}" mysql_port="${dsn_plesk_port:+--port=$dsn_plesk_port}" mysql_user="--user=${dsn_plesk_username:-admin}" mysql_passwd="${dsn_plesk_password:-$admin_passwd}" if find_my_cnf 'non-fatal'; then mysql_defaults="--defaults-file=$my_cnf" else mysql_defaults="--no-defaults" fi if [ -z "$PLESK_MYSQL_AUTH_SKIP_CONNECTION_CHECK" ]; then mysql_test_connection 10 || die "$inten" fi suc } get_admin_passwd() { [ -z "$admin_passwd" ] || return 0 if [ -f "$mysql_passwd_file" ]; then admin_passwd=`cat "$mysql_passwd_file"` return 0 fi admin_passwd=`get_random_string 1 'A-Z'` admin_passwd+=`get_random_string 1 'a-z'` admin_passwd+=`get_random_string 1 '0-9'` admin_passwd+=`get_random_string 1 '_%=+\-@<>:.,:'` admin_passwd+=`get_random_string 10 'A-Za-z0-9_%=+\-@<>:.,:'` admin_passwd=`printf "%s" "$admin_passwd" | fold -w 1 | shuf - | tr -d '\n'` [ -n "$admin_passwd" ] || admin_passwd="$PRODUCT_DEFAULT_PASSWORD" touch "$admin_password_needs_encryption_flag" } mysql_test_connection() { inten="establish test connection" echo_try $inten attempts=${1:-1} for i in `sequence $attempts`; do echo "" | mysql_direct mysql >> "$product_log" 2>&1 if [ "$?" -eq "0" ]; then p_echo "connected" return 0 fi [ "$attempts" -eq "1" ] || sleep 1 done p_echo "failed" return 1 } get_mysqldump_default_charset() { local dump_client="${MYSQL_BIN_D}/mysqldump" [ -f "$MYSQL_BIN_D/mariadb-dump" ] && dump_client="$MYSQL_BIN_D/mariadb-dump" env LC_ALL=C LANG=C "$dump_client" "$@" --help | awk '/default-character-set/{print $2}' | xargs } create_my_cnf_d() { [ -z "$my_cnf_d" ] || return 0 my_cnf_d="/etc/mysql/conf.d" [ -d "$my_cnf_d" ] || mkdir -p "$my_cnf_d" echo_try "configure and create MySQL conf.d $my_cnf_d" mk_backup "$my_cnf" cp f echo "!includedir $my_cnf_d" >> $my_cnf && suc need_mysql_restart=yes } configure_mysqldump_utf8mb4() { local inten="configure default charset for mysqldump to utf8mb4" local builtin_character_set=`get_mysqldump_default_charset --no-defaults` local configured_character_set=`get_mysqldump_default_charset --defaults-file="$my_cnf"` echo_try "$inten" if [ "$builtin_character_set" != "utf8" ]; then p_echo "not required: built-in character set is not utf8 ($builtin_character_set)." return 0 fi if [ "$configured_character_set" != "$builtin_character_set" ]; then p_echo "not required: configured character set ($configured_character_set) differs from built-in ($builtin_character_set)" return 0 fi if [ -z "$my_cnf_d" ]; then create_my_cnf_d fi local config="$my_cnf_d/plesk-utf8mb4.cnf" if [ -f "$config" ]; then p_echo "already configured in $config" return 0 fi cat <<EOT > "$config" # Configure utf8mb4 default character set for mysqldump # If you don't need it, please comment out the lines below [mysqldump] default-character-set=utf8mb4 EOT suc } try_disable_mysqldump_utf8mb4() { # This function is required to remove our customization of default character set for # mysqldump when mysql server was upgraded local config="$my_cnf_d/plesk-utf8mb4.cnf" local inten="remove utf8mb4 as default character set for mysqldump" [ -n "$my_cnf_d" ] || return 0 [ -f "$config" ] || return 0 echo_try "$inten" local builtin_character_set=`get_mysqldump_default_charset --no-defaults` if [ "$builtin_character_set" = "utf8" ]; then p_echo "not required: built-in character set is utf8" return 0 fi rm "$config" && suc || warn "$inten" } find_my_cnf() { local non_fatal="$1" local cnf_files="/etc/my.cnf /etc/mysql/my.cnf /var/db/mysql/my.cnf" for my_cnf in $cnf_files; do if [ -f ${my_cnf} ]; then break fi done [ -f "$my_cnf" -o -n "$non_fatal" ] || { local msg="find the MySQL server configuration file. \ If you use a third-party MySQL server build, make sure you do not use implicit default configuration \ and you have the MySQL configuration file in one of the following locations: $cnf_files" echo "Failed to $msg" | write_structured_report 'stage=mysqlsetup' 'level=fatal' 'errtype=mysqlnomycnf' "mycnffiles=$cnf_files" || : die "$msg" } [ -f "$my_cnf" ] || return 1 my_cnf_d=`awk '/^\s*!includedir\>/ {print $2}' "$my_cnf" | head -1` return 0 } run_configure_mysql_funcs() { local need_mysql_restart= local my_cnf= local my_cnf_d= find_my_cnf for func in "$@"; do eval "$func $my_cnf" done if [ -n "$need_mysql_restart" ]; then pleskrc mysql restart fi } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # -*- vim:ft=sh # mysql set_mysqld_params() { mysqld_user="mysql" mysqld_UID=3306 mysqld_group="mysql" mysqld_GID=3306 product_db_sql="$PRODUCT_BOOTSTRAPPER_DIR/db/${PRODNAME}_db.sql" admin_password_needs_encryption_flag="$PLESK_DB_DIR/.encrypt_admin_password.flag" set_mysql_server_params set_mysql_client_params } set_mysql_server_params() { get_mysql_socket if [ -n "/lib/systemd/system" -a -f "/lib/systemd/system/mariadb.service" ]; then mysql_service="mariadb" elif [ -n "/lib/systemd/system" -a -f "/lib/systemd/system/mysqld.service" ]; then # Percona server mysql_service="mysqld" elif [ -n "/lib/systemd/system" -a -f "/lib/systemd/system/mysql.service" ]; then # MySQL 8 installations on debian / ubuntu mysql_service="mysql" elif [ -x "${PRODUCT_RC_D}/mysql" ]; then mysql_service="mysql" elif [ -x "${PRODUCT_RC_D}/mysql.sh" ]; then mysql_service="mysql.sh" elif [ -x "${PRODUCT_RC_D}/mysqld" ]; then mysql_service="mysqld" elif [ -x "${PRODUCT_RC_D}/mysql" ]; then mysql_service="mysql" else die "detect MySQL service name" fi } set_mysql_client_params() { mysql_client="$MYSQL_BIN_D/mysql" [ -f "$MYSQL_BIN_D/mariadb" ] && mysql_client="$MYSQL_BIN_D/mariadb" # Override this variable as needed mysql_db_name="$PRODNAME" mysql_passwd_file="$product_etc/.${PRODNAME}.shadow" mysql_args="-N" mysql_args_raw="-Br" local dsn_plesk_dbname="`get_dsn plesk dbname`" [ -z "$dsn_plesk_dbname" ] || mysql_db_name="$dsn_plesk_dbname" } #Invoke mysql dbclient_invoke() { mysql_anydb -D$mysql_db_name "$@" } mysql_anydb() { ( export MYSQL_PWD="$mysql_passwd" $mysql_client $mysql_defaults $mysql_host $mysql_port $mysql_user $mysql_args "$@" 2>>"$product_log" local status=$? if [ $status -gt 0 ]; then $mysql_client $mysql_defaults $mysql_host $mysql_port $mysql_user $mysql_args -D$mysql_db_name $mysql_args_raw -e "SHOW ENGINE innodb status" >>"$product_log" 2>&1 fi unset MYSQL_PWD return $status ) } # Invoke mysql without any wrapper or something else mysql_direct() { # a bit dirty but it works properly for passwords with spaces, quotes and double quotes: if [ -n "$mysql_passwd" ]; then ( export MYSQL_PWD="$mysql_passwd" $mysql_client $mysql_defaults $mysql_host $mysql_port $mysql_user $mysql_args "$@" 2>>"$product_log" rc=$? unset MYSQL_PWD return $rc ) else $mysql_client $mysql_defaults $mysql_host $mysql_port $mysql_user $mysql_args "$@" 2>>"$product_log" fi } # Invoke mysql in raw mode mysql_raw() { dbclient_invoke $mysql_args_raw "$@" } mysql_raw_anydb() { mysql_anydb $mysql_args_raw "$@" } set_db_version() { # Install or update db version local product_db_version="018000000" echo_try "set psa database version to $product_db_version" db_do "replace into misc (param,val) values('version', '$product_db_version')" suc } uses_mysql_client() { # Initialize local MySQL authentication parameters and test connection. # After that db_*() and mysql*() functions may be used. # Any MySQL-specific overrides (e.g., local mysql_db_name="whatever") should appear # *after* calling this function. # This function is for use in component installers that require access to MySQL DB. set_mysql_server_params set_mysql_client_params set_mysql_auth } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # -*- vim:ft=sh # MySQL service action handlers ### FIXME: probably need var service_restart warn true mysql_stop mysql_stop() { local op_result i inten="stop MySQL server" echo_try $inten service_ctl stop $mysql_service mysql op_result=$? if [ "X$linux_distr" = "Xdebian" ]; then # Debian has well designed mysql stopping code [ "$op_result" -eq 0 ] || die $inten suc return 0 fi for i in 2 4 6 8 16; do if ! mysql_status ; then suc return 0 fi # I just want to be sure that mysql really stopped killall -TERM mysqld mysql safe_mysqld mysqld_safe >> $product_log 2>&1 sleep $i done die "$inten" } true mysql_status mysql_status() { # Previously this handler also checked for mysqld pid on Debian systems. Should not be needed nowadays. service_ctl status $mysql_service mysql } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:syntax=sh set_nginx_params() { nginx_service=nginx nginx_rc_config="/etc/default/nginx" nginx_user="nginx" nginx_bin="/usr/sbin/nginx" } nginx_is_installed() { [ -x "$nginx_bin" ] } initial_enable_nginx() { local check_initialize="$1" set_nginx_params if ! nginx_is_installed; then p_echo "Skipping nginx initialization: nginx is not installed." return 0 fi if [ -n "$check_initialize" ]; then if [ "$(db_read_misc_attr psa_configured)" = "true" ]; then p_echo "Skipping nginx initialization: Plesk is initialized." return 0 fi fi echo_try "enable nginx and its modules" if ! "${PRODUCT_ROOT_D}/admin/sbin/nginxmng" --enable >> "$product_log" 2>&1; then warn "enabling nginx" return 1 fi if ! "${PRODUCT_ROOT_D}/bin/nginx" --enable-module brotli >> "$product_log" 2>&1; then warn "enabling nginx modules" return 1 fi suc } true postfix_status postfix_status() { # here be dragons. # the practical experience shows that simple checking of status of # Postfix "master" process is not enough. So we read Postfix master # process pid file if any, then try to look for a process with # name ``qmgr'' and parent pid being equal to # the pid read from the pidfile. If pgrep finds such a process # it returns 0, if not its exit status is non-zero. # pgrep is portable enough to prefer it to "hand-made" alternatives # such as famous ``ps | grep $name | grep -v grep...'' pipes # bug 147822. do not interrupt installation for FreeBSD [ -f "/var/spool/postfix/pid/master.pid" ] || return 1 local ppid read ppid </var/spool/postfix/pid/master.pid 2>/dev/null if [ $? -ne 0 -o -z "$ppid" ]; then # not found or other error return 1; fi pgrep -P $ppid qmgr >/dev/null 2>/dev/null } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: core_python_obsolete_modules() { # List removed modules and modules entirely converted to .pex here. local py_libs_d="$PRODUCT_ROOT_D/admin/lib/python" local py_modules_d="$PRODUCT_ROOT_D/lib/modules/python" local py_share_d="$PRODUCT_ROOT_D/admin/share" deinstall_python_modules \ "$py_libs_d/compat" \ "$py_libs_d" \ "$py_modules_d/cfgmon" \ "$py_modules_d/reconfig" \ "$py_share_d/agent_copy" \ "$py_share_d/backup_unpack/commands" \ "$py_share_d/backup_unpack" \ "$py_share_d/pmmcli" \ } is_service_enabled_pylibplesk() { local service="$1" local PEX="$PRODUCT_ROOT_D/admin/sbin/phpinimng" local is_enabled is_enabled="`"/usr/bin/python3" -c 'import sys; \ sys.path[:0] = ["'"$PEX"'"]; \ import plesk_service; \ print(plesk_service.is_registered("'"$service"'"));' 2>>"$product_log"`" || { warn "attempt to determine whether service $service is enabled" return 2 } [ "$is_enabled" = "True" ] } reregister_enabled_phpfpm_services() { local inten="add missing PHP-FPM services into registered services database" echo_try "$inten" local phpfpm_services="`"$PRODUCT_ROOT_D/admin/sbin/php_handlers_control" --list-json | "/usr/bin/python3" -c 'import sys, json; \ handlers = json.load(sys.stdin)["php"]; \ print("\n".join(h.get("service") for h in handlers if h.get("service")));' 2>>"$product_log"`" for service in $phpfpm_services; do is_service_enabled_pylibplesk "$service" || continue "$PRODUCT_ROOT_D/admin/sbin/phpinimng" --enable-service --service-name "$service" >>"$product_log" 2>&1 || warn "attempt to re-enable service $service. System resource limits may not be applied to it." done suc } update_registered_services_db_for_resctrl() { reregister_enabled_phpfpm_services } disable_public_html_for_httpd() { local userdir_path="$HTTPD_CONF_D/mods-available/userdir.conf" [ -f "$userdir_path" ] || return 0 sed -i '/^<IfModule mod_userdir.c>/a \\tUserDir disabled' "$userdir_path" deferred_apache_restart } disable_home_public_html_for_httpd() { : } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: core_upgrader_repair_arg() { if [ -n "$1" -o -n "$do_repair" ]; then echo "--repair" fi } core_upgrader_action_name() { if [ -n "`core_upgrader_repair_arg $1`" ]; then echo "upgrade and repair" else echo "upgrade" fi } core_run_upgrade_post_stage() { local action_name="`core_upgrader_action_name $1`" local optional_repair_arg="`core_upgrader_repair_arg $1`" pp_echo "===> Cumulative Plesk $action_name (final stage) has been started." cu_run_upgrader "core" core_upgrader \ --load-config-data core_cu_load_config_data --store-config-data core_cu_store_config_data \ --prepare core_upgrader_prepare --rollback core_upgrader_rollback --commit core_upgrader_commit \ --from-version "$prev_short_version" --stage "post" $optional_repair_arg --update-version local ret="$?" if [ "$ret" -eq 0 ]; then pp_echo "===> Cumulative $action_name of Plesk (final stage) has been completed." else pp_echo "===> Plesk was not upgraded completely. See installation log for details." # reflect error in exit-code in repair mode: [ ! "$1" = "repair" ] || return "$ret" fi } core_cu_load_config_data() { local upgrader_name="$1" [ "$upgrader_name" = "core" ] || return 0 db_test_table "misc" || return 0 cu_db_current_timestamp="` db_read_misc_attr 'upgrader_config_current_timestamp'`" cu_db_continue_from="` db_read_misc_attr 'upgrader_config_continue_from'`" cu_db_failed_actions_list="`db_read_misc_attr 'upgrader_config_failed_actions_list'`" cu_db_latest_timestamp="` db_read_misc_attr 'upgrader_config_latest_timestamp'`" } core_cu_store_config_data() { local upgrader_name="$1" [ "$upgrader_name" = "core" ] || return 0 db_test_table "misc" || die "save core upgrader state to 'misc' table" db_do "REPLACE INTO misc (param, val) VALUES \ ('upgrader_config_current_timestamp', '$cu_db_current_timestamp'), \ ('upgrader_config_continue_from', '$cu_db_continue_from'), \ ('upgrader_config_failed_actions_list', '$cu_db_failed_actions_list'), \ ('upgrader_config_latest_timestamp', '$cu_db_latest_timestamp')" } core_upgrader_prepare() { local upgrader_name="$1" local upgrader_stage="$2" local upgrader_aux="$3" # DB is backed up earlier via safe_prep_db_backup_all() if [ "$upgrader_stage" = "prep" ]; then pp_echo "===> Preparing Plesk database upgrade (revertable stage)." else pp_echo "===> Preparing Plesk upgrade (final stage)." fi db_added_columns= } core_upgrader_commit() { local upgrader_name="$1" local upgrader_stage="$2" if [ "$upgrader_stage" = "prep" ]; then set_db_version fi } core_upgrader_rollback() { # Empty rollback function is required to suppress default upgrader behavior # (switching configuration into broken state). Actual rollback will be done # in safe_prep_db_upgrade_rollback(). : } core_upgrader() { local db="cu_check_stage prep" local files="cu_check_stage post" local ver="cu_check_version" local ts="cu_check_timestamp" local do="cu_do" cu_start_upgrade_from_version 017000017 $files $ts '2017/03/09 12:43:00' $do update_registered_services_db_for_resctrl cu_start_upgrade_from_version 017005002 $files $ts '2017/03/09 13:22:00' $do update_dump_d_mode_17_5_3 cu_start_upgrade_from_version 017008007 $files $ts '2017/11/13 14:55:00' $do update_dump_d_mode_17_8_8 $files $ts '2017/11/23 12:26:00' $do set_recipient_address_mapper_17_8_8 cu_start_upgrade_from_version 017008008 $files $ts '2017/12/12 17:03:00' $do remove_tomcat_support $files $ts '2017/12/14 12:32:00' $do set_recipient_address_mapper_17_8_9 $files $ts '2017/12/15 12:49:00' $do upgrade_mail_handlers_17_8_9 cu_start_upgrade_from_version 017008009 $files $ts '2018/02/02 11:13:00' $do dovecot_reconfigure_ssl_17_8_10 cu_start_upgrade_from_version 017008010 $files $ver $do install_advisor_ext $files $ts '2018/03/01 00:02:00' $do restore_files_in_etc_from_dpkg_dist_17_8_10 $files $ver $do move_out_custom_vhost_templates custom_templates_before_17.8 cu_start_upgrade_from_version 017009001 $files $ts '2018/05/21 15:28:00' $do core_python_obsolete_modules --deinstall $files $ts '2018/05/21 15:37:45' $do add_plesk_service_localdomain_virtual_host cu_start_upgrade_from_version 017009002 $files $ts '2018/06/06 11:30:00' $do deferred_httpd_reconfigure_all cu_start_upgrade_from_version 017009005 $files $ts '2018/09/11 13:00:00' $do drop_core_version_file cu_start_upgrade_from_version 017009006 $files $ts '2018/10/19 17:00:00' $do drop_readme_psa_shadow cu_start_upgrade_from_version 017009011 $files $ts '2019/02/15 17:00:00' $do drop_apsc_conf cu_start_upgrade_from_version 017009012 $files $ts '2019/03/27 11:37:00' $do deferred_swcpserver_restart cu_start_upgrade_from_version 018000016 $files $ts '2019/07/23 17:00:00' $do deferred_swcpserver_restart cu_start_upgrade_from_version 018000017 $files $ts '2019/07/29 15:18:00' $do deferred_httpd_reconfigure cu_start_upgrade_from_version 018000019 $files $ts '2019/09/25 10:08:18' $do apsc_odbcinst_upgrade_18_0_20 cu_start_upgrade_from_version 018000022 $files $ts '2020/01/10 16:26:10' $do remove_panel_logs_from_plesk_logrotate cu_start_upgrade_from_version 018000023 $files $ts '2020/01/22 17:00:00' $do move_ip_remap_config cu_start_upgrade_from_version 018000024 $files $ts '2020/03/10 12:00:00' $do deferred_systemctl_daemon_reload cu_start_upgrade_from_version 018000026 $files $ts '2020/04/23 16:40:00' $do create_stored_sslmng_configuration $files $ts '2020/04/23 16:40:01' $do create_stored_sslmng_configuration cu_start_upgrade_from_version 018000027 $files $ts '2020/06/05 14:40:00' $do remove_old_custom_sslmng_configuration $files $ts '2020/06/16 14:00:00' $do add_plesk_service_localdomain_virtual_host cu_start_upgrade_from_version 018000028 $files $ts '2020/07/08 16:21:00' $do deferred_httpd_reconfigure_all cu_start_upgrade_from_version 018000032 $files $ts '2020/12/28 11:15:00' $do disable_public_html_for_httpd $files $ts '2021/01/14 15:20:00' $do deferred_swcpserver_restart cu_start_upgrade_from_version 018000033 $files $ts '2021/02/18 18:18:33' $do initial_enable_nginx if-plesk-is-not-configured cu_start_upgrade_from_version 018000036 $files $ts '2021/06/29 14:21:00' $do deferred_httpd_reconfigure cu_start_upgrade_from_version 018000037 $files $ts '2021/07/27 11:09:00' $do add_missed_nginx_tls_1_3_protocol $files $ts '2021/07/27 11:09:02' $do deferred_httpd_reconfigure cu_start_upgrade_from_version 018000038 $files $ts '2021/09/17 13:39:00' $do remove_nodemng_data_18_0_38 cu_start_upgrade_from_version 018000043 $files $ts '2022/05/10 11:26:00' $do deferred_httpd_reconfigure cu_start_upgrade_from_version 018000044 $files $ts '2022/06/17 20:09:06' $do deferred_swcpserver_restart cu_start_upgrade_from_version 018000045 $files $ts '2022/08/04 13:09:06' $do enable_mysql_utf8_18_0_46 cu_start_upgrade_from_version 018000046 $files $ts '2022/09/05 12:00:00' $do core_psa_purge_sysvinit $files $ts '2022/09/24 13:30:00' $do deferred_swcpserver_restart cu_start_upgrade_from_version 018000052 $files $ts '2023/05/22 13:15:00' $do clear_sw_engine_opcache $files $ts '2023/05/24 16:26:00' $do disable_home_public_html_for_httpd cu_start_upgrade_from_version 018000056 $files $ts '2023/10/27 14:30:00' $do enable_nginx_passenger_module cu_start_upgrade_from_version 018000057 $files $ts '2023/11/27 12:00:00' $do deferred_swcpserver_restart $files $ts '2023/12/21 15:00:00' $do fix_domainkeys_access cu_start_upgrade_from_version 018000060 $files $ts '2024/05/10 15:00:00' $do deferred_httpd_reconfigure $files $ts '2024/05/11 15:00:00' $do disable_reuseport_http3 $files $ts '2024/05/12 05:00:00' $do disable_panel_http3 cu_start_upgrade_from_version 018000063 $files $ts '2024/08/14 18:00:00' $do selinux_relabel_dir "$mysql_passwd_file" cu_start_upgrade_from_version 018000065 $files $ts '2024/11/20 17:23:00' $do deferred_httpd_reconfigure cu_start_upgrade_from_version 018000067 $files $ts '2025/02/13 11:23:00' $do deferred_swcpserver_restart # DO NOT MODIFY ANYTHING BELOW THIS LINE! (without approval of review from the entire Linux team) call_optional_function dev_core_upgrader cu_verify_latest_timestamp_lt '2025/03/02 11:00:00' } ### ### HOW TO ADD UPGRADE ACTION ### ### Short version: ### To the end of core_upgrader() function (above) add a line in the format: ### STAGE $ts 'TIMESTAMP' $do UPGRADE_ACTION ### Where: ### STAGE is either '$db' or '$files' without quotes. If your upgrade action affects only Plesk DB and doesn't require any ### new files (e.g. PHP scripts) to run, then use '$db'. Otherwise use '$files'. ### TIMESTAMP is a current (at the moment you write your code) timestamp in the format 'YYYY/MM/DD hh:mm:ss'. ### You can use output of `date '+%Y/%m/%d %H:%M:%0S'` command. ### UPGRADE_ACTION is a call to the function that implements your upgrade action. While you can supply arguments here, it is ### advised not to. Function name should reflect what you are upgrading and *from* which version. Usually if you use ### '$db' as STAGE then function name should start with 'db_upgrade_', otherwise with 'upgrade_'. ### ### ### Slightly longer version: ### 'cu_start_upgrade_from_version <version>' calls mark groups of upgrade actions for upgrade from a given version. They ### affect only upgrade actions with '$ver' filter, but they also serve a role of self-documentation and therefore ### should be added for any product version change. ### '$db' marks upgrade actions that will only be executed in prep-install bootstrapper stage. In case of critical failure ### all changes made to Plesk DB here will be automatically reverted. You should do only changes to Plesk DB during ### this stage; don't use anything not packaged with bootstrapper here (you may use db_*() functions). ### '$files' marks upgrade actions that will only be executed in post-install bootstrapper stage. During this stage all ### required '$db' actions have already been completed (unless some of them were optional), all new packaged files ### are already in place and there are no old removed packaged files. You can do pretty much everything at this stage. ### '$ver' marks that whether or not this upgrade action should be called will be checked based on previous DB or product ### version (for use with '$db' and '$files' respectively). This requires cu_start_upgrade_from_version() call somewhere ### above. ### '$ts TIMESTAMP' marks that whether or not this upgrade action should be called will be checked based on core component ### configuration version (which is a timestamp). Any upgrade actions with a TIMESTAMP newer than configuration version ### will be executed. This approach allowes smooth upgrade between builds within a same product version. Use it. ### TIMESTAMP is a string with a current timestamp in any sensible format which allows sane ordering when punctuation is ### stripped. Output of the following command is recommended for use as TIMESTAMP: `date '+%Y/%m/%d %H:%M:%0S'`. ### Usually timestamps will increase from previous upgrade action to the next, but it is not a requirement. ### '$do' is just a required word that should preceed upgrade action call. The call itself may include parameters, but it's ### advised not to, since it's much better to have upgrade actions uniquely identifiable by name. ### ### Upgrade action should be more or less self-contained. Never do several unrelated things in a single upgrade action. ### Never separate related upgrade actons across several calls in core_upgrader() unless there is a good reason to. ### ### Upgrade actions can be either critical or optional. Optional upgrade actions should indicate failure with non-zero return ### code. Critical upgrade actions should indicate failure with die() or similar calls (that terminate execution). Note that ### using most of db_*() functions makes an upgrade action a critical one. Critical failure of a '$db' upgrade action will ### trigger DB rollback. Critical failure of a '$files' upgrade action will switch core configuration into a broken unusable ### state, from which one can recover only by successfully passing all required upgrade actions during bootstrapper repair ### procedure. "Optional" failure of any upgrade action will add it to the list of failed ones and continue with upgrade. ### All failed actions are rerun during bootstrapper repair procedure. ### move_out_custom_vhost_templates() { local move_to="$1" local template_d="$PRODUCT_ROOT_D/admin/conf/templates" local move_to_d= [ -d "$template_d/custom" ] || return 0 if ! move_to_d="`mktemp -d "$template_d/${move_to}_XXXXXXXX" 2>>"$product_log"`"; then warn "move out custom web server templates - cannot create temporary directory under $template_d" return 1 fi local inten="move out custom web server templates from $template_d/custom/ to $move_to_d/" echo_try "$inten" if ! mv -f "$template_d/custom"/* "$move_to_d/"; then warn "$inten - unable to move custom templates" return 1 fi rm -rf "$template_d/custom" suc pp_echo "Custom web server templates that may break web server reconfiguration were moved from $template_d/custom/ to $move_to_d/" cat <<-EOF > "$move_to_d/README" This directory contains previously active custom web server templates that may break web server reconfiguration. Custom templates were moved here on `date` during product update or upgrade. EOF } update_dump_d_mode_17_5_3() { local psa_conf=/etc/psa/psa.conf if [ -f "$psa_conf" ]; then local DUMP_D=`conf_getvar $psa_conf DUMP_D` if [ ! -z "$DUMP_D" ]; then chmod 750 "$DUMP_D" fi fi } update_dump_d_mode_17_8_8() { # Fix default dumps directory from package plesk-base local dump_d='/var/lib/psa/dumps' chmod 750 "$dump_d" || true } set_recipient_address_mapper_17_8_8() { if /opt/psa/admin/sbin/mailmng-server --features | egrep -q "SMTP_Server_package.*postfix" 2>/dev/null; then /opt/psa/admin/sbin/mailmng-server --add-recipient-address-mapper >>"$product_log" 2>&1 || true fi } set_recipient_address_mapper_17_8_9() { local master_cf_path="/etc/postfix/master.cf" [ -f "$master_cf_path" ] || return 0 # Set unlimit postfix-srs spawn processes after upgrade from 17.8.8 # It takes a string from master.cf like: # 127.0.0.1:12346 inet n n n - 4 spawn user=popuser:popuser argv=/usr/lib64/plesk-9.0/postfix-srs # and removes limit of spawn processess sed -i 's|^\(127.*inet.*\)[[:digit:]]\(.*spawn.*postfix-srs\)|\1-\2|g' $master_cf_path } remove_tomcat_support() { local need_reconfigure="`ls -1 $CATALINA_HOME 2>/dev/null | head -1`" [ -z "$need_reconfigure" ] || httpdmng_reconfigure all } upgrade_mail_handlers_17_8_9() { $PRODUCT_ROOT_D/admin/sbin/mail_handlers_control --remove --name=dd52-domainkeys --type=global --queue=before-local $PLESK_LIBEXEC_DIR/mail_spf_restore $PLESK_LIBEXEC_DIR/mail_dk_restore } dovecot_reconfigure_ssl_17_8_10() { is_dovecot_active || return 0 is_compliance_mode=0 [ ! -f "${DOVECOT_INCLUDE_DIR}/11-plesk-security-pci.conf" ] || is_compliance_mode=1 if [ $is_compliance_mode -eq 0 ]; then configure_service_ssl_ciphers_protocols dovecot else local resolver="$PRODUCT_ROOT_D/admin/bin/pci_compliance_resolver" [ -x "$resolver" ] || return 0 $resolver --enable dovecot >>"$product_log" 2>&1 fi } install_advisor_ext() { local inten="install advisor extension" echo_try "$inten" $PRODUCT_ROOT_D/bin/extension --install advisor && suc || warn "$inten" } restore_files_in_etc_from_dpkg_dist_17_8_10() { # Shipped in MU2. # sw-cp-serverd will be restarted by bootstrapper post-install. local files=" /etc/sw-cp-server/conf.d/plesk.inc /etc/cron.daily/50plesk-daily " for file in $files; do if [ -f "$file.dpkg-dist" ]; then mv -f "$file.dpkg-dist" "$file" >>"$product_log" 2>&1 fi done return 0 } add_plesk_service_localdomain_virtual_host() { deferred_httpd_reconfigure } drop_core_version_file() { rm -f "$PRODUCT_ROOT_D/core.version" } drop_readme_psa_shadow() { rm -f "$product_etc/README.psa.shadow" } drop_apsc_conf() { rm -f /etc/sw-cp-server/conf.d/apsc.conf } remove_panel_logs_from_plesk_logrotate() { local inten="transfer panel logs to system logrotate" echo_try "$inten" sed -i \ -e '/psi.log/,/^[^#]*}/d' \ -e '/_log.processed/,/^[^#]*}/d' \ -e '/install\/\*.log/,/^[^#]*}/d' \ -e '/plesk\/\*.log/,/^[^#]*}/d' "$PRODUCT_ROOT_D/etc/logrotate.conf" && suc || warn "$inten" } move_ip_remap_config() { local param_name="ip_auto_remap" local param_value=`db_read_misc_attr "$param_name"` if [ -n "$param_value" ]; then [ "$param_value" != "true" ] || register_service plesk-ip-remapping db_do "delete from misc where param='$param_name'" fi } create_stored_sslmng_configuration() { local sslmng_json="$PLESK_DB_DIR/sslmng.json" [ ! -s "$sslmng_json" ] || return 0 p_echo "Creating $sslmng_json based on current settings in DB" "$PRODUCT_ROOT_D/bin/server_pref" --show | "/usr/bin/python3" -c ' import sys, json prefs = dict((k.strip(), v.strip()) for k, v in [line.split(":", 1) for line in sys.stdin.readlines() if ":" in line]) protos = "SSLv3 TLSv1 TLSv1.1 TLSv1.2 TLSv1.3".split() data = { "services": { "all": { "protocols": [p for p in protos if p in prefs["ssl-protocols"].split()] or protos[1:], "ciphers": prefs["ssl-ciphers"], "cipher_server_order": prefs["ssl-cipher-server-order"] == "true", } } } print(json.dumps(data, indent=4)) ' > "$sslmng_json" 2>>"$product_log" } remove_old_custom_sslmng_configuration() { rm -f "$PLESK_DB_DIR"/ssl_*.conf >>"$product_log" 2>&1 } add_missed_nginx_tls_1_3_protocol() { # Just reapply nginx settings to make sure it will contain TLSv1.3 if this protocol is enabled local sslmng="$PRODUCT_ROOT_D/admin/sbin/sslmng" [ -x "$sslmng" ] || return 0 "$sslmng" --services "nginx" --set >>"$product_log" 2>&1 } remove_nodemng_data_18_0_38() { rm -f /var/lib/plesk/node_versions.json >>"$product_log" 2>&1 } enable_mysql_utf8_18_0_46() { run_configure_mysql_funcs configure_mysqldump_utf8mb4 } core_psa_purge_sysvinit() { [ -f /etc/init.d/psa ] || return 0 ! [ -x /usr/sbin/update-rc.d ] || /usr/sbin/update-rc.d -f psa remove rm -f /etc/init.d/psa } clear_sw_engine_opcache() { set_sw_engine_params pleskrc sw_engine reload } enable_nginx_passenger_module() { [ -e "/etc/nginx/modules.available.d/phusion-passenger.load" ] || return 0 if [ ! -e "/etc/nginx/modules.conf.d" ] ; then mkdir "/etc/nginx/modules.conf.d" fi ln -snfT "../modules.available.d/phusion-passenger.load" "/etc/nginx/modules.conf.d/phusion-passenger.conf" if [ ! -e "/etc/nginx/conf.d" ] ; then mkdir "/etc/nginx/conf.d" fi ln -snfT "../modules.available.d/phusion-passenger.conf" "/etc/nginx/conf.d/phusion-passenger.conf" deferred_nginx_restart } fix_domainkeys_access() { local domainkeys_conf_d="/etc/domainkeys/" [ -d "$domainkeys_conf_d" ] || return 0 find "$domainkeys_conf_d" -type f -execdir chown root:popuser {} \; -execdir chmod 0640 {} \; } disable_reuseport_http3() { set_swcpserver_params [ -f "$swcpserver_includedir/http3_plesk.inc" ] || return 0 for f in http3_plesk.inc http3_ipv6_ports.inc; do [ -f "$swcpserver_includedir/$f" ] || continue sed -i 's/\ reuseport//g' "$swcpserver_includedir/$f" done deferred_swcpserver_restart } disable_panel_http3() { $PRODUCT_ROOT_D/bin/http3_pref --disable -panel >>"$product_log" 2>&1 } clean_backend_cache() { db_do "DELETE FROM BackendCache" rm -f "$PRODUCT_ROOT_D/tmp/*.cache" } clean_upgrade_info() { db_do "TRUNCATE smb_productUpgrades; TRUNCATE smb_componentUpdates;" db_do "DELETE FROM misc WHERE param = 'available_patch'" } forced_key_upgrade_cmd() { local KEYUPGRADE_PATH="/usr/lib/plesk-9.0/key-upgrade" # set to run key upgrade script p_echo "Starting forced Plesk Key upgrade task" nohup /bin/sh -c "$KEYUPGRADE_PATH force 2>&1 | logger -p daemon.err -t plesk-key-upgrader" \ >/dev/null 2>&1 & } encrypt_psa_shadow() { local init_conf_bin="$PRODUCT_ROOT_D/bin/init_conf" if [ ! -e "$admin_password_needs_encryption_flag" ]; then p_echo "Admin password encryption not required" return fi local inten="encrypt admin password" echo_try "$inten" [ -x "$init_conf_bin" ] && \ PSA_PASSWORD="$admin_passwd" "$init_conf_bin" --set-admin-password -passwd '' -encrypted-password -allow-weak-passwords >> "$product_log" 2>&1 if [ "$?" -eq 0 ]; then admin_passwd= suc set_mysql_auth else warn "encrypt admin password" fi rm -f "$admin_password_needs_encryption_flag" } setup_utf8_locale() { if [ ! -x /usr/bin/locale -o ! -x /usr/sbin/locale-gen ]; then pnnl_echo "Warning: unable to allow UTF8 locale - package 'locales' is not installed!" return fi /usr/bin/locale -a | grep -q en_US.utf8 if [ $? -eq 0 ]; then pnnl_echo "UTF8 locale is already allowed" return fi /usr/sbin/locale-gen "en_US.UTF-8" } setup_plesk_firewalld_configuration() { is_product_installation || return 0 local firewall_cmd="/usr/bin/firewall-cmd" local firewall_offline_cmd="/usr/bin/firewall-offline-cmd" local firewall_plesk_zone="$PRODUCT_ROOT_D/etc/firewalld/zones/plesk.xml" [ -x "$firewall_cmd" ] || return 0 if [ -n "$PLESK_INSTALLER_SKIP_FIREWALLD" ]; then pp_echo "Skip firewalld configuration: \$PLESK_INSTALLER_SKIP_FIREWALLD is set" return 0 fi echo_try "set up plesk firewalld configuration" if "$firewall_cmd" --state > /dev/null; then "$firewall_cmd" --permanent --new-zone-from-file="$firewall_plesk_zone" \ && "$firewall_cmd" --reload \ && "$firewall_cmd" --set-default-zone="plesk" \ && suc || warn "setting up plesk firewalld configuration" else "$firewall_offline_cmd" --new-zone-from-file="$firewall_plesk_zone" \ && "$firewall_offline_cmd" --set-default-zone="plesk" \ && suc || warn "setting up offline plesk firewalld configuration" fi } detect_network_manager() { is_product_installation || return 0 local ifmng_cmd="$PRODUCT_ROOT_D/admin/sbin/ifmng" local manager= manager=$($ifmng_cmd --detect-manager) local ret_code="$?" if [ "$ret_code" -ne 0 ] ; then warn "detection of currently active network manager" return 0 fi p_echo "Detected current network manager: $manager" $ifmng_cmd --set-manager "$manager" } gethostbyname() { local name="$1" [ -n "$1" ] || return 1 perl -e ' use Socket; my $host = shift; my $info = gethostbyname($host) or exit 1; print inet_ntoa(scalar($info)); ' "$name" } setup_hostname() { if [ -n "$PLESK_DISABLE_HOSTNAME_CHECKING" ]; then p_echo "Hostname configuration skipped because PLESK_DISABLE_HOSTNAME_CHECKING is set" return fi detect_vz if [ "$PLESK_VZ" = "1" ]; then p_echo "Hostname configuration skipped on Virtuozzo environment" return fi detect_lxc if [ "$PLESK_LXC" = "1" ]; then p_echo "Hostname configuration skipped on LXC / Docker environment" return fi local hostname="`(hostname -f || hostname) 2>>$product_log`" local ipaddress="`gethostbyname $hostname 2>>$product_log`" get_domain if [ -z "$non_fqdn_hostname" -a -n "$ipaddress" ]; then p_echo "FQDN hostname $fullhost already configured to $ipaddress" return fi if [ -z "$ipaddress" ]; then # Hostname doesn't resolved to any ip address, so we got ip address from system # or fallback to 127.0.0.1 ipaddress="`get_ip_address_list | head -1`" [ -n "$ipaddress" ] || ipaddress="127.0.0.1" fi local inten="configure hostname $fullhost with address $ipaddress" echo_try "$inten" "$PRODUCT_ROOT_D/admin/sbin/reconfig" "$host" "$domain" "$ipaddress" >> "$product_log" 2>&1 && suc || warn "$inten" } core_bootstrapper_post_install() { set_local_params check_product_root uses_mysql_client if is_product_installation; then # following commands have been moved from install_post # because they try to call cu utils which may not be ready to work on that stage pbm_create_certificate encrypt_psa_shadow setup_hostname fi selinux_init # en_US.UTF-8 requred for Backup/Restore and vhostmng-find setup_utf8_locale import_default_certificate upgrade_apsc_post_stage core_run_upgrade_post_stage if is_product_installation; then # Seems it should be in service-node-utilities update_registered_services_db_for_resctrl disable_public_html_for_httpd disable_home_public_html_for_httpd fi # Deprecated. Should be removed later. label_file_once "${PRODUCT_ETC_D}/webalizer.conf" "${AUTOGENERATED_CONFIGS}" run_bl_upgrade # During Plesk installation, sw-engine (psasem minit) will create # $PRODUCT_ROOT_D/var with wrong ownership on debian/ubuntu if [ -d "${PRODUCT_ROOT_D}/var" ]; then chown psaadm:psaadm "${PRODUCT_ROOT_D}/var" chmod 0755 "${PRODUCT_ROOT_D}/var" fi clean_backend_cache clean_upgrade_info packagemng_set_dirty_flag autoinstall_extensions if is_product_upgrade; then forced_key_upgrade_cmd clean_panel_sessions run_configure_mysql_funcs try_disable_mysqldump_utf8mb4 fi # Seems it should be executed with exact service names # Now it looks very common. # E.g. mail services should be configured in mail related packages if is_product_installation; then configure_ssl_ciphers_protocols fi selinux_close if is_product_installation; then register_service sw-engine register_service sw-cp-server register_service psa fi setup_plesk_firewalld_configuration update_upgrade_history if is_product_installation; then install_default_license_key deferred_nginx_configure fi detect_network_manager } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. progname="$0" action="$1" usage() { echo "Usage: $progname <post-install | repair>" exit 1 } if [ "X${PLESK_INSTALLER_DEBUG}" != "X" ]; then set -x fi if [ "X${PLESK_INSTALLER_STRICT_MODE}" != "X" ]; then set -e fi reexec_with_clean_env "$@" export PLESK_INTERNAL_PHP_EXEC=1 PACKAGE_ID="core" PACKAGE_ACTION="bootstrapper_post" case "$action" in post-install) package_script_safe_run_with_report plesk-core 18.0 core_bootstrapper_post_install ;; repair) package_script_safe_run_with_report plesk-core 18.0 core_run_repair ;; *) usage ;; esac