]> git.sesse.net Git - backup.sh/blob - backup.sh
e39cade4655560d67dd23dcd0eb8350004d333d2
[backup.sh] / backup.sh
1 #!/bin/sh
2
3 # Locking
4 LOCKFILE=/home/backup/backuprun.lockfile
5
6 # Initially, we consider everything to be just fine.
7 R=0
8
9 # Die gracefully (ie. remove lockfile)
10 die() {
11         echo `date`": Something nasty happened, and since I fork a lot:"
12         echo `date`": I CANNOT CLEAN UP THE MESS MYSELF."
13         echo `date`": You need to get rid of lost process named stuff like $0, tar and ssh."
14         exit 255
15 }
16
17 # Trap C-c and kill
18 trap die SIGINT SIGTERM
19
20 # Don't start if we're already running
21 if [ -e $LOCKFILE ]; then
22         echo `date`": $LOCKFILE exists, exiting."
23         exit 1
24 fi;
25 touch $LOCKFILE
26
27 #dirs
28 confdir=/home/backup/conf/           #configuration files
29 storagedir=/backup                   #mountpoint of huge disc
30 ###
31
32 #exclude-pattern
33 exclude=$confdir/exclude
34 [ ! -f $exclude ] && printf "tmp\ncore\n" > $exclude
35 ###
36
37 #syntax of remotestat:
38 #hostname:/directory/
39 remotestatf=$confdir/remotestat
40 [ -f $remotestatf ] && remotestat=`cat $remotestatf`
41 ###
42
43 PATH=/local/bin:$PATH:/store/bin
44 export PATH
45
46 #start the logfile
47 echo `date`": Backup run starting"
48 echo "Exclude pattern:"
49 cat $exclude
50 echo "End exclude pattern"
51
52 umask 027
53
54 # The computers we want to back up
55 unixcomputers=`cat $confdir/computers.unix \
56   | grep -v "^#" \
57   | grep -v "^$" `
58
59 # Backup only one computer, from command line?
60 if [ $1 ] ; then 
61   unixcomputers=$1
62 fi;
63
64 #disse bør kunne varieres fra fs til fs?
65 maksantallfulle=3          #hvor mange fulle vi tar vare på
66 dagermellomfulle=30        #antall dager før det er på tide med ny full
67 logw=40
68 ###
69
70 #lager datovariabeler
71 DATE=`date "+%Y%m%d%H%M"`            #format: touch
72 DATEs=`date "+%Y-%m-%d %H:%M"`       #format: tar
73 ###
74
75 #selve backupen
76 # krever at noen variabler er satt
77 # krever at vi er i rett katalog
78 backup()
79 {
80
81  printf "%-${logw}s %s\n" "$computer:$filesystem" "$backuplevel backup"
82
83  if [ "$backuplevel" = "daglig" ] || [ "$backuplevel" = "incremental" ]
84  then
85   lastd=`cat ../.date`
86   lastcmd="--newer='$lastd'"
87  else
88   lastcmd=""
89  fi
90
91  #if this client has a special tar
92  #we need to find a better solution to this conf-issue
93  if [ -f $confdir/tar.$computer ] ; then
94    tar=`cat $confdir/tar.$computer`
95  else 
96    tar=tar
97  fi
98
99  #expand the exclude-path for use with tar
100  exf=`ssh $computer "ls ~/.backup/exclude"`
101
102  #We try to run tar on the remote computer
103  #    c create archive
104  #    C change to directory first
105  #    - output to stdout (we pipe to gzip, then to dd)
106  #    . where to start taring (see C)
107  #    $lastcmd only files newer than this
108  #    --exclude-from file to get exclusion pattern from
109  #    pipe to gzip, which in turn pipes over the ssh-stream
110  #    ..to dd, to output to a file. We surpress messages from dd.
111  #    And at last, redirect stderr to stdout, to get output logged.
112  TARFILE=$DATE.tmp
113  TARCMD="ssh $computer \"$tar --one-file-system -cf - -C $filesystem . $lastcmd \
114         --exclude-from=$exf | gzip\" | (dd of=$TARFILE 2> /dev/null) 2>&1"
115  echo "cmdline: $TARCMD"
116  eval $TARCMD
117
118  # Ideally, we should check wether the tar command returned 0 or not, but it
119  # seems a pipe in bash returns the value of the last command in the pipe.
120  # Instead, we check wether the resulting file has zero size, in which case we
121  # consider it an error.
122  if [ -s $TARFILE ]; then
123   echo `date`": command probably ran without errors."
124   #perhaps it did work
125   mv $TARFILE $DATE.tgz
126   #make a filelist.
127   #update the datefile if the filelist is ok.
128   tar tvfz $DATE.tgz > $DATE.idx 2>&1 &&
129     echo $DATEs > ../.date            &&
130     touch -t $DATE ../.date
131
132   #make a sortet filelist
133   grep -v ^d $DATE.idx | sort -n -r +2 > $DATE.sdx
134
135   #fix perm
136   chmod 600 *tgz                      #only for us
137   chmod 644 *sdx *idx 2>/dev/null     #everyone can read
138
139  else
140   #it did not work
141   rm $TARFILE
142   echo `date`": $TARFILE empty. $backuplevel backup of $computer:$filesystem failed and deleted"
143
144   # We don't want to return 0
145   R=1
146  fi
147
148 }
149
150 #Løper gjennom listen av unixmaskiner som vi skal ta backup av
151 for computer in $unixcomputers
152 do
153
154  echo `date`": Backing up $computer"
155
156  # Try to SSH to the computer without entering a password.
157  if `ssh -n -o NumberOfPasswordPrompts=0 $computer /bin/true`; then
158   echo "Passwordless SSH to $computer works."
159  else
160   echo "Could not use passwordless SSH to $computer."
161
162   # We don't want to return 0
163   R=1
164   break;
165  fi
166  
167  #tømmer variabelen for sikkerhets skyld
168  filesystems=""
169
170  #Sjekker nest siste felt i fstab. Om det er 0 tar vi ikke backup
171  filesystems=`ssh -n $computer "cat /etc/fstab" \
172   | grep -v nfs \
173   | grep -v "^#" \
174   | grep -v "^$" \
175   | awk '{ if ( $(NF-1) != "0" ) print $2}' `
176
177  echo "Filesystems to backup on $computer: $filesystems"
178
179  #clean up our dir at this client
180  if ! ssh $computer "rm -r ~/.backup ; mkdir -m 700 ~/.backup"; then
181   echo "Could not create backup staging area at $computer:~/.backup"
182   # We don't want to return 0
183   R=1
184   break;
185  fi
186
187  #try to copy $exclude to $computer
188  if ! scp $exclude $computer:~/.backup/exclude > /dev/null; then
189   echo "Could not copy exclude.txt to $computer"
190   # We don't want to return 0
191   R=1
192   break;
193  fi
194
195  #try to copy preeexec and postexec if they exist
196 # TODO: Gah, clean this mess!
197  [ -f $confdir/preexec.$computer ] && (
198     scp $confdir/preexec.$computer  $computer:~/.backup/preexec ||
199      ( echo "Could not copy preexec.$computer to $computer:~/.backup/preexec";
200        R=1
201        break
202      )
203     )
204  [ -f $confdir/postexec.$computer ] && (
205     scp $confdir/postexec.$computer $computer:~/.backup/postexec ||
206      ( echo "Could not copy postexec.$computer to $computer:~/.backup/postexec"
207        break
208        R=1
209      )
210     )
211
212  #try to run preexec if it exist
213  ssh $computer "[ -f ~/.backup/preexec ] && /bin/bash -x ~/.backup/preexec"
214
215  for filesystem in $filesystems
216  do
217   #lager en variant uten tegnet "/" eller $ (gjelder NT)
218   sfilesystem=`echo $filesystem | tr '\/\$' '__'`
219
220   #lager det som trengs av kataloger
221   mkdir -m 755 -p $storagedir/$computer/$sfilesystem/{full,daglig} 2>/dev/null
222
223   echo $filesystem > ${storagedir}/${computer}/.${sfilesystem}.name 
224   chmod 644 ${storagedir}/${computer}/.${sfilesystem}.name
225
226   #set default backuplevel
227   backuplevel=daglig
228
229   if [ ! -f $storagedir/$computer/$sfilesystem/.date ]
230   then
231    #take the first full backup of this filesystem on this computer
232    backuplevel=full
233    echo $DATEs > $storagedir/$computer/$sfilesystem/.date
234   fi
235
236   #sjekker om det er på tide med en full
237   if [ -z "`find $storagedir/$computer/$sfilesystem/full/ -name \*tgz -mtime -$dagermellomfulle`" ]; then
238    backuplevel=full
239   fi
240  
241   #gå ned i rett katalog, eller dø 
242   # TODO bør sende mail om dette skjer!
243   cd $storagedir/$computer/$sfilesystem/$backuplevel || die
244
245   #perform the actual backup
246   backup
247
248   # Sjekk om det skal være et annet antall fulle backuper av en boks
249   if [ -f $confdir/maksfulle.$computer ] ; then
250     mf=$((`cat $confdir/maksfulle.$computer`+1))
251   else
252     mf=$(($maksantallfulle+1))
253   fi
254
255   #delete complete backups
256   for full in `ls -1t $storagedir/$computer/$sfilesystem/full/*tgz | tail +$mf`
257   do
258    prefix=`echo $full | sed "s/\.[^.]*$//"`
259    echo "$computer:$filesystem sletter full $prefix (for mange)"
260    rm $prefix*
261   done
262
263   #delete incremental backups older than the oldest complete backup
264   oldf=`ls -t1 $storagedir/$computer/$sfilesystem/full/*tgz | tail -1`
265   find \
266      $storagedir/$computer/$sfilesystem/daglig \
267      -type f \
268      \! -newer $oldf \
269      -exec rm {} \;
270  done
271
272  #try to run postexec if it exist
273  ssh $computer "[ -f ~/.backup/postexec ] && /bin/bash -x ~/.backup/postexec"
274
275 )  
276 done &
277
278 wait
279
280 # print diskusage to logfile
281 echo "Current disk usage:"
282 df -k $storagedir
283
284 # Remove lockfile
285 rm $LOCKFILE
286
287 # Did anything go wrong?
288 if [ $R != 0 ]; then
289         echo `date`": Backup run ended with errors, check logs."
290         exit 1
291 else
292         echo `date`": Backup run ended"
293 fi