This is an archived cached-text copy of the developerWorks article. Please consider viewing the original article at: IBM developerWorks



Skip to main content

skip to main content

developerWorks  >  Open source  >

Shut down idle computers on your network automatically

Save electricity, bandwidth, and wear by detecting inactive systems on your network and shutting them down automatically

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


Learn and share!

Exchange know-how with your peers -- try our new Pass It Along beta app


Rate this page

Help us improve this content


Level: Introductory

Nathan Harrington, Programmer, IBM

21 Oct 2008

Learn how to use Argus and client/server Perl code to monitor your network connections and shut down machines no longer in use.

Recent pushes for "green" technology focus mostly on talk, with little action for the typical home- or small-office environment. Many users leave their systems online continuously through laziness or ignorance, resulting in a significant source of power consumption, as well as an additional vector for malware propagation. The tools and code presented here allow you to find those inactive systems and securely start the shutdown process. With a Linux® box monitoring your network connections using Argus and some custom Perl code, any system that supports Perl can be set to be remotely shut down when a centralized set of inactivity rules are met.

Hardware requirements

Although the control code for securely communicating and shutting down systems is cross-platform, you'll need hardware capable of running Linux for the monitoring computer, which we'll call the control node. Such a node could be a Linksys NSLU2 or similar low-wattage device running Linux. The control node needs a method for monitoring network connections throughout your entire network. Minimal processing power, RAM, and storage are required, unless you intend to monitor hundreds of machines on a high-bandwidth pipe. Virtually any PC manufactured after 2000 with an appropriate network adapter and the capability to run Linux should provide enough capacity to monitor a small office or home network. One network adapter for the control node will suffice in low-traffic settings, but two is better on busy networks. See the "Hardware placement" section for suggestions on where to place your selected system.



Back to top


Software requirements

Snort, Tcpdump, or a number of network monitoring software packages will work in theory for measuring usage. This article uses a combination of Argus and racluster (an Argus client) due to its simplicity and light weight for tracking connection status. In addition to the heavy lifting done by Argus, you'll need Perl and the associated modules for network communications, random string generation, and encryption. These modules are POE, String::MkPasswd, and Crypt::Blowfish, respectively. See Resources for details on finding and installing these modules.

Hardware placement

Selecting an appropriate location for a full-network monitoring connection point is a complex topic with many variables dependent on specific network attributes. A certain amount of familiarity with network topologies is assumed, as well as the ability to identify the upstream connection point all network traffic flows through.

For example, consider a typical home network with four computers connected to a wireless access point (WAP) or router with an outbound connection through a cable or DSL modem. (I realize using the term "modem" here is inaccurate, but since the industry latched onto the misnomer, I'll use it, too — albeit reluctantly.) Placing the control node network connection between the WAP and the modem allows monitoring of all connections to the Internet from any machine on network. Alternatively, many home-office WAPs are also routers with wired Ethernet ports, so connecting to the WAP's wired port directly may suffice.

Keep in mind that the correct placement of the control node (with the appropriate network interface) and the associated configuration of the network adapter or adapters on the control node may present a formidable challenge for the casual reader. See Resources for tutorials and research materials to help you get started in network monitoring.

Argus setup on the control node

Providing simple measurements of network connection attributes with minimal resource consumption is ideal for this article. Instead of writing Snort rules or analyzing Tcpdump output, use Argus and some command-line switches to provide a minimal list of network connections. On the control node, install Argus and run the following command.


Listing 1. Argus command on control node

argus -w /tmp/argusOutput -i eth0 

Transaction records recorded by Argus are appended to the /tmp/argusOutput file, and the eth0 interface is assigned to sniff packets in promiscuous mode. Now that Argus is set up to monitor the connections for each host on the network, use one of the many Argus clients to print out information about those connections. Use the command racluster -L0 -M -u -r /tmp/argusOutput tcp to show the current connections on the network, including information like total bytes transferred and connection start times. Note that this command may produce a great deal of information, so consider isolating the most active connections with the command racluster -L0 -M -u -r /tmp/argusOutput tcp -w - | rasort -m bytes | head.

rasort combined with head will produce a list of the top 10 connections by the number of bytes transferred.

Note that the Argus and process_racluster.pl program (listed below) need to be run as a user with appropriate privileges for accessing the network adapter in promiscuous mode. In most cases, this is the root user.



Back to top


Selecting machines and bandwidth usage

Each machine to be monitored for inactivity needs to have an identifier, a number of bytes indicating inactivity, and a command to run when inactivity is detected. Listing 2 shows an example activityRules file.


Listing 2. Example activityRules file

192.168.1.30_#_10_#_xmessage "Elena's laptop inactive"
192.168.1.35_#_400_#_xmessage "Alexander's desktop inactive"

Note that these numbers are examples only and need to be tuned for the specific usage of machines on the network. The first line will pop up a message on the control nodes' display (if X Window System is active), letting the user know that the laptop is inactive and needs to be powered down.



Back to top


process_racluster.pl for inactivity monitoring

Reading the activityRules file, processing racluster output and executing commands is performed by the process_racluster.pl program. Listing 3 lists the first part of process_racluster.pl.


Listing 3. process_racluster.pl part 1

#!/usr/bin/perl -w
# process_racluster.pl - run commands if inactivity conditions are met
use strict;

my $sleepTime     = 600; # seconds between network activity reads
my $argusFile     = "/tmp/argusOutput";
my $raclusterFile = "/tmp/raclusterOutput";
my %rules         = (); # store commands, thresholds for each ip

open( INFILE, "activityRules" )  or die "can't open rules file";
  while(my $line = <INFILE> )
  {
    my( $ip, $threshold, $command ) = split "_#_", $line;
    $rules{ $ip }{ threshold } = $threshold;
    $rules{ $ip }{ command   } = $command;

  }#while in file

close( INFILE );

After variable declaration and reading the activityRules file, the next step is the main logic loop.


Listing 4. process_racluster.pl part 2

while( 1 )
{
  # check if Argus is running 
  my $cmd = qq{ps -aef | grep argus | wc -l };
  die "argus no longer running" unless ( `$cmd` ne "1\n" );

  # transform the argus data into human readable form
  $cmd = qq{racluster -L0 -M -u  -r $argusFile tcp > $raclusterFile};
  $cmd = `$cmd`;

  my %hosts = ();
  open( INFILE, "$raclusterFile" ) or die "can't open argus output file";
    while(my $line = <INFILE> )
    {
      # grab destination addr, port, total packets, bytes, connection status
      $line = substr($line,68);
   
      my( $ipAndPort, undef, $bytes ) = split " ", $line;
      my @parts = split '\.', $ipAndPort;

      # skip if there is no port associated (probably not tcp)
      next unless( $#parts  == 4 );
  
      # get just the ip address 
      my $ip = substr($ipAndPort, 0, rindex($ipAndPort,".") );
   
      $hosts{$ip} += $bytes;
    
    }#while linein
  close(INFILE);

At each pass of the main logic, a simple process check is run to make sure the Argus network connection monitor is active. If so, the racluster command described above is run, and the IP address and associated byte count for all connections to that address is recorded in the %hosts hash. Listing 5 shows the remainder of the main logic loop.


Listing 5. process_racluster.pl part 3

  for my $key( keys %rules )
  {
    my $foundKey = 0;
    $foundKey = 1 if( ! exists($hosts{$key}) );

    if( exists($hosts{$key}) && ($hosts{$key} < $rules{$key}{threshold})  )
    {
      $foundKey = 1;
    }#if key is not found

    next unless ( $foundKey == 1 );

    # run the command, remove the entry from processing
    system( $rules{$key}{command} );
    delete $rules{$key};

  }#for each key in the rules

  # delete the processed files, wait for next pass
  $cmd = qq{ rm $argusFile $raclusterFile };
  $cmd = `$cmd`;

  sleep($sleepTime);

}#while 1 loop

Each defined IP in the rule dataset is checked for existence in the %hosts hash. If the IP is not found, or the byte threshold is less than that specified in the rules file, the inactive command is run. After an inactive state has been found, the entry in the rule dataset is removed to prevent further processing of an already-inactive host. /tmp/argusOutput is deleted to reset the recorded connection status, and the temporary racluster output file is deleted to save space. After sleeping for 10 minutes, the process repeats.

To begin the inactivity processing, run the command perl process_racluster.pl with the same privileges as the ID that ran Argus (usually root). As the "laptop" and "desktop" systems move to inactive states, you'll see a pop-up message on the control node showing you which person to give the Kermit guilt trip ("It's not easy being green").



Back to top


Remote shutdown of systems

Providing a method for remotely shutting down systems is a great way to open doors for control and abuse. To prevent some of these simpler attacks, the following client and server implement a simple challenge-response system. This approach helps ensure that systems will only respond to the shutdown command if the correct encryption key is used.

Listen for shutdown requests: shutdownListener.pl

Each system that will respond to remote shutdown requests needs to run the shutdownListener.pl program.


Listing 6. shutdownListener.pl

#!/usr/bin/perl -w
# shutdownListener.pl - manage challenge/response, shutdown if authenticated
use strict;
use POE qw(Component::Server::TCP Filter::Reference);
use Crypt::Blowfish;
use String::MkPasswd qw(mkpasswd);

my $key = "<enter your key of choice here>";
my $random = substr(mkpasswd,0,8);
my $cipher = new Crypt::Blowfish $key;
my $cipherText = "";

# heavily influenced by the POE cookbook:
POE::Component::Server::TCP->new
  ( Alias => "shutdownListener",
    Port         => 11211,
    ClientFilter => "POE::Filter::Reference",
    ClientInput  => sub {
        my ( $sender, $heap, $input ) = @_[ SESSION, HEAP, ARG0 ];

        if( $cipherText eq "" )
        { 
          # send the challenge text back to client
          $heap->{client}->put( \$random );
          $cipherText = $cipher->encrypt( $random );

        }elsif( $cipherText eq $$input )
        { 
          print "challenge response match, shutting down\n";
          # uncomment this line to actually shutdown the system
          # system(`shutdown -h now`);   # linux
          # system(`shutdown -s -t 01`); # windows
          exit(0);
        }else
        { 
          print "challenge response does not match, exiting server";
          exit(1);
        }#if cipher text not generated
    },
  );

$poe_kernel->run();
exit 0;

The POE module provides one of the easiest ways to create bidirectional TCP communications using Perl. When a shutdown request is received, the current random string is sent back to the client. If the client's encrypted return string and the locally encrypted string match, the shutdown command is run. Note that you should replace the key with a unique value that matches the key defined in clientShutdown.pl below. In addition to shutting down the system, the shutdownListener.pl will terminate its execution if the challenge response is not met to deter repeated connection attempts.

Save the shutdownListener.pl code on each machine that should enable remote shutdowns. shutdownListener.pl should be run as a user with appropriate privileges to shutdown the machine and access the port selected.



Back to top


Send shutdown requests: clientShutdown.pl

Each machine to be shut down listens to the control node for shutdown connection requests. These requests come from the clientShutdown.pl program.


Listing 7. clientShutdown.pl

#!/usr/bin/perl -w
# clientShutdown.pl - compute challenge request, send response
use strict;
use POE qw( Component::Client::TCP Filter::Reference);
use Crypt::Blowfish;

die "specify hostname " unless @ARGV == 1;

my $host    = $ARGV[0];
my $key     = "<enter your key of choice here>";
my $cipher  = new Crypt::Blowfish $key;
my $port    = 11211;
my $sendStr = "getChallenge";

# heavily influenced by the POE cookbook:
POE::Component::Client::TCP->new
  ( RemoteAddress => $host,
    RemotePort    => $port,
    Filter        => "POE::Filter::Reference",
    Connected     => sub {
        $_[HEAP]->{server}->put( \$sendStr );
    },
    ConnectError => sub {
        die "could not connect to $host:$port ...\n";
    },
    ServerInput => sub {

      # get the server response
      my ( $input ) = @_[ ARG0 ];

      # encrypt the response, send back
      my $cipherText = $cipher->encrypt( $$input );
      $_[HEAP]->{server}->put( \$cipherText );

    },
  );
$poe_kernel->run();
exit 0;

Similar to shutdownListener.pl, the clientShutdown.pl program connects to the machine to be shut down, retrieves the random text, encrypts it with the shared key, and returns the encrypted text. Again, the encryption key needs to match between the client and server. Modify the activityRules file by inserting the line shown in Listing 8.


Listing 8. Remote shutdown in activityRules

192.168.1.33_#_5555554_#_perl clientShutdown.pl 192.168.1.33

Now when process_racluster.pl is restarted and the total network usage for the 192.168.1.33 machine drops below approximately 5.5 MB over 10 minutes, the shutdown command will be sent.



Back to top


Usage

To recap the usage instructions: Modify the activityRules file to run the appropriate commands for each machine. Start shutdownListener.pl on any machine that should be remotely shut down. Start Argus and the process_racluster.pl program on the control node.

Conclusion, further examples

With the tools and code in this article, you can set up a network monitor to track the total usage of specific machines on your network. Use the programs presented herein to enforce an automatic shutdown when systems become inactive on the network. Consider modifying Argus to search for specific connection types or hosts. Shut down systems when only 15-minute e-mail checks are active, or when a streaming audio connection is left playing in mute for hours. Monitor the protocol used or the connection start times to further enhance the detection of inactivity that triggers a shutdown.




Back to top


Download

DescriptionNameSizeDownload method
Sample codeos-automatic-shutdown-inactiveShutdown_0.1.zip3KBHTTP
Information about download methods


Resources

Learn

Get products and technologies

Discuss


About the author

Nathan Harrington

Nathan Harrington is a programmer at IBM currently working with Linux and resource-locating technologies.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top