EBS-admin JJB template

This page is Under Edit.

I am going to write a JJB template to experience deploying daily EBS adadmin activities on Jenkins.

My Goal is ASA an EBS OVM (Oracle Virtual Machine) instance is created, machine owner can run the JJB deployer to deploy the EBS adadmin activities as a set of jobs on Jenkins. In this way, people can do EBS adadmin activities via Jenkins on one go.

My Target: The following EBS adadmin activities should be implemented on this JJB template,

  • Run AutoConfig on DB/Apps Tier
  • Enable/Diable tablespace Auto extend
  • Tech inventory report on DB/Apps Tier
  • Invalid objects report
  • Compile invalid objects using UTLRP
  • Startup/shutdown mid-tier services
  • Startup/shutdown db and listener
  • TBD

Duke – Java Web Server

I assembled a core Java Web Engine named as Duke. Duke is a light weight Java Servlet Container. I use it to refresh my Java skills/Design Pattern and to understood how Tomcat works.

Basically the code here was extracted from Tomcat Server.

Detailed info
* A Simple Servlet Container in Java: http://luohuahuang.com/2013/08/15/a-simple-servlet-container-in-java/
* façade pattern: http://luohuahuang.com/2013/08/15/facade-pattern/
* Singleton pattern: http://luohuahuang.com/2013/08/16/singleton-pattern/
* Java Thread: http://luohuahuang.com/2013/08/17/java-thread/
* Tomcat Connector: http://luohuahuang.com/2013/08/18/tomcat-connector/
* Tomcat Servlet Container: http://luohuahuang.com/2013/08/18/tomcat-servlet-container/


Automated Patching database – 2

In my eariler post Automated Patching database, I automated the database patching process. But there is one bug in the post. See,


# Step 4. apply patches,

foreach patchnum in todo_staging.lst

system(“opatch apply -silent -ocmrf ocm.rsp $patchnum”); # How to define a ocm.rsp file, pls. check here.

# Step 5. Startup db and listener,

system(“lsnrctl start $ORACLE_SID”);


# Step 5. Apply post-install steps,

foreach patchnum in todo_staging.lst

foreach item in post_install.lst for patchnum

run the item listed in post_install.lst


For example, if in the todo list, it has 123, 456, 789 and all of them have post install steps. Supposed that 789 fails to apply due to conflict and the script halts due to that conflict. If ppl restart the script from there, the post install steps will be skipped for 123 & 456 as they are already in the inventory!

My enhancement,


sub applypatches{

my $newerr = 0;

open (checklist, "$todo_patch_lst");

my @lines = "";

my $line = "";

@lines = <checklist>;

foreach $line (@lines) {

$line =~ s/^\s+|\s+$//g;

my ($patchnum) = split(/:/, $line);

$patchnum =~ s/^\s+|\s+$//g;

$patchdir = GetOSPath("$ENV{PATCH_TOP}/patch/$patchnum/$patchnum");

my $opatchcmd = GetOSPath("$ENV{DB_OH}/OPatch/opatch apply -silent -ocmrf $rsp_conf $patchdir");

$newerr =system("$opatchcmd");

if ($newerr ne 0){

print "can not apply $patchnum...\n";


} else {

print "apply $patchnum successfully ... \n";



close checklist ;

print "*** For more detailed log, go to $ENV{DB_OH}/cfgtoollogs/opatch ... *** \n";



sub applypatches{

my $newerr = 0;

open (checklist, "$todo_patch_lst");

my @lines = "";

my $line = "";

@lines = <checklist>;

foreach $line (@lines) {

$line =~ s/^\s+|\s+$//g;

my ($patchnum) = split(/:/, $line);

$patchnum =~ s/^\s+|\s+$//g;

$patchdir = GetOSPath("$ENV{PATCH_TOP}/patch/$patchnum/$patchnum");

my $opatchcmd = GetOSPath("$ENV{DB_OH}/OPatch/opatch apply -silent -ocmrf $rsp_conf $patchdir");

$newerr =system("$opatchcmd");

while ($newerr ne 0){

print "Can not apply $patchnum...\n";

my $resume = GetOSPath("$ENV{PATCH_TOP}/RESUME");

while (!(-e $resume)) {

print "Refer to the log and fix the issue. After that run command 'touch $ENV{PATCH_TOP}/RESUME', the flow should continue to run in next 1 mintue automatically... \n";

print "Ensure that the db instance is down before you create the RESUME file... \n";

sleep 60;


$newerr =system("$opatchcmd");

unlink $resume ;


print "Apply $patchnum successfully ... \n";


close checklist ;

print "*** For more detailed log, go to $ENV{DB_OH}/cfgtoollogs/opatch ... *** \n";


Camel – A pure Perl web server

To refresh my Perl skills, I wrote a web server in Perl. I called it as ‘Camel Server’. Camel Server has following features:

  • Support HTTP GET/POST/HEAD methods.
  • Support multi-processes.
  • Support any static text pages.
  • Support PERL files execution.
  • Support default pages.
  • Support Form & CGI parameters.
  • Support XML configuration.
  • Support Authentication & Session management page per page.
  • Certified by Perl Testing framework (of course it is not bug-free!)

You can refer to its README file for more details. https://github.com/luohuahuang/Camel/blob/master/README.md

In this page, I will introduce Camel’s filesystem and recipes.


bin – it contains script to start Camel Server.

conf – it contains .xml configuration files to configure authentication and server setting.

lib – it is the core component of Camel.

camelauth.pm – for authentication

camelcgi.pm – for CGI & parameters pass

camelhttp.pm – main module. Server will be started here and listen/receive requests

camellogger.pm – utility to log logs

camelutils.pm – utility subroutines

camelxml.pm – XML engine. It takes care of XML interactive.

t – it contains testing cases

temp – it is a temporary working directory. Camel requires temp directory to handle .pl execution.

webapps – the root directory of apps.

firstapp – it is a demo apps.

manager – it is the administrator apps shipped with Camel Server. You can not delete it. Currently it has only one page named as login_portal.pl. It is the core .pl page to implement Authentication. In future releasing, I will add more utility pages under manager directory.

logs – it contains the logs file logged by camellogger.pm


The followings are my posts about Camel Server. You might find some useful recipes.

Auth mechanism in Camel

HTTP is a stateless protocol which means each time a client retrieves a Web page, the client opens a separate connection to the web server.

Still there are following three ways to maintain session between weh client and web server. I will have a brief introduction to these ways to maintain session. And then I will introduce the implementation in my light weight pure Web server – Camel.


A web server can assign a set of key/value to each web client and the server can use these values to track session. For example, in Perl,

set cookie with Perl CGI module:

print "Set-Cookie: user=luohua; domain=abc.def.com; " .
"path=/cgi-bin/report.pl; expires=Mon, 12-Aug-2013 12:00:00 GMT\n";
print "Set-Cookie: passwd=xxxxxxxxxxx; domain=abc.def.com; " .
"path=/cgi-bin/report.pl; expires=Mon, 12-Aug-2013 12:00:00 GMT\n\n";

Check cookie with Perl CGI module:

@cookie = split(/; /, $ENV{'HTTP_COOKIE'});
($cookiename,$cookievalue) = split(/=/,$_) ;


The good point is, cookie is very convenient to maintain sessions through the site. However the weak point is that, some of the browsers/clients don’t support cookies or allow cookies.

Hidden Form Fields:

A web server can send a hidden HTML form with its required value:

<p><input type="hidden" name="username" value="Luohua"></p>
 <p><input type="hidden" name="password" value="xxxxxxxxx"></p>

These two entries mean that, when the form is submitted, the specified names and values are automatically included in the GET or POST method.

For example,

POST /firstapp/test.pl HTTP/1.1
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-us,en;q=0.5
Host: localhost:8080
Referer: http://localhost:8080/firstapp/login.html
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Content-Length: 37
Content-Type: application/x-www-form-urlencoded


The good point is, you can use Hidden Form anywhere. However the weak point is that, you have to do Form Authentication page by page.

URL Rewriting:

Also you can append your session data on the end of each URL. For example,


Session Object:

In JEE, JSP & Servelet use session object to maintain session status. For example, in Tomcat, Catalina use Session Manager to maintain session objects. Servelet instance can getSession() from HttpServletRequest interface. In the front tier, developer can just easily use  tags like <%@ page session=”1234567 %> in their jsp pages to get/set values.

Now come back to Camel’s session mechanism.

In my design, basically I use the way of Hidden Form Fields.

1. In the auth.xml, it will define the role/account and pages privileges.

<?xml version='1.0' encoding='utf-8'?>
 <!-- Camel Web Server -->
 <!-- The contents of this file will be loaded for each web application -->
 <role rolename="administrator"/>
 <role rolename="update"/>
 <role rolename="view"/>
 <user username="admin" password="admin123" roles="administrator,view,update"/>
 <user username="luhuang" password="luhuang123" roles="view, update"/>
 <user username="homer" password="simpsons" roles="view"/>
 <user username="update1" password="update123"/>
 <user username="view1" password="view123"/>
 <user username="view2" password="view123"/>

 <page path="/firstapp/auth_admin.pl">
 <page path="/firstapp/auth_update.pl">
 <page path="/firstapp/auth_view.pl">
 <page path="/firstapp/welcome.html">

When a request comes, it will check the page privilege.

If the page is a public page (not indicated in the auth.xml), then authentication pass.

If the page is under prvs control and the user has logged in, then authentication pass.

If the page is under prvs control and the user has not yet log in, then redirect to login portal.

If username or password is wrong, then authentication fail.

If user has no privs to access the page, then authentication fail.

The main logic:

sub authAgent{
	my ($username, $password, $page) = @_;
	if (camelxml::isPageAuth($page)){
			my $pwd = camelxml::getPasswordByUsername($username);
			if ($pwd){
				if ($pwd eq $password) {
					if (camelxml::getAuthByUsernameAndPage($username, $page)){
						return AUTH;
					} else {
						return NOPRVS;
				} else {
					return WRGACNT;
			} else {
				return WRGACNT;

		} else {
			return UNAUTH;
	} else {
		return PUB;

In the login portal, it will inherit the original url and redirect back if the authentication passes.

use strict;
use camelcgi;

my $cgiinst = camelcgi->new(PARAMS => $ARGV[0]);

print <<END_HTML;
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<title>Camel Server - Login Form</title>
<style type="text/css">
img {border: none;}
<p>*** Camel Server - Applications Login Portal ***</p>
<p>You are going to $cgiinst->{originaluri}. $cgiinst->{message}</p>
<form action="$cgiinst->{originaluri}" method="post">
<p>User name: &nbsp;&nbsp;
<input type="text" name="username" value="apps"/></p>
<p>Password: &nbsp;&nbsp;&nbsp;&nbsp;
<input type="password" name="password" /></p>
<p><input type="submit" name="Submit" value="Submit" /></p>

In the form, it will use Camel’s CGI to pass its original url automatically. More details about Camel CGI: Tricky way to instantiate a class in Perl

At last, it will pass the values in the way of Hidden form:

<form action="/firstapp/auth_update.pl" method="post">
 <p>Result: $test </p>
 <p><input type="text" name="test" value=""></p>
 <p><input type="hidden" name="username" value="$cgiinst->{username}"></p>
 <p><input type="hidden" name="password" value="$cgiinst->{password}"></p>
 <p><input type="submit" name="Submit" value="Submit" /></p>

In this way, Camel can track client’s session information.

Let me conclude Camel’s session process here:

1. Define the account/roles and pages prvs in auth.xml. Camel session mechanism will help you track prvs.

2. If you want to pass username/password to other pages, then just inject below two lines in your form:

<p><input type="hidden" name="username" value="$cgiinst->{username}"></p>
 <p><input type="hidden" name="password" value="$cgiinst->{password}"></p>

# So light weight! 🙂

Parsing XML in Perl

Today I started to design the config engine to remove the hardcode from my Camel Web Server (You can refer to my previous posts for more details about this light weighted pure Perl Web container). Basically my target is that all of the configuration files should come from the config engine which work upon context.xml file.

In a short, Perl provides three ways to interactive with XML files.

Method 1,

Parsing XML into Dta Structures. In this way, Perl process XML data with a combination of hashes and arrays. Say, you can use $data->(author}{book}{ISBN} to access the content of ISBN.

In method 1, you will need to use XML::Simple;

Method 2,

Parsing XML into a DOM Tree.

DOM is abbr. of Document Object Model. Each element is a node in the tree, with which you can do operations like find its children nodes. E.g, $dom->getElementsByTagName(“book”);

In method2, you will need to use XML::LibXML;

Method 3,

Parsing XML into SAX Events.

Simple API for XML (SAX) is faster and uses less memory than parsers that build a DOM tree.

In method 3, you will need to use XML::SAX::ParserFactory.

In my case, I just used the Method 1 as my configuration file is very simple. Below is the receipt.

XML file:

<?xml version=’1.0′ encoding=’utf-8′?>
<!– Camel Web Server –>
<!– The contents of this file will be loaded for each web application –>
<!– Server Host –>
<!–Define a non-SSL HTTP/1.1 port –>
<!– root dir for all of the web application –>
<!– log dir –>
<!– temp dir –>
<!– debug level –>

.pl file:

use strict;
use warnings;
use XML::Simple;

my $ctxfile = “../conf/context.xml”;

my $parser = XML::Simple->new();
my $data = $parser->XMLin($ctxfile);
sub debug{
print $data->{ServerHost} . “\n”;
print $data->{PortNumber} . “\n”;
print $data->{BaseDir} . “\n”;
print $data->{TemporaryDir} . “\n”;
print $data->{LogDir} . “\n”;
print $data->{DebugLevel} . “\n”;
print $data->{WelcomeList}->{WeclomeFile}->[0] . “\n”;
print $data->{WelcomeList}->{WeclomeFile}->[1] . “\n”;
print $data->{WelcomeList}->{WeclomeFile}->[2] . “\n”;

# It is just in a very hard-coded way to demo how to parse XML with Perl.

Actually you can use below snippet to parse,

sub readConfig{
 my %config;
 my @element = qw(ServerHost PortNumber BaseDir TemporaryDir LogDir DebugLevel);
 foreach my $key (@element){
 $config{$key} = $data->{$key};
 my $count = 1;
 for (@{ $data->{WelcomeList}->{WeclomeFile} }) {
 my $id = "WelcomeFile" . $count++;
 $config{$id} = $_;
 return %config;

#It is a bit elegant, but we can improve it further like reading the element name from its tag.

Form content enctype

On my previous post, Tricky way to instantiate a class in Perl I shared how to instantiate a class in a tricky way. On that post, while I did the instantiation of my CGI class it was supposed that the Form was posted with its default content enctype – ‘application/x-www-form-urlencoded’.

More details about Form content enctype:


enctype = content-type [CI]This attribute specifies the content type used to submit the form to the server (when the value of method is “post”). The default value for this attribute is “application/x-www-form-urlencoded”. The value”multipart/form-data” should be used in combination with the INPUT element, type=”file”.

 By default, Form is posted with application/x-www-form-urlencoded type and its content will be as below,

Content-Type: application/x-www-form-urlencoded


# Here it is easily to strip its key/value

 However for type multipart/form-data, its content will be,

Content-Type: multipart/form-data; boundary=—————————11619042012130902275583102315 —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”dbsid” fx2 —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”appsschema” APPSSHEMA —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”appspass” APPSPASSWORD —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”systemschema” SYSTEMSCHEMA —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”systempass” MANAGER —————————–11619042012130902275583102315 Content-Disposition: form-data; name=”Submit” Submit—————————–11619042012130902275583102315–

# It is a bit difficult to strip key/value from it. To support that I added a method parseContent() to retrieve the key/value pairs in the same format as ‘application/x-www-form-urlencoded’ – key=value&key1=value1&key2=value2

# check whether form type is “multipart/form-data”

# if yes, it will retrieve it in format of ‘key=value&key1=value1’.

sub parseContent{
my $paramstr = shift;
my $pos = index($paramstr, "name=");
my $replaced = substr($paramstr, 0, $pos);
my @array = split(/$replaced/, $paramstr);
my $s = "";
my $count = 0;
my $sclr = scalar(@array) - 1;
foreach my $line (@array){
if(($count &gt; 0) and ($count &lt; $sclr)){
$line =~ s/name=//;
$line =~ s/\s+/=/;
$line =~ s/^\s*|\s*$//g;
$s .= $line . "\&amp;";
$s = substr($s,0,length($s)-1);
return $s;

sub getParams{
my $s = shift;
#$s = "hahaid=haha1&amp;abcid=abc2&amp;Submit=submit";
if ($s =~ m/form-data/){
$s = parseContent($s);
my %params = {};
my @pairs = split(/\&amp;/,$s);
foreach (@pairs){
my ($key, $value) = split(/=/,$_);
$params{$key} = $value;
return %params;

Tricky way to instantiate a class in Perl

Today I started to design a CGI engine for my light weighted Perl Web container and I wanted to share my solution about how to instantiate the CGI class here.

Let’s see how a form works on Perl CGI:

1. User submit a form request,

<form action="/firstapp/test.pl" method="post"><select name="userid">
<option value="user1">1</option><option value="user2">2</option>
</select><select name="dbid">
<option value="db1">1</option>
<option value="db2">2</option></select>
<input type="submit" name="Submit" value="submit" />


2. The form request comes to server side in below format,

POST /firstapp/test.pl HTTP/1.1
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: en-us,en;q=0.5
Host: localhost:8080
Referer: http://localhost:8080/firstapp/login.html
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Content-Length: 37
Content-Type: application/x-www-form-urlencoded


3. On Server side, CGI engine injects parameters submitted via form ‘hahaid=haha2&abcid=abc1&Submit=submit’ to the action page ‘/firstapp/test.pl’,

4. Developer can access those paramters using method my  param(‘$key’); E.g,

use CGI qw(:standard);

my $userid  = param('userid'); # Here it will return value "user2".

In my CGI implementation, I design it as below process,

Step 1, 2 are the same as above as both are client operation.

Starting from Step 3,

On Server side, my CGI engine class,

sub new{
my $classname = shift;
my $self = {};
bless($self, $classname);
return $self;

sub _init {
my $self = shift;
if (@_) {
my %extra = @_;
my $paramstr = $extra{"PARAMS"};# PARAMS is a keyword here
if (defined $paramstr) {
my %params = getParams($paramstr);
@$self{keys %params} = values %params;

sub getParams{
my $s = shift;
my %params = {};
my @pairs = split(/\&/,$s);
foreach (@pairs){
my ($key, $value) = split(/=/,$_);
$params{$key} = $value;
return %params;

Step 4. Developer can access those paramters with below steps:

1. import my CGI,

use camelcgi;

2. Instantiate it,

my $cgiinst = camelcgi-&gt;new(PARAMS =&gt; $ARGV[0]); 
# In my HTTP Daemon, it will pass the request content to it 
# as $ARG[0] automatically.

# E.g, $ARGV[0]=”userid=user2&dbid=db1&Submit=submit”

3. Retrieve those parameters with,

$cgiinst-&gt;{userid} ;

Let’s summarize the snippet here,

use camelcgi;

my $cgiinst = camelcgi-&gt;new(PARAMS =&gt; $ARGV[0]);

$cgiinst-&gt;{userid} ;

It is tricky, right? 🙂

HTTP:Daemon in Perl

Yesterday I tried socket in Perl in post Socket in Perl. Today I will share my experience in HTTP:Daemon.

Basically HTTP package implements the necessary elements defined in http://tools.ietf.org/pdf/rfc2616.pdf (Hypertext Transfer Protocol — HTTP/1.1). For example, on my post Socket in Perl yesterday, the connection is established using socket and it doesn’t support cache and this brings unnecessary network load.  However from http://tools.ietf.org/pdf/rfc2616.pdf, it defines the GET method as below. (I tested HTTP:Daemon seems it is implemented well to support cache).

9.3 GET
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.
If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the entity
in the response and not the source text of the process, unless that text happens to be the output of the process.
The semantics of the GET method change to a “conditional GET” if the request message includes an IfModified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. A
conditional GET method requests that the entity be transferred only under the circumstances described by the
conditional header field(s). The conditional GET method is intended to reduce unnecessary network usage by
allowing cached entities to be refreshed without requiring multiple requests or transferring data already held by the

API provided by HTTP:Daemon from http://search.cpan.org/~gaas/libwww-perl-5.837/lib/HTTP/Daemon.pm

Additionally, I introduced the fork in this Daemon script.

The logic in the script:

1. Accept a http request connection.

2. Fork a process to take care of the request.

3. Return to 1.


package camelhttp;

use strict;
use HTTP::Daemon;
use HTTP::Status;
use Carp;
use FileHandle;
use camellogger;
use camelutils;

my $portno = "8080";
my $base_dir = "../webapps";
my $SERVER = HTTP::Daemon->new(LocalPort => $portno, LocalAddr => 'localhost')
 or die;


sub camelHttpCore{

# autoreaping of zombies
while (1)
while (my $con = $SERVER->accept){
logger(0, "$0 - Camel is forking a worker");
next if my $pid = fork; #parent
die logger(0, "$0 - Camel can't fork a worker : $!") unless defined $pid;
while (my $req = $con->get_request){

my $path = $base_dir . $req->uri->path;
if ($req->method eq 'GET'){
if (_handleGet($path) eq "YES"){
} else {

} elsif ($req->method eq 'POST') {
} continue {
# nothing here so far.

sub _handleGet{
my ($path) = @_;
if ($fshash{"$path"}){
return "YES";
} else {
return "NO";

sub _destroy {
logger(0, "$0 - Camel is destroying itself");
close ($SERVER) if (defined $SERVER);


Socket in Perl

Today I started to wrote a light weighted Web container in Perl (I named it as Camel – hats off to Tomcat). Basically my target is, it can take care of all of the static requests (html, txt, etc) in async, and it can support Perl CGI. It should have an elegant architecture (basically I will steal the filesystem architecture from Tomcat). Below is the snippet which can provide HTTP service for static requests in sync mode.

I think it is a tiniest web container with Perl’s Socket support.
#Pls. comment out the logger() from below snippet if you want to run it from your side.

package camel;

use strict;
use Socket;
use Carp;
use FileHandle;
use camellogger;
use camelutils;

my $portno = "8080";
my $base_dir = "../webapps";

logger(0, "$0 - Loading Camel Core");
logger(2, "$0 - Making, Setting and binding socket");
socket(SERVER, PF_INET, SOCK_STREAM, getprotobyname('tcp')) 
or die "can't make the socket : $! \n";
or die "can't set socket options : $! \n";
my $my_addr = sockaddr_in($portno, INADDR_ANY);
bind(SERVER, $my_addr) 
or die "can't bind to port $portno : $! \n";
logger(0, "$0 - Camel is listening on port $portno");
or die "can't listen on port $portno : $! \n";

while (1)
while (my $newcon = accept(CLIENT, SERVER)){
my ($portno, $ipaddr) = sockaddr_in($newcon);
my $hostname = gethostbyaddr($ipaddr, AF_INET);

logger(0, "$0 - Camel is connecting with $hostname");
my $getinfo = &lt;CLIENT&gt; ;
print "getinfo: $getinfo \n";
my $rsppage = $base_dir . "/" . dispatch($getinfo);

open IN, "&lt; $rsppage" 
or warn logger(0, "$0 - Camel can't talk with $hostname : $!");
while (&lt;IN&gt;)
print CLIENT $_ . "\n";
close IN;
logger(0, "$0 - Goodbye with $hostname");

sub dispatch(){
my ($info) = @_;
my ($s, $url) = split(" ",$info);
$url =~ s/^\s*|\s*$//g;
$url = substr($url, 1);
if ($url eq "") {
$url = "login.html";
return $url;