Thursday, 24 October 2013

BlueStacks Android Emulator

Android Emulation

My new TV has an android and iOS App as an alternative to the old fashioned remote control. I sometimes use this on my iphone or ipad, but because the App can also be used to stream what's on the TV, I want to run it on my Laptop too.

When I started looking into this I discovered you could run an Android disk image in a VM such as VirtualBox. But I carried on looking for a hassle free way and eventually found an emulator called BlueStacks App Player. The best thing is that it's available for both Windows (XP, Vista, Win7/8) and Mac OSX (which is still in Beta right now) and it's FREE.

Installing BlueStacks

After downloading the install went easily, but it took some time to start up. So go and make yourself a cup of tea or something, you've got something like a 5 minute wait.
BlueStacks Loading Screen - Takes A While

Once initialised you come to a My App screen where you'll see there's a few popular apps preloaded. But click on the Top Charts icon to start adding new ones.

If you're running a laptop with a standard trackpad then you can scroll up and down through the lists quite easily, but you might have problems if you use a mouse. I struggled a bit with my aging Mighty Mouse because the little scroll ball doesn't work very well anymore.

(I couldn't find any other way of page scrolling)


The Charts Screen - Lets Add Some Apps

Click on the search icon (the blue magnifying glass) you'll find all of the apps that you could ever want (and loads more besides). Sadly my TV app refused to install but I did find that most of the popular games I tried worked fine.

As soon as you make a selection BlueStacks will take you through the login or registration process. I then turned off the automatic account syncing because I don't have any other Android devices (although it listed my old phone in the Google account details as a registered device).


Angry Birds - The Defacto Test

Generally it works great (even if my TV app was a no-go) so I went on to install it on the kid's Mac Mini. I left them playing "Cut The Rope" as most things run ok with a mouse, and then half an hour later I noticed they'd installed and were playing other games.

It's that easy to use, it's kid's play!

I hope this has been some use to those who want to run a android apps on their laptop or desktop computer. It's easy to do and completely free, so don't waste any more time, lets get those green piggies.

Monday, 14 October 2013

Minecraft Server Log Web Interface

Introduction

If you run a Minecraft server in Linux then you'll probably like to keep track of what's going on in the server log. I found that it was a real pain having to remote shell into the server all the time and if it was available via a web browser I could keep track of it more conveniently, or by using my phone or tablet.

I decided that I would use PHP (even though PERL is generally my scripting weapon of choice) and once I'd found the correct commands and syntax it came together quite quickly. If you fancy giving it a try you just need a web server (such as Apache) and of course PHP running on your server.

How it works

It started out being a simple process of opening the server.log file and outputting each line as pre-formatted html.

Something like this..

<html>
<head><title>Minecraft Log</title></head>
<body><pre>
<?php
$filepath = "/opt/minecraft/server.log";
$file = file($filepath);
foreach($file as $line) {
    echo $line."<br/>\n";
}
?>
</pre></body>
</html>

This was great at first, but after we'd been running for a few weeks the server.log file gets too big and the web page gets slower and slower to load. What we needed was a way of only showing the current days log entries, and maybe a date selector so you can reference older stuff.

So I created version two with the following enhancements:-
  • Default to showing today's log entries.
  • A date selector is added to the top of the web page which sends the new value if altered.
  • If page requested with a date parameter then use this instead of today's date.
  • If no date has been passed then run a javascript function to scroll the log section to the bottom.
There's a nice feature in the log where text is coloured. This would typically be for chat or system warnings, but as it stood they left messy control codes littered about the log. Rather than remove these I decided to replace them and style the text to match these colours.

This enhancement required the following:-
  • Create a series of regular expression replaces to match the control codes.
  • Each colour code instance is replaced by a span tag which corresponds to a set of pre-defined styles (the css for these is defined in the head section).
  • Put log into a scrollable div.
Here's the completed code..

<!DOCTYPE html>
<html>
<head>
<title>Minecraft Log</title>
<style>
    #log{
        height:400px;
        overflow-y: scroll;
        margin: 5px 0 0 1px;
        border: 1px solid;
    }
    .green{color: green;}
    .red{color: red;}
    .purple{color: purple;}
    .black{color: black;}
    .blue{color: darkblue;}
    .gold{color: gold;}
    .cyan{color: darkcyan;}
    .aqua{color: cadetblue;}
    .gray{color: #888;}
    .bold{ font-weight:bold;}
</style>
<script>
    function scroll(){
    //scroll div to bottom
    var objDiv = document.getElementById("log");
    objDiv.scrollTop = objDiv.scrollHeight;
    }
</script>
</head>
<?php
$pass = htmlspecialchars($_POST["date"]);
if (!$pass){
    echo "<body onLoad='scroll()'>";
}
else {
    echo "<body>";
}

$filepath = "/opt/minecraft/server.log";
$file = file($filepath);
$dates = array();
$last = "";


/* Get List Of Dates */
foreach($file as $line) {
    $date = substr($line, 0, 10);
    if ($date != $last){
        if(preg_match('/\d{4}\-\d{2}\-\d{2}/',$date)) {
            array_push($dates, substr($line, 0, 10));
            $last = $date;
        }
    }
}

/* If Date Provided Use This */
if(preg_match('/\d{4}\-\d{2}\-\d{2}/',$pass)) {
    $last = $pass;
}

/* Add Form Element and Date Selector */
echo "<form method='post'>Select date: <select name='date' onchange='this.form.submit()'>\n";
foreach($dates as $value){
    $select = "";
    if ($value == $last){ $select = " selected='selected'"; }
    echo "<option value='".$value."'".$select.">".$value."</option>\n";
}
echo "</select></form>\n";

/* Output Log For Required Date */
echo "<div id='log'>";
foreach($file as $line) {
    /* Remap Problem Characters */
    $line = preg_replace("/</","&lt;",$line);
    $line = preg_replace("/>/","</span>&gt;",$line);
    $date = substr($line, 0, 10);
    if ($date == $last) {
        /* Remove Unrequired Formatting Codes */
        $line = str_replace("[m","",$line);
        $line = str_replace("[21m","",$line);
        $line = str_replace("[3m","",$line);
        /* Split Log Line Into Sections to Using First Formatting Code Style */
        $segarray = preg_split( '/(\[0|\[m)/', $line );
        for ($i = 1; $i < count($segarray); ++$i){
            /* Do Replace to Add Styled Spans */
            if (preg_match('/;\d{2};\d+m/', $segarray[$i])) {
                $segarray[$i] = preg_replace("/;30/","<span class='black",$segarray[$i]);
                $segarray[$i] = preg_replace("/;31/","<span class='red",$segarray[$i]);
                $segarray[$i] = preg_replace("/;32/","<span class='green",$segarray[$i]);
                $segarray[$i] = preg_replace("/;33/","<span class='gold",$segarray[$i]);
                $segarray[$i] = preg_replace("/;34/","<span class='blue",$segarray[$i]);
            $segarray[$i] = preg_replace("/;35/","<span class='purple",$segarray[$i]);
                $segarray[$i] = preg_replace("/;36/","<span class='aqua",$segarray[$i]);
                $segarray[$i] = preg_replace("/;37/","<span class='gray",$segarray[$i]);
                $segarray[$i] = preg_replace("/;22m/","'>",$segarray[$i]);
                $segarray[$i] = preg_replace("/;1m/"," bold'>",$segarray[$i]);
                $segarray[$i] = $segarray[$i]."</span>";
            }
        }
        /* Rejoin Then Split Log Line Using Second Formatting Code Style */
        $line = join("",$segarray);
        $segarray = preg_split( '/§/', $line );
        for ($i = 1; $i < count($segarray); ++$i){
            /* Do Replace to Add Styled Spans */
            $segarray[$i] = preg_replace("/^0/","<span class='black'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^1/","<span class='blue'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^2/","<span class='green'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^3/","<span class='aqua'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^4/","<span class='red'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^5/","<span class='purple'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^6/","<span class='gold'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^7/","<span class='gray'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^8/","<span class='gray'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^9/","<span class='blue'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^a/","<span class='green'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^b/","<span class='aqua'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^c/","<span class='red'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^d/","<span class='purple'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^e/","<span class='gold'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^f/","<span class='black'>",$segarray[$i]);
            $segarray[$i] = preg_replace("/^r/","<span class='black'>",$segarray[$i]);
            $segarray[$i] = $segarray[$i]."</span>";
        }
        /* Rejoin and Output to Webpage */
        $line = join("",$segarray);
        echo $line."<br/>\n";
    }
}

echo "</div>";
?>
</body>
</html>

nb. Notice the log must now be read twice!

Installation Instructions

Copy the code into a file called index.php and drop it into a directory accessible to your web server and ensure you grant the file execution rights.

Alter the $filepath = "/opt/minecraft/server.log"; line so that it points to your server log and ensure the web server has read rights to this file.

How to Use

Just open the PHP code in your browser and the a page should open showing today's log entries, scrolled to the bottom of the log.

If you then select an earlier date the page will refresh and show this dates entries without the auto-scrolling.

Friday, 4 October 2013

OSX Time Machine kills old disks

Round 1

Earlier this year I decided to replace the disk in my Macbook Pro with an SSD. The HDD was only a year old but I was already having problems that required frequent fixing in DiskUtility. Also those odd clicking noises it was making were making me feel nervous.

I decided to backup asap (once I'd fixed the disk again) and set about manually copying my data to an external disk in the finder window. After is kept failing on a bad block I decided to try and update my Time Machine backup instead. It ran for a while and then the machine froze, forcing me to reboot the machine. I then found that the disk was corrupted and Diskutility could see it but refused to touch it.

I bought a copy of Stellar Phoenix Mac Recovery because the demo version showed all my files listed, but after recovering about 200Gb they all proved unusable. Other attempts have proved equally unsuccessful with days of processing not finding any files what-so-ever. My experience of buying recovery software has proven to be a huge waste of money! (I found similar trying to recover ReiserFS partitions)

So I ran fsck (details here) to see if it would fix it..

admin$ fsck /dev/disk1s2 -f

BAD SUPER BLOCK: MAGIC NUMBER WRONG

LOOK FOR ALTERNATIVE SUPERBLOCKS? [yn] y


CANNOT READ: BLK 16585216

CONTINUE? [yn] y

THE FOLLOWING DISK SECTORS COULD NOT BE READ: 16585216, 16585217, 16585218, 16585219, 16585220, 16585221, 16585222, 16585223,

CANNOT READ: BLK 567944160

CONTINUE? [yn] y

THE FOLLOWING DISK SECTORS COULD NOT BE READ: 567944168, 567944169, 567944170, 567944171, 567944172, 567944173, 567944174, 567944175,

SEARCH FOR ALTERNATE SUPER-BLOCK FAILED. YOU MUST USE THE
-b OPTION TO FSCK TO SPECIFY THE LOCATION OF AN ALTERNATE
SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8).

So that didn't work then! (I haven't got a clue where I'd find an alternative super-block.)

Round 2

This last weekend I bought a new USB portable disk unit to use as a new Time Machine volume. My wife's Macbook hadn't been backed up since earlier this year so I decided that was the first machine to try it out on.

I hooked up the drive, kicked off Time Machine and left it running. I noticed after about an hour that it was stuck at 48% so I attempted to kill it and start again, but the Macbook refused to respond. In the end I rebooted the machine but wasn't able to get past a blank grey startup screen. I booted from a startup CD can ran DiskUtility but it refused to see the internal disk.

In the end I replaced the HDD (which was 4½ years old) and recovered from her old Time Machine backup. So that's another internal disk that Time Machine pushed over the edge, and this time it refused to show as a drive.

I'm definitely reaching the conclusion that you shouldn't use Time Machine if you have any doubts over the integrity of your hard disk. Make sure you backup regularly and you'll not lose much.

You do backup don't you?

Update (10th Oct)

I've found that Disk Warrior 4.4 can cope with a faulty disk and will mount this disk in read-only mode so that you can copy off your files. It wasn't without it's problems though, the failing disk causing disk copies to occasionally hang on certain files.

Monday, 19 August 2013

PermissionsEx: Anti-griefing methods for Minecraft

More Grief

If you've already installed and configured Grey Lists using PermissionsEx (as described in my earlier post) then you are well aware of the protection this provides against those pesky griefers. Yeah I know you can white-list your server but this helps you want to showcase your work without risk.

My son can be a little too trusting, and far too keen to attract new builders, and it always seems to be when we have friends of friends connected that trouble starts brewing. A few days ago the inevitable happened, a school pal and his friend paired up for an orgy of TNT abuse, dropping as many buildings as they could in full view of the regular builders. The server was downed soon as he figured out what was happening and the permissions.yml file altered to remove the offenders building rights, but ultimately damage had been done. One of the longest standing buildings was hit so hard that it was reduced to a crater, all in about a minutes chaos!

PermissionsEx Revisited

All the griefing we've experienced so far has been the result of fire or TNT, with the latter obliterating buildings well beyond repair. It made sense after this attack to limit their use, removing them from the standard 'builder' groups rights, but after well over an hour of altering permissions and testing we had only managed to remove the rights to place TNT. It's really a case of trying different modifyworld  restriction in the permissions.yml file until you get a result.

The first thing to be aware of is the order of the permission lines is important. The file is scanned top to bottom and as soon as a match is found it stops. This means restrictions must go before grants using wildcards or they'll never be reached.

Builder:
      prefix: '&0(&8Builder&0)&7 '
      permissions:
      - -modifyworld.blocks.place.46
      - -modifyworld.bucket.empty.10
      - -modifyworld.bucket.fill.10
      - -modifyworld.items.pickup.259
      - -modifyworld.items.craft.259
      - -modifyworld.items.use.259.on.block.*
      - -modifyworld.items.use.259
      - modifyworld.*
      options:
          rank: '900'

nb. Our restrictions go before modifyworld.* and use the '-' minus symbol to differentiate them from grants. The objects are listed by item/block id (46 = tnt, 10 = lava, 259 = flint and steel)

Now when TNT (46) is placed by a builder the block gets picked straight back up again, similarly the restrictions on flint and steel (259) don't allow you to pick it up or use it. The lava bucket (327) lines need to restrict filling and emptying of a liquid, in this case lava.

You will also need to enable some options in the modifyworld.yml before this works.

item-restrictions: true
use-material-names: false
drop-restricted-item: true
item-use-check: true

I set use-material-names to false so that I could use item and block numbers, but you can leave this set to 'true' and use proper names. Then your restriction would read as per the example below:-

   - -modifyworld.blocks.place.TNT

Don't mix the two up or your permissions won't work. Also be aware that the plugins are very sensitive to formatting errors, so check your server log after changing and if necessary run your config through a YAML file validator.

In the mean time I'll work on the fire problem & if anyone has any suggestions then please comment.

Tuesday, 6 August 2013

Automatic for the People - Part 2

Down in the Dumps

I run a few systems that use databases and these (I'd assumed) were being backed up using my weekly backup script. But just copying the system files while the database was running proved to be a great way of losing the last few records should you need to restore. And I'd invariably be forced to run myisamchk on the recovered database before mySQL was happy.

After this happened a few times I started dumping my databases on a daily basis. A dump is not something nasty, but a flat file containing the database structure and contents in an SQL format.


- Example for mySQL
For each database you have add a line as follows to your backup script:-

mysqldump wiki --password=iliketurtles --add-drop-table > /var/backups/wiki.sql

Here I'm dumping my wiki database and it assumes I have an admin user for mySQL called root with a password as 'iliketurtles'. For convenience we'll make it add drop table commands, and recovery would be as simple as:-

   pingu:=# mysql -u root -p wiki < wiki.sql

NB. You would need to ensure is that a database called wiki exists or it will thrown an error, but an empty one will do.

- Example for PostgreSQL
For each database add a line as follows to your backup script:-

pg-dump -Fc davical > /var/backups/davical.pgdump

This also assumes you have a superuser called root in Postgresql.

NB. Again the database must exist before you can recover, then you would just enter the following command:-

   pingu:=# pg_restore -d davical davical.pgdump

So two very different databases engines taken care of. Just duplicate these commands for all the databases you want to backup.

Daily Increments

The weekly backup I described last time has served me well for many years, and the few times that I've needed to recover anything have been a success. But more recently my friend Grunders (who'd been storing open office files in his home directory over a VPN) lost his files when the hard disk failed. I offered him the backup from the weekend but he said it was no good because he'd made too many changes.

Luck was with him though, that old trick of turning off the computer and powering back on brought the drive back to life long enough to retrieve his missing files. However we went on to convinced ourselves that we needed a daily incremental backup. My Initially attempt at doing this simply failed as I'd tied myself in bash script syntax knots. But a few days later Grunders came to the rescue with the following code:-

SOURCE=/home/
TARGET=$(date +%Y.%m.%d-%H%M)
RUNTIME=$(date +%Y%m%d%H%M)

cd "$SOURCE"
IFS="
"
LIST=`ls`
for i in $LIST
do
  DirUser=$(echo "$i")
  echo $DirUser
  cd /home/$DirUser
  find * \( ! -regex '.*/\..*' \) -newer /root/REFTIME -print0 | xargs -0 tar --no-recursion -cpf /DailyBackup/$DirUser/$TARGET.tar &gt;&gt; /root/dailyincbackup.log
  if [ -e /TimeMachine/DailyBackup/$DirUser/$TARGET.tar ]
  then
    mkdir /TimeMachine/DailyBackup/$DirUser/$TARGET
    tar -xvf /DailyBackup/$DirUser/$TARGET.tar -C /TimeMachine/DailyBackup/$DirUser/$TARGET
    rm /DailyBackup/$DirUser/$TARGET.tar
  fi
done
touch -t $RUNTIME /root/REFTIME

It's actually a little bit odd as the script is using find to locate files that have changed since the date held in /root/REFTIME and create a tar file of the changes. (The regex part removes hidden files, such as those that Apple Mac's add) Then to avoid tar files he's untarred the resulting file and deleted it. Obviously this is not ideal, but there is an alternative way that uses a cp command:-

find . \( ! -regex '.*/\..*' \) -newer /root/REFTIME -exec cp -Ra {} /DailyBackup/$DirUser/$TARGET \;

The receiving directory must exist for this to work, and it may leave empty directories if no changes where detected, so I'll leave the script as it is for now.

Thanks G, all it needs now is something to purge old files.

Sunday, 4 August 2013

Minecraft Grey Lists

Anti-Griefing

A few weeks ago my son's Minecraft world became trashed (buildings completely obliterated) by friends of a friend who visited our server. Thankfully we had a backup to restore from and then to ensure it didn't happen again we turned on white lists in the server.properties file.

But the story doesn't end here... a few days ago number one son asked me if I knew anything about grey lists, informing me roughly what they're supposed to do. Apparently if you set up a grey list then it enables anyone to join the server but they're restricted from building. This was new to me and seems ideal, meaning he'd be able to pass about the server connection details freely without worrying about being griefed again.

Grey Lists

The vanilla server only supports black lists (to ban people) or white lists (to only allow access to those listed). There's no setting in the properties file to enable grey lists, so I googled until I found that a plugin called PermissionsEx. This enables you to define access groups with appropriate rights, and assign users to them.

But you can't add plugins to the vanilla version of Minecraft server,.. there's no plugin directory, instead you have to use the CraftBukkit server instead. (Click here for download page)

While I'm at it, here's the link for the PermissionsEx plugin. (Click here for download page)

Installing Bukkit & PermissionsEx

Here's the steps we went through:
  1. Edit your server.properties file and disable white-lists if you where using them.
  2. Download the craftbukkit jar file and copy it into your minecraft server directory.
  3. Run the craftbukkit server program to create the extra files and folders (it also converts your world files).
  4. Download the PermissionEx jar files and copy these into the newly created plugin directory.
  5. Restart the server again and a PermissionEx sub-directory will be created.
  6. Change to this directory but leave config.yml alone.
  7. Edit the permissions.yml file and replace the contents with the following lines, then add your users into the end section (in line with the examples).
groups:
    default:
        default: true
        options:
            rank: '1000'
        permissions:
        - modifyworld.chat
    Builder:
        prefix: '&0(&8Builder&0)&7 '
        permissions:
        - modifyworld.*
        options:
            rank: '900'
    Moderator:
        prefix: '&0(&1Moderator&0)&7 '
        permissions:
        - -modifyworld.mobtarget.*
        - modifyworld.*
        options:
            rank: '100'
    Admin:
        prefix: '&0(&4Admins&0)&7 '
        permissions:
        - -modifyworld.mobtarget.*
        - modifyworld.*
        - permissions.*
        options:
            rank: '1'
users:
    SomePlayerName:
        group:
        - Builder
    YourPlayerName:
        group:
        - Admin

NB. Ensure you don't add any spaces before 'groups' or 'users' or the file will be ignored.

We found that if your name is in the ops.txt file then you automatically get admin level.

Follow this link for further details on configuration and how to type /pex commands when in the game.

Finally I updated the /etc/init.d/minecraft startup script to use the CraftBukkit jar file.

Thursday, 1 August 2013

Automatic for the People - Part 1

DYBB

Many, many years ago I took over a Support Manager role at a snack factory and I inherited my predecessor's office. Chris (who I'd met a few years before when I'd worked there in my summer holidays) now worked at head office, but was around a few days a week to help me learn the systems. He'd left me a lot of his old stuff and there was an A4 (approx US letter size) page stuck to the wall above the HP Deskjet 500 printer that just had the letters 'DYBB' filling the page. I pondered for a while what this should mean (I was sure it was nothing to do with boy scouts and "Doing Your Best") and eventually I just asked him.

Chris smiled and replied, "That what I tell all my users... Do Your Bloody Backups!"

Of course this was back in the days of 386 and 486 computers running DOS, and hardly anyone stored files on the Novell file server. Therefore there was a portable Colorado Trakker tape backup unit that the finance departments looked after, and it was up to the few PC owners to run weekly backups to QIC tapes. Some did, some didn't, and many just relied on floppy disks.

Technology has a way of picking the least opportune moment to catch us out (see my previous blog entry). Over the last ten years of running a home server I've learnt that if you're passionate about keeping your data, then backups need to be automatic.


Keeping it Simple

I took the decision many years ago that I'd just perform a backup once a week for my /data, /etc, /var and /home directories. I have a 2Tb backup disk formatted with a 50Gb partition, with another partition for the remainder. There are no entries in the fstab file so it's not mounted automatically, and it uses standard EXT3 formatting.

1 - Backup of data is just a straight copy of new and updated files onto the bigger backup partition. The disk is temporarily mounted to /mnt and then a simple copy refreshes the files and directories within it. It does have a few disadvantages but I love the simplicity of it, and so far it's served me well.

Here's the bit of script that does this:-

Drive="/dev/sda2"
mount -t ext3 $Drive /mnt
# CHECK MOUNTED OK
OK=`df | awk '/[ \/]mnt/ {print $1}'`
if [ "$OK" = "$Drive" ]; then
  cp -Rvu /data /mnt
  umount /mnt
  sleep 120
  hdparm -y /dev/sda
fi

It's using df and awk to check to ensure the drive gets mounted OK before continuing, then cp to copy the data. At the end it unmounts the drive and uses hdparm to put it to sleep.

2 - The Etc, Var and Home directories contain files where I wanted to keep a few versions because file updates are common. So here I mount the smaller backup partition and implement a Grandfather, Father, Son approach to the problem. Son is a directory containing a full copy of the files, which then gets GZ Tarred the next week to become the 'father', and later 'grandfather' file. I hate untarring files so it means if needed I can retrieve recently versions of files conveniently from the /son directory. In practice I've hardly ever had to resort to plundering the older tar files.

Here's the bit of script that does this:-

Drive="/dev/sda2"
# CHECK MOUNTED OK
OK=`df | awk '/[ \/]mnt/ {print $1}'`
if [ "$OK" = "$Drive" ]; then
  # if exist father, rename to grandfather
  cd /mnt/
  if [ father.tar.gz ] ; then
    mv father.tar.gz grandfather.tar.gz
  fi

  # if exist /son then tar to father.tar.gz

  if [ son ] ; then
    tar -zcf father.tar.gz son/*
    rm son -R

  fi


  mkdir son

  cd son
  mkdir etc
  mkdir var
  mkdir home
  mkdir root

  cp /etc/* /mnt/son/etc/ -a

  cp /var/* /mnt/son/var/ -a
  cp /home/* /mnt/son/home/ -a
  cp /root/* /mnt/son/root/ -a

  # Unmount drive

  cd /
  umount /mnt
  sleep 120
  hdparm -y /dev/sda

fi

Again it's using df and awk to ensure the drive gets mounted, then performs the grandfather, father, son backup as described above. Finally it unmounts the drive and puts it to sleep.

I've completely ignored database backups and incremental backups for now, I'll continue with that next time.