@@ -4,7 +4,7 @@ shopt -s nullglob
4
4
5
5
# logging functions
6
6
mysql_log () {
7
- local type=$1 ; shift
7
+ local type=" $1 " ; shift
8
8
printf " $( date --rfc-3339=seconds) [${type} ] [Entrypoint]: $@ \n"
9
9
}
10
10
mysql_note () {
@@ -18,22 +18,6 @@ mysql_error() {
18
18
exit 1
19
19
}
20
20
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
-
37
21
# usage: file_env VAR [DEFAULT]
38
22
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
39
23
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
@@ -55,25 +39,28 @@ file_env() {
55
39
unset " $fileVar "
56
40
}
57
41
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
63
45
docker_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
+
72
49
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
73
60
}
74
61
75
62
mysql_check_config () {
76
- toRun=( " $@ " --verbose --help )
63
+ local toRun=( " $@ " --verbose --help ) errors
77
64
if ! errors=" $( " ${toRun[@]} " 2>&1 > /dev/null) " ; then
78
65
mysql_error " mysqld failed while attempting to check config\n\tcommand was: ${toRun[*]} \n\t$errors "
79
66
fi
@@ -85,13 +72,13 @@ mysql_check_config() {
85
72
mysql_get_config () {
86
73
local conf=" $1 " ; shift
87
74
" $@ " --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 }'
89
76
# match "datadir /some/path with/spaces in/it here" but not "--xyz=abc\n datadir (xyz)"
90
77
}
91
78
92
79
# Do a temporary startup of the MySQL server, for init purposes
93
80
docker_temp_server_start () {
94
- result=0
81
+ local result=0
95
82
%%SERVERSTARTUP%%
96
83
if [ " $result " != " 0" ]; then
97
84
mysql_error " Unable to start server. Status code $result ."
@@ -100,8 +87,9 @@ docker_temp_server_start() {
100
87
# For 5.7+ the server is ready for use as soon as startup command unblocks
101
88
if [ " ${MYSQL_MAJOR} " = " 5.5" ] || [ " ${MYSQL_MAJOR} " = " 5.6" ]; then
102
89
mysql_note " Waiting for server startup"
90
+ local i
103
91
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
105
93
break
106
94
fi
107
95
sleep 1
@@ -115,8 +103,8 @@ docker_temp_server_start() {
115
103
# Stop the server. When using a local socket file mysqladmin will block until
116
104
# the shutdown is complete.
117
105
docker_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=$?
120
108
if [ " $result " != " 0" ]; then
121
109
mysql_error " Unable to shut down server. Status code $result ."
122
110
fi
@@ -129,10 +117,23 @@ docker_verify_minimum_env() {
129
117
fi
130
118
}
131
119
132
- # Creates and initializes the database directory
120
+ # creates folders for the database
121
+ # also ensures permission for user mysql of run as root
133
122
docker_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
134
127
mkdir -p " $DATADIR "
135
128
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 () {
136
137
mysql_note " Initializing database files"
137
138
%%DATABASEINIT%%
138
139
mysql_note " Database files initialized"
@@ -150,11 +151,7 @@ docker_setup_env() {
150
151
# Get config
151
152
DATADIR=" $( mysql_get_config ' datadir' " $@ " ) "
152
153
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
+
158
155
# Initialize values that might be stored in a file
159
156
file_env ' MYSQL_ROOT_HOST' ' %'
160
157
file_env ' MYSQL_DATABASE'
@@ -163,27 +160,25 @@ docker_setup_env() {
163
160
file_env ' MYSQL_ROOT_PASSWORD'
164
161
}
165
162
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
167
167
docker_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 " " $@ "
172
171
fi
173
- echo " $SQL " | mysql --defaults-file=" ${PASSFILE} " --protocol=socket -uroot -hlocalhost --socket=" ${SOCKET} " " $DB "
174
- }
175
172
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} " " $@ "
179
174
}
180
175
181
176
# Initializes database with timezone info and root password, plus optional extra db/user
182
177
docker_setup_db () {
183
178
# Load timezone info into database
184
179
if [ -z " $MYSQL_INITDB_SKIP_TZINFO " ]; then
185
180
# 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
187
182
fi
188
183
# Generate random root password
189
184
if [ ! -z " $MYSQL_RANDOM_ROOT_PASSWORD " ]; then
@@ -202,102 +197,117 @@ docker_setup_db() {
202
197
EOSQL
203
198
fi
204
199
205
- docker_process_sql "
200
+ docker_process_sql --database=mysql << - EOSQL
206
201
-- What's done in this file shouldn't be replicated
207
202
-- or products like mysql-fabric won't work
208
203
SET @@SESSION.SQL_LOG_BIN=0;
209
204
210
205
%%PASSWORDSET%%
211
206
GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ;
207
+ FLUSH PRIVILEGES ;
212
208
${rootCreate}
213
209
DROP DATABASE IF EXISTS test ;
214
- FLUSH PRIVILEGES ;
215
- "
210
+ EOSQL
216
211
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}" )
218
215
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
223
220
fi
224
221
225
222
# Creates a custom database and user if specified
226
223
if [ " $MYSQL_DATABASE " ]; then
227
224
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\` ;"
230
226
fi
231
227
232
228
if [ " $MYSQL_USER " -a " $MYSQL_PASSWORD " ]; then
233
229
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' ;"
235
231
236
232
if [ " $MYSQL_DATABASE " ]; then
237
233
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'@'%' ;"
239
235
fi
240
236
241
- docker_process_sql " FLUSH PRIVILEGES ;"
237
+ docker_process_sql --database=mysql <<< " FLUSH PRIVILEGES ;"
242
238
fi
243
239
}
244
240
245
241
# Mark root user as expired so the password must be changed before anything
246
242
# else can be done (only supported for 5.6+)
247
243
mysql_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
252
252
fi
253
253
}
254
254
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
+
255
269
_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."
257
278
258
- if [ " $1 " = ' mysqld' -a -z " $wantHelp " ]; then
259
279
# Load various environment variables
260
280
docker_setup_env " $@ "
281
+ mysql_check_config " $@ "
261
282
262
283
# 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
267
286
mysql_note " Switching to dedicated user 'mysql'"
268
287
exec gosu mysql " $BASH_SOURCE " " $@ "
269
288
fi
270
289
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
273
292
274
293
# If this is true then there's no database, and it needs to be initialized
275
294
if [ ! -d " $DATADIR /mysql" ]; then
276
295
docker_verify_minimum_env
277
- docker_create_db_directories " $@ "
278
- docker_init_client_command
296
+ docker_init_database_dir " $@ "
279
297
280
298
mysql_note " Starting temporary server"
281
299
docker_temp_server_start " $@ "
282
300
mysql_note " Temporary server started."
283
301
284
-
285
302
docker_setup_db
303
+ docker_process_init_files /docker-entrypoint-initdb.d/*
286
304
287
- echo
288
- for f in /docker-entrypoint-initdb.d/* ; do
289
- docker_process_init_files " $f "
290
- done
305
+ mysql_expire_root_user
291
306
292
- if [ ! -z " $MYSQL_ONETIME_PASSWORD " ]; then
293
- mysql_expire_root_user
294
- fi
295
307
mysql_note " Stopping temporary server"
296
308
docker_temp_server_stop
297
309
mysql_note " Temporary server stopped"
298
310
299
- # Remove the password file now that initialization is complete
300
- rm -f " ${PASSFILE} "
301
311
unset PASSFILE
302
312
echo
303
313
mysql_note " MySQL init process done. Ready for start up."
@@ -306,8 +316,10 @@ _main() {
306
316
fi
307
317
exec " $@ "
308
318
}
319
+
309
320
# This checks if the script has been sourced from elsewhere.
310
321
# If so we don't perform any further actions
322
+ # https://unix.stackexchange.com/a/215279
311
323
if [ " ${FUNCNAME[${#FUNCNAME[@]} - 1]}" != ' source' ]; then
312
324
_main " $@ "
313
325
fi
0 commit comments