root/alternc/trunk/src/sqlbackup.sh

Revision 2437, 15.5 kB (checked in by anarcat, 3 weeks ago)

be more resilient to errors

  • Property svn:executable set to *
Line 
1 #!/bin/bash
2
3 # $Id: sqlbackup.sh,v 2.0 2006/10/17 17:32:05 mistur Exp $
4 # ----------------------------------------------------------------------
5 # AlternC - Web Hosting System
6 # Copyright (C) 2002 by the AlternC Development Team.
7 # http://alternc.org/
8 # ----------------------------------------------------------------------
9 # Based on:
10 # Valentin Lacambre's web hosting softwares: http://altern.org/
11 # ----------------------------------------------------------------------
12 # LICENSE
13 #
14 # This program is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License (GPL)
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 # GNU General Public License for more details.
23 #
24 # To read the license please visit http://www.gnu.org/copyleft/gpl.html
25 # ----------------------------------------------------------------------
26 # Original Author of file: Benjamin Sonntag - 2003-03-23
27 # Purpose of file: MySQL Database backup shell script for AlternC
28 # ----------------------------------------------------------------------
29 # Changed by Yoann Moulin : 2006-10-16
30 # * Adding an other possibilty for name of the backup files which
31 #   avoid renaming old backup files (name rotation methode)
32 #   this methode include the date of the backup day in the name of the
33 #   file
34 #   Usefull for person who use rsync, rsnapshot, etc... this methode
35 #   avoid to sync old files which just has been rename but seem diff
36 #   for sync script
37 set -e
38
39 # Get mysql user and password :
40 . /etc/alternc/local.sh
41
42 # get the date of the day
43 DATE=`date +"%Y%m%d"`
44
45 # echo function, used for output wrapping when run in daemon
46 # mode.
47 # usage: print [option] <message>
48 #   without option, print <message> in any case on the stdout 
49 #
50 # options :
51 #   error : print <message> in any case and indicate that an error message
52 #   debug : print <message> if debug mode is active
53 #   info  : print <message> if verbose mode is active
54 #
55 # notes :
56 #   if backup running in daemon mode, printing in log file if an otpion
57 #   is gived to the function
58 print() {
59    
60     # if a log level is given to the print function
61     # 'error', 'info' or 'debug'
62     log_level=""
63     if [ "$1" == "error" ] || [ "$1" == "info" ] || [ "$1" == "debug" ];
64     then
65         # read it and remove it for arg list
66         log_level="$1"
67         shift
68     fi
69
70     # if
71     #  - No log level is specified
72     #  - Log level equal to 'error'
73     #    => print in any case on stdout
74     #    => add to log file as well if $DAEMON set to 'ON'
75     #  - Log level equal to 'debug' and $DEBUG is set to on
76     #  - Log level equal to 'info' and $VERBOSE set to 'ON'
77     #     => print on log file if $DAEMON set to 'ON', on stdout if not
78     if [ -z "$log_level" ] ||
79     [ "$log_level" == "error" ] ||
80     [ "$DEBUG" == "ON"  -a  "$log_level" == "debug" ]  ||
81     [ "$log_level" == "info" -a  "$VERBOSE" == "ON" ] ;
82     then
83         if [ "$DAEMON" == "ON" ] ; then
84             # function without option must be print on stdout in anycase
85             # even if print in the log file
86             if [ -z "$log_level" ] || [ "$log_level" == "error" ];
87             then
88                 echo "$EXEC_CMD $log_level: $*"
89             fi
90             logger -p local0.$log_level -t sqlbackup "$*"
91         else
92             if [ -z "$log_level" ];
93             then
94                 echo "$*"
95             else
96                 echo "$log_level: $*"
97             fi
98         fi
99     fi
100    
101 }
102
103 error() {
104     print "error" $*
105 }
106
107 info() {
108     print "info" $*
109 }
110 debug() {
111     print "debug" $*
112 }
113
114 function dobck() {
115     local ext
116     local i
117     local old_ifs
118    
119     # mysql -B uses tab as a separator between fields, so we have to mess
120     # with IFS in order to get the correct behaviour
121     old_ifs="$IFS"
122     IFS="       "
123     # read parameter given by mysql
124     while read login pass db count compressed target_dir; do
125        
126         debug "read $login \$pass $db $count $compressed $target_dir"
127         # restore $IFS after read parameter
128         IFS="$old_ifs"
129
130         # by default : DOBAKCUP set to yes
131         DO_BACKUP="YES"
132        
133         if [ "$compressed" -eq 1 ]; then
134             ext=".gz"
135         else
136             ext=""
137         fi
138
139         # if $SQLBACKUP_TYPE is set to "rotate" classical rotation files methode will be used
140         # use incrementale number in the name of files where the highest number indicate
141         # the oldest files
142         # if the rotate type is not set or set to date, the name of the export file will contain the date
143         # of the backup on won't be rotate by the classic rotate number
144         # usefull if you're using rsync or rsnapshop or everything base on rsync to avoir to copy
145         # rotate files which just change name
146         #
147         # ------------------------------------------------------------------ #
148         # the variable SQLBACKUP_TYPE must be set in /etc/alternc/local.sh #
149         # ------------------------------------------------------------------ #
150         if [ $SQLBACKUP_TYPE == "rotate" ]; then
151            
152             i="$count"
153            
154             # rotate all backup
155             while [ $i -gt 1 ] ; do
156              
157               next_i=$(($i - 1))
158            
159               if [ -e "${target_dir}/${db}.sql.${next_i}${ext}" ]; then
160                 mv -f "${target_dir}/${db}.sql.${next_i}${ext}" \
161                       "${target_dir}/${db}.sql.${i}${ext}" 2>/dev/null || true
162               fi
163               i=$next_i # loop should end here
164             done
165            
166             # move most recently backup with a rotate file name
167             if [ -e "${target_dir}/${db}.sql${ext}" ]; then
168               mv -f "${target_dir}/${db}.sql${ext}" \
169                     "${target_dir}/${db}.sql.${i}${ext}" 2>/dev/null || true
170             fi
171
172             name_backup_file="${db}"
173          else   
174             # ---------------
175             # default methode   
176             # ---------------
177             # calcul the mtime parameter for find
178             # $count is the number of backup to keep
179             # daily  : if we are keeping X backup, deleting the file which has the mtime at X + 1 days
180             # weekly : if we are keeping X backup, deleting the file which has the mtime at (X + 1) * 7 day
181             # echo "last2del=( $count + 1 ) * $coef "
182             #
183             last2del=$(( ( $count + 1 ) * $coef ))
184            
185             # find the oldest backup file need to be delete
186             # find ${target_dir}     : in the target_dir
187             # -name \"${db}.*sql.*\" : All files like <db_name>.*sql.*
188             # -maxdepth 0            : only in the target dir (on not in the subdirectory)
189             # -mtime $last2del       : files with the exact mtime set to $last2del
190             #                          daily  : ( number of backup to keep + 1 ) days
191             #                          weekly : ( number of backup to keep + 1 ) * 7 days
192             # -exec rm -f {} \;      : remove all files found
193             #
194             debug "find ${target_dir} -name \"${db}.*sql${ext}\" -maxdepth 1 -mtime +$last2del -exec rm -f {} \; -ls"
195             find ${target_dir} -name "${db}.*sql${ext}" -maxdepth 1 -mtime +${last2del} -exec rm -f {} \; -ls || true
196            
197             # set the name of the backup file with the date of the day
198             name_backup_file="${db}.${DATE}"
199            
200        fi
201      
202        # if the backup exite and SQLBACKUP_OVERWRITE is set to NO, cancel backup
203        if [ -f "${target_dir}/${name_backup_file}.sql${ext}" ] && [ "$SQLBACKUP_OVERWRITE"  == "no" ] ; then
204            
205            info "sqlbackup.sh: ${target_dir}/${name_backup_file}.sql${ext}: already exist"
206            info "              => no backup done as specify in allow-overwrite = $SQLBACKUP_OVERWRITE"
207            DO_BACKUP="NO"
208
209         # if the backup exite and SQLBACKUP_OVERWRITE is set to RENAME, add 
210         elif [ -f "${target_dir}/${name_backup_file}.sql${ext}" ] && [ "$SQLBACKUP_OVERWRITE"  == "rename" ] ; then
211
212            info "sqlbackup.sh: ${target_dir}/${name_backup_file}.sql${ext}: already exist"
213            info "              => renaming the new file as specify in allow-overwrite = $SQLBACKUP_OVERWRITE"
214            hours=`date +"%H%M"`
215            name_backup_file="${name_backup_file}.${hours}"
216
217         # if the backup exite and SQLBACKUP_OVERWRITE is set OVERWRITE, add 
218         elif [ -f "${target_dir}/${name_backup_file}.sql${ext}" ] && [ "$SQLBACKUP_OVERWRITE"  == "overwrite" ] ; then
219
220            info "sqlbackup.sh: ${target_dir}/${name_backup_file}.sql${ext}: already exist"
221            info "              => overwrite file as specify in allow-overwrite = $SQLBACKUP_OVERWRITE"
222            
223        fi
224
225        ###
226        # mysqldump Option :
227        # --add-drop-table  : Add a 'drop table' before each create.
228        #                     usefull if you want to override the database without delete table before
229        #                     this is need to used restore from the alternc interface
230        # --allow-keywords  : Allow creation of column names that are keywords.
231        #                     
232        # --quote-names     : Quote table and column names with `
233        #                     Usefull if you have space in table or column names
234        # --force           : Continue even if we get an sql-error.
235        #                     To avoid end of script during backup script execution
236        #                     Allow script to backup other database if one of the have an error
237        # --quick           : Don't buffer query, dump directly to stdout.
238        #                     optimisation option
239        # --extended-insert : Allows utilization of the new, much faster INSERT syntax.
240        #                     optimization option
241        # --add-locks       : Add locks around insert statements.
242        # --lock-tables     : Lock all tables for read.
243        #                      those 2 options avoid insert during dump which can create an unconsistent
244        #                      state of the database backup
245        if [ "$DO_BACKUP" == "YES" ]; then
246            command="mysqldump --defaults-file=/etc/alternc/my.cnf --add-drop-table --allow-keywords --quote-names --force --quick --add-locks --lock-tables --extended-insert $db"
247            if [ "$compressed" -eq 1 ] ; then
248                debug "$command > ${target_dir}/${name_backup_file}.sql${ext}"
249                $command | gzip -c > "${target_dir}/${name_backup_file}.sql${ext}" || echo "backup failed for ${name_backup_file}"
250            else
251                debug "$command > ${target_dir}/${name_backup_file}.sql${ext}"
252                $command > "${target_dir}/${name_backup_file}.sql${ext}" || echo "backup failed for ${name_backup_file}"
253            fi
254         fi
255
256         IFS="   "
257     done
258     IFS="$old_ifs"
259 }
260
261 # read_parameters gets all command-line arguments and analyzes them
262 #
263 # return:
264 read_parameters() {
265
266     # for all parameter give to the script
267     while [ "$1" != "" ] ; do
268         case "$1" in
269             -h|--help) usage; exit ;;
270             -v|--verbose) VERBOSE="ON" ;;
271             -d|--debug) DEBUG="ON" ;;
272             -t|--type) shift; TYPE="$1";;
273             -n|--name-methode) shift; SQLBACKUP_TYPE="$1";;
274             -a|--allow-ovewrite) shift; SQLBACKUP_OVERWRITE="$1" ;;
275             daily|weekly) TYPE="$1";; # backwards compatibility
276             *)
277                 error "invalid option -- $1"
278                 error "Try \`sqlbackup.sh --help' for more information."
279                 exit ;;
280         esac
281         # in case of no argument give to an option
282         # shift execute an exit if already empty
283         # add test to avoid this at least to print error message
284         [ "$1" != "" ] && shift   
285     done
286
287     debug "TYPE = $TYPE"
288     debug "SQLBACKUP_TYPE = $SQLBACKUP_TYPE"
289     debug "SQLBACKUP_OVERWRITE = $SQLBACKUP_OVERWRITE"
290    
291
292     # check options
293     if [ "$TYPE" == "daily" ]; then
294         # Daily :
295         mode=2
296         coef=1
297     elif [ "$TYPE" == "weekly" ] ; then
298         # Weekly:
299         mode=1
300         coef=7
301     elif [ -n "$TYPE" ] ; then
302         error "missing argument: type"
303         error "Try \`sqlbackup.sh --help' for more information."
304         exit
305     else
306         error "invalid argument: type -- $TYPE"
307         error "Try \`sqlbackup.sh --help' for more information."
308         exit
309     fi
310
311     if ! ( [ -z "$SQLBACKUP_TYPE" ] ||
312            [ "$SQLBACKUP_TYPE" == "date" ] ||
313            [ "$SQLBACKUP_TYPE" == "rotate" ] ) ; then
314         error "invalid argument: name-methode -- $SQLBACKUP_TYPE"
315         error "Try \`sqlbackup.sh --help' for more information."
316         exit
317      fi
318
319     if ! ( [ -z  "$SQLBACKUP_OVERWRITE" ] ||
320            [ "$SQLBACKUP_OVERWRITE" == "no" ] ||
321            [ "$SQLBACKUP_OVERWRITE" == "rename" ] ||
322            [ "$SQLBACKUP_OVERWRITE" == "overwrite" ] ); then
323         error "invalid argument: allow-ovewrite -- $SQLBACKUP_OVERWRITE"
324         error "Try \`sqlbackup.sh --help' for more information."
325         exit
326      fi
327
328 }
329
330 # a quick intro to the software, displayed when no params found
331 usage() {
332     echo "Usage: sqlbackup.sh [OPTION] -t TYPE
333
334 sqlbackup.sh is a script used by alternc for sql backup
335
336 Mandatory arguments to long options are mandatory for short options too.
337   -v, --verbose                 set verbose mode on
338   -d, --debug                   set debug mode on
339   -n, --name-method  METHOD     set the method type for files' name
340   -a, --allow-override OVERRIDE specify the behaviour if backup files already exist
341   -t, --type TYPE               set backup type
342   -h, --help                    display this help and exit
343
344 the TYPE arguments specify type of backup.  Here are the values:
345
346     daily           Execute a daily backup on all databases set to daily backup
347     weekly          Execute a daily backup on all databases set to weekly backup
348
349 the METHOD argument the type for files' name.  Here are the values:
350
351     date            insert in the backup file's name the date of the backup
352                     (default value)
353     rotate          rename file as file.<number><extension> where <number>
354                     is incremented
355
356 the OVERRIDE argument the behaviour of the script if a backup file already exist.
357 Here are the values:
358
359     no              if a backup file already exist, no backup done
360     rename          if a backup file already exist, add an extension to the new
361                     backup file
362
363     overwrite       if a backup file already exist, overwrite it with the new 
364                     backup"
365
366 }
367 debug begin $@
368 # read all paramter before doing anything before
369 read_parameters $@
370 debug end
371
372 ###
373 # select backup information from the alternc database in the db table
374 # all backup for the specify mode (daily or weekly)
375 # option :
376 #   --batch : Print results with a tab as separator, each row on a new line.
377 #             avoid seperator like "|" which are not usefull in a shell script
378 #             need to set the IFS environment variable to "\t" (tabbulation) for
379 #             the `read' command (indicate field separator by default `read'
380 #             use space)
381 # tail -n '+2' permit to skip the first line (legende line)
382 # execut dobck on all database found by the sql request
383 #
384 # the "<< EOF" mean send data to the command until EOF (end of file)
385 #
386 debug /usr/bin/mysql --defaults-file=/etc/alternc/my.cnf --batch
387 /usr/bin/mysql --defaults-file=/etc/alternc/my.cnf --batch << EOF | tail -n '+2' | dobck
388 SELECT login, pass, db, bck_history, bck_gzip, bck_dir
389   FROM db
390  WHERE bck_mode=$mode;
391 EOF
392
393 # vim: et sw=4
394
Note: See TracBrowser for help on using the browser.