The lead developer at Mosaic, Brighton with a passion for web application development and motorcycles.
I have ended up with a very old piece of hardware and of course the first thing I did was wipe the Windows 2000 installation and stick a few versions of Linux on it. Unfortunately it only came with 128MB of memory from factory so nothing would run very well. The PCMIA wireless card that came with it wouldn’t work with WPA2 under Windows 2000 so an upgrade was required.
£5 later I got a matched pair of Crucial 256MB sticks on ebay so I could try out Linux Mint 6 Fluxbox edition and Linux Mint 10 LXDE. Whilst both worked right out of the box I did have to configure the X11 monitor settings so that it would support the full 1024x768 splendour that the Inspiron 2500 affords you. See the gist I have setup on github for my configuration file and some short instructions on getting it setup.
In the end I decided to go with Mint 10 LXDE as it used a similar level of system resources, but ran more smoothly and of course benefited from being the latest version with full package update support.

I expected to have a load of issues getting the Belkin wireless adapter working, but in the end this distro had all the drivers so need for all those ndiswrapper recipes that are out there. It is running much better than the Windows 2000 OS was as well and benefits from having and running newer versions of all the applications I need.
Of course web streaming of Flash video is somewhat staccato with such a small CPU but it is a perfectly usable web browser and word processor.
External Link: Why won't ssh-agent save my unencrypted key for later use?
I recently was annoyed by always having to enter my private keys passphrase every time I wanted to do a git push to or pull from a public git repository. Turns out that if you are logged into a Gnome session on an Ubuntu machine it will automatically add you key to ssh-agent, but if you are logged into a bash session (as I was) then it won’t.
So you can either manually do the ssh-add yourself or following the instructions in the answer to my question you can setup an automatic way of facilitating this.
One problem I discovered is that if you have git displaying the current branch information in your bash prompt like me then when you start a session it will ask you for your passphrase before rendering your bash prompt.
I am thinking that to work around this I could change the git function in the .bash_profile file to look at the arguments passed to it and if it is a remote operation such as a pull, push or clone then trigger the ssh-add otherwise it can safely skip it.
Any other ideas or patches?
External Link: php_ssdeep Fuzzy Hashing PHP Extension
Updated 16/9: php_ssdeep is now in PECL so I have updated this post to reflect that.
On a recent project I needed a fast way to compare documents for likeness and return a percentage match. With much research and one unanswered Stackoverflow post later I came across Jesse Kornblum’s ssdeep utility intended for computer forensics such as looking for signatures in files when hunting rootkits etc. All the technical details of fuzzy hashing are described in his 2006 journal article Identifying almost identical files using context triggered piecewise hashing.
I was using a wrapper around the ssdeep binary written in pure PHP, but I recently decided to write my first PHP extension by tying into the fuzzy hashing API ssdeep provides. The result is now a PECL extension or module and the BSD licensed source code is hosted on PHP.net’s SVN.
There are full instructions provided in the PHP manual and the php_ssdeep site to install the exension and use the provided functions. If you end up using this extension or its code I would be very interested to find out more about your project.
A project I am working on at the moment requires time delayed job queues and having found nothing yet that can manage it properly so I decided to wrap up `at` into a PHP class. This gives you simple methods to add, list and remove jobs from the `at` queue using object oriented code.
The code is very simple and I have documented it reasonably well so along with the examples you should get on your way quickly. The class can, of course, be used from the command line as well so if you want to write batch scripts in PHP to handle adding a collection of predefined `at` jobs for example - it can make it easier.
I feel the important features of `at` have been added into the code, but if you want to wrap any of the other functions then please do fork my code and make a patch or post a pull request. For more information on what `at` can do please either run `man at` in your console or visit the Edinburgh University’s man at page.
You can get the code from my repository on GitHub: PHP-at-Job-Queue-Wrapper.
If you do have any trouble getting the `at` daemon (atd) going then my previous post may help you debug it - please see If you are having problems getting Ubuntu atd running for more on that.
External Link: Batch remove extensions in Ubuntu
Sometimes you will want to batch remove extensions from a load of files:
for i in $(ls *.png); do mv $i ${i%.png}; done
If you want to remove extensions from files with a .txt extension then you would replace the two instances of .png in the script above with .txt.
You can take the extension off of all files using the following:
for i in $(ls *.*); do mv $i ${i%.*}; done
I also extended it by using it for a batch change of extensions rather than just removing the extension:
for i in $(ls *.html); do mv $i ${i%.html}.htm; done
Note the extra .htm after the curly braces in the above command.
If you just cannot get atd to start running check the permissions on your /var/spool/cron/atjobs and /var/spool/cron/atspool directories. The should be `chmod 770` and then `chmod +t`. Also they should be owned by `daemon:daemon`.
I was getting this error when attempting to set new at jobs:
Can’t open /var/run/atd.pid to signal atd. No atd running?
If I tried to start the daemon through the service management I was getting:
sudo service atd start
start: Job is already running: atd
But running `ps -elf | grep ‘atd’` yielded no appropriate results.
When trying to start the atd daemon manually on the command line I was getting:
simon@forge:~$ sudo atd -d
Cannot change to /var/spool/cron/atjobs: Permission denied
Which finally gave me the clue!
As you have found this page I am sure you have accidentally hit the control+s short cut whilst inside a PuTTY shell and following that no keystrokes appear to affect the session. Basically hitting ctrl+s causes PuTTY to stop executing the stream coming in from the keyboard. It does however still listen to your keystrokes and it basically adds them to a queue.
Hitting control+q will re-open the stream execution, but it is worthwhile noting that it will also execute all the queued up commands as well!
So far I have tried out Ubuntu, eeeBuntu, EasyPeasy Linux and CrunchBang Linux (all of which are Debian based) on the Asus Eee PC. Ubuntu was a bit of an annoyance to get setup and it was troublesome trying to get all the buttons or the sound and microphone to work so I then tried EasyPeasy. It was easy to install (it already contains the Array.org kernal) and I liked the NBR interface with its easy to use tabbed system not to mention that after using Ubuntu it was nice to be able to see most of the system dialogues on screen (Ubuntu’s dialogues were so long they fell off screen! – Hint: hold down alt and click to move dialogues without titlebars). The major problem I had with it was that it took way too long to boot and came preloaded with a whole host of applications I will never need.
So after some research I saw a lot of good things being said about eeeBuntu, which again in the version I chose uses the NBR interface. Also whilst it was very nice to use not everything worked, which was very annoying. The microphone and some of the shortcut buttons did not work, which is useless if you need to use Skype. The selection of preloaded applications was also not to my tastes (although better than EasyPeasy) and boot time was also quite painful although not as bad as EasyPeasy. Speaking of boot times make sure you remove any SD cards from the onboard slot before booting the machine as this will add significantly to the load time of the OS.
After using eeeBuntu for some time I finally grew tired of the lag and decided it was time to do some more research into the available packages for easy installing on the Eee PC. This is when I came across CrunchBang (#!) Linux, which is a very light weight version of Ubuntu running the Openbox shell. This distro boots quickly, comes with a nice set of applications that I can easily build upon. I am really pleased with the OS so far and I am even writing this post from within it.
Installation is very simple just use UNetbootin to transfer the Cruncheee image to a USB key and boot from the USB key to try out the ‘live disc’ version of the OS to see if it is for you. If you like it then right click on the desktop and choose install from the resultant menu.
Not everything will work right of the box though. The microphone does not record immediately and requires some tweeking to the Volume Control interface. You can bring up the interface by pressing the shortcut Super + v (in my case the Super key is emblazoned with the Windows logo). Now click the preferences button at the bottom of the dialogue and check the boxes beside Front Mic Boost, Capture and Input Source. Now in the playback tab unmute Front Mic and raise the volume, on the recording tab unmute the microphone icon and raise the volume and finally on the options tab choose Front Mic from the drop down.
Unfortunately there is one further annoyance. The volume buttons do not bring up the on screen display but Super + v will bring up the mixer which is fine for me at the moment.

Sun VirtualBox Logo
Often Linux just does it better! Often I find myself developing a Windows machine without access to a Linux development server, but I still need to access to some of the Linux binaries and features such as cron jobs, the at command and binaries such as imagemagick, pdftotext, etc. Some things can be emulated with ported binaries or through Cygwin, but I feel a lot more comfortable developing on a platform that is representative of the live server the web site will run on.
I have not removed XAMPP as it is very useful for developing small scripts or sites without the overhead of running a virtual machine. Therein lies the problem with this solution – it is virtual machine based and that will mean your local development machine will suffer when the virtual machine steals computing time from the CPU or more memory. So whilst I can use XAMPP on my netbook I am not so sure a VM will run smoothly.
The idea behind this step is to minimise the time it takes to setup a new host on the server. Basically all you have to do is create a new directory an away you go. (Don’t forget it will still need to appear in your hosts file).
VirtualDocumentRoot sets the directory that the VHosts public directory is contained in. This will basically convert http://subdomain.localhost/ to /mnt/htdocs/subdomain and pull the relevant files through.
A side note when using this method that you should be aware of. This will affect your rewrite rules if they are placed into a .htaccess file. To avoid any problems always declare the RewriteBase rule in your .htaccess.
For example:
Nathan brought a glaring omission from my post to the fore – thanks! Currently you maybe having permissions issues with your shared folders because they might be being mounted as root:root. To get them to load with a specified user and group you will need to edit your /etc/fstab file again and change ‘defaults‘ to be ‘uid=username,gid=groupname‘ – an example would be ‘uid=simon,gid=www-data‘.
A full line example would be from:htdocs /mnt/htdocs vboxsf defaults 0 0
tohtdocs /mnt/htdocs vboxsf uid=simon,gid=www-data 0 0
In the PHP development arena the LAMP (Linux, Apache, MySQL and PHP) stack is very common, but once in a while a client will come through the door with a Microsoft background. So what do you do if your CMS or framework was built with a Linux base layer in mind? Sounds easy, but what if you have jobs loaded into your Linux crontab for processing mail outs or after hours records processing?
There are options for implementing cron jobs with a Windows server, one of which is to offload your cron jobs to a Linux server and use WGet to remotely activate the processing script on the Windows server. For more information on setting up cron jobs on a Linux box please see Introducing Cron. Whilst this does work well, what if the Linux box goes down or its internet connection is severed and your scripts on the Windows server do not run? Or perhaps you do not have a Linux box to hand or you don’t feel comfortable sending cron job requests across the web.
Well you are in luck because Windows does offer a way to run cron jobs although its termed Scheduled Tasks by Microsoft. You will also need a small Windows binary that performs all the functions of the Linux WGet programme. I use the WGet for Windows utility compiled by Christopher Lewis. I usually stick it into the Windows Programs Files directory, but you can put it anywhere you like.
You might be thinking to yourself why use WGet when the box already has Internet Explorer and Mozilla Firefox installed; surely they can open a web address? Well, yes, they can but every time the task/cron job is invoked a new instance of the browser is created. Once the cron job is complete they do not self close either so your server would be left with many useless open processes running! No good at all.
Now we have the necessary software installed lets turn our attention to the setup of our Scheduled Task and as usual Windows has a nice wizard interface that can be found by accessing Start > Programs > Accessories > System Tools > Scheduled Tasks. However if you prefer the command line I go into detail further down the article about using schtasks.exe. When you open the utility you will see a Windows Explorer window containing any pre-existing tasks and an “Add Scheduled Task” icon which is the one we want to double click.
Let us step through the wizard setup process:
Once back viewing the Scheduled Tasks folder you can right click on the Task and choose Run to see if it works as expected.
OK so that is great if we know what tasks we want to setup, but what if your system needs make its own Tasks on the fly or you prefer setting up Tasks via the command line. Obviously you need to be exceptionally careful when you allow a web accessible script to setup Scheduled Tasks just as you would with cron jobs.
You must be running your server on a Windows system with Server 2003, Server 2008 or XP Professional otherwise you will not have the required schtasks.exe executable. To find out if you have it run schtasks on the command line. It should return a list of Scheduled Tasks currently on the system and if you see this read on otherwise you will need to investigate the windows at utility (type at /? on the command line).
The best place to start is by running schtasks /? which will bring up the help manual, but for further information on creating a Scheduled Task you should also read schtasks /Create /?. As there are lots of confusing options there I will break it down for you with a brief example.
In the above statement we are creating a new Scheduled Task that is to run every five minutes and it executes WGet, which in turn calls our PHP script. /RU and /RP are the username and password (respectively) of the account that will be used to run the task and /SC is the definition of the iteration interval which can be from minutes to monthly. The /MO is the modifier which determines when it runs, in our example above its every 5 minutes, /TN is predictably the unique name of your Scheduled Task and /TR is the command to be run when the Scheduled Task is activated.
As another example if we wanted a script to be run every second month we would enter something like this:
So that should give you a basic idea of the command line utility and adjusting the examples with the help of the manual available by typing schtasks /? and schtasks /Create /? should get you on your way.
If you are like me you are now wondering if I am recommending security via obscurity; absolutely not! The problem with the examples I have given above is that if someone were to guess the URL of your processing php script (http://www.webaddress.com/script.php in the example above) they could access it via their web browser. Now think of the ramifications if your script is meant to send out emails once a day, but instead your script gets hit by a bot and sends your users 1,000+ emails a day!
So to lock your scripts down a bit I recommend the following:
These checks combined should halt any bots in their tracks. So let us take a look at how this would work in real life with the following example code. Firstly the call we would issue to Scheduled Tasks and WGet:
–header=TASK_KEY:my-secret-key tells WGet to add an HTTP header called TASK_KEY which contains the value of “my-secret-key” to its request for the processing script. We can then compare the contents of this header with the copy of the secret key in our processing script – this will make more sense when I show you the PHP side of the coding. You will also notice that I have added -U my-agent, which changes the user agent that WGet identifies itself as – in this case to “my-agent”. Obviously you would want to change the key and user agent to make them harder for a bot maker (who could be reading this) to guess. I would use a SHA1 hash for your secret key just to make it even harder to guess – it is a lot harder to luck upon a94a8fe5ccb19ba61c4c0873d391e987982fbbd3, which is the word test hashed rather than simply the word test.
The PHP side of this is also very simple:
So again you can see here that you need to change the values of “my-agent” and “my-secret-key” to your own private values. Now to break down the code by looking at the operations in the if statement.
The first line ‘my-agent’ !== $_SERVER[‘HTTP_USER_AGENT’] checks to see whether the value of the user agent matches the specified value “my-agent”.
The second line $_SERVER[‘SERVER_ADDR’] !== $_SERVER[‘REMOTE_ADDR’] checks that the server requesting the script be processed is the same as the one the script is being executed on. Basically this means that only requests coming from the same IP address as the server will be run.
The third, fourth and fifth lines:
!isset($_SERVER[‘HTTP_TASK_KEY’]) or
empty($_SERVER[‘HTTP_TASK_KEY’]) or
‘my-secret-key’ !== $_SERVER[‘HTTP_TASK_KEY’])
Check that the HTTP header has been set then if the header contains something and finally if the header contents match our secret key; “my-secret-key” in this case.
If any of the criteria are found to be missing then the script will display the message “You do not have permission to execute this script!” and halt otherwise the code placed after the end of the if will be executed. So in this example upon a successful run the script would print out “Executing operation”.
Before I go I shall also very briefly show you how to setup a Scheduled Task from a PHP script. This can be very useful if you have a very large list of emails to send out to and the PHP script times out whilst your Scheduled Task is executing. What you can do is split it up into job lots of say 500 emails at a time and schedule the send out as multiple Scheduled Tasks dynamically from PHP. Of course this same principle would apply to large dataset changes that timeout during their operation as well.
To dynamically schedule the task we simply send it to the system via the PHP exec() function. As I said earlier you must ensure that you are very careful of what you put in an exec() call and I would advise that no user supplied data is allowed lest they maliciously insert a command like format C:! Here is the basic PHP to work from:
The only new concept here is that I always make sure I delete any Scheduled Task that may already be in place with the same name before attempting to setup the new Scheduled Task.
To summarise we have now got a Scheduled Task up and running with some security to ensure only our server is able execute the processing script. You have learnt how to setup a new Scheduled Task either via the command line or the graphical user interface wizard or via the exec() function in PHP to dynamically setup new Scheduled Tasks on the fly. Experiment further and enjoy!