1#!/bin/sh 2# vim: tabstop=4 shiftwidth=4 expandtab 3# 4# MySQL backup tool. 5# 6# Copyright 2010-2014, Alexey Degtyarev <alexey@renatasystems.org>. 7# All rights reserved. 8# 9# $Id: mysqlbackup 396 2014-05-24 09:30:53Z degtyarev.alexey@gmail.com $ 10 11REVISION="$Revision: 396 $" 12VERSION="2.8" 13 14# Some default values. 15DEFAULT_ARCHIVE_DAYS=5 # How long to keep backups. 16DEFAULT_ARCHIVE_DIR="/var/backups" # According to hier(7). 17DEFAULT_MYSQLDUMP_OPTIONS="--opt --skip-lock-tables --quote-names" 18DEFAULT_MYSQLCHECK_OPTIONS=\ 19"--auto-repair --check-only-changed --extended --silent" 20DEFAULT_MYSQLOPTIMIZE_OPTIONS="--optimize --silent" 21DEFAULT_DIR_MODE=0700 22DEFAULT_FILE_MODE=0600 23DEFAULT_SAVE_MYCNF="yes" # Either save my.cnf or not 24DEFAULT_PATH_MYCNF="%%DATADIR%%my.cnf" # Macros will be replaced 25DEFAULT_CHECK_TABLES="yes" # yes | no 26DEFAULT_OPTIMIZE_TABLES="yes" # yes | no 27DEFAULT_LOCKFILE="/var/tmp/mysqlbackup.%%UID%%.lock" # Path to lockfile. 28DEFAULT_LOCKFILE_EXPIRE=90000 # Seconds to expire lockfile. 29DEFAULT_SLAVE_STATUS_FILE="slave-status" # Filename for slave status. 30 31# CHECK TABLE only works for MyISAM, InnoDB, and (as of MySQL 5.0.16+) ARCHIVE 32# tables. All the `check-ready' engines should be indicated here separated by 33# space character. 34CHECK_READY_ENGINES="MyISAM InnoDB Archive" 35 36# For InnoDB tables, OPTIMIZE TABLE is mapped to ALTER TABLE, which rebuilds 37# the table to update index statistics and free unused space in the clustered 38# index. Beginning with MySQL 5.1.27, this is displayed in the output of 39# OPTIMIZE TABLE. 40OPTIMIZE_READY_ENGINES="MyISAM" 41 42# If a complete line of input is not read while reading the password within 43# given seconds - give up and continue with no password. 44ASK_PASSWORD_TIMEOUT=60 45 46# Configurable options ends here. 47 48OPTIONS="au:h:p:P:x:o:l:d:z:ZF:D:m:C:O:L:t:SvVHI" 49 50# These keys will be used while invoking mysql(1) program. 51: ${MYSQL_KEYS="--batch --silent"} 52 53# Outputs program usage instructions. 54usage() 55{ 56 me=`basename $0` 57 cat << EOF 58${me} meant to create MySQL databases backup on a periodic basis. 59 60Usage: ${me} [OPTIONS] [database [database [ ... ] ]] 61 62Options: 63 -a Dump all available databases except "information_schema" 64 and "performance_schema" databases. 65 -u user The MySQL user name to use when connecting to the server. 66 -h host Connect to host. 67 -p password Password to use when connecting to server. You should 68 note that specifying a password on the command line should 69 be considered insecure. See the section named "SECURITY". 70 -P filename|ask Read the clear password from the file. The file must 71 normally not be readable by "others" and must contain 72 exactly one line. Password will be prompted from the 73 command line if the special keyword "ask" specified here. 74 -x login-path MySQL login-path 75 -o option|no Additional mysqldump option. To specify multiple options 76 you should repeat this key for each mysqldump-option. The 77 default options are: ${DEFAULT_MYSQLDUMP_OPTIONS}. To not 78 use the default options force "no" option. 79 -l days Keep created backups for the specified number of the days. 80 The default is ${DEFAULT_ARCHIVE_DAYS} days. 81 -d directory Target directory to archive backups. 82 The default is ${DEFAULT_ARCHIVE_DIR} (will be created if 83 need). 84 -z xz|pbzip2|bzip2|gzip|7z|no 85 Compress dumps with specified program. Unless explicitly 86 set or "no" keyword used, the compressor is selected in 87 the next order: if xz(1) compressor found in \$PATH, it 88 will be used. If it not found, bzip2(1), gzip(1) and 89 7z(1) programs will be searched and used if found. If 90 none found, plain dumps will be created. 91 -Z Pipeline mysqldump to compressor program. By default, 92 mysqlbackup create plain SQL dump for whole database and 93 call compressor program afterwards. This help to make 94 MySQL locktime as small as possible. If long locktime for 95 huge databases is not a problem but filesystem space usage 96 is - use this key to save disk space. 97 -F mode Create files with given mode access permissions. 98 The default mode is ${DEFAULT_FILE_MODE}. 99 -D mode Create directories with given mode access permissions. 100 The default mode is ${DEFAULT_DIR_MODE}. 101 -m path|yes|no Save my.cnf config or specify it alternate path. 102 Default is: ${DEFAULT_SAVE_MYCNF}, ${DEFAULT_PATH_MYCNF}. 103 -C yes|no|keys Check tables before doing backup or use specified keys 104 for mysqlcheck(1) program while perfoming check. 105 Default: ${DEFAULT_CHECK_TABLES}, 106 keys: ${DEFAULT_MYSQLCHECK_OPTIONS}. 107 -O yes|no|keys Optimize tables before doing backup or use specified keys 108 for mysqlcheck(1) program while perfoming optimization. 109 Note that not all table engines supports table 110 optimization. Please refer to "OPTIMIZE TABLE Syntax" 111 paragraph of MySQL documentation. 112 Default: ${DEFAULT_OPTIMIZE_TABLES}, 113 keys: ${DEFAULT_MYSQLOPTIMIZE_OPTIONS}. 114 -L lockfile Alternate default path to lockfile (${DEFAULT_LOCKFILE}). 115 -t seconds Timeout in seconds to expire existing lockfile. 116 By default lockfile expires after ${DEFAULT_LOCKFILE_EXPIRE} 117 seconds. 118 -S Slave mode. Under this mode mysqlbackup assumes it is 119 running on MySQL slave. Then, prior to his work, 120 mysqlbackup stops the slave and saves "SHOW SLAVE STATUS" 121 output. After work is done, the slave is started up. The 122 output is saved to "${DEFAULT_SLAVE_STATUS_FILE}" file. 123 -I Ignore errors while dumping database. mysqlbackup will 124 not stop if mysqldump(1) running on any database will 125 return an error. Excludes -Z because there is no way to 126 detect which program has failed. 127 -v Be verbose. 128 -V Print version and exit. 129 -H Print this help and exit. 130 131Examples: 132 133 mysqlbackup Do nothing, print help. 134 mysqlbackup -av Verbose backup all the accessible databases on the 135 local MySQL server. 136 mysqlbackup -z no mysql Backup MySQL system database without output dump 137 being compressed. 138 mysqlbackup -a -P ask You are prompted for password to backup all the 139 databases available under current user. 140 mysqlbackup -aS Operate in slave mode. Save SLAVE STATUS for 141 further replication restore. 142 143Report bugs to <alexey@renatasystems.org> 144EOF 145 146 # Do not debug after usage() exit while cleanup(). 147 DEBUG="no" 148 149 if [ $# -ne 0 ]; then 150 exit $1 151 fi 152 exit 0 153} 154 155# Outputs program version and exit. 156version() 157{ 158 cat <<- EOF 159$(basename $0) ${VERSION} 160 161Written by Alexey Degtyarev 162EOF 163 164 # Do not debug cleanup() trap. 165 DEBUG="no" 166 167 exit 0 168} 169 170# Helper function to determine given argument either `yes' or `no'. 171# Return exit codes: 172# 0 - given argument in $1 is a `positive' answer 173# 1 - argument is a `negative' answer 174# 2 - neither `positive' nor `negative' argument given 175# 3 - wrong number of arguments given 176check_yesno() 177{ 178 # Accepts exactly one argument. 179 [ $# -eq 1 ] || return 3 180 181 case $1 in 182 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0;; 183 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1;; 184 *) return 2; 185 esac 186 187 # Should not be here ever. 188 return 0 189} 190 191# Check for locking: if lockfile found and it has not expired yet - give up 192# with error. Expire lockfile in other case. 193check_lockfile() 194{ 195 [ -f "${LOCKFILE}" ] || return 0 196 [ -r "${LOCKFILE}" ] || return 1 197 198 file_stat ${LOCKFILE} 199 200 # Check that lockfile mtime is older than LOCKFILE_EXPIRE seconds. If 201 # it does - expire it and set the new one. If lockfile is not expired - 202 # this could mean that another process still running, so we give up. 203 if [ ${st_mtime} -lt $((`date +%s`-${LOCKFILE_EXPIRE})) ]; then 204 if ! touch_lockfile; then 205 echo "Can not expire lockfile (${LOCKFILE})." 206 return 2 207 else 208 echo "Lockfile expired (unclean shutdown previous time?)" 209 return 0 210 fi 211 # Check that process id saved in LOCKFILE is a real process. 212 elif check_process ${LOCKFILE}; then 213 _pid=`cat ${LOCKFILE}` 214 echo "Lockfile found. Another process is still running?" 215 echo "Found mysqlbackup program running with pid ${_pid}." 216 return 1 217 # Process running with stored pid is not me. 218 elif ! touch_lockfile; then 219 echo "Can not release lockfile ($?)" 220 return 1 221 # Releasing lockfile. 222 else 223 echo "Lockfile released (unclead shutdown previous time?)" 224 return 0 225 fi 226 227 return 0 228} 229 230check_process() 231{ 232 [ $# -eq 1 ] || return 1 233 [ ! -z $1 ] || return 1 234 [ -f $1 -a -r $1 ] || return 1 235 236 # Take the process name and arguments from process tree. 237 case ${UNAME_s} in 238 Linux) 239 _pid="`cat $1`" 240 _procname="`pgrep -l -f mysqlbackup |grep ^${_pid}`" 241 ;; 242 FreeBSD) 243 _procname="`pgrep -j none -F $1 -l -f`" 244 ;; 245 *) 246 return 1 247 ;; 248 esac 249 250 if [ $? -ne 0 ]; then 251 return 3 252 fi 253 254 # 14659 /bin/sh /usr/local/bin/mysqlbackup -a 255 set -- ${_procname} 256 257 # Take the basename of interpreter running puppetd instance we got from 258 # process list. 259 _running_interpreter=${2##*/} 260 261 # And take the interpreter from the executable script. 262 read _interpreter < $3 263 case "${_interpreter}" in 264 # strip #! 265 \#!*) _interpreter=${_interpreter#\#!} 266 set -- ${_interpreter} 267 _interpreter=${_interpreter##*/} 268 ;; 269 *) _interpreter="/nonexistent" 270 ;; 271 esac 272 273 # Compare two interpretators from ps list and from executable script. 274 if [ ${_running_interpreter} != ${_interpreter} ]; then 275 return 4 276 fi 277 278 return 0 279} 280 281# Write down to lockfile current process id. 282touch_lockfile() 283{ 284 echo $$ >${LOCKFILE} || return $? 285 chmod 600 ${LOCKFILE} 286 return 0 287} 288 289# Release lockfile while normal program exit. 290release_lockfile() 291{ 292 debug "Releasing lockfile" 293 294 if [ ! -f ${LOCKFILE} ]; then 295 debug "Lockfile not found (${LOCKFILE})" 296 return 1 297 fi 298 if ! rm ${LOCKFILE}; then 299 _rc=$? 300 debug "Can't unlink lockfile (${LOCKFILE}): ${_rc}" 301 return ${_rc} 302 fi 303 304 return 0 305} 306 307# Set all the variables to their default values. 308set_defaults() 309{ 310 MYSQL_USER= 311 MYSQL_HOST= 312 MYSQL_PASSWORD= 313 MYSQL_PASSWORD_FILE= 314 MYSQL_LOGIN_PATH= 315 MYSQLDUMP_OPTIONS=${DEFAULT_MYSQLDUMP_OPTIONS} 316 MYSQLCHECK_OPTIONS=${DEFAULT_MYSQLCHECK_OPTIONS} 317 MYSQLOPTIMIZE_OPTIONS=${DEFAULT_MYSQLOPTIMIZE_OPTIONS} 318 MYSQL_AUTH_KEYS= 319 ARCHIVE_DAYS=${DEFAULT_ARCHIVE_DAYS} 320 ARCHIVE_DIR=${DEFAULT_ARCHIVE_DIR} 321 DUMP_ALL="no" 322 DEBUG="no" 323 DUMP_DATABASE= 324 FILE_MODE=${DEFAULT_FILE_MODE} 325 DIR_MODE=${DEFAULT_DIR_MODE} 326 SAVE_MYCNF=${DEFAULT_SAVE_MYCNF} 327 PATH_MYCNF=${DEFAULT_PATH_MYCNF} 328 CHECK_TABLES=${DEFAULT_CHECK_TABLES} 329 OPTIMIZE_TABLES=${DEFAULT_OPTIMIZE_TABLES} 330 ONLY_PRINT_VERSION="no" 331 ONLY_PRINT_HELP="no" 332 CLEANUP_BACKUP_DIR="yes" 333 CLEANUP_LOCKFILE="no" 334 SLAVE_MODE="no" 335 SLAVE_STATUS_FILE=${DEFAULT_SLAVE_STATUS_FILE} 336 SLAVE_STOPPED="no" # Did we stopped slave? 337 SLAVE_STOP="no" # Do we need to stop slave? 338 IGNORE_ERRORS="no" # Don't ignore errors, but rather exit on error. 339 PIPELINE_COMPRESSOR="no" # Pipeline mysqldump to compressor? 340 341 : ${DEBUG_IDENT="yes"} 342 343 COMPRESSOR= 344 local c e 345 for c in xz pbzip2 bzip2 gzip 7z; do 346 e=`which $c` 347 if [ -x "$e" ]; then 348 COMPRESSOR=${e##*/} 349 break 350 fi 351 done 352 353 # Set MySQL installation prefix. 354 if [ -z "${MYSQL_PREFIX}" ]; then 355 e=`which mysql` 356 if [ -x "$e" ]; then 357 MYSQL_PREFIX=${e%/bin/mysql} 358 fi 359 fi 360 361 # Shortcut. 362 UNAME_s=`uname -s` 363 364 # Configure MySQL stuff. 365 MYSQL="${MYSQL_PREFIX}/bin/mysql" 366 MYSQLDUMP="${MYSQL_PREFIX}/bin/mysqldump" 367 MYSQLCHECK="${MYSQL_PREFIX}/bin/mysqlcheck" 368 MYSQLOPTIMIZE="${MYSQL_PREFIX}/bin/mysqlcheck" 369 370 _id=`id -u || echo 65534` 371 LOCKFILE=`echo -n ${DEFAULT_LOCKFILE} |sed -E "s#%%UID%%#${_id}#g"` 372 LOCKFILE_EXPIRE=${DEFAULT_LOCKFILE_EXPIRE} 373 374 return 0 375} 376 377# Wrap stat(1) for different OSes. 378file_stat() 379{ 380 if [ $# -ne 1 ]; then 381 return 1 382 fi 383 384 [ ! -z "${UNAME_s}" ] || UNAME_s=`uname -s` 385 386 case ${UNAME_s} in 387 Linux) 388 eval `stat --printf st_mode=%a $1` 389 eval `stat --printf st_mtime=%Y $1` 390 ;; 391 FreeBSD) 392 eval `stat -s $1` 393 ;; 394 *) 395 debug "file_stat(): operating system not supported: ${UNAME_s}"; 396 exit 1 397 ;; 398 esac 399 400 # Determine shell we are running on: Linux Ubuntu has /bin/sh linked to 401 # dash, other Linuxes link it to bash. Try to avoid chaos. 402 SHELL=/bin/sh 403 if [ -h /bin/sh ]; then 404 local t 405 t=`readlink /bin/sh` 406 SHELL=${t##*/} 407 fi 408 409 return 0 410} 411 412# Parse given options using builtin getopts function. 413parse_options() 414{ 415 while getopts ${OPTIONS} option $@; do 416 case ${option} in 417 u) MYSQL_USER=${OPTARG};; 418 h) MYSQL_HOST=${OPTARG};; 419 p) MYSQL_PASSWORD=${OPTARG};; 420 P) MYSQL_PASSWORD_FILE=${OPTARG};; 421 x) MYSQL_LOGIN_PATH=${OPTARG};; 422 l) ARCHIVE_DAYS=${OPTARG};; 423 d) ARCHIVE_DIR=${OPTARG};; 424 a) DUMP_ALL="yes";; 425 F) FILE_MODE=${OPTARG};; 426 D) DIR_MODE=${OPTARG};; 427 C) check_yesno "${OPTARG}"; 428 case $? in 429 0) CHECK_TABLES="yes";; 430 1) CHECK_TABLES="no";; 431 *) MYSQLCHECK_OPTIONS=${OPTARG};; 432 esac;; 433 O) check_yesno "${OPTARG}"; 434 case $? in 435 0) OPTIMIZE_TABLES="yes";; 436 1) OPTIMIZE_TABLES="no";; 437 *) MYSQLOPTIMIZE_OPTIONS=${OPTARG};; 438 esac;; 439 m) check_yesno "${OPTARG}"; 440 case $? in 441 0) SAVE_MYCNF="yes";; 442 1) SAVE_MYCNF="no";; 443 *) PATH_MYCNF=${OPTARG};; 444 esac;; 445 z) case ${OPTARG} in 446 xz|7z|gzip|bzip2|pbzip2) 447 if ! which ${OPTARG} >/dev/null 2>/dev/null; then 448 echo "${OPTARG} unavailable on this system" 449 return 1 450 fi 451 COMPRESSOR=${OPTARG} 452 ;; 453 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) 454 COMPRESSOR=no;; 455 *) echo "Wrong arg for -z option"; return 1;; 456 esac;; 457 Z) PIPELINE_COMPRESSOR="yes";; 458 o) case ${OPTARG} in 459 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) 460 MYSQLDUMP_OPTIONS=;; 461 -*) 462 MYSQLDUMP_OPTIONS="${MYSQLDUMP_OPTIONS} ${OPTARG}";; 463 *) echo "Wrong arg for -o option: ${OPTARG}"; 464 return 1;; 465 esac;; 466 L) LOCKFILE=${OPTARG};; 467 t) LOCKFILE_EXPIRE=${OPTARG};; 468 S) SLAVE_MODE="yes";; 469 v) DEBUG="yes";; 470 V) ONLY_PRINT_VERSION="yes";; 471 H) ONLY_PRINT_HELP="yes";; 472 I) IGNORE_ERRORS="yes";; 473 ?) exit 1;; 474 esac 475 done 476 477 shift $(($OPTIND-1)) 478 479 # Take the database name(s) if given in command line. 480 DUMP_DATABASE=$@ 481 482 return 0 483} 484 485# This will check that any options set are not conflicts with each others and 486# all the necessary options are set. 487check_options() 488{ 489 # Was -v or -h keys specified? 490 check_yesno ${ONLY_PRINT_VERSION} && version 491 check_yesno ${ONLY_PRINT_HELP} && usage 492 493 # Either -a OR explicit database(s) name(s) must be specified, not 494 # both together. 495 if check_yesno "${DUMP_ALL}"; then 496 if [ ! -z "${DUMP_DATABASE}" ]; then 497 _str="Flag '-a' conflicts with explicit database" 498 _str="${_str} name given." 499 echo ${_str} 500 return 1 501 fi 502 else 503 if [ -z "${DUMP_DATABASE}" ]; then 504 _str="Specify database name to dump or set '-a'" 505 _str="${_str} to dump all the available databases." 506 echo ${_str} 507 return 1 508 fi 509 fi 510 511 # Check if all binaries exists and are executables. 512 binaries="${MYSQL} ${MYSQLDUMP} ${MYSQLCHECK} ${MYSQLOPTIMIZE}" 513 for binary in ${binaries}; do 514 if [ ! -x "${binary}" ]; then 515 _str="Binary \`${binary}' not found, " 516 _str="${_str} check your MYSQL_PREFIX path" 517 echo ${_str} 518 return 1 519 fi 520 done 521 522 # Can't specify both login path and password/password file/user/host 523 if [ ! -z "${MYSQL_LOGIN_PATH}" ]; then 524 if [ ! -z "${MYSQL_PASSWORD}" -o \ 525 ! -z "${MYSQL_PASSWORD_FILE}" -o \ 526 ! -z "${MYSQL_HOST}" -o \ 527 ! -z "${MYSQL_USER}" ]; then 528 echo "The -x and -P -p -u -h options are mutually exclusive" 529 return 1 530 fi 531 fi 532 533 534 # Can't specify both password and password file. 535 if [ ! -z "${MYSQL_PASSWORD}" -a \ 536 ! -z "${MYSQL_PASSWORD_FILE}" ]; then 537 echo "The -p and -P options are mutually exclusive" 538 return 1 539 fi 540 541 # Check for "ask" keyword and prompt password if keyword given. 542 if echo ${MYSQL_PASSWORD_FILE} |egrep -q "[Aa][Ss][Kk]"; then 543 # Preserve all the current settings for the terminal. 544 _stty=$(stty -g) 545 546 # Do not echo back every character typed while reading the 547 # password. 548 stty -echo 549 550 # Dash sheel doesn't support -t key for 'read'. 551 local k 552 case ${SHELL} in 553 bash|sh) 554 k=-t ${ASK_PASSWORD_TIMEOUT} 555 ;; 556 *) k= 557 ;; 558 esac 559 560 # Read the password. 561 read -p "Password: " $k MYSQL_PASSWORD && \ 562 echo || \ 563 echo "time out!" 564 565 # Restore terminal settings. 566 stty ${_stty} 567 568 # Flush password file variable. 569 MYSQL_PASSWORD_FILE= 570 fi 571 572 # Login path given, use it 573 if [ ! -z "${MYSQL_LOGIN_PATH}" ]; then 574 MYSQL_AUTH_KEYS="--login-path=${MYSQL_LOGIN_PATH}" 575 fi 576 577 # Password file given, let's check if it is ok. 578 if [ ! -z "${MYSQL_PASSWORD_FILE}" ]; then 579 580 # Check if item is file and is accessable. 581 if [ ! -f "${MYSQL_PASSWORD_FILE}" -o \ 582 ! -r "${MYSQL_PASSWORD_FILE}" ] 583 then 584 echo "Can't read password file: ${MYSQL_PASSWORD_FILE}" 585 return 1 586 fi 587 588 # Check permissions: must not be world readable. 589 # First take the file permissions mode as a shell variable. 590 file_stat ${MYSQL_PASSWORD_FILE} 591 592 # Yeld the last digit and check if `r' bit is set. 593 if [ $((${st_mode##${st_mode%%?}} & 4)) -ne 0 ]; then 594 echo "Password file is readable by others" 595 return 1 596 fi 597 598 # Password file must contain exactly one line (password). 599 _line_count=`wc -l ${MYSQL_PASSWORD_FILE} |awk '{print $1}' |\ 600 tr -d ' '` 601 if [ ${_line_count} -ne 1 ]; then 602 echo "Password file MUST contain exactly one line" 603 return 1 604 fi 605 606 # Seems that the password file is ok, so read it. 607 MYSQL_PASSWORD=`cat ${MYSQL_PASSWORD_FILE}` 608 fi 609 610 # Keep entered password in secure: use MySQL option 611 # --defaults-extra-file to read MySQL client configuration. 612 if [ ! -z "${MYSQL_PASSWORD}" ]; then 613 614 # This will create temporary file only accessable by creator. 615 MYSQL_EXTRA_FILE=`mktemp -t mysqlbackup.XXXXXX` || \ 616 { echo "Cannot create temporary file"; return 1; } 617 618 # Write the password. Handling carefully with Ubuntu's Linux 619 # /bin/sh (actually, dash) - it doesn't have -e option. 620 cat - >>${MYSQL_EXTRA_FILE} <<- EOF 621 [client] 622 password=${MYSQL_PASSWORD} 623EOF 624 625 MYSQL_AUTH_KEYS="${MYSQL_AUTH_KEYS} \ 626 --defaults-extra-file=${MYSQL_EXTRA_FILE}" 627 fi 628 629 # Check the lockfile timeout: should be greater than zero. 630 [ "${LOCKFILE_EXPIRE}" -gt 0 ] >/dev/null 2>&1 ||\ 631 { echo "Timeout value must be numeric number of seconds" && 632 return 1; } 633 634 # "-z no -Z" — conflicting combination. 635 if [ "${COMPRESSOR}" = "no" ] && \ 636 check_yesno "${PIPELINE_COMPRESSOR}" ; then 637 echo "You didn't set compressor — nowhere to pipeline" 638 return 1 639 fi 640 641 # There is no way to detect which command force error. 642 if check_yesno "${PIPELINE_COMPRESSOR}" && \ 643 check_yesno "${IGNORE_ERRORS}" ; then 644 echo "Meaningless options: -I in conjunction with -Z." 645 return 1 646 fi 647 648 # Seems that all is ok. 649 return 0 650} 651 652# Output $@ if debug flag is enabled. When DEBUG_IDENT is "yes", append 653# identification information: timestamp, selfname, pid. 654debug() 655{ 656 check_yesno ${DEBUG} || return 0 657 658 msgprefix= 659 if check_yesno ${DEBUG_IDENT}; then 660 msgprefix="`date +%c` `basename $0` [$$]: " 661 fi 662 663 echo "${msgprefix}$@" 664 665 return 0 666} 667 668# This will raise error end exit with custom error code if given as $1 669# argument. When DEBUG_IDENT is "yes", append identification information: 670# timestamp, selfname, pid. 671error() 672{ 673 check_yesno ${DEBUG} || debug $@ 674 675 msgprefix= 676 if check_yesno ${DEBUG_IDENT}; then 677 msgprefix="`date +%c` `basename $0` [$$]: " 678 fi 679 680 echo "${msgprefix}$@" 681 682 [ ! -z "$1" ] && code=1 || code=$? 683 684 exit ${code} 685} 686 687# Clean up before exit: release the lockfile, remove backup directory if 688# CLEANUP_BACKUP_DIR set, clean up extra defaults options if exists. Used by 689# traps while emergency or normal exit. 690cleanup() 691{ 692 debug "Cleaning up" 693 694 # Start slave if it was stopped by us. 695 if check_yesno ${SLAVE_STOPPED}; then 696 ${MYSQL} ${MYSQL_AUTH_KEYS} -e "START SLAVE;" 697 if [ $? -ne 0 ]; then 698 error "Can't start slave" 699 fi 700 debug "Slave started" 701 SLAVE_STOPPED="no" 702 fi 703 704 # Remove extra defaults options. 705 if [ -n "${MYSQL_EXTRA_FILE}" -a -e "${MYSQL_EXTRA_FILE}" ]; then 706 debug "Removing extra options (${MYSQL_EXTRA_FILE})" 707 rm -f ${MYSQL_EXTRA_FILE} || \ 708 debug "Problem removing extra options" 709 fi 710 711 # Remove backup directory if it was NOT completely created. 712 if check_yesno ${CLEANUP_BACKUP_DIR} ; then 713 if [ -d ${BACKUP_DIR} ]; then 714 debug "Removing backup directory" 715 rm -r ${BACKUP_DIR} 2>/dev/null 716 fi 717 fi 718 719 # Release lockfile only if it was created by THIS process. 720 if check_yesno ${CLEANUP_LOCKFILE}; then 721 [ -f ${LOCKFILE} ] && ( release_lockfile || \ 722 debug "Problem removing lockfile" ) 723 fi 724 725 # Release `exit' trap to avoid cleanup loop. 726 trap - EXIT 727 728 debug "Exit" 729 exit 0 730} 731 732# Check that MySQL server is available with given credentials. 733check_mysql_connect() 734{ 735 local _mysql_out 736 737 # Check if we can reach MySQL server. 738 debug "Pinging MySQL..." 739 if ! ${MYSQL} ${MYSQL_AUTH_KEYS} -e "QUIT"; then 740 error "MySQL connect error" 741 fi 742 debug "MySQL ok" 743 744 # Are we run on slave? 745 if check_yesno ${SLAVE_MODE}; then 746 debug "Slave mode requested, checking slave" 747 748 _mysql_out=`${MYSQL} ${MYSQL_AUTH_KEYS} \ 749 --batch -e "SHOW SLAVE STATUS\G" 2>&1 |\ 750 grep "Slave_IO_Running:" |\ 751 awk '{print $2}'` 752 if [ $? -ne 0 ]; then 753 error "MySQL error: ${_mysql_out}" 754 fi 755 756 # Empty output means no slave was configured. 757 if [ -z "${_mysql_out}" ]; then 758 debug "Slave not configured" 759 SLAVE_MODE="no" 760 elif [ "${_mysql_out}" != "Yes" ]; then 761 debug "Slave IO Running: No" 762 else 763 # Slave IO running, request to stop slave. 764 SLAVE_STOP="yes" 765 debug "Slave IO Running: Yes" 766 fi 767 fi 768 769 return 0 770} 771 772# This function checks either do backup my.cnf configuration file or not. 773check_mycnf() 774{ 775 debug "Checking for my.cnf backup" 776 777 # Normalaize variable value. 778 check_yesno ${SAVE_MYCNF} 779 case $? in 780 0) SAVE_MYCNF="yes";; 781 1) SAVE_MYCNF="no";; 782 *) SAVE_MYCNF="no";; 783 esac 784 785 if check_yesno ${SAVE_MYCNF}; then 786 787 # Retrieve the data directory configuration value. 788 datadir=`${MYSQL} ${MYSQL_AUTH_KEYS} ${MYSQL_KEYS} \ 789 -e "SHOW VARIABLES LIKE '%datadir%';" |\ 790 awk '{print $2}'` 791 792 # Replace macros if given in pathname. 793 PATH_MYCNF=`echo ${PATH_MYCNF} |\ 794 sed -e "s#%%DATADIR%%#${datadir}#"` 795 796 # The last check - if file is readable, will backup it. 797 if [ -f ${PATH_MYCNF} ]; then 798 debug "Will backup my.cnf from: ${PATH_MYCNF}" 799 else 800 debug "Will not backup my.cnf (no access)" 801 SAVE_MYCNF="no" 802 fi 803 else 804 debug "Will not backup my.cnf" 805 fi 806 807 return 0 808} 809 810# Set and create directory where to save backups based on date or backup 811# number. 812create_target_dir() 813{ 814 debug "Reading datadir from MySQL" 815 datadir=`${MYSQL} ${MYSQL_AUTH_KEYS} ${MYSQL_KEYS} \ 816 -e "SHOW VARIABLES LIKE '%datadir%';" |\ 817 awk '{print $2}'` 818 debug "MySQL says: ${datadir}" 819 datadir=`basename ${datadir}` 820 821 # We will preserve previous backups untouched. 822 date_suffix=`date +%Y%m%d` 823 max_oneday_backups=20 824 825 # Today's backup already done, take the next directory name. 826 if [ -d "${ARCHIVE_DIR}/${datadir}/${date_suffix}" ]; then 827 i=1 828 while [ $i -le ${max_oneday_backups} ]; do 829 _d="${ARCHIVE_DIR}/${datadir}/${date_suffix}.$i" 830 831 # Take the first unused name. 832 if [ ! -d ${_d} ]; then 833 date_suffix="${date_suffix}.$i" 834 break 835 fi 836 i=$(($i+1)) 837 done 838 839 # Preserve any error with directory naming. 840 [ $i -ge ${max_oneday_backups} ] && ( \ 841 _str="Too much similar backups found," && \ 842 _str="${_str} consider remove previous backups." && \ 843 error ${_str} ) 844 fi 845 846 local _dirs="${ARCHIVE_DIR} \ 847 ${ARCHIVE_DIR}/${datadir} \ 848 ${ARCHIVE_DIR}/${datadir}/${date_suffix}" 849 850 # Create directories with given directory mode permissions. 851 for dir in ${_dirs}; do 852 [ -d ${dir} ] && continue 853 debug "Creating directory: ${dir}" 854 if ! mkdir -m ${DIR_MODE} ${dir}; then 855 error "Can't create directory: ${dir}" 856 return 1 857 fi 858 done 859 860 BACKUP_DIR="${ARCHIVE_DIR}/${datadir}/${date_suffix}" 861 862 return 0 863} 864 865# Remove older backups. 866remove_old_backups() 867{ 868 debug "Removing old backups" 869 870 local d=$((${ARCHIVE_DAYS} + 1)) 871 872 # Different OS'es handle dates differently. 873 local _date_flags 874 local _uname_s=`uname -s` 875 case ${_uname_s} in 876 Linux) 877 _date_flags="--date=\$d day ago" 878 ;; 879 FreeBSD) 880 _date_flags="-v-\${d}d" 881 ;; 882 *) 883 debug "Not yet supported for $_uname_s"; 884 return 1 885 ;; 886 esac 887 888 while [ ${d} -lt $((${ARCHIVE_DAYS} + 30)) ]; do 889 _date_flags_e=`eval "echo ${_date_flags}"` 890 _purge_date=`date "${_date_flags_e}" +%Y%m%d` 891 find ${BACKUP_DIR}/../ \ 892 -maxdepth 1 \ 893 -mindepth 1 \ 894 -name "${_purge_date}*" \ 895 -type d \ 896 -exec rm -r {} \; 897 d=$((${d} + 1)) 898 done 899 900 return 0 901} 902 903# Stop slave and save it's status if "slave mode" requested. 904handle_slave_mode() 905{ 906 if ! check_yesno ${SLAVE_MODE}; then 907 return 0 908 fi 909 910 if [ ! -e "${BACKUP_DIR}" ]; then 911 debug "Backup dir does not exist?" 912 return 1 913 fi 914 915 if ! check_yesno ${SLAVE_STOP}; then 916 debug "Don't need to stop slave" 917 else 918 ${MYSQL} ${MYSQL_AUTH_KEYS} -e "STOP SLAVE;" 919 if [ $? -ne 0 ]; then 920 error "Can't stop slave" 921 fi 922 debug "Slave stopped" 923 SLAVE_STOPPED="yes" 924 fi 925 926 ${MYSQL} ${MYSQL_AUTH_KEYS} --batch -e "SHOW SLAVE STATUS\G" \ 927 > ${BACKUP_DIR}/${SLAVE_STATUS_FILE} 928 if [ $? -ne 0 ]; then 929 debug "Can't get slave status" 930 fi 931 debug "Slave status saved: ${BACKUP_DIR}/${SLAVE_STATUS_FILE}" 932 933 return 0 934} 935 936# Create databases-to-backup list. 937get_databases() 938{ 939 BACKUP_DATABASES= 940 941 # -a key given? 942 if check_yesno ${DUMP_ALL}; then 943 debug "Reading available databases" 944 BACKUP_DATABASES=`${MYSQL} ${MYSQL_AUTH_KEYS} ${MYSQL_KEYS} \ 945 -e "SHOW DATABASES;" |\ 946 egrep -v "^(information_schema|performance_schema)$"` 947 [ $? -eq 0 ] || error "Can't get available databases" 948 949 else 950 # Select databases by matching pattern. 951 for database in ${DUMP_DATABASE}; do 952 db=`${MYSQL} ${MYSQL_AUTH_KEYS} ${MYSQL_KEYS} \ 953 -e "SHOW DATABASES LIKE '${database}';"` 954 [ $? -eq 0 ] || error "Can't use database: ${database}" 955 BACKUP_DATABASES="${BACKUP_DATABASES} ${db}" 956 done 957 fi 958 959 count=`echo ${BACKUP_DATABASES} |wc -w |tr -d ' '` 960 [ -z "${count}" ] && count=0 961 debug "Got ${count} database(s)" 962 963 if [ ${count} -le 0 ]; then 964 debug "Nothing to do, exiting" 965 exit $? 966 fi 967 968 return 0 969} 970 971# Set ${OUTPUT_SUFFIX} to suffix according to selected comressor program. 972# Arguments: 973# None. 974# Return: 975# 0: OK 976# >0: NOK 977set_output_suffix() 978{ 979 # Select correct compressor program and suffix. 980 case ${COMPRESSOR} in 981 xz) 982 SUFFIX="sql.xz" 983 ;; 984 gzip) 985 SUFFIX="sql.gz" 986 ;; 987 bzip2|pbzip2) 988 SUFFIX="sql.bz2" 989 ;; 990 7z) 991 SUFFIX="sql.7z" 992 ;; 993 no) 994 SUFFIX="sql" 995 ;; 996 *) 997 return 1 998 esac 999 1000 return 0 1001} 1002 1003# Dump database and compress it inline by pipelining compressor. 1004# Arguments: 1005# $1: database to dump 1006# $2: outputfile NOT including compressor suffix 1007# Return: 1008# 0: OK 1009# >0: NOK 1010dump_pipe_compress() 1011{ 1012 [ $# -eq 2 ] || return 1 1013 1014 _db=$1 1015 _out_file=$2 1016 1017 set_output_suffix || return 1 1018 1019 # If compressor is not set to valid compressor program, abort to do 1020 # pipeline. 1021 if [ "${SUFFIX}" = "sql" ]; then 1022 error "Nowere to pipeline" 1023 fi 1024 1025 debug "${_db}: creating compressed SQL-dump" 1026 1027 case ${COMPRESSOR} in 1028 xz|pbzip2|bzip2|gzip) 1029 ${MYSQLDUMP} \ 1030 ${MYSQL_AUTH_KEYS} \ 1031 ${MYSQLDUMP_OPTIONS} \ 1032 ${_db} \ 1033 | ${COMPRESSOR} > ${_out_file}.${SUFFIX} \ 1034 || return 1 1035 ;; 1036 7z) 1037 ${MYSQLDUMP} \ 1038 ${MYSQL_AUTH_KEYS} \ 1039 ${MYSQLDUMP_OPTIONS} \ 1040 ${_db} \ 1041 | ${COMPRESSOR} -bd a -si \ 1042 ${_out_file}.${SUFFIX} \ 1043 >/dev/null \ 1044 || return 1 1045 ;; 1046 esac 1047 1048 chmod ${FILE_MODE} ${_out_file}.${SUFFIX} || return 1 1049 1050 debug "${_db}: dumped and compressed" 1051 1052 return 0 1053} 1054 1055# Arguments: 1056# $1: database to dump 1057# $2: filename where to dump 1058dump_database() 1059{ 1060 [ $# -eq 2 ] || return 1 1061 1062 _db=$1 1063 _out_file=$2 1064 1065 # Invoke mysqldump(1) for database 1066 debug "${db}: dumping" 1067 ${MYSQLDUMP} \ 1068 ${MYSQL_AUTH_KEYS} \ 1069 ${MYSQLDUMP_OPTIONS} \ 1070 ${db} > ${_out_file} 1071 _rc=$? 1072 1073 if [ ${_rc} -ne 0 ]; then 1074 # Are we forced to ignore any errors? 1075 if ! check_yesno ${IGNORE_ERRORS} ; then 1076 debug "${db}: error occurred (rc=${_rc})" 1077 return ${_rc} 1078 fi 1079 debug "${db}: error occurred (rc=${_rc}), ignored" 1080 else 1081 debug "${db}: dumped" 1082 fi 1083 1084 return 0 1085} 1086 1087# Compress SQL dump using given compressor. 1088# Arguments: 1089# $1: filename to compress 1090# Return: 1091# 0: ok 1092# not 0: error occurred 1093compress_dump() 1094{ 1095 [ $# -eq 1 ] || return 1 1096 1097 _out_file=$1 1098 1099 set_output_suffix || return 1 1100 1101 # Nothing to do? 1102 if [ "${SUFFIX}" = "sql" ]; then 1103 mv ${_out_file} ${_out_file}.sql 1104 chmod ${FILE_MODE} ${_out_file}.sql 1105 return 0 1106 fi 1107 1108 debug "${db}: compressing" 1109 case ${COMPRESSOR} in 1110 xz|pbzip2|bzip2|gzip) 1111 ${COMPRESSOR} --stdout ${_out_file} \ 1112 > ${_out_file}.${SUFFIX} || \ 1113 return $? 1114 ;; 1115 7z) 1116 # If verbose mode is inactive - volume 1117 # down 7z compressor. I can't find any 1118 # tunes to disable all the output from 1119 # this compressor. 1120 if ! check_yesno ${DEBUG} ; then 1121 ${COMPRESSOR} -bd a \ 1122 ${_out_file}.${SUFFIX} \ 1123 ${_out_file} 1>/dev/null || \ 1124 return $? 1125 else 1126 ${COMPRESSOR} -bd a \ 1127 ${_out_file}.${SUFFIX} \ 1128 ${_out_file} || \ 1129 return $? 1130 fi 1131 ;; 1132 esac 1133 debug "${db}: compressed" 1134 1135 rm ${_out_file} 1136 1137 chmod ${FILE_MODE} ${_out_file}.${SUFFIX} || return 1 1138 1139 return 0 1140} 1141 1142# Backup databases. 1143do_backup() 1144{ 1145 [ ! -z "${BACKUP_DATABASES}" ] || return 0 1146 1147 if check_yesno "${PIPELINE_COMPRESSOR}"; then 1148 debug "Compression will use pipeline to ${COMPRESSOR}" 1149 elif [ "${COMPRESSOR}" != "no" ]; then 1150 debug "Using ${COMPRESSOR} as compressor" 1151 else 1152 debug "Not using compresion" 1153 fi 1154 1155 _check_tables="no" 1156 if check_yesno ${CHECK_TABLES}; then 1157 _check_tables="yes" 1158 fi 1159 _optimize_tables="no" 1160 if check_yesno ${OPTIMIZE_TABLES}; then 1161 _optimize_tables="yes" 1162 fi 1163 1164 db_count=0 1165 db_total=`echo ${BACKUP_DATABASES} |wc -w` 1166 for db in ${BACKUP_DATABASES}; do 1167 db_count=$((${db_count}+1)) 1168 db_left=$((${db_total}-${db_count})) 1169 debug "${db}: doing database backup (${db_left} left)" 1170 1171 # Get tables in database. This is need to check if table engine 1172 # allows checking or optimizing. 1173 tables_total=`${MYSQL} \ 1174 ${MYSQL_AUTH_KEYS} \ 1175 ${MYSQL_KEYS} \ 1176 ${db} \ 1177 -e "SHOW TABLES;"` 1178 1179 # No tables found, nothing to do? 1180 tables_total_c=`echo ${tables_total} |wc -w |tr -d ' '` 1181 if [ ${tables_total_c} -le 0 ]; then 1182 debug "${db}: no tables found, skipping" 1183 continue 1184 fi 1185 debug "${db}: ${tables_total_c} table(s) found in total" 1186 1187 # Check tables. 1188 if [ ${_check_tables} = "yes" ]; then 1189 1190 # Not all tables are ready to check or optimize. We 1191 # should trim off such tables from the all tables list. 1192 local tables_checkready="" 1193 1194 for engine in ${CHECK_READY_ENGINES}; do 1195 1196 # Get tables with one of checkready-engine 1197 _tables=`${MYSQL} \ 1198 ${MYSQL_AUTH_KEYS} \ 1199 ${MYSQL_KEYS} \ 1200 ${db} \ 1201 -e "SHOW TABLE STATUS WHERE Engine LIKE '${engine}';" |\ 1202 awk '{print $1}'` 1203 1204 # Try another engine if no tables with such engine found. 1205 local _tables_c=`echo ${_tables} |wc -w |tr -d ' '` 1206 [ ${_tables_c} -eq 0 ] && continue; 1207 debug "${db}: check: ${_tables_c} ${engine}-engine table(s)" 1208 1209 tables_checkready="${tables_checkready} ${_tables}" 1210 done 1211 1212 tables_checkready_c=`echo ${tables_checkready} |wc -w |tr -d ' '` 1213 debug "${db}: check: will try to check ${tables_checkready_c} table(s)" 1214 1215 # Check tables filtered by engine. 1216 debug "${db}: check: checking tables" 1217 c=0 1218 for table in ${tables_checkready}; do 1219 if ! ${MYSQLCHECK} \ 1220 ${MYSQL_AUTH_KEYS} \ 1221 ${MYSQLCHECK_OPTIONS} \ 1222 ${db} ${table}; then 1223 debug "${db}: check: ${table} check fail" 1224 continue 1225 fi 1226 c=$((c+1)) 1227 #debug "${db}: check: ${table} check ok" 1228 done 1229 debug "${db}: check: checked ${c} table(s)" 1230 fi 1231 1232 # Optimize tables. 1233 if [ ${_optimize_tables} = "yes" ]; then 1234 1235 # Collect OPTIMIZE-ready tables names into 1236 # tables_optimizeready array. 1237 local tables_optimizeready="" 1238 1239 # Foreach engines that has known compatibility with 1240 # OPTIMIZE. 1241 for engine in ${OPTIMIZE_READY_ENGINES}; do 1242 1243 # Get tables with one of the optimize-ready 1244 # engine. 1245 _tables=`${MYSQL} \ 1246 ${MYSQL_AUTH_KEYS} \ 1247 ${MYSQL_KEYS} \ 1248 ${db} \ 1249 -e "SHOW TABLE STATUS WHERE Engine LIKE '${engine}';" |\ 1250 awk '{print $1}'` 1251 1252 # Try another engine if no tables with such 1253 # engine found. 1254 _tables_c=`echo ${_tables} |wc -w |tr -d ' '` 1255 [ ${_tables_c} -eq 0 ] && continue; 1256 debug "${db}: optimize: ${_tables_c} ${engine}-engine table(s)" 1257 1258 # Catenate arrays with previous search results 1259 # (if was). 1260 tables_optimizeready="${tables_optimizeready} ${_tables}" 1261 done 1262 1263 tables_optimizeready_c=`echo ${tables_optimizeready} |wc -w |tr -d ' '` 1264 debug "${db}: optimize: will try to optimize ${tables_optimizeready_c} table(s)" 1265 1266 # Check tables filtered by engine. 1267 c=0 1268 for table in ${tables_optimizeready}; do 1269 if ! ${MYSQLOPTIMIZE} \ 1270 ${MYSQL_AUTH_KEYS} \ 1271 ${MYSQLOPTIMIZE_OPTIONS} \ 1272 ${db} ${table}; then 1273 debug "${db}: optimize: table ${table} optimizing failed" 1274 continue 1275 fi 1276 c=$((c+1)) 1277 #debug "${db}: ${table} optimize ok" 1278 done 1279 debug "${db}: optimize: optimized ${c} table(s)" 1280 fi 1281 1282 local _out_file=${BACKUP_DIR}/${db} 1283 1284 if check_yesno ${PIPELINE_COMPRESSOR}; then 1285 dump_pipe_compress "${db}" "${_out_file}" || return 1 1286 else 1287 dump_database "${db}" "${_out_file}" || return 1 1288 compress_dump "${_out_file}" || return 1 1289 fi 1290 done 1291 1292 # Backup my.cnf 1293 if check_yesno ${SAVE_MYCNF}; then 1294 mycnf=`basename ${PATH_MYCNF}` 1295 debug "Backup my.cnf: ${PATH_MYCNF} -> ${BACKUP_DIR}/${mycnf}" 1296 cp ${PATH_MYCNF} ${BACKUP_DIR}/${mycnf} 1297 chmod ${FILE_MODE} ${BACKUP_DIR}/${mycnf} 1298 fi 1299 1300 # Set flag "do not remove backups while doing cleanup()" 1301 CLEANUP_BACKUP_DIR="no" 1302 1303 return 0 1304} 1305 1306# Remember the time of successful completion. 1307set_done_flag() 1308{ 1309 date +%s > ${BACKUP_DIR}/.done 1310 1311 return $? 1312} 1313 1314analyze_done_flag() 1315{ 1316 # Retrieve the data directory configuration value. 1317 local datadir=`${MYSQL} ${MYSQL_AUTH_KEYS} ${MYSQL_KEYS} \ 1318 -e "SHOW VARIABLES LIKE '%datadir%';" |\ 1319 awk '{print $2}'` 1320 1321 DATADIR= 1322} 1323 1324# The main cycle. 1325main() 1326{ 1327 debug "Ready (version ${VERSION}, rev${REVISION% $})" 1328 check_mysql_connect || return $? 1329 create_target_dir || return $? 1330 handle_slave_mode || return $? 1331 check_mycnf || return $? 1332 get_databases || return $? 1333 do_backup || return $? 1334 remove_old_backups || return $? 1335 set_done_flag || return $? 1336 debug "Done" 1337 1338 return 0 1339} 1340 1341# Make sure we find utilities from the base system 1342export PATH=${PATH}:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin 1343 1344# Set LC_ALL in order to avoid problems with character ranges like [A-Z]. 1345export LC_ALL=C 1346 1347set_defaults 1348 1349[ $# -eq 0 ] && usage 1350 1351parse_options $@ || exit 255 1352 1353check_options || exit 255 1354 1355trap cleanup EXIT 1356 1357check_lockfile && touch_lockfile || exit 1358 1359# From here we assume that the lockfile either didn't exist before or was 1360# re-created due timeout, so we should remove it while program will cleanup at 1361# exit. 1362CLEANUP_LOCKFILE="yes" 1363 1364# Define authorization credentials if they are set. 1365[ ! -z "${MYSQL_USER}" ] && \ 1366 MYSQL_AUTH_KEYS="${MYSQL_AUTH_KEYS} --user=${MYSQL_USER}" 1367 1368[ ! -z "${MYSQL_HOST}" ] && \ 1369 MYSQL_AUTH_KEYS="${MYSQL_AUTH_KEYS} --host=${MYSQL_HOST}" 1370 1371# Use shortcuts for signals to deal with Linux's sh. 1372trap cleanup INT TERM EXIT 1373 1374main 1375