12 Feb

File server, using Raspberry Pi as the controller

This weekend’s task is to convert an ageing and awkward file server into something more manageable. I’ll do this by basically replacing the current controller (an old laptop) with a Raspberry Pi, and shoving everything into a box.

Firstly, an apology: I’m a hacker, in that when I create something, it will work, but it may not be pretty. I’ll leave the works of art to others, and focus on what I’m good at: solving the current problem with what I currently have at hand.

Here is the old system:

IMAG1051

Four hard-drives, a laptop, a USB hub, lots of cables, lots of power supplies.

The goal is to cut it all down into just one box, one power cable, one ethernet cable.

IMAG1148

So, first, I got an old ATX power supply from a machine I had lying around, and converted it so it didn’t require a motherboard to run.

IMAG1038

To do this, open up the power supply box, remove any cables (leading to outside the box) that are not red, orange, black or yellow, and connect the green cable to one of the blacks (remove some of the insulation from the black cable first, obviously). The green cable tells the power supply that it’s okay to turn on.

IMAG1048

While in there, I cleaned out some of the dust and fluff, and neatened the cables a little. There are four voltages that come from the power supply unit: 0v (black), 3.3v (orange), 5v (red), 12v (yellow). I wasn’t sure how many of each of these cables are actually needed to supply enough current, so when I was pruning cables from the ATX box, I just left these all intact.

Next, I laid out everything in a way that I thought made sense:

IMAG1056

If the cables are removed, then the above will fit into a box that’s 27cm x 27cm x 18cm, with a few millimetres spare on sides and top, and a few centimetres at the back. We need the space at the back so we can re-organise the cables.

So, I cut out the pieces for the box walls. The wood I had (which I scavenged from the attic “floor”) is 1.2cm in width, so the panels I cut are:

  • two 27cm x 27cm, for bottom and top of the box
  • two 27cm x 15.6cm, for front and back
  • two 24.6cm x 15.6cm, for side walls

The last two, I left a little shy of 15.6. Partly because of ventilation, but mostly because I forgot the old adage to measure twice before cutting.

I put the floors and walls of the box together, with wood glue and screws.

IMAG1060

Next, I needed to make the power rails. After a little thought, I decided to use two coat hangers (yes, seriously!), straightened then cut into four lengths of 27cm each. The resistance of each length was between .5 and .7 ohm, so I was pretty sure they’d do fine.

IMAG1059

I drilled holes at the back end of the side walls and put the rails in. I’ve been told since building the thing that this setup may cause electro-magnetic interference – especially as the rails look a bit like aerials. If it causes a problem, I think I’ll need to coat the box with metal.

IMAG1061

Next, I started wiring up the PSU. First, I placed the PSU in the box and measured roughly how long the cables should be, then I cut them about 3cm longer than that, and stripped the extra 3cm of each wire.

IMAG1062

I then tied each wire to a rail making sure the stripped area of the wire was in full contact with the rail. Orange (3v) at the bottom as it’s unlikely to be used, then red (5v), black (earth) and yellow (12v) at the top. In retrospect, the 12v and 5v should be the other way around for neatness, but I didn’t think of that at the time.

IMAG1063

Most hard drive enclosures use a 12v input, so what I did next was to take the hard drive power supplies, snip the cables so they were about 15cm long from the hard-drive end, then strip the ends and wire them up. The cables were wrapped in black plastic, the “live” wire having a broken white line printed along it. I connected the live wire to the 12v rail and the other to the 0v.

IMAG1064

Next, I took the case off the USB hub, and screwed it and the Raspberry Pi to the underside of the case’s box.

IMAG1066

The power supply for the USB hub is 5v, and when I stripped it, I found this was indicated by the live wire (5v) being red. I left this cable longish (about 30cm), so I can hinge the top of the case and open it without disconnecting anything.

Power for the Raspberry Pi is supplied through a micro-USB cable. I scrounged one from somewhere in the house and stripped it down to about 30cm in length. The cable has four wires – black, red, green, white. We only need the red (5v) and black (0v).

Finally, I inserted all the hard-drives with USB cables connected, and hooked it all up. I’ll need to get shorter USB cables to neaten it further, but the original cables will do for now.

IMAG1148

Software-wise, I used Raspbian for the operating system, and ZFS for connecting the drives together and serving to the network as one large hard-drive.

28 Sep

networking a raspberry pi through your laptop

I finally got my own Raspberry Pi; a credit-card-sized computer that’s very cheap and low-power.

It didn’t come with any of the niceties that you would expect from another computer, such as a power supply or a case, or keyboard, monitor, or anything else. Basically, it’s like being given the motherboard of a desktop computer and you need to do the rest yourself.

So first thing was to install the operating system on it. This was easy. Just buy an SD card, and download the ISO of the OS that you want and copy it onto the card.

I already had a micro-SD card from an old Bada phone, so I just stuck that in an adaptor to bring it up to SD card size, then installed the Fedora remix using the Fedora Arm Installer. Painless.

Raspberry Pi connected to laptop using cross-over cable

Next, we need to connect the machine to the network.

My network is mostly WiFi-based, so I chose to hook the RaspPi to the network by piping its network through my laptop.

We need to set up the laptop so it can hand out IP addresses. Install a DHCP server on your laptop. I’m using Fedora, so installed with “yum install dhcp”, then edited /etc/dhcp/dhcpd.conf:

option domain-name "example.org";
option domain-name-servers ns1.example.org, ns2.example.org;
default-lease-time 600;
max-lease-time 7200;
log-facility local7;
subnet 10.5.5.0 netmask 255.255.255.224 {
  range 10.5.5.26 10.5.5.30;
  option domain-name-servers ns1.internal.example.org;
  option domain-name "internal.example.org";
  option routers 10.5.5.1;
  option broadcast-address 10.5.5.31;
  default-lease-time 600;
  max-lease-time 7200;
} 

Then start the DHCP server with “service dhcpd start”.

Next, we need to connect the laptop to the RaspPi. To do this, I made a cross-over cable, and plugged it into the RaspPi.

Before plugging it into the laptop, we need to tell the laptop’s network manager not to set up DHCP over eth0 (as we’re the server, not the client, as far as the cable is concerned). To do this in Gnome, right-click your netowkr icon on the top-right, click Network Settings, and in Wired, click Options, then change the type to “Shared connection” (or whatever sounds like that).

Now plugin the ethernet cable into the laptop, then plug a USB cable into the laptop and the RaspPi.

If you “tail -f /var/log/messages”, you should get something like the below after a minute:

Sep 28 21:12:30 iga dnsmasq-dhcp[30147]: DHCPDISCOVER(em1) 192.168.1.19 b8:27:eb:87:1d:86
Sep 28 21:12:30 iga dnsmasq-dhcp[30147]: DHCPOFFER(em1) 10.42.0.62 b8:27:eb:87:1d:86
Sep 28 21:12:30 iga dnsmasq-dhcp[30147]: DHCPREQUEST(em1) 10.42.0.62 b8:27:eb:87:1d:86
Sep 28 21:12:30 iga dnsmasq-dhcp[30147]: DHCPACK(em1) 10.42.0.62 b8:27:eb:87:1d:86 raspi

That 10.42.0.62 is the address of the RaspPi. You can ssh into it (username root, password fedoraarm), and do stuff!

02 Aug

installing kv-WebME in CentOS 6

I’m setting up a new server and need to install kv-WebME in it. The previous instructions (for Fedora 16) were fine for an older version of WebME, but the most recent requires more uptodate packages.

So, first, we need to tell CentOS 6 to use more uptodate packages than are provided by default.

su -
rpm --import  http://apt.sw.be/RPM-GPG-KEY.dag.txt
rpm -ivh http://packages.sw.be/rpmforge-release/rpmforge-release-0.5.2-2.el6.rf.x86_64.rpm

Now we need to install PHP (at /least/ 5.3), Apache, MySQL, etc.

yum install httpd mysql-server php php-gd php-mysql php-xml git zip unzip

Add a user account for the website, and download the kv-webme repository

adduser webme
su - webme
git clone https://github.com/kaeverens/kvwebme webme
chmod 755 /home/webme
exit

Now add the web configuration to /etc/httpd/conf/httpd.conf

NameVirtualHost *:80
<Directory /home/webme/kv-webme>
  AllowOverride All
</Directory>
<VirtualHost *:80>
  DocumentRoot /home/webme/kv-webme
</VirtualHost>

And finally, turn it all on.

service httpd start
service mysqld start
chkconfig --level 35 mysqld on
chkconfig --level 35 httpd on

That’s it!

27 Jun

Raspberry Pi coming

I got an email earlier today saying that my Raspberry Pi would be dispatched in two weeks.

Exciting! I’m looking forward to getting back into my robot and finally giving it some intelligence.

I’ve been working on this robot idea for years, and always wanted to basically have a very very small programmable bot that could do some things intelligently, such as pick up rubbish, cut weeds, do a little but of mapping, etc.

Now I need to decide what language I’ll use for the programming.

I’ve been doing PHP and JavaScript professionally for about 15 years, but Java and C are probably more appropriate.

I think I’ll be brushing the dust off my C books!

24 Mar

musical intervals trainer, web version

last weekend, I wrote an intervals trainer app for practicing recognising intervals.

I want other people to use it, but haven’t got a Google development account yet so can’t upload an app.

So, today, I improved the app and made a web-accessible version.

try it out!

it’s designed to move up from very simple intervals (major/minor 2nd intervals, with only natural notes) to more difficult intervals (diminished/augmented, with double sharps and flats), but it’s also designed to only get more difficult at a rate that /you/ can manage.

to do this, the app uses a “levels” system, where each level has one more extra type of interval or note type, and you are tested on them. over 50% of the time, the question will be from the level you’re on, and the rest of the time, the question will be randomly chosen from every other level that you have already passed.

get 10 in a row correct, and you go to the next level.

but, get 5 wrong in a row, and you go down a level.

at the moment, there are 24 levels – all the way up to augmented 8ths – can you get through all the levels?

give it a try!

14 Mar

Adding a trigger-based plugin to WebME

One of my clients has his own fork of WebME that he keeps current with the SVN version.

He mentioned a collision recently, where a hack he had written into the online store plugin was overwritten by updates.

So, in this post, I’ll demonstrate how to add a plugin that uses a trigger to run some code.

First, we define what needs to be done.

The client wants that when an order in an online store has been processed, an email is sent out to that client.

This immediately points out where the trigger point goes. The processing of orders is done in the file ww.plugins/online-store/verify/process-order.php, so we need to put the trigger in there as well, at the end of the OnlineStore_processOrder() function:

  Core_trigger('after-order-processed', array($order));

What happens at that point is that the CMS will check all plugins to see if there are any that have a trigger of the name “after-order-processed”, and if there are, then that trigger will be fired.

So, next we need to create the plugin. In fact, it’s so easy I’ll just write it straight out. I’m calling this one “DemoPlugin”, so we start by creating the directory /ww.plugins/demo-plugin”, and placing the file plugin.php in it:

<?php
$plugin=array(
  'name' => 'DemoPlugin',
  'triggers'      => array(
    'after-order-processed'=>'DemoPlugin_afterOrderProcessed',
  ),
  'description' => 'Sends an email after an order is processed',
);

function DemoPlugin_afterOrderProcessed($PAGEDATA, $order) {
  mail('kae.verens@gmail.com', 'subject line', print_r($order, true));
}

Simple!

Now, in order to do this, I edited one file which is part of the “official” WebME package, to add the trigger. If you find you need to do this, please contact me and tell me what you’re trying to do, and what you edited, so I can add it to the SVN version and not break your code in future releases.

05 Oct

installing WebME in Fedora 16

I got a new laptop today, and as I’ve still got work to do, I need to get the system up and running so it can install WebME.

Here’s a minimal instruction set for installing WebME on your Fedora computer.

As ‘root’, run this in a console:

yum install httpd mysql-server php php-gd php-mysql php-pear-Log php-xml svn zip unzip
echo "127.0.0.1 kvwebme" >> /etc/hosts

Then edit /etc/selinux/config, change “enabled” to “disabled” and restart your machine.

Login as your normal user, run this in the console:

mkdir ~/websites && cd ~/websites
svn co https://kv-webme.googlecode.com/svn/trunk/ kv-webme
chmod 755 /home/kae

‘su’ to ‘root’ again. Add this to /etc/httpd/conf/httpd.conf:

NameVirtualHost *:80
<Directory /home/kae/websites>
  AllowOverride All
</Directory>
<VirtualHost *:80>
  ServerName kvwebme
  DocumentRoot /home/kae/websites/kv-webme
</VirtualHost>

And finally, in the console again:

service httpd start
service mysqld start
chkconfig --level 35 mysqld on
chkconfig --level 35 httpd on

That’s it – your system is set up. From that point, just go to http://kvwebme/ and follow the instructions from there!

25 Aug

jQuery maskImage plugin

I had a need today to write some code which involved masking one image with another, dynamically.

There is no simple way to do this in JavaScript. The nearest I came to finding working code online that does it was edge.js, but that’s not free.

So, I wrote my own.

demo

Download: zip (25k), bz2 (24k)

In the demo, what’s happening is that an image such as this:

is being used as a mask for another image:

to create a masked image:

For Firefox and Chrome and other recent browsers, it works using pure JavaScript (even works on the iPhone).

For Internet Explorer (sigh) you need to do a little bit of server-side setup.

Basically, on the server-side, make sure you have PHP installed, with the iMagick extension, then make the ‘cache’ directory writable by the server.

To use, insert an image into your document:

<img id="image-id" src="an-image.png" alt="" />

And then apply the mask using jQuery:

$('#image-id').maskImage({src:"the-mask.png"});

Simple, innit! That’s a few hours of my own life dedicated to saving a few minutes of yours 😉

13 Aug

new API for WebME

As I said in the last post, an API would be required to make the system more testable and more consistent.

I started straight away and wrote up something quickly. Over the next week, it solidified into something that appears to cover any needs that I have.

So here’s how the API works:

Requests are sent to a URL which is generated like this:

/a
[/p=plugin-name]
/f=function-name
[/other-parameters]

The plugin name is optional. Leaving it out means you want to call a core function.

Parameters can be added by adding /key=value pairs to the URL.

An example URL might be this:

/a/f=login/email=kae@verens.com

Sending that, with a POST parameter for the password, will log me in.

To log out, I can use this URL:

/a/f=logout

Simple!

Function Names

mod_rewrite is used to direct a request through a script which tears the URL apart into parameters.

If a p parameter is given, the function is named after the plugin, rewritten to match the WebME coding standard.

For example, if the URL is /a/p=comments/f=editComment, then the “comments” part is rewritten as Comments, and ‘_’ and “editComment” are appended to form the function name “Comments_editComment”, which is called and the result returned to the browser.

For double-barrel plugin names, such as “image-gallery”, the name is rewritten to “ImageGallery”.

If no p parameter is given, then the request is a core function, and “Core_” is prepended to the function name.

For example, the login URL above, /a/p=login calls the function Core_login.

If a function name begins with “admin”, it is an admin function (see below for more on this).

File names

If no plugin name is supplied, then the core API file, /ww.incs/api-funcs.php is loaded. This contained common API functions that might be used by any core script or plugin.

If a plugin name is supplied, then the API file is expected to be located at /ww.plugins/plugin-name/api.php for common functions, and /ww.plugins/plugin-name/api-admin.php for admin functions.

For core functions, common functions are at /ww.incs/api-funcs.php and admin functions are at /ww.incs/api-admin.php

Security things

Having a central point for RPC means that we can apply security rules in one place and know that they cover all scripts. Before-hand, I would sometimes come across scripts and realise that they were open for abuse if someone knows that magic URL incantation. I would silently curse myself or whoever had written the script and fix it up. Now, though, having one single point of entry means I can secure everything at once.

If a function name starts with “admin”, then the script checks to see if the user is logged in and is a member of the administrators group. If not, the API will return an error. It’s as simple as that!

Of course, this doesn’t stop abuse by people that are logged in as admins or who are victims of XSS, but it helps stop a few problems caused by developers not noticing their scripts were open to use by anyone at all.

Conclusion

So now, when people are creating new plugins for WebME, the following could be used as a bare-bones directory structure:

/ww.plugins/plugin-name/plugin.php    details, server-side functions
/ww.plugins/plugin-name/api.php       common RPC functions
/ww.plugins/plugin-name/api-admin.php admin RPC functions
/ww.plugins/plugin-name/admin.js      admin scripts in JS