Commit f92cd0c5 authored by Julius Goryavsky's avatar Julius Goryavsky

MDEV-25669: SST scripts should check all server groups in config files

1) This commit implements reading all sections from configuration
files while looking for the current value of any server variable,
which were previously only read from the [mysqld.suffix] group and
from [mysqld], but not from other groups such as [mariadb.suffix],
[mariadb] or, for example, [server].

2) This commit also fixes misrecognition of some parameters when
parsing a command line containing a special marker for the end
of the list of options ("--") or when short option names (such
as "-s", "-a" and "-h arg") chained together (like a "-sah arg").
Such parameters can be passed to the SST script in the list of
arguments after "--mysqld-args" if the server is started with a
complex set of options - this was revealed during manual testing
of changes to read configuration files.

3) The server-side preparation code for the "--mysqld-args"
option list has also been simplified to make it easier to change
in the future (if needed), and has been improved to properly
handle the special backquote ("`") character in the argument
values.
parent 16437e5e
...@@ -240,44 +240,108 @@ case "$1" in ...@@ -240,44 +240,108 @@ case "$1" in
original_cmd="" original_cmd=""
shift shift
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
# check if the argument is the short option lname="${1#--}"
# (starting with "-" instead of "--"): # "--" is interpreted as the end of the list of options:
if [ "${1#--}" = "$1" -a "${1#-}" != "$1" ]; then if [ -z "$lname" ]; then
option="${1#-}" shift
if [ $# -gt 0 ]; then
# copy "--" to the output string:
original_cmd="$original_cmd --"
# All other arguments must be copied unchanged:
while [ $# -gt 0 ]; do
original_cmd="$original_cmd '$1'"
shift
done
fi
break;
fi
# Make sure the argument does not start with "--", otherwise it
# is a long option, which is processed after this "if":
if [ "$lname" = "$1" ]; then
# Check if the argument is the short option or the short
# options list, starting with "-":
options="${1#-}"
if [ "$options" != "$1" -a -n "$options" ]; then
slist=""
while [ -n "$options" ]; do
# Let's separate the first character as the current
# option name:
if [ -n "$BASH_VERSION" ]; then
option="${options:0:1}"
else
# If it's not bash, then we need to use slow
# external utilities:
option=$(echo "$options" | cut -c1-1)
fi
# And the subsequent characters consider option value:
value="" value=""
# check that the option value follows the name, if [ ${#options} -gt 0 ]; then
# without a space: value="${options#?}"
if [ ${#option} -gt 1 ]; then fi
# let's separate the first character as the option name, # Check for options without argument:
# and the subsequent characters consider its value: if [ "$option" != '?' -a \
value="${1#-?}" "$option" != 'a' -a \
option="${1%$value}" "$option" != 's' -a \
# check that the option name consists of one letter "$option" != 'v' ]
# and there are the following arguments: then
elif [ ${#option} -eq 1 -a $# -gt 1 ]; then # If the option value is absent, then check
# if the next argument does not start with a "-" character, # the following argument:
# then this is the value of the current option: if [ -z "$value" -a $# -gt 1 ]; then
# if the next argument does not start with
# the "-" character, then next argument is
# the current option value:
if [ "${2#-}" = "$2" ]; then if [ "${2#-}" = "$2" ]; then
value="$2"
shift shift
value="$1"
fi fi
fi fi
shift if [ $option == 'h' ]; then
if [ "$option" = 'h' ]; then
if [ -z "$WSREP_SST_OPT_DATA" ]; then if [ -z "$WSREP_SST_OPT_DATA" ]; then
MYSQLD_OPT_DATADIR="${value%/}" MYSQLD_OPT_DATADIR="${value%/}"
fi fi
elif [ "$option" != 'u' -a \ elif [ $option != 'u' -a \
"$option" != 'P' ]; then $option != 'P' ]
if [ -z "$original_cmd" ]; then then
original_cmd="'-$option$value'" if [ -z "$value" ]; then
slist="$slist$option"
elif [ -z "$slist" ]; then
slist="$option '$value'"
else
slist="$slist -$option '$value'"
fi
fi
break
else else
original_cmd="$original_cmd '-$option$value'" slist="$slist$option"
fi
options="$value"
done
if [ -n "$slist" ]; then
original_cmd="$original_cmd -$slist"
fi fi
elif [ -z "$options" ]; then
# We found an equal sign without any characters after it:
original_cmd="$original_cmd -"
else
# We found a value that does not start with a minus -
# it is a positional argument or the value of previous
# option. Copy it to output string (as is):
original_cmd="$original_cmd '$1'"
fi fi
shift
continue; continue;
fi fi
# Now we are sure that we are working with an option
# that has a "long" name, so remove all characters after
# the first equal sign:
option="${1%%=*}" option="${1%%=*}"
# The "--loose-" prefix should not affect the recognition
# of the option name:
if [ "${option#--loose-}" != "$option" ]; then
option="--${option#--loose-}"
fi
# Some options just need to be removed from the list:
if [ "$option" != '--defaults-file' -a \ if [ "$option" != '--defaults-file' -a \
"$option" != '--defaults-extra-file' -a \ "$option" != '--defaults-extra-file' -a \
"$option" != '--defaults-group-suffix' -a \ "$option" != '--defaults-group-suffix' -a \
...@@ -340,20 +404,15 @@ case "$1" in ...@@ -340,20 +404,15 @@ case "$1" in
;; ;;
esac esac
if [ $skip_mysqld_arg -eq 0 ]; then if [ $skip_mysqld_arg -eq 0 ]; then
if [ -z "$original_cmd" ]; then
original_cmd="'$1'"
else
original_cmd="$original_cmd '$1'" original_cmd="$original_cmd '$1'"
fi fi
fi fi
fi
shift shift
done done
WSREP_SST_OPT_MYSQLD="$original_cmd" WSREP_SST_OPT_MYSQLD="${original_cmd# *}"
break break
;; ;;
*) # must be command *) # Must be command usage
# usage
# exit 1 # exit 1
;; ;;
esac esac
...@@ -601,9 +660,9 @@ parse_cnf() ...@@ -601,9 +660,9 @@ parse_cnf()
# of the groups list (as if it were a prefix): # of the groups list (as if it were a prefix):
groups="${groups#$group}" groups="${groups#$group}"
groups="${groups#\|}" groups="${groups#\|}"
# if the group name is the same as the "[--]mysqld", then # If the group name is the same as the "mysqld" without "--" prefix,
# try to use it together with the group suffix: # then try to use it together with the group suffix:
if [ "${group#--}" = 'mysqld' -a -n "$WSREP_SST_OPT_SUFFIX_VALUE" ]; then if [ "$group" = 'mysqld' -a -n "$WSREP_SST_OPT_SUFFIX_VALUE" ]; then
reval=$($MY_PRINT_DEFAULTS "mysqld$WSREP_SST_OPT_SUFFIX_VALUE" | awk "$pattern") reval=$($MY_PRINT_DEFAULTS "mysqld$WSREP_SST_OPT_SUFFIX_VALUE" | awk "$pattern")
if [ -n "$reval" ]; then if [ -n "$reval" ]; then
break break
...@@ -616,7 +675,7 @@ parse_cnf() ...@@ -616,7 +675,7 @@ parse_cnf()
fi fi
done done
# use default if we haven't found a value: # Use default if we haven't found a value:
if [ -z "$reval" ]; then if [ -z "$reval" ]; then
[ -n "${3:-}" ] && reval="$3" [ -n "${3:-}" ] && reval="$3"
fi fi
...@@ -648,9 +707,9 @@ in_config() ...@@ -648,9 +707,9 @@ in_config()
# of the groups list (as if it were a prefix): # of the groups list (as if it were a prefix):
groups="${groups#$group}" groups="${groups#$group}"
groups="${groups#\|}" groups="${groups#\|}"
# if the group name is the same as the "[--]mysqld", then # If the group name is the same as the "mysqld" without "--" prefix,
# try to use it together with the group suffix: # then try to use it together with the group suffix:
if [ "${group#--}" = 'mysqld' -a -n "$WSREP_SST_OPT_SUFFIX_VALUE" ]; then if [ "$group" = 'mysqld' -a -n "$WSREP_SST_OPT_SUFFIX_VALUE" ]; then
found=$($MY_PRINT_DEFAULTS "mysqld$WSREP_SST_OPT_SUFFIX_VALUE" | awk "$pattern") found=$($MY_PRINT_DEFAULTS "mysqld$WSREP_SST_OPT_SUFFIX_VALUE" | awk "$pattern")
if [ $found -ne 0 ]; then if [ $found -ne 0 ]; then
break break
......
...@@ -404,7 +404,6 @@ read_cnf() ...@@ -404,7 +404,6 @@ read_cnf()
# avoid CA verification if not set explicitly: # avoid CA verification if not set explicitly:
# nodes may happen to have different CA if self-generated # nodes may happen to have different CA if self-generated
# zeroing up tcert does the trick # zeroing up tcert does the trick
local mode=$(parse_cnf 'sst' 'ssl-mode')
[ "${tmode#VERIFY}" != "$tmode" ] || tcert="" [ "${tmode#VERIFY}" != "$tmode" ] || tcert=""
fi fi
fi fi
...@@ -421,8 +420,9 @@ read_cnf() ...@@ -421,8 +420,9 @@ read_cnf()
sockopt=$(parse_cnf sst sockopt "") sockopt=$(parse_cnf sst sockopt "")
progress=$(parse_cnf sst progress "") progress=$(parse_cnf sst progress "")
ttime=$(parse_cnf sst time 0) ttime=$(parse_cnf sst time 0)
cpat=$(parse_cnf sst cpat '.*galera\.cache$\|.*sst_in_progress$\|.*\.sst$\|.*gvwstate\.dat$\|.*grastate\.dat$\|.*\.err$\|.*\.log$\|.*RPM_UPGRADE_MARKER$\|.*RPM_UPGRADE_HISTORY$') cpat='.*galera\.cache$\|.*sst_in_progress$\|.*\.sst$\|.*gvwstate\.dat$\|.*grastate\.dat$\|.*\.err$\|.*\.log$\|.*RPM_UPGRADE_MARKER$\|.*RPM_UPGRADE_HISTORY$'
[ $OS = 'FreeBSD' ] && cpat=$(parse_cnf sst cpat '.*galera\.cache$|.*sst_in_progress$|.*\.sst$|.*gvwstate\.dat$|.*grastate\.dat$|.*\.err$|.*\.log$|.*RPM_UPGRADE_MARKER$|.*RPM_UPGRADE_HISTORY$') [ "$OS" = 'FreeBSD' ] && cpat=$(echo "$cpat" | sed 's/\\|/|/g')
cpat=$(parse_cnf sst cpat "$cpat")
scomp=$(parse_cnf sst compressor "") scomp=$(parse_cnf sst compressor "")
sdecomp=$(parse_cnf sst decompressor "") sdecomp=$(parse_cnf sst decompressor "")
...@@ -445,9 +445,7 @@ read_cnf() ...@@ -445,9 +445,7 @@ read_cnf()
fi fi
if [ $ssyslog -ne -1 ]; then if [ $ssyslog -ne -1 ]; then
if $MY_PRINT_DEFAULTS mysqld_safe | grep -q -- "--syslog"; then ssyslog=$(in_config 'mysqld_safe' 'syslog')
ssyslog=1
fi
fi fi
} }
...@@ -771,7 +769,7 @@ monitor_process() ...@@ -771,7 +769,7 @@ monitor_process()
while true ; do while true ; do
if ! ps -p "$WSREP_SST_OPT_PARENT" &>/dev/null; then if ! ps -p "$WSREP_SST_OPT_PARENT" &>/dev/null; then
wsrep_log_error "Parent mysqld process (PID:${WSREP_SST_OPT_PARENT}) terminated unexpectedly." wsrep_log_error "Parent mysqld process (PID: $WSREP_SST_OPT_PARENT) terminated unexpectedly."
exit 32 exit 32
fi fi
if ! ps -p "$sst_stream_pid" &>/dev/null; then if ! ps -p "$sst_stream_pid" &>/dev/null; then
...@@ -1139,7 +1137,7 @@ then ...@@ -1139,7 +1137,7 @@ then
if ! ps -p "$WSREP_SST_OPT_PARENT" &>/dev/null if ! ps -p "$WSREP_SST_OPT_PARENT" &>/dev/null
then then
wsrep_log_error "Parent mysqld process (PID:${WSREP_SST_OPT_PARENT}) terminated unexpectedly." wsrep_log_error "Parent mysqld process (PID: $WSREP_SST_OPT_PARENT) terminated unexpectedly."
exit 32 exit 32
fi fi
......
...@@ -218,23 +218,21 @@ SSTKEY=$(parse_cnf 'sst' 'tkey') ...@@ -218,23 +218,21 @@ SSTKEY=$(parse_cnf 'sst' 'tkey')
SSTCERT=$(parse_cnf 'sst' 'tcert') SSTCERT=$(parse_cnf 'sst' 'tcert')
SSTCA=$(parse_cnf 'sst' 'tca') SSTCA=$(parse_cnf 'sst' 'tca')
SST_SECTIONS="--mysqld|sst"
check_server_ssl_config() check_server_ssl_config()
{ {
local section="$1" SSTKEY=$(parse_cnf "$SST_SECTIONS" 'ssl-key')
SSTKEY=$(parse_cnf "$section" 'ssl-key') SSTCERT=$(parse_cnf "$SST_SECTIONS" 'ssl-cert')
SSTCERT=$(parse_cnf "$section" 'ssl-cert') SSTCA=$(parse_cnf "$SST_SECTIONS" 'ssl-ca')
SSTCA=$(parse_cnf "$section" 'ssl-ca')
} }
SSLMODE=$(parse_cnf 'sst' 'ssl-mode' | tr [:lower:] [:upper:]) SSLMODE=$(parse_cnf "$SST_SECTIONS" 'ssl-mode' | tr [:lower:] [:upper:])
# no old-style SSL config in [sst], check for new one:
if [ -z "$SSTKEY" -a -z "$SSTCERT" -a -z "$SSTCA" ] if [ -z "$SSTKEY" -a -z "$SSTCERT" -a -z "$SSTCA" ]
then then
# no old-style SSL config in [sst], check for new one check_server_ssl_config
check_server_ssl_config 'sst'
if [ -z "$SSTKEY" -a -z "$SSTCERT" -a -z "$SSTCA" ]; then
check_server_ssl_config '--mysqld'
fi
fi fi
if [ -z "$SSLMODE" ]; then if [ -z "$SSLMODE" ]; then
...@@ -602,7 +600,7 @@ EOF ...@@ -602,7 +600,7 @@ EOF
if ! ps -p $MYSQLD_PID >/dev/null if ! ps -p $MYSQLD_PID >/dev/null
then then
wsrep_log_error \ wsrep_log_error \
"Parent mysqld process (PID:$MYSQLD_PID) terminated unexpectedly." "Parent mysqld process (PID: $MYSQLD_PID) terminated unexpectedly."
kill -- -$MYSQLD_PID kill -- -$MYSQLD_PID
sleep 1 sleep 1
exit 32 exit 32
......
...@@ -658,6 +658,49 @@ static int sst_append_env_var(wsp::env& env, ...@@ -658,6 +658,49 @@ static int sst_append_env_var(wsp::env& env,
return -env.error(); return -env.error();
} }
#ifdef __WIN__
/*
Space, single quote, ampersand, backquote, I/O redirection
characters, caret, all brackets, plus, exclamation and comma
characters require text to be enclosed in double quotes:
*/
#define IS_SPECIAL(c) \
(isspace(c) || c == '\'' || c == '&' || c == '`' || c == '|' || \
c == '>' || c == '<' || c == ';' || c == '^' || \
c == '[' || c == ']' || c == '{' || c == '}' || \
c == '(' || c == ')' || c == '+' || c == '!' || \
c == ',')
/*
Inside values, equals character are interpreted as special
character and requires quotation:
*/
#define IS_SPECIAL_V(c) (IS_SPECIAL(c) || c == '=')
/*
Double quotation mark and percent characters require escaping:
*/
#define IS_REQ_ESCAPING(c) (c == '""' || c == '%')
#else
/*
Space, single quote, ampersand, backquote, and I/O redirection
characters require text to be enclosed in double quotes. The
semicolon is used to separate shell commands, so it must be
enclosed in double quotes as well:
*/
#define IS_SPECIAL(c) \
(isspace(c) || c == '\'' || c == '&' || c == '`' || c == '|' || \
c == '>' || c == '<' || c == ';')
/*
Inside values, characters are interpreted as in parameter names:
*/
#define IS_SPECIAL_V(c) IS_SPECIAL(c)
/*
Double quotation mark and backslash characters require
backslash prefixing, the dollar symbol is used to substitute
a variable value, therefore it also requires escaping:
*/
#define IS_REQ_ESCAPING(c) (c == '"' || c == '\\' || c == '$')
#endif
static size_t estimate_cmd_len (bool* extra_args) static size_t estimate_cmd_len (bool* extra_args)
{ {
/* /*
...@@ -682,22 +725,16 @@ static size_t estimate_cmd_len (bool* extra_args) ...@@ -682,22 +725,16 @@ static size_t estimate_cmd_len (bool* extra_args)
char c; char c;
while ((c = *arg++) != 0) while ((c = *arg++) != 0)
{ {
/* if (IS_SPECIAL(c))
Space, single quote, ampersand, and I/O redirection characters {
require text to be enclosed in double quotes: quotation= true;
*/ }
if (isspace(c) || c == '\'' || c == '&' || c == '|' || else if (IS_REQ_ESCAPING(c))
#ifdef __WIN__
c == '>' || c == '<')
#else
/*
The semicolon is used to separate shell commands, so it must be
enclosed in double quotes as well:
*/
c == '>' || c == '<' || c == ';')
#endif
{ {
cmd_len++;
#ifdef __WIN__
quotation= true; quotation= true;
#endif
} }
/* /*
If the equals symbol is encountered, then we need to separately If the equals symbol is encountered, then we need to separately
...@@ -717,58 +754,20 @@ static size_t estimate_cmd_len (bool* extra_args) ...@@ -717,58 +754,20 @@ static size_t estimate_cmd_len (bool* extra_args)
} }
while ((c = *arg++) != 0) while ((c = *arg++) != 0)
{ {
/* if (IS_SPECIAL_V(c))
Space, single quote, ampersand, and I/O redirection characters
require text to be enclosed in double quotes:
*/
if (isspace(c) || c == '\'' || c == '&' || c == '|' ||
#ifdef __WIN__
c == '>' || c == '<')
#else
/*
The semicolon is used to separate shell commands, so it must be
enclosed in double quotes as well:
*/
c == '>' || c == '<' || c == ';')
#endif
{ {
quotation= true; quotation= true;
} }
/* else if (IS_REQ_ESCAPING(c))
Double quotation mark or backslash symbol requires backslash
prefixing:
*/
#ifdef __WIN__
else if (c == '"' || c == '\\')
#else
/*
The dollar symbol is used to substitute a variable, therefore
it also requires escaping:
*/
else if (c == '"' || c == '\\' || c == '$')
#endif
{ {
cmd_len++; cmd_len++;
#ifdef __WIN__
quotation= true;
#endif
} }
} }
break; break;
} }
/*
Double quotation mark or backslash symbol requires backslash
prefixing:
*/
#ifdef __WIN__
else if (c == '"' || c == '\\')
#else
/*
The dollar symbol is used to substitute a variable, therefore
it also requires escaping:
*/
else if (c == '"' || c == '\\' || c == '$')
#endif
{
cmd_len++;
}
} }
/* Perhaps we need to quote the entire argument or its right part: */ /* Perhaps we need to quote the entire argument or its right part: */
if (quotation) if (quotation)
...@@ -811,23 +810,17 @@ static void copy_orig_argv (char* cmd_str) ...@@ -811,23 +810,17 @@ static void copy_orig_argv (char* cmd_str)
char c; char c;
while ((c = *arg_scan++) != 0) while ((c = *arg_scan++) != 0)
{ {
/* if (IS_SPECIAL(c))
Space, single quote, ampersand, and I/O redirection characters
require text to be enclosed in double quotes:
*/
if (isspace(c) || c == '\'' || c == '&' || c == '|' ||
#ifdef __WIN__
c == '>' || c == '<')
#else
/*
The semicolon is used to separate shell commands, so it must be
enclosed in double quotes as well:
*/
c == '>' || c == '<' || c == ';')
#endif
{ {
quotation= true; quotation= true;
} }
else if (IS_REQ_ESCAPING(c))
{
plain= false;
#ifdef __WIN__
quotation= true;
#endif
}
/* /*
If the equals symbol is encountered, then we need to separately If the equals symbol is encountered, then we need to separately
process the right side: process the right side:
...@@ -862,13 +855,13 @@ static void copy_orig_argv (char* cmd_str) ...@@ -862,13 +855,13 @@ static void copy_orig_argv (char* cmd_str)
while (m) while (m)
{ {
c = *arg++; c = *arg++;
if (IS_REQ_ESCAPING(c))
{
#ifdef __WIN__ #ifdef __WIN__
if (c == '"' || c == '\\') *cmd_str++ = c;
#else #else
if (c == '"' || c == '\\' || c == '$')
#endif
{
*cmd_str++ = '\\'; *cmd_str++ = '\\';
#endif
} }
*cmd_str++ = c; *cmd_str++ = c;
m--; m--;
...@@ -897,58 +890,20 @@ static void copy_orig_argv (char* cmd_str) ...@@ -897,58 +890,20 @@ static void copy_orig_argv (char* cmd_str)
/* Let's deal with the left side of the expression: */ /* Let's deal with the left side of the expression: */
while ((c = *arg_scan++) != 0) while ((c = *arg_scan++) != 0)
{ {
/* if (IS_SPECIAL_V(c))
Space, single quote, ampersand, and I/O redirection characters
require text to be enclosed in double quotes:
*/
if (isspace(c) || c == '\'' || c == '&' || c == '|' ||
#ifdef __WIN__
c == '>' || c == '<')
#else
/*
The semicolon is used to separate shell commands, so it must be
enclosed in double quotes as well:
*/
c == '>' || c == '<' || c == ';')
#endif
{ {
quotation= true; quotation= true;
} }
/* else if (IS_REQ_ESCAPING(c))
Double quotation mark or backslash symbol requires backslash
prefixing:
*/
#ifdef __WIN__
else if (c == '"' || c == '\\')
#else
/*
The dollar symbol is used to substitute a variable, therefore
it also requires escaping:
*/
else if (c == '"' || c == '\\' || c == '$')
#endif
{ {
plain= false; plain= false;
#ifdef __WIN__
quotation= true;
#endif
} }
} }
break; break;
} }
/*
Double quotation mark or backslash symbol requires backslash
prefixing:
*/
#ifdef __WIN__
else if (c == '"' || c == '\\')
#else
/*
The dollar symbol is used to substitute a variable, therefore
it also requires escaping:
*/
else if (c == '"' || c == '\\' || c == '$')
#endif
{
plain= false;
}
} }
if (n) if (n)
{ {
...@@ -971,13 +926,13 @@ static void copy_orig_argv (char* cmd_str) ...@@ -971,13 +926,13 @@ static void copy_orig_argv (char* cmd_str)
{ {
while ((c = *arg++) != 0) while ((c = *arg++) != 0)
{ {
if (IS_REQ_ESCAPING(c))
{
#ifdef __WIN__ #ifdef __WIN__
if (c == '"' || c == '\\') *cmd_str++ = c;
#else #else
if (c == '"' || c == '\\' || c == '$')
#endif
{
*cmd_str++ = '\\'; *cmd_str++ = '\\';
#endif
} }
*cmd_str++ = c; *cmd_str++ = c;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment