Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update mariadb #6965

Merged
merged 1 commit into from
Nov 13, 2019
Merged

Update mariadb #6965

merged 1 commit into from
Nov 13, 2019

Conversation

yosifkit
Copy link
Member

Changes:

Changes:

- MariaDB/mariadb-docker@3b2e52a: Merge pull request MariaDB/mariadb-docker#271 from infosiftr/rsync-with-mysql
@yosifkit
Copy link
Member Author

Diff:
diff --git a/mariadb_10.1/docker-entrypoint.sh b/mariadb_10.1/docker-entrypoint.sh
index 7ee5efd..77c5d4c 100755
--- a/mariadb_10.1/docker-entrypoint.sh
+++ b/mariadb_10.1/docker-entrypoint.sh
@@ -2,21 +2,21 @@
 set -eo pipefail
 shopt -s nullglob
 
-# if command starts with an option, prepend mysqld
-if [ "${1:0:1}" = '-' ]; then
-	set -- mysqld "$@"
-fi
-
-# skip setup if they want an option that stops mysqld
-wantHelp=
-for arg; do
-	case "$arg" in
-		-'?'|--help|--print-defaults|-V|--version)
-			wantHelp=1
-			break
-			;;
-	esac
-done
+# logging functions
+mysql_log() {
+	local type="$1"; shift
+	printf '%s [%s] [Entrypoint]: %s\n' "$(date --rfc-3339=seconds)" "$type" "$*"
+}
+mysql_note() {
+	mysql_log Note "$@"
+}
+mysql_warn() {
+	mysql_log Warn "$@" >&2
+}
+mysql_error() {
+	mysql_log ERROR "$@" >&2
+	exit 1
+}
 
 # usage: file_env VAR [DEFAULT]
 #    ie: file_env 'XYZ_DB_PASSWORD' 'example'
@@ -27,8 +27,7 @@ file_env() {
 	local fileVar="${var}_FILE"
 	local def="${2:-}"
 	if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
-		echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
-		exit 1
+		mysql_error "Both $var and $fileVar are set (but are exclusive)"
 	fi
 	local val="$def"
 	if [ "${!var:-}" ]; then
@@ -40,56 +39,106 @@ file_env() {
 	unset "$fileVar"
 }
 
-_check_config() {
-	toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" )
-	if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
-		cat >&2 <<-EOM
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
 
-			ERROR: mysqld failed while attempting to check config
-			command was: "${toRun[*]}"
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions
+docker_process_init_files() {
+	# mysql here for backwards compatibility "${mysql[@]}"
+	mysql=( docker_process_sql )
 
-			$errors
-		EOM
-		exit 1
+	echo
+	local f
+	for f; do
+		case "$f" in
+			*.sh)     mysql_note "$0: running $f"; . "$f" ;;
+			*.sql)    mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;;
+			*.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
+			*)        mysql_warn "$0: ignoring $f" ;;
+		esac
+		echo
+	done
+}
+
+mysql_check_config() {
+	local toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" ) errors
+	if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
+		mysql_error $'mysqld failed while attempting to check config\n\tcommand was: '"${toRun[*]}"$'\n\t'"$errors"
 	fi
 }
 
 # Fetch value from server config
 # We use mysqld --verbose --help instead of my_print_defaults because the
 # latter only show values present in config files, and not server defaults
-_get_config() {
+mysql_get_config() {
 	local conf="$1"; shift
 	"$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \
-		| awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
+		| awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
 	# match "datadir      /some/path with/spaces in/it here" but not "--xyz=abc\n     datadir (xyz)"
 }
 
-# allow the container to be started with `--user`
-if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then
-	_check_config "$@"
-	DATADIR="$(_get_config 'datadir' "$@")"
-	mkdir -p "$DATADIR"
-	find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
-	exec gosu mysql "$BASH_SOURCE" "$@"
-fi
+# Do a temporary startup of the MySQL server, for init purposes
+docker_temp_server_start() {
+	"$@" --skip-networking --socket="${SOCKET}" &
+	mysql_note "Waiting for server startup"
+	local i
+	for i in {30..0}; do
+		# only use the root password if the database has already been initializaed
+		# so that it won't try to fill in a password file when it hasn't been set yet
+		extraArgs=()
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			extraArgs+=( '--dont-use-mysql-root-password' )
+		fi
+		if docker_process_sql "${extraArgs[@]}" --database=mysql <<<'SELECT 1' &> /dev/null; then
+			break
+		fi
+		sleep 1
+	done
+	if [ "$i" = 0 ]; then
+		mysql_error "Unable to start server."
+	fi
+}
 
-if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
-	# still need to check config, container may have started with --user
-	_check_config "$@"
-	# Get config
-	DATADIR="$(_get_config 'datadir' "$@")"
+# Stop the server. When using a local socket file mysqladmin will block until
+# the shutdown is complete.
+docker_temp_server_stop() {
+	if ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then
+		mysql_error "Unable to shut down server."
+	fi
+}
 
-	if [ ! -d "$DATADIR/mysql" ]; then
-		file_env 'MYSQL_ROOT_PASSWORD'
+# Verify that the minimally required password settings are set for new databases.
+docker_verify_minimum_env() {
 	if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
-			echo >&2 'error: database is uninitialized and password option is not specified '
-			echo >&2 '  You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
-			exit 1
+		mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
 	fi
+}
+
+# creates folders for the database
+# also ensures permission for user mysql of run as root
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
+	# TODO other directories that are used by default? like /var/lib/mysql-files
+	# see https://github.com/docker-library/mysql/issues/562
 	mkdir -p "$DATADIR"
 
-		echo 'Initializing database'
+	if [ "$user" = "0" ]; then
+		# this will cause less disk access than `chown -R`
+		find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
+	fi
+}
+
+# initializes the database directory
+docker_init_database_dir() {
+	mysql_note "Initializing database files"
 	installArgs=( --datadir="$DATADIR" --rpm )
 	if { mysql_install_db --help || :; } | grep -q -- '--auth-root-authentication-method'; then
 		# beginning in 10.4.3, install_db uses "socket" which only allows system user root to connect, switch back to "normal" to allow mysql root without a password
@@ -99,40 +148,67 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
 	fi
 	# "Other options are passed to mysqld." (so we pass all "mysqld" arguments directly here)
 	mysql_install_db "${installArgs[@]}" "${@:2}"
-		echo 'Database initialized'
+	mysql_note "Database files initialized"
+}
 
-		SOCKET="$(_get_config 'socket' "$@")"
-		"$@" --skip-networking --socket="${SOCKET}" &
-		pid="$!"
+# Loads various settings that are used elsewhere in the script
+# This should be called after mysql_check_config, but before any other functions
+docker_setup_env() {
+	# Get config
+	declare -g DATADIR SOCKET
+	DATADIR="$(mysql_get_config 'datadir' "$@")"
+	SOCKET="$(mysql_get_config 'socket' "$@")"
 
-		mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )
+	# Initialize values that might be stored in a file
+	file_env 'MYSQL_ROOT_HOST' '%'
+	file_env 'MYSQL_DATABASE'
+	file_env 'MYSQL_USER'
+	file_env 'MYSQL_PASSWORD'
+	file_env 'MYSQL_ROOT_PASSWORD'
 
-		for i in {30..0}; do
-			if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then
-				break
+	declare -g DATABASE_ALREADY_EXISTS
+	if [ -d "$DATADIR/mysql" ]; then
+		DATABASE_ALREADY_EXISTS='true'
 	fi
-			echo 'MySQL init process in progress...'
-			sleep 1
-		done
-		if [ "$i" = 0 ]; then
-			echo >&2 'MySQL init process failed.'
-			exit 1
+}
+
+# Execute sql script, passed via stdin
+# usage: docker_process_sql [--dont-use-mysql-root-password] [mysql-cli-args]
+#    ie: docker_process_sql --database=mydb <<<'INSERT ...'
+#    ie: docker_process_sql --dont-use-mysql-root-password --database=mydb <my-file.sql
+docker_process_sql() {
+	passfileArgs=()
+	if [ '--dont-use-mysql-root-password' = "$1" ]; then
+		passfileArgs+=( "$1" )
+		shift
 	fi
+	# args sent in can override this db, since they will be later in the command
+	if [ -n "$MYSQL_DATABASE" ]; then
+		set -- --database="$MYSQL_DATABASE" "$@"
+	fi
+
+	mysql --defaults-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@"
+}
 
+# Initializes database with timezone info and root password, plus optional extra db/user
+docker_setup_db() {
+	# Load timezone info into database
 	if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then
 		# sed is for https://bugs.mysql.com/bug.php?id=20545
-			mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql
+		mysql_tzinfo_to_sql /usr/share/zoneinfo \
+			| sed 's/Local time zone must be set--see zic manual page/FCTY/' \
+			| docker_process_sql --dont-use-mysql-root-password --database=mysql
+			# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is not set yet
 	fi
-
-		if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
+	# Generate random root password
+	if [ -n "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
 		export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
-			echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
+		mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
 	fi
-
-		rootCreate=
+	# Sets root password and creates root users for non-localhost hosts
+	local rootCreate=
 	# default root to listen for connections from anywhere
-		file_env 'MYSQL_ROOT_HOST' '%'
-		if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then
+	if [ -n "$MYSQL_ROOT_HOST" ] && [ "$MYSQL_ROOT_HOST" != 'localhost' ]; then
 		# no, we don't care if read finds a terminating character in this heredoc
 		# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
 		read -r -d '' rootCreate <<-EOSQL || true
@@ -141,59 +217,115 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
 		EOSQL
 	fi
 
-		"${mysql[@]}" <<-EOSQL
+	# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is just now being set
+	docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL
 		-- What's done in this file shouldn't be replicated
 		--  or products like mysql-fabric won't work
 		SET @@SESSION.SQL_LOG_BIN=0;
 
 		DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
 		SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ;
+		-- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365
+		-- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369
+		DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
+
 		GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
+		FLUSH PRIVILEGES ;
 		${rootCreate}
 		DROP DATABASE IF EXISTS test ;
-			FLUSH PRIVILEGES ;
 	EOSQL
 
-		if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
-			mysql+=( -p"${MYSQL_ROOT_PASSWORD}" )
+	# Creates a custom database and user if specified
+	if [ -n "$MYSQL_DATABASE" ]; then
+		mysql_note "Creating database ${MYSQL_DATABASE}"
+		docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;"
 	fi
 
-		file_env 'MYSQL_DATABASE'
-		if [ "$MYSQL_DATABASE" ]; then
-			echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
-			mysql+=( "$MYSQL_DATABASE" )
-		fi
+	if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then
+		mysql_note "Creating user ${MYSQL_USER}"
+		docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;"
 
-		file_env 'MYSQL_USER'
-		file_env 'MYSQL_PASSWORD'
-		if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
-			echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}"
+		if [ -n "$MYSQL_DATABASE" ]; then
+			mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}"
+			docker_process_sql --database=mysql <<<"GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;"
+		fi
 
-			if [ "$MYSQL_DATABASE" ]; then
-				echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
+		docker_process_sql --database=mysql <<<"FLUSH PRIVILEGES ;"
 	fi
+}
+
+_mysql_passfile() {
+	# echo the password to the "file" the client uses
+	# the client command will use process substitution to create a file on the fly
+	# ie: --defaults-file=<( _mysql_passfile )
+	if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MYSQL_ROOT_PASSWORD" ]; then
+		cat <<-EOF
+			[client]
+			password="${MYSQL_ROOT_PASSWORD}"
+		EOF
 	fi
+}
 
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
-			case "$f" in
-				*.sh)     echo "$0: running $f"; . "$f" ;;
-				*.sql)    echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
-				*)        echo "$0: ignoring $f" ;;
+# check arguments for an option that would cause mysqld to stop
+# return true if there is one
+_mysql_want_help() {
+	local arg
+	for arg; do
+		case "$arg" in
+			-'?'|--help|--print-defaults|-V|--version)
+				return 0
+				;;
 		esac
-			echo
 	done
+	return 1
+}
 
-		if ! kill -s TERM "$pid" || ! wait "$pid"; then
-			echo >&2 'MySQL init process failed.'
-			exit 1
+_main() {
+	# if command starts with an option, prepend mysqld
+	if [ "${1:0:1}" = '-' ]; then
+		set -- mysqld "$@"
 	fi
 
+	# skip setup if they aren't running mysqld or want an option that stops mysqld
+	if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then
+		mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started."
+
+		mysql_check_config "$@"
+		# Load various environment variables
+		docker_setup_env "$@"
+		docker_create_db_directories
+
+		# If container is started as root user, restart as dedicated mysql user
+		if [ "$(id -u)" = "0" ]; then
+			mysql_note "Switching to dedicated user 'mysql'"
+			exec gosu mysql "$BASH_SOURCE" "$@"
+		fi
+
+		# there's no database, so it needs to be initialized
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir "$@"
+
+			mysql_note "Starting temporary server"
+			docker_temp_server_start "$@"
+			mysql_note "Temporary server started."
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			mysql_note "Stopping temporary server"
+			docker_temp_server_stop
+			mysql_note "Temporary server stopped"
+
 			echo
-		echo 'MySQL init process done. Ready for start up.'
+			mysql_note "MySQL init process done. Ready for start up."
 			echo
 		fi
-fi
+	fi
+	exec "$@"
+}
 
-exec "$@"
+# If we are sourced from elsewhere, don't perform any further actions
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/mariadb_10.2/docker-entrypoint.sh b/mariadb_10.2/docker-entrypoint.sh
index 7ee5efd..77c5d4c 100755
--- a/mariadb_10.2/docker-entrypoint.sh
+++ b/mariadb_10.2/docker-entrypoint.sh
@@ -2,21 +2,21 @@
 set -eo pipefail
 shopt -s nullglob
 
-# if command starts with an option, prepend mysqld
-if [ "${1:0:1}" = '-' ]; then
-	set -- mysqld "$@"
-fi
-
-# skip setup if they want an option that stops mysqld
-wantHelp=
-for arg; do
-	case "$arg" in
-		-'?'|--help|--print-defaults|-V|--version)
-			wantHelp=1
-			break
-			;;
-	esac
-done
+# logging functions
+mysql_log() {
+	local type="$1"; shift
+	printf '%s [%s] [Entrypoint]: %s\n' "$(date --rfc-3339=seconds)" "$type" "$*"
+}
+mysql_note() {
+	mysql_log Note "$@"
+}
+mysql_warn() {
+	mysql_log Warn "$@" >&2
+}
+mysql_error() {
+	mysql_log ERROR "$@" >&2
+	exit 1
+}
 
 # usage: file_env VAR [DEFAULT]
 #    ie: file_env 'XYZ_DB_PASSWORD' 'example'
@@ -27,8 +27,7 @@ file_env() {
 	local fileVar="${var}_FILE"
 	local def="${2:-}"
 	if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
-		echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
-		exit 1
+		mysql_error "Both $var and $fileVar are set (but are exclusive)"
 	fi
 	local val="$def"
 	if [ "${!var:-}" ]; then
@@ -40,56 +39,106 @@ file_env() {
 	unset "$fileVar"
 }
 
-_check_config() {
-	toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" )
-	if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
-		cat >&2 <<-EOM
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
 
-			ERROR: mysqld failed while attempting to check config
-			command was: "${toRun[*]}"
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions
+docker_process_init_files() {
+	# mysql here for backwards compatibility "${mysql[@]}"
+	mysql=( docker_process_sql )
 
-			$errors
-		EOM
-		exit 1
+	echo
+	local f
+	for f; do
+		case "$f" in
+			*.sh)     mysql_note "$0: running $f"; . "$f" ;;
+			*.sql)    mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;;
+			*.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
+			*)        mysql_warn "$0: ignoring $f" ;;
+		esac
+		echo
+	done
+}
+
+mysql_check_config() {
+	local toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" ) errors
+	if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
+		mysql_error $'mysqld failed while attempting to check config\n\tcommand was: '"${toRun[*]}"$'\n\t'"$errors"
 	fi
 }
 
 # Fetch value from server config
 # We use mysqld --verbose --help instead of my_print_defaults because the
 # latter only show values present in config files, and not server defaults
-_get_config() {
+mysql_get_config() {
 	local conf="$1"; shift
 	"$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \
-		| awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
+		| awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
 	# match "datadir      /some/path with/spaces in/it here" but not "--xyz=abc\n     datadir (xyz)"
 }
 
-# allow the container to be started with `--user`
-if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then
-	_check_config "$@"
-	DATADIR="$(_get_config 'datadir' "$@")"
-	mkdir -p "$DATADIR"
-	find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
-	exec gosu mysql "$BASH_SOURCE" "$@"
-fi
+# Do a temporary startup of the MySQL server, for init purposes
+docker_temp_server_start() {
+	"$@" --skip-networking --socket="${SOCKET}" &
+	mysql_note "Waiting for server startup"
+	local i
+	for i in {30..0}; do
+		# only use the root password if the database has already been initializaed
+		# so that it won't try to fill in a password file when it hasn't been set yet
+		extraArgs=()
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			extraArgs+=( '--dont-use-mysql-root-password' )
+		fi
+		if docker_process_sql "${extraArgs[@]}" --database=mysql <<<'SELECT 1' &> /dev/null; then
+			break
+		fi
+		sleep 1
+	done
+	if [ "$i" = 0 ]; then
+		mysql_error "Unable to start server."
+	fi
+}
 
-if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
-	# still need to check config, container may have started with --user
-	_check_config "$@"
-	# Get config
-	DATADIR="$(_get_config 'datadir' "$@")"
+# Stop the server. When using a local socket file mysqladmin will block until
+# the shutdown is complete.
+docker_temp_server_stop() {
+	if ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then
+		mysql_error "Unable to shut down server."
+	fi
+}
 
-	if [ ! -d "$DATADIR/mysql" ]; then
-		file_env 'MYSQL_ROOT_PASSWORD'
+# Verify that the minimally required password settings are set for new databases.
+docker_verify_minimum_env() {
 	if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
-			echo >&2 'error: database is uninitialized and password option is not specified '
-			echo >&2 '  You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
-			exit 1
+		mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
 	fi
+}
+
+# creates folders for the database
+# also ensures permission for user mysql of run as root
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
+	# TODO other directories that are used by default? like /var/lib/mysql-files
+	# see https://github.com/docker-library/mysql/issues/562
 	mkdir -p "$DATADIR"
 
-		echo 'Initializing database'
+	if [ "$user" = "0" ]; then
+		# this will cause less disk access than `chown -R`
+		find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
+	fi
+}
+
+# initializes the database directory
+docker_init_database_dir() {
+	mysql_note "Initializing database files"
 	installArgs=( --datadir="$DATADIR" --rpm )
 	if { mysql_install_db --help || :; } | grep -q -- '--auth-root-authentication-method'; then
 		# beginning in 10.4.3, install_db uses "socket" which only allows system user root to connect, switch back to "normal" to allow mysql root without a password
@@ -99,40 +148,67 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
 	fi
 	# "Other options are passed to mysqld." (so we pass all "mysqld" arguments directly here)
 	mysql_install_db "${installArgs[@]}" "${@:2}"
-		echo 'Database initialized'
+	mysql_note "Database files initialized"
+}
 
-		SOCKET="$(_get_config 'socket' "$@")"
-		"$@" --skip-networking --socket="${SOCKET}" &
-		pid="$!"
+# Loads various settings that are used elsewhere in the script
+# This should be called after mysql_check_config, but before any other functions
+docker_setup_env() {
+	# Get config
+	declare -g DATADIR SOCKET
+	DATADIR="$(mysql_get_config 'datadir' "$@")"
+	SOCKET="$(mysql_get_config 'socket' "$@")"
 
-		mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )
+	# Initialize values that might be stored in a file
+	file_env 'MYSQL_ROOT_HOST' '%'
+	file_env 'MYSQL_DATABASE'
+	file_env 'MYSQL_USER'
+	file_env 'MYSQL_PASSWORD'
+	file_env 'MYSQL_ROOT_PASSWORD'
 
-		for i in {30..0}; do
-			if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then
-				break
+	declare -g DATABASE_ALREADY_EXISTS
+	if [ -d "$DATADIR/mysql" ]; then
+		DATABASE_ALREADY_EXISTS='true'
 	fi
-			echo 'MySQL init process in progress...'
-			sleep 1
-		done
-		if [ "$i" = 0 ]; then
-			echo >&2 'MySQL init process failed.'
-			exit 1
+}
+
+# Execute sql script, passed via stdin
+# usage: docker_process_sql [--dont-use-mysql-root-password] [mysql-cli-args]
+#    ie: docker_process_sql --database=mydb <<<'INSERT ...'
+#    ie: docker_process_sql --dont-use-mysql-root-password --database=mydb <my-file.sql
+docker_process_sql() {
+	passfileArgs=()
+	if [ '--dont-use-mysql-root-password' = "$1" ]; then
+		passfileArgs+=( "$1" )
+		shift
 	fi
+	# args sent in can override this db, since they will be later in the command
+	if [ -n "$MYSQL_DATABASE" ]; then
+		set -- --database="$MYSQL_DATABASE" "$@"
+	fi
+
+	mysql --defaults-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@"
+}
 
+# Initializes database with timezone info and root password, plus optional extra db/user
+docker_setup_db() {
+	# Load timezone info into database
 	if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then
 		# sed is for https://bugs.mysql.com/bug.php?id=20545
-			mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql
+		mysql_tzinfo_to_sql /usr/share/zoneinfo \
+			| sed 's/Local time zone must be set--see zic manual page/FCTY/' \
+			| docker_process_sql --dont-use-mysql-root-password --database=mysql
+			# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is not set yet
 	fi
-
-		if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
+	# Generate random root password
+	if [ -n "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
 		export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
-			echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
+		mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
 	fi
-
-		rootCreate=
+	# Sets root password and creates root users for non-localhost hosts
+	local rootCreate=
 	# default root to listen for connections from anywhere
-		file_env 'MYSQL_ROOT_HOST' '%'
-		if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then
+	if [ -n "$MYSQL_ROOT_HOST" ] && [ "$MYSQL_ROOT_HOST" != 'localhost' ]; then
 		# no, we don't care if read finds a terminating character in this heredoc
 		# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
 		read -r -d '' rootCreate <<-EOSQL || true
@@ -141,59 +217,115 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
 		EOSQL
 	fi
 
-		"${mysql[@]}" <<-EOSQL
+	# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is just now being set
+	docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL
 		-- What's done in this file shouldn't be replicated
 		--  or products like mysql-fabric won't work
 		SET @@SESSION.SQL_LOG_BIN=0;
 
 		DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
 		SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ;
+		-- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365
+		-- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369
+		DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
+
 		GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
+		FLUSH PRIVILEGES ;
 		${rootCreate}
 		DROP DATABASE IF EXISTS test ;
-			FLUSH PRIVILEGES ;
 	EOSQL
 
-		if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
-			mysql+=( -p"${MYSQL_ROOT_PASSWORD}" )
+	# Creates a custom database and user if specified
+	if [ -n "$MYSQL_DATABASE" ]; then
+		mysql_note "Creating database ${MYSQL_DATABASE}"
+		docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;"
 	fi
 
-		file_env 'MYSQL_DATABASE'
-		if [ "$MYSQL_DATABASE" ]; then
-			echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
-			mysql+=( "$MYSQL_DATABASE" )
-		fi
+	if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then
+		mysql_note "Creating user ${MYSQL_USER}"
+		docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;"
 
-		file_env 'MYSQL_USER'
-		file_env 'MYSQL_PASSWORD'
-		if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
-			echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}"
+		if [ -n "$MYSQL_DATABASE" ]; then
+			mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}"
+			docker_process_sql --database=mysql <<<"GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;"
+		fi
 
-			if [ "$MYSQL_DATABASE" ]; then
-				echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
+		docker_process_sql --database=mysql <<<"FLUSH PRIVILEGES ;"
 	fi
+}
+
+_mysql_passfile() {
+	# echo the password to the "file" the client uses
+	# the client command will use process substitution to create a file on the fly
+	# ie: --defaults-file=<( _mysql_passfile )
+	if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MYSQL_ROOT_PASSWORD" ]; then
+		cat <<-EOF
+			[client]
+			password="${MYSQL_ROOT_PASSWORD}"
+		EOF
 	fi
+}
 
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
-			case "$f" in
-				*.sh)     echo "$0: running $f"; . "$f" ;;
-				*.sql)    echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
-				*)        echo "$0: ignoring $f" ;;
+# check arguments for an option that would cause mysqld to stop
+# return true if there is one
+_mysql_want_help() {
+	local arg
+	for arg; do
+		case "$arg" in
+			-'?'|--help|--print-defaults|-V|--version)
+				return 0
+				;;
 		esac
-			echo
 	done
+	return 1
+}
 
-		if ! kill -s TERM "$pid" || ! wait "$pid"; then
-			echo >&2 'MySQL init process failed.'
-			exit 1
+_main() {
+	# if command starts with an option, prepend mysqld
+	if [ "${1:0:1}" = '-' ]; then
+		set -- mysqld "$@"
 	fi
 
+	# skip setup if they aren't running mysqld or want an option that stops mysqld
+	if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then
+		mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started."
+
+		mysql_check_config "$@"
+		# Load various environment variables
+		docker_setup_env "$@"
+		docker_create_db_directories
+
+		# If container is started as root user, restart as dedicated mysql user
+		if [ "$(id -u)" = "0" ]; then
+			mysql_note "Switching to dedicated user 'mysql'"
+			exec gosu mysql "$BASH_SOURCE" "$@"
+		fi
+
+		# there's no database, so it needs to be initialized
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir "$@"
+
+			mysql_note "Starting temporary server"
+			docker_temp_server_start "$@"
+			mysql_note "Temporary server started."
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			mysql_note "Stopping temporary server"
+			docker_temp_server_stop
+			mysql_note "Temporary server stopped"
+
 			echo
-		echo 'MySQL init process done. Ready for start up.'
+			mysql_note "MySQL init process done. Ready for start up."
 			echo
 		fi
-fi
+	fi
+	exec "$@"
+}
 
-exec "$@"
+# If we are sourced from elsewhere, don't perform any further actions
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/mariadb_10.3/docker-entrypoint.sh b/mariadb_10.3/docker-entrypoint.sh
index 7ee5efd..77c5d4c 100755
--- a/mariadb_10.3/docker-entrypoint.sh
+++ b/mariadb_10.3/docker-entrypoint.sh
@@ -2,21 +2,21 @@
 set -eo pipefail
 shopt -s nullglob
 
-# if command starts with an option, prepend mysqld
-if [ "${1:0:1}" = '-' ]; then
-	set -- mysqld "$@"
-fi
-
-# skip setup if they want an option that stops mysqld
-wantHelp=
-for arg; do
-	case "$arg" in
-		-'?'|--help|--print-defaults|-V|--version)
-			wantHelp=1
-			break
-			;;
-	esac
-done
+# logging functions
+mysql_log() {
+	local type="$1"; shift
+	printf '%s [%s] [Entrypoint]: %s\n' "$(date --rfc-3339=seconds)" "$type" "$*"
+}
+mysql_note() {
+	mysql_log Note "$@"
+}
+mysql_warn() {
+	mysql_log Warn "$@" >&2
+}
+mysql_error() {
+	mysql_log ERROR "$@" >&2
+	exit 1
+}
 
 # usage: file_env VAR [DEFAULT]
 #    ie: file_env 'XYZ_DB_PASSWORD' 'example'
@@ -27,8 +27,7 @@ file_env() {
 	local fileVar="${var}_FILE"
 	local def="${2:-}"
 	if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
-		echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
-		exit 1
+		mysql_error "Both $var and $fileVar are set (but are exclusive)"
 	fi
 	local val="$def"
 	if [ "${!var:-}" ]; then
@@ -40,56 +39,106 @@ file_env() {
 	unset "$fileVar"
 }
 
-_check_config() {
-	toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" )
-	if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
-		cat >&2 <<-EOM
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
 
-			ERROR: mysqld failed while attempting to check config
-			command was: "${toRun[*]}"
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions
+docker_process_init_files() {
+	# mysql here for backwards compatibility "${mysql[@]}"
+	mysql=( docker_process_sql )
 
-			$errors
-		EOM
-		exit 1
+	echo
+	local f
+	for f; do
+		case "$f" in
+			*.sh)     mysql_note "$0: running $f"; . "$f" ;;
+			*.sql)    mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;;
+			*.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
+			*)        mysql_warn "$0: ignoring $f" ;;
+		esac
+		echo
+	done
+}
+
+mysql_check_config() {
+	local toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" ) errors
+	if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
+		mysql_error $'mysqld failed while attempting to check config\n\tcommand was: '"${toRun[*]}"$'\n\t'"$errors"
 	fi
 }
 
 # Fetch value from server config
 # We use mysqld --verbose --help instead of my_print_defaults because the
 # latter only show values present in config files, and not server defaults
-_get_config() {
+mysql_get_config() {
 	local conf="$1"; shift
 	"$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \
-		| awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
+		| awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
 	# match "datadir      /some/path with/spaces in/it here" but not "--xyz=abc\n     datadir (xyz)"
 }
 
-# allow the container to be started with `--user`
-if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then
-	_check_config "$@"
-	DATADIR="$(_get_config 'datadir' "$@")"
-	mkdir -p "$DATADIR"
-	find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
-	exec gosu mysql "$BASH_SOURCE" "$@"
-fi
+# Do a temporary startup of the MySQL server, for init purposes
+docker_temp_server_start() {
+	"$@" --skip-networking --socket="${SOCKET}" &
+	mysql_note "Waiting for server startup"
+	local i
+	for i in {30..0}; do
+		# only use the root password if the database has already been initializaed
+		# so that it won't try to fill in a password file when it hasn't been set yet
+		extraArgs=()
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			extraArgs+=( '--dont-use-mysql-root-password' )
+		fi
+		if docker_process_sql "${extraArgs[@]}" --database=mysql <<<'SELECT 1' &> /dev/null; then
+			break
+		fi
+		sleep 1
+	done
+	if [ "$i" = 0 ]; then
+		mysql_error "Unable to start server."
+	fi
+}
 
-if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
-	# still need to check config, container may have started with --user
-	_check_config "$@"
-	# Get config
-	DATADIR="$(_get_config 'datadir' "$@")"
+# Stop the server. When using a local socket file mysqladmin will block until
+# the shutdown is complete.
+docker_temp_server_stop() {
+	if ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then
+		mysql_error "Unable to shut down server."
+	fi
+}
 
-	if [ ! -d "$DATADIR/mysql" ]; then
-		file_env 'MYSQL_ROOT_PASSWORD'
+# Verify that the minimally required password settings are set for new databases.
+docker_verify_minimum_env() {
 	if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
-			echo >&2 'error: database is uninitialized and password option is not specified '
-			echo >&2 '  You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
-			exit 1
+		mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
 	fi
+}
+
+# creates folders for the database
+# also ensures permission for user mysql of run as root
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
+	# TODO other directories that are used by default? like /var/lib/mysql-files
+	# see https://github.com/docker-library/mysql/issues/562
 	mkdir -p "$DATADIR"
 
-		echo 'Initializing database'
+	if [ "$user" = "0" ]; then
+		# this will cause less disk access than `chown -R`
+		find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
+	fi
+}
+
+# initializes the database directory
+docker_init_database_dir() {
+	mysql_note "Initializing database files"
 	installArgs=( --datadir="$DATADIR" --rpm )
 	if { mysql_install_db --help || :; } | grep -q -- '--auth-root-authentication-method'; then
 		# beginning in 10.4.3, install_db uses "socket" which only allows system user root to connect, switch back to "normal" to allow mysql root without a password
@@ -99,40 +148,67 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
 	fi
 	# "Other options are passed to mysqld." (so we pass all "mysqld" arguments directly here)
 	mysql_install_db "${installArgs[@]}" "${@:2}"
-		echo 'Database initialized'
+	mysql_note "Database files initialized"
+}
 
-		SOCKET="$(_get_config 'socket' "$@")"
-		"$@" --skip-networking --socket="${SOCKET}" &
-		pid="$!"
+# Loads various settings that are used elsewhere in the script
+# This should be called after mysql_check_config, but before any other functions
+docker_setup_env() {
+	# Get config
+	declare -g DATADIR SOCKET
+	DATADIR="$(mysql_get_config 'datadir' "$@")"
+	SOCKET="$(mysql_get_config 'socket' "$@")"
 
-		mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )
+	# Initialize values that might be stored in a file
+	file_env 'MYSQL_ROOT_HOST' '%'
+	file_env 'MYSQL_DATABASE'
+	file_env 'MYSQL_USER'
+	file_env 'MYSQL_PASSWORD'
+	file_env 'MYSQL_ROOT_PASSWORD'
 
-		for i in {30..0}; do
-			if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then
-				break
+	declare -g DATABASE_ALREADY_EXISTS
+	if [ -d "$DATADIR/mysql" ]; then
+		DATABASE_ALREADY_EXISTS='true'
 	fi
-			echo 'MySQL init process in progress...'
-			sleep 1
-		done
-		if [ "$i" = 0 ]; then
-			echo >&2 'MySQL init process failed.'
-			exit 1
+}
+
+# Execute sql script, passed via stdin
+# usage: docker_process_sql [--dont-use-mysql-root-password] [mysql-cli-args]
+#    ie: docker_process_sql --database=mydb <<<'INSERT ...'
+#    ie: docker_process_sql --dont-use-mysql-root-password --database=mydb <my-file.sql
+docker_process_sql() {
+	passfileArgs=()
+	if [ '--dont-use-mysql-root-password' = "$1" ]; then
+		passfileArgs+=( "$1" )
+		shift
 	fi
+	# args sent in can override this db, since they will be later in the command
+	if [ -n "$MYSQL_DATABASE" ]; then
+		set -- --database="$MYSQL_DATABASE" "$@"
+	fi
+
+	mysql --defaults-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@"
+}
 
+# Initializes database with timezone info and root password, plus optional extra db/user
+docker_setup_db() {
+	# Load timezone info into database
 	if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then
 		# sed is for https://bugs.mysql.com/bug.php?id=20545
-			mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql
+		mysql_tzinfo_to_sql /usr/share/zoneinfo \
+			| sed 's/Local time zone must be set--see zic manual page/FCTY/' \
+			| docker_process_sql --dont-use-mysql-root-password --database=mysql
+			# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is not set yet
 	fi
-
-		if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
+	# Generate random root password
+	if [ -n "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
 		export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
-			echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
+		mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
 	fi
-
-		rootCreate=
+	# Sets root password and creates root users for non-localhost hosts
+	local rootCreate=
 	# default root to listen for connections from anywhere
-		file_env 'MYSQL_ROOT_HOST' '%'
-		if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then
+	if [ -n "$MYSQL_ROOT_HOST" ] && [ "$MYSQL_ROOT_HOST" != 'localhost' ]; then
 		# no, we don't care if read finds a terminating character in this heredoc
 		# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
 		read -r -d '' rootCreate <<-EOSQL || true
@@ -141,59 +217,115 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
 		EOSQL
 	fi
 
-		"${mysql[@]}" <<-EOSQL
+	# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is just now being set
+	docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL
 		-- What's done in this file shouldn't be replicated
 		--  or products like mysql-fabric won't work
 		SET @@SESSION.SQL_LOG_BIN=0;
 
 		DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
 		SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ;
+		-- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365
+		-- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369
+		DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
+
 		GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
+		FLUSH PRIVILEGES ;
 		${rootCreate}
 		DROP DATABASE IF EXISTS test ;
-			FLUSH PRIVILEGES ;
 	EOSQL
 
-		if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
-			mysql+=( -p"${MYSQL_ROOT_PASSWORD}" )
+	# Creates a custom database and user if specified
+	if [ -n "$MYSQL_DATABASE" ]; then
+		mysql_note "Creating database ${MYSQL_DATABASE}"
+		docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;"
 	fi
 
-		file_env 'MYSQL_DATABASE'
-		if [ "$MYSQL_DATABASE" ]; then
-			echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
-			mysql+=( "$MYSQL_DATABASE" )
-		fi
+	if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then
+		mysql_note "Creating user ${MYSQL_USER}"
+		docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;"
 
-		file_env 'MYSQL_USER'
-		file_env 'MYSQL_PASSWORD'
-		if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
-			echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}"
+		if [ -n "$MYSQL_DATABASE" ]; then
+			mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}"
+			docker_process_sql --database=mysql <<<"GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;"
+		fi
 
-			if [ "$MYSQL_DATABASE" ]; then
-				echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
+		docker_process_sql --database=mysql <<<"FLUSH PRIVILEGES ;"
 	fi
+}
+
+_mysql_passfile() {
+	# echo the password to the "file" the client uses
+	# the client command will use process substitution to create a file on the fly
+	# ie: --defaults-file=<( _mysql_passfile )
+	if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MYSQL_ROOT_PASSWORD" ]; then
+		cat <<-EOF
+			[client]
+			password="${MYSQL_ROOT_PASSWORD}"
+		EOF
 	fi
+}
 
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
-			case "$f" in
-				*.sh)     echo "$0: running $f"; . "$f" ;;
-				*.sql)    echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
-				*)        echo "$0: ignoring $f" ;;
+# check arguments for an option that would cause mysqld to stop
+# return true if there is one
+_mysql_want_help() {
+	local arg
+	for arg; do
+		case "$arg" in
+			-'?'|--help|--print-defaults|-V|--version)
+				return 0
+				;;
 		esac
-			echo
 	done
+	return 1
+}
 
-		if ! kill -s TERM "$pid" || ! wait "$pid"; then
-			echo >&2 'MySQL init process failed.'
-			exit 1
+_main() {
+	# if command starts with an option, prepend mysqld
+	if [ "${1:0:1}" = '-' ]; then
+		set -- mysqld "$@"
 	fi
 
+	# skip setup if they aren't running mysqld or want an option that stops mysqld
+	if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then
+		mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started."
+
+		mysql_check_config "$@"
+		# Load various environment variables
+		docker_setup_env "$@"
+		docker_create_db_directories
+
+		# If container is started as root user, restart as dedicated mysql user
+		if [ "$(id -u)" = "0" ]; then
+			mysql_note "Switching to dedicated user 'mysql'"
+			exec gosu mysql "$BASH_SOURCE" "$@"
+		fi
+
+		# there's no database, so it needs to be initialized
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir "$@"
+
+			mysql_note "Starting temporary server"
+			docker_temp_server_start "$@"
+			mysql_note "Temporary server started."
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			mysql_note "Stopping temporary server"
+			docker_temp_server_stop
+			mysql_note "Temporary server stopped"
+
 			echo
-		echo 'MySQL init process done. Ready for start up.'
+			mysql_note "MySQL init process done. Ready for start up."
 			echo
 		fi
-fi
+	fi
+	exec "$@"
+}
 
-exec "$@"
+# If we are sourced from elsewhere, don't perform any further actions
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/mariadb_latest/docker-entrypoint.sh b/mariadb_latest/docker-entrypoint.sh
index 7ee5efd..77c5d4c 100755
--- a/mariadb_latest/docker-entrypoint.sh
+++ b/mariadb_latest/docker-entrypoint.sh
@@ -2,21 +2,21 @@
 set -eo pipefail
 shopt -s nullglob
 
-# if command starts with an option, prepend mysqld
-if [ "${1:0:1}" = '-' ]; then
-	set -- mysqld "$@"
-fi
-
-# skip setup if they want an option that stops mysqld
-wantHelp=
-for arg; do
-	case "$arg" in
-		-'?'|--help|--print-defaults|-V|--version)
-			wantHelp=1
-			break
-			;;
-	esac
-done
+# logging functions
+mysql_log() {
+	local type="$1"; shift
+	printf '%s [%s] [Entrypoint]: %s\n' "$(date --rfc-3339=seconds)" "$type" "$*"
+}
+mysql_note() {
+	mysql_log Note "$@"
+}
+mysql_warn() {
+	mysql_log Warn "$@" >&2
+}
+mysql_error() {
+	mysql_log ERROR "$@" >&2
+	exit 1
+}
 
 # usage: file_env VAR [DEFAULT]
 #    ie: file_env 'XYZ_DB_PASSWORD' 'example'
@@ -27,8 +27,7 @@ file_env() {
 	local fileVar="${var}_FILE"
 	local def="${2:-}"
 	if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
-		echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
-		exit 1
+		mysql_error "Both $var and $fileVar are set (but are exclusive)"
 	fi
 	local val="$def"
 	if [ "${!var:-}" ]; then
@@ -40,56 +39,106 @@ file_env() {
 	unset "$fileVar"
 }
 
-_check_config() {
-	toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" )
-	if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
-		cat >&2 <<-EOM
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
 
-			ERROR: mysqld failed while attempting to check config
-			command was: "${toRun[*]}"
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions
+docker_process_init_files() {
+	# mysql here for backwards compatibility "${mysql[@]}"
+	mysql=( docker_process_sql )
 
-			$errors
-		EOM
-		exit 1
+	echo
+	local f
+	for f; do
+		case "$f" in
+			*.sh)     mysql_note "$0: running $f"; . "$f" ;;
+			*.sql)    mysql_note "$0: running $f"; docker_process_sql < "$f"; echo ;;
+			*.sql.gz) mysql_note "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
+			*)        mysql_warn "$0: ignoring $f" ;;
+		esac
+		echo
+	done
+}
+
+mysql_check_config() {
+	local toRun=( "$@" --verbose --help --log-bin-index="$(mktemp -u)" ) errors
+	if ! errors="$("${toRun[@]}" 2>&1 >/dev/null)"; then
+		mysql_error $'mysqld failed while attempting to check config\n\tcommand was: '"${toRun[*]}"$'\n\t'"$errors"
 	fi
 }
 
 # Fetch value from server config
 # We use mysqld --verbose --help instead of my_print_defaults because the
 # latter only show values present in config files, and not server defaults
-_get_config() {
+mysql_get_config() {
 	local conf="$1"; shift
 	"$@" --verbose --help --log-bin-index="$(mktemp -u)" 2>/dev/null \
-		| awk '$1 == "'"$conf"'" && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
+		| awk -v conf="$conf" '$1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
 	# match "datadir      /some/path with/spaces in/it here" but not "--xyz=abc\n     datadir (xyz)"
 }
 
-# allow the container to be started with `--user`
-if [ "$1" = 'mysqld' -a -z "$wantHelp" -a "$(id -u)" = '0' ]; then
-	_check_config "$@"
-	DATADIR="$(_get_config 'datadir' "$@")"
-	mkdir -p "$DATADIR"
-	find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
-	exec gosu mysql "$BASH_SOURCE" "$@"
-fi
+# Do a temporary startup of the MySQL server, for init purposes
+docker_temp_server_start() {
+	"$@" --skip-networking --socket="${SOCKET}" &
+	mysql_note "Waiting for server startup"
+	local i
+	for i in {30..0}; do
+		# only use the root password if the database has already been initializaed
+		# so that it won't try to fill in a password file when it hasn't been set yet
+		extraArgs=()
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			extraArgs+=( '--dont-use-mysql-root-password' )
+		fi
+		if docker_process_sql "${extraArgs[@]}" --database=mysql <<<'SELECT 1' &> /dev/null; then
+			break
+		fi
+		sleep 1
+	done
+	if [ "$i" = 0 ]; then
+		mysql_error "Unable to start server."
+	fi
+}
 
-if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
-	# still need to check config, container may have started with --user
-	_check_config "$@"
-	# Get config
-	DATADIR="$(_get_config 'datadir' "$@")"
+# Stop the server. When using a local socket file mysqladmin will block until
+# the shutdown is complete.
+docker_temp_server_stop() {
+	if ! mysqladmin --defaults-extra-file=<( _mysql_passfile ) shutdown -uroot --socket="${SOCKET}"; then
+		mysql_error "Unable to shut down server."
+	fi
+}
 
-	if [ ! -d "$DATADIR/mysql" ]; then
-		file_env 'MYSQL_ROOT_PASSWORD'
+# Verify that the minimally required password settings are set for new databases.
+docker_verify_minimum_env() {
 	if [ -z "$MYSQL_ROOT_PASSWORD" -a -z "$MYSQL_ALLOW_EMPTY_PASSWORD" -a -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
-			echo >&2 'error: database is uninitialized and password option is not specified '
-			echo >&2 '  You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
-			exit 1
+		mysql_error $'Database is uninitialized and password option is not specified\n\tYou need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD'
 	fi
+}
+
+# creates folders for the database
+# also ensures permission for user mysql of run as root
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
+	# TODO other directories that are used by default? like /var/lib/mysql-files
+	# see https://github.com/docker-library/mysql/issues/562
 	mkdir -p "$DATADIR"
 
-		echo 'Initializing database'
+	if [ "$user" = "0" ]; then
+		# this will cause less disk access than `chown -R`
+		find "$DATADIR" \! -user mysql -exec chown mysql '{}' +
+	fi
+}
+
+# initializes the database directory
+docker_init_database_dir() {
+	mysql_note "Initializing database files"
 	installArgs=( --datadir="$DATADIR" --rpm )
 	if { mysql_install_db --help || :; } | grep -q -- '--auth-root-authentication-method'; then
 		# beginning in 10.4.3, install_db uses "socket" which only allows system user root to connect, switch back to "normal" to allow mysql root without a password
@@ -99,40 +148,67 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
 	fi
 	# "Other options are passed to mysqld." (so we pass all "mysqld" arguments directly here)
 	mysql_install_db "${installArgs[@]}" "${@:2}"
-		echo 'Database initialized'
+	mysql_note "Database files initialized"
+}
 
-		SOCKET="$(_get_config 'socket' "$@")"
-		"$@" --skip-networking --socket="${SOCKET}" &
-		pid="$!"
+# Loads various settings that are used elsewhere in the script
+# This should be called after mysql_check_config, but before any other functions
+docker_setup_env() {
+	# Get config
+	declare -g DATADIR SOCKET
+	DATADIR="$(mysql_get_config 'datadir' "$@")"
+	SOCKET="$(mysql_get_config 'socket' "$@")"
 
-		mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )
+	# Initialize values that might be stored in a file
+	file_env 'MYSQL_ROOT_HOST' '%'
+	file_env 'MYSQL_DATABASE'
+	file_env 'MYSQL_USER'
+	file_env 'MYSQL_PASSWORD'
+	file_env 'MYSQL_ROOT_PASSWORD'
 
-		for i in {30..0}; do
-			if echo 'SELECT 1' | "${mysql[@]}" &> /dev/null; then
-				break
+	declare -g DATABASE_ALREADY_EXISTS
+	if [ -d "$DATADIR/mysql" ]; then
+		DATABASE_ALREADY_EXISTS='true'
 	fi
-			echo 'MySQL init process in progress...'
-			sleep 1
-		done
-		if [ "$i" = 0 ]; then
-			echo >&2 'MySQL init process failed.'
-			exit 1
+}
+
+# Execute sql script, passed via stdin
+# usage: docker_process_sql [--dont-use-mysql-root-password] [mysql-cli-args]
+#    ie: docker_process_sql --database=mydb <<<'INSERT ...'
+#    ie: docker_process_sql --dont-use-mysql-root-password --database=mydb <my-file.sql
+docker_process_sql() {
+	passfileArgs=()
+	if [ '--dont-use-mysql-root-password' = "$1" ]; then
+		passfileArgs+=( "$1" )
+		shift
 	fi
+	# args sent in can override this db, since they will be later in the command
+	if [ -n "$MYSQL_DATABASE" ]; then
+		set -- --database="$MYSQL_DATABASE" "$@"
+	fi
+
+	mysql --defaults-file=<( _mysql_passfile "${passfileArgs[@]}") --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" "$@"
+}
 
+# Initializes database with timezone info and root password, plus optional extra db/user
+docker_setup_db() {
+	# Load timezone info into database
 	if [ -z "$MYSQL_INITDB_SKIP_TZINFO" ]; then
 		# sed is for https://bugs.mysql.com/bug.php?id=20545
-			mysql_tzinfo_to_sql /usr/share/zoneinfo | sed 's/Local time zone must be set--see zic manual page/FCTY/' | "${mysql[@]}" mysql
+		mysql_tzinfo_to_sql /usr/share/zoneinfo \
+			| sed 's/Local time zone must be set--see zic manual page/FCTY/' \
+			| docker_process_sql --dont-use-mysql-root-password --database=mysql
+			# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is not set yet
 	fi
-
-		if [ ! -z "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
+	# Generate random root password
+	if [ -n "$MYSQL_RANDOM_ROOT_PASSWORD" ]; then
 		export MYSQL_ROOT_PASSWORD="$(pwgen -1 32)"
-			echo "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
+		mysql_note "GENERATED ROOT PASSWORD: $MYSQL_ROOT_PASSWORD"
 	fi
-
-		rootCreate=
+	# Sets root password and creates root users for non-localhost hosts
+	local rootCreate=
 	# default root to listen for connections from anywhere
-		file_env 'MYSQL_ROOT_HOST' '%'
-		if [ ! -z "$MYSQL_ROOT_HOST" -a "$MYSQL_ROOT_HOST" != 'localhost' ]; then
+	if [ -n "$MYSQL_ROOT_HOST" ] && [ "$MYSQL_ROOT_HOST" != 'localhost' ]; then
 		# no, we don't care if read finds a terminating character in this heredoc
 		# https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151
 		read -r -d '' rootCreate <<-EOSQL || true
@@ -141,59 +217,115 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then
 		EOSQL
 	fi
 
-		"${mysql[@]}" <<-EOSQL
+	# tell docker_process_sql to not use MYSQL_ROOT_PASSWORD since it is just now being set
+	docker_process_sql --dont-use-mysql-root-password --database=mysql <<-EOSQL
 		-- What's done in this file shouldn't be replicated
 		--  or products like mysql-fabric won't work
 		SET @@SESSION.SQL_LOG_BIN=0;
 
 		DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root') OR host NOT IN ('localhost') ;
 		SET PASSWORD FOR 'root'@'localhost'=PASSWORD('${MYSQL_ROOT_PASSWORD}') ;
+		-- 10.1: https://github.com/MariaDB/server/blob/d925aec1c10cebf6c34825a7de50afe4e630aff4/scripts/mysql_secure_installation.sh#L347-L365
+		-- 10.5: https://github.com/MariaDB/server/blob/00c3a28820c67c37ebbca72691f4897b57f2eed5/scripts/mysql_secure_installation.sh#L351-L369
+		DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%' ;
+
 		GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
+		FLUSH PRIVILEGES ;
 		${rootCreate}
 		DROP DATABASE IF EXISTS test ;
-			FLUSH PRIVILEGES ;
 	EOSQL
 
-		if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then
-			mysql+=( -p"${MYSQL_ROOT_PASSWORD}" )
+	# Creates a custom database and user if specified
+	if [ -n "$MYSQL_DATABASE" ]; then
+		mysql_note "Creating database ${MYSQL_DATABASE}"
+		docker_process_sql --database=mysql <<<"CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;"
 	fi
 
-		file_env 'MYSQL_DATABASE'
-		if [ "$MYSQL_DATABASE" ]; then
-			echo "CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;" | "${mysql[@]}"
-			mysql+=( "$MYSQL_DATABASE" )
-		fi
+	if [ -n "$MYSQL_USER" ] && [ -n "$MYSQL_PASSWORD" ]; then
+		mysql_note "Creating user ${MYSQL_USER}"
+		docker_process_sql --database=mysql <<<"CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;"
 
-		file_env 'MYSQL_USER'
-		file_env 'MYSQL_PASSWORD'
-		if [ "$MYSQL_USER" -a "$MYSQL_PASSWORD" ]; then
-			echo "CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;" | "${mysql[@]}"
+		if [ -n "$MYSQL_DATABASE" ]; then
+			mysql_note "Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE}"
+			docker_process_sql --database=mysql <<<"GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;"
+		fi
 
-			if [ "$MYSQL_DATABASE" ]; then
-				echo "GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;" | "${mysql[@]}"
+		docker_process_sql --database=mysql <<<"FLUSH PRIVILEGES ;"
 	fi
+}
+
+_mysql_passfile() {
+	# echo the password to the "file" the client uses
+	# the client command will use process substitution to create a file on the fly
+	# ie: --defaults-file=<( _mysql_passfile )
+	if [ '--dont-use-mysql-root-password' != "$1" ] && [ -n "$MYSQL_ROOT_PASSWORD" ]; then
+		cat <<-EOF
+			[client]
+			password="${MYSQL_ROOT_PASSWORD}"
+		EOF
 	fi
+}
 
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
-			case "$f" in
-				*.sh)     echo "$0: running $f"; . "$f" ;;
-				*.sql)    echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
-				*)        echo "$0: ignoring $f" ;;
+# check arguments for an option that would cause mysqld to stop
+# return true if there is one
+_mysql_want_help() {
+	local arg
+	for arg; do
+		case "$arg" in
+			-'?'|--help|--print-defaults|-V|--version)
+				return 0
+				;;
 		esac
-			echo
 	done
+	return 1
+}
 
-		if ! kill -s TERM "$pid" || ! wait "$pid"; then
-			echo >&2 'MySQL init process failed.'
-			exit 1
+_main() {
+	# if command starts with an option, prepend mysqld
+	if [ "${1:0:1}" = '-' ]; then
+		set -- mysqld "$@"
 	fi
 
+	# skip setup if they aren't running mysqld or want an option that stops mysqld
+	if [ "$1" = 'mysqld' ] && ! _mysql_want_help "$@"; then
+		mysql_note "Entrypoint script for MySQL Server ${MYSQL_VERSION} started."
+
+		mysql_check_config "$@"
+		# Load various environment variables
+		docker_setup_env "$@"
+		docker_create_db_directories
+
+		# If container is started as root user, restart as dedicated mysql user
+		if [ "$(id -u)" = "0" ]; then
+			mysql_note "Switching to dedicated user 'mysql'"
+			exec gosu mysql "$BASH_SOURCE" "$@"
+		fi
+
+		# there's no database, so it needs to be initialized
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir "$@"
+
+			mysql_note "Starting temporary server"
+			docker_temp_server_start "$@"
+			mysql_note "Temporary server started."
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			mysql_note "Stopping temporary server"
+			docker_temp_server_stop
+			mysql_note "Temporary server stopped"
+
 			echo
-		echo 'MySQL init process done. Ready for start up.'
+			mysql_note "MySQL init process done. Ready for start up."
 			echo
 		fi
-fi
+	fi
+	exec "$@"
+}
 
-exec "$@"
+# If we are sourced from elsewhere, don't perform any further actions
+if ! _is_sourced; then
+	_main "$@"
+fi

@yosifkit
Copy link
Member Author

Build test of #6965; 5026d50; amd64 (mariadb):

$ bashbrew build mariadb:10.4.10-bionic
Building bashbrew/cache:68217c6a61a230e094f80e664189b34d6ad4de2c052fac97cfeb07417ee980c8 (mariadb:10.4.10-bionic)
Tagging mariadb:10.4.10-bionic
Tagging mariadb:10.4-bionic
Tagging mariadb:10-bionic
Tagging mariadb:bionic
Tagging mariadb:10.4.10
Tagging mariadb:10.4
Tagging mariadb:10
Tagging mariadb:latest

$ test/run.sh mariadb:10.4.10-bionic
testing mariadb:10.4.10-bionic
	'utc' [1/7]...passed
	'cve-2014--shellshock' [2/7]...passed
	'no-hard-coded-passwords' [3/7]...passed
	'override-cmd' [4/7]...passed
	'mysql-basics' [5/7].........passed
	'mysql-initdb' [6/7].........passed
	'mysql-log-bin' [7/7].........passed


$ bashbrew build mariadb:10.3.20-bionic
Building bashbrew/cache:59d578f7e9a229477460875392a079ccca442e69c38518a1ed19933087490b25 (mariadb:10.3.20-bionic)
Tagging mariadb:10.3.20-bionic
Tagging mariadb:10.3-bionic
Tagging mariadb:10.3.20
Tagging mariadb:10.3

$ test/run.sh mariadb:10.3.20-bionic
testing mariadb:10.3.20-bionic
	'utc' [1/7]...passed
	'cve-2014--shellshock' [2/7]...passed
	'no-hard-coded-passwords' [3/7]...passed
	'override-cmd' [4/7]...passed
	'mysql-basics' [5/7].........passed
	'mysql-initdb' [6/7].........passed
	'mysql-log-bin' [7/7].........passed


$ bashbrew build mariadb:10.2.29-bionic
Building bashbrew/cache:03451b5f833c53b3488efb2426f8a8efa33b6baa9442429c577b784c435bc709 (mariadb:10.2.29-bionic)
Tagging mariadb:10.2.29-bionic
Tagging mariadb:10.2-bionic
Tagging mariadb:10.2.29
Tagging mariadb:10.2

$ test/run.sh mariadb:10.2.29-bionic
testing mariadb:10.2.29-bionic
	'utc' [1/7]...passed
	'cve-2014--shellshock' [2/7]...passed
	'no-hard-coded-passwords' [3/7]...passed
	'override-cmd' [4/7]...passed
	'mysql-basics' [5/7].........passed
	'mysql-initdb' [6/7].........passed
	'mysql-log-bin' [7/7].........passed


$ bashbrew build mariadb:10.1.43-bionic
Building bashbrew/cache:2cd60c22b4e734355c71ef400bcd4b9a9dc21a5beb1b11bdfbe836e835a7c5be (mariadb:10.1.43-bionic)
Tagging mariadb:10.1.43-bionic
Tagging mariadb:10.1-bionic
Tagging mariadb:10.1.43
Tagging mariadb:10.1

$ test/run.sh mariadb:10.1.43-bionic
testing mariadb:10.1.43-bionic
	'utc' [1/7]...passed
	'cve-2014--shellshock' [2/7]...passed
	'no-hard-coded-passwords' [3/7]...passed
	'override-cmd' [4/7]...passed
	'mysql-basics' [5/7]...........passed
	'mysql-initdb' [6/7]...........passed
	'mysql-log-bin' [7/7]...........passed

@yosifkit yosifkit merged commit f19bb6c into docker-library:master Nov 13, 2019
@yosifkit yosifkit deleted the mariadb branch November 13, 2019 00:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants