Forráskód Böngészése

Add MySQL support

Emmanuel Bouthenot 1 éve
szülő
commit
5d9b2d44c7
1 módosított fájl, 246 hozzáadás és 79 törlés
  1. 246 79
      autopostgresqlbackup

+ 246 - 79
autopostgresqlbackup

@@ -1,7 +1,6 @@
 #!/usr/bin/env bash
 
 # {{{ License and Copyright
-# PostgreSQL Backup Script
 # https://github.com/k0lter/autopostgresqlbackup
 # Copyright (c) 2005 Aaron Axelsen <axelseaa@amadmax.com>
 #               2005 Friedrich Lobenstock <fl@fl.priv.at>
@@ -40,83 +39,58 @@ CONFIG="/etc/default/autopostgresqlbackup"
 # Email Address to send errors to. If empty errors are displayed on stdout.
 MAILADDR="root"
 
-# By default, on Debian systems (and maybe others), only postgres user is
-# allowed to access PostgreSQL databases without password.
-#
-# In order to dump databases we need to run pg_dump/psql commands as postgres
-# with su. This setting makes possible to run backups with a substitute user
-# using su. If
-#
-# empty, su usage will be disabled)
-SU_USERNAME=
+# Database engines supported: postgresql, mysql
+DBENGINE="postgresql"
 
-# Username to access the PostgreSQL server
-USERNAME="postgres"
+# Only while using PostgreSQL DB Engine
+SU_USERNAME=""
 
-# Password settings
-# In order to use a password to connect to database create a file
-# ${HOME}/.pgpass containing a line like this
-#
-# hostname:*:*:dbuser:dbpass
-#
-# replace hostname with the value of ${DBHOST}, dbuser with the value of
-# ${USERNAME} and dbpass with the password.
+# Username to access the Database server
+USERNAME=""
 
-# Host name (or IP address) of PostgreSQL server.
-# Use 'localhost' for socket connection or '127.0.0.1' to force TCP connection
+# Password to access then Database server
+PASSWORD=""
+
+# Host name (or IP address) of the Database server.
 DBHOST="localhost"
 
-# Port of PostgreSQL server.
-# It is also used if ${DBHOST} is localhost (socket connection) as socket name
-# contains port
-DBPORT="5432"
+# Port of Database server.
+DBPORT=""
 
-# List of database(s) names(s) to backup If you would like to backup all
-# databases on the server set DBNAMES="all".
-#
-# If set to "all" then any new databases will automatically be backed up
-# without needing to modify this settings when a new database is created.
-#
-# If the database you want to backup has a space in the name replace the space
-# with a % ("data base" will become "data%base").
+# List of database(s) names(s) to backup.
 DBNAMES="all"
 
-# List of databases to exclude if DBNAMES is not set to all.
+# List of databases to exclude
 DBEXCLUDE=""
 
-# Pseudo database name used to dump global objects (users, roles, tablespaces)
+# Virtual database name used to dump global objects (users, roles, tablespaces)
 GLOBALS_OBJECTS="postgres_globals"
 
 # Backup directory
 BACKUPDIR="/var/backups"
 
-# Include CREATE DATABASE in backups?
+# Include CREATE DATABASE statement
 CREATE_DATABASE="yes"
 
-# Which day do you want weekly backups? (1 to 7 where 1 is Monday)
-# When set to 0, weekly backups are disabled
+# Which day do you want weekly backups?
 DOWEEKLY=7
 
 # Which day do you want monthly backups?
-# When set to 0, monthly backups are disabled
 DOMONTHLY=1
 
-# Backup retention count for daily backups, older backups are removed.
+# Backup retention count for daily backups.
 BRDAILY=14
 
-# Backup retention count for weekly backups, older backups are removed.
+# Backup retention count for weekly backups.
 BRWEEKLY=5
 
-# Backup retention count for monthly backups, older backups are removed.
+# Backup retention count for monthly backups.
 BRMONTHLY=12
 
-# Compression tool. It could be gzip, pigz, bzip2, xz, zstd or any compression
-# tool that supports to read data to be compressed from stdin and outputs them
-# to stdout).
-# If the tool is not in ${PATH}, the absolute path can be used.
+# Compression tool.
 COMP="gzip"
 
-# Compression tools options to be used with COMP
+# Compression tools options.
 COMP_OPTS=
 
 # Options string for use with pg_dump (see pg_dump manual page).
@@ -125,6 +99,9 @@ PGDUMP_OPTS=
 # Options string for use with pg_dumpall (see pg_dumpall manual page).
 PGDUMPALL_OPTS=
 
+# Options string for use with mysqldump (see myqldump manual page).
+MYDUMP_OPTS=
+
 # Backup files extension
 EXT="sql"
 
@@ -132,22 +109,12 @@ EXT="sql"
 PERM=600
 
 # Minimum size (in bytes) for a dump/file (compressed or not).
-# File size below this limit will raise an warning.
 MIN_DUMP_SIZE=256
 
 # Enable encryption (asymmetric) with GnuPG.
 ENCRYPTION="no"
 
 # Encryption public key (path to the key)
-# Export your public key=""
-#     gpg --export 0xY0URK3Y1D --output mypubkey.gpg or \
-#     gpg --export --armor 0xY0URK3Y1D --output mypubkey.asc
-#     then copy mypubkey.asc or mypubkey.gpg to the path pointed by the
-#     ${ENCRYPTION_PUBLIC_KEY}.
-#
-# Decryption
-#     gpg --decrypt --output backup.sql.gz backup.sql.gz.enc
-#
 ENCRYPTION_PUBLIC_KEY=
 
 # Suffix for encyrpted files
@@ -238,6 +205,46 @@ log_warn() {
 }
 # }}}
 
+# {{{ arg_encode()
+arg_encode() {
+    while read -r arg ; do
+        echo "${arg}" | sed \
+        -e 's/%/%25/g' \
+        -e 's/ /%20/g' \
+        -e 's/\$/%24/g' \
+        -e 's/`/%60/g' \
+        -e 's/"/%22/g' \
+        -e "s/'/%27/g" \
+        -e "s/#/%23/g" \
+        -e 's/=/%3D/g' \
+        -e 's/\[/%5B/g' \
+        -e 's/\]/%5D/g' \
+        -e 's/!/%21/g' \
+        -e 's/>/%3E/g' \
+        -e 's/</%3C/g' \
+        -e 's/|/%7C/g' \
+        -e 's/;/%3B/g' \
+        -e 's/{/%7B/g' \
+        -e 's/}/%7D/g' \
+        -e 's/(/%28/g' \
+        -e 's/)/%29/g' \
+        -e 's/\*/%2A/g' \
+        -e 's/:/%3A/g' \
+        -e 's/\?/%3F/g' \
+        -e 's/&/%26/g' \
+        -e 's/\//%2F/g'
+    done
+}
+# }}}
+
+# {{{ arg_decode()
+arg_decode() {
+    while read -r arg ; do
+        echo -e "${arg//%/\\x}"
+    done
+}
+# }}}
+
 # {{{ gpg_setup()
 gpg_setup() {
     GPG_HOMEDIR="$(mktemp --quiet --directory -t "${NAME}.XXXXXX")"
@@ -267,20 +274,24 @@ compression () {
 }
 # }}}
 
-# {{{ pgdb_init()
-pgdb_init () {
+# {{{ postgresqldb_init()
+postgresqldb_init () {
+    if [ -z "${DBPORT}" ]; then
+        DBPORT="5432"
+    fi
     CONN_ARGS=(--port "${DBPORT}")
     if [ "${DBHOST}" != "localhost" ]; then
         CONN_ARGS+=(--host "${DBHOST}")
     fi
-    if [ -n "${USERNAME}" ]; then
-        CONN_ARGS+=(--username "${USERNAME}")
+    if [ -z "${USERNAME}" ]; then
+        USERNAME="postgres"
     fi
+    CONN_ARGS+=(--username "${USERNAME}")
 }
 # }}}
 
-# {{{ pgdb_list()
-pgdb_list () {
+# {{{ postgresqldb_list()
+postgresqldb_list () {
     local cmd_prog cmd_args raw_dblist dblist dbexcl databases
 
     cmd_prog="psql"
@@ -304,6 +315,7 @@ pgdb_list () {
     read -r -a dblist <<< "$(
         printf "%s" "${raw_dblist}" | \
             sed -E -n 's/^([^:]+):.+$/\1/p' | \
+            arg_encode | \
             tr '\n' ' '
     )"
     log_debug "Automatically found databases: ${dblist[*]}"
@@ -328,8 +340,8 @@ pgdb_list () {
 }
 # }}}
 
-# {{{ pgdb_dump()
-pgdb_dump () {
+# {{{ postgresqldb_dump()
+postgresqldb_dump () {
     local db_name cmd_prog cmd_args pg_args
 
     db_name="${1}"
@@ -353,7 +365,11 @@ pgdb_dump () {
         pg_args=("${PGDUMPALL_ARGS[@]}")
     else
         cmd_prog="pg_dump"
-        cmd_args=("${db_name}")
+        if [ -n "${SU_USERNAME}" ]; then
+            cmd_args=("'${db_name}'")
+        else
+            cmd_args=("${db_name}")
+        fi
         pg_args=("${PGDUMP_ARGS[@]}")
         if [ "${CREATE_DATABASE}" = "yes" ]; then
             pg_args+=(--create)
@@ -378,6 +394,156 @@ pgdb_dump () {
 }
 # }}}
 
+# {{{ mysqldb_init()
+mysqldb_init () {
+    CONN_ARGS=()
+    if [ -z "${DBPORT}" ]; then
+        DBPORT="3306"
+    fi
+    if [ "${DBHOST}" != "localhost" ]; then
+        CONN_ARGS+=(--host "${DBHOST}")
+    fi
+    if [ "${DBPORT}" != "3306" ]; then
+        CONN_ARGS+=(--port "${DBPORT}")
+    fi
+    if [ -z "${USERNAME}" ]; then
+        USERNAME="root"
+    fi
+    CONN_ARGS+=(--user "${USERNAME}")
+    if [ -n "${PASSWORD}" ]; then
+        CONN_ARGS+=(--password "${PASSWORD}")
+    fi
+}
+# }}}
+
+# {{{ mysqldb_list()
+mysqldb_list () {
+    local cmd_prog cmd_args raw_dblist dblist dbexcl databases
+
+    cmd_prog="mysql"
+    cmd_args=(--batch --skip-column-names --execute 'SHOW DATABASES;')
+
+    if [ "${#CONN_ARGS[@]}" -gt 0 ]; then
+        cmd_args+=("${CONN_ARGS[@]}")
+    fi
+
+    log_debug "Running command: ${cmd_prog} ${cmd_args[*]}"
+    raw_dblist=$(
+        if ! "${cmd_prog}" "${cmd_args[@]}" 2> >(logger "err" "error"); then
+            log_error "Running command '${cmd_prog} ${cmd_args[*]}' has failed"
+        fi
+    )
+
+    read -r -a dblist <<< "$(
+        printf "%s" "${raw_dblist}" | \
+            arg_encode | \
+            tr '\n' ' '
+    )"
+    log_debug "Automatically found databases: ${dblist[*]}"
+
+    if [ -n "${DBEXCLUDE}" ]; then
+        IFS=" " read -r -a dbexcl <<< "${DBEXCLUDE}"
+    else
+        dbexcl=()
+    fi
+    dbexcl+=(information_schema performance_schema mysql)
+    log_debug "Excluded databases: ${dbexcl[*]}"
+
+    mapfile -t databases < <(
+        comm -23 \
+            <(IFS=$'\n'; echo "${dblist[*]}" | sort) \
+            <(IFS=$'\n'; echo "${dbexcl[*]}" | sort) \
+        )
+    log_debug "Database(s) to be backuped: ${databases[*]}"
+
+    printf "%s " "${databases[@]}"
+}
+# }}}
+
+# {{{ mysqldb_dump()
+mysqldb_dump () {
+    local db_name cmd_prog cmd_args my_args
+
+    db_name="${1}"
+
+    if [ -n "${MYDUMP_OPTS}" ]; then
+        IFS=" " read -r -a MYDUMP_ARGS <<< "${MYDUMP_OPTS}"
+    else
+        MYDUMP_ARGS=()
+    fi
+
+    cmd_prog="mysqldump"
+    cmd_args=("${db_name}")
+    my_args=("${MYDUMP_ARGS[@]}")
+    my_args+=(--quote-names --events --routines)
+    if [ "${CREATE_DATABASE}" = "no" ]; then
+        my_args+=(--databases)
+    else
+        my_args+=(--no-create-db)
+    fi
+
+    if [ "${#CONN_ARGS[@]}" -gt 0 ]; then
+        cmd_args+=("${CONN_ARGS[@]}")
+    fi
+    if [ "${#my_args[@]}" -gt 0 ]; then
+        cmd_args+=("${my_args[@]}")
+    fi
+
+    log_debug "Running command: ${cmd_prog} ${cmd_args[*]}"
+    if ! "${cmd_prog}" "${cmd_args[@]}" 2> >(logger "err" "error"); then
+        log_error "Running command '${cmd_prog} ${cmd_args[*]}' has failed"
+    fi
+}
+# }}}
+
+# {{{ db_init()
+db_init () {
+    case ${DBENGINE} in
+        postgresql)
+            postgresqldb_init
+            ;;
+        mysql)
+            mysqldb_init
+            ;;
+        *)
+            log_error "Unsupported database engine ${DBENGINE}, check DBENGINE configuration parameter"
+            return 1
+            ;;
+    esac
+}
+# }}}
+
+# {{{ db_list()
+db_list () {
+    case ${DBENGINE} in
+        postgresql)
+            postgresqldb_list
+            ;;
+        mysql)
+            mysqldb_list
+            ;;
+        *)
+            log_error "Unsupported database engine ${DBENGINE}, check DBENGINE configuration parameter"
+            return 1
+            ;;
+    esac
+}
+# }}}
+
+# {{{ db_dump()
+db_dump () {
+    case ${DBENGINE} in
+        postgresql|mysql)
+            ${DBENGINE}db_dump "${1}"
+            ;;
+        *)
+            log_error "Unsupported database engine ${DBENGINE}, check DBENGINE configuration parameter"
+            return 1
+            ;;
+    esac
+}
+# }}}
+
 # {{{ dump()
 dump() {
     local db_name dump_file comp_ext
@@ -410,16 +576,16 @@ dump() {
 
     if [ -n "${COMP}" ] && [ "${ENCRYPTION}" = "yes" ]; then
         log_debug "Dumping (${db_name}) +compress +encrypt to '${dump_file}'"
-        pgdb_dump "${db_name}" | compression | encryption > "${dump_file}"
+        db_dump "${db_name}" | compression | encryption > "${dump_file}"
     elif [ -n "${COMP}" ]; then
         log_debug "Dumping (${db_name}) +compress to '${dump_file}'"
-        pgdb_dump "${db_name}" | compression > "${dump_file}"
+        db_dump "${db_name}" | compression > "${dump_file}"
     elif [ "${ENCRYPTION}" = "yes" ]; then
         log_debug "Dumping (${db_name}) +encrypt to '${dump_file}'"
-        pgdb_dump "${db_name}" | encryption > "${dump_file}"
+        db_dump "${db_name}" | encryption > "${dump_file}"
     else
         log_debug "Dumping (${db_name}) to '${dump_file}'"
-        pgdb_dump "${db_name}" > "${dump_file}"
+        db_dump "${db_name}" > "${dump_file}"
     fi
 
     if [ -f "${dump_file}" ]; then
@@ -618,7 +784,7 @@ fi
 # {{{ main()
 log_info "${NAME} version ${VERSION}"
 log_info "Homepage: ${HOMEPAGE}"
-log_info "Backup of Database Server - ${HOST}"
+log_info "Backup of Database Server (${DBENGINE}) - ${HOST}"
 
 if [ -n "${COMP}" ]; then
     if ! command -v "${COMP}" >/dev/null ; then
@@ -665,24 +831,25 @@ fi
 
 # If backing up all DBs on the server
 if [ "${DBNAMES}" = "all" ]; then
-    DBNAMES="$(pgdb_list)"
+    DBNAMES="$(db_list)"
 fi
 
-pgdb_init
+db_init
+
+for db_enc in ${DBNAMES} ; do
+    db="$(echo "${db_enc}" | arg_decode)"
 
-for db in ${DBNAMES} ; do
-    db="${db//%/ / }"
     log_info "Backup of Database (${period}) '${db}'"
 
-    backupdbdir="${BACKUPDIR}/${period}/${db}"
+    backupdbdir="${BACKUPDIR}/${period}/${db_enc}"
     if [ ! -e "${backupdbdir}" ]; then
         log_debug "Creating Backup DB directory '${backupdbdir}'"
         mkdir -p "${backupdbdir}"
     fi
 
-    cleanup "${BACKUPDIR}" "${db}" "${period}" "${rotate}"
+    cleanup "${BACKUPDIR}" "${db_enc}" "${period}" "${rotate}"
 
-    backupfile="${backupdbdir}/${db}_${DATE}.${EXT}"
+    backupfile="${backupdbdir}/${db_enc}_${DATE}.${EXT}"
     dump "${db}" "${backupfile}"
 done
 log_info "Backup End: $(date)"