7
\$\begingroup\$

I have a backup script which should backup a folder and send it to email. This should be done once a day. As this is on my laptop which is not online 24/7 I need to check that I am online and can send email. For this script I have an entry in crontab running every 2 hours.

Because the folder is a really important to me and I am quite new to BASH, I would like to ask you if there are any weak points in my code:

#!/usr/bin/env bash

cd ~/projects

DATE=`date +%Y%m%d`
PASS="mysecretpasswordiwontwritehere"
# lastbackup file stores date of last backup in YYYYMMDD format
LASTBACKUP=$(< ./personalwiki/lastbackup)

# Check if backup already exists for current date
# and exit if yes.

if [[ "$LASTBACKUP" == "$DATE" ]]; then
  echo "Do nothing, already backed up."
  exit 0
fi

# Check if we are online
# and exit if not.

ping -c 1 www.google.com

if [[ $? -ne 0 ]]; then
  echo "Offline, cannot do anything."
  exit 0
fi

echo "Online and not equal, need to start backup"

# Pack an encrypt folder
tar cz personalwiki | openssl enc -aes-256-cbc -e -pass pass:$PASS > $DATE.enc

# Send email
MTO="mybackupemail@gmail.com"
MSUB="PW backup as on $DATE"
MATT="$DATE.enc"
EMAIL="backuprobot@mymachine.com" mutt -s "$MSUB" -a $MATT -- $MTO < /dev/null

# Write date of backup to file
echo -n $DATE > ./personalwiki/lastbackup
\$\endgroup\$
7
\$\begingroup\$

I would make the following changes, in order to make it more robust, and simpler:

  1. Do not use openssl and symmetric encryption. Use gpg and public key encryption. The advantage is that you do not have to store any passwords, anywhere (keep the secret key away from the backup script). Public key encryption is great for backups (you only need to access your secret key when you have to restore a backup, which happens rarely).

  2. Do not mess around with timestamp files. Instead, keep the last archive around, and inspect the datestamp of the archive (you can use find <path> -ctime 1 to see if the archive is older than 24 hours). If your last backup is too old, make a new one. Keeping one old backup around is another safety line for you, even though it's only stored locally.

  3. Do not use a cron job. Instead, invoke your script from a DHCP exit hook, which is run whenever your computer acquires an IP address. You can then check if you have Internet access, and mail your backup straight away. This also works if you're only online for a short while (and your cron job might miss that window of opportunity).

  4. Check exit codes of everything and make sure your script alerts you somehow if things went wrong. You might need to find a way to interface with your desktop environment in order to do that (notify-send works in Ubuntu).

\$\endgroup\$
6
\$\begingroup\$

This is an incomplete answer; I'd just like to contribute more comments than easily fit in an actual comment.

  1. Consider replacing PASS="mysecretpasswordiwontwritehere" with PASS=`cat mysecretpassword.file` and moving your password into that file. Then you don't have to worry about always remembering to hide it when sharing your source code (or when editing/debugging it).

  2. For the test whether the laptop is online, consider being more specific, such as pinging the actual hostname you need to be up (gmail.com rather than www.google.com). Logging (repeated) failure could be a good idea. Beware of the possibility that Google might decide to disable ICMP ping responses (without stopping email processing). If you want to improve further, you may want to look into testing if the service you use for email submission (SMTP/IMAP/whatever) is up. Maybe (I am guessing) there is an option to let mutt do that for you, such that you aren't effectively doubling email configuration? I'm guessing because I barely know mutt.

  3. Beware that your tar invocation depends on the environment variable TAPE not being set. Unless you're willing to write its sensitive output into a temporary file, specifying /dev/stdout may be the only alternative, though.

  4. You are not logging any other failures, at least not beyond relying on whatever cron is configured to do for you. If tar or openssl somehow aborts, you may be much better off aborting (and logging that there was an error) rather than continuing to the point of logging the date of the completed backup. Likewise in case mutt fails, but that is more complicated since your code may(?) start an asynchronous email transfer. For the rest, you can re-use your code for aborting upon ping failure to detect other errors, ideally logging and aborting (use e.g. exit 1 rather than exit 0 for errors).

  5. Beware that aspects on how this works or fails depend on how cron is configured. Don't accidentally make the mistake of having it run as root rather than under your user account, for example.

\$\endgroup\$
3
\$\begingroup\$

Just a few minor things to add on top of what others already said.

Prefer $(...) instead of `...`

This is the modern way and it's easier to nest:

DATE=$(date +%Y%m%d)

No need to quote inside [[...]]

This will work just fine:

if [[ $LASTBACKUP == $DATE ]]; then
  echo "Do nothing, already backed up."
  exit
fi

And the 0 in exit 0 is redundant, as 0 is the default value.

Simplify exit code checking

Instead of this:

ping -c 1 www.google.com

if [[ $? -ne 0 ]]; then
  echo "Offline, cannot do anything."
  exit 0
fi

I recommend this way:

if ! ping -c1 www.google.com; then
  echo "Offline, cannot do anything."
  exit 1
fi

That is, you could move the ping in the if condition, and in this case I think exit 1 is more appropriate, as not being able to ping is kind of an error/failure.

The trailing newline really bothers you?

Instead of this:

echo -n $DATE > ./personalwiki/lastbackup

This will work just as fine:

echo $DATE > ./personalwiki/lastbackup

So unless you really don't want the trailing newline for some reason, I recommend to simplify by dropping that -n.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.