D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
opt
/
psa
/
bootstrapper
/
pp18.0.68-bootstrapper
/
Filename :
bootstrapper.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" | mysql >> "$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'" | mysql >> "$product_log" 2>&1 && \ echo "GRANT ALL PRIVILEGES ON $apsc_db_name.* TO '$apsc_db_user'@'$host'" | mysql >> "$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 mysql < "$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" | mysql >> "$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'" | mysql >> "$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_prep_stage() { local action_name="`apsc_upgrader_action_name $1`" local optional_repair_arg="`apsc_upgrader_repair_arg $1`" # if DB is not created (clean installation or upgrade from versions where there is no APS DB) just exit from PRE stage # DB will be created in POST stage check_apsc_installed || return 0 read_apsc_connection_params_db p_echo "===> Cumulative APS controller database ($apsc_db_name) ${action_name} has been started." local apsc_db_version=`read_apsc_current_db_version` if [ -z "$apsc_db_version" ] || ! expr "$apsc_db_version" + 0 >/dev/null 2>&1 ; then # db_version is not set or invalid. Create it. apsc_db_version="$prev_short_version" save_apsc_current_db_version "$apsc_db_version" fi cu_run_upgrader "apsc" apsc_upgrader \ --load-config-data apsc_cu_load_config_data --store-config-data apsc_cu_store_config_data \ --prepare apsc_upgrader_prepare --rollback apsc_upgrader_rollback \ --from-version "$apsc_db_version" --stage "prep" $optional_repair_arg if [ "$?" -eq 0 ]; then p_echo "===> Cumulative ${action_name} of APS controller database has been completed." else pp_echo "===> APS controller database was not upgraded completely. See installation log for details." 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 } apsc_run_repair() { local ret=0 if ! check_apsc_installed; then install_apsc else upgrade_apsc_prep_stage repair if ! apsc_passwd_repair; then warn "fix credentials for APS controller database" ret=1 fi upgrade_apsc_post_stage repair fi return $ret } apsc_passwd_repair() { check_apsc_installed || return 0 read_apsc_connection_params_db is_local_mysql_instance "$apsc_db_host" || return 0 ! sw_engine_pleskrun "$PRODUCT_ROOT_D/admin/plib/scripts/check_apsc_connection.php" >>"$product_log" 2>&1 || return 0 local apsc_db_passwd apsc_db_passwd=`get_random_string 12 'A-Za-z0-9_'` reset_user_password "$apsc_db_user" "$apsc_db_passwd" "$apsc_db_host" && \ save_apsc_connection_params_db && \ sw_engine_pleskrun "$PRODUCT_ROOT_D/admin/plib/scripts/check_apsc_connection.php" >>"$product_log" 2>&1 } 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" | mysql >> "$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_prepare() { local apsc_db_name=`db_read_misc_attr "aps_database"` db_backup "mysql.preupgrade.apsc.${prev_version}-${product_version}" "$apsc_db_name" APSC_UPGRADER_DB_BACKUP="$dfile" } apsc_upgrader_rollback() { [ -n "$APSC_UPGRADER_DB_BACKUP" ] || return 1 pp_echo "===> Restoring APS controller database from backup '$APSC_UPGRADER_DB_BACKUP'" zcat "$APSC_UPGRADER_DB_BACKUP" | mysql_anydb || { pp_echo " [ERROR] Database restoration failed. Try to restore database from '$APSC_UPGRADER_DB_BACKUP' manually." return } APSC_UPGRADER_DB_BACKUP= } 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() { : } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. drop_upgrade_flag() { # created by core bootstrapper prep-install rm -f "${PRODUCT_ROOT_D}/version.upg" # created by bootstrapper via write_target_plesk_version rm -f /var/lock/plesk-target-version* } # 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" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # db_backup [ dump_prefix [ db1 [ db2 [...] ] ] ] db_backup() { local timestamp=`date +%Y%m%d-%H%M%0S` local dfile_prefix="${1:-mysql.preupgrade.${prev_version}-${product_version}}" dfile="$DUMP_D/${dfile_prefix}.${timestamp}.dump.gz" local databases [ -n "$*" ] && shift if [ -n "$*" ]; then databases="$*" else local apsc_db_name=`db_read_misc_attr "aps_database"` local psa_db_name="`get_dsn plesk dbname`" local horde_db_name="`get_dsn horde dbname`" local roundcube_db_name="`get_dsn roundcube dbname`" local phpmyadmin_db_name="`get_phpmyadmin_db_name`" local wpb_db_name="sitebuilder5" databases="mysql ${psa_db_name:-"$PRODNAME"} $apsc_db_name ${horde_db_name:-"horde"} ${roundcube_db_name:-"roundcubemail"} $phpmyadmin_db_name $wpb_db_name" fi if test -f $dfile; then # dump names should not collide, but better be safe than sorry local i i=1 while test -f "$dfile.$i"; do i=$(($i+1)) done mv -v "$dfile" "$dfile.$i" >> "$product_log" || die "save previous backup $dfile" fi inten="backup MySQL database" db_backup_aux "$inten" "$dfile" $databases p_echo " MySQL databases are dumped to $dfile" } db_backup_aux() { local inten="$1" local dfile="$2" shift 2 local dump_dir="`dirname "$dfile"`" echo_try "$inten" mkdir -p -m 750 "$dump_dir" ( umask 077 && touch "$dfile" ) || die "$inten ('umask 077; touch $dfile' failed with $?)" if [ -z "${PLESK_ALLOW_INSECURE_DUMPS:-}" ]; then local perms="`stat -c %a "$dfile"`" if [ "$perms" != '600' ]; then # try to perform chmod chmod 0600 "$dfile" perms="`stat -c %a "$dfile"`" if [ "$perms" != '600' ]; then # let's check permissions on $dump_dir local dir_perms="`stat -c %a "$dump_dir"`" if ! expr match "$dir_perms" '.*0$' >/dev/null; then rm -f "$dfile" die "$inten: '$dfile' is created with insecure permissions '$perms' (expected '600'), " \ "dumps directory '$dump_dir' has world-readable permissions '$dir_perms'. " \ "Try to fix permissions on '$dump_dir' or/and fix mount options for '$dump_dir'. " \ "(Or set environment variable PLESK_ALLOW_INSECURE_DUMPS=1)" fi fi fi fi local compress='cat -' ! expr match "$dfile" '.*.gz$' > /dev/null || compress='gzip -9' mysqldump_stderr=$(mktemp) ( set -o pipefail dump_db "$@" 2>"$mysqldump_stderr" | $compress > "$dfile" ) || { # Save stderr to product logs cat "$mysqldump_stderr" >> "$product_problems_log" cat "$mysqldump_stderr" >> "$product_log" # Write output to stderr cat "$mysqldump_stderr" >&2 rm -f "$dfile" "$mysqldump_stderr" die "$inten" } cat "$mysqldump_stderr" >&2 rm -f "$mysqldump_stderr" suc } # 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" mysql -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 } # --- db_install helpers --- dbi_try_create_db() { local mysql_db_name="mysql" echo "CREATE DATABASE $DB_NAME DEFAULT CHARSET=utf8" | mysql >> "$product_log" 2>&1 } dbi_modify_db_name() { DB_NAME="${DB_BASENAME}_`get_random_string 4 'a-z0-9'`" } dbi_modify_db_user() { DB_USER_NAME="${DB_USER_BASENAME}_`get_random_string 4 'a-z0-9'`" } dbi_try_initialize_db() { local initializer_func="$1" local mysql_db_name="$DB_NAME" if [ "$DB_USER_NAME" != "admin" ]; then local mysql_user="--user=$DB_USER_NAME" local mysql_passwd="$DB_PASSWORD" fi $initializer_func } 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 } db_install() { # Usage: db_install "<DB description for log messages>" options ... (see below) local opt_db_desc="$1" shift local opt_name= opt_basename= opt_user_name= opt_user_basename= local opt_user_creator= opt_skip_when_func= opt_initializer_func= opt_commit_func= local inten="install $opt_db_desc database" while [ "$#" -gt 0 ]; do case "$1" in --name) # New DB name. If --basename is also specified, then this is the initial name to try. opt_name="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --basename) # New DB name prefix. Several tries would be made to create database with name '<basename>_<random suffix>'. opt_basename="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --user-name) # New DB user name. If --user-basename is also specified, then this is the initial name to try. opt_user_name="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --user-basename) # New DB user prefix. Several tries would be made to create user with name '<user basename>_<random suffix>'. opt_user_basename="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --user-creator) # DB user creation callback. If not present, 'admin' account will be used for DB creation and initialization. opt_user_creator="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --skip-when) # Predicate function that returns 0 when DB creation should be skipped (e.g. if DB already exists). opt_skip_when_func="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --initializer) # DB initializer callback. Initialize your DB schema and populate DB here. Required argument. # MySQL connection will be properly set up for this callback - use db_*() and mysql*() functions. opt_initializer_func="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --commit) # Commit callback. Save DB connection properties here. Following variable will be available in it: # DB_HOST, DB_NAME, DB_USER_NAME, DB_PASSWORD (empty if DB_USER_NAME = "admin"). opt_commit_func="$2" [ "$#" -ge 2 ] && shift 2 || break ;; *) die "$inten: unknown key '$1'" ;; esac done # Sanity checks [ -n "$opt_db_desc" ] || die "$inten: empty database description" [ -z "$opt_user_creator" ] || is_function "$opt_user_creator" || die "$inten: invalid value for --user-creator argument" [ -z "$opt_skip_when_func" ] || is_function "$opt_skip_when_func" || die "$inten: invalid value for --skip-when argument" [ -n "$opt_initializer_func" ] && is_function "$opt_initializer_func" || die "$inten: invalid value for --initializer argument" [ -z "$opt_commit_func" ] || is_function "$opt_commit_func" || die "$inten: invalid value for --commit argument" [ -n "$opt_name$opt_basename" ] || die "$inten: at least --name or --basename must be specified" [ -z "$opt_user_creator" -o -n "$opt_user_name$opt_user_basename" ] || die "$inten: at least --user-name or --user-basename must be specified if --user-creator is set" # Further code assumes that current MySQL access credentials are sufficient for DB creation if [ -n "$opt_skip_when_func" ] && $opt_skip_when_func ; then p_echo "Installation of $opt_db_desc database is skipped" return 0 fi echo_try "$inten" local DB_HOST="localhost" local DB_NAME="$opt_name" local DB_BASENAME="$opt_basename" local DB_USER_NAME="$opt_user_name" local DB_USER_BASENAME="$opt_user_basename" local DB_PASSWORD="`get_random_string 12 'A-Za-z0-9_'`" ! [ -z "$DB_NAME" -a -n "$DB_BASENAME" ] || dbi_modify_db_name ! [ -z "$DB_USER_NAME" -a -n "$DB_USER_BASENAME" ] || dbi_modify_db_user # Create DB if [ -n "$DB_BASENAME" ]; then repeat_until_succeeds dbi_try_create_db dbi_modify_db_name 50 || return 1 else dbi_try_create_db || return 1 fi # Create user if required, otherwise use current user if [ -n "$opt_user_creator" ]; then if [ -n "$DB_USER_BASENAME" ]; then repeat_until_succeeds "$opt_user_creator" dbi_modify_db_user 50 || return 1 else $opt_user_creator || return 1 fi else DB_USER_NAME="admin" DB_PASSWORD= fi dbi_try_initialize_db "$opt_initializer_func" || return 1 [ -z "$opt_commit_func" ] || $opt_commit_func p_echo "Installation of $opt_db_desc database finished" } db_create_user() { local opt_user= opt_host= opt_password= opt_force= opt_host="localhost" while [ "$#" -gt 0 ]; do case "$1" in --user) # New DB User name. opt_user="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --host) # New DB User host. If omitted, localhost would be used opt_host="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --password) # New DB user password. opt_password="$2" [ "$#" -ge 2 ] && shift 2 || break ;; --force) # Recreate user if it exists. If this option is not set # error should be returned if the user exists opt_force="1" shift ;; *) die "db_create_user(): unknown key '$1'" ;; esac done # Sanity checks [ -n "$opt_user" ] || die "db_create_user(): empty user" [ -n "$opt_host" ] || die "db_create_user(): empty host" [ -n "$opt_password" ] || die "db_create_user(): empty password" # Check that user already exists local mysql_db_name="mysql" db_select "SELECT User FROM mysql.user WHERE User='$opt_user' AND Host='$opt_host'" if [ -n "$db_select_output" ]; then p_echo "user $opt_user already exists" [ -n "$opt_force" ] || return 1 db_do "DROP USER '$opt_user'@'$opt_host'" fi local password="`mysql_quote_string $opt_password`" db_do "CREATE USER '$opt_user'@'$opt_host' IDENTIFIED BY '$password'" } db_drop_user() { # Drops all entries for given user name local user="$1" local mysql_db_name="mysql" db_select "(SELECT DISTINCT Host FROM user WHERE User = '$user') UNION (SELECT DISTINCT Host FROM db WHERE User = '$user') UNION (SELECT DISTINCT Host FROM tables_priv WHERE User = '$user') UNION (SELECT DISTINCT Host FROM columns_priv WHERE User = '$user') UNION (SELECT DISTINCT Host FROM procs_priv WHERE User = '$user') ORDER BY Host" [ -n "$db_select_output" ] || return 0 for host in $db_select_output; do db_do "DROP USER '$user'@'$host'" done db_do "FLUSH PRIVILEGES" } ### 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_webmail() { touch "/var/lock/plesk_deferred_httpd_reconfigure_webmail" } 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_webserver_apache_configure() { touch "/var/lock/plesk_deferred_webserver_apache_configure" } run_now_webserver_apache_configure() { [ ! -x "$PLESK_LIBEXEC_DIR/services/webserver.apache" ] || $PLESK_LIBEXEC_DIR/services/webserver.apache configure } deferred_ftp_proftpd_configure() { touch "/var/lock/plesk_deferred_ftp_proftpd_configure" } run_now_ftp_proftpd_configure() { [ ! -x "$PLESK_LIBEXEC_DIR/services/ftp.proftpd" ] || $PLESK_LIBEXEC_DIR/services/ftp.proftpd configure } deferred_awstats_configure() { touch "/var/lock/plesk_deferred_awstats_configure" } run_now_awstats_configure() { [ ! -x "$PLESK_LIBEXEC_DIR/statistics/awstats" ] || $PLESK_LIBEXEC_DIR/statistics/awstats configure } run_now_notify_imap_pop3_service_about_mailserver_change() { set_courier_imap_params if pleskrc courier_authdaemon exists; then pleskrc courier_authdaemon condrestart fi # Dovecot, on the other hand, will notice authentication DB (mail server) change # in a couple of minutes when authentication worker respawns. No need to restart entire service. } deferred_swcpserver_restart() { touch "/var/lock/plesk_deferred_swcpserver_restart" } deferred_install_postfix_smtpd_sasl_config() { touch "/var/lock/plesk_deferred_install_postfix_smtpd_sasl_config" } deferred_postfix_restart() { touch "/var/lock/plesk_deferred_postfix_restart" } deferred_mail_pc_driver_transport_restore() { # restarts postfix touch "/var/lock/plesk_deferred_mail_pc_driver_transport_restore" } deferred_systemctl_daemon_reload() { touch "/var/lock/plesk_deferred_systemctl_daemon_reload" } run_now_logrotate_anonimization_setup() { uses_mysql_client local logrot_mng="$PRODUCT_ROOT_D/admin/sbin/logrot_mng" if [ "`db_read_misc_attr 'logrotate_anonymize_ips'`" = "true" -a -x "${logrot_mng}" ]; then for target in `cat "/var/lock/plesk_deferred_logrotate_anonimization_setup" | sort | uniq`; do "$logrot_mng" --${target}-logs --anonymize-ip=true || warn "setup log anonimization for $target" done fi } ### perform actions : perform_deferred_actions() { # Actions are not independent so order of some actions is important: # - Commands in one action may be subset of commands in another action # so the former is not necessary if the latter is executed. # - One action may request another action so the latter must follow # the former. # For example, customers often complains if they notice unnecessary # (repeated) service restart. Beware of manipulations with # remove_deferred_action_flag() below. # # Keep mail_pc_driver_transport_restore just before postfix_restart # as the former restarts postfix though a core tool. # Keep install_postfix_smtpd_sasl_config before mail_pc_driver_transport_restore # as the latter performs postfix restart expected by the former. local flag_files="\ systemctl_daemon_reload \ relabel_plesk_directories \ super_server_action_configure \ ftp_proftpd_configure \ webserver_apache_configure \ nginx_configure \ httpd_reconfigure_all \ httpd_reconfigure_webmail \ httpd_reconfigure \ awstats_configure \ apache_restart \ nginx_restart \ mail_restore_all \ mail_spam_restore_full \ mail_outgoing_restore_full \ notify_imap_pop3_service_about_mailserver_change \ apply_ssl_settings_to_imap_pop3 \ apply_ssl_settings_to_smtp \ mail_restore_imap_pop3 \ mail_mailbox_restore_full \ product_restart \ swcpserver_restart \ logrotate_anonimization_setup \ install_postfix_smtpd_sasl_config \ mail_pc_driver_transport_restore \ postfix_restart \ named_reload \ named_restart \ mailman3_configuration_fixup \ mailman3_restart \ " local res=0 for flag in $flag_files; do if has_deferred_action_flag "$flag"; then local local_res="" case $flag in systemctl_daemon_reload) [ ! -x /bin/systemctl ] || /bin/systemctl daemon-reload ;; ftp_proftpd_configure) run_now_ftp_proftpd_configure ;; webserver_apache_configure) run_now_webserver_apache_configure ;; awstats_configure) run_now_awstats_configure ;; httpd_reconfigure_all) httpdmng_reconfigure all ;; httpd_reconfigure_webmail) httpdmng_reconfigure webmail ;; httpd_reconfigure) httpdmng_reconfigure server ;; relabel_plesk_directories) if is_product_installation; then relabel_plesk_directories "$HTTPD_VHOSTS_D" "$PLESK_MAILNAMES_D" else relabel_plesk_directories fi ;; mail_restore_all) mail_restore_all ;; mail_mailbox_restore_full) local mail_mailbox_restore="$PLESK_LIBEXEC_DIR/mail_mailbox_restore" [ ! -x "$mail_mailbox_restore" ] || "$mail_mailbox_restore" >>"$product_log" 2>&1 ;; mail_spam_restore_full) local mail_spam_restore="$PLESK_LIBEXEC_DIR/mail_spam_restore" [ ! -x "$mail_spam_restore" ] || "$mail_spam_restore" >>"$product_log" 2>&1 ;; mail_outgoing_restore_full) local mail_outgoing_restore="/usr/lib/plesk-9.0/remote_mail_restore/mail_outgoing_restore" [ ! -x "$mail_outgoing_restore" ] || "$mail_outgoing_restore" >>"$product_log" 2>&1 ;; notify_imap_pop3_service_about_mailserver_change) run_now_notify_imap_pop3_service_about_mailserver_change ;; apply_ssl_settings_to_imap_pop3) configure_service_ssl_ciphers_protocols mail-imap-pop3 ;; apply_ssl_settings_to_smtp) configure_service_ssl_ciphers_protocols mail-smtp ;; apache_restart) set_apache_params pleskrc apache restart ;; nginx_restart) set_nginx_params local nginx_ctl="$PRODUCT_ROOT_D/admin/sbin/nginx_control" if [ -x "${nginx_ctl}" -a -x "${PRODUCT_RC_D}/${nginx_service}" ]; then "$nginx_ctl" --restart >>"$product_log" 2>&1 || warn "restart nginx web-server" fi ;; nginx_configure) set_nginx_params if nginx_is_installed; then initial_enable_nginx \ && remove_deferred_action_flag httpd_reconfigure_all fi ;; super_server_action_configure) set_xinetd_params register_service "$xinetd_service" pleskrc xinetd try-reload ;; mail_restore_imap_pop3) local mail_imap_restore="/usr/lib/plesk-9.0/remote_mail_restore/mail_imap_restore" [ ! -x "$mail_imap_restore" ] || "$mail_imap_restore" >>"$product_log" 2>&1 ;; product_restart) set_psa_params pleskrc psa restart ;; swcpserver_restart) set_swcpserver_params pleskrc swcpserver restart ;; logrotate_anonimization_setup) run_now_logrotate_anonimization_setup ;; install_postfix_smtpd_sasl_config) deferred_postfix_restart install_postfix_smtpd_sasl_config ;; mail_pc_driver_transport_restore) local mail_transport_restore="/usr/lib/plesk-9.0/remote_mail_restore/mail_transport_restore" if [ -x "$mail_transport_restore" ]; then "$mail_transport_restore" >>"$product_log" 2>&1 \ && remove_deferred_action_flag postfix_restart fi ;; postfix_restart) set_postfix_params pleskrc postfix restart || : ;; named_reload) set_named_params pleskrc named reload || : ;; named_restart) set_named_params pleskrc named restart || : ;; mailman3_configuration_fixup) if [ -x "/usr/lib/plesk-9.0/mailman_conf_init" ]; then /usr/lib/plesk-9.0/mailman_conf_init "/etc/mailman3/mailman.cfg" >>"$product_log" 2>&1 ||: fi fix_mailman_django_settings >>"$product_log" 2>&1 fix_mailman_postorius_template_base_url >>"$product_log" 2>&1 fix_mailman_web_account_adapter >>"$product_log" 2>&1 fix_mailman_web_middleware >> "$product_log" 2>&1 /bin/systemctl try-restart mailman3 mailman3-web || : ;; mailman3_restart) /bin/systemctl try-restart mailman3 mailman3-web || : ;; *) ;; esac local temp_res=$? if [ -z "$local_res" ]; then local_res="$temp_res" fi if [ "0" = "$local_res" ]; then remove_deferred_action_flag $flag else res=1 p_echo "Warning : failed to perform deferred action : $flag" fi fi done run_bl_upgrade --deferred || res=$? drop_upgrade_flag return $res } has_deferred_action_flag() { local flag="$1" [ -e "/var/lock/plesk_deferred_$flag" ] } remove_deferred_action_flag() { local flag="$1" rm -f "/var/lock/plesk_deferred_$flag" # check for depending actions: case "$flag" in httpd_reconfigure_all) remove_deferred_action_flag httpd_reconfigure_webmail remove_deferred_action_flag httpd_reconfigure ;; httpd_reconfigure) remove_deferred_action_flag apache_restart remove_deferred_action_flag nginx_restart ;; mail_restore_all) remove_deferred_action_flag mail_restore_imap_pop3 remove_deferred_action_flag mail_pc_driver_transport_restore remove_deferred_action_flag postfix_restart ;; product_restart) remove_deferred_action_flag swcpserver_restart ;; mailman3_configuration_fixup) remove_deferred_action_flag mailman3_restart ;; *) ;; esac } dump_incomplete_deferred_actions() { ls -1 /var/lock/plesk_deferred_* 2>/dev/null || : } ### 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" ] } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # -*- vim:ft=sh prepare_dsn_ini_section() { local section_name="$1" local connection_string="$2" local common_section="$3" local default_dbname="$4" local dbname_parameter="dbname" local section= [ "$section_name" != "common" ] || dbname_parameter="dbname_prefix" if [ -n "$connection_string" ]; then local dbname="`parse_mysql_dsn_get_db "$connection_string"`" local host="`parse_mysql_dsn_get_host "$connection_string"`" local port="`parse_mysql_dsn_get_port "$connection_string"`" local username="`parse_mysql_dsn_get_user "$connection_string"`" local password="`parse_mysql_dsn_get_passwd "$connection_string"`" [ -n "$dbname" ] || dbname="$default_dbname" [ -n "$port" ] || port="3306" section="`cat <<-EOL [$section_name] type = MySQL $dbname_parameter = $dbname host = $host port = $port username = $username password = $password EOL `" elif [ -n "$common_section" ]; then section=`echo "$common_section" | sed -e "s/^\[common\]/\[$section_name\]/" -e "s/^dbname_prefix\s*=\s*\(.*\)$/dbname = \1$default_dbname/g"` fi echo "$section" } try_recreate_dsn_config() { # this function should be called only during clean installation # it removes existing config and then tries to write new one # if corresponding environment variables are set local dsn_ini="/etc/psa/private/dsn.ini" rm -f "$dsn_ini" local common="`prepare_dsn_ini_section common "$PLESK_DB_DSN_PREFIX"`" local plesk="`prepare_dsn_ini_section plesk "$PLESK_DB_DSN" "$common" "${PRODNAME:-psa}"`" local apsc="`prepare_dsn_ini_section apsc "$PLESK_DB_APSC_DSN" "$common" apsc`" local horde="`prepare_dsn_ini_section horde "$PLESK_DB_HORDE_DSN" "$common" horde`" local roundcube="`prepare_dsn_ini_section roundcube "$PLESK_DB_ROUNDCUBE_DSN" "$common" roundcubemail`" [ -n "$plesk" -a -n "$apsc" ] || return 1 local dsn_ini_file_dir="`dirname "$dsn_ini"`" if [ ! -d "$dsn_ini_file_dir" ]; then mkdir -p "$dsn_ini_file_dir" chown psaadm:root "$dsn_ini_file_dir" chmod 0700 "$dsn_ini_file_dir" fi ( umask 0077 touch "$dsn_ini" ) chown psaadm:root "$dsn_ini" cat > "$dsn_ini" <<-EOF `printf "${AUTOGENERATED_CONFIGS//#/;}"` $plesk $apsc $horde $roundcube EOF } 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" } test_dsn() { [ -n "$dsn_user" ] || simply_die "DSN '$dsn_name' is invalid: 'username' is not specified" [ -n "$dsn_passwd" ] || simply_die "DSN '$dsn_name' is invalid: 'password' is not specified" [ -n "$dsn_host" ] || simply_die "DSN '$dsn_name' is invalid: 'host' is not specified" [ -n "$dsn_port" ] || simply_die "DSN '$dsn_name' is invalid: 'port' is not specified" [ -n "$dsn_db_name" ] || simply_die "DSN '$dsn_name' is invalid: 'dbname' is not specified" local mysql_user="--user $dsn_user" local mysql_passwd="$dsn_passwd" local mysql_host="--host $dsn_host" local mysql_port="--port $dsn_port" local mysql_db_name # check connection only at first to make sure that credentials are valid mysql_raw_anydb -e '' || simply_die "Unable to establish test connection with 'mysql://$dsn_user:${dsn_passwd//?/*}@$dsn_host:$dsn_port'." # inspect set of privileges granted to user # privileges related to particular schema stored in information_schema.schema_privileges # global ones are stored in information_schema.user_privileges # try to obtain set of privileges which should be granted true set_horde_params set_roundcube_params local privileges if is_function "set_${dsn_name}_params"; then "set_${dsn_name}_params" eval "privileges=(\"\${${dsn_name}_db_user_privileges[@]}\")" fi # use full set of privileges which is equal to 'ALL PRIVILEGES' # if set of privileges isn't requested explicitly or if 'ALL PRIVILEGES' is required [ -n "${privileges[*]}" -a "${privileges[*]/ALL PRIVILEGES/}" = "${privileges[*]}" ] || privileges=( "ALTER" "ALTER ROUTINE" "CREATE" "CREATE ROUTINE" "CREATE TEMPORARY TABLES" "CREATE VIEW" "DELETE" "DROP" "EVENT" "EXECUTE" "INDEX" "INSERT" "LOCK TABLES" "REFERENCES" "SELECT" "SHOW VIEW" "TRIGGER" "UPDATE" ) local query=( "select ps.privilege_type" "from (" ) for p in "${privileges[@]}"; do query+=( "select '$p' as privilege_type" "union all" ) done unset 'query[${#query[@]}-1]' # mysql returns current user as unescaped user@host # but information_schema operates escaped one # see https://bugs.mysql.com/bug.php?id=75423 local current_user="`mysql_raw_anydb -e 'select CURRENT_USER()'`" local current_host="'${current_user##*@}'" current_user="'${current_user%@*}'@'${current_user##*@}'" # simple substring replacement cannot be used for escaping of quotes # due to differencies between bash versions # "${var//\'/\'}" works fine on bash-4.2.46 shipped with RH7 # but bash"${var//\'/\\\'}" should be used since 4.4.20 shipped with RH8 local current_user_esc="$(echo "$current_user" | sed -e "s/'/\\\\'/g")" # We need to retrieve roles from the specific role_edges table in AWS Aurora MySQL. # Because default Aurora user has no necessary privileges, while the role it belongs to does. # If the database is not Aurora, the request will just fail, and we will not use the roles in the privileges check. local roles="" local role_list="" local role_esc="" roles="`mysql_raw_anydb -e \"select CONCAT(FROM_USER, '@', FROM_HOST) from mysql.role_edges where TO_USER='$dsn_user' and TO_HOST=$current_host\"`" || : while IFS= read -r role; do if [ -z "$role" ]; then continue fi role_esc="'${role%@*}'@'${role##*@}'" role_esc="$(echo "$role_esc" | sed -e "s/'/\\\\'/g")" role_list="$role_list, '$role_esc'" done <<< "$roles" query+=( ") ps" "where" " not exists (" " select 42" " from user_privileges up" " where up.privilege_type=ps.privilege_type" " and up.grantee in ('$current_user_esc' $role_list)" " )" " and not exists (" " select 42" " from schema_privileges sp" " where sp.privilege_type=ps.privilege_type" " and sp.grantee in ('$current_user_esc' $role_list)" " and sp.table_schema='psa'" " )" "order by privilege_type" ) local missed_privileges mysql_db_name='information_schema' missed_privileges="`mysql_raw -e \"${query[*]}\"`" || simply_die "Unable to fetch privileges of $current_user at 'mysql://$dsn_host:$dsn_port'." [ -z "$missed_privileges" ] || simply_die "$current_user at 'mysql://$dsn_host:$dsn_port' is not permitted to ${missed_privileges//$'\n'/, } on '$dsn_db_name'." # make sure that either database doesn't exist or at least it doesn't contain any tables mysql_db_name="$dsn_db_name" ! db_test --any-db "show databases" "\$1 == \"$dsn_db_name\"" || ! db_test "show tables" 1 \ || simply_die "Database 'mysql://$dsn_host:$dsn_port/$dsn_db_name' is not empty." } validate_dsn_config() { set_mysql_client_params local dsn_name dsn_user dsn_passwd dsn_host dsn_port dsn_db_name for dsn_name in "plesk" "apsc" "horde" "roundcube"; do dsn_user="`get_dsn "$dsn_name" username`" dsn_passwd="`get_dsn "$dsn_name" password`" dsn_host="`get_dsn "$dsn_name" host`" dsn_port="`get_dsn "$dsn_name" port`" dsn_db_name="`get_dsn "$dsn_name" dbname`" # consider section as filled if at least one parameter is specified [ -z "$dsn_user" -a -z "$dsn_passwd" -a -z "$dsn_host" -a -z "$dsn_port" -a -z "$dsn_db_name" ] || test_dsn done } ### 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" } comm_err() { p_echo " Unfortunately, this situation has not yet been" p_echo " resolved." p_echo " Please, visit $support_contact for assistance." p_echo p_echo "Exiting..." p_echo exit 1 } gid_err() { gid="$1" p_echo p_echo "ERROR: It seems there is a group with GID=$gid" p_echo " in this system, but $PRODUCT_NAME needs the same GID" p_echo " for its operation." report_problem "error" "Error: group with GID=$gid already exists." comm_err } group_err() { group="$1" gid="$2" p_echo p_echo "ERROR: It seems that there is group $group" p_echo " in your system. $PRODUCT_NAME uses the same group" p_echo " name but with another group ID ($gid)." report_problem "error" "Error: group $group exists, but it doesn't have GID=$gid." comm_err } uid_err() { uid="$1" p_echo p_echo "ERROR: It seems there is a user with UID=$uid" p_echo " in this system, but $PRODUCT_NAME needs the same UID" p_echo " for its operation." report_problem "error" "Error: user with UID=$uid already exists." comm_err } logname_err() { user="$1" uid="$2" p_echo p_echo "ERROR: It seems that there is a $user in your system" p_echo " with a UID other than that used by $PRODUCT_NAME ($uid)" p_echo " or the $user is in the primary group, but this" p_echo " group is not the one that $PRODUCT_NAME uses." report_problem "error" "Error: user $user exists, but it doesn't have UID=$uid or it belongs to wrong primary group." comm_err } 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. 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" } set_default_locale_params() { local default_locale_file="$PRODUCT_ROOT_D/admin/htdocs/locales/default" if [ -f $default_locale_file ]; then test -r "$default_locale_file" || die "set up default locale: cannot read '$default_locale_file'" default_locale="`cat $default_locale_file | sed 's/^\([^:]*\):.*/\1/'`" default_locale_name="`cat $default_locale_file | sed 's/^[^:]*:\([^:]*\):.*/\1/'`" default_locale_country="`cat $default_locale_file | sed 's/^[^:]*:[^:]*:[^:]*:\([^:]*\);.*/\1/'`" else default_locale="en-US" default_locale_name="ENGLISH" default_locale_country="UNITED STATES" fi } 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 } 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. set_xinetd_params() { xinetd_conf="/etc/xinetd.conf" xinetd_service="xinetd" xinetd_dir="/etc/xinetd.d" } # vim: ft=sh ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. lockfile() { local tempfile="$1.$$" local lockfile="$1.lock" echo $$ > $tempfile 2>/dev/null || { p_echo "Unable to create lock file in `dirname $tempfile`" return 1 } ln $tempfile $lockfile >/dev/null 2>&1 && { rm -f $tempfile return 0 } kill -0 `cat $lockfile` >/dev/null 2>&1 && { rm -f $tempfile return 1 } p_echo "Removing stale lock file $lockfile" rm -f $lockfile ln $tempfile $lockfile >/dev/null 2>&1 && { rm -f $tempfile return 0 } rm -f $tempfile return 1 } unlockfile() { rm -f "$1.lock" } ### 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 } set_log_action_name() { CUSTOM_LOG_ACTION_NAME="$1" } 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 || : } 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" } set_maintenance_mode_flag() { touch /var/lock/parallels-panel-maintenance-mode.flag } reset_upgrade_failure_flag() { rm -f /var/lock/parallels-panel-maintenance-mode.flag rm -f /var/lock/parallels-panel-upgrade-failure.flag } reset_maintenance_mode_flag() { rm -f /var/lock/parallels-panel-maintenance-mode.flag } set_repair_mode_flag() { touch /var/lock/plesk-panel-repair-mode.flag } remove_repair_mode_flag() { rm -f /var/lock/plesk-panel-repair-mode.flag } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. generate_password_file() { local passwd_file="$1" local title="$2" local user="$3" local group="$4" local mode="$5" if [ "$#" -ne 5 ]; then die "Some arguments was not defined for this function" fi local passwd local inten="generate random password for $title" echo_try $inten if [ ! -s "$passwd_file" ]; then passwd=`get_random_string` || die $inten echo "$passwd" > "$passwd_file" || die $inten set_ac $user $group $mode $passwd_file || die $inten else set_ac $user $group $mode $passwd_file || die $inten fi suc } ### 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' } pre_accumulate_error() { if [ -n "$ACCUMULATE_ERROR_RETURN_CODE_VAR" -o -n "$ACCUMULATE_ERROR_FAILED_ACTIONS_VAR" ]; then echo "Unable to init accumulate_error-stuff twice. calling post_accumulate_error() might be required." >&2 exit 1 fi ACCUMULATE_ERROR_RETURN_CODE_VAR="$1" ACCUMULATE_ERROR_FAILED_ACTIONS_VAR="$2" } accumulate_error() { if [ -z "$ACCUMULATE_ERROR_RETURN_CODE_VAR" -o -z "$ACCUMULATE_ERROR_FAILED_ACTIONS_VAR" ]; then echo "accumulate_error-stuff is not set up. call pre_accumulate_error() first." >&2 exit 1 fi local action="$1" local actions eval ${ACCUMULATE_ERROR_RETURN_CODE_VAR}=1 eval actions='$'${ACCUMULATE_ERROR_FAILED_ACTIONS_VAR} eval "$ACCUMULATE_ERROR_FAILED_ACTIONS_VAR=\"\${actions:+$actions, }$action\"" } ### 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. get_user_id() { local name="$1" [ -n "$name" ] || int_err "Wrong value of argument 'name': $name" getent passwd "$name" 2>/dev/null | awk -F':' '{print $3}' } get_group_id() { local name="$1" [ -n "$name" ] || int_err "Wrong value of argument 'name': $name" getent group "$name" 2>/dev/null | awk -F':' '{print $3}' } add_user_to_group() { local user group existing newlist user="$1" group="$2" if [ -z "`get_group_id $group`" ]; then p_echo " Group '$group' not exists" p_echo " It is necessary to add group '$group'" err fi inten="add supplementary group '$group' for user '$user'" echo_try "$inten" existing=`id -Gn "$user"|sed 's|[[:space:]]\+|,|g'` if test "`id -gn "$user"`" = "$group" \ || echo "$existing" | grep -q "\\<$group\\>" then p_echo " already there" return fi if test -z "$existing"; then newlist="$group" else newlist="$existing,$group" fi usermod -G "$newlist" "$user" 2>>"$product_log" && suc || die "$inten" } group_op() { local group="$1" local gid="$2" local id_force="$3" local gid_min="${4:-30}" local gid_max="${5:-400}" if [ "X$gid" = "Xsearch" ]; then gid=`get_user_group_id "$group" "gid" $gid_min $gid_max` fi local inten="add group '$group'" local group_id="`get_group_id $group`" case "$id_force" in true) if [ -n "$group_id" ]; then [ "$group_id" = "$gid" ] && p_echo " Group '$group' already exists" || group_err "$group" "$gid" else # We do not expect any created group with ID == $gid getent group 2>/dev/null | egrep -q "^[^:]*:[*x]*:$gid:" && gid_err "$gid" || : echo_try "$inten" groupadd -g "$gid" "$group" >> $product_log 2>&1 && suc || die $inten fi ;; false) if [ -n "$group_id" ]; then p_echo " Group '$group' already exists" else echo_try "$inten" groupadd "$group" >> $product_log 2>&1 && suc || die $inten fi ;; *) int_err "group_op: wrong last parameter -- must be 'true' or 'false'" ;; esac } user_op() { user="$1" uid="$2" group="$3" u_desc="$4" u_home="$5" u_shell="$6" id_force="$7" gid_force="$8" local inten="add user $user" if [ "X$uid" = "Xsearch" ]; then uid=`get_user_group_id "$user" "uid" 30 400` fi p_echo " Checking for the user '$user'..." user_id="`get_user_id $user`" case "$id_force" in true) if [ -n "$user_id" ]; then chk_res=`id "$user" 2>&1 | egrep '(^uid=.*gid=.*)|(^id.*user)'` if [ "X${gid_force}" = "Xfalse" ]; then chk_uid=`echo "$chk_res" | egrep "uid=""$uid""\(""$user""\)"` else chk_uid=`echo "$chk_res" | egrep "uid=""$uid""\(""$user""\)" | egrep "gid=[0-9]*\(""$group""\)"` fi case "$chk_uid" in uid=*gid=*) p_echo " User '$user' already exists" ;; *) logname_err "$user" "$uid" ;; esac usermod -s "$u_shell" "$user" >> $product_log 2>&1 else getent passwd 2>/dev/null | egrep -q "^[^:]*:[^:]*:""$uid"":" && uid_err "$uid" || : pnnl_echo " Trying to add user '$user'... " useradd -r -u "$uid" -g "$group" -d "$u_home" -s "$u_shell" -c "$u_desc" "$user" >> $product_log 2>&1 && suc || die "$inten" fi ;; false) if [ -n "$user_id" ]; then p_echo " User '$user' already exists" else pnnl_echo " Trying to add user '$user'... " useradd -r -g "$group" -d "$u_home" -s "$u_shell" -c "$u_desc" "$user" >> $product_log 2>&1 && suc || die "$inten" fi ;; *) int_err "user_op: wrong last parameter -- must be 'true' or 'false'" ;; esac } get_user_group_id() { local src local name="$1" local src_name="$2" local min_num="$3" local max_num="$4" case "$src_name" in uid) src="passwd" uid="`get_user_id $name`" [ -n "$uid" ] && echo "$uid" && return 0 || : ;; gid) src="group" gid="`get_group_id $name`" [ -n "$gid" ] && echo "$gid" && return 0 || : ;; *) p_echo "$src_name for $name was not set" die "detect uid/gid. Source file was not detect." ;; esac if [ $min_num -le 0 -o $min_num -ge $max_num ]; then int_err "group_op(): wrong group ID's range to look for the empty ID: $gid_min ... $gid_max" fi # list of busy ids nums="`getent $src 2>/dev/null | awk -F: '{print $3}' | sort -g | xargs echo -n`" # Find an empty id for num in `seq $min_num $max_num`; do echo " $nums " | grep -q " $num " && continue echo "$num" return 0 done p_echo "$src_name for $name was not set" die "get free $src_name. Free $src_name not found." } add_login_shell() { local shell="$1" p_echo " Checking that $shell registered as login shell..." case $linux_distr in debian) add-shell $shell ;; *) if grep -q "^$shell\$" /etc/shells; then p_echo "$shell already registered as a login shell" else echo "$shell" >> /etc/shells 2>> $product_log || die "register login shell $shell" fi ;; esac } is_package_installed_apt() { [ "`dpkg-query -W --showformat='${Status}' $1 2>/dev/null`" = "install ok installed" ] } ### 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 } conf_update() { local def="$1.default" umask 0022 [ -s "$def" ] || return 1 [ -s "$1" ] || echo '#' > "$1" perl -e ' open (my $config, $ARGV[0]) or die "Cannot open config $ARGV[0]\n"; while(<$config>){ s/#.*$//; next if /^\s*$/; /(\w+)\s+(.+)$/; $cf{$1}=$2; } close $config; $cf{PRODUCT_ROOT_D}=$ENV{PRODUCT_ROOT_D} if $ENV{PRODUCT_ROOT_D}; open (my $default, $ARGV[1]) or die "Cannot open default config $ARGV[1]\n"; while(<$default>){ s/^\s*(\w+)(\s+)(.+)$/"$1$2".( (exists($cf{$1}))? $cf{$1}:$3 )/e; print; } close $default; ' "$1" "$def" > "$1.new" if [ -s "$1.new" ]; then if cmp -s $1.new $1; then rm -f $1.new return 1 else rm -f $1.old ln $1 $1.old mv -f $1.new $1 chmod 644 $1 # bug 64313, 65043 p_echo "config updated" return 0 fi fi return 2 } # 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 } unregister_service() { [ -n "$1" ] || die "unregister_service: service name not specified" local inten="unregister service $1" echo_try $inten { local rs_db="$PRODUCT_ROOT_D/admin/sbin/register_service_db" [ ! -x "$rs_db" ] || "$rs_db" -r "$1" disable_respawn_service "$1.service" # systemctl daemon-reload is performed implicitly unless --no-reload is passed /bin/systemctl disable "$1.service" # purge sysvinit symlinks from /etc/rc.d which might be created by systemd-sysv-install # it spawns `update-rc.d defaults` or `chkconfig --add` if sysvinit script exists /usr/sbin/update-rc.d -f "$1" remove 1>/dev/null 2>&1 || : } >> $product_log 2>&1 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 } disable_respawn_service() { [ -n "$1" ] || die "disable_respawn_service: service name not specified" local inten="disable automatic respawn for service $1" echo_try "$inten" local respawn_unit="/lib/systemd/system/$1.d/respawn.conf" rm -f "$respawn_unit" 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" } relabel_named_directories() { [ -d "$NAMED_RUN_ROOT_D" ] || return 0 selinux_configuration_is_required || return 0 if [ -d "$NAMED_RUN_ROOT_D/proc" ]; then # Looks like /proc exists in chroot only on RedHat/CentOS 5 selinux_relabel_dir -e "$NAMED_RUN_ROOT_D/proc" "$NAMED_RUN_ROOT_D" else selinux_relabel_dir "$NAMED_RUN_ROOT_D" fi } relabel_php_fpm() { local ret=0 local php_d for php_d in $(/usr/bin/find "/opt/plesk/php" -mindepth 1 -maxdepth 1 -type d -regex "/opt/plesk/php/[0-9]+\.[0-9]+" 2>/dev/null); do local fpm_service="plesk-php$(basename $php_d | sed 's/\.//g')-fpm" selinux_relabel_dir "/var/log/$fpm_service" || ret=1 # make sure that php-fpm has undesired context [ -n "$(restorecon -i -v -n $php_d/sbin/php-fpm 2>/dev/null)" ] || continue # restart all loaded php-fpm services incuding dedicated ones selinux_relabel_dir "$php_d/sbin/php-fpm" \ && /bin/systemctl try-restart "$fpm_service*.service" \ || ret=1 done [ -z "$do_repair" ] || return $ret } relabel_plesk_directories() { selinux_configuration_is_required || return 0 local ret=0 local verbose_mode= if [ "$1" = "--verbose" ]; then verbose_mode="yes" shift fi set_horde_params if selinux_is_active; then for dir in "$PRODUCT_ROOT_D" \ /var/log/passenger* \ "$horde_logdir" \ "$QMAIL_ROOT_D/alias" "$QMAIL_ROOT_D/bin" "$QMAIL_ROOT_D/boot" "$QMAIL_ROOT_D/control" \ "$QMAIL_ROOT_D/plugins" "$QMAIL_ROOT_D/popuser" "$QMAIL_ROOT_D/queue" "$QMAIL_ROOT_D/users" \ /var/db/kav /var/db/Quarantine "$PLESK_DB_DIR" \ "/usr/lib/postfix/sbin" \ /var/drweb /opt/drweb "$PLESK_LIBEXEC_DIR" \ "/etc/nginx" "/usr/sbin/nginx" /var/lib/nginx \ "/var/log/nginx" "/var/run/nginx.pid" "/usr/share/passenger_temp" \ "/var/lib/plesk/mail" /opt/kav "/usr/lib/x86_64-linux-gnu/php/modules" "/var/run" \ "/var/lib/php/sessions" \ /etc/psa/.psa.shadow \ /etc/resolv.conf \ /usr/sbin/courierlogger \ /etc/courier-imap \ /usr/bin/imapd \ /usr/sbin/authdaemond \ /usr/sbin/imaplogin \ "$@"; do [ -e "$dir" ] || continue [ -z "$verbose_mode" ] || pp_echo " Restoring SELinux context on '$dir'" selinux_relabel_dir "$dir" || ret=1 done [ -z "$verbose_mode" ] || pp_echo " Restoring SELinux context on '$NAMED_RUN_ROOT_D'" relabel_named_directories || ret=1 [ -z "$verbose_mode" ] || pp_echo " Restoring SELinux context for php-fpm services" relabel_php_fpm || ret=1 fi [ -z "$do_repair" ] || return $ret } 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 } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # -*- vim:syntax=sh set_syslog_params() { syslog_conf_ng="/etc/syslog-ng/syslog-ng.conf" syslog_conf="" for config in rsyslog.conf rsyslog.d/50-default.conf rsyslog.early.conf syslog.conf; do [ -f "/etc/$config" ] && syslog_conf="$syslog_conf /etc/$config" done syslog_service="" syslog_binary="" # Make sure the sequence of services is correlate with binaries local syslog_services="syslog sysklogd rsyslog syslog-ng" local syslog_binaries="syslogd syslogd rsyslogd syslog-ng" for service in $syslog_services; do [ -f "/lib/systemd/system/${service}.service" ] && \ syslog_service="$service" && break done for binary in $syslog_binaries; do for bin_path in /sbin /usr/sbin; do [ -x "$bin_path/${binary}" ] && \ syslog_binary="$bin_path/${binary}" && break done [ -n "$syslog_binary" ] && break done [ -n "$syslog_conf" ] || p_echo "\$syslog_conf: not found" [ -n "$syslog_service" ] || p_echo "\$syslog_service: not found" [ -n "$syslog_binary" ] || p_echo "\$syslog_binary: not found" [ -n "$syslog_conf" -a -n "$syslog_service" -a -n "$syslog_binary" ] } 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 } # Clean installation of the product is being performed is_product_installation() { [ "X$do_upgrade" != "X1" -a ! -s "/opt/psa/version.upg" ] } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh mail_restore_all() { if [ ! -x "/usr/lib/plesk-9.0/mailsrv_entities_dump" ]; then p_echo "/usr/lib/plesk-9.0/mailsrv_entities_dump isn't yet on its place. Deferring mail_restore execution to a later stage" >&2 return fi if [ ! -x "/usr/lib/plesk-9.0/mail_restore" ]; then p_echo "/usr/lib/plesk-9.0/mail_restore isn't yet on its place. Deferring mail_restore execution to a later stage" >&2 return fi # FIXME: There's a possible race during upgrade procedure. Noone # managed to catch the bug in debug mode, so be stupid: sync; sync; sleep 3 local inten="execute plesk repair mail to synchronize mail server settings and Plesk Database" echo_try $inten plesk repair mail -y >>$product_log 2>&1 # plesk repair mail should return proper exit code. return "$?" } 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 } install_postfix_smtpd_sasl_config() { local smtpd_sasl_conf smtpd_sasl_conf_bak smtpd_sasl_conf="/etc/postfix/sasl/smtpd.conf" if [ -f "$smtpd_sasl_conf" ]; then smtpd_sasl_conf_bak="${smtpd_sasl_conf}.saved_by_plesk.`date +%s`" if ! cp -fp "$smtpd_sasl_conf" "$smtpd_sasl_conf_bak" ; then simply_die "unable to preserve $smtpd_sasl_conf to $smtpd_sasl_conf_bak" fi fi mkdir -p "$(dirname $smtpd_sasl_conf)" postfix_smtpd_sasl_config_init "$smtpd_sasl_conf" } function postfix_smtpd_sasl_config_init() { local saslauthd_path saslauthd_path_relative="/private/plesk_saslauthd" saslauthd_path=$saslauthd_path_relative cat >"$1" <<EOF pwcheck_method: auxprop saslauthd auxprop_plugin: plesk saslauthd_path: $saslauthd_path mech_list: DIGEST-MD5 CRAM-MD5 PLAIN LOGIN sql_engine: intentionally disabled log_level: 4 EOF } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. #-*- vim:syntax=sh select_maillog() { local mail_log="/var/log/maillog" local inten="set maillog file to $mail_log" echo_try "$inten" set_syslog_params if [ "$syslog_service" = "rsyslog" ]; then for config in $syslog_conf; do if [ -f "$config" ]; then # comment out default 'mail.*' records: perl -i -ple 's/^(mail\.\*.*)/#\1/' "$config" fi done elif [ -f "$syslog_conf_ng" ]; then # override mail log destination perl -i -ple 's#^destination d_mail \{ .* \};\s*$#destination d_mail { file("'"${mail_log}"'"); };#' "$syslog_conf_ng" else warn "Unknown syslog: rsyslog or syslog-ng were not found" return fi # Move old logfile into new place. local old_mail_log='/var/log/mail.log' if [ -f "$old_mail_log" -a ! -e "$mail_log" ]; then mv "$old_mail_log" "$mail_log" fi ! [ -f "$mail_log" ] || chmod 640 "$mail_log" pleskrc syslog reload-or-restart || return 1 suc } set_foreign_mtas_params() { # need for pleskrc to work properly exim_service="exim" exim4_service="exim4" sendmail_service="sendmail" } fix_mailman_django_settings() { : } fix_mailman_postorius_template_base_url() { : } fix_mailman_web_account_adapter() { : } fix_mailman_web_middleware() { : } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # -*- vim:ft=sh # MySQL server configuration # MySQL server configuration code writes error report into file $PLESK_INSTALLER_ERROR_REPORT # in json format with the following fields: # - "stage": "mysqlsetup" # - "level": "fatal" (installation cannot continue in case of mysql-related errors) # - "errtype": one of the following: # * "mysqlnomycnf" - my.cnf was not found. # List of my.cnf location is written into field "mycnffiles". # * "mysqlsetupfailed" - failed to establish connection to database. # * "mysqlcreateuserfailed" - failed to create 'admin' db user. # - "date": time of error occurance. # - "error": human readable error message. generate_mysql_credentials() { local args="$*" mysql_defaults=`echo $args | cut -d: -f 1` mysql_user=`echo $args | cut -d: -f 2` [ -n "$mysql_user" ] && mysql_user="--user=$mysql_user" mysql_passwd=`echo $args | cut -d: -f 3-` [ -n "$mysql_passwd" ] && mysql_passwd="$mysql_passwd" } # This function must be called *ONLY* for psa installation setup_admin_login() { true mysql_quote_string unset MYSQL_PWD local mysql_db_name="mysql" local sl local inten="define valid mysql credentials" local i echo_try "$inten" get_admin_passwd # --no-defaults:admin:$admin_passwd admin with pass # --no-defaults:root: root without pass # :root: root with pass in defaults # --no-defaults:admin: admin withiyt pass # --no-defaults:admin:$PRODUCT_DEFAULT_PASSWORD admin with default pass # :admin: admin with pass in defaults # :: user and pass in defaults # --defaults-file=/root/.my.cnf hspc defaults (paranoid) # for sl in `sequence 20`; do for i in "--no-defaults:admin:$admin_passwd" \ "--no-defaults:root:" \ ":root:" \ "--no-defaults:admin:" \ "--no-defaults:admin:$PRODUCT_DEFAULT_PASSWORD" \ ":admin:" \ "::" \ "--defaults-file=/root/.my.cnf::"; do generate_mysql_credentials "$i" if mysql_test_connection; then suc # create/Update admin superuser if [ "$mysql_user" = "--user=admin" ]; then reset_admin_passwd else echo "create user 'admin'@'localhost' identified by '`mysql_quote_string $admin_passwd`'; grant all privileges on *.* to 'admin'@'localhost' with grant option;flush privileges" | mysql_direct mysql || { if db_test "SELECT COUNT(*) FROM mysql.user WHERE User='admin' AND Host='localhost';" '$1!="0"'; then break else local msg="create 'admin' MySQL user. \ Plesk has been able to successfully connect to MySQL server, \ but assigning password and privileges to the new user failed. \ If you are installing Plesk on an already configured MySQL server with enabled \ password strength validation, you may need to disable it for the initial Plesk \ installation. After the installation is finished, you may re-enable it." echo "Failed to $msg" | write_structured_report 'stage=mysqlsetup' 'level=fatal' 'errtype=mysqlcreateuserfailed' || : die "$msg" fi } fi update_psa_shadow # backup private my.cnf if it contains password (vz/hspc related) rm -f /.my.cnf if grep -q '\s*password\s*=' /root/.my.cnf 2>/dev/null; then p_echo "WARNING: You current personal mysql config /root/.my.cnf has been renamed into /root/.my.cnf.bak" mv -f /root/.my.cnf /root/.my.cnf.bak fi set_mysql_auth return 0 fi done p_echo "One more attempt to connect" try_reset_mysql_auth sleep "$sl" done local msg="$inten. If you are installing Plesk on an already configured MySQL server, you need to specify the administrator's credentials to succeed with the installation. \ To do this, you need to create a file - /root/.my.cnf with the 'client' section where you need to provide user and its password \ (\"user = \$admin_name\" and \"password = \$admin_pass\"). \ After installation is finished, the file /root/.my.cnf will be renamed to /root/.my.cnf.bak" echo "Failed to $msg" | write_structured_report 'stage=mysqlsetup' 'level=fatal' 'errtype=mysqlsetupfailed' || : die "$msg" } update_psa_shadow() { echo "$admin_passwd" > $mysql_passwd_file || die chown $admin_user:$admin_group ${mysql_passwd_file} || die chmod 600 ${mysql_passwd_file} || die selinux_relabel_dir "$mysql_passwd_file" } 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_quote_string() { echo "$1" | perl -pe 's|\\|\\\\|g' } reset_admin_passwd() { true mysql_quote_string [ -n "$admin_passwd" ] || die "admin_passwd is not set yet" # I bet someone somewhere replaces 'localhost' with '%' (for weird automation purposes), so be nice echo "SET PASSWORD FOR 'admin'@'localhost' = PASSWORD('`mysql_quote_string $admin_passwd`')" | mysql_direct mysql || \ echo "SET PASSWORD FOR 'admin'@'%' = PASSWORD('`mysql_quote_string $admin_passwd`')" | mysql_direct mysql || \ echo "ALTER USER 'admin'@'localhost' IDENTIFIED BY '`mysql_quote_string $admin_passwd`')" | mysql_direct mysql || \ echo "ALTER USER 'admin'@'%' IDENTIFIED BY '`mysql_quote_string $admin_passwd`')" | mysql_direct mysql } 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 } configure_mysql_innodb() { local my_cnf="$1" if grep -q '^skip-innodb' "$my_cnf"; then sed -i -e '/^skip-innodb/ d' "$my_cnf" need_mysql_restart=yes fi } #bug #46837. Disable "LOAD DATA LOCAL INFILE" command. configure_mysql_infile() { local my_cnf="$1" local awk_script if ! (test -f $my_cnf && grep -q '^\[mysqld\]' "$my_cnf"); then echo "[mysqld]" >> $my_cnf echo "local-infile=0" >> $my_cnf need_mysql_restart=yes return fi awk_script='/^\[mysqld\]$/{print; print "local-infile=0"; next;} {print}' if ! grep -q '^local-infile' "$my_cnf"; then awk "$awk_script" "$my_cnf" >"$my_cnf.tmp" && mv "$my_cnf.tmp" "$my_cnf" || die "edit $my_cnf" need_mysql_restart=yes fi } configure_mysql_address() { local my_cnf="$1" local address="$2" if ! grep -q "^bind-address\s*=\s*$address\s*$" "$my_cnf"; then sed -e "/^bind-address\s*=\s*/d" -i "$my_cnf" || \ die "remove 'bind-address' directive from '$my_cnf'" sed -e "s|^\(\[mysqld\].*\)|\1\nbind-address = $address|" -i "$my_cnf" || \ die "configure MySQL server via '$my_cnf' to listen on address $address" need_mysql_restart=yes fi } check_ipv6_network_available() { ping6 -c1 -q "::1" >/dev/null 2>&1 } configure_mysql_address_local() { local ipv6_supported_version="5.5.3" local current_version="`mysql_raw_anydb -e \"select version();\"`" local my_cnf="$1" local address="127.0.0.1" # if we cannot detect mysql-server version # use ipv4 only address if [ -n "$current_version" ]; then mysql_compare_versions "$current_version" "$ipv6_supported_version" if [ $? -ne 1 ] && check_ipv6_network_available; then address="::ffff:127.0.0.1" fi # Before MariaDB 10.6.0 "::" implied listening additionally on IPv4 addresses like "*". From 10.6.0 onwards it refers to IPv6 stictly. mysql_compare_versions "$current_version" "10.6.0" if [ $? -ne 1 ]; then address="127.0.0.1" fi fi configure_mysql_address "$my_cnf" "$address" } configure_mysql_disable_old_passwords() { local my_cnf="$1" local awk_script='/^[[:space:]]*old_passwords/ { print "# Forced OLD_PASSWORD format is turned OFF by Plesk\n#" $0; next } { print }' if awk "$awk_script" "$my_cnf" > "$my_cnf.tmp" || die "edit $my_cnf"; then if diff -q "$my_cnf" "$my_cnf.tmp" 1>/dev/null ; then rm -f "$my_cnf.tmp" else mv "$my_cnf.tmp" "$my_cnf" || die "disable old_passwords in $my_cnf" need_mysql_restart=yes fi fi } configure_mysql_default_auth_plugin() { local my_cnf="$1" local mysql_db_name="mysql" db_select "SHOW VARIABLES LIKE 'default_authentication_plugin'" local default_auth_plugin=`get_narg 2 ${db_select_output}` if [ -z "$default_auth_plugin" -o "$default_auth_plugin" != "caching_sha2_password" ]; then p_echo "MySQL default authentication plugin is ok ($default_auth_plugin)" return fi # We need to also update admin password if it was set by incompatible with sw-engine plugin echo "ALTER USER 'admin'@'localhost' IDENTIFIED WITH mysql_native_password BY '`mysql_quote_string $admin_passwd`'" | mysql_direct mysql || die "execute SQL query to update 'admin' password" echo_try "switch MySQL default authentication from caching_sha2_password to mysql_native_password" if ! (test -f "$my_cnf" && grep -q '^\[mysqld\]' "$my_cnf"); then echo "[mysqld]" >> $my_cnf echo "default_authentication_plugin = mysql_native_password" >> $my_cnf suc need_mysql_restart=yes return fi local awk_script='/^\[mysqld\]$/{print; print "default_authentication_plugin = mysql_native_password"; next;} {print}' if ! grep -q '^default_authentication_plugin' "$my_cnf"; then awk "$awk_script" "$my_cnf" >"$my_cnf.tmp" && mv "$my_cnf.tmp" "$my_cnf" || die "edit $my_cnf" need_mysql_restart=yes fi suc } configure_mysql_no_strict_mode() { local my_cnf="$1" local mysql_db_name="mysql" db_select "SELECT @@""GLOBAL.sql_mode" local sql_mode="$db_select_output" [ -n "$sql_mode" ] || return 0 local ok_sql_mode="`echo "$sql_mode" | tr ',' '\n' | egrep -v 'STRICT_TRANS_TABLES|STRICT_ALL_TABLES|NO_ZERO_DATE' | xargs | tr ' ' ','`" [ "$ok_sql_mode" != "$sql_mode" ] || return 0 sed -e "/^sql_mode.*=.*/d" -i "$my_cnf" || \ die "remove 'sql_mode' directive from '$my_cnf'" sed -e "s|^\(\[mysqld\].*\)|\1\nsql_mode=$ok_sql_mode|" -i "$my_cnf" || \ die "configure MySQL server via '$my_cnf' with no strict mode" need_mysql_restart=yes } configure_mysql_disable_password_checker() { local my_cnf="$1" local mysql_db_name="mysql" # Switch off MySQL 8+ password checking db_select "SHOW VARIABLES LIKE 'validate_password%'" if [ -z "$db_select_output" ]; then p_echo "builtin MySQL password checker is disabled" return fi local inten="switch off builtin MySQL password checker" echo_try "$inten" db_do --inten "$inten" "UNINSTALL COMPONENT 'file://component_validate_password'" suc } get_mysqldump_default_charset() { env LC_ALL=C LANG=C mysqldump "$@" --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 } 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 } mysql_install_init() { local with_managed="yes" [ "$1" != "--skip-managed" ] || with_managed= register_service "$mysql_service" run_configure_mysql_funcs \ configure_mysql_innodb \ configure_mysql_infile \ configure_mysql_disable_old_passwords \ setup_admin_login mysql_fix_remove_bad_users true configure_mysql_address_local run_configure_mysql_funcs \ ${with_managed:+configure_mysql_address_local} \ configure_mysql_no_strict_mode \ configure_mysql_default_auth_plugin \ configure_mysql_disable_password_checker \ configure_mysqldump_utf8mb4 \ } mysql_fix_remove_bad_users() { local mysql_db_name="mysql" db_do "drop database if exists test" db_drop_user '' # This removes the root user if possible. # On Debian > 8 and Ubuntu > 16.04 the debian-sys-maint user is no longer present/used # and got "replaced" with a root user, configured to be authenticated using the unix socket. # On RPM-based systems there are no default maintenance tasks, so removing root is OK as well. egrep -q "^user\s*=\s*root\s*$" /etc/mysql/debian.cnf 2>/dev/null || db_drop_user 'root' } mysql_compare_versions() { local l_greater=2 local l_equal=0 local l_less=1 local lhs_major="`echo $1 | cut -d'.' -f1`" local lhs_minor="`echo $1 | cut -d'.' -f2`" local lhs_patch="`echo $1 | cut -d'.' -f3 | grep -o -e '^[0-9]\+'`" local rhs_major="`echo $2 | cut -d'.' -f1`" local rhs_minor="`echo $2 | cut -d'.' -f2`" local rhs_patch="`echo $2 | cut -d'.' -f3 | grep -o -e '^[0-9]\+'`" # TODO(galtukhov): rewrite string comparision as python one-liner if [ "$lhs_major" -gt "$rhs_major" ]; then return $l_greater elif [ "$lhs_major" -lt "$rhs_major" ]; then return $l_less else if [ "$lhs_minor" -gt "$rhs_minor" ]; then return $l_greater elif [ "$lhs_minor" -lt "$rhs_minor" ]; then return $l_less else if [ "$lhs_patch" -gt "$rhs_patch" ]; then return $l_greater elif [ "$lhs_patch" -lt "$rhs_patch" ]; then return $l_less else return $l_equal fi fi 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" # 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" } set_mysql_version_fork_params() { # Must be called only after `uses_mysql_client` and `set_mysqld_params` # Requires working connection to the MySQL server mysql_client_fork="" mysql_server_fork="" mysql_client_version="" mysql_server_version="" local version_string="" version_string=$(mysql_raw -V) if ! mysql_client_fork=$(detect_mysql_fork_by_version "$version_string"); then return 1 fi # @see \Plesk\Db\Mysql\Version # MySQL below 8.0, Percona below 8.0 and MariaDB (any) # Example version string: mysql Ver 15.1 Distrib 10.9.3-MariaDB, for Linux (x86_64) using readline 5.1 mysql_client_version=$(echo "$version_string" | grep -Po 'Distrib\s+([\d.]+)' | grep -Eo '[0-9.]+') if [ -z "$mysql_client_version" ]; then # MySQL 8.0, Percona 8.0 and above # Example version string: mysql Ver 8.0.27 for Linux on x86_64 (MySQL Community Server - GPL) mysql_client_version=$(echo "$version_string" | grep -Po 'Ver\s+([\d.]+)' | grep -Eo '[0-9.]+') if [ -z "$mysql_client_version" ]; then p_echo "Cannot reliably detect client MySQL version" return 1 fi fi version_string=$(mysql_anydb -e 'SELECT CONCAT(@@''version, @@''version_comment);') if ! mysql_server_fork=$(detect_mysql_fork_by_version "$version_string"); then return 1 fi if ! mysql_server_version=$(echo "$version_string" | grep -Eo '^[0-9.]+'); then p_echo "Cannot reliably detect server MySQL version" return 1 fi } #Invoke mysql mysql() { 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() { mysql $mysql_args_raw "$@" } mysql_raw_anydb() { mysql_anydb $mysql_args_raw "$@" } check_db() { local inten="find Plesk database $mysql_db_name" echo_try $inten # If database not exists if ! db_test_database "$mysql_db_name"; then p_echo "not found" # DB should be installable (schema file must be present) [ -f "$product_db_sql" ] || die "$inten: file not found: $product_db_sql" return 1 fi # Database found, but version undefined if ! db_select "select val from misc where param='version'" || [ -z "$db_select_output" ]; then p_echo "DATABASE ERROR!!!" p_echo "Database $mysql_db_name found, but version is not defined" die $inten fi prev_db_version="$db_select_output" # Now check psa database version p_echo "version is $prev_db_version" if [ "$prev_db_version" -le "011010" ] ; then prev_db_short_version=`expr ${prev_db_version} / 10` else # support upgrade on patch version prev_db_short_version="$prev_db_version" fi return 0 } product_install_db_local() { p_echo "===> Installing Plesk database" local dsn_plesk_dbname="`get_dsn plesk dbname`" db_install "Plesk" --name "${dsn_plesk_dbname:-psa}" --skip-when check_db --initializer product_db_initializer_local \ || die "install initial Plesk database" } product_db_initializer_local() { product_db_installer # DB initialization specific to this machine p_echo "===> Initializing Plesk database" db_init_spamassassin db_init_default_locale db_init_updater_state } product_db_installer() { # DB schema installation and initialization not bound to this machine mysql < "$product_db_sql" || die "Cannot initialize Plesk database from $product_db_sql" set_db_version } db_init_spamassassin() { detect_vz if [ "$PLESK_VZ" = "1" ]; then db_do "replace into ServiceNodeConfiguration (\`serviceNodeId\`, \`section\`, \`name\`, \`value\`) values (1, 'mailServer', 'spamfilter_max_children', '1')" # default is 5 fi } db_init_default_locale() { local default_locale default_locale_name default_locale_country p_echo 'Setting the default locale' set_default_locale_params db_do "replace into misc (param, val) values('def_locale', '$default_locale')" db_do "replace into locales(id) values('$default_locale')" db_do "alter table clients alter locale set default '$default_locale'" p_echo "The default locale is set to $default_locale ($default_locale_name, $default_locale_country)" } db_init_updater_state() { # By default show updater UI in Panel local disable_updater="false" mysql <<EOF || die "init updater state" REPLACE INTO misc(param, val) VALUES('disable_updater', '$disable_updater'); EOF } 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 } dump_db() { local databases="" ignore_tables="" for db in "$@"; do if db_test_database "$db"; then databases="$databases $db" fi done # Assume that MYSQLDUMP_OPTIONS may contain --all-databases or other options to select targets [ -n "$databases" -o -n "$MYSQLDUMP_OPTIONS" ] || return 1 ignore_tables=$(populate_ignore_tables) ( set -o pipefail export MYSQL_PWD="$mysql_passwd" ${MYSQL_BIN_D}/mysqldump $mysql_defaults $mysql_user $mysql_host $mysql_port --quote-names $MYSQLDUMP_OPTIONS $ignore_tables ${databases:+--databases $databases} | sed -e ' /^USE `mysql`;$/,/^USE / { /^LOCK TABLES `innodb_index_stats` WRITE;$/,/^UNLOCK TABLES;$/ d; /^LOCK TABLES `innodb_table_stats` WRITE;$/,/^UNLOCK TABLES;$/ d; }' rc=$? unset MYSQL_PWD return $rc ) } requires_configured_mysql() { # Initialize and configure local MySQL server if it is not usable yet. # This function is for use in components that require MySQL, particularly on SN. local reinitialize_mysql="$1" set_mysqld_params if is_remote_db_feature_enabled; then ! pleskrc mysql status || pleskrc mysql stop unregister_service "$mysql_service" set_mysql_auth [ -s "$mysql_passwd_file" ] || update_psa_shadow return 0 fi if [ ! -s "$mysql_passwd_file" -o -n "$reinitialize_mysql" ]; then mysql_install_init ${reinitialize_mysql:+--skip-managed} else set_mysql_auth fi } 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 } populate_ignore_tables() { # Returns --ignore-tables=<> parameters for use in mysqldump to stdout # Parameters are based on fork and version of client and server if ! set_mysql_version_fork_params; then return 1 fi local ignore_tables="" local exit_code="" # Note: This check may also be applied unconditionally, as new mysqldump in MariaDB always skips this table if [ "$mysql_server_fork" = "mariadb" ]; then # https://jira.mariadb.org/browse/MDEV-16028 (mysqldump from MariaDB < 10.3 and any MySQL is not compatible w/ MariaDB > 10.3) # https://github.com/MariaDB/server/blob/10.3/client/mysqldump.c#L1079 mysql_compare_versions "10.3.0" "$mysql_server_version" exit_code=$? if [ "$exit_code" -ne "2" ]; then mysql_compare_versions "10.3.0" "$mysql_client_version" exit_code=$? if [ "$mysql_client_fork" != "mariadb" ] || [ "$exit_code" -eq "2" ]; then ignore_tables="--ignore-table=mysql.transaction_registry" fi fi fi echo "$ignore_tables" } detect_mysql_fork_by_version() { local version_string="$1" if [ -z "$version_string" ]; then p_echo "Cannot reliably detect fork from empty version string" return 1 fi if echo "$version_string" | grep -qi 'mariadb'; then echo "mariadb" elif echo "$version_string" | grep -qi 'percona'; then echo "percona" else echo "mysql" fi } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # Get version of local mysql service get_local_mysqld_version() { PATH="$PATH:/usr/libexec:/libexec" mysqld -V 2>/dev/null| sed 's/.*Ver //' | awk '{print $1}' } # Reset mysqld authentication in case we are unable to guess password create_reset_mysql_auth_sql_file() { true mysql_quote_string ( umask 0027 local sql="`mktemp /tmp/plesk-reset-mysql-auth.XXXXXX.sql`" cat > "$sql" <<-EOT -- WARNING: each statement in this file MUST be on one line, no comments on statement lines. -- DB must be selected to create procedures. USE mysql; EOT local mysql_version=`get_local_mysqld_version` case "$mysql_version" in 8.*) cat >> "$sql" <<-EOT DROP USER IF EXISTS 'admin'@'localhost'; CREATE USER 'admin'@'localhost' IDENTIFIED BY '`mysql_quote_string "$admin_passwd"`'; GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost' WITH GRANT OPTION; EOT ;; *) cat >> "$sql" <<-EOT -- Remove password strength validation. CREATE PROCEDURE plesk_uninstall_validate_password() BEGIN IF EXISTS (SELECT * FROM mysql.plugin WHERE name = 'validate_password') THEN UNINSTALL PLUGIN validate_password; END IF; END; CALL plesk_uninstall_validate_password(); DROP PROCEDURE IF EXISTS plesk_uninstall_validate_password; -- Create or reset admin superuser. GRANT ALL PRIVILEGES ON *.* TO 'admin'@'localhost' IDENTIFIED BY '`mysql_quote_string "$admin_passwd"`' WITH GRANT OPTION; FLUSH PRIVILEGES; EOT ;; esac cat >> "$sql" <<-EOT -- Shut down mysqld. SHUTDOWN; EOT chgrp "$mysqld_group" "$sql" >&2 chmod 640 "$sql" >&2 echo "$sql" ) } try_reset_mysql_auth() { # Don't try this more than once. It won't succeed anyway. [ -z "$PLESK_INSTALLER_TRIED_MYSQL_AUTH_RESET" ] || return 0 PLESK_INSTALLER_TRIED_MYSQL_AUTH_RESET="yes" local inten="reset authentication for MySQL server" echo_try "$inten" pleskrc mysql stop get_admin_passwd # Run mysqld in "reset authentication" mode. p_echo "Starting mysqld in authentication reset mode." local init_sql="`create_reset_mysql_auth_sql_file`" PATH="$PATH:/usr/libexec:/libexec" mysqld --user "$mysqld_user" --init-file "$init_sql" >> "$product_log" 2>&1 & local pid="$!" # Normally the command above should stop by itself. Wait for it. local tries=60 i= pnnl_echo "Waiting for mysqld to stop ($tries seconds at most)" for i in `sequence $tries`; do kill -0 "$pid" 2>/dev/null || break pnnl_echo "." sleep 1 done pnnl_echo " " # If it didn't stop, likely error occurred before SHUTDOWN query. Request shutdown via signal. kill -s TERM "$pid" 2>/dev/null \ && p_echo "We had to send mysqld signal to shutdown. This likely means that authentication reset failed (see mysqld log for details, search for 'init_file')." \ || p_echo "Looks like mysqld stopped normally." # SIGTERM might not have been honored by mysqld, but I've seen no such behavior. pnnl_echo "Waiting for mysqld return code (if this hangs, try 'kill -9 $pid')... " wait "$pid" local rc="$?" if [ "$rc" -ne 0 ]; then p_echo "mysqld failed with code $rc, authentication reset failed (see mysqld log for details)." else suc fi rm -f "$init_sql" pleskrc mysql restart } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. reset_user_password() { true mysql_quote_string local user="$1" local passwd="$2" local hosts="${3:-localhost}" local inten="reset database user password for '${user}@${hosts}'" echo_try "${inten}" if [ "$hosts" = "127.0.0.1" -o "$hosts" = "::1" ] ; then hosts="${hosts} localhost" fi hosts="${hosts} %" local quoted_passwd=`mysql_quote_string "$passwd"` # MySQL has dropped PASSWORD() in 8.0.11: # https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-11.html local has_passwd_fun="" # mysql_direct() writes errors directly to $product_log, so we set # it to /dev/null to silence the expected error message about PASSWORD() # not existing if echo "SELECT PASSWORD('test')" | product_log=/dev/null mysql_direct mysql &>/dev/null; then has_passwd_fun=1 fi local host="" for host in $hosts; do local query="" if [ -n "$has_passwd_fun" ]; then query="SET PASSWORD FOR '$user'@'$host' = PASSWORD('$quoted_passwd')" else query="ALTER USER '$user'@'$host' IDENTIFIED BY '$quoted_passwd'" fi if echo "$query" | mysql_direct mysql; then suc return 0 fi done warn "$inten - database user does not exist." return 1 } is_local_mysql_instance() { local host="$1" [ -z "$host" -o "$host" = "localhost" -o "$host" = "127.0.0.1" -o "$host" = "::1" ] } check_db_accessible() { local mysql_db_name="$1" local mysql_user="--user=$2" local mysql_passwd="$3" #INFO: we should not die here echo "" | mysql_direct } reset_sitebuilder_passwd() { local no_force="$1" local sb_conf="/usr/local/sb/config" [ -f "$sb_conf" ] || return 0 local sb_host=` get_ini_conf_var "$sb_conf" "database" "host"` local sb_username=`get_ini_conf_var "$sb_conf" "database" "username"` local sb_password=`get_ini_conf_var "$sb_conf" "database" "password"` local sb_dbname=` get_ini_conf_var "$sb_conf" "database" "dbname"` is_local_mysql_instance "$sb_host" || return 0 if [ -n "$no_force" ]; then check_db_accessible "$sb_dbname" "$sb_username" "$sb_password" && return 0 || : fi if reset_user_password "$sb_username" "$sb_password" "$sb_host"; then check_db_accessible "$sb_dbname" "$sb_username" "$sb_password" else # https://jira.plesk.ru/browse/PPP-4676 return 0 fi } get_webmail_config_param() { local webmail_config="$1" local config_var="$2" local param="$3" local default_val="$4" local result=`sw_engine_pleskrun -n -r 'include "'$webmail_config'"; echo $'${config_var}${param}';' 2>/dev/null` [ -n "$result" ] || result="$default_val" echo "$result" } get_atmail_config_param() { get_webmail_config_param "/var/www/atmail/libs/Atmail/Config.php" "pref" "$1" "$2" } parse_mysql_dsn_get_user() { echo $1 | perl -nle 'm|^mysql://([^:]+):([^@]+)@([^/:]+)(?::(\d+))?/(.*)$| and print $1 or exit 1' } parse_mysql_dsn_get_passwd() { echo $1 | perl -nle 'm|^mysql://([^:]+):([^@]+)@([^/:]+)(?::(\d+))?/(.*)$| and print $2 or exit 1' } parse_mysql_dsn_get_host() { echo $1 | perl -nle 'm|^mysql://([^:]+):([^@]+)@([^/:]+)(?::(\d+))?/(.*)$| and print $3 or exit 1' } parse_mysql_dsn_get_port() { echo $1 | perl -nle 'm|^mysql://([^:]+):([^@]+)@([^/:]+)(?::(\d+))?/(.*)$| and print $4 or exit 1' } parse_mysql_dsn_get_db() { echo $1 | perl -nle 'm|^mysql://([^:]+):([^@]+)@([^/:]+)(?::(\d+))?/(.*)$| and print $5 or exit 1' } reset_atmail_passwd() { local no_force="$1" local atmail_shadow="/etc/psa-webmail/atmail/.atmail.shadow" local at_host=` get_atmail_config_param "['sql_host']" "localhost"` local at_user=` get_atmail_config_param "['sql_user']" "atmail"` local at_dbname=`get_atmail_config_param "['sql_table']" "atmail"` local at_passwd=`get_atmail_config_param "['sql_pass']"` [ -n "$at_passwd" ] || at_passwd=`cat "$atmail_shadow" 2>/dev/null` [ -n "$at_passwd" -a -n "$at_user" -a -n "$at_dbname" ] || return 0 is_local_mysql_instance "$at_host" || return 0 if [ -n "$no_force" ]; then ! check_db_accessible "$at_dbname" "$at_user" "$at_passwd" || return 0 fi reset_user_password "$at_user" "$at_passwd" "$at_host" && \ check_db_accessible "$at_dbname" "$at_user" "$at_passwd" } reset_phpmyadmin_passwd() { local no_force="$1" local pmahost="localhost" local controlpass= pmadb= set_phpmyadmin_params local pma_config="$PHPMYADMIN_CONFIG_BACKUP" [ -f "$pma_config" ] || pma_config="$PHPMYADMIN_CONFIG" pma__set_values_from_config "$pma_config" 'controluser' 'controlpass' 'pmadb' [ -n "$controlpass" -a -n "$controluser" -a -n "$pmadb" ] || return 0 if [ -n "$no_force" ]; then ! check_db_accessible "$pmadb" "$controluser" "$controlpass" || return 0 fi reset_user_password "$controluser" "$controlpass" "$pmahost" && \ check_db_accessible "$pmadb" "$controluser" "$controlpass" } ### 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:ft=sh: set_named_params() { # set up default values bind_UID=53 bind_GID=53 bind_user="bind"; bind_group="bind"; # get UID of named user, if exists bind_UID="`get_user_id $bind_user`" # get GID of named group, if exists bind_GID="`get_group_id $bind_group`" # path to directory of internal named NAMED_ROOT_D="${PRODUCT_ROOT_D:?'PRODUCT_ROOT_D is undefined'}/named" # path to directory of named pid file bind_run="${NAMED_RUN_ROOT_D:?'NAMED_RUN_ROOT_D is undefined'}/var/run/named" named_bin="/usr/sbin/named" named_service="named" named_log="/dev/null" named_sysconf_file="/etc/default/$named_service" # path to named config file named_conf="/etc/named.conf" named_run_root_template="/opt/psa/var/run-root.tar" rndc_conf="/etc/rndc.conf" rndc_namedb_conf="/etc/namedb/rndc.conf" rndc_bind_conf="/etc/bind/rndc.conf" named_user_options_conf="/etc/named-user-options.conf" #140025. Restrict CPU cores for Bind bind_number_of_workers=2 } ### 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 } pear_register_packages() { local action=$1 local pkg_descr_dir=$2 [ -n "${pkg_descr_dir}" -a -d ${pkg_descr_dir} ] || die "act on PEAR packages - description directory is not specified or does not exist" case "${action}" in install) action=upgrade ;; uninstall) ;; *) die "act on PEAR packages - unknown command '${action}'" ;; esac if [ "${action}" = "upgrade" ]; then if ! ${psa_pear_bin} -c "$psa_pear_conf" "${action}" --offline --nodeps --register-only --nobuild ${pkg_descr_dir}/*.xml < /dev/null >> $product_log 2>&1; then warn "Registering of pear packages failed" return 1 fi else for i in ${pkg_descr_dir}/*.xml; do local pkg_name #on uninstall we should use channel/app-name, for example - horde/Horde_Data pkg_name="horde/"`basename "${i}" | cut -d '.' -f 1` if ! ${psa_pear_bin} -c "$psa_pear_conf" "${action}" --offline --nodeps --register-only "${pkg_name}" < /dev/null >> $product_log 2>&1; then warn "${action} package ${i} in PEAR" return 1 fi done fi } get_phpmyadmin_db_name() { local controluser= controlpass= pmadb= set_phpmyadmin_params pma__set_default_values pma__set_values_from_config "$PHPMYADMIN_CONFIG" 'pmadb' echo "$pmadb" } repair_phpmyadmin() { local controluser= controlpass= pmadb= # first just try to reset password in DB reset_phpmyadmin_passwd no_force || : set_phpmyadmin_params pma__set_values_from_config "$PHPMYADMIN_CONFIG" 'controluser' 'controlpass' 'pmadb' # if that didn't help, recreate DB and user (unless already occupied) if [ -n "$controlpass" -a -n "$controluser" -a -n "$pmadb" ]; then ! check_db_accessible "$pmadb" "$controluser" "$controlpass" || return 0 fi pma__drop_db_and_user "$pmadb" "$controluser" pma__configure pma__clean_sessions } set_phpmyadmin_params() { PHPMYADMIN_D="$PRODUCT_ROOT_D/phpMyAdmin" PHPMYADMIN_CONFIG="$PHPMYADMIN_D/config.plesk.php" # Executed as root, so must be placed in a directory which is root-owned and not world-writable PHPMYADMIN_CONFIG_BACKUP="$PLESK_DB_DIR/phpMyAdmin.config.php.save" # Layout of phpMyAdmin before 18.0.30 PHPMYADMIN_D_OLD="$PRODUCT_ROOT_D/admin/htdocs/domains/databases/phpMyAdmin" PHPMYADMIN_CONFIG_OLD="$PHPMYADMIN_D_OLD/libraries/config.default.php" } pma__write_values_to_config() { ( umask 0027 sed -e "`pma__sed_script`" < "$PHPMYADMIN_CONFIG.tpl" > "$PHPMYADMIN_CONFIG" || return 1 set_admin_params set_ac root "$admin_group" 0640 "$PHPMYADMIN_CONFIG" ) } pma__configure() { local controluser controlpass pmadb pma__set_default_values if is_remote_db_feature_enabled || pma__is_db_or_user_occupied; then pma__configure_without_db else pma__create_pmadb pma__create_controluser pma__write_values_to_config fi } pma__configure_without_db() { local controluser= controlpass= pmadb= pma__write_values_to_config is_remote_db_feature_enabled || warn "phpMyAdmin was configured without configuration storage in database" } pma__clean_sessions() { rm -f $PRODUCT_ROOT_D/var/phpMyAdmin/session/sess_* } pma__set_values_from_config() { eval `pma__read_values "$@"` } pma__read_values() { local PHPMYADMIN_CONFIG="$1" shift [ -f "$PHPMYADMIN_CONFIG" ] && sw_engine_pleskrun -r ' try { error_reporting(0); define("TEMP_DIR", "/tmp"); // specific value is irrelevant require_once("'$PHPMYADMIN_CONFIG'"); for ($i = 1; $i < $argc; $i++) if (isset($cfg["Servers"][1][$argv[$i]])) echo $argv[$i] . "=\"" . $cfg["Servers"][1][$argv[$i]] . "\"\n"; } catch (Exception $e) {}' -- "$@" 2>/dev/null || true } pma__set_default_values() { controluser="phpmyadmin" controlpass="`get_random_string 16`" pmadb="phpmyadmin" } pma__create_pmadb() { local create_tables_sql="$PHPMYADMIN_D/sql/create_tables.sql" local inten="initialize phpMyAdmin configuration storage ('$pmadb' DB) from $create_tables_sql" echo_try "$inten" # create pmadb cat "$create_tables_sql" | mysql_direct >> "$product_log" 2>&1 || warn "$inten" suc } pma__create_controluser() { local mysql_db_name="mysql" local controluser_host="localhost" db_do "CREATE DATABASE IF NOT EXISTS $pmadb DEFAULT CHARACTER SET utf8 COLLATE utf8_bin" db_create_user --user "$controluser" --host "$controluser_host" --password "$controlpass" --force db_do "GRANT SELECT, INSERT, DELETE, UPDATE, ALTER ON $pmadb.* TO '$controluser'@'$controluser_host'" } pma__is_db_or_user_occupied() { ! db_test_database "$pmadb" || return 0 ! db_test "SELECT COUNT(*) FROM mysql.user WHERE User='$controluser' LIMIT 1;" '$1!="0"' || return 0 return 1 } pma__drop_db_and_user() { local pmadb="$1" local controluser="$2" p_echo "Dropping phpMyAdmin '$pmadb' DB and '$controluser' DB user" [ -z "$controluser" ] || echo "DROP USER '$controluser'@'localhost'" | mysql_direct >> "$product_log" 2>&1 || : [ -z "$pmadb" ] || echo "DROP DATABASE $pmadb" | mysql_direct >> "$product_log" 2>&1 || : } pma__sed_script() { echo " s|@@""CONTROLUSER""@@|$controluser|g s|@@""CONTROLPASS""@@|$controlpass|g s|@@""PMADB""@@|$pmadb|g " } write_postconf_value() { local param="$1" local value="$2" local conf_value="`echo "$value" | awk 'BEGIN { ORS=""; PORS="" } { print PORS; print; PORS=", " }'`" /usr/sbin/postconf -e "$param=$conf_value" >>"$product_log" 2>&1 } postconf_param_exists() { local param="$1" local value=`/usr/sbin/postconf -d "$param" 2>/dev/null` test -n "$value" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. set_postfix_params() { postfix_service="postfix" pc_remote_service="pc-remote" } 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. set_psa_params() { psa_service="psa" } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh # Follow variables are supported in <package>_upgrader_conf: # upgrader_name - Upgrader id # upgrader_description - Short description which will be used in terminal and logs output. # upgrader_func_prefix - Prefix to generate function names used in upgrader. # upgrader_stages - Stages which upgrader supports for this component. # IMPORTANT: This stages list should be sorted in execution order because # last stage in list will store data into upgrader's cache and # upgrader_run_post do not care about one either. # # Optional flags to execute / do not execute some upgrader's parts. Default 'true': # upgrader_call_init - upgrader_init() control # upgrader_call_reset - upgrader_reset() control # # Example: # upgrader_name="dns-bind-driver" # upgrader_description="DNS service support" # upgrader_func_prefix="dnsbinddriver" # upgrader_stages="prep post bootstrapper-post" # # upgrader_call_init="false" # upgrader_call_reset="false" upgrader_conf_init() { [ -n "$PACKAGE_ID" ] || die "initialize upgrader: PACKAGE_ID is not set" # Check package uses the default upgrader is_function "${PACKAGE_ID}_upgrader_conf" || return 1 upgrader_call_init="true" upgrader_call_reset="true" "${PACKAGE_ID}_upgrader_conf" # Sanity checks [ -z "$upgrader_name" -o -z "$upgrader_description" -o -z "$upgrader_func_prefix" -o -z "$upgrader_stages" ] \ && die "initialize upgrader for $PACKAGE_ID: some upgrader variables are not defined" return 0 } upgrader_run() { # Usage: upgrader_run [--repair] [stage ...] upgrader_conf_init || return 0 local options= local optional_repair_arg= if [ "$1" = "--repair" ]; then optional_repair_arg="$1" shift fi ! is_function "${upgrader_func_prefix}_cu_load_config_data" || \ options="--load-config-data ${upgrader_func_prefix}_cu_load_config_data" ! is_function "${upgrader_func_prefix}_cu_store_config_data" || \ options="$options --store-config-data ${upgrader_func_prefix}_cu_store_config_data" local stages_to_run= [ -n "$*" ] && stages_to_run="$*" || stages_to_run="$upgrader_stages" local last_stage="${upgrader_stages##* }" for stage in $stages_to_run; do local opt_update_version= if [ "$stage" = "$last_stage" ]; then opt_update_version="--update-version" fi local inten="upgrade${optional_repair_arg:+ and repair} $upgrader_description ($stage stage)" echo_try "$inten" cu_run_upgrader "$upgrader_name" "${upgrader_func_prefix}_upgrader" \ --stage "$stage" $optional_repair_arg $options $opt_update_version if [ "$?" = "0" ]; then suc else warn "$inten" # return non-zero exit code on error in repair mode: [ -z "$optional_repair_arg" ] || return 1 fi done } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. set_horde_params() { horde_datadir="/usr/share/psa-horde" horde_confdir="/etc/psa-webmail/horde" horde_sysconfd="$horde_confdir/horde" horde_logdir="/var/log/psa-horde" horde_passwd_file="/etc/psa-webmail/horde/.horde.shadow" horde_title="Horde Web Based mail client" imp_datadir="$horde_datadir/imp" imp_sysconfd="$horde_confdir/imp" turba_datadir="$horde_datadir/turba" turba_sysconfd="$horde_confdir/turba" kronolith_datadir="$horde_datadir/kronolith" kronolith_sysconfd="$horde_confdir/kronolith" ingo_datadir="$horde_datadir/ingo" ingo_sysconfd="$horde_confdir/ingo" mnemo_datadir="$horde_datadir/mnemo" mnemo_sysconfd="$horde_confdir/mnemo" passwd_datadir="$horde_datadir/passwd" passwd_sysconfd="$horde_confdir/passwd" horde_user="horde_sysuser" horde_group="horde_sysgroup" horde_php_ini="/etc/psa-webmail/horde/horde/php.ini" horde_conf_php="/etc/psa-webmail/horde/horde/conf.php" horde_db_user_privileges=( "ALTER" "CREATE" "DELETE" "DROP" "INDEX" "INSERT" "REFERENCES" "SELECT" "UPDATE" ) } horde_run_migration_script() { local app=$1 local migrate_cmd="${psa_pear_dir}/pear/horde-db-migrate --config=${psa_pear_conf} ${app}" local horde_conf=${horde_sysconfd}/conf.php if [ -z "${app}" ]; then #we can not exec script if cache driver is sql, cause cache table may be not installed yet #if app is specified it means that framework is already configured, so cache tables is already created sed -i -e "s|\$conf\['cache'\]\['driver'\].*|\$conf['cache']['driver'] = 'File';|g" ${horde_conf} fi horde_run_php_script ${migrate_cmd} || warn "install or upgrade horde database, please re-run ${migrate_cmd}" if [ -z "${app}" ]; then sed -i -e "s|\$conf\['cache'\]\['driver'\].*|\$conf['cache']['driver'] = 'File';|g" ${horde_conf} fi } horde_run_php_script() { env PHP_PEAR_SYSCONF_DIR=$psa_pear_dir "/opt/plesk/php/7.4/bin/php" -d "include_path=${psa_pear_include_dir}:." $@ >> $product_log 2>&1 } generate_horde_password() { local inten='generate horde password' local dsn_horde_password="`get_dsn horde password`" if [ -n "$dsn_horde_password" -a ! -s "$horde_passwd_file" ]; then echo "$dsn_horde_password" > "$horde_passwd_file" fi generate_password_file "$horde_passwd_file" "$horde_title" root "$horde_group" 640 horde_password=`cat $horde_passwd_file` || die $inten } generate_horde_conf_php() { local horde_protocol='unix' local horde_hostspec='' local horde_port='null' local horde_mysql_socket='/var/run/mysqld/mysqld.sock' local dsn_horde_dbname="`get_dsn horde dbname`" local dsn_horde_host="`get_dsn horde host`" local dsn_horde_port="`get_dsn horde port`" local dsn_horde_username="`get_dsn horde username`" [ ! -n "$dsn_horde_dbname" ] || horde_protocol='tcp' [ ! -n "$dsn_horde_port" ] || horde_port="$dsn_horde_port" if [ -n "$dsn_horde_host" ]; then horde_hostspec="$dsn_horde_host" horde_mysql_socket='' fi cat "${horde_conf_php}.tpl" | sed \ -e "s|@horde_user@|${dsn_horde_username:-horde}|g" \ -e "s|@horde_protocol@|${horde_protocol}|g" \ -e "s|@horde_hostspec@|${horde_hostspec}|g" \ -e "s|@horde_port@|${horde_port}|g" \ -e "s|@horde_dbname@|${dsn_horde_dbname:-horde}|g" \ -e "s|@horde_mysql_socket@|${horde_mysql_socket}|g" \ > "${horde_conf_php}" } pear_register_horde_packages() { local action="$1" # 'install' or 'uninstall' local suite="$2" local packages_path="$3" if ! ( export PHP_PEAR_PHP_BIN="/opt/plesk/php/7.4/bin/php" pear_register_packages "$action" "${packages_path}" ); then warn "$action $suite" return 1 fi } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:set ft=sh: set_roundcube_params() { roundcube_passwd_file="/usr/share/psa-roundcube/config/.roundcube.shadow" roundcube_title="Roundcube Web Based mail client" roundcube_conf="/usr/share/psa-roundcube/config/config.inc.php" roundcube_conf_defaults="/usr/share/psa-roundcube/config/defaults.inc.php" roundcube_sql_d="/usr/share/psa-roundcube/SQL" roundcube_user="roundcube_sysuser" roundcube_group="roundcube_sysgroup" roundcube_home="/usr/share/psa-roundcube" roundcube_logs_d="/var/log/plesk-roundcube" roundcube_temp_d="/var/tmp" roundcube_php_ini="/etc/psa-webmail/roundcube/php.ini" roundcube_php_ini_tpl="/usr/share/psa-roundcube/config/php.ini.tpl" roundcube_preupgrade_version_file="/etc/psa-webmail/roundcube/version.upg" roundcube_db_user_privileges=( "ALTER" "CREATE" "DELETE" "DROP" "INDEX" "INSERT" "REFERENCES" "SELECT" "UPDATE" ) } set_component_pkgname_list() { COMPONENT_PKGNAME_horde=psa-horde COMPONENT_PKGNAME_fail2banconfigurator=plesk-fail2ban-configurator COMPONENT_PKGNAME_l10n=plesk-l10n COMPONENT_PKGNAME_core=plesk-core COMPONENT_PKGNAME_psa=psa COMPONENT_PKGNAME_roundcube=plesk-roundcube COMPONENT_PKGNAME_mailpcdriver=plesk-mail-pc-driver COMPONENT_PKGNAME_mailqcdriver=plesk-mail-qc-driver # hack to forbid sh_optimizer to wipe bootstrapper instructions out true horde_bootstrapper_prep_install true fail2banconfigurator_bootstrapper_prep_install true l10n_bootstrapper_prep_install true core_bootstrapper_prep_install true psa_bootstrapper_prep_install true roundcube_bootstrapper_prep_install true mailpcdriver_bootstrapper_prep_install true mailqcdriver_bootstrapper_prep_install } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. true horde_upgrader_conf horde_upgrader_conf() { upgrader_name="horde" upgrader_description="Horde webmail" upgrader_func_prefix="horde" upgrader_stages="files" # Horde uses upgrader during the install and upgrade procedures. upgrader_call_init="false" # Do not wipe it out after the sh_optimizer processing true horde_upgrader } horde_upgrader() { local install_or_upgrade="cu_check_stage files" local upgrade_only="cu_check_stage files horde_cu_upgrade_only_actions" local ts="cu_check_timestamp" local do="cu_do" # Required for upgrade / migration $install_or_upgrade $ts '2013/05/31 12:06:37' $do generate_horde_password $install_or_upgrade $ts '2013/05/31 12:06:39' $do install_horde_db $install_or_upgrade $ts '2013/05/31 12:07:41' $do generate_horde_conf_php # Upgrade Horde from 5.1.4 to 5.2.1 (plesk 12.0.18 mu14) $install_or_upgrade $ts '2014/05/11 15:01:37' $do echo_try "register horde5.2.1 and its application in local PEAR" $install_or_upgrade $ts '2014/05/11 15:01:39' $do horde_install_framework $install_or_upgrade $ts '2014/05/11 15:01:41' $do imp_install_application $install_or_upgrade $ts '2014/05/11 15:01:43' $do turba_install_application $install_or_upgrade $ts '2014/05/11 15:01:45' $do ingo_install_application $install_or_upgrade $ts '2014/05/11 15:01:47' $do mnemo_install_application $install_or_upgrade $ts '2014/05/11 15:01:50' $do kronolith_install_application $install_or_upgrade $ts '2014/05/11 15:01:52' $do passwd_install_application $install_or_upgrade $ts '2014/05/11 15:01:54' $do suc $install_or_upgrade $ts '2014/05/11 15:05:34' $do echo_try "execute horde5.2.1 upgrade scripts" $install_or_upgrade $ts '2014/05/11 15:05:35' $do horde_run_migration_script $install_or_upgrade $ts '2014/05/11 15:05:36' $do horde_run_migration_script imp $install_or_upgrade $ts '2014/05/11 15:05:38' $do horde_run_migration_script turba $install_or_upgrade $ts '2014/05/11 15:05:39' $do horde_run_migration_script ingo $install_or_upgrade $ts '2014/05/11 15:05:41' $do horde_run_migration_script mnemo $install_or_upgrade $ts '2014/05/11 15:05:43' $do horde_run_migration_script kronolith $install_or_upgrade $ts '2014/05/11 15:05:46' $do suc # switch webmails to plesk-php7x $upgrade_only $ts '2018/12/26 17:23:00' $do deferred_httpd_reconfigure_webmail $upgrade_only $ts '2019/08/06 14:36:00' $do generate_horde_conf_php # switch webmails to plesk-php74 $upgrade_only $ts '2021/08/30 10:00:00' $do deferred_httpd_reconfigure_webmail # ATTENTION! This should be the last action in list (Don't forget to update timestamp). # upgrade_only actions are not registered on clean installation. # so here comes a little hack to update current timestamp. $install_or_upgrade $ts '2021/08/30 17:52:00' $do true # WARNING: all $install_or_upgrade actions will be executed on clean installation as well! # DO NOT MODIFY ANYTHING BELOW THIS LINE! (without approval of review from the entire Linux team) call_optional_function dev_horde_upgrader cu_verify_latest_timestamp_lt '2021/10/10 11:00:00' } horde_install_framework() { [ -z "$horde_packages_registered" ] || return 0 if pear_register_horde_packages install 'Horde 5 framework' "${horde_datadir}/.xml"; then horde_packages_registered="True" else unset horde_packages_registered fi } imp_install_application() { pear_register_horde_packages install 'H5-application imp' "${imp_datadir}/.xml" || : } ingo_install_application() { pear_register_horde_packages install 'H5-application ingo' "${ingo_datadir}/.xml" || : } kronolith_install_application() { pear_register_horde_packages install 'H5-application kronolith' "${kronolith_datadir}/.xml" || : } mnemo_install_application() { pear_register_horde_packages install 'H5-application mnemo' "${mnemo_datadir}/.xml" || : } turba_install_application() { pear_register_horde_packages install 'H5-application turba' "${turba_datadir}/.xml" || : } passwd_install_application() { pear_register_horde_packages install 'H5-application passwd' "${passwd_datadir}/.xml" || : } horde_cu_upgrade_only_actions() { [ "$HORDE_CLEAN_INSTALL_FLAG" = "yes" ] && return "$@" } horde_bootstrapper_prep_install() { requires_configured_mysql } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. true fail2banconfigurator_upgrader_conf fail2banconfigurator_upgrader_conf() { upgrader_name="fail2ban" upgrader_description="Fail2ban" upgrader_func_prefix="fail2ban" upgrader_stages="bootstrapper-prep bootstrapper-post" upgrader_call_init="false" # Do not wipe it out after the sh_optimizer processing true fail2ban_upgrader # set params set_fail2ban_params } fail2ban_upgrader() { local bprep="cu_check_stage bootstrapper-prep" local bpost="cu_check_stage bootstrapper-post" local ts="cu_check_timestamp" local do="cu_do" # These are main configuration upgrade actions. Bump their timestamp when significant changes to configuration are made. $bprep $ts '2025/01/14 14:00:01' $do fail2ban_conf_upgrade_prep $bpost $ts '2025/01/14 14:00:02' $do fail2ban_conf_upgrade_post # One-time jails initialization - if HTTPD_VHOSTS_D was altered, fix Apache-related jails. # Note that anything that customizes jails should go after fail2ban_conf_upgrade_*. $bpost $ts '2014/02/11 10:16:20' $do fail2ban_fix_httpd_vhosts_d_path_in_jail "plesk-apache" $bpost $ts '2014/02/11 10:16:21' $do fail2ban_fix_httpd_vhosts_d_path_in_jail "plesk-apache-badbot" # remove fail2ban database if it is quite big, ie it has met with the dbpurge-related issue (fixed in 0.11) $bprep $ts '2021/05/19 18:00:00' $do fail2ban_remove_never_purged_database $bpost $ts '2021/05/19 18:00:01' $do fail2ban_remove_old_backups # DO NOT MODIFY ANYTHING BELOW THIS LINE! (without approval of review from the entire Linux team) call_optional_function dev_fail2ban_upgrader cu_verify_latest_timestamp_lt '2025/01/19 11:00:00' } fetch_current_fail2ban_version() { [ -x /usr/bin/fail2ban-server ] || return 0 /usr/bin/fail2ban-server --version | head -n1 | sed -ne 's|^Fail2Ban v\(.*\)$|\1| p' } set_fail2ban_params() { fail2ban_service="fail2ban" ### Historical hardcode of values from //unix/plesk/Mk/plesk.fail2ban.defs.py ### Feel free to fix by passing them at compile time fail2ban_conf_d="/etc/fail2ban" fail2ban_jail_d="$fail2ban_conf_d/jail.d" fail2ban_filter_d="$fail2ban_conf_d/filter.d" fail2ban_action_d="$fail2ban_conf_d/action.d" fail2ban_fail2ban_d="$fail2ban_conf_d/fail2ban.d" fail2ban_upg_source_conf_d="$fail2ban_conf_d.previous" fail2ban_upg_source_version_f="$fail2ban_upg_source_conf_d/.version" fail2ban_upg_need_start_flag="$fail2ban_upg_source_conf_d/.needs-service-start" } fail2ban_fix_httpd_vhosts_d_path_in_jail() { local jail="$1" local f2bmng="$PRODUCT_ROOT_D/admin/sbin/f2bmng" local httpd_vhosts_d_orig="/var/www/vhosts" echo_try "fix HTTPD_VHOSTS_D value in '$jail' Fail2Ban jail" local jail_config_json="`$f2bmng --get-jail \"$jail\"`" 2>> "$product_log" if [ -n "$jail_config_json" ]; then # FIXME: Technically, both pattern and replacement should be escaped for sed; also strip trailing slash # echo in dash interprets escape sequences, so use printf instead printf "%s" "$jail_config_json" | \ sed -e 's#\(\s"\|\\n\)'"$httpd_vhosts_d_orig"'/#\1'"$HTTPD_VHOSTS_D"'/#g' | \ $f2bmng --set-jail "$jail" >> "$product_log" 2>&1 \ && suc || { p_echo "failed"; return 1; } else p_echo "Skipped, no '$jail' configuration found" fi } fail2ban_conf_upgrade_prep() { local previous_f2b_version="`fetch_current_fail2ban_version`" if [ -e "$fail2ban_upg_source_conf_d" ]; then p_echo "Removing old '$fail2ban_upg_source_conf_d'" rm -rf "$fail2ban_upg_source_conf_d" fi if [ -d "$fail2ban_conf_d" ]; then cp -ar "$fail2ban_conf_d" "$fail2ban_upg_source_conf_d" [ -z "$previous_f2b_version" ] || echo "$previous_f2b_version" > "$fail2ban_upg_source_version_f" cat > "$fail2ban_upg_source_conf_d/README.upgrade" <<-EOF This folder contains Fail2Ban configuration prior to upgrade from version '${previous_f2b_version:-<unknown>}'. Actual current configuration is located at '$fail2ban_conf_d'. You may safely delete this folder and all of its content as long as you're satisfied with current Fail2Ban operation. EOF ! pleskrc fail2ban status || echo "yes" > "$fail2ban_upg_need_start_flag" else [ -z "$previous_f2b_version" ] || \ p_echo "Previous Fail2Ban version is '$previous_f2b_version', but no configuration found in '$fail2ban_conf_d'. Configuration upgrade will be skipped." fi } fail2ban_conf_upgrade_failure_readme() { # WARNING: this function MUST have stdin, or it'll hang cat > "$fail2ban_conf_d/README.upgrade" <<-EOF Fail2Ban or its Plesk-specific configuration was upgraded to the current version on `date -R`. Unfortunately, automatic configuration upgrade either failed or was not possible. Original Fail2Ban configuration from before upgrade was copied to '$fail2ban_upg_source_conf_d'. You may use it to manually upgrade current configuration. Once you're satisfied with the Fail2Ban operation, you may simply remove this copy. EOF echo >> "$fail2ban_conf_d/README.upgrade" cat - >> "$fail2ban_conf_d/README.upgrade" } fail2ban_conf_upgrade_success_readme() { cat > "$fail2ban_conf_d/README.upgrade" <<-EOF Fail2Ban or its Plesk-specific configuration was successfully upgraded to the current version on `date -R`. Original Fail2Ban configuration from before upgrade was copied to '$fail2ban_upg_source_conf_d'. If you experience problems with the current Fail2Ban configuration you may use the previous one as a reference to fix the current configuration. Once you're satisfied with the Fail2Ban operation, you may simply remove the copy. EOF } fail2ban_conf_remove_customizations() { echo_try "remove local customization from '$fail2ban_conf_d' to prepare for upgrade" find "$fail2ban_conf_d" -name '*.local' -delete >> "$product_log" 2>&1 find "$fail2ban_jail_d" "$fail2ban_filter_d" "$fail2ban_action_d" -mindepth 1 -name '*.d' -type d -empty -delete >> "$product_log" 2>&1 find "$fail2ban_fail2ban_d" -name '*.d' -type d -empty -delete >> "$product_log" 2>&1 suc } fail2ban_conf_remove_obsolete_configs() { [ -x "/usr/bin/dpkg-query" ] || return 0 # Drop obsolete configuration files on deb-based systems. # dpkg will still remember them but only till another upgrade, so that should be OK. /usr/bin/dpkg-query -f '${Conffiles}\n' -W fail2ban plesk-fail2ban-configurator | \ awk '$NF == "obsolete" && $1 ~ "^/etc/fail2ban/" {print $1}' | xargs -n 100 rm -f } fail2ban_conf_overwrite_packaged_file_customizations() { local confd="$1" local suff=".dpkg-dist" echo_try "overwrite fail2ban packaged file customizations at $confd" find "$confd" -type f -name "*.conf$suff" -print | while read line; do local fname="`echo -n "$line" | sed -e "s/$suff\$//"`" mv -f "$line" "$fname" done suc } fail2ban_conf_upgrade_post() { local previous_f2b_version="`cat \"$fail2ban_upg_source_version_f\" 2>/dev/null`" local current_f2b_version="`fetch_current_fail2ban_version`" [ -d "$fail2ban_conf_d" ] || { p_echo "Skipped Fail2Ban upgrade - no target config directory"; return 0; } [ -d "$fail2ban_upg_source_conf_d" ] || { p_echo "Skipped Fail2Ban upgrade - no source config directory"; return 0; } [ -n "$previous_f2b_version" -a -n "$current_f2b_version" ] || { fail2ban_conf_upgrade_failure_readme <<-EOF Upgrade was not preformed as source (${previous_f2b_version:-<unknown>}) or target (${current_f2b_version:-<unknown>}) configuration versions were not known. EOF return 0 } fail2ban_conf_remove_obsolete_configs fail2ban_conf_overwrite_packaged_file_customizations "$fail2ban_conf_d" fail2ban_conf_remove_customizations local inten="upgrade Fail2Ban configuration from version '$previous_f2b_version' to version '$current_f2b_version'" echo_try "$inten" local f2bmng="$PRODUCT_ROOT_D/admin/sbin/f2bmng" local output= output=`$f2bmng --upgrade-configuration --source-basedir="$fail2ban_upg_source_conf_d" --source-version="$previous_f2b_version" 2>&1` if [ "$?" -eq 0 ]; then fail2ban_conf_upgrade_success_readme [ -z "$output" ] || { p_echo "Additional output from the upgrade utility:" echo "$output" >> "$product_log" } [ ! -s "$fail2ban_upg_need_start_flag" ] || pleskrc fail2ban start suc else fail2ban_conf_upgrade_failure_readme <<EOF Upgrade was not performed as upgrade utility failed. See upgrade log below: $output EOF p_echo "Upgrade utility failed with following messages:" echo "$output" >> "$product_log" warn "$inten" fi } fail2ban_remove_never_purged_database() { local inten="check version and size of the fail2ban database" echo_try "$inten" local db="/var/lib/fail2ban/fail2ban.sqlite3" if ! [ -e "$db" ]; then pnnl_echo "database not found. " && suc return 0 fi local db_version="$(sqlite3 "$db" 'select version from fail2banDb' 2>/dev/null)" local db_size="$(stat --format %s "$db" 2>/dev/null)" pnnl_echo "version ${db_version:-unknown}, size ${db_size:-0} bytes. " && suc # if db has already been successfully migrated to v4 or greater # then it can be purged by f2b since dbpurge-related bug was fixed in 0.11 [ "$db_version" = "2" -o "$db_version" = "3" ] || return 0 # if db is "quite small" then it can be migrated in the normal way # note: customers complained if size was greather than 2Gb # so keep db as is if its size less than 512Mb (eq to ~4M of banned IPs) [ ${db_size:-0} -gt $((512 * 1024 * 1024)) ] || return 0 inten="purge fail2ban database" echo_try "$inten" ! pleskrc fail2ban status 2>/dev/null || local f2b_is_active="yes" [ -z "$f2b_is_active" ] || pleskrc fail2ban stop rm -f "$db" [ -z "$f2b_is_active" ] || pleskrc fail2ban start suc } fail2ban_remove_old_backups() { local inten="remove backups of the fail2ban database" echo_try "$inten" rm -f /var/lib/fail2ban/fail2ban.sqlite3.20* suc } # vim:ft=sh: ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. fail2banconfigurator_bootstrapper_prep_install() { # Note that cu_upgrader_conf_init shouldn't be called anywhere for fail2ban upgrader # That is, upgrade is attempted on new installations as well (e.g., upgrade from system fail2ban package) # upgrader_run requires PACKAGE_ID to be set, but nobody does it in bootstrapper prep-install. local PACKAGE_ID="fail2banconfigurator" upgrader_run bootstrapper-prep } # vim:ft=sh: ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # mode: shell-script # vim:syntax=sh set_locales_params() { active_locales_filename="${PRODUCT_ROOT_D}/var/psa_active_locales" } save_active_locales() { db_select "SELECT id FROM locales WHERE active = 'true'" if [ -z "$db_select_output" ]; then rm -f "$active_locales_filename" else echo "$db_select_output" > "$active_locales_filename" fi } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # mode: shell-script # vim:syntax=sh l10n_bootstrapper_prep_install() { if ! is_product_installation; then uses_mysql_client set_locales_params save_active_locales fi } ### 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" \ } safe_prep_db_upgrade() { p_echo p_echo "===> Upgrading database" p_echo [ ! -s "$PRODUCT_ROOT_D/version" ] || get_prev_version "$PRODUCT_ROOT_D" uses_mysql_client check_db safe_prep_db_backup_all upgrade_apsc_prep_stage core_run_upgrade_prep_stage } safe_prep_db_backup_all() { # backup database and register rollback action db_backup SAFE_PREP_DB_BACKUP="$dfile" transaction_add_rollback_action "safe_prep_db_upgrade_rollback" } safe_prep_db_upgrade_rollback() { if [ -n "$SAFE_PREP_DB_BACKUP" ]; then p_echo p_echo "===> Restoring database from backup $SAFE_PREP_DB_BACKUP" p_echo zcat "$SAFE_PREP_DB_BACKUP" | mysql_anydb if [ $? -ne 0 ]; then p_echo p_echo " [ERROR] Database restoring failed" p_echo " Try to restore database from $SAFE_PREP_DB_BACKUP manually" p_echo return fi SAFE_PREP_DB_BACKUP= fi } safe_prep_db_install() { requires_configured_mysql product_install_db_local } safe_prep_db() { p_echo p_echo "===> Performing safe prep-install database actions" p_echo # perform database-related prep actions if is_product_installation; then safe_prep_db_install else safe_prep_db_upgrade fi } 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_prep_stage() { local action_name="`core_upgrader_action_name $1`" local optional_repair_arg="`core_upgrader_repair_arg $1`" pp_echo "===> Cumulative Plesk database $action_name (revertable 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_db_short_version" --stage "prep" $optional_repair_arg local ret="$?" if [ "$ret" -eq 0 ]; then pp_echo "===> Cumulative $action_name of Plesk database (revertable stage) has been completed." else pp_echo "===> Plesk database was not upgraded completely. See installation log for details." # reflect error in exit-code in repair mode: [ ! "$1" = "repair" ] || return "$ret" 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 } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: create_missing_localtime_file() { [ -f /etc/localtime ] && return 0 which timedatectl >/dev/null 2>&1 || { warn "There is no /etc/localtime file in system and timedatectl is missing. Please set up timezone" return 1 } timedatectl set-timezone Etc/UTC || return 1 return 0 } check_tmp_permissions() { local tmp_perm_mask=777 [ $((0`stat -c %a /tmp` & 0$tmp_perm_mask)) -eq $((0$tmp_perm_mask)) ] && return 0 if [ -f /tmp/pp-bootstrapper-ez-templates-prep-install.flag ]; then chmod 1777 /tmp p_echo " /tmp file access permissions were adjusted to 1777." p_echo return 0 else warn "/tmp should have at least $tmp_perm_mask file access permissions." \ "Run 'chmod 1777 /tmp' as root to set up proper file access permissions." return 1 fi } preinstall_checks() { check_tmp_permissions || exit 1 } maintainer_main_pre() { set_local_params # debian will try upgrade unconfigured package # it is not an upgrade, actually if [ "X$do_upgrade" = "X1" ]; then if [ ! -s ${PRODUCT_ROOT_D}/version ]; then do_upgrade=0 fi fi # Upgrade psa -> plesk-core if [ "X$do_upgrade" = "X0" -a -s ${PRODUCT_ROOT_D}/version ]; then do_upgrade=1 fi create_missing_localtime_file || exit 1 preinstall_checks if [ "X$do_upgrade" = "X1" -a -s ${PRODUCT_ROOT_D}/version ]; then cp ${PRODUCT_ROOT_D}/version ${PRODUCT_ROOT_D}/version.upg fi add_login_shell "$PRODUCT_ROOT_D/bin/chrootsh" } core_bootstrapper_prep_install() { selinux_init maintainer_main_pre # Perform safe prep-install db actions safe_prep_db selinux_close } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:syntax=sh declare_legacy_variables() { echo_try "got legacy variables" [ -n "$prod_conf_t" ] || prod_conf_t="/etc/psa/psa.conf" if [ ! -s "$prod_conf_t" ]; then p_echo "product conf not exists, nothing to be done" return fi local var PLESK_LEGACY_VARS="$*" for var in $PLESK_LEGACY_VARS; do unset $var eval $var=`conf_getvar $prod_conf_t $var` # save the value done suc } dump_legacy_variables() { echo_try "save legacy variables" [ -n "$prod_conf_t" ] || prod_conf_t="/etc/psa/psa.conf" if [ ! -s "$prod_conf_t" ]; then p_echo "product conf not exists, cannot dump anything" return fi local var local value for var in $PLESK_LEGACY_VARS; do value="`eval echo \\$${var}`" [ -n "$value" -a -z "`conf_getvar $prod_conf_t $var`" ] || continue # variable is not defined or already in psa.conf echo "$var $value" >> $prod_conf_t done suc } product_logs_dir_fix_permissions() { [ -d "$PRODUCT_LOGS_D" ] || mkdir -p "$PRODUCT_LOGS_D" chmod 0750 "$PRODUCT_LOGS_D" chown "psaadm:0" "$PRODUCT_LOGS_D" } psa_rollback_product_config() { if [ -n "$prod_conf_t_bak" -a -f "$prod_conf_t_bak" ]; then mv -f "$prod_conf_t_bak" "$prod_conf_t" unset prod_conf_t_bak fi } psa_commit_product_config() { if [ -n "$prod_conf_t_bak" -a -f "$prod_conf_t_bak" ]; then rm -f "$prod_conf_t_bak" unset prod_conf_t_bak fi } psa_bootstrapper_prep_install() { # Create users and groups (rollback is not required) p_echo "Create user 'psaadm' and group 'psaadm'" group_op "psaadm" "search" false user_op "psaadm" "search" "psaadm" "psa user" "${PRODUCT_ROOT_D}/admin" "$dummy_shell" false p_echo "Create group 'swkey-data'" group_op "swkey-data" "search" false add_user_to_group "psaadm" "swkey-data" # for psaservices (only after creating apache and psaftp) p_echo "Create group 'psaserv'" group_op "psaserv" "10003" false add_user_to_group "psaadm" "psaserv" set_apache_params p_echo "Create group 'psacln'" group_op "psacln" search false "10001" $max_suexec_GID # Create or update product config prod_conf_t="/etc/psa/psa.conf" if [ -f "$prod_conf_t" ]; then prod_conf_t_bak="${prod_conf_t}.bak" cp -pf "$prod_conf_t" "$prod_conf_t_bak" transaction_add_rollback_action "psa_rollback_product_config" transaction_add_commit_action "psa_commit_product_config" fi cp -f $PRODUCT_BOOTSTRAPPER_DIR/psa.conf.default /etc/psa/psa.conf.default chmod 755 /etc/psa declare_legacy_variables CURL_CA_BUNDLE_FILE HTTPD_BIN_D LOGROTATE CGI_PHP_BIN FTPD_BIN_D FTPD_VAR_D FTPD_CONF FTPD_CONF_INC FTPD_SCOREBOARD \ DRWEB_ROOT_D DRWEB_ETC_D conf_update $prod_conf_t && p_echo "===> Updated $prod_conf_t" dump_legacy_variables product_logs_dir_fix_permissions } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: true roundcube_upgrader_conf roundcube_upgrader_conf() { upgrader_name="roundcube" upgrader_description="Roundcube webmail" upgrader_func_prefix="roundcube" upgrader_stages="files" # Do not wipe it out after the sh_optimizer processing true roundcube_upgrader # set params uses_mysql_client set_apache_params set_roundcube_params } roundcube_upgrader() { local files="cu_check_stage files" local ts="cu_check_timestamp" local do="cu_do" cu_start_upgrade_from_version 017008009 # grant references to mysql-5.5 support $files $ts '2018/02/15 15:34:00' $do roundcube_fix_db_grants cu_start_upgrade_from_version 017009009 # switch webmails to plesk-php7x $files $ts '2018/12/26 17:23:00' $do deferred_httpd_reconfigure_webmail cu_start_upgrade_from_version 018000025 # upgrade Roundcube from 1.3.10 to 1.4.3 cu_start_upgrade_from_version 018000038 # switch webmails to plesk-php74 $files $ts '2021/08/30 10:00:00' $do deferred_httpd_reconfigure_webmail cu_start_upgrade_from_version 018000047 # switch webmails to plesk-php80 $files $ts '2022/11/14 10:00:00' $do deferred_httpd_reconfigure_webmail cu_start_upgrade_from_version 018000056 # switch webmails to plesk-php82 $files $ts '2023/11/01 10:00:00' $do deferred_httpd_reconfigure_webmail cu_start_upgrade_from_version 018000066 # switch webmails to plesk-php83 $files $ts '2024/12/30 10:24:00' $do deferred_httpd_reconfigure_webmail # DO NOT MODIFY ANYTHING BELOW THIS LINE! (without approval of review from the entire Linux team) call_optional_function dev_roundcube_upgrader cu_verify_latest_timestamp_lt '2025/01/19 11:00:00' } roundcube_fix_db_grants() { local mysql_db_name='mysql' db_do "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, REFERENCES ON \`roundcubemail\`.* TO 'roundcube'@'localhost'; FLUSH PRIVILEGES;" } roundcube_bootstrapper_prep_install() { requires_configured_mysql } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh postfix_disable_smtputf8() { # We decided to disable smtputf8 for a while. # These changes must be rolled back after the root of a problem has been fixed. # Original value of smtputf8_enable is '${{$compatibility_level} < {1} ? {no} : {yes}}' local smtputf8_value=`/usr/sbin/postconf -h smtputf8_enable 2>/dev/null` if [ "$smtputf8_value" != "no" ]; then /usr/sbin/postconf -e smtputf8_enable=no deferred_postfix_restart fi } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: true mailpcdriver_upgrader_conf mailpcdriver_upgrader_conf() { upgrader_name="mail-pc-driver" upgrader_description="Postfix Plesk mail driver" upgrader_func_prefix="mailpcdriver" upgrader_stages="post" # Do not wipe it out after the sh_optimizer processing true mailpcdriver_upgrader } mailpcdriver_upgrader() { local post="cu_check_stage post" local ts="cu_check_timestamp" local do="cu_do" cu_start_upgrade_from_version 017009004 $post $ts '2018/08/22 16:30:00' $do postfix_restrict_authorized_queue_users cu_start_upgrade_from_version 018000014 $post $ts '2019/05/21 16:30:00' $do postfix_fix_debian_chroot $post $ts '2019/05/23 15:00:00' $do postfix_force_systemd_respawn $post $ts '2019/05/24 12:30:00' $do deferred_install_postfix_smtpd_sasl_config $post $ts '2019/05/31 13:44:00' $do postfix_sni_support cu_start_upgrade_from_version 018000020 $post $ts '2019/10/23 10:17:14' $do postfix_clear_smtp_privileged cu_start_upgrade_from_version 018000031 $post $ts '2020/10/27 10:30:00' $do postfix_revert_smtpd_timeouts_to_default cu_start_upgrade_from_version 018000033 $post $ts '2021/01/26 08:10:00' $do postfix_increase_compatibility_level $post $ts '2021/03/01 08:10:00' $do postfix_reenable_chroot cu_start_upgrade_from_version 018000034 $post $ts '2021/04/05 09:10:00' $do postfix_disable_smtputf8 cu_start_upgrade_from_version 018000037 $post $ts '2021/07/19 14:30:00' $do postfix_enable_postfix_local_tracing cu_start_upgrade_from_version 018000046 $post $ts '2022/08/26 12:00:00' $do postfix_pc_remote_purge_sysvinit cu_start_upgrade_from_version 018000057 $post $ts '2024/01/06 15:00:00' $do prevent_smtp_smuggling_attack cu_start_upgrade_from_version 018000061 $post $ts '2024/06/11 14:00:00' $do select_maillog cu_start_upgrade_from_version 018000066 $post $ts '2024/10/28 10:00:00' $do move_postfix_sasl2_conf # DO NOT MODIFY ANYTHING BELOW THIS LINE! (without approval of review from the entire Linux team) call_optional_function dev_mailpcdriver_upgrader cu_verify_latest_timestamp_lt '2024/12/08 11:00:00' } postfix_restrict_authorized_queue_users() { /usr/sbin/postconf -e authorized_mailq_users= authorized_flush_users= } postfix_fix_debian_chroot() { # chroot environment may not be configured in Debian # because call of configure-instance was omitted in debian startup script pleskrc postfix try-restart } postfix_force_systemd_respawn() { # systemd respawn config may not be created because systemd unit was omitted enable_respawn_service postfix.service /bin/systemctl daemon-reload } postfix_sni_support() { # set tls_server_sni_maps in main.cf write_postconf_value "tls_server_sni_maps" "hash:/var/spool/postfix/plesk/certs" # create empty certs.db database even if SNI is not supported on this OS [ ! -f "/var/spool/postfix/plesk/certs.db" ] || return 0 echo "" | /usr/sbin/postmap -i "hash:/var/spool/postfix/plesk/certs" } postfix_clear_smtp_privileged() { # Clear unpriv=n for smtp transport with explicit IP or hostname # since process run as root requires CAP_DAC_OVERRIIDE capability # and related SELinux permission to access /var/spool/postfix directories. deferred_mail_pc_driver_transport_restore } postfix_revert_smtpd_timeouts_to_default() { local is_need_restart= local old_value=`/usr/sbin/postconf -h smtpd_timeout` if [ "$old_value" = "3600s" ]; then /usr/sbin/postconf -# smtpd_timeout is_need_restart=true fi old_value=`/usr/sbin/postconf -h smtpd_proxy_timeout` if [ "$old_value" = "3600s" ]; then /usr/sbin/postconf -# smtpd_proxy_timeout is_need_restart=true fi [ -z "$is_need_restart" ] || deferred_postfix_restart } postfix_increase_compatibility_level() { local is_need_restart= local old_value=`/usr/sbin/postconf -h compatibility_level` if [ "$old_value" = "0" ] || [ "$old_value" = "1" ]; then /usr/sbin/postconf compatibility_level=2 is_need_restart=true fi [ -z "$is_need_restart" ] || deferred_postfix_restart } postfix_reenable_chroot() { local is_need_restart= local chroot_value=`/usr/sbin/postconf -Fh smtps/inet/chroot 2>/dev/null` if [ "$chroot_value" = "-" ]; then /usr/sbin/postconf -F smtps/inet/chroot=y is_need_restart=true fi chroot_value=`/usr/sbin/postconf -Fh smtp/inet/chroot 2>/dev/null` if [ "$chroot_value" = "-" ]; then /usr/sbin/postconf -F smtp/inet/chroot=y is_need_restart=true fi [ -z "$is_need_restart" ] || deferred_postfix_restart } postfix_enable_postfix_local_tracing() { local inten="enable tracing in postfix-local" echo_try "$inten" local param="plesk_virtual/unix/command" local val=$("/usr/sbin/postconf" -F "$param") 2>>"$product_log" if [ -z "$val" ]; then warn "postconf failed to read \"$param\"" return 1 elif ! [ "${val/ -q /}" = "$val" ]; then p_echo "queue_id is already specified among args passed to postfix-local" return 0 fi "/usr/sbin/postconf" -Fe "$val -q \${queue_id}" >>"$product_log" 2>&1 if [ $? -ne 0 ]; then warn "postconf failed to set \"param\"" return 1 fi deferred_postfix_restart suc } postfix_pc_remote_purge_sysvinit() { # sysvinit scripts weren't shipped for systemd OSes # except for debian ones due to value of `HAS_SYSVINIT` # which is set to True up to debian 11 : } prevent_smtp_smuggling_attack() { # Prevent SMTP smuggling attacks https://www.postfix.org/smtp-smuggling.html # same logic as in mailsrv_conf_init.cc echo_try "configure postfix to prevent SMTP smuggling attack" if postconf_param_exists "smtpd_forbid_bare_newline"; then p_echo "enable smtpd_forbid_bare_newline" write_postconf_value "smtpd_forbid_bare_newline" "yes" write_postconf_value "smtpd_forbid_bare_newline_exclusions" '$mynetworks' elif postconf_param_exists "smtpd_forbid_unauth_pipelining"; then p_echo "enable smtpd_forbid_unauth_pipelining" write_postconf_value "smtpd_forbid_unauth_pipelining" "yes" write_postconf_value "smtpd_discard_ehlo_keywords" "chunking, silent-discard" else p_echo "reject unauth pipelining" write_postconf_value "smtpd_data_restrictions" "reject_unauth_pipelining" write_postconf_value "smtpd_discard_ehlo_keywords" "chunking, silent-discard" fi suc deferred_postfix_restart } move_postfix_sasl2_conf() { : } mailpcdriver_bootstrapper_prep_install() { # sendmail-bin.prerm script doesn't stop sendmail properly on deb-base OSes # it removes /usr/sbin/sendmail and then tries to check it in its sysvinit script # so it should be stopped before call of its prerm # disabling other mta's seems a little excessive # but they can also contain some surprises as well as sendmail set_foreign_mtas_params for mta in exim exim4 sendmail; do if pleskrc $mta exists; then pleskrc $mta stop unregister_service $mta fi done } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. # vim:ft=sh: true mailqcdriver_upgrader_conf mailqcdriver_upgrader_conf() { upgrader_name="mail-qc-driver" upgrader_description="Qmail Plesk mail driver" upgrader_func_prefix="mailqcdriver" upgrader_stages="post" # Do not wipe it out after the sh_optimizer processing true mailqcdriver_upgrader } mailqcdriver_upgrader() { local post="cu_check_stage post" local ts="cu_check_timestamp" local do="cu_do" cu_start_upgrade_from_version 018000061 $post $ts '2024/06/11 14:00:00' $do select_maillog # DO NOT MODIFY ANYTHING BELOW THIS LINE! (without approval of review from the entire Linux team) call_optional_function dev_mailqcdriver_upgrader cu_verify_latest_timestamp_lt '2024/06/23 11:00:00' } mailqcdriver_bootstrapper_prep_install() { # sendmail-bin.prerm script doesn't stop sendmail properly on deb-base OSes # it removes /usr/sbin/sendmail and then tries to check it in its sysvinit script # so it should be stopped before call of its prerm # disabling other mta's seems a little excessive # but they can also contain some surprises as well as sendmail set_postfix_params set_foreign_mtas_params for mta in postfix exim exim4 sendmail; do if pleskrc $mta exists; then pleskrc $mta stop unregister_service $mta fi done } ### Copyright 1999-2025. WebPros International GmbH. All rights reserved. bootstrapper_log_title() { local title="Bootstrapper $product_version $script_action" if [ -n "$script_target" ]; then title="$title for $script_target" fi title="$title AT `date`" echo "$title" } bootstrapper_log_subject() { local subject="Product $script_action" if [ -n "$script_target" ]; then subject="$subject for $script_target" fi echo "$subject" } lock_bootstrapper() { lockfile "/var/lock/parallels-panel-bootstrapper-running" || { p_echo "Another instance of bootstrapper is already running. Please wait until it finishes." p_echo "If you are completely sure there is no other instance running, " p_echo "just remove /var/lock/parallels-panel-bootstrapper-running.lock file and retry." p_echo "Note: running more than one instance at once may badly damage your system." exit 1 } } unlock_bootstrapper() { unlockfile "/var/lock/parallels-panel-bootstrapper-running" } bootstrapper_transaction_begin() { lock_bootstrapper log_start "`bootstrapper_log_title`" set_error_report_context "`bootstrapper_log_title`. `report_context_action`" set_common_params read_conf transaction_begin # LIFO rollback order transaction_add_rollback_action "unlock_bootstrapper" transaction_add_rollback_action "show_report_after_rollback" transaction_add_rollback_action "drop_upgrade_flag" p_echo p_echo "**** Product $script_action started." # Detect upgrade (most of the scripts depend on it) check_product_root } bootstrapper_transaction_end() { # FIFO commit order transaction_add_commit_action "show_report_after_commit" transaction_add_commit_action "unlock_bootstrapper" transaction_commit } show_report_after_rollback() { # Report fatal error log_stop "`bootstrapper_log_title`" "`bootstrapper_log_subject`" # Show manual restart recomendation for bootstrapped post-install action only. if [ -f /tmp/pp-bootstrapper-mode.flag -a "X$script_action" = "Xpost-install" ]; then p_echo "To complete product installation one should run bootstrapper script:" p_echo " $PRODUCT_BOOTSTRAPPER_DIR/bootstrapper.sh repair" p_echo fi } show_report_after_commit() { # show possible warnings in problem report log_stop "`bootstrapper_log_title`" "`bootstrapper_log_subject`" } update_plesk_completion_rules() { local update_plesk_completion_bin="$PRODUCT_ROOT_D/admin/sbin/update_plesk_completion" [ -x "$update_plesk_completion_bin" ] || return 0 # the utility is quite noisy, filter out useless messages "$update_plesk_completion_bin" 2>&1 | grep -v '^Usage:' >>$product_log } db_bootstrapper_prep_install() { if [ "$do_upgrade" != "1" -a "$do_repair" != "1" ]; then # This action doesn't required for clean install return fi pp_echo "===> Plesk database scheme upgrade has been started." local out out=`$PRODUCT_BOOTSTRAPPER_DIR/db.php migrate 2>&1` if [ $? -eq 0 ]; then echo "$out" | tee -a "$product_log" pp_echo "===> Plesk database scheme upgrade has been completed." else echo "$out" | tee -a "$product_log" "$product_problems_log" local err_msg="===> Plesk database scheme was not upgraded completely." if [ "$do_repair" = "1" ]; then pp_echo "$err_msg" return 1 else simply_die "$err_msg" fi fi } component_base_prep_install() { selinux_is_active if [ "$SELINUX_ENFORCE" = "Enforcing" ]; then pp_echo "Set selinux into the Permissive mode" touch /var/lock/selinux_disabled.flag && setenforce Permissive setenforce Permissive fi psa_bootstrapper_prep_install if [ ! -s "$PRODUCT_ROOT_D/version" ]; then ! try_recreate_dsn_config || validate_dsn_config fi core_bootstrapper_prep_install db_bootstrapper_prep_install l10n_bootstrapper_prep_install if [ "X$PREP_INSTALL_ROLLBACK_CHECK" = "XYes" ]; then simply_die "We die to check bootstrapper rollback" fi } # bootstrapper post install actions shipped as the packages scripts # and should be executed separately. execute_bootstrapper_script() { local component="$1" local action="$2" local is_essential="$3" local script_d="/opt/psa/bootstrapper/components" local script_f="$script_d/${component}.sh" if [ ! -x "$script_f" ]; then [ -z "$is_essential" ] \ || die "Unable to execute ${action} actions for component ${component}. File not found ($script_f)" return 0 fi $script_f $action } component_base_post_install() { execute_bootstrapper_script "psa" "post-install" "true" execute_bootstrapper_script "libpam-plesk" "post-install" "true" execute_bootstrapper_script "core" "post-install" "true" execute_bootstrapper_script "l10n" "post-install" "true" execute_bootstrapper_script "backup-utilities" "post-install" "true" update_plesk_completion_rules if [ -f "/var/lock/selinux_disabled.flag" ]; then pp_echo "Set selinux into the Enforcing mode" setenforce Enforcing rm -f /var/lock/selinux_disabled.flag fi } set_optional_component_params() { # Note: the following function is generated at build time. set_component_pkgname_list optional_components=" horde roundcube mail-pc-driver mail-qc-driver mail-mc-driver psa-vhost spammng courier-imap dovecot fail2ban-configurator config-troubleshooter php-configurator " } is_component_installed() { local component=$1 local package=$(eval "echo \$$(echo COMPONENT_PKGNAME_`echo $component | tr -d '-'`)") [ -n "$package" ] || return 1 is_package_installed_apt $package } is_component_installed_old() { # assume that packages in components weren't renamed is_component_installed "$@" } component_optional_prep_install() { local component="$1" if [ "${component}" = "BASE" ]; then component_base_prep_install return $? fi if is_function "`echo $component | tr -d '-'`_bootstrapper_prep_install"; then "`echo $component | tr -d '-'`_bootstrapper_prep_install" else p_echo "Warning: there is no bootstrapper 'prep-install' action for component '$component'" fi if [ "X$PREP_INSTALL_ROLLBACK_CHECK" = "XYes" ]; then simply_die "We die to check bootstrapper rollback" fi } component_optional_post_install() { local component="$1" if [ "${component}" = "BASE" ]; then component_base_post_install return $? fi execute_bootstrapper_script "$component" "post-install" } configure_local_components() { deferred_webserver_apache_configure deferred_ftp_proftpd_configure deferred_awstats_configure } prepare_repair_actions() { set_local_params uses_mysql_client check_db safe_prep_db_backup_all } fix_services_registration() { local pleskrc_bin="$PRODUCT_ROOT_D/admin/sbin/pleskrc" local services_list="sw-cp-server sw-engine qmail courier-imapd courier-imaps courier-pop3d courier-pop3s dovecot kavehost drwebd spamassassin pc-remote nginx psa" for service in $services_list; do if "$pleskrc_bin" "$service" exists; then register_service "$service" || true "$pleskrc_bin" "$service" status >/dev/null 2>&1 || \ "$pleskrc_bin" "$service" start || true fi done } reset_lock_manager() { local cache_dir="$PRODUCT_ROOT_D/var/cache" set_admin_params [ ! -d "$cache_dir" ] && mkdir -p "$cache_dir" set_ac $admin_user root 0700 "$cache_dir" find "$cache_dir" -type f -name "SharedLockManagerStorage*" -print0 | xargs -0 rm -f add_user_to_group $admin_user lock-manager } run_repair_actions() { local ret=0 local failed_actions= pre_accumulate_error ret failed_actions pp_echo "Started bootstrapper repair procedure${PLESK_INSTALLER_FAST_REPAIR:+ (fast mode)}. This may take a while." pp_echo "Certain actions may be skipped if not applicable." pp_echo prepare_repair_actions set_optional_component_params pp_echo " Finishing up upgrade procedures and rerunning previously failed upgrade actions..." reset_lock_manager || accumulate_error "reset lock manager" apsc_run_repair || accumulate_error "fix apsc" reset_sitebuilder_passwd no_force || accumulate_error "fix credentials for WPB database" repair_phpmyadmin || accumulate_error "fix PhpMyAdmin configuration storage access" reset_atmail_passwd no_force || accumulate_error "fix credentials for Atmail database" core_run_upgrade_prep_stage repair || accumulate_error "cumulative Plesk upgrade and repair revertible stage" db_bootstrapper_prep_install || accumulate_error "upgrade Plesk database scheme" run_bl_upgrade --repair || accumulate_error "upgrade Plesk business logic" core_run_upgrade_post_stage repair || accumulate_error "cumulative Plesk upgrade and repair final stage" execute_bootstrapper_script "spammng" "repair" || accumulate_error "fix SpamAssassin configuration" execute_bootstrapper_script "courier-imap" "repair" || accumulate_error "fix Courier IMAP configuration" execute_bootstrapper_script "roundcube" "repair" || accumulate_error "fix RoundCube configuration" execute_bootstrapper_script "horde" "repair" || accumulate_error "fix Horde configuration" execute_bootstrapper_script "fail2ban-configurator" "repair" || accumulate_error "fix Fail2Ban configuration" execute_bootstrapper_script "dns-bind-driver" "repair" || accumulate_error "fix DNS Bind configuration" execute_bootstrapper_script "mail-mc-driver" "repair" || accumulate_error "fix MSMTP configuration" execute_bootstrapper_script "mail-pc-driver" "repair" || accumulate_error "fix Postfix configuration" execute_bootstrapper_script "mail-qc-driver" "repair" || accumulate_error "fix Qmail configuration" execute_bootstrapper_script "collectd" "repair" || accumulate_error "fix Plesk collectd configuration" execute_bootstrapper_script "web-socket" "repair" || accumulate_error "fix Plesk web-socket configuration" if [ -z "${PLESK_INSTALLER_SKIP_MCHK}" ]; then pp_echo " Reconfiguring mail subsystem..." mail_restore_all ${PLESK_INSTALLER_FAST_REPAIR:-full} || accumulate_error "restore mail" fi pp_echo " Reconfiguring Apache web server..." run_now_webserver_apache_configure || accumulate_error "fix Apache configuration" pp_echo " Reconfiguring ProFTPD FTP server..." run_now_ftp_proftpd_configure || accumulate_error "fix ProFTPD configuration" pp_echo " Reconfiguring AWStats web statistics..." run_now_awstats_configure || accumulate_error "fix AWStats configuration" pp_echo " Restoring SELinux contexts..." if [ -n "$PLESK_INSTALLER_FAST_REPAIR" ]; then relabel_plesk_directories --verbose || accumulate_error "fix SELinux contexts" else relabel_plesk_directories --verbose "$HTTPD_VHOSTS_D" "$PLESK_MAILNAMES_D" || accumulate_error "fix SELinux contexts" fi fix_services_registration || accumulate_error "fix services registration" pp_echo " Reconfiguring SSL ciphers and protocols..." configure_ssl_ciphers_protocols || accumulate_error "configure SSL ciphers and protocols" if [ -z "$PLESK_INSTALLER_FAST_REPAIR" ]; then pp_echo " Regenerating web servers' configuration files..." httpdmng_reconfigure all || accumulate_error "regenerate web servers configuration files" fi pp_echo " Cleaning active Panel sessions..." clean_panel_sessions || accumulate_error "clean Plesk panel sessions" pp_echo " Fixing permissions on Panel packages files" "$PRODUCT_ROOT_D/admin/sbin/fsmng" --fix-plesk-packages || accumulate_error "fix Plesk packages permissions" local installer_fail_flag="/var/parallels/autoinstaller-installation-failure.flag" if [ -e "$installer_fail_flag" ]; then pp_echo "The file '$installer_fail_flag' is present, which may indicate problems with recent update." pp_echo "Please check '$product_log' for details" fi pp_echo pp_echo "Bootstrapper repair finished." if [ -n "$failed_actions" ]; then pp_echo "Errors occurred while performing the following actions: $failed_actions." pp_echo "Check '$product_log' and '$product_problems_log' for details." else pp_echo "If problems persist, please check installer logs ('$product_log' and '$product_problems_log') for errors." fi pp_echo "If you can't resolve the issue on your own, please address Plesk support." return $ret } write_target_plesk_version() { # Write expected target Plesk version into file where package scripts will be able to read it. # This avoids baking in Plesk version into many package scripts. # The files will be removed either on rollback or on perform-deferred-actions at the end of installation. # It's OK to call this function multiple times during installation. local version_file="/var/lock/plesk-target-version" echo "18.0.68" > "$version_file" cat > "$version_file.README.txt" <<-EOT $version_file holds target Plesk version during installation, update or upgrade. If no Plesk installation, update or upgrade is running, both of these files may be safely removed. EOT } check_bootstrapper_version() { check_product_root if [ "$do_upgrade" != 1 ]; then return 0 fi local short_product_version=`form_undotted_version ${product_version}` if [ $short_product_version -lt $prev_short_version ]; then p_echo "It seems that there is a newer version (${prev_version}) of ${PRODUCT_NAME} installed" p_echo "so that a new bootstrapper script should be used instead." exit 1 fi return 0 } usage() { local prog="`basename $0`" echo "" echo "Bootstrap setup actions for ${PRODUCT_FULL_NAME} ${product_version}" echo "" echo "Usage: $prog <prep-install|post-install|rerun|perform-deferred-actions> [component|BASE]" echo " $prog <repair|perform-deferred-actions>" echo "" echo "Running '$prog repair' with PLESK_INSTALLER_FAST_REPAIR=1 environment" echo "variable set will skip some of the most time-consuming restore actions." echo "" } if [ "X${PLESK_INSTALLER_DEBUG}" != "X" ]; then set -x fi reexec_with_clean_env "$@" export PLESK_INTERNAL_PHP_EXEC=1 script_action="$1" script_target="$2" case "$script_action" in prep-install|rerun|repair) write_target_plesk_version ;; esac product_default_conf initial_conf check_bootstrapper_version case "$script_action" in prep-install) repair_action="$script_action" bootstrapper_transaction_begin set_optional_component_params if [ -z "$script_target" ]; then component_base_prep_install # We don't know here whether a certain optional component is going to be installed. # So do not execute prep-install actions for it here. # However, we may detect upgrade. In this case prep-install is executed. for comp in $optional_components; do is_component_installed_old $comp && component_optional_prep_install $comp; done else component_optional_prep_install $script_target fi bootstrapper_transaction_end ;; post-install) repair_action="repair" bootstrapper_transaction_begin set_optional_component_params if [ -z "$script_target" ]; then component_base_post_install for comp in $optional_components; do component_optional_post_install $comp; done configure_local_components perform_deferred_actions else component_optional_post_install $script_target fi bootstrapper_transaction_end ;; rerun) repair_action="rerun" do_repair=1 set_log_action_name "installation_rerun" bootstrapper_transaction_begin transaction_add_commit_action "reset_upgrade_failure_flag" transaction_add_rollback_action "reset_maintenance_mode_flag" set_maintenance_mode_flag set_optional_component_params if [ -z "$script_target" ]; then component_base_prep_install for comp in $optional_components; do is_component_installed $comp && component_optional_prep_install $comp; done for comp in $optional_components; do component_optional_post_install $comp; done component_base_post_install configure_local_components perform_deferred_actions else component_optional_prep_install $script_target component_optional_post_install $script_target fi bootstrapper_transaction_end ;; repair) ret=0 repair_action="repair" do_repair=1 set_repair_mode_flag set_log_action_name "repair" bootstrapper_transaction_begin transaction_add_commit_action "reset_upgrade_failure_flag" transaction_add_rollback_action "reset_maintenance_mode_flag" transaction_add_commit_action "remove_repair_mode_flag" transaction_add_rollback_action "remove_repair_mode_flag" set_maintenance_mode_flag run_repair_actions || ret=1 perform_deferred_actions || ret=1 bootstrapper_transaction_end exit $ret ;; perform-deferred-actions) bootstrapper_transaction_begin perform_deferred_actions bootstrapper_transaction_end ;; dump-incomplete-deferred-actions) dump_incomplete_deferred_actions ;; *) usage exit 1 ;; esac