From 7065cb69f1009c3cc145986bcdd5d8dbb31180f9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 31 Mar 2025 17:37:11 +0200 Subject: [PATCH] ejabberdctl: New "mnesia_change" command, a frontend to mnesia_change_nodename --- ejabberdctl.template | 124 ++++++++++++++++++++++++++++++++++++++-- src/ejabberd_ctl.erl | 11 ++++ src/ejabberd_mnesia.erl | 7 ++- 3 files changed, 134 insertions(+), 8 deletions(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 9d91e261c..58a0d8be6 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -318,6 +318,13 @@ check_start() # allow sync calls wait_status() { + wait_status_node "$ERLANG_NODE" $1 $2 $3 +} + +wait_status_node() +{ + CONNECT_NODE=$1 + shift # args: status try delay # return: 0 OK, 1 KO timeout="$2" @@ -329,9 +336,9 @@ wait_status() status="$1" else exec_erl "$(uid ctl)" -hidden -noinput \ - -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ -s ejabberd_ctl \ - -extra "$ERLANG_NODE" $NO_TIMEOUT status > /dev/null + -extra "$CONNECT_NODE" $NO_TIMEOUT status > /dev/null status="$?" fi done @@ -340,19 +347,26 @@ wait_status() exec_other_command() { + exec_other_command_node $ERLANG_NODE "$@" +} + +exec_other_command_node() +{ + CONNECT_NODE=$1 + shift if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \ || [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \ || [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then exec_erl "$(uid ctl)" -hidden -noinput \ - -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \ + -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \ -s ejabberd_ctl \ - -extra "$ERLANG_NODE" $NO_TIMEOUT "$@" + -extra "$CONNECT_NODE" $NO_TIMEOUT "$@" result=$? case $result in 3) help;; *) :;; esac - exit $result + return $result else exec_ctl_over_http_socket "$@" fi @@ -393,6 +407,103 @@ cd "$SPOOL_DIR" || { exit 6 } +printe() +{ + printf "\n" + printf "\e[1;40;32m==> %s\e[0m\n" "$1" +} + +## Function copied from tools/make-installers +user_agrees() +{ + question="$*" + + if [ -t 0 ] + then + printe "$question (y/n) [n]" + read -r response + case "$response" in + [Yy]|[Yy][Ee][Ss]) + return 0 + ;; + [Nn]|[Nn][Oo]|'') + return 1 + ;; + *) + echo 'Please respond with "yes" or "no".' + user_agrees "$question" + ;; + esac + else # Assume 'yes' if not running interactively. + return 0 + fi +} + +mnesia_change() +{ + ERLANG_NODE_OLD="$1" + [ "$ERLANG_NODE_OLD" = "" ] \ + && echo "Error: Please provide the old erlang node name, for example:" \ + && echo " ejabberdctl mnesia_change ejabberd@oldmachine" \ + && exit 1 + + SPOOL_DIR_BACKUP=$SPOOL_DIR/$ERLANG_NODE_OLD-backup/ + OLDFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE_OLD.backup + NEWFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE.backup + + printe "This changes your mnesia database from node name '$ERLANG_NODE_OLD' to '$ERLANG_NODE'" + + [ -d "$SPOOL_DIR_BACKUP" ] && printe "WARNING! A backup of old node already exists in $SPOOL_DIR_BACKUP" + + if ! user_agrees "Do you want to proceed?" + then + echo 'Operation aborted.' + exit 1 + fi + + printe "Starting ejabberd with old node name $ERLANG_NODE_OLD ..." + exec_erl "$ERLANG_NODE_OLD" $EJABBERD_OPTS -detached + wait_status_node $ERLANG_NODE_OLD 0 30 2 + result=$? + case $result in + 1) echo "There was a problem starting ejabberd with the old erlang node name. " \ + && echo "Check for log errors in $EJABBERD_LOG_PATH" \ + && exit $result;; + *) :;; + esac + exec_other_command_node $ERLANG_NODE_OLD "status" + + printe "Making backup of old database to file $OLDFILE ..." + mkdir $SPOOL_DIR_BACKUP + exec_other_command_node $ERLANG_NODE_OLD backup "$OLDFILE" + + printe "Changing node name in new backup file $NEWFILE ..." + exec_other_command_node $ERLANG_NODE_OLD mnesia_change_nodename "$ERLANG_NODE_OLD" "$ERLANG_NODE" "$OLDFILE" "$NEWFILE" + + printe "Stopping old ejabberd..." + exec_other_command_node $ERLANG_NODE_OLD "stop" + wait_status_node $ERLANG_NODE_OLD 3 30 2 && stop_epmd + + printe "Moving old mnesia spool files to backup subdirectory $SPOOL_DIR_BACKUP ..." + mv $SPOOL_DIR/*.DAT $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.DCD $SPOOL_DIR_BACKUP + mv $SPOOL_DIR/*.LOG $SPOOL_DIR_BACKUP + + printe "Starting ejabberd with new node name $ERLANG_NODE ..." + exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached + wait_status 0 30 2 + exec_other_command "status" + + printe "Installing fallback of new mnesia..." + exec_other_command install_fallback "$NEWFILE" + + printe "Stopping new ejabberd..." + exec_other_command "stop" + wait_status 3 30 2 && stop_epmd + + printe "Finished, now you can start ejabberd normally" +} + # main case $1 in start) @@ -452,6 +563,9 @@ case $1 in set_dist_client wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout ;; + mnesia_change) + mnesia_change $2 + ;; *) set_dist_client exec_other_command "$@" diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 72aac3780..d2bad4f19 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -1148,6 +1148,17 @@ get_commands_spec() -> desc = "Get list of commands, or help of a command (only ejabberdctl)", longdesc = "This command is exclusive for the ejabberdctl command-line script, " "don't attempt to execute it using any other API frontend."}, + #ejabberd_commands{name = mnesia_change, tags = [ejabberdctl, mnesia], + desc = "Change the erlang node name in the mnesia database (only ejabberdctl)", + longdesc = "This command internally calls the _`mnesia_change_nodename`_ API. " + "This is a special command that starts and stops ejabberd several times: " + "do not attempt to run this command when ejabberd is running. " + "This command is exclusive for the ejabberdctl command-line script, " + "don't attempt to execute it using any other API frontend.", + note = "added in 25.xx", + args = [{old_node_name, string}], + args_desc = ["Old erlang node name"], + args_example = ["ejabberd@oldmachine"]}, #ejabberd_commands{name = mnesia_info_ctl, tags = [ejabberdctl, mnesia], desc = "Show information of Mnesia system (only ejabberdctl)", note = "renamed in 24.02", diff --git a/src/ejabberd_mnesia.erl b/src/ejabberd_mnesia.erl index f3191b910..5b82dbb50 100644 --- a/src/ejabberd_mnesia.erl +++ b/src/ejabberd_mnesia.erl @@ -80,10 +80,11 @@ init([]) -> Schema = read_schema_file(), {ok, #state{schema = Schema}}; false -> - ?CRITICAL_MSG("Node name mismatch: I'm [~ts], " - "the database is owned by ~p", [MyNode, DbNodes]), + ?CRITICAL_MSG("Erlang node name mismatch: I'm running in node [~ts], " + "but the mnesia database is owned by ~p", [MyNode, DbNodes]), ?CRITICAL_MSG("Either set ERLANG_NODE in ejabberdctl.cfg " - "or change node name in Mnesia", []), + "or change node name in Mnesia by running: " + "ejabberdctl mnesia_change ~ts", [hd(DbNodes)]), {stop, node_name_mismatch} end.