@@ -4,7 +4,7 @@ shopt -s nullglob
44
55# logging functions
66mysql_log () {
7- local type=$1 ; shift
7+ local type=" $1 " ; shift
88 printf " $( date --rfc-3339=seconds) [${type} ] [Entrypoint]: $@ \n"
99}
1010mysql_note () {
@@ -18,22 +18,6 @@ mysql_error() {
1818 exit 1
1919}
2020
21- # if command starts with an option, prepend mysqld
22- if [ " ${1: 0: 1} " = ' -' ]; then
23- set -- mysqld " $@ "
24- fi
25-
26- # skip setup if they want an option that stops mysqld
27- wantHelp=
28- for arg; do
29- case " $arg " in
30- -' ?' |--help|--print-defaults|-V|--version)
31- wantHelp=1
32- break
33- ;;
34- esac
35- done
36-
3721# usage: file_env VAR [DEFAULT]
3822# ie: file_env 'XYZ_DB_PASSWORD' 'example'
3923# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
@@ -55,25 +39,28 @@ file_env() {
5539 unset " $fileVar "
5640}
5741
58- # usage: docker_process_init_files FILENAME MYSQLCOMMAND...
59- # ie: docker_process_init_files foo.sh mysql -uroot
60- # (process a single initializer file, based on its extension. we define this
61- # function here, so that initializer scripts (*.sh) can use the same logic,
62- # potentially recursively, or override the logic used in subsequent calls)
42+ # usage: docker_process_init_files [file [file [...]]]
43+ # ie: docker_process_init_files /always-initdb.d/*
44+ # process initializer files, based on file extensions
6345docker_process_init_files () {
64- local f=" $1 " ; shift
65-
66- case " $f " in
67- * .sh) mysql_note " $0 : running $f " ; . " $f " ;;
68- * .sql) mysql_note " $0 : running $f " ; docker_process_sql " $( cat $f ) " ; echo ;;
69- * .sql.gz) mysql_note " $0 : running $f " ; docker_process_sql " $( gunzip -c $f ) " ; echo ;;
70- * ) mysql_warn " $0 : ignoring $f " ;;
71- esac
46+ # mysql here for backwards compatibility "${mysql[@]}"
47+ mysql=( docker_process_sql )
48+
7249 echo
50+ local f
51+ for f; do
52+ case " $f " in
53+ * .sh) mysql_note " $0 : running $f " ; . " $f " ;;
54+ * .sql) mysql_note " $0 : running $f " ; docker_process_sql < " $f " ; echo ;;
55+ * .sql.gz) mysql_note " $0 : running $f " ; gunzip -c " $f " | docker_process_sql; echo ;;
56+ * ) mysql_warn " $0 : ignoring $f " ;;
57+ esac
58+ echo
59+ done
7360}
7461
7562mysql_check_config () {
76- toRun=( " $@ " --verbose --help )
63+ local toRun=( " $@ " --verbose --help ) errors
7764 if ! errors=" $( " ${toRun[@]} " 2>&1 > /dev/null) " ; then
7865 mysql_error " mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]} \n\t$errors "
7966 fi
@@ -85,13 +72,13 @@ mysql_check_config() {
8572mysql_get_config () {
8673 local conf=" $1 " ; shift
8774 " $@ " --verbose --help --log-bin-index=" $( mktemp -u) " 2> /dev/null \
88- | awk ' $1 == " ' " $ conf" ' " && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
75+ | awk -v conf= " $conf " ' $1 == conf && /^[^ \t]/ { sub(/^[^ \t]+[ \t]+/, ""); print; exit }'
8976 # match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)"
9077}
9178
9279# Do a temporary startup of the MySQL server, for init purposes
9380docker_temp_server_start () {
94- result=0
81+ local result=0
9582 %%SERVERSTARTUP%%
9683 if [ " $result " != " 0" ]; then
9784 mysql_error " Unable to start server. Status code $result ."
@@ -100,8 +87,9 @@ docker_temp_server_start() {
10087 # For 5.7+ the server is ready for use as soon as startup command unblocks
10188 if [ " ${MYSQL_MAJOR} " = " 5.5" ] || [ " ${MYSQL_MAJOR} " = " 5.6" ]; then
10289 mysql_note " Waiting for server startup"
90+ local i
10391 for i in {30..0}; do
104- if docker_process_sql " SELECT 1" & > /dev/null; then
92+ if docker_process_sql --database=mysql <<< ' SELECT 1' & > /dev/null; then
10593 break
10694 fi
10795 sleep 1
@@ -115,8 +103,8 @@ docker_temp_server_start() {
115103# Stop the server. When using a local socket file mysqladmin will block until
116104# the shutdown is complete.
117105docker_temp_server_stop () {
118- result=0
119- mysqladmin --defaults-extra-file=" ${PASSFILE} " shutdown -uroot --socket=" ${SOCKET} " || result=$?
106+ local result=0
107+ mysqladmin --defaults-extra-file=<( echo " ${PASSFILE} " ) shutdown -uroot --socket=" ${SOCKET} " || result=$?
120108 if [ " $result " != " 0" ]; then
121109 mysql_error " Unable to shut down server. Status code $result ."
122110 fi
@@ -129,10 +117,23 @@ docker_verify_minimum_env() {
129117 fi
130118}
131119
132- # Creates and initializes the database directory
120+ # creates folders for the database
121+ # also ensures permission for user mysql of run as root
133122docker_create_db_directories () {
123+ local user; user=" $( id -u) "
124+
125+ # TODO other directories that are used by default? like /var/lib/mysql-files
126+ # see https://github.com/docker-library/mysql/issues/562
134127 mkdir -p " $DATADIR "
135128
129+ if [ " $user " = " 0" ]; then
130+ # this will cause less disk access than `chown -R`
131+ find " $DATADIR " \! -user mysql -exec chown mysql ' {}' +
132+ fi
133+ }
134+
135+ # initializes the database directory
136+ docker_init_database_dir () {
136137 mysql_note " Initializing database files"
137138 %%DATABASEINIT%%
138139 mysql_note " Database files initialized"
@@ -150,11 +151,7 @@ docker_setup_env() {
150151 # Get config
151152 DATADIR=" $( mysql_get_config ' datadir' " $@ " ) "
152153 SOCKET=" $( mysql_get_config ' socket' " $@ " ) "
153-
154- # We create a file to store the root password in so we don''t use it on the command line
155- TMPDIR=" $( mktemp -d) "
156- PASSFILE=" $( mktemp ${TMPDIR} /XXXXXXXXXX) "
157-
154+
158155 # Initialize values that might be stored in a file
159156 file_env ' MYSQL_ROOT_HOST' ' %'
160157 file_env ' MYSQL_DATABASE'
@@ -163,27 +160,25 @@ docker_setup_env() {
163160 file_env ' MYSQL_ROOT_PASSWORD'
164161}
165162
166- # Execute sql script
163+ # Execute sql script, passed via stdin
164+ # usage: docker_process_sql [mysql-cli-args]
165+ # ie: docker_process_sql --database=mydb <<<'INSERT ...'
166+ # ie: docker_process_sql --database=mydb <my-file.sql
167167docker_process_sql () {
168- SQL=$1
169- DB=$2
170- if [ -z " $SQL " ]; then
171- mysql_error " Empty sql script provided"
168+ # args sent in can override this db, since they will be later in the command
169+ if [ -n " $MYSQL_DATABASE " ]; then
170+ set -- --database=" $MYSQL_DATABASE " " $@ "
172171 fi
173- echo " $SQL " | mysql --defaults-file=" ${PASSFILE} " --protocol=socket -uroot -hlocalhost --socket=" ${SOCKET} " " $DB "
174- }
175172
176- # Define the client command that's used in various places
177- docker_init_client_command () {
178- mysql=( mysql --defaults-file=" ${PASSFILE} " --protocol=socket -uroot -hlocalhost --socket=" ${SOCKET} " )
173+ mysql --defaults-file=<( echo " ${PASSFILE} " ) --protocol=socket -uroot -hlocalhost --socket=" ${SOCKET} " " $@ "
179174}
180175
181176# Initializes database with timezone info and root password, plus optional extra db/user
182177docker_setup_db () {
183178 # Load timezone info into database
184179 if [ -z " $MYSQL_INITDB_SKIP_TZINFO " ]; then
185180 # sed is for https://bugs.mysql.com/bug.php?id=20545
186- docker_process_sql " $( mysql_tzinfo_to_sql /usr/share/zoneinfo | sed ' s/Local time zone must be set--see zic manual page/FCTY/' ) " mysql
181+ mysql_tzinfo_to_sql /usr/share/zoneinfo | sed ' s/Local time zone must be set--see zic manual page/FCTY/' | docker_process_sql --database= mysql
187182 fi
188183 # Generate random root password
189184 if [ ! -z " $MYSQL_RANDOM_ROOT_PASSWORD " ]; then
@@ -202,102 +197,117 @@ docker_setup_db() {
202197 EOSQL
203198 fi
204199
205- docker_process_sql "
200+ docker_process_sql --database=mysql << - EOSQL
206201 -- What's done in this file shouldn't be replicated
207202 -- or products like mysql-fabric won't work
208203 SET @@SESSION.SQL_LOG_BIN=0;
209204
210205 %%PASSWORDSET%%
211206 GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
207+ FLUSH PRIVILEGES ;
212208 ${rootCreate}
213209 DROP DATABASE IF EXISTS test ;
214- FLUSH PRIVILEGES ;
215- "
210+ EOSQL
216211
217- # Write the password to the file the client uses
212+ # Write the password to the "file" the client uses
213+ # the client command will use process substitution to create a file on the fly
214+ # ie: --defaults-file=<( echo "${PASSFILE}" )
218215 if [ ! -z " $MYSQL_ROOT_PASSWORD " ]; then
219- cat > " ${ PASSFILE} " << EOF
220- [client]
221- password="${MYSQL_ROOT_PASSWORD} "
222- EOF
216+ read -r -d ' ' PASSFILE << - EOF || true
217+ [client]
218+ password="${MYSQL_ROOT_PASSWORD} "
219+ EOF
223220 fi
224221
225222 # Creates a custom database and user if specified
226223 if [ " $MYSQL_DATABASE " ]; then
227224 mysql_note " Creating database ${MYSQL_DATABASE} "
228- docker_process_sql " CREATE DATABASE IF NOT EXISTS \` $MYSQL_DATABASE \` ;"
229- mysql+=( " $MYSQL_DATABASE " )
225+ docker_process_sql --database=mysql <<< " CREATE DATABASE IF NOT EXISTS \`$MYSQL_DATABASE\` ;"
230226 fi
231227
232228 if [ " $MYSQL_USER " -a " $MYSQL_PASSWORD " ]; then
233229 mysql_note " Creating user ${MYSQL_USER} "
234- docker_process_sql " CREATE USER '$MYSQL_USER '@'%' IDENTIFIED BY '$MYSQL_PASSWORD ' ;"
230+ docker_process_sql --database=mysql <<< " CREATE USER '$MYSQL_USER'@'%' IDENTIFIED BY '$MYSQL_PASSWORD' ;"
235231
236232 if [ " $MYSQL_DATABASE " ]; then
237233 mysql_note " Giving user ${MYSQL_USER} access to schema ${MYSQL_DATABASE} "
238- docker_process_sql " GRANT ALL ON \` $MYSQL_DATABASE \` .* TO '$MYSQL_USER '@'%' ;"
234+ docker_process_sql --database=mysql <<< " GRANT ALL ON \`$MYSQL_DATABASE\`.* TO '$MYSQL_USER'@'%' ;"
239235 fi
240236
241- docker_process_sql " FLUSH PRIVILEGES ;"
237+ docker_process_sql --database=mysql <<< " FLUSH PRIVILEGES ;"
242238 fi
243239}
244240
245241# Mark root user as expired so the password must be changed before anything
246242# else can be done (only supported for 5.6+)
247243mysql_expire_root_user () {
248- if [ " ${MYSQL_MAJOR} " = " 5.5" ]; then
249- mysql_warn " MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)"
250- else
251- docker_process_sql " ALTER USER 'root'@'%' PASSWORD EXPIRE;"
244+ if [ ! -z " $MYSQL_ONETIME_PASSWORD " ]; then
245+ if [ " ${MYSQL_MAJOR} " = " 5.5" ]; then
246+ mysql_warn " MySQL 5.5 does not support PASSWORD EXPIRE (required for MYSQL_ONETIME_PASSWORD)"
247+ else
248+ docker_process_sql --database=mysql << -EOSQL
249+ ALTER USER 'root'@'%' PASSWORD EXPIRE;
250+ EOSQL
251+ fi
252252 fi
253253}
254254
255+ # check arguments for an option that would cause mysqld to stop
256+ # return true if there is one
257+ _want_help () {
258+ local arg
259+ for arg; do
260+ case " $arg " in
261+ -' ?' |--help|--print-defaults|-V|--version)
262+ return 0
263+ ;;
264+ esac
265+ done
266+ return 1
267+ }
268+
255269_main () {
256- mysql_note " Entrypoint script for MySQL Server ${MYSQL_VERSION} started."
270+ # if command starts with an option, prepend mysqld
271+ if [ " ${1: 0: 1} " = ' -' ]; then
272+ set -- mysqld " $@ "
273+ fi
274+
275+ # skip setup if they aren't running mysqld or want an option that stops mysqld
276+ if [ " $1 " = ' mysqld' ] && ! _want_help " $@ " ; then
277+ mysql_note " Entrypoint script for MySQL Server ${MYSQL_VERSION} started."
257278
258- if [ " $1 " = ' mysqld' -a -z " $wantHelp " ]; then
259279 # Load various environment variables
260280 docker_setup_env " $@ "
281+ mysql_check_config " $@ "
261282
262283 # If container is started as root user, restart as dedicated mysql user
263- if [ " $( id -u) " = ' 0' ]; then
264- mysql_check_config " $@ "
265- mkdir -p " $DATADIR "
266- chown -R mysql:mysql " $DATADIR "
284+ if [ " $( id -u) " = " 0" ]; then
285+ docker_create_db_directories
267286 mysql_note " Switching to dedicated user 'mysql'"
268287 exec gosu mysql " $BASH_SOURCE " " $@ "
269288 fi
270289
271- # still need to check config, container may have started with --user
272- mysql_check_config " $@ "
290+ # just in case the script was not started as root
291+ docker_create_db_directories
273292
274293 # If this is true then there's no database, and it needs to be initialized
275294 if [ ! -d " $DATADIR /mysql" ]; then
276295 docker_verify_minimum_env
277- docker_create_db_directories " $@ "
278- docker_init_client_command
296+ docker_init_database_dir " $@ "
279297
280298 mysql_note " Starting temporary server"
281299 docker_temp_server_start " $@ "
282300 mysql_note " Temporary server started."
283301
284-
285302 docker_setup_db
303+ docker_process_init_files /docker-entrypoint-initdb.d/*
286304
287- echo
288- for f in /docker-entrypoint-initdb.d/* ; do
289- docker_process_init_files " $f "
290- done
305+ mysql_expire_root_user
291306
292- if [ ! -z " $MYSQL_ONETIME_PASSWORD " ]; then
293- mysql_expire_root_user
294- fi
295307 mysql_note " Stopping temporary server"
296308 docker_temp_server_stop
297309 mysql_note " Temporary server stopped"
298310
299- # Remove the password file now that initialization is complete
300- rm -f " ${PASSFILE} "
301311 unset PASSFILE
302312 echo
303313 mysql_note " MySQL init process done. Ready for start up."
@@ -306,8 +316,10 @@ _main() {
306316 fi
307317 exec " $@ "
308318}
319+
309320# This checks if the script has been sourced from elsewhere.
310321# If so we don't perform any further actions
322+ # https://unix.stackexchange.com/a/215279
311323if [ " ${FUNCNAME[${#FUNCNAME[@]} - 1]}" != ' source' ]; then
312324 _main " $@ "
313325fi
0 commit comments