Creating a cost efficient Linux Intranet

Like many people working in IT, I have a small server running at home doing various background jobs like sort my Email, provide network drives, monitor my house etc. This doesn’t require much processing power, and there are a great range of small Linux devices out there that are perfect for the job.
However there are times when a bit more power is required. So rather than have a high powered machine running all of the time, I thought of using two. One to do the lower power stuff, and another that kicks in when it’s needed.

Problem #1 Switching on the machine

The first problem is to switch on the high powered machine when it’s needed. There are several off the shelf solutions for this, including APC power strips and an interesting solution using relays.

However most computers already have a solution buried deep within their BIOS. Wake-On-Lan is a protocol from the late 90’s which allows a small power to be sent to the network card of a server, even when the server is switched off. The network card can thereby listen for specially encoded messages and turn the computer on when it receives one.

Once Wake-On-Lan has been enabled in the BIOS, any other computer (on the same network) can send a special message just by knowing the network cards MAC address. The MAC address is a (supposedly) unique number burned into all network cards when they are manufactured (on the rare occation you find two machines with the same MAC address you can usually manually change it).

Finding the MAC address

On the server you wish to turn on, open up a terminal and type

/sbin/ifconfig

The response should look something like this:

eth0      Link encap:Ethernet  HWaddr 12:34:56:78:9a:bc
inet addr:192.168.1.12  Bcast:192.168.1.255  Mask:255.255.255.0
inet6 addr: fe80::baac:6fff:fe8b:82f0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:67594 errors:0 dropped:0 overruns:0 frame:0
TX packets:44832 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:23639322 (22.5 MiB)  TX bytes:7850032 (7.4 MiB)
Interrupt:16 Memory:da000000-da012800

lo        Link encap:Local Loopback
inet addr:127.0.0.1  Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING  MTU:16436  Metric:1
RX packets:198 errors:0 dropped:0 overruns:0 frame:0
TX packets:198 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:73471 (71.7 KiB)  TX bytes:73471 (71.7 KiB)

For each network card you will see a set of information including the IP address, speed and transmission statistics (you will also see a “loop back” network card, this can be ignored). It’s the hardware address (HWaddr) you are looking for, this should be a series of six hexadecimal pairs seperated by colons.

Once you have this, you can power down the machine.

One the computer you are using to control this server (i.e. the low powered one) you will need an application that can send the appropriate packet over the network. Most Linux distributions will probably have the command “wakeonlan” (you may have to install it from your package manager). Simply type:

/usr/bin/wakeonlan 12:34:56:78:9a:bc

(replace the hardware address with your own)

to turn on the server on.

Problem #2 Switching the server off when it’s not being used

Now that you can turn a machine on, we need to turn it off when we’re not using it. For this we’re going to create a small application (Note: I’ve done this in Java but there is no reason it couldn’t be done in any other language). This will check to see if the server is no longer in use, if it isn’t then it will run the shutdown command.

Note: To run the shutdown command, the application will need administration (root) privileges. As I’m the only one with access to these servers (and I’m doing this quickly) I’ll just use the root account to run it. However there is always a risk of doing it this way. A better solution would be to run the app as a normal user and then give this user access to the shutdown command (but nothing else); perhaps using sudo.

We will use cron to run the programme once every 15 minutes, for me the crontab looks like this:

*/15 * * * * /usr/local/bin/java -jar /usr/local/bin/ServerShutdown.jar

Because most of my programmes run as web-apps (on Tomcat) I’ve chosen to turn the server off when both of the following happen:

  • The server has been running for more than 2 hours.
  • There hasn’t been a request for any web pages in three hours.

But you could easily add any other rules, some ideas are:

  • Don’t turn the server off if there is anybody logged in via SSH
  • Don’t turn the server off if the CPU load is above a particular limit.
  • Don’t turn the server off if particular processes are running.

You will need to tailor this to suit your needs but most of this information can be obtained by reading files in the /proc directory.

Here the quick hack I used:

[java]
package org.smarthomethinking;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ServerShutdown {

public void testShutdown(){
boolean shutdown = true;
// test that the server has been on long enough
System.out.println("Testing uptime");
double uptime = getUptime();
System.out.println("Uptime = " + uptime);
if (uptime < 2 * 60 * 60) {
shutdown = false;
}
// test how long since a web request
System.out.println("Testing last web access");
Date access = new Date(getLastWebAccess());
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss zzz");
System.out.println("Last access = " + sdf.format(access));
if (access.after(new Date(System.currentTimeMillis() – 3 * 60 * 60 * 1000))) {
shutdown = false;
}
// shutdown the server
if (shutdown) {
try {
System.out.println("System not in use – shutting down");
Process p = Runtime.getRuntime().exec("/sbin/shutdown -h now");
int exitVal = p.waitFor();
} catch (IOException ex) {
Logger.getLogger(ServerShutdown.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(ServerShutdown.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
System.out.println("System in use");
}
}

private long getLastWebAccess() {
long access = 0;
File fdir = new File("/usr/tomcat/logs");
File flist[] = fdir.listFiles(new AccessLogFilter());
for (File f : flist) {
if ((f.lastModified() > access)
&& (f.length() > 0)) {
access = f.lastModified();
}
}
return access;
}

private double getUptime() {
double result = 0.0;
try {
BufferedReader in = new BufferedReader(new FileReader("/proc/uptime"));
String line = in.readLine();
in.close();
result = Double.parseDouble(line.split(" ")[0]);
} catch (IOException e) {
System.err.println("Error reading /proc/uptime");
e.printStackTrace();
}

return result;
}

public static void main(String[] args) {
ServerShutdown server = new ServerShutdown();
server.testShutdown();
}

}

package org.smarthomethinking;

import java.io.File;
import java.io.FilenameFilter;

public class AccessLogFilter implements FilenameFilter {

public boolean accept(File dir, String name) {
return name.toLowerCase().contains("_access_log");
}

}
[/java]

Problem #3 Setting up the Intranet

Once we can turn the computer on, and it turns itself off when it’s not in use; the missing piece is a nice front end. This should detect whether the server is switched on and alter its menu appropriately. As the low powered server runs apache with PHP, I’ve copied the solution from this post (http://www.kirupa.com/forum/showthread.php?339124-Check-if-server-is-online) to detect if the second server is running or not.

[php]
<html>
<?php

// Function to check response time
function pingDomain($domain){
$starttime = microtime(true);
$file = fsockopen ($domain, 80, $errno, $errstr, 1);
$stoptime = microtime(true);
$status = 0;

if (!$file) $status = -1; // Site is down
else {
fclose($file);
$status = ($stoptime – $starttime) * 1000;
$status = floor($status);
}
return $status;
}

$server2 = pingDomain("192.168.1.12");

?>
<head>
<meta http-equiv="refresh" content="10">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Home Intranet</title>
<link href="intranet.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id=’list-wrapper’>
<ul class="listing">
<a href="emoncms"><li><img src="images/gnome-power-statistics.png" width="48" height="48">Emon CMS</li></a>
<a href="/books/index/index.html"><li><img src="images/books.png" width="48" height="48">Library</li></a>
<a href=’http://192.168.1.6/’><li><img src="images/user-home.png" width="48" height="48">Home Automation</li></a>
<?php
if ($server2 > -1) {
?>
<a href="http://192.168.1.12:8080/"><li><img src="images/run-build-configure.png" width="48" height="48">Jenkins CI</li></a>
<a href="http://192.168.1.12/DB"><li><img src="images/x-office-address-book.png" width="48" height="48">Main Database App</li></a>
<?php
} else {
?>
<a href="wakeonlan.php"><li><img src="images/system-switch_user.png" width="48" height="48">Switch on DB Server</li></a>
<?php
}
?>
</ul>
</div>
</body>
</html>
[/php]

Selecting “Switch on DB Server” calls the wake on lan function
[php]
<?php

`/usr/bin/wakeonlan b8:ac:6f:8b:82:f0`;

header( ‘Location: index.php’ ) ;
?>
[/php]
As you can see, the results are simple but effective:

IntranetPower

Be Sociable, Share!

    Leave a Reply

    Your email address will not be published. Required fields are marked *