I set up small home server using HP TERMINAL HP T5000 (Thin Client). I installed Ubuntu server on it. It works great - but recently I realized that I need to have this server up only for very limited number of scenarios i.e. when I'm working on one of my PCs (so I want to have access to server resources e.g. samba, cups, DNS server etc) or I'm downloading something from the internet using transmission-daemon. To optimize my server power consumption I decided to set up Wake On Lan on my ubuntu and wrote 2 simple scripts in ruby.
How to set up WOL on ubuntu?
Log in as administrator.
Install ethtool:
sudo apt-get install ethtool
Change to the startup script directory and start editing a new file:
cd /etc/init.d/
nano wakeonlanconfig
Paste, or type this into the file, replacing eth0 with your network device, repeat the ethtool line as many times for your devices before the exit line:
#!/bin/bash
ethtool -s eth0 wol g
exit
Set the permissions of the file:
chmod a+x wakeonlanconfig
Make the script run on startup:
update-rc.d -f wakeonlanconfig defaults
You should see something like:
Adding system startup for /etc/init.d/wakeonlanconfig ...
/etc/rc0.d/K20wakeonlanconfig -> ../init.d/wakeonlanconfig
/etc/rc1.d/K20wakeonlanconfig -> ../init.d/wakeonlanconfig
/etc/rc6.d/K20wakeonlanconfig -> ../init.d/wakeonlanconfig
/etc/rc2.d/S20wakeonlanconfig -> ../init.d/wakeonlanconfig
/etc/rc3.d/S20wakeonlanconfig -> ../init.d/wakeonlanconfig
/etc/rc4.d/S20wakeonlanconfig -> ../init.d/wakeonlanconfig
/etc/rc5.d/S20wakeonlanconfig -> ../init.d/wakeonlanconfig
Now finish by running it, and making sure there are no errors.
/etc/init.d/wakeonlanconfig
Now when the server can be started remotely I want to present you 2 scrips which I wrote in ruby to make my life easier.
First for client machines.
All I need to do in this script is checking state of my server and if it is down send magic packet to power it on.
begin
require 'Win32/Console/ANSI'
rescue LoadError
raise 'You must gem install win32console to use color on Windows'
end
require 'logger'
require 'net/ping'
require File.dirname(__FILE__) + "/wakeonlan.rb"
class String
def red; colorize(self, "\e[1m\e[31m"); end
def green; colorize(self, "\e[1m\e[32m"); end
def dark_green; colorize(self, "\e[32m"); end
def yellow; colorize(self, "\e[1m\e[33m"); end
def blue; colorize(self, "\e[1m\e[34m"); end
def dark_blue; colorize(self, "\e[34m"); end
def pur; colorize(self, "\e[1m\e[35m"); end
def colorize(text, color_code) "#{color_code}#{text}\e[0m" end
end
log = Logger.new(File.dirname(__FILE__) + '/logs/log.txt', 'monthly')
log.debug "Script has been started"
puts "Ping ubuntu server"
log.debug "Ping ubuntu server"
p = Net::Ping::TCP.new('my_server_IP', 'http').ping?
if !p
puts "Getting server up"
log.debug "Getting server up"
puts "Sending magic packet to ubuntu server"
wol=WakeOnLan.new
wol.wake("my_server_mac_address", "broadcast", "my_server_IP")
# args: MAC_address Broadcast IP_address
wol.close
else
puts "Server is running".dark_green
log.debug "Server is running"
end
Below I present code for WakeOnLan class which I used in above code:
# K.Kodama 2003-04-20 revised/bug fix
# K.Kodama 2000-05-10
# This program is distributed freely
# in the sense of GNU General Public License or ruby's.
require "socket"
class WakeOnLan
attr :socket
def initialize
@socket=UDPSocket.open()
@socket.setsockopt(Socket::SOL_SOCKET,Socket::SO_BROADCAST,1)
end;
def close; @socket.close; @socket=""; end
def wake(mac_addr, broadcast="", ip_addr="")
wol_magic=(0xff.chr)*6+(mac_addr.split(/:/).pack("H*H*H*H*H*H*"))*16
if broadcast==""; # Set broadcast. Assume that standard IP-class.
ips=ip_addr.split(/\./);c=ips[0].to_i
if c<=127; ips[1]="255";end # class A:1--127
if c<=191; ips[2]="255";end # class B:128--191
if c<=223; ips[3]="255";end # class C:192--223
# class D:224--239 multicast
broadcast=ips.join(".")
end
3.times{ @socket.send(wol_magic,0,broadcast,"discard") }
end
end
if $0 == __FILE__
=begin
Sample for WakeOnLan class.
Use as: ruby wakeonlan.rb [broadcast_address] < data_text
line format of data_text: ip-address hostname MAC-address
Data sample: 10.20.30.40 wake-pc 100:110:120:130:140:150
Note. To check WOL packet, use tcpdump -x.
=end
if ARGV.size>=1; broadcast=ARGV[0]; ARGV.clear; else broadcast=""; end
wol=WakeOnLan.new
while gets
ip,name,hw=$_.sub(/^\s+/,"").split(/\s+/)
wol.wake(hw, broadcast, ip)
end
wol.close
end
Second for server.
I'm checking here state of network i.e. if any of my PCs is working. In case when all my PCs are offline server could be turn off but before I'll send halt command I have to check status of transmission-daemon - check if there are any elements being downloaded at the moment. If all conditions passed server is going to be shut down.
require 'net/ping'
require 'logger'
require 'transmission_api'
class TorrentStatus
attr_accessor :all_torrents_finished
def initialize()
@transmission_api = TransmissionApi.new(
:username => "my_user",
:password => "password",
:url => "http://myserver:9091/transmission/rpc"
)
torrents = @transmission_api.all
@all_torrents_finished = true
torrents.each{|t|
#puts "#{t["name"]} - [#{t["percentDone"]}]"
@all_torrents_finished &= (t["percentDone"] == 1)
}
end
end
class LanStatus
def initialize()
@interfaces = ["192.168.zzz.xxx","192.168.zzz.yyy"]
end
def active_computers_in_network?
active = false
@interfaces.each{|ip|
active |= Net::Ping::External.new(ip).ping?
}
active
end
end
log = Logger.new(File.dirname(__FILE__) + '/logs/shutdown_log.txt')
log.debug "Script has been started"
puts "Checking system state"
log.debug "Checking system state"
print " * Torrent status "
torrent_status = TorrentStatus.new
all_torrents_finished = torrent_status.all_torrents_finished
puts "- [#{all_torrents_finished ? "idle" : "active"}]"
log.debug " * Torrent status - [#{all_torrents_finished ? "idle" : "active"}]"
print " * Lan status"
lan_status = LanStatus.new
active_computers_in_network = lan_status.active_computers_in_network?
puts "- [#{active_computers_in_network ? "active" : "inactive"}]"
log.debug " * Lan status - [#{active_computers_in_network ? "active" : "inactive"}]"
if (all_torrents_finished && !active_computers_in_network)
puts "Sending HALT command to system"
log.debug "Sending HALT command to system"
system "sudo shutdown -h now"
end
There was still problem how to make it automated process. From the server side i use cron to schedule job which will execute in every 5 minutes and run ruby script.
To read more about setting cron please read
this post.
I added to crontab below entry:
0/5 * * * * ruby /home/luke/scripts/shut_down.rb
All my clients work under windows system and they are not attached to domain. So i just added ruby script to logon scripts.
To add logon script for Windows 7 Home Premium:
I've created a script at C:\Windows\System32\repl\import\scripts\start.bat
Which executes command:
ruby /path_to_my_script/login.rb
Then I invoked from command line
net user USERNAME /scriptpath:start.bat